Voir les contributions

Cette section vous permet de consulter les contributions (messages, sujets et fichiers joints) d'un utilisateur. Vous ne pourrez voir que les contributions des zones auxquelles vous avez accès.


Messages - trimarco232

Pages: [1] 2 3 ... 11
1
Bonjour,
de me suis finalement décidé à dessiner ce décodeur
la base est la même que que le décodeur à uln2003 https://forum.locoduino.org/index.php?topic=1431.0
les uln2003 sont remplacés par de petits mosfets en boitier double sot23-6 ; les caractéristiques de ces mosfets sont 30v, 2A5 continu et 10A pulsé
cela suffit peut-être pour du peco ou du seep, mais je l'ai plutôt prévu pour des solénïdes de roco, fleischmann, trix ...
le dispositif de protection prévu pour les mosfets utilise des NUP2105L, normalement faits pour protéger les lignes CAN, est ambitieux, optimiste ou raisonnable, en tous cas pas encore évalué, on est donc sur de l'expérimental pour cette partie
je ne conçois pas de commande de solénoïde sans dispositif CDU, même si les moteurs sont munis de fin de course ; il vaut même mieux, amha, mettre les fins de course hors service et utiliser le cdu
ici, le cdu est un peu particulier : l'alim se fait en 10-12v, et les tensions nécessaires aux solénoïdes sont 16v, voire 24v pour du peco ; on a donc un convertisseur dcdc boost qui charge la capa cdu à la tension voulue ; l'arduino peut choisir cette tension entre 2 valeurs (par ex 16v ou 24v), on peut donc panacher les moteurs d'aiguille ; le CI convertisseur dcdc est aussi en boitier sot23-6 : c'est un MT3608
le cdu doit stopper la charge de la capa cdu tant qu'il y a une consommation au niveau des solénoïdes : c'es le rôle de l'électronique de contrôle située à droite de la capa cdu (on intervient "simplement" sur la broche enable du CI convertisseur dcdc)
l'arduino mesure aussi la tension de la capa cdu, le but c'est d'attendre qu'elle doit rechargée avant de commander la décharge suivante
en pj, un dessin du décodeur avec l'identification des différentes parties
bien entendu, il y a aussi les cartes d'extensions, visibles en situation sur la 2ème pj

2
la carte décodeur dcc est pilotée par un nano, il y a l'optocoupleur rapide dcc, un module dcdc 5v (option, si on ne peut pas utiliser le 5v du nano), 3 uln2003 et 2 74hc595 ;
j'ai préféré  mettre 3 uln2003 à la place de 2 uln2803, car c'est finalement moins encombrant, d'avantage disponible, et globalement moins cher ... de + ça permet de doubler certaines sorties, utile s'il faut + de courant ;
j'ai mis 2 74hc595 ; le but est de libérer des broches du nano, mais aussi d'appliquer un pwm à l'entrée OE des 74hc595, ce qui permet de moduler la puissance appliquée aux moteurs ; les broches libérées permettent de créer 2 liaisons de type spi (une à gauche, une à droite), pour piloter les cartes d’extension à base de 74hc595 ; les broches en rab sont sorties sur le bornier du bas, agrémenté quelques lignes d'alimentation ;
les cartes d'extension sont comme le décodeur, moins l'optocoupleur, le nano, et le convertisseur dcdc ; les cartes à implanter à gauche sont (légèrement) différentes de celle à implanter à droite, mais incompatibles entre elles ;
une astuce, je commande le pwm des 74hc595 par la ligne du latch ; c'est faisable dans cette application, cela me permet d'économiser un fil ; (cette idée m'est venue tardivement, ça m'a fait modifier tous mes dessins déjà réalisés ...)
la connectique, tant pour les aiguillages ou les modules entre eux, laisse le choix entre des borniers à visser au pas de 3.5, ou des nappes jst xh au pas de 2.5 ;
je n'ai pas chiffré le coût, mais je pense que ça reste très bas par rapport aux produits du commerce ; à part le nano, dont le prix des clones a grimpé, la disponibilité des composants choisis n'a jamais posé de problèmes ; il me faut 2 jours (ou 1 jour et 1 nuit pour dessiner la carte décodeur dcc)
.
le soft reste à faire .. il faut :
- mettre les commandes dans une queue et les actionner à tour de rôle ; (supprimer les commandes contradictoires dans la queue)
- dans l'idéal, je verrais une petite ihm, qui invite depuis la console à saisir et à placer dans l'eeprom quelques paramètres, tels : nombre d'extensions à gauche et à droite, adresse de base, taux du pwm ... cela permettrait à un non arduiniste de paramétrer confortablement le truc (le téléversement du firmware étant effectué), une option qui amha fait souvent défaut dans notre sphère


3
Bonjour,
j'avais dessiné des modules d'extension qui permettent de commander des moteurs type mtb (à la rigueur, ça peut marcher avec des solénoïdes à faible consommation genre fleischmann)
emporté par mon élan, j'ai aussi dessiné un décodeur dcc qui peut servir de maître pour ces modules : la capacité est impressionnante

(j'ai aussi sous le coude des modules à mosfets costauds (pour peco), ou moyens (pour tous solénoïdes raisonnables), le tout commandé par "ma" centrale
je dessinerai peut-être un décodeur maître pour ceux-cis, (s'il y a une demande), un peu moins simple car j'y ajouterai une cdu à xl6007)
édit : le décodeur dcc pour solénoïdes courants (roc, flesichmann, trix ...) est dessiné, voir l'article https://forum.locoduino.org/index.php?topic=1433.msg15526#msg15526

une image, en attendant :

4
j'ai dessiné ces modules pour réduire le nombre de fils entre l'arduino et le tco : 7 en tout
ça fait quand-même du travail, car il faut toujours câbler les leds et les boutons à la fin
.
je suis dans le compromis : pour les leds j'ai choisi des modules à tlc5947 pour éviter les résistances (il me faut aussi pouvoir dimmer chaque led individuellement), mais pour les boutons j'ai choisi les 74hc165 à la place des modules à mcp23017, quite à devoir faire les modules moi-même et devoir y mettre les résistances de pull-up, le but c'était d'avoir quelque chose qui s'intègre bien dans la suite du câblage à 7 fils

pm, on peut faire 3 choses avec des 74hc595 :
- extension du nombre de sorties
- application globale d'un pwm par l'entrée OE (output enable) : je m'en sers pour la commande de moteurs type mtb, le 12v est trop brutal, alors je l'adoucis par du pwm
- synchronisation des sorties : toutes les sorties basculent simultanément lors du latch, ce qu'on ne peut pas faire au delà de 8 sorties avec un arduino ; j'aurai l'occasion d'en reparler

5
Vos projets / Re : centrales dcc esp32 trimarco232
« le: juin 26, 2022, 05:16:48 pm »
bonjour el gringo

je n'ai pas regardé ton code
je te mets la dernière version (toujours très incomplète) du programme
cela fonctionne bien à l'analyseur logique (clone salae + pulseview + plugin dcc)

6
Bonjour msport,
merci pour le test, car j'ai dû me contenter de faire des simulations

7
suite à la demande de Pierrre sur https://www.locoduino.org/spip.php?article161#forum6190

/*
   Decoder for traffic light with three or two /// or 4 leds on ARDUINO NANO

   by default the red leds are switched on
   pin 2 receives DCC interrupts

   TRAFFIC LIGHT WITH THREE leds (GREEN, RED, YELLOW)
   5 decoders of traffic lights with three leds on Arduino NANO/UNO
   the leds are controlled by pins 3 to A3 by triplet
   traffic light 1 : 3,4,5
   traffic light 2 : 6,7,8
   traffic light 3 : 9,10,11
   traffic light 4 : 12,13,A0
   traffic light 5 : A1,A2,A3
   two addresses by traffic light
   even addresses for green and red lights
   odd addresses for yellow light

   /// TRAFFIC LIGHT WITH 4 leds (green, red, yellow, carre))
   4 decoders of traffic lights with 4 leds on Arduino NANO/UNO
   the leds are controlled by pins 3 to A4 by quadruplets
   traffic light 1 : 3,4,5,6
   traffic light 2 : 7,8,9,10
   traffic light 3 : 11,12,13,A0
   traffic light 4 : A1,A2,A3,A4
   two addresses by traffic light
   even addresses for green and red lights
   /// odd addresses for yellow and carre indications

   TRAFFIC LIGHT WITH TWO leds (GREEN, RED)
   8 decoders of traffic lights with two leds on Arduino NANO/UNO
   the leds are controlled by pins 3 to A4 by pair
   traffic light 1 : 3,4
   traffic light 2 : 5,6
   traffic light 3 : 7,8
   traffic light 4 : 9,10
   traffic light 5 : 11,12
   traffic light 6 : 13,A0
   traffic light 7 : A1,A2
   traffic light 8 : A3,A4
   one address by traffic light

   CONFIGURATION
   MODE : determined by the common of the led, LOW if common = HIGH, HIGH if common = LOW
   FIRST_ID_DCC : DCC address of the first traffic light
   NB_TRAFFIC_LIGHT : determined by the kind of traffic light, BICOLOR for two leds, TRICOLOR for three leds

*/

/// modifs / ajouts pour 4 feux
//// dégobage pour modifs pour 4 feux : supprimer la ligne

/***************************************************************************************
              CONFIGURATION SETTING

 ****************************************************************************************/

#define CONSOLE                             // output console, comment this line after checking the soft
#define MODE   LOW                          // LOW or HIGH
#define FIRST_ID_DCC   90                   // first DCC address, DCC_CODE
#define NB_TRAFFIC_LIGHT  FOURCOLOR         //  TRICOLOR or BICOLOR /// ou FOURCOLOR

/**********************************************************************************
     DON'T CHANGE THE FOLLOWING
 *********************************************************************************/

/******************************************************************************
      INTERNAL PARAMETERS

 ********************************************************************************/

//  DCC

#include "DCC_Decoder.h"                // Minabay library /// "DCC_Decoder.c" must be present too in the folder
#define kDCC_INTERRUPT    0             // pin 2 receives DCC interrupts
int previous_address = 0;               // avoids multiple DCC addresses
int previous_position = 2;              // avoids multiple DCC orders
volatile boolean update_light;          // set if an update should be processed after DCC control

// traffic light

#define BICOLOR  8                     // 8 traffic lights with two leds
#define TRICOLOR 5                     // 5 traffic lights with three leds
#define FOURCOLOR 4                    /// 4 traffic lights with 4 leds
#define FIRST_PIN         3            // pin of the first traffic light
#define GREEN             0            // address DCC/0
#define RED               1            // address DCC/1
#define YELLOW            2            // address DCC+1/0
#define CARRE             3            /// address DCC+1/1

// traffic light definition

struct light {
  int address;                    // its DCC address
  int current_position;           // green / red / yellow /// carre
  int green;                      // pin of the green led
  int red;                        // pin of the red led
  int yellow;                     // pin of the yellow led
  int carre;                      /// pin of the carre led
  boolean activation_request;     // request of activation
};
light traffic_light[NB_TRAFFIC_LIGHT];    // the set of traffic light

/********************************************************************
   method called if a request is made by the DCC

 *******************************************************************/

void activation_traffic_light() {
  for (int i = 0; i < NB_TRAFFIC_LIGHT; i++)                 // for all traffic lights
  {
    if (traffic_light[i].activation_request == true)  // if the traffic_light is waiting for activation
    {
      switch (traffic_light[i].current_position)           // we look the current position
      {
        case GREEN : {                                         /// indication GREEN
            digitalWrite(traffic_light[i].green, MODE);        // switch on green
            digitalWrite(traffic_light[i].red, !MODE);         // switch off red
            if ( NB_TRAFFIC_LIGHT == TRICOLOR || NB_TRAFFIC_LIGHT == FOURCOLOR) {
              digitalWrite(traffic_light[i].yellow, !MODE);    // switch off yellow
            }
            if ( NB_TRAFFIC_LIGHT == FOURCOLOR) {         ///
             digitalWrite(traffic_light[i].carre, !MODE);     /// switch off carre
            }
            /**/#ifdef CONSOLE
            Serial.print("active -> signal "); Serial.print(i); Serial.println(" : green led");
            /**/#endif
            break;
          }
        case RED : {                                         /// indication RED
            digitalWrite(traffic_light[i].green, !MODE);     // switch off green
            digitalWrite(traffic_light[i].red, MODE);        // switch on red  /// incomplet ?
            if ( NB_TRAFFIC_LIGHT == TRICOLOR || NB_TRAFFIC_LIGHT == FOURCOLOR) {
              digitalWrite(traffic_light[i].yellow, !MODE);  // switch off yellow
            }
            if ( NB_TRAFFIC_LIGHT == FOURCOLOR) {       ///
              digitalWrite(traffic_light[i].carre, !MODE);   /// switch off carre
            }
            /**/#ifdef CONSOLE
            Serial.print("active -> signal "); Serial.print(i); Serial.println(" : red led");
            /**/#endif
            break;
          }
        case YELLOW : {                                         /// indication YELLOW
            digitalWrite(traffic_light[i].green, !MODE);        // switch off green
            digitalWrite(traffic_light[i].red, !MODE);          // switch off red
            digitalWrite(traffic_light[i].yellow, MODE);        // switch on yellow
            if ( NB_TRAFFIC_LIGHT == FOURCOLOR) {               ///
              digitalWrite(traffic_light[i].carre, !MODE);      /// switch off carre
            }
            /**/#ifdef CONSOLE
            Serial.print("active -> signal "); Serial.print(i); Serial.println(" : yellow led");
            /**/#endif
            break;
          }
        case CARRE : {                                          /// indication CARRE
            digitalWrite(traffic_light[i].green, !MODE);        // switch off green
            digitalWrite(traffic_light[i].red, MODE);           /// switch on red  // incomplet ?
            digitalWrite(traffic_light[i].yellow, !MODE);       // switch off yellow
            digitalWrite(traffic_light[i].carre, MODE);         /// switch on carre
            /**/#ifdef CONSOLE
            Serial.print("active -> signal "); Serial.print(i); Serial.println(" : carre + red led");
            /**/#endif
            break;
          }
      }
    }
    traffic_light[i].activation_request = false;            // the traffic light is updated
  }
  update_light = false;                                        // all updates are made
}

/*************************************************************************************
    DCC method

 ***********************************************************************************/

void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data)
{
  address -= 1; address *= 4; address += 1; address += (data & 0x06) >> 1;    // DCC address decoding
  int led = (data & 0x01) ? GREEN : RED;                                      // DCC/0 or DCC/1
  int traffic_light_index = address;                                          // index of a traffic light
  int color = led;                                                            // the color of the led
  boolean activation = false;
  if ((address != previous_address) || ((led != previous_position) && (address == previous_address))) { // if we change the address or the led
    switch (NB_TRAFFIC_LIGHT) {
      case BICOLOR : {      // if the address is in our range for traffic light with two leds
          if ((address >= FIRST_ID_DCC) && (address < FIRST_ID_DCC + NB_TRAFFIC_LIGHT)) {
            traffic_light_index = address - FIRST_ID_DCC;                                  // index of the traffic light
            activation = true;
          }
          break;
        }
      case TRICOLOR : {     // if the address is in our range for traffic light with three leds
          if ((address >= FIRST_ID_DCC) && (address < FIRST_ID_DCC + (2 * NB_TRAFFIC_LIGHT))) {
            if (address % 2 != 0) {
              traffic_light_index = address - 1;  // if odd address => yellow led
              color = YELLOW;
            }
            traffic_light_index = (traffic_light_index - FIRST_ID_DCC) / 2;                  // index of the traffic light
            activation = true;
          }
          break;
        }
      case FOURCOLOR : {     /// if the address is in our range for traffic light with 4 leds
          if ((address >= FIRST_ID_DCC) && (address < FIRST_ID_DCC + (2 * NB_TRAFFIC_LIGHT))) {
            if (address % 2 != 0) {
              traffic_light_index = address - 1;  // if odd address => yellow led
              if (data & 0x01) color = CARRE; else color = YELLOW; /// en fonction du poids faible
            }
            traffic_light_index = (traffic_light_index - FIRST_ID_DCC) / 2;                  // index of the traffic light
            activation = true;
            /**/#ifdef CONSOLE
            Serial.print("s"); Serial.print(traffic_light_index); Serial.print(" a"); Serial.print(address); Serial.print("/"); Serial.print(data & 0x01); Serial.print(" ");
            /**/#endif
          }
          break;
        }
    }
    traffic_light[traffic_light_index].activation_request =  activation;    // activation is requested
    traffic_light[traffic_light_index].current_position = color;            // state is requested (color of the led)
    update_light = activation;                                              // traffic light update is requested
  }
  previous_address = address; previous_position = led;                     // the current activation is saved
}
/**********************************************************************************************
    setup

 ******************************************************************************************/

void setup() {
#ifdef CONSOLE
  Serial.begin(115200);
#endif
  delay(1500); /// compatible debogage promicro

  int pin_jump = 0;                                                    // a jump for traffic light pins
  int traffic_light_jump = 0;                                          // a jump for traffic light number
  for (int i = 0; i < NB_TRAFFIC_LIGHT; i++) {                         // for all the traffic lights
    traffic_light[i].activation_request = false;                       // no activation request
    traffic_light[i].green = pin_jump + FIRST_PIN;                     // pin number of the green led
    pinMode(traffic_light[i].green, OUTPUT);                           // green led in output(ID DCC/0)
    digitalWrite(traffic_light[i].green, !MODE);                       // green led switch off
    traffic_light[i].red = 1 + pin_jump + FIRST_PIN;                   // pin number of the red led
    pinMode(traffic_light[i].red, OUTPUT);                             // red led in output  (ID DCC/1)
    digitalWrite(traffic_light[i].red, MODE);                          // red led switch on
    if (NB_TRAFFIC_LIGHT == TRICOLOR || NB_TRAFFIC_LIGHT == FOURCOLOR ) {   // if 3 leds /// or 4 leds
      traffic_light[i].address = traffic_light_jump + FIRST_ID_DCC + i;   // its DCC ID
      traffic_light[i].yellow = 2 + pin_jump + FIRST_PIN;              // pin number of the yellow led
      pinMode(traffic_light[i].yellow, OUTPUT);                        // yellow led in output  (ID DCC+1/0)
      digitalWrite(traffic_light[i].yellow, !MODE);                    // yellow led switch off
      traffic_light_jump++;                                            // the following traffic light
      pin_jump += 3;                                                   // the following pin for three leds
      if (NB_TRAFFIC_LIGHT == FOURCOLOR) {                             /// if 4 leds
        traffic_light[i].carre = pin_jump + FIRST_PIN;
        pinMode(traffic_light[i].carre, OUTPUT);                       // carre led in output  (ID DCC+1/1)
        digitalWrite(traffic_light[i].carre, MODE);                    // carre led switch on
        pin_jump += 1;                                                 /// the following pin for 4 leds
      }
    }
    else {                                                             // if two leds
      traffic_light[i].address = FIRST_ID_DCC + i;                     // its DCC ID
      pin_jump += 2;                                                   // the following pin for two leds
    }

  }

  DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true);   // instanciate the DCC
  DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT );                                   // its IT
  update_light = false;                                                             // no update

#ifdef CONSOLE
  //// Serial.begin(115200);
  Serial.println(""); Serial.println(""); Serial.println(""); ///
  for (int i = 0; i < NB_TRAFFIC_LIGHT; i++) {
    Serial.print("traffic light "); Serial.println(i);
    Serial.print("\t green led on pin : "); Serial.print(traffic_light[i].green); Serial.print(" , DCC address : "); Serial.print(traffic_light[i].address); Serial.println("/0");
    Serial.print("\t red led on pin : "); Serial.print(traffic_light[i].red); Serial.print(" , DCC address : "); Serial.print(traffic_light[i].address); Serial.println("/1");
    if (NB_TRAFFIC_LIGHT == TRICOLOR) {
      Serial.print("\t yellow led on pin : "); Serial.print(traffic_light[i].yellow); Serial.print(" , DCC address : "); Serial.print(traffic_light[i].address + 1); Serial.println("/0");
    }
    else   if (NB_TRAFFIC_LIGHT == FOURCOLOR) { ///
      Serial.print("\t yellow led on pin : "); Serial.print(traffic_light[i].yellow); Serial.print(" , DCC address : "); Serial.print(traffic_light[i].address + 1); Serial.println("/0");
      Serial.print("\t carre led on pin : "); Serial.print(traffic_light[i].carre); Serial.print(" , DCC address : "); Serial.print(traffic_light[i].address + 1); Serial.println("/1");
    }
  }
