From: hovercraft-github Date: Wed, 19 Oct 2016 02:54:49 +0000 (+0800) Subject: Implement triggering by level for external interrupts. Fixes bug with Sense Control... X-Git-Tag: v1.4~20^2 X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=65b601e83727e818cb27b57f4a2dfb59e4d9edf4;p=sx%2Fsimavr.git Implement triggering by level for external interrupts. Fixes bug with Sense Control Bits number detection. --- diff --git a/simavr/sim/avr_extint.c b/simavr/sim/avr_extint.c index 9a663b0..2d6dbbb 100644 --- a/simavr/sim/avr_extint.c +++ b/simavr/sim/avr_extint.c @@ -24,7 +24,101 @@ #include #include #include "avr_extint.h" +#include "avr_ioport.h" +typedef struct avr_extint_poll_context_t { + uint32_t eint_no; // index of particular interrupt source we are monitoring + avr_extint_t *extint; +} avr_extint_poll_context_t; + +static avr_cycle_count_t avr_extint_poll_level_trig( + struct avr_t * avr, + avr_cycle_count_t when, + void * param) +{ + avr_extint_poll_context_t *poll = (avr_extint_poll_context_t *)param; + avr_extint_t * p = (avr_extint_t *)poll->extint; + + char port = p->eint[poll->eint_no].port_ioctl & 0xFF; + avr_ioport_state_t iostate; + if (avr_ioctl(avr, AVR_IOCTL_IOPORT_GETSTATE( port ), &iostate) < 0) + goto terminate_poll; + uint8_t bit = ( iostate.pin >> p->eint[poll->eint_no].port_pin ) & 1; + if (bit) + goto terminate_poll; // Only poll while pin level remains low + + if (avr->sreg[S_I]) { + uint8_t raised = avr_regbit_get(avr, p->eint[poll->eint_no].vector.raised) || p->eint[poll->eint_no].vector.pending; + if (!raised) + avr_raise_interrupt(avr, &p->eint[poll->eint_no].vector); + } + + return when+1; + +terminate_poll: + free(poll); + return 0; +} + +static avr_extint_t * avr_extint_get(avr_t * avr) +{ + if (!avr) + return NULL; + avr_io_t * periferal = avr->io_port; + while (periferal) { + if (!strcmp(periferal->kind, "extint")) { + return (avr_extint_t *)periferal; + } + periferal = periferal->next; + } + return NULL; +} + +static uint8_t avr_extint_count(avr_extint_t *extint) +{ + uint8_t count = 0; + if (!extint) + return 0; + while (count < EXTINT_COUNT) { + if (!extint->eint[count].port_ioctl) + break; + count++; + } + return count; +} + +/** + * @brief avr_extint_is_strict_lvl_trig + * @param avr + * @param extint_no: an ext interrupt number, e.g. 0 or 1 (corresponds to INT0 or INT1) + * @return -1 if irrelevant extint_no given, strict + * level triggering flag otherwise. + */ +int avr_extint_is_strict_lvl_trig(avr_t * avr, uint8_t extint_no) +{ + avr_extint_t *p = avr_extint_get(avr); + if (!p || (avr_extint_count(p) <= extint_no)) + return -1; + if (!p->eint[extint_no].isc[1].reg) + return -1; // this is edge-only triggered interrupt + return p->eint[extint_no].strict_lvl_trig; +} + +/** + * @brief avr_extint_set_strict_lvl_trig + * @param avr + * @param extint_no: an ext interrupt number, e.g. 0 or 1 (corresponds to INT0 or INT1) + * @param strict: new value for level triggering flag + */ +void avr_extint_set_strict_lvl_trig(avr_t * avr, uint8_t extint_no, uint8_t strict) +{ + avr_extint_t *p = avr_extint_get(avr); + if (!p || (avr_extint_count(p) <= extint_no)) + return; + if (!p->eint[extint_no].isc[1].reg) + return; // this is edge-only triggered interrupt + p->eint[extint_no].strict_lvl_trig = strict; +} static void avr_extint_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param) { @@ -34,7 +128,8 @@ static void avr_extint_irq_notify(struct avr_irq_t * irq, uint32_t value, void * int up = !irq->value && value; int down = irq->value && !value; - uint8_t isc_bits = p->eint[irq->irq + 1].isc->reg ? 2 : 1; + // ?? uint8_t isc_bits = p->eint[irq->irq + 1].isc->reg ? 2 : 1; + uint8_t isc_bits = p->eint[irq->irq].isc[1].reg ? 2 : 1; uint8_t mode = avr_regbit_get_array(avr, p->eint[irq->irq].isc, isc_bits); // Asynchronous interrupts, eg int2 in m16, m32 etc. support only down/up @@ -42,18 +137,42 @@ static void avr_extint_irq_notify(struct avr_irq_t * irq, uint32_t value, void * mode +=2; switch (mode) { - case 0: - // unsupported + case 0: // Level triggered (low level) interrupt + { + /** + Datasheet excerpt: + >When the external interrupt is enabled and is configured as level triggered (only INT0/INT1), + >the interrupt will trigger as long as the pin is held low. + Thus we have to query the pin value continiously while it's held low and try to trigger the interrupt. + This can be expensive, so avr_extint_set_strict_lvl_trig function provisioned to allow the user + to turn this feature off. In this case bahaviour will be similar to the falling edge interrupt. + */ + if (!value) { + if (avr->sreg[S_I]) { + uint8_t raised = avr_regbit_get(avr, p->eint[irq->irq].vector.raised) || p->eint[irq->irq].vector.pending; + if (!raised) + avr_raise_interrupt(avr, &p->eint[irq->irq].vector); + } + if (p->eint[irq->irq].strict_lvl_trig) { + avr_extint_poll_context_t *poll = malloc(sizeof(avr_extint_poll_context_t)); + if (poll) { + poll->eint_no = irq->irq; + poll->extint = p; + avr_cycle_timer_register(avr, 1, avr_extint_poll_level_trig, poll); + } + } + } + } break; - case 1: + case 1: // Toggle-triggered interrupt if (up || down) avr_raise_interrupt(avr, &p->eint[irq->irq].vector); break; - case 2: + case 2: // Falling edge triggered if (down) avr_raise_interrupt(avr, &p->eint[irq->irq].vector); break; - case 3: + case 3: // Rising edge trigggerd if (up) avr_raise_interrupt(avr, &p->eint[irq->irq].vector); break; @@ -68,10 +187,12 @@ static void avr_extint_reset(avr_io_t * port) avr_irq_register_notify(p->io.irq + i, avr_extint_irq_notify, p); if (p->eint[i].port_ioctl) { + if (p->eint[i].isc[1].reg) // level triggering available + p->eint[i].strict_lvl_trig = 1; // turn on repetitive level triggering by default avr_irq_t * irq = avr_io_getirq(p->io.avr, p->eint[i].port_ioctl, p->eint[i].port_pin); - avr_connect_irq(irq, p->io.irq + i); + avr_connect_irq(irq, p->io.irq + i); } } } diff --git a/simavr/sim/avr_extint.h b/simavr/sim/avr_extint.h index 5c73d5a..62aa154 100644 --- a/simavr/sim/avr_extint.h +++ b/simavr/sim/avr_extint.h @@ -58,11 +58,15 @@ typedef struct avr_extint_t { uint32_t port_ioctl; // ioctl to use to get port uint8_t port_pin; // pin number in said port + uint8_t strict_lvl_trig;// enforces a repetitive interrupt triggering while the pin is held low } eint[EXTINT_COUNT]; } avr_extint_t; void avr_extint_init(avr_t * avr, avr_extint_t * p); +int avr_extint_is_strict_lvl_trig(avr_t * avr, uint8_t extint_no); +void avr_extint_set_strict_lvl_trig(avr_t * avr, uint8_t extint_no, uint8_t strict); + // Declares a typical INT into a avr_extint_t in a core. // this is a shortcut since INT declarations are pretty standard.