Parlons Arduino > Vos projets
les centrales dcc à esp32 de trimarco232
trimarco232:
Bonjour,
ça fait un bail que j'ai ça sous mon coude gauche volumineux, c'est le moment d'en parler un peu
les choses à l'envers ? j'ai commencé par dessiner et réaliser le pcb, puis j'ai attaqué le soft de la génération et de l'envoi des bits dcc
actuellement j'en suis à l'étape suivante : le scheduler (ou planificateur de l'envoi des packets dcc), défi au niveau de l'analyse et de la programmation
ce que j'ai fait c'est la chose suivante, assez simple
les packets dcc créés sont classés prioritaires ou pas, selon leur nature ; par exemple les packets constituant un message de freinage, ou une commande d'accessoire dont le timing est critique, seront prioritaires, les autres non
les packets prioritaires sont placées dans des queues qui seront répétées, et exécutées avant la queue non prioritaire ; ça donne un truc de ce genre :
p3 /// queue fifo où sont mis, donc d'où sont envoyés, les nouveaux packets prioritaires
p2 /// queue fifo de la 1ère répétition des packets prioritaires
p1 /// queue fifo de la 2nde et dernière répétition des packets prioritaires, suite à quoi les packets ex prioritaires sont définitivement placée dans la queue p0
p0 /// queue qui envoie en boucle tous les packets existants, sans notion de priorité
la queue p0 ne fonctionne que si les 3 autres ont été vidées de leur packet(s)
un même packet ne peut être envoyé 2x directement à sa suite : par exemple un packet prioritaire x, qui passe de p3 à p2, attendra qu'un autre packet y soit envoyé, avant que ce packet x soit envoyé de p2 à son tour
ça, ça marche
reste quelques questions au niveau de l'analyse
- chaque packet de vitesse doit rester dans la boucle p0, dans laquelle ils sera (normalement) remplacé par un nouveau packet de vitesse qui a la même adresse
- les packets d'accessoires n'ont pas lieu de rester ni même de figurer dans la boucle p0 ?
- les packets de fonction doivent-ils rester dans la boucle p0 ? lesquels (je pense l'éclairage) ? pourquoi ?
ElGringo:
Bonjour, voilà grâce à des aides précieuses je me suis tenté une petite expérience pour créer une centrale DCC sur esp32.
Ma démarche est elle bonne? mon programme s'inspire des idées de Trimarco.
Si je suis sur le bon chemin est ce que je dois utiliser memcpy pour garder le bit paquet en mémoire ou bien est ce que je peux utiliser une structure pour l'envoyer comme message struct t_message?
en gros dans la fonction d'interruption, le signal pwm se répète tant que le bit ne change pas. Ainsi pour les 20 premiers bit 1, il n'y a pas d interruption. Dès que le bit start est atteint on envoie une interruption et ainsi de suite. ma synchro est elle bonne?
la fonction SpeedAndDir( 3, CRAN_1,128, 1);
permet dans le loop de sélectionner la loco 3, une vitesse sur 28 pas et une marche avant (j ai normé la vitesse a 128 pas discrets avec une interpolation linéaire).
Merci pour votre aide (et je ne suis qu'un humble débutant).
--- Code: ---
#include "driver/ledc.h"
uint64_t paquet;//=0b... 48 bits
uint64_t NewPacket;//0b.. 48bits;
uint64_t masque;
uint64_t dccBit;
bool alter =1;
bool previous=0;
#define CRAN_0 0
#define CRAN_1 1
#define CRAN_2 2
#define CRAN_IDLE 5
byte dccSpeed;
byte addr;
byte adresse;
byte vitesse;
byte cksum;
uint64_t paquet_start=0b111111111111111111110; // 20 bits + bit start
byte startBit=0;
byte stopBit=1;
const int dcc_pin = 19; // Sortie du signal DCC
// setting PWM properties
#define dcc_sig_1_freq 8621 // = 115,99 microS
#define dcc_sig_0_freq 5000 // 200µs 5000 Hz donne 200 microS
const int dcc_pin_channel = 0; // on ouvre un canal qu on reliera à une sortie PWMN
const int dcc_sig_resolution = 1; // correspond à une résolution de 1 bit => 2^1bit =2 pas discrets0 et 100%.
const int dcc_sig_duty = 1; // correspond à une résolution de 1 bit => 2^1bit =2 pats discrets 0 et 100%.
uint32_t dcc_sig_freq = dcc_sig_1_freq; // on part de la fréquence du bit 1 toutes les trames commencent par une série de 20 bits a 1
void dcc_sig_isr() {
dccBit=!!(paquet&masque); // les bits différents de 0 valent 1
masque>>=1; // le masque permet de parcourir les bits
if (dccBit==previous) { // si le bit parcouru est == 0 alors il faut envoyer une interruption, alter qui valait 1 au début se transforme en 0
alter=!alter;
//Serial.print(alter);
alter?dcc_sig_freq =dcc_sig_1_freq:dcc_sig_freq =dcc_sig_0_freq;
}
ledc_set_freq(LEDC_HIGH_SPEED_MODE, LEDC_TIMER_0, dcc_sig_freq); // new -or not - period value
//speed_mode: Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
//timer_num: LEDC timer index (0-3), select from ledc_timer_t
//freq_hz: Set the LEDC frequency
// est ce que je dois laisser cette ligne de code? quelle est son utilité ici? je comprends qu elle permet de selectionner le timer 0 mais pourquoi la mettre ici et pas ailleurs?
}
void setup() {
Serial.begin(115200);
uint64_t paquet =0; // on initialise la trame
uint64_t NewPacket=0; // on utilise une trmne reformatée plus simple pour limiter les interruptions et aider l'esp32
// configuration du canal pwm 0 avec une fréquence et la résolution
ledcSetup(dcc_pin_channel, dcc_sig_freq, dcc_sig_resolution); // on set up le canal avec une résolution de 1 bit cad 50%
ledcAttachPin(dcc_pin, dcc_pin_channel); /// et ensuite on l attache au PIN19
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) =>moi non plus
}
/// Select Cran = 14,28 ou 128
/// Select Speed = 0......128
/// Select Direction 1 Avant, 2 Arriere, 0 Arret
struct __attribute__((packed)) t_message {
byte adresse;
byte vitesse;
byte cksum;
}; t_message unMessage;
// fonction de calcul 3 octets
void SpeedAndDir( byte addr=0b11111111, byte SelectCran=0b00000000, byte SelectSpeed=0b00000000, bool SelectDirection=0) // vecteur de direction et de vitesse
{
byte type;
byte ext;
byte data;
byte cksum=0;
uint64_t paquet=0;
switch(SelectCran) {
case 0: ////DCC_PACKET_TYPE_STEP_14
dccSpeed=map(SelectSpeed,0,128,0,14);
if (dccSpeed) dccSpeed++; // pas de cran 1
data=(SelectDirection?0x60:0x40)|dccSpeed;
cksum=addr^data;
break;
case 1: ///DCC_PACKET_TYPE_STEP_28
dccSpeed=map(SelectSpeed,0,128,0,28);
if (dccSpeed) dccSpeed+=3; // pas de cran 1,2,3
ext=(((dccSpeed&0x01)<<5)|dccSpeed)>>1; ///
data=(SelectDirection?0x60:0x40)|ext;
cksum=addr^data;
break;
case 2: //DCC_PACKET_TYPE_STEP_128;
dccSpeed=map(SelectSpeed,0,128,0,126);
if (dccSpeed) dccSpeed++; // pas de cran 1
data=(SelectDirection?0x60:0x40)|dccSpeed;
cksum=addr^data;
break;
case 5: //IDLE;
data=0b00000000;
cksum=addr^data;
break;
}
//unMessage.adresse=addr;
// unMessage.vitesse=data;
// unMessage.cksum = unMessage.adresse ^ unMessage.vitesse;
//memcpy(payload, &unMessage, sizeof unMessage);
//DccByteCount=sizeof unMessage;
paquet=(paquet_start << 27) |(addr<<19)|(startBit<<18)|(data<<10)|(startBit<<9)|(cksum<<1)|(stopBit<<0);
}
void loop() {
SpeedAndDir( 3, CRAN_1,128, 1);
delay(50000);
}
--- Fin du code ---
Dominique:
J’ai fusionné ces 2 sujets pour éviter le désordre dans le forum :-[
Au passage vous pouvez utiliser le circuit imprimé et le schéma de LaBox pour ce projet :
https://forum.locoduino.org/index.php?topic=922.msg14938#msg14938
N’oubliez pas également le projet de BobyAndCo:
https://forum.locoduino.org/index.php?topic=1352.msg14748#msg14748
On attend des résultats avec impatience, mais prennez votre temps pour bien tester. !
trimarco232:
Bonjour,
(merci Dominique pour la fusion ! )
"Ainsi pour les 20 premiers bit 1, il n'y a pas d interruption"
c'était mon idée de départ ; mais pour l'instant, j'ai laissé l'interruption pour chaque bit du préamble, car il faudra bien un jour introduire le cutout railcom ...
tu es un peu + avancé que moi, pour l'instant je fais fonctionner le truc avec des packets uint64_t formés ... manuellement
le but, c'est de tester l'envoi correct des bits (à l'analyseur logique), ainsi que le fonctionnement du scheduler
la prochaine étape c'est de construire les packets à partir des adresses et des données
pour tester ton code, si tu n'as pas d'analyseur logique, utilises le SerialPrint : 1 ou 0 selon le bit envoyé, tu verras bien si tes packets partent correctement ; mais pour cela, multiplie la durée des périodes pwm par 10, (si non ça plante l'esp32 ...) faudra pas oublier de passer à la bonne vitesse pour un test réel
demain je mets mon code à l'état actuel, ça te donnera peut-être des idées
trimarco232:
je te mets mon code actuel en l'état
il est fait pour mon pcb que j'ai depuis un certain temps, donc il y a du spi dont tu peux ne pas tenir compte
c'est le bazar dans l'organisation, car c'est en phase de recherches, mais ça fonctionne
je suis sous platformio, il faut peut-être adapter pour l'ide arduino
--- Code: ---#include <Arduino.h>
// pour générer les bits dcc par pwm, // 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 du périphérique ledc sont décrites ici
#include "driver/gpio.h" //
////#include "driver/spi_master.h" //
#include "SPI.h" //
#define VSPI_MISO_S88N 13 // inS88n ; à redéfinir pour s88 : 35
#define VSPI_MOSI 18
#define VSPI_SCLK 16
// le hc595 à bord
#define HSPI_MOSI 26 // le hc595 à bord : DATAx
#define HSPI_SCLK 14
#define HSPI_SS 27 // pour le latch automatique
// 595 0 1 2 3 4 5 6 7 8 //
#define L595 1
#define L5947 2
#define PS88 4
#define RST88 8
#define RST88N 16
#define PS88N 32
#define o6 64
#define L7912 128
#define SENSE_SEL 129 // pas de LATCHx
volatile uint8_t latch_x_ram = 0 ; /// voir pour l'initialisation
SPIClass * vspi = NULL;
SPIClass * hspi = NULL;
#define DCC1 22
#define PWM1 25
#define DCC2 23
#define PWM2 19
volatile int32_t old_millis_595 = 0;
#define DCC_PIN 22 // choix de la broche dcc out : n'importe laquelle peut être utilisée /// en se servant de la matrice du gpio
// on définit les périodes pwm des bits dcc 1 et 0
const uint16_t DCC_BIT_1_FREQ = 8621 ; // le sdk demande la fréquence : celle-ci correspond à ~116µs ... quelque chose arrondira à la valeur exacte
const uint16_t DCC_BIT_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_BIT_1_FREQ;
/* les queues du scheduler */
/* cheduler */
volatile uint8_t schedule_p3[16], schedule_p3n = 0 ; // tableau queue, pointeur, nombre
volatile uint8_t schedule_p2[16], schedule_p2n= 0 ; // tous les pointeurs commencent à 1
volatile uint8_t schedule_p1[16], schedule_p1n = 0 ; // pointeur à 0 = pas de packet dans la queue
volatile uint8_t schedule_p0[128], schedule_p0p = 1 , schedule_p0n = 0 ; // 128 à dicuter
volatile uint64_t packet_bits[128] ; // à revoir la profondeur
volatile uint8_t packet_length[128] , packet_bit_pointer, packet_to_send = 0 ;
const uint8_t PREAMBLE_LENGTH = 10, PACKET_2_LENGTH = 28 ;
const uint8_t PACKET_3_LENGTH = 37, PACKET_4_LENGTH = 46 ;
void example_init(void) { /* quelques packets manuels pour tester le générateur de bits dcc */
/// iddle 1111111111+11111111+00000000+EEEEEEEE1 // les + sont des 0
// +11111111+00000000+EEEEEEEE11111111111 // avec preamble à la fin
packet_bits[1] = (uint64_t)0b01111111100000000001111111111111111111; /// 10+9+9+9+1=38
packet_bits[2] = (uint64_t)0b01111111100000000001111111011111111111; /// e bidon ///
packet_length[1] = PACKET_2_LENGTH + PREAMBLE_LENGTH ;
schedule_p0n++ ; schedule_p0[schedule_p0n] = 1 ; // met décodeur 0 dans la queue p0
packet_length[2] = PACKET_2_LENGTH + PREAMBLE_LENGTH ;
schedule_p0n++ ; schedule_p0[schedule_p0n] = 2 ;
/// v = 128 1111111111+0AAAAAAA+00111111+DSSSSSSS+EEEEEEEE1 /// 38+9=47
// : +0AAAAAAA+00111111+DSSSSSSS+EEEEEEEE11111111111 /// avec preamble à la fin
packet_bits[3] = 0b00000111100011111101000011101011011111111111111; /// a=15+ s=7
packet_length[3] = PACKET_3_LENGTH + PREAMBLE_LENGTH;
schedule_p3n++ ; schedule_p3[schedule_p3n] = 3 ; // met décodeur 2 dans la queue p3
/// V127 long: 1111111111+11AAAAAA+AAAAAAAA+00111111+DSSSSSSS+EEEEEEEE1 /// 47+9=56
// +11AAAAAA+AAAAAAAA+00111111+DSSSSSSS+EEEEEEEE11111111111 // avec preamble à la fin
packet_bits[4] = 0b01100000100000000100011111101000100000111011111111111111; /// a=16+ s=8
packet_length[4] = PACKET_4_LENGTH + PREAMBLE_LENGTH;
schedule_p0n++ ; schedule_p0[schedule_p0n] = 4 ;
/// /ACC1-2047: 1111111111+10AAAAAA+0AAA0AA1+000xxxxx+EEEEEEEE1 /// 47 // AAA = msb inversés ?
// +10AAAAAA+0AAA0AA1+000xxxxx+EEEEEEEE11111111111 // avec preamble à la fin
packet_bits[5] = 0b01000000000111001100001010101110011011111111111;
packet_length[5] = PACKET_3_LENGTH + PREAMBLE_LENGTH;
schedule_p0n++ ; schedule_p0[schedule_p0n] = 5 ;
//schedule_p3n++ ; schedule_p3[schedule_p3n] = 5 ; // met décodeur 5 dans la queue p3
/// F20-F19-F18-F17-F16-F15-F14-F13 puis F28-F27-F26-F25-F24-F23-F22-F21
// FCT 13-20: 1111111111+0AAAAAAA+11011110+09876543+EEEEEEEE1 /// 47
// +0AAAAAAA+11011110+09876543+EEEEEEEE11111111111// avec preamble à la fin
packet_bits[6] = 0b00101010101101111000001000001001101111111111111; /// a=16+ s=8
packet_length[6] = PACKET_3_LENGTH + PREAMBLE_LENGTH;
/// schedule_p0n++ ; schedule_p0[schedule_p0n] = 6 ;
}
/* déclarations des voids */
void dcc_sig_isr() ; /// il faut les déclarer
void setup_ledc(void) ;
void setup_spi(void) ;
void latch_x_low_high(uint8_t latche, uint8_t low_high) ;
void send_byte_595f(uint8_t byte_595) ;
void scheduler(void) ;
static uint8_t parti = 0; //// pour test entrée nouveau packet
void setup() {
Serial.begin(460800); // c'est la vitesse du téléversement
setup_ledc() ;
setup_spi() ;
example_init();
/// pour commencer
packet_to_send = 0 ;
Serial.print("pbp:") ; Serial.println(packet_bit_pointer);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite (LED_BUILTIN, HIGH); // allumé
}
void loop() {
/* envoi des packets, appel scheduler */
if (packet_bit_pointer > PACKET_4_LENGTH + PREAMBLE_LENGTH ) { //// grosse erreur pointeur
packet_bit_pointer = 0;
Serial.print("err_pbp > 5 : ");
Serial.println(packet_bit_pointer);
}
/* very poor men's scheduler pour tester */
else if (packet_bit_pointer == 0 ) { // un packet est parti
scheduler();
packet_bit_pointer = packet_length[packet_to_send] ; // initialise le pointeur
Serial.print(""); Serial.print(packet_to_send); Serial.println(" ");
}
/* timer millis pour test spi */
int32_t millis_595 = millis();
if ((millis_595 - old_millis_595) > 10 ) {
old_millis_595 = millis_595 ;
//// Serial.println("o");
/*
if (latch_x_ram & L5947) {
latch_x_low_high(L5947, LOW); // test QA-QH
latch_x_low_high(SENSE_SEL, LOW); // test QH'
digitalWrite (LED_BUILTIN, LOW); // pas allumé
}
else {
latch_x_low_high(L5947, HIGH); // test QA-QH
latch_x_low_high(SENSE_SEL, HIGH); // test QH'
digitalWrite (LED_BUILTIN, HIGH); // allumé
}
*/
send_byte_595f(0);
}
}
void dcc_sig_isr() {
/* moteur pour la génération des bits dcc et l'égrainage des uint64_t */
// dans l'isr, ne pas envoyer de serial pour déboger les bits, ça plante l'esp32 !
static uint32_t old_dcc_sig_freq ;
///// Serial.println(packet_bit_pointer);
if (packet_bit_pointer == 0) { // nouveau packet pas prêt, alors ...
dcc_sig_freq = DCC_BIT_1_FREQ ; // ... on continue le preamble en attendant le packet
////Serial.print("x"); // signale "l'erreur" ///
}
else {
packet_bit_pointer-- ; // décrémente le pointeur de bit du packet
uint64_t bit_pointer_mask = (uint64_t)1 << packet_bit_pointer ; // il faut caster le << sur 64 bits
if ( bit_pointer_mask & packet_bits[packet_to_send]) { // si le bit pointé = 1 ...
dcc_sig_freq = DCC_BIT_1_FREQ ; // ... envoie le bit dcc 1
}
else {
dcc_sig_freq = DCC_BIT_0_FREQ ; // ... si non, envoie le bit dcc 0
}
}
// 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)
if (dcc_sig_freq != old_dcc_sig_freq) { // si la période pwm doit être changée
ledc_set_freq(LEDC_HIGH_SPEED_MODE, LEDC_TIMER_0, dcc_sig_freq); // new period value
dcc_sig_freq = old_dcc_sig_freq ; // actualisation du old
/// Serial.println("c");
}
}
void setup_ledc(void) {
/* configuration d'un timer pour l'envoi de bits dcc méthode pwm */
// 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, FALLING); // l'interruption "dcc_sig_isr" provoquée à la fin de chaque période (voire au début de la suivante)
// le signal DCC est inversé pour mes besoins ; si pas besoin, mettre RISING à la place de FALLING ci-avant et ...
// ... enlever la ligne qui suit :
gpio_matrix_out(DCC_PIN, LEDC_HS_SIG_OUT0_IDX, true, false); // inverse le signal LEDC 0
/// sortie PWM active pour débogage
pinMode(PWM1, OUTPUT); //HSPI SS
digitalWrite(PWM1, LOW); // HC04 : changer dznd la matrice pour le pwm
}
void setup_spi(void) {
/* initialisation du hspi pour le hc595 à bord, et du vspi pour les S88 et modules à décalage externes */
hspi = new SPIClass(HSPI); // initialisation of onboard 74hct595
hspi->begin(HSPI_SCLK, -1, HSPI_MOSI, -1);
hspi->setHwCs(true); // end of /CS triggers automatically the latch of 74hct595
pinMode(HSPI_SS, OUTPUT); //HSPI SS
digitalWrite(HSPI_SS, HIGH); //pull ss high to signify end of data transfer
vspi = new SPIClass(VSPI); // initialisation of outboard SPIs
vspi->begin(VSPI_SCLK, VSPI_MISO_S88N, VSPI_MOSI, -1);
// void gpio_matrix_out(uint32_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv)
gpio_matrix_out(VSPI_SCLK, VSPICLK_OUT_IDX, true, false); // true = invert SCLK on VSPI
gpio_matrix_out(VSPI_MOSI, VSPID_OUT_IDX, true, false); // true = invert MOSI on VSPI
}
void latch_x_low_high(uint8_t latche, uint8_t low_high) {
/* positionne un des signaux du hc595x à board */
uint8_t sense_sel ;
///// hspi->setHwCs(true); // mode Cs automatiquene peut pas être utilisé à cause du QH' (latches parasites aux changements de mode)
if (latche == SENSE_SEL) { // cas particulier : pas de changement en ram ni de latch
sense_sel = 1 ; // flag pour pas de latch
if (low_high == LOW) latche = latch_x_ram & ~128; // désactivation de QH'
else latche = latch_x_ram | 128; // activation de QH'
}
else { // cas courant : latch et changement en ram
sense_sel = 0 ; // flag pour latch
if (low_high == LOW) latch_x_ram &= ~latche; // désactivation en ram
else latch_x_ram |= latche; // activation en ram
latche = latch_x_ram ;
}
///Serial.print("n"); Serial.println(latche);
hspi->beginTransaction(SPISettings {1000000, MSBFIRST, SPI_MODE0}); // à 1MHz ça fait 10us pour l'opération
if (! sense_sel) digitalWrite(HSPI_SS, LOW); // prepare le latch
hspi->transfer(latche);
if (! sense_sel) digitalWrite(HSPI_SS, HIGH); // latch
hspi->endTransaction();
}
void send_byte_595f(uint8_t byte_595) { /// pour commencer pour tester le vspi
// envoie un octet en externe par le vspi
///Serial.print("595 "); Serial.println(byte_595);
vspi->beginTransaction(SPISettings {10000, MSBFIRST, SPI_MODE0}); // à 10kHz ça fait 1ms pour un octet
latch_x_low_high(L5947, LOW); // prepare le latch
vspi->transfer(0b01010101);
latch_x_low_high(L5947, HIGH); // latch
vspi->endTransaction();
}
void scheduler(void) {
/* il y a 2 niveaux de priorité : 3 et 0
les packets assez urgents arrivent en (priorité 3) p3, tous les autres en p0 (priorité 0)
la p3, c'est par exemple les commandes de réduction de vitesse, ou des commandes de solénoîdes, dont le timing est critique
tous le reste arriven en priorité p0
les packets en p3 doivent être traités d'aabord, puis répétés (2x, à débattre) d'abord aussi
ainsi un packet en p3 passe en p2 pui en p1 et finit en p0, après chacun de ses envois
les packets en priorité p0 sont mis à la queue p0, ils y tournent en boucle
le scheduler va d'abord voir s'il y a des packets en p3, il les envoie, puis s'il ny en a plus, va voir en p2, etc.
à la fin de chaque envoi, on retourne voir ce qu'il y a en p1, etc
si un packet prioritaire pour être envoyé ... vient d'être envoyé, il passe son tour
*/
// va voir en p3 ; si le tableau est vide, schedule_p3n = 0 ;
if (schedule_p3n && (packet_to_send != schedule_p3[1])) { // priorité 3 // vérifie que le packet prioritaire ne vien pas d'être envoyé
packet_to_send = schedule_p3[1];
for (uint8_t i = 1; i < schedule_p3n; i++) { // après envoi du 1er, décale tout à gauche, vois pas comment faire autrement ...
schedule_p3[i] = schedule_p3[i+1] ;
}
schedule_p3n--; // décrémente le nombre de packets présents dans le p0
Serial.print("p3 "); Serial.println(packet_to_send); /// debog
schedule_p2n++; schedule_p2[schedule_p2n] = packet_to_send ;
}
else if (schedule_p2n && (packet_to_send != schedule_p2[1])) { // priorité 2
packet_to_send = schedule_p2[1];
for (uint8_t i = 1; i < schedule_p2n; i++) {
schedule_p2[i] = schedule_p2[i+1] ;
}
schedule_p2n--;
Serial.print("p2 "); Serial.println(packet_to_send);
schedule_p1n++; schedule_p1[schedule_p1n] = packet_to_send ;
}
else if (schedule_p1n && (packet_to_send != schedule_p1[1])) { // priorité 1
packet_to_send = schedule_p1[1];
for (uint8_t i = 1; i < schedule_p1n; i++) {
schedule_p1[i] = schedule_p1[i+1] ;
}
schedule_p1n--;
Serial.print("p1 "); Serial.println(packet_to_send);
schedule_p0n++; schedule_p0[schedule_p0n] = packet_to_send ;
}
else { // priorité 0 ; boucle de fond
packet_to_send = schedule_p0[schedule_p0p]; // envoi du packet pointé
Serial.print("p0 "); Serial.println(packet_to_send);
schedule_p0p++; // pointeur va 1 rang à droite
if (schedule_p0p > schedule_p0n) { // pointeur sorti de la queue
schedule_p0p = 1; // les pointeurs commencent à 1
}
int read0 = digitalRead(0) ; /// pour tester la réception d'un nouveau packet par le scheduler
if ( ! read0 && ! parti ) { // évite la récetion en boucle
digitalWrite (LED_BUILTIN, LOW);
parti = 1;
schedule_p3n++ ; schedule_p3[schedule_p3n] = 6 ;
}
else
digitalWrite (LED_BUILTIN, HIGH); /// test button
}
}
--- Fin du code ---
et une image du hard visé :
Navigation
[#] Page suivante
Utiliser la version classique