Hi fellas,
I need some help to implement an external INT0 interrupt which powers the MCU from power down mode to idle mode and start ADC conversion. I have only 2 months worth experience of ATMEL studio 7 and needless to say I am yet to gain a strong foothold.
The program I am trying to interrupt has the following algorithm -
- Set ATTiny20 in power down mode with ADC switched off (for reduced power consumption).
- Set an external interrupt INT0 (on Pin B2) as rising edge level which detects an external signal.
- Once rising edge is detected up to few clock cycles, the Tiny20 is woken up through the ISR routine and ADC parameters are set.
- execute ADC in free run mode.
I am attaching my code below -
#include <avr/io.h> #include <avr/sleep.h> #include <avr/interrupt.h> uint16_t adcValue; //Stores all 10 bits uint8_t low; // stores bits 0 and 1 of ADCL ISR(INT0_vect) // called immediately on encountering sei() { MCUCR |= (0<<SE); //Clear enable sleep MCUCR |= (0 << SM1); // powering up the controller to idle mode PRR |= (0 << PRADC); // powering up the ADC ADCSRA |= (0 << ACD); // powering up the comparator DDRA |= (1 << 1)|(1 << 2)|(1 << 3)|(1 << 4)|(1 << 5)|(1 << 6)|(1 << 7); //PA1 - 7 set as outputs DIDR0 |= (1 << ADC0D); //PA0 input buffer disabled to reduce current DDRB |= (1<<1)|(1<<3)|(1 <<4); //PB1,3,4 set as outputs ADMUX |= (0 << REFS); // Reference = Vcc ADCSRA |= (1 << ADATE); ADCSRB |= (1 << ADLAR) | (0 << ADTS2) | (1 << ADTS1) | (0 << ADTS0); // rising edge of int0 ADCSRA |= (1 << ADPS2) | (1 << ADEN) | (1 << ADSC); } int main (void) { ADCSRA |= (0 << ADEN); // disabling the ADC before shutting it down PRR |= (1 << PRADC); // shutting down the ADC // comparator disabled automatically MCUCR |= (1 << SE); // sleep enable bit set to 1 MCUCR |= (1 << SM1); // powering down the controller GIMSK |= (1 << INT0);//Enable INT0 interrupt MCUCR |= (1 << ISC00) |(1 << ISC01);//INT0 interrupt = rising level interrupt sei(); // triggers the interrupt ADCSRA |= (1 << ADSC); for(;;) // Loop Forever { //ADCSRA |= (1 << ADSC); uint8_t low = ADCL; uint16_t adcValue = (ADCH<<2)|(low>>6); //This variable stores the total 10 bits value if(adcValue <= 32 && adcValue >=0) { PORTA |= (1 << 1); // LED 1 ON, Rest others OFF PORTA &= ~(1 << 2) &~(1 << 3) &~(1 << 4) &~(1 << 5) &~(1 << 6) &~(1 << 7); PORTB &= ~(1 << 1) &~(1 << 3); } else if(adcValue <= 64 && adcValue >32) { /* switch on other LEDs for different values of ADC output*/ } else { } } }
I programmed the above code and I get the following behavior -
- The controller is powered down.
- When an external signal interrupt is applied to Pin B2 (INT0), the ADC apparently turns ON. This is because the first IF case gets executed and LED1 gets turned on.
- However, with changing analog input values at Pin A0, I do not get corresponding changes in other LEDs.
Further note - This program when run in free running mode without any interrupts works perfectly fine. The main goal of this code was to display each bit of the ten bit value as an LED light. (Yes, I am still working on Fleury's LCD display!).
I think the problem is not studio related, but more general your understanding of how things work.
The moment you power down the processor it will no longer know what it has been doing in the past.
When you then apply power it will just start with a reset ( clearing all the registers)
And start from the beginning of main again....
Setting up an interrupt and then doing a sei() does not trigger the interrupt.......
the sei() command enables ALL the enabled interrupts to be able to generate an interrupt.
with the ADC disabled, it has no use to start an ADC conversion.
uint16_t MyAdc = ADC; should get you all the data in one go, will make your code simpler ;)
with the ADC disabled the result of reading out the ADC register will always be 0 as the register is cleared by the rest and applying power.
So in the end it will look like it is stuck but actually as the ADC never gets turned on nothing will happen.
If you want to do ADC measurements right after power on, then just in main init the ADC peripheral before entering the for loop, and there check if a conversion is complete....
Top
- Log in or register to post comments
You may want to toss the first reading in the trashcan,as things are just getting situated & settled. If you are turning on the reference (along with a filter cap), allow enough time for it to stabilize.
When in the dark remember-the future looks brighter than ever. I look forward to being able to predict the future!
Top
- Log in or register to post comments
Looks like nonsense - what on earth is the factor of 6 doing in here?
This entire code could be:
there's no need to access ADCH and ADCL separately.
Top
- Log in or register to post comments
OK then. I thought maybe I was supposed to access ADCL first and then ADCH later.
Top
- Log in or register to post comments
Thanks. That helps me understand. So I guess I will put the register initializations in the main (). I will also make changes to the ADC variable.
Top
- Log in or register to post comments
The compiler knows the correct order!
Click Link: Get Free Stock: Retire early! PM for strategy
share.robinhood.com/jamesc3274
get $5 free gold/silver...
Top
- Log in or register to post comments
There is a pretty fundamental misunderstanding in post #1
It simply does not work that way. It wakes up on the interrupt, period. There will be some delay, especially if the clock was turned off (delay for the clock to start up and stabilize). That is it. You have no choice to "detect up to a few clock cycles". For "Power Down Mode" of sleep, the Tiny20 manual says:
In this mode, there is a substantial wakeup time. It takes a fixed time, same as when you first turn it on. Also, you MUST use a LEVEL interrupt into INT0 pin OR a pin change interrupt on any pin that permits pin change detection.
Meslomp was also incorrect. It DOES NOT start from reset on wakeup. It starts at the next instruction after it was put to sleep. From the Tiny20 manual:
Jim
Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA
Top
- Log in or register to post comments
So if I understand correctly these are the things I need to do -
1. Remove extra ADC value variables.
2. Disregard the first reading.
3. Use a Level interrupt by changing the corresponding bit values.
I still have a question regarding the construct of the program. So, from my understanding, I am supposed to define the ADC register settings in main() and not in the ISR(int0_Vect). This leads me to another question - what happens in the ISR(int0_Vect)? I was under the assumption that the ISR() body gets executed once the sei() is hit and then continues from the loop onward..
Top
- Log in or register to post comments
No SEI, allows interrupts to be automatically responded to (including any that may already be pending)...when sei is "off" (CLI) ,interrupts stay pending (unless you or something cancels them, or set SEI) SEI itself sets the global IRQ response flag
SEI: Sets the Global Interrupt flag (I) in SREG (status register). The instruction following SEI will be executed before any pending interrupts.
CLI: Clears the Global Interrupt flag (I) in SREG (status register). The interrupts will be immediately disabled. No interrupt will be executed after the CLI instruction, even if it occurs simultaneously with the CLI instruction.
If no IRQ's are already pending, then SEI does nothing at that time, but a response will be generated upon future IRQ's. Think of it as an overall IRQ on (SEI)/off (CLI) switch
When in the dark remember-the future looks brighter than ever. I look forward to being able to predict the future!
Top
- Log in or register to post comments