ICP: Added Input capture pin support to timers
authorMichel Pollet <buserror@gmail.com>
Thu, 29 Apr 2010 10:51:45 +0000 (11:51 +0100)
committerMichel Pollet <buserror@gmail.com>
Thu, 29 Apr 2010 10:51:45 +0000 (11:51 +0100)
Added (untested as of now) Input Pin Capture to the 16 bits
timers. The "edge" flag is also handled. The code is untested
for now, it will need an "example" board that will be
checked in later.

Signed-off-by: Michel Pollet <buserror@gmail.com>
simavr/cores/sim_mega128.c
simavr/cores/sim_megax4.h
simavr/cores/sim_megax8.h
simavr/cores/sim_tiny2313.c
simavr/sim/avr_timer.c
simavr/sim/avr_timer.h

index bc5a22c..d1c5b35 100644 (file)
@@ -271,6 +271,9 @@ struct mcu_t {
                .r_icrh = ICR1H,
                .r_tcnth = TCNT1H,
 
+               .ices = AVR_IO_REGBIT(TCCR1B, ICES1),
+               .icp = AVR_IO_REGBIT(PORTD, 4),
+
                .overflow = {
                        .enable = AVR_IO_REGBIT(TIMSK, TOIE1),
                        .raised = AVR_IO_REGBIT(TIFR, TOV1),
@@ -379,6 +382,9 @@ struct mcu_t {
                .r_icrh = ICR3H,
                .r_tcnth = TCNT3H,
 
+               .ices = AVR_IO_REGBIT(TCCR3B, ICES3),
+               .icp = AVR_IO_REGBIT(PORTE, 7),
+
                .overflow = {
                        .enable = AVR_IO_REGBIT(ETIMSK, TOIE3),
                        .raised = AVR_IO_REGBIT(ETIFR, TOV3),
index 2f59a27..ae02e32 100644 (file)
@@ -284,9 +284,12 @@ struct mcu_t SIM_CORENAME = {
                .cs_div = { 0, 0, 3 /* 8 */, 6 /* 64 */, 8 /* 256 */, 10 /* 1024 */  /* External clock T1 is not handled */},
 
                .r_tcnt = TCNT1L,
+               .r_tcnth = TCNT1H,
                .r_icr = ICR1L,
                .r_icrh = ICR1H,
-               .r_tcnth = TCNT1H,
+
+               .ices = AVR_IO_REGBIT(TCCR1B, ICES1),
+               .icp = AVR_IO_REGBIT(PORTD, 6),
 
                .overflow = {
                        .enable = AVR_IO_REGBIT(TIMSK1, TOIE1),
index 4ffe209..6f6d006 100644 (file)
@@ -232,9 +232,12 @@ struct mcu_t SIM_CORENAME = {
                .cs_div = { 0, 0, 3 /* 8 */, 6 /* 64 */, 8 /* 256 */, 10 /* 1024 */  /* External clock T1 is not handled */},
 
                .r_tcnt = TCNT1L,
+               .r_tcnth = TCNT1H,
                .r_icr = ICR1L,
                .r_icrh = ICR1H,
-               .r_tcnth = TCNT1H,
+
+               .ices = AVR_IO_REGBIT(TCCR1B, ICES1),
+               .icp = AVR_IO_REGBIT(PORTB, 0),
 
                .overflow = {
                        .enable = AVR_IO_REGBIT(TIMSK1, TOIE1),
index c78ebaa..e0af609 100644 (file)
@@ -167,6 +167,9 @@ static struct mcu_t {
                .r_icrh = ICR1H,
                .r_tcnth = TCNT1H,
 
+               .ices = AVR_IO_REGBIT(TCCR1B, ICES1),
+               .icp = AVR_IO_REGBIT(PORTD, 6),
+
                .overflow = {
                        .enable = AVR_IO_REGBIT(TIMSK, TOIE1),
                        .raised = AVR_IO_REGBIT(TIFR, TOV1),
index cb2c99f..d20f313 100644 (file)
@@ -120,22 +120,27 @@ static avr_cycle_count_t avr_timer_tov(struct avr_t * avr, avr_cycle_count_t whe
        return when + p->tov_cycles;
 }
 
+static uint16_t _avr_timer_get_current_tcnt(avr_timer_t * p)
+{
+       avr_t * avr = p->io.avr;
+       if (p->tov_cycles) {
+               uint64_t when = avr->cycle - p->tov_base;
+
+               return (when * p->tov_top) / p->tov_cycles;
+       }
+       return 0;
+}
 
 static uint8_t avr_timer_tcnt_read(struct avr_t * avr, avr_io_addr_t addr, void * param)
 {
        avr_timer_t * p = (avr_timer_t *)param;
        // made to trigger potential watchpoints
 
-       if (p->tov_cycles) {
-               uint64_t when = avr->cycle - p->tov_base;
-
-               uint16_t tcnt = (when * p->tov_top) / p->tov_cycles;
-       //      printf("%s-%c when = %d tcnt = %d/%d\n", __FUNCTION__, p->name, (uint32_t)when, tcnt, p->tov_top);
+       uint16_t tcnt = _avr_timer_get_current_tcnt(p);
 
-               avr->data[p->r_tcnt] = tcnt;
-               if (p->r_tcnth)
-                       avr->data[p->r_tcnth] = tcnt >> 8;
-       }
+       avr->data[p->r_tcnt] = tcnt;
+       if (p->r_tcnth)
+               avr->data[p->r_tcnth] = tcnt >> 8;
        
        return avr_core_watch_read(avr, addr);
 }
@@ -291,6 +296,32 @@ static void avr_timer_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, v
        avr_timer_reconfigure(p);
 }
 
+static void avr_timer_irq_icp(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+       avr_timer_t * p = (avr_timer_t *)param;
+       avr_t * avr = p->io.avr;
+
+       // input capture disabled when ICR is used as top
+       if (p->mode.top == avr_timer_wgm_reg_icr)
+               return;
+       int bing = 0;
+       if (avr_regbit_get(avr, p->ices)) { // rising edge
+               if (!irq->value && value)
+                       bing++;
+       } else {        // default, falling edge
+               if (irq->value && !value)
+                       bing++;
+       }
+       if (!bing)
+               return;
+       // get current TCNT, copy it to ICR, and raise interrupt
+       uint16_t tcnt = _avr_timer_get_current_tcnt(p);
+       avr->data[p->r_icr] = tcnt;
+       if (p->r_icrh)
+               avr->data[p->r_icrh] = tcnt >> 8;
+       avr_raise_interrupt(avr, &p->icr);
+}
+
 static void avr_timer_reset(avr_io_t * port)
 {
        avr_timer_t * p = (avr_timer_t *)port;
@@ -315,6 +346,15 @@ static void avr_timer_reset(avr_io_t * port)
                        avr_connect_irq(&port->irq[TIMER_IRQ_OUT_COMP + compi], req.irq[0]);
                }
        }
+       avr_ioport_getirq_t req = {
+               .bit = p->icp
+       };
+       if (avr_ioctl(port->avr, AVR_IOCTL_IOPORT_GETIRQ_REGBIT, &req) > 0) {
+               // cool, got an IRQ for the input capture pin
+//             printf("%s-%c ICP Connecting PIN IRQ %d\n", __FUNCTION__, p->name, req.irq[0]->irq);
+               avr_irq_register_notify(req.irq[0], avr_timer_irq_icp, p);
+       }
+
 }
 
 static avr_io_t        _io = {
@@ -330,6 +370,10 @@ void avr_timer_init(avr_t * avr, avr_timer_t * p)
        p->io.irq_count = TIMER_IRQ_COUNT;
        p->io.irq = avr_alloc_irq(0, p->io.irq_count);
        p->io.irq_ioctl_get = AVR_IOCTL_TIMER_GETIRQ(p->name);
+
+       // marking IRQs as "filtered" means they don't propagate if the
+       // new value raised is the same as the last one.. in the case of the
+       // pwm value it makes sense not to bother.
        p->io.irq[TIMER_IRQ_OUT_PWM0].flags |= IRQ_FLAG_FILTERED;
        p->io.irq[TIMER_IRQ_OUT_PWM1].flags |= IRQ_FLAG_FILTERED;
 
index a69fcb3..755531e 100644 (file)
@@ -95,6 +95,8 @@ typedef struct avr_timer_t {
        avr_regbit_t    cs[4];
        uint8_t                 cs_div[16];
        avr_regbit_t    as2;            // asynchronous clock 32khz
+       avr_regbit_t    icp;            // input capture pin, to link IRQs
+       avr_regbit_t    ices;           // input capture edge select
 
        struct {
                avr_int_vector_t        interrupt;              // interrupt vector