[SOLVED] Timer0 überschreiben möglich?

Hej,

ich hätte mal eine Frage bezüglich dem Timer0, der ja für millis() und delay() verwendet wird.
In meinem Sketch verwende ich eigentlich keine dieser Funktionen und möchte den Timer0 für andere Aufgaben verwenden. Jedoch lässt sich das OCR0A nicht überschreiben, sodass die Frequenz immer bei 1kHz bleibt. Lässt sich das irgendwie ändern?
Eigentlich bin ich mit der Konfiguration der Register recht vertraut, nur bei Timer0 lässt sich nichts ändern. Eventuell kann mir ja jemand helfen. Mein Sketch ruft im setup() Teil folgende Funktion auf:

Nachtrag: geht um den Atmega2560

void set_timer0_pid_isr(void)
{
  cli();  // disable all interrupts

  TCCR0A = 0; // set entire TCCR0A register to 0
  TCCR0B = 0; // set entire TCCR0B register to 0

  switch (PRESCALER_PID_CONTROLLER) // PRESCALER_PID_CONTROLLER = 64
  {
    case 1:                      // SET CS02 & CS01 & CS00 bit for prescaler 1
      TCCR0B &= ~(1 << CS02);
      TCCR0B &= ~(1 << CS01);
      TCCR0B |=  (1 << CS00);    
      break;
    case 8:                     // SET CS02 & CS01 & CS00 bit for prescaler 8
      TCCR0B &= ~(1 << CS02); 
      TCCR0B |=  (1 << CS01);
      TCCR0B &= ~(1 << CS00);   
      break;
    case 64:                    // SET CS02 & CS01 & CS00 bit for prescaler 64
      TCCR0B &= ~(1 << CS02); 
      TCCR0B |=  (1 << CS01);
      TCCR0B |=  (1 << CS00);    
      break;
    case 256:                   // SET CS02 & CS01 & CS00 bit for prescaler 256
      TCCR0B |=  (1 << CS02); 
      TCCR0B &= ~(1 << CS01);
      TCCR0B &= ~(1 << CS00);    
      break;
    case 1024:                  // SET CS02 & CS01 & CS00 bit for prescaler 1024
      TCCR0B |=  (1 << CS02); 
      TCCR0B &= ~(1 << CS01);
      TCCR0B |=  (1 << CS00);    
      break;
    default:                    // SET CS02 & CS01 & CS00 bit for prescaler 64
      TCCR0B &= ~(1 << CS02); 
      TCCR0B |=  (1 << CS01);
      TCCR0B |=  (1 << CS00);    
      break;
  }

  OCR0A = (CPU_FREQ / (PRESCALER_PID_CONTROLLER * 2000UL)) - 1; // set compare value for 0,5 ms -> 2 kHz
  TIMSK0 = (1 << OCIE0A); // enable compare match A interrupt
  TCNT0 = 0;

  sei();  // enable all interrupts

  Serial.print("OCR0A: "); Serial.println(OCR0A);
}

ISR(TIMER0_COMPA_vect)
{
  PORTB ^= (1<<7);
  //PID_CONTROLLER_SAMPLING_TIME_COUNTER < PID_CONTROLLER_SAMPLING_TIME - 1 ? PID_CONTROLLER_SAMPLING_TIME_COUNTER++ : PID_CONTROLLER_SAMPLING_TIME_COUNTER = 0;
}

Die ISR soll nur mit 2kHz den Pin13 Toggeln. Im OCR0A steht auch der richtige Wert 124, allerdings bleibt die Frequenz am Pin weiterhin bei 1kHz.

23:48:38.491 -> OCR0A: 124

Wie kommst du auf die lustige Idee, dass OCR0A im Mode 0 was an der Frequenz ändert?

Fast 2 kHz Toggle (1kHz Rechteck) an Pin 13(PB5), ohne das dir delay() oder millis() verloren gehen.




void set_timer0_pid_isr(void)
{
  cli();  // disable all interrupts
  OCR0A = 42;
  OCR0B = 42+128;
  TIMSK0 = (1 << OCIE0A)|(1 << OCIE0B);
  sei();  // enable all interrupts
}

ISR(TIMER0_COMPA_vect)
{
  PINB = (1 << PB5);
}

ISR(TIMER0_COMPB_vect, ISR_ALIASOF(TIMER0_COMPA_vect));



void setup()
{
  DDRB |= (1 << PB5);
  set_timer0_pid_isr();
}

void loop()
{

}

Das ist nicht Pin 13, der hängt an PB5.

Programm
ISR(TIMER0_COMPA_vect)
{
  PORTB ^= (1<<5);
}

void setup() {
  pinMode(13, OUTPUT);
  cli();//stop interrupts
  TCCR0A = 0;// set entire TCCR2A register to 0
  TCCR0B = 0;// same for TCCR2B
  TCNT0  = 0;//initialize counter value to 0
  // set compare match register for 2khz increments
  OCR0A = 61;
  // turn on CTC mode
  TCCR0A |= (1 << WGM01);
  // Set CS01 and CS00 bits for 64 prescaler
  TCCR0B |= (1 << CS01) | (1 << CS00);
  // enable timer compare interrupt
  TIMSK0 |= (1 << OCIE0A);
  sei();//allow interrupts
}

void loop() {}

OCR0A gibt das obere Ende des TCNT0 an, wann die ISR im CTC Modus aufgerufen wird nach meinem Verständnis.

OCR0A = CPU_FREQ / (PRESCALER * F_TOGGLE) - 1

Im CTC Mode! (Mode 2)
Du verwendest Mode 0

Ich habs auch grade in deiner Korrektur gesehen, dass ich die WGM Bits gar nicht gesetzt habe...

Danke. Naja Wald und Bäume manchmal. :upside_down_face:

Laut Doku ist beim Mega D13 PB7. Hatte ich nicht erwähnt, sorry.

image

Ja!
Aber wie gesagt, mein Beispiel macht auch (fast) 2kHz, ohne den Timer für Arduino kaputt zu konfigurieren.

Nix sorry.

Wieso setzt du OCR0A auf 42?

42 sagt: Der Wert ist irrelevant!

Ersetze beide 42 durch 4711, macht keinen Unterschied.
Wichtig ist alleine der Abstand(128) der beiden.

OCR0A = random();
OCR0B = OCR0A +128;

Ok danke für die Erklärung.

Hallo,

deine Prescalerfunktion würde ich umbauen. Zu viel Code-Dopplungen drin.

void stopTimer0 (void)
{
  TCCR0B &= ~( _BV(CS02) | _BV(CS01) | _BV(CS00) );
}

void setPrescalerTimer0 (const unsigned int prescaler)
{  
  switch (prescaler) {
    case    1 : TCCR0B |= _BV(CS00);              break;
    case    8 : TCCR0B |= _BV(CS01);              break;
    case   64 : TCCR0B |= _BV(CS01) | _BV(CS00);  break;
    case  256 : TCCR0B |= _BV(CS02);              break;
    case 1024 : TCCR0B |= _BV(CS02) | _BV(CS00);  break;
    default :                                     break;  
  }
}

Die Timer Stoppfunktion kannste meinetwegen auch in der SetPrescaler Funktion aufrufen. Je nach deinen Anforderungen. Man sollte sich jedoch immer an eine einfache Regel halten. Vor irgendwelchen Änderungen am Timer diesen zuerst stoppen und erst wieder nach allen Änderungen starten. Das heißt zuerst die Prescaler Bits löschen und zum Schluss setzen. Ansonsten kann es passieren das der Timer mit einer falschen bzw. noch unfertigen Konfiguration losläuft und nicht das macht was er sollte.

Kann dann bei dir so aussehen. Damit kann auf cli/sei verzichtet werten, weil sowieso nichts passiert solange der Timer gestoppt ist. Und wenn der Prescaler Parameter falsch ist passiert auch nichts, weil der Timer bleibt gestoppt. Ansonsten müßte man noch die Formel für OCR0A absichern.

void reConfigTimer0(const unsigned int prescaler)
{
  stopTimer0();
  OCR0A = (CPU_FREQ / (prescaler * 2000UL)) - 1; // set compare value for 0,5 ms -> 2 kHz
  TIMSK0 = _BV(OCIE0A); // enable compare match A interrupt
  TCNT0 = 0;
  setPrescalerTimer0(prescaler);
  Serial.print("OCR0A: "); Serial.println(OCR0A);
}

Wenn CPU_FREQ dem tatsächlichen Controllertakt [Hz] entspricht, kannste auch F_CPU einsetzen.

Zum generellen Timer-Mode einstellen würde ich noch eine extra Funktion schreiben, dann kann auch TIMSK0 dort drin einmalig geändert werden usw..

2 Likes

Danke für den ganzen Input. Werde ich definitiv berücksichtigen und den Code nochmal etwas überarbeiten. Muss zugeben mit den Makros (sind doch Makros oder?) wie _BV bin ich nicht wirklich vertraut. Gibt es dazu irgendwo eine Doku? Konnte dazu auf die Schnelle nichts finden.

Tipp:
Dann wirst du dich daran gewöhnen dürfen länger zu suchen!

https://www.nongnu.org/avr-libc/user-manual/group__avr__sfr.html#ga11643f271076024c395a93800b3d9546

1 Like

Danke für den Link. :wink:

Hallo,

_BV() ist nur eine noch kürzere Schreibweise fürs Bit schupsen. Beim Lesen wird damit besser deutlich was man hier machen möchte. Noch eine Ergänzung damit erst gar keine Missverständnisse aufkommen. Timer stoppen/starten macht natürlich nur Sinn wenn signifikante Änderungen an der Konfigurationen vorgenommen werden. Also wenn du einmal einen PWM Mode verwendest und den Compare Wert für Duty Cycle ständig änderst, dann macht es natürlich keinen Sinn den Timer ein- und auszuschalten. Ansonsten kommt es wie immer auf die Details an. :wink:

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.