#endif
}

/*************************************************************************
   loop

 ************************************************************************/

void loop() {
  DCC.loop();                   // Is there a DCC command ?

   delay(2000) ; ////
   BasicAccDecoderPacket_Handler(24, 1, 2); ////

  if (update_light) {
    activation_traffic_light(); // if yes, activation of traffic lights
  }

   delay(2000) ; ////
   BasicAccDecoderPacket_Handler(24, 1, 5); ////

   if (update_light) { ////
    activation_traffic_light(); // if yes, activation of traffic lights ////
   }    ////



}

édits : regarder svp. la date du dernier Modifié pour s'assurer d'avoir la dernière version

8
Vos projets / Re : centrales dcc esp32 trimarco232
« le: mai 22, 2022, 11:47:47 am »
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
#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
 
  }

 }


et une image du hard visé :

9
Vos projets / Re : centrales dcc esp32 trimarco232
« le: mai 22, 2022, 12:04:13 am »
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

10
Vos projets / centrales dcc esp32 trimarco232
« le: mai 20, 2022, 12:29:54 pm »
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 ?

11
oui, on est dans la même fratrie technique ; le 74hc165, avec le 74hc595, sont particulièrement adaptés à être utilisés sur une carte, car il n'y sont pas sujets aux problèmes liés à la longueur des fils ; dans mon cas, l'arduino est placé sous le tco, la longueur du câble n'excède pas 50cm entre l'arduino et le 1er module, puis autant entre le 1er et le dernier module
je n'ai pas noté de problème imputable aux parasites, mais il faut dire que les données pour les leds sont rafraîchies périodiquement, et les donnée en entrée font l'objet d'une évaluation majoritaire, cad. un parasite de passage n'est pas pris en compte

