Interrupt loops problem

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

Hi only just joined. I have a problem understanding interrupts. Hope some one can point me in the right direction. Can't find any related threads on this but I expect their are some. So would be nice to know which ones to read on this subject.

Not sure what correct forum protocol is here, so many apologies in advance if I have got this wrong.

Anyway problem is I have written some code to test interrupts using a hall effect transistor to take an input pb4 low to switch off an led. Problem is the ISR() has a wait loop which should switch led back on again after loop has finished. But it fails to switch led back on. So although the ISR is activated I seem to have a problem with my code in there.

Very grateful for any ideas on this as to why LED fails to be relit. I am using atmega48pv and a usbasp programmer.

Here is the code (problem I think must be with the code in ISR routine.)

/*Filename: interrupt blink.c */
#include 
#include 


void setup(void);


ISR(PCINT0_vect) // INTERRUPT SERVICE ROUTINE name PCINT0_vect
{
unsigned int i;
PORTB |= (1<<PB5); // pb5 LED Off leave rest alone 
for (i=0;i<100;i++); // simple delay
PORTB |=(0<<PB5); // pb5 LED On leave rest alone

// HERE LED SHOULD BE BACK ON BUT ITS NOT!??

}



void setup(void)
{
DDRB = 0B11101111; // PB4 input
PORTB = 0x0; // START WITH LEDS ALL On

PCICR |= (1<<PCIE0); // Pin Change interrupt Control 
// Register. Set the 0 bit high PCIE0
// to allow an interrupt for the range of pins PCINT[1:7]

PCMSK0 |=(1<<PCINT4); // here we now select the individual pin(s) we want to use as an interrupt eg in this example its just one (PCINT4)
// by setting the Pin Change Mask Register for the number 0 range of pins. 


sei(); // set and enable the interrupt. 
}


