Forum: Mikrocontroller und Digitale Elektronik ATmega32u4 OCR1BH nicht setzbar


von Myster (Gast)


Lesenswert?

Hallo Leute,
Ich habe versucht bei einem Arduino Programm den Timer1 mit 
einzubeziehen. Programmiert auf einem Arduino Micro.
Normalerweise bereiten Timer mir keine Probleme, aber hier bin ich auf 
den Fehler gestoßen, dass ich irgendwie das high-Byte, des 
Output-Compare-Registers B des Timer1 einfach nicht setzen kann.
Ich habe mich bereits durch das Datenblatt des ATmega32u4 gearbeitet und 
auch einige Stunden rechachciert, konnte aber keine funktionsfähige 
Lösung finden.
Daher meine Frage an euch:
Weiß jemand, woher das kommt, bzw wie ich das OCR1BH beschreiben kann?

Teil des Setups:
1
  TCCR1B = 11;
2
  TIMSK1 = (1<<OCIE1B);

Teil der void Loop:
1
  OCR1BH = frequenz / 256;
2
  OCR1BL = frequenz % 256;
3
  Serial1.println(OCR1BL);
4
  Serial1.println(OCR1BH);

Frequenz ist eine 16Bit Variable in der ich den Wert abgespeichert habe, 
der in das Compare-Register geladen werden soll.
Falls ihr weiter informationen zum Code braucht, bitte bescheid sagen.
Schon mal danke fürs Lesen und für die Kommentare, die kommen werden.

von ei leik arduino (Gast)


Lesenswert?

Myster schrieb:
> Weiß jemand, woher das kommt, bzw wie ich das OCR1BH beschreiben kann?

Hab es jetzt nicht für jeden Fall ausprobiert, aber wenn man das
Register TCCR1A mit 0 initialisiert sollte es funktionieren.

Offensichtlich braucht der Timer erst mal eine Initialisierung.
Dabei scheint TCCR1A massgeblich zu sein, also dieses mindestens
einmal beschreiben, dann kann man sich ja immer noch den anderen
Registern widmen.

von Myster (Gast)


Lesenswert?

........Ich muss grade über mich selbst den Kopf schütteln.
man merkt, meine Konzentration ist nicht die beste grade.
Hatte ich doch tatsächlich geschrieben, dass dann ja kein Vorteiler und 
CTC eingeställt wären.....Nur, dass es TCCR1*B* wäre, du aber meintest, 
dass ich TCCR1*A* beschreiben soll. Ja, nicht meine meisterleistung im 
Mitdenken.

Ich hab´s ausprobiert und es funktioniert.
Würde ich hier gerne sagen. Aber aktuell stürzt mein Terminal-Programm 
immer ab, wenn ich versuche etwas an den Arduino zu senden.
also wird die Rückmeldung wohl noch etwas brauchen.

von Myster (Gast)


Lesenswert?

Okay, Terminal Programm geschickt umgangen und jetzt kann ich es 
bestätigen:
Es funktioniert!
Also, vielen Dank an "ei leik" für die Hilfe!
Immer wieder eine Rettung, dass hier immer Jemand zu sein scheint, der 
eine Antwort hat.

von ei leik arduino (Gast)


Lesenswert?

Ja ... Dank auch für die Rückmeldung.

Immer wieder auch hilfreich für andere Suchende.

von Myster (Gast)


Lesenswert?

Ja, ich muss nochmal zurückrudern......
Ich hab heute mich drangesetzt weiterzuarbeiten und hab ein paar 
ungereimtheiten festgestellt.....
Wenn ich TCCR1A auf 0 setze werden zwar die Compare-Werte richtig 
gesetzt, aber der Timer läuft gar nicht.
Die frage ist: Wieso? Laut Datenblatt sind die bits 2-7 des TCCR1A für 
Output modi, was ich aber nicht brauche und Bit´s 0-1 sind Fast-PWM, was 
ich auch nicht brauche. Ich setze in TCCR1B das CTC-Bit (Bit3) und 
Vorteiler auf 64, also insgesammt B00001011 ausgeschrieben. Unter diesen 
werten wird die Compare-ISR aber nichtmehr ausgelößt...
Wofür ein Haus das verrückte macht, dieser Timer tuts grade auch. xD
Jemand ne Idee was hier bei mir vor sich geht?

