How to set ISR to run every second - C Atmega328p - c

I'm writing a code that needs to check a sensor's input every 0.5s. I want to use an ISR because I want my code to execute until the sensor's input changes.
How would I set up this ISR to execute every 0.5s?
Thanks :)

I would suggest using a Timer interrupt. For an example go here. http://www.avrfreaks.net/forum/tut-c-newbies-guide-avr-timers?page=all
I haven't tested it myself but here is a section of code on that.
#include
#include
int main (void)
{
DDRB |= (1 << 0); // Set LED as output
TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
TIMSK |= (1 << OCIE1A); // Enable CTC interrupt
sei(); // Enable global interrupts
OCR1A = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64
TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64
for (;;)
{
}
}
ISR(TIMER1_COMPA_vect)
{
PORTB ^= (1 << 0); // Toggle the LED
}

Related

How to turn a digital output off after 2 seconds using a timer

I have an Atmega328p and want to turn on a digital output with a button press, then have it turn off automatically after 2 seconds.
I know how to use a hardware interrupt for the button, but how do I set up a timer interrupt to automatically turn the digital output back off?
UPDATE:
I was able to figure it out. Here's my solution (only showing the pertinent functions):
static inline void initTimer1(void) {
TCCR1B |= (1 << WGM12); // CTC Mode, immediate
TCCR1B |= (1 << CS10) | (1 << CS12); // Clock speed: 16 MHz / 1024, ~= 15.6 ticks per ms
}
void set_valve_on_time(uint16_t on_time) {
OCR1A = on_time; // set output compare register for valve on time
}
void open_valve(uint8_t state) {
if (state > 0) {
PORTD |= (1 << PIND6); //turn on PD6, open valve
PORTD &= ~(1 << PIND7); //turn off PD7, turn off close valve in case it was on
if (state == 2) {
TCNT1 = 0;
TIFR1 |= (1 << OCF1A); // clear output compare match flag
TIMSK1 |= (1 << OCIE1A); // enable output compare interrupt
}
}
else {
PORTD &= ~(1 << PIND6); //turn off PD6, stop opening valve
}
}
ISR(TIMER1_COMPA_vect) {
TIMSK1 &= ~(1 << OCIE1A); // disable output compare interrupt
open_valve(0); //turn off close valve output
}
The open_valve function is called by a button press (not shown). The hardest time I had was figuring out that I needed TIFR1 |= (1 << OCF1A) for it to work correctly. I still don't quite understand why, because I thought the ISR was supposed to do this automatically.
you have to roughly follow these steps:
on your button handling routine set up the timer with the folling properties:
best use 16bit timer in CTC mode (if you have free timers available)
set the prescaler so that the timer otherflows a bit slower than your compare value:
for 2sec and 10MHz CPU frequency i would run it on the 1/1024 prescaler, so a overflow would occure on (10.000.000/1024/65536) -> 1 overflow per ~6.7s (with 265 it would overflow more often than once per 2s)
set the ctc top value (see description of your selected mode - the specific register varies) to a value that is reached after 2s : 10.000.000/1024 * 2s --> 19531
implement the ISR (see which would be the correct one in the selected CTC mode) the and activate the Interupt in the mask register
in the ISR set your output, and stop the timer
Bonus: setup the timer that it uses the output compare pins to deactivate the output
Then no ISR is required at all, just set the Compare Output Mode to 'clear on Match'
If you do not have a free 16bit timer, i would suggest to use 1 timer in CTC mode to generate a (10) millisecond timebase, and implement the time-counting logic in this ms-event handling.

Weird behaviour of timer2 on ATmega328

