Nested Interrupts

nest
Normally, the AVR hardware clears the global interrupt flag (in SREG) before entering an interrupt. This means other interrupts are disabled inside the running handler until the handler finishes and exits.

The RETI instruction is the normal function epilogue for an interrupt handler, and thus re-enables interrupts (just like executing an SEI instruction).

For this reason, normally, interrupt handlers can not be nested.

For most interrupt handlers, this is the desired behavior. For some its even required in order to prevent infinitely recursive interrupts. Recursion could easily exceed the available stack space and crash the running program. In rare circumstances it might be desirable to re-enable the global interrupt flag as early as possible inside the interrupt handler, allowing recursive interrupt handling. Carefully.

This could be accomplished by inserting an sei() instruction right at the beginning of the interrupt handler, but this still leaves a few instructions inside the (compiler-generated) interrupt prologue to run with global interrupts disabled. A better method is to instruct the compiler to insert an SEI instruction at the very beginning of the interrupt handler by declaring the handler in the following way (XXX_vect is the name of a valid interrupt vector):

ISR(XXX_vect, ISR_NOBLOCK) {
  ...
}

Compare the following three disassembly examples which demonstrate how this works:

Non-nested, normal method:

ISR(TIM0_OVF_vect) {
  //do nothing
}

     PUSH      R1             
     PUSH      R0             
     IN        R0, 0x3F        
     PUSH      R0             
     CLR       R1             
     POP       R0             
     OUT       0x3F, R0        
     POP       R0             
     POP       R1             
     RETI                     ; <<-- Interrupts re-enabled here!

Nested, SEI inserted after prologue:

ISR(TIM0_OVF_vect) {
  sei();
  //do nothing
}

     PUSH      R1             
     PUSH      R0             
     IN        R0, 0x3F        
     PUSH      R0             
     CLR       R1             
     SEI                      ; <<-- Global Interrupts re-enable here!
     POP       R0             
     OUT       0x3F, R0        
     POP       R0             
     POP       R1             
     RETI                     

Nested, best method:

ISR(TIM0_OVF_vect, ISR_NOBLOCK) {
  //do nothing
}

     SEI                      ; <<-- Global Interrupts Enabled here!
     PUSH      R1             
     PUSH      R0             
     IN        R0, 0x3F        
     PUSH      R0             
     CLR       R1             
     POP       R0             
     OUT       0x3F, R0        
     POP       R0             
     POP       R1             
     RETI                     
Posted in Uncategorized | Tagged , , , | Leave a comment

Bad Interrupt

shock
If during the running of your program, an unexpected interrupt were to occur (an interrupt for which no handler is installed) bad things could occur. Default behavior is for the program to jump through the reset interrupt vector and attempt to restart your program. Best case is your program simply starts over.

But maybe this isn’t what you want to happen. Certainly for a critical microcontroller application (think medical device here), this might not be the best course of action. The program could get stuck in an endless error loop. If your device is a heart defibrillator, that could mean repeated shocking of the patient.

Ouch!

So, for the sake of writing robust code, we should plan and implement for this circumstance. This default behavior can be overridden by supplying a function named BADISR_vect defined as an ISR().

Example:

#include <avr/interrupt.h>

ISR(BADISR_vect)  {
  //flash warning lamp
  PORTB |= (1<<WARNING_LED_PIN);
  //set flag
  Bad_Interrupt_Occured = true;
}

The catch-all interrupt vector is described in the documentation here.

Empty Interrupt

Having covered the BAD INTERRUPT vector, we now must briefly cover the EMPTY INTERRUPT vector. With the BADISR_vect set to execute our specific code, we may still want some of they undefined interrupts to trigger a reset. We can accommodate this functionality through the use of the EMPTY_INTERRUPT macro:

EMPTY_INTERRUPT(ADC_vect);

Note: there is no body to this macro.

An example for this could be the case for interrupts that are enabled for the purpose of getting the controller out of sleep_mode().

Posted in Uncategorized | Tagged , , | Leave a comment

Simple Test LED

Test LED

This is an easy method to conduct quick pin/port tests with a microcontroller. It’s helpful for debugging purposes and can be used many times without needing to wire up a breadboard. I have a few of these scattered around the workbench, allowing me to perform quick checks.

Simply solder a limiting resistor to the leg of an LED. Any ohm between 150 and 330 works well. It doesn’t matter which leg it is soldered to, but it is helpful to keep the anode (+) leg slightly longer than the cathode (-) in order to easily tell them apart (remember, many LEDs also have a flat molded into the plastic lens).

Here is a photo of the test LED plugged into a SparkFun Tiny AVR Programmer
Test LED with Programmer

Posted in Uncategorized | Tagged , , | Leave a comment

Un-Bricking an ATtiny13

Smashing Bricks

I was playing with my ATtiny13 timer #0 fast PWM settings to test the range of frequencies I could achieve. I started with the fastest setting using the internal oscillator. 9.6MHz, no prescaler, no timer divider and using a TCNT0 count of 256 (ORC0A sets the duty cycle). This generated a PWM output of 37.6 kHz.

