diff --git a/simio/simio_timer.c b/simio/simio_timer.c index b40157e..2d304c3 100644 --- a/simio/simio_timer.c +++ b/simio/simio_timer.c @@ -168,14 +168,40 @@ static int config_irq(int *irq, char **arg_text) return 0; } +static void trigger_capture(struct timer *tr, int which, int oldval, int value) +{ + uint16_t edge_flags = 0; + + tr->ctls[which] &= ~CCI; + if (value) + tr->ctls[which] |= CCI; + + if (oldval && !value) + edge_flags |= CM1; + if (!oldval && value) + edge_flags |= CM0; + + printc_dbg("Timer channel %d: %s => %s\n", + which, oldval ? "H" : "L", value ? "H" : "L"); + + if ((tr->ctls[which] & edge_flags) && (tr->ctls[which] & CAP)) { + if (tr->ctls[which] & CCIFG) { + printc_dbg("Timer capture overflow\n"); + tr->ctls[which] |= COV; + } else { + printc_dbg("Timer capture interrupt triggered\n"); + tr->ccrs[which] = tr->tar; + tr->ctls[which] |= CCIFG; + } + } +} + static int config_channel(struct timer *tr, char **arg_text) { char *which_text = get_arg(arg_text); char *value_text = get_arg(arg_text); address_t which; address_t value; - int oldval; - uint16_t edge_flags = 0; if (!(which_text && value_text)) { printc_err("timer: config: expected channel and value\n"); @@ -199,29 +225,7 @@ static int config_channel(struct timer *tr, char **arg_text) return -1; } - oldval = tr->ctls[which] & CCI; - tr->ctls[which] &= ~CCI; - if (value) - tr->ctls[which] |= CCI; - - if (oldval && !value) - edge_flags |= CM1; - if (!oldval && value) - edge_flags |= CM0; - - printc_dbg("Timer channel %d: %s => %s\n", - which, oldval ? "H" : "L", value ? "H" : "L"); - - if ((tr->ctls[which] & edge_flags) && (tr->ctls[which] & CAP)) { - if (tr->ctls[which] & CCIFG) { - printc_dbg("Timer capture overflow\n"); - tr->ctls[which] |= COV; - } else { - printc_dbg("Timer capture interrupt triggered\n"); - tr->ccrs[which] = tr->tar; - tr->ctls[which] |= CCIFG; - } - } + trigger_capture(tr, which, tr->ctls[which] & CCI, value); return 0; } @@ -311,9 +315,12 @@ static int timer_write(struct simio_device *dev, if (addr >= tr->base_addr + 2 && addr < tr->base_addr + (tr->size << 1) + 2) { int index = ((addr & 0xf) - 2) >> 1; + uint16_t oldval = tr->ctls[index]; - tr->ctls[index] = (data & 0xf9f7) | - (tr->ctls[index] & 0x0608); + tr->ctls[index] = (data & 0xf9f7) | (oldval & 0x0608); + /* Check capture initiated by Software */ + if ((data & (CAP | CCIS1)) == (CAP | CCIS1)) + trigger_capture(tr, index, oldval & CCI, data & CCIS0); return 0; } diff --git a/simio/tests/test_timer.c b/simio/tests/test_timer.c index 4581d4b..6d77589 100644 --- a/simio/tests/test_timer.c +++ b/simio/tests/test_timer.c @@ -505,7 +505,79 @@ static void test_timer_divider() assert(read_timer(dev, TxR) == 10); } -static void test_timer_capture() +static void test_timer_capture_by_software() +{ + dev = create_timer(""); + + /* Continuous mode, ACLK, clear */ + write_timer(dev, TxCTL, MC1 | TASSEL0 | TACLR); + + /* Capture mode, input GND, both edge */ + write_timer(dev, TxCCTL(0), CAP | CCIS1 | CM1 | CM0); + step_aclk(dev, 10); + // Rising edge. + write_timer(dev, TxCCTL(0), read_timer(dev, TxCCTL(0)) | CCIS0); + assert(read_timer(dev, TxCCTL(0)) & CCI); + assert_not(read_timer(dev, TxCCTL(0)) & COV); + assert(read_timer(dev, TxCCTL(0)) & CCIFG); + assert(read_timer(dev, TxCCR(0)) == 10); + + write_timer(dev, TxCCTL(0), read_timer(dev, TxCCTL(0)) & ~CCIFG); + step_aclk(dev, 10); + // Falling edge. + write_timer(dev, TxCCTL(0), read_timer(dev, TxCCTL(0)) & ~CCIS0); + assert_not(read_timer(dev, TxCCTL(0)) & CCI); + assert_not(read_timer(dev, TxCCTL(0)) & COV); + assert(read_timer(dev, TxCCTL(0)) & CCIFG); + assert(read_timer(dev, TxCCR(0)) == 20); + + // Keep CCIFG on and capture causes COV */ + step_aclk(dev, 10); + // Rising edge. + write_timer(dev, TxCCTL(0), read_timer(dev, TxCCTL(0)) | CCIS0); + assert(read_timer(dev, TxCCTL(0)) & CCI); + assert(read_timer(dev, TxCCTL(0)) & COV); + assert(read_timer(dev, TxCCTL(0)) & CCIFG); + assert(read_timer(dev, TxCCR(0)) == 20); + + /* Capture mode, input GND, rising edge */ + write_timer(dev, TxCCTL(1), CAP | CCIS1 | CM0); + // Rising edge. + write_timer(dev, TxCCTL(1), read_timer(dev, TxCCTL(1)) | CCIS0); + assert(read_timer(dev, TxCCTL(1)) & CCI); + assert_not(read_timer(dev, TxCCTL(1)) & COV); + assert(read_timer(dev, TxCCTL(1)) & CCIFG); + assert(read_timer(dev, TxCCR(1)) == 30); + + write_timer(dev, TxCCTL(1), read_timer(dev, TxCCTL(1)) & ~CCIFG); + step_aclk(dev, 10); + // Falling edge. + write_timer(dev, TxCCTL(1), read_timer(dev, TxCCTL(1)) & ~CCIS0); + assert_not(read_timer(dev, TxCCTL(1)) & CCI); + assert_not(read_timer(dev, TxCCTL(1)) & COV); + assert_not(read_timer(dev, TxCCTL(1)) & CCIFG); + assert(read_timer(dev, TxCCR(1)) == 30); + + /* Capture mode, input GND, falling edge */ + write_timer(dev, TxCCTL(2), CAP | CCIS1 | CM1); + // Rising edge. + write_timer(dev, TxCCTL(2), read_timer(dev, TxCCTL(2)) | CCIS0); + assert(read_timer(dev, TxCCTL(2)) & CCI); + assert_not(read_timer(dev, TxCCTL(2)) & COV); + assert_not(read_timer(dev, TxCCTL(2)) & CCIFG); + assert(read_timer(dev, TxCCR(2)) == 0); + + step_aclk(dev, 10); + // Falling edge. + write_timer(dev, TxCCTL(2), read_timer(dev, TxCCTL(2)) & ~CCIS0); + assert_not(read_timer(dev, TxCCTL(2)) & CCI); + assert_not(read_timer(dev, TxCCTL(2)) & COV); + assert(read_timer(dev, TxCCTL(2)) & CCIFG); + assert(read_timer(dev, TxCCR(2)) == 50); +} + + +static void test_timer_capture_by_signal() { dev = create_timer(""); @@ -685,6 +757,7 @@ int main(int argc, char **argv) RUN_TEST(test_timer_up_change_period); RUN_TEST(test_timer_updown_change_period); RUN_TEST(test_timer_divider); - RUN_TEST(test_timer_capture); + RUN_TEST(test_timer_capture_by_software); + RUN_TEST(test_timer_capture_by_signal); RUN_TEST(test_timer_compare); }