Forum: Compiler & IDEs zwei 8 bit Variablen aus 16bit Variable


von Frederick (Gast)


Lesenswert?

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




von Rolf Magnus (Gast)


Lesenswert?

> 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;

von Stefan (Gast)


Lesenswert?

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?

von Frederick (Gast)


Lesenswert?

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.












von FBI (Gast)


Lesenswert?

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

von Hagen R. (hagen)


Lesenswert?

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

von Rolf Magnus (Gast)


Lesenswert?

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.

von Frederick (Gast)


Lesenswert?

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!

von Frederick (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.