Skip to content

Instantly share code, notes, and snippets.

@NZSmartie
Created April 16, 2016 22:13
Show Gist options
  • Save NZSmartie/1867be12e7283cd0289e840a5eac8c5e to your computer and use it in GitHub Desktop.
Save NZSmartie/1867be12e7283cd0289e840a5eac8c5e to your computer and use it in GitHub Desktop.
[ATMega32u4] Why is TIFRO's OCF0B flag set and not OCF0A in the TIMER0_COMPA ISR as it's been configured to do so?
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "Timers.h"
#define TIMER_CALLBACKS_MAX 16
static int timer_callbacks_size = 0;
bool (*timer_callbacks[TIMER_CALLBACKS_MAX])(void);
static void Timer_DoCalllbacks(void);
const uint8_t timer_clock_select = 3; // Clk_io/64
const uint8_t timer_output_compare_A = 250;
// Flags are set from the Interrupt service routine and processed in the main loop
volatile static struct {
volatile uint8_t t_1ms :1;
}timer_flags;
ISR(TIMER0_COMPA_vect){
if(TIFR0 & (1<<OCF0A)){
// This should be executing. but I don't see PD7 toggling at all or the timer flag being set at all
TIFR0 |= (1<<OCF0A);
timer_flags.t_1ms = 1;
PIND |= (1<<7);
}
if(TIFR0 & (1<<OCF0B)){
// This shouldn't be executing. but my scope shows that PC6 is toggling
PINC |= (1<<6);
}
}
/** @brief Sets up timer module.
*
* Sets up the interrupt routines and clears out any existing timer callbacks
*/
void Timer_Setup(){
timer_callbacks_size = 0;
cli();
Timer_Stop();
// Set up the timer0 registers
TCCR0A = (0 << COM0A0) | (0<<COM0B0) | (2<<WGM00); // CTC Mode with Output Compare disconnected
// 16MHz (System Clock) / 64 (Prescaler) / 250 (Compare Register) = 1KHz (1ms interval)
TCCR0B = (0 << FOC0A) | (0 << FOC0B) | (0 << WGM02) | (timer_clock_select << CS00); // Clock source is Clk_io/64
OCR0A = timer_output_compare_A; // in CTC mode, OC0RA is used to set when the counter resets and sets the interrupt flag
Timer_Start();
DDRC |= (1<<6);
DDRD |= (1<<7);
sei();
}
/** @brief Starts the timer module */
void Timer_Start(){
Timer_Stop(); // Clear out the existing clock select value (just in case)
TCCR0B |= (timer_clock_select << CS00);
TIMSK0 = (1<<OCIE0A); // Enable the timer0 compare interrupt
}
/** @brief Stops the timer module */
void Timer_Stop(){
TIMSK0 = 0; // Disable the timer0 compare interrupt
TCCR0B &= ~(7<<CS00); // Set the timer to no clock source (disables the counter)
}
/** @brief Adds the provided callback to the timer module
*
* @param callback A pointer to a function with the signature of `bool f(void)`
* @remarks If there are too many callbacks, then this will silently fail
*/
void Timer_AddCallback(bool (*callback)(void)){
if(timer_callbacks_size >= TIMER_CALLBACKS_MAX || callback == NULL)
return;
timer_callbacks[timer_callbacks_size++] = callback;
}
/**
* @brief Removes provided callback from the timer module
* @param callback A pointer to a function with the signature of `bool f(void)`
* @remarks This will silently fail if the pointer is not already registered
*/
void Timer_RemoveCallback(bool (*callback)(void)){
int i;
if(timer_callbacks_size <= 0 || callback == NULL)
return;
// Find the index of the callback pointer in our array
for(i=0;i<=timer_callbacks_size;i++){
if(timer_callbacks[i] == callback)
break;
}
// Shift the remaining pointers back by one, starting from the found callback index
for(;i<timer_callbacks_size;i++){
timer_callbacks[i] = timer_callbacks[i+1];
}
timer_callbacks_size--;
}
/**
* @brief Process the callbacks at their respective intervals
*
* The callbacks will not be called if the interval has not lapsed. Thus this function is non-blocking
*
* @note This needs to be called from the main loop. It will process the callbacks when a
* flag is received from the Interrupt Service Routine.
*
* @todo Allow callbacks to fire at 10ms, 100ms, or <application-defined>ms intervals
*/
void Timer_Task(){
if(timer_flags.t_1ms){
//PINC |= (1<<6);
timer_flags.t_1ms = 0;
Timer_DoCalllbacks();
}
}
static void Timer_DoCalllbacks(){
int i;
for(i = 0; i < timer_callbacks_size; i++){
// Remove the callback if it returned false
if(!(*timer_callbacks[i])()){
Timer_RemoveCallback(timer_callbacks[i]);
// Since removing the callback results in the callback array shifting back by one starting at `i`
// revert `i` by one, to ensure we don't miss a callback
i--;
continue;
}
}
}
@NZSmartie
Copy link
Author

Turns out, the OCF0A flag is cleared as the ISR is executed, so it will always be read as 0.
A misunderstanding of documentation can lead to hours wasted 😸

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment