Showing posts with label AT90USB162. Show all posts
Showing posts with label AT90USB162. Show all posts

Saturday, October 23, 2010

AT90USB162 Atmel AVR Interrupts

In order to do most anything useful your micro controller is going to have to interact with the external world. For example instead of just blinking an led you might want to turn on the led when the user takes an action like manipulating the joystick or inserting a memory card. There are two ways you could go about doing this. You can either use a method called polling, meaning you constantly loop and check if some action had happened, but this is in efficient. The better way to achieve this result is through the use of interrupts. An interrupt is like an alarm you set it and then forget about it and it alerts you when some event you were interested in happens. In this example we will use an Olimex AVR-STK-USB board and use an interrupt to turn the on board led on and off when a memory card is inserted into the MMC slot.

To start we need to look at the board schematic and determine how the MMC slot is wired to the micro controller. The schematic shows the MMC slot in the upper right corner. Notice that pins 13 and 15 on the MMC slot are labeled "cp1" and "cp2". "cp", in this case stands for "card presence". Examining the schematic further we see that pin 15 is wired to ground with a resistor and pin 13 is wired to power and is also labeled "cp" indicating it is connected to the micro controller. Looking at the micro controller block in the schematic pin PD1 is also labeled "cp" so we can conclude that this is pin connected to the card presence circuit on the MMC slot.

Judging from the schematic we can conclude that PD1 will be a logic high when there is no card and logic low when a card is inserted. You can test this theory for yourself using a multi meter. Set up your meter to measure DC voltage. Attached the black prob to the ground pin and the red prob to the PD1 pin. Power the board and the meter on. The meter will read 3.3 volts. Now insert a card and watch what happens. With a card inserted your meter should read near zero volts.

Next we need to figure out how the interrupt mechanism of the micro controller works. The relevant documentation is in chapter 12 page 84 of the AT90USB data sheet. The interrupt documentation for the avr-libc library is also helpful reading. Note: PD1 also happens to be the same as INT1 or External Interrupt #1

From these two documents we learn that the interrupt can be programed to trigger in four different ways outlined in table 12-1. We want it to trigger whenever there is any change in the value of PD1 so we will need to set ISC11 to zero and ISC10 to one in the EICRA register. Also the the INT1 bit in the EIMSK register will need to be set to enable external interrupt #1. Putting it altogether this is what the code looks like.

#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

#define MMC_IS_PRESENT() (!(PIND & _BV(PIND1)))
#define LED_ON() (PORTD |= _BV(PD4))
#define LED_OFF() (PORTD &= ~_BV(PD4))

int main(int argc, char **argv) {

/* disable the watch dog timer */
MCUSR &= ~_BV(WDRF);
wdt_disable();

/* setup card presence input */
DDRD &= ~_BV(DDD1);
PORTD &= ~_BV(PD1);

/* setup interrupt */
EICRA |= _BV(ISC10);
EICRA &= ~_BV(ISC11);
EIMSK |= _BV(INT1);

/* setup LED in correct state */
DDRD |= _BV(DDD4);
if (MMC_IS_PRESENT()) { LED_ON(); }
else { LED_OFF(); }

sei(); /* enable interrupts */
while(1) { sleep_mode(); }

return 0;
}

/* Interrupt routine */
ISR(INT1_vect) {
if (MMC_IS_PRESENT()) { LED_ON(); }
else { LED_OFF(); }
return;
}


Full code with makefile is available on Github A good exercise is to try modifying the above code to also turn the led on whenever the joystick is clicked.

Monday, August 3, 2009

Hello World, Getting Started with the Atmel AVR AT90USB

Once you've built your AVR tool chain for Linux you're probably chomping at the bit to write your first program. A student's first assignment on any new computer system is usally to construct the proverbial "Hello, world." program. On a micro controller like the AT90USB162 the academic equivalent is manipulating the on board LED.