I slowly progressed to slower frequencies, setting the internal oscillator to 4.8MHz and a 256 prescaler and a 1024 clock divider. This resulted in a PWM output of 0.07Hz.

However, I could no longer program the ATtiny13. I couldn’t even change the fuse bits. It became completely unresponsive. I thought I bricked it.

Brick

However, this is when I discovered the –B argument to AVRDUDE. The AVRDUDE Manual states the –B option is for:

-B bitclock

Specify the bit clock period for the JTAG interface or the ISP clock (JTAG ICE only). The value is a floating-point number in microseconds. The default value of the JTAG ICE results in about 1 microsecond bit clock period, suitable for target MCUs running at 4 MHz clock and above. Unlike certain parameters in the STK500, the JTAG ICE resets all its parameters to default values when the programming software signs off from the ICE, so for MCUs running at lower clock speeds, this parameter must be specified on the command-line. It can also be set in the configuration file by using the ’default_bitclock’ keyword.

The following AVRDUDE commandline resurrected my ATtiny13:

&lt;br /&gt;&lt;br /&gt;
avrdude –p attiny13 –c usbtiny –F –B 250 –U flash:w:attiny13.hex&lt;br /&gt;&lt;br /&gt;

See the ATMEL ATtiny13 datasheet for further information on the ATtiny13 Timer #0 PWM settings.

Posted in Uncategorized | Tagged , , , , , , , | Leave a comment

11ATtiny13 Blink Using an Interrupt

ATtiny13
Note to self: Ensure you set the fuse bits for internal oscillator correctly.
Code:

//  blinky
//  James M. Eli
//  5/14/2010
// 
//  Set fuses: int osc @ 9.6MHz, slow startup, serial prog enabled
//  avrdude -p attiny13 -P usb -c usbtiny -U lfuse:w:0x7a:m
//  avrdude -p attiny13 -P usb -c usbtiny -U hfuse:w:0xff:m
//  
//  program hex: 
//  avrdude -p attiny13 -P usb -c usbtiny -U flash:w:tiny13.hex 
// 
#include <avr/interrupt.h> 

ISR(TIM0_OVF_vect) {
  //overflow occurs at 1024/9600000*256=27.3ms/18.3Hz (measured at 26.7ms/18.75Hz) 
  PORTB ^= (1<<PB0);   //toggle led pin 
} 

int main(void) {
  cli();
  //set port B pin0 as output
  DDRB = (1<<DDB0);
  PORTB = 0;
  //prescale timer to 1024   
  TCCR0B |= (1<<CS02) | (1<<CS00);    
  //enable timer overflow interrupt   
  TIMSK0 |= (1<<TOIE0);   
  sei();    
  while(1);
}
Posted in Uncategorized | Tagged , , , , , , , | Leave a comment

ATtiny13 Project & SparkFun AVR Programmer

I decided to work on an ATtiny13 project. I will be using a test board based on the following schematic:

1
Here is the board.

Top:
1

Bottom:
2

Here are the results of a simple “blink” test program:

3

Code:

#include <avr/io.h>
#include <util/delay.h>

//basic bit manipulation macros/defines
#define bit_set(p, m) ((p) |= (m))
#define bit_clear(p, m) ((p) &= ~(m))

//SparkFun AVR Programmer has LED/330R on PB0 (pin 5)
#define LEDPIN PB0
#define PIN5   5

int main(void) {

  //configure LEDPIN as output
  DDRB |= (1<<LEDPIN);

  while (1) {
    bit_set(PORTB, PIN5);    //PORTB |= (1<<LEDPIN);
	_delay_ms(1000);
    bit_clear(PORTB, PIN5);  //PORTB &= ~(1<<LEDPIN);
    _delay_ms(1000);
  }

  return 0;
} 

SparkFun AVR Programmer
Programmer
I used SparkFun’s Tiny AVR Programmer. It worked flawless, however I do have a couple of reservations about the programmer. I list my thoughts below:

    AVR Programmer Positives:

  1. Plugs directly into the USB port.
  2. Works with all 8-pin AVR chips.
  3. Incorporates breakout pins for prototyping directly from the programmer.
    AVR Programmer Negatives:

  1. Many have reported problems (comments section) loading the USBTinyISP driver.
  2. Solder pads for reset/power options.
  3. Although it can be used as an ISP, requires soldering on a 6-pin header (not furnished).

I feel the solder pads could have easily been changed to jumper pins. The original version of the programmer can be found here.

Here is the avrdude script used to program the ATtiny13:

avrdude -p attiny13 -c usbtiny -U flash:w:attiny13_test.hex

Posted in Uncategorized | Tagged , , , , , | Leave a comment

Mixing 3.3V and 5V Logic [update]

JeeLabs

Updating my post on this subject, here’s a great 3-part series on mixing 3.3V logic with 5V logic at JeeLabs.

Posted in Uncategorized | Leave a comment