Bonjour.
Ci joint le soft avec un correctif pour décoder les trames inversées.
Fonctionne aussi avec un Attiny85 en version bêta.
Attention changement des Pins pour compatibilité avec l’Attiny
2 Entrée Dcc
3 Sortie vers IN1 (ou Dir pour LMD18200)
4 Sortie vers IN2 (ou Brake pour LMD18200)
Prévu pour LMD18200 mais rien testé, j’attends la réception de ce module.
Ligne n° 10: //#define LMD18200
Bien à vous.
// RailCom: Programme pour insérer un CutOut dans les trames DCC. Arduino Uno, Nano, Micro. Attiny85.(Betâ)
//
// V.1.0. 24-04-24 Testé avec Bridge Driver L6203 et L298N
// V.1.1. 27-04-24 Upgrade pour Attiny85 et polarité inverse.
//
// lebelge2@yahoo.fr
//--------------------------------- Sélectionné votre Hardware Bridge Driver------------------------------------
#define L298N // L6203
//#define LMD18200
//----------------------------------------------------------------------------------------------
volatile uint8_t dccrecState;
volatile uint8_t tempByte;
bool PolariteDcc = true;
bool Brake;
unsigned long currentTime = 0;
unsigned long previousTime = 0;
unsigned long Temps;
#define WAIT_PREAMBLE 0
#define WAIT_START_BIT 1
#define WAIT_DATA 2
#define WAIT_END_BIT 3
#define MaxDccSize 6 // DCC messages can have a length upto this value
class DccMessage {
public:
volatile uint8_t size;
volatile uint8_t data[MaxDccSize]; // The contents of the last dcc message received
} dccMessage;
struct {
uint8_t bitCount; // Count number of preamble bits / if we have a byte
volatile uint8_t tempMessage[MaxDccSize]; // Once we have a byte, we store it in the temp message
volatile uint8_t tempMessageSize; // Here we keep track of the size, including XOR
} dccrec; // The received DCC message is assembled here
struct {
uint8_t port;
uint8_t bit; // Bitmask for reading the input Port
volatile uint8_t *portRegister;
} dccIn;
//---------------------------------------------- SetUp -------------------------------------------------------
void setup() {
#ifdef LMD18200
Brake = true;
#endif
#ifdef __AVR_ATmega328P__ // --- Arduino AVR ---
#define PORTx PORTD // PORTD
Serial.begin(250000);
init_timer2(); // Timer 2
#elif ARDUINO_AVR_ATTINYX5 // --- ATTINY CORE ATTINY85 --- (Micronucleus DigiSpark)
#define PORTx PORTB // PORTB
init_timer1(); // Timer 1
#else
#error "Unsupported CPU, you need to add another configuration section for your CPU"
#endif
#define PinIn 2 // Entrée trames Dcc sans CutOut
#define PinOut1 3 // Sortie trames DCC avec CutOut (LMD18200: Dir (sans CutOut))
#define PinOut2 4 // Sortie trames DCC inversées avec CutOut (LMD18200: Brake (uniquement CutOut))
pinMode(PinIn, INPUT);
pinMode(PinOut1, OUTPUT);
pinMode(PinOut2, OUTPUT);
attachInterrupt(digitalPinToInterrupt(PinIn), Dcc_Interrupt, CHANGE );
dccIn.port = digitalPinToPort(PinIn);
dccIn.bit = digitalPinToBitMask(PinIn);
dccIn.portRegister = portInputRegister(dccIn.port);
}
//--------------------------------------------- Loop --------------------------------------------------------
void loop() {}
//------------------------------------------- Attiny 85 -----------------------------------------------------------
#ifdef ARDUINO_AVR_ATTINYX5
void init_timer1(void) {
noInterrupts(); // disable all interrupts
TCCR1 = 0; // B0 à B3: diviseur par 1, 2, 4, 8
TCNT1 = 102; // preload the timer
TIMSK = 4; // the timer is used in overflow interrupt mode
interrupts(); // enable all interrupts
}
//--------------------------------------------- AVR ---------------------------------------------------------
#elif __AVR_ATmega328P__
void init_timer2(void) {
noInterrupts(); // disable all interrupts
TCCR2A = 0; // should be zero for our purpose
TCCR2B = 0; // 0 => timer is stopped
TCNT2 = 102; // preload the timer
TIMSK2 |= 1; // the timer is used in overflow interrupt mode
interrupts(); // enable all interrupts
}
#endif
//----------------------------------------------------------------------------
void CutOut() {
if (PolariteDcc == false) { // Si DCC inversé,
delayMicroseconds(38); // retarder CutOut d'un demis-bit
PORTx = PORTx | B00001000;
PORTx = PORTx &= ~ B00010000;
delayMicroseconds(20);
}
delayMicroseconds(8);
PORTx = PORTx | B00010000;
PORTx = PORTx | B00001000;
delayMicroseconds(430);
PORTx = PORTx &= ~ B00001000;
PORTx = PORTx &= ~ B00010000;
}
//------------------------------------------ DCC Interrupt -------------------------------------------
void Dcc_Interrupt() {
#ifdef __AVR_ATmega328P__
if ((bitRead(PIND, PinIn)) == 1) { // bitRead en 3µs, digitalRead en 7µs
TCCR2B |= 2; // Start Timer 2
#elif ARDUINO_AVR_ATTINYX5
if ((bitRead(PINB, PinIn)) == 1) {
TCCR1 |= 4; // Start Timer 1
#endif
if (Brake == false)
PORTx = PORTx &= ~ B00010000; // Temps d'exécution de ces instructions:
PORTx = PORTx | B00001000; // 0,125µs en ASM, 0,170µs en C, 6µs en C++
}
else {
if (Brake == false)
PORTx = PORTx | B00010000;
PORTx = PORTx &= ~ B00001000;
}
if (dccrecState == WAIT_START_BIT) { // L'intervalle de temps après le préambule est évalué,
Temps = micros();
previousTime = Temps - currentTime;
currentTime = Temps; // si l'écart entre le dernier et le premier zéro est de 116 µs,
if (previousTime > 90) // alors le signal est reconnu dans la bonne phase.
PolariteDcc = true; // Polarité correcte
else
PolariteDcc = false; // Polarité inversée
}
}
//------------------------------------------- ISR Timers ----------------------------------------------------
#ifdef __AVR_ATmega328P__
ISR(TIMER2_OVF_vect) {
TCCR2B = 0; // 0 => timer is stopped
TCNT2 = 102; // preload the timer. Fr. 16Mhz
#elif ARDUINO_AVR_ATTINYX5
ISR(TIMER1_OVF_vect) {
TCCR1 = 0; // 0 => timer is stopped
TCNT1 = 112; // preload the timer. Fr. 16,5Mhz
#endif
uint8_t DccBitVal;
DccBitVal = !(*dccIn.portRegister & dccIn.bit);
dccrec.bitCount++;
switch (dccrecState) {
case WAIT_PREAMBLE:
if (DccBitVal) { // a "1" bit is received
if (dccrec.bitCount >= 10)
dccrecState = WAIT_START_BIT;
}
else
dccrec.bitCount = 0; // not a valid preamble.
break;
case WAIT_START_BIT:
if ( !DccBitVal ) { // a "0" bit is received
dccrecState = WAIT_DATA;
dccrec.tempMessageSize = 0;
uint8_t i;
for (i = 0; i < MaxDccSize; i++ )
dccrec.tempMessage[i] = 0;
dccrec.bitCount = 0;
}
break;
case WAIT_DATA:
if ( dccrec.bitCount == 8 ) { // byte is complete
if (dccrec.tempMessageSize == MaxDccSize ) { // Packet is too long - abort
dccrecState = WAIT_PREAMBLE;
dccrec.bitCount = 0;
}
else
dccrecState = WAIT_END_BIT; // Wait for next byte or end of packet
}
break;
case WAIT_END_BIT:
if ( DccBitVal ) { // End of packet?
CutOut();
dccrecState = WAIT_PREAMBLE;
}
else // Get next Byte
dccrecState = WAIT_DATA;
dccrec.bitCount = 0; // prepare for the next byte
}
}