Handling multiple interrupts in avr - c

I am new to AVR programming, so sorry if question is trivial.
Using :
OS : Windows7
IDE : Atmel studio
uC = m328p
Pins:
ADC signal - ADC0/PC0
LED_values - (PB0 - PB7)
LED_START - PD1
LED_LIGHT - PD0
BUTTON - PD2
Goal: When you press the button it turns On the LED_START and it needs to start with conversion.
AVR gets interrupt and starts ADC conversion. Basically program has two interrupts. I know that INT0 interrupt has highest priority.
I dont know how to deal with them.
I have tried several things like adding global variable "start" and changing it. And also when i only set LED START it turns On and it stays in that state until LED_values reach certain value, then LED START turns Off by it self.
So please can you show me how to handle two interrupts so that fulfills stated goal and explain me what im doing wrong.
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define F_CPU 1000000UL
#define BIT_IS_SET(byte, bit) (byte & (1 << bit))
#define BIT_IS_CLEAR(byte, bit) (!(byte & (1 << bit)))
typedef enum{false, true} bool;
bool previousState = false;
bool start = false;
char num;
void setup();
void loop();
void ADC_init();
void EI_init(); // External Interrupt
int main(void)
{
setup();
loop();
}
void setup(){
DDRC &= ~(0x1); // LDR Input
DDRB = 0xFF; //LEDs value Output
DDRD |= 0x3; //LED light LED start Output
DDRD &= ~(1 << PIND2); //Button Input
}
void loop(){
PORTD |= (1 << PIND2);
EI_init();
ADC_init();
sei();
if(start){
ADCSRA |= (1 << ADSC);
}
while(1){}
}
void ADC_init(){
ADMUX = 0x60;
ADCSRA = 0x8B;
ADCSRB = 0x0;
ADCH = 0x0;
}
ISR(ADC_vect) {
PORTB = ADCH; // assign contents of ADC high register to Port D pins
int b = (int)ADCH;
if(b > 180) { //100
PORTD = 0x1;
}else{
PORTD &= ~(0x1);
}
_delay_ms(100);
ADCSRA |= (1 << ADSC); // start next ADC
}
void EI_init(){
EIMSK |= (1 << INT0); // Interrupt enabled
EICRA |= (1 << ISC00); // any state change
}
ISR(INT0_vect){
if(BIT_IS_CLEAR(PORTD,PIND2)){
start = true;
}else{
start = false;
}
}
Here is scheme : scheme

First of all, you should make start be volatile since it is being used by both the main loop and the interrupt. The volatile keyword tells the compiler that the variable might be modified by things outside of its control, so it cannot optimize away any reads or writes to the variable:
volatile bool start = false;
Secondly, you probably want to remove this line you wrote at the end of loop:
while(1){}
That line is bad because it causes your program to go into an infinite loop where it does nothing. I think you actually want the code you wrote about it in the loop function to run multiple times.
Secondly, after you detect that the start flag has been set, you probably need to set it to 0, or else it will just be 1 forever.
Third, setting start to false in the INT0 ISR might be a bad idea, because it might get set to false before you main loop has a chance to observe it being true and handle the event. I guess it really depends on exactly what you are trying to do. You could try adding details to your question about exactly what problem you are trying to solve using the AVR. See What is the XY problem?.
There are probably other issues with your code that need to be debugged. Can you think of any ways to make this simpler? Maybe you can reduce the number of interrupts you are using. To debug, you can try blinking some LEDs to figure out what parts of your program are executing.

Related

ISR-defining vs checking for TIFR corresponding bit in AVR timer programming

