Mega 8 Programming
Home Up Mini Sumo Workshop SAMbot Instrux AVR Resources Mega 8 Programming Working with Materials Molding Tires Gears PCB Design Parts Sources Batteries Logic 101 Library Pneumatics Fusion Reactors

 

 

Home

Contact us

Sponsors

News and Events:
 
 

 

Photo's and Video's of 2006 Event here!

Robot Builders Meetings "RBM"

Each Saturday @ 10:00 am Aerospace Museum

Come on down for a coffee and build!

 

Webmaster

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;       

        }

    }

  }

}