von Myster (Gast)


Lesenswert?

Myster schrieb:
> aber der Timer läuft gar nicht.

Sry, im nachinein im Lesen, das ist nicht ganz korrekt, der Timer zählt 
hoch, die Compare-ISR wird nur nicht ausgelößt, obwohl ich am TCCR1B 
nichts geändert habe.

von ei leik arduino (Gast)


Lesenswert?

Myster schrieb:
> die Compare-ISR wird nur nicht ausgelößt, obwohl ich am TCCR1B
> nichts geändert habe.
1
sei();

vergessen?
Wenn nicht dann musst du wohl deinen ganzen Programmcode zeigen.

von Myster (Gast)


Lesenswert?

Sei(); sollte nicht das Problem sein, da es funktioniert, wenn ich 
TCCR1A die WGM10 /WGM11 setze, was ja aber der PWM-Modus ist, nicht der 
normale CTC.
1
char Portexpander [9] = {0x27, 0x23, 0x24, 0x20, 0x25, 0x21, 0x26, 0x22, '\0'}; //Adressen der 8 Portexpander im Uhrzeigersinn.
2
char ExpanderNr [129];            //Zu welchem portexpander gehört die LED?
3
char ledzahl [129];               //Welche LED soll am entsprechenden Expander angesteuert werden?
4
char highbyte [9];                //Übertragung an die Portexpander Byte1
5
char lowbyte  [9];                //Übertragung an die Portexpander Byte2
6
char bufer [257];                 //Buffer
7
String DataIN, Befehl, AZ;        //Datenstrings anlegen
8
uint8_t i, x=0, a, anzahl, modulo;//Variablen anlegen
9
uint16_t z, frequenz;             //
10
float rechenvariable;                  
11
12
void setup()
13
{
14
  Wire.begin();               //Initialisieren der Bibliothek
15
  Serial.begin(9600) ;        //festlegen der Baud-Rate
16
  Serial1.begin(9600);
17
  Serial1.println("begin");
18
19
  TCCR1A = B00000000;  
20
  TCCR1B = B00001011;    //Prescaler to 64; CTC on (Clear-Timer-on-Compare)
21
  OCR1BH = 0xFF;
22
  OCR1BL = 0xFF;
23
  TIMSK1 = (1<<OCIE1B);  //Output-Compare-Interrupt enable
24
}
25
26
27
void loop()
28
{
29
  /*~~~~~Neuen String einlesen~~~~~*/
30
  if ( Serial1.available() || Serial1.available() ) 
31
  {                                                 
32
    bufer [0] = '\0';                               
33
    if (  Serial1.available() )                     
34
    {
35
      anzahl  = Serial1.readBytesUntil('#',bufer,256);//Wie viele Daten befinden sich im Eingangspuffer? Und diese in bufer schreiben        
36
      anzahl = anzahl - 1;                          //Anzahl um CR reduzieren, sodass die Befehlszeichen (ersten2/3) nicht bis auf 4 kommen und so die anzahl verfälschen würden
37
      modulo = anzahl % 2;                          //überprüfen, Gesammtanzahl der Zeichen grade, oder ungrerade ist (wichtig für Setz-Befehl)
38
      anzahl  = anzahl / 4;                         //Anzahl der zu setzenden LED´s ausrechnen (..._xxx... --> 4zeichen pro LED) da Befehlszeichen < 4, werden diese nicht mitgezählt
39
      DataIN  = bufer;                              //Eingangsstring von Array in String umwandeln, um ihn bearbeiten zu können
40
      AZ = DataIN.substring(0,1);                   //1.Zeichen auslesen
41
      if (AZ == "B")                                //Wenn dieser µC gemeint ist
42
      { Befehl = DataIN.substring(1,2); }           //2.Zeichen auslesen     
43
      else                                          //Wenn nicht
44
      { 
45
        Befehl = " ";                               //Dann auch nichts ausfüllen
46
        bufer [0] = '\0';                           //buffer löschen
47
        DataIN = " ";                               //Eingangsstring löschen
48
        anzahl = 0;                                 //anzahl auf 0 zurücksetzen
49
      }
50
      if ( ( Befehl == "s" ) && (modulo == 1) )     //Wenn es ein Setz-Befehl ist und die Gesammtanzahl der Zeichen ungerade ist (--> 3 Befehlszeichen, also MIT X/Y) 
51
      { Str = DataIN.substring(2,3);  }             //3.Zeichen auslesen (ob X oder Y )
52
    }
53
54
55
   if ( Befehl == "f" )
56
   { 
57
    frequenz =            (1000 * (DataIN[3] - 0x30)); //1000er Stelle 
58
    frequenz = frequenz + ( 100 * (DataIN[4] - 0x30)); // 100er Stelle
59
    frequenz = frequenz + (  10 * (DataIN[5] - 0x30)); //  10er Stelle   
60
    frequenz = frequenz + (   1 * (DataIN[6] - 0x30)); //   1er Stelle
61
    Serial1.print("Frequenz: "), Serial1.println(frequenz);
62
    rechenvariable = 41667 / frequenz;       //20833     
63
    frequenz = round(rechenvariable);
64
    OCR1BH = frequenz / 256;
65
    OCR1BL = frequenz % 256;    
66
    Serial1.print("L: "), Serial1.println(OCR1BL);
67
    Serial1.print("H: "), Serial1.println(OCR1BH);
68
    TCCR1B = 11;   
69
   }
70
71
   if ( Befehl != "f")
72
   { 
73
    Serial1.println("!=f");
74
    TCCR1B = 0;  
75
   } 
76
77
 }//Ende Serial availabile
78
79
 if (Befehl == "f" )
80
 {  Bild(); }
81
  
82
83
}//Ende Main Loop  
84
85
86
// Setz Befehl             
87
 