Our first step is to figure out how the LEDs on your board are wired to the micro controller. In my case I'm using the Olimex AVR-USB-STK dev board sold by Sparkfun Electronics. The data sheet for the board is available on the Sparkfun site and the applicable information is on page 7. You'll notice this board has one LED and it is connected to the pin labeled "PD4". Now we go an look at the micro controller data sheet from Atmel to see how to drive that pin in software. This board has the AT90USB162 variant of the chip so we need to look at the AT90USB162 version of the data sheet. The section we are most interested in is 11.2.1 discussing configuring the pin as a general purpose digital io port. In this case there are two applicable memory mapped io registers, DDRxn and PORTxn, that control the behavior of the pin (Note: a grouping of 8 io pins is referred to as a "port"). This being an 8-bit micro controller each register is 8 bits wide. The "xn" portion of each registers name refers to "port x, bit n". That is to say there are multiple 8 bit wide io registers and each bit of a given register controls a different physical pin. Further more the direction of the pin, input vs. output, is configured with the DDRxn register and the value can be read / written with the PORTxn register.

The next question is how do we access these PORTxn and DDRxn locations. Luckily AVR libc defines constant for each chip so we don't have to look up all the memory locations in the data sheet. If you look in the AVR libc header files (Note: on my system these files are located in /opt/avr/avr/include/avr) you will see a number of .h files corresponding to various AVR families and individual chips. For the AT90USB162 there is a .h file named iousb163.h but if we look in there we find that it also includes the file iousbxx2.h which contains the majority of the definitions. If we seach for "PORT" within iousbxx2.h we eventually find definitions for PORTA, PORTB, PORTC and PORTD. After each port definition are individual pin definitions. Looking at PORTD you may notice pin 4 is defined as "PD4" which matches the name of the LED's pin on the schematic for our board.

So now we know all the basic information we need to write our program lets put it all together.

#include <avr/io.h>

int main(int argc, char **argv) {

DDRD |= _BV(DDD4); /* set pin as output */
PORTD |= _BV(PD4); /* set pin to 1, high */

while(1);

return 0;
}

The _BV() macro is the equivalent of writing, (1<<PD4). Next we'll want to compile our program and prepare it for uploading to the MCU as follows.

avr-gcc -g -Os -mmcu=at90usb162 light.c -o light.elf
avr-objcopy -j .text -j .data -O ihex light.elf light.hex

If you are using a different MCU than the at90usb162 adjust the -mmcu option accordingly. Also of interest is the -Os option passed to gcc, this tells gcc to optimize of program size. The objcopy step takes the gcc output and packages it in ihex format which is suitable for uploading to the MCU via dfu-programmer. This also strips the debugging information, so, even though we passed the -g option to gcc the debug symbols are not making it into the end executable. This will, however, help you later if you want to attached to the chip with a debugger device.

Now to upload the ihex file to our device. You'll want to plug in your board via USB and put it into program mode by holding the HWB button and pushing RST to reset the board (this applies to most board, boards like the Teensy are different). Next run the following commands.

dfu-programmer at90usb162 flash light.hex
dfu-programmer at90usb162 reset

make sure to change the controller type accordingly if you aren't using the 162. I've seen some instructions that also include the command "dfu-programmer at90usb162 erase" which clears the flash before uploading the new code. In my experience this command always fails quietly (returns 1 instead of 0 w/o an error message) and isn't needed except in certain cases when the code protection bit is set.

Once you issue the above commands the LED on your board should turn on and stay on. Except it doesn't, you'll notice the LED is blinking which isn't what we intended. So what's going on here. It turns out the AT90USB MCUs have a watch dog timer that is enabled by default and automatically resets the chip after a set amount of time if it isn't either disabled or reset at regular intervals. If you look at the AVR libc documentation you'll notice a function, wdt_disable(). Which one assumes should disable the watchdog timer, it doesn't, at least not by itself. On the AT90USB you need to zero the WDRF bit in the MCUSR register before calling wdt_disable() to disable the watchdog timer. So lets try our little program again only this time adding code to disable the watch dog timer.

#include <avr/io.h>
#include <avr/wdt.h>

int main(int argc, char **argv) {

MCUSR &= ~_BV(WDRF);
wdt_disable();

DDRD |= _BV(DDD4);
PORTD |= _BV(PD4);

while(1);

return 0;
}

Compile, upload and reset. Your LED should now turn on and stay on. Congratulations you just wrote your first program for the AT90USB162. The complete code example from this post including a makefile is available in theRandomBit repository on GitHub.