Getting started with AVR programming

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

Previous topic - Next topic

soggybag

Thanks that helps explain what is going with the ADC.

I noticed that I was trying to use ADCH as the delay time with _delay_ms(). But I kept getting a compile error. Looking at the docs for GCC I found that _delay_ms( time ) expects time as a double. ADCH is another type, maybe uint8? How can you convert from one type to another? or is there a better method to handle this type of situation?

My next task is to attempt to generate a random number. The chip doesn't seem to have a method for this. But I suspect there might be something in the GCC library. If not there must a standard system to generate random values. 

The Tone God

Quote from: soggybag on April 04, 2009, 09:01:56 PM
I noticed that I was trying to use ADCH as the delay time with _delay_ms(). But I kept getting a compile error. Looking at the docs for GCC I found that _delay_ms( time ) expects time as a double. ADCH is another type, maybe uint8? How can you convert from one type to another? or is there a better method to handle this type of situation?

The problem is that _delay_ms() wants a constant. It will not take a variable. There are several ways to create variable length delays including the loop you were doing. A better option is using the timer so the Tiny13 can be busy doing other things.

Quote from: soggybag on April 04, 2009, 09:01:56 PMMy next task is to attempt to generate a random number. The chip doesn't seem to have a method for this. But I suspect there might be something in the GCC library. If not there must a standard system to generate random values.

There is a random function but you have to "seed" the expression with a figure from somewhere. The function eats up alot of space. There are lots of other methods to generate pseudo-random and true random depending on you needs.

Andrew

soggybag

That's some interesting info. I have a lot of experience programming for the internet. This Microcontroller thing puts a whole new twist on it. The idea that you can't pass a variable to function is something that I would not expect.

I'm not too worried about coming up with the most optimized code at this point. If I can just get some basic thing working I'll be happy. I'm trying to keep my goals realistic. That said I'm very curious about this counter thing. I'll have to look it up...

The Tone God

Quote from: soggybag on April 05, 2009, 12:03:24 AM
That's some interesting info. I have a lot of experience programming for the internet. This Microcontroller thing puts a whole new twist on it. The idea that you can't pass a variable to function is something that I would not expect.

There was some recent talk about that issue. I believe the issue is that the delay loops are worked out at compile time so it can't take a variable. There are still basic delay loops provided by GCC or you can loop the fixed delay loop like you were before or you could use a timer.

soggybag

I found a tutorial on Timers. I tried modifying the code to work with attiny13. But it is not working. I must have missed something. This code should blink the LED without the use of the loop and delay, by instead using a timer. It seems to compile without error, but does nothing.

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

volatile uint8_t count;

int main() {
   // Prescaler = FCPU / 1024
   TCCR0B |= ( 1 << CS02 ) | ( CS01 );

   //Enable Overflow Interrupt Enable
   TIMSK0 |= ( 1 << TOIE0 );

   //Initialize Counter
   TCNT0 = 0;

   //Initialize our varriable
   count = 0;

   //Port B[3,2,1,0] as out put
   DDRB |= 0x0F;

   //Enable Global Interrupts
   sei();

   //Infinite loop
   while( 1 );
}

ISR( TIMER0_OVF_vect ) {
   //This is the interrupt service routine for TIMER0 OVERFLOW Interrupt.
   //CPU automatically call this when TIMER0 overflows.

   //Increment our variable
   count++;
   if ( count == 61 ) {
      PORTB = ~PORTB; //Invert the Value of PORTB
      count = 0;
   }
}

idiot savant

haven't had much time to code this weekend.

you probably want to use CTC mode rather than an interrupt. Doing this you can set your timer compare match to ADCH where the ADC value is returned.

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

check out part 4, CTC mode.

soggybag

Here's what I came up with for PWM. The goal of this example is to make an LED increase and decrease in brightness. It looks correct, to me and my limited knowledge, but doesn't work for some reason. I'm missing something.

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