88
void Bild(void)
89
{
90
 if (z == 2 )
91
 {
92
  for ( i=0 ; i<8 ; i++)
93
  {      
94
    Wire.beginTransmission(Portexpander[i]);    //Bereitet die Datenübertragung an den Portexpander vor
95
    Wire.write(0xFF);                           //Alle LED´s AUS
96
    Wire.write(0xFF);                           //Alle LED´s AUS
97
    Wire.endTransmission();                     //Senden der Daten
98
  }
99
  //Serial1.println("AUS");
100
 }
101
 
102
 if ( z >= 360 )
103
 {          
104
  for( i=0 ; i<8 ; i++)
105
  {
106
    if (( lowbyte[i] != 0xFF ) || ( highbyte[i] != 0xFF )) 
107
    {
108
      Wire.beginTransmission( Portexpander[i] ); //Bereitet die Datenübertragung an den Portexpander vor
109
      Wire.write( lowbyte[i]  );                 //Schreibt den Inhalt für das 1.Byte in den Sendepuffer
110
      Wire.write( highbyte[i] );                 //Schreibt den Inhalt für das 1.Byte in den Sendepuffer
111
      Wire.endTransmission();                    //Überträgt die gepufferten Daten
112
    }
113
  }
114
  Serial1.println("AN");
115
  z = 0;
116
 }
117
118
}//Ende Bild
119
120
121
// Timer0 ISR
122
ISR(TIMER1_COMPB_vect)
123
{
124
 z++;  
125
}

so, ich denke das sollte Die funktion des Programms Zeigen, ohne, dass 
es die restliche funktion preisgiebt.

von ei leik arduino (Gast)


Angehängte Dateien:

Lesenswert?

Deinen Code durchblicke ich nicht, hab auch keine Lust dazu
es näher anzuschauen.

Im Anhang ein Beispiel wie es funktioniert. Lerne daraus.

von Oliver S. (oliverso)


Lesenswert?

Beim Arduino gilt die Regel, grundsätzlich alle Register eines Timers 
auf die gewünschten Werte zu setzen.