I'm currently working on a piece of code, which should wait for a comparator interrupt and execute some other code after a set amount of time. Now, I thought using Timer2 in CTC mode would be a good idea to make sure that the program waits for the right amount of time and came up with this:
void setup(){
...
// Set up the timer
TCCR2A = 0;
TCCR2B = 0;
TCNT2 = 0;
OCR2A = 255; // compare match register
TCCR2A = (1 << WGM21); // CTC mode
TCCR2B = ((1 << CS22) | (1 << CS21)); // 256 prescaler
TIMSK2 &= ~(1 << OCIE2A); // disable interrupt
}
ISR(ANALOG_COMP_vect) {
// switchTime is in µs, usual value: around 500µs
// with a 16 Mhz crystal and a 256 prescale we need to devide
// the switchTime by 16 (2^4)
OCR2A = switchTime >> 4;
TCNT2 = 0; // reset counter
TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt
}
ISR(TIMER2_COMPA_vect) {
TIMSK2 &= ~(1 << OCIE2A); // disable interrupt
// do stuff
}
The awkward thing is, it doesn't work. The ISR timer is immediately called after we leave the ISR comparator (I checked this by toggling a pin in both routines and measuring with an oscilloscope). After a few hours of reading datasheets and randomly changing the code I came up with a line of code that fixed it:
ISR(TIMER2_COMPA_vect) {
TIMSK2 &= ~(1 << OCIE2A); // disable interrupt
OCR2A = 255; // <- apparently fixes all my problems
// do stuff
}
I'm quite confused about this because the frequency of the timer shouldn't be a matter after we call the routine and deactivate the interrupt.
Now I'm quite glad that I've found the solution but I want to know why it works. Something about knowing how to fish and accidentally catching a fish by randomly inserting code.
I think you missed the clearing of pending timer interrupts.
ISR(TIMER2_COMPA_vect) {
TIMSK2 &= ~(1 << OCIE2A); // disable interrupt
/* Clear pending interrupts */
TIFR2 = (1 << TOV2) | (1 << OCF2A) | (1 << OCF2B);
// do stuff
}

How to stop timer on ATmega328

