Estoy intentando implementar un temporizador global, para poder llamar a time_us () en cualquier parte del programa y la función devolverá microsegundos desde el inicio del programa. He hecho esto usando TIMER2 (reloj de 8 bits) e interrupción en el desbordamiento, y contando los desbordamientos:
volatile uint64_t _time_overflow_cnt;
void time_reset(){
TCCR2A=0; // no pin output,
TCCR2B=(1<<CS21); // prescaler=8
TIMSK2=1<<TOIE2; // interrupt on overflow (256 cycles)
sei();
TCNT2=0; // reset timer to 0
_time_overflow_cnt=0;
}
ISR(TIMER2_OVF_vect){
_time_overflow_cnt++;
}
uint64_t time_us(){
return (_time_overflow_cnt*256 + TCNT2)/(F_CPU/8/1000000ULL);
}
Sin embargo, este código tiene una condición de carrera, que en mi caso es bastante notable: si reescribimos este código en ensamblador, vemos que el time_us es bastante largo (los cálculos de 64 bits tardan un tiempo) y si TCNT2 se desborda durante Esta vez, los cálculos están sesgados por 256 ciclos completos. Intenté desactivar las interrupciones antes y volver a encenderlas después del cálculo, pero esto no ayuda, solo protege el contador de desbordamiento, mientras que el TCNT2 sigue aumentando. También intenté guardar el contador de desbordamiento, luego guardar TCNT2, verificar si el anterior cambió en el medio, y algunas otras 'soluciones' similares a esta, pero todas ellas tenían algunos problemas de TOCTOU (Tiempo de verificación al Tiempo de uso). ¿Tienes una mejor idea para la implementación?