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.
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
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.
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
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?
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.
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.
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. ^^
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.
> 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.
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.
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.
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.
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.
@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.
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.