heute möchte ich Euch zeigen, wie man Pulsweitenmodulation (PWM) erzeugt. PWM? Das ist ein Rechtecksignal mit fester Frequenz, bei dem die Ein- und Auszeiten variert werden können. Damit kann man dann z.B. eine LED in der Helligkeit steuern. Wird die Ein-Zeit länger gemacht, bekommt die LED länger Strom und leuchtet länger. Da die Frequenz aber so schnell ist, kommt unser Auge da gar nicht mit und erkennt nur eine LED, die eben in der Helligkeit variiert.
Wir haben 2 Möglichkeiten diese PWM zu erzeugen. Als erstes machen wir dies, in dem wir die Hardware-PWM unseres Atmega nutzen. Wir können die Timer auch so konfigurieren, daß sie an vorgegebenen Pins ein PWM-Signal ausgeben. Der Atmega8 hat 3 Ausgänge für Hardware PWM: Timer1 gibt sie auf den Pins PB1 und PB2 aus, Timer2 belegt PB3. Seht euch doch bitte im Datenblatt die "Pin configurations" auf Seite 2 an. Dort sind diese Pins so bezeichnet: OC1A, OC1B und OC2. Also Timer1 hat 2 PWM-Kanäle, Timer2 hat einen und Timer0 kann gar keine Hardware-PWM.
Config Timer1 = PWM: hier sagen wir dem Timer1 daß er im PWM-Modus arbeiten soll PWM = 10: hier wird bestiimt, wie fein abgestuft die PWM sein soll. Hier können 8-Bit, also 256 Stufen, 9-Bit = 512 Stufen und 10 Bit = 1024 Stufen konfiguriert werden. Compare A Pwm = Clear Down: ääähhhh ja. Seht euch mal schnell auf Seite 111 des Datenblattes die Grafik an. Der Timer ist ja ein Zähler. Er zählt bei unserer 10-Bit-PWM von 0 bis 1023 und von 1023 wieder zurück auf 0. Also diese Zickzack-Linie "TCNTn" im Bild. Der jeweilige Zählerstand wird fortlaufend mit einem Vergleichswert, den wir vorgeben, verglichen. In der Grafik ist der Vergleichswert bei den ersten 2 Zacken auf maximal, also Wert 1023, danach in der Mitte und zum Schluß bei ca.80%. Wir sehen uns die mittleren beiden Punkte an und erkennen, daß wenn der Timer runterzählt und mit dem Vergleichswert gleich ist, wird der Ausgang auf High gesetzt. Beim nächsten Treffer ist der Zähler beim raufzählen und der Ausgang wird Low. Dieser Modus nennt sich in Bascom "Compare A Pwm = Clear Up". Eine invertierte PWM erhält man bei "Clear Down", und genau brauchen wir wegen der invertierten Logic der LED (0=Ein, 1=Aus) Achtung: seit Bascom Version 2.0.7.1 ist diese Einstellung wie beschrieben, bei älteren Versionen war Clear Up/Down getauscht.
Prescale = 64: wie schon bei der normalen Timer-Konfiguration, können wir auch hier einen Prescaler benutzen. Ich rechne wieder: 8 MHz Systemtakt, 256 Prescale und 1024 PWM Auflösung. Das Ergebnis hiervon müssen wir nochmals halbieren: wer die Grafik aufmerksam anschaut, bemerkt daß der Timer von 0 - 1023 und wieder zurück zählen muß, damit ein PWM-Signal entsteht: 8000000 / (64 * 1024 * 2) = 61Hz
Do 'Beginn Hauptschleife For Zaehler = 0 To 1023 'in ca. 1sec aufdimmen Pwm1a = Zaehler Waitms 1 Next Zaehler
Wait 1 '1sec halten
For Zaehler = 1023 To 0 Step -1 'in ca. 1sec abdimmen Pwm1a = Zaehler Waitms 1 Next Zaehler
Wait 1 '1sec halten
Loop 'Ende Hauptschleife
End
Der große Nachteil an der Hardware-PWM ist, daß für 2 PWM-Kanäle ein Timer belegt ist. Da Timer aber eh meist zu wenige sind und wir gar keine so hohe Auflösung für ne LED brauchen, zeig ich euch morgen, wie man das "von Hand" machen kann, ähhm, die PWM mein ich.
Gruß Gerhard
------------------------------------------------------------------------------------------- hier mein aktueller TinyKatalog
heute zeig ich euch, wie man eine (oder mehrere) LEDs per PWM in Software auf und abdimmt. Dieses Grundschema kann auch auf andere Situationen angepaßt werden, z.B. wenn ein Motor angesteuert werden soll.
Paar Gedanken im Vorfeld: wir wollen ja „nur“ eine LED dimmen. Da unser Auge aber so träge ist, brauchen wir gar nicht die Auflösung, wie wir sie gestern programmiert haben. 50 unterschiedliche Helligkeitsstufen sollten reichen, mehr kann das Auge eh nicht erkennen. Genauso steht´s mit der „Bildwiederholrate“, hier reichen auch 50Hz.
Im Programm müssen wir das Verhalten der Hardware-PWM nachbilden. Also eine Variable wird fortlaufend hochgezählt und mit einem Helligkeitswert verglichen. Um dies gleichmäßig zu machen, wird dieser Programmteil in einer Interruptroutine eines Timers abgearbeitet.
Jetzt geht´s darum, herauszufinden, wie oft diese Interruptroutine angesprungen werden muß. Dazu multiplizieren wir die Bildwiederholrate mir den Helligkeitsstufen: 50 x 50 = 2500Hz Wir müssen also den Timer so konfigurieren, daß er 2500 mal die Sekunde überläuft, oder anders gesprochen er muß jeweils 3200 Takte zählen (8000000 / 2500 = 3200). Dazu verwenden wir beispielhaft den Timer0, der 8-Bit zählen kann, also bis 255. Wir rechnen wieder, um den nötigen Prescale rauszufinden: 3200 / 256 = 12,5 Wir müssen also den nächstgrößeren Prescale auswählen, Prescale = 64 ist die nächste Möglichkeit. Jetzt müssen wir noch ausrechnen, wieviel der Timer vorgeladen werden muß, um auf die genaue Frequenz zu kommen. 3200 / 64 = 50 Der Timer muß also genau 50 Zählschritte machen, wir müssen ihn also mit 205 vorladen.
Und was muß alles in die Interruptroutine? Als erstes muß der Timer 0 vorgeladen werden. Dies muß immer als allererstes gemacht werden, da sonst Takte verloren gehen. Danach Inkrementieren wir die Variable, mit der wir die Helligkeitsstufe der LED vergleichen, ich hab sie Timerzähler genannt. Da wir nur 50 Helligkeitsstufen wollen, begrenzen wir die Variable auch auf 50. Danach vergleichen wir den Timerzähler mit dem gewünschten Helligkeitswert, für den ich die Variable Led1 verwendet habe. Ist der Timerzähler noch kleiner als Led1, muß die LED eingeschaltet werden. Wegen unserer invertierten Ansteuerung also den Ausgang auf 0 setzen. Den hab ich am Anfang mit Alias auf Led1_pin getauft.
In der Main-Loop können wir dann wieder spielen und die LED auf- und abdimmen, oder sonst was…
'*** Ein/Ausgänge deklarieren ************************************************** Config Portb.0 = Output 'PortB als Ausgang Led1_pin Alias Portb.0 'Ausgang PortB.0 heitßt ab jetzt "Led1"
'*** Variablen definieren ****************************************************** Dim Zaehler As Byte Dim Led1 As Byte 'Auflösung 0 - 49 Dim Timerzaehler As Byte 'Maximalwerte wie LED1
Do 'Beginn Hauptschleife For Zaehler = 0 To 49 'in ca. 1sec aufdimmen Led1 = Zaehler 'hier wird der Schleifenwert der LED zugewiesen Waitms 20 Next Zaehler
Wait 1 '1sec halten
For Zaehler = 49 To 0 Step -1 'in ca. 1sec abdimmen Led1 = Zaehler Waitms 20 Next Zaehler
Wait 1 Loop 'Ende Hauptschleife
End
'*** Subs ********************************************************************** Timerroutine: 'Beginn Subroutine Timer0 = 205 Incr Timerzaehler If Timerzaehler = 50 Then Timerzaehler = 0 'Timerzaehler auf LED-Auflösung begrenzen If Timerzaehler < Led1 Then Led1_pin = 0 Else Led1_pin = 1 'Vergleich Timerzähler mit LED-Wert -> LED Ein- od. Ausschalten Return 'Ende Subroutine
------------------------------------------------------------------------------------------- hier mein aktueller TinyKatalog
Dateianlage:
Aufgrund eingeschränkter Benutzerrechte werden nur die Namen der Dateianhänge angezeigt Jetzt anmelden!
bek09-2.bas.txt
gebt mir bitte Feedback, ob die Timergeschichte - zumindest für´s Erste - ausreichend erklärt ist. Ich würd das hier gerne bald abschließen, um zur ersten richtigen Anwendung zu kommen. Sonst wird´s noch zu trocken. Und ob es jetzt wirklich sinnvoll ist, alle Möglichkeiten der Timer durchzukauen, glaub ich eh nicht.
Gruß Gerhard
------------------------------------------------------------------------------------------- hier mein aktueller TinyKatalog
und hier noch ein Beispiel, wie mehrere LEDs gedimmt werden. Ist so ein Knight-Rider-Lauflicht mit Nachlaufeffekt incl. Lookup-Funktion. Viel Spaß beim grübeln wie es funktioniert
Gruß Gerhard
------------------------------------------------------------------------------------------- hier mein aktueller TinyKatalog
Dateianlage:
Aufgrund eingeschränkter Benutzerrechte werden nur die Namen der Dateianhänge angezeigt Jetzt anmelden!
bek09-3.bas.txt
Erst noch mal Danke für deine Erklärungen und deine Mühe!
Ich für mich mich muss sagen, das ich zur Zeit noch etwas zu kämpfen habe mit der Materie, werde aber schon mitkommen! Zur Not hilft mir mein Sohn mit dem Erklären wenn ich mal was nicht Verstehe!
Mit dem Timer bin ich soweit noch mit gekommen. Das mit der PWM Geschichte muss ich mir noch mal auf der Zunge zergehen lassen. Ist wohl schon etwas spät für die graue Masse ;-) Leider habe ich gerade auch kein Bascom installiert, somit fällt das Testen auch aus. :-( Am Wochenende sollte ich mehr Zeit haben. Aber alles in allem, wieder sehr Gut erklärt von Dir. Vielen Dank für Deine Arbeit.
Bascom sollte schon verfügbar sein, sonst bringt´s eh nix. Am Besten erst mal alles durchkauen, dann selbst eine Aufgabe setzen und schaun wie man es umsetzt. Nur so steigt man auch voll durch.
Wer dann mal ne Frage hat, der macht doch bitte nen eigenen Thread auf, postet seine Aufgabenstellung und das Programm. Dann schaun wir uns das Gemeinsam an. Davon haben dann alle was. Und auch wer es alleine Geschafft hat, kann sein Programm posten. Weitere Beispiele können nie schaden. Macht die Threads dann auch ruhig in diesem Forenbereich auf. Die Kursbeiträge hab ich ja durch die eckigen Klammern gekennzeichnet, die bleiben auch automatisch in der Übersicht oben stehen.
Gruß Gerhard
------------------------------------------------------------------------------------------- hier mein aktueller TinyKatalog
Hallo Gerhard, Bascom ist ja schon vorhanden, nur ich habe mein Win XP auf Win 7 aktualisiert und bis Heute noch kein Bascom installiert. Arbeite normalerweise auf eine Apfel und habe nur Win für solche Sachen. Leider Gibt es kein Bascom für Mac OS :-( Werde jetzt mal schauen ob alles läuft und der DX ISP Prog richtig installiert ist. Der Treiber hatte etwas herumgezigt Gruß Ingo