LOCODUINO

Parlons Arduino => Le logiciel DCC++ => Discussion démarrée par: trimarco232 le octobre 05, 2019, 08:08:01 pm

Titre: DCC++ avec ESP32 ?
Posté par: trimarco232 le octobre 05, 2019, 08:08:01 pm
Bonjour,
juste pour vous dire que j'ai écrit un bout de code qui pourrait servir de petit début à une station dcc animée par un esp32

le but est de générer des bits 0 et 1 en dcc, selon un principe proche de celui utilisé pour dcc++

explication :
un bit dcc ressemble à une période pwm, dont la durée est 200µs pour le bit 0 et 116µs pour le bit 1, le rapport cyclique étant dans tous les cas 1/2 ... simple ...
il faut donc après l'émission de chaque bit dcc, générer une interruption au service de laquelle on indiquera au timer la prochaine période à respecter : si la valeur du bit dcc change, on rentre la nouvelle période (200 ou 116µs) ; si la valeur du bit dcc ne change pas, il n'y a rien à faire /// dans tous les cas on dispose de 58µs pour rentrer la nouvelle période, un éternité pour l'esp32

auparavant il faut configurer un canal de timer :
- en pwm
- avec un rapport cyclique de 1/2
- pour qu'une interruption se déclenche à la fin de chaque période

concrètement :
(je suis loin d'être un pro du soft, je me suis débrouillé avec des fonctions dénichées dans le sdk, on peut sûrement faire mieux)

on commence par les déclarations :
// on utilise un module qui s'appelle ledc ... selon la tradition esp32 les modules pwm servent d'abord aux leds ...
#include "driver/ledc.h" // les fonctions spécifiques sont décrites ici

const int dcc_pin = 19;  // choix de la broche dcc out : n'importe laquelle peut être utilisée /// en se servant de la matrice du gpio

// setting PWM properties
#define dcc_sig_1_freq 8621 // le sdk demande la fréquence : celle-ci correspond à ~116µs ... quelque chose arrondira à la valeur exacte
#define dcc_sig_0_freq 5000 // 200µs (ici j'utilise des macros pour définir ces constantes)
const int dcc_pin_channel = 0; // il y a foison de canaux pwm, j'ai choisi le 1er
const int dcc_sig_resolution = 1; // nombre de bits définissant la résolution du pwm ; ici un seul bit (2 valeurs différentes) suffit
/// la résolution, c'est la granulométrie du rapport cyclique : la + grosse (ne) permet (que) de faire du 50%
const int dcc_sig_duty = 1; // pour le rapport cyclique de 1/2 il faut la valeur 1 (si non c'est 0 ... )
// initialisation de la variable au bit dcc 0 ; why not ; noter que la fonction utilisatrice veut le type uint32_t pour la fréquence
uint32_t dcc_sig_freq = dcc_sig_0_freq;

et voici le setup :
void setup() {
  // configuration du canal pwm 0 avec une fréquence et la résolution
  ledcSetup(dcc_pin_channel, dcc_sig_freq, dcc_sig_resolution); // ledc les faire ...
  // relie le canal pwm à la pin choisie pour la sortie pwm /// ... et dcc par conséquent
  ledcAttachPin(dcc_pin, dcc_pin_channel); /// ! pour détacher (si besoin), il faut impérativement passer par la matrice gpio
  // fixe le rapport cyclique du canal à 1/2
  ledcWrite(dcc_pin_channel, dcc_sig_duty);
  // programme l'interruption à la fin de la période
// je n'ai pas trouvé de manière élégante pour le faire avec le timer, alors je me suis rabattu pragmatiquement sur une interruption provoquée ... par le basculement de la pin
  attachInterrupt(dcc_pin, dcc_sig_isr, RISING); // l'interruption "dcc_sig_isr" provoquée à la fin de chaque période (voire au début de la suivante)
}

et l'isr :
pour vérifier le fonctionnement je génère une suite de 0/1/0/1 ad lib
il faudra adapter pour dcc++
void dcc_sig_isr() {
  if (dcc_sig_freq == dcc_sig_0_freq) dcc_sig_freq = dcc_sig_1_freq ; // inverse la valeur du bit dcc précédent
  else dcc_sig_freq = dcc_sig_0_freq ;
// la fonction qui permet d'écrire la nouvelle fréquence demande :
// - le mode : ici high speed, (au pif) qui va bien
// - le timer utilisé : j'ai supposé bêtement que le canal pwm 0 correspond au timer pwm 0 /// je crois qu'il y a 2 canaux par timer
// - la fréquence, of course
  // ledc_set_freq(ledc_mode_tspeed_mode, ledc_timer_ttimer_num, uint32_t freq_hz)
  ledc_set_freq(LEDC_HIGH_SPEED_MODE, LEDC_TIMER_0, dcc_sig_freq); // new -or not - period value
}

pour ceux qui veulent vérifier, un petit main qui fait travailler l'esp32 :
void loop() {
  delay(10); // mettez ici ce que vous voudrez
}


les mesures réalisées donnent des résultats très corrects : il manque parfois 41ns à une période. Peut-être mon modeste matériel, en tous cas, je n'ai pas les moyens d'en trouver la cause, et on est de toute façon largement dans les clous