Este es un tipo de problema "clásico", y creo que tengo una solución, pero quiero examinarla con esta comunidad. Estoy creando un proyecto con el microcontrolador ATtiny88 y estoy programando en avr-gcc. Lo necesito para manejar las siguientes interrupciones:
- TWI (I2C)
- Timer0 Overflow
- Captura del temporizador1
Quiero usar el Desbordamiento de Timer0 para mantener una marca de tiempo de milisegundos de 32 bits (similar al concepto millis()
de Arduino). Para mi aplicación, no puedo hacer que nada se interponga en la interrupción TWI porque mi ATtiny88 está actuando como esclavo TWI y, como tal, nunca puedo borrar las interrupciones. Los ISR del temporizador deben declararse NO_BLOCK
.
Dado que el ATtiny88 es un procesador de 8 bits, el acceso a las variables que son más anchas que 8 bits es seguro que tomará varios ciclos para completar. La forma en que el núcleo de Arduino maneja este dilema es mediante la protección del acceso a su variable interna de 32 bits timer0_millis
con un cli()
dentro de la función millis()
. Ese enfoque me resulta desagradable porque significa que podría pasar por alto una interrupción de TWI porque llamo a millis()
. No me importa que de vez en cuando falte un "tic" de la base de tiempo debido a una interrupción TWI que se está manejando.
Así que escribí timebase.h / timebase.c con la esperanza de evitar este problema a costa de un poco de almacenamiento adicional, mediante doble búfer .
timebase.h
/*
* timebase.h
*
* Created on: Dec 3, 2012
* Author: vic
*/
#ifndef TIMEBASE_H_
#define TIMEBASE_H_
// timer 0 is set up as a 1ms time base
#define TIMER0_1MS_OVERFOW_PRESCALER 3 // 8MHz / 64 = 125 kHz
#define TIMER0_1MS_OVERFLOW_TCNT 131 // 255 - 131 + 1 = 125 ticks
void timebase_init();
uint32_t timebase_now();
#endif /* TIMEBASE_H_ */
timebase.c
/*
* timebase.c
*
* Created on: Dec 3, 2012
* Author: vic
*/
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "timebase.h"
// double buffered timestamp
volatile uint32_t timestamp_ms_buf[2] = {0, 1};
volatile uint8_t timestamp_ms_buf_volatile_idx = 0;
void timebase_init(){
// set up the timer0 overflow interrupt for 1ms
TCNT0 = TIMER0_1MS_OVERFLOW_TCNT;
TIMSK0 = _BV(TOIE0);
// start timer0
TCCR0A = TIMER0_1MS_OVERFOW_PRESCALER;
}
// fires once per millisecond, don't block
// can't miss TWI interrupts for anything
ISR(TIMER0_OVF_vect, ISR_NOBLOCK){
TCNT0 = TIMER0_1MS_OVERFLOW_TCNT;
// modify the volatile index value
timestamp_ms_buf[timestamp_ms_buf_volatile_idx] += 2;
// change the volatile index
timestamp_ms_buf_volatile_idx = 1 - timestamp_ms_buf_volatile_idx; // always 0 or 1
}
uint32_t timebase_now(){
uint8_t idx = timestamp_ms_buf_volatile_idx; // copy the current volatile index
return timestamp_ms_buf[1 - idx]; // return the value from the non-volatile index
}
Mi pregunta es, ¿el código que he escrito aquí resuelve de manera efectiva el problema que he descrito? ¿He implementado exitosamente el acceso atómico a la base de tiempo sin borrar las interrupciones? Si no es así, ¿por qué no y cómo puedo lograr mis objetivos?