Optimiseur OSCCAL pour attiny85 à partir d'un ArduinoISP

Bonjour,
voilà la problématique:

Je fabrique des parties électroniques pour du modélisme bateau. Variateurs switch etc ...

Pour cela il faut décoder le signal PWM du récepteur pour commander un pont en H ou divers relais.

J'ai donc un signal de ce type.

signal

et je dois mesurer avec la meilleure précision un temps qui varie de 1000 microsecondes à 2000 microsecondes.

Quand j'ai un arduino avec un quartz cela ne pose pas vraiment de problème.

Pour un Attiny 85 sur horloge interne la précision c'est plus du tout ça.

Ou on a du bol ...ou (et c'est plus souvent le cas ) on a un décalage en plus ou en moins insupportable pour le neutre (1500) ce qui fait que a zéro on entends le moteur couiner.

Alors on peut toujours décaler le zéro de la radiocommande .... mais bon.

La solution : Régler la fréquence de l'attiny avec OSCCAL.

J'ai lu relu et rerelu un tas de site avec des solutions plus ou moins exotiques, qui vont du réglage par tâtonnement a carrément concevoir et réaliser un appareil qui ne fait que ça (mais qui n'évite pas le chargement d'un programme dans l'attiny).

Chargement d'un programme de réglage dans l'attiny ! soit il faut en passer par là.

Mais moi je fais çà avec une platine arduinoISP (voir un autre post)

:bulb: pourquoi ne pas l'utiliser pour le réglage ??

Aussitôt dis presque aussitôt fait.

Écriture du programme attiny.

// Réglage de la frequence interne de l'attiny 85.
// Ce programme nécessite l'utilisation de l'ArduinoISP modifié.

#include <EEPROM.h>
byte EEpOsc;

// Timer/Counter0 Compare Match A interrupt handler
ISR (TIMER0_COMPA_vect) {
  PORTB ^= 1 << PINB4;        // Invert pin PB4
}

void setup() {
  pinMode(4, OUTPUT);         // Sortie 10kHz
  pinMode(2, INPUT);         // entrée --
  pinMode(0, INPUT);            // entrée ++
  pinMode(1, INPUT);            // entrée  validation

  EEPROM.get(0, EEpOsc); // Récupération de la valeur sauvegardée
  if (EEpOsc !=  0xFF) {
    OSCCAL = EEpOsc;
  } else {EEpOsc = OSCCAL;}

  TCNT0 = 0;                  // Count up from 0
  TCCR0A = 2 << WGM00; // CTC mode

  if (CLKPR == 3)             // If clock set to 1MHz
    TCCR0B = (1 << CS00);   // Set prescaler to /1 (1uS at 1Mhz)
  else                        // Otherwise clock set to 8MHz
    TCCR0B = (2 << CS00);   // Set prescaler to /8 (1uS at 8Mhz)

  GTCCR |= 1 << PSR0;         // Reset prescaler
  OCR0A = 49;                 // 49 + 1 = 50 microseconds (10KHz)
  TIFR = 1 << OCF0A;          // Clear output compare interrupt flag
  TIMSK |= 1 << OCIE0A;       // Enable output compare interrupt

}

void loop() {
  
  if (digitalRead(1) == HIGH) { // uniquement si validation

  if ((digitalRead(0) == HIGH) & ( digitalRead(2) == HIGH)) {
      // sauvegarde OSCCAL
      EEPROM.put(0, EEpOsc);

    } else if ((digitalRead(2) == HIGH) & ( digitalRead(0) == LOW)) {
     EEpOsc --;

    } else if ((digitalRead(2) == LOW) & ( digitalRead(0) == HIGH)) {
      EEpOsc ++;
    }
    while (digitalRead(1) == HIGH) {}; // attendre fin de validation
    OSCCAL = EEpOsc;
  }

}

Reste plus qu'a modifier le programme arduino ISP

