Created
April 16, 2016 22:13
-
-
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?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 😸