I was recently trying to make an interrupt on my atmega328p using atmelstudio to make a LED that is connected to digitalpin 13/PB5/PCINT5 blink four times as slow as normal when the button that is connected to a 5V output and digitalpin 2/PD0/PCINT18 is pressed down.
But whenever I run the code and press the button it will never(as far as i can tell) go true the interrupt code.
#include <avr/io.h>
#include <avr/interrupt.h>
volatile int t = 1;
int main(void)
{
init();
Serial.begin(9600);
DDRB = (1 << 5);
DDRD &= ~(1 << DDD2); // Clear the PD2 pin
// PD2 (PCINT0 pin) is now an input
PORTD |= (1 << PORTD2); // turn On the Pull-up
// PD2 is now an input with pull-up enabled
EICRA |= (1 << ISC00); // set INT0 to trigger on ANY logic change
EIMSK |= (1 << INT0); // Turns on INT0
sei(); // turn on interrupts
Serial.println("loop started");
while(1)
{
PORTB ^= (1 << 5);
delay(500*t);
}
}
ISR (INT0_vect)
{
Serial.println("interrupt");
if(t=1){
t=4;
}
else{
t=1;
}
}
I went through the datasheet multiple times and eventually stumbled onto this code online (yea yea i know i'm a real piece of work) and added my own pieces to it.
but it even this does not work, does anybody know why?
There are a few possible problems in your code:
The most important is the assignment in the if condition that was already mentioned in the comments.
clearly another one is the also mentioned serial.print stuff in the ISR.
ISRs should be as short and simple as possible.
Another is the hardware. If you press a button, they bounces and usually give multiple interrupts. so look for some de-bouncing code or have a look in the arduino library if there is something there. you may have to change the code, because usually the hardware logic itself is handled in interrupts, but the actual testing of the button states should belong to main code.
advanced stuff - if you currently reading tutorials and teach your self - ignore this, but may be keep in mind for actual projects
Another issue is the program design: your processor now cannot do anything else then toggling LEDs because his main program flow waits.
Normally you would want to use a hardware timer for this kind of tasks.
Either use it as a time base, that signals passed intervals to the main via a volatile flag variable. or directly use the PWM-Feature to directly interface the LED via one of the Output Compare Pins (OC[012][AB]).
Related
I am learning embedded and I had the simple task of lighting up a led, but also i want to implement a timer that turns the led on for a second and then turns it off for another seconds(and so on), but i am not sure how to do it. I am working on a ATmega324PB microcontroller using the IAR Embedded Workbench. Here is my code:
include "iom324pb.h"
#include <ioavr.h>
#include <intrinsics.h>
void set_output(int pin)
{
DDRC |=(1<< pin);
}
void set_pin(int pin)
{
PORTC |=(1 << pin);
}
void turn_off(int pin)
{
PORTC &= ~(1 << pin);
}
int main(void)
{
set_output(7);
while (1)
{
set_pin(7);
turn_off(7);
}
}
I have searched on the internet but the codes I've found were far too complex for my understanding.I've also tried using the __delay_cycles(1000); function but it doesn't do anything.
ATmega324PB has an internal 8MHz RC oscillator, while the code has no explicit initial process for the peripheral of MCU. Then, assuming ATmega324PB runs with a main clock of 8MHz by default, so a __delay_cycles(1000); would wait for about 125uS and some more. Instead, __delay_cycles(8000000); or a finite-loop, e.g. for(i=2000000;i>0;i--);, might work.
Suggestion: Try to use its internal Timer to implement the functionality of delay. The application note AVR130: Using the timers on tinyAVR and megaAVR devices would be a viable reference.
I've been trying to make my LEDs on my Arduino go on and off with the corresponding button press.
I'm using interrupts to make it happen and the button press does get registered, but for some reason it doesn't change the global variable's value(int button_pressed1,...);
What's supposed to happen is that when I press button 1, Led 1 is supposed to go on and off, same with button 2 and button 3.
I really appreciate you taking a look, interrupts are pretty new to me so it might be a minor overlook. <3
*I left out the code for button2 and 3. If I can make the LEDs turn on on button 1, I'll be able to make them turn on for the others.
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "usart.h"
#define LED_DDR DDRB
#define LED_PORT PORTB
#define BUTTON_DDR DDRC
#define BUTTON_PORT PORTC
#define BUTTON_PIN PINC
int button_pressed1 = 0; //globale variabele to turn on functions
ISR(PCINT1_vect)
{
if (bit_is_clear(BUTTON_PIN, PC1))
{
_delay_us(500); //debounce
if (bit_is_clear(BUTTON_PIN, PC1))
{
button_pressed1 = 1;
printf("button 1 pressed\n");
}
}
}
int main()
{
LED_DDR |= _BV(PB2) | _BV(PB3) | _BV(PB4); //registrer pins output(bit = 1)
LED_PORT |= _BV(PB2) | _BV(PB3) | _BV(PB4);
BUTTON_DDR &= ~_BV(PC1) & ~_BV(PC2) & ~_BV(PC3); //registrer inputs(bit = 0)
BUTTON_PORT |= _BV(PC1) | _BV(PC2) | _BV(PC3); // pull up ( bit =1 )
PCICR |= _BV(PCIE1); //type pins doorgeven
PCMSK1 |= _BV(PC1) | _BV(PC2) | _BV(PC3); //pin/button doorgeven aan change mask
initUSART();
sei();
while (1)
{ //infinte loop
if (button_pressed1 == 1)
{
LED_PORT &= ~_BV(PB2); //turn led on
_delay_ms(500);
LED_PORT |= _BV(PB2); //turn led off
_delay_ms(500);
}
}
return 0;
}
A couple of fundamental problems:
All variables shared with an ISR should be declared volatile and have protection against race conditions. See this for details.
You should not have busy-delays inside an ISR. Instead you should setup the timer interrupt to trigger again, within a certain time period. Generally, it is hard to use GPIO interrupts for buttons specifically, polling from a cyclic timer interrupt is preferable. You can use interrupts, but it is rather intricate, details here.
500us isn't likely sufficient time for a de-bounce. Mechanical signal bounces are relatively slow. It is more common to wait ~10ms or so. You can quite easily measure the bounce characteristics with a scope, by adding supply to the button, then press it and capture the edge. It will look like a sinusoidal and you easily measure for how long it will last.
The 500ms delays in your main loop will get experienced as "lag" by the user. They might start to suspect a broken button. You might want to at least skip the turn off delay.
I am new to Free RTOS, and I was following some tutorial line by line but things didn't sum up correctly, I used free RTOS to toggle 3 LEDS but it lights just 2 of them without toggling! random 2 LEDs, whatever I change the priorities or the delay time of toggling. random 2 LEDs just switch on and nothing more, I tried the code on proteus simulation and on real hardware and the same problem exists. can someone help me with this?
M/C: ATMEGA32A
RTOS: FreeRTOS
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
/* FreeRTOS files. */
#include "FreeRTOS.h"
#include "task.h"
#include "croutine.h"
#include "FreeRTOSConfig.h"
/* Define all the tasks */
static void ledBlinkingtask1(void* pvParameters);
static void ledBlinkingtask2(void* pvParameters);
static void ledBlinkingtask3(void* pvParameters);
int main(void) {
/* Call FreeRTOS APIs to create tasks, all tasks has the same priority "1" with the
same stack size*/
xTaskCreate( ledBlinkingtask1,"LED1",
configMINIMAL_STACK_SIZE, NULL, 1, NULL );
xTaskCreate( ledBlinkingtask2,"LED2",
configMINIMAL_STACK_SIZE, NULL,1, NULL );
xTaskCreate( ledBlinkingtask3,"LED3",
configMINIMAL_STACK_SIZE, NULL,1, NULL );
// Start the RTOS kernel
vTaskStartScheduler();
/* Do nothing here and just run infinte loop */
while(1){};
return 0;
}
static void ledBlinkingtask1(void* pvParameters){
/* Define all variables related to ledBlinkingtask1*/
const uint8_t blinkDelay = 100 ;
/* make PB0 work as output*/
DDRB |= (1<<0); //PB0
/* Start the infinte task 1 loop */
while (1)
{
PORTB ^= (1<<0); //toggle PB0 //PB0
vTaskDelay(blinkDelay); //wait some time
}
}
static void ledBlinkingtask2(void* pvParameters){
/* Define all variables related to ledBlinkingtask2*/
const uint8_t blinkDelay = 100;
/* make PB1 work as output*/
DDRB |= (1<<1);//PB0
/* Start the infinte task 2 loop */
while (1)
{
PORTB ^= (1<<1); //toggle PB0 //PB0
vTaskDelay(blinkDelay); //wait some time
}
}
static void ledBlinkingtask3(void* pvParameters){
/* Define all variables related to ledBlinkingtask3*/
const uint16_t blinkDelay = 100;
/* make PB2 work as output*/
DDRB |= (1<<2); //PB2
/* Start the infinte task 3 loop */
while (1)
{
PORTB ^= (1<<2); //toggle PB0 //PB0
vTaskDelay(blinkDelay); //wait some time
}
}
ps: every task works well alone but not together!
As already mentioned in comments - the major problem seems to be that access to the port register driving the LEDs is neither
PORTB ^= (1<<0); // in task 1
[...]
PORTB ^= (1<<1); // in task 2
[...]
PORTB ^= (1<<2); // in task 3
atomic
protected (by disabling interrupts during access, or by RTOS measures such as a mutex)
deployed to one unique task:
It may be misleading that the access to HW register is performed using a single instruction in the C code every time.
Still, this doesn't help because the compiler generates several assembler instructions (e.g., load previous port value to register, modify that register value, write it back to the port). This way, one task can interrupt another between those assembler/CPU instructions and modify the intermediate value.
Several tasks writing back "their" register value to the port in turn can revert what other task(s) may have just written to the port, so you miss a blinky event (or several, if this happens systematically).
The solution is therefore to protect the assignments against each other.
In the same order as numbered above, this may mean either of the following:
Check if the hardware offers a "set value" or "reset value" register beside the main PORTB port register. If so, writing a single bit to that port would be an atomic way to have the LED toggle.
I'm sorry that I don't know the hardware interface of Atmega. Maybe, this isn't possible, and you have to go on directly to 2. and 3.
a. Disable interrupts before changing the port register, reenable it afterwards. This way, the task scheduler won't run during that period (= critical section) and nobody disturbs the task that accesses the hardware.
b. Use taskENTER_CRITICAL()/taskEXIT_CRITICAL()
c. Use a mutex or similar.
Create a fourth task which waits (blocking) at a mailbox/queue.
Whenever it receives a value from the mailbox, it processes it (e.g., by XOR-ing it to the port register).
The three existing tasks don't access the LED port register themselves, but instead send such a value (= request message) to the new task.
Assign a higher priority to the new task in order to get a smooth blinking pattern.
If option 1. is possible on your controller, it is fastest (but it requires certain features in the hardware platform...). Otherwise, I agree with the hint from #Richard, option 2.b. are fastest (2.a. is as fast, but not as clean because you break the layering of the FreeRTOS lib).
Option 2.c. may introduce a notable overhead, and option 3. is very clean but a complete overkill in your situation: If your question is really only about blinking LEDs, please leave the bulldozer inside the garage and choose option 2.
I'm trying to put my AtTiny 13 to sleep and wake it up with interrupt. It does go to sleep but it never wakes up. The whole code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>
#include <avr/sleep.h>
#define RED (1<<PB4)
#define RED_HIGH PORTB |=RED
#define RED_LOW PORTB &= ~RED
#define RED_TOG PORTB ^= RED
#define BUTTON 1<<PB1
volatile static bool is_sleeping;
ISR(INT0_vect)
{
RED_TOG;
is_sleeping = true;
}
int main(void){
GIMSK |= 1<<INT0;
MCUCR |= 0<<ISC00 | 1<<ISC01;
sei();
DDRB |= RED;
DDRB &= ~BUTTON;
PORTB |= BUTTON;
RED_HIGH;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
while(1){
if(is_sleeping){
sleep_enable();
sei();
sleep_cpu();
sleep_disable();
}
}
}
According to sleep.h data it should work. Any ideas?
Update: it does not have problems with waking up from IDLE mode;
Assuming no hardware wiring issues your code is working as follow: after booting your LED is turned on and while-loop is idling since is_sleeping is initially set to zero. First INT0 interrupt toggles LED (turns it off) and sets is_sleeping flag so that while-loop will enter to guarded code in next turn. That code turns MCU to sleep on sleep_mcu() line. Once INT0 interrupt awaits MCU it continues from last place i.e. it goes back to sleep because is_sleeping is still set! (and in your code is never turned back to false). It means that right after MCU awakes it goes to sleep almost instantly and is off until next INT0 interrupt.
So to answer you question it never wakes up I would say: it does wake up but for really short moment. If you measure current (e.g. with scope and shunt resistor) you would observe spikes when it wakes and goes asleep immediatelly.
Regardless of you main problem pay attention to code quality. Embedded programming is far from forgiving and you may stuck for hours on trivial mistakes. For instance always be defensive with macro definitions. You defined BUTTON as 1<<PB1 without parens. Difference is that later on you get hit by operators precedence. For instance using DDRB &= ~BUTTON you do not have what you expect. Your right side expression unfolds to 11111100 (because ~1<<1 is 11111110 << 1) while you wanted 11111101 (because ~(1<<1) is ~ 00000010). If you use PB0 for something else you would expect unwanted behavior.
Also when copying sample code make sure you understand what it stands for. The sample in sleep.h relies on using both sei and cli complementary. In your code you only insist on re-enablig interrupts in loop, which is pointless here.
EDIT: Since you claim wake up works in "idle" mode, then your next issue is that you expect system to wake up on falling edge by setting pair (ISC00,ISC01) to (0,1) in MCUCR. See datasheet chapter 9.2 that says "Note that recognition of falling or rising edge interrupts on INT0 requires the presence of an I/O clock" while table in chapter 7.1 says that Clk_I/0 not present in power-down mode. Your only choice it to make INT0 external interrupt to trigger on low level by setting pair (ISC00,ISC01) to (0,0) in MCUCR.
I tried to create a CTC timer interrupt on my ATmega32U4 leonardo board. When I continuously check the value of OCF1A I have no problem detecting when the output reaches the desired value, however once I move the code into an interrupt, the interrupt never triggers.
Timer setup:
#include <avr/io.h>
void setupTimer()
{
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= ((0 << CS10) | (0 << CS11) | (1 << CS12)); // set up prescaler
OCR1A = 6249; // 100 ms set up output compare value for interrupt
TIMSK1 |= (1 << OCIE1A); // enable interrupt on clock compare
}
The loop that works:
setupTimer();
for (;;) {
if (TIFR1 & (1 << OCF1A)) {
PORTC ^= (1 << PORTC7);
TIFR1 = (1 << OCF1A);
}
}
The interrupt that does not work:
#include <avr/interrupt.h>
ISR(TIMER1_COMPA_vect) {
PORTC ^= (1 << PORTC7);
}
I must be missing something as from what I have seen in the tutorials the above code should work. One interesting observation here is that if I have both the loop and the interrupt in my code at once if I call sei(), the LED does not blink as if the OCF1A register was cleared prematurely.
I'm pretty sure it is irrelevant in this case but the fuses are as follows: E:CB, H:D8, L:FF.
I use avr-g++ to compile and the code is spread out between several files.
Given that someone got here through google two years after this question was asked I suppose I should share my own findings on this matter.
The code provided in my question is correct and assuming that there is a call to sei()somewhere after the setupTimer() the interrupt should trigger correctly. The issue was just as c0redumb described in his answer - the bootloader was messing with some registers and thus preventing the code from running correclty. However my solution to this problem was slightly different as in my case, the interrupt would not trigger even after unplugging and re-plugging the board (it is possible that the bootloader has changed in the two years since I have asked this question).
The simplest way to prevent a conflict between the code and the bootloader is to simply remove the bootloader. By using USBasp programmer one can simply load their own code onto the board and thus be sure that it is the only thing running on the CPU.
You have two problems:
You need to make sure main() doesn't return even when it's not doing anything except waiting for the interrupt
You need to enable interrupts via sei() after setting everything up.
Here's a working example (I changed the LED port to PB5 because I've tested this on an Arduino Uno and that already has an LED built-in)
#include <avr/interrupt.h>
#include <avr/io.h>
void main ()
{
DDRB |= 1 << DDB5;
TCCR1B |= 1 << WGM12;
TCCR1B |= 1 << CS12;
OCR1A = 6249;
TIMSK1 |= 1 << OCIE1A;
sei();
for(;;);
}
ISR(TIMER1_COMPA_vect)
{
PORTB ^= 1 << PORTB5;
}
This issue puzzles me today as well. Through search I found your question. I did some more searches all around and found no answer to this one. I had thought that I must have forgotten to enable some circuit or set some flags. Finally, with an LED as my debugger, I have found the cause.
The problem is with the bootloader, not your code. To get it to work, you just unplug the board from USB (after having written code through bootloader), and re-plugin so the bootloader jumps directly to your code at powerup, and it works there. The bootloader must have done some fancy footwork in uploading the code, and didn't work all well afterward in this situation.
As a reference, I used a ProMicro board which I believe has Caterina bootloader same as the Leonardo board you used.