I wrote some simple code where I am using the timer1 on my Arduino Uno. The problem is I can't stop the timer any way I try.
I am using this program to count and show on the display the number of external interrupts on pin 2 while measuring the time. But when I press button fin I want to stop generating interrupts for the program which is increasing the variable time called cas. Can you help somehow, please?
My code:
#include <OLED_I2C.h>
#define alarm_output 10
#define fin 13
int suma=0;
int alarm=0;
float cas=0;
OLED myOLED(SDA, SCL, 8);
extern uint8_t MediumNumbers[];
extern uint8_t BigNumbers[];
extern uint8_t SmallFont[];
void setup(void) {
pinMode(alarm_output,OUTPUT);
digitalWrite(alarm_output,LOW);
pinMode(fin,INPUT_PULLUP);
pinMode(9,INPUT_PULLUP);
//interrupt
interrupts();
attachInterrupt(digitalPinToInterrupt(2), displej, CHANGE);
//first screen
myOLED.begin();
myOLED.setFont(SmallFont);
myOLED.print("TIME:", 0, 30);
myOLED.print("INTERRUPT:", 0, 56);
myOLED.print("Laser game", CENTER, 0);
myOLED.setFont(MediumNumbers);
myOLED.printNumF(cas,1,RIGHT,20);
myOLED.setFont(BigNumbers);
myOLED.printNumI(suma, RIGHT, 40);
myOLED.update();
//start loop
up:;
if(digitalRead(9)==1)
goto up;
// TIMER 1 for interrupt frequency 10 Hz:
cli(); // stop interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1 = 0; // initialize counter value to 0
// set compare match register for 10 Hz increments
OCR1A = 24999; // = 16000000 / (64 * 10) - 1 (must be <65536)
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS12, CS11 and CS10 bits for 64 prescaler
TCCR1B |= (0 << CS12) | (1 << CS11) | (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei(); // allow interrupts
}
void displej(){
suma++;
alarm=3;
}
ISR(TIMER1_COMPA_vect){
cas=cas+0.1;
if(alarm>0)
alarm--;
}
void loop(void) {
myOLED.setFont(MediumNumbers);
myOLED.printNumF(cas,1,RIGHT,20);
myOLED.setFont(BigNumbers);
myOLED.printNumI(suma, RIGHT, 40);
myOLED.update();
if(digitalRead(fin)==0){
cli();
TCCR1B |= (0 << CS12) | (0 << CS11) | (0 << CS10); //this do now work
detachInterrupt(digitalPinToInterrupt(2));
sei();
}
if(alarm>0)
digitalWrite(alarm_output,HIGH);
else
digitalWrite(alarm_output,LOW);
delay(10);
}
I've tested all three methods for "turning off the timer." Just comment out the one you prefer in the code below to see it demonstrated. All three are effective at getting the Arduino's LED to quit blinking.
void setup(void) {
pinMode(13,OUTPUT);
digitalWrite(13,LOW);
interrupts();
// TIMER 1 for interrupt frequency 10 Hz:
cli(); // stop interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1 = 0; // initialize counter value to 0
// set compare match register for 10 Hz increments
OCR1A = 24999; // 200 millisecond cycle
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS12, CS11 and CS10 bits for 64 prescaler
TCCR1B |= (0 << CS12) | (1 << CS11) | (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei(); // allow interrupts
}
volatile uint8_t count = 0;
volatile uint8_t timer_flip = 0;
ISR(TIMER1_COMPA_vect){
if (timer_flip == 0)
timer_flip = 1;
else
timer_flip = 0;
if (timer_flip == 1)
digitalWrite(13, HIGH);
else
digitalWrite(13, LOW);
count++;
}
void loop(void)
{
if (count > 100) // runs for a few seconds
{
//cli(); // One way to disable the timer, and all interrupts
//TCCR1B &= ~(1<< CS12); // turn off the clock altogether
//TCCR1B &= ~(1<< CS11);
//TCCR1B &= ~(1<< CS10);
//TIMSK1 &= ~(1 << OCIE1A); // turn off the timer interrupt
}
}
This exact code is running on an Uno right beside me now. I've tested all three "turn off" mechanisms above and they all work.
To make a minimal, verifiable example I stripped out all the OLED stuff. I changed pin 13 to an output and set it to blink the LED while it could (and when it stops the timers and/or interrupts are clearly disabled).
A couple of learning points:
You should not use pin 13 for your "fin" button without additional circuitry -- see this Arduino official reference.
You should declare as volatile any variable that is written to in your interrupt service routines.
Your choice of which method to turn off the timer depends on your goal. You just disable the timer interrupt, disable all interrupts, or simply turn the clock source off. The choice is yours.
Sure thing! There are two ways you can do it. You can simply stop the timer interrupt but leave the timer running using:
TIMSK1 &= ~(1 << OCIE1A);
Or, you can stop the timer altogether by altering the clock source to "none" like:
TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10));
which effectively undoes what you did to select the clock source in the first place. After this the CS12-CS11-CS10 bits will be 0-0-0 and the clock source will be stopped. See p. 134 of the datasheet.

ATMEGA 328P varying frequency

