Hallo, ich habe ein C-Problem und komme nicht dahinter was ich falsch mache: Ich habe folgenden Code: volatile uint8_t var1; // 8bit variable volatile uint8_t var2; // 8bit variable volatile uint16_t var3; // 16 bit variable Dann in der "compare match B ISR" des 16bit timer1 im ATmega168: var1 = OCR1BL ; // lowbyte zuerst lesen var2 = OCR1BH ; // highbyte danach lesen var3 = (var2 * 256) + var1 ; // umrechnen in eine 16bit variable var3 += 1000; // 1000 hinzuaddieren OCR1BH = var3 / 256; // highbyte in OCR1H schreiben OCR1BL = var3 - (var3 / 256); // lowbyte in OCR1L schreiben Die ISR wird problemlos aufgerufen und auch die Variablen var1 und var2 werden korrekt ausgelesen. Nur bei der Umrechnung von dem 16 bit Wert var3 in die zwei 8 bit Werte var1 und var2 klappt es nicht so recht... Was mache ich falsch? Danke fuer Tips!!! Frederick
> var1 = OCR1BL ; // lowbyte zuerst lesen > var2 = OCR1BH ; // highbyte danach lesen > > var3 = (var2 * 256) + var1 ; // umrechnen in eine 16bit variable > > var3 += 1000; // 1000 hinzuaddieren > > OCR1BH = var3 / 256; // highbyte in OCR1H schreiben > OCR1BL = var3 - (var3 / 256); // lowbyte in OCR1L schreiben Einfacher wäre: var3 = OCR1B; var3 += 1000; OCR1B = var3; oder (wenn var3 eigentlich nicht gebraucht wird): OCR1B += 1000; > Die ISR wird problemlos aufgerufen und auch die Variablen var1 und var2 > werden korrekt ausgelesen. Nur bei der Umrechnung von dem 16 bit Wert > var3 in die zwei 8 bit Werte var1 und var2 klappt es nicht so recht... Du verwendest recht unübliche Schreibweisen. Mir ist auch nicht klar, wie OCR1BL = var3 - (var3 / 256); // lowbyte in OCR1L schreiben zum gewünschten Ergebnis führen soll. Üblicher ist für die Zusammensetzung: var3 = (var2 << 8) | var1 ; und für den umgekehrten Weg: OCR1BH = var3 >> 8; OCR1BL = var3 & 0xff;
Was klappt denn nicht so recht? Dass du manchmal unsinnige (zu kleine) Werte wegen dem Überlauf der 16-Bit bei der Addition der 1000 bekommst?
Hallo! Erstmal vielen Dank fuer die Antworten!!! Ja, meine Schreibweise ist etwas unueblich. Ich bin mir mit den Bitoperationen in C (noch) nicht so sicher... bin gerade nach sehr sehr viel Assembler auf C umgestiegen mit AVR GCC... Also: Ich habe folgendes Anliegen: Der ATmega168 laeuft mit 8 MHz. Ich benoetige den 16bit Timer allerdings fuer zwei verschiedene Aufgaben gleichzeitig. Einmal eine PWM mit 12 bit Aufloesung und einmal muss ich einen definierten Takt mit ca. 4 kHz an einem Portpin erzeugen. Die 8bit Timer haben eine zu schlechte Aufloesung in meinem Falle. Ich mache also beides mit dem 16bit Timer1. Ich habe den 16bit-Timer1 nun so konfiguriert, dass er im Fast-PWM-Mode laeuft und dass TOP = ICR1 ist. Im Register ICR1 habe ich den Wert 4095 stehen, entsprechend 12bit PWM Aufloesung. Der Timer zehlt also immer schoen brav bis 4095 und beginnt dann wieder bei 0. Die PWM erzeuge ich mit dem OCR1A Compare Register. Funktioniert wunderbar. Kann ich mit dem Oszilloskop am Pin sehen. Nun habe ich noch den Interrupt fuer das Compare Register OCR1B aktiviert. Diese ISR wird auch aufgerufen. Und zwar mit einer Frequenz von 8 MHz / 4096 = 2000 mal pro Sekunde. Bis jetzt ist also noch alles in Ordnung. Nun moechte ich aber diese ISR mit einer hoeheren Frequenz aufrufen als 2000 mal pro Sekunde, denn ich will mit Ihr den 4kHz Takt an einem Portpin ausgeben. Dazu moechte Ich in der ISR das Register OCR1B veraendern, so dass diese ISR noch ein weiteres mal aufgerufen wird bevor der Timer wieder bei 0 beginnt. Dazu habe ich folgende (eigentlich elegante) Idee: volatile uint16_t variable; volatile uint16_t definierter_wert; ISR (TIMER0_COMPB_vect) { variable = OCR1B; variable += definierter_wert; if(variable > 4095) // auf Ueberlauf testen da timer auf 12bit begrenzt { variable = variable - 4095; // den Ueberlauf beruecksichtigen } OCR1B = variable; }//ISR Ich probiere das seit heute morgen zum laufen zu kriegen, aber es geht einfach nicht. Ich weiss auch nicht so genau was der GCC Compiler alles macht und was nicht, da ich wenig C Erfahrung habe. Die ISR wird weiterhin mit nur einer Frequenz von 2000 mal pro Sekunde aufgerufen. Trotz der Veraenderung der Register OCR1BH und OCR1BL. Vielen Dank fuer Tips!! Frederick.
Hi, das OCR1B Register ist in allen PWM-Modi "double buffered" (OCR1A übrigens auch). D.h. alle Änderungen an diesem Register werden erst wirksam, wenn Dein Timer überläuft (4095 => 0). Dieses Verhalten ist im Datenblatt unter "14.7 Output Compare Units" (bei meiner Version Seite 117/118) beschrieben. CU Frank
fasse mal zusammen >> 16bit-Timer1, TOP = ICR1 >> Nun habe ich... OCR1B aktiviert. >> Diese ISR wird auch aufgerufen 2000 mal pro Sekunde. >> Bis jetzt ist also noch alles in Ordnung. Stimmt >>Nun moechte ich mit einer hoeheren Frequenz aufrufen Nun nicht mehr, du machst da einen Denkfehler ! Der Timer1 läuft immer mit der Frequenz die in seinem Prescaler und TOP definiert wurde. Bei dir also ICR1. Wenn du nun an den OCR Registern rumwurschtelst kannst du die Frequenz vom Timer1 nicht verändern, sondern nur den Zeitpunkt an dem dein OCR Interrupt relativ zum Overflow ISR des Timer1 eintrifftst. Das ist dann eine Vorstufe zur PWM und man stellt über OCRxx nur den Duty Cycle dieser PWM ein. Aktiviere mal noch OCR1A und OCR1B. Setzte die Ausgangspin beider PWMs entsprerchend und messe mit dem Oszi beide Kanäle gleichzeitig. OCR1B stellst du auf 50%=1000 fest ein und OCR1A lässt du mit deinem jetzigen Code laufen. Du wirst sehen das sich die Frequenz beider Ausgäng nicht ändern und gleich sind. Aber die Phase und Duty der OCR1A wird sich ändern. Was du also machen möchtest geht so nicht. Entweder nimmst du noch einen zweiten Timer (Timer2,3 bei Mega128 zb.) oder musst Timer1 auf eine hohe Frequenz setzen und dann selber dynamisch scalieren, quasi einen Sofwtaretimer. Geruß Hagen
Oder zwei Interrupt-Funktionen. Der OC1B-Interrupt wird nach der Hälfte (2048) Schritte ausgelöst, der ICP-Interrupt am Schluss. Dann bekommst du pro Timerdurchlauf auch zwei Interrupts und kannst in den beiden Handlern eine gemeinsame Funktion aufrufen. Auf 4kHz kommst du aber trotzdem nicht, denn: > Diese ISR wird auch aufgerufen. Und zwar mit einer Frequenz von 8 MHz / > 4096 = 2000 mal pro Sekunde. Bis jetzt ist also noch alles in Ordnung. Pro kompletter Periode muß der Pin zweimal getoggelt werden. Bei 2000 Interrupts/Sekunde kommt also nur 1kHz raus.
Hallo!! Mensch! Vielen Dank fuer die ganzen Tips!! Das was mir nicht aufgefallen ist, ist das mit dem "doppelt gepufferten" Register OCR1B. Natuerlich erklaert das ganz genau mein Problem!!! Das war mir nicht klar... da haette ich noch lange suchen koennen! Vielen Dank fuer die Hinweise!! Super Forum!!! Danke!!! Gruss Frederick!
Erfolgsmeldung: Habe es im CTC Mode realisiert anstelle im PWM mode. Ohne "double buffered" Problem. Funktioniert jetzt wunderbar! Danke nochmals fuer die Hilfen!!
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.