Hallo, Ich habe folgendes Problem. Ich möchte in 8 verschiedenen Timer-durchläufen bei einem Atmega644 unterschiedliche Pulslängen und Pegelflanken am OC0A Pin erzeugen. Initialisiert habe ich Timer0 mit FastPWM, von 0-0xFF und Overflow interrupt. Bei jedem Interrupt lade ich einen neuen Wert ins OCR0A Register und sage ob der Pin bei match gesetzt "set" werden soll oder gelöscht "clear" werden soll. Es funktioniert eigentlich alles, ausser der letzte Durchgang an dem ich den Pin löschen möchte funktioniert nicht. Der Timer läuft immer voll (high) durch. Umgekehrt, der letzte "set" Durchgang funktionert wenn ich danach COM0A0 auf "1" setzte. Hier mal die ISR: /*********************************************************************** ******/ ISR(TIMER0_OVF_vect) { static unsigned char CycleCount_uc=0,CycleRest_uc=0,ReloadValue_uc=0; static unsigned int Temp_ui; static unsigned int CycleTime_ui = 5760; static unsigned int ServoValue1_ui=432; if(CycleCount_uc==0) { sbi(TIFR0,OCF0A); sbi(TCCR0A,COM0A0); //sbi(TCCR0A,COM0A1); OCR0A=255; } if(CycleCount_uc==1) { sbi(TIFR0,OCF0A); sbi(TCCR0A,COM0A0); //sbi(TCCR0A,COM0A1); OCR0A=255; } if(CycleCount_uc==2) { sbi(TIFR0,OCF0A); sbi(TCCR0A,COM0A0); //sbi(TCCR0A,COM0A1); OCR0A=255; } if(CycleCount_uc==3) { sbi(TIFR0,OCF0A); sbi(TCCR0A,COM0A0); //sbi(TCCR0A,COM0A1); OCR0A=255; } if(CycleCount_uc==4) { sbi(TIFR0,OCF0A); cbi(TCCR0A,COM0A0); //sbi(TCCR0A,COM0A1); OCR0A=150; } if(CycleCount_uc==5) { sbi(TIFR0,OCF0A); cbi(TCCR0A,COM0A0); sbi(TCCR0A,COM0A1); OCR0A=100;// DIESER Durchgang geht nicht!! Warum immer 0xFF } if(CycleCount_uc==6) { sbi(TIFR0,OCF0A); sbi(TCCR0A,COM0A0); sbi(TCCR0A,COM0A1); OCR0A=255; } if(CycleCount_uc==7) { sbi(TIFR0,OCF0A); sbi(TCCR0A,COM0A0); //sbi(TCCR0A,COM0A1); OCR0A=255; /**********************/ CycleCount_uc=255; /**********************/ } CycleCount_uc++; PORTB ^= 0x10; } /*******************************/ Anbei auch noch ein Oszi-bild (habe Kanal 2 mit einem 1:10 Tastkopf gemessen, also nicht am Pegel stören) Ich habe im Datenblatt keine Einschränkung dazu gefunden und auch sonst keinen Rat mehr. Ich benutze den aktuellen GCC (20100110). Hat jemand eine Idee? Ich hoffe ich habe mein Problem halbwegs verständlich rüber gebracht. Danke
Ist die compiler optimierung an? Läuft der Timer mit prescaler=1, d.h. vollem Takt? Dann sind bis zu deinem Test auf 5 womöglich schon 100 Takte vergangen. Und: - die PWM-pins funktionieren auch, wenn die flags dazu die ganze zeit gesetzt sind - C kennt switch/case (und ggf. else!):
1 | /*Alles ungetestet*/
|
2 | ISR(TIMER0_OVF_vect) |
3 | {
|
4 | // static-Variablen werden automatisch mit 0 initialisiert
|
5 | static unsigned char CycleCount_uc, CycleRest_uc, ReloadValue_uc; |
6 | static unsigned int Temp_ui; |
7 | static unsigned int CycleTime_ui = 5760; |
8 | static unsigned int ServoValue1_ui = 432; |
9 | switch(CycleCount_uc) { |
10 | case 0: // Fall through |
11 | case 1: |
12 | case 2: |
13 | case 3: |
14 | OCR0A = 0xFF; |
15 | break; |
16 | case 4: |
17 | OCR0A = 150; |
18 | break; |
19 | case 5: |
20 | OCR0A = 100;// geht's nun? |
21 | break; |
22 | case 6: //Koennte man mit dem ersten zusammenlegen |
23 | OCR0A = 0xFF; |
24 | break; |
25 | case 7: |
26 | OCR0A = 0xFF; |
27 | CycleCount_uc = (unsigned char)-1; // Wird noch einmal erhoeht |
28 | }
|
29 | CycleCount_uc++; |
30 | PORTB ^= (1<<PB4); |
31 | }
|
- Für deine vielen Fälle bietet sich eine 'enum' an hth, Jörg
'Tschuldige bitte - ich hab da ein bisschen zuviel gekürzt, das switch() sollte effektiv so aussehen (um das gleiche zu tun wie deine if's):
1 | switch(CycleCount_uc) { |
2 | case 0: |
3 | case 1: |
4 | case 2: |
5 | case 3: |
6 | TCCR0A |= (1<<COM0A0); |
7 | OCR0A = 0xFF; |
8 | break; |
9 | case 4: // Wenn der Timer nicht mit |
10 | TCCR0A &= ~(1<<COM0A0); // gesetzem COM0A1-Bit initialisiert |
11 | OCR0A = 150; // wurde, ist die PWM jetzt aus ! |
12 | break; |
13 | case 5: |
14 | OCR0A = 100; |
15 | TCCR0A |= (1<<COM0A1); |
16 | break; |
17 | case 6: |
18 | OCR0A = 0xFF; |
19 | TCCR0A |= (1<<COM0A0); |
20 | break; |
21 | case 7: |
22 | OCR0A = 0xFF; |
23 | CycleCount_uc = (unsigned char)-1; |
24 | }
|
sry, Jörg
Hallo Jörg, danke für die schnelle Rückmeldung. Zu den Punkten. Pre-scaler steht auf 64. Ich denke langsam genug. Optimierung auf -O2. Was könnte aber hier wegoptimiert werden? Der code wie ich ihn hier gepostet habe ist das Frust-Ergebnis nach unzähligen Versuche. Das ständige zurücksetzen der Flags ist wirklich nicht nötig, aber ich wollte nichts unversucht lassen. Auch die ganzen If-Dinger. Nicht schön aber ich wollte endlich verstehen wo es eigentlich hängt. Jetzt weiss ich zwar wo, aber immer noch nicht warum? Ich verstehe es halt nicht warum der umgekehrt weg (erst fallende Flanke und dann Steigende) funktioniert. Da ist in deinem Beispiel code gar nicht enthalten. (je nach dem ob COM0A0 gesetzt oder nicht gesetzt ist) ups.. habe gerade gesehen du hast schon ein update ... werde ich mal ausprobieren Gruß Christian
Hallo Joerg, habe gerade mal deinen Code-Vorschlag getestet. Leider das gleiche Ergebnis. d.h. case 4 geht und case 5 wird mit 0xff durchlaufen. Ich habe auch noch die anderen Compiler-Optimierungen ausprobiert. Bringt alle nichts. Ich weiss nicht mehr weiter, habe keine Idee mehr? Kann das der AVR in die Richtung nicht schnell genug? Kann doch eigentlich nicht sein. Hat keiner noch eine Idee? Danke und Gruß Christian
Funktioniert es, wenn du den Pin komplett von Hand steuerst? (TIMER0_COMPA-Interrupt dazunehmen) Sonst: - poste mal deine Initialisierung (besser: ein minimales Testprogramm als Anhang) - Beschreibe mal genauer was du machen willst. Im Datenblatt ist zwar immer nur von set (oder clear) on match die Rede, aber der Pin wird ja auch beim Überlauf geschaltet, soll heißen, wenn du irgendwo von clear-on-match auf set-on-match umschaltest, muss es einen Durchlauf geben, bei dem sich der Pegel nicht ändert - oder schalten die COMxyz-bits den Pin auch sofort, wenn man die ändert (und ich bin fehl-/unterinformiert) ?. hth, Jörg
So wie ich den Timer0 verstehe, werden die OCR0x Register in den PWM Modes nur bei TOP übernommen. Damit wird erreicht, dass es keine Glitches gibt. Der TIMER0_OVF wird aber (kurz) nach TOP ausgeführt, d.h. für den gerade begonnenen Durchlauf ist es zu spät. Volle Kontrolle hat man im CTC Mode: ( - OCR0B auf den gewünschten Zeitpunkt setzen - TCCR0?:COM0B auf die gewünschte Flanke setzen - OC0B-Int enablen - bei OC0B-Int von vorne mit den dann aktuellen Zeiten/Flanken ) aber keine automatische "Rück-Flanke" bei TOP. Gruß, Jürgen
Hallo Zusammen, zu den Vorschlägen: @Juergen: Die OCR0x Register werden bei FastPWM bei Bottom aktualisiert. (Datenblatt) Bei PWM (Phase correct) ist Top. CTC geht nicht, da ich sowohl OCRA und OCRB brauche, und im CTC Mode OCRA als Timer reload verwendet wird. @Joerg: Ich möchte auf den OC0A / OC0B Pins ein Jitter-freies PM Signal erzeugen. Daher kann ich es nicht mit "Handgesteuerten" Ports machen. Jedes einzelne PWM Signal soll eine individuelle Zeit bekommen. Auch über Timerüberläufe hinweg. Ich habe trotzdem mal deinen Rat befolgt und es von Hand gemacht (Ovflow + TIMER0_COMPA-Interrupt) und bin auf mehr Fragen als Antworten gestoßen. Hier mal der Code und ein Oszi-bild. /***********************************************/ void Timer0_Init(void) { TCCR0A=(1<<WGM01)|(1<<WGM00); TCCR0B=(1<<CS01)|(1<<CS00); sbi(TIMSK0,TOIE0); sbi(TIMSK0,OCIE0A); cbi(TCCR0A,COM0A0); cbi(TCCR0A,COM0A1); cbi(TCCR0A,COM0B0); cbi(TCCR0A,COM0B1); OCR0A=255; } ISR(TIMER0_OVF_vect) { switch(CycleCount_uc) { case 0: PORTB &= ~(1<<PB3); OCR0A = 0xFF; break; case 1: PORTB &= ~(1<<PB3); OCR0A = 0xFF; break; case 2: PORTB &= ~(1<<PB3); OCR0A = 0xFF; break; case 3: PORTB &= ~(1<<PB3); OCR0A = 0xFF; break; case 4: PORTB |= (1<<PB3); OCR0A = 180; break; case 5: PORTB |= (1<<PB3); // Diese Hochsetzen wird nicht ausgeführt, OCR0A = 80; // obwohl OCR0A richtig geladen und ausgeführt wird break; case 6: PORTB &= ~(1<<PB3); OCR0A = 0xFF; break; case 7: PORTB &= ~(1<<PB3); OCR0A = 0xFF; CycleCount_uc = (unsigned char)-1; } CycleCount_uc++; PORTB ^= (1<<PB4); } /*********************************************************************** ******/ ISR(TIMER0_COMPA_vect) { PORTB &= ~(1<<PB3); PORTB ^= (1<<PB4); } /**************************************************/ In Case 5 wird die Port-high Schaltung einfach nicht ausgeführt (Oszi 1,85ms). Stattdessen wird schon bei case 3 der Port high geschaltet obwohl ich das gar nicht will. Die COMPA_ISR wird aber immer korrekt ausgeführt. (siehe rotes Signal).Zur Erklärung Rot ist PB4 und Blau PB3. Irgendwie ist immer ein Versatz drin. Diesmal nur ein "Task" nach vorne. Nebenbei wie kann man code so schön wie du reinkopieren? Gruß Christian
Wie gut funktioniert das Testprogramm im Anhang?
Und deine Schaltung ist in Ordnung (kein anderer Ausgang gegen PB3, kein
Pull-up vergessen, kalte Lötstelle etc.)?
>Nebenbei wie kann man code so schön wie du reinkopieren?
[c ] ... [/ c] -- steht auch direkt über dem Eingabefenster...
Könntest du das Signal, dass du erzeugen willst genauer beschreiben?
hth, Jörg
Hallo Joerg, danke für den entscheidenden Tip. Ich habe mal eine andere Hardware genommen und da tut alles was ich will. Es ist zum verrückt werden. An dem Port hängt nichts dran. Ich habe die gleiche SW auf eine identische Platine geflasht und es hat sofort getan. Wie gesagt, keine externe Beschaltung auch eine kalte Lötstelle kann ich zu 99% ausschließen. Kann es sein das im AVR nur beim Timer0 irgendwas im Eimer ist? So ein Fehler ist mir wirklich noch nicht bei den AVR´s untergekommen. Irgendwie bin ich jetzt ein bischen verunsichert. Ich danke für die gute Unterstützung. Gruß Christian
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.