Arduino UNO R3 Frequence d'horloge et préscalaire

Miaou,

Sur une carte Arduino UNO R3, il y a un quartz 16MHz.
Ai-je raison de penser qu'il est utilisé pour cadencer le microcontrôleur à cette fréquence ?

Le datasheet du microcontrôleur mentionne de nombreuses abrv. pour l'horloge, j'ai du mal à m'y retrouver...

  • CPU Clock – clkCPU correspond-il à ces 16MHz ?

Vient ensuite le préscaler qu'on peut configurer pour les timer via les registres :
Si je code :

TCCR0B |= 0b00000101;

Je m'attends à avoir un diviseur de 1024 donc 15625kHz (soit 64µs entre 2 "tick" du timer0).
Est-ce correct ?
A vous lire,

Le chat.

oui c'est cette horloge à 16Mhz est celui qui pilote le MCU.

votre ligne de code TCCR0B |= 0b00000101; ne force pas à zéro le bit 1 donc s'il était à 1 avant vous pourriez créer un prescaler inattendu.

mais si vous avez bien 101 sur les 3 bits alors oui, cela vous donne 1024/16000000 = 64µs

Notez aussi que sur l’Arduino Uno, l’horloge principale de l’ATmega328P est assurée par un résonateur céramique de 16 MHz, choisi pour son coût réduit tout en offrant une précision suffisante pour la plupart des applications. Sa fréquence nominale de 16 MHz est donc théorique et peut varier légèrement en fonction de la température, de la tension d’alimentation et des tolérances de fabrication du résonateur, contrairement à un quartz qui offre une stabilité beaucoup plus élevée.

Merci pour ce retour rapide.
Bien noté pour la nuance quartz / oscillateur.

Concernant TCCR0B, il est mentionné dans le datasheet que sa valeur initiale est 0.
C'est dans le setup() que je configure ce registre, donc le bit 1 devrait être à 0.
Mais il est je suppose plus sûr de coder :

TCCR0B |= (1<<CS02 & ~(1<<CS01) | 1<<CS00);

oui en théorie mais le setup est appelé après que le fichier main() ait appelé init() qui fait tout un tas de configurations qui ne sont pas super documentées. En pratique je pense que vous avez raison et que ce sera OK, mais vaut mieux être prudent.

Non tant que vous faites un |= vous ne forcerez pas un 0. un OU ne peut mettre que des 1, jamais un 0.

soit vous écrivez directement le registre complet

TCCR0B = (TCCR0B & 0b11111000) | 0b101;

soit vous faites en deux fois avec un &= et un |=

TCCR0B &= 0b11111000; // on ne garde que ce qu'il y a dans les bits de poids forts
TCCR0B |= 0b101;      // on met à 1 dans les 3 bits de poids faible ceux que l'on veut.

Ah oui, effectivement ; merci pour le correctif. :slightly_smiling_face:

Je cherche à effectuer une routine d'interruption sur un overflow du timer0 :

ISR(TIMER0_OVF_vect)
{
  // fait des trucs...
}

A la compilation j'ai ça comme erreur :

wiring.c.o (symbol from plugin): In function __vector_16':* *(.text+0x0): multiple definition of __vector_16'
C:\Users\Utilisateur\AppData\Local\arduino\sketches\AA0468DE9D89FDE7BA4C907B7893278D\sketch\essai.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
exit status 1

Compilation error: exit status 1

Il semble avoir une double def du vecteur... Comprends pas :thinking:, une idée ?

Le vecteur TIMER0_OVF_vect est déjà défini dans le core Arduino ➜ Il est utilisé pour maintenir millis() via une ISR interne sur l’overflow du timer0.

Vous ne pouvez pas l'utiliser si vous prenez le main() arduino.

Vous avez 3 timers pour jouer ➜ Le timer0 est un timer 8 bits, le timer1 est un timer 16 bits et le timer2 est un timer 8 bits.

par exemple pour avoir un tick environ toutes les secondes avec timer2 dans la loop

constexpr uint32_t timer2Prescaler = 64;
constexpr uint32_t deltaT_ms = 1;
constexpr uint8_t seuilOCR = (F_CPU / timer2Prescaler) * deltaT_ms / 1000UL - 1;

volatile unsigned long compteur = 0;
ISR(TIMER2_COMPA_vect) {
  compteur++;
}

void setup() {
  noInterrupts();       // désactive les interruptions globales
  TCCR2A = 1 << WGM21;  // timer2 en mode CTC
  TCCR2B = 1 << CS22;   // préscaler = 64
  OCR2A  = seuilOCR;    // valeur calculée pour un pas de temps en millisecondes
  TIMSK2 = 1 << OCIE2A; // autorise l'interruption sur comparaison A du timer2
  interrupts();         // réactive les interruptions globales
  Serial.begin(115200);
  Serial.println("\n-------------------------");
  Serial.print("F_CPU = "); Serial.print(F_CPU); Serial.println(" Hz");
  Serial.print("∆t = "); Serial.print(deltaT_ms); Serial.println(" ms");
  Serial.print("Seuil = "); Serial.println(seuilOCR);
  Serial.println("-------------------------");
}

void loop() {
  bool tick = false;
  // section critique
  noInterrupts();       // désactive les interruptions globales
  if (tick = (compteur >= 1000)) compteur = 0;
  interrupts();         // réactive les interruptions globales
  if (tick) {
    Serial.println("tick");
  }
}

à tester ici

Ha ha, c'était donc ça, je m'en suis douté après coup... :grinning_face_with_smiling_eyes:

Effectivement, en passant sur le Timer2, tout rentre dans l'ordre (faut juste redéfinir et configurer les registres du Timer2).
Ok, je laisse le Timer0 à millis() et ses comparses delay() et micros()... :saluting_face:

Merci pour ces infos utiles. :slight_smile:
Le chat.