Und ganz grundsätzlich sollte man das immer mit (1<< <Bitname>) machen. 
Niemand hat Lust, deine magischen Zahlen im Datenblatt nachzurechnen.

Oliver

von ei leik arduino (Gast)


Lesenswert?

ei leik arduino schrieb:
> Deinen Code durchblicke ich nicht

an Myster:

Aus meinem Beispiel entnehme ich dass du eine Timer-
Konfiguration wählst die es erfordert den Timer "von Hand"
in der ISR zurückzusetzen.

von Oliver S. (oliverso)


Lesenswert?

Myster schrieb:
> OCR1BH = frequenz / 256;
> 65    OCR1BL = frequenz % 256;

Lass das doch lieber den Compiler machen. Der kommt mit
1
OCR1B = frequenz;
schon klar.

Oliver

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Oliver S. schrieb:
> Beim Arduino gilt die Regel, grundsätzlich alle Register eines Timers
> auf die gewünschten Werte zu setzen.
>
> Und ganz grundsätzlich sollte man das immer mit (1<< <Bitname>) machen.
> Niemand hat Lust, deine magischen Zahlen im Datenblatt nachzurechnen.
>
> Oliver

Genauso ist das. Zusätzlich kann man sich das Gehampel mit low und high 
Byte schenken. OCR1B = '16Bitwert' ist einfacher.

von Myster (Gast)


Lesenswert?

ei leik arduino schrieb:
> Aus meinem Beispiel entnehme ich dass du eine Timer-
> Konfiguration wählst die es erfordert den Timer "von Hand"
> in der ISR zurückzusetzen.

Das würde bedeuten, kein CTC, was wiederum heißt, dass der Timer 
überläuft, aber stehenbleiben tut ein timer normalerweise nicht von 
alleine.
Außerdem habe ich das TCCR1B auf den selben wert gesetzt, wie du in 
deinem Beispiel? Es hat also genauso den CTC modus?

von Axel R. (axlr)


Lesenswert?

z++ im interrupt
Muss das nicht als volatile deklariert werden, oder macht diese 
Arduino-Umgebung das automatisch? (eher nich)

von Myster (Gast)


Lesenswert?

Es muss nicht sein. Ist eine gute Vorsichtsmaßnahme, aber hier nicht 
zwigend notwendig.
Durch Volentile würde die variable direkt aktualisiert werden. d.h. wenn 
ich in der ISR noch mit der variable weiterarbeiten wollte, müsste ich 
volentile nehmen, um sicherzustellen, dass die variable auch ihren 
korrekten wert hat, da es sonst sein kann, dass sie erst nach dem 
beenden der ISR richtig aktualisiert wird.

So habe ich es zumindest verstanden.

von Marco H. (damarco)


Lesenswert?

Ich ich hatte das Problem auch schon mal und es lag daran das dieser 
Timer im Framework verwendet wurde.

von Myster (Gast)


Lesenswert?

Naja, so unfreundlich/aggresiv/generft (schwer auszumachen, welches und 
ich will keine Unterstellungen machen) ei leik in seiner letzten Naricht 
auch war, sein Test programm funktioniert und daher möchte ich mich 
bedanken, da unabhängig von der Art, Er immernoch geholfen hat und das 
ist nicht selbstverständlich.

Oliver S. schrieb:
> Lass das doch lieber den Compiler machen. Der kommt mitOCR1B = frequenz;
> schon klar.
Auch danke für die Zusicherung, dass der Compiler die 16Bit den Timer1 
auf einmal setzen kann. Ich war mir da nicht Sicher und wollte daher auf 
Nummer sicher gehen und habe sie daher seperat gesetzt.

Wie ihr wahrscheinlich gemerkt habt, egal wie gut ich bin(ich sage 
nicht, dass das extrem viel ist, nur mehr als meine Freunde die ich 
kenne :/), fehlt mir etwas die Erfahrung. Daher werde ich mir auch das 
setzen von Registern mit
Oliver S. schrieb:
> Und ganz grundsätzlich sollte man das immer mit (1<< <Bitname>) machen.
> Niemand hat Lust, deine magischen Zahlen im Datenblatt nachzurechnen.
angewöhnen.

Alles in allem hab ich hier wieder mal das ein oder andere gelernt und 
möchte mich eben bei allen beteiligen bedanken.

von Myster (Gast)


Lesenswert?

Marco H. schrieb:
> Ich ich hatte das Problem auch schon mal und es lag daran das
> dieser
> Timer im Framework verwendet wurde.

Framework? Das sagt mir jetzt erstmal nix.

von Myster (Gast)


Lesenswert?

Okay, jetzt wird´s komisch.
Ich habe ja gesagt, dass das Beispielprogramm von ei like funktioniert.
Das stimmt auch.
Aber wenn ich die Comparewerte ändere, dann nicht mehr. also
1
OCR1BH = 0x00;
2
OCR1BL = 0x00;
zu z.B.
1
OCR1BH = 0x00;
2
OCR1BL = 0xFF;
dann geht plötzlich gar nichts mehr.
Ändere ich es wieder zurück zu 0x00, geht es wieder. Hat jemand auch 
dafür ne antwort?

von Michael F. (Firma: IAR Systems) (michael_iar)


Lesenswert?

Myster schrieb:
> Es muss nicht sein. Ist eine gute Vorsichtsmaßnahme, aber hier nicht
> zwigend notwendig.

Nein, das "volatile" ist zwingend erforderlich wenn eine Variable sowohl 
in einer ISR als auch im restlichen Programm genutzt wird:

z.B:
https://stackoverflow.com/questions/40760366/is-volatile-keyword-really-needed-when-variable-changed-in-interrupt-routine

https://www.microchip.com/forums/m667344.aspx

https://arduino.stackexchange.com/questions/20994/why-the-need-to-use-the-volatile-keyword-on-global-variables-when-handling-inter

Das Inkrementieren von "z" erfolgt nur in der ISR (sofern ich in Deinem 
Code nix übersehen habe) und somit außerhalb des normalen 
Programmablaufs. Bei stärkerer Optimierung wird der Compiler "z=0" als 
festen Wert annehmen und die if-Abfragen entsprechend optimieren, bzw. 
entfernen, da diese ja mit der getroffenen Annahme nie "true" werden 
können.

Gruß,
Michael

von Marco H. (damarco)


Lesenswert?

Auf Deutsch... der Timer wird in der Arduino IDE benutzt um gewisse 
Dinge zu realisieren... Systick etc... deine Register werden von Code 
der IDE überschrieben...

von Myster (Gast)


Lesenswert?

okay, dsa ist schon mal gut zu wissen, merk ich mir.

Bleibt noch die Frage, wieso der Timer keinen höheren compare-wert 
nimmt, als 0xFA im Low-byte. (250 in Dezimal)
Ich hab die Werte mal systematisch durchprobiert und sobald ich einen 
höheren Compare-Wert als 250 einstelle, funktioniert es nicht mehr...

Angewendet und gestestet mit diesem Textprogramm:
ei leik arduino schrieb:
> Im Anhang ein Beispiel wie es funktioniert.

von Axel R. (axlr)


Lesenswert?

Mach mal den Timer langsamer, so testhalberweise mit anderem 
Vorteiler...

von Myster (Gast)


Lesenswert?

okay, versuch ich nacher mal.
Obwohl das schon sehr komisch wäre, wenn der das nicht schaffen würde. 
Würde ja die ganze funktion eines 16Bit Timers nutzlos machen. Dann kann 
ich auch gleich Timer2 verwenden.
Ich würde ja schon lieber Klassisch C statt Arduino verwenden, aber hab 
hier grade leider keine Wahl.
Es hieß die Anwendung ist nicht so Zeitkritisch, dass ich Timer 
bräuchte, also mach Ich´s jetzt mit der Programmablaufzeit.
Ich bin halt etwas perfektionistisch, also hätte ich es eigentlich gerne 
wirklich exakt gemacht. --> mit Timer. Aber naja.... xD

von M. K. (sylaina)


Lesenswert?

Michael F. schrieb:
> Nein, das "volatile" ist zwingend erforderlich wenn eine Variable sowohl
> in einer ISR als auch im restlichen Programm genutzt wird:

