Forum: Mikrocontroller und Digitale Elektronik Tiny >> Software-PWM >> Epic-Fail


von Tiny10Nutzer (Gast)


Lesenswert?

Ich habe folgendes Problem:

Ich sitze jetzt über dem nachstehendem Program inzwischen über eine 
Woche. Der Übersicht wegen habe ich mein Program jetzt auch auf die 
wichtigen Teile verkürtzt. Es soll eine Software-PWM-Steuerung sein. 
Gebe ich das Programm in den AVR-Studio-4-Simulator, sieht alles richtig 
aus. Alle Register und Ports nehmen zum richtigen Zeitpunkt die 
richtigen Werte an. Es wird aber bereits im Simulatür deutlich, dass die 
Routinen deutliche schneller aberufen werden, als wie ich sie durch 
Schleifen verzögert habe. Die Realität im echten Tiny sieht dabei noch 
übler aus.

In meinem Programm wird PORTB1 so schnell auf 0 und 1 gesetzt, dass man 
nur eine gedimmte LED sehen sollte. Die LED am PORTB1 leuchtet aber zum 
einen generell auf volle Kraft (auch bei channel0value = 0x00) und zum 
anderen bekomme ich nur ein sinnloses Geflacker, jenseits von gut und 
böse.

Lasse ich ein anderes TestProgramm laufen, das einfach nur ein Blinken 
erzeugt, bekommei ch keine Probleme.
1
.include "tn13Adef.inc"
2
 
3
.def tmp = r16
4
.def output = r17
5
.def itmp = r18
6
.def isreg = r19
7
8
.def softpwmband = r20
9
.def softpwmcount = r21
10
.def channel0value = r22
11
.def count1 = r23
12
 
13
.org 0x0000
14
        rjmp    reset_order        ; Reset Handler
15
.org 0x0006
16
        rjmp    timer0_compare_match    ; Timer Interrupt Handler
17
18
19
reset_order:
20
 ldi     tmp, low(RAMEND)    ; Stackpointer initialisieren
21
 out     SPL, tmp
22
23
 ldi softpwmband, 0x20 ; PWM Auflösung 
24
 ldi     softpwmcount, 0x00
25
 ldi channel0value,0x00 ; PWM Einstellung
26
 ldi     count1, 0x00
27
        
28
 ldi     output, 0x03      ; Port B 0 und 1 auf Ausgang
29
 out     DDRB, output
30
 out     PORTB, output    ; Port B 0 und 1 auf High
31
 
32
 ldi     tmp, 1 << CS00     ; CS00 setzen: für Timerzähler kein Vorteiler
33
 out     TCCR0B, tmp
34
 ldi     tmp, 0x1D          ; wenn timerzähler 30-1 erreich interruppt auslösen
35
 out     OCR0A, tmp
36
 ldi     tmp, 1 << OCIE0A     ; OCIE0A: Interrupt bei Timer Vergleich A
37
 out     TIMSK0, tmp
38
 ldi     tmp, 1 << WGM01
39
 out     TCCR0A, tmp
40
; ldi     tmp, 1 << OCF0A   ; Timer rücsetzen bei Vergleich A
41
; out     TIFR0, tmp
42
 sei              ; Interrupt erlauben         
43
  rjmp mainloop  
44
       
45
timer0_compare_match:          ; Timer 0 Interrupt Handler
46
 in isreg, SREG 
47
48
 cp softpwmcount,channel0value   ; Vergleiche Zähler mit PWM-Einstellung
49
 in itmp, SREG ; das SReg in Register laden
50
 bst itmp, 2   ; das Negativ Bit 2 aus Register in das T-Flag schreiben
51
 bld output,1 ; schreibt das T-Flag in das Zielbit von output
52
 out PORTB,output ; auf das PortB schreiben
53
 
54
 inc softpwmcount                ; Interner und Externer Zähler +1
55
 inc count1                    
56
57
 cp softpwmcount, softpwmband ; wenn PWM-Durchlauf das Ende erreicht hat
58
 breq pwmcycleback ; erneut beginnen
59
60
 out SREG,isreg
61
  reti ; Interrput braucht bis hier 15 CPU-Zyklen
62
63
pwmcycleback:
64
 ldi softpwmcount,0x00 ; neuer PWM-Durchlauf
65
66
 out SREG,isreg
67
  reti ; Interrupt braucht bis hier 17 CPU-Zyklen
68
69
mainloop:   
70
  rjmp mainloop ; mainloop braucht bis hier 1 CPU-Zyklen

Um dann mal in die Runde zu fragen: Was würde Sheldon tun?

