Digital

 

 

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.

La lista de componentes es muy reducida, y todos ellos pueden conseguirse fácilmente:

Lista de componentes

  • 1 x Arduino Nano 16 MHz 5V con USB
  • 1 x Optoacoplador 6N137
  • 1 x Led cualquiera
  • 3 x 10 kΩ resistencias de 1/8 W
  • Los conectores que se ven en las fotos (28 total)
  • 1 x placa de circuito impreso
...

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.

The part list is quite reduced, and all components are easily found everywhere:

 

Part list

  • 1 x Arduino Nano 16 MHz 5V with USB
  • 1 x Optocoupler 6N137
  • 1 x Led (any)
  • 3 x 10 kΩ resistor 1/8 W
  • Connectors shown in pictures (28 total)
  • 1 x printed circuit board

/* 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á).

IMPORTANTE:

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.

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í.

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.

...

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).

IMPORTANT:

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.

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.

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!

ACCESO SOCIOS