/* D18 ACC DECODER Introduction: FRANCAIS: Sans extension, ce decodeur permet de contrôler 16 sorties directes (ou 8 paires de sorties). Cela est le double d'un decodeur d'accessoires traditionnel (4 paires de sorties). Les sorties peuvent etre fixes ou generer des impulsions. Vous pouvez ajouter des modules MAX7219/21 a la place de 3 sorties directes. Chaque module supporte 64 LEDs. Le programme actuel supporte jusqu'a 4 modules, soit 256 LEDs. Vous pouvez aussi contrôler le clignotement des LEDs ainsi que la phase. Vous pouvez ajouter des modules PCA9685 a la place de 2 sorties directes. Chaque module ajoute 16 sorties. Chacune de ces sorties peut fonctionner en mode PWM (0-100%), cela permet de faire varier l'eclairage ou la vitesse d'un moteur. Pour une sortie traditionnelle, utilisez 0% ou 100% Chacune de ces sorties peut fonctioner en mode SERVO (500ms->2500ms 50Hz), pour contrôler un servo-moteur. Le programme actuel supporte jusqu'a 6 modules, soit 96 nouvelles sorties. Vous pouvez ajouter des modules TLC5947 a la place de 3 sorties directes. Chaque module supporte 24 LEDs reglables en luminosite. Le programme actuel supporte jusqu'a 8 modules, soit 192 LEDs. Vous pouvez aussi contrôler des Neopixels (LEDs chainables multicolores) a la place d'une sortie directe. Pour etre compatible avec toutes les centrales (et surtout la vôtre) et etre passe partout. L’utilisateur peut utiliser n'importe quel ordre DCC ("Accessory" ou "Output") pour contrôler n'importe quelle sortie. La configuration se fait dans ce code (dans la partie USER) au lieu d'utiliser les CV. Par defaut le programme est configure pour contrôler 8 paires d sorties. ENGLISH: Without extension, this decoder allows to control 16 direct outputs (or 8 output pairs). This is the double of traditional DCC accessories decoders having 4 output pairs. Output can be fix or generate pulses Configuration is done in this code (in USER part) instead of using CVs. You can add MAX7219/21 modules in place of 3 direct outputs. Each module can drive 64 LEDs. Actual SW supports up to 4 modules so 256 leds. You can also control LEDs blinking and blinking phase. You can add PCA9685 modules in place of 2 direct outputs. Each module adds 16 outputs. Each of these outputs can work in PWM mode (0-100%), this allow dimming to control for instance lights intensity or motor speed. To have the behavior of a traditional output, you can une 0% and 100%. Each of these outputs can also work in SERVO mode to control a servo-motor. Actual SW supports up to 6 modules so 96 new outputs. You can add TLC5947 modules in place of 3 direct outputs. Each module support 24 LEDs with dimming Actual SW supports up to 8 modules so 192 pwm leds. You can also drive Neopixels (chained RGB leds) in place of 1 direct out. To be compatible with any centrals (and mainly your central) and be versatile, User can map any order ("Accessory" or "Output") from DCC signal to any output. By default SW is delivered to manage 8 pairs on address 10 & 11 History: 2020-04-08: Ulysse Delmas-Begue: ajout des aiguillages multiplexes 2020-04-06: Ulysse Delmas-Begue: correction d'un bug sur user_out 2020-03-05: Ulysse Delmas-Begue: ajout du support des TLC5947 (24 sorties pwm pour des leds et par TLC5947 8max=192leds) possibilite de changer les pattes de connexion des modules remplacement des constantes d'activation des modules par le nombre de modules 2020-03-03: Ulysse Delmas-Begue: clean acvant release 2020-03-02: Ulysse Delmas-Begue: ajout de la detection des paquets dupliques (pour eventuellement les rejetter) ajout de l'eeprom dans le moniteur ajout du deplacement lent pour onboard_servo (test ok) 2020-02-29: Ulysse Delmas-Begue: ajout de onboard_servo (12 max, sans mouvement lent pour l'instant) (test ok) --> mais vibrations au debut des tests ajout de onboard_pwm (6max sur UNO, 13max sur MEGA, sans fading pour l'instant) (test ok) 2020-02-29: Ulysse Delmas-Begue: ajout du fading sur ls sorties pwm (test ok) --> mais clignotement lors du passage de 50% a 100% avec T=10000ms 2020-02-28: Ulysse Delmas-Begue: commande des servos sans glitchs (test ok) 2020-02-27: Ulysse Delmas-Begue: ajout du moniteur serie pour mettre au point/debuguer plus facilement (test ok) 2020-02-26: Ulysse Delmas-Begue: commande des pca9685 dans fastloop() pour eviter les sacades des servos (test ok) 2017-02-08: Ulysse Delmas-Begue: ajout de la rotation lente des servos changement de la resolution des tempos de 20ms a 125ms 2017-01-12: Ulysse Delmas-Begue: support Arduino Mega board to add 50 direct outputs in addition to Arduino UNO and Nano AT Mega is enbaled when user select Arduino Mega board in Arduino IDE serigraphied number is now used for direct outputs instead of out 0-15 output number is checked in user_out function 2017-01-12: Ulysse Delmas-Begue: support 256LEDs (via 4 MAX7219/21) and 96PWM/SERCO (via 6 PCA9685) 2017-01-11: Ulysse Delmas-Begue: add 128leds (via MAX7219/21) OK, 48pwm/servos (via PCA9685 I2C), 60neopixels (test MAX, PCA(PWM&SRV) et pulses ok, neo not stable board finish to freeze) 2017-01-10: Ulysse Delmas-Begue: creation (et test out ok avec D17) To be done: - le fading ne fonctionne pas correctement - reduire a 20 ou 50ms la mise a jour des mouvements lents et fading. - reduire l'utilisation memoire pour les mouvements lents - ajout du decodage des decodeurs des loc ? - rajouter fading sur onboard pwm ? - rajouter des aides pour le multiplexage des aiguillages ? License: GPL v2 Notes: - I used foloowing code as startup point: Simple DCC Led Accessory Decoder Luca Dentella, 25.11.2017 http://www.lucadentella.it/en/2017/11/25/dcc-decoder-accessori-per-led/ Normally decoder address and pulses are set by following CVs but here they are hardcoded - CV513 ADR LSB 6bits - CV515 pair 0 pulse - CV516 pair 1 pulse - CV517 pair 2 pulse - CV518 pair 3 pulse duration - CV521 ADR MSB 3bits Pinout on an Arduino UNO L5947 CLI +-+ +~+ +~+ +~+ +++ +++ +~+ +~+ +++ +~+ DCC TX RX D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 +++ = out ########################################################### +~+ = out with pwm #USB###########################################TOP#view#### +-+ = out or used by MAX,PCA,NEO,TLC5947 (it is possible to change pin) ########################################################### use following numbers in SW: 3-12 & A0-A5 ########################################################### max out = 16 x x x x x x x x A0 A1 A2 A3 A4 A5 +-+ +-+ +-+ +-+ +-+ +-+ CLK DAT LLED NEO SDA SCK Pinout on an Arduino Nano L5947 +++ +~+ +~+ +~+ +++ +++ +~+ +~+ +++ +~+ DCC TX RX +++ = out D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 GND RST D1 D0 +~+ = out with pwm ############################################################## +-+ = out or used by MAX,PCA,NEO,TLC5947 (it is possible to change pin) USB ##############################################TOP#view#### use following numbers in SW: 3-12 & A0-A5 ############################################################## max out = 16 D13 3.3 X A0 A1 A2 A3 A4 A5 A6 A7 5V RST GND VIN CLI +-+ +-+ +-+ +-+ +-+ +-+ CLK DAT LLED NEO SDA SCK Pinout on Arduino Mega horizontal connectors L5947 CLI +~+ +~+ +~+ +~+ +~+ +~+ +~+ +~+ +~+ +~+ DCC TX RX +++ +++ +++ +++ +++ +++ +++ +++ D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 D14 D15 D16 D17 D18 D19 D20 D21 ####################################################################################################### USB############################################TOP#view################################################ ####################################################################################################### ####################################################################################################### x x x x x x x x A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15 +-+ +-+ +-+ +-+ +-+ +-+ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ CLK DAT LLED NEO SDA SCK vertical connector: GND ##### GND +++ D22 ##### D23 +++ +++ = out +++ D24 ##### D25 +++ +~+ = out with pwm +++ D26 ##### D27 +++ +-+ = out or used by MAX,PCA,NEO,TLC5947 (it is possible to change pin) +++ D28 ##### D29 +++ use following numbers in SW: 3-12,14-53 & A0-A15 +++ D30 ##### D31 +++ max out = 66 +++ D32 ##### D33 +++ +++ D34 ##### D35 +++ +++ D36 ##### D37 +++ +++ D38 ##### D39 +++ +++ D40 ##### D41 +++ +++ D42 ##### D43 +++ +~+ D44 ##### D45 +~+ +~+ D46 ##### D47 +++ +++ D48 ##### D49 +++ +++ D50 ##### D51 +++ +++ D52 ##### D53 +++ GND ##### GND note: onboard pwm Uno, Nano, Mini: 3, 5, 6, 9, 10, 11 timer 0 (pins 5, 6) 980Hz timer 1 (pins 9, 10) 490Hz 9 et 10 non dispo si arduino servos timer 2 (pins 3, 11) 490Hz 3 et 11 non dispo si utilisation de tone Mega 2 - 13, 44 - 46 timer 0 (pins 4, 13 ) 980Hz 13 non dispo car LED-CLI timer 1 (pins 11, 12 ) 490Hz timer 2 (pins 9, 10 ) 490Hz 9 et 10 non dispo si arduino servos timer 3 (pins 2, 3, 5) 490Hz 2 non dispo car DCC-IN, 2,3,5 non dispo si utilisation de tone timer 4 (pins 6, 7, 8) 490Hz */ #include //============================================================================================== // USER PART (Partie utilisateur, vous pouvez faire des modifications) //============================================================================================== // nombre d'elements supportes (verifier qu'il reste suffisament de memoire sur UNO/nano) #define MAX7219or21_NB 0 // nombre de MAX7219/21 supportes (out A0, A1 & A2 ne sont alors plus disponibles) // 4max (4*64=256 leds) #define PCA9685_NB 0 // nombre de PCA9685 supportes (si different de 0: out A3 & A4 ne sont alors plus disponibles) // 6max (6*16=96 sorties) mais verifier qu'il reste suffisament de memoire sur UNO/nano #define TLC5947_NB 0 // nombre de TLC5947 supportes(si different de 0: out A0, A1, 12 ne sont alors plus disponibles) // 8max (8*24=192 leds pwm) #define NEO_NB 0 // nombre de neopixels supportes (out A5 ne sont alors plus disponibles) // 60max #define ARDUINO_SRV_NB 0 // nombre de servos directement connectes sur l'Arduino (pas sur un PCA9685) // 12max byte arduino_srv_pins[] = {}; // liste des pattes de l'arduino sur lesquelles sont connectes les servos // ex: { 4, 5}; leur nombre doit etre egal a ARDUINO_SRV_NB #define TEMPO_NB 80 // nombre de tempo (mettre un multiple de 8) #define MUX_AIG_NB 0 // nombre d'aiguillages multiplexes // functions disponibles pour l'utilisateur void user_out(byte num, byte val); // num = numero de la sortie 3-12,A0-A6(UNO/nano) +14-53,A7-A15(MEGA), val 0=off-1=on void user_neo(byte num, byte r, byte v, byte b); // num = numero du neopixel 0-59, r=rouge=0-255, v=vert=0-255, b=bleu=0-255 void user_blink(void); // fait cligoter la led de la carte a 4Hz pendant 2s // sur MAX7219/21 void user_max7219_led(byte num, byte val); // num = numero de la led 0-255, val 0=off-1=on void user_max7219_led_cli(byte num, byte val); // val 0=pas de cli, 1 = cli void user_max7219_led_pha(byte num, byte val); // val 0=phase 1=phase inverse // sur PCA9685 (num = numero de la sortie 0-95 (pca0:0-15, pca1:16-31 ...) void user_pca9685_pwm_0_100(byte num, byte val); // pwm 0-100% void user_pca9685_servo_500_2500(byte num, unsigned int val); // 500us-2500us (souvent sur 9G: 500=-90° 1000=-45° 1500=0°=neutre 2000=+45° 2500=+90°) void user_pca9685_servo_speed(byte num, unsigned int speed); // 0-10000ms (temps pour faire varier l'impulsion de 1ms = 90° sur servo 9G 10000=10s/90°) byte user_is_pca9685_servo_in_position(byte num); // indique si le servo a termine son deplacement lent 0=non 1=oui // sur TLC5947 void user_tlc5947_pwmled_0_100(byte num, byte val); // 0-100% // sur la carte Arduino (num = numero de la sortie) void user_arduino_pwm_0_100(byte num, byte val); // val=0-100% seulement dispo sur certaines pins, pas de fading void user_arduino_servo_500_2500(byte num, unsigned int val); // val=500-2500us void user_arduino_servo_speed(byte num, unsigned int speed); // vitesse=0-10000ms (temps en ms pour faire varier l'impulsion de 1ms, soit 90° sur servo 9G) byte user_is_arduino_servo_in_position(byte num); // indique si le servo a termine son deplacement lent 0=non 1=oui // aiguillages multiplexes void user_cmd_mux_aig(byte num, byte pos); // demande de mettre l'aiguillage num en position pos (0=direct, 1=devie) void user_define_mux_aig_pulse_ms(byte num, unsigned int pulse); // regle la duree de l'impulsion de commande en ms 125-60000ms (l'impulsion sera un multiple de 125ms) // autres void user_tempo_start(byte num_tempo, unsigned int duration_ms); // num_tempo= //EEPROM.write(unsigned long addr_0_1024, byte val_0_255); //commentee car definit dans une bibliotheque //EEPROM.read(unsigned long addr_0_1024); // return: 0_255 // Ce code est fourni avec un exemple permettant de se comporter comme 2 decodeurs de 4 paires chacun // (2 sorties par paire. Les adresse 1 a 510 des 2 decodeurs basiques sont les constantes DECODER_ADR_1 & 2 ) // - mettre a 1 (5V) une sortie de la paire, met automatiquement a 0 (0V=masse) l'autre sortie de la paire // - les sorties ne sont pas impulsionnelles mais fixes // - la led de la carte Arduino cligote a 4Hz pendant 2s lors de la reception d'un paquet pour le decodeur // Bien entendu, ce n'est qu'un exemple et vous devraez surement le modifier pour l'adapter a vos besoins. // Constantes utilisateur #define DECODER_ADR_1 10 // adresse du premier decodeur de 4 paires #define DECODER_ADR_2 11 // adresse du second decodeur de 4 paires // appelee au demarrage void user_init(void) { } // appelee lorsque le decodeur recoit un ordre pour un decodeur basique void user_notify_bas_acc_dec(unsigned int adr_1_510, byte out_0_7, byte val_0_1, byte duplicate_0_1) { if(duplicate_0_1) return; if(adr_1_510 == DECODER_ADR_1) { user_blink(); if(out_0_7 == 0) { user_out(12, val_0_1); user_out(11, 0); } if(out_0_7 == 1) { user_out(11, val_0_1); user_out(12, 0); } if(out_0_7 == 2) { user_out(10, val_0_1); user_out( 9, 0); } if(out_0_7 == 3) { user_out( 9, val_0_1); user_out(10, 0); } if(out_0_7 == 4) { user_out( 8, val_0_1); user_out( 7, 0); } if(out_0_7 == 5) { user_out( 7, val_0_1); user_out( 8, 0); } if(out_0_7 == 6) { user_out( 6, val_0_1); user_out( 5, 0); } if(out_0_7 == 7) { user_out( 5, val_0_1); user_out( 6, 0); } } if(adr_1_510==DECODER_ADR_2) { user_blink(); if(out_0_7 == 0) { user_out( 4, val_0_1); user_out( 3, 0); } if(out_0_7 == 1) { user_out( 3, val_0_1); user_out( 4, 0); } if(out_0_7 == 2) { user_out(A0, val_0_1); user_out(A1, 0); } if(out_0_7 == 3) { user_out(A1, val_0_1); user_out(A0, 0); } if(out_0_7 == 4) { user_out(A2, val_0_1); user_out(A3, 0); } if(out_0_7 == 5) { user_out(A3, val_0_1); user_out(A2, 0); } if(out_0_7 == 6) { user_out(A4, val_0_1); user_out(A5, 0); } if(out_0_7 == 7) { user_out(A5, val_0_1); user_out(A4, 0); } } } // appelee lorsque le decodeur recoit un ordre pour un decodeur etendu void user_notify_ext_acc_dec(unsigned int adr_1_2044, byte val_0_31, byte duplicate_0_1) { } // appelee lorsque la tempo num_tempo expire void user_notify_tempo_end(byte num_tempo) { } // appelee toute les 125ms (pour faire des taches recurentes) void user_125ms() { } // demande a l'utilisateur de commencer la commande de l'aiguillage num en position pos void user_notify_start_mux_aig(byte num, byte pos) { } // demande a l'utilisateur d'arreter de commander l'aiguillage num void user_notify_stop_mux_aig(byte num, byte pos) { } //============================================================================================== // END OF USER PART (Fin de la partie utilisateur) // // (Ne pas faire de modifications apres de point (a moins de savoir ce que vous faite) //============================================================================================== //============================================================================================== // IO (if needed, you can redefine these pins) //============================================================================================== #define PIN_LED 13 #define PIN_DCC 2 // Les pattes suivantes sont utilisees en fonction des modules actives #define CLK_PIN A0 #define DAT_PIN A1 #define LLED_PIN A2 #define NEO_PIN A3 #define SDA_PIN A4 #define SCK_PIN A5 #define L5947_PIN 12 //============================================================================================== // Leds (256) //============================================================================================== #define IO_LED_NB 256 // pour l'instant si on active, on supporte 4 modules soit 256 leds #if MAX7219or21_NB >= 1 byte io_led[IO_LED_NB / 8] = { 0, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0 ,0, 0, 0, 0 }; // possibilite d'initialiser les valeurs byte io_cli[IO_LED_NB / 8] = { 0, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0 ,0, 0, 0, 0 }; // ex led clignotante byte io_pha[IO_LED_NB / 8] = { 0, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0 ,0, 0, 0, 0 }; byte led_cpt = 0; void led_shift8(unsigned char dat) { unsigned char i; pinMode(DAT_PIN, OUTPUT); for(i = 0; i < 8; i++) { if(dat & 0x80) digitalWrite(DAT_PIN, HIGH); else digitalWrite(DAT_PIN, LOW); delayMicroseconds(4); digitalWrite(CLK_PIN, HIGH); delayMicroseconds(4); //periode=8us = 125KHz (ok sur longue distances) digitalWrite(CLK_PIN, LOW); dat = dat<<1; } } void led_reg(unsigned char reg0, unsigned char dat0, unsigned char reg1, unsigned char dat1, unsigned char reg2, unsigned char dat2, unsigned char reg3, unsigned char dat3) { digitalWrite(CLK_PIN, LOW); digitalWrite(LLED_PIN, HIGH); delayMicroseconds(10); digitalWrite(LLED_PIN, LOW); delayMicroseconds(10); led_shift8(reg3); led_shift8(dat3); led_shift8(reg2); led_shift8(dat2); led_shift8(reg1); led_shift8(dat1); led_shift8(reg0); led_shift8(dat0); digitalWrite(LLED_PIN, HIGH); delayMicroseconds(10); } byte compute_led_on_cli_phase(byte on, byte cli, byte phase) { byte led; if(led_cpt >= 4) led = 0xff; else led = 0; //clignotement 1Hz led ^= phase; //changement de phase led |= ~cli; //allumer si pas clignotant led &= on; //eteindre si pas on return led; } // needs about 2x 2.5ms to update all LEDs void led_maj(byte index) { byte i, j, dat0, dat1, dat2, dat3; if(index == 0) for(i = 0; i < 4; i++) { dat0 = compute_led_on_cli_phase(io_led[ i], io_cli[ i], io_pha[ i]); dat1 = compute_led_on_cli_phase(io_led[ 8 + i], io_cli[ 8 + i], io_pha[ 8 + i]); dat2 = compute_led_on_cli_phase(io_led[16 + i], io_cli[16 + i], io_pha[16 + i]); dat3 = compute_led_on_cli_phase(io_led[24 + i], io_cli[24 + i], io_pha[24 + i]); led_reg(1 + i, dat0, 1 + i, dat1, 1 + i, dat2, 1 + i, dat3); } if(index == 1) for(i = 4; i < 8; i++) { dat0 = compute_led_on_cli_phase(io_led[ i], io_cli[ i], io_pha[ i]); dat1 = compute_led_on_cli_phase(io_led[ 8 + i], io_cli[ 8 + i], io_pha[ 8 + i]); dat2 = compute_led_on_cli_phase(io_led[16 + i], io_cli[16 + i], io_pha[16 + i]); dat3 = compute_led_on_cli_phase(io_led[24 + i], io_cli[24 + i], io_pha[24 + i]); led_reg(1 + i, dat0, 1 + i, dat1, 1 + i, dat2, 1 + i, dat3); } } void led_init(void) { // R0 : bypass // R1-8 : data led_reg(9 , 0, 9, 0, 9, 0, 9, 0); // R9 : decode mode off (1ere fois pas sure que ca marche) led_reg(9 , 0, 9, 0, 9, 0, 9, 0); // R9 : decode mode off led_reg(0xa,0x4,0xa,0x4,0xa,0x4,0xa,0x4); // R10 : intensite 4/15 (0xf=max) led_reg(0xb, 7,0xb, 7,0xb, 7,0xb, 7); // R11 : 8 digit led_reg(0xc, 1,0xc, 1,0xc, 1,0xc, 1); // R12 :shutdown mode off // R13-14: NA led_reg(0xf, 0,0xf, 0,0xf, 0,0xf, 0); // R15 :test mode off (1=on) led_maj(0); led_maj(1); } #endif void user_max7219_led(byte num, byte val) //on { #if MAX7219or21_NB >= 1 byte msk; if(num > IO_LED_NB) return; msk = 1 << (num & 7); if(val) io_led[num / 8] |= msk; else io_led[num / 8] &= (0xff ^ msk); #endif } void user_max7219_led_cli(byte num, byte val) { #if MAX7219or21_NB >= 1 byte msk; if(num > IO_LED_NB) return; msk = 1 << (num & 7); if(val) io_cli[num / 8] |= msk; else io_cli[num / 8] &= (0xff ^ msk); #endif } void user_max7219_led_pha(byte num, byte val) { #if MAX7219or21_NB >= 1 byte msk; if(num > IO_LED_NB) return; msk = 1 << (num & 7); if(val) io_pha[num / 8] |= msk; else io_pha[num / 8] &= (0xff ^ msk); #endif } //============================================================================================== // Leds PWM par TLC5947(24 par TLC5947) //============================================================================================== void fast_loop(void); // user 0-100% --> storage 0-255 --> TLC5947 0-4095 // 4096 steps precision reduced to 256 to save space #if TLC5947_NB >= 1 byte tlc5947_to_update = 0; byte tlc5947_val0_255[24 * TLC5947_NB]; void update_tlc5947(void) { digitalWrite(CLK_PIN, LOW); digitalWrite(L5947_PIN, LOW); delayMicroseconds(10); for(byte k = 0; k < TLC5947_NB; k++) { for(byte j = 0; j < 24; j++) { byte data = tlc5947_val0_255[24 * k + j]; // 8MSB for(byte i = 0; i < 8; i++) { if(data & 0x80) digitalWrite(DAT_PIN, HIGH); else digitalWrite(CLK_PIN, LOW); delayMicroseconds(4); digitalWrite(CLK_PIN, HIGH); data = data << 1; delayMicroseconds(4); digitalWrite(CLK_PIN, LOW); } digitalWrite(DAT_PIN, LOW); // 4LSB to 0 for(byte i = 0; i < 4; i++) { delayMicroseconds(4); digitalWrite(CLK_PIN, HIGH); delayMicroseconds(4); digitalWrite(CLK_PIN, LOW); } } fast_loop(); } digitalWrite(L5947_PIN, HIGH); delayMicroseconds(10); digitalWrite(L5947_PIN, LOW); delayMicroseconds(10); tlc5947_to_update = 1; } void init_tlc5947(void) { for(byte i = 0; i < (24 * TLC5947_NB); i++) tlc5947_val0_255[i] = 0; tlc5947_to_update = 1; update_tlc5947(); } #endif void user_tlc5947_pwmled_0_100(byte num, byte val) // 0-100% { #if TLC5947_NB >= 1 if(num > (24 * TLC5947_NB)) return; tlc5947_val0_255[num] = map(val, 0, 100, 0, 255); tlc5947_to_update = 1; #endif } //============================================================================================== // NEOPIXELS (60) //============================================================================================== /* neopixel official ns H0 200-500 L0 650-950 H1 550-850 L1 450-750 RESET 50000 min test H0 200-500 H1 550-oo L 450-5000 RESET 6000 min G(8bits7-->0)R(8bits)B(8bits) */ #if NEO_NB >= 1 #define NEO_R 0 #define NEO_V 1 #define NEO_B 2 volatile unsigned char neo_rvb[NEO_NB][3]; volatile byte neo_to_be_updated = 0; #define NOP __asm__ __volatile__ ("nop\n\t") //62.5ns /* Addr Name Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 ... 0x03 PINB PINB7 PINB6 PINB5 PINB4 PINB3 PINB2 PINB1 PINB0 0x04 DDRB DDB7 DDB6 DDB5 DDB4 DDB3 DDB2 DDB1 DDB0 0x05 PORTB PORTB7 PORTB6 PORTB5 PORTB4 PORTB3 PORTB2 PORTB1 PORTB0 0x06 PINC x PINC6 PINC5 PINC4 PINC3 PINC2 PINC1 PINC0 0x07 DDRC x DDC6 DDC5 DDC4 DDC3 DDC2 DDC1 DDC0 0x08 PORTC x PORTC6 PORTC5 PORTC4 PORTC3 PORTC2 PORTC1 PORTC0 0x09 PIND PIND7 PIND6 PIND5 PIND4 PIND3 PIND2 PIND1 PIND0 0x0A DDRD DDD7 DDD6 DDD5 DDD4 DDD3 DDD2 DDD1 DDD0 0x0B PORTD PORTD7 PORTD6 PORTD5 PORTD4 PORTD3 PORTD2 PORTD1 PORTD0 */ void neo_reset(void) //6 us mini -> 10us { unsigned char i; __asm__ __volatile__ ("cbi 8, 3\n\t"); // 8,3 = PORTC bit3 = AN3 ou 5,5=D13 for(i = 0; i < 100; i++) // par bcl: 3cycle+nb nop 1nop -> 4*62.5ns = 250ns 01us=40bcl { //__asm__ __volatile__("nop\n\t"); //62.5ns NOP; } } // mesures // 1court: 0.375us // 1long : 0.750us // bas : 0.750-937us 1625us entre 2 bytes 3125us entre 2 24bits void neo_tx_byte(unsigned char val) //UU: check the code of this function in assembly (mainly after opimization !!!!) { unsigned char i; for(i = 0; i < 8; i++) { if(val & 0x80) // TX_H_1 550-oo ns --> 600ns 10nop+1port=10*62.5=625ns //uu:try to reduce { asm volatile( "sbi 8, 3\n\t" //mise a 1 AN3 PC3 "nop\n\t" //9x NOP "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "cbi 8, 3\n\t"); //mise a 0 AN3 PC3 } else // TX_H_0 200-500ns --> 400ns 4nop+port=5*62.5=310ns { asm volatile( "sbi 8, 3\n\t" //mise a 1 digital 13 PB5 "nop\n\t" //4x NOP "nop\n\t" "nop\n\t" "nop\n\t" "cbi 8, 3\n\t"); //mise a 0 digital 13 PB5 } // TX_L 450->5000ns --> 600ns sans les nop:500ns, 4nop: +248ns -> 1us //uu: try tu reduce asm volatile( "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t"); val = val << 1; } } // dure 60 * 24* 1.5us = 2.1ms max pour 60 neopixels // desactive les ITs void neo_display(void) { unsigned char i; //wait an end of packet //loco_pkt = 0; //while(loco_pkt == 0) { process_packet(); } noInterrupts(); neo_reset(); for(i = 0; i < NEO_NB; i++) { neo_tx_byte(neo_rvb[i][NEO_V]); neo_tx_byte(neo_rvb[i][NEO_R]); neo_tx_byte(neo_rvb[i][NEO_B]); } neo_reset(); interrupts(); Serial.print("$"); } void neo_set_rvb_pixel(unsigned char pixel, unsigned char r, unsigned char v, unsigned char b) { neo_rvb[pixel][NEO_R] = r; neo_rvb[pixel][NEO_V] = v; neo_rvb[pixel][NEO_B] = b; } void neo_init(void) { byte i; for(i = 0; i < NEO_NB; i++) { neo_set_rvb_pixel(i, 0, 0, 0); //on demarre a 0 pour ne pas trop charger une faible alimentation } neo_display(); neo_display(); } void neo_maj(void) { neo_to_be_updated = 1; //neo_display(); } #endif // NEO_NB >= 1 void user_neo(byte num, byte r, byte v, byte b) { #if NEO_NB >= 1 if(num <= NEO_NB) neo_set_rvb_pixel(num, r, v, b); #endif } //============================================================================================== // I2C (I2C fait a la main pour pouvoir utiliser une basse frequence et n'importe quelle patte) //============================================================================================== //on emule un collecteur ouvert en mettant les sorties a 0 et en jouant sur la direction. #define SDA_1 pinMode(SDA_PIN, INPUT) #define SDA_0 pinMode(SDA_PIN, OUTPUT) #define SCK_1 pinMode(SCK_PIN, INPUT) #define SCK_0 pinMode(SCK_PIN, OUTPUT) void i2c_init(void) { pinMode(SCK_PIN, INPUT); digitalWrite(SCK_PIN, LOW); pinMode(SDA_PIN, INPUT); digitalWrite(SDA_PIN, LOW); } void i2c_start(void) { SDA_1; SCK_1; delayMicroseconds(5); SDA_0; delayMicroseconds(5); SCK_0; delayMicroseconds(5); } void i2c_stop(void) { SDA_0; SCK_0; delayMicroseconds(5); SCK_1; delayMicroseconds(5); SDA_1; delayMicroseconds(5); } void i2c_wr_8(byte data) { byte i; for(i = 0; i < 8; i++) { if(data & 0x80) { SDA_1; } else { SDA_0; } delayMicroseconds(4); SCK_1; delayMicroseconds(4); SCK_0; delayMicroseconds(1); data = data << 1; } SDA_1; delayMicroseconds(1); } byte i2c_rd_ack(void) { byte ack; delayMicroseconds(4); //before 0 SCK_1; delayMicroseconds(4); if(digitalRead(SDA_PIN) == HIGH) ack = 0; /*nack*/ else ack=1; /*ack*/ SCK_0; delayMicroseconds(1); return ack; } byte i2c_rd_8(void) { byte i, data; data=0; for(i=0;i<8;i++) { delayMicroseconds(4); //before 0 SCK_1; delayMicroseconds(4); if(digitalRead(SDA_PIN) == HIGH) data |= 1; SCK_0; delayMicroseconds(1); data = data << 1; } return data; } byte i2c_wr_ack(byte ack) //1=ack, 0=nack { if(ack) { SDA_0; } else { SDA_1; } delayMicroseconds(4); SCK_1; delayMicroseconds(4); SCK_0; delayMicroseconds(1); } //============================================================================================== // PCA9685 (6x16=96) //============================================================================================== //#define PCA9685_NB est maintenant definit dans la partie utilisateur #define IO_PCA9685_OUT_NB (16 * PCA9685_NB) #if PCA9685_NB >= 1 #define PCA9685_MODE1_REG 0x00 #define PCA9685_MODE2_REG 0x01 #define PCA9685_LED0_REG 0x06 #define PCA9685_PRESCALE_REG 0xfe unsigned int pca9685_on[ IO_PCA9685_OUT_NB]; unsigned int pca9685_off[IO_PCA9685_OUT_NB]; byte srv_speed[IO_PCA9685_OUT_NB]; unsigned int srv_millis_init[IO_PCA9685_OUT_NB]; byte srv_start[IO_PCA9685_OUT_NB]; byte srv_target[IO_PCA9685_OUT_NB]; byte srv_pos[IO_PCA9685_OUT_NB]; //UU: verifier si on peut l'enlever byte srv_status[IO_PCA9685_OUT_NB]; // bit7=first bit2=srv_init bit1=srv bit0=update byte set_pca9685_reg(byte i2c_adr, byte adr, byte dat) { i2c_start(); i2c_wr_8(0x80 + 2 * i2c_adr); //A6=1,A5-A0=pin,R/W# if(i2c_rd_ack() == 0) { i2c_stop(); return 0; } i2c_wr_8(adr); if(i2c_rd_ack() == 0) { i2c_stop(); return 0; } i2c_wr_8(dat); if(i2c_rd_ack() == 0) { i2c_stop(); return 0; } i2c_stop(); return 1; } byte set_pca9685_on_off(byte i2c_adr, byte out, unsigned int on, unsigned int off) { byte on_h, on_l, off_h, off_l; on_h = byte(on >> 8); on_l = byte(on & 255); //existe aussi lowByte highByte off_h = byte(off >> 8); off_l = byte(off & 255); i2c_start(); // A6=1,A5-A0=pin,R/W# i2c_wr_8(0x80+2*i2c_adr); i2c_rd_ack(); i2c_wr_8(PCA9685_LED0_REG+4*out); i2c_rd_ack(); i2c_wr_8(on_l); i2c_rd_ack(); i2c_wr_8(on_h); i2c_rd_ack(); i2c_wr_8(off_l); i2c_rd_ack(); i2c_wr_8(off_h); i2c_rd_ack(); i2c_stop(); } byte set_pca9685_only_off_out(byte i2c_adr, byte out, unsigned int off) { byte on_h, on_l, off_h, off_l; off_h = byte(off >> 8); off_l = byte(off & 255); i2c_start(); // A6=1,A5-A0=pin,R/W# i2c_wr_8(0x80 + 2 * i2c_adr); i2c_rd_ack(); i2c_wr_8(PCA9685_LED0_REG + 4 * out + 2); i2c_rd_ack(); i2c_wr_8(off_l); i2c_rd_ack(); i2c_wr_8(off_h); i2c_rd_ack(); i2c_stop(); } // to set directly pwm (just used at init) void set_pca9685_pwm0_100(byte num, byte pwm) { byte i2c_adr; byte out; unsigned int off; i2c_adr = num / 16; out = num & 15; off = map(pwm, 0, 100, 0, 4096); if(off == 0) set_pca9685_on_off(i2c_adr, out, 0, 4096); else if(off == 4096) set_pca9685_on_off(i2c_adr, out, 4096, 0); else set_pca9685_on_off(i2c_adr, out, 0, off ); } // to set directly srv (no more used) void set_paca9685_srv50_250(byte num, byte pulse) //50 -> 150 -> 250 { byte i2c_adr; byte out; unsigned int off; i2c_adr = num / 16; out = num & 15; off = map(pulse, 50, 250, 102, 512); set_pca9685_on_off(i2c_adr, out, 0, off); } byte i2c_pca9685_online[PCA9685_NB]; void pca9685_init(void) { byte i; byte i2c_adr; for(i2c_adr = 0; i2c_adr < PCA9685_NB; i2c_adr++) { // reset et config MODE1 i2c_pca9685_online[i2c_adr] = set_pca9685_reg(i2c_adr, PCA9685_MODE1_REG, 0xB0); //reset=1, extclk=0, autoincrement=1, sleep=1 (all leds are off after reset) Serial.print(F("PCA")); Serial.print(i2c_adr); if(i2c_pca9685_online[i2c_adr]) { Serial.println(F("=ONLINE")); } else Serial.println(F("=OFFLINE")); if(i2c_pca9685_online[i2c_adr] == 0) continue; delay(1); // Frequence de decoupage // 50Hz pour les servos: 25.000.000/(4096*50Hz) - 1 = 122-1 = 121. // 60Hz pour les servos: 25.000.000/(4096*60Hz) - 1 = 102-1 = 101. // mesure: // 121 -> 18.7ms -1.3ms err -6.5% // 130 -> 19.92ms -0.08ms err 0.4% // 131 -> 20.07ms +0.07ms err 0.4% set_pca9685_reg(i2c_adr, PCA9685_PRESCALE_REG, 130); // exit from sleep set_pca9685_reg(i2c_adr, PCA9685_MODE1_REG, 0x20); //reset=0, extclk=0, autoincrement=1, sleep=0 delay(1); //wait exit from low power mode // config MODE2 set_pca9685_reg(i2c_adr, PCA9685_MODE2_REG, 0x04); // invert=0 change=onSTOP out=totem off=led_0 UU: voir si pa smieux de clr sorties avant // mise des sorties a 0 for(i = 0; i < 16; i++) { byte n = 16 * i2c_adr + i; set_pca9685_pwm0_100(n, 0); pca9685_on[n] = 0; pca9685_off[n] = 4096; srv_speed[n] = 0; srv_status[n] = 0x80; } } } void update_pwm(byte num) { byte i2c_adr = num / 16; byte out = num & 15; if(srv_status[num] & 4) // init { unsigned int pulse = map(srv_target[num], 0, 100, 0, 4096); if(pulse == 0) { pca9685_on[num] = 0; pca9685_off[num] = 4096; } else if(pulse == 4096) { pca9685_on[num] = 4096; pca9685_off[num] = 0; } else { pca9685_on[num] = 0; pca9685_off[num] = pulse; } set_pca9685_on_off(i2c_adr, out, pca9685_on[num], pca9685_off[num]); srv_status[num] = 0; return; } unsigned int pwm; unsigned int pulse; if(srv_speed[num] == 0) // slow deactivated { pwm = srv_target[num]; pulse = map(pwm, 0, 100, 0, 4096); } else // slow { unsigned int duration = (unsigned int) millis() - srv_millis_init[num]; //if(num==3) { Serial.print("duration="); Serial.print(duration); } unsigned int max_duration; if(srv_target[num] >= srv_start[num]) max_duration = srv_speed[num] * (srv_target[num] - srv_start[num]); //0-100*0-100=0-10s else max_duration = srv_speed[num] * (srv_start[num] - srv_target[num]); if(duration > max_duration) duration = max_duration; //if(num==3) { Serial.print("/"); Serial.print(max_duration); } unsigned int pwm100 = map(duration, 0, max_duration, 100*srv_start[num], 100*srv_target[num]); //*100 astuce pour avoir plus de pas pwm = pwm100 / 100; pulse = map(pwm100, 0, 10000, 0, 4096); //if(num==3) { Serial.print("-->pulse="); Serial.println(pulse); } } if(pwm == 0 || pwm == 100 || pca9685_on[num] == 4096 || pca9685_off[num] == 4096) { if(pulse == 0) { pca9685_on[num] = 0; pca9685_off[num] = 4096; } else if(pulse == 4096) { pca9685_on[num] = 4096; pca9685_off[num] = 0; } else { pca9685_on[num] = 0; pca9685_off[num] = pulse;} set_pca9685_on_off(i2c_adr, out, pca9685_on[num], pca9685_off[num]); } else { unsigned int current_pulse = (pca9685_off[num] - pca9685_on[num]) & 4095; if(current_pulse != pulse) { // ce dispositif permet d'annuller les glitches // si on veut une plus grande pulse on augmente OFF // si on veut une plus petite pulse on augmente ON if(pulse > current_pulse) pca9685_off[num] = (pca9685_on[num] + pulse) & 4095; else pca9685_on[num] = (pca9685_off[num] - pulse) & 4095; set_pca9685_on_off(i2c_adr, out, pca9685_on[num], pca9685_off[num]); } } if(pwm == srv_target[num]) srv_status[num] = 0; } void update_servo(byte num) { byte i2c_adr = num / 16; byte out = num & 15; if(srv_status[num] & 4) // force (so no slow) { pca9685_on[num] = 0; pca9685_off[num] = map(srv_target[num], 50, 250, 102, 512); //4096=20ms > 102=0.5ms 512=2.5ms set_pca9685_on_off(i2c_adr, out, pca9685_on[num], pca9685_off[num]); srv_status[num] = 2; // init=0, servo=1, update=0 return; } byte pulse_50_250; if(srv_speed[num] == 0) // slow deactivated { pulse_50_250 = srv_target[num]; } else // slow { unsigned int duration = (unsigned int) millis() - srv_millis_init[num]; //if(num==3) { Serial.print("duration="); Serial.print(duration); } unsigned int max_duration; // ex: speed=100 (10s) 0->90=1ms=100 --> 10s if(srv_target[num] >= srv_start[num]) max_duration = srv_speed[num] * (srv_target[num] - srv_start[num]); else max_duration = srv_speed[num] * (srv_start[num] - srv_target[num]); //if(num==3) { Serial.print("/"); Serial.print(max_duration); } if(duration > max_duration) duration = max_duration; pulse_50_250 = map(duration, 0, max_duration, srv_start[num], srv_target[num]); //if(num==3) { Serial.print("-->pulse="); Serial.println(pulse_50_250); } } //unsigned int current_pulse = (pca9685_off[num] - pca9685_on[num]) & 4095; if(pulse_50_250 == srv_pos[num]) return; //deja dans cette position srv_pos[num] = pulse_50_250; // ce dispositif permet d'annuller les glitches // si on veut une plus grande pulse on augmente OFF // si on veut une plus petite pulse on augmente ON unsigned int pulse_9685 = map(pulse_50_250, 50, 250, 102, 512); if(pulse_50_250 >= srv_start[num]) pca9685_off[num] = (pca9685_on[num] + pulse_9685) & 4095; else pca9685_on[num] = (pca9685_off[num] - pulse_9685) & 4095; set_pca9685_on_off(i2c_adr, out, pca9685_on[num], pca9685_off[num]); if(pulse_50_250 == srv_target[num]) srv_status[num] = 2; // init=0, servo=1, update=0 } // mise a jour d'une sortie pca9685 (avec variation lente des servos) void pca9685_update_one(void) { static byte num = IO_PCA9685_OUT_NB; num ++; if(num >= IO_PCA9685_OUT_NB) num = 0; if(i2c_pca9685_online[num / 16] == 0) // PCA9685 module is not present { num += 16; return; } if((srv_status[num] & 1) == 0) return; // update only if((srv_status[num] & 2) == 0) update_pwm(num); else update_servo(num); } #endif // PCA9685_NB // speed = time (in ms) to make the pulse changing by 1ms (90°) // 0 = deactivated (max) // 100 = 100ms, saved 1 // 1000 = 1s, saved 10 // 10000 = 10s, saved 100 void user_pca9685_servo_speed(byte num, unsigned int speed) { #if PCA9685_NB >= 1 if(num >= IO_PCA9685_OUT_NB) return; if(speed > 10000) speed = 10000; srv_speed[num] = speed / 100; //1=100ms #endif } void user_pca9685_pwm_0_100(byte num, byte pwm) { #if PCA9685_NB >= 1 unsigned int off; if(num >= IO_PCA9685_OUT_NB) return; if(srv_status[num] & 0x82) // power up or was servo { srv_start[num] = pwm; srv_target[num] = pwm; srv_status[num] = 4 + 1; // init + update return; } srv_start[num] = srv_target[num]; // on laisse, mais on pourrait prendre le on/off srv_target[num] = pwm; srv_millis_init[num] = millis(); srv_status[num] = 1; // update #endif } void user_pca9685_servo_500_2500(byte num, unsigned int val) { #if PCA9685_NB >= 1 if(num >= IO_PCA9685_OUT_NB) return; if(val < 500 || val > 2500) return; byte pulse_50_250 = val / 10; if((srv_status[num] & 2) == 0) // etat en pwm; passe en servo { srv_target[num] = pulse_50_250; srv_pos[num] = pulse_50_250; srv_status[num] = 4 + 2 + 1; // force + servo + update return; } if(pulse_50_250 == srv_pos[num]) return; if(srv_speed[num] == 0) { srv_target[num] = pulse_50_250; srv_start[num] = srv_pos[num]; srv_status[num] = 2 + 1; // servo + update (pas de force pour ne pas faire de glitch) return; } srv_millis_init[num] = millis(); srv_start[num] = srv_pos[num]; srv_target[num] = pulse_50_250; srv_status[num] = 2 + 1; // servo + update #endif } byte user_is_servo_in_position(byte num) { #if PCA9685_NB >= 1 if(num >= IO_PCA9685_OUT_NB) return 0; return srv_status[num] & 1; #else return 0; #endif } //============================================================================================== // onboard servos & PWM //============================================================================================== #define ONBOARD_SRV_NB ARDUINO_SRV_NB #if ONBOARD_SRV_NB >= 1 #include Servo servos[ONBOARD_SRV_NB]; byte onsrv_start[ONBOARD_SRV_NB]; byte onsrv_target[ONBOARD_SRV_NB]; byte onsrv_pos[ONBOARD_SRV_NB]; unsigned int onsrv_millis_init[ONBOARD_SRV_NB]; byte onsrv_status[ONBOARD_SRV_NB]; // bit2=first bit1=force bit0=update byte onsrv_speed[ONBOARD_SRV_NB]; void onboard_servo_init(void) { for(int i = 0; i < ONBOARD_SRV_NB; i++) { servos[i].attach(arduino_srv_pins[i]); onsrv_status[i] = 4; // first } } void onboard_servo_update(byte ind) { //byte num = arduino_srv_pins[ind]; if(onsrv_status[ind] & 2) // force { byte val = onsrv_pos[ind]; byte pulse_180 = map(val, 50, 250, 0, 180); servos[ind].write(pulse_180); //if(num==4) { Serial.print("P180="); Serial.print(pulse_180); } onsrv_status[ind] = 0; return; } if((onsrv_status[ind] & 1) == 0) return; unsigned int duration = (unsigned int) millis() - onsrv_millis_init[ind]; //if(num==4) { Serial.print("duration="); Serial.print(duration); } unsigned int max_duration; // ex: speed=100 (10s) 0->+180=1ms=100 --> 10s if(onsrv_target[ind] >= onsrv_start[ind]) max_duration = onsrv_speed[ind] * (onsrv_target[ind] - onsrv_start[ind]); else max_duration = onsrv_speed[ind] * (onsrv_start[ind] - onsrv_target[ind]); //if(num==4) { Serial.print("/"); Serial.print(max_duration); } if(duration > max_duration) duration = max_duration; byte pulse_50_250 = map(duration, 0, max_duration, onsrv_start[ind], onsrv_target[ind]); //if(num==4) { Serial.print("-->pulse="); Serial.println(pulse_50_250); } if(pulse_50_250 == onsrv_pos[ind]) return; //bonne position byte pulse_180 = map(pulse_50_250, 50, 250, 0, 180); servos[ind].write(pulse_180); //if(num==4) { Serial.print("p180="); Serial.print(pulse_180); Serial.print("/"); Serial.print(onsrv_target[ind]); } onsrv_pos[ind] = pulse_50_250; if(pulse_50_250 == onsrv_target[ind]) onsrv_status[ind] = 0; } void onboard_servo_update_one(void) { static byte cpt = 96; cpt++; if(cpt > 96) cpt = 0; // pour faire comme pca9685 if(cpt < ONBOARD_SRV_NB) onboard_servo_update(cpt); } #endif void user_arduino_pwm_0_100(byte num, byte val) //UU: mettre la liste et les timers { analogWrite(num, map(val, 0, 100, 0, 255)); } // no fading on onboard PWM because else need to create variables for each pin // speed = time (in ms) to make the pulse changing by 1ms (90°) // 0 = deactivated (max) // 100 = 100ms, saved 1 // 1000 = 1s, saved 10 // 10000 = 10s, saved 100 void user_arduino_servo_speed(byte num, unsigned int speed) { #if ONBOARD_SRV_NB >= 1 int ind = -1; for(int i = 0; i < ONBOARD_SRV_NB; i++) if(arduino_srv_pins[i] == num) ind = i; if(ind == -1) return; if(speed > 10000) speed = 10000; onsrv_speed[ind] = speed / 100; //1=100ms #endif } void user_arduino_servo_500_2500(byte num, unsigned int val) { #if ONBOARD_SRV_NB >= 1 int ind = -1; for(int i = 0; i < ONBOARD_SRV_NB; i++) if(arduino_srv_pins[i] == num) ind = i; if(ind == -1) return; if(val<500 || val>2500) return; val = val / 10; if((onsrv_status[ind] & 4) || onsrv_speed[ind]==0) { onsrv_pos[ind] = val; onsrv_status[ind] = 2 + 1; // force update return; } if(val == onsrv_pos[ind]) return; onsrv_millis_init[ind] = millis(); onsrv_start[ind] = onsrv_pos[ind]; onsrv_target[ind] = val; onsrv_status[ind] = 1; // update #endif } byte user_is_arduino_servo_in_position(byte num) { #if ONBOARD_SRV_NB >= 1 int ind = -1; for(int i = 0; i < ONBOARD_SRV_NB; i++) if(arduino_srv_pins[i] == num) ind = i; if(ind == -1) return 0; return onsrv_status[ind] & 1; #endif #if ONBOARD_SRV_NB == 0 return 0; #endif } //============================================================================================== // Aiguillages multiplexes //============================================================================================== byte mux_aig_cmd[MUX_AIG_NB]; //0=direct, 1=devie byte mux_aig_pos[MUX_AIG_NB]; //0=direct, 1=devie, 2=non initialise byte mux_aig_pulse[MUX_AIG_NB]; //duree de la commande (1=125ms ...) byte mux_aig_num = 255; // aiguillage courant byte mux_aig_next_pos = 0; //position a atteindre byte mux_aig_dcpt = 0; // decompteur byte mux_aig_init_dcpt = 16; // attente 2s au demarrage avant de commander les aiguillages // demande de mettre l'aiguillage num en position pos (0=direct, 1=devie) void user_cmd_mux_aig(byte num, byte pos) { if(num >= MUX_AIG_NB) return; mux_aig_cmd[num] = pos; } // regle la duree de l'impulsion de commande en ms 125-60000ms (l'impulsion sera un multiple de 125ms) void user_define_mux_aig_pulse_ms(byte num, unsigned int pulse) { byte p = pulse / 125; if(p == 0) p = 1; if(num < MUX_AIG_NB) mux_aig_pulse[num] = p; if(num == 255) for(byte i = 0; i < MUX_AIG_NB; i++) mux_aig_pulse[i] = p; return; } void mux_aig_maj(void) { if(mux_aig_init_dcpt) { mux_aig_init_dcpt--; return; } if(mux_aig_dcpt) { mux_aig_dcpt--; if(mux_aig_dcpt) return; user_notify_stop_mux_aig(mux_aig_num, mux_aig_next_pos); mux_aig_pos[mux_aig_num] = mux_aig_next_pos; } for(byte i=0; i < MUX_AIG_NB; i++) { mux_aig_num++; if(mux_aig_num >= MUX_AIG_NB) mux_aig_num = 0; if(mux_aig_cmd[mux_aig_num] != mux_aig_pos[mux_aig_num]) { mux_aig_next_pos = mux_aig_cmd[mux_aig_num]; user_notify_start_mux_aig(mux_aig_num, mux_aig_next_pos); mux_aig_dcpt = mux_aig_pulse[mux_aig_num]; break; } } } void mux_aig_init(void) { for(byte i = 0; i < MUX_AIG_NB; i++) { mux_aig_cmd[i] = 0; // direct mux_aig_pos[i] = 2; // non initialise mux_aig_pulse[i] = 16; // 125 ms } } //============================================================================================== // DCC decoder engine part //============================================================================================== // remenber to use volatile when variable is shared between main and isr volatile unsigned int actMicros, lastMicros, bitMicros; volatile byte in_ext_irq = 0; volatile byte last_bit_val = 0; volatile byte preambule_state = 1; volatile byte preambule_len = 0; volatile byte bit_phase = 0; volatile byte bit_nb = 0; volatile byte byte_val = 0; volatile byte dat[4]; //including xor volatile byte dat_len = 0; volatile byte skip_sep = 0; volatile byte msg[4]; volatile byte msg_len = 0; volatile byte msg_ok; volatile byte err_preambule = 0; volatile byte err_same = 0; volatile byte err_nested_it = 0; volatile byte err_short = 0; volatile byte err_pkt_long = 0; volatile byte err_pkt_short = 0; byte err_checksum = 0; byte dbg_pkt3 = 0; byte dbg_pkt4 = 0; byte dbg_pkt_idle = 0; inline void process_bitval(byte bit_val) { if(preambule_state) { if(bit_val) { preambule_len++; return; } if(preambule_len < 18) { preambule_len = 0; err_preambule++; return; } //waiting at last 9 x "1" symbol preambule_len = 0; preambule_state = 0; bit_phase = 0; bit_nb = 0; dat_len = 0; skip_sep = 1; return; } bit_phase ^= 1; if((bit_phase & 1)==0) return; if(bit_val != last_bit_val) { preambule_state = 1; err_same++; return; } if(skip_sep) { skip_sep=0; return; } //skip the first 0 separator beetween preambule and first byte #if NEO_NB >= 1 // To update, neopixels, we need to stop interrupts for sometime (ie:2.1ms for 60 neopixels) // During this time, decoder is not able to decode the DCC signal // Since this accessory decoder do not take care about loco packets, as soon as we detect the begining of a loco packet // we call neo_display() if an update was required by neo_to_be_updated. // loco packet will last about 4ms, so we will be ready to sync on the following preambule // Note this ughly method causes some errors in statistics if(neo_to_be_updated) if(dat_len == 0) if(bit_nb == 0) if(bit_val == 0) { neo_display(); //Serial.print("£"); neo_to_be_updated = 0; preambule_state = 1; return; } #endif byte_val = (byte_val << 1) | bit_val; bit_nb++; if(bit_nb < 8) return; if(bit_nb == 8) { dat[dat_len] = byte_val; dat_len++; return; } // bit == 9 if(bit_val == 0) { bit_nb = 0; if(dat_len >= 4) { err_pkt_long++; preambule_state = 1; } return; } msg_ok = 1; msg_len = dat_len; msg[0] = dat[0]; msg[1] = dat[1]; msg[2] = dat[2]; msg[3] = dat[3]; preambule_state = 1; return; } void ExternalInterruptHandler(void) { byte bit_val = 0; if(in_ext_irq) { err_nested_it++; return; } //nested IT actMicros = micros(); bitMicros = actMicros - lastMicros; if (bitMicros < 35) { err_short++; return; } //glitch lastMicros = actMicros; in_ext_irq = 1; interrupts(); //to not block micros if(bitMicros < 82) bit_val = 1; else bit_val=0; process_bitval(bit_val); last_bit_val = bit_val; in_ext_irq = 0; return; } void print_err(void); // IDLE : 1111111111 0 11111111 0 00000000 0 EEEEEEEE 1 // // VIT 28 : 1111111111 0 0AAAAAAA 0 01DFSSSS 0 EEEEEEEE 1 // VIT 127 : 1111111111 0 0AAAAAAA 0 00111111 0 DSSSSSSS 0 EEEEEEEE 1 // // FCT 0-4 : 1111111111 0 0AAAAAAA 0 100-FL-F4-F3-F2-F1 0 EEEEEEEE 1 // FCT 5-8 : 1111111111 0 0AAAAAAA 0 1011-F8-F7-F6-F5 0 EEEEEEEE 1 // FCT 9-12 : 1111111111 0 0AAAAAAA 0 1010-F12-F11-F10-F9 0 EEEEEEEE 1 // FCT 13-20: 1111111111 0 0AAAAAAA 0 11011110 0 F20-F19-F18-F17-F16-F15-F14-F13 0 EEEEEEEE 1 // FCT 11-28: 1111111111 0 0AAAAAAA 0 11011111 0 F28-F27-F26-F25-F24-F23-F22-F21 0 EEEEEEEE 1 // // ACC 1-511: 1111111111 0 10AAAAAA 0 1AAA1DDD 0 EEEEEEEE 1 // decodeur d'accessoire attention, les 3xAAA sont inverses, ils forment aussi le MSB adr0 est reserve le 1 entre AetD peut etre mis a 0 pour mettre la sortie a 0, mais normalement allumer la 0 eteint la 1 et inversement. ou alors remise a 0 automatique apres une pulse // ACC1-2044: 1111111111 0 10AAAAAA 0 0AAA0AA1 0 000DDDDD 0 EEEEEEEE 1 // decodeur de sortie 32 valeurs pour chaque sorties (ex: signal multi aspects, servos, pwm ...) // une explication accessible sur le DCC est dispo sur: http://trainminiature.discutforum.com/t12784-dcc-comment-ca-marche-place-a-la-technique unsigned int last_adr_1_511 = 0; byte last_out_0_7; byte last_state_0_1; void notify_dcc_acc(unsigned int adr_1_511, byte out_0_7, byte state_0_1) { Serial.print(F(" BAS ACC pkt adr(1-511)=")); Serial.print(adr_1_511); Serial.print(F(" out(0-7)=")); Serial.print(out_0_7); Serial.print(F(" val(0-1)=")); Serial.print(state_0_1); byte duplicate = 0; if((adr_1_511 == last_adr_1_511) && (out_0_7 == last_out_0_7) && (state_0_1 == last_state_0_1)) duplicate = 1; last_adr_1_511 = adr_1_511; last_out_0_7 = out_0_7; last_state_0_1 = state_0_1; Serial.print(F(" dup(0-1)=")); Serial.println(duplicate); user_notify_bas_acc_dec(adr_1_511, out_0_7, state_0_1, duplicate); } unsigned int last_out_1_2044 = 0; byte last_val_0_31; void notify_dcc_ext(unsigned int out_1_2044, byte val_0_31) { Serial.print(F(" EXT ACC pkt out(1-2044)=")); Serial.print(out_1_2044); Serial.print(F(" val(0-31)=")); Serial.print(val_0_31); byte duplicate = 0; if((out_1_2044 == last_out_1_2044) && (val_0_31 == last_val_0_31)) duplicate = 1; last_out_1_2044 = out_1_2044; last_val_0_31 = val_0_31; Serial.print(F(" dup(0-1)=")); Serial.println(duplicate); user_notify_ext_acc_dec(out_1_2044, val_0_31, duplicate); } void process_packet(void) //to call at least each 5ms { unsigned int adr_1_511; byte out_0_7; byte state_0_1; unsigned int out_1_2044; byte val_0_31; if(msg_ok == 0) return; msg_ok = 0; Serial.print("#"); Serial.print(msg_len); //print_err(); if(msg_len < 3) { err_pkt_short++; return; } Serial.print(" "); Serial.print(msg[0]); Serial.print(" "); Serial.print(msg[1]); Serial.print(" "); Serial.print(msg[2]); if(msg_len == 3) { if(msg[0] ^ msg[1] ^ msg[2]) { err_checksum++; return; } dbg_pkt3++; // IDLE: 1111111111 0 11111111 0 00000000 0 EEEEEEEE 1 if(msg[0] == 0xff && msg[1] == 0x00) { dbg_pkt_idle++; } // ACC 1-511: 1111111111 0 10AAAAAA 0 1AAA1DDD 0 EEEEEEEE 1 else if((msg[0] & 0xC0) == 0x80) { adr_1_511 = (((unsigned int)((msg[1] & 0x70) ^ 0x70)) << 2) | msg[0] & 0x3f; out_0_7 = msg[1] & 7; state_0_1 = (msg[1] >> 3) & 1; if(adr_1_511) notify_dcc_acc(adr_1_511, out_0_7, state_0_1); } Serial.println(""); return; } Serial.print(" "); Serial.println(msg[3]); if(msg_len == 4) { // msg_len == 4 if(msg[0] ^ msg[1] ^ msg[2] ^ msg[3]) { err_checksum++; return; } dbg_pkt4++; // ACC1-2044: 1111111111 0 10AAAAAA 0 0AAA0AA1 0 000DDDDD 0 EEEEEEEE 1 // decodeur de sortie 32 valeurs pour chaque sorties (ex: signal multi aspects, servos, pwm ...) if((msg[0] & 0xC0) == 0x80 && (msg[1] & 0b10001001) == 1 && (msg[2] & 0xE0) == 0) { adr_1_511 = (((unsigned int)((msg[1] & 0x70) ^ 0x70)) << 2) | msg[0] & 0x3f; // OutputAddress = (((BoardAddress - 1) << 2 ) | TurnoutPairIndex) + 1 ; // 1-511 // 0-2040 +0..3 + 1 1-2044 out_1_2044 = (((adr_1_511 - 1) << 2) | ((msg[1] >> 1) & 3)) + 1 ; val_0_31 = msg[2] & 0x1F; notify_dcc_ext(out_1_2044, val_0_31); } Serial.println(""); return; } } void print_err(void) { Serial.print(F("PMerr_nested_it = ")); Serial.println(err_nested_it); Serial.print(F("PMerr_short = ")); Serial.println(err_short); Serial.print(F("PMerr_preambule = ")); Serial.println(err_preambule); Serial.print(F("PMerr_same = ")); Serial.println(err_same); Serial.print(F("PMerr_pkt_long = ")); Serial.println(err_pkt_long); Serial.print(F("PMerr_pkt_short = ")); Serial.println(err_pkt_short); Serial.print(F("PMerr_checksum = ")); Serial.println(err_checksum); Serial.print(F("PMdbg_pkt3 = ")); Serial.println(dbg_pkt3); Serial.print(F("PMdbg_pkt4 = ")); Serial.println(dbg_pkt4); Serial.print(F("PMdbg_pkt_idle = ")); Serial.println(dbg_pkt_idle); } //============================================================================================== // Serial commands //============================================================================================== /* Commandes: - A taper dans le moniteur serie de l'environement Arduino - Appuyer sur le buton send ou la touche entree pour envoyer la commande - les commandes peuvent etre en minuscule ou majuscule - OK est retourne si la commande est executee, ERR-cmd ou ERR-ard dans le cas contraire Les indications UNO valent aussi pour Mini et Nano H : pour l'aide B,, : simule la reception d'une commande pour un decodeur d'accessoire basique d'adresse adr_0_511 pour mettre la sortie 0_7 a la valeur 0_1 ex: B10,3,1 E, : simule la reception d'une commande pour un decodeur d'accessoire etendu de sortie out_1_2044 a mettrec dans l'etat val_0_31 ex: E20,16 R: affiche un rapport de statistiques du decodeur DCC (nombre de commandes, erreurs ...) U: fait clignoter la LED de la carte Arduino a 4Hz pendant 2s L, : met la LED led_0_255 des MAX7219/21 a la valeur val_0_7 val_0_7 = 1 (si on) + 2 (si cli) + 4 (si inversion de phase) O,<0,1> : met la sortie out_3_19 de l'arduino UNO a la valeur val_0_1 (0=0V, 1=5V) O,<0,1> : met la sortie out_3_69 de l'arduino MEGA a la valeur val_0_1 sur UNO: A0-A5=14-19 sur MEGA:A0-A15=54-69 S, : commande le servo servo_0_95 des PCA9685 avec des impulsions de 500_2500us S, : commande le servo connecte sur la patte servo_103_169-100 de l'arduino ex: S0,1500 servo sur le PCA9685 d'adresse 0 et sortie 0, impulsion de 1500us=1.5ms=neutre=0° ex: S10,2500 servo sur le PCA9685 d'adresse 0 et sortie 10, impulsion de 2500us=2.5ms=neutre=+90° ex: S95,500 servo sur le PCA9685 d'adresse 5 et sortie 15, impulsion de 500us=0.5ms=neutre=-90° ex: S103,1500 servo sur la patte 3 de l'Arduino ex: S114,1500 servo sur la patte A0 de l'Arduino UNO ou 14 de l'Arduino MEGA ex: S169,1500 servo sur la patte A15 de l'Arduino MEGA T, : duree de deplacement en ms pour tourner de 90° pour le servo servo_0_95 des PCA9685 T, : duree de deplacement en ms pour tourner de 90° pour le servo connecte sur la patte servo_103_169-100 de l'arduino 0 desactive le mouvement lent ex: T0,0 desactivation de mouvement lent pour le servo sur le PCA9685 d'adresse 0 et sortie 0 ex: T95,1000 regalge du mouvement lent a 1 sec / 90° pour le servo sur le PCA9685 d'adresse 5 et sortie 15 ex: T105,10000 regalge du mouvement lent a 10 sec / 90° pour le servo sur la patte 5 de l'Arduino P, : commande la sortie pca9685_pwm_0_95 des PCA9685 avec une pwm a pwm_0_100% P, : commande la sortie arduino_pwm_103_169-100 de l'Arduino avec une pwm a pwm_0_100% P, : commande la sortie tlc5947_pwm_200_295-200 des TLC5947 avec une pwm a pwm_0_100% ex: P0,25 sortie 0 du PCA9685 d'adresse 0 en mode PWM a 25% ex: P17,50 sortie 1 du PCA9685 d'adresse 1 en mode PWM a 50% ex: P103,60 sortie 3 de l'Arduino en mode PWM a 60% ex: P200,70 sortie 0 du TLC5947 numero 0 a 70% ex: P223,71 sortie 23 du TLC5947 numero 0 a 71% ex: P224,70 sortie 0 du TLC5947 numero 1 a 72% sur UNO seules les sorties suivantes fonctionnent en pwm: 3, 5, 6, 9, 10, 11 sur MEGA seules les sorties suivantes fonctionnent en pwm: 3 - 12, 44 - 46 si les servos sont utilises sur Arduino 9 et 10 ne fonctionnent pas en pwm T, : duree de variation en ms de la sortie pwm_0_95 des PCA9685 pour passer de 0 a 100% 0 desactive le fading (la variation lente) Cette commande est la meme que les servos N,,, : met la couleur du neopixel neo_0_59 a rouge=r_0_255=0-255 vert=v_0_255=0-255 bleu=b_0_255=0-255 ex: N0,255,0,0 rouge au max pour le neopixel 0 ex: N1,0,128,0 vert a 50% pour le neopixel 1 ex: N59,0,0,64 bleu a 25% pour le neopixel 59 ex: N2,32,32,0 jaune (rouge+vert) a 12% (32/255) pour le neopixel 2 ex: N3,255,255,255 blanc (rouge+vert+bleu) a 100% pour le neopixel 3 W, : ecrit la valeur val_0_255 dans l'EEPROM a l'adresse adr_0_1023 G : lit et affiche la valeur qui se trouve dans l'EEPROM a l'adresse adr_0_1023 ex: W12,100 ex: G28 sur MEGA l'adresse est de 0 a 4095 M,[,pulse] commande l'aiguillage multiplexe en position pos [change si besoin la duree de l'impulsion en ms] */ void maj_debug_serial(void) { static byte serial_order = ' '; static byte serial_step = 0; static unsigned int val; static unsigned int cmd1, cmd2, cmd3, cmd4; while(Serial.available()) { int rcmd = Serial.read(); if(rcmd < 0) return; unsigned char cmd = (unsigned char)rcmd; if(cmd >= 'a' && cmd <='z') cmd += 'A' - 'a'; if(cmd >= 'A' && cmd <='Z') { serial_order = cmd; serial_step = 0; val = 0; continue;} if(serial_order == ' ') break; if(cmd >='0' && cmd <='9') val = 10 * val + (cmd - '0'); if(cmd == ',' || cmd == '\n' || cmd == ';') { if(serial_step == 0) cmd1 = val; if(serial_step == 1) cmd2 = val; if(serial_step == 2) cmd3 = val; if(serial_step == 3) cmd4 = val; serial_step++; val = 0; } if(cmd == '\n' || cmd == ';') { char s[2]; s[0]=serial_order; s[1]='\0'; Serial.print(s); if(serial_order == 'H' || serial_order == 'U' || serial_order == 'R') serial_step = 0; if(serial_step >= 1) { Serial.print(cmd1); } if(serial_step >= 2) { Serial.print(","); Serial.print(cmd2); } if(serial_step >= 3) { Serial.print(","); Serial.print(cmd3); } if(serial_step >= 4) { Serial.print(","); Serial.print(cmd4); } Serial.println(""); byte ok=2; if(serial_order == 'H') { Serial.println(F("H B511,7,1 E2044,31 Sn,500-2500 Tn,10000 ")); Serial.println(F("Pn,100 Ln,7 On,1 Nn,255,255,255 U R")); Serial.println(F("W1023,255 G1023 Mn,1[,ms]")); ok = 1; } else if(serial_order == 'B' && serial_step == 3) { if(cmd1 >=1 && cmd1 <=511 && cmd2 <=7 && cmd3 <=1) { notify_dcc_acc(cmd1, cmd2, cmd3); ok = 1; } } else if(serial_order == 'E' && serial_step == 2) { if(cmd1 >=1 && cmd1 <=2044 && cmd2 <=31) { notify_dcc_ext(cmd1, cmd2); ok = 1; } } else if(serial_order == 'L' && serial_step == 2) { if(cmd1 <= 255 && cmd2 <= 7) { user_max7219_led(cmd1, cmd2&1); user_max7219_led_cli(cmd1, (cmd2>>1)&1); user_max7219_led_pha(cmd1, (cmd2>>2)&1); ok = 1; } } else if(serial_order == 'O' && serial_step == 2) { if(cmd2 <=1) //UNO:A0-A5=14-19 MEGA:A0-A15=54-69 { user_out(byte(cmd1), byte(cmd2)); ok = 1; } } else if(serial_order == 'S' && serial_step == 2) { if(cmd1 < IO_PCA9685_OUT_NB && cmd2 >=500 && cmd2 <=2500) { user_pca9685_servo_500_2500(cmd1, cmd2); ok = 1; } if(cmd1 >= 100 && cmd2 >=500 && cmd2 <=2500) { user_arduino_servo_500_2500(cmd1-100, cmd2); ok = 1; } } else if(serial_order == 'T' && serial_step == 2) { if(cmd1 < IO_PCA9685_OUT_NB && cmd2 <=10000) { user_pca9685_servo_speed(cmd1, cmd2); ok = 1; } if(cmd1 >= 100 && cmd2 <=10000) { user_arduino_servo_speed(cmd1-100, cmd2); ok = 1; } } else if(serial_order == 'P' && serial_step == 2) { if(cmd1 < IO_PCA9685_OUT_NB && cmd2 <=100) { user_pca9685_pwm_0_100(cmd1, cmd2); ok = 1; } if(cmd1 >= 100 && cmd1 < 200 && cmd2 <=100) { user_arduino_pwm_0_100(cmd1-100, cmd2); ok = 1; } if(cmd1 >= 200 && cmd1 < 400 && cmd2 <=100) { user_tlc5947_pwmled_0_100(cmd1-200, cmd2); ok = 1; } } else if(serial_order == 'N' && serial_step == 4) { if(cmd1 < NEO_NB && cmd2 <= 255 && cmd3 <= 255 && cmd4 <= 255) { user_neo(cmd1, cmd2, cmd3, cmd4); ok = 1; } } else if(serial_order == 'U') { user_blink(); ok = 1; } else if(serial_order == 'R') { print_err(); ok = 1; } else if(serial_order == 'W' && serial_step == 2) { if(cmd1 < EEPROM.length() && cmd2 <= 255) { EEPROM.write(cmd1, cmd2); ok = 1; } } else if(serial_order == 'G' && serial_step == 1) { if(cmd1 < EEPROM.length()) { Serial.print(F("dat=")); Serial.println(EEPROM.read(cmd1)); ok = 1; } } else if(serial_order == 'M' && serial_step <= 3) { if(cmd1 < MUX_AIG_NB && cmd2 <= 1) { if(serial_step == 3) user_define_mux_aig_pulse_ms(cmd1, cmd3); user_cmd_mux_aig(cmd1, cmd2); ok = 1; } } else ok = 0; if(ok==0) Serial.println(F("ERR-cmd")); else if(ok == 2) Serial.println(F("ERR-arg")); else Serial.println("OK"); serial_order = ' '; serial_step = 0; } } } //============================================================================================== // MAIN part //============================================================================================== byte wait_125ms = 0; // 125ms flag to update MAX & tempos byte rx = 0; // rx indicates packet matching void user_blink(void) { rx = 16; // * 125ms = 2sec } //#define TEMPO_NB 80 //mettre un multiple de 8 // definit dans la partie utilisateur maintenant unsigned int tempo_dcpt[TEMPO_NB]; byte tempo_flag[TEMPO_NB / 8]; void user_tempo_start(byte num_tempo, unsigned int duration_ms) { if(num_tempo >= TEMPO_NB) return; tempo_dcpt[num_tempo] = duration_ms / 125; // 125ms increment if(duration_ms) tempo_flag[num_tempo / 8] |= (1 << (num_tempo & 7)); else tempo_flag[num_tempo / 8] &= (0xff ^ (1 << (num_tempo & 7))); } void tempo_maj(void) { byte i, j, num; for(j = 0; j < (TEMPO_NB / 8); j++) if(tempo_flag[j]) for(i = 0; i < 8; i++) if(tempo_dcpt[i]) { num = 8*j + i; tempo_dcpt[num]--; if(tempo_dcpt[num] == 0) { tempo_flag[num / 8] &= (0xff ^ (1 << (num & 7))); user_notify_tempo_end(num); } } if(rx) rx--; } void user_out(byte num, byte val) { #if defined(__AVR_ATmega2560__) //in arduino lib Dmax is before A0, so there is no output after Amax if(num > A15) return; #else if(num > A5) return; #endif if(num <= 1) return; // Tx & Rx if(num == PIN_DCC) return; if(num == PIN_LED) return; #if MAX7219or21_NB >= 1 if(num == CLK_PIN || num == DAT_PIN || num == LLED_PIN) return; #endif #if TLC5947_NB >= 1 if(num == CLK_PIN || num == DAT_PIN || num == L5947_PIN) return; #endif #if PCA9685_NB >= 1 if(num == SDA_PIN || num == SCK_PIN) return; #endif #if NEO_NB >= 1 if(num == NEO_PIN) return; #endif if(val) digitalWrite(num, HIGH); else digitalWrite(num, LOW); } unsigned int time0; //normaly unsigned long, but unsigned int is suffiscient //byte t0_delay; //byte flag_delay = 0; void setup(void) { // Serial output for debugging Serial.begin(115200); Serial.println(F("D18 DCC Accessory Decoder v20200306a")); // configure pins // D13-2-1-0 not in the list because D13 is led, D2 DCC in, D1 TX, D0 RX // les sorties sont mises a 0 a l'initialisation, pour changer cela et les mettre a 1, rajouter digitalWrite(numero de la pin, HIGH); pinMode(12, OUTPUT); pinMode(11, OUTPUT); pinMode(10, OUTPUT); pinMode(9, OUTPUT); pinMode(8, OUTPUT); pinMode(7, OUTPUT); pinMode(6, OUTPUT); pinMode(5, OUTPUT); pinMode(4, OUTPUT); pinMode(3, OUTPUT); //on Nano there is also A6 & A7 but they cannot be used as output pinMode(A0, OUTPUT); pinMode(A1, OUTPUT); pinMode(A2, OUTPUT); pinMode(A3, OUTPUT); pinMode(A4, OUTPUT); pinMode(A5, OUTPUT); #if defined(__AVR_ATmega2560__) Serial.println("AT_MEGA detected"); pinMode(14, OUTPUT); pinMode(15, OUTPUT); pinMode(16, OUTPUT); pinMode(17, OUTPUT); pinMode(18, OUTPUT); pinMode(19, OUTPUT); pinMode(20, OUTPUT); pinMode(21, OUTPUT); pinMode(22, OUTPUT); pinMode(23, OUTPUT); pinMode(24, OUTPUT); pinMode(25, OUTPUT); pinMode(26, OUTPUT); pinMode(27, OUTPUT); pinMode(28, OUTPUT); pinMode(29, OUTPUT); pinMode(30, OUTPUT); pinMode(31, OUTPUT); pinMode(32, OUTPUT); pinMode(33, OUTPUT); pinMode(34, OUTPUT); pinMode(35, OUTPUT); pinMode(36, OUTPUT); pinMode(37, OUTPUT); pinMode(38, OUTPUT); pinMode(39, OUTPUT); pinMode(40, OUTPUT); pinMode(41, OUTPUT); pinMode(42, OUTPUT); pinMode(43, OUTPUT); pinMode(44, OUTPUT); pinMode(45, OUTPUT); pinMode(46, OUTPUT); pinMode(47, OUTPUT); pinMode(48, OUTPUT); pinMode(49, OUTPUT); pinMode(50, OUTPUT); pinMode(51, OUTPUT); pinMode(52, OUTPUT); pinMode(53, OUTPUT); pinMode(A6, OUTPUT); pinMode(A7, OUTPUT); pinMode(A8, OUTPUT); pinMode(A9, OUTPUT); pinMode(A10, OUTPUT); pinMode(A11, OUTPUT); pinMode(A12, OUTPUT); pinMode(A13, OUTPUT); pinMode(A14, OUTPUT); pinMode(A15, OUTPUT); #endif pinMode(PIN_LED, OUTPUT); #if MAX7219or21_NB >= 1 led_init(); #endif #if PCA9685_NB >= 1 i2c_init(); pca9685_init(); #endif #if NEO_NB >= 1 neo_init(); #endif #if ONBOARD_SRV_NB >= 1 onboard_servo_init(); #endif #if TLC5947_NB >= 1 void init_tlc5947(); #endif #if MUX_AIG_NB >= 1 mux_aig_init(); #endif user_init(); #if NEO_NB >= 1 neo_display(); #endif pinMode(PIN_DCC, INPUT); digitalWrite(PIN_DCC, HIGH); //enable pull-up //or: pinMode(PIN_DCC, INPUT_PULLUP); attachInterrupt( digitalPinToInterrupt(PIN_DCC), ExternalInterruptHandler, CHANGE); //on UNO: IT 0 = ExtInt Pin 2 or use digitalPinToInterrupt(2) time0 = millis(); } void fast_loop(void) { // gestion du nouveau paquet process_packet(); //A appeller au moins toutes les 5ms pour ne pas manquer de packets // mise a jour des sorties PWM et servos // dans fast_loop pour eviter les sacades sur les servos et en fading pwm #if PCA9685_NB >= 1 pca9685_update_one(); #endif #if ONBOARD_SRV_NB >= 1 onboard_servo_update_one(); #endif } void loop(void) { static byte cpt_125ms = 0; unsigned int time1 = millis(); //unsigned int is suffiscient fast_loop(); //if(((byte)time1 - t0_delay) > 25) // set flag each 25ms //{ // flag_delay = 1; // t0_delay = time1; //} if((time1 - time0) < 125) return; // on arrive ici toute les 125ms //Serial.println(time1 - time0); time0 = time1; cpt_125ms++; // gestion des tempos tempo_maj(); // tache de fond de l'utilisateur user_125ms(); // gestion des aiguillages multiplexes #if MUX_AIG_NB >= 1 mux_aig_maj(); #endif // clignotement de la LED a 1Hz (ou 4Hz pendant 2s lorsque l'on recoit un packet) if(rx == 0) { if(cpt_125ms & 4) digitalWrite(PIN_LED, HIGH); else digitalWrite(PIN_LED, LOW); } else { if(cpt_125ms & 1) digitalWrite(PIN_LED, HIGH); else digitalWrite(PIN_LED, LOW); } // mise a jour des LEDs #if MAX7219or21_NB >= 1 led_cpt++; if(led_cpt == 8) led_cpt=0; // 0 - 7 used to blink leds (off 0-3, on 4-7) led_maj(0); fast_loop(); // total led_maj lasts 5ms, so we cut it to call process_packet() between; led_maj(1); fast_loop(); #endif // mise a jour #if TLC5947_NB >= 1 update_tlc5947(); #endif // demande de mise a jour des neopixels #if NEO_NB >= 1 neo_maj(); #endif // debug maj_debug_serial(); //Serial.print(pca9685_on[3]);Serial.print("-");Serial.println(pca9685_off[3]); }