Wenn auch noch einer weiß wie ich die Interrupt-Routine um weitere 
Zyklen verkürzen kann bin ich auch ganz Ohr.

Grüße

von Detlef K. (adenin)


Lesenswert?

Tiny10Nutzer schrieb:
> Ich habe folgendes Problem:
> Der Übersicht wegen habe ich mein Program jetzt auch auf die
> wichtigen Teile verkürtzt.

Das ist mit Sicherheit das Problem.

von Tiny10Nutzer (Gast)


Lesenswert?

Das Program das du siehst ist so vollständig und läuft. Alles was 
darunter steht verwirrt hier nur und lenkt vom eigendlichen Problem ab.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Tiny10Nutzer schrieb:
> ldi     tmp, 1 << CS00     ; CS00 setzen: für Timerzähler kein
> Vorteiler
>  out     TCCR0B, tmp
>  ldi     tmp, 0x1D          ; wenn timerzähler 30-1 erreich interruppt
> auslösen
>  out     OCR0A, tmp
>  ldi     tmp, 1 << OCIE0A     ; OCIE0A: Interrupt bei Timer Vergleich A
>  out     TIMSK0, tmp
>  ldi     tmp, 1 << WGM01
>  out     TCCR0A, tmp

Ich kann dir nur empfehlen, die Initialisierung des Timers andersherum 
zu machen, d.h. erst mal den Mode setzen (CTC, sieht richtig aus), dann 
TOP in OCR0A, und zum Schluss Timer starten und IRQ freigeben.

Tiny10Nutzer schrieb:
> cp softpwmcount,channel0value   ; Vergleiche Zähler mit PWM-Einstellung
>  in itmp, SREG ; das SReg in Register laden
>  bst itmp, 2   ; das Negativ Bit 2 aus Register in das T-Flag schreiben
>  bld output,1 ; schreibt das T-Flag in das Zielbit von output
>  out PORTB,output ; auf das PortB schreiben

Das sieht unnötig kompliziert aus und beschreibt als Seiteneffekt den 
ganzen Port. Das möchtest du wahrscheinlich gar nicht. Wie wäre es mit
1
 cp softpwmcount,channel0value   ; Vergleiche Zähler mit PWM-Einstellung
2
 brcc pnoset   ; für invertiert kann man einfach brcs schreiben.
3
 sbi PORTB,1
4
 rjmp outofhere
5
pnoset:
6
 cbi PORTB, 1
7
outofhere:                
8
 inc softpwmcount   ; etc. pp.
Als Seiteneffekt kannst du auf die Art auch gleich mehrere Kanäle 
behandeln, wenn das mal kommt. Ürigens wird count1 in der ISR zwar hoch 
gezählt, scheint aber sonst keine Funktion zu haben. Wird auch nicht 
zurückgesetzt?

von Detlef K. (adenin)


Lesenswert?

Tiny10Nutzer schrieb:
> Das Program das du siehst ist so vollständig und läuft.

:)

> Alles was darunter steht verwirrt hier nur und lenkt vom eigendlichen Problem 
ab.

Könnte mal bitte Jemand nachfragen lassen, was denn das "eigendlichen 
Problem" ist.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Detlef Kunz schrieb:
> Könnte mal bitte Jemand nachfragen lassen, was denn das "eigendlichen
> Problem" ist.
Schreibt er doch:
Tiny10Nutzer schrieb:
> Die LED am PORTB1 leuchtet aber zum
> einen generell auf volle Kraft (auch bei channel0value = 0x00) und zum
> anderen bekomme ich nur ein sinnloses Geflacker, jenseits von gut und
> böse.
@TE: Wenn dir übrigens ein Kanal reicht, kannste mal probieren, echte 
Hardware PWM über OCR0B zu erzeugen. Also nach wie vor den CTC Mode 
benutzen, aber den OC0B Ausgang auf noninverted COM schalten und in 
OCR0B den Kanalwert zu schreiben. Dann läuft das alles ohne extra ISR.

: Bearbeitet durch User
von Detlef K. (adenin)


Lesenswert?

Matthias Sch. schrieb:
> Detlef Kunz schrieb:
>> Könnte mal bitte Jemand nachfragen lassen, was denn das "eigendlichen
>> Problem" ist.
> Schreibt er doch:
> Tiny10Nutzer schrieb:
>> Die LED am PORTB1 leuchtet aber zum
>> einen generell auf volle Kraft (auch bei channel0value = 0x00) und zum
>> anderen bekomme ich nur ein sinnloses Geflacker, jenseits von gut und
>> böse.
> @TE: Wenn dir übrigens ein Kanal reicht, kannste mal probieren, echte
> Hardware PWM über OCR0B zu erzeugen. Also nach wie vor den CTC Mode
> benutzen, aber den OC0B Ausgang schalten und in OCR0B den Kanalwert zu
> schreiben. Dann läuft das alles ohne extra ISR.

