Forum: Mikrocontroller und Digitale Elektronik Benötige hilfe! ADC und PWM Programm AVR-GCC Atmega 8


von Jonny B. (raymoe)


Lesenswert?

Hallo,
ich bin gerade dabei ein PWM Programm in C zu schreiben. Dazu benutze 
ich ein Poti am AD-Wandler aber irgendwie funktioniert das ganze nicht 
so wie ich es mir erhoffe. Irgendwo beim ADC habe ich einen Fehler den 
ich nicht finde.
Ich benutze ein Atmega mit externer Referenzspannung, das Poti ist am 
ADC3, der PWM ausgang ist  PB1(OC1A)

Hier ist mal mein Programm wenn jemand eine Lösung findet wär echt cool 
:)




#include <avr/io.h>
unsigned int ADCwert = 0;
unsigned int wert = 0;
void PWM_init(void)
{
  DDRB |= (1<<PB1);

  TCCR1A = (1<<WGM10) | (1<<COM1A1) | (1<<WGM11); //wgm= 10 bit PWM,
  TCCR1B = (1<<CS10) | (1<<CS12) ; //Clockselect bit

}
void PWM_set (unsigned int wert)
{
  OCR1A = wert ; // Vergleicher

  }


void ADC_init(void)
{
  ADMUX |= (0b01000011); //Externe Referenzspannung Kanal auswählen
  ADCSRA = (1<<ADEN)| (1<<ADFR) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADIE) ; 
// ADC Free run, Teilungsfaktor 64, ADC interrupt aktivieren
  ADCSRA |= (1<<ADSC); //ADC start

 }

int ADC_get (void)

{
  for (int i = 0 ; i < 2 ; i++) //schleife wegen "Warmlaufen des ADC"
  ADCwert = ADC; //Wert auslesen

  while (ADCSRA & (1<<ADSC) ) //auf Abschluss der Konvertierung warten


  return ADCwert; // Wert zurückgeben

}

int main(void)
{
  PWM_init();
  ADC_init();
    ADC_get();
  while(1)
    {
    PWM_set(ADC_get); // wert von adc an pwm

    }
}

von Stefan E. (sternst)


Lesenswert?

1) ADC-Interrupt eingeschaltet, aber nicht benutzt (keine ISR).
2) While-Schleife mit fehlenden Semikolon.
3) Warten auf gelöschtes ADSC funktioniert im Free-Running-Modus nicht.

Zusatz: Das "Warmlaufen des ADC" ist Unsinn.

von Marcus P. (marc2100)


Lesenswert?

Hi,
du solltest dich entscheiden was du machen willst.
Zuerst stelltst du den ADC auf Interrupt ein, dann rufst du aber eine 
Funktion auf, die zuerst eine Schleife ausführt, die 1. sinnlos ist und 
2. wahrscheinlich vom Compiler deshalb wegoptimiert wird, und dann den 
ADC per polling abfragen will.

An deiner Stelle würde ich den ADC im Hintergrund, per Interrupt, 
einfach immer den Wert in eine Variabel speichern lassen (volatile nicht 
vergessen!).
Diese kannst du dann einfach für die PWM benutzen, wie du es auch vor 
hattest.

Gruß
Marcus

von Jonny B. (raymoe)


Lesenswert?

Danke für eure Hilfe aber paar fragen hab ich noch :D
Ich bin noch ein Anfänger beim C Programmieren also was genau ist ISR 
und wie benutze ich es und  soll ich jetzt nur noch meine while schleife 
löschen und das "ISR" einfügen?

von Stefan E. (sternst)


Lesenswert?


von Jonny B. (raymoe)


Lesenswert?

Ich habe das Tutorial bereits gesehen aber es ist schwer ohne Hilfe 
durchzuarbeiten. Mein Programm ist ja schon fast fertig kannst du mir da 
nicht schnell weiterhelfen?

von Düsendieb (Gast)


Lesenswert?


von Jonny B. (raymoe)


Lesenswert?

Da ist aber leider kein ADC dabei

von Marcus P. (marc2100)


Lesenswert?

Hi,
also für die ISRs solltest du dir den Artikel mal ansehen:
http://www.mikrocontroller.net/articles/Interrupt
Der ist zwar für UART, aber das prinzip ist überall das gleich.

welche while()-Schleife willste löschen? Wenn es um die in der Funktion 
int ADC_get (void) geht, du kannst eigentlich die komplette Funktion 
löschen.

Zudem solltest du dir auch mal gedanken machen WANN du den 
Vergleichswert der PWM änderst, im moment machst du es nämlich 
permanend, das kann sich ziemlich komisch auf die PWM auswirken.

Dein Programm sollte grob so aussehen.
//Anfang
volatile char adc_wert

int main()
PWM init;
ADC init;

while()-schleife
//hier wird nichts gemacht, da alles per Interrupt gemacht wird ;)
return;
//Main-Ende

ISR (adc)
 adc_wert=ADCW;

ISR (pwm)
 OCR1A = adc_wert;
//Ende

Gruß
Marcus

