initial useable status
This commit is contained in:
@@ -0,0 +1,550 @@
|
||||
// DCCMFL1616 - DIY DCC Multifunction Loco decoder
|
||||
// licensed under GPL v3
|
||||
//
|
||||
// based on NMRA Dcc Multifunction Motor Decoder Demo by Alex Shepherd
|
||||
//
|
||||
// This firmware requires these Arduino Libraries:
|
||||
//
|
||||
// 1) The NmraDcc Library from: http://mrrwa.org/download/
|
||||
//
|
||||
// These libraries can be found and installed via the Arduino IDE Library Manager
|
||||
//
|
||||
// Functionality implemented:
|
||||
// - motor control (speed & direction) with start and maximum PWM values defined in CV2 & CV5
|
||||
// - integrated acceleration / deceleration profile controlled by CV3 & CV4
|
||||
// - exterieur lighting (F0 white head lights, F1 red rear lights, in combination with F5 white head and rear lights) in regards to driving direction
|
||||
// - defined default Decoder address: 3 according to NMRA & NEM standard
|
||||
// - factory reset by setting CV8 to 255
|
||||
// - debugging on UART (TX) can be actived (may slow down reaction of the decoder to updated controls)
|
||||
//
|
||||
// Functionality NOT implemented yet:
|
||||
// analog DC driving mode --> DCC-timeout?
|
||||
//
|
||||
|
||||
#include <NmraDcc.h>
|
||||
// Uncomment any of the lines below to enable debug messages for different parts of the code
|
||||
//#define DEBUG_FUNCTIONS
|
||||
//#define DEBUG_SPEED
|
||||
//#define DEBUG_PWM
|
||||
//#define DEBUG_DCC_ACK
|
||||
//#define DEBUG_DCC_MSG
|
||||
//#define DEBUG_DCC_RESET
|
||||
|
||||
#if defined(DEBUG_FUNCTIONS) or defined(DEBUG_SPEED) or defined(DEBUG_PWM) or defined(DEBUG_DCC_ACK) or defined(DEBUG_DCC_MSG) or defined(DEBUG_DCC_RESET)
|
||||
#define DEBUG_PRINT
|
||||
#endif
|
||||
|
||||
// This is the default DCC Address
|
||||
#define DEFAULT_DECODER_ADDRESS 3
|
||||
|
||||
// default Version
|
||||
#define DEFAULT_VERSION_ID 1
|
||||
|
||||
// This section defines the Arduino UNO Pins to use --> for testing purposes; not used in production
|
||||
#ifdef __AVR_ATmega328P__
|
||||
|
||||
// Define the Arduino input Pin number for the DCC Signal
|
||||
#define DCC_PIN 2
|
||||
|
||||
#define LED_PIN_WHITE_FRONT 5
|
||||
#define LED_PIN_WHITE_REAR 6
|
||||
#define LED_PIN_RED_FRONT 12
|
||||
#define LED_PIN_RED_REAR 13
|
||||
#define LED_PIN_CABIN 9
|
||||
#define MOTOR_PIN_1 11
|
||||
#define MOTOR_PIN_2 10
|
||||
|
||||
// This section defines the Arduino ATTiny1616/1626/3216/3226 Pins to use
|
||||
#elif defined __AVR_ATtinyxy6__
|
||||
|
||||
// Define the Arduino input Pin number for the DCC Signal
|
||||
#define DCC_PIN PIN_PA1
|
||||
|
||||
#define LED_PIN_WHITE_FRONT PIN_PB0
|
||||
#define LED_PIN_WHITE_REAR PIN_PB1
|
||||
#define LED_PIN_RED_FRONT PIN_PA5
|
||||
#define LED_PIN_RED_REAR PIN_PC0
|
||||
#define LED_PIN_CABIN PIN_PA7
|
||||
#define MOTOR_PIN_1 PIN_PA3
|
||||
#define MOTOR_PIN_2 PIN_PA4
|
||||
|
||||
#else
|
||||
#error "Unsupported CPU, you need to add another configuration section for your CPU"
|
||||
#endif
|
||||
|
||||
// Some global state variables
|
||||
|
||||
byte FN_0_4_state = 0;
|
||||
byte FN_5_8_state = 0;
|
||||
byte FN_9_12_state = 0;
|
||||
byte FN_13_20_state = 0;
|
||||
byte FN_21_28_state = 0;
|
||||
|
||||
// if cabin LED are connected to white LED pins pull cabin LED pin low
|
||||
bool invert_cabin_light_logic = true;
|
||||
|
||||
uint8_t newDirection = 0;
|
||||
uint8_t lastDirection = 0;
|
||||
|
||||
//speed variables
|
||||
uint8_t newSpeed = 0;
|
||||
uint8_t targetSpeed = 0;
|
||||
uint32_t speedChangeTime = 0;
|
||||
uint8_t numSpeedSteps = SPEED_STEP_128;
|
||||
uint8_t currentPwm = 0;
|
||||
uint8_t targetPwm = 0;
|
||||
uint8_t lastPwm = 0;
|
||||
uint8_t accRate = 0;
|
||||
uint8_t decRate = 0;
|
||||
|
||||
uint8_t vStart;
|
||||
uint8_t vHigh;
|
||||
|
||||
// Structure for CV Values Table
|
||||
struct CVPair
|
||||
{
|
||||
uint16_t CV;
|
||||
uint8_t Value;
|
||||
};
|
||||
|
||||
// CV Addresses we will be using according to NMRA 9.2.2 Table 1
|
||||
#define CV_VSTART 2
|
||||
#define CV_ACC_RATE 3
|
||||
#define CV_DEC_RATE 4
|
||||
#define CV_VHIGH 5
|
||||
|
||||
|
||||
// Default CV Values Table
|
||||
CVPair FactoryDefaultCVs [] =
|
||||
{
|
||||
// The CV Below defines the Short DCC Address
|
||||
{CV_MULTIFUNCTION_PRIMARY_ADDRESS, DEFAULT_DECODER_ADDRESS},
|
||||
|
||||
// Three Step Speed Table
|
||||
{CV_VSTART, 120},
|
||||
{CV_VHIGH, 255},
|
||||
|
||||
//Acceleration and deceleration; defaults to 0 (deactivated)
|
||||
{CV_ACC_RATE, 0},
|
||||
{CV_DEC_RATE, 0},
|
||||
|
||||
// CV7 Manufacturer Version ID
|
||||
{ CV_VERSION_ID, DEFAULT_VERSION_ID},
|
||||
|
||||
// CV8 --> Decoder Reset? (set CV to 255 to initiate factory reset)
|
||||
{CV_MANUFACTURER_ID, MAN_ID_DIY},
|
||||
|
||||
// These two CVs define the Long DCC Address
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, CALC_MULTIFUNCTION_EXTENDED_ADDRESS_MSB(DEFAULT_DECODER_ADDRESS)},
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, CALC_MULTIFUNCTION_EXTENDED_ADDRESS_LSB(DEFAULT_DECODER_ADDRESS)},
|
||||
|
||||
// ONLY uncomment 1 CV_29_CONFIG line below as approprate
|
||||
// {CV_29_CONFIG, 0}, // Short Address 14 Speed Steps
|
||||
{CV_29_CONFIG, CV29_F0_LOCATION}, // Short Address 28/128 Speed Steps
|
||||
// {CV_29_CONFIG, CV29_EXT_ADDRESSING | CV29_F0_LOCATION}, // Long Address 28/128 Speed Steps
|
||||
};
|
||||
|
||||
NmraDcc Dcc ;
|
||||
|
||||
uint8_t FactoryDefaultCVIndex = 0;
|
||||
|
||||
// This call-back function is called when a CV Value changes so we can update CVs we're using
|
||||
void notifyCVChange( uint16_t CV, uint8_t Value)
|
||||
{
|
||||
switch(CV)
|
||||
{
|
||||
case CV_VSTART:
|
||||
vStart = Value;
|
||||
break;
|
||||
|
||||
case CV_VHIGH:
|
||||
vHigh = Value;
|
||||
break;
|
||||
|
||||
case CV_ACC_RATE:
|
||||
accRate = Value;
|
||||
break;
|
||||
|
||||
case CV_DEC_RATE:
|
||||
decRate = Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DCC_RESET
|
||||
void notifyDccReset(uint8_t hardReset) {
|
||||
Serial.printf(F("notifyDccReset: %6s.\n"), hardReset ? "HARD" : "NORMAL");
|
||||
}
|
||||
#endif
|
||||
|
||||
void notifyCVResetFactoryDefault()
|
||||
{
|
||||
// Make FactoryDefaultCVIndex non-zero and equal to num CV's to be reset
|
||||
// to flag to the loop() function that a reset to Factory Defaults needs to be done
|
||||
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
|
||||
#ifdef DEBUG_DCC_RESET
|
||||
Serial.println("Factory reset");
|
||||
#endif
|
||||
};
|
||||
|
||||
// This call-back function is called whenever we receive a DCC Speed packet for our address
|
||||
void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps )
|
||||
{
|
||||
#ifdef DEBUG_SPEED
|
||||
Serial.print("notifyDccSpeed: Addr: ");
|
||||
Serial.print(Addr,DEC);
|
||||
Serial.print( (AddrType == DCC_ADDR_SHORT) ? "-S" : "-L" );
|
||||
Serial.print(" Speed: ");
|
||||
Serial.print(Speed,DEC);
|
||||
Serial.print(" Steps: ");
|
||||
Serial.print(SpeedSteps,DEC);
|
||||
Serial.print(" Dir: ");
|
||||
Serial.println( (Dir == DCC_DIR_FWD) ? "Forward" : "Reverse" );
|
||||
#endif
|
||||
|
||||
newDirection = Dir;
|
||||
newSpeed = Speed;
|
||||
numSpeedSteps = SpeedSteps;
|
||||
};
|
||||
|
||||
// This call-back function is called whenever we receive a DCC Function packet for our address
|
||||
void notifyDccFunc(uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState)
|
||||
{
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print("notifyDccFunc: Addr: ");
|
||||
Serial.print(Addr,DEC);
|
||||
Serial.print( (AddrType == DCC_ADDR_SHORT) ? 'S' : 'L' );
|
||||
Serial.print(" Function Group: ");
|
||||
Serial.print(FuncGrp,DEC);
|
||||
#endif
|
||||
|
||||
/* Liste der Funktionen aus NEM 608 Betriebsart 1 (eine Ebene) / List of functions according to NEM608 operation mode 1 (one layer)
|
||||
Funk | Traktionsart | Dampf | Verbrennung | Elektrisch | SchaltFunktion | Bemerkung
|
||||
| Kategorie | | | | |
|
||||
F0 | Beleuchtung | Fahrtrichtung vorwärts / rückwärts | Fahrtrichtung vorwärts / rückwärts | Fahrtrichtung vorwärts / rückwärts | ein / aus |
|
||||
F1 | Beleuchtung | Rückwärtiges Licht | Rückwärtiges Licht / Schlusslicht | Rückwärtiges Licht / Schlusslicht | ein / aus |
|
||||
F2 | Betrieb | Achtungspfiff | Achtungspfiff / Signalhorn | Achtungspfiff | Moment | Pfiff oder Horn entsprechend der Ausrüstung. Länge des Pfiffs / Horns wird von der Zeit der Betätigung der Taste bestimmt.
|
||||
F3 | Geräusche | Stand, Anfahren, Fahren, Bremsen | Anlassen, Motor(en), Stand, Anfahren, Fahren, Bremsen, Abschalten Motor(en) | Stand, Anfahren, Fahren, Bremsen | ein / aus |
|
||||
F4 | Betrieb | Entkuppeln | Entkuppeln | Entkuppeln |Moment |
|
||||
F5 | Betrieb | Rangiergang | Rangiergang | Rangiergang | ein / aus | F0 und F1 schalten Beleuchtung ein / aus
|
||||
F6 | Beleuchtung | Führerstand | Führerstand | Führerstand | ein / aus | Bei zwei Führerständen entsprechend der Fahrtrichtung
|
||||
F7 | Beleuchtung | Feuerbüchse | Motorraum- / Innenbeleuchtung | Motorraum- / Innenbeleuchtung | ein / aus |
|
||||
F8 | Auf- /Abrüsten | Dampferzeuger | Abgaserzeuger | Pantograph | ein / aus bzw. auf / ab | Bei zwei Pantographen entsprechend der Fahrtrichtung
|
||||
F9 | Geräusche | Luftpumpe | Kompressor | Kompressor | ein / aus |
|
||||
*/
|
||||
|
||||
switch(FuncGrp) {
|
||||
case FN_0_4:
|
||||
FN_0_4_state = FuncState;
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print(" FuncState: ");
|
||||
Serial.print(FuncState);
|
||||
Serial.print(" FN 0: ");
|
||||
Serial.print((FuncState & FN_BIT_00) ? 1 : 0);
|
||||
Serial.print(" FN 1: ");
|
||||
Serial.print((FuncState & FN_BIT_01) ? 1 : 0);
|
||||
Serial.print(" FN 2: ");
|
||||
Serial.print((FuncState & FN_BIT_02) ? 1 : 0);
|
||||
Serial.print(" FN 3: ");
|
||||
Serial.print((FuncState & FN_BIT_03) ? 1 : 0);
|
||||
Serial.print(" FN 4: ");
|
||||
Serial.print((FuncState & FN_BIT_04) ? 1 : 0);
|
||||
#endif
|
||||
break;
|
||||
case FN_5_8:
|
||||
FN_5_8_state = FuncState;
|
||||
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print(" FuncState: ");
|
||||
Serial.print(FuncState);
|
||||
Serial.print(" FN 5: ");
|
||||
Serial.print((FuncState & FN_BIT_05) ? 1 : 0);
|
||||
Serial.print(" FN 6: ");
|
||||
Serial.print((FuncState & FN_BIT_06) ? 1 : 0);
|
||||
Serial.print(" FN 7: ");
|
||||
Serial.print((FuncState & FN_BIT_07) ? 1 : 0);
|
||||
Serial.print(" FN 8: ");
|
||||
Serial.print((FuncState & FN_BIT_08) ? 1 : 0);
|
||||
#endif
|
||||
break;
|
||||
case FN_9_12:
|
||||
FN_9_12_state = FuncState;
|
||||
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print(" FuncState: ");
|
||||
Serial.print(FuncState);
|
||||
Serial.print(" FN 9: ");
|
||||
Serial.print((FuncState & FN_BIT_09) ? 1 : 0);
|
||||
Serial.print(" FN 10: ");
|
||||
Serial.print((FuncState & FN_BIT_10) ? 1 : 0);
|
||||
Serial.print(" FN 11: ");
|
||||
Serial.print((FuncState & FN_BIT_11) ? 1 : 0);
|
||||
Serial.print(" FN 12: ");
|
||||
Serial.print((FuncState & FN_BIT_12) ? 1 : 0);
|
||||
#endif
|
||||
break;
|
||||
case FN_13_20:
|
||||
FN_13_20_state = FuncState;
|
||||
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print(" FuncState: ");
|
||||
Serial.print(FuncState);
|
||||
Serial.print(" FN 13: ");
|
||||
Serial.print((FuncState & FN_BIT_13) ? 1 : 0);
|
||||
Serial.print(" FN 14: ");
|
||||
Serial.print((FuncState & FN_BIT_14) ? 1 : 0);
|
||||
Serial.print(" FN 15: ");
|
||||
Serial.print((FuncState & FN_BIT_15) ? 1 : 0);
|
||||
Serial.print(" FN 16: ");
|
||||
Serial.print((FuncState & FN_BIT_16) ? 1 : 0);
|
||||
Serial.print(" FN 17: ");
|
||||
Serial.print((FuncState & FN_BIT_17) ? 1 : 0);
|
||||
Serial.print(" FN 18: ");
|
||||
Serial.print((FuncState & FN_BIT_18) ? 1 : 0);
|
||||
Serial.print(" FN 19: ");
|
||||
Serial.print((FuncState & FN_BIT_19) ? 1 : 0);
|
||||
Serial.print(" FN 20: ");
|
||||
Serial.print((FuncState & FN_BIT_20) ? 1 : 0);
|
||||
#endif
|
||||
break;
|
||||
case FN_21_28:
|
||||
FN_21_28_state = FuncState;
|
||||
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print(" FuncState: ");
|
||||
Serial.print(FuncState);
|
||||
Serial.print(" FN 21: ");
|
||||
Serial.print((FuncState & FN_BIT_21) ? 1 : 0);
|
||||
Serial.print(" FN 22: ");
|
||||
Serial.print((FuncState & FN_BIT_22) ? 1 : 0);
|
||||
Serial.print(" FN 23: ");
|
||||
Serial.print((FuncState & FN_BIT_23) ? 1 : 0);
|
||||
Serial.print(" FN 24: ");
|
||||
Serial.print((FuncState & FN_BIT_24) ? 1 : 0);
|
||||
Serial.print(" FN 25: ");
|
||||
Serial.print((FuncState & FN_BIT_25) ? 1 : 0);
|
||||
Serial.print(" FN 26: ");
|
||||
Serial.print((FuncState & FN_BIT_26) ? 1 : 0);
|
||||
Serial.print(" FN 27: ");
|
||||
Serial.print((FuncState & FN_BIT_27) ? 1 : 0);
|
||||
Serial.print(" FN 28: ");
|
||||
Serial.print((FuncState & FN_BIT_28) ? 1 : 0);
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.println();
|
||||
#endif
|
||||
}
|
||||
|
||||
// This call-back function is called whenever we receive a DCC Packet and message-debugging is activated
|
||||
#ifdef DEBUG_DCC_MSG
|
||||
void notifyDccMsg( DCC_MSG * Msg)
|
||||
{
|
||||
Serial.print("notifyDccMsg, Number of Preamble Bits: ") ;
|
||||
Serial.print (Msg->PreambleBits);
|
||||
Serial.print("; Data Bytes: ");
|
||||
for(uint8_t i = 0; i < Msg->Size; i++)
|
||||
{
|
||||
Serial.print(Msg->Data[i], HEX);
|
||||
Serial.write(' ');
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
||||
|
||||
// This call-back function is called by the NmraDcc library when a DCC ACK needs to be sent
|
||||
// Calling this function should cause an increased 60ma current drain on the power supply for 6ms to ACK a CV Read
|
||||
// So we will just turn the motor on for 8ms and then turn it off again.
|
||||
// Pay attention to your control station, as it may limit power consumption on the programming track
|
||||
// (e.g. EX-CommandStation needs the command <D PROGBOOST> sent before programming to allow a
|
||||
// power consumption of more than 250mA and ACK working properly)
|
||||
|
||||
void notifyCVAck(void)
|
||||
{
|
||||
#ifdef DEBUG_DCC_ACK
|
||||
Serial.println("notifyCVAck") ;
|
||||
#endif
|
||||
|
||||
digitalWrite(MOTOR_PIN_1, HIGH);
|
||||
digitalWrite(MOTOR_PIN_2, LOW);
|
||||
|
||||
delay( 8 );
|
||||
|
||||
digitalWrite(MOTOR_PIN_1, LOW);
|
||||
digitalWrite(MOTOR_PIN_2, LOW);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
#ifdef DEBUG_PRINT
|
||||
Serial.begin(115200);
|
||||
uint8_t maxWaitLoops = 255;
|
||||
while(!Serial && maxWaitLoops--)
|
||||
delay(20);
|
||||
|
||||
Serial.println("DCCMFL1616 Debugging interface");
|
||||
Serial.print("Decoder-Address: ");
|
||||
Serial.println(Dcc.getCV(CV_MULTIFUNCTION_PRIMARY_ADDRESS), DEC);
|
||||
#endif
|
||||
|
||||
// Setup the Pins for the Fwd/Rev LED for Function 0 Headlight
|
||||
pinMode(LED_PIN_WHITE_FRONT, OUTPUT);
|
||||
pinMode(LED_PIN_WHITE_REAR, OUTPUT);
|
||||
pinMode(LED_PIN_RED_FRONT, OUTPUT);
|
||||
pinMode(LED_PIN_RED_REAR, OUTPUT);
|
||||
pinMode(LED_PIN_CABIN, OUTPUT);
|
||||
|
||||
// Setup the Pins for the Motor H-Bridge Driver
|
||||
pinMode(MOTOR_PIN_1, OUTPUT);
|
||||
pinMode(MOTOR_PIN_2, OUTPUT);
|
||||
|
||||
|
||||
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||
// Many Arduino Cores now support the digitalPinToInterrupt() function that makes it easier to figure out the
|
||||
// Interrupt Number for the Arduino Pin number, which reduces confusion.
|
||||
#ifdef digitalPinToInterrupt
|
||||
Dcc.pin(DCC_PIN, 0);
|
||||
#else
|
||||
Dcc.pin(0, DCC_PIN, 1);
|
||||
#endif
|
||||
|
||||
Dcc.init( MAN_ID_DIY, DEFAULT_VERSION_ID, FLAGS_MY_ADDRESS_ONLY | FLAGS_AUTO_FACTORY_DEFAULT, 0 );
|
||||
|
||||
// Uncomment to force CV Reset to Factory Defaults; usually not needed
|
||||
// notifyCVResetFactoryDefault();
|
||||
|
||||
// Read the current CV values
|
||||
vStart = Dcc.getCV(CV_VSTART);
|
||||
vHigh = Dcc.getCV(CV_VHIGH);
|
||||
accRate = Dcc.getCV(CV_ACC_RATE);
|
||||
decRate = Dcc.getCV(CV_DEC_RATE);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
|
||||
Dcc.process();
|
||||
// Handle Speed changes
|
||||
if(targetSpeed != newSpeed) {
|
||||
targetSpeed = newSpeed;
|
||||
if (newSpeed <= 1) {
|
||||
targetPwm = 0;
|
||||
}
|
||||
else {
|
||||
// Calculate PWM value in the range 1..255
|
||||
uint8_t vScaleFactor;
|
||||
|
||||
vScaleFactor = ((vHigh > 1) && (vHigh > vStart)) ? vHigh - vStart : 255 - vStart;
|
||||
|
||||
uint8_t modSpeed = newSpeed - 1;
|
||||
uint8_t modSteps = numSpeedSteps - 1;
|
||||
|
||||
targetPwm = (uint8_t) vStart + modSpeed * vScaleFactor / modSteps;
|
||||
|
||||
#ifdef DEBUG_PWM
|
||||
Serial.print("New Speed: vStart: ");
|
||||
Serial.print(vStart);
|
||||
Serial.print(" vHigh: ");
|
||||
Serial.print(vHigh);
|
||||
Serial.print(" modSpeed: ");
|
||||
Serial.print(modSpeed);
|
||||
Serial.print(" vScaleFactor: ");
|
||||
Serial.print(vScaleFactor);
|
||||
Serial.print(" modSteps: ");
|
||||
Serial.print(modSteps);
|
||||
Serial.print(" targetPwm: ");
|
||||
Serial.println(targetPwm);
|
||||
#endif
|
||||
}
|
||||
lastPwm = currentPwm;
|
||||
speedChangeTime = millis();
|
||||
}
|
||||
|
||||
if(targetPwm > currentPwm) {
|
||||
currentPwm = (accRate == 0) ? targetPwm : lastPwm + floor( (vHigh - vStart) * (millis() - speedChangeTime) / (accRate * 0.896 * 1000) + 0.5);
|
||||
if (currentPwm < vStart) {
|
||||
currentPwm = vStart;
|
||||
lastPwm = vStart;
|
||||
}
|
||||
else if (currentPwm > targetPwm) currentPwm = targetPwm;
|
||||
#ifdef DEBUG_PWM
|
||||
Serial.print("increase speed; accRate: ");
|
||||
Serial.print(accRate);
|
||||
Serial.print(" currentPwm: ");
|
||||
Serial.print(currentPwm);
|
||||
Serial.println();
|
||||
#endif
|
||||
}
|
||||
else if(targetPwm < currentPwm) {
|
||||
currentPwm = (decRate == 0) ? targetPwm : lastPwm - floor( (vHigh - vStart) * (millis() - speedChangeTime) / (decRate * 0.896 * 1000) + 0.5);
|
||||
if (currentPwm < targetPwm) currentPwm = targetPwm;
|
||||
if (currentPwm < vStart) currentPwm = 0;
|
||||
|
||||
#ifdef DEBUG_PWM
|
||||
Serial.print("reduce Speed; decRate: ");
|
||||
Serial.print(decRate);
|
||||
Serial.print(" currentPwm: ");
|
||||
Serial.print(currentPwm);
|
||||
Serial.println();
|
||||
#endif
|
||||
}
|
||||
|
||||
analogWrite(MOTOR_PIN_1, newDirection ? currentPwm : 0);
|
||||
analogWrite(MOTOR_PIN_2, newDirection ? 0 : currentPwm);
|
||||
|
||||
/*
|
||||
* lighting
|
||||
*/
|
||||
//F0 & F5 white LEDs front
|
||||
digitalWrite(LED_PIN_WHITE_FRONT, ( FN_0_4_state & FN_BIT_00 & (newDirection << 4 | FN_5_8_state << 4 ) ) ? HIGH : LOW);
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print("whithe LEDs front: ");
|
||||
Serial.println( ( FN_0_4_state & FN_BIT_00 & (newDirection << 4 | FN_5_8_state << 4 ) ) ? 1 : 0);
|
||||
#endif
|
||||
|
||||
//F0 & F5 white LEDs rear
|
||||
digitalWrite(LED_PIN_WHITE_REAR, ( FN_0_4_state & FN_BIT_00 & (~(newDirection << 4) | FN_5_8_state << 4 ) ) ? HIGH : LOW);
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print("whithe LEDs rear: ");
|
||||
Serial.println( ( FN_0_4_state & FN_BIT_00 & (~(newDirection << 4) | FN_5_8_state << 4 ) ) ? 1 : 0);
|
||||
#endif
|
||||
|
||||
//F1 red LEDs front
|
||||
digitalWrite(LED_PIN_RED_FRONT, ( FN_0_4_state & FN_BIT_00 & ~(newDirection << 4) & FN_0_4_state << 4 & ~(FN_5_8_state << 4) ) ? HIGH : LOW);
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print("RED LEDs front: ");
|
||||
Serial.println( ( FN_0_4_state & FN_BIT_00 & ~(newDirection << 4) & FN_0_4_state << 4 & ~(FN_5_8_state << 4) ) ? 1 : 0);
|
||||
#endif
|
||||
|
||||
//F1 red LEDs rear
|
||||
digitalWrite(LED_PIN_RED_REAR, ( FN_0_4_state & FN_BIT_00 & newDirection << 4 & FN_0_4_state << 4 & ~(FN_5_8_state << 4) ) ? HIGH : LOW);
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print("RED LEDs rear: ");
|
||||
Serial.println( ( FN_0_4_state & FN_BIT_00 & newDirection << 4 & FN_0_4_state << 4 & ~(FN_5_8_state << 4) ) ? 1 : 0);
|
||||
#endif
|
||||
|
||||
//F6 cabin light (standard: only works if white LED front/rear active)
|
||||
digitalWrite(LED_PIN_CABIN, (FN_5_8_state & FN_BIT_06 & invert_cabin_light_logic << 1) ? LOW : HIGH );
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print("CABIN LEDs: ");
|
||||
Serial.println( (FN_5_8_state & FN_BIT_06 & invert_cabin_light_logic << 1) ? LOW : HIGH );
|
||||
#endif
|
||||
|
||||
// Handle resetting CVs back to Factory Defaults
|
||||
if( FactoryDefaultCVIndex && Dcc.isSetCVReady())
|
||||
{
|
||||
FactoryDefaultCVIndex--; // Decrement first as initially it is the size of the array
|
||||
Dcc.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV, FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
|
||||
#ifdef DEBUG_DCC_RESET
|
||||
Serial.print("Factory reset: ");
|
||||
Serial.print("CV");
|
||||
Serial.print(FactoryDefaultCVs[FactoryDefaultCVIndex].CV);
|
||||
Serial.print(" = ");
|
||||
Serial.println(FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,65 @@
|
||||
# DCCMFL-Firmware
|
||||
Firmware to all our DCC Multi-Functional Locomotive Controllers.
|
||||
|
||||
Firmware to all our DCC Multi-Functional Locomotive Controllers.
|
||||
## Prerequisits
|
||||
- Arduino IDE 2
|
||||
- megaTinyCore library: can be installed from Arduino IDE boards manager by adding this URL to the "Additional boards manager URLs": http://drazzy.com/package_drazzy.com_index.json or for manual install you can find the git-repository here: [https://github.com/SpenceKonde/megaTinyCore](https://github.com/SpenceKonde/megaTinyCore)
|
||||
- NmraDcc library: can be installed from Arduino IDE library manager or from the git-repository at [https://github.com/mrrwa/NmraDcc](https://github.com/mrrwa/NmraDcc)
|
||||
- Arudino Uno or Mega (or a compatible clone) with [JTAG2UPDI programmer](https://github.com/ElTangas/jtag2updi) burned onto it
|
||||
- some jupmer wires to connect the decoder to the Arduino Uno
|
||||
|
||||
## Compilation
|
||||
|
||||
### DCCMFL1616 series - ATtiny1616 based decoders
|
||||
- In the Arduino IDE go to the Tools-menu and select Board -> megaTinyCore -> ATtiny3226/3216/1626/1616/1606/826/816/806/426/416/406
|
||||
- In the Arduino IDE go to the Tools-menu and select BOD-voltage -> 4.2V (20MHz or less)
|
||||
- In the Arduino IDE go to the Tools-menu and select Chip -> ATtiny1616
|
||||
- In the Arduino IDE go to the Tools-menu and select Clock -> 20 MHz internal
|
||||
- To build the sketch go to Sketch-menu and select Verify Compile
|
||||
- Wait for the firmware to compile
|
||||
- After successful compilation you are ready to [upload](#upload) the firmware to your decoder
|
||||
|
||||
## Upload
|
||||
|
||||
### DCCMFL1616 series - Connect the decoder with the Arduino
|
||||
- disconnect decoder and the Arduino UNO from any power sources (USB, power supply, track power, ...)
|
||||
- Connect the GND pin of the decoder programming-interface with the GND pin on the Arduino
|
||||
- Connect VCC pin on the decoder with the 5V pin on the Arduino
|
||||
- connect the UDPI pin on the decoder with digital pin 6 on the Arduino
|
||||
- connect the Arduino to your computer
|
||||
|
||||
#### Upload firmware
|
||||
- In the Arduino IDE go to the Tools-menu and select Port -> Serial Port of the connected Arduino
|
||||
- In the Arduino IDE go to the Tools-menu and select Programmer -> jtag2updi
|
||||
- In the Arduino IDE go to the Sketch-menu and select Upload Using Programmer
|
||||
- Wait for the firmware to compile and upload to the decoder
|
||||
- Disconnect your Arduino from your commputer
|
||||
- Disconnect your decoder from the Arduino
|
||||
|
||||
## Setup decoder
|
||||
- Assemble your decoder on your loco
|
||||
- Put your loco onto the programming track and activate the programming track power
|
||||
- Programm your decoder using your DCC programming tools (Standard-DCC-Address is 3 but it might not be properly assigned when the decoder is flashed for the first time)
|
||||
|
||||
## Functionaly implemented
|
||||
- motor control (speed & direction) with start and maximum PWM values defined in CV2 & CV5
|
||||
- integrated acceleration / deceleration profile controlled by CV3 & CV4
|
||||
- exterieur lighting (F0 white head lights, F1 red rear lights, in combination with F5 white head and rear lights) in regards to driving direction
|
||||
- direction related cabin light
|
||||
- defined default Decoder address: 3 (according to NMRA & NEM standard)
|
||||
- factory reset by setting CV8 to 255
|
||||
- debugging on UART (TX) can be activated (may slow down reaction of the decoder to updated controls)
|
||||
|
||||
## Functionality planned but NOT implemented yet:
|
||||
- analog DC driving mode --> DCC-timeout?
|
||||
|
||||
## Adapting the firmware to your needs
|
||||
Just as the electronics design, the firmware is free to use and modify. So if you need any specific functions driven by the decoder you can easily add this to the firmware.
|
||||
Pretty much all of the firmware (other than the libraries) is found in the DCCMFL1616.ino sketch. The firmware is based on an example of the NmraDcc library. It was heavily altered though to realize a much more functional decoder. There are plenty of comments in the sketch so you should be able to understand how it is working if you have at least some basic programming skills.
|
||||
|
||||
So you added a new feature to the firmware? Why don't you let us know about it. Create a fork of the [official repository](https://git.schauaus.at/Fahrzeugsteuerungen/DCCMFL-Firmware.git), add your changes and leave us a message on the fedivers (@modellbau@schauaus.at).
|
||||
If we think your feature may be useful to others as well, we will test it and probably add it to the official repository eventually. Credits will be granted to you in this case of course!
|
||||
|
||||
## Feature request
|
||||
In case you do not have the programming skills needed and you would like to see an additional feature added to the standard-firmware feel free to reach out to us on the fediverse (@modellbau@schauaus.at) and tell us about what you would like to see added.
|
||||
There is no guaranty though that or when a new feature may be added.
|
||||
|
||||
Reference in New Issue
Block a user