Forum: Mikrocontroller und Digitale Elektronik Led´s mit überblenden PWM


von Tobias X. (tobiasblome)


Angehängte Dateien:

Lesenswert?

Hallo,
ich möchte 2 Leds über PWM dimmen.
Eine LED ist rot und die andere ist blau. Nun soll von blau über lila
nach rot übergeblendet werden.

Da mein µC (Atmel At90S2313) nur einen PWM Pin hat, möchte ich das über
software steuern.

Kann mir jemand sagen, wie man das in Basic programmiert oder zumindes
prinziepjell?
Ich habe leider nicht´s finden können! Überall steht, wie man die PWM
Pinns ansteuert, aber nirgend wo steht wie man beliebige Pinns mit PWM
ansteuert.

vielen Dank,

Tobias

von Mike (Gast)


Lesenswert?

Du kannst ja mal nach Pulsweitenmodulation suchen.

Oder dir den Abschnitt im Datenblatt zu den PWM funktionen durchlesen,
dann solltest du auch verstehen, wie eine PWM funktioniert.

von Sonic (Gast)


Lesenswert?

Gebe ich Mike recht. Per Interrupt ist das Ganze kein Problem.

von Tobias X. (tobiasblome)


Lesenswert?

Hallo Mike, genau das habe ich gemacht und überall steht wie man die
fest definierten PWM Pinns ansteuert! Aber ich möchte dafür beliebige
Pinns benutzen und das habe ich nirgens gefunden! Also wenn jemand eine
Seite weiß wo NICHT die PWM Ausgänge benuzt werden dann bitt zu mir.

-Sonic Wie meinst du das per Interrupt? Pin bei jedem Interrupt
togglen?! Dann hätte ich ja immer 50%...

Tobias

von Hannes L. (hannes)


Lesenswert?

Lass im Timer-Interrupt einen Zähler (Variable) laufen und vergleiche
dessen Zählerstand mit den Sollwerten der einzelnen PWM-Kanäle. Ist der
Sollwert überschritten, machst Du die LED aus, ist er unterschritten,
machst Du sie an.

...

von johnny.m (Gast)


Lesenswert?

Oder mit 2 Interrupts:
Im Compare-Interrupt-Handler wird die eine LED jeweils ein- und die
andere ausgeschaltet, im Overflow-Interrupt-Handler umgekehrt. Wenn man
den Compare-Wert jetzt kontinuierlich ändert, bekommt die eine LED mehr
und die andere weniger Saft ab.

von Nico Schümann (Gast)


Lesenswert?

Hier mal ein Beispiel für ein PWM für drei Ausgänge (PORTD, Pin 5-7)
/*Hauptdatei*/
volatile unsigned char pwm0; // PortD - Pin 5
volatile unsigned char pwm1; // PortD - Pin 6
volatile unsigned char pwm2; // PortD - Pin 7
/* Jetzt kannst du jeden Kanal mit pwm0 = 100; oder so steuern, wobei
0=0 und 255=max ist. */

/*Interruptdatei, kannst du auch in eine machen*/
volatile unsigned char tm0;
extern volatile unsigned char pwm0;
extern volatile unsigned char pwm1;
extern volatile unsigned char pwm2;
ISR(TIMER0_OVF_vect)
{  ++tm0;
  if(tm0 == 255)
  {  tm0 = 0;
    PORTD &= ~((1<<5)|(1<<6)|(1<<7));
  }
  else
  {  if(tm0 == 0xFF-pwm0)
      PORTD |= (1<<5);
    if(tm0 == 0xFF-pwm1)
      PORTD |= (1<<6);
    if(tm0 == 0xFF-pwm2)
      PORTD |= (1<<7);
  }
}

von johnny.m (Gast)


Lesenswert?

@Nico:
Ich glaube, wenn er in Basic programmieren will, wird ihn Dein Code
nicht wirklich weiterbringen...

von Tobias X. (tobiasblome)


Angehängte Dateien:

Lesenswert?

Danke HanneS, das ist doch mal eine Auskunft!
Und damit die Led automatisch langsam abgedimmt wird nehme ich wohl
einen 2.timer oder? Dann ist nur noch die Frage, wie ich es schaffe,
das z.b. die blaue Led 100% leuchtet und so lange wartet bis die Rote
auch mit 100% leuchtet und erst dann runter dimmt wärend die Rote
wartet (hab das Bild noch mal drangehängt).

Die Version von johnny.m hab ich noch nicht verstanden... dann macht
die eine Led immer das Gegenteil von der ersten? Dann habe ich aber
nicht, das eine auf 100% ist wären die ander hoch dimmt...

Tobias

von Hannes L. (hannes)


Lesenswert?

> Und damit die Led automatisch langsam abgedimmt wird nehme ich wohl
> einen 2.timer oder?

Nööö... Du hast doch schon einen Timer-Interrupt, darin hannst Du doch
weitere Variablen hoch- oder runterzählen, die Du dann im Hauptprogramm
nutzen kannst, z.B. für abweisende Schleifen.

...

von johnny.m (Gast)


Lesenswert?

Bei meiner Version hättest Du eine LED voll an, die dann schwächer wird,
während die andere entsprechend heller wird. Am Ende ist die LED, die am
Anfang eingeschaltet war, aus und die andere leuchtet mit 100%. Und
genau das ist dann doch genau der Farbverlauf, den Du brauchst, um von
100% blau über lila (z.B. 50% rot und 50% blau) nach 100% rot zu
dimmen... Oder hab ich Dein Vorhaben falsch verstanden?

von Tobias X. (tobiasblome)