int main() {
unit8_t brightness = 0;

TCCR0A = 0x81; // Timer/Counter Control Register A
TCCR0B = 0x01; // Timer/Counter Control Register B
TCNT0  = 0x00; // Timer/Counter Register
OCR0A  = 0x12; // Output Compare Register A
OCR0B  = 0x00; // Output Compare Register B

DDRB   = 0x08; // Set PB4 (Pin 3) as output

while( 1 ) {
// Increase brightness from 0 to 255
for ( brightness = 0; brightness < 255; brightness++ ) {
OCR0A = brightness; // Set PWM duty based on brightness value
_delay_ms( 10 ); // Delay 10 ms
}

// Decrease brightness from 255 to 0
for ( brightness = 255; brightness > 0; brightness-- ) {
OCR0A = brightness; // Set PWM duty based on brightness value
_delay_ms( 10 ); // Delay 10 ms
}
}
return 1;
}

idiot savant

#27
Quote from: soggybag on April 07, 2009, 01:08:21 AM
Here's what I came up with for PWM. The goal of this example is to make an LED increase and decrease in brightness. It looks correct, to me and my limited knowledge, but doesn't work for some reason. I'm missing something.


had a very quick look through.

typo in "uint8_t"

PWM tables:



maybe I'm not seeing everything so double-check your COM0A0, and COM0A1 bits, and WGM bits, and prescaling bits. try mode 5 with WGM02 set. 


soggybag

Thanks, I think I'm really close to getting this working. I just need to get the Timer/Counter registers setup with the correct settings.

soggybag

Thanks for the all of the help. I'm trying to set up the PWM mode 5 as per your suggestion. Here's what I have so far. I'm still missing something because this doesn't work yet. But I think I'm pretty close.

I think this sets up TCCR0A and TCCR0B correctly for Mode 5.
TCCR0A |= ( 1 << COM0B1 ) | ( 0 << COM0B0 ) | ( 1 << WGM01 ) |( 1 << WGM00 );
TCCR0B |= ( 0 << CS02 ) | ( 0 << CS01 ) | ( 1 << CS00 ) | ( 1 << WGM02 );

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

int main() {
uint8_t brightness = 0;

TCCR0A |= ( 1 << COM0B1 ) | ( 0 << COM0B0 ) | ( 1 << WGM01 ) |( 1 << WGM00 );
    TCCR0B |= ( 0 << CS02 ) | ( 0 << CS01 ) | ( 1 << CS00 ) | ( 1 << WGM02 );
TCNT0  = 0x00; // Timer/Counter Register -
OCR0A  = 0x12; // Output Compare Register A
OCR0B  = 0x00; // Output Compare Register B

// DDRB |= ( 1 << PB0 ); // Is this same as the line below?
DDRB   = 0x01; // Set PB0 (Pin 5) as output

while( 1 ) {
// Increase brightness from 0 to 255
for ( brightness = 0; brightness < 255; brightness++ ) {
OCR0A = brightness; // Set PWM duty based on brightness value
_delay_ms( 10 ); // Delay 10 ms
}

// Decrease brightness from 255 to 0
for ( brightness = 255; brightness > 0; brightness-- ) {
OCR0A = brightness; // Set PWM duty based on brightness value
_delay_ms( 10 ); // Delay 10 ms
}
}
return 1;
}

idiot savant

You are very close. think of it this way....

Timer/Counter0 has 2 compare registers, OCR0A, and OCR0B.

OCR0A corresponds to PORTB0 or pin 5 on the chip
OCR0B corresponds to PORTB1 or pin 6.

So, if we decide we want some PWM action goin on, we gotta use one of those pins...

From the table we can see that Phase-correct PWM has 2 modes.
Mode 1 or mode 5 for phase-correct PWM.
Mode 3 or mode 7 for Fast PWM.

to set mode 1, we:

TCCR0A |= (1<<WGM00));

to use mode 5, we add:

TCCR0B |= (1<<WGM02);

you have your prescaling off so you would:

TCCR0B |=(1<<CS00);


