2227 lines
74 KiB
C++
2227 lines
74 KiB
C++
/* 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 <EEPROM.h>
|
||
|
||
|
||
//==============================================================================================
|
||
// 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.h>
|
||
|
||
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<adr_1_511>,<out_0_7>,<val_0_1> : 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<out_1_2044>,<val_0_31> : 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<led_0-255>,<val_0-7> : 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<out_3_19>,<0,1> : met la sortie out_3_19 de l'arduino UNO a la valeur val_0_1 (0=0V, 1=5V)
|
||
O<out_3_69>,<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<servo_0_95>,<pulse_500_2500> : commande le servo servo_0_95 des PCA9685 avec des impulsions de 500_2500us
|
||
S<servo_103_169>,<pulse_500_2500> : 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<servo_0_95>,<duree_0_10000> : duree de deplacement en ms pour tourner de 90° pour le servo servo_0_95 des PCA9685
|
||
T<servo_103_169>,<duree_0_10000> : 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<pca9685_pwm_0_95>,<pwm_0_100> : commande la sortie pca9685_pwm_0_95 des PCA9685 avec une pwm a pwm_0_100%
|
||
P<arduino_pwm_103_169>,<pwm_0_100> : commande la sortie arduino_pwm_103_169-100 de l'Arduino avec une pwm a pwm_0_100%
|
||
P<tlc5947_pwm_200_391>,<pwm_0_100> : 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<pwm_0_95>,<duree_0_10000> : 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<neo_0_59>,<r_0_255>,<v_0-255>,<b_0_255> : 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<adr_0_1023>,<val_0_255> : ecrit la valeur val_0_255 dans l'EEPROM a l'adresse adr_0_1023
|
||
G<adr_0_1023> : 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<num>,<pos>[,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]);
|
||
}
|