Lesenswert?

jain...

wenn es nicht viel aufwändiger ist würde ich gerne Lila bei 100% rot
und 100% blau wegen der Helligkeit (2x100% ist heller als 2x50%).

1.Zeile Rot
2. Blau
3. beide zusammen

0%  rot  --dimmt hoch-100%rot/// bleibt so-100%rot ///bleibt so-100%r
100% blau--beibt so---100blau///dimmt runter-0%blau///dimmt ho100
BLAU-------------------LILA  ///LILA-----------ROT ///

von Hannes L. (hannes)


Lesenswert?

50% PWM ist nicht 50% Helligkeit... ;-)

Daher kommt Johnnys Vorschlag Deinem Wunsch schon etwas entgegen.

Aber mit einzeln ansteuerbarer mehrfach-PWM bist Du natürlich bedeutend
flexibler. Ob das in BASCOM schnell genug realisierbar ist,
ist natürlich eine andere Frage. Wir hatten hier kürzlich erst das
Thema 8-Kanal-Software-PWM und Anbindung eines LCD, aber das wurde in
Assembler realisiert.

...

von Tobias X. (tobiasblome)


Lesenswert?

Ich dachte ich hätte PWM verstanden ?!

wieso ist 50% PWM nicht auch 50% helligkeit?

Bei 50% ist die LED doch genauso lange an wie aus oder? und dann müsste
die doch auch bei hälfte an hälfte aus halbe helligkeit haben...?

Aber wenn nicht ist doch 2x 100% heller als 2x 50% oder auch nicht?

von Hannes L. (hannes)


Lesenswert?

> wieso ist 50% PWM nicht auch 50% helligkeit?

Objektiv ja, subjektiv aber nicht weil Dein Auge da anderer Meinung
ist.

...

von TravelRec. (Gast)


Lesenswert?

Ohr und Auge des Menschen folgen einer eher logarithmischen Kennlinie.
Beim Auge kommt noch Trägheit hinzu.

von Hannes L. (hannes)


Lesenswert?

Stimmt, ist ja Dein Fachgebit... :-)

Gruß nach Halberstadt...

...

von Sonic (Gast)


Lesenswert?

Ich meinte z.B. Timer Overflow 31250 Hz (Teiler x1 bei 8MHz
Taktfrequenz), dann eine Variable auf 256 hochzählen und bei einem
bestimmten Wert den Pin auf '1', anschließend Pin auf '0'. Das gibt
einen PWM mit ca. 122 Hz.

von Tobias X. (tobiasblome)


Lesenswert?

Hallo,
ich dachte ein Film hat 25 Bilter pro sekunde und darum hab ich
versucht meine Variable 25 mal pro sekunde um 1 zu erhöhen. Aber da ist
irgendwo der wurm drin...

'PWM über Zähler
'----------------------------------------------------------------------- 
--------
'Konfiguration µC:
$regfile = "2313def.dat"
'AT90S2313-Deklarationen
$crystal = 3686400                                          'Quarz:
3.6864 MHz
'----------------------------------------------------------------------- 
--------
'----------------------------------------------------------------------- 
--------
'Variablen
'----------------------------------------------------------------------- 
--------
Dim Sollwert As Byte
Dim Zähler0 As Byte
'----------------------------------------------------------------------- 
--------
' Ein- und Ausgänge
'----------------------------------------------------------------------- 
--------
'Pin Eingang/Ausgang 1
Portd. 0 = 1                                                'Ausgang
LED1
Portd. 1 = 1                                                'Ausgang
LED2
'----------------------------------------------------------------------- 
--------
Config Timer0 = Timer , Prescale = 64
'3686400/64/100=576Hz
Enable Timer0
Timer0 = 155                                                'Starte
bei 155
Enable Interrupts                                           'erlaube
Interrupts
On Timer0 Ontimer0overflow
'Timer0-Interruptroutine deklarieren

'----------------------------------------------------------------------- 
--------
'Hauptprogramm
'----------------------------------------------------------------------- 
--------
 If Sollwert < Zähler0 Then
      Portd.0 = 1
    Else
      Portd.0 = 0
 End If


'----------------------------------------------------------------------- 
--------
'Sprungmarken
'----------------------------------------------------------------------- 
--------
Ontimer0overflow:
'Timer0-Interruptroutine
 Incr Zähler0

Return

von Tobias X. (tobiasblome)


Lesenswert?

hab noch etwas vergessen als Sollwerte wollte ich 100 nehmen. 100 = 100%
Einschaltdauer ;-)

Tobias

von Hannes L. (hannes)


Angehängte Dateien:

Lesenswert?

Also das Programm im Anhang macht eine 8-Kanal-PWM auf dem Mega8515 mit
8MHz intern mit einer PWM-Frequenz von etwa 860Hz und gibt die
PWM-Sollwerte der 8 Kanäle auf einem LCD 2x16 in % aus.

Es ist in ASM geschrieben. Es ist allerdings noch nicht aufgeräumt, es
wurde aus anderen Programmen zusammenkopiert. Es ist auch nicht für ein
ernsthaftes Projekt gemacht worden, sondern nur als Demonstration, wie
man es machen könnte.

Vielleicht hilft ja die Analyse beim Verständnis der Dinge.

...

von Tobias X. (tobiasblome)


Lesenswert?

Also die led blinkt langsam ...
das hat aber noch nichts mit dimmen zu tun...
hat da jemand eine idee???

ich habe zum testen mal ein lcd angeschlossen

'PWM über Zähler
'----------------------------------------------------------------------- 
--------
'Konfiguration µC:
$regfile = "2313def.dat"
'AT90S2313-Deklarationen
$crystal = 3686400                                 'Quarz: 3.6864 MHz
'----------------------------------------------------------------------- 
--------
'----------------------------------------------------------------------- 
--------
'Variablen
'----------------------------------------------------------------------- 
--------
Dim Zähler1 As Byte
Dim Zähler11 As Byte
Dim Zähler2 As Byte
Dim Zähler1merker As Bit
Dim Zähler11merker As Byte
Dim Zähler2merker As Bit
'----------------------------------------------------------------------- 
--------
' Ein- und Ausgänge
'----------------------------------------------------------------------- 
--------
'Pin Eingang/Ausgang 1
'Ddrd = &B0000011                             'Ausgang LED1&2
 Ddrd = &B1011011
Portd. 0 = 1
  Portd. 3 = 1
  Portd. 4 = 1
'----------------------------------------------------------------------- 
--------
Config Timer0 = Timer , Prescale = 1
'3686400/1024/101 = 35,644Hz
Enable Timer0
Timer0 = 155                              'Starte bei 155
Enable Interrupts                        'erlaube Interrupts
On Timer0 Ontimer0overflow       'Timer0-Interruptroutine deklarieren

'======================================================================= 
========
'    LCD Parametrieren
'======================================================================= 
========
  Dim Sresult As String * 40 At &H64 Overlay
 Config Lcd = 40 * 2
Config Lcdpin = Pin , Db4 = Portb.4 , Db5 = Portb.3 , Db6 = Portb.2 ,
Db7 = Portb.1 , E = Portd.6 , Rs = Portb.0

 Cls
 'Cursor Off Noblink


'----------------------------------------------------------------------- 
--------
'Hauptprogramm
'----------------------------------------------------------------------- 
--------
 Do

 cls
 Lcd "Z 1: "
 Lcd Zähler1
 Lcd "  Z 11: "
 Lcd Zähler11


 If Zähler1 < Zähler11 Then                                 'richtig
      Portd.0 = 1
    Else
      Portd.0 = 0
 End If

 Loop

'----------------------------------------------------------------------- 
--------
'Sprungmarken
'----------------------------------------------------------------------- 
--------
Ontimer0overflow:                        'Timer0-Interruptroutine

 If Zähler1 > 50 Then
 Zähler1merker = 1
  End If

 If Zähler1 < 1 Then
 Zähler1merker = 0
 End If

 If Zähler1merker = 0 Then
 Incr Zähler1                             'um 1 hochzählen
 Else
 Decr Zähler1                               'um 1 runterzählen
 End If
 '--

 If Zähler11 > 100 Then
 Zähler11merker = 1
  End If

 If Zähler11 < 1 Then
 Zähler11merker = 0
 End If

 If Zähler11merker = 0 And Zähler1 = 25then
 Incr Zähler11                              'um 1 hochzählen
 Else
 Decr Zähler11                               'um 1 runterzählen
 End If


Return

von Tobias X. (tobiasblome)


Lesenswert?

also es läuft ABER viel zu langsam led blinkt und wird nicht gedimmt
obwohl der Timer mit Quarztakt läuft!!!

'PWM über Zähler
'----------------------------------------------------------------------- 
--------
'Konfiguration µC:
$regfile = "2313def.dat"               'AT90S2313-Deklarationen
$crystal = 3686400                      'Quarz: 3.6864 MHz
'----------------------------------------------------------------------- 
--------
'----------------------------------------------------------------------- 
--------
'Variablen
'----------------------------------------------------------------------- 
--------
Dim Zähler1 As Byte
Dim Zähler11 As Long
Dim Zähler2 As Byte
Dim Zähler11merker As Byte
'----------------------------------------------------------------------- 
--------
' Ein- und Ausgänge
'----------------------------------------------------------------------- 
--------
'Pin Eingang/Ausgang 1024
'Ddrd = &B0000011                      'Ausgang LED1&2
 Ddrd = &B1011011
Portd. 0 = 1
  Portd. 3 = 1
  Portd. 4 = 1
'----------------------------------------------------------------------- 
--------
Config Timer1 = Timer , Prescale = 1   '3686400/1024/101 = 35,644Hz
Enable Timer1
Timer1 = 65534                           'Starte bei 155
Enable Interrupts                      'erlaube Interrupts
On Timer1 Ontimer1overflow       'Timer1-Interruptroutine deklarieren

'======================================================================= 
========
'    LCD Parametrieren
'======================================================================= 
========
  Dim Sresult As String * 40 At &H64 Overlay
 Config Lcd = 40 * 2
Config Lcdpin = Pin , Db4 = Portb.4 , Db5 = Portb.3 , Db6 = Portb.2 ,
Db7 = Portb.1 , E = Portd.6 , Rs = Portb.0

 Cls
 'Cursor Off Noblink


'----------------------------------------------------------------------- 
--------
'Hauptprogramm
'----------------------------------------------------------------------- 
--------
 Do

 cls
 Lcd "Z 1: "
 Lcd Zähler1
 Lcd "  Z 11: "
 Lcd Zähler11


 If Zähler1 < Zähler11 Then
      Portd.0 = 1
    Else
      Portd.0 = 0
 End If

 Loop

'----------------------------------------------------------------------- 
--------
'Sprungmarken
'----------------------------------------------------------------------- 
--------
Ontimer1overflow:                        'Timer0-Interruptroutine

'Zähler 1 von 0 - 100       SCHNELL!!!
 Incr Zähler1                            'um 1 hochzählen
 If Zähler1 = 20 Then
 Zähler1 = 0
 End If


 'Zähler 11 von 0 - 100    langsam
 If Zähler11 = 20 Then
 Zähler11merker = 1
  End If

 If Zähler11 = 1 Then
 Zähler11merker = 0
 End If

 If Zähler11merker = 0 And Zähler1 = 0 Then
 Incr Zähler11                              'um 1 hochzählen
 End If
 If Zähler11merker = 1 And Zähler1 = 0 Then
 Decr Zähler11                              'um 1 runterzählen
 End If
Return

von Hannes L. (hannes)


Lesenswert?

Du hast in Deiner Timer-ISR kein Timer-Reload. Der Timer braucht daher
(außer beim ersten mal) immer 65536 Takte bis zum Überlauf.
:-(

Bei Verwendung des 16-Bit-Timers bietet sich der Compare-Interrupt mit
CTC-Mode an, da wird der Timer beim Erreichen des
Compare-Register-Wertes auf 0 gesetzt und der Interrupt ausgelöst.

Wenn es unbedingt der Überlauf-Interrupt sein soll, dann nimm doch den
8-Bit-Timer, da geht Reload schneller weil nur 8 Bit erforderlich ist.

...

von Tobias X. (tobiasblome)


Lesenswert?

timer reload?

also in der unterroutine einfach "Timer0 = xxx" gleicher wert wie in
der configuration?

>Wenn es unbedingt der Überlauf-Interrupt sein soll, dann nimm doch
>den 8-Bit-Timer, da geht Reload schneller weil nur 8 Bit >erforderlich
ist.

Welchen Interrups würdes du empfehlen? gibt es einen der
schneller/öfter kommt als der Überlauf-Interrupt?

Tobias

P.S. die LED blinkt immer noch...:-(
'PWM über Zähler
'----------------------------------------------------------------------- 
--------
'Konfiguration µC:
$regfile = "2313def.dat"                  'AT90S2313-Deklarationen
$crystal = 3686400                      'Quarz: 3.6864 MHz
'----------------------------------------------------------------------- 
--------
'----------------------------------------------------------------------- 
--------
'Variablen
'----------------------------------------------------------------------- 
--------
Dim Zähler1 As Byte
Dim Zähler11 As Byte
Dim Zähler2 As Byte
Dim Zähler11merker As Byte
'----------------------------------------------------------------------- 
--------
' Ein- und Ausgänge
'----------------------------------------------------------------------- 
--------
'Pin Eingang/Ausgang 1024
'Ddrd = &B0000011                      'Ausgang LED1&2
 Ddrd = &B1011011
Portd. 0 = 1
  Portd. 3 = 1
  Portd. 4 = 1
'----------------------------------------------------------------------- 
--------
Config Timer0 = Timer , Prescale = 8
'3686400/1024/101 = 35,644Hz
Enable Timer0
Timer0 = 219                         'Starte bei 155
Enable Interrupts                        'erlaube Interrupts
On Timer0 Ontimer0overflow       'Timer0-Interruptroutine deklarieren

'======================================================================= 
========
'    LCD Parametrieren
'======================================================================= 
========
  Dim Sresult As String * 40 At &H64 Overlay
 Config Lcd = 40 * 2
Config Lcdpin = Pin , Db4 = Portb.4 , Db5 = Portb.3 , Db6 = Portb.2 ,
Db7 = Portb.1 , E = Portd.6 , Rs = Portb.0

 Cls
 'Cursor Off Noblink


'----------------------------------------------------------------------- 
--------
'Hauptprogramm
'----------------------------------------------------------------------- 
--------
 Do

 Cls
 Lcd "Z 1: "
 Lcd Zähler1
 Lcd "  Z 11: "
 Lcd Zähler11


 If Zähler1 < Zähler11 Then                     'richtig
      Portd.0 = 1
    Else
      Portd.0 = 0
 End If

 Loop

'----------------------------------------------------------------------- 
--------
'Sprungmarken
'----------------------------------------------------------------------- 
--------
Ontimer0overflow:                   'Timer0-Interruptroutine

Timer0 = 219

'Zähler 1 von 0 - 100       SCHNELL!!!
 Incr Zähler1                       'um 1 hochzählen
 If Zähler1 = 99 Then
 Zähler1 = 0
 End If




 'Zähler 11 Von 0 - 100 Langsam!!!
 If Zähler11 = 99 Then
 Zähler11merker = 1
  End If

 If Zähler11 = 1 Then
 Zähler11merker = 0
 End If

 If Zähler11merker = 0 And Zähler1 = 0 Then
 Incr Zähler11       'um 1 hochzählen
 End If
 If Zähler11merker = 1 And Zähler1 = 0 Then
 Decr Zähler11           'um 1 runterzählen
 End If
Return

von Hannes L. (hannes)


Lesenswert?

> Welchen Interrups würdes du empfehlen? gibt es einen der
> schneller/öfter kommt als der Überlauf-Interrupt?

Wie schnell/oft ein Timer-Interrupt "kommt", bestimmt einzig und
allein der Programmierer. Dabei muss er natürlich die vorhandenen
Ressourcen im Auge behalten. Er kann einen Interrupt, der 300 Takte
dauert, nicht alle 100 Takte aufrufen wollen, denn da ist der Interrupt
ja noch nicht fertig.

Also ist das erste Gebot: Interrupt-Service-Routinen so kurz wie
möglich halten. Dann kannst Du ermitteln, wie lange (wieviele Takte)
die ISR dauert. Daraus kannst Du dann ableiten, wie oft Du den Timer
maximal einen Interrupt auslösen lassen darfst.

Nun meinst Du sicherlich, dass es Dir schwerfällt das in BASCOM zu
ermitteln. Da hast Du recht, deshalb verschmähe ich ja auch BASCOM und
programmiere in ASM, da ist der Umgang mit der Hardware nämlich völlig
durchschaubar. Da Eiert man nicht rum, sondern sieht, was man macht.

Bei einer Software-PWM, deren Frequenz möglichst hoch sein soll, ist es
unabdingbar, die ISR extrem kurz zu halten. Denn sie soll ja möglichst
oft aufgerufen werden. Wie oft das geht, hängt von der Effizienz der
Programmierung ab. Kann ich es mir leisten, in der ISR mit
Exklusivregistern zu arbeiten, so erspare ich mir das Sichern und
Wiederherstellen der Inhalte der benutzten Register. Das ist in ASM
kein Problem, in BASCOM geht der Eiertanz aber los, den BASCOM sichert
gleich mal ungefragt in der ISR alle Register, was schonmal 128 Takte
kostet und 32 Bytes Stack. Die Timer-ISR in meinem ASM-Beispiel dauert
weniger als 60 Takte (geschätzt, jetzt wegen Faulheit nicht
nachgezählt), da würden die 128 Takte Registersicherung sich schon arg
auswirken. In diesen 60 Takten ist dann neben der Sicherung des SREG,
Timer-Reload, PWM-Erzeugung für 8 Kanäle auch noch die Entprellung von
8 Tastern mit drin.

Welchen Interrupt ich empfehlen würde, hängt davon ab, was der AVR für
Interrupts hat und was er sonst noch tun muss.

Beim AT90S2313 gibt es Timer0 mit Überlauf und Timer 1 mit Überlauf,
CompA, CompB und ICP. Jetzt kommt es auch noch darauf an, inwieweit man
das Datenblatt verstanden hat und mit den vorhandenen Features umgehen
kann. Meine ersten Programme nutzten daher erstmal nur den
Überlauf-Interrupt des Timer0. Dieser ist nämlich sehr einfach, gut zu
verstehen, und hat keine zusätzlichen Features, die man falsch
interpretieren könnte. Man setzt (auch in der ISR!) der Timer auf einen
Startwert (Reload) und lässt ihn bis zum Überlauf klappern. Da Timer0
nur 8 Bit breit ist, genügt zum Reload ein einziger OUT-Befehl, wenn
man den Reload-Wert in einem Exklusivregister vorhält. Beim 16-Bit
Timer sind das schon 2 Out-Befehle und 2 blockierte Register. Das würde
ich nur opfern, wenn ich die 16 Bit des Timer1 wirklich brauche.

Wenn ich aber weiß, dass ich keines der weiteren Features des Timer1
brauchen werde, dann kann ich den Compare-Interrupt des Timer1
verwenden. Der lässt sich nämlich mit dem CTC-Bit bei jedem Interrupt
automatisch löschen, was das Timer-Reload (Zeit in der ISR) spart.
Natürlich geht dabei die Möglichkeit verloren, gleichzeitig die anderen
Features des Timer1 zu nutzen, die man aber auch nicht immer braucht.
Mit dem Compare-Interrupt im CTC-Mode sparst Du also das Timer-Reload.
- Das ist in ASM ein großer Gewinn, zwei Takte eingespart, zwei Takte
schnellere ISR, bei 50 Takten 4% Performance-Gewinn... In BASCOM ist
das in Anbetracht der 120 Takte Registersicherung ein lächerlicher
Gewinn, den man unterm Strich nicht sieht...

Du hast nun die Möglichkeit, weiter mit dem Baukasten BASCOM
vorgefertigte Routinen zu Programmen zusammenzustellen und Dich über
den schnellen Erfolg freuen.
Oder Du beschäftigst Dich mit dem Datenblatt und dem ASM-Befehlssatz
und lernst, effiziente Programme zu schreiben. Der BASCOM-Weg ist der
leichtere, führt aber in die Sackgasse. Hardwarenahe effiziente
Programme sind damit nicht (oder nur mit extrem viel Hintergrundwissen
in BASIC und Assembler) möglich.

...

von Tobias X. (tobiasblome)


Lesenswert?

Vielen Dank für den ausfürlichen Beitrag!

Also als erstes werde ich den Quarz von 4 auf 8 MHz erhöhen ;-))