void Optimisation() {
  SERIAL.println();
  SERIAL.println("Procédure d'optimisation Osccal");
  SERIAL.println("--------------------------------------");
  SERIAL.println();
  SERIAL.println("X sortie mode optimisation");
  SERIAL.println("W sauvegarde optimisation");
  SERIAL.println("+ Augmenter OSCCAL");
  SERIAL.println("- Diminuer OSCCAL");
  SERIAL.println(); 

      digitalWrite(PIN_SCK, LOW);
      digitalWrite(PIN_MOSI, LOW);
      pinMode(PIN_SCK, OUTPUT);
      pinMode(PIN_MOSI, OUTPUT);
      pinMode(PIN_MISO, OUTPUT);
      
  uint8_t ch = 0;
  while (ch != 'X')
  {
   ch = getch();
  switch (ch) {
      case '+': 
       SERIAL.println("+ Augmenter OSCCAL");
      digitalWrite(PIN_MISO,HIGH); 
      digitalWrite(PIN_MOSI,HIGH);
      delay(200);
      digitalWrite(PIN_MOSI,LOW);
      digitalWrite(PIN_MISO,LOW);
      delay(200);
      break; 
          case '-': 
      SERIAL.println("- Diminuer OSCCAL");
      digitalWrite(PIN_MISO,HIGH); 
      digitalWrite(PIN_SCK,HIGH);
      delay(200);
      digitalWrite(PIN_SCK,LOW);
      digitalWrite(PIN_MISO,LOW);
      delay(200);
      break;
          case 'W': 
        SERIAL.println("W sauvegarde optimisation");
        digitalWrite(PIN_MISO,HIGH); 
        digitalWrite(PIN_MOSI,HIGH);
        digitalWrite(PIN_SCK,HIGH);
        delay(200);
        digitalWrite(PIN_MOSI,LOW);
        digitalWrite(PIN_SCK,LOW);
        digitalWrite(PIN_MISO,LOW);
        delay(200);
      break;
  }
    }
  pinMode(PIN_MISO, INPUT);
  SERIAL.println();
  SERIAL.println("Sortie procédure d'optimisation Osccal");
  SERIAL.println("--------------------------------------");
  SERIAL.println();
}

ajout dans le setup:

  SERIAL.begin(BAUDRATE);
  
  SERIAL.println("--------------------------------------");
  SERIAL.println();
  SERIAL.println("Arduino ISP Opti_OSCCAL ATTINY");
  SERIAL.println("Tapez O pour modifier l'OSCCAL");
  SERIAL.println();
  SERIAL.println("--------------------------------------");
  SERIAL.println();
  

ajout dans avrisp()

 case 'O': // Mode d'optimisation osccal ATTiny
      Optimisation();
      empty_reply();
      break;

et voila :smiley:

il suffit de regler pour avoir un signal a 10 kHz sur l'attiny (fréquencemètre, oscillo) puis de faire une sauvegarde.

dans le programme d’utilisation recharger la valeur OSCCAL stockée en EEprom.

EEPROM.get(0, EEpOsc); // Récupération de la valeur sauvegardée
  if (EEpOsc !=  0xFF) {
    OSCCAL = EEpOsc;
  } else {EEpOsc = OSCCAL;}

PS ne pas oublier de déclarer la variable. Moi je flash mes attiny a 8 mHz donc j'ai fais tous les essais a 8 mHz ... mais ça marche surement a 1 mHz.
PS2: pour reflasher un arduinoISP câblé ne pas oublier de débrancher le condo.

Et en température les réglages sont stables ?

Un frigo c’est autour de 6°
Avec un four il devrait etre possible de faire 50°
Un congelo c’est - 18°

Temps de stabilisation en température : compter entre 1 et 2 h.

Non ca bouge avec la temperature.
Il faudrait surement dans ce cas calibrer a la temperature d utilisation.
Apres peut etre pas oblige de mettre l attiny au frais mais juste la sonde.

C’est l’atmega qui bouge avec la température.
Il devrait etre possible de faire une compensation en température.

Le DS3231, ultra précis, ne fonctionne pas autrement.

Il me semble bien que les atmega ont une information sur la température interne de la puce, accessible en utilisant les registres du multiplexeur analogique.

L’information n’est pas calibrée en degré, elle ne peut pas remplacer une vrai sonde de température.

C’est pour cela qu’elle est passée sous silence, mais pour l’utilisation présente elle est parfaitement utilisable.
Réserve: il n’est pas exclu que la réponse dépende aussi du lot de fabrication.

Encore un peu de travail pour que la réalisation soit totalement finie. :grinning:

Bah pour moi c'est finis puisque c'est la réponse a un besoin.( je ne travaille pas avec des 328 sans quartz et avec ça bouge pas des masses)

C'est l'oscillateur interne de l'attiny qui bouge avec la température. mais bon moi ça me gêne pas trop.

Si j'avais anticipé la chose j'aurais relié sur le pcb toutes les entrées attiny a l'ISP et je suis sur qu'il est possible de faire une procédure automatique.
L'arduino nano mesure la période sur une dizaine de cycle rectifie automatiquement l'OSCCAL, vérifie et sauvegarde puis sort en donnant la valeur de la fréquence rectifiée.

BON la je pose un attiny dans le zif .. je le flashe pour 8 mHz, je transfère le programme OSCCAL ..je tape O, je mesure la fréquence, je fais + ou - autant de fois que nécessaire quand j'ai 10 kHz je sauve, je reviens sur le programme ISP et je transfère le programme définitif .

En fait je perds pas vraiment de temps par rapport a un mode auto surtout qu'avec l'habitude je tape une série de - ou de + en fonction de la fréquence lue ...
Par exemple sur le post plus haut j’ai directement tapé ------ 6 x moins + entrée et ça a gentiment passé a 10.0 kHz.

Après si on considère que c'est pas un projet finis on le passe dans le BAR. Ça me gêne pas plus que ça.

C’etait une boutade :smiley:.
Et puis si tu avais prolongé (si l’attiny le permet) j’en connais ici qui auraient été intéressés.

Bonjour

alors branchement de la sortie 10 k de l'attiny sur D5 du nano ISP mesure de la fréquence (comptage de front) et affichage sur le COM....

et ça marche d’où création d'une procédure de réglage automatique de l'Osccal ....

du coup bah plus besoin de fréquencemètre juste la platine ISP (faut que je refasse un CI car des fils volants Beurkkk )

La suite bientôt des que tout est stable et testé sur plusieurs attiny.



unsigned int Frequence;
void(* resetFunc) (void) = 0; //declare reset function @ address 0

void  MesurerFrequence() {
  pinMode(5, INPUT);
  TCCR1A = 0;   // Le Timer 1 est configuré en simple compteur.
  // 111 pour Source externe sur D5 et comptage sur le front montant :
  TCCR1B = (1 << CS12) | (1 << CS11) | (1 << CS10);
  // compteur à zéro pour une nouvelle mesure.
  TCNT1 = 0;

  //============= Durée de mesures ===============
  delay(99);
  delayMicroseconds(986); // Ajuster si besoin
  // =============================================
  Frequence = TCNT1;
  Frequence = Frequence * 10;
}

void AugmenterOsccal() {
  digitalWrite(PIN_SCK, LOW);
  digitalWrite(PIN_MOSI, HIGH);
  digitalWrite(PIN_MISO, HIGH);
  delay(200);
  digitalWrite(PIN_MISO, LOW);
  digitalWrite(PIN_MOSI, LOW);
  delay(200);
}

void DiminuerOsccal() {
  digitalWrite(PIN_MOSI, LOW);
  digitalWrite(PIN_SCK, HIGH);
  digitalWrite(PIN_MISO, HIGH);
  delay(200);
  digitalWrite(PIN_MISO, LOW);
  digitalWrite(PIN_SCK, LOW);
  delay(200);
}

void SauvegardeEEprom() {
  digitalWrite(PIN_MOSI, HIGH);
  digitalWrite(PIN_SCK, HIGH);
  digitalWrite(PIN_MISO, HIGH);
  delay(200);
  digitalWrite(PIN_MISO, LOW);
  digitalWrite(PIN_MOSI, LOW);
  digitalWrite(PIN_SCK, LOW);
  delay(200);
}

void ReglageAuto()
{
  MesurerFrequence();
  if (Frequence > 10000) {
    while (Frequence > 10000) {
      DiminuerOsccal();
      MesurerFrequence();
      SERIAL.print("Ajustement fréquence : "); SERIAL.println(Frequence);
    }
    AugmenterOsccal();
  } else {
    while (Frequence < 10000) {
      AugmenterOsccal();
      MesurerFrequence();
      SERIAL.print("Ajustement fréquence : "); SERIAL.println(Frequence);
    }
  }
  MesurerFrequence();
  SERIAL.print(F("Ajustement terminé ! fréquence : ")); SERIAL.println(Frequence);
}
void ResetATT() {
pinMode(10, OUTPUT);
  SERIAL.println();
  SERIAL.println(F("------------   RESET ATTiny  ------------"));
  SERIAL.println();
  digitalWrite(10, LOW);
  delay(500);
  digitalWrite(10, HIGH);
  delay(500);
}

void Optimisation() {
  SERIAL.println();
  SERIAL.println(F("Procédure d'optimisation Osccal"));
  SERIAL.println(F("--------------------------------------"));
  SERIAL.println();
  SERIAL.println(F("X Sortie mode optimisation"));
  SERIAL.println(F("W Sauvegarde optimisation et Reset"));
  SERIAL.println(F("+ Augmenter OSCCAL"));
  SERIAL.println(F("- Diminuer OSCCAL"));
  SERIAL.println(F("C Calibrage Automatique"));
  SERIAL.println(F("M Mesurer la fréquence"));
  SERIAL.println(F("R Reset ATTiny"));
  SERIAL.println();
  SERIAL.println(F("***** Tapez une commande *****"));
  SERIAL.println(F("--------------------------------------"));

  pinMode(PIN_SCK, OUTPUT);
  pinMode(PIN_MOSI, OUTPUT);
  pinMode(PIN_MISO, OUTPUT);
  digitalWrite(PIN_SCK, LOW);
  digitalWrite(PIN_MOSI, LOW);
  digitalWrite(PIN_MISO, LOW);
  MesurerFrequence();
  SERIAL.print("Fréquence actuelle : "); SERIAL.println(Frequence);
  int DeltaOsc = 0;
  uint8_t ch = 0;
  while (ch != 'X')
  {
    delay(200);
    MesurerFrequence();
    SERIAL.print("Fréquence actuelle : "); SERIAL.println(Frequence);

    ch = getch();
    switch (ch) {
      case 'C':
        DeltaOsc = 0;
        ReglageAuto();
        break;
      case '+':
        DeltaOsc++;
        SERIAL.print(F("Augmentation OSCCAL : DelTa ")); SERIAL.println(DeltaOsc);
        AugmenterOsccal();
        break;
      case 'M':
        SERIAL.println("Mesurer la fréquence");
        break;
      case '-':
        DeltaOsc--;
        SERIAL.print(F("Diminution OSCCAL : DelTa ")); SERIAL.println(DeltaOsc);
        DiminuerOsccal();
        break;
      case 'W':
        SERIAL.println(F("W sauvegarde optimisation et Reset"));
        SauvegardeEEprom();
        ResetATT();
        break;
      case 'R':
        ResetATT();
        break;
    }
  }

  pinMode(PIN_MISO, INPUT);
  SERIAL.println();
  SERIAL.println(F("Sortie procédure d'optimisation Osccal"));
  SERIAL.println();
  SERIAL.println(F("------------   RESET ISP  ------------"));
  SERIAL.println();
  delay(1000);
  resetFunc();
}

a insérer avant le setup

l'ajout dans le setup est inchangé (voir plus haut)
celui dans l' avrisp idem (voir plus haut)

Pour résumé il est possible de choisir un OSCCAL avec + et - ou lancer une opti automatique.
ensuite il faut graver en eeprom attiny l'osccal par w
le reset est fait et on peut controler

si ok taper X et l'ISP fait un reset.

PS1 ne pas oublier de faire la liaison attiny et D5 de l'arduino.
PS2 La mesure se fait sur 100 ms (possible d'ajuster) c'est largement suffisant vu que la plage de réglage OSCCAL +-1 change la fréquence avec un delta supérieur.
PS3 je ne prétends pas a un exemple de programmation ça doit être améliorable.(la mesure pourrait être une fonction par exemple) mais bon ...

That's all folks !

Gerber_NEW_PCB2_2023-09-06.zip (30.0 KB)

Fichier gerber du pcb modifié


une led cms a été ajoutée ainsi qu une résistance permettant de tester un blink choix par jumper.

Hello jfs59, il est bien beau ce pcb, je suis nouveau, est-ce utilisable pour programmer des Attiny85 par exemple ? Aurais-tu une petite url qui m'explique la méthode ?
Merci d'avance.

Bonjour,
Bien sur il peut programmer la serie des attiny x5 et attiny x4. Et les at328.
La procedure est simple il suffit de choisir dans l environnement ide arduino isp.
Je l utilise aussi pour programmer l eeprom a partir d un programme que j ai ecris en lazarus.

Ps: depuis j ai cablé le nouveau pcb et écris un blink adapté mis le tout en boitier. Je mettrais des photos.

IMG_20231208_111535

Boite plastique allie ....
support CI imprimante 3D
petits pieds idem
je referme et un élastique autour ... faudrait que je fasse un système de fermeture mais bon pas que ça a gérer.
https://fr.aliexpress.com/item/1005005765725627.html

Il me reste 3 Circuits imprimés version 1 sans réglage mais il suffit de souder un fil de liaison

il me reste 3 circuits version 2 (en fait 4 mas je garde un secours) ..

je peux envoyer contre 1 timbre + lettre affranchie au nom du destinataire la version 1
je peux envoyer contre 2 timbre + lettre affranchie au nom du destinataire la version 2
1.16 € la lettre verte ça fait tout compris
1.16 x3 version 1
1.16 x 4 version 2

ou alors vous avez la solution chinoise.

s'inscrire ici en précisant la version (premier arrivé premier servi) puis me mp pour les formalités.
je fais pas de benef ça couvre les frais a peine.
merci.

version 1 ici

j'ai fais tous les tests avec la 1 sauf qu il faut souder un fil dessous et qu il y a pas la led et les jumper.

Bonjour
Votre programme me donne de l'espoir pour régler OSCCAL sur des Attiny 13 que je n'arrive ni à exploiter pour lire des PulseIn d'un recepteur RC, ni à exploiter en Serial. est ce que le code peut gérer l'Attiny 13 ?qui est sur une autre gamme d'horloge (je suis sur 9.6Mhz).
merci

Bonjour
N utilisant pas l attiny 13 je n ai aucune idee de son fonctionnement au niveau des timer
Il faudrait injecter le programme regarder la frequence de sortie et adapter le coefficient de division
Un spécialiste de l att13 pourrait mieux repondre que moi.
Désolé

Pourquoi utiliser att13 a cette frequence exotique ..un interet particulier??
Je fais beaucoup de detection rc et j utilise soit un mini pro ou un att85

Bonjour
L’horloge interne à 9.6Mhz est ce qui était activé par defaut avec la libraire MicroCore souvent utilisé pour l’Attiny13. Pour l’instant je vis avec un changement de l’osccal pendant le setup pour avoir à peu près une seconde de clignotement d’un led testé. Mais ca ne marche pas pour la detection RC. J’ai pas vraiment testé d’autres fréquences. Vu les ressources limitées sur l’attiny13 à 1kb de flash je pense que je vais changer vers le 85 avec une memoire plus grande et tester ensuite votre code.
Merci pour votre aide.

Le 85 est suffisant en RC quand il ne faut pas trop d'E/S
j'ai réalisé un décodeur RC pour variateur de vitesse
Un décodeur RC permettant de commander 2 relais sur une voie avec ou sans mémorisation de l'état
Toutes valeurs paramétrables. plage neutre maxi et mini

Merci pour l’information et les photos ca justifie pourquoi l’attiny 85 est parmi les plus populaire de la série.