ATTINY20 ADC external INT0 interrupt help.

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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 - 

  1. Set ATTiny20 in power down mode with ADC switched off (for reduced power consumption).
  2. Set an external interrupt INT0 (on Pin B2) as rising edge level which detects an external signal.
  3. Once rising edge is detected up to few clock cycles, the Tiny20 is woken up through the ISR routine and ADC parameters are set.
  4. 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 - 

  1. The controller is powered down.
  2. 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.
  3. 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!).

Last Edited: Mon. Oct 7, 2019 - 06:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

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

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2
		uint8_t low = ADCL;
		uint16_t adcValue = (ADCH<<2)|(low>>6);  //This variable stores the total 10 bits value

Looks like nonsense - what on earth is the factor of 6 doing in here?

 

This entire code could be:

		uint16_t adcValue = ADC;  //This variable stores the total 10 bits value

there's no need to access ADCH and ADCL separately.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Looks like nonsense - what on earth is the factor of 6 doing in here?

there's no need to access ADCH and ADCL separately.

 

OK then. I thought maybe I was supposed to access ADCL first and then ADCH later.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Maverick123 wrote:
OK then. I thought maybe I was supposed to access ADCL first and then ADCH later.

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 https://www.onegold.com/join/713...

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

There is a pretty fundamental misunderstanding in post #1

 

  1. Set an external interrupt INT0 (on Pin B2) as rising edge level which detects an external signal.
  2. Once rising edge is detected up to few clock cycles, the Tiny20 is woken up through the ISR routine and ADC parameters are set.

 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:

 

  1. When bits SM[2:0] are written to 010, the SLEEP instruction makes the MCU enter Power-down mode. In this mode, the oscillator is stopped, while the external interrupts, TWI and the watchdog continue operating (if enabled). Only a watchdog reset, an external level interrupt on INT0, a pin change interrupt, or a TWI slave interrupt can wake up the MCU. This sleep mode halts all generated clocks, allowing operation of asynchronous modules only.

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:

 

If an enabled interrupt occurs while the MCU is in a sleep mode, the MCU wakes up. The MCU is then halted for four cycles in addition to the start-up time, executes the interrupt routine, and resumes execution from the instruction following SLEEP. The contents of the Register File and SRAM are unaltered when the device wakes up from sleep. If a reset occurs during sleep mode, the MCU wakes up and executes from the Reset Vector.

Jim

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Oct 7, 2019 - 08:47 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 I was under the assumption that the ISR() body gets executed once the sei() is hit 

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!

Last Edited: Wed. Oct 9, 2019 - 07:36 AM