Der TE erlaubt sich einen Spaß, weil:

Tiny10Nutzer schrieb:
> Das Program das du siehst ist so vollständig und läuft.

Und das tut es auch machen tun. ^^

von Tiny10Nutzer (Gast)


Lesenswert?

Im AVR-Simulator läuft das Program auch. Nur im Tiny grad nicht richtig 
(nur so halb).

Ich möchte 5 bzw. 6 PWM ansteuern. Da ich sicherlich nicht zwei 
Hardware-PWMs nutze, nur um dann drei weitere als Software umzusetzen, 
ist klar.

Der Count1 wird später ausserhalb des Interrupts genutzt (korrekte 
Verzögerungszeiten).

Die Timereinstellungen werde ich nachher mal so umstellen wie du es 
vorgeschlagen hast. Die Vorschläge mit den Sprungbefehlen sieht aber auf 
den ersten Blick deutlich länger aus, als das was ich da fabriziert 
habe. Ich will ja hier möglichst wenige CPU-Zyklen verbraten. Mit meiner 
Lösung muss ich für jeden weiteren Kanal nur drei Befehle (3 CPU-Zyklen) 
hinzu schreiben.

von Karl H. (kbuchegg)


Lesenswert?

>  cp softpwmcount,channel0value   ; Vergleiche Zähler mit PWM-Einstellung
>  in itmp, SREG ; das SReg in Register laden
>  bst itmp, 2   ; das Negativ Bit 2 aus Register in das T-Flag schreiben
>  bld output,1 ; schreibt das T-Flag in das Zielbit von output
>  out PORTB,output ; auf das PortB schreiben

Hä?

Ehe ich da jetzt darüber nachdenke:
Warum schreibst du das alles nicht einfach mal ganz konventionell. So 
richtig mit Compare, Branch if less und dergleichen, ehe du dann 
anfängst rumzutricksen.


> ldi     tmp, 0x1D          ; wenn timerzähler 30-1 erreich interruppt auslösen

Wenn du 29 meinst, dann schreib auch 29
1
   ldi    tmp, 29
oder wenn dir die 30 wichtig sind
1
   ldi,   tmp, 30 - 1
Es gibt keinen Grund sich selbst da das Leben durch Verwendung von 
Hex-Zahlen selber schwer zu machen.

Welche PWM-Frequenz ist eigentlich angestrengt?
Eine Aufrufhäufigkeit von alle 30 Takte ist dann schon sehr sportlich, 
um es mal so zu sagen.

von Karl H. (kbuchegg)


Lesenswert?

Im übrigen.
Im AVR-Tutorial gibt es ein Beispiel mit mehreren Soft-PWM. War 
glaub ich bei der Besprechung, wie man mit dem SRAM umgeht.

OK, das ist konventionell und nicht getrickst programmiert. Schon fast 
langweilig. Aber es funktioniert. Es ist zwar für einen Mega8 gemacht, 
aber das lässt sich ja ummodeln.

: Bearbeitet durch User
von Tiny10Nutzer (Gast)


Lesenswert?

Das Tricksen hat schon seinen Grund. Ich möchte mit so wenig Aufwand wie 
möglich mehr oder weniger Kanäle ansteuern und dabei so wenige 
CPU-Zyklen wie möglich zu verbrauchen. Der Trick hat einen Sondereffekt. 
Wenn ich während eines PWM-Durchlaufs die PWM-Einstellung ändere, wird 
diese in Echtzeit übernommen, ohne dass ich dafür Extraprogramme 
schreiben muss. Weiter versuche ich das Interrupt-Durchlauf so konstant 
wie möglich zu halten (also nicht mal so viele Zyklen mal so viele 
Zyklen).

Was vieleicht noch schön währe ist, dass ich das T-Flag direkt auf das 
PortB ausgeben kann, ohne über das Register output zu gehen.

Anstreben tu ich so halbwegs 1 kHz. Aktuell bin ich bei 1,2 MHz 
(Originaltakt des Tiny) durch 30 (alle 30 CPU-Zyklen ein Interrrupt) 
durch Auflösung des PWM (zwischen 0 und 255 wählbar und bei angestrebt 
200). Macht 200 Hz. Später kann ich locker den CPU-Takt auf 4 MHz setzen 
und ich habe 666 Hz. Da es hier nur um LEDs geht, reicht das vollkommen 
aus. Aber das ist auch grade ein anderes Thema.

