Forum: Mikrocontroller und Digitale Elektronik Software-PWM eines AVR


von EGSler (Gast)


Lesenswert?

Huhu, ich versuche gerade für ein Arduino-Projekt eine Software-PWM mit 
dem Timer2 des AtMega238 zu programmieren. Ich bin Anfänger, daher habe 
ich mir die Registerkonfiguration so aus dem Internet gesucht (mit 
Datenblatt weitesgehend nachvollzogen) und nur die paar Zeilen drum rum 
geschrieben. Wenn ich die Eingabe über ein Poti am Analogport weglasse 
und den PWM-Wert einfach nur in Zeile 3 vorgebe, dann klappt alles super 
(bei pwm = 0 ist die LED aus, bei pwm=255 leuchtet sie am hellsten).
Mein Problem liegt nun darin, dass ich bei Heilligkeitverstellung über 
ein angeschlossenes Poti (zwischen 0 und 5V, Schleifer am Analogeingang) 
einen sonderbaren Effekt habe: Ich kann die Helligkeit zwar verstellen, 
regel ich sie aber herunter und beende die Drehung, dann dimmt die LED 
wieder sichbar auf. Ich bekomme sie also nicht ganz auf Null.
Also hab ich versucht, mir den aktuellen pwm-Wert über den seriellen 
Monitor ausgeben zu lassen, um der Sache auf den Grund zu gehen. Füge 
ich aber die letzte auskommentierte Zeile in den Code ein, dann arbeitet 
das Programm nicht mehr... Ich habe keine Ausgabe im seriellen Monitor 
und die Helligkeit der LED lässt sich nicht verstellen...
Wenn ich dagegen das Beispielprogramm "Basics - Read Analog Voltage" 
öffne und aufspiele, dann kann ich mir über den seriellen Monitor schön 
eine Spannung zwischen 0 und 5V anzeigen lassen, inklusive der beiden 
Extremwerte.

Bevor jemand auf die Idee kommt, ich könnte ja einfach die Hardware-PWM 
verwenden: Nein, leider nicht. Das ist eine Aufgabe für die Schule und 
da benötige ich 14 über PWM dimmbare Ausgänge^^
1
int led = 11;
2
byte zaehler = 0;
3
int pwm = 100;
4
5
6
void setup() 
7
{                
8
   pinMode(led, OUTPUT);
9
   Serial.begin(9600);
10
      
11
   cli();//stop interrupts
12
  
13
  TCCR2A = 0;// set entire TCCR2A register to 0
14
  TCCR2B = 0;// same for TCCR2B
15
  TCNT2  = 0;//initialize counter value to 0
16
  // set compare match register for 100khz increments
17
  OCR2A = 19;
18
  // turn on CTC mode
19
  TCCR2A |= (1 << WGM21);
20
  // Set CS21 bit for 8 prescaler
21
  TCCR2B |= (1 << CS21);   
22
  // enable timer compare interrupt
23
  TIMSK2 |= (1 << OCIE2A);   
24
  
25
  sei();//allow interrupts
26
}
27
28
  ISR(TIMER2_COMPA_vect)
29
  {
30
    zaehler++;
31
    
32
    if(zaehler<=pwm) digitalWrite(led, HIGH);
33
    if(zaehler>pwm) digitalWrite(led, LOW);
34
  
35
  }
36
37
38
39
void loop() 
40
{
41
  pwm = analogRead(A5/4); 
42
  //Serial.println(pwm); 
43
  
44
}

Viele Grüße

von Markus (Gast)


Lesenswert?

Wenn du es mit dem Arduino-Framework erstellst, ist es keine gute Idee, 
die Timer-Register manuell zu anzupassen. Aausser du weisst ganz genau 
was du machst und welche du verwenden darfst, ohne dass sie mit den 
Arudino-Funktionen kollidieren.
Da verwendest du besser eine der zahlreichen Librarys, z.B.:
http://code.google.com/p/rogue-code/wiki/SoftPWMLibraryDocumentation

Oder du machst alles selber, und ohne das Arduino-Framework. Dann siehst 
du hier ein gutes Beispiel:
http://www.mikrocontroller.net/articles/Soft-PWM

von Marcel (Gast)


Lesenswert?

EGSler schrieb:
> int pwm = 100;

Sollte volatile sein weil ein Zugriff innerhalb und ausserhalb eines 
Interrupts erfolgt.

EGSler schrieb:
> Füge
> ich aber die letzte auskommentierte Zeile in den Code ein, dann arbeitet
> das Programm nicht mehr

Wahrscheinlich weil du deine Zahl nicht in einen String umwandelst und 
diesen dann verschickst. Oder macht das die Arduino-Library automatisch 
für dich?

von Gerhard W. (gerhard86)


Lesenswert?

Marcel schrieb:
> Oder macht das die Arduino-Library automatisch
> für dich?

Ja macht sie. Aber der Code wird so leider extrem unverständlich, wenn 
man sich mit der Arduino Lib nicht auskennt.

Macht  pwm = analogRead(A5/4); denn das richtige, was ist A5? Die 
Funktion erwartet laut Doku die Pinnummer des Analogeinganges. 
Vielleicht liest du einfach nicht den richtigen Pin ein bzw übergibst 
ein ungültiges Argument.

von Karl H. (kbuchegg)


Lesenswert?

Gerhard W. schrieb:
> Marcel schrieb:
>> Oder macht das die Arduino-Library automatisch
>> für dich?
>
> Ja macht sie.


Aber sie liest sicher nicht vom Pin, dessen Nummer sich ergibt, indem 
man die Pinnummer A5 durch 4 dividiert.

