Go Down

Topic: Problème générateur d'impulsions à fréquence et rapport cyclique variables (Read 8814 times) previous topic - next topic

Ervann

Salut !

J'aimerais faire un générateur d'impulsions controlé par 2 potentiomètres avec mon arduino mega.
Mais mon code me pose quelques soucis et lorsque je contrôle la sortie des impulsions à l'oscilloscope, c'est assez chaotique.
Il faut pouvoir faire varier la fréquence de 1 Hz à 20 kHz et le rapport cyclique de 0 à 1.
Je me suis déja heurté au problème de la limite de 65536 dans le delayMicroseconds(), mais la je ne vois pas d'où vient le problème ...

Code: [Select]
const int FPIN = 1;
const int RPIN = 2;
const int SIGNAL = 13;

double f;
double t;
double x;

void setup()
{
  pinMode(13, OUTPUT);
}

void loop()
{
  f = map(analogRead(FPIN),0,1023,1,20000);
  t = 1000000/f;
  x = analogRead(RPIN)/1023;
 
 
  if (t > 65000)
  {
    digitalWrite(SIGNAL,HIGH);
    delay(x*t/1000);
    digitalWrite(SIGNAL,LOW);
    delay(1000-x*t/1000);
  }
  else
  {
    digitalWrite(SIGNAL,HIGH);
    delayMicroseconds(x*t);
    digitalWrite(SIGNAL,LOW);
    delayMicroseconds(1000000-x*t);
  }
 
}

Super_Cinci

Malheureusement, les fonctions delay*() ne sont jamais très précises et chaotiques à la base. Ensuite, les digitalWrite() ne prennent pas forcément le même temps d'éxécution (j'ai déjà fait un relevé d'un simple ON/OFF sur une pin sans délai : le signal vibre, il est très loin d'être régulier). Les analogRead ne sont pas constants non plus.

Bref, je te propose de tout reprendre à zéro et de bien te prendre la tête (mais je t'aiderai si tu n'es pas trop pressé, ce week-end). Il va falloir que tu utilises un timer 16 bits (timer1 par exemple), et surtout que tu oublies toutes les fonctions déjà toutes faites du core arduino si tu cherches un minimum de précision.

Si j'ai bien compris, tu as un potar pour la fréquence et un autre pour le rapport cyclique? On utilisera donc le timer 4, qui possède une fonction PWM en hard (c'est-à-dire que c'est déjà électroniquement câblé dans la puce). Il va falloir configurer les registres du timer dans le setup, puis le reste se fera tout seul, tu n'auras à utiliser ni les fonctions delay(), ni digitalWite().

Ton loop ressemblera alors à ceci :

Code: [Select]

void Loop(){
  f = analogRead(FPIN);  // lecture de la fréquence
  x = analogRead(RPIN); // lecture du rapport cyclique
  calcul(f, x);  // mise à jour du timer (quelques µs max)
  // et c'est tout...
}


La fonction calcul() se contentera juste de trouver la bonne valeur à donner aux registres du timer4. Le timer4, une fois configuré se débrouillera tout seul pour que ton signal apparaisse en continu sur une pin (que tu ne pourras cependant pas choisir, puisque c'est déjà câblé dans la puce). Tu pourras donc rajouter tout ce que tu veux comme fonctions dans ton programme, car une fois que tu auras renseigné les registres du timer, il tournera en boucle tout seul dans son coin et de manière totalement transparente vis-à-vis du programme (ne ralentira donc pas du tout le programme).

J'espère que tu n'es pas allergique à l'anglais, il va falloir étudier un peu le datasheet du ATMEGA2560 que je te propose de télécharger dès maintenant sur le site de ATMEL (http://www.atmel.com/Images/doc2549.pdf) et de commencer à regarder un peu, je te guiderai au milieu des 448 pages, n'aies pas peur, c'est pas si compliqué...

J'ai choisi le timer 4, on aurait pu prendre le 1, 3 ou 5 aussi, mais c'est le 4 qui semble le moins contraignant si tu dois ajouter des fonctions à ton programme. Autant préparer le terrain, mais la config de ces 4 timers est identique (ce qui veut dire qu'avec 8 potars, tu pourrais générer 4 signaux PWM de fréquence et rapport cyclique indépendants).

on en reparle demain?

Ervann

Merci pour la réponse !

Oui on peut en reparler demain et je n'ai pas de problèmes avec l'anglais ^^.
(honnêtement, je ne pensais pas qu'il faudrait se prendre la tête pour ce générateur d'impulsions au départ  ;) mais bidouiller les registres devrait être intéressant)
Par contre, je n'aurais pas l'oscillo à disposition ce week-end donc ce sera difficile pour moi de voir le signal de sortie (à part sur la LED de la pin 13, avec des frequences TRES basses pour que je me rende compte des variations de celles-ci) ...

unisev

Je n'y avais jamais pensé mais tu devrais effectivement pouvoir tester ton code avec la LED en changeant simplement la valeur du prescaler :

Sur l'arduino UNO (je ne connais pas le MEGA), le prescaler peut aller jusqu'à "Clk/1024", ce qui donne une durée d'un peu plus de 4 secondes pour un timer 16bit.

La variation de la LED serait donc "lisible" et interpretable à l'oeil...  :)

Sev
EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

icare

Bonsoir,
Tu trouveras également pas mal d'informations dans l'aire de jeu  XD
http://arduino.cc/playground/Main/InterfacingWithHardware#Communication et recherche la rubrique PWM
@+
icare
2B OR NOT(2B) = FF
Arduino 1.0.5 à 1.8.5 + gEdit + Ubuntu 18.04 LTS

Super_Cinci

Salut,

En effet, on va tenter de faire un prog où deux constantes sont à déclarer : Fmin et Fmax. pour la première compilation, on pourrait travailler avec Fmin = 0.25 et Fmax = 10. On ne pourra pas descendre en dessous de 0.25Hz sans changer radicalement le code et donc faire de la PWM soft (par logiciel). Ensuite, on mettra Fmin = 1 et Fmax = 20000 et ça marchera tout seul.

Pour les tests, tu pourras monter un petit HP de radioréveil par exemple en sortie de ton arduino, la PWM s'entend très bien jusqu'à une fréquence de 5 / 6KHz...

Un petit LCD 2 x 16 ne sera pas de trop pour afficher les caractéristiques de ton signal, non? Si tu n'en as pas, c'est pas grave, on utilisera le terminal en audit.

Le temps de regarder un peu (préparer mon cours en quelque sorte) et je reviens vers toi.

Cinci

PS : je ferai un "condensé" de ce topic au fur et à mesure pour sortir un tuto sur la PWM en hard, ça pourrait servir...

Super_Cinci

En attendant, je t'invite à lire dans le chapitre 17 (16 bits Timer/Counter 1, 3, 4 and 5) p.136, les parties 17.1, 17.2, 17.7, 17.9 et 17.11 (les autres sont moins importantes, c'est plus de la chipoterie technique, je ne les ai pas lues). Puis aussi le chapitre 18 (prescaler) en p.169

Ca te fera une première base. même si tu ne retiens pas tout, tu seras moins perdu quand je te parlerai du WGM ou de registres aux noms exotiques, ça te dira déjà quelque chose...

Ervann

Merci à tous pour vos réponses ! J'ai lu les parties de la doc de l'Atmega sur les Timer/Control, même si je ne maitrise pas encore tout et je me renseigne sur les topics du playground que vous m'avez envoyé. Je suis en train d'essayé de comprendre ça et de voir à quels pins correspondent les sorti du microprocesseur ...

Je vous enverrais un bout de code dès que j'aurais un truc qui ressemble à quelque chose.

unisev

Après avoir lu la datasheet je me suis fait un memo :

MemoCounter PDF
MemoCounter DOC

Il est un peu déstructuré mais je m'en sert pour faire des copier/coller de nom de registre ou de valeur, et je m'en sers aussi pour parametrer mes timers.

Si ça peut te servir...

Sev
EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

Super_Cinci

Le plus plus important est de se faire des tableaux qui répertorient ce que l'on peut tirer d'un timer en fonction de sa config (fréquences min et max, précisions, valeurs pour une certaine fréquence...). Par exemle, dans ton cas, on peut par exemple utiliser un prescaler de 1024, et ainsi pouvoir générer une fréquence de 0.25Hz à 15,625KHz. Mais pour ce qui est de faire une PWM précise, à 15,625KHz, on ne peut même pas faire de PWM, puisque le compteur va compter de 0 à 1 en boucle. les seules valeurs de PWM se limiteront à 0 ou 1 et ne donneront aucun signal. Il faudra donc trouver un prescaleur correct (par exemple, en 1/256, ta fréquence min sera 0.95Hz, et là, tu pourras encore faire de la PWM à 15.625KHz (enfin tu auras 4 valeurs possibles seulement)... Le tout est de trouver pour chaque config de timer les plages de fréquences où la PWM est possible (avec 1024 valeurs minimum pour rester précis par rapport à la valeur mesurée du potar correspondant).

Du coup, en regardant tout ça, j'ai trouvé des solutions pour mes gestions de servo encore plus stable que celle que j'avais avant, car après tout, un signal de commande servo, c'est une PWM avec une fréquence entre 50 et 65 Hz, le rapport cyclique donnant la position à prendre...

Dponc merci à toi!

unisev

HS :
Super_Cinci, je suis intéressé par ta gestion de servo, voici la mienne :
http://arduino.cc/forum/index.php/topic,116024.msg873332.html#msg873332

EDIT : Super_Cinci a fait un réponse très interressante ICI.

Fin du HS
EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

Ervann

Salut !

Désolé d'avoir fait trainer ce topic mais j'ai eu une semaine de fou, ou je ne pouvais toucher à rien ...
Merci pour les nouvelles infos et le mémo ! Cette semaine, je vais pouvoir m'y remettre (en espérant que vous aussi parce que je ne me sens pas de le faire seul ... XD )
Je dois envoyer des plans à l'usinage, et je pense qu'après je serais à fond dedans !

Ervann

Désolé, ce topic date un peu, mais vous pouvez le mettre en Résolu voici mon code final :

Code: [Select]
void setup()
{
  TCCR1A = _BV(COM1B1) | _BV(WGM11) | _BV(WGM10);
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);
  OCR1A = 65500;
  OCR1B = 25000;
 
  pinMode(12, OUTPUT);
 
}

void loop()
{
 
}


Finalement, je ne modifie pas les valeurs de fréquence et de rapport cyclique par des potentiomètres, j'ai une table des valeurs possibles par timers et j'ajuste OCR1A et OCR1B dans chaque "case" pour avoir les valeurs exactes que je souhaite.

barbudor

Tu peux le mettre en résolu toi même en éditant le 1er post.

D'ailleurs tu peux aussi mettre la solution en conclusion dand le 1er post

Go Up