und dann versuche ich die ISR zu kürzen...denn der µC soll noch etwas
anderes machen auser die 2 LED´s dimmen!

und zum schluss noch der Befehl "NOSAVE"?


Tobias

von Tobias X. (tobiasblome)


Lesenswert?

also mit dem nosave befehl geht gar nichts mehr...

von Tobias X. (tobiasblome)


Lesenswert?

Also ich komme nicht weiter. Hat jemand einen Vorschlag wie ich die LED
schneller bekomme? Das einfach viel zu langsam!

Wenn ich die Version von johnny.m nehme habe ich dann auch das Problem
das die "PWM" zulansam ist?:

johnny.m:
Oder mit 2 Interrupts:
Im Compare-Interrupt-Handler wird die eine LED jeweils ein- und die
andere ausgeschaltet, im Overflow-Interrupt-Handler umgekehrt. Wenn man
den Compare-Wert jetzt kontinuierlich ändert, bekommt die eine LED mehr
und die andere weniger Saft ab.

von Dirk (Gast)


Lesenswert?

Hallo, dein Interrupt muss doch nur alle 39µs kommen bei 8Bit Aufloesung
des PWM, somit ergibt sich eine PWM Grundfreq. von 100Hz.

ISR

If Zähler = 0 Then
PORTB = 0
End If

 If Zähler = PWMWERT0 and Zähler > 0 Then
  PORTB.0 = 1
 End If
 If Zähler = PWMWERT1 and Zähler > 0 Then
  PORTB.1 = 1
 End If