pour illustrer la facilité de câblage, j'ai fait ce dessin
dans l'exemple le nombre d'entrées est de 3x16 = 48, et le nombre de sorties leds dimmables est de 2x24 = 48 aussi
les + futés auront remarqué qu'il y a en fait 49 entrées ...
l'arduino et l'alim 5v ou 3v3 sont à gauche
j'ai alterné les modules, mais on peut faire se suivre plusieurs modules du même type, selon le besoin
je dessinerai aussi un module à 74hc595 qui pourra s'insérer, et donner des sorties ordinaires (quand je suis trop fatigué pour faire de la prog, je dessine des pcb ...)
les signaux utilisés sont, de haut en bas :
- MISO : entrée série (spi) de l'arduino
- SHIFT/LOAD : décalage série ou entrées parallèles des 74hc165
- LATCH : verrouillage des sorties des TLC5947 et des 74hc595
- ici s'insère le output enable des TLC5947 et des 74hc595, non utilisé ni raccordé
- MOSI : sortie série (spi) de l'arduino vers les TLC5947 et les 74hc595
- CLOCK : horloge (spi) pour le décalage des données des 74hc165 et des TLC5947, et des 74hc595
- GND : 0 volt
- +5v : pas testé avec du 3v3

édit : j"ai dessiné un module à 74hc595, je l'ai inséré dans la chaîne :



