Escolar Documentos
Profissional Documentos
Cultura Documentos
Motivation
Just to get started on the wrong foot: "sometimes Arduino is the wrong choice." Depending on what you're doing, maybe your app's structure isn't very well suited to the Arduino framework. Maybe you need to write smaller code than what would be produced by Arduino, sometimes you just can't run Arduino at all... I'm in that latter category. I'm not such a big fan of Java, and I'm comfortable with a text editor and driving the compiler by hand, so I'm going to do this the hard way. Arduino trowels some nice plaster over top but it's nothing you can't do by hand. Really, all you should need is avr-gcc, avr-libc, avr-binutils and avrdude. Getting these packages is beyond the scope of this document, it's quite likely there are pre-built packages for your OS. This document was written give a basic introduction to some of the specifics of AVR programming, assuming you already have a handle on C. It approximates the order I came to understand things while learning to program the AVR. I'm doing a lot of this "the hard way", there are a number of macros in avr-libc or avrlib to do much of this but it's important to understand the underlying principles. If you understand what's going on, moving your code (from a '168 to a '644P for example) is very easy. 1. A blinking LED - Busy Waits and IO ports 2. Two blinking LEDs - Addressing Pins 3. A switched LED - Digital Input and output 4. Serial Output 5. Serial Input 6. Putting it together: printing button presses and controlling LEDs 7. Analog Output 8. avr-libc goodies 9. Analog Input 10. A blinking LED - Interrupts and timers 11. Persistent Storage 12. Input Capture 13. Watchdog
which makes use of the delay routines defined in <util/delay.h>. If you try run this, you'll find your LED blinking very rapidly. Far more rapidly than you'd like. A brief inspection of delay.h is instructive: with respect to _delay_ms, "The maximal possible delay is 262.14 ms / F_CPU in MHz." This can easily be addressed by computing the maximum time _delay_ms will sleep, and given that, the number of times to call _delay_ms to achieve the desired delay interval. You may wish to
put this computation into a separate function which you can call whenever you need to delay. lesson1.c is one possible solution.
Indeterminate. The pin is said to be floating. Typically, pins are connected to high or low through a relatively high-value resistor, called a pull-up or pulldown. This offers a weak signal to that pin, putting it into a known state. This known state is then selectively overriden by shorting the pin to low or high. Think of it as someone yelling "zero", and someone whispering "one". The very loud zero will overpower the quiet one, but in the absence of other input, you'll hear the one. As to what value of resistor to use, I've heard "it depends", "low enough to get the signal, high enough to not waste power", and "47K". My point in mentioning this is to let you know that something will need to be done to the input signal so the AVR knows it's there. If you're lazy (or wise), you can tell the AVR to use internal pull-ups rather than having to mess with them on your own. Using Port D as an example, let's set up Digital Pin 2 (PD2) as an input with internal pull-up:
DDRD &= 0xFB; /* leave all the other bits alone, just zero bit 2 */ PORTD |= 0x04 /* leave all the other bits alone, just set bit 2 */ value = PORTD & 0x04; /* and that's what's on the pin... */
Try a program to read a switch connected between digital pin 2 and ground, and use this to control an LED. One possible solution is lesson3.c
must recompile if you change your device's clock rate. The datasheet also includes tables listing the magic values. The BRR value is written into a clock generator which clocks the UART. Since the baud rate register is 12 bits wide and the AVR is an 8-bit core, we need to split the write into two halves. According to the datasheet the clock generator is updated when the low byte is written. Thus the baud rate register should be written to high-byte first. Next is framing, most things these days default to "8N1", and that's what we will do here. Section 19.10.4 (UCSR0C) describes the framing controls. To use asynchronous mode, set the top 2 bits to 00. To inhibit parity generation and checking, set the next 2 bits to 00. To use 1 stop bit, set the next bit to 0. To use 8 data bits, set the next 2 bits to 11 (decimal 3). In async mode, the bottom bit should be 0. Altogether, UCSR0C = 0x06; or UCSR0c = (3 << UCSZ00); will program the register correctly. Once the serial port is initialized, you can write to it by writing to UDR0. You should check that the port is actually ready to transmit; the usual way to do this seems to be a busy wait that tests if the USART Data Register is Empty -(UCSR0A & (1 << UDRE0)). Try write a program that prints a fixed string to the serial port. One way to do this is lesson4.c
I haven't written a sample solution to this yet, but at this point you should know everything you need to read a switch or the serial port and use this to control an LED (and print its status to the serial port). When I do finish this example, you can see lesson6.c
Macro
loop_until_bit_is_set(PORTD, _BV(PD2)); PORTD = _BV(PD2); #define BAUD 9600 #include <util/setbaud.h> UBRR0H = UBRRH_VALUE; UBRR0L = UBRRL_VALUE; UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); UCSR0B = _BV(RXEN0) | _BV(TXEN0);
I'm rather fond of stdio and formatted IO. I've been spoiled by big machines with multiple multi-GHz cpus, a few GB of memory and a few TB of storage. Programming an embedded device makes me realize how wonderfulprintf("sensor %d value %f\n", n, values[n]) is. Avr-libc has some glue to simulate stdio. Here's an example of how to use this: lesson8.c. The manual warns that printf and scanf are quite resourceintensive. This is not even a little bit of an exaggeration. Have a look at the annotated assembly files and be afraid. Be very afraid. In my example implementation, I also use the program space macros to avoid keeping constant strings in RAM when they could simply be used straight from flash.
straightforward; Because I'm using a thermistor as my analog device, I added some "fluff" to help visualize the input, but it's not strictly necessary. There are a few little things to do for a simple, polled analog sample. Ensure that the pin you're reading has pull-ups disabled, since these interfere with the sampling. Writing 0 to the pin register (PORTC = 0) or setting it to output mode (DDRC = 0) Set the ADC clock correctly - it needs to be clocked at 50kHz-200kHz. The Arduino's 16MHz clock divided by 128 is a very comfortable 165kHz. Set the reference voltage correctly. This determines the upper limit for measurement; using Vcc is probably the best bet. USB power will probably be very stable. Finally, set the ADC enable bit and force one conversion - the first conversion takes extra time due to some internal setup that is required. Here is lesson9.c.
4. enable interrupt processing Each of these items takes a just single line of code. First, the clock source must be configured. The timer system can accept external inputs or use the system clock - optionally divided down to a slower rate. The Clock Select (CS00-CS02) bits in the timer/counter control register B (TCCR0B) are used. Assuming a 16MHz clock, a prescale of 1024 will cause 15625 increment operations per second. Since the counter can hold 256 unique values, it will overflow at 61Hz. If a higher interrupt rate is needed, the prescaler can be set lower, or a smaller Output Compare (OCR0A) can be used. The timer/counter interrupt mask register defaults to 0 no interrupts will be generated by the timer/counter system. To enable timer overflow interrupts, set the TOIE0 bit of TIMSK0. Timers can be set and reset by storing into them, in this case TCNT0 = 0. Finally, the sei() function enables interrupt processing. One way to do this is lesson10.c. My version can blink either the onboard LED on PORTB.5 or a set of LEDs on PORTB.1 - PORTB.3.
differences when running on this hardware. I recently ported an application I built on the '168 to the '644P. The only changes I needed to make were to the pins with the LEDs and the input capture pin. Most Arduino compatible boards seem to use PORTD.5 for the on-boardblinkenlicht - the Sanguino I'm working with uses PORTB.0. Input capture moves from PORTB.0 to PORTD.6. After some thought I changed all of the program's references to specific ports and pins to preprocessor macros allowing me to use a different set of pins depending on the board I'm running.
ATtiny85-specific
Most of this document should be applicable to any AVR board. I wrote it using a Freeduino SB which has an ATmega168. I'm also running some ATtiny85 parts. This section covers some of the differences when running on this hardware. While porting my firefly simulator to the tiny85, I found the smaller peripheral mix got to be very annoying. In particular, TIMER2 which can wake the AVR from sleep does not exist, nor does the SLEEP_MODE_PWR_SAVE. Power down mode does exist, but it may disable too many other parts of the processor. In this case, I was able to use the watchdog timer to wake from SLEEP_MODE_PWR_DOWN, but the big lesson was to carefully read the datasheets before committing to a particular part.
AT90USB162-specific
Most of this document should be applicable to any AVR board. I wrote it using a Freeduino SB which has an ATmega168. I'm also running a Teensy which is based on the AT90USB162. This section covers some of the differences when running on this hardware.
References
[1] pin_map.html [2] http://www.atmel.com/dyn/resources/prod_documents/avr_3_04.pdf [3] http://www.atmel.com/dyn/resources/prod_documents/doc2545.pdf http://avrbasiccode.wikispaces.com/Atmega168
http://javiervalcarce.es/wiki/Program_Arduino_with_AVR-GCC http://www.ladyada.net/learn/Arduino/ http://ccrma.stanford.edu/courses/250a/docs/avrlib/html/ http://www.nongnu.org/avr-libc/user-manual/ http://www.engbedded.com/cgi-bin/fc.cgi http://piconomic.berlios.de/ http://www.sanguino.cc/ http://www.pjrc.com/teensy/ ... and the rest of atmel's ginormous tech library
NB: older versions of GCC may not support newer mcu types like -mmcu=atmega168 or -mmcu=atmega644p. For some of these examples you may be able to use -mmcu=avr5, but things like the serial port won't work correctly or at all without the correct mcu flag. Arduino-0011 uses GCC 4.0.4, that should be considered the minimum required version. Copyright 2008,2009 Chris Kuethe <chris.kuethe@gmail.com>
$CSK: index.html,v 1.27 2010/01/09 21:54:37 ckuethe Exp $