1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-27 01:56:14 +01:00

Add support for HBRIDGE Turnouts, allowing control of Kato turnouts that require reverse of polarity and short power application, easily configurable through 2-pin controlled Motor H-Bridge.

This commit is contained in:
Sergei Kotlyachkov 2023-11-06 21:54:25 -05:00
parent 7a9dee9bed
commit dba5e88d04
7 changed files with 149 additions and 3 deletions

View File

@ -162,6 +162,7 @@ const int16_t HASH_KEYWORD_T='T';
const int16_t HASH_KEYWORD_X='X'; const int16_t HASH_KEYWORD_X='X';
const int16_t HASH_KEYWORD_LCN = 15137; const int16_t HASH_KEYWORD_LCN = 15137;
const int16_t HASH_KEYWORD_HAL = 10853; const int16_t HASH_KEYWORD_HAL = 10853;
const int16_t HASH_KEYWORD_HBRIDGE=-20585;
const int16_t HASH_KEYWORD_SHOW = -21309; const int16_t HASH_KEYWORD_SHOW = -21309;
const int16_t HASH_KEYWORD_ANIN = -10424; const int16_t HASH_KEYWORD_ANIN = -10424;
const int16_t HASH_KEYWORD_ANOUT = -26399; const int16_t HASH_KEYWORD_ANOUT = -26399;
@ -917,6 +918,9 @@ bool DCCEXParser::parseT(Print *stream, int16_t params, int16_t p[])
if (params == 3 && p[1] == HASH_KEYWORD_VPIN) { // <T id VPIN n> if (params == 3 && p[1] == HASH_KEYWORD_VPIN) { // <T id VPIN n>
if (!VpinTurnout::create(p[0], p[2])) return false; if (!VpinTurnout::create(p[0], p[2])) return false;
} else } else
if (params == 5 && p[1] == HASH_KEYWORD_HBRIDGE) { // <T id HBRIDGE pin1 pin2 delay>
if (!HBridgeTurnout::create(p[0], p[2], p[3], p[4])) return false;
} else
if (params >= 3 && p[1] == HASH_KEYWORD_DCC) { if (params >= 3 && p[1] == HASH_KEYWORD_DCC) {
// <T id DCC addr subadd> 0<=addr<=511, 0<=subadd<=3 (like <a> command).<T> // <T id DCC addr subadd> 0<=addr<=511, 0<=subadd<=3 (like <a> command).<T>
if (params==4 && p[2]>=0 && p[2]<512 && p[3]>=0 && p[3]<4) { // <T id DCC n m> if (params==4 && p[2]>=0 && p[2]<512 && p[3]>=0 && p[3]<4) { // <T id DCC n m>

View File

@ -239,6 +239,15 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
break; break;
} }
case OPCODE_HBRIDGETURNOUT: {
VPIN id=operand;
VPIN pin1=getOperand(progCounter, 1);
VPIN pin2=getOperand(progCounter, 2);
uint16_t delay=getOperand(progCounter, 3);
setTurnoutHiddenState(HBridgeTurnout::create(id,pin1, pin2, delay));
break;
}
case OPCODE_AUTOSTART: case OPCODE_AUTOSTART:
// automatically create a task from here at startup. // automatically create a task from here at startup.
// Removed if (progCounter>0) check 4.2.31 because // Removed if (progCounter>0) check 4.2.31 because

View File

@ -51,7 +51,8 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
OPCODE_POM, 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, OPCODE_HBRIDGETURNOUT,
OPCODE_PRINT,OPCODE_DCCACTIVATE, OPCODE_PRINT,OPCODE_DCCACTIVATE,
OPCODE_ONACTIVATE,OPCODE_ONDEACTIVATE, OPCODE_ONACTIVATE,OPCODE_ONDEACTIVATE,
OPCODE_ROSTER,OPCODE_KILLALL, OPCODE_ROSTER,OPCODE_KILLALL,

View File

@ -62,6 +62,7 @@
#undef FWD #undef FWD
#undef GREEN #undef GREEN
#undef HAL #undef HAL
#undef HBRIDGE_TURNOUT
#undef IF #undef IF
#undef IFAMBER #undef IFAMBER
#undef IFCLOSED #undef IFCLOSED
@ -187,6 +188,7 @@
#define FWD(speed) #define FWD(speed)
#define GREEN(signal_id) #define GREEN(signal_id)
#define HAL(haltype,params...) #define HAL(haltype,params...)
#define HBRIDGE_TURNOUT(id,pin1,pin2,dly,description...)
#define IF(sensor_id) #define IF(sensor_id)
#define IFAMBER(signal_id) #define IFAMBER(signal_id)
#define IFCLOSED(turnout_id) #define IFCLOSED(turnout_id)

View File

@ -174,6 +174,8 @@ void RMFT2::printMessage(uint16_t id) {
#define TURNOUT(id,addr,subaddr,description...) O_DESC(id,description) #define TURNOUT(id,addr,subaddr,description...) O_DESC(id,description)
#undef TURNOUTL #undef TURNOUTL
#define TURNOUTL(id,addr,description...) O_DESC(id,description) #define TURNOUTL(id,addr,description...) O_DESC(id,description)
#undef HBRIDGE_TURNOUT
#define HBRIDGE_TURNOUT(id,pin1,pin2,delay_ms,description...) O_DESC(id,description)
#undef PIN_TURNOUT #undef PIN_TURNOUT
#define PIN_TURNOUT(id,pin,description...) O_DESC(id,description) #define PIN_TURNOUT(id,pin,description...) O_DESC(id,description)
#undef SERVO_TURNOUT #undef SERVO_TURNOUT
@ -293,6 +295,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = {
#define FWD(speed) OPCODE_FWD,V(speed), #define FWD(speed) OPCODE_FWD,V(speed),
#define GREEN(signal_id) OPCODE_GREEN,V(signal_id), #define GREEN(signal_id) OPCODE_GREEN,V(signal_id),
#define HAL(haltype,params...) #define HAL(haltype,params...)
#define HBRIDGE_TURNOUT(id,pin1,pin2,delay,description...) OPCODE_HBRIDGETURNOUT,V(id),OPCODE_PAD,V(pin1),OPCODE_PAD,V(pin2),OPCODE_PAD,V(delay),
#define IF(sensor_id) OPCODE_IF,V(sensor_id), #define IF(sensor_id) OPCODE_IF,V(sensor_id),
#define IFAMBER(signal_id) OPCODE_IFAMBER,V(signal_id), #define IFAMBER(signal_id) OPCODE_IFAMBER,V(signal_id),
#define IFCLOSED(turnout_id) OPCODE_IFCLOSED,V(turnout_id), #define IFCLOSED(turnout_id) OPCODE_IFCLOSED,V(turnout_id),

View File

@ -187,6 +187,10 @@
// VPIN turnout // VPIN turnout
tt = VpinTurnout::load(&turnoutData); tt = VpinTurnout::load(&turnoutData);
break; break;
case TURNOUT_HBRIDGE:
// HBRIDGE turnout
tt = HBridgeTurnout::load(&turnoutData);
break;
default: default:
// If we find anything else, then we don't know what it is or how long it is, // If we find anything else, then we don't know what it is or how long it is,
// so we can't go any further through the EEPROM! // so we can't go any further through the EEPROM!
@ -477,6 +481,93 @@
#endif #endif
} }
/*************************************************************************************
* HBridgeTurnout - Turnout controlled through a pair of HAL pins.
* Typically connected to Motor H-Bridge. Delay is used to quickly turn on/off power.
*************************************************************************************/
// Constructor
HBridgeTurnout::HBridgeTurnout(uint16_t id, VPIN pin1, VPIN pin2, uint16_t millisDelay, bool closed) :
Turnout(id, TURNOUT_HBRIDGE, closed)
{
_hbridgeTurnoutData.pin1 = pin1;
_hbridgeTurnoutData.pin2 = pin2;
_hbridgeTurnoutData.millisDelay = millisDelay;
}
// Create function
/* static */ Turnout *HBridgeTurnout::create(uint16_t id, VPIN pin1, VPIN pin2, uint16_t millisDelay, bool closed) {
Turnout *tt = get(id);
if (tt) {
// Object already exists, check if it is usable
if (tt->isType(TURNOUT_HBRIDGE)) {
// Yes, so set parameters
HBridgeTurnout *hbt = (HBridgeTurnout *)tt;
hbt->_hbridgeTurnoutData.pin1 = pin1;
hbt->_hbridgeTurnoutData.pin2 = pin2;
hbt->_hbridgeTurnoutData.millisDelay = millisDelay;
// Don't touch the _closed parameter, retain the original value.
return tt;
} else {
// Incompatible object, delete and recreate
remove(id);
}
}
tt = (Turnout *)new HBridgeTurnout(id, pin1, pin2, millisDelay, closed);
return tt;
}
// Load a VPIN turnout definition from EEPROM. The common Turnout data has already been read at this point.
/* static */ Turnout *HBridgeTurnout::load(struct TurnoutData *turnoutData) {
#ifndef DISABLE_EEPROM
HBridgeTurnoutData hbridgeTurnoutData;
// Read class-specific data from EEPROM
EEPROM.get(EEStore::pointer(), hbridgeTurnoutData);
EEStore::advance(sizeof(hbridgeTurnoutData));
// Create new object
HBridgeTurnout *tt = new HBridgeTurnout(turnoutData->id, hbridgeTurnoutData.pin1,
hbridgeTurnoutData.pin2, hbridgeTurnoutData.millisDelay, turnoutData->closed);
return tt;
#else
(void)turnoutData;
return NULL;
#endif
}
// Report 1 for thrown, 0 for closed.
void HBridgeTurnout::print(Print *stream) {
StringFormatter::send(stream, F("<H %d HBRIDGE %d %d %d>\n"), _turnoutData.id, _hbridgeTurnoutData.pin1, _hbridgeTurnoutData.pin2,
!_turnoutData.closed);
}
void HBridgeTurnout::turnUpDown(VPIN pin) {
// HBridge turnouts require very small, prescribed time to keep pin1 or pin2 in HIGH state.
// Otherwise internal coil of the turnout will burn.
IODevice::write(pin, HIGH);
// HARD LIMIT to maximum 0.5 second to avoid burning the coil
delay(min(_hbridgeTurnoutData.millisDelay, 500));
IODevice::write(pin, LOW);
}
bool HBridgeTurnout::setClosedInternal(bool close) {
turnUpDown(close ? _hbridgeTurnoutData.pin2 : _hbridgeTurnoutData.pin1);
_turnoutData.closed = close;
return true;
}
void HBridgeTurnout::save() {
#ifndef DISAB LE_EEPROM
// Write turnout definition and current position to EEPROM
// First write common servo data, then
// write the servo-specific data
EEPROM.put(EEStore::pointer(), _turnoutData);
EEStore::advance(sizeof(_turnoutData));
EEPROM.put(EEStore::pointer(), _hbridgeTurnoutData);
EEStore::advance(sizeof(_hbridgeTurnoutData));
#endif
}
/************************************************************************************* /*************************************************************************************
* LCNTurnout - Turnout controlled by Loconet * LCNTurnout - Turnout controlled by Loconet

View File

@ -37,6 +37,7 @@ enum {
TURNOUT_SERVO = 2, TURNOUT_SERVO = 2,
TURNOUT_VPIN = 3, TURNOUT_VPIN = 3,
TURNOUT_LCN = 4, TURNOUT_LCN = 4,
TURNOUT_HBRIDGE = 5,
}; };
/************************************************************************************* /*************************************************************************************
@ -284,6 +285,41 @@ protected:
}; };
/*************************************************************************************
* HBridgeTurnout - Turnout controlled through a pair of HAL pins.
*
* Hard limited to maximum 0.5 second to avoid burning the coil
* Typical millisDelay should be within between 50 and 100
*************************************************************************************/
class HBridgeTurnout : public Turnout {
private:
// HBridgeTurnoutData contains data specific to this subclass that is
// written to EEPROM when the turnout is saved.
struct HBridgeTurnoutData {
VPIN pin1;
VPIN pin2;
uint16_t millisDelay;
} _hbridgeTurnoutData; // 6 bytes
// Constructor
HBridgeTurnout(uint16_t id, VPIN pin1, VPIN pin2, uint16_t millisDelay, bool closed);
public:
// Create function
static Turnout *create(uint16_t id, VPIN pin1, VPIN pin2, uint16_t millisDelay, bool closed=true);
// Load a HBRIDGE turnout definition from EEPROM. The common Turnout data has already been read at this point.
static Turnout *load(struct TurnoutData *turnoutData);
void print(Print *stream) override;
protected:
bool setClosedInternal(bool close) override;
void save() override;
private:
void turnUpDown(VPIN pin);
};
/************************************************************************************* /*************************************************************************************
* LCNTurnout - Turnout controlled by Loconet * LCNTurnout - Turnout controlled by Loconet