|

Contact us
Sponsors
| |
Following is the transcripts from Craig Limbers talk on the ATMEL AVR
chips ( March 13, 2003 )
The AVR is a line of popular 8-bit RISC microcontrollers from Atmel.
The one we will focus on today is the mega8l in the 28pin plastic DIP
package, the type that Quentin arranged for a group purchase of last year. As of
March 12, 2003, Digikey.ca sells them for $8.54 each or $6.78 each if you buy 25
or more.
The "l" part of the name signifies that it is the low power model
and will work from 2.7 - 5.5V power supplies and up to 8 MHz clock speeds. The
other version needs 4.5 - 5.5V but will work up to 16 MHz.
A few highlights of its capabilities:
23 I/O lines
In System Programmable
(i.e cheap and easy for us to program)
1K RAM
8K FLASH PROM
512 bytes EEPROM
2 8 bit counter/timers
1 16 bit counter/timer
three PWM channels (one
8-bit, two 10/8-bit) Great for that omnidirectional robot I want to build
somedayrealsoonnow.
programmable serial
USART
master/slave SPI
interface
programmable watchdog
timer
on chip analog
comparator
6 channel ADC converter
(4 10-bit, 2 8-bit)
built-in RC clock if
you can't be bothered with crystals/resonators
programmable 10K pullup
resistors on input pins
and a whole schwack of
other cool and useful stuff
And, best of all, it is well supported by higher level languages such as C,
C++ and basic.
Here's a pinout:
For the clock speed, you can use either:
built-in RC clock
which will run up to 8MHz (cheapest, least accurate, uses no I/O pins),
attach a ceramic
resonator (needs 2 caps and resonator, more accurate than internal RC clock,
uses 2 I/O pins)
attach a crystal
(same as resonator above but can be even more accurate)
attach an external
oscillator (needs oscillator and no caps, as accurate as crystal, uses only
one I/O pin).
From the factory, it is automagically setup to use it's internal RC
oscillator at 1 MHz. If you want to change the clock speed or where the clock
comes from you can adjust it by setting "fuses" with the programmer
software. It can't be set from within the program you download to the AVR.
The IO pins are lumped into three groups, B, C and D and are labeled Dxn
where X is the group and n is the number. i.e. PB1. You can even use the
!reset pin (pin 1) as I/O but I recommend against that if you are programming
it with ISP.
The cable I use (the schematic is on the AVR resources page and on
Quentin's CD) is the "DAPA" style programmer. I use UISP (free
software) to drive it and I have confirmed it works in both Windows (at least
2000 and XP) and Linux. Don't omit the resistors. Although it says elsewhere
(and I've echoed it) we don't need them if we aren't using those pins for I/O
I put them in anyways as insurance. If I screw up and short across them they
will limit the current and help prevent blowing something up. Replacing a
motherboard to fix the parallel port would be REALLY annoying.
Here is a little circuit board design that plugs into the power strips of
your average breadboard and has a socket for the AVR, a place to connect the
ISP cable (with resistors and everything) and a double row of female headers
to plug 22-24 guage wire into for hooking up your breadboards. This particular
version has no provision for a crystal/ resonator. Follow THIS HERE LINK to go
to my webpage that has a PDF of the artwork, parts list and other stuff you
need to etch and build one.
For programming, I use the GCC compiler (FREE!), the avr-libc (ALSO FREE!)
which contains the functions and stuff specific to the AVR and UISP (mentioned
above) to handle communication between my computer and the AVR. For windows
users check out THIS PAGE for detailed documentation on how to set up the
equivalent on a windows box. Here is an example program:
#include < io.h > // snag stuff we need
int main(void) // always have to have a place to start
{
long x, y; // couple of 32 bit variables*
outp(_BV(DDB1), DDRB); // set PB1 (pin 15) as output
while (1) // do it forever
{
cbi(PORTB, PB1); // set PB1 to "low" (LED ON)
for (x=0; x < 100000; x++) y-; // simple wait loop
sbi(PORTB, PB1); // set PB1 to "HIGH" (LED OFF)
for (x=0; x < 100000; x++) y-; // simple wait loop
}
}
There ya have it, the 'ol flashing LED program. The wait loops decrement y
just to have something to do since some compilers might optimize empty loops
out of existance.
Using a higher level language like this makes our job SOO much easier. For
example, in the program above I used two variable, x and y, of the type long.
That's a 32 bit integer. The mega8l is an 8 bit processor. To do 32 bit
arithmetic in assembler language is a fair amount of work. Here, we can toss
them around at will and not have to worry about the details.
For the curious, here is the same program but disassembled as it appeared
from the avr-objdump program:
avr-objdump -S r.out
r.out: file format elf32-avr
Disassembly of section .text:
00000000 <__vectors>:
0: 12 c0 rjmp .+36 ; 0x26
2: 2b c0 rjmp .+86 ; 0x5a
4: 2a c0 rjmp .+84 ; 0x5a
6: 29 c0 rjmp .+82 ; 0x5a
8: 28 c0 rjmp .+80 ; 0x5a
a: 27 c0 rjmp .+78 ; 0x5a
c: 26 c0 rjmp .+76 ; 0x5a
e: 25 c0 rjmp .+74 ; 0x5a
10: 24 c0 rjmp .+72 ; 0x5a
12: 23 c0 rjmp .+70 ; 0x5a
14: 22 c0 rjmp .+68 ; 0x5a
16: 21 c0 rjmp .+66 ; 0x5a
18: 20 c0 rjmp .+64 ; 0x5a
1a: 1f c0 rjmp .+62 ; 0x5a
1c: 1e c0 rjmp .+60 ; 0x5a
1e: 1d c0 rjmp .+58 ; 0x5a
20: 1c c0 rjmp .+56 ; 0x5a
22: 1b c0 rjmp .+54 ; 0x5a
24: 1a c0 rjmp .+52 ; 0x5a
00000026 <__ctors_end>:
26: 11 24 eor r1, r1
28: 1f be out 0x3f, r1 ; 63
2a: cf e5 ldi r28, 0x5F ; 95
2c: d4 e0 ldi r29, 0x04 ; 4
2e: de bf out 0x3e, r29 ; 62
30: cd bf out 0x3d, r28 ; 61
00000032 <__do_copy_data>:
32: 10 e0 ldi r17, 0x00 ; 0
34: a0 e6 ldi r26, 0x60 ; 96
36: b0 e0 ldi r27, 0x00 ; 0
38: e2 e9 ldi r30, 0x92 ; 146
3a: f0 e0 ldi r31, 0x00 ; 0
3c: 02 c0 rjmp .+4 ; 0x42
0000003e <.do_copy_data_loop>:
3e: 05 90 lpm r0, Z+
40: 0d 92 st X+, r0
00000042 <.do_copy_data_start>:
42: a0 36 cpi r26, 0x60 ; 96
44: b1 07 cpc r27, r17
46: d9 f7 brne .-10 ; 0x3e
00000048 <__do_clear_bss>:
48: 10 e0 ldi r17, 0x00 ; 0
4a: a0 e6 ldi r26, 0x60 ; 96
4c: b0 e0 ldi r27, 0x00 ; 0
4e: 01 c0 rjmp .+2 ; 0x52
00000050 <.do_clear_bss_loop>:
50: 1d 92 st X+, r1
00000052 <.do_clear_bss_start>:
52: a0 36 cpi r26, 0x60 ; 96
54: b1 07 cpc r27, r17
56: e1 f7 brne .-8 ; 0x50
58: 01 c0 rjmp .+2 ; 0x5c
0000005a <__bad_interrupt>:
5a: d2 cf rjmp .-92 ; 0x0
0000005c
:
#include
// snag stuff we need
int main(void) // always have to have a place to start
{
5c: cf e5 ldi r28, 0x5F ; 95
5e: d4 e0 ldi r29, 0x04 ; 4
60: de bf out 0x3e, r29 ; 62
62: cd bf out 0x3d, r28 ; 61
long x, y; // couple of 32 bit variables*
outp(_BV(DDB1), DDRB); // set PB1 (pin 15) as output
64: 82 e0 ldi r24, 0x02 ; 2
66: 87 bb out 0x17, r24 ; 23
while (1) // do it forever
{
cbi(PORTB, PB1); // set PB1 to "low" (LED ON)
68: c1 98 cbi 0x18, 1 ; 24
for (x=0; x < 100000; x++) y--; // simple wait loop
6a: 8f e9 ldi r24, 0x9F ; 159
6c: 96 e8 ldi r25, 0x86 ; 134
6e: a1 e0 ldi r26, 0x01 ; 1
70: b0 e0 ldi r27, 0x00 ; 0
72: 49 97 sbiw r24, 0x19 ; 25
74: a1 09 sbc r26, r1
76: b1 09 sbc r27, r1
78: b7 ff sbrs r27, 7
7a: fb cf rjmp .-10 ; 0x72
sbi(PORTB, PB1); // set PB1 to "HIGH" (LED OFF)
7c: c1 9a sbi 0x18, 1 ; 24
for (x=0; x < 100000; x++) y--; // simple wait loop
7e: 8f e9 ldi r24, 0x9F ; 159
80: 96 e8 ldi r25, 0x86 ; 134
82: a1 e0 ldi r26, 0x01 ; 1
84: b0 e0 ldi r27, 0x00 ; 0
86: 49 97 sbiw r24, 0x19 ; 25
88: a1 09 sbc r26, r1
8a: b1 09 sbc r27, r1
8c: b7 ff sbrs r27, 7
8e: fb cf rjmp .-10 ; 0x86
90: eb cf rjmp .-42 ; 0x68
Here is the source code for my (almost finished) AVR-based switch debouncer
design. As you can see we get to use all of the C stuff that we get to use in
"normal" programs. It's not quite done yet. Needs a little more
comments and the pulse function hasn't been started yet. However, the rest
totally works!
// this here program is to set up an Atmel mega8L microcontroller to run
// 4 debounced switches. It uses nearly every I/O line there is.
// windows users just need to include < io.h >
#include < inttypes.h >
#include < avr/io.h >
#define BOUNCEONTIME 60 // milliseconds after press we figure not bouncing
#define BOUNCEOFFTIME 30 // milliseconds after release we figure not bouncing
// these are the four states the switches can be in
enum {OFF, BOUNCINGON, ON, BOUNCINGOFF};
int8_t switches[] = {OFF, OFF, OFF, OFF}, // start states for the switches
bouncetimes[] = {0,0,0,0}, // time switch been bouncing
toggles[] = {0,0,0,0}; // current switch toggle status
//-------------------------------------------------------------------------
void sendpulse(void)
{
// ain't done yet.
}
//-------------------------------------------------------------------------
int ispressed(int8_t sw)
//
// this here function returns 1 if the switch sw is currently being
// pressed. 0 if not.
//
// this here function returns true if the specified switch is on or not
// DON'T FORGET! The switches are active low with pullups
//
{
switch (sw)
{
case 0: return(!(PINB & _BV(PINB0)));
case 1: return(!(PINB & _BV(PINB7)));
case 2: return(!(PIND & _BV(PIND4)));
case 3: return(!(PIND & _BV(PIND3)));
}
return 0;
}
//-------------------------------------------------------------------------
void toggle(int8_t sw)
//
// this here function changes the state of the toggle output associated
// with the switch
{
if (toggles[sw])
{
toggles[sw] = 0;
switch (sw)
{
case 0: cbi(PORTB, PB3); break;
case 1: cbi(PORTC, PC0); break;
case 2: cbi(PORTC, PC3); break;
case 3: cbi(PORTD, PD2); break;
}
}
else
{
toggles[sw] = 1;
switch (sw)
{
case 0: sbi(PORTB, PB3); break;
case 1: sbi(PORTC, PC0); break;
case 2: sbi(PORTC, PC3); break;
case 3: sbi(PORTD, PD2); break;
}
}
}
//-------------------------------------------------------------------------
void turnon(int8_t sw)
//
// this here function is called when a switch that was considered off
// now has the ON state.
{
toggle(sw); // toggles on swith pressed
switch (sw)
{
case 0:
sbi(PORTB, PB2); // turn on the momentary
cbi(PORTB, PB1); // turn off the !momentary
sendpulse();
break;
case 1:
sbi(PORTC, PC1); // turn on the momentary
cbi(PORTC, PC2); // turn off the !momentary
break;
case 2:
sbi(PORTC, PC4); // turn on the momentary
cbi(PORTC, PC5); // turn off the !momentary
break;
case 3:
sbi(PORTD, PD1); // turn on the momentary
cbi(PORTD, PD0); // turn off the !momentary
break;
}
}
//-------------------------------------------------------------------------
//
// This here function is called when the switch was on and is now
// considered off.
//
void turnoff(int8_t sw)
{
switch (sw)
{
case 0:
cbi(PORTB, PB2); // turn off the momentary
sbi(PORTB, PB1); // turn on the !momentary
break;
case 1:
cbi(PORTC, PC1); // turn off the momentary
sbi(PORTC, PC2); // turn on the !momentary
break;
case 2:
cbi(PORTC, PC4); // turn off the momentary
sbi(PORTC, PC5); // turn on the !momentary
break;
case 3:
cbi(PORTD, PD1); // turn off the momentary
sbi(PORTD, PD0); // turn on the !momentary
break;
}
}
//-------------------------------------------------------------------------
int main (void)
{
uint16_t counter, // number of milliseconds passed for heartbeat
loop; // wait loop variable
uint8_t crap, // something to change for wait loop
sw=0; // the switch number we are dealing with
// configure port outputs
outp((BV(DDB1) | BV(DDB2) | BV(DDB3) | BV(DDB4) | BV(DDB5) | BV(DDB6)),DDRB);
outp((BV(DDC0) | BV(DDC1) | BV(DDC2) | BV(DDC3) | BV(DDC4) | BV(DDC5)), DDRC);
outp((BV(DDD0) | BV(DDD1) | BV(DDD2)), DDRD);
// turn on pull-up resistors and set ports that start high
outp((BV(PB0) | BV(PB1) | BV(PB5) | BV(PB7)), PORTB);
outp((BV(PC2) | BV(PC5)), PORTC);
outp((BV(PD0) | BV(PD3) | BV(PD4) | BV(PD5) | BV(PD6) | BV(PD7)), PORTD);
counter = 0;
while (1)
{
for (loop=0; loop<1000; loop++) // decrement crap for something to do
crap--; // waits one millisecond
// toggle PB6 to show a heartbeat
if (counter == 1000)
sbi(PORTB, PB6);
if (counter == 2000)
{
counter = 0;
cbi(PORTB, PB6);
}
counter++;
for (sw=0; sw <= 3; sw++) // loop through all four switches
{
if (ispressed(sw)) // check if switch pressed
{
switch(switches[sw]) // do something depending on current state
{
case OFF: // switch just pressed, wait to stop bouncing
switches[sw] = BOUNCINGON;
bouncetimes[sw] = 0;
break;
case BOUNCINGON: // still bouncing, check how long
if (bouncetimes[sw] == BOUNCEONTIME) // bouncing finished, now ON
{
switches[sw] = ON;
turnon(sw);
}
else
bouncetimes[sw]++; // still bouncing, keep waiting
break;
case ON:
break; // still holding button, we no care
case BOUNCINGOFF:
//bouncetimes[sw]++;
break;
}
}
else // switch is NOT being pressed
switch (switches[sw])
{
case ON:
switches[sw] = BOUNCINGOFF;
bouncetimes[sw] = 0;
break;
case BOUNCINGOFF:
if (bouncetimes[sw] == BOUNCEOFFTIME)
{
switches[sw] = OFF;
turnoff(sw);
}
else
bouncetimes[sw]++;
break;
case BOUNCINGON:
case OFF:
break;
}
}
}
}
|