END ISR.


Oder hab ich irgendetwas verpasst.

Gruß,
Dirk

von Hannes L. (hannes)


Lesenswert?

Du hast zwei Compare-Interrupts. In Verbindung mit dem
Overflow-Interrupt kannst Du den Zählumfang begrenzen. Du müsstest
dazu

- im Overflow-Interrupt den Timer auf einen Wert 65536 minus Anzahl der
gewünschten Takte voreinstellen und beide Ausgänge einschalten. (Der
Timer klappert also immer nur die letzten Takte bis zum Überlauf)

- im CompareA-Interrupt den Ausgang 1 ausschalten und den (evtl. neuen)
PWM-Wert aus einer Variable in des CompareA-Register schreiben.

- im CompareB-Interrupt den Ausgang 2 ausschalten und den PWM-Wert in
das CompareB-Register schreiben.

- in der Mainloop die PWM-Werte in Deinen Variablen ggf verändern.

Somit kann Dein Timer1 langsamer laufen, denn er löst nur dann einen
Interrupt aus, wenn eine Schalthandlung vorzunehmen ist, also nur drei
Interrupts pro PWM-Periode (aber drei verschiedene).

Du möchtest bei 4MHz Quarz einen PWM-Zählumfang von 100 (100%)
realisieren. Also muss der Zählumfang des Timers auf die letzten 100
Takte begrenzt werden. Dir stehen dazu Vorteiler von 1, 8, 64, 256 und
1024 zur Verfügung. Damit könntest Du folgende PWM-Frequenzen
erreichen:

- VT    1: 4000000/100/1 = 40kHz (zu schnell, nicht genug Zeit für
           3 ISRs)

- VT    8: 4000000/100/8 =  5kHz (knapp, 800 Takte für 3 ISRs)

- VT   64: 4000000/100/64 = 625Hz (besser, 6400 Takte für 3 ISRs)

- VT  256: 4000000/100/256 = 156Hz (noch ok, 25600 Takte für 3 ISRs)

- VT 1024: 4000000/100/1024 = 39Hz (zu langsam)

Ich würde es zuerst mit Vorteiler 64 versuchen und bei Timing-Problemen
auf 256 umstellen. Gehen wir erstmal von 64 aus:

Alle 6400 Takte löst der Overflow-Interrupt aus. Dort wird:
- Timer1 auf 65536-100 gesetzt,
- Ausgang 1 und 2 eingeschaltet, falls deren PWM-Wert nicht 0 ist.

Bei Timerstand 65436 + n wird ein Compare-Interrupt ausgelöst. n steht
hierbei für den Tastgrad (0..99) der PWM (PWM-Wert der LED). Dies
geschieht im Raster von 64 Takten, aber nur zweimal (einmal pro Kanal)
innerhalb 6400 Takten. Die dabei auftretenden Verzögerungen der anderen
ISR-Abarbeitungen relativieren sich, Du willst ja schließlich kein
TV-Synchronsignal generieren, sondern nur zwei LEDs dimmen. In der
jeweiligen Compare-ISR (Compare1A und Compare1B) wird:
- die betreffende LED ausgeschaltet,
- auf den Timer-Startwert von 65436 der Tastgrad-Wert (0..99)
  aufaddiert und ins entsprechende Compare-Register geschrieben.

Ein "nosave" sollte nicht nötig sein, wenn doch, dann musst Du das
SREG und die in der ISR verwendeten Register von Hand sichern. Damit
kannst Du auch schnelleen Code realisieren, brauchst aber etwas
ASM-Wissen.

Ich denke, Dein Projekt mit nur zwei LEDs ist auch in BASCOM gut
realisierbar, bei drei LEDs wird es aber mangels dritter
Compare-Einheit verdammt eng...

...

von Hannes L. (hannes)


Lesenswert?

> Hallo, dein Interrupt muss doch nur alle 39µs kommen bei 8Bit
> Aufloesung des PWM, somit ergibt sich eine PWM Grundfreq. von 100Hz.
> ...
> Oder hab ich irgendetwas verpasst.

Er will ja keine 8-Bit-PWM, sondern nur 100 Schritte, damit der
Tastgrad-Wert in Prozent angegeben werden kann.

100Hz für LEDs empfinden viele Leute schon als flackernd.

...

von Dirk (Gast)


Lesenswert?

Hallo,

sorry das ich diese Frage stelle, aber ich hab gestern abend das Forum
nach dem Thread durchsucht und leider nicht gefunden.

Ich glaube die Antwort die ich suche war von Dir ...Hannes... und zwar
ging es um asynchrone PWM. Du hattest auch ein Windows Programm
gepostet. Weisst du noch wie der Thread hieß? oder kann man deine
Aussagen woanders nach lesen?

Gruß,
Dirk

von Hannes L. (hannes)


Lesenswert?

> ging es um asynchrone PWM.

Das war mit Sicherheit nicht von mir. Denn als ich mich zum ersten mal
mit PWM beschäftigte, wusste ich noch nicht, wie asynchrone PWM
arbeitet und wie sie funktioniert. Es war mir damals auc sehr wichtig,
dass die PWM synchron war, denn mein erstes PWM-Projekt war ein
Modellbahn-Fahrtregler, bei dem die PWMs synchronisiert sein mussten,
um beim Übergang von einem Fahrstromabschnitt auf den anderen keine
Sprünge zu haben (60% und 50% bei gleicher Phasenlage bleiben 60%, bei
unterschiedlicher Phasenlage können daraus schon mal unerwünschte 100%
werden).