int main (void)
{

setup();
while (1);
return 0;

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

Quote:

PORTB |=(0<<PB5); // pb5 LED On leave rest alone

Anything ORed with zero remains unchanged.

Start with this: https://www.avrfreaks.net/index.p...

Other notes:
-- Looping/delaying in an ISR is a bad idea in a "real" application
-- The compiler is likely to totally discard your loop as it does nothing useful
-- Even if the compiler did not discard the loop, it would only be a few microseconds--you might not see the LED at all
-- For your test/learning app, look at the delay functions provided by your toolchain
http://www.nongnu.org/avr-libc/u...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

This is happening too fast to notice any change in the LED state. Assuming that something triggers the PCINT0 interrupt, for example a push on a switch on port B4. On each edge of the debounce, the PCINT flag goes high. The interrupt happens, LED goes ON, a delay of about 325 system clock cycles:

Load register with 100
here: dec register
register < 0?
if yes, jump back to here

about 3 cycles (with a good compiler) per delay loop.
325 system cycles is way below any human perception level. You want a delay of a few tens of a second for an LED blink.

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

The compiler will most likely optimise that delay loop out unless you declare the variable volatile. Use the library delay functions like delay_ms()

The other issue is switch bounce - has been spoken about many, many, many times here.

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

The way to use interrupts to time out various procedures is to have a timer interrupt happening repeatedly at regular short intervals. Then in the IRQ, keep counts for longer intervals and set flags when those intervals are up.

To blink an LED, set the timer (any timer) to a 1 millisecond interval. Either load the timer with a value that will cause an interrupt when it overflows (goes from 0xff to 0x00 for an 8-bit timer). Assuming a 1MHz clock, set the timer prescaler to 8. Load 256 -125 (the overflow value - ( sys clock/8)) into the timer. In 1 millisecond it will trigger the TimerOverflow TOV.

In the TOV IRQ routine, decrement two counters. One has the value of fiveteen, the other 200. When a pushswitch is pressed, load one counter with 15. ON each interrupt, decrement this counter. When it reaches zero, check that the pin is at the same logic for on or off. This debounces the switch.

At the other counter, each time the it reaches 0 from 200, toggle the state of the LED. This blinks it several times a second.

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

Delays inside ISRs are evil in almost every case. A common way to do what you want to do is to start a timer in the ISR (the ISR that triggers the START action) and have the timer generate its own interrupt after the delay value you want, and in that ISR do the STOP action. For example, this is a clean way to do light dimming, where the START interrupt is a zero-crossing of the AC line, and the STOP timer interrupt turns on the triac for the rest of the AC half-cycle.

You can generally use an already running timer and an output compare to generate this time delay, as long as the timer is free-running and not resetting before its maximum count (as happens in CTC and some PWM modes). In that case you simply add the delay to the current timer value and store that in the output compare register - it's a one-line operation, e.g.:

OCR1A = TCNT1 + DESIRED_DELAY;

Assuming you've enabled the output compare interrupt, it will fire DESIRED_DELAY timer clocks after this line is executed.

Simonetta's post describes another very common way of doing time delays. Each way has advantages depending on the situation.

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

Those answers came back fast. Was not expecting that speed of response!

theusch: I changed that. No idea why I had those ORed. Your points are really welcome and makes total sense.

simonetta: great info thankyou. But I have not touched C code for 30 years. So its a tad new to me.

I have no idea about the timer register at moment. But will look into that soon. Thanks for the heads up. Fascinating stuff. Only read atmega48 Datasheet today and still not that sure how it all works. I thought it would be easy. But its huge all these uC details.

Kartman: Switch bounce I don't think will be a problem as I am using a hall effect transistor which has hysteresis built in. But this has helped me think this through a lot more. Many thanks.

I will make some changes post back where I get to. I am attempting to build a trip counter using Ic2 to drive a chip that drives an LED. Its a first project using a uC. So I will be probably asking a few more questions before its finished.

very impressed with all these replies. Thanks again.

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

Quote:
I thought it would be easy. But its huge all these uC details.
You can take one system at a time. They are fore the most part independent of each other (except where they happen to share a pin, or where a timer can trigger the ADC, etc.).

Regards,
Steve A.

The Board helps those that help themselves.

Last Edited: Tue. Apr 30, 2013 - 11:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch: BTW Thanks for both those links. Both are very helpful.

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

kk6gm: I am going to give that a go. Thanks.

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

Quote:

using Ic2 to drive a chip that drives an LED

Hmmm--most of us would skip the middleman and drive LED/LED displays directly from the AVR.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Simonetta: I just quickly checked the timer interrupts and yes I get what you mean. Brilliant. Will give this a go also.

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

theusch: Skip the middleman would definitely be the sensible option. But the second objective is to get up to speed with Ic2 as I know zero about it. So I thought making that part of this in some way would be a good way to learn it. All these chips are cheap, but the learning curve is expensive. So I want to get all that under my belt first before I start building real stuff. Or maybe this is the wrong approach. Value your opinion on this.

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

theusch: There is also a sort of kinda plan. I have to eventually build a very tiny trip counter for a winding machine that has to fit into a tiny space. So I thought I could use an SMD attiny85 driving a small smd IC2 LCD driver connected to an LCD. basically a tiny box slightly bigger than the 4 character LCD for the attiny and smd space.

But I have not sourced it all yet. Thought I would learn basics first. So using the atmega48 dil and other dil packages first. When its all working with leds will then move it all over to an attiny85 and the smd ic2 LCD driver.

If I ever get that far I'll be happy.

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

Newer AVRs allow you to toggle the output by writing a '1' to a PIN register's bit. See the I/O section of the datasheet.

 PINB |= 1<< PB5; // Toggle it

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

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

Quote:

attiny85 and the smd ic2 LCD driver

So you figger you will save space and time and money by adding another chip?

Let's look at that a bit. The Tiny85 is in two SO packages, 5mm x 6mm and 5.25mm x 8mm. So 30 or 40 mm2. (The smaller one doesn't seem to be around at distis.)

TQFP32 is 9mm x 9mm. 81mm2. Twice as much. How big is the interface chip?

Tiny85 is $1.18/qty 1 and $0.66/qty 100. Tiny88 is about twice as much; $1.83; $1.13. How much does the interface chip cost?

As you can tell, I'm kind of a fan of near-single-chip...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

As per the very good suggestions above, I have modified this now to use volatile values and a timer routine. But I am still having same issue. At moment when power is switched on I have no LEDs lit. When I move magnet into vicinity of Hall effect transistor (Which uses a Schmitt trigger) the lamp goes off. So up to then it is all good and as I expect. But then moving magnet away from transistor the input is then taken low (checked with MM). But Leds do not come back on.

I am sure its not the compiler or the circuit, so it is my logic here that is wrong.

Here is the modified code. Could someone have at look and tell me what I am doing that is failing to swap the state of the output pins (led on/off).

/*Filename: interrupt blink.c */
#include 
#include 

volatile int counter,onoff;

void setup(void);



void timer0_init(void)
{
// prescaler bits are CS00, CS01 and CS02 in register TCCROB
// see datasheet TCCROB |= 0B00000101
TCCR0B |=((1<<CS02)|(1<<CS00)); // this sets interrupt rate
TCNT0 = 0; // sets initial value of timer/counter 0
TIMSK0 |= (1<<TOIE0);
// TIMSK0 = Timer Interrupt mask Register 0,
// TOIE0 = TIMER 1 Interrupt 0 enable bit
}


