Atmega328: Problem med PWM-period!

C, C++, Pascal, Assembly, Raspberry, Java, Matlab, Python, BASIC, SQL, PHP, etc.
Användarvisningsbild
Castello
Inlägg: 127
Blev medlem: 9 februari 2014, 13:40:08
Ort: Göteborg

Atmega328: Problem med PWM-period!

Inlägg av Castello »

Hej!

Jag ska försöka kontrollera ett servo med en atmega328. Det finns ganska mycket information om detta online, men jag har råkat ut får ett problem som jag inte har hittat lösningen på.
Min systemklocka ligger på 16MHz och jag använder "fast PWM"-tekniken. För att komma i närheten av en PWM-period på 50Hz behöver jag använda en prescaler på 1024. Då fås

f_pwm = f_osc/(1024*256) = 61Hz

Nu till problemet: Jag KAN ställa in prescalern till 1, 8, 64 och 256 (verifierat med oscilloskop), men INTE 1024. Om jag sätter rätt bitar för prescaler = 1024 för jag istället PWM-perioden för prescaler =256, dvs 244 Hz. Oberoende av vad jag hade för prescaler innan. Det är som om det var ett "golv" i PWM-frekvensen. Mycket märkligt.
En lösning för att komma ner i periodtid skulle ju vara att sänka systemklockans frekvens, men det känns som att det borde finnas ett bättre sätt. Som att använda prescalern, till exempel.

Är det någon som har varit med om det här innan? Eller som kan hjälpa mig att hitta felet? Svar mottages tacksamt!

*---------------- KOD -------------------*

// Servo test - read level from ADC and control servo using PWM

#define F_CPU 16000000 // External crystal

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

int dutyCycle = 0;

void setupADC();
void startConversion();

int main(void)
{
DDRD = (1 << PORTD6); // Set port D6 as output

TCCR0A = (1 << COM0A1) | (1 << WGM00) | (1 << WGM01); //Timer/Counter Control A (for timer 0)
TIMSK0 = (1 << TOIE0); // Timer/Counter Interrupt Mask Register: Enable overflow interupt enable

sei();

TCCR0B = 0;
TCCR0B |= (1 << CS00); // Start PWM timer
//TCCR0B |= (1 << CS01);
TCCR0B = (1 << CS02);

setupADC();
startConversion();

while(1)
{

}
}

void setupADC()
{
ADMUX = (1 << REFS0) | (1 << MUX0) | (1 << MUX2) | (1 << ADLAR); // Selecting voltage reference and ADC channel
ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2); //Enabling the ADC and selecting prescaler for conversion rate
DIDR0 = (1 << ADC5D); //Disable digital input buffer
}

void startConversion()
{
ADCSRA |= (1 << ADSC);
}

ISR(TIMER0_OVF_vect)
{
OCR0A = 40 + dutyCycle*0.45; // This works!
}

ISR(ADC_vect)
{
dutyCycle = ADCH;
startConversion();
}


*-------------- KOD SLUT -----------------*
Användarvisningsbild
sodjan
EF Sponsor
Inlägg: 43152
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping
Kontakt:

Re: Atmega328: Problem med PWM-period!

Inlägg av sodjan »

Det blir snyggare om du använder code taggarna... :-)

Kod: Markera allt

[code]
code...
code...
[/code]-
Sen så... Det handlar alltså om vanliga RC-servon med 1.5 ms +/- 0.5 ms pulser?
Det är ganska vanligt att man sätter någon timer och sköter servohanteringen
i en interrupt rutin. De vanliga PWM modulerna är normalt byggda för högre frekvenser,
vilket du ju också har upptäckt. :-)
Användarvisningsbild
Klas-Kenny
Inlägg: 11292
Blev medlem: 17 maj 2010, 19:06:14
Ort: Växjö/Alvesta

Re: Atmega328: Problem med PWM-period!

Inlägg av Klas-Kenny »

Kod: Markera allt

