![]() |
There are times when a program just needs to give up and ask for human assistance. Suppose a sensor or motor becomes disconnected or the battery is dying or perhaps there is a bug in the code which leads down a dark and dangerous code alley. Below is a short piece of code that provides an error routine which will halt the chip and blink an LED, in this case pin PORTB5 (Arduino pin 13), with up to 14 error codes. Ideally, the function can be called if the program determines it's in a unrecoverable state and can provide a code to tell the programmer where it failed. |
The function, void error(char code), which halts execution on the CPU, disables its peripherals and blinks an LED in a certain 4-bit code. Halting the CPU is a trivial task and can be accomplished by disabling interrupts and starting an infinite loop. With no way to escape the loop and no way to be interrupted, the CPU should never escape. Incidentally, a state such as this is affectionately named a roach motel by my discrete math teacher. In any case, we must also disable the peripherals because they could be generating waveforms regardless of what code is executing. Fortunately, the AVR has some power saving features, using the register PRR, and we can selectively shut down peripherals.
Another feature this code provides is calling the error function when an interrupt without a defined handler is called. This is generally a bad thing and indicates an interrupt was enabled by accident.
Reading the code from the LED blinking is fairly easy for what it is. A 1-second (really-long) blink indicates the code is about to start. The code is then a series of short, 250mS, or long, 500mS, bursts, representing 0 and 1 respectively. Just remember, the longer blink is a 1. To make things easier, the codes 0 and 15 shouldn't be used because they have no variation, although I guess one could consider them to be the same value.
#include <inttypes.h> #include <avr/io.h> #include <avr/pgmspace.h> #include <avr/eeprom.h> #include <avr/interrupt.h> #include <stdio.h> #include <util/delay.h> #include <avr/interrupt.h> #include "error.h" ISR(BADISR_vect) { cli(); error(1); } #define ERR_ON PORTB |= _BV(5) #define ERR_OFF PORTB &= ~_BV(5) void error(char val) { int c = 0; cli(); // add code as needed to actively shut something down (eg. serial motor controllers) // shutting down all peripherals PRR = 0xFF; //disable potentially conflicting peripherals UCSR0B &= ~_BV(RXEN0) & ~_BV(TXEN0); // USART SPI mode UCSR0C &= ~_BV(UMSEL01) & ~_BV(UMSEL00); // asynchronous USART mode means no clock, right? // initialize LED ( B5 ) DDRB |= _BV(5); while(1) { // error header ERR_ON; _delay_ms(1000); ERR_OFF; _delay_ms(1000); for ( c = 3; c >= 0; c-- ) { ERR_ON; if ( val & (1<<c) ) { _delay_ms(500); } else { _delay_ms(250); } ERR_OFF; _delay_ms(250); } _delay_ms(750); } }