mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-25 00:56:13 +01:00
Added OnOverload
Added code for OnOverload EXRAIL command. Untested at this point.
This commit is contained in:
parent
33306219c8
commit
ae1c1d0e9a
41
EXRAIL2.cpp
41
EXRAIL2.cpp
|
@ -2,7 +2,7 @@
|
||||||
* © 2021 Neil McKechnie
|
* © 2021 Neil McKechnie
|
||||||
* © 2021-2023 Harald Barth
|
* © 2021-2023 Harald Barth
|
||||||
* © 2020-2023 Chris Harlow
|
* © 2020-2023 Chris Harlow
|
||||||
* © 2022 Colin Murdoch
|
* © 2022-2023 Colin Murdoch
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of CommandStation-EX
|
* This file is part of CommandStation-EX
|
||||||
|
@ -94,6 +94,8 @@ LookList * RMFT2::onAmberLookup=NULL;
|
||||||
LookList * RMFT2::onGreenLookup=NULL;
|
LookList * RMFT2::onGreenLookup=NULL;
|
||||||
LookList * RMFT2::onChangeLookup=NULL;
|
LookList * RMFT2::onChangeLookup=NULL;
|
||||||
LookList * RMFT2::onClockLookup=NULL;
|
LookList * RMFT2::onClockLookup=NULL;
|
||||||
|
//CHM
|
||||||
|
LookList * RMFT2::onOverloadLookup=NULL;
|
||||||
|
|
||||||
#define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter)
|
#define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter)
|
||||||
#define SKIPOP progCounter+=3
|
#define SKIPOP progCounter+=3
|
||||||
|
@ -175,6 +177,8 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
|
||||||
onGreenLookup=LookListLoader(OPCODE_ONGREEN);
|
onGreenLookup=LookListLoader(OPCODE_ONGREEN);
|
||||||
onChangeLookup=LookListLoader(OPCODE_ONCHANGE);
|
onChangeLookup=LookListLoader(OPCODE_ONCHANGE);
|
||||||
onClockLookup=LookListLoader(OPCODE_ONTIME);
|
onClockLookup=LookListLoader(OPCODE_ONTIME);
|
||||||
|
//CHM
|
||||||
|
onOverloadLookup=LookListLoader(OPCODE_ONOVERLOAD);
|
||||||
|
|
||||||
|
|
||||||
// Second pass startup, define any turnouts or servos, set signals red
|
// Second pass startup, define any turnouts or servos, set signals red
|
||||||
|
@ -266,12 +270,12 @@ void RMFT2::setTurnoutHiddenState(Turnout * t) {
|
||||||
char RMFT2::getRouteType(int16_t id) {
|
char RMFT2::getRouteType(int16_t id) {
|
||||||
for (int16_t i=0;;i+=2) {
|
for (int16_t i=0;;i+=2) {
|
||||||
int16_t rid= GETHIGHFLASHW(routeIdList,i);
|
int16_t rid= GETHIGHFLASHW(routeIdList,i);
|
||||||
if (rid==0) break;
|
if (rid==INT16_MAX) break;
|
||||||
if (rid==id) return 'R';
|
if (rid==id) return 'R';
|
||||||
}
|
}
|
||||||
for (int16_t i=0;;i+=2) {
|
for (int16_t i=0;;i+=2) {
|
||||||
int16_t rid= GETHIGHFLASHW(automationIdList,i);
|
int16_t rid= GETHIGHFLASHW(automationIdList,i);
|
||||||
if (rid==0) break;
|
if (rid==INT16_MAX) break;
|
||||||
if (rid==id) return 'A';
|
if (rid==id) return 'A';
|
||||||
}
|
}
|
||||||
return 'X';
|
return 'X';
|
||||||
|
@ -610,6 +614,7 @@ void RMFT2::loop2() {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPCODE_SPEED:
|
case OPCODE_SPEED:
|
||||||
|
forward=DCC::getThrottleDirection(loco)^invert;
|
||||||
driveLoco(operand);
|
driveLoco(operand);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -883,18 +888,13 @@ void RMFT2::loop2() {
|
||||||
while(loopTask) loopTask->kill(F("KILLALL"));
|
while(loopTask) loopTask->kill(F("KILLALL"));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
#ifndef DISABLE_PROG
|
||||||
case OPCODE_JOIN:
|
case OPCODE_JOIN:
|
||||||
TrackManager::setPower(POWERMODE::ON);
|
TrackManager::setPower(POWERMODE::ON);
|
||||||
TrackManager::setJoin(true);
|
TrackManager::setJoin(true);
|
||||||
CommandDistributor::broadcastPower();
|
CommandDistributor::broadcastPower();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPCODE_POWERON:
|
|
||||||
TrackManager::setMainPower(POWERMODE::ON);
|
|
||||||
TrackManager::setJoin(false);
|
|
||||||
CommandDistributor::broadcastPower();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OPCODE_UNJOIN:
|
case OPCODE_UNJOIN:
|
||||||
TrackManager::setJoin(false);
|
TrackManager::setJoin(false);
|
||||||
CommandDistributor::broadcastPower();
|
CommandDistributor::broadcastPower();
|
||||||
|
@ -920,6 +920,13 @@ void RMFT2::loop2() {
|
||||||
forward=true;
|
forward=true;
|
||||||
invert=false;
|
invert=false;
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case OPCODE_POWERON:
|
||||||
|
TrackManager::setMainPower(POWERMODE::ON);
|
||||||
|
TrackManager::setJoin(false);
|
||||||
|
CommandDistributor::broadcastPower();
|
||||||
|
break;
|
||||||
|
|
||||||
case OPCODE_START:
|
case OPCODE_START:
|
||||||
{
|
{
|
||||||
|
@ -983,6 +990,8 @@ void RMFT2::loop2() {
|
||||||
case OPCODE_ONGREEN:
|
case OPCODE_ONGREEN:
|
||||||
case OPCODE_ONCHANGE:
|
case OPCODE_ONCHANGE:
|
||||||
case OPCODE_ONTIME:
|
case OPCODE_ONTIME:
|
||||||
|
//CHM
|
||||||
|
case OPCODE_ONOVERLOAD:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1136,6 +1145,15 @@ void RMFT2::clockEvent(int16_t clocktime, bool change) {
|
||||||
handleEvent(F("CLOCK"),onClockLookup,25*60+clocktime%60);
|
handleEvent(F("CLOCK"),onClockLookup,25*60+clocktime%60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//CHM
|
||||||
|
void RMFT2::powerEvent(char track, bool overload) {
|
||||||
|
// Hunt for an ONOVERLOAD for this item
|
||||||
|
if (Diag::CMD)
|
||||||
|
DIAG(F("Looking for Power event on track : %c"), track);
|
||||||
|
if (overload) {
|
||||||
|
handleEvent(F("POWER"),onOverloadLookup,track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RMFT2::handleEvent(const FSH* reason,LookList* handlers, int16_t id) {
|
void RMFT2::handleEvent(const FSH* reason,LookList* handlers, int16_t id) {
|
||||||
int pc= handlers->find(id);
|
int pc= handlers->find(id);
|
||||||
|
@ -1242,7 +1260,10 @@ void RMFT2::thrungeString(uint32_t strfar, thrunger mode, byte id) {
|
||||||
DCCEXParser::parseOne(&USB_SERIAL,(byte*)buffer->getString(),NULL);
|
DCCEXParser::parseOne(&USB_SERIAL,(byte*)buffer->getString(),NULL);
|
||||||
break;
|
break;
|
||||||
case thrunge_broadcast:
|
case thrunge_broadcast:
|
||||||
// TODO CommandDistributor::broadcastText(buffer->getString());
|
CommandDistributor::broadcastRaw(CommandDistributor::COMMAND_TYPE,buffer->getString());
|
||||||
|
break;
|
||||||
|
case thrunge_withrottle:
|
||||||
|
CommandDistributor::broadcastRaw(CommandDistributor::WITHROTTLE_TYPE,buffer->getString());
|
||||||
break;
|
break;
|
||||||
case thrunge_lcd:
|
case thrunge_lcd:
|
||||||
LCD(id,F("%s"),buffer->getString());
|
LCD(id,F("%s"),buffer->getString());
|
||||||
|
|
15
EXRAIL2.h
15
EXRAIL2.h
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* © 2021 Neil McKechnie
|
* © 2021 Neil McKechnie
|
||||||
* © 2020-2022 Chris Harlow
|
* © 2020-2022 Chris Harlow
|
||||||
* © 2022 Colin Murdoch
|
* © 2022-2023 Colin Murdoch
|
||||||
* © 2023 Harald Barth
|
* © 2023 Harald Barth
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -45,7 +45,10 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
|
||||||
OPCODE_RED,OPCODE_GREEN,OPCODE_AMBER,OPCODE_DRIVE,
|
OPCODE_RED,OPCODE_GREEN,OPCODE_AMBER,OPCODE_DRIVE,
|
||||||
OPCODE_SERVO,OPCODE_SIGNAL,OPCODE_TURNOUT,OPCODE_WAITFOR,
|
OPCODE_SERVO,OPCODE_SIGNAL,OPCODE_TURNOUT,OPCODE_WAITFOR,
|
||||||
OPCODE_PAD,OPCODE_FOLLOW,OPCODE_CALL,OPCODE_RETURN,
|
OPCODE_PAD,OPCODE_FOLLOW,OPCODE_CALL,OPCODE_RETURN,
|
||||||
OPCODE_JOIN,OPCODE_UNJOIN,OPCODE_READ_LOCO1,OPCODE_READ_LOCO2,OPCODE_POM,
|
#ifndef DISABLE_PROG
|
||||||
|
OPCODE_JOIN,OPCODE_UNJOIN,OPCODE_READ_LOCO1,OPCODE_READ_LOCO2,
|
||||||
|
#endif
|
||||||
|
OPCODE_POM,
|
||||||
OPCODE_START,OPCODE_SETLOCO,OPCODE_SENDLOCO,OPCODE_FORGET,
|
OPCODE_START,OPCODE_SETLOCO,OPCODE_SENDLOCO,OPCODE_FORGET,
|
||||||
OPCODE_PAUSE, OPCODE_RESUME,OPCODE_POWEROFF,OPCODE_POWERON,
|
OPCODE_PAUSE, OPCODE_RESUME,OPCODE_POWEROFF,OPCODE_POWERON,
|
||||||
OPCODE_ONCLOSE, OPCODE_ONTHROW, OPCODE_SERVOTURNOUT, OPCODE_PINTURNOUT,
|
OPCODE_ONCLOSE, OPCODE_ONTHROW, OPCODE_SERVOTURNOUT, OPCODE_PINTURNOUT,
|
||||||
|
@ -59,6 +62,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
|
||||||
OPCODE_ONCHANGE,
|
OPCODE_ONCHANGE,
|
||||||
OPCODE_ONCLOCKTIME,
|
OPCODE_ONCLOCKTIME,
|
||||||
OPCODE_ONTIME,
|
OPCODE_ONTIME,
|
||||||
|
OPCODE_ONOVERLOAD,
|
||||||
|
|
||||||
// OPcodes below this point are skip-nesting IF operations
|
// OPcodes below this point are skip-nesting IF operations
|
||||||
// placed here so that they may be skipped as a group
|
// placed here so that they may be skipped as a group
|
||||||
|
@ -77,7 +81,8 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
|
||||||
// Ensure thrunge_lcd is put last as there may be more than one display,
|
// Ensure thrunge_lcd is put last as there may be more than one display,
|
||||||
// sequentially numbered from thrunge_lcd.
|
// sequentially numbered from thrunge_lcd.
|
||||||
enum thrunger: byte {
|
enum thrunger: byte {
|
||||||
thrunge_print, thrunge_broadcast, thrunge_serial,thrunge_parse,
|
thrunge_print, thrunge_broadcast, thrunge_withrottle,
|
||||||
|
thrunge_serial,thrunge_parse,
|
||||||
thrunge_serial1, thrunge_serial2, thrunge_serial3,
|
thrunge_serial1, thrunge_serial2, thrunge_serial3,
|
||||||
thrunge_serial4, thrunge_serial5, thrunge_serial6,
|
thrunge_serial4, thrunge_serial5, thrunge_serial6,
|
||||||
thrunge_lcn,
|
thrunge_lcn,
|
||||||
|
@ -126,6 +131,7 @@ class LookList {
|
||||||
static void activateEvent(int16_t addr, bool active);
|
static void activateEvent(int16_t addr, bool active);
|
||||||
static void changeEvent(int16_t id, bool change);
|
static void changeEvent(int16_t id, bool change);
|
||||||
static void clockEvent(int16_t clocktime, bool change);
|
static void clockEvent(int16_t clocktime, bool change);
|
||||||
|
static void powerEvent(char track, bool overload);
|
||||||
static const int16_t SERVO_SIGNAL_FLAG=0x4000;
|
static const int16_t SERVO_SIGNAL_FLAG=0x4000;
|
||||||
static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000;
|
static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000;
|
||||||
static const int16_t DCC_SIGNAL_FLAG=0x1000;
|
static const int16_t DCC_SIGNAL_FLAG=0x1000;
|
||||||
|
@ -184,6 +190,9 @@ private:
|
||||||
static LookList * onGreenLookup;
|
static LookList * onGreenLookup;
|
||||||
static LookList * onChangeLookup;
|
static LookList * onChangeLookup;
|
||||||
static LookList * onClockLookup;
|
static LookList * onClockLookup;
|
||||||
|
//CHM
|
||||||
|
static LookList * onOverloadLookup;
|
||||||
|
|
||||||
|
|
||||||
// Local variables - exist for each instance/task
|
// Local variables - exist for each instance/task
|
||||||
RMFT2 *next; // loop chain
|
RMFT2 *next; // loop chain
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* © 2020-2022 Chris Harlow. All rights reserved.
|
* © 2020-2022 Chris Harlow. All rights reserved.
|
||||||
* © 2022 Colin Murdoch
|
* © 2022-2023 Colin Murdoch
|
||||||
* © 2023 Harald Barth
|
* © 2023 Harald Barth
|
||||||
*
|
*
|
||||||
* This file is part of CommandStation-EX
|
* This file is part of CommandStation-EX
|
||||||
|
@ -93,6 +93,8 @@
|
||||||
#undef ONTIME
|
#undef ONTIME
|
||||||
#undef ONCLOCKTIME
|
#undef ONCLOCKTIME
|
||||||
#undef ONCLOCKMINS
|
#undef ONCLOCKMINS
|
||||||
|
//CHM
|
||||||
|
#undef ONOVERLOAD
|
||||||
#undef ONGREEN
|
#undef ONGREEN
|
||||||
#undef ONRED
|
#undef ONRED
|
||||||
#undef ONTHROW
|
#undef ONTHROW
|
||||||
|
@ -101,7 +103,9 @@
|
||||||
#undef PAUSE
|
#undef PAUSE
|
||||||
#undef PIN_TURNOUT
|
#undef PIN_TURNOUT
|
||||||
#undef PRINT
|
#undef PRINT
|
||||||
|
#ifndef DISABLE_PROG
|
||||||
#undef POM
|
#undef POM
|
||||||
|
#endif
|
||||||
#undef POWEROFF
|
#undef POWEROFF
|
||||||
#undef POWERON
|
#undef POWERON
|
||||||
#undef READ_LOCO
|
#undef READ_LOCO
|
||||||
|
@ -142,6 +146,7 @@
|
||||||
#undef VIRTUAL_SIGNAL
|
#undef VIRTUAL_SIGNAL
|
||||||
#undef VIRTUAL_TURNOUT
|
#undef VIRTUAL_TURNOUT
|
||||||
#undef WAITFOR
|
#undef WAITFOR
|
||||||
|
#undef WITHROTTLE
|
||||||
#undef XFOFF
|
#undef XFOFF
|
||||||
#undef XFON
|
#undef XFON
|
||||||
|
|
||||||
|
@ -214,6 +219,8 @@
|
||||||
#define ONCLOCKMINS(mins)
|
#define ONCLOCKMINS(mins)
|
||||||
#define ONDEACTIVATE(addr,subaddr)
|
#define ONDEACTIVATE(addr,subaddr)
|
||||||
#define ONDEACTIVATEL(linear)
|
#define ONDEACTIVATEL(linear)
|
||||||
|
//CHM
|
||||||
|
#define ONOVERLOAD(track_id)
|
||||||
#define ONCLOSE(turnout_id)
|
#define ONCLOSE(turnout_id)
|
||||||
#define ONGREEN(signal_id)
|
#define ONGREEN(signal_id)
|
||||||
#define ONRED(signal_id)
|
#define ONRED(signal_id)
|
||||||
|
@ -223,7 +230,9 @@
|
||||||
#define PIN_TURNOUT(id,pin,description...)
|
#define PIN_TURNOUT(id,pin,description...)
|
||||||
#define PRINT(msg)
|
#define PRINT(msg)
|
||||||
#define PARSE(msg)
|
#define PARSE(msg)
|
||||||
|
#ifndef DISABLE_PROG
|
||||||
#define POM(cv,value)
|
#define POM(cv,value)
|
||||||
|
#endif
|
||||||
#define POWEROFF
|
#define POWEROFF
|
||||||
#define POWERON
|
#define POWERON
|
||||||
#define READ_LOCO
|
#define READ_LOCO
|
||||||
|
@ -264,6 +273,7 @@
|
||||||
#define VIRTUAL_SIGNAL(id)
|
#define VIRTUAL_SIGNAL(id)
|
||||||
#define VIRTUAL_TURNOUT(id,description...)
|
#define VIRTUAL_TURNOUT(id,description...)
|
||||||
#define WAITFOR(pin)
|
#define WAITFOR(pin)
|
||||||
|
#define WITHROTTLE(msg)
|
||||||
#define XFOFF(cab,func)
|
#define XFOFF(cab,func)
|
||||||
#define XFON(cab,func)
|
#define XFON(cab,func)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* © 2021 Neil McKechnie
|
* © 2021 Neil McKechnie
|
||||||
* © 2020-2022 Chris Harlow
|
* © 2020-2022 Chris Harlow
|
||||||
* © 2022 Colin Murdoch
|
* © 2022-2023 Colin Murdoch
|
||||||
* © 2023 Harald Barth
|
* © 2023 Harald Barth
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -81,14 +81,14 @@ void exrailHalSetup() {
|
||||||
#define ROUTE(id, description) id,
|
#define ROUTE(id, description) id,
|
||||||
const int16_t HIGHFLASH RMFT2::routeIdList[]= {
|
const int16_t HIGHFLASH RMFT2::routeIdList[]= {
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
0};
|
INT16_MAX};
|
||||||
// Pass 2a create throttle automation list
|
// Pass 2a create throttle automation list
|
||||||
#include "EXRAIL2MacroReset.h"
|
#include "EXRAIL2MacroReset.h"
|
||||||
#undef AUTOMATION
|
#undef AUTOMATION
|
||||||
#define AUTOMATION(id, description) id,
|
#define AUTOMATION(id, description) id,
|
||||||
const int16_t HIGHFLASH RMFT2::automationIdList[]= {
|
const int16_t HIGHFLASH RMFT2::automationIdList[]= {
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
0};
|
INT16_MAX};
|
||||||
|
|
||||||
// Pass 3 Create route descriptions:
|
// Pass 3 Create route descriptions:
|
||||||
#undef ROUTE
|
#undef ROUTE
|
||||||
|
@ -153,6 +153,8 @@ const int StringMacroTracker1=__COUNTER__;
|
||||||
lcdid=id;\
|
lcdid=id;\
|
||||||
break;\
|
break;\
|
||||||
}
|
}
|
||||||
|
#undef WITHROTTLE
|
||||||
|
#define WITHROTTLE(msg) THRUNGE(msg,thrunge_withrottle)
|
||||||
|
|
||||||
void RMFT2::printMessage(uint16_t id) {
|
void RMFT2::printMessage(uint16_t id) {
|
||||||
thrunger tmode;
|
thrunger tmode;
|
||||||
|
@ -188,7 +190,7 @@ const FSH * RMFT2::getTurnoutDescription(int16_t turnoutid) {
|
||||||
// Pass 6: Roster IDs (count)
|
// Pass 6: Roster IDs (count)
|
||||||
#include "EXRAIL2MacroReset.h"
|
#include "EXRAIL2MacroReset.h"
|
||||||
#undef ROSTER
|
#undef ROSTER
|
||||||
#define ROSTER(cabid,name,funcmap...) +1
|
#define ROSTER(cabid,name,funcmap...) +(cabid <= 0 ? 0 : 1)
|
||||||
const byte RMFT2::rosterNameCount=0
|
const byte RMFT2::rosterNameCount=0
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
;
|
;
|
||||||
|
@ -199,7 +201,7 @@ const byte RMFT2::rosterNameCount=0
|
||||||
#define ROSTER(cabid,name,funcmap...) cabid,
|
#define ROSTER(cabid,name,funcmap...) cabid,
|
||||||
const int16_t HIGHFLASH RMFT2::rosterIdList[]={
|
const int16_t HIGHFLASH RMFT2::rosterIdList[]={
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
0};
|
INT16_MAX};
|
||||||
|
|
||||||
// Pass 7: Roster names getter
|
// Pass 7: Roster names getter
|
||||||
#include "EXRAIL2MacroReset.h"
|
#include "EXRAIL2MacroReset.h"
|
||||||
|
@ -221,7 +223,7 @@ const FSH * RMFT2::getRosterFunctions(int16_t id) {
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
return F("");
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass 8 Signal definitions
|
// Pass 8 Signal definitions
|
||||||
|
@ -320,13 +322,16 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = {
|
||||||
#define ONCLOCKMINS(mins) ONCLOCKTIME(25,mins)
|
#define ONCLOCKMINS(mins) ONCLOCKTIME(25,mins)
|
||||||
#define ONDEACTIVATE(addr,subaddr) OPCODE_ONDEACTIVATE,V(addr<<2|subaddr),
|
#define ONDEACTIVATE(addr,subaddr) OPCODE_ONDEACTIVATE,V(addr<<2|subaddr),
|
||||||
#define ONDEACTIVATEL(linear) OPCODE_ONDEACTIVATE,V(linear+3),
|
#define ONDEACTIVATEL(linear) OPCODE_ONDEACTIVATE,V(linear+3),
|
||||||
|
#define ONOVERLOAD(track_id) OPCODE_ONOVERLOAD,V(track_id),
|
||||||
#define ONGREEN(signal_id) OPCODE_ONGREEN,V(signal_id),
|
#define ONGREEN(signal_id) OPCODE_ONGREEN,V(signal_id),
|
||||||
#define ONRED(signal_id) OPCODE_ONRED,V(signal_id),
|
#define ONRED(signal_id) OPCODE_ONRED,V(signal_id),
|
||||||
#define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id),
|
#define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id),
|
||||||
#define ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id),
|
#define ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id),
|
||||||
#define PAUSE OPCODE_PAUSE,0,0,
|
#define PAUSE OPCODE_PAUSE,0,0,
|
||||||
#define PIN_TURNOUT(id,pin,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(pin),
|
#define PIN_TURNOUT(id,pin,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(pin),
|
||||||
|
#ifndef DISABLE_PROG
|
||||||
#define POM(cv,value) OPCODE_POM,V(cv),OPCODE_PAD,V(value),
|
#define POM(cv,value) OPCODE_POM,V(cv),OPCODE_PAD,V(value),
|
||||||
|
#endif
|
||||||
#define POWEROFF OPCODE_POWEROFF,0,0,
|
#define POWEROFF OPCODE_POWEROFF,0,0,
|
||||||
#define POWERON OPCODE_POWERON,0,0,
|
#define POWERON OPCODE_POWERON,0,0,
|
||||||
#define PRINT(msg) OPCODE_PRINT,V(__COUNTER__ - StringMacroTracker2),
|
#define PRINT(msg) OPCODE_PRINT,V(__COUNTER__ - StringMacroTracker2),
|
||||||
|
@ -368,6 +373,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = {
|
||||||
#define UNLATCH(sensor_id) OPCODE_UNLATCH,V(sensor_id),
|
#define UNLATCH(sensor_id) OPCODE_UNLATCH,V(sensor_id),
|
||||||
#define VIRTUAL_SIGNAL(id)
|
#define VIRTUAL_SIGNAL(id)
|
||||||
#define VIRTUAL_TURNOUT(id,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(0),
|
#define VIRTUAL_TURNOUT(id,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(0),
|
||||||
|
#define WITHROTTLE(msg) PRINT(msg)
|
||||||
#define WAITFOR(pin) OPCODE_WAITFOR,V(pin),
|
#define WAITFOR(pin) OPCODE_WAITFOR,V(pin),
|
||||||
#define XFOFF(cab,func) OPCODE_XFOFF,V(cab),OPCODE_PAD,V(func),
|
#define XFOFF(cab,func) OPCODE_XFOFF,V(cab),OPCODE_PAD,V(func),
|
||||||
#define XFON(cab,func) OPCODE_XFON,V(cab),OPCODE_PAD,V(func),
|
#define XFON(cab,func) OPCODE_XFON,V(cab),OPCODE_PAD,V(func),
|
||||||
|
|
362
MotorDriver.cpp
362
MotorDriver.cpp
|
@ -4,6 +4,7 @@
|
||||||
* © 2021 Fred Decker
|
* © 2021 Fred Decker
|
||||||
* © 2020-2023 Harald Barth
|
* © 2020-2023 Harald Barth
|
||||||
* © 2020-2021 Chris Harlow
|
* © 2020-2021 Chris Harlow
|
||||||
|
* © 2023 Colin Murdoch
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of CommandStation-EX
|
* This file is part of CommandStation-EX
|
||||||
|
@ -26,16 +27,18 @@
|
||||||
#include "DCCWaveform.h"
|
#include "DCCWaveform.h"
|
||||||
#include "DCCTimer.h"
|
#include "DCCTimer.h"
|
||||||
#include "DIAG.h"
|
#include "DIAG.h"
|
||||||
|
#include "EXRAIL2.h"
|
||||||
|
|
||||||
bool MotorDriver::commonFaultPin=false;
|
unsigned long MotorDriver::globalOverloadStart = 0;
|
||||||
|
|
||||||
volatile portreg_t shadowPORTA;
|
volatile portreg_t shadowPORTA;
|
||||||
volatile portreg_t shadowPORTB;
|
volatile portreg_t shadowPORTB;
|
||||||
volatile portreg_t shadowPORTC;
|
volatile portreg_t shadowPORTC;
|
||||||
|
|
||||||
MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin,
|
MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, int16_t brake_pin,
|
||||||
byte current_pin, float sense_factor, unsigned int trip_milliamps, int8_t fault_pin) {
|
byte current_pin, float sense_factor, unsigned int trip_milliamps, int16_t fault_pin) {
|
||||||
powerPin=power_pin;
|
const FSH * warnString = F("** WARNING **");
|
||||||
|
|
||||||
invertPower=power_pin < 0;
|
invertPower=power_pin < 0;
|
||||||
if (invertPower) {
|
if (invertPower) {
|
||||||
powerPin = 0-power_pin;
|
powerPin = 0-power_pin;
|
||||||
|
@ -91,35 +94,54 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i
|
||||||
}
|
}
|
||||||
else dualSignal=false;
|
else dualSignal=false;
|
||||||
|
|
||||||
brakePin=brake_pin;
|
|
||||||
if (brake_pin!=UNUSED_PIN){
|
if (brake_pin!=UNUSED_PIN){
|
||||||
invertBrake=brake_pin < 0;
|
invertBrake=brake_pin < 0;
|
||||||
brakePin=invertBrake ? 0-brake_pin : brake_pin;
|
if (invertBrake)
|
||||||
|
brake_pin = 0-brake_pin;
|
||||||
|
if (brake_pin > MAX_PIN)
|
||||||
|
DIAG(F("%S Brake pin %d > %d"), warnString, brake_pin, MAX_PIN);
|
||||||
|
brakePin=(byte)brake_pin;
|
||||||
getFastPin(F("BRAKE"),brakePin,fastBrakePin);
|
getFastPin(F("BRAKE"),brakePin,fastBrakePin);
|
||||||
// if brake is used for railcom cutout we need to do PORTX register trick here as well
|
// if brake is used for railcom cutout we need to do PORTX register trick here as well
|
||||||
pinMode(brakePin, OUTPUT);
|
pinMode(brakePin, OUTPUT);
|
||||||
setBrake(true); // start with brake on in case we hace DC stuff going on
|
setBrake(true); // start with brake on in case we hace DC stuff going on
|
||||||
|
} else {
|
||||||
|
brakePin=UNUSED_PIN;
|
||||||
}
|
}
|
||||||
else brakePin=UNUSED_PIN;
|
|
||||||
|
|
||||||
currentPin=current_pin;
|
currentPin=current_pin;
|
||||||
if (currentPin!=UNUSED_PIN) ADCee::init(currentPin);
|
if (currentPin!=UNUSED_PIN) {
|
||||||
|
int ret = ADCee::init(currentPin);
|
||||||
|
if (ret < -1010) { // XXX give value a name later
|
||||||
|
DIAG(F("ADCee::init error %d, disable current pin %d"), ret, currentPin);
|
||||||
|
currentPin = UNUSED_PIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
senseOffset=0; // value can not be obtained until waveform is activated
|
senseOffset=0; // value can not be obtained until waveform is activated
|
||||||
|
|
||||||
faultPin=fault_pin;
|
if (fault_pin != UNUSED_PIN) {
|
||||||
if (faultPin != UNUSED_PIN) {
|
|
||||||
invertFault=fault_pin < 0;
|
invertFault=fault_pin < 0;
|
||||||
faultPin=invertFault ? 0-fault_pin : fault_pin;
|
if (invertFault)
|
||||||
|
fault_pin = 0-fault_pin;
|
||||||
|
if (fault_pin > MAX_PIN)
|
||||||
|
DIAG(F("%S Fault pin %d > %d"), warnString, fault_pin, MAX_PIN);
|
||||||
|
faultPin=(byte)fault_pin;
|
||||||
DIAG(F("Fault pin = %d invert %d"), faultPin, invertFault);
|
DIAG(F("Fault pin = %d invert %d"), faultPin, invertFault);
|
||||||
getFastPin(F("FAULT"),faultPin, 1 /*input*/, fastFaultPin);
|
getFastPin(F("FAULT"),faultPin, 1 /*input*/, fastFaultPin);
|
||||||
pinMode(faultPin, INPUT);
|
pinMode(faultPin, INPUT);
|
||||||
|
} else {
|
||||||
|
faultPin=UNUSED_PIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This conversion performed at compile time so the remainder of the code never needs
|
// This conversion performed at compile time so the remainder of the code never needs
|
||||||
// float calculations or libraray code.
|
// float calculations or libraray code.
|
||||||
senseFactorInternal=sense_factor * senseScale;
|
senseFactorInternal=sense_factor * senseScale;
|
||||||
tripMilliamps=trip_milliamps;
|
tripMilliamps=trip_milliamps;
|
||||||
rawCurrentTripValue=mA2raw(trip_milliamps);
|
#ifdef MAX_CURRENT
|
||||||
|
if (MAX_CURRENT > 0 && MAX_CURRENT < tripMilliamps)
|
||||||
|
tripMilliamps = MAX_CURRENT;
|
||||||
|
#endif
|
||||||
|
rawCurrentTripValue=mA2raw(tripMilliamps);
|
||||||
|
|
||||||
if (rawCurrentTripValue + senseOffset > ADCee::ADCmax()) {
|
if (rawCurrentTripValue + senseOffset > ADCee::ADCmax()) {
|
||||||
// This would mean that the values obtained from the ADC never
|
// This would mean that the values obtained from the ADC never
|
||||||
|
@ -134,20 +156,16 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentPin==UNUSED_PIN)
|
if (currentPin==UNUSED_PIN)
|
||||||
DIAG(F("** WARNING ** No current or short detection"));
|
DIAG(F("%S No current or short detection"), warnString);
|
||||||
else {
|
else {
|
||||||
DIAG(F("Track %c, TripValue=%d"), trackLetter, rawCurrentTripValue);
|
DIAG(F("Pin %d Max %dmA (%d)"), currentPin, raw2mA(rawCurrentTripValue), rawCurrentTripValue);
|
||||||
|
|
||||||
// self testing diagnostic for the non-float converters... may be removed when happy
|
// self testing diagnostic for the non-float converters... may be removed when happy
|
||||||
// DIAG(F("senseFactorInternal=%d raw2mA(1000)=%d mA2Raw(1000)=%d"),
|
// DIAG(F("senseFactorInternal=%d raw2mA(1000)=%d mA2Raw(1000)=%d"),
|
||||||
// senseFactorInternal, raw2mA(1000),mA2raw(1000));
|
// senseFactorInternal, raw2mA(1000),mA2raw(1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare values for current detection
|
|
||||||
sampleDelay = 0;
|
|
||||||
lastSampleTaken = millis();
|
|
||||||
progTripValue = mA2raw(TRIP_CURRENT_PROG);
|
progTripValue = mA2raw(TRIP_CURRENT_PROG);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MotorDriver::isPWMCapable() {
|
bool MotorDriver::isPWMCapable() {
|
||||||
|
@ -156,7 +174,12 @@ bool MotorDriver::isPWMCapable() {
|
||||||
|
|
||||||
|
|
||||||
void MotorDriver::setPower(POWERMODE mode) {
|
void MotorDriver::setPower(POWERMODE mode) {
|
||||||
bool on=mode==POWERMODE::ON;
|
if (powerMode == mode) return;
|
||||||
|
//DIAG(F("Track %c POWERMODE=%d"), trackLetter, (int)mode);
|
||||||
|
lastPowerChange[(int)mode] = micros();
|
||||||
|
if (mode == POWERMODE::OVERLOAD)
|
||||||
|
globalOverloadStart = lastPowerChange[(int)mode];
|
||||||
|
bool on=(mode==POWERMODE::ON || mode ==POWERMODE::ALERT);
|
||||||
if (on) {
|
if (on) {
|
||||||
// when switching a track On, we need to check the crrentOffset with the pin OFF
|
// when switching a track On, we need to check the crrentOffset with the pin OFF
|
||||||
if (powerMode==POWERMODE::OFF && currentPin!=UNUSED_PIN) {
|
if (powerMode==POWERMODE::OFF && currentPin!=UNUSED_PIN) {
|
||||||
|
@ -196,8 +219,8 @@ bool MotorDriver::canMeasureCurrent() {
|
||||||
return currentPin!=UNUSED_PIN;
|
return currentPin!=UNUSED_PIN;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Return the current reading as pin reading 0 to 1023. If the fault
|
* Return the current reading as pin reading 0 to max resolution (1024 or 4096).
|
||||||
* pin is activated return a negative current to show active fault pin.
|
* If the fault pin is activated return a negative current to show active fault pin.
|
||||||
* As there is no -0, cheat a little and return -1 in that case.
|
* As there is no -0, cheat a little and return -1 in that case.
|
||||||
*
|
*
|
||||||
* senseOffset handles the case where a shield returns values above or below
|
* senseOffset handles the case where a shield returns values above or below
|
||||||
|
@ -214,14 +237,12 @@ int MotorDriver::getCurrentRaw(bool fromISR) {
|
||||||
// if (fromISR == false) DIAG(F("%c: %d"), trackLetter, current);
|
// if (fromISR == false) DIAG(F("%c: %d"), trackLetter, current);
|
||||||
current = current-senseOffset; // adjust with offset
|
current = current-senseOffset; // adjust with offset
|
||||||
if (current<0) current=0-current;
|
if (current<0) current=0-current;
|
||||||
if ((faultPin != UNUSED_PIN) && powerMode==POWERMODE::ON) {
|
// current >= 0 here, we use negative current as fault pin flag
|
||||||
if (invertFault && isLOW(fastFaultPin))
|
if ((faultPin != UNUSED_PIN) && powerPin) {
|
||||||
return (current == 0 ? -1 : -current);
|
if (invertFault ? isHIGH(fastFaultPin) : isLOW(fastFaultPin))
|
||||||
if (!invertFault && !isLOW(fastFaultPin))
|
|
||||||
return (current == 0 ? -1 : -current);
|
return (current == 0 ? -1 : -current);
|
||||||
}
|
}
|
||||||
return current;
|
return current;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ANALOG_READ_INTERRUPT
|
#ifdef ANALOG_READ_INTERRUPT
|
||||||
|
@ -259,6 +280,7 @@ void MotorDriver::startCurrentFromHW() {
|
||||||
#endif //ANALOG_READ_INTERRUPT
|
#endif //ANALOG_READ_INTERRUPT
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
#ifdef VARIABLE_TONES
|
||||||
uint16_t taurustones[28] = { 165, 175, 196, 220,
|
uint16_t taurustones[28] = { 165, 175, 196, 220,
|
||||||
247, 262, 294, 330,
|
247, 262, 294, 330,
|
||||||
349, 392, 440, 494,
|
349, 392, 440, 494,
|
||||||
|
@ -267,16 +289,43 @@ uint16_t taurustones[28] = { 165, 175, 196, 220,
|
||||||
330, 284, 262, 247,
|
330, 284, 262, 247,
|
||||||
220, 196, 175, 165 };
|
220, 196, 175, 165 };
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
void MotorDriver::setDCSignal(byte speedcode) {
|
void MotorDriver::setDCSignal(byte speedcode) {
|
||||||
if (brakePin == UNUSED_PIN)
|
if (brakePin == UNUSED_PIN)
|
||||||
return;
|
return;
|
||||||
|
switch(brakePin) {
|
||||||
#if defined(ARDUINO_AVR_UNO)
|
#if defined(ARDUINO_AVR_UNO)
|
||||||
TCCR2B = (TCCR2B & B11111000) | B00000110; // set divisor on timer 2 to result in (approx) 122.55Hz
|
// Not worth doin something here as:
|
||||||
|
// If we are on pin 9 or 10 we are on Timer1 and we can not touch Timer1 as that is our DCC source.
|
||||||
|
// If we are on pin 5 or 6 we are on Timer 0 ad we can not touch Timer0 as that is millis() etc.
|
||||||
|
// We are most likely not on pin 3 or 11 as no known motor shield has that as brake.
|
||||||
#endif
|
#endif
|
||||||
#if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560)
|
#if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560)
|
||||||
TCCR2B = (TCCR2B & B11111000) | B00000110; // set divisor on timer 2 to result in (approx) 122.55Hz
|
case 9:
|
||||||
TCCR4B = (TCCR4B & B11111000) | B00000100; // same for timer 4 but maxcount and thus divisor differs
|
case 10:
|
||||||
|
// Timer2 (is differnet)
|
||||||
|
TCCR2A = (TCCR2A & B11111100) | B00000001; // set WGM1=0 and WGM0=1 phase correct PWM
|
||||||
|
TCCR2B = (TCCR2B & B11110000) | B00000110; // set WGM2=0 ; set divisor on timer 2 to 1/256 for 122.55Hz
|
||||||
|
//DIAG(F("2 A=%x B=%x"), TCCR2A, TCCR2B);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
case 7:
|
||||||
|
case 8:
|
||||||
|
// Timer4
|
||||||
|
TCCR4A = (TCCR4A & B11111100) | B00000001; // set WGM0=1 and WGM1=0 for normal PWM 8-bit
|
||||||
|
TCCR4B = (TCCR4B & B11100000) | B00000100; // set WGM2=0 and WGM3=0 for normal PWM 8 bit and div 1/256 for 122.55Hz
|
||||||
|
break;
|
||||||
|
case 46:
|
||||||
|
case 45:
|
||||||
|
case 44:
|
||||||
|
// Timer5
|
||||||
|
TCCR5A = (TCCR5A & B11111100) | B00000001; // set WGM0=1 and WGM1=0 for normal PWM 8-bit
|
||||||
|
TCCR5B = (TCCR5B & B11100000) | B00000100; // set WGM2=0 and WGM3=0 for normal PWM 8 bit and div 1/256 for 122.55Hz
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
// spedcoode is a dcc speed & direction
|
// spedcoode is a dcc speed & direction
|
||||||
byte tSpeed=speedcode & 0x7F; // DCC Speed with 0,1 stop and speed steps 2 to 127
|
byte tSpeed=speedcode & 0x7F; // DCC Speed with 0,1 stop and speed steps 2 to 127
|
||||||
byte tDir=speedcode & 0x80;
|
byte tDir=speedcode & 0x80;
|
||||||
|
@ -284,11 +333,13 @@ void MotorDriver::setDCSignal(byte speedcode) {
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
{
|
{
|
||||||
int f = 131;
|
int f = 131;
|
||||||
|
#ifdef VARIABLE_TONES
|
||||||
if (tSpeed > 2) {
|
if (tSpeed > 2) {
|
||||||
if (tSpeed <= 58) {
|
if (tSpeed <= 58) {
|
||||||
f = taurustones[ (tSpeed-2)/2 ] ;
|
f = taurustones[ (tSpeed-2)/2 ] ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
DCCTimer::DCCEXanalogWriteFrequency(brakePin, f); // set DC PWM frequency to 100Hz XXX May move to setup
|
DCCTimer::DCCEXanalogWriteFrequency(brakePin, f); // set DC PWM frequency to 100Hz XXX May move to setup
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -327,7 +378,60 @@ void MotorDriver::setDCSignal(byte speedcode) {
|
||||||
interrupts();
|
interrupts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void MotorDriver::throttleInrush(bool on) {
|
||||||
|
if (brakePin == UNUSED_PIN)
|
||||||
|
return;
|
||||||
|
if ( !(trackMode & (TRACK_MODE_MAIN | TRACK_MODE_PROG | TRACK_MODE_EXT)))
|
||||||
|
return;
|
||||||
|
byte duty = on ? 208 : 0;
|
||||||
|
if (invertBrake)
|
||||||
|
duty = 255-duty;
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
if(on) {
|
||||||
|
DCCTimer::DCCEXanalogWrite(brakePin,duty);
|
||||||
|
DCCTimer::DCCEXanalogWriteFrequency(brakePin, 62500);
|
||||||
|
} else {
|
||||||
|
ledcDetachPin(brakePin);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if(on){
|
||||||
|
switch(brakePin) {
|
||||||
|
#if defined(ARDUINO_AVR_UNO)
|
||||||
|
// Not worth doin something here as:
|
||||||
|
// If we are on pin 9 or 10 we are on Timer1 and we can not touch Timer1 as that is our DCC source.
|
||||||
|
// If we are on pin 5 or 6 we are on Timer 0 ad we can not touch Timer0 as that is millis() etc.
|
||||||
|
// We are most likely not on pin 3 or 11 as no known motor shield has that as brake.
|
||||||
|
#endif
|
||||||
|
#if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560)
|
||||||
|
case 9:
|
||||||
|
case 10:
|
||||||
|
// Timer2 (is different)
|
||||||
|
TCCR2A = (TCCR2A & B11111100) | B00000011; // set WGM0=1 and WGM1=1 for fast PWM
|
||||||
|
TCCR2B = (TCCR2B & B11110000) | B00000001; // set WGM2=0 and prescaler div=1 (max)
|
||||||
|
DIAG(F("2 A=%x B=%x"), TCCR2A, TCCR2B);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
case 7:
|
||||||
|
case 8:
|
||||||
|
// Timer4
|
||||||
|
TCCR4A = (TCCR4A & B11111100) | B00000001; // set WGM0=1 and WGM1=0 for fast PWM 8-bit
|
||||||
|
TCCR4B = (TCCR4B & B11100000) | B00001001; // set WGM2=1 and WGM3=0 for fast PWM 8 bit and div=1 (max)
|
||||||
|
break;
|
||||||
|
case 46:
|
||||||
|
case 45:
|
||||||
|
case 44:
|
||||||
|
// Timer5
|
||||||
|
TCCR5A = (TCCR5A & B11111100) | B00000001; // set WGM0=1 and WGM1=0 for fast PWM 8-bit
|
||||||
|
TCCR5B = (TCCR5B & B11100000) | B00001001; // set WGM2=1 and WGM3=0 for fast PWM 8 bit and div=1 (max)
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
analogWrite(brakePin,duty);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
unsigned int MotorDriver::raw2mA( int raw) {
|
unsigned int MotorDriver::raw2mA( int raw) {
|
||||||
//DIAG(F("%d = %d * %d / %d"), (int32_t)raw * senseFactorInternal / senseScale, raw, senseFactorInternal, senseScale);
|
//DIAG(F("%d = %d * %d / %d"), (int32_t)raw * senseFactorInternal / senseScale, raw, senseFactorInternal, senseScale);
|
||||||
return (int32_t)raw * senseFactorInternal / senseScale;
|
return (int32_t)raw * senseFactorInternal / senseScale;
|
||||||
|
@ -356,64 +460,174 @@ void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & res
|
||||||
// DIAG(F(" port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x"),port, result.inout,input,result.maskHIGH);
|
// DIAG(F(" port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x"),port, result.inout,input,result.maskHIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) {
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
if (millis() - lastSampleTaken < sampleDelay) return;
|
// checkPowerOverload(useProgLimit, trackno)
|
||||||
lastSampleTaken = millis();
|
// bool useProgLimit: Trackmanager knows if this track is in prog mode or in main mode
|
||||||
int tripValue= useProgLimit?progTripValue:getRawCurrentTripValue();
|
// byte trackno: trackmanager knows it's number (could be skipped?)
|
||||||
|
//
|
||||||
|
// Short ciruit handling strategy:
|
||||||
|
//
|
||||||
|
// There are the following power states: ON ALERT OVERLOAD OFF
|
||||||
|
// OFF state is only changed to/from manually. Power is on
|
||||||
|
// during ON and ALERT. Power is off during OVERLOAD and OFF.
|
||||||
|
// The overload mechanism changes between the other states like
|
||||||
|
//
|
||||||
|
// ON -1-> ALERT -2-> OVERLOAD -3-> ALERT -4-> ON
|
||||||
|
// or
|
||||||
|
// ON -1-> ALERT -4-> ON
|
||||||
|
//
|
||||||
|
// Times are in class MotorDriver (MotorDriver.h).
|
||||||
|
//
|
||||||
|
// 1. ON to ALERT:
|
||||||
|
// Transition on fault pin condition or current overload
|
||||||
|
//
|
||||||
|
// 2. ALERT to OVERLOAD:
|
||||||
|
// Transition happens if different timeouts have elapsed.
|
||||||
|
// If only the fault pin is active, timeout is
|
||||||
|
// POWER_SAMPLE_IGNORE_FAULT_LOW (100ms)
|
||||||
|
// If only overcurrent is detected, timeout is
|
||||||
|
// POWER_SAMPLE_IGNORE_CURRENT (100ms)
|
||||||
|
// If fault pin and overcurrent are active, timeout is
|
||||||
|
// POWER_SAMPLE_IGNORE_FAULT_HIGH (5ms)
|
||||||
|
// Transition to OVERLOAD turns off power to the affected
|
||||||
|
// output (unless fault pins are shared)
|
||||||
|
// If the transition conditions are not fullfilled,
|
||||||
|
// transition according to 4 is tested.
|
||||||
|
//
|
||||||
|
// 3. OVERLOAD to ALERT
|
||||||
|
// Transiton happens when timeout has elapsed, timeout
|
||||||
|
// is named power_sample_overload_wait. It is started
|
||||||
|
// at POWER_SAMPLE_OVERLOAD_WAIT (40ms) at first entry
|
||||||
|
// to OVERLOAD and then increased by a factor of 2
|
||||||
|
// at further entries to the OVERLOAD condition. This
|
||||||
|
// happens until POWER_SAMPLE_RETRY_MAX (10sec) is reached.
|
||||||
|
// power_sample_overload_wait is reset by a poweroff or
|
||||||
|
// a POWER_SAMPLE_ALL_GOOD (5sec) period during ON.
|
||||||
|
// After timeout power is turned on again and state
|
||||||
|
// goes back to ALERT.
|
||||||
|
//
|
||||||
|
// 4. ALERT to ON
|
||||||
|
// Transition happens by watching the current and fault pin
|
||||||
|
// samples during POWER_SAMPLE_ALERT_GOOD (20ms) time. If
|
||||||
|
// values have been good during that time, transition is
|
||||||
|
// made back to ON. Note that even if state is back to ON,
|
||||||
|
// the power_sample_overload_wait time is first reset
|
||||||
|
// later (see above).
|
||||||
|
//
|
||||||
|
// The time keeping is handled by timestamps lastPowerChange[]
|
||||||
|
// which are set by each power change and by lastBadSample which
|
||||||
|
// keeps track if conditions during ALERT have been good enough
|
||||||
|
// to go back to ON. The time differences are calculated by
|
||||||
|
// microsSinceLastPowerChange().
|
||||||
|
//
|
||||||
|
|
||||||
|
void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) {
|
||||||
|
|
||||||
// Trackname for diag messages later
|
|
||||||
switch (powerMode) {
|
switch (powerMode) {
|
||||||
case POWERMODE::OFF:
|
|
||||||
sampleDelay = POWER_SAMPLE_OFF_WAIT;
|
case POWERMODE::OFF: {
|
||||||
|
lastPowerMode = POWERMODE::OFF;
|
||||||
|
power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT;
|
||||||
break;
|
break;
|
||||||
case POWERMODE::ON:
|
|
||||||
// Check current
|
|
||||||
lastCurrent=getCurrentRaw();
|
|
||||||
if (lastCurrent < 0) {
|
|
||||||
// We have a fault pin condition to take care of
|
|
||||||
lastCurrent = -lastCurrent;
|
|
||||||
setPower(POWERMODE::OVERLOAD); // Turn off, decide later how fast to turn on again
|
|
||||||
if (commonFaultPin) {
|
|
||||||
if (lastCurrent < tripValue) {
|
|
||||||
setPower(POWERMODE::ON); // maybe other track
|
|
||||||
}
|
}
|
||||||
// Write this after the fact as we want to turn on as fast as possible
|
|
||||||
// because we don't know which output actually triggered the fault pin
|
case POWERMODE::ON: {
|
||||||
DIAG(F("COMMON FAULT PIN ACTIVE: POWERTOGGLE TRACK %c"), trackno + 'A');
|
lastPowerMode = POWERMODE::ON;
|
||||||
|
bool cF = checkFault();
|
||||||
|
bool cC = checkCurrent(useProgLimit);
|
||||||
|
if(cF || cC ) {
|
||||||
|
if (cC) {
|
||||||
|
unsigned int mA=raw2mA(lastCurrent);
|
||||||
|
DIAG(F("TRACK %c ALERT %s %dmA"), trackno + 'A',
|
||||||
|
cF ? "FAULT" : "",
|
||||||
|
mA);
|
||||||
} else {
|
} else {
|
||||||
DIAG(F("TRACK %c FAULT PIN ACTIVE - OVERLOAD"), trackno + 'A');
|
DIAG(F("TRACK %c ALERT FAULT"), trackno + 'A');
|
||||||
if (lastCurrent < tripValue) {
|
|
||||||
lastCurrent = tripValue; // exaggerate
|
|
||||||
}
|
}
|
||||||
|
setPower(POWERMODE::ALERT);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
// all well
|
||||||
|
if (microsSinceLastPowerChange(POWERMODE::ON) > POWER_SAMPLE_ALL_GOOD) {
|
||||||
|
power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT;
|
||||||
}
|
}
|
||||||
if (lastCurrent < tripValue) {
|
break;
|
||||||
sampleDelay = POWER_SAMPLE_ON_WAIT;
|
}
|
||||||
if(power_good_counter<100)
|
|
||||||
power_good_counter++;
|
case POWERMODE::ALERT: {
|
||||||
else
|
// set local flags that handle how much is output to diag (do not output duplicates)
|
||||||
if (power_sample_overload_wait>POWER_SAMPLE_OVERLOAD_WAIT) power_sample_overload_wait=POWER_SAMPLE_OVERLOAD_WAIT;
|
bool notFromOverload = (lastPowerMode != POWERMODE::OVERLOAD);
|
||||||
} else {
|
bool powerModeChange = (powerMode != lastPowerMode);
|
||||||
|
unsigned long now = micros();
|
||||||
|
if (powerModeChange)
|
||||||
|
lastBadSample = now;
|
||||||
|
lastPowerMode = POWERMODE::ALERT;
|
||||||
|
// check how long we have been in this state
|
||||||
|
unsigned long mslpc = microsSinceLastPowerChange(POWERMODE::ALERT);
|
||||||
|
if(checkFault()) {
|
||||||
|
throttleInrush(true);
|
||||||
|
lastBadSample = now;
|
||||||
|
unsigned long timeout = checkCurrent(useProgLimit) ? POWER_SAMPLE_IGNORE_FAULT_HIGH : POWER_SAMPLE_IGNORE_FAULT_LOW;
|
||||||
|
if ( mslpc < timeout) {
|
||||||
|
if (powerModeChange)
|
||||||
|
DIAG(F("TRACK %c FAULT PIN (%M ignore)"), trackno + 'A', timeout);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DIAG(F("TRACK %c FAULT PIN detected after %4M. Pause %4M)"), trackno + 'A', mslpc, power_sample_overload_wait);
|
||||||
|
throttleInrush(false);
|
||||||
setPower(POWERMODE::OVERLOAD);
|
setPower(POWERMODE::OVERLOAD);
|
||||||
|
//CHM
|
||||||
|
RMFT2::powerEvent(trackno + 'A', true); // Tell EXRAIL we have an overload
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (checkCurrent(useProgLimit)) {
|
||||||
|
lastBadSample = now;
|
||||||
|
if (mslpc < POWER_SAMPLE_IGNORE_CURRENT) {
|
||||||
|
if (powerModeChange) {
|
||||||
|
unsigned int mA=raw2mA(lastCurrent);
|
||||||
|
DIAG(F("TRACK %c CURRENT (%M ignore) %dmA"), trackno + 'A', POWER_SAMPLE_IGNORE_CURRENT, mA);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
unsigned int mA=raw2mA(lastCurrent);
|
unsigned int mA=raw2mA(lastCurrent);
|
||||||
unsigned int maxmA=raw2mA(tripValue);
|
unsigned int maxmA=raw2mA(tripValue);
|
||||||
power_good_counter=0;
|
DIAG(F("TRACK %c POWER OVERLOAD %4dmA (max %4dmA) detected after %4M. Pause %4M"),
|
||||||
sampleDelay = power_sample_overload_wait;
|
trackno + 'A', mA, maxmA, mslpc, power_sample_overload_wait);
|
||||||
DIAG(F("TRACK %c POWER OVERLOAD %dmA (limit %dmA) shutdown for %dms"), trackno + 'A', mA, maxmA, sampleDelay);
|
throttleInrush(false);
|
||||||
if (power_sample_overload_wait >= 10000)
|
setPower(POWERMODE::OVERLOAD);
|
||||||
power_sample_overload_wait = 10000;
|
//CHM
|
||||||
else
|
RMFT2::powerEvent(trackno + 'A', true); // Tell EXRAIL we have an overload
|
||||||
power_sample_overload_wait *= 2;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case POWERMODE::OVERLOAD:
|
}
|
||||||
// Try setting it back on after the OVERLOAD_WAIT
|
// all well
|
||||||
|
unsigned long goodtime = micros() - lastBadSample;
|
||||||
|
if (goodtime > POWER_SAMPLE_ALERT_GOOD) {
|
||||||
|
if (true || notFromOverload) { // we did a RESTORE message XXX
|
||||||
|
unsigned int mA=raw2mA(lastCurrent);
|
||||||
|
DIAG(F("TRACK %c NORMAL (after %M/%M) %dmA"), trackno + 'A', goodtime, mslpc, mA);
|
||||||
|
}
|
||||||
|
throttleInrush(false);
|
||||||
setPower(POWERMODE::ON);
|
setPower(POWERMODE::ON);
|
||||||
sampleDelay = POWER_SAMPLE_ON_WAIT;
|
}
|
||||||
// Debug code....
|
|
||||||
DIAG(F("TRACK %c POWER RESTORE (check %dms)"), trackno + 'A', sampleDelay);
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case POWERMODE::OVERLOAD: {
|
||||||
|
lastPowerMode = POWERMODE::OVERLOAD;
|
||||||
|
unsigned long mslpc = (commonFaultPin ? (micros() - globalOverloadStart) : microsSinceLastPowerChange(POWERMODE::OVERLOAD));
|
||||||
|
if (mslpc > power_sample_overload_wait) {
|
||||||
|
// adjust next wait time
|
||||||
|
power_sample_overload_wait *= 2;
|
||||||
|
if (power_sample_overload_wait > POWER_SAMPLE_RETRY_MAX)
|
||||||
|
power_sample_overload_wait = POWER_SAMPLE_RETRY_MAX;
|
||||||
|
// power on test
|
||||||
|
DIAG(F("TRACK %c POWER RESTORE (after %4M)"), trackno + 'A', mslpc);
|
||||||
|
setPower(POWERMODE::ALERT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
sampleDelay = 999; // cant get here..meaningless statement to avoid compiler warning.
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user