ISR(TIMER0_OVF_vect)
{
// 
counter--; // decrement counter on each pass

if (counter<0) { // only do this section after 100000 entries into this ISR

if (onoff==0) { // swap flag value. on becomes off, off becomes on.
onoff=1;} 
else { // must currently be a 1 so make it a zero
 onoff=0;
}

counter=100000; // set counter back to start value
} // end if


}
 



ISR(PCINT0_vect) // INTERRUPT SERVICE ROUTINE name PCINT0_vect
{
unsigned int i;

if (onoff==0) PORTB = 0x0; // set all leds to on

if (onoff==1) PORTB = 0xff; // set all leds to off

}



void setup(void)
{
DDRB = 0B11101111; // PB4 input
PORTB = 0xff; // BEGIN WITH LEDS ALL OFF
PCICR |= (1<<PCIE0); // Pin Change interrupt Control Register. Set the 0 bit high PCIE0
// to allow an interrupt for the range of pins PCINT[1:7]
PCMSK0 |=(1<<PCINT4); // here we now select the individual pin(s) we want to use as an interrupt eg in this example its just one (PCINT4)
// by setting the Pin Change Mask Register for the number 0 range of pins. 
sei(); // set and enable the interrupt. 
}


int main (void)
{
counter=100000;onoff=0; // initialise volatile variables
setup();
while (1);
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
counter=100000; // set counter back to start value 

It would be a good trick to get 100000 into an int on an AVR. For the same reason:

// only do this section after 100000 entries into this ISR 

you mean 32768.

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

Quote:

At moment when power is switched on I have no LEDs lit. When I move magnet into vicinity of Hall effect transistor (Which uses a Schmitt trigger) the lamp goes off.

I'm getting very confused. Is "lamp" the LEDs? If there are none lit at startup, how do you know they are turning off?

I've lost track of the timing here. How fast is one timer tick? If one millisecond, then it takes 100 seconds to get 100000. If 10 milliseconds, then it takes 1000 seconds. Are you patient enough?

But wait--how do you load 100000 into a counter that holds 32k max? (I'd guess that your counter is starting out negative.)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Ok. I did for a while have the LED flashing on and off. Which is not quite what I want. Part of error was not calling my timer init routine.

Now its gone back to staying on after first successful transition. But now again it just stays in that state from then onwards.

Here is more detail. Circuit has a hall effect transistor connected to pb4. The hall effect is acting as a current sink so when I move the magnet close to the hall effect transistor the current flows through it and takes the uC pin pb4 low (which is connected to the output of the hall effect device). When I then move the magnet away to a further threshold distance away the transistor switches off so the pb4 input is then high 5v again. (Hall effect uses a pull up resistor as it is an opencollector)

The code I want to write should pole the pb4 input and light the led on and off when the transition described above happens. Every transition should keep pb5 output at same state until next magnet transition.

pb5 use pull up resistors. So makes this description more confusing.

At moment all that happens is the initial step which is correct. Ie. leds on pb5 switched off as initially pb5 high 5v then when magnet comes close to hall effect device PB4 taken low so I then make all PORTB outputs low 0v (led on). So the led comes on which is connected to pb5. But when I then remove magnet and PB4 goes back to high value I should be setting PORTB high so pb5 led goes out again off. But it does not go out but stays lit up.

Hope that makes sense. It is very confusing. Getting my head around high, low voltages mixed with logic high and lows is painful.

Last Edited: Wed. May 1, 2013 - 08:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch: You have lost me on those questions. I don't know is the answer. I am just guessing at moment. I obtained this timer routine from a book by someone called Chris leaver about AVR uC. Was the only one I could find which seemed relevant and had fair reviews on amazon.

Before yesterday I have never seen any of this before. So I don't as yet fully understand the timer info. But I am assuming it is almost there as I now have a blinking LED.

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

theusch: You are right about the size thing and sticking to a single uC. But I am going to do it the dumb way, as I think I will learn more this way (with the three packages). I don't have a deadline on this its just for fun. Plus I have to now learn about uC, ic2 is important for me as that is what I will need to use in the end for other projects. So I will hopefully learn a number of these elements within one single basic project. BTW I like your replies, they are making me think!

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

I have it working. :) It turned out to be I had the outer counter to high. When timer was all ready set to a pause of a second or so. So my 100000 was why LED stayed on forever. Changed the outer count to 1 and its working correctly.

This is not easy stuff to learn, so many thanks to every one for all the suggestions. I think I would have been struggling a lot longer with out this. I am sure this is not the best way to do this. But its taught me a lot. Now I can dig a bit deeper and make it better.