With timers, I want to toggle an LED every one second. I'm using ATMega32 and the clock frequency is 1MHz. I can get to 0.1 second using the 8-bit counter, and for each 10 timer interrupts, I blink the led.
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
typedef unsigned char u8_t;
typedef unsigned short u16_t;
void func();
int main(void)
{
DDRC = 0x80;
TCCR0 |= (1 << WGM01); // CTC bit.
TCCR0 |= (1 << CS02) | (1 << CS00); // Prescalar = 1024.
OCR0 = 98; // 98 ticks correspond to roughly 0.1 second with 1024 prescaler
TIMSK |= (1 << OCIE0);
TCNT0 = 0;
sei();
while(1)
{
if (!(TIFR & (1 << OCF0))) {
func();
}
}
}
void func()
{
static u8_t extra_time = 0;
if (extra_time == 10)
{
extra_time = 0;
PORTC ^= 0x80;
}
else extra_time++;
TIFR |= (1 << OCF0);
}
In the preceding code, I do not define an ISR for the TIMER0_COMP_vect interrupt.
From the datasheet:
The OCF0 bit is set (one) when a compare match occurs between the Timer/Counter0 and the
data in OCR0 – Output Compare Register0. OCF0 is cleared by hardware when executing the
corresponding interrupt handling vector. Alternatively, OCF0 is cleared by writing a logic one to
the flag. When the I-bit in SREG, OCIE0 (Timer/Counter0 Compare Match Interrupt Enable), and
OCF0 are set (one), the Timer/Counter0 Compare Match Interrupt is executed.
emphasis mine.
Therefore, by the emphasized sentence, I can check for the OCF0 bit for being a zero, and if so, I can "handle" the on-compare-match event.
However, the LED blinks much more frequently (not even a tenth second between each blink but I cannot measure of course).
This works fine if I just set an ISR on TIMER0_COMP_vect and check for nothing, but I want to know why is the OCF0 always(?) logic 0, hence "on", even though I set it to high on each func() call. And what's the problem with this method.
Keep reading the next line in the data sheet
When the I-bit in SREG, OCIE0 (Timer/Counter0 Compare Match Interrupt Enable), and OCF0 are set (one), the Timer/Counter0 Compare Match Interrupt is executed.
then take a look at your code
you have Enabled Compare Match Interrupt
TIMSK |= (1 << OCIE0);
you have Enabled the Global interrupt (I-bit in SREG)
sei();
so whenever output compare flag OCF0 is set then all the 3 conditions for interrupt have occurred and interrupt is automatically fired
when an interrupt has been fired the program flow of execution will jump to a specific memory location corresponding to this interrupt to execute the code and handle it,
but you did not provide any code for this interrupt (no ISR), so the microcontroller does not know what he can do because you did not tell him, so simply he will RESET
and so on, interrupt with no Handler keep fired makes the microcontroller keep
reset
finally, when you add an empty ISR you Provide a code which tell the microcontroller to do nothing if this interrupt is fired and the micro will not reset because he knows how to handle it
if you want to keep track OCF0 flag by yourself delete this line
TIMSK |= (1 << OCIE0);

My Interrupt is not updating my value to my main, how should i set up my Interrupt?