Stimmt so allgemein nicht. Die Frage, die man sich stellen muss, ist: Wo 
wird die Variable überall geändert?
Richtig ist jedoch: Änderungen in der ISR sieht der Compiler nicht. Wird 
die Variable nur dort geändert können Optimierungen des Compilers 
Abfragen der Variablen außerhalb der ISR weg optimieren.

von Myster (Gast)


Lesenswert?

M. K. schrieb:
> Richtig ist jedoch: Änderungen in der ISR sieht der Compiler nicht. Wird
> die Variable nur dort geändert können Optimierungen des Compilers
> Abfragen der Variablen außerhalb der ISR weg optimieren.

Ähh....kannst du das bitte nochmal in einfach wiederholen?

von Veit D. (devil-elec)


Lesenswert?

Hallo,

wollen wir es jetzt richtig machen? Was ich mitbekommen habe ist, dass 
der Timer im CTC Mode mit variabler Frequenz laufen soll. Laut deiner 
Config CTC Mode 4. Übrigens sind Binär- und Hexzahlen nicht hilfreich.

Du hast konfiguriert:
1
  TCCR1A = 0;  
2
  TCCR1B = _BV(WGM12) | _BV(CS11) | _BV(CS10); // Prescaler 64, CTC Mode 4
3
  OCR1B  = 65535
4
  TIMSK1 = (1<<OCIE1B);  //Output-Compare-Interrupt enable

<Ironie Tag an>
Das blöde ist, im CTC Mode gibts kein OCR1B was man für TOP nutzen kann. 
Das heißt du verwendest die völlig falschen Register. Generell kann man 
je nach Mode nur ORCnA oder ICRn für TOP verwenden.
<Ironie Tag aus>

Schau in die Tabelle der Modi welches Register für TOP zuständig ist.
Mode  4: OCR1A
Mode 12: ICR1

Das wäre dazu die Grundlage.
1
  TCCR1B = 0;             // Resets
2
  TCCR1A = 0;             //
3
  TCCR1C = 0;             //
4
  TIMSK1 = 0;             //
5
  TCNT1  = 0;             //
6
  OCR1A  = 65535;         // TOP
7
  TIMSK1 = _BV(OCIE1A);   // enable Output-Compare-A Interrupt
8
  TCCR1B  = _BV(WGM12);   // CTC Mode 4
9
  TCCR1B |= _BV(CS11) | _BV(CS10); // Prescaler 64

Die Formel zur Frequenzberechnung steht unter Timing Diagramm im CTC 
Abschnitt.
Beachte den geänderten ISR Vector.
Ich lösche immer zuerst TCCR1B, damit ist der Timer gestoppt und ich 
setzte TCCR1B als letztes, damit alles konfiguriert ist bevor er 
überhaupt wieder läuft. Warum? Weil sich darin die Prescaler Bits 
befinden die das steuern. Die Zuweisung kann man auch optisch aufteilen.

Die Variable z die du in der ISR änderst muss volatile sein und du musst 
mittels atomic den Wert auslesen. Ansonsten handiert dein Programm nur 
mit Zahlenmüll bzw. Zufallszahlen.

Übrigens, wegen deinen Einwand
> Ich würde ja schon lieber Klassisch C statt Arduino verwenden,
> aber hab hier grade leider keine Wahl.

Das hat hiermit überhaupt nichts zu tun. Das hier gezeigte ist direkte 
Registerprogrammierung. Direkter gehts nichts. Hättest du demzufolge 
wissen müssen - laut deinem Einwand. Beim nächstenmal schiebste die 
Schuld bitte nicht auf Arduino. In der IDE kannste auch "klassisch" C 
programmieren. Arduino ist übrigens C++. Also auch ganz klassisch. Du 
kannst auch main/while statt setup/loop verwenden, dann biste wirklich 
auf dich allein gestellt und musst auch keine vorbelegten Register 
löschen. Hast dann aber auch keine fertige Serial zur Verfügung usw.

: Bearbeitet durch User
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.