12
Bonjour,
j'ai dessiné ceci (sans savoir si je vais l'adopter)

c'est un module de 16 entrées par registre à décalage 74hc165 ; on peut donc les chaîner pour augmenter le nombre d'entrées
il faut 3 broches de l'arduino, idéalement les miso et sck du périphérique spi, mais des io ordinaires le font aussi avec une bibliothèque qui va bien
il y a 16 résistances de pull-up placées de part et d'autres des 74hc165 : il faut donc que les boutons faisant l'objet des entrées donnent un GND
les résistances situées à droite ne sont placées que sur le dernier module, elles donnent une certaine immunité vis à vis des parasites
la connectique représentée est du jst xh au pas de 2.5mm, mais des bornes à visser au pas de 2.54, ou des fil directement soudés le font aussi
j'ai aussi dessiné ce module avec un seul 74hc165 (8 entrées)
à noter qu'alternativement j'ai aussi mis en oeuvre des modules du commerce i2c à mcp23017
avantages du module à 74hc595 trimarco :
- pas besoin de mettre d'adresse, chainage "illimité"
- disponibilité et prix en période de pénurie
- connectique ad hoc permettant un raccordement nickel
avantages du module i2c mc23017 du commerce :
- pas besoin de résistance de pull-up
- existe tout fait
- peut aussi servir en sortie

le pcb comprend 2 autres modules curieux, qui ne comportent pas de cuivre ... c'est en fait des supports pour les modules
le 2ème dessin reprend la silhouette du 1er, mais les trous de fixation font 3mm au lieu de 2 ; il est prévu à être collé sur la face intérieure du tco ; dans le trou de 3mm, il y aura des canons-écrous qui seront emmanchés à force (ou collés) : le vrai module prendra donc place sur ce support, maintenu par des vis de 2mm vissées dans les canons ; cette solution permet de faire propre et démontable
le 3ème module ne correspond pas aux 2 autres, mais sert de support, selon le même principe, à un module du commerce à tlc5947, pour la commande de 24 leds ; à noter que la connectique aux extrémités du module à 74hc165 permet d’insérer les modules à tlc5947 avec la même élégance au niveau du câblage ; j'ai choisi les modules à tlc5947 principalement parce qu'il n'y a pas besoin de résistances pour les leds
à suivre, peut-être ...

13
Bonjour,
et si on remplace un module, faut-il recompiler tous les autres, pour tenir compte de la nouvelle adresse mac ?

14
Vos projets / Re : Re : AU LABO: montages utiles
« le: avril 14, 2022, 09:13:41 pm »
Bonsoir
(...)
Voici le pcb dessine ce jour pour compléter le "LABO".
(...)
Laurent
au "hazard" : attiny1616 ?

15
Vos projets / Re : Nouveau satellite à 2 composants
« le: avril 14, 2022, 08:36:41 pm »
Bonjour,
en général, 74hct240 voire 74hct04, pour ce qui me concerne

Pages: [1] 2 3 ... 11