Gemeint war wohl eher, dass das Ergebnis vom analogRead durch 4 
dividiert werden soll.
1
   pwm = analogRead(A5) / 4;

: Bearbeitet durch User
von EGSler (Gast)


Lesenswert?

Karl Heinz schrieb:
> Gemeint war wohl eher, dass das Ergebnis vom analogRead durch 4
> dividiert werden soll.   pwm = analogRead(A5) / 4;

Ich danke euch für die Antworten, Karl Heinz tatsächlich Recht. Was ein 
dummer Fehler, natürlich möchte ich nicht den Pin A5/4 einlesen, sondern 
den Pin A5 und diesen Wert dann durch vier teilen (um die Werte des 
10bit ADC auf 8bit zu reduzieren). Mit der Division außerhalb der 
Klammer klappt dann auch allles ;)
Allerdings erklärt das noch nicht, aus welchem Grund der serielle 
Monitor in dem Programm zum Absturz führt. Den brauch ich jetzt zwar 
grad nicht mehr, da der Fehler gefunden wurde, aber es irritiert mich ;/

von DrWright (Gast)


Lesenswert?

Du solltest auch die Variablen pwm und zaehler als volatile 
initialisieren.

von Karl H. (kbuchegg)


Lesenswert?

EGSler schrieb:

> Allerdings erklärt das noch nicht, aus welchem Grund der serielle
> Monitor in dem Programm zum Absturz führt. Den brauch ich jetzt zwar
> grad nicht mehr, da der Fehler gefunden wurde, aber es irritiert mich ;/


Ist das ein e Sodtware-Serielle?

Wenn ja, dann überprüf mal, ob die nicht den Timer 2 benötigt.

von EGSler (Gast)


Lesenswert?

Ah, ich habs. Am Timer2 lag es nicht, aber Serial.println brauchte 
zuviel Rechenzeit und die Interrupts kamen zu häufig. Mit niedriger 
Interruptfrequenz läufts sauber.

Volatile möchte ich eigentlich nicht verwenden, es ist mir nicht so 
wichtig, dass die Werte nun hoch aktuell sind, es läuft ohne volatile 
sauber und ich spare mir die Zeit, in der der µC die Daten zwischen Ram 
und Flash hin und herschiebt.

Ein weiteres Problem durch die häufigen Interrupts kam mit allen 14 
Kanälen parallel noch dazu: digitalWrite brauchte viel zu lange, um den 
Pin zu setzen. Ich habs jetzt durch eine Veroderung wie beim setzen der 
Register gelöst, so läufts wieder flüssig.

von Oliver (Gast)


Lesenswert?

EGSler schrieb:
> Volatile möchte ich eigentlich nicht verwenden, es ist mir nicht so
> wichtig, dass die Werte nun hoch aktuell sind, es läuft ohne volatile
> sauber und ich spare mir die Zeit, in der der µC die Daten zwischen Ram
> und Flash hin und herschiebt.

Na ja, wenn es sauber ohne volatile läuft, dann hast du die Optimierung 
nicht eingeschaltet. Kann man machen, aber dann darfst du dich über zu 
lange Laufzeiten auch nicht beschweren.

Es geht da auch nicht um "nicht ganz aktuell". Wenn du die Optimierung 
einschaltest, wird es ohne volatile überhaupt nicht mehr funktionieren.

Also mach die Variablen volatile, und schalte die Optimierung ein.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Oliver schrieb:

> Na ja, wenn es sauber ohne volatile läuft, dann hast du die Optimierung
> nicht eingeschaltet. Kann man machen, aber dann darfst du dich über zu
> lange Laufzeiten auch nicht beschweren.


Ihm kommt das Arduino Modell zu gute, wonach der Schleifenteil der 
Hauptschleife ausserhalb von loop() liegt.

Dem Compiler bleibt aber sowieso nichts anderes übrig, als alle 
Nebeneffekte, wie zb gecachte Variablen mit Funktionsende zu beenden, so 
dass der nächste Aufruf von loop() wieder bei 0 startet und Variablen 
auf jeden Fall aus dem Speicher nachgeladen werden müssen.

Kann man machen. Solange man sich an das Arduino Modell hält. Sobald man 
aber sich die Hauptschleife selber in loop() reinzieht, kommt es dann zu 
den tollsten Effekten.

von c-hater (Gast)


Lesenswert?

EGSler schrieb:

> Volatile möchte ich eigentlich nicht verwenden, es ist mir nicht so
> wichtig, dass die Werte nun hoch aktuell sind, es läuft ohne volatile
> sauber und ich spare mir die Zeit, in der der µC die Daten zwischen Ram
> und Flash hin und herschiebt.

Oh Herr, laß' Hirn wachsen. Biiitte...

von Gerhard W. (gerhard86)


Lesenswert?

EGSler schrieb:
> Volatile möchte ich eigentlich nicht verwenden, es ist mir nicht so
> wichtig, dass die Werte nun hoch aktuell sind, es läuft ohne volatile
> sauber

Macht nichts. Du wirst ein paarmal auf die Schnauze fliegen weil du, 
sobald dein Programm mehr als 10 Zeilen hat, irgendwelche total 
unerklärlichen Fehler haben wirst die auftauchen oder verschwinden wenn 
du ganz woanders etwas modifizierst. Selbst wenn du die Compileroptionen 
änderst, oder einen anderen Compiler benutzt. Dann wirst du volatile auf 
einmal sehr gern benutzen.

: Bearbeitet durch User
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.