I am trying to generate a 16kHz pwm... this is the code i am working with right now. `
int main(void){
DDRD |= (1 << DDD6);
// PD6 is now an output
OCR0A = 128;
// set PWM for 50% duty cycle
TCCR0A |= (1 << COM0A1);
// set none-inverting mode
TCCR0A |= (1 << WGM01) | (1 << WGM00);
// set fast PWM Mode
TCCR0B |= (1 << CS01);
// set prescaler to 8 and starts PWM
while (1);
{
// we have a working Fast PWM
}}`
The default frequency is set to 64kHz... is there any way i can change the default frequency ? Because changing the prescalars does not help me get a frequency of 16kHz...
If you can use CTC mode instead of fast PWM (which means, you don't need to vary the pulse-pause-width ratio), you can use CTC mode to accomplish that:
DDRD |= (1 << DDD6);
// PD6 is now an output
OCR0A = 32;
// set PWM for 12.5% duty cycle
TCCR0A |= (1 << COM0A0);
// set toggle mode
TCCR0A |= (1 << WGM01);
// set CTC Mode
TCCR0B |= (1 << CS01);
// set prescaler to 8 and start PWM

AVR timer overflow interrupt not working

Hello good people of stack overflow. My problem is an interrupt service routine (ISR) that seemingly never executes! Here's some info on my set up:
I am flashing an avr attiny85. I have the bare bones of a project set up so far with simply a main.c and two modules: timer and hardwareInit. In the timer module, I have a timer0_init function that I am using to set up timer0 for CTC mode to overflow ever 1 ms. Here is the function:
void timer0_init( void )
{
cli();
TCCR0B |= 3; //clock select is divided by 64.
TCCR0A |= 2; //sets mode to CTC
OCR0A = 0x7C; //sets TOP to 124 so the timer will overflow every 1 ms.
TIMSK |= 2; //Enable overflow interrupt
sei(); //enable global interrupts
}
with the timer set up, I added an ISR to increment ticks every time the counter overflows, so I can keep track of how much time has elapsed, etc.
ISR(TIMER0_OVF_vect)
{
cli();
//ticks ++;
PORTB |= ( 1 << PORTB0 );
sei();
}
as you can see, I commented out the ticks++ because it wasn't working, and replaced it with PORTB |= ( 1 << PORTB0 ); which simply turns on an LED, so if the interrupt is ever executed, I will know by proof of the LED being on.
Unfortunately, I can't get it to turn on and can't see what I'm missing. (to prove that I 1. have the LED set up on the right pin, and 2. am manipulating the correct bit in the correct register, I put just this statement PORTB |= ( 1 << PORTB0 ); in my infinite loop and confirmed the LED came on)
For further explanation, here is my main.c:
/*================================= main.c =================================*/
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "timer.h"
#include "hardwareInit.h"
int main(){
//Initialize hardware HERE
DDRB |= ( 1 << PORTB0 ); //set this pin as an output for an LED
SetClockPrescale(1); //internal clock divided by 1 = 8 MHz, from hardwareInit
timer0_init(); //set up timer0 for 1 ms overflow
while(1)
{
/* if( getTicks() > 0 )
{
PORTB |= ( 1 << PORTB0 );
_delay_ms(1000);
PORTB &= ~( 1 << PORTB0 );
_delay_ms(1000);
} */
}
return 0;
}
So, what you see in the infinite loop is what I tried first, but after that didn't work, I tried something simpler, just having an empty loop (commented out previous stuff), and waiting for the interrupt to get triggered which would turn on the LED.
Any help you could give would be really appreciated. I'm quite puzzled why this hasn't been working.
You are using the wrong ISR as #andars has pointed out correctly. In CTC "Clear Timer on Compare" mode the timer will never overflow as it will be cleared on compare match.
So you enabled the wrong interrupt of the timer as well. Bit 1 of TIMSK register enables timer overflow interrupt on timer0. That won't be triggered because of the previous reason. Taken from datasheet.
As you are using OCR0A to set the compare value, you have to enable Bit 4 – OCIE0A: Timer/Counter0 Output Compare Match A Interrupt Enable.
Back to the ISR, you need the ISR(TIMER1_COMPA_vect) or ISR(TIMER1_COMPB_vect) depending on which bit you set in TIMSK. Note that the compare value should be written into the matching registers as well, OCR0A or OCR0B.
Note that, you can use the bit names in your code just like the register names, in my opinion it makes the code more transparent.
Your code should be changed as follows to enable the corresponding interrupt:
void timer0_init( void )
{
cli();
TCCR0B |= (1<<CS01) | (1<<CS00); //clock select is divided by 64.
TCCR0A |= (1<<WGM01); //sets mode to CTC
OCR0A = 0x7C; //sets TOP to 124 so the timer will overflow every 1 ms.
TIMSK |= (1<<OCIE0A); //Output Compare Match A Interrupt Enable
sei(); //enable global interrupts
}
The ISR:
ISR(TIMER0_COMPA_vect)
{
cli();
//ticks ++;
PORTB |= ( 1 << PORTB0 );
sei();
}

Resources