Getting started with AVR programming

Started by soggybag, March 30, 2009, 04:26:42 PM

Previous topic - Next topic

soggybag

I want to create a project that will use an AVR. So far I have built the USBtinyISP. I have also built a small programming board with an 8 pin DIP socket and a 6 pin header for the USBtinyISP. I'm using the attiny13 I checked the pin out wired the header based on what was in the documentation. Hopefully I've wired these up correctly...

I have also installed the AVRMacPack-20090319. I ran a couple command lines tests on this. I made the demo project from the examples. It created the main.c file and Makefile file in the correct directory, so I assume that it is is installed correctly.

At this point I want to write a program, compile it and download it into a attiny13. So the questions I have are:
1) Where can I find some info on writing simple programs? Flashing an LED reading a value from a pot.
2) How do I compile and upload the program using AVRDUDE?

If anyone can point me to this info on the web or post a short (or long answer if you prefer) I would be most appreciative, thanks!

zyxwyvu

AVR Freaks is a great place to start. Check out the forum, especially the tutorial section.

Another thing that took me a while to figure out: read the datasheets! Almost everything you need to know to program each chip is in there. They're available on Atmel's site, on the product page for each chip. Each controller is unique, and certain registers may be different. The most important parts of the datasheets are the 'register description' sections. They list all the registers relevant to the section, and what each value does.

For example, to read a value from a pot, you need to use the ADC. This means you have to check the datasheet for all the relevant registers - for setting the input pin, clock speed, triggering, etc. I believe there is a tutorial on AVR freaks on the ADC, but you'll need to use the datasheet to check the registers they use.

soggybag

Thanks for the reply. I'm looking at the data sheet right now. Lots of good info there. For me it's just a matter of deciphering it.

I'm trying to make an LFO with the AVR. My thoughts are that I would start by getting an LED to Flash on and off. Then try and add a pot to control the speed. From here try and create some kind of wave form like a triangle.

Here's my sample code. I was able to download this on to the AVR. But it isn't working. I'm using the attiny13v It was pointed out that the attiny13 doesn't have a PORTD! Oops.

/* Name: main.c
* Author: <insert your name here>
* Copyright: <insert your copyright message here>
* License: <insert your license reference here>
*/

#include <avr/io.h>
#include <util/delay.h>
int main(void) {
   DDRD = 1 << 4;    /* make the LED pin an output */
   for(;;) {
      char i;
       for(i = 0; i < 10; i++){
         _delay_ms(30);    /* max is 262.14 ms / F_CPU in MHz */
      }
      PORTD ^= 1 << 4; /* toggle the LED */
   } return 0; /* never reached */
}

The Tone God

#3
If you are using GCC I have a simple LED/Hello world program at my site. From there you start using the datasheet and the complier (and its documentation) to learn A/D and so forth.

I haven't used AVRDUDE but you should have the documentation on you computer if you have installed the AVR tool set.

AVRFreaks is a good place to hangout too.

Edit for new code posting:

1. Do not declare a variable inside the main loop. Put it outside the loop. Probably under the direction register assignment.
2. Tiny13 does not have PORTD, only PORTB.
3. It won't affect functionality but a "While(1)" is more traditional then "for(;;)" for the main loop.
4. It looks like your for loop is trying to create a 300ms loop. Take the loop out and try three 100ms delays in a row and see if that works at first as it is simpler.

Andrew

soggybag

Thanks I'm reading your site right now. This is very informative.

idiot savant

read the documentation for avrgcc. I also found the book from smiley micros to be extremely helpful.

www.smileymicros.com

free quickstart guide:

http://smileymicros.com/Quick%20Start%20Guide%20-%20The%20Next%20Generation.pdf?&MMN_position=106:106


He also does a great series of articles for nuts & volts.

soggybag

Thanks for the reply.

Last night I got an LED flashing by downloading a program to the attiny13, so I'm making progress.

The next step is to add a pot and use it to set the speed of the blinking.

Lurco


soggybag

Here's the program I used to flash an LED. Thanks Andrew, for pointing out that I was using the the wrong port.

#include<avr/io.h>
#include <util/delay.h>

volatile uint8_t i = 0;

