From: Michel Pollet Date: Sat, 10 Apr 2010 12:16:37 +0000 (+0100) Subject: timer: Added comparator modes for pins X-Git-Tag: v1.0a2~8 X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=1c66d76823558731f3c7d091bdff84a5816f0a1b;p=sx%2Fsimavr.git timer: Added comparator modes for pins Refactored the comparators code, Added the 3 common modes of pin toggles. Signed-off-by: Michel Pollet --- diff --git a/simavr/sim/avr_timer.c b/simavr/sim/avr_timer.c index b853868..b52671b 100644 --- a/simavr/sim/avr_timer.c +++ b/simavr/sim/avr_timer.c @@ -26,21 +26,16 @@ #include #include "avr_timer.h" - +#include "avr_ioport.h" /* * The timers are /always/ 16 bits here, if the higher byte register * is specified it's just added. */ -static uint16_t _timer_get_ocra(avr_timer_t * p) -{ - return p->io.avr->data[p->r_ocra] | - (p->r_ocrah ? (p->io.avr->data[p->r_ocrah] << 8) : 0); -} -static uint16_t _timer_get_ocrb(avr_timer_t * p) +static uint16_t _timer_get_ocr(avr_timer_t * p, int compi) { - return p->io.avr->data[p->r_ocrb] | - (p->r_ocrbh ? (p->io.avr->data[p->r_ocrbh] << 8) : 0); + return p->io.avr->data[p->comp[compi].r_ocr] | + (p->comp[compi].r_ocrh ? (p->io.avr->data[p->comp[compi].r_ocrh] << 8) : 0); } static uint16_t _timer_get_tcnt(avr_timer_t * p) { @@ -52,46 +47,75 @@ static uint16_t _timer_get_icr(avr_timer_t * p) return p->io.avr->data[p->r_icr] | (p->r_tcnth ? (p->io.avr->data[p->r_icrh] << 8) : 0); } +static avr_cycle_count_t avr_timer_comp(avr_timer_t *p, avr_cycle_count_t when, uint8_t comp) +{ + avr_t * avr = p->io.avr; + avr_raise_interrupt(avr, &p->comp[comp].interrupt); + + // check output compare mode and set/clear pins + uint8_t mode = avr_regbit_get_array(avr, p->comp[comp].com, + ARRAY_SIZE(p->comp[comp].com)); + avr_irq_t * irq = &p->io.irq[TIMER_IRQ_OUT_COMP + comp]; + + switch (mode) { + case avr_timer_com_normal: // Normal mode OCnA disconnected + break; + case avr_timer_com_toggle: // Toggle OCnA on compare match + if (p->comp[comp].com_pin.reg) // we got a physical pin + avr_raise_irq(irq, + 0x100 + (avr_regbit_get(avr, p->comp[comp].com_pin) ? 0 : 1)); + else // no pin, toggle the IRQ anyway + avr_raise_irq(irq, + p->io.irq[TIMER_IRQ_OUT_COMP + comp].value ? 0 : 1); + break; + case avr_timer_com_clear: + avr_raise_irq(irq, 0); + break; + case avr_timer_com_set: + avr_raise_irq(irq, 1); + break; + } + + return p->tov_cycles ? 0 : p->comp[comp].comp_cycles ? when + + p->comp[comp].comp_cycles : 0; +} static avr_cycle_count_t avr_timer_compa(struct avr_t * avr, avr_cycle_count_t when, void * param) { - avr_timer_t * p = (avr_timer_t *)param; - avr_raise_interrupt(avr, &p->compa); - return p->tov_cycles ? 0 : p->compa_cycles ? when + p->compa_cycles : 0; + return avr_timer_comp((avr_timer_t*)param, when, AVR_TIMER_COMPA); } static avr_cycle_count_t avr_timer_compb(struct avr_t * avr, avr_cycle_count_t when, void * param) { - avr_timer_t * p = (avr_timer_t *)param; - avr_raise_interrupt(avr, &p->compb); - return p->tov_cycles ? 0 : p->compb_cycles ? when + p->compb_cycles : 0; + return avr_timer_comp((avr_timer_t*)param, when, AVR_TIMER_COMPB); +} + +static avr_cycle_count_t avr_timer_compc(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + return avr_timer_comp((avr_timer_t*)param, when, AVR_TIMER_COMPC); } static avr_cycle_count_t avr_timer_tov(struct avr_t * avr, avr_cycle_count_t when, void * param) { avr_timer_t * p = (avr_timer_t *)param; int start = p->tov_base == 0; - + if (!start) avr_raise_interrupt(avr, &p->overflow); p->tov_base = when; - if (p->compa_cycles) { - if (p->compa_cycles < p->tov_cycles) - avr_cycle_timer_register(avr, - p->compa_cycles - (avr->cycle - p->tov_base), - avr_timer_compa, p); - else if (p->tov_cycles == p->compa_cycles && !start) - avr_timer_compa(avr, when, param); - } - - if (p->compb_cycles) { - if (p->compb_cycles < p->tov_cycles) - avr_cycle_timer_register(avr, - p->compb_cycles - (avr->cycle - p->tov_base), - avr_timer_compb, p); - else if (p->tov_cycles == p->compb_cycles && !start) - avr_timer_compb(avr, when, param); + static const avr_cycle_timer_t dispatch[AVR_TIMER_COMP_COUNT] = + { avr_timer_compa, avr_timer_compb, avr_timer_compc }; + + for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) { + if (p->comp[compi].comp_cycles) { + if (p->comp[compi].comp_cycles < p->tov_cycles) + avr_cycle_timer_register(avr, + p->comp[compi].comp_cycles - (avr->cycle - p->tov_base), + dispatch[compi], p); + else if (p->tov_cycles == p->comp[compi].comp_cycles && !start) + dispatch[compi](avr, when, param); + } } return when + p->tov_cycles; @@ -136,6 +160,7 @@ static void avr_timer_tcnt_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t avr_cycle_timer_cancel(avr, avr_timer_tov, p); avr_cycle_timer_cancel(avr, avr_timer_compa, p); avr_cycle_timer_cancel(avr, avr_timer_compb, p); + avr_cycle_timer_cancel(avr, avr_timer_compc, p); uint64_t cycles = (tcnt * p->tov_cycles) / p->tov_top; @@ -152,31 +177,32 @@ static void avr_timer_tcnt_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t static void avr_timer_configure(avr_timer_t * p, uint32_t clock, uint32_t top) { - uint32_t ocra = _timer_get_ocra(p); - uint32_t ocrb = _timer_get_ocrb(p); - float fa = clock / (float)(ocra+1), fb = clock / (float)(ocrb+1); float t = clock / (float)(top+1); float frequency = p->io.avr->frequency; - p->compa_cycles = p->compb_cycles = p->tov_cycles = 0; + p->tov_cycles = 0; p->tov_top = top; - printf("%s-%c clock %d top %d a %d b %d\n", __FUNCTION__, p->name, clock, top, ocra, ocrb); p->tov_cycles = frequency / t; // avr_hz_to_cycles(frequency, t); printf("%s-%c TOP %.2fHz = %d cycles\n", __FUNCTION__, p->name, t, (int)p->tov_cycles); - if (ocra && ocra <= top) { - p->compa_cycles = frequency / fa; // avr_hz_to_cycles(p->io.avr, fa); - printf("%s-%c A %.2fHz = %d cycles\n", __FUNCTION__, p->name, fa, (int)p->compa_cycles); - } - if (ocrb && ocrb <= top) { - p->compb_cycles = frequency / fb; // avr_hz_to_cycles(p->io.avr, fb); - printf("%s-%c B %.2fHz = %d cycles\n", __FUNCTION__, p->name, fb, (int)p->compb_cycles); + for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) { + uint32_t ocr = _timer_get_ocr(p, compi); + float fc = clock / (float)(ocr); + + p->comp[compi].comp_cycles = 0; +// printf("%s-%c clock %d top %d OCR%c %d\n", __FUNCTION__, p->name, clock, top, 'A'+compi, ocr); + + if (ocr && ocr <= top) { + p->comp[compi].comp_cycles = frequency / fc; // avr_hz_to_cycles(p->io.avr, fa); + printf("%s-%c %c %.2fHz = %d cycles\n", __FUNCTION__, p->name, + 'A'+compi, fc, (int)p->comp[compi].comp_cycles); + } } if (p->tov_cycles > 1) { avr_cycle_timer_register(p->io.avr, p->tov_cycles, avr_timer_tov, p); - // calling it once, with when == 0 tells it to arm the A/B timers if needed + // calling it once, with when == 0 tells it to arm the A/B/C timers if needed p->tov_base = 0; avr_timer_tov(p->io.avr, p->io.avr->cycle, p); } @@ -189,13 +215,15 @@ static void avr_timer_reconfigure(avr_timer_t * p) avr_timer_wgm_t zero={0}; p->mode = zero; // cancel everything - p->compa_cycles = 0; - p->compb_cycles = 0; + p->comp[AVR_TIMER_COMPA].comp_cycles = 0; + p->comp[AVR_TIMER_COMPB].comp_cycles = 0; + p->comp[AVR_TIMER_COMPC].comp_cycles = 0; p->tov_cycles = 0; avr_cycle_timer_cancel(avr, avr_timer_tov, p); avr_cycle_timer_cancel(avr, avr_timer_compa, p); avr_cycle_timer_cancel(avr, avr_timer_compb, p); + avr_cycle_timer_cancel(avr, avr_timer_compc, p); long clock = avr->frequency; @@ -220,10 +248,10 @@ static void avr_timer_reconfigure(avr_timer_t * p) avr_timer_configure(p, f, (1 << p->mode.size) - 1); break; case avr_timer_wgm_ctc: { - avr_timer_configure(p, f, _timer_get_ocra(p)); + avr_timer_configure(p, f, _timer_get_ocr(p, AVR_TIMER_COMPA)); } break; case avr_timer_wgm_pwm: { - uint16_t top = p->mode.top == avr_timer_wgm_reg_ocra ? _timer_get_ocra(p) : _timer_get_icr(p); + uint16_t top = p->mode.top == avr_timer_wgm_reg_ocra ? _timer_get_ocr(p, AVR_TIMER_COMPA) : _timer_get_icr(p); avr_timer_configure(p, f, top); } break; case avr_timer_wgm_fast_pwm: @@ -238,16 +266,17 @@ static void avr_timer_write_ocr(struct avr_t * avr, avr_io_addr_t addr, uint8_t { avr_timer_t * p = (avr_timer_t *)param; avr_core_watch_write(avr, addr, v); + switch (p->mode.kind) { case avr_timer_wgm_pwm: if (p->mode.top != avr_timer_wgm_reg_ocra) { - avr_raise_irq(p->io.irq + TIMER_IRQ_OUT_PWM0, _timer_get_ocra(p)); - avr_raise_irq(p->io.irq + TIMER_IRQ_OUT_PWM1, _timer_get_ocrb(p)); + avr_raise_irq(p->io.irq + TIMER_IRQ_OUT_PWM0, _timer_get_ocr(p, AVR_TIMER_COMPA)); + avr_raise_irq(p->io.irq + TIMER_IRQ_OUT_PWM1, _timer_get_ocr(p, AVR_TIMER_COMPB)); } break; case avr_timer_wgm_fast_pwm: - avr_raise_irq(p->io.irq + TIMER_IRQ_OUT_PWM0, _timer_get_ocra(p)); - avr_raise_irq(p->io.irq + TIMER_IRQ_OUT_PWM1, _timer_get_ocrb(p)); + avr_raise_irq(p->io.irq + TIMER_IRQ_OUT_PWM0, _timer_get_ocr(p, AVR_TIMER_COMPA)); + avr_raise_irq(p->io.irq + TIMER_IRQ_OUT_PWM1, _timer_get_ocr(p, AVR_TIMER_COMPB)); break; default: printf("%s-%c mode %d\n", __FUNCTION__, p->name, p->mode.kind); @@ -269,8 +298,24 @@ static void avr_timer_reset(avr_io_t * port) avr_cycle_timer_cancel(p->io.avr, avr_timer_tov, p); avr_cycle_timer_cancel(p->io.avr, avr_timer_compa, p); avr_cycle_timer_cancel(p->io.avr, avr_timer_compb, p); - p->compa_cycles = 0; - p->compb_cycles = 0; + avr_cycle_timer_cancel(p->io.avr, avr_timer_compc, p); + + // check to see if the comparators have a pin output. If they do, + // (try) to get the ioport corresponding IRQ and connect them + // they will automagically be triggered when the comparator raises + // it's own IRQ + for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) { + p->comp[compi].comp_cycles = 0; + + avr_ioport_getirq_t req = { + .bit = p->comp[compi].com_pin + }; + if (avr_ioctl(port->avr, AVR_IOCTL_IOPORT_GETIRQ_REGBIT, &req) > 0) { + // cool, got an IRQ +// printf("%s-%c COMP%c Connecting PIN IRQ %d\n", __FUNCTION__, p->name, 'A'+compi, req.irq[0]->irq); + avr_connect_irq(&port->irq[TIMER_IRQ_OUT_COMP + compi], req.irq[0]); + } + } } static avr_io_t _io = { @@ -281,7 +326,6 @@ static avr_io_t _io = { void avr_timer_init(avr_t * avr, avr_timer_t * p) { p->io = _io; - //printf("%s timer%c created\n", __FUNCTION__, p->name); // allocate this module's IRQ p->io.irq_count = TIMER_IRQ_COUNT; @@ -291,8 +335,6 @@ void avr_timer_init(avr_t * avr, avr_timer_t * p) p->io.irq[TIMER_IRQ_OUT_PWM1].flags |= IRQ_FLAG_FILTERED; avr_register_io(avr, &p->io); - avr_register_vector(avr, &p->compa); - avr_register_vector(avr, &p->compb); avr_register_vector(avr, &p->overflow); avr_register_vector(avr, &p->icr); @@ -303,12 +345,12 @@ void avr_timer_init(avr_t * avr, avr_timer_t * p) * high bytes because the datasheet says that the low address is always * the trigger. */ - avr_register_io_write(avr, p->r_ocra, avr_timer_write_ocr, p); - if (p->r_ocrb) // not all timers have B register - avr_register_io_write(avr, p->r_ocrb, avr_timer_write, p); - if(p->r_ocrc) // but some have a C one - avr_register_io_write(avr, p->r_ocrc, avr_timer_write, p); - avr_register_io_write(avr, p->r_tcnt, avr_timer_tcnt_write, p); + for (int compi = 0; compi < AVR_TIMER_COMP_COUNT; compi++) { + avr_register_vector(avr, &p->comp[compi].interrupt); + if (p->comp[compi].r_ocr) // not all timers have all comparators + avr_register_io_write(avr, p->comp[compi].r_ocr, avr_timer_write_ocr, p); + } + avr_register_io_write(avr, p->r_tcnt, avr_timer_tcnt_write, p); avr_register_io_read(avr, p->r_tcnt, avr_timer_tcnt_read, p); } diff --git a/simavr/sim/avr_timer.h b/simavr/sim/avr_timer.h index 8ef1d95..bb2a878 100644 --- a/simavr/sim/avr_timer.h +++ b/simavr/sim/avr_timer.h @@ -24,15 +24,26 @@ #include "sim_avr.h" +enum { + AVR_TIMER_COMPA = 0, + AVR_TIMER_COMPB, + AVR_TIMER_COMPC, + + AVR_TIMER_COMP_COUNT +}; + enum { TIMER_IRQ_OUT_PWM0 = 0, TIMER_IRQ_OUT_PWM1, - TIMER_IRQ_COUNT + TIMER_IRQ_OUT_COMP, // comparator pins output IRQ + + TIMER_IRQ_COUNT = TIMER_IRQ_OUT_COMP + AVR_TIMER_COMP_COUNT }; // Get the internal IRQ corresponding to the INT #define AVR_IOCTL_TIMER_GETIRQ(_name) AVR_IOCTL_DEF('t','m','r',(_name)) +// Waweform generation modes enum { avr_timer_wgm_none = 0, // invalid mode avr_timer_wgm_normal, @@ -40,6 +51,16 @@ enum { avr_timer_wgm_pwm, avr_timer_wgm_fast_pwm, }; + +// Compare output modes +enum { + avr_timer_com_normal = 0,// Normal mode, OCnx disconnected + avr_timer_com_toggle, // Toggle OCnx on compare match + avr_timer_com_clear, // clear OCnx on compare match + avr_timer_com_set, // set OCnx on compare match + +}; + enum { avr_timer_wgm_reg_constant = 0, avr_timer_wgm_reg_ocra, @@ -59,14 +80,15 @@ typedef struct avr_timer_wgm_t { #define AVR_TIMER_WGM_OCPWM() { .kind = avr_timer_wgm_pwm, .top = avr_timer_wgm_reg_ocra } #define AVR_TIMER_WGM_ICPWM() { .kind = avr_timer_wgm_pwm, .top = avr_timer_wgm_reg_icr } + typedef struct avr_timer_t { avr_io_t io; char name; avr_regbit_t disabled; // bit in the PRR - avr_io_addr_t r_ocra, r_ocrb, r_ocrc, r_tcnt, r_icr; - avr_io_addr_t r_ocrah, r_ocrbh, r_ocrch, r_tcnth, r_icrh; - + avr_io_addr_t r_tcnt, r_icr; + avr_io_addr_t r_tcnth, r_icrh; + avr_regbit_t wgm[4]; avr_timer_wgm_t wgm_op[16]; @@ -74,16 +96,19 @@ typedef struct avr_timer_t { uint8_t cs_div[16]; avr_regbit_t as2; // asynchronous clock 32khz - avr_int_vector_t compa; // comparator A - avr_int_vector_t compb; // comparator B - avr_int_vector_t compc; // comparator C TODO: code + struct { + avr_int_vector_t interrupt; // interrupt vector + avr_io_addr_t r_ocr; // comparator register low byte + avr_io_addr_t r_ocrh; // comparator register hi byte + avr_regbit_t com[2]; // comparator output mode registers + avr_regbit_t com_pin; // where comparator output is connected + uint64_t comp_cycles; + } comp[AVR_TIMER_COMP_COUNT]; + avr_int_vector_t overflow; // overflow avr_int_vector_t icr; // input capture avr_timer_wgm_t mode; - uint64_t compa_cycles; - uint64_t compb_cycles; - uint64_t compc_cycles; uint64_t tov_cycles; uint64_t tov_base; // we we last were called uint16_t tov_top; // current top value to calculate tnct