
Lo primero es agradecer a Norber toda la información que me a dado para poder hacer este decodificador DCC de accesorios. Sin él me hubiera resultado imposible. Muchas gracias Norber.
Como dice el título, es un circuito con un Arduino Nano que nos permite mover desvíos con servos desde la central XpressNet o la tablet usando la z21.
El decodificador puede mover hasta 6 servos y hasta 11 señales de dos aspectos a la vez.
Para hacer este decodificador necesitaremos:
- 1 Arduino Nano
- 1 Optoacoplador 6N137 (ó 6N135)
- 1 Diodo 1N4004
- 1 LED verde alto brillo
- 1 Resistencia 2K2Ω
- 2 Resistencias 10kΩ
- Zócalos y conectores al gusto.
- 1 Placa de circuito impreso cuyo fotolito se saca del PDF adjunto
- 1 fuente de alimentación 5 V cc de PC.
1ª Parte: la fuente de alimentación de PC.
- Con la fuente de PC podremos disponer de varias tensiones muy útiles para la maqueta: 3.3 V, 5 V, 12 V y otras.
- Para este circuito necesitaremos 5 V que cogeremos de cualquier par de cables rojo y negro.
- Para que arranque la fuente tendremos que unir o ponerle un interruptor al único cable verde con uno negro, (en la web hay muchos tutoriales de como hacerlo).
2ª Parte: el circuito.
- Os adjunto el PDF para imprimir. Hay varios metodos para hacer el circuito. Yo he usado el metodo de la plancha (en la web de Paco Cañadas hay un tutorial de como se hace).
- Soldar los componentes. En el adjunto viene también la posición de la polaridad que tenéis que ponerlos.
- Las resistencias de las señales las ponéis a vuestro gusto, pero nunca serán inferiores a 1 kΩ para que el consumo
que soporte el micro del Arduino no lo dañe.
http://www.forotrenes.com/foro/download/file.php?mode=view&id=422832&sid=497b08a9c1e2e78ace8b4ee4c631edbe
http://www.forotrenes.com/foro/download/file.php?mode=view&id=422833&sid=497b08a9c1e2e78ace8b4ee4c631edbe
3ª parte: programar el Arduino Nano:
- Hay que instalar el software del Arduino (Arduino IDE, gratuito).
- Una vez abierto el programa, conectar el Arduino al PC, y sobre un sketch completamente en blanco copiar y pegar el siguiente código:
Código: Seleccionar todo
/* Arduino Nano Pro as
- DCC accessory decoder
- (x7) Servo controller
- (x10) Two aspects signal controller
Code by German Trinidad and Norber, October 2016
Freely distributable for private, non commercial, use.
Connections for DCC (adapted through 6N137 fast optocoupler):
DCC sig to ARD pin 2
*/
// *****************************************************************
// Ajustar y asignar direcciones desde aqui....
// *****************************************************************
#define ADR_MMAUS_01 30 // Multimaus address for signal 01 on Arduino pin D3
#define ADR_MMAUS_02 31 // Multimaus address for signal 02 on Arduino pin D4
#define ADR_MMAUS_03 32 // Multimaus address for signal 03 on Arduino pin D5
#define ADR_MMAUS_04 33 // Multimaus address for signal 04 on Arduino pin D6
#define ADR_MMAUS_05 34 // Multimaus address for signal 05 on Arduino pin D7
#define ADR_MMAUS_06 35 // Multimaus address for signal 06 on Arduino pin D8
#define ADR_MMAUS_07 36 // Multimaus address for signal 07 on Arduino pin D9
#define ADR_MMAUS_08 37 // Multimaus address for signal 08 on Arduino pin D10
#define ADR_MMAUS_09 38 // Multimaus address for signal 09 on Arduino pin D11
#define ADR_MMAUS_10 39 // Multimaus address for signal 10 on Arduino pin D12
#define ADR_MMAUS_11 42 // Multimaus address for servo 01 on Arduino pin D13
#define ADR_MMAUS_12 43 // Multimaus address for servo 02 on Arduino pin A0
#define ADR_MMAUS_13 44 // Multimaus address for servo 03 on Arduino pin A1
#define ADR_MMAUS_14 45 // Multimaus address for servo 04 on Arduino pin A2
#define ADR_MMAUS_15 46 // Multimaus address for servo 05 on Arduino pin A3
#define ADR_MMAUS_16 47 // Multimaus address for servo 06 on Arduino pin A4
#define ADR_MMAUS_17 48 // Multimaus address for servo 07 on Arduino pin A5
// *****************************************************************
// ...hasta aqui. El resto no hay que tocarlo.
// *****************************************************************
#define NUM_DCC_ADDR 17 // Number of DCC addresses to be processed
#define SI 0
#define NO 1
#define TICKS 180 // Prescaler 8 -> pulso de Timer cada 0.5 µs -> 200 ticks son 100 µs
#define SLOTuS 6660 // Ajuste de servos = 6660 No tocar
#define VEL 20 // Velocidad del movimiento de todos los servos
#define DESV 2000 // Posicion desviada: mínimo 1000 y siempre múltiplo entero de VEL
#define RECT 4000 // Posicion recta: maximo 4500 y siempre múltiplo entero de VEL
volatile byte buf_dcc[6]; // Buffer del último comando recibido
volatile boolean comandoRecibido = false;
volatile byte activeAcc = NUM_DCC_ADDR - 1;
volatile int counter = 0, sumar, dif;
struct Accesorios {
byte address;
byte port;
byte pin;
boolean Serv;
int pNow;
int pTgt;
};
Accesorios miAcc[] = {
//Address , Port , Pin, Serv , pNow, pTgt
{((ADR_MMAUS_01 - 1) / 4) + 128, ((ADR_MMAUS_01 - 1) % 4), 3 , false, DESV, DESV },
{((ADR_MMAUS_02 - 1) / 4) + 128, ((ADR_MMAUS_02 - 1) % 4), 4 , false, DESV, DESV },
{((ADR_MMAUS_03 - 1) / 4) + 128, ((ADR_MMAUS_03 - 1) % 4), 5 , false, DESV, DESV },
{((ADR_MMAUS_04 - 1) / 4) + 128, ((ADR_MMAUS_04 - 1) % 4), 6 , false, DESV, DESV },
{((ADR_MMAUS_05 - 1) / 4) + 128, ((ADR_MMAUS_05 - 1) % 4), 7 , false, DESV, DESV },
{((ADR_MMAUS_06 - 1) / 4) + 128, ((ADR_MMAUS_06 - 1) % 4), 8 , false, DESV, DESV },
{((ADR_MMAUS_07 - 1) / 4) + 128, ((ADR_MMAUS_07 - 1) % 4), 9 , false, RECT, RECT },
{((ADR_MMAUS_08 - 1) / 4) + 128, ((ADR_MMAUS_08 - 1) % 4), 10 , false, RECT, RECT },
{((ADR_MMAUS_09 - 1) / 4) + 128, ((ADR_MMAUS_09 - 1) % 4), 11 , false, DESV, DESV },
{((ADR_MMAUS_10 - 1) / 4) + 128, ((ADR_MMAUS_10 - 1) % 4), 12 , false, DESV, DESV },
{((ADR_MMAUS_11 - 1) / 4) + 128, ((ADR_MMAUS_11 - 1) % 4), 13 , true , DESV, DESV },
{((ADR_MMAUS_12 - 1) / 4) + 128, ((ADR_MMAUS_12 - 1) % 4), A0 , true , DESV, DESV },
{((ADR_MMAUS_13 - 1) / 4) + 128, ((ADR_MMAUS_13 - 1) % 4), A1 , true , DESV, DESV },
{((ADR_MMAUS_14 - 1) / 4) + 128, ((ADR_MMAUS_14 - 1) % 4), A2 , true , DESV, DESV },
{((ADR_MMAUS_15 - 1) / 4) + 128, ((ADR_MMAUS_15 - 1) % 4), A3 , true , DESV, DESV },
{((ADR_MMAUS_16 - 1) / 4) + 128, ((ADR_MMAUS_16 - 1) % 4), A4 , true , DESV, DESV },
{((ADR_MMAUS_17 - 1) / 4) + 128, ((ADR_MMAUS_17 - 1) % 4), A5 , true , DESV, DESV }
};
void setup() {
pinMode(2, INPUT); // pin2 = DCC
for (int i = 3; i < 19; i++) {
pinMode(i, OUTPUT);
digitalWrite(i, HIGH);
}
attachInterrupt(0, dcc_int, CHANGE); // pin2 = DCC externalInterrupt 0
cli();
// Prescaler 8 (0.5 µs/tick) en Timer0 - DCC
TCCR0A = 0;
TCCR0B = 0;
TCCR0B |= (1 << CS01);
// Fast PWM frecuencia fija, mode 14
TCCR1A = 0;
TCCR1B = 0;
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM13) | (1 << WGM12);
ICR1 = SLOTuS; // Frecuencia fija PWM
// Prescaler 8 (0.5 µs/tick) en Timer1 - Servos
TCCR1B |= (1 << CS11);
// Activar interrupciones Timer1 tipo Compare-Match-A & Overflow
TIMSK1 = 0;
TIMSK1 |= (1 << OCIE1A) | (1 << TOIE1);
sei();
}
void loop() {
static byte servo, estado;
if (comandoRecibido == true) {
byte port = buf_dcc[1];
byte addr = buf_dcc[0];
byte port1 = (port & 0x06) >> 1; // 0x06 = B0000 0110
for (int i = NUM_DCC_ADDR - 1; i > -1; i--) {
if ((addr == miAcc[i].address) && (port1 == miAcc[i].port)) { // De los míos
if (miAcc[i].Serv) { // It's a servo
if (port & 0x01) miAcc[i].pTgt = DESV; // pulsado √
else miAcc[i].pTgt = RECT; // pulsado --
}
else { // Es una señal
if (port & 0x01) { // pulsado √
digitalWrite(miAcc[i].pin, HIGH);
}
else { // pulsado --
digitalWrite(miAcc[i].pin, LOW);
}
}
comandoRecibido = false;
break; // Acabar for-loop aquí
}
}
comandoRecibido = false;
}
}
void dcc_int() { // ISR(INT0_vect) Rutina de interrupción externa en pin2
static int tmp_pulso;
static byte bit_dcc;
static byte preamb;
static byte aux_dcc, x_or, idx, num_bits;
if (PIND & (0x04)) TCNT0 = 0; // pin2 acaba de pasar a HIGH
else { // cuando acaba de pasar a LOW
tmp_pulso = TCNT0; // lee los microsegundos (un byte)
if (tmp_pulso > TICKS) bit_dcc = 0; // duración mayor de XX us => cero
else bit_dcc = 1; // y menor uno
if (preamb == SI) { // preámbulo
if (bit_dcc) { // otro '1'
if (num_bits) num_bits--;
}
else {
if (!num_bits) { // el '0' de inicio de datos
preamb = NO;
num_bits = 9;
x_or = 0;
idx = 0;
}
else num_bits = 10; // no se cumple la trama, vuelta a inicio
}
}
else { // recepción de los bytes
if (--num_bits) aux_dcc = aux_dcc * 2 + bit_dcc;
else { // el separador de bytes
if (!bit_dcc) { // cero, a por el siguiente byte
buf_dcc [idx++] = aux_dcc;
x_or ^= aux_dcc; // actualiza la x_or
num_bits = 9;
}
else { // uno, fin del paquete
preamb = SI;
num_bits = 10; // vuelta a empezar
if (x_or == aux_dcc) comandoRecibido = true; // paquete correcto
}
}
}
}
}
ISR(TIMER1_OVF_vect) { // Interrupción rebose Timer1 para servos cada 3300 µS
if (miAcc[activeAcc].Serv && sumar) {
digitalWrite(miAcc[activeAcc].pin, HIGH);
}
}
ISR(TIMER1_COMPA_vect) { // Interrupción Compare-Match-A del Timer1 para servos
digitalWrite(miAcc[activeAcc].pin, LOW);
if (--activeAcc < 10) activeAcc = NUM_DCC_ADDR - 1;
sumar = 0;
dif = miAcc[activeAcc].pNow - miAcc[activeAcc].pTgt;
if (dif < 0) sumar = VEL;
else if (dif > 0) sumar = -VEL;
if (sumar) {
miAcc[activeAcc].pNow += sumar;
OCR1A = miAcc[activeAcc].pNow;
}
}
Luego pulsar en "Verificar" para comprobar que el código se ha pegado bien, pulsar en "Subir" y ya estará programado el Arduino.
Sobre los ángulos y velocidad de los servos, tenéis que modificar las líneas siguientes del código:
#define VEL 20 // Velocidad del movimiento de todos los servos
#define DESV 2000 // Posición desviada: mínimo 1000 y siempre múltiplo entero de VEL
#define RECT 4000 // Posición recta: máximo 4500 y siempre múltiplo entero de VEL
Para ir a "múltiplos enteros de VEL", que como podéis ver vale "20", varía de veinte en veinte los valores de "DESV" y de "RECT" y vais probando hasta conseguir la posición que necesitáis. Con valores de VEL menores, por ejemplo 10, los servos van más despacio. Pero siempre hay que respetar la regla de que "DESV" y "RECT" sean múltiplos enteros de VEL.
4ª Parte: crear los desvíos y señales en nuestra central, conectar la fuente PC al decodificador (GND, 5V+ ) y la z21 (J K) y todo debería funcionar.
Espero haber sido lo más claro posible.