Bonjour
J'ai comme prévu passe un peu de temps sur une implémentation "qui va bien" avec un cœur AVRx.
J ai retenu les TINY x6 de la serie 2: ATTINY426, 826 1626, 3226.
Le code est également 100% compatible avec leur dérivés x7 ATTINY 427,827,1627,3227.
Bien que désignés par le terme ATTINY il s agit de "MEGATINY" dont le cœur et les mécanismes sont plus proches des AVR Dx et ATMEGA serie 0 ( x08,x09) que des TINY historiques.
Ici on met à contribution la hard natif du chip
( TIMER B, LOGIC, COMPARATEUR pour la protection, EVENT pour lier le tout.)
Au final la boucle principale est ... vide de code!
Tout se gère via des mécanismes internes et il n'y a plus qu'à "profiter" du résultat pour piloter le pont H L6203 par ses broches ENABLE, IN1 et IN2.
Le MAPPING retenu est en commentaire dans le code ci joint avec un brochage SOIC20.
Le code va permettre de traiter 3 cas de figure:
SIMPLE BOOSTER: amplifie les signaux reçus sur les entrées PIN_PA1 et PIN_PA2, assure une protection contre les CC et surcharges.(avec sortie LED temoin,...)
Rm: Ce signal peut déjà comporter un CUTOUT RAILCOM, il sera alors reproduit.
BOOSTER GERANT LES TIMINGS SANS CUTOUT RAILCOM: on laisse ici le hard redécouper les trames pilotant le pont en H par les inputs sur PIN_PA1 et PIN_PA2.
BOOSTER AVEC CUTOUT RAILCOM: on insère le CUTOUT dans un signal en entrée sans CUTOUT sur PIN_PA1 et PIN_PA2.
A tester sans modération!
CODE PART1:#include <Arduino.h>
/*
author: LTR Laurent ROEKENS
version v0.0.1
last update date: 01/05/2024
*/
/*
CUTOUT GENERATOR:
Select mode:
Simple BOOSTER (H bridge driven by input signals)
BOOSTER without RAILCOM (H bridge drive by this CPU timings)
BOOSTER WITH RAILCOM (H bridge drive by this CPU timings)
In all modes CURRENT LIMITOR is activated (by measure on COMPARATOR AC0)
*/
/*
HOW IS IT?:
FROM DCC SIGNAL INPUTS IN PINS PA_PA2 & PIN_PA3:
analyses DCC messages
if required introduce CUTOUT at end of DCC messages with security GAP to drive L6203 H BRIDGE PINS
use of EVENT software
use of LOGIC hardware
use of COMPARATOR for protection (overload/shorcut)
use TCB0 counter in capture frequency mode
L6203 IN1 and IN2 are driven by logic outputs
L6203_ENABLE_PIN is driven by logic output
// Connect the Attinyx26 as follows in SOIC20 SUPPORT:
//
// --------U--------
// VCC (+5V) -| 1 VCC GND 20|- GND
// LOGIC_CUTOUT_OUT_PIN -| 2 PA4 PA3 19|- LOGIC_CUTOUT_IN_PIN
// LOGIC_ENABLE_OUT_PIN -| 3 PA5 PA2 18|- DCC_PIN_L
// LOGIC_ENABLE_IN_PIN -| 4 PA6 PA1 17|- DCC_PIN_R
// L6203_ENABLE_PIN -| 5 PA7 PA0 16|- UDPI
// COMP_IN+ -| 6 PB5 PC3 15|- RAILCOM_MODE_SELECTOR_INPUT_PIN
// COMP_IN- -| 7 PB4 PC2 14|- LED_SHORCUT_OVERLOAD
// L6203_IN2 -| 8 PB3 PC1 13|- LED_RAILCOM_ENABLE
// L6203_IN1 -| 9 PB2 PC0 12|- LED_SIMPLE_AMPLIFICATOR_MODE
// EXT_START_STOP_PIN -|10 PB1 PB0 11|- SIMPLE_AMPLIFICATOR_MODE_INPUT_PIN
// -----------------
// CODE & PIN MAPPING FULY COMPATIBLE WITH ATTINYx27
*/
#include "Logic.h"
#include "Event.h"
#include "Comparator.h"
//RM: MILLIS_USES_TIMERB1
#define MaxDccSize 6 // DCC messages can have a length upto this value
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
#define dccrecState GPIOR0 // fast: saves 3 clock cycli, but requires a free GPIOR
#define dccHalfBit GPIOR1 // fast
#define RAILCOM GPIOR2 // fast
#define AMPLIFICATOR_MODE GPIOR3 // fast
//******************************************************************************************************
// Defines, definitions and instantiation of local types and variables
//******************************************************************************************************
// Values for half bits from RCN 210, section 5: http://normen.railcommunity.de/RCN-210.pdf
#define ONE_BIT_MIN F_CPU / 1000000 * 52
#define ONE_BIT_MAX F_CPU / 1000000 * 64
#define ZERO_BIT_MIN F_CPU / 1000000 * 90
#define ZERO_BIT_MAX F_CPU / 1000000 * 119
// Possible values for dccrecState
#define WAIT_PREAMBLE (1<<0) //WAIT_PREAMBLE = 0;
#define WAIT_START_BIT (1<<1) //WAIT_START_BIT = 1;
#define WAIT_DATA (1<<2) //WAIT_DATA = 2;
#define WAIT_END_BIT (1<<3) //WAIT_END_BIT = 3;
// Possible values for dccHalfBit
#define EXPECT_ZERO (1<<0) //EXPECT_ZERO = 0;
#define EXPECT_ONE (1<<1) //EXPECT_ONE = 1;
#define EXPECT_ANYTHING (1<<2) //EXPECT_ANYTHING = 2;
// Values for half bits from RCN 217,
#define ENABLE_STOP_TIME F_CPU / 1000000 * 26 // CUTOUT MIN START VALUE
#define ENABLE_START_TIME F_CPU / 1000000 * 488 // CUTPOUT MAX STP¨VALUE
// Value according OPEN DCC best pracitces:
#define CUTOUT_START_TIME F_CPU / 1000000 * 30 //4us security gap cutout at start
#define CUTOUT_STOP_TIME F_CPU / 1000000 * 484 //4us secutity gap cutout at stop
#define DCC_TIMER TCB0
#define DCC_PIN_R PIN_PA1 //DCC FROM R INPUT SIGNAL DRIVEN BY LUT0 IN1
#define DCC_PIN_L PIN_PA2 //DCC FROM L INPUT SIGNAL DRIVEN BY LUT0 IN2
//PIN_PA3:
#define CUTOUT_LOGIC_IN_PIN PIN_PA3 //EVENT CHANNEL 1 IN
//PIN_PA4:
#define LOGIC_CUTOUT_OUT_PIN PIN_PA4
#define LOGIC_CUTOUT_OUT_PIN_ON PORTA.OUTSET = PIN4_bm
#define LOGIC_CUTOUT_OUT_PIN_OFF PORTA.OUTCLR = PIN4_bm
//
//RM: HARDWARE SHOULD LINK PIN_PA3 WITH PIN_PA4
//
//PIN_PA5:
#define LOGIC_ENABLE_OUT_PIN PIN_PA5
#define LOGIC_ENABLE_OUT_PIN_ON PORTA.OUTSET = PIN5_bm
#define LOGIC_ENABLE_OUT_PIN_OFF PORTA.OUTCLR = PIN5_bm
//PIN_PA6:
#define LOGIC_ENABLE_IN_PIN PIN_PA6
//
//RM: HARDWARE SHOULD LINK PIN_PA5 WITH PIN_PA6
//
//PIN_PA7:
#define L6203_ENABLE_OUT_PIN PIN_PA7 //L6203_ENABLE_PIN
//PIN_PB2:
#define L6203_IN1 PIN_PB2 //DRIVEN BY EVOUTB
//PIN_PB3:
#define L6203_IN2 PIN_PB3 //DRIVEN BY LUT2 OUTPUT
#define RAILCOM_MODE_SELECTOR_INPUT_PIN PIN_PC3 //RAILCOM SWITCH ENABLE/DISABLE
#define LED_SHORCUT_OVERLOAD_PIN PIN_PC2 //LED INDICATOR FOR OVERLOAD OR SHORCIRCUIT
#define LED_RAILCOM_ENABLE_PIN PIN_PC1
#define LED_RAILCOM_ENABLE_PIN_ON PORTC.OUTSET = PIN1_bm
#define LED_RAILCOM_ENABLE_PIN_OFF PORTC.OUTCLR = PIN1_bm
#define LED_SIMPLE_AMPLIFICATOR_MODE_PIN PIN_PC0
#define LED_SIMPLE_AMPLIFICATOR_MODE_PIN_ON PORTC.OUTSET = PIN0_bm
#define LED_SIMPLE_AMPLIFICATOR_MODE_PIN_OFF PORTC.OUTCLR = PIN0_bm
#define EXT_STOP_START_INPUT_PIN PIN_PB1 //STOP_START_INPUT_PIN: DRIVE EXTERNAL STOP /START
#define SIMPLE_AMPLIFICATOR_MODE_INPUT_PIN PIN_PB0 //AMPLIFICATOR MODE: SIMPLE APLIFICATOR OR TIMINGS MANAGE BY THIS HARDWARE DRIVING L6203
void INIT_PINS(){
//RAILCOM_MODE_SELECTOR_PIN:
//pinMode(RAILCOM_MODE_SELECTOR_INPUT_PIN,INPUT); //INPUT slow
PORTC.DIRCLR = PIN3_bm; //INTPUT fast
//PORTC.PIN3CTRL |= PORT_PULLUPEN_bm; /// use the internal pullup resistor on PC3
PORTC.PIN3CTRL &= ~PORT_PULLUPEN_bm; // don't use the internal pullup resistor on PC3
//AMPLIFICATOR_MODE_MODE_SELECTOR_INPUT_PIN:
//pinMode(SIMPLE_AMPLIFICATOR_MODE_INPUT_PIN,INPUT); //INPUT slow
PORTB.DIRCLR = PIN0_bm; //INTPUT fast
//PORTC.PIN0CTRL |= PORT_PULLUPEN_bm; /// use the internal pullup resistor on PB0
PORTC.PIN0CTRL &= ~PORT_PULLUPEN_bm; // don't use the internal pullup resistor on PB0
//STOP_START_PIN:
//pinMode(EXT_STOP_START_INPUT_PIN,INPUT); //INPUT slow
PORTB.DIRCLR = PIN1_bm;
//PORTB.PIN1CTRL |= PORT_PULLUPEN_bm; /// use the internal pullup resistor on PC2
PORTB.PIN1CTRL &= ~PORT_PULLUPEN_bm; // don't use the internal pullup resistor on PC2
//LED_RAILCOM_ENABLE
//pinMode(LED_RAILCOM_ENABLE_PIN,OUTPUT); //OUTPUT slow
PORTC.DIRSET = PIN1_bm; //OUTPUT fast
PORTC.DIRCLR = PIN1_bm; //INIT STATE OFF
//LED_SIMPLE_AMPLIFICATOR_MODE
//pinMode(LED_SIMPLE_AMPLIFICATOR_MODE_PIN,OUTPUT); //OUTPUT slow
PORTC.DIRSET = PIN0_bm; //OUTPUT fast
PORTC.DIRCLR = PIN0_bm; //INIT STATE OFF
//LED_SHORCUT_OVERLOAD_PIN PIN_PC2
//pinMode(LED_SHORCUT_OVERLOAD_PIN,OUTPUT); //OUTPUT slow
PORTC.DIRSET = PIN2_bm; //OUTPUT fast
PORTC.DIRCLR = PIN2_bm; //INIT OFF
}
void INIT_ADDITIONNAL_PINS(){
//INIT PINS DONT MANAGED BY LUT OR YET SET:
//LOGIC_CUTOUT_OUT_PIN:
//pinMode(LOGIC_CUTOUT_OUT_PIN,OUTPUT); //slow
PORTA.DIRSET = PIN4_bm; //OUTPUT fast
PORTA.OUTCLR = PIN4_bm; //INIT STATE OFF
//LOGIC_CUTOUT_IN_PIN:
//pinMode(CUTOUT_LOGIC_IN_PIN,INPUT); //slow
PORTA.DIRCLR = PIN3_bm; //INPUT fast
//PORTA.PIN3CTRL |= PORT_PULLUPEN_bm; // use the internal pullup resistor on PA3
PORTA.PIN3CTRL &= ~PORT_PULLUPEN_bm; // don't use the internal pullup resistor on PA3
//LOGIC_ENABLE_OUT_PIN
//pinMode(LOGIC_ENABLE_OUT_PIN,OUTPUT); //slow
PORTA.DIRSET = PIN5_bm; //OUTPUT fast
PORTA.OUTCLR = PIN5_bm; //INIT STATE OFF
//LOGIC_ENABLE_IN_PIN:
//pinMode(LOGIC_ENABLE_IN_PIN,INPUT); //slow
PORTA.DIRCLR = PIN6_bm; //INPUT FAST
//PORTA.PIN6CTRL |= PORT_PULLUPEN_bm; /// use the internal pullup resistor on PA6
PORTA.PIN6CTRL &= ~PORT_PULLUPEN_bm; // don't use the internal pullup resistor on PA6
//L6203_IN1: PB2
//pinMode(L6203_IN1,OUTPUT); //slow
PORTB.DIRSET = PIN2_bm; //OUTPUT fast
PORTB.OUTCLR = PIN2_bm; //INIT STATE OFF
//L6203_IN2:PB3: already done by LOGIC block init
//pinMode(L6203_IN2,OUTPUT);
//L6203_ENABLE_PIN
//pinMode(L6203_ENABLE_OUT_PIN,OUTPUT); //OUTPUT slow
PORTA.DIRSET = PIN7_bm; //OUTPUT fast
PORTA.OUTCLR = PIN7_bm; //INIT STATE OFF
}
void INIT_TCB(){
// Reset registers. See the data sheets for details
noInterrupts();
// Clear the main timer control registers. Needed since the Arduino core creates some presets
DCC_TIMER.CTRLA = 0;
DCC_TIMER.CTRLB = 0;
DCC_TIMER.EVCTRL = 0;
DCC_TIMER.INTCTRL = 0;
DCC_TIMER.CCMP = 0;
DCC_TIMER.CNT = 0;
DCC_TIMER.INTFLAGS = 0;
// Set values:
DCC_TIMER.CTRLA = TCB_ENABLE_bm;
DCC_TIMER.CTRLB = TCB_CNTMODE_FRQ_gc;
DCC_TIMER.EVCTRL = TCB_CAPTEI_bm | TCB_FILTER_bm;
DCC_TIMER.INTCTRL |= TCB_CAPT_bm;
interrupts();
}