Die Asynchrone PWM erfolgt durch Addieren der PWM-Sollwerte zu den
PWM-Zählern (jeder Kanal hat seinen eigenen Zähler) und Auswerten des
Carry-Flags. Dabei wird nach jeder Addition das Carry mittels ROR oder
ROL in ein Register geschoben, welches nach den 8 Additonen (und
Rotierungen) an den Port ausgegeben wird. Dies geht zwar in einer
Schleife, ist aber ohne Schleife bedeutend schneller (keine bedingten
Sprungbefehle). Der Vorteil der asynchronen PWM liegt in der Verteilung
der Strom-Belastung, es schalten also nicht alle LEDs zum gleichen
Zeitpunkt ein.

Um den Thread zu finden, könntest Du nach Einsammeln und Carry suchen,
das habe ich (glaube) dazu geschrieben... ;-)
Hier könntest Du also fündig werden:
http://www.mikrocontroller.net/forum/forum.php?query=%2Bcarry*+%2Beinsammeln&forums%5B%5D=1&number=20&action=sendsearch

...

von Tobias X. (tobiasblome)


Lesenswert?

Hallo,
ich hab nun noch ein Programm gefunden. Die 3 LED´s haben aber feste
werte! Nun habe ich versucht mit timer1 über die variable "LED" die
LED B hochlaufen zu lassen und bei 255 auf 0 zu setzen. Muss ich dafür
einen 2. Timer benutzen oder wie würdet ihr das machen?

' PULS WEITEN MODULATION (PWM)
' Sprache: Bascom AVR-Basic (Demoversion 1.11.6.2)
' Getestet mit 8 MHz auf 90S2333.Ausgabewert wird dann ca. 121 mal pro
Sekunde
' erneuert.
' 3 Kanäle an Port B0, B1, B2
' Bewust einfaches Programm zur Erklärung wie eine Software PWM
funktionieren kann.
' Timerinterupt, der in gleichmäßigen Abständen
' das Hauptprogramm unterbricht und ein Unterprogramm
(Interupthandler) aufruft
' das die Ausgänge ein oder ausschaltet. Im Mittel ergibt sich dann
der gewünschte
' Ausgabewert (Normale Multimeter sind langsam genug d.h. der
Ausgabewert kann
' für Testzwecke direkt am Pin gemessen werden)
' Sept. 2002 Bernhard T.

'Konfiguration µC:
$regfile = "2313def.dat"
'AT90S2313-Deklarationen
$crystal = 8000000                                          'Quarz: 8
MHz

'Timer konfigurieren, Vorteiler auf 1 ist gut fürs Simulieren (evtl.
anpassen, dann aber ausgang mit R-C Tiefpass)
Config Timer0 = Timer , Prescale = 1
Config Timer1 = Timer , Prescale = 8
Enable Timer1
On Timer1 Prog
'Definiere den Interrupthandler
On Ovf0 Tim0_isr

'Wichtig:
Config Pinb.0 = Output                                      'Port B =
Ausgang
Config Pinb.1 = Output
Config Pinb.2 = Output

Enable Timer0                                               'timer
einschalten
Enable Interrupts
'interrupts  einschalten

Dim R As Byte                                               ' In diese
Variablen muss man
Dim G As Byte                                               ' im
Hauptprogram die gewünschten
Dim B As Byte                                               '
Ausgabewerte laden

Dim Z As Word                                               'Byte
           ' Zähler

Dim Ri As Byte                                              '
Hilfsregister
Dim Gi As Byte
Dim Bi As Byte
Dim Led As Byte



Z = 0

Do
   'your program goes here
'Bsp.:


 R = 51                                                     ' bei Ub=
5V ca. 1Volt
 G = 102                                                    ' ca. 2V
 B = 1 + Led
                                                    ' 5V/256 =
0.01953125 V Soll

 'R = 255
 'G = 255
 'B = 255

'Schleife:
'Goto Schleife


Loop

'Interupthandler, wird bei jedem Timerüberlauf(bei 8 MHz und
Vorteiler=1 alle 32 µSec.)
'aufgerufen:
Tim0_isr:

    If Z = 0 Then
'Gewünschte Ausgabewerte an
    Ri = R
'Hilfsregister übergeben
    Gi = G
    Bi = B
    Z = 255
    End If

    Z = Z - 1

    If Ri > 0 Then                                          'R > Port
B.0
    Portb.0 = 1                                             'bei R =
51 wird hier
    Else                                                    'Port B.0
51 mal auf 1
    Portb.0 = 0                                             'und 205
mal auf 0 geschaltet
    End If                                                  'das
ergibt bei 5V Ub im Mittel
    Ri = Ri - 1                                             ' ca. 1 V
    If Ri = 255 Then Ri = 0

    If Gi > 0 Then                                          'G > Port
B.1
    Portb.1 = 1
    Else
    Portb.1 = 0
    End If
    Gi = Gi - 1
    If Gi = 255 Then Gi = 0

    If Bi > 0 Then
    Portb.2 = 1                                             'B > Port
B.2
    Else
    Portb.2 = 0
    End If
    Bi = Bi - 1
    If Bi = 255 Then Bi = 0

    ' Hier könnten eigentlich noch weitere Kanäle eingefügt werden

    Return


    Prog:

    Timer1 = 25536
    Incr Led

    If Led > 255 Then
    Led = 0
    End If

    Return

von Tobias X. (tobiasblome)