TCCR0B = 0;
TCCR0B |= (1 << CS00); // Start PWM timer
//TCCR0B |= (1 << CS01);
TCCR0B = (1 << CS02);
I sista raden sätter du registret till enbart 1<<CS02 (prescaler 256). Det skulle nog varit |= där.
Användarvisningsbild
jesse
Inlägg: 9233
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Atmega328: Problem med PWM-period!

Inlägg av jesse »

eller
TCCR0B = 0x05;
Användarvisningsbild
Castello
Inlägg: 127
Blev medlem: 9 februari 2014, 13:40:08
Ort: Göteborg

Re: Atmega328: Problem med PWM-period!

Inlägg av Castello »

sodjan: Ja, det stämmer, vanliga RC-servon. Jag försökte använda kod-taggarna men det misslyckades ju helt uppenbart. Ska försöka göra bot och bättring till nästa gång.
Kan du utveckla hur du menar att man ska göra det i en separat isr? Menar du att man uppdaterar duty cycle på sina pwm-utgångar i en separat isr säg, två gånger per sekund? Eller skapar man en "egen PWM" genom att använda "digital out" och sätta pinens state (hög eller låg) med hjälp av timerns isr?

Klas-Kenny, jesse: Ja! Det var ju helt uppenbart nu när man såg det! :doh: Båda sätten funkar. Tack!
Användarvisningsbild
sodjan
EF Sponsor
Inlägg: 43152
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping
Kontakt:

Re: Atmega328: Problem med PWM-period!

Inlägg av sodjan »

> Eller skapar man en "egen PWM" genom att använda "digital out" och sätta pinens state (hög eller låg) med hjälp av timerns isr?

Ja, lite så. Antingen genom en timer tid som motsvarar "upplösning" man vill ha på servot.
Fungerar om man inte behöver finjustera servot allt för mycket. Om det t.ex. räcker med
20 positioner, så fungerar det med en tidbas på 1 ms / 20 = 50 us. Visst, det blir ganska
täta interrupt och man får ha effektiv kod, men det går.

Sen så kan man spela med timer tiden direkt och ställa hela tiden för pulsen där.

Slutligen så tror jag att en del moderna AVR eller PIC processorer även har utvecklade
PWM moduler som fixar lite lägre frekvenser som passar servon bättre. Men de typiska
PWM modulerna i AVR/PIC fungerar inte jättebra för RC-servon, som du har upptäckt.
Gimbal
Inlägg: 7879
Blev medlem: 20 april 2005, 15:43:53

Re: Atmega328: Problem med PWM-period!

Inlägg av Gimbal »

Jasså, vill minnas att det fungerade alldeles utmärkt med de inbyggda timerarna i AVR. Kan dock inte svära på att jag kört servon och 20MHz på en gång. Offrade man 16bitars timern fick man väldigt fin upplösning.
Användarvisningsbild
sodjan
EF Sponsor
Inlägg: 43152
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping
Kontakt:

Re: Atmega328: Problem med PWM-period!

Inlägg av sodjan »

Jag säger inte att det inte går alls, men det krävde lite eftertanke.
PWM modulerna är primärt tänkta för lite högre frekvens på signalen.
Användarvisningsbild
Castello
Inlägg: 127
Blev medlem: 9 februari 2014, 13:40:08
Ort: Göteborg

Re: Atmega328: Problem med PWM-period!

Inlägg av Castello »

Ah, ok! Ja, det kan vara värt att testa. Får hitta på något fiffigt projekt att använda servon i nu bara så att jag får användning för mina nyvunna kunskaper :)
Användarvisningsbild
sodjan
EF Sponsor
Inlägg: 43152
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping
Kontakt:

Re: Atmega328: Problem med PWM-period!

Inlägg av sodjan »

Och som sagt, en hel del beror också på vilken upplösning
men behöver. Det finns fall där servot bara ska styra något
"öppet" eller "stängt" och då behövs ju bara två olika pulser,
1.0 eller 2.0 ms med 20 ms intervall. Om man behöver flera
hundra steg så behövs det en lite annan lösning...
Skriv svar