von Walleby (Gast)


Lesenswert?

Hey,

@Marcus, die ISR für die PWM würde er ja nach seinen gesetzten Registern 
nicht benötigen und könnte in der ISR vom ADC gleich den Vergleichswert 
setzen:

ISR (ADC)
OCR1A = ADCW;

Damit wäre dann auch gleich ein bischen Platz gespart:)

MfG Walleby

von Marcus P. (marc2100)


Lesenswert?

Hi,
das stimmt natürlich, aber ich meinte mal gelesen zu haben, das es 
"besser" wäre den PWM-Zyklus fertig werden zu lassen, und dann erst den 
neuen Wert zu übergeben.

Einfacher ist es natürlich, es direkt in der ISR(ADC) zu machen.

Gruß
Marcus

von Walleby (Gast)


Lesenswert?

Ja du hast recht. Ich hatte mal das selbe Problem beim Ansteuern von 
Brushlessmotoren.
Habs dann durch pollen gelöst.

@Ramon, wenn es nicht unbedingt mit Interrupts gelöst werden muss, 
kannst du dir mal den folgenden Link anschaun. Hab ich selber damals mit 
angefangen^^

http://derjulian.net/mikrocontroller#input

MfG Walleby

von Jonny B. (raymoe)


Lesenswert?

Mein Programm funktioniert immer noch nicht :(
ich kriegs echt nicht hin..

von Jonny B. (raymoe)


Lesenswert?

Kann mir vielleicht jemand mein Programm einfach verbessern und mir 
wieder schicken ich sitze schon so lange davor.. wär echt nett

von Walleby (Gast)


Lesenswert?

#include <avr/io.h>
#include <util/delay.h>//Ka obs die richtige war  wenn nicht dann 
//avr/delay.h

#define F_CPU 8000000UL//Hier dein Takt angeben!

void PWM_init(void)
{
  DDRB |= (1<<PB1);

  TCCR1A = (1<<WGM10) | (1<<COM1A1) | (1<<WGM11); //wgm= 10 bit PWM,
  TCCR1B = (1<<CS10) | (1<<CS12) ; //Clockselect bit

}
void PWM_set (uint16_t wert)
{
  OCR1A = wert ; // Vergleicher

  }

uint16_t adc(uint8_t admux)
{
    ADCSRA  =  (1<<ADEN)  | (1<<ADPS1) | (1<<ADPS0);
    ADMUX   =  admux;
    ADMUX  |=  (1<<REFS1) | (1<<REFS0);
    ADCSRA |=  (1<<ADSC);
    while      (ADCSRA    & (1<<ADSC));
    uint16_t val     = ADCW;
    ADCSRA &= ~(1<<ADEN);
    return val;
}

int main(void)
{
  PWM_init();
  while(1)
    {
    PWM_set(adc(3)); // wert von adc an pwm
_delay_ms(1000);
    }
}


Schnell mal zusammengebastelt. Delay drin um PWM nicht zu stören und 
Prescaler von ADC musst du noch anpassen

MfG Walleby

PS. Keine Garantie dasses läuft^^

von Jonny B. (raymoe)


Lesenswert?

OK danke danke ich probiers mal aus

von Jonny B. (raymoe)


Lesenswert?

Es funktioniert leider immer noch nichts..
Wenn ich unter "int ADC_get" bei return statt "ADCwert" nur "400" 
eingebe müsste er diesen wert ja an die PWM senden, macht er aber nicht 
also habe ich wahrscheinlich bei der PWM noch einen Fehler. Weis da 
vielleicht jemand was ich falsch mache??

von Martin28 (Gast)


Lesenswert?

Habe grade so ein ähnliches Problem...allerdings läuft mein ADC und 
meine PWM schon! Das Problem bei mir sind gerade bei gleichzeitig laufen 
zu lassen...

Sobald Ich es zum laufen gebracht habe, sage Ich dir bescheid. Dann 
können wir das ja mal zusammen durchgehen ;)

Grüße Martin

von Martin28 (Gast)


Lesenswert?

Also:

Schau dir mal meinen Code aus dem Link an.

Beitrag "ADC/Timer läuft nicht zusammen"

Dort solltest du alles finden was du noch brauchen könntest um dein 
Programm laufen zu lassen.

Grüße Martin

von Karl H. (kbuchegg)


Lesenswert?

Ramon B. schrieb:

> also habe ich wahrscheinlich bei der PWM noch einen Fehler.

Kannst du ja leicht ausprobieren.
Lass halt mal den ADC gleich ADC sein und teste nur, ob deine PWM 
funktioniert
1
...
2
3
int main()
4
{
5
  PWM_init();
6
7
  while(1)
8
  {
9
    PWM_set( 400 );
10
  }
11
}

Wenn sich am Pin nichts rührt, dann hast du ein Problem in der PWM oder 
in der Schaltung, die am Pin hängt :-)

Im Allgemeinen ist es keine gute Idee, 2 Subsysteme ungetestet in 
Betrieb zu nehmen. Du weißt dann nie, welches das Problem verursacht.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.