Lesenswert?

so nun dimmen die LED´s beide - gut.
nur wenn die LED 100% erreicht hat geht sie kurz aus und wieder an und
dimmt erst dann wieder runten? Kann mir jemand sagen warum die LED kurz
aus geht?!

________________________

' PULS WEITEN MODULATION (PWM)
' Sprache: Bascom AVR-Basic (Demoversion 1.11.6.2)
' Getestet mit 8 MHz auf 90S2333.Ausgabewert wird dann ca. 121 mal pro
Sekunde
' erneuert.
' 3 Kanäle an Port B0, B1, B2
' Bewust einfaches Programm zur Erklärung wie eine Software PWM
funktionieren kann.
' Timerinterupt, der in gleichmäßigen Abständen
' das Hauptprogramm unterbricht und ein Unterprogramm
(Interupthandler) aufruft
' das die Ausgänge ein oder ausschaltet. Im Mittel ergibt sich dann
der gewünschte
' Ausgabewert (Normale Multimeter sind langsam genug d.h. der
Ausgabewert kann
' für Testzwecke direkt am Pin gemessen werden)
' Sept. 2002 Bernhard T.

'Konfiguration µC:
$regfile = "2313def.dat"
'AT90S2313-Deklarationen
$crystal = 8000000                                          'Quarz: 8
MHz

'Timer konfigurieren, Vorteiler auf 1 ist gut fürs Simulieren (evtl.
anpassen, dann aber ausgang mit R-C Tiefpass)
Config Timer0 = Timer , Prescale = 1
Config Timer1 = Timer , Prescale = 8
Enable Timer1
On Timer1 Prog
'Definiere den Interrupthandler
On Ovf0 Tim0_isr

'Wichtig:
Config Pinb.0 = Output                                      'Port B =
Ausgang
Config Pinb.1 = Output
Config Pinb.2 = Output

Enable Timer0                                               'timer
einschalten
Enable Interrupts
'interrupts  einschalten

Dim R As Byte                                               ' In diese
Variablen muss man
Dim G As Byte                                               ' im
Hauptprogram die gewünschten
Dim B As Byte                                               '
Ausgabewerte laden

Dim Z As Word                                               'Byte
           ' Zähler

Dim Ri As Byte                                              '
Hilfsregister
Dim Gi As Byte
Dim Bi As Byte
Dim Led1 As Byte
Dim Led1m As Byte
Dim Led2 As Byte
Dim Led2m As Byte


Z = 0
Led1 = 255

Do
   'your program goes here
'Bsp.:


 R = 1 + Led2                                               ' bei Ub=
5V ca. 1Volt
 G = 102                                                    ' ca. 2V
 B = 1 + Led1
                                                    ' 5V/256 =
0.01953125 V Soll

 'R = 255
 'G = 255
 'B = 255

'Schleife:
'Goto Schleife


Loop

'Interupthandler, wird bei jedem Timerüberlauf(bei 8 MHz und
Vorteiler=1 alle 32 µSec.)
'aufgerufen:
Tim0_isr:

    If Z = 0 Then
'Gewünschte Ausgabewerte an
    Ri = R
'Hilfsregister übergeben
    Gi = G
    Bi = B
    Z = 255
    End If

    Z = Z - 1

    If Ri > 0 Then                                          'R > Port
B.0
    Portb.0 = 1                                             'bei R =
51 wird hier
    Else                                                    'Port B.0
51 mal auf 1
    Portb.0 = 0                                             'und 205
mal auf 0 geschaltet
    End If                                                  'das
ergibt bei 5V Ub im Mittel
    Ri = Ri - 1                                             ' ca. 1 V
    If Ri = 255 Then Ri = 0

    If Gi > 0 Then                                          'G > Port
B.1
    Portb.1 = 1
    Else
    Portb.1 = 0
    End If
    Gi = Gi - 1
    If Gi = 255 Then Gi = 0

    If Bi > 0 Then
    Portb.2 = 1                                             'B > Port
B.2
    Else
    Portb.2 = 0
    End If
    Bi = Bi - 1
    If Bi = 255 Then Bi = 0

    ' Hier könnten eigentlich noch weitere Kanäle eingefügt werden

    Return


    Prog:

    Timer1 = 25536



    If Led1 = 255 Then
    Led1m = 0
    End If

    If Led1 = 0 Then
    Led1m = 1
    End If

    'Zähler1 hoch/runter
 If Led1m = 1 Then
 Incr Led1                                                  'um 1
hochzählen
 End If

 If Led1m = 0 Then
 Decr Led1                                                  'um 1
runterzählen
 End If


    '-------
    If Led2 = 255 Then
    Led2m = 0
    End If

    If Led2 = 0 Then
    Led2m = 1
    End If

    'Zähler1 hoch/runter
 If Led2m = 1 Then
 Incr Led2                                                  'um 1
hochzählen
 End If

 If Led2m = 0 Then
 Decr Led2                                                  'um 1
runterzählen
 End If


    Return

von Tobias X. (tobiasblome)


Lesenswert?

Hallo,
vielen dank für eure intensive Mithilfe!!!
Das Programm läuft und die LED´s dimmen!!!

Tobias

von Hannes L. (hannes)


Lesenswert?

> vielen dank für eure intensive Mithilfe!!!

Sorry, ich war nicht da. Manchmal habe ich auch Anderes zu tun und bin
dann nicht online.

...

von Tobias X. (tobiasblome)


Lesenswert?

???
das war ernst gemeint und nicht ironisch!

Außerdem hast doch gerade du am meisten geschrieben / geholfen!

Tobias

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.