1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-06-07 16:55:23 +02:00

5.5.25 IO_Bitmap

This commit is contained in:
Asbelos 2025-05-01 09:29:05 +01:00
parent fefc4c6035
commit 54f8aaf116
10 changed files with 294 additions and 22 deletions

View File

@ -263,6 +263,8 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
case OPCODE_ATLT:
case OPCODE_IFGTE:
case OPCODE_IFLT:
case OPCODE_IFBITMAP_ALL:
case OPCODE_IFBITMAP_ANY:
case OPCODE_DRIVE: {
DIAG(F("EXRAIL analog input VPIN %u"),(VPIN)operand);
IODevice::configureAnalogIn((VPIN)operand);
@ -273,6 +275,10 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
if (compileFeatures & FEATURE_SENSOR)
new EXRAILSensor(operand,progCounter+3,true );
break;
case OPCODE_ONBITMAP:
if (compileFeatures & FEATURE_SENSOR)
new EXRAILSensor(operand,progCounter+3,true, true );
break;
case OPCODE_ONBUTTON:
if (compileFeatures & FEATURE_SENSOR)
new EXRAILSensor(operand,progCounter+3,false );
@ -763,6 +769,14 @@ void RMFT2::loop2() {
skipIf=IODevice::readAnalogue(operand)>=(int)(getOperand(1));
break;
case OPCODE_IFBITMAP_ALL: // do next operand if sensor & mask == mask
skipIf=(IODevice::readAnalogue(operand) & getOperand(1)) != getOperand(1);
break;
case OPCODE_IFBITMAP_ANY: // do next operand if sensor & mask !=0
skipIf=(IODevice::readAnalogue(operand) & getOperand(1)) == 0;
break;
case OPCODE_IFLOCO: // do if the loco is the active one
skipIf=loco!=(uint16_t)operand; // bad luck if someone enters negative loco numbers into EXRAIL
break;
@ -1095,6 +1109,26 @@ void RMFT2::loop2() {
//if (diag) DIAG(F("EXRAIL begin(%d)"),operand);
break;
case OPCODE_BITMAP_INC:
IODevice::writeAnalogue(operand,IODevice::readAnalogue(operand)+1);
break;
case OPCODE_BITMAP_DEC:
{ int newval=IODevice::readAnalogue(operand)-1;
if (newval<0) newval=0;
IODevice::writeAnalogue(operand,newval);
}
break;
case OPCODE_BITMAP_AND:
IODevice::writeAnalogue(operand,IODevice::readAnalogue(operand) & getOperand(1));
break;
case OPCODE_BITMAP_OR:
IODevice::writeAnalogue(operand,IODevice::readAnalogue(operand) | getOperand(1));
break;
case OPCODE_BITMAP_XOR:
IODevice::writeAnalogue(operand,IODevice::readAnalogue(operand) ^ getOperand(1));
break;
case OPCODE_AUTOSTART: // Handled only during begin process
case OPCODE_PAD: // Just a padding for previous opcode needing >1 operand byte.
case OPCODE_TURNOUT: // Turnout definition ignored at runtime
@ -1114,6 +1148,7 @@ void RMFT2::loop2() {
case OPCODE_ONTIME:
case OPCODE_ONBUTTON:
case OPCODE_ONSENSOR:
case OPCODE_ONBITMAP:
#ifndef IO_NO_HAL
case OPCODE_DCCTURNTABLE: // Turntable definition ignored at runtime
case OPCODE_EXTTTURNTABLE: // Turntable definition ignored at runtime

View File

@ -78,10 +78,11 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,OPCODE_TOGGLE_TURNOUT,
OPCODE_ROUTE_DISABLED,
OPCODE_STASH,OPCODE_CLEAR_STASH,OPCODE_CLEAR_ALL_STASH,OPCODE_PICKUP_STASH,
OPCODE_CLEAR_ANY_STASH,
OPCODE_ONBUTTON,OPCODE_ONSENSOR,
OPCODE_ONBUTTON,OPCODE_ONSENSOR,OPCODE_ONVP_SENSOR,
OPCODE_NEOPIXEL,
OPCODE_ONBLOCKENTER,OPCODE_ONBLOCKEXIT,
OPCODE_ESTOPALL,OPCODE_XPOM,
OPCODE_BITMAP_AND,OPCODE_BITMAP_OR,OPCODE_BITMAP_XOR,OPCODE_BITMAP_INC,OPCODE_BITMAP_DEC,OPCODE_ONBITMAP,
// OPcodes below this point are skip-nesting IF operations
// placed here so that they may be skipped as a group
// see skipIfBlock()
@ -96,6 +97,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,OPCODE_TOGGLE_TURNOUT,
OPCODE_IFLOCO,
OPCODE_IFTTPOSITION,
OPCODE_IFSTASH,
OPCODE_IFBITMAP_ALL,OPCODE_IFBITMAP_ANY,
};
// Ensure thrunge_lcd is put last as there may be more than one display,

View File

@ -94,6 +94,8 @@
#undef IFTIMEOUT
#undef IFTTPOSITION
#undef IFRE
#undef IFBITMAP_ALL
#undef IFBITMAP_ANY
#undef INVERT_DIRECTION
#undef JMRI_SENSOR
#undef JOIN
@ -133,6 +135,7 @@
#undef ONBUTTON
#undef ONSENSOR
#undef ONTHROW
#undef ONBITMAP
#undef ONCHANGE
#undef PARSE
#undef PAUSE
@ -195,6 +198,11 @@
#undef VIRTUAL_TURNOUT
#undef WAITFOR
#ifndef IO_NO_HAL
#undef BITMAP_AND
#undef BITMAP_OR
#undef BITMAP_XOR
#undef BITMAP_INC
#undef BITMAP_DEC
#undef WAITFORTT
#endif
#undef WITHROTTLE
@ -651,6 +659,22 @@
* @param value
*/
#define IFRE(vpin,value)
/**
* @def IFBITMAP_ALL(vpin,mask)
* @briaf Checks if (vpin pseudo-analog value & mask) == mask.
* @see IF
* @param vpin
* @param mask Binary mask applied to vpin value
*/
#define IFBITMAP_ALL(vpin,mask)
/**
* @def IFBITMAP_ANY(vpin,mask)
* @briaf Checks if vpin pseudo-analog value & mask is non zero
* @see IF
* @param vpin
* @param mask Binary mask applied to vpin value
*/
#define IFBITMAP_ANY(vpin,mask)
/**
* @def INVERT_DIRECTION
* @brief Marks current task so that FWD and REV commands are inverted.
@ -899,6 +923,12 @@
* @param vpin
*/
#define ONSENSOR(vpin)
/**
* @def ONBITMAP(vpin)
* @brief Start task here when bitmap sensor changes state (debounced)
* @param vpin
*/
#define ONBITMAP(vpin)
/**
* @def ONBUTTON(vpin)
* @brief Start task here when sensor changes HIGH to LOW.
@ -1322,9 +1352,42 @@
* @param description... quoted text or HIDDEN
*/
#define VIRTUAL_TURNOUT(id,description...)
/**
* @def BITMAP_AND(vpin1,mask)
* @brief Performs a bitwise AND operation on the given vpin analog value and mask.
* @param vpin1
* @param mask Binary mask to be ANDed with vpin1 value
*/
#define BITMAP_AND(vpin1,mask)
/**
* @def BITMAP_INC(vpin)
* @brief Increments poesudo analog value by 1
* @param vpin
*/
#define BITMAP_INC(vpin)
/**
* @def BITMAP_DEC(vpin)
* @brief Decrements poesudo analog value by 1 (to zero)
* @param vpin
*/
#define BITMAP_DEC(vpin)
/**
* @def BITMAP_OR(vpin1,mask)
* @brief Performs a bitwise OR operation on the given vpin analog value and mask.
* @param vpin1
* @param mask Binary mask to be ORed with vpin1 value
*/
#define BITMAP_OR(vpin1,mask)
/**
* @def BITMAP_XOR(vpin1,mask)
* @brief Performs a bitwise XOR operation on the given vpin analog value and mask.
* @param vpin1
* @param mask Binary mask to be XORed with vpin1 value
*/
#define BITMAP_XOR(vpin1,mask)
/**
* @def WAITFOR(vpin)
* @brief WAits for completion of servo movement
* @brief Waits for completion of servo movement
* @param vpin
*/
#define WAITFOR(pin)

View File

@ -160,6 +160,8 @@ bool exrailHalSetup() {
#define ONBUTTON(vpin) | FEATURE_SENSOR
#undef ONSENSOR
#define ONSENSOR(vpin) | FEATURE_SENSOR
#undef ONBITMAP
#define ONBITMAP(vpin) | FEATURE_SENSOR
#undef ONBLOCKENTER
#define ONBLOCKENTER(blockid) | FEATURE_BLOCK
#undef ONBLOCKEXIT
@ -481,6 +483,8 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define IFTTPOSITION(id,position) OPCODE_IFTTPOSITION,V(id),OPCODE_PAD,V(position),
#endif
#define IFRE(sensor_id,value) OPCODE_IFRE,V(sensor_id),OPCODE_PAD,V(value),
#define IFBITMAP_ALL(vpin,mask) OPCODE_IFBITMAP_ALL,V(vpin),OPCODE_PAD,V(mask),
#define IFBITMAP_ANY(vpin,mask) OPCODE_IFBITMAP_ANY,V(vpin),OPCODE_PAD,V(mask),
#define INVERT_DIRECTION OPCODE_INVERT_DIRECTION,0,0,
#define JMRI_SENSOR(vpin,count...)
#define JOIN OPCODE_JOIN,0,0,
@ -533,6 +537,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id),
#define ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id),
#define ONSENSOR(sensor_id) OPCODE_ONSENSOR,V(sensor_id),
#define ONBITMAP(sensor_id) OPCODE_ONBITMAP,V(sensor_id),
#define ONBUTTON(sensor_id) OPCODE_ONBUTTON,V(sensor_id),
#define PAUSE OPCODE_PAUSE,0,0,
#define PICKUP_STASH(id) OPCODE_PICKUP_STASH,V(id),
@ -595,6 +600,11 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define UNLATCH(sensor_id) OPCODE_UNLATCH,V(sensor_id),
#define VIRTUAL_SIGNAL(id)
#define VIRTUAL_TURNOUT(id,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(0),
#define BITMAP_AND(vpin,mask) OPCODE_BITMAP_AND,V(vpin),OPCODE_PAD,V(mask),
#define BITMAP_INC(vpin) OPCODE_BITMAP_INC,V(vpin),
#define BITMAP_DEC(vpin) OPCODE_BITMAP_DEC,V(vpin),
#define BITMAP_OR(vpin,mask) OPCODE_BITMAP_OR,V(vpin),OPCODE_PAD,V(mask),
#define BITMAP_XOR(vpin,mask) OPCODE_BITMAP_XOR,V(vpin),OPCODE_PAD,V(mask),
#define WITHROTTLE(msg) PRINT(msg)
#define WAITFOR(pin) OPCODE_WAITFOR,V(pin),
#ifndef IO_NO_HAL

View File

@ -59,7 +59,7 @@ void EXRAILSensor::checkAll() {
bool EXRAILSensor::check() {
// check for debounced change in this sensor
inputState = RMFT2::readSensor(pin);
inputState = useAnalog?IODevice::readAnalogue(pin):RMFT2::readSensor(pin);
// Check if changed since last time, and process changes.
if (inputState == active) {// no change
@ -83,18 +83,18 @@ bool EXRAILSensor::check() {
return false;
}
EXRAILSensor::EXRAILSensor(VPIN _pin, int _progCounter, bool _onChange) {
// Add to the start of the list
//DIAG(F("ONthing vpin=%d at %d"), _pin, _progCounter);
EXRAILSensor::EXRAILSensor(VPIN _pin, int _progCounter, bool _onChange, bool _useAnalog) {
nextSensor = firstSensor;
firstSensor = this;
pin=_pin;
progCounter=_progCounter;
onChange=_onChange;
useAnalog=_useAnalog;
IODevice::configureInput(pin, true);
active = IODevice::read(pin);
active = useAnalog?IODevice::readAnalogue(pin): IODevice::read(pin);
inputState = active;
latchDelay = minReadCount;
}

View File

@ -29,7 +29,8 @@ class EXRAILSensor {
public:
static void checkAll();
EXRAILSensor(VPIN _pin, int _progCounter, bool _onChange);
EXRAILSensor(VPIN _pin, int _progCounter, bool _onChange, bool _useAnalog=false);
bool check();
private:
@ -42,9 +43,10 @@ class EXRAILSensor {
EXRAILSensor* nextSensor;
VPIN pin;
int progCounter;
bool active;
bool inputState;
uint16_t active;
uint16_t inputState;
bool onChange;
bool useAnalog;
byte latchDelay;
};
#endif

View File

@ -20,19 +20,31 @@
This is the list of HAL drivers automatically included by IODevice.h
It has been moved here to be easier to maintain than editing IODevice.h
*/
#include "IO_AnalogueInputs.h"
#include "IO_DFPlayer.h"
#include "IO_DS1307.h"
#include "IO_duinoNodes.h"
#include "IO_EncoderThrottle.h"
#include "IO_EXFastclock.h"
#include "IO_EXIOExpander.h"
#include "IO_EXSensorCAM.h"
#include "IO_HALDisplay.h"
#include "IO_HCSR04.h"
#include "IO_I2CDFPlayer.h"
#include "IO_I2CRailcom.h"
#include "IO_MCP23008.h"
#include "IO_MCP23017.h"
#include "IO_NeoPixel.h"
#include "IO_PCA9555.h"
#include "IO_PCA9685pwm.h"
#include "IO_PCF8574.h"
#include "IO_PCF8575.h"
#include "IO_PCA9555.h"
#include "IO_duinoNodes.h"
#include "IO_EXIOExpander.h"
#include "IO_trainbrains.h"
#include "IO_EncoderThrottle.h"
#include "IO_RotaryEncoder.h"
#include "IO_Servo.h"
#include "IO_TCA8418.h"
#include "IO_NeoPixel.h"
#include "IO_TM1638.h"
#include "IO_EXSensorCAM.h"
#include "IO_DS1307.h"
#include "IO_I2CRailcom.h"
#include "IO_TouchKeypad.h"
#include "IO_trainbrains.h"
#include "IO_Bitmap.h"
#include "IO_VL53L0X.h"

89
IO_Bitmap.h Normal file
View File

@ -0,0 +1,89 @@
/*
* © 2025, Chris Harlow. All rights reserved.
*
* This file is part of DCC-EX API
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef IO_Bitmap_h
#define IO_Bitmap_h
#include <Arduino.h>
#include "defines.h"
#include "IODevice.h"
/*
Bitmap provides a set of virtual pins with no hardware.
Bitmap pins are able to be output and input and may be set and tested
as digital or analogue values.
When writing a digital value, the analogue value is set to 0 or 1.
When reading a digital value, the return is LOW for value 0 or HIGH for any other value
or analogue.
Bitmap pins may be used for any purpose, this is easier to manage than LATCH in EXRAIL
as they can be explicitely set and tested without interfering with underlying hardware.
Bitmap pins may be set, reset and tested in the same way as any other pin.
They are not persistent across reboots, but are retained in the current session.
Bitmap pins may also be monitored by JMRI_SENSOR() and <S> as for any other pin.
*/
class Bitmap : public IODevice {
public:
static void create(VPIN firstVpin, int nPins) {
if (IODevice::checkNoOverlap(firstVpin,nPins))
new Bitmap( firstVpin, nPins);
}
Bitmap(VPIN firstVpin, int nPins) : IODevice(firstVpin, nPins) {
_pinValues=(int16_t *) calloc(nPins,sizeof(int16_t));
// Connect to HAL so my _write, _read and _loop will be called as required.
IODevice::addDevice(this);
}
// Called by HAL to start handling this device
void _begin() override {
_deviceState = DEVSTATE_NORMAL;
_display();
}
int _read(VPIN vpin) override {
int pin=vpin - _firstVpin;
return _pinValues[pin]?1:0;
}
void _write(VPIN vpin, int value) override {
int pin = vpin - _firstVpin;
_pinValues[pin]=value!=0; // this is digital write
}
int _readAnalogue(VPIN vpin) override {
int pin=vpin - _firstVpin;
return _pinValues[pin]; // this is analog read
}
void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override {
int pin=vpin - _firstVpin;
_pinValues[pin]=value; // this is analog write
}
void _display() override {
DIAG(F("Bitmap Configured on Vpins:%u-%u"),
(int)_firstVpin,
(int)_firstVpin+_nPins-1);
}
private:
int16_t* _pinValues;
};
#endif

View File

@ -0,0 +1,58 @@
Virtual Bitmap device pins.
a Bitmap device pin is a software representation of a virtual hardware device that has the ability to store a 16bit value.
This this is easier to manage than LATCH in EXRAIL as they can be explicitely set and tested without interfering with underlying hardware or breaching the 255 limit.
Virtual pins may be set, reset and tested in the same way as any other pin. Unlike sensors and leds, these device pins are both INPUT and OUTPUT These can be used in many ways:
As a simple digital flag to assist in inter-thread communication.
A flag or value that can be set from commands and tested in EXRAIL.(e.g. to stop a sequence)
As a counter for looping or occupancy counts such as trains passing over a multi track road crossing.
As a collection of 16 digital bits that can be set, reset, toggled, masked and tested.
Existing <> and exrail commands for vpins work on these pins.
Virtual pin creation:
HAL(Bitmap,firstpin,npins)
creates 1 or more virtual pins in software. (RAM requirement approximately 2 bytes per pin)
e.g. HAL(Bitmap,1000,20) creates pins 1000..1019
Simple use as flags:
This uses the traditional digital pin commands
SET(1013) RESET(1013) sets value 1 or 0
SET(1000,20) RESET(1000,20) sets/resets a range of pins
IF(1000) tests if pin value!=0
Commands can set 1/0 values using <z 1010> <z -1010> as for any digital output.
BLINK can be used to set them on/off on a time pattern.
In addition, Exrail sensor comands work as if these pins were sensors
ONBUTTON(1013) triggers when value changes from 0 to something.
ONSENSOR(1013) triggers when value changes to or from 0.
<S 1013 1013 1> and JMRI_SENSOR(1013) report <Q/q responses when changing to or from 0.
Use as analog values:
Analog values may be set into the virtual pins and tested using the existing analog value commands and exrail macros.
<z vpin value> <D ANIN vpin> etc.
Use as counters:
For loop counting, counters can be incremented by BITMAP_INC(1013) and decremented by BITMAP_DEC(1013) and tested with IF/IFNOT/IFGTE etc.
Counters be used to automate a multi track crossing where each train entering increments the counter and decrements it on clearing the crossing. Crossing gate automation can be started when the value changes from 0, and be stopped when the counter returns to 0. Detecting the first increment from 0 to 1 can be done with ONBUTTON(1013) and the automation can use IF(1013) or IFNOT(1013) to detect when it needs to reopen the road gates.
Use as binary flag groups:
Virtual pins (and others that respond to an analog read in order to provide bitmapped digital data, such as SensorCam) can be set and tested with new special EXRAIL commands
IFBITMAP_ALL(vpin,mask) Bitwise ANDs the the vpin value with the mask value and is true if ALL the 1 bits in the mask are also 1 bits in the value.
e.g. IFBITMAP_ALL(1013,0x0f) would be true if ALL the last 4 bits of the value are 1s.
IFBITMAP_ANY(1013,0x0f) would be true if ANY of the last 4 bits are 1s.
Modifying bitmap values:
BITMAP_AND(vpin,mask) performs a bitwise AND operation.
BITMAP_OR(vpin,mask) performa a bitwise OR operation
BITMAP_XOR(vpin,mask) performs a bitwise EXCLUSIVE OR (which is basically a toggle)

View File

@ -3,7 +3,8 @@
#include "StringFormatter.h"
#define VERSION "5.5.24"
#define VERSION "5.5.25"
// 5.2.25 - IO_Bitmap and assicated Exrail macros
// 5.5.24 - SensorCAM in I2C scan and automatically setClock
// 5.5.23 - Reminder loop Idle packet optimization
// 5.5.22 - (5.4.9) Handle non-compliant decoders returning 255 for cv 20 and confusing <R> with bad consist addresses.