int main( void ) {
   DDRB = 0b00001000;
   PORTB = 0b00000000;

   while( 1 ) {
      char i;
      for( i = 0; i < 10; i++ ) {
         _delay_ms( 30 ); /*   max is 262.14 ms / F_CPU in MHz   */
      }
      PORTB ^= 1 << 4; /*   toggle the LED   */
   }
   return 0;
}


I have  few questions at this point.

DDRB = 0b00001000;

Sets the direction of the port and pins. In this case this line seems to set PORTB pin 2 as an output. I'm not quite clear on the syntax of this. How do you set this to configure various pins as inputs or outputs?

The Tone God

First thing first, lets clean up this code.

1. You declared the variable "i" twice and once in the while loop. Both are bad form.

2. There is no point in assigning a value to "i" as the for loop will do that for you.

3. The "0b00000000" format is really not well recognized and may not work in all situations. I know it looks easier as a beginner to read but really these assignments should be done in hex. For now you can get away with it but I recommend learning hex. Your computer's OS calculator should have a conversion mode for this. If not grab a free one off the net somewhere. It will make life easier.

4. Indent the blocks and try to make your brackets line up. It makes the code easier to read which when things get more complicated will help you out when you are scanning code quickly. Good coding habits start now when you are first learning. Later on it is harder to break bad habits.

5. Lots of comments! Especially if you want other people to read your code.

#include <avr/io.h>
#include <util/delay.h>

uint8_t i; // Counter varible

int main( void )
{
DDRB = 0x08; // Set PB4 (Pin 3) as output
PORTB = 0x00;

while( 1 )
{
for( i = 0; i < 10; i++ ) // Delay loop for 300ms
{
_delay_ms( 30 ); // max is 262.14 ms / F_CPU in MHz
}

PORTB ^= 1 << 4; // toggle the LED
}

   return 0;
}


Quote from: soggybag on April 02, 2009, 02:47:36 PM
DDRB = 0b00001000;

Sets the direction of the port and pins. In this case this line seems to set PORTB pin 2 as an output. I'm not quite clear on the syntax of this. How do you set this to configure various pins as inputs or outputs?

That line actually sets PB4 (physical pin 3) as an output. If you want physical pin 2 you want PB3 so that is:

0b00000100;

or better yet in hex

0x04;

You will need to change the toggle line too to:

PORTB ^= 1 << 3;

or you can make life easier and do this:

PORTB ^= 1 << PB3;

Did you read the "Digital Input and Output - Pivotal Digital" article at my site ? I cover this in that article. Also the datasheet explains alot too.

BTW if you are using a newer version of GCC the "_delay_ms" no longer has the 262 restriction. You can drop 300 in and lose the loop altogether. Check the documentation that came with your GCC version. Its on your computer as it comes as part of the GCC installation.

Andrew

idiot savant

#10
Quote from: soggybag on April 02, 2009, 02:47:36 PM

 
I have  few questions at this point.

DDRB = 0b00001000;

Sets the direction of the port and pins. In this case this line seems to set PORTB pin 2 as an output. I'm not quite clear on the syntax of this. How do you set this to configure various pins as inputs or outputs?

the data direction register bits all have names that you can use to make your code more readable
i.e. bit 2 on DDRB is labelled 'DDB2'

so you could:


DDRB |= (1<<DDB2);


bit manipulation 101 is required reading here:

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=37871





soggybag

Thanks Tone God! This is the information I was looking for. I missed that article "Digital Input and Output - Pivotal Digital", For some reason I thought is was about something else.

soggybag

With Andrew's help I simplified my program to:
#include <avr/io.h>
#include <util/delay.h>

uint8_t i; // Counter varible

int main( void ) {
DDRB = 0x08; // Set PB4 (Pin 3) as output
PORTB = 0x00;

while( 1 ) {
_delay_ms( 300 ); // delay 300ms
PORTB ^= 1 << 4; // toggle the LED
}
return 0;
}


It appears that _delay_ms() does support numbers larger than 262.

Next step reading a pot to set the delay time...

idiot savant

#13
I'll follow this thread, I've got some tiny13's to play with.


Quote from: soggybag on April 03, 2009, 12:23:17 PM
With Andrew's help I simplified my program to:
#include <avr/io.h>
#include <util/delay.h>

uint8_t i; // Counter varible

int main( void ) {
DDRB = 0x08; // Set PB4 (Pin 3) as output
PORTB = 0x00;

while( 1 ) {
_delay_ms( 300 ); // delay 300ms
PORTB ^= 1 << 4; // toggle the LED
}
return 0;
}


It appears that _delay_ms() does support numbers larger than 262.

Next step reading a pot to set the delay time...


you still dont need that 'i' variable in there.

here's some bits to help you setup the ADC, this example is left adjusted for an 8-bit(0-255) measurement stored to ADCH



#include <avr/io.h>
#include <util/delay.h>


/*

blinky for the attiny13

     ___ ___
PB5=|   V   |=VCC
PB3=|     |=PB2
PB4=|     |=PB1
GND=|_______|=PB0


ISP---MISO-PB1
  MOSI-PB0
  SCK-PB2
  RESET-PB5


LED on PB4
ADC on PB3


use PB0 or PB1 for PWM output


will work on other attiny's

*/

int main( void )
{
/*
port setup

*/

DDRB |= (1<<DDB4); //PORTB4 output pin
PORTB = 0x00; //clear PORTB

/*
ADC setup - free running mode
    1MHz clock / 8 = 125KHz sample rate
*/

ADCSRA |= (1<<ADPS0) | (1<<ADPS1); //ADC prescaler to 8
ADMUX |= (1<<ADLAR) | (1<<MUX0) | (1<<MUX1); //left adjust for 8-bit result in ADCH, PORTB3 ADC input
ADCSRA |= (1<<ADEN) | (1<<ADSC); //enable, start converting


// do something with ADC



while(1)
{

_delay_ms(300); // delay 300ms
PORTB ^= (1<<PORTB4); // toggle the LED
}

return 1;
}

soggybag

Thanks for the reply. I'll give this a try tonight. I have some similar code I was working on. But I had trouble figuring out how to set the pin number for the ADC.

idiot savant

Quote from: soggybag on April 04, 2009, 11:01:35 AM
Thanks for the reply. I'll give this a try tonight. I have some similar code I was working on. But I had trouble figuring out how to set the pin number for the ADC.

selecting the pins for ADC input is done by setting the MUX0 and MUX1 bits in the ADMUX register.

soggybag

Thanks, great reply. I'm still confused on how to read the value in the ADC correctly? Or maybe the question is really how to use the value correctly.

I want to use the value as the delay time passed to the _delay_ms() method.

Here's the code I have been using below. If I use _delay_ms( 300 ), it compiles without error and the program works, the light flashes on and off.

If I use _delay_ms( val ), the program doesn't compile correctly. I get an error:
avrdude: ERROR: address 0x0410 out of range at line 65 of main.hex
avrdude: write to file 'main.hex' failed

/*

blinky for the attiny13

     ___ ___
PB5=|   V   |=VCC
PB3=|     |=PB2
PB4=|     |=PB1
GND=|_______|=PB0


ISP---MISO-PB1
  MOSI-PB0
  SCK-PB2
  RESET-PB5

LED on PB4
ADC on PB3

use PB0 or PB1 for PWM output
will work on other attiny's

*/

#include <avr/io.h>
#include <util/delay.h>

int ReadADC(); // Function to read ADC
int val = 0; // Holds value from ADC

int main( void ) {
/* port setup */

DDRB |= ( 1 << DDB4 ); //PORTB4 output pin
PORTB = 0x00; //clear PORTB

/*
ADC setup - free running mode
1MHz clock / 8 = 125KHz sample rate
*/

ADCSRA |= ( 1 << ADPS0 ) | ( 1 << ADPS1 ); //ADC prescaler to 8
ADMUX  |= ( 1 << ADLAR ) | ( 1 << MUX0 ) | ( 1 << MUX1 ); //left adjust for 8-bit result in ADCH, PORTB3 ADC input
ADMUX  |= ( 0 << REFS0 ); // VCC as Reference 
ADCSRA |= ( 1 << ADEN  ) | ( 1 << ADSC ); //enable, start converting

// do something with ADC ( ADCH? )

while( 1 ) {
val = ReadADC();
_delay_ms( 300 ); // delay 300ms
PORTB ^= ( 1 << PORTB4 ); // toggle the LED
}
return 1;
}

int ReadADC() {
     ADMUX |= (0 << REFS0); // VCC as Reference
     ADMUX = 2;// ADC2 PB.4
     ADCSRA |= (1 << ADSC); // Start Converstion

     while( ( ADCSRA & 0x40 ) != 0 ) { }; // wait for conv complete

     return ADC;
}

soggybag

Another small success! I was able to read the value of the pot with the following code. Here I have a B10K pot with the center pin connected to pin 3 on the AVR and an LED connected to pin 7.

#include <avr/io.h>
#include <util/delay.h>

#define F_CPU 9600000               // Define software reference clock for delay duration
#define LED PB2                     // Define led ext output pin on PB2

int i;                              // 8 bits integer


int main( void ) {

    DDRB   |= ( 1 << LED );             // Set output direction on LED
    ADCSRA |= ( 1 << ADEN  ) |          // Analog-Digital enable bit
              ( 1 << ADPS1 ) |          // set prescaler to 8    (clock / 8)
              ( 1 << ADPS0 );           // set prescaler to 8    (clock / 8)

    ADMUX |=  ( 1 << ADLAR ) |          // AD result store in (more significant bit in ADCH)
              ( 1 << MUX1  );           // Choose AD input AD2 (BP 4)

    while (1) {

    ADCSRA |= ( 1 << ADEN );          // Analog-Digital enable bit
    ADCSRA |= ( 1 << ADSC );          // Discarte first conversion

        while ( ADCSRA & ( 1 << ADSC ) ); // wait until conversion is done

    ADCSRA |= ( 1 << ADSC );          // start single conversion

        while ( ADCSRA & ( 1 << ADSC ) )  // wait until conversion is done

    ADCSRA &= ~( 1 << ADEN );         // shut down the ADC

//----------Set rate of blinking based on ADCH byte---------

    for ( i = 0 ; i < ADCH ; i++ ) { // Loop x time until i reach ADCH value
    _delay_ms( 10 );                   // Loop delay
}

PORTB ^= ( 1 << LED );                  // Inverte led bit and show it
}
return 0;
}


Next step, random numbers...

The Tone God

Little busy to look over code right now but I do recommend you put a resistor in series with the pot wiper feeding to the Tiny13's pin. Something like a 1K. At the Tiny13's pin add a cap like a 0.1uF, just like the one you are decoupling the Tiny13's power supply with right ? ;)

Andrew

idiot savant

Quote from: soggybag on April 04, 2009, 05:46:55 PM
Thanks, great reply. I'm still confused on how to read the value in the ADC correctly? Or maybe the question is really how to use the value correctly.

I want to use the value as the delay time passed to the _delay_ms() method.

Here's the code I have been using below. If I use _delay_ms( 300 ), it compiles without error and the program works, the light flashes on and off.

If I use _delay_ms( val ), the program doesn't compile correctly. I get an error:
avrdude: ERROR: address 0x0410 out of range at line 65 of main.hex
avrdude: write to file 'main.hex' failed



sorry, I should've been more specific about the ADC setup.

single conversion mode is nice when you need to switch and poll multiple channels. my previous example uses free-running mode.

for this application both do the same thing, free-running is less setup. in fact you could paste that for loop you wrote into my code and it works fine.


#include <avr/io.h>
#include <util/delay.h>


/*

blinky for the attiny13

     ___ ___
PB5=|   V   |=VCC
PB3=|     |=PB2
PB4=|     |=PB1
GND=|_______|=PB0


ISP---MISO-PB1
  MOSI-PB0
  SCK-PB2
  RESET-PB5


LED on PB4
ADC on PB3


use PB0 or PB1 for PWM output


will work on other attiny's

*/

int main( void )
{
/*
port setup

*/

DDRB |= (1<<DDB4); //PORTB4 output pin
PORTB = 0x00; //clear PORTB

/*
ADC setup - free running mode
    1MHz clock / 8 = 125KHz sample rate
*/

ADCSRA |= (1<<ADPS0) | (1<<ADPS1); //ADC prescaler to 8
ADMUX |= (1<<ADLAR) | (1<<MUX0) | (1<<MUX1); //left adjust for 8-bit result in ADCH, PORTB3 ADC input
ADCSRA |= (1<<ADEN) | (1<<ADSC); //enable, start converting




while(1)
{

uint8_t i;

for (i = 0; i < ADCH; i++); // Loop x time until i reach ADCH value
{
    _delay_ms(10); // Loop delay
}


PORTB ^= (1<<PORTB4); // Invert led bit and show it

}

return 1;
}