Ich wundere mich gerade ein wenig, den Artikel über Soft-PWM hier habe ich gelesen. timer1, 1 Kanal direkt via OCR1A, top OCR1C, 3 Kanäle via Software Sortieren, Masken, nächsten PWM-Kanal via OCR1B-Interrupt etc, alles gemacht, funktioniert auch - bis auf einen Fall: 2 Werte unterscheiden sich nur um ein LSB, dann werden die folgenden Kanäle nicht bedient Probeweise den Vorteiler erhöht - änderte nichts. unsigned char OCR1B_reload[3]={10,20,30}; //klappt unsigned char OCR1B_reload[3]={10,11,30}; //Kanal 1 ok, 2 und 3 nicht Die Software hatte auf einem Mega88 funktioniert (denke ich zumindest, vielleicht ist es auch nur nicht aufgefallen, habe aber leider keinen hier um das mal zu testen). In der Simulation funktionert es aber mit dem 88er, mit dem Tiny25 aber auch nicht. Man kann das Problem eigentlich auf folgendes herabbrechen: // Timer1 output compare B interrupt service routine interrupt [TIM1_COMPB] void timer1_compb_isr(void) { OCR1B++; //eigentlich kommt der neue Wert aus einem sortierten array } In Wirklichkeit passiert natürlich noch ein bisschen mehr, aber diese eine Zeile zeigt das Problem. Der Interrupt mit OCR1B+1 kommt nicht. Den Mega88-Timer hatte ich auch nur mit 8bit laufen, top on ICR1.
{ OCR1B++; } Als ASM-Doof-ling kann ich nur beisteuern, dass OCR1B kein Standard-Register, sondern ein I/O-Register ist. Fürs Inkrementieren muss es in ein Standard-Register gelesen werden, dieses inkrementieren und den neuen Inhalt wieder nach OCR1B schreiben. Ist ja eine tolle Erleichterung, wenn die Libraries bei Tiny und Mega nicht vergleichbar arbeiten! Dann lieber gleich ASM.
Daran liegt es nicht, das macht der Compiler schon richtig ; 0000 0045 OCR1B++; 000059 b5eb IN R30,0x2B 00005a 5fef SUBI R30,-LOW(1) 00005b bdeb OUT 0x2B,R30 Und genau in der Art muss man es in Assembler auch machen. Hat auch mit libraries überhaupt nichts zu tun. Ich incrementiere auch nicht wirklich, sondern laden den nächsten OCR-Wert aus einem array nach. Das mit OCR1B++ ist nur der Einfachheit halber (nächster Wert soll wieder einen OCR1B-Int auslösen. Ich habe im Datenblatt keinen Hinweis gefunden, dass das nicht gehen sollte. Erste Vermutung, dass der Interrupt zu lange dauert und deshalb den nächsten nicht erwischt. Ist aber auch nicht das Problem. Vorteiler original ist 64, ISR dauert um die 30 Takte. Mit Vorteiler 128 dasselbe. Erhöhe ich OCR1B nur um eins, funktioniert das beim Tiny25-Timer1 offensichtlich nicht. Und das ist Mist. Das Problem müsste eigentlich schon mal jemand gehabt haben, wenn es denn tatsächlich eins ist. Gefunden habe ich darüber nichts, deswegen denke ich, dass ich das Problem bin und nicht der arme kleine Tiny. Nur fällt mir nichts mehr ein, da habe ich schon ein paar Stunden draufrumgekaut :-(
Falls niemand sonst helfen will - erklär mir doch mal "auf ASM", was SW-PWM mit > unsigned char OCR1B_reload[3]={10,20,30}; //klappt > unsigned char OCR1B_reload[3]={10,11,30}; //Kanal 1 ok, 2 und 3 nicht bedeuten soll. Manchmal wird einem beim Erklärungsversuch schon klar, was man sich vorher nicht selbst klar gemacht hat. ;-)
Auch das hat mit meinem eigentlichen Problem nichts zu tun. Assembler kann ich aber nicht. Grober Ablauf: -der timer1_ov setzt alle Ausgänge, deren PWM-Wert grösser 0 ist -ins OCR1B-Register wird der kleinste PWM-Wert aller Kanäle geschrieben -der folgende OCR1B-Interrupt setzt den (oder die Ausgänge, falls gleiche Werte), die diesem PWM-Wert entsprechen zurück und lädt den nächstgrösseren PWM-Wert usw und irgendwann ganz von vorne Funktioniert auch alles prächtig, solange sich 2 PWM-Werte um min. 2 unterscheiden. Unterscheiden sie sich aber nur um 1, wird der eigentlich nächste auszuführende OCR1B-Int nicht ausgeführt. Damit entfällt dann sowohl das Abbrechen des zweiten Kanals und logischerweise damit auch das Nachladen des 3.Kanals. PWM1_Sollwert 10 PWM2_Sollwert 20 PWM3_Sollwert 30 -> alles ist gut PWM1_Sollwert 10 -> funktioniert PWM2_SOllwert 11 -> funktioniert nicht, Kanal bleibt bei 100% PWM3_Sollwert egal -> auch 100% Es spucken auch keine anderen Interrupts dazwischen, ich habe alles andere rausgeschmissen - Problem bleibt. Und wenn man dann sowas macht (Pseudocode) pwm[0]=50; pwm[1]=80; test: for (loop=0;loop<255;loop++) {pwm[2]=loop; set_pwm(); //sortieren, Masken setzen etc delay_ms(100); //nein, nicht wirklich } goto test; //auch nicht wirklich entstehen lustige Sachen bei loop=49, 51 79 und 81
Gerhard schrieb: > PWM3_Sollwert 30 -> alles ist gut > > PWM1_Sollwert 10 -> funktioniert > PWM2_SOllwert 11 -> funktioniert nicht, Kanal bleibt bei 100% > PWM3_Sollwert egal -> auch 100% > > Es spucken auch keine anderen Interrupts dazwischen, ich habe alles > andere rausgeschmissen - Problem bleibt. > > Und wenn man dann sowas macht (Pseudocode) > delay_ms(100); //nein, nicht wirklich > goto test; //auch nicht wirklich > > entstehen lustige Sachen bei loop=49, 51 79 und 81 Das komplette Programm liegt nicht vor. Durch subjektive Kommentare, die aber nur für den OP bestimmt sinnvoll sind und viel Bla Bla, die den Programmcode anscheinend ersetzten sollen, ist es mir einfach zu mühsam dem OP zu folgen und dem Problem näher zu rücken.
Jakob schrieb: > Dann lieber gleich ASM. Das hat nichts mit ASM zu tun, daß Atmel die Timerfunktionen bei jedem AVR-Derivat neu gemischt hat. Mich hat das auch schon aufgeregt, daß es keine einheitlichen Timer gibt.
Lieber jedeZweite, danke für den äusserst wertvollen und kurzweiligen Kommentar, den rahme ich mir ein! Mit der einen Zeile war eigentlich alles wichtige gesagt. Es ist tasächlich so: der Timer1 bekommt es nicht gebacken, 2 direkt aufeinanderfolgende OCR1x-Werte zu verarbeiten. Immerhin ist der Simulator so nett und macht es wie der reale Prozessor. Gerade mit den Timern habe ich mit AVR-Studio schon nette Sachen erlebt Mit dem Timer0 klappt es wie erwartet, da müsste ich allerdings alle 4 Kanäle via Soft-PWM bedienen, da der wiederum im fast-PWM-Mode OCR0A/B wohl erst bei Überlauf übernimmt, auch wenn nur einer als Hardware-PWM arbeitet OCR1A dasselbe Verhalten wie OCR1B. // Timer1 output compare A interrupt service routine interrupt [TIM1_COMPA] void timer1_compa_isr(void) { OCR1A+=2; PORTB.0=!PORTB.0; } arbeitet völlig korrekt, mit +1 passiert gar nichts. Jetzt werde ich noch mal tief in der Kiste graben, ob ich noch ein paar andere AVRs finde. Ein NET-I/O habe ich auf jeden Fall noch mit Mega644. Und Tiny2313 müsste auch noch da sein. Interessiert mich jetzt.
Gerhard schrieb: > Mit der einen Zeile war eigentlich alles wichtige gesagt. Zeig trotzdem mal einen compilierfähigen Code, d.h. mit Init und Main. Niemand hat Lust, erstmal rumprobieren zu müssen. Gerhard schrieb: > mit +1 passiert gar nichts. Wirklich nichts oder erst nach 257 Timerzyklen?
Peter D. schrieb: > Gerhard schrieb: >> Mit der einen Zeile war eigentlich alles wichtige gesagt. > > Zeig trotzdem mal einen compilierfähigen Code, d.h. mit Init und Main. Mach ich gleich mal, Mnimalvariante. > Niemand hat Lust, erstmal rumprobieren zu müssen. Es sollte ja auch niemand probieren. Ich hatte auf Erfahrungen gehofft -Ne, geht nicht weil xyz oder -muss gehen, bei mir hat es funktioniert So exotisch schien mir die Aufgabe nicht > Gerhard schrieb: >> mit +1 passiert gar nichts. > > Wirklich nichts oder erst nach 257 Timerzyklen? Nichts mehr, da beim Überlauf eh wieder der kleinste OCR-Wert geladen wird.
Vermutlich benutzt Du den PWM-Mode, dann gilt das hier: "Note that in PWM mode, writing to the Output Compare Registers OCR1A or OCR1B, the data value is first transferred to a temporary location. The value is latched into OCR1A or OCR1B when the Timer/Counter reaches OCR1C." D.h. der neue OCR1B wird erst beim Erreichen von OCR1C übernommen.
Das ist das Problemchen, dass ich im Moment mit dem Timer0 habe. Sobald da einer von beiden als Hardware-PWM-arbeitet, wird auch der zweite erst beim Überlauf übernommen. Beim Timer1 ist es anders, es gilt nur für den, der auch an die PWM-Einheit gekoppelt ist. Der andere wird direkt geschrieben (sonst würde das folgende ja auch nicht funktionieren)
1 | /*******************************************************
|
2 | Chip type : ATtiny25
|
3 | AVR Core Clock frequency: 8,000000 MHz
|
4 | Memory model : Tiny
|
5 | External RAM size : 0
|
6 | Data Stack size : 32
|
7 | *******************************************************/
|
8 | |
9 | #include <tiny25.h> |
10 | |
11 | #define LED1 0 //PortB0
|
12 | #define LED2 2 //PortB2
|
13 | |
14 | unsigned char pwm_on=(1<<LED1) | (1<<LED2); |
15 | unsigned char OCR1B_reload[3]={10, //ch1 |
16 | 20, //ch2 |
17 | 0xff}; //OCR1C+1, never reached |
18 | |
19 | |
20 | |
21 | unsigned char pwm_off[2]= {~(1<<LED1), |
22 | ~(1<<LED2)}; |
23 | unsigned char channel_select; |
24 | |
25 | // Timer1 overflow interrupt service routine
|
26 | interrupt [TIM1_OVF] void timer1_ovf_isr(void) |
27 | {PORTB|= pwm_on; |
28 | channel_select=0; |
29 | OCR1B=OCR1B_reload[0]; //ch1 |
30 | }
|
31 | |
32 | // Timer1 output compare B interrupt service routine
|
33 | interrupt [TIM1_COMPB] void timer1_compb_isr(void) |
34 | {PORTB&=pwm_off[channel_select]; |
35 | channel_select++; |
36 | OCR1B=OCR1B_reload[channel_select]; |
37 | |
38 | }
|
39 | |
40 | void main(void) |
41 | {
|
42 | |
43 | // Crystal Oscillator division factor: 1
|
44 | #pragma optsize-
|
45 | CLKPR=(1<<CLKPCE); |
46 | CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (0<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0); |
47 | #ifdef _OPTIMIZE_SIZE_
|
48 | #pragma optsize+
|
49 | #endif
|
50 | |
51 | // Input/Output Ports initialization
|
52 | // Port B initialization
|
53 | // Function: Bit5=In Bit4=Out Bit3=Out Bit2=Out Bit1=Out Bit0=Out
|
54 | DDRB=(0<<DDB5) | (1<<DDB4) | (1<<DDB3) | (1<<DDB2) | (1<<DDB1) | (1<<DDB0); |
55 | // State: Bit5=T Bit4=0 Bit3=0 Bit2=0 Bit1=1 Bit0=0
|
56 | PORTB=(0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (1<<PORTB1) | (0<<PORTB0); |
57 | |
58 | |
59 | // Timer/Counter 1 initialization
|
60 | // Clock source: System Clock
|
61 | // Clock value: 125,000 kHz
|
62 | // Mode: PWMA top=OCR1C
|
63 | // OC1A output: OC1A=PWM, /OC1A disc.
|
64 | // OC1B output: Disconnected
|
65 | // Timer Period: 2,04 ms
|
66 | // Output Pulse(s):
|
67 | // OC1A Period: 2,04 ms Width: 1,02 us
|
68 | // Timer1 Overflow Interrupt: On
|
69 | // Compare A Match Interrupt: Off
|
70 | // Compare B Match Interrupt: On
|
71 | PLLCSR=(0<<PCKE) | (0<<PLLE) | (0<<PLOCK); |
72 | |
73 | TCCR1=(0<<CTC1) | (1<<PWM1A) | (1<<COM1A1) | (0<<COM1A0) | (0<<CS13) | (1<<CS12) | (1<<CS11) | (1<<CS10); |
74 | GTCCR=(0<<TSM) | (0<<PWM1B) | (0<<COM1B1) | (0<<COM1B0) | (0<<PSR1) | (0<<PSR0); |
75 | TCNT1=0x00; |
76 | OCR1A=0x80; |
77 | OCR1B=0x00; |
78 | OCR1C=0xFE; |
79 | |
80 | // Timer(s)/Counter(s) Interrupt(s) initialization
|
81 | TIMSK=(0<<OCIE1A) | (1<<OCIE1B) | (0<<OCIE0A) | (0<<OCIE0B) | (1<<TOIE1) | (0<<TOIE0); |
82 | |
83 | |
84 | // Global enable interrupts
|
85 | #asm("sei")
|
86 | |
87 | while (1) |
88 | {
|
89 | }
|
90 | }
|
Setzt man OCR1B_reload[0] auf 19, ist Kanal 2 tot (bzw. 100%). Ganze Aufbereitung/Sortierung habe ich jetzt mal weggelassen. Und nein, an evtl. fehlendem volatile liegt es 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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.