Notas:
- Responde a los desvíos números 1 a 16 de la central LENZ. Para asignarle otros hay que cambiar las definiciones en las líneas iniciales del código antes de volcarlo al Arduino.
Para Roco Multimaus donde dice ((DIR_01 - 1) / 4) + 129... hay que cambiar el 129 por un 128 (en las 16 líneas donde aparece).
La señal se lee en el pin2 de un Arduino Nano a través de un optoacoplador.
Los motores de los desvíos se controlan con los pines 11, 12 y 13 mediante un circuito integrado L293.
Solo se puede mover un desvío a la vez.
Código: Seleccionar todo
/*
Para MACI
Deco DCC accesorios para 16 motores lentos
Arduino Nano Pro como
- Decodificador para accesorios DCC central LENZ LH100
- L293D para doble puente H
- 16 canales si/no para relés
Code by German Trinidad and Norber, November 2020
Freely distributable for private, non commercial, use.
*/
#define DUR_OP 540000 // Duración de cada maniobra (540000 = 3s)
#define NUM_ACC 16 // Número de motores lentos a controlar
// Bloque "A"
#define DIR_01 1 // Direcciones de cada motor en la central
#define DIR_02 2
#define DIR_03 3
#define DIR_04 4
#define DIR_05 5
#define DIR_06 6
#define DIR_07 7
#define DIR_08 8
// Bloque "B"
#define DIR_09 9
#define DIR_10 10
#define DIR_11 11
#define DIR_12 12
#define DIR_13 13
#define DIR_14 14
#define DIR_15 15
#define DIR_16 16
struct Accesorios {
byte direc;
byte port;
byte pinV;
};
// #58 en ROCO: (58-1)/4=57/4=14.25->14+128=142
// #58 en LENZ: (58-1)/4=57/4=14.25->14+129=143
Accesorios miAcc[] = {
//Address , Port , Pin√
{((DIR_01 - 1) / 4) + 129, ((DIR_01 - 1) % 4), 10 },
{((DIR_02 - 1) / 4) + 129, ((DIR_02 - 1) % 4), 9 },
{((DIR_03 - 1) / 4) + 129, ((DIR_03 - 1) % 4), 8 },
{((DIR_04 - 1) / 4) + 129, ((DIR_04 - 1) % 4), 7 },
{((DIR_05 - 1) / 4) + 129, ((DIR_05 - 1) % 4), 6 },
{((DIR_06 - 1) / 4) + 129, ((DIR_06 - 1) % 4), 5 },
{((DIR_07 - 1) / 4) + 129, ((DIR_07 - 1) % 4), 4 },
{((DIR_08 - 1) / 4) + 129, ((DIR_08 - 1) % 4), 3 },
{((DIR_09 - 1) / 4) + 129, ((DIR_09 - 1) % 4), 1 },
{((DIR_10 - 1) / 4) + 129, ((DIR_10 - 1) % 4), 0 },
{((DIR_11 - 1) / 4) + 129, ((DIR_11 - 1) % 4), A5 },
{((DIR_12 - 1) / 4) + 129, ((DIR_12 - 1) % 4), A4 },
{((DIR_13 - 1) / 4) + 129, ((DIR_13 - 1) % 4), A3 },
{((DIR_14 - 1) / 4) + 129, ((DIR_14 - 1) % 4), A2 },
{((DIR_15 - 1) / 4) + 129, ((DIR_15 - 1) % 4), A1 },
{((DIR_16 - 1) / 4) + 129, ((DIR_16 - 1) % 4), A0 }
};
byte rele[] = {10, 9, 8, 7, 6, 5, 4, 3, 1, 0, A5, A4, A3, A2, A1, A0};
#include <EEPROM.h>
#define SI 0
#define NO 1
#define TICKS 180 // Prescaler 8 -> pulso de Timer cada 0.5 µs -> 200 ticks son 100 µs
volatile byte buf_dcc[6]; // Buffer del último comando recibido
volatile boolean comandoRecibido = false;
#define ENA 13
#define IN1 12
#define IN2 11
void setup() {
for (int i = 0; i < 20; i++) {
if ( i != 2) {
pinMode(i, OUTPUT);
digitalWrite(i, HIGH); // Los relés se activan por bajos
}
else pinMode(i, INPUT); // pin2 = DCC
}
digitalWrite(ENA, LOW);
pruebaReles();
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
sei();
}
void pruebaReles() {
for (byte i = 0; i < 16; i++) {
digitalWrite(rele[i], LOW);
delay(100);
digitalWrite(rele[i], HIGH);
delay(50);
}
}
void aDesviado(byte i) {
if (EEPROM.read(20 + i) == 1) return; // Ya estaba desviado
digitalWrite(miAcc[i].pinV, LOW);
delay(10000);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(ENA, HIGH);
delay(DUR_OP);
digitalWrite(ENA, LOW);
digitalWrite(IN1, LOW);
delay(10000);
digitalWrite(miAcc[i].pinV, HIGH);
delay(10000);
EEPROM.write(20 + i, 1);
}
void aRecto(byte i) {
if (EEPROM.read(20 + i) == 0) return; // Ya estaba recto
digitalWrite(miAcc[i].pinV, LOW);
delay(10000);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(ENA, HIGH);
delay(DUR_OP);
digitalWrite(ENA, LOW);
digitalWrite(IN2, LOW);
delay(10000);
digitalWrite(miAcc[i].pinV, HIGH);
delay(10000);
EEPROM.write(20 + i, 0);
}
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 (byte i = 0; i < NUM_ACC; i++) {
if ((addr == miAcc[i].direc) && (port1 == miAcc[i].port)) { // Es de los míos
if (port & 0x01) aDesviado(i); // √ selected
else aRecto(i); // -- selected
break; // No hace falta seguir buscando coincidencia
}
}
comandoRecibido = false;
}
}
void dcc_int() { // ISR(INT0_vect) External interrupt routine for signal on 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
}
}
}
}
}
La fuente de alimentación del Arduino y los relés es lo del centro. Reduce a 5 V cc la tensión que llega de un transformador de aproximadamente 12 V cc, que es la que se aplica a los motores de los desvíos. Regulando esta tensión se consigue que vayan más o menos lentos, a gusto de cada uno. La fuente se adapta automáticamente para sacar 5 V constantes.
Es un circuito sencillo y muy barato de hacer. Y los componentes son totalmente convencionales.
A ver cuándo lo podemos ver todo en acción!!!