Alle 30 Zyklen einen Interrupt auslösen ist so richtig. Ein CPU-Takt 
geht dabei immer verlohren. Also 30-1. Das ich die Zahl jetzt einfach so 
da rein schreiben kann wuste ich nicht.

von Karl H. (kbuchegg)


Lesenswert?

Tiny10Nutzer schrieb:
> Das Tricksen hat schon seinen Grund. Ich möchte mit so wenig Aufwand wie
> möglich mehr oder weniger Kanäle ansteuern und dabei so wenige
> CPU-Zyklen wie möglich zu verbrauchen. Der Trick hat einen Sondereffekt.

Du kennst Donald Knuth?

Das war einer der ganz großen Pioniere der Informatik.
Er hat (unter anderem) gesagt: Preamture optimization is the root of all 
evil.

So wie ich das sehe, dürfte er recht gehabt haben, wenn du über eine 
Woche lang an einer simplen Soft-PWM hängst.

: Bearbeitet durch User
von Boris O. (bohnsorg) Benutzerseite


Lesenswert?

Karl Heinz schrieb:
> Donald Knuth?
>
> Das war einer der ganz großen Pioniere der Informatik.
> Er hat (unter anderem) gesagt: Preamture optimization is the root of all
> evil.

PWM mit 666Hz ist die Wurzel allen Übels. Eine LED so präzise 
anzusteuern erfordert m.E. noch eine Echtzeit-Uhr und eine peinlich 
genaue Synchronisation des Mikrocontroller-Taktes. Es sollen ja nicht 
einmal 667Hz und einmal 665Hz sein.

Für mehr PWM-Kanäle darf ruhig ein größerer Mikrocontroller eingesetzt 
werden. Es gibt so viele davon, dass man bei den geringen Stückkosten 
von ca. 2 Euro (inkl. MwSt) keine 8 Arbeitsstunden investieren muss, ein 
bereits gelöstes Problem zu lösen. Mit einem Sockel auf der Platine 
ließen sich sogar Bausteine nach Speicherbedarf und Leistungsvermögen 
tauschen. Mit etwas geschicktem Layout und ein paar Steck- oder 
Lötbrücken gehen sicher auch verschiedene Gehäusegrößen. Ein 
nimmerversiegender Quell unendlicher Möglichkeiten LEDs zu betreiben.

von Magic S. (magic_smoke)


Lesenswert?

Solange beim Controller der Deckel noch zu ist, reichts nicht für 
"epic"!

von Tiny10Nutzer (Gast)


Lesenswert?

@Boris Ohnsorg
Das Problem liegt nicht in der Qualität, sondern in der Quantität. Ich 
brauche einige Hundert Chips, die je 5 LEDs ansteuern und einen Pin für 
Kommunikation frei halten. Die 200 AtTiny13A die ich hier habe kosten 
0,34 € das Stück.

Ihr mögt auch alle Recht haben. Nur ich habe jetzt und hier das Problem, 
dass mein Program auf dem Simulator läuft, auf dem Chip selbst aber 
nicht.

von Karl H. (kbuchegg)


Lesenswert?

Wie ich immer.
Ich denke, das Programm müsste gehen.

Wie sieht die Hardware aus?
Abblockkondensatoren?

von Tiny10Nutzer (Gast)


Lesenswert?

Ich verwende einen im IC-Sockel integrierten Kondensator. Dazu 
AVR-Dragon. AVR-Studio-4.19 sagt: Chip Indentvy Ok > Erease Ok > Flash 
Ok > Verify Ok.

von Ulf H. (ulfh) Benutzerseite


Lesenswert?

Karl Heinz schrieb:
> Du kennst Donald Knuth?
> Das war einer der ganz großen Pioniere der Informatik.

Er war es nicht, er ist es.

von Karl H. (kbuchegg)


Lesenswert?

Tiny10Nutzer schrieb:
> Ich verwende einen im IC-Sockel integrierten Kondensator.


hmm.
sollte iegentlich reichen.

Versuch doch mal rauszukriegen, ob du ständige Resets hast, weil du dir 
mit der PWM die Versorgungspannung zerdepperst.
In die Richtung würd ich das jetzt mal ansetzen, wenn im Simulator alles 
ok ist und beim langsamen Blinken nichts passiert.

: Bearbeitet durch User
von Uwe (Gast)


Lesenswert?

> im IC-Sockel integrierten Kondensator
An welchen Pins ist der denn drann ?

von Detlef K. (adenin)


Lesenswert?

Ich würde ja mal fragen, ob er den Reset disabled hat, aber ich will 
nicht.

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.