Este artículo muestra cómo convertir un simple Arduino Nano en un eficiente descodificador DCC para controlar hasta 8 señales de dos aspectos. El programa proporciona un bonito efecto de conmutación gradual, que apaga poco a poco la luz roja antes de encender progresivamente la verde, y al revés. |
This page shows how to convert a simple Arduino Nano into an efficient DCC decoder for eight two aspect signals. The code is also able to perform a nice dimming effect, so red light fades slowly off before green light goes progressively on, and viceversa. |
|
La lista de componentes es muy reducida, y todos ellos pueden conseguirse fácilmente: |
The part list is quite reduced, and all components are easily found everywhere: |
|
Lista de componentes
|
Part list
|
/* 2 x 8 dimming LEDs with Arduino Nano DCC decoder By German Trinidad and Norber, Spain, December 2015 Freely distributable for private, non commercial, use Attach led anodes (+) to #1 to #8 outputs Attach all led catodes (-) to GNDs Don't forget to add serial resistors! */ #define ADR_MMAUS_01 129 // Assign here your addresses #define ADR_MMAUS_02 130 #define ADR_MMAUS_03 131 #define ADR_MMAUS_04 132 #define ADR_MMAUS_05 133 #define ADR_MMAUS_06 134 #define ADR_MMAUS_07 135 #define ADR_MMAUS_08 136 #define ADR_MMAUS_09 137 byte comb[][4] = { //Address , Port , Pin-√ , Pin-- {((ADR_MMAUS_01 - 1) / 4) + 128, ((ADR_MMAUS_01 - 1) % 4), 4 , 5 }, {((ADR_MMAUS_02 - 1) / 4) + 128, ((ADR_MMAUS_02 - 1) % 4), 6 , 7 }, {((ADR_MMAUS_03 - 1) / 4) + 128, ((ADR_MMAUS_03 - 1) % 4), 8 , 9 }, {((ADR_MMAUS_04 - 1) / 4) + 128, ((ADR_MMAUS_04 - 1) % 4), 10 , 11 }, {((ADR_MMAUS_05 - 1) / 4) + 128, ((ADR_MMAUS_05 - 1) % 4), 12 , 13 }, {((ADR_MMAUS_06 - 1) / 4) + 128, ((ADR_MMAUS_06 - 1) % 4), 14 , 15 }, {((ADR_MMAUS_07 - 1) / 4) + 128, ((ADR_MMAUS_07 - 1) % 4), 16 , 17 }, {((ADR_MMAUS_08 - 1) / 4) + 128, ((ADR_MMAUS_08 - 1) % 4), 18 , 19 }, {((ADR_MMAUS_09 - 1) / 4) + 128, ((ADR_MMAUS_09 - 1) % 4), 20 , 21 }, }; #define DCC_INT 2 #define SI 0 #define NO 1 #define TICKS 215 // Prescaler 8 -> Timer pulse every 0.5 µs -> 200 ticks are 100 µs #define FREQ 120 // 15 ms aprox #define DELTA 3 // PWM #define TO_OFF 20 #define PAUSE 30 #define TO_ON 40 volatile byte buf_dcc[6]; volatile byte pinPWM_1, pinPWM_2; volatile boolean varyingLed = false; volatile int duty = FREQ - 1; volatile byte state = TO_OFF; void setup() { for (int i = 4; i < 20; i++) { pinMode(i, OUTPUT); if (i & 0x01) digitalWrite(i, LOW); // Green leds at odd pins start off else digitalWrite(i, HIGH); // Red leds at even pins start on } pinMode(DCC_INT, INPUT); Serial.begin(9600); Serial.print("Iniciando..."); delay(500); attachInterrupt(0, dcc_int, CHANGE); // pin2 = DCC externalInterrupt 0 cli(); TCCR0A = 0; TCCR0B = 0; TCCR0B |= (1 << CS01); // Set CS01 bit for 8 prescaler (0.5 µs) on Timer0 - DCC TCCR2A = 0; TCCR2B = 0; TCCR2B |= (1 << CS21); // Set CS21 bit for 8 prescaler (0.5 µs) on Timer2 - Dimming TIMSK2 = 0; TIMSK2 |= (1 << TOIE2); // Enable Timer2 Overflow interrupt sei(); } void dcc_int() { // ISR(INT0_vect) External interrupt routine for signal on pin2 static int tmp_pulse; // German is a genius! static byte bit_dcc; static byte preamb; static byte aux_dcc, x_or, idx, num_bits; if (PIND & (0x04)) TCNT0 = 0; else { tmp_pulse = TCNT0; if (tmp_pulse > TICKS) bit_dcc = 0; else bit_dcc = 1; if (preamb == SI) { if (bit_dcc) { if (num_bits) num_bits--; } else { if (!num_bits) { preamb = NO; num_bits = 9; x_or = 0; idx = 0; } else num_bits = 10; } } else { if (--num_bits) aux_dcc = aux_dcc * 2 + bit_dcc; else { if (!bit_dcc) { buf_dcc [idx++] = aux_dcc; x_or ^= aux_dcc; num_bits = 9; } else { preamb = SI; num_bits = 10; if (x_or == aux_dcc) { // Data received OK! Serial.print(buf_dcc[0]); // buf_dcc[0] is Address // buf_dcc[1] is Port and command if (!varyingLed) { // While commuting, ignore commands byte port = (buf_dcc[1] & 0x06) >> 1; // 0x06 = B0000 0110 for (int i = 0; i < 9; i++) { if ((buf_dcc[0] == comb[i][0]) && (port == comb[i][1])) { // It's ours if (buf_dcc[1] & 0x01) { pinPWM_1 = comb[i][2]; // Pin to dim off pinPWM_2 = comb[i][3]; // Pin to dim on } else { pinPWM_1 = comb[i][3]; pinPWM_2 = comb[i][2]; } if (digitalRead(pinPWM_1)) { // Needs to be dimmed off indeed varyingLed = true; duty = 255; state = TO_OFF; } return; } } } } } } } } } ISR(TIMER2_OVF_vect) { // Timer2 Overflow interrupt every 128 µs for dimmming leds if (varyingLed == true) { static int cont; cont++; switch (state) { case TO_OFF: if (cont == FREQ) { // High pulse starts when 'cont' reaches FREQ digitalWrite(pinPWM_1, HIGH); cont = 0; duty = duty - DELTA; } if (cont == duty) { digitalWrite(pinPWM_1, LOW); // And ends when 'cont' reaches 'duty' if (duty < DELTA) state = PAUSE; } break; case PAUSE: if (cont == 5 * FREQ) { // Small dramatic pausae state = TO_ON; duty = DELTA; cont = 0; } break; case TO_ON: if (cont == FREQ) { digitalWrite(pinPWM_2, HIGH); cont = 0; duty = duty + DELTA; if (duty > 255 - DELTA) varyingLed = false; // We're done } if (cont == duty) { digitalWrite(pinPWM_2, LOW); } break; } } } void loop() { }
Prepare la placa de circuito impreso, suelde los componentes y programe el Arduino con el código de arriba (copiar y pegar en el Arduino IDE bastará). |
Prepare the PCB, sold the components in place and program the Arduino whit the code below (just copy from this page and paste on Arduino IDE). |
|
IMPORTANTE: |
IMPORTANT: |
|
No olvide añadir resistencias en serie con cada LED. Recomendamos resistencias de 1/8 W entre 2.2 kΩ y 33 kΩ, dependiendo de la eficiencia de los LEDs elegidos. Si no las coloca puede destruir para siempre el microcontrolador. |
Don't forget to add resistors in series with each LED. We use 1/8 W resistors ranging from 2.2 kΩ to 33 kΩ, depending on the efficacity of the chosen LEDs. If you fail to add these resistors you may destroy the microcontroller for ever. |
|
Este descodificador responderá a las direcciones de accesorios que escriba al principio del programa. Por ejemplo, #define ADR_MMAUS_01 129 significa que la señal conectada en la posición #1 obedecerá a las órdenes dadas para el accesorio 129 y así. |
The accessory addresses this decoder will respond to are written at the beginning of the code. For instance, #define ADR_MMAUS_01 129 means that the signals attached to #1 will be controlled by address number 129 and so on. | |
Este programa ha sido diseñado para las centrales DCC de tipo Lenz / Roco Multimaus. Con otros sistemas las direcciones a programar pueden variar (pueden estar como adelantadas 4 posiciones, de forma que la #129 sería la #125 -o quizá es al revés... Nunca me acuerdo, lo siento!). Haga sus pruebas. Es facilísimo. |
This code has been prepared for Lenz / Roco Multimaus type DCC command stations. With other systems the addresses may vary (they may be shifted 4 places down, so #129 is #125 -or maybe the opposite... I never remember, sorry!) . Do your tests. It's easy! |