Funktionsweise ist folgende:
die PWM-werte werden sortiert und werden mit den zu schaltendem Pins in
das g_pwmData - Felg geschriegen geschrieben.
die overflow-routine schaltet alle Kanäle ein.
und läd den ersten Compare wert.
in der ISR werden die zugehörigen Kanäle abgeschalten und der nächste
Wert geladen.
die ISRs müssen so kurz wie möglich sein, besonders die
TIMER1_COMPA_vect, damit nahe beieinanderliegende PWM-Werte auch die
Interupts bekommen und er nicht noch in dem letzten steckt.
Ich hätte angenommen, das das ganze mit load und store am anfang ende,
sowie 3
LD Rd, [X|Y|Z]+ Load Indirect and Post-Inc.
sowie 3 outs gegessen ist, aber das macht der Compiler daraus:
1
ISR(TIMER1_COMPA_vect)
2
{
3
ef4: 1f 92 push r1
4
ef6: 0f 92 push r0
5
ef8: 0f b6 in r0, 0x3f ; 63
6
efa: 0f 92 push r0
7
efc: 11 24 eor r1, r1
8
efe: 8f 93 push r24
9
f00: af 93 push r26
10
f02: bf 93 push r27
11
f04: ef 93 push r30
12
f06: ff 93 push r31
13
PWM_RGB_PIN = *g_dataPtr; // Pins togglen
14
f08: a0 91 86 01 lds r26, 0x0186
15
f0c: b0 91 87 01 lds r27, 0x0187
16
f10: fd 01 movw r30, r26
17
f12: 81 91 ld r24, Z+
18
f14: 89 b9 out 0x09, r24 ; 9
19
++g_dataPtr;
20
OCR1AH = *g_dataPtr;
21
f16: 11 96 adiw r26, 0x01 ; 1
22
f18: 8c 91 ld r24, X
23
f1a: 80 93 89 00 sts 0x0089, r24
24
++g_dataPtr;
25
OCR1AL = *g_dataPtr;
26
f1e: 81 81 ldd r24, Z+1 ; 0x01
27
f20: 80 93 88 00 sts 0x0088, r24
28
++g_dataPtr;
29
f24: 32 96 adiw r30, 0x02 ; 2
30
f26: f0 93 87 01 sts 0x0187, r31
31
f2a: e0 93 86 01 sts 0x0186, r30
32
}
33
f2e: ff 91 pop r31
34
f30: ef 91 pop r30
35
f32: bf 91 pop r27
36
f34: af 91 pop r26
37
f36: 8f 91 pop r24
38
f38: 0f 90 pop r0
39
f3a: 0f be out 0x3f, r0 ; 63
40
f3c: 0f 90 pop r0
41
f3e: 1f 90 pop r1
42
f40: 18 95 reti
Hat jemand einen Tip warum der Compiler nicht das macht, was ich
erwarte?
gibt es eine Möglichkeit, dem compiler zu sagen, dass er für den
g_dataPtr eins der XYZ register fest alloziert und sonst nirgends
verwendet?
Auch sichert er register, die gar nicht benötigt werden.
@ Vlad Tepesch (vlad_tepesch)
>Ich möchte eine 16bit-Software PWM bauen.>controller ist ein atmega168
Optimist oder Rechenschwäche?
Mein Tipp. Lies mal was über LED-Fading und Soft-PWM, rechne
nochmal nach und vergiss es.
MfG
Falk
naja prinzipiell ist es nur eine 12bit pwm, dh. die 12bit-WErte werden
mit 16 multipliziert.
zumindest hatte ich mir erst mal 12bit vorgenommen, könnte auch noch
versuchen auf 11 zu gehen, das würde die verfügbare Zeit von 16 auf 32
Takte vergrößern.
rechnerisch sollte das möglich sein
der 16bit zähler läuft ohne Vorteiler und es wird immer der nächste
compare wert gesetzt.
man kommt bei 8Mhz auf eine PWM-Frequenz von ~120Hz.
@ Vlad Tepesch (vlad_tepesch)
>naja prinzipiell ist es nur eine 12bit pwm,
Und warum schreibst du das nicht?
> dh. die 12bit-WErte werden mit 16 multipliziert.
Mogelpackung, die rein gar nichts bringt.
>zumindest hatte ich mir erst mal 12bit vorgenommen, könnte auch noch>versuchen auf 11 zu gehen, das würde die verfügbare Zeit von 16 auf 32>Takte vergrößern.
Ich sag mal viel mehr als 10 Bit ist kaum sinnvoll machbar. Und das
reicht auch, siehe Artikel LED-Fading.
>man kommt bei 8Mhz auf eine PWM-Frequenz von ~120Hz.
Macht bei 12 Bit 16 Takte/PWM-Takt. Sportlich. Bleibt wenig Zeit für
andere Sachen.
MFG
Falk
P S Vielleicht könnte man es wie die AVR-Videoausgabeterminals machen,
mit Hilfe des SPI-Moduls. Da hat man den vollen Takt.
Falk Brunner schrieb:
> Ich sag mal viel mehr als 10 Bit ist kaum sinnvoll machbar. Und das> reicht auch, siehe Artikel LED-Fading.
das Fading soll aber extrem langsam sein.
>>>man kommt bei 8Mhz auf eine PWM-Frequenz von ~120Hz.>> Macht bei 12 Bit 16 Takte/PWM-Takt. Sportlich. Bleibt wenig Zeit für> andere Sachen.
für andere Sachen ist mehr als genug Zeit.
es gibt ja nur 3 Kanäle.
Das heißt pro PWM-Zyklus wird 3x die Compare-Routine aufgerufen und
einmal die Overflow-Routine.
macht ~100 Takte pro PWM-Zyklus * 120Hz macht eine Auslastung von 0,15%
bei 8Mhz
Das Problem ist nur, wie kriege ich die (eigendlich total mikrige) ISR
klein.
Ich habe gehofft, das mir einer der ASM-Profis hier weiterhelfen kann.
@ Vlad Tepesch (vlad_tepesch)
>das Fading soll aber extrem langsam sein.
Immer diese Extrawünsche. Dann nimm einen AVR mit drei 16-Bit
Hardware-PWMs oder drei Tinys. Fettig.
MfG
Falk
@holger (Gast)
>>macht ~100 Takte pro PWM-Zyklus * 120Hz macht eine Auslastung von 0,15%>>bei 8Mhz>120Hz * 12Bit => 120 * 4096 => 491,5kHz PWM Frequenz!
Richtig. Für Hardware ein Klacks. In Software schon SEHR heftig.
MFG
Falk
Falk Brunner schrieb:
> @ Vlad Tepesch (vlad_tepesch)>>>das Fading soll aber extrem langsam sein.>> Immer diese Extrawünsche. Dann nimm einen AVR mit drei 16-Bit> Hardware-PWMs oder drei Tinys. Fettig.>
da habe ich noch keinen gefunden.
sollte möglichst klein und dip sein.
Aber hier ist der mega168 gessetzt.
Vielleich tkönnen wir uns auf das Problem konzentrieren.
Eine feste Allocation eines der XYZ-Register würde hier enorm helfen.
dann könnte es meiner Meinung nach so aussehen:
1
push r0
2
in r0, 0x3f
3
push r0
4
ld r0, Z+
5
out 0x09, r0
6
ld r0, Z+
7
sts 0x0089, r0
8
ld r0, Z+
9
sts 0x0088, r0
10
pop r0
11
out 0x3f, r0
12
pop r0
13
reti
das wären 25 Takte, würde immerhin für 11bit Reichen
aber wie überrede ich den compiler zu dieser festen Allokation und dazu
den sinnlos großen Frame um die ISR wegzulassen
holger schrieb:
>>macht ~100 Takte pro PWM-Zyklus * 120Hz macht eine Auslastung von 0,15%>>bei 8Mhz>> 120Hz * 12Bit => 120 * 4096 => 491,5kHz PWM Frequenz!
Mir scheint du hast das falsch verstanden.
es läuft ein 16bit-Timer bei 8Mhz ohne prescaler mit -> ~120 Hz
die pwm ist 12 bit, da aber der Prescaler recht grob ist werden die
12bit werte gespreizt, damit zwischen zwei nebeneinanderliegenden Werten
genug zeit für die PWM-administrierung bleibt (16 reichen nicht - wie
gerade ermittelt)
Ich ätte auch einen prescaler von 8 nehmen können und alle 12bit-werte
mit 2 multiplizieren können.
die PWM-LUT sieht also so aus:
0, 16, 16, 16, 32, 32, .. 65000
Das problem besteht also nur im unteren bereich der PWM. im oberen
bereich sind die Compare-Werte sowso weit genug auseinander, das
genügend zeit zum Umladen bleibt.
edit:
Das problem ist also nicht die Gesammtrechenzeit der PWM, sondern das
Interupts verloren gehen, wenn 2 PWM-Werte dicht beieinander liegen und
ISR boch nicht beendet ist, wenn der nächste Compare schon erreicht ist.
Vlad Tepesch schrieb:
> Ich ätte auch einen prescaler von 8 nehmen können und alle 12bit-werte> mit 2 multiplizieren können.
hier hätte der timer aber im CTC modus bis 8196 laufen müssen, so dass
man einen zusätzlichen outputcompare verliert.
Vlad Tepesch schrieb:
> Falk Brunner schrieb:>> @ Vlad Tepesch (vlad_tepesch)>>>>>das Fading soll aber extrem langsam sein.>>>> Immer diese Extrawünsche. Dann nimm einen AVR mit drei 16-Bit>> Hardware-PWMs oder drei Tinys. Fettig.>>> da habe ich noch keinen gefunden.>> sollte möglichst klein und dip sein.>> Aber hier ist der mega168 gessetzt.>
Auch wenn der gesetzt ist: Mega163 hat 2 16-Bit Timer mit je 2
unabhängigen PWMs. Dafür halt keinen AD.
Christian T. schrieb:
> Auch wenn der gesetzt ist: Mega163 hat 2 16-Bit Timer mit je 2> unabhängigen PWMs. Dafür halt keinen AD.
laut Datenblatt hat der (wie der Nachfolger auch) 2 8bit- und 1
16bit-Timer
und insgesammt 3 PWM-Kanäle
Wie gesagt, ich such schon seit ner weile nach nem schön kleinen
Controller in DIP mit 3 16bit pwm Kanälen.
Am liebsten wär mir ein 8Pinner.
wobei max. 24 auch noch ok wär.
aber 3 16bit pwm-Kanäle haben nur ndie dicken, dies auch gar nicht in
DIP gibt
unter zuhilfenahme der GPIORs zum sichern der Register lässst scih das
ganze nochmal verkürzen.
1
in 0x2A, r0 // R0 -> GPIOR1
2
in 0x1E, 0x3f // SREG -> GPIOR0
3
ld r0, Z+
4
out 0x09, r0 // r0 -> PIND
5
ld r0, Z+
6
sts 0x0089, r0 // r0 -> OC1AH
7
ld r0, Z+
8
sts 0x0088, r0 // r0 -> OC1AL
9
out 0x3f, 0x1E // GPIOR0 -> SREG
10
out r0, 0x2A // GPIOR1 -> R0
Ich glaube kürzer geht es nicht.
das sind jetzt 19 Takte
Ich habe leider immer noch keinen Plan, wie man dem Compiler verbietet
das Z-Register zu benutzen.
wenn man dieses noch sichern/wiederherstellen muss und den Pointer erst
da rein laden und zurück speichern muss macht das nochmal 16 Takte
>Ich glaube kürzer geht es nicht.>das sind jetzt 19 Takte
Da kommen dann noch 7 Takte für die Interrupt-Verarbeitung hinzu (4
Takte interrupt execution response time + 3 Takte für den Sprung in die
ISR). Und nach dem Reti wird ein Befehl des Hauptprograms abgearbeitet,
das sind dann nochmals 1 oder 2 Takte.
Oliver
Oliver schrieb:
> Da kommen dann noch 7 Takte für die Interrupt-Verarbeitung hinzu (4> Takte interrupt execution response time + 3 Takte für den Sprung in die> ISR).> Und nach dem Reti wird ein Befehl des Hauptprograms abgearbeitet,> das sind dann nochmals 1 oder 2 Takte.
könnten also auch 4 Takte sein, wenn er gerade an einem RET hängt
macht also zusätzliche 11
da das mit dem Register ach nicht zu klappen scheint kommen die oben
erwähnten 16 also auch noch dazu
macht insgesamt 46 Takte.
und damit 46 16bit-PWM-Steps Mindestabstand zwischen 2 PWM-Stufen
Macht rund 10,5bit PWM
Oder ich lege die Stufen doch näher, fasse aber, wenn mehrere Kanäle
näher zusammen liegen als 50 Takte diese zu einem Schaltvorgang
zusammen.
Mal sehen, ob das auffällt.
Ich befürchte aber, dass das im ohnehin empfindlichen unteren Bereich
auffällt.
Das erste müsste ein out sein, und das zweite geht so grundsätzlich
nicht. Du kannst nicht direkt den Inhalt von einem SFR zu einem anderen
SFR verschieben.
Ist Unsinn und unnötig wenn man keine Befehle verwendet, die das SREG
verändern, ld, sts, in & out wie im dargestellten Code haben keinen
Einfluss auf das SREG.
Vlad Tepesch schrieb:
> Ich habe leider immer noch keinen Plan, wie man dem Compiler verbietet> das Z-Register zu benutzen.> wenn man dieses noch sichern/wiederherstellen muss und den Pointer erst> da rein laden und zurück speichern muss macht das nochmal 16 Takte
Wieso 16? Ich komme auf 8.
>> Ist Unsinn und unnötig wenn man keine Befehle verwendet, die das SREG> verändern, ld, sts, in & out wie im dargestellten Code haben keinen> Einfluss auf das SREG.
stimmt.
Ich hab nur im automatisch generierten Code geschaut und gesehen, dass
da das Status-Regsiter gerettet wird und gedacht "stimmt, das muss auch"
Ich bin ein wenig schockiert über den gcc, dass er nicht nur die
Register sichert, die wirklich verändert werden.
Gerade dass wär so einfach.
Stefan Ernst schrieb:
> Wieso 16? Ich komme auf 8.
push R30
push R31
ld R30, Highbyteadresse von pointer
ld R31, Lowbyteadresse von pointer
...rest von oben
st Highbyteadresse von pointer, R30
st Lowbyteadresse von pointer, R30
pop R31
pop R30
mach t 8 instructionen à 2 Takte = 16 zusätzliche Takte
Detlef _a schrieb:
> Habe Beispielcode als Anschauungsmuster angehängt. (naked) spart Dir die> push/pop Orgie.
ok, Danke.
Das mit dem Naked habe ich schon mal irgendwo gesehen.
Detlef _a schrieb:
> möglicherweise fällt einem ja noch ein anderer Algorithmus ein, der das> Problem nahe beieinanderliegender Werte strukturell besser angeht, da> gibts bestimmt was.
Naheliegende Werte müssen ja nur beim setzen neuer PWM-Werte
berücksichtigt werden, das ist an sich kein Problem, gleiche Werte
werden ja auch schon zusammengefasst, man müsste nur die
Gleichheitsbedingung anpassen.
Die Frage ist, wie es aussieht, wenn man die einfach gleichzeitig
schaltet, da damit eine PWM-Stufe ja übersprungen wird.
Edit:
@detlef:
kanns du noch fix sagen, wie ich die Speziellen Register per namen
(OC1AH, oder GPIOR0, ...) in den inline-Asm geben kann?
Vlad Tepesch schrieb:
> Stefan Ernst schrieb:>> Wieso 16? Ich komme auf 8.> ...> mach t 8 instructionen à 2 Takte = 16 zusätzliche Takte
Peinlich, hatte ich doch glatt die push/pops vergessen. ;-)
> kanns du noch fix sagen, wie ich die Speziellen Register per namen> (OC1AH, oder GPIOR0, ...) in den inline-Asm geben kann?
Ich sehe nicht, welchen Vorteil dir hier Inline-ASM bringen soll. Du
machst dir die Sache einfacher, wenn du die ISR in eine separate
ASM-Datei packst.
Stefan Ernst schrieb:
> Ich sehe nicht, welchen Vorteil dir hier Inline-ASM bringen soll. Du> machst dir die Sache einfacher, wenn du die ISR in eine separate> ASM-Datei packst.
Vorteile wären:
- die ISR da ist, wo sie thematisch hingehört.
- die benötigten Variablen sind direkt verfügbar.
(wie bekomme ich in einer separaten asm-Date die Adresse der
Pointer-Variablen)
- ein File weniger
Vlad Tepesch schrieb:
> - die benötigten Variablen sind direkt verfügbar.> (wie bekomme ich in einer separaten asm-Date die Adresse der> Pointer-Variablen)
Variable in C-Datei:
>> (wie bekomme ich in einer separaten asm-Date die Adresse der Pointer-Variablen)
über ein .extern Commando in der asm-Datei
>>kanns du noch fix sagen, wie ich die Speziellen Register per namen
(OC1AH, oder GPIOR0, ...) in den inline-Asm geben kann?
nee, weiß ich nicht aus dem Stehgreif, hägt auch davon ab, wie das
#define OC1AH jetzt aussieht. Würde aber tippen entweder lds r10,(OC1AH)
oder dasgleiche ohne Klammer, einfach ins produzierte Assemblerlisting
kucken.
Cheers
Detlef
Detlef _a schrieb:
> nee, weiß ich nicht aus dem Stehgreif, hägt auch davon ab, wie das> #define OC1AH jetzt aussieht. Würde aber tippen entweder lds r10,(OC1AH)> oder dasgleiche ohne Klammer, einfach ins produzierte Assemblerlisting> kucken.
naja, so einfach war das nicht - hatte ich letztens gelesen.
der asm-Preprozessor geht über das inline-asm nicht mehr drüber und kann
die nicht mehr ersetzen.
Irgendwie musste man die dem per % und M(SFR...) oder sowas
reinreichen.
Detlef _a schrieb:
>>> (wie bekomme ich in einer separaten asm-Date die Adresse der
Pointer-Variablen)
>> über ein .extern Commando in der asm-Datei
Das hat aber nur einen rein dokumentarischen Zweck. Der Assembler
betrachtet sowieso alle unbekannten Symbole als extern.