Commit 65b601e83727e818cb27b57f4a2dfb59e4d9edf4
authorhovercraft-github <hovercraft@yandex.ru>
Wed, 19 Oct 2016 02:54:49 +0000 (10:54 +0800)
committerhovercraft-github <hovercraft@yandex.ru>
Wed, 19 Oct 2016 02:54:49 +0000 (10:54 +0800)
2 files changed:
simavr/sim/avr_extint.c
simavr/sim/avr_extint.h

index 9a663b00f5aa71a8d0adcd87fcc2d28b0aaebd86..2d6dbbb9dca683ce3289cd020d1f1ec27681773a 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #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);                    
                }
        }
 }
index 5c73d5a98e649c900f4e89ad1eb5c7dcb33f80c5..62aa154342817cc8532741420c561ab547ece639 100644 (file)
@@ -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.