avr sleep mode and wake up - c

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.

Related

freeRTOS doesn't work properly on atmega32A

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.

AVR C Why wont this interrupt?

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]).

Stopwatch using a ATMEL 2549 Microcontroller

i am trying to understand a program but i have some questions, maybe you can help me. The microcontroller used is a ATMEL 2549 - 8bit. Thank you in advance.
Atmel-2549 8-bit AVR Microcontroller ATmega640 1280-1281-2560-2561 datasheet
Set up a stop watch with the following features:
• There are three push buttons: START, STOP, and RESET
• Your system generates an interrupt every 100ms by using Timer1.
• There is an LCD at port A. Use the usual library for controlling the
LCD!
• On the LCD, you display the time that has elapsed since the START
button was pushed. Show minutes, seconds and tenth of seconds.
• After 59 min 59.9 s, the display starts from scratch again.
#include <stdint.h>
#include <avr/io.h>
#include "lcd.h"
#include <stdio.h>
#include <avr/interrupt.h>
void timer1_config(void);
void exinterrupt_config(void);
void send_string(void);
display myLCD;
volatile char text[20];
volatile uint8_t minute=0,sekunde=0,zehnt=0;
ISR(TIMER1_COMPA_vect) { //Interrupt for a timer with minute, second and decisecond.
zehnt++; //from 0 to 59min 59,9sec. After that time is elapsed,
if (zehnt>9) { //it should be showed on LCD display.
zehnt=0;
sekunde=sekunde+1;
}
if (sekunde>59) {
sekunde=0;
minute=minute+1;
}
if (minute>59) {
minute=0;
sekunde=0;
zehnt=zehnt+1;
}
send_string();
}
ISR(INT0_vect) { //Interrupt for starting the timer.
// --- No. *1 ---
TCCR1B|= (1<<CS11);
}
ISR(INT1_vect) { //Interrupt for stopping the timer.
TCCR1B&=~((1<<CS10)|(1<<CS11)|(1<<CS12));
}
ISR(INT2_vect) { //Interrupt for resetting the timer.
minute=0; //Sets everything to 0 and shows time on LCD display.
sekunde=0;
zehnt=0;
TCNT1=0;
send_string();
}
int main(void) {
// --- No. *2 ---
DDRD&=~((1<<PIN0)|(1<<PIN1)|(1<<PIN2));
timer1_config(); //Load all three functions.
exinterrupt_config();
send_string();
lcd_init(&myLCD ,&PORTA); //Start the LCD display on port A.
lcd_send_string(&myLCD,"-------Watch-------",1,1);
sei();
for(;;){};
return(0);
}
void timer1_config(void){
TCCR1B|=(1<<WGM12);
OCR1A=12499;
// --- No. *3 ---
TIMSK1|=(1<<OCIE1A);
}
void exinterrupt_config(void){
EIMSK|=((1<<INT0)|(1<<INT1)|(1<<INT2)); //Enable all 3 interrupts.
// --- No. *4 ---
EICRA|=((1<<ISC01)|(1<<ISC11)|(1<<ISC21));
}
void send_string(void){ //Sends text to LCD display.
sprintf(text,"%i.min %i.sek %i.zehnt",minute,sekunde,zehnt);
lcd_send_string(&myLCD,text,3,1);
}
1: I understand this is the command to make the timer start counting, but on the description from the datasheet it says "clkI/O/8 (From prescaler)" for setting the bit CS11 high. I cant understand it and how it works.
2: Is it setting the bits from DDRD to input (0)? If so, why is it being done if port D inst even being used?
3: I dont understand what it does!
4: The description from the datasheet says "The falling edge of INTn generates asynchronously an interrupt request", but i dont really get what it does. Whats the difference to "The rising edge of INTn generates asynchronously an interrupt request"?
Thank you again!
1: Setting CS11 High
From Table 17-6, it really sets the clock to clk(I/O)/8. That means it will increment the internal counter on every eighth tick of the internal I/O clock. Maybe you couldn't count every tick in the timer's register in a second, so you need to prescale it.
2: Setting DDRD bits to input
Those are for the buttons. The buttons must be on PIND of your panel, one bit for each button. Although the program does not read PIND, the external interrupt handler does, so the data direction must be set up accordingly.
Buttons, switches are inputs, leds are outputs. It depend's on your developer panel on which ports are they wired.
3: Setting up TIMSK1
§17.11.36
• Bit 1 – OCIEnA: Timer/Countern, Output Compare A Match Interrupt
Enable When this bit is written to one, and the I-flag in the Status
Register is set (interrupts globally enabled), the Timer/Countern
Output Compare A Match interrupt is enabled. The corresponding
Interrupt Vector (see “Interrupts” on page 101) is executed when the
OCFnA Flag, located in TIFRn, is set.
The timer peripherial can operate in different modes. This setting is related to output compare mode, and it will tell the hardware to issue an interrupt when the timer's internal counter reaches a limit (the limit is set in OCR1A).
The timer is set to CTC (Clear Timer on Compare) mode in TCCR1B (Table 17-2), so it restarts counting when the limit is reached.
4: Falling edge
A falling edge is when a signal goes from high to low. A rising edge is when the signal goes from low to high. These buttons are usually Active-LOW, so a falling edge means the button is pressed. (and a rising edge means the button is released)

AVR ATmega32U4 Timer compare interrupt not triggering

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.

Program exits endless loop (PIC microcontroller)?

I wrote a program for the PIC 16f690 microcontroller after noticing my programs seemed to be exiting an endless loop.
There are LEDs on pins 3,4,5 of PORTC.
#include <xc.h>
#define _XTAL_FREQ 4000000
void main(void) {
TRISC = 0x00;
PORTC = 0b00111000;
while (1)
{
__delay_ms(1000);
PORTC = 0x00;
}
}
As far as I understand, the LEDS should be on for ~1sec, and then be off forever. Instead they keep blinking every second.
I wrote this in MPLABX and programmed using PICkit3 and C8 compiler.
You are likely being bitten by the watchdog. Disable the watchdog for your tests or clear it before it reset the MCU.
I had the same issue but in place of leds, I was using serial communications. while(1) was not really a loop forever and execute the stuff between brackets.
As soon as I disabled the watchdog, only the stuff inside the brackets was called after the first iteration.
#pragma config WDTE = OFF // Watchdog Timer Enable (WDT enabled)
I was also observing trash after every uart packet and that went away with the watchdog disabled.
FYI pic 16f1708.
This caused some erratic behavior since the uart initialization functions, IO setup, and oscillator divider were called every loop iteration.

Resources