Im trying to build a program that has a scannerlight with at least 5 leds and one button connected to an external interrupt pin. A single button press (and release) to start the scanner light. Pressing (and releasing) stops the scanner light again (and so on...).
I have a ground side switch on PD2
i have my LEDS on pin PD3,PD4,PD5,PD6 and PD7.
Im useing ATMega328P
I know my towering light works when i press on the button the towering light stops but when i press again it feels like it doesnt return the value to 1.
My code:
#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define BIT_IS_CLEAR(byte, bit) (!(byte & (1 << bit)))
volatile int value = 1;
int main(void)
{
DDRD = 0b111110000;
DDRD &= ~(1 << PD2); // clear DDRD bit 2, sets PD2 (pin 4) for input
PORTD |= (1 << PD2); // set PD2/INT0 (pin 4) internal pull-up resistor
PCICR = 0b00000100;
PCMSK2 = 0b00000100;
sei();
while (value==1) //when value is 1 it should start having a towerlight
{
PORTD= 0x80;
_delay_ms(15000);
PORTD= 0x40;
_delay_ms(15000);
PORTD= 0x20;
_delay_ms(15000);
PORTD= 0x10;
_delay_ms(15000);
PORTD= 0x08;
_delay_ms(15000);
}
}
ISR(PCINT2_vect)
{
if(BIT_IS_CLEAR(PIND, PD2) & value==1) { // if switch is pressed (logic low)
value=0;
} else if(value == 0) {
value=1;
} else {
// ideally should never get here, but may occasionally due to timing
}
}```
regarding:
while (value==1)
{
....
}
When value is 0 the loop is exited, execution then exits the program. This is a serious logic flaw in the program
You are using a bitwise AND where it should be a logical AND. Change this
if(BIT_IS_CLEAR(PIND, PD2) & value==1)
to this
if(BIT_IS_CLEAR(PIND, PD2) && (0 != value))
Comparing for non-zero as opposed to equal to 1 guards against value being corrupted; since you want a zero or non-zero condition. Adding brackets makes the intention very clear. Yoda comparisons (putting the constant on the left hand side) prevents accidental assignment.
One other thing to consider is what you're doing to debounce the switch - in an analogue fashion with R-C+Schmitt or monostable, or in a digital fashion where you have a timer routine that samples the input every so often and counts "how many 1s or 0s over the last, say, 16 samples"?
I find that edge triggered interrupts don't work particularly well for manual switch inputs.

_delay_ms seems not to work when using pwm

I'm relatively new to programming microcontrollers, so I started experimenting on an ATtiny85.
First program was simply turning on an LED and off with a little delay (classic blinky program).
Now I wanted to slowly increase the brightness of the LED. Here is the code:
#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
#define LED PB0
void setup(void);
void loop(void);
void analogOutput(int8_t brightness);
int main(void)
{
setup();
while (1)
{
loop();
}
return 0;
}
void setup()
{
TCCR0A = (1 << WGM00) | (1 << WGM01);
TCCR0B = (1 << CS00) | (1 << CS01);
DDRB = (1 << LED);
PORTB = 0;
}
void loop()
{
int8_t brightness = 0;
analogOutput(brightness);
_delay_ms(500);
for (brightness = 0; brightness < 256; ++brightness)
{
analogOutput(brightness);
_delay_ms(10);
}
}
void analogOutput(int8_t brightness)
{
if (brightness == 0)
{
// digital output LOW
TCCR0A &= ~(1 << COM0A1);
PORTB &= ~(1 << LED);
}
else if (brightness == 255)
{
// digital output HIGH
TCCR0A &= ~(1 << COM0A1);
PORTB |= (1 << LED);
}
else
{
PORTB &= ~(1 << LED);
// analog output
TCCR0A |= (1 << COM0A1);
OCR0A = brightness;
}
}
I expected the LED to be turned off for half a second and then increase the brightness.
But if i run the code the LED simply becomes more bright, as if the _delay_ms(500) was ignored.
The analogOutput function is inspired by the function of the Arduino library.
What is wrong here? Why does the delay not work as expected?
EDIT
I changed my loop function, new content:
void loop()
{
analogOutput(127);
_delay_ms(500);
analogOutput(255);
_delay_ms(500);
}
This works, I now have a LED that constantly switches from dimmed to fully on and back.
__delay_ms() is converted to cycle-wasting instructions by the compiler, so as long as your F_CPU matches the current speed of the processor, then it should work as expected.
I would look for other places in your code that are not working as you expect them to.
A half second delay at power up is not a great diagnostic test since it happens quickly, only once, and there may be other things going on then as well.
Maybe instead try blinking between two different brightness levels with a 500ms delay between. Visually this should be unambiguous.
if that works as expected, then maybe try doing a brightness ramp and changing the delay inside the loop to see if that works as expected.
Eventually you will work your way up to something that is not as expected, and then you will know where the problem is. It is still possible you will not understand what is going on, but at least you will be able to ask a much more specific question here and be more likely to get a helpful answer.
Report back with your results!
I feel stupid.. It was just an overflow error, since I used uint8_t and set < 256 in the for loop, the condition was always true (255 + 1 = 0).
My solution was to use uint16_t for the loop

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();
}

Atmel Button Debounce not working

I am trying to make a button debounce software by the help of an led toggle function that returns a different boolean each time which i got by asking the question before but that never worked :
#include <avr/io.h>
bool ledToggle();
int main(void)
{
DDRB |= (1 << 0);
DDRB &= ~(1 << 0);
while(1)
{
//TODO:: Please write your application code
if (ledToggle() == true)
{
//led on
PORTB |= (1 << 0);
}else{
//led off
PORTB &= ~(1 << 0);
}
}
}
bool ledToggle()
{
static bool state = false;
if(bit_is_clear(PINB, 1)){
state = !state;
}
return state;
}
EDIT
I get no errors or anything when I try to compile it just doesn't work...
I don't recognize in which way this code would debounce a switch connected to Port B / 1. Debouncing means to
check and store the key logic state
wait a certain amount of time (depending on hardware, 5 - 50 ms)
compare the (now) logic state with what was read before
if equal the (now) logic state is the debounced key state
Provided the program is functioning well, the LED would bounce at the same pace as the switch.
In your bool ledToggle() I suggest you declare static volatile bool state; to ensure the variable is created in RAM (rather than a CPU register)
In the second line of main () you are mistakenly setting the LED port instead of the BUTTON port as an input.
I suggest using defines as a way of making this kind of mistakes less likely:
#define LED 0
#define BUTTON 1
DDRB |= (1 << LED);
DDRB &= ~(1 << BUTTON);

Resources