Part 5: Using a timer
Next step is to replace the dumb delay routine with a real timer controlled interrupt routine. However, our first step is to just use a timer controlled delay loop, without interrupts.
First the timer registers, we’ll use timer 0:
#define TGLB_REG 0x10000100 /* Timers global control register */
#define T0CTL_REG 0x10000110 /* Timer 0 control register */
#define T0LMT_REG 0x10000114 /* Timer 0 limit register */
#define T0_REG 0x10000118 /* Timer 0 counter register */
#define TxCTL_Tx_PRES_OFFSET 16 /* Timer 0/1 prescale offset */
#define TxCTL_TxEN (1 << 7) /* Timer 0/1 enable */
And here’s the relevant code which now uses this timer:
static void timer_delay(unsigned int delay_in_ms)
{
// start by disabling timer
write_l(T0CTL_REG, 0);
write_l(TGLB_REG, 0);
write_l(T0LMT_REG, delay_in_ms);
// enable the timer, set prescale to 1ms
write_l(T0CTL_REG, (1000 << TxCTL_Tx_PRES_OFFSET) | TxCTL_TxEN);
while ((read_l(T0CTL_REG) & TxCTL_TxEN) != 0) {
// busy wait until timer expires
}
}
/* A less dumb delay routine */
static void delay()
{
const auto milliseconds = 500;
const auto granularity = 100;
static_assert(milliseconds % granularity == 0,
"milliseconds should be a multiple of granularity");
for (auto i = 0; i < (milliseconds / granularity); i++) {
reset_if_button_pressed();
serial_input();
timer_delay(granularity);
}
}
Note that I switched to C++ meanwhile.
Now, this still uses a busy wait for the timer, but at least the timeouts can be specified exactly.
Next step: replace the busy wait with a wait for interrupt.
To be continued…