#include <stdio.h>
#include "avr_timer.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)
+{
+ return p->io.avr->data[p->r_ocrb] |
+ (p->r_ocrbh ? (p->io.avr->data[p->r_ocrbh] << 8) : 0);
+}
+static uint16_t _timer_get_tcnt(avr_timer_t * p)
+{
+ return p->io.avr->data[p->r_tcnt] |
+ (p->r_tcnth ? (p->io.avr->data[p->r_tcnth] << 8) : 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;
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;
- avr_raise_interrupt(avr, &p->overflow);
+ 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);
+ }
- if (p->compa_cycles && p->tov_cycles > p->compa_cycles)
- avr_cycle_timer_register(avr, p->compa_cycles, avr_timer_compa, p);
- if (p->compb_cycles && p->tov_cycles > p->compb_cycles)
- avr_cycle_timer_register(avr, p->compb_cycles, avr_timer_compb, p);
return when + p->tov_cycles;
}
+
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;
+ avr_timer_t * p = (avr_timer_t *)param;
// made to trigger potential watchpoints
+
+ 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);
+
+ avr->data[p->r_tcnt] = tcnt;
+ if (p->r_tcnth)
+ avr->data[p->r_tcnth] = tcnt >> 8;
+
return avr_core_watch_read(avr, addr);
}
-/*
- * 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)
+static void avr_timer_tcnt_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
{
- 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)
-{
- return p->io.avr->data[p->r_ocrb] |
- (p->r_ocrbh ? (p->io.avr->data[p->r_ocrbh] << 8) : 0);
+ avr_timer_t * p = (avr_timer_t *)param;
+ avr_core_watch_write(avr, addr, v);
+ uint16_t tcnt = _timer_get_tcnt(p);
+
+ if (!p->tov_top)
+ return;
+
+ if (tcnt >= p->tov_top)
+ tcnt = 0;
+
+ // this involves some magicking
+ // cancel the current timers, recalculate the "base" we should be at, reset the
+ // timer base as it should, and re-shedule the timers using that base.
+
+ 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);
+
+ uint64_t cycles = (tcnt * p->tov_cycles) / p->tov_top;
+
+// printf("%s-%c %d/%d -- cycles %d/%d\n", __FUNCTION__, p->name, tcnt, p->tov_top, (uint32_t)cycles, (uint32_t)p->tov_cycles);
+
+ // this reset the timers bases to the new base
+ p->tov_base = 0;
+ avr_cycle_timer_register(avr, p->tov_cycles - cycles, avr_timer_tov, p);
+ avr_timer_tov(avr, avr->cycle - cycles, p);
+
+// tcnt = ((avr->cycle - p->tov_base) * p->tov_top) / p->tov_cycles;
+// printf("%s-%c new tnt derive to %d\n", __FUNCTION__, p->name, tcnt);
}
static void avr_timer_configure(avr_timer_t * p, uint32_t clock, uint32_t top)
float frequency = p->io.avr->frequency;
p->compa_cycles = p->compb_cycles = 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);
- if (top != ocra) {
- p->tov_cycles = frequency / t; // avr_hz_to_cycles(frequency, t);
- printf("%s-%c TOP %.2fHz = cycles = %d\n", __FUNCTION__, p->name, t, (int)p->tov_cycles);
- }
+ p->tov_cycles = frequency / t; // avr_hz_to_cycles(frequency, t);
+ printf("%s-%c TOP %.2fHz = cycles = %d\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 = cycles = %d\n", __FUNCTION__, p->name, fa, (int)p->compa_cycles);
printf("%s-%c B %.2fHz = cycles = %d\n", __FUNCTION__, p->name, fb, (int)p->compb_cycles);
}
- if (p->tov_cycles > 1)
+ if (p->tov_cycles > 1) {
avr_cycle_timer_register(p->io.avr, p->tov_cycles, avr_timer_tov, p);
- else {
- if (p->compa_cycles > 1)
- avr_cycle_timer_register(p->io.avr, p->compa_cycles, avr_timer_compa, p);
- if (p->compb_cycles > 1)
- avr_cycle_timer_register(p->io.avr, p->compb_cycles, avr_timer_compb, p);
+ // calling it once, with when == 0 tells it to arm the A/B timers if needed
+ p->tov_base = 0;
+ avr_timer_tov(p->io.avr, p->io.avr->cycle, p);
}
}
-static void avr_timer_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+static void avr_timer_reconfigure(avr_timer_t * p)
{
- avr_timer_t * p = (avr_timer_t *)param;
+ avr_t * avr = p->io.avr;
+ // cancel everything
p->compa_cycles = 0;
p->compb_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_core_watch_write(avr, addr, v);
long clock = avr->frequency;
// only can exists on "asynchronous" 8 bits timers
uint8_t cs = avr_regbit_get_array(avr, p->cs, ARRAY_SIZE(p->cs));
if (cs == 0) {
printf("%s-%c clock turned off\n", __FUNCTION__, p->name);
- 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);
return;
}
uint8_t cs_div = p->cs_div[cs];
uint32_t f = clock >> cs_div;
- printf("%s-%c clock %d, div %d(/%d) = %d ; mode %d\n", __FUNCTION__, p->name, clock, cs, 1 << cs_div, f, mode);
+ //printf("%s-%c clock %d, div %d(/%d) = %d ; mode %d\n", __FUNCTION__, p->name, clock, cs, 1 << cs_div, f, mode);
switch (p->wgm_op[mode].kind) {
case avr_timer_wgm_normal:
avr_timer_configure(p, f, (1 << p->wgm_op[mode].size) - 1);
} break;
default:
printf("%s-%c unsupported timer mode wgm=%d (%d)\n", __FUNCTION__, p->name, mode, p->wgm_op[mode].kind);
- }
+ }
+}
+
+static void avr_timer_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+{
+ avr_timer_t * p = (avr_timer_t *)param;
+ avr_core_watch_write(avr, addr, v);
+ avr_timer_reconfigure(p);
}
static void avr_timer_reset(avr_io_t * port)
*/
avr_register_io_write(avr, p->r_ocra, avr_timer_write, p);
avr_register_io_write(avr, p->r_ocrb, avr_timer_write, 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);
}
--- /dev/null
+/*
+ atmega88_timer16.c
+
+ Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+ This file is part of simavr.
+
+ simavr is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ simavr is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with simavr. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <avr/io.h>
+#include <stdio.h>
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+
+/*
+ * This demonstrate how to use the avr_mcu_section.h file
+ * The macro adds a section to the ELF file with useful
+ * information for the simulator
+ */
+#include "avr_mcu_section.h"
+AVR_MCU(F_CPU, "atmega88");
+
+/*
+ * This small section tells simavr to generate a VCD trace dump with changes to these
+ * registers.
+ * Opening it with gtkwave will show you the data being read & written to these
+ * It also demonstrate how you can use unused pins to generate your own traces, with
+ * your own events to be displayed.
+ *
+ * Here the port B first 2 bits are used to display when a tick occurs, and when a
+ * TCNT reset occurs.
+ */
+const struct avr_mmcu_vcd_trace_t _mytrace[] _MMCU_ = {
+ { AVR_MCU_VCD_SYMBOL("TCNT1L"), .what = (void*)&TCNT1L, },
+ { AVR_MCU_VCD_SYMBOL("TCNT1H"), .what = (void*)&TCNT1H, },
+ { AVR_MCU_VCD_SYMBOL("tick"), .mask = (1 << 0), .what = (void*)&PORTB, },
+ { AVR_MCU_VCD_SYMBOL("reset_timer"), .mask = (1 << 1), .what = (void*)&PORTB, },
+};
+
+volatile uint16_t tcnt;
+
+ISR(TIMER2_COMPA_vect) // handler for Output Compare 2 overflow interrupt
+{
+ // this really doesn't no anything but proves a way to wake the main()
+ // from sleep at regular intervals
+ PORTB ^= 1;
+}
+
+int main()
+{
+ //
+ // start the 16 bits timer, with default "normal" waveform
+ // and no interupt enabled. This just increments TCNT1
+ // at a regular rate
+ //
+ // timer prescaler to 64
+ TCCR1B |= (0<<CS12 | 1<<CS11 | 1<<CS10);
+
+ DDRB = 3;
+
+ //
+ // now enable a tick counter
+ // using an asynchronous mode
+ //
+ ASSR |= (1 << AS2); // use "external" 32.7k crystal source
+ TCCR2A = (1 << WGM21); // use CLK/8 prescale value, clear timer/counter on compareA match
+ TCCR2B = (2 << CS20); // prescaler
+ OCR2A = 63; // 64 hz
+ TIMSK2 |= (1 << OCIE2A);
+
+ sei();
+
+ int count = 0;
+ while (count++ < 100) {
+ // we read TCNT1, which should contain some sort of incrementing value
+ tcnt = TCNT1; // read it
+ if (tcnt > 10000) {
+ TCNT1 = 500; // reset it arbitrarily
+ PORTB ^= 2; // mark it in the waveform file
+ }
+ sleep_cpu(); // this will sleep until a new timer2 tick interrupt occurs
+ }
+ // sleeping with interrupt off is interpreted by simavr as "exit please"
+ cli();
+ sleep_cpu();
+}