Now then... We see that the COM0A0, and COM0A1 bits in TCCR0A set the behavior of the OCR0A(the COM0B0, COM0B1 control OCR0B) 

register(our PWM pin).

look at the table, for this application we would want OCR0A cleared at the top of the count and set on the downcount...

...as we change OCR0A this varies the duty-cycleof our PWM.

then we:

TCCR0A |= (1<<COM0A1);


put it all together you have:


TCCR0A |= (1<<COM0A1) | (1<<WGM00); //0x81; // PWM Phase correct mode 1
TCCR0B |= (1<<CS00);//0x01; // --no prescale



The rest of your code is pretty much OK.

try this:



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

#define F_CPU 9600000UL


int main(void)
{

TCCR0A |= (1<<COM0A1) | (1<<WGM00); // PWM Phase correct mode 1
TCCR0B |= (1<<CS00);// --no prescale
TCNT0  = 0x00; // Timer/Counter Register
OCR0A  = 0x12; // Output Compare Register A


DDRB   |= (1<<PORTB0); // Set PB0 (Pin 5) as output



uint8_t brightness = 0x00;

while( 1 )
{
// Increase brightness from 0 to 255
for ( brightness = 0; brightness < 255; brightness++ )
{
OCR0A = brightness; // Set PWM duty based on brightness value
_delay_ms( 10 ); // Delay 10 ms
}

// Decrease brightness from 255 to 0
for ( brightness = 255; brightness > 0; brightness-- )
{
OCR0A = brightness; // Set PWM duty based on brightness value
_delay_ms( 10 ); // Delay 10 ms
}
}
return 1;
}



Sorry, I sorta misguided you before. I neglected to mention, that if you use mode 5 PWM, you have to use OC0B as the output since
OC0A becomes the TOP to the Counter. My bad, as usual, I overcomplicate things. :icon_redface:

I did verify this code in hardware, so it works!!! now just get an ADC channel to set the compare value, and you have a spiffy little PWM
LFO to use in stuff!!!


-Morgan


soggybag

Thanks man! That was exactly it. This worked great.

soggybag

Success at last! Here's the program to create a Sample and Hold with the AVR attiny 13. Thanks to everyone who provide me with such great help in this thread.

This probably still needs a little work. But it seems to be working pretty well so far.

/*
* Sample and Hold Demo. Flashes an LED at a random brightness at a rate set by a pot
* LED PB0 connected to pin 5
* Pot AD2 (PB4) connected to pin 3
*
*/

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

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

int i;                              // 8 bits integer

int main( void ) {

// ******************************************************************
// *** Set up PWM ***
TCCR0A |= (1<<COM0A1) | (1<<WGM00); // PWM Phase correct mode 1
TCCR0B |= (1<<CS00);// --no prescale
TCNT0  = 0x00; // Timer/Counter Register
OCR0A  = 0x12; // Output Compare Register A

DDRB   |= (1<<PORTB0); // Set PB0 (Pin 5) as output

uint8_t brightness = 0x00;
// ******************************************************************

    // *******************************************************************
    // *** Set up ADC ***
    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 (PB4) pin 3
    // *******************************************************************

    while ( 1 ) { // Main program

    ADCSRA |= ( 1 << ADEN );          // Analog-Digital enable bit
    ADCSRA |= ( 1 << ADSC );          // Discard 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
}
brightness = (uint8_t) rand(); // Need a random number from 0 - 255
OCR0A = brightness;
}
return 0;
}

idiot savant

very cool, I hacked in some additional functionality. I'll try to post it later.

soggybag

Right on, I can't wait to see it, I'm sure I will learn something new.

I had tried making the Maestro FSH-1 filter sample and hold a while ago and had bad luck with clock noise. I also tried to make a 9v version that also had trouble with clock noise. Then I had the idea to do it a uC. Which I think will solve this clock problem, and cut down on the number of parts.

At the moment I'm working on making an actual pedal. I drew up a layout last night. Give me a few more days. I'll post some pictures when it's finished.