Ce que j'ai fait n'est pas sorcier, en partant d'un exemple.
L'IDE Arduino propose un générateur de code morse, moi j'ai pris un exemple de télécommande infrarouge ailleurs sur le web.
Les includes sont tous dans le core ESP32 (aucune bibliothèque à ajouter):
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "Arduino.h"
#include "esp32-hal.h"
J'ai ensuite créé une série de bits d'un paquet IDLE qui sera répété indéfiniment.
#define NR_OF_IDLE_BITS 40
///////////////////////////////////////////////////////////////////////////////
// The NMRA DCC Signal is sent as a square wave with each half having
// identical timing (or nearly identical). Packet Bytes have a minimum of 11
// preamble ONE bits in order to be considered valid by the decoder. For
// RailCom support it is recommended to have at least 16 preamble bits. For the
// Programming Track it is required to have a longer preamble of at least 22
// bits. Packet data follows immediately after the preamble bits, between the
// packet bytes a DCC ZERO is sent. After the last byte of packet data a DCC
// ONE is sent.
//
// DCC ZERO:
// ----------------
// | 96 |
// ---| usec | 96 ---
// | usec |
// ----------------
// DCC ONE:
// --------
// | 58 |
// ---| usec | 58 ---
// | usec |
// --------
//
// The timing can be adjusted via menuconfig with the above being the default
// values when using the APB clock.
// Une trame IDLE est émise quand il n’y a pas de trame de commande à envoyer,
// de façon à alimenter continuellement la voie :
// 111111111111 0 11111111 0 00000000 0 11111111 1
//
///////////////////////////////////////////////////////////////////////////////
int IDLE_pattern[NR_OF_IDLE_BITS] = {1,1,1,1,1,1,1,1,1,1,1,1, 0, 1,1,1,1,1,1,1,1, 0, 0,0,0,0,0,0,0,0, 0, 1,1,1,1,1,1,1,1, 1};
rmt_data_t idle_data[NR_OF_IDLE_BITS];
rmt_obj_t* rmt_send = NULL;
et les 2 variables idle_data (les formes de bits à émettre et rmt_send, pointeur nécessaires au driver.
Ensuite, tout est dans le setup:
void setup()
{
Serial.begin(115200);
Serial.println("init RMT pin33");
if ((rmt_send = rmtInit(33, true, RMT_MEM_64)) == NULL) // init sur DCC pin = 33
{
Serial.println("init sender failed\n");
}
/**
* Sets the clock/divider of timebase the nearest tick to the supplied value in nanoseconds
* return the real actual tick value in ns
*/
float realTick = rmtSetTick(rmt_send, 1000); // a calculer pour 1 uS
Serial.printf("real tick set to: %fns\n", realTick);
int i=0;
/*
* Preparation du jeu d'essai : une trame IDLE
*/
for (i=0;i<NR_OF_IDLE_BITS; i++) {
if (IDLE_pattern[i]==0) { // UN
idle_data[i].level0 = 1;
idle_data[i].duration0 = 96;
idle_data[i].level1 = 0;
idle_data[i].duration1 = 96;
} else { // ZERO
idle_data[i].level0 = 1;
idle_data[i].duration0 = 58;
idle_data[i].level1 = 0;
idle_data[i].duration1 = 58;
}
}
pinMode(32, OUTPUT);
digitalWrite(32, HIGH);
Serial.println("loop");
// Send the data
rmtLoop(rmt_send, idle_data, NR_OF_IDLE_BITS);
}
rmtInit(33, true, RMT_MEM_64) définit la pin de sortie de l'ESP32, le mode émission seulement et la taille du tampon mémoire utilisé.
rmtSetTick(rmt_send, 1000) définit la période des TICKS, ici proches de la microseconde, ce que confirme realTick.
Ensuite la variable Idle_data est garnie avec les valeurs correspondant à chaque bit de la trame DCC
idle_data
.level0 = 1; // le 1/2 bit haut
idle_data.duration0 = 96; //aura une durée de 96 uS
idle_data.level1 = 0; // le 1/2 bit bas
idle_data.duration1 = 96; // également 96 uS
Et l'instruction rmtLoop(rmt_send, idle_data, NR_OF_IDLE_BITS) se charge d'envoyer le signal DCC automatiquement et de façon répétitive.
De ce fait tout cet exemple tient dans le setup.
Mais j'aurai pu utiliser dans la loop l'instruction rmtWrite(rmt_send, idle_data, NR_OF_IDLE_BITS) avec les même arguments,
qui pourrait donner les mêmes résultats ou permettre d'émettre des séquences de trames DCC plus complexes.
Mais il vaut mieux contrôler les flux entre le programme et le tampon RMT, probablement comme l'indique Sébastien en utilisant les interruptions disponibles lorsqu'une fraction du tampon est disponible, car la norme DCC nécessite que les émissions soient permanentes pour alimenter le matériel roulant.
Maintenant il faut intégrer ce mécanismes avec toutes les autres tâches (Wifi, Serial, Can, I2C, AnalogInputs, ..)
Mais cela peut nous rassurer pour le moment, en espérant que ce projet puisse intéresser plusieurs contributeurs pour se renforcer