From 76989ef0376c7ed569335cdd1766af0c0e787f5b Mon Sep 17 00:00:00 2001 From: Florian Glaser Date: Wed, 3 Feb 2021 00:37:44 +0100 Subject: [PATCH 01/14] First integration of P Gahtow XpressNetMaster library --- c6021light/include/WProgram.h | 27 + c6021light/platformio.ini | 2 +- c6021light/src/WProgram.cpp | 14 + c6021light/src/XpressNet/XpressNetMaster.cpp | 1364 +++++++++++++++++ c6021light/src/XpressNet/XpressNetMaster.h | 330 ++++ c6021light/src/XpressNet/XpressNetMsg.cpp | 44 + c6021light/src/XpressNet/XpressNetMsg.h | 56 + c6021light/src/hal/LibOpencm3Hal.cpp | 11 +- c6021light/src/hal/LibOpencm3Hal.h | 2 + c6021light/src/main.cpp | 3 + .../src/tasks/ConsoleTask/ConsoleTask.cpp | 5 + .../src/tasks/ConsoleTask/ConsoleTask.h | 1 + .../src/tasks/RoutingTask/RoutingTask.cpp | 30 + .../src/tasks/RoutingTask/RoutingTask.h | 4 + .../tasks/RoutingTask/XpressNetForwarder.cpp | 84 + .../tasks/RoutingTask/XpressNetForwarder.h | 37 + 16 files changed, 2012 insertions(+), 2 deletions(-) create mode 100644 c6021light/src/XpressNet/XpressNetMaster.cpp create mode 100644 c6021light/src/XpressNet/XpressNetMaster.h create mode 100644 c6021light/src/XpressNet/XpressNetMsg.cpp create mode 100644 c6021light/src/XpressNet/XpressNetMsg.h create mode 100644 c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp create mode 100644 c6021light/src/tasks/RoutingTask/XpressNetForwarder.h diff --git a/c6021light/include/WProgram.h b/c6021light/include/WProgram.h index 23424ec..962d56f 100644 --- a/c6021light/include/WProgram.h +++ b/c6021light/include/WProgram.h @@ -9,8 +9,11 @@ #include #include +#include #include +typedef uint8_t byte; // Arduino pre-defined data types + /// The I/O mode of a GPIO enum PinMode { OUTPUT = 0, // Assume Push-Pull @@ -18,6 +21,12 @@ enum PinMode { INPUT_PULLUP }; +/// The I/O value of a GPIO +enum PinValue { + LOW = 0, + HIGH +}; + /** * \brief Possible GPIO ports of the Bluepill board. * @@ -35,6 +44,9 @@ enum PinNames : uint8_t { /// Macro to define ISR functions. #define ISR(f) extern "C" void f(void) +/// Arduino uses function-style casts.. +#define word(h, l) ((uint16_t) ((h << 8) | (l & 0xFF))) + /// Macro to obtain the contents of the output register for a GPIO* #define portOutputRegister(port) (&(GPIO_ODR(port))) @@ -42,6 +54,7 @@ enum PinNames : uint8_t { #define portInputRegister(port) (&(GPIO_IDR(port))) uint32_t digitalPinToPort(PinNames pin); uint16_t digitalPinToBitMask(PinNames pin); +void digitalWrite(PinNames pin, PinValue value); void pinMode(PinNames pin, PinMode mode); /// Forwarding functions for implicit conversion @@ -59,11 +72,25 @@ inline uint16_t digitalPinToBitMask(std::underlying_type::type pin) { return digitalPinToBitMask(static_cast(pin)); } +inline void digitalWrite(std::underlying_type::type pin, PinValue value) { + return digitalWrite(static_cast(pin), value); +} + #define noInterrupts() (portDISABLE_INTERRUPTS()) #define interrupts() (portENABLE_INTERRUPTS()) #define delay(ms) (vTaskDelay(pdMS_TO_TICKS(ms))) +/// Dirty emulation of Arduino tick functions +inline uint32_t millis(void) { + return xTaskGetTickCount() * portTICK_PERIOD_MS; +} + +/// TODO this needs to be done properly, millis resolution is not sufficient +inline uint32_t micros(void) { + return millis() * 1000; +} + /* 01 VBAT 02 PC 13 diff --git a/c6021light/platformio.ini b/c6021light/platformio.ini index d45ded8..c03982e 100644 --- a/c6021light/platformio.ini +++ b/c6021light/platformio.ini @@ -28,7 +28,7 @@ framework = libopencm3 board_build.ldscript = stm32f103c8.ld monitor_speed = 115200 -monitor_port = COM4 +monitor_port = COM5 monitor_flags = --raw ;upload_protocol = serial diff --git a/c6021light/src/WProgram.cpp b/c6021light/src/WProgram.cpp index 579ea7e..d20ae6d 100644 --- a/c6021light/src/WProgram.cpp +++ b/c6021light/src/WProgram.cpp @@ -43,3 +43,17 @@ void pinMode(PinNames pin, PinMode mode) { break; } } + +void digitalWrite(PinNames pin, PinValue value) { + uint32_t port = digitalPinToPort(pin); + uint16_t pinMask = digitalPinToBitMask(pin); + + switch (value) { + case PinValue::HIGH: + gpio_set(port, pinMask); + break; + case PinValue::LOW: + gpio_clear(port, pinMask); + break; + } +} \ No newline at end of file diff --git a/c6021light/src/XpressNet/XpressNetMaster.cpp b/c6021light/src/XpressNet/XpressNetMaster.cpp new file mode 100644 index 0000000..3825a83 --- /dev/null +++ b/c6021light/src/XpressNet/XpressNetMaster.cpp @@ -0,0 +1,1364 @@ +/* +***************************************************************************** + * XpressNetMaster.h - library for XpressNet protocoll + * Copyright (c) 08/2016 - 2020 Philipp Gahtow All right reserved. + * +***************************************************************************** + * FUNKTIONS: + * + * Build a XpressNet Master for driving and switch + * Support for Roco LokMaus2 and Multimaus + * Basic support for direct CV programming (read and write) + * Check XOR before packet dekoding + * + * -> Please contact Lenz Inc. for more details about XpressNet. + * + * not yet Supported: + * - Emergency stop a locomotive: 0x92 AddrH AddrL [XOR] + * - Emergency stop a locomotive: 0x91 loco_addr [XOR] (v2) + * - Service Mode: only direct Mode + * - DCC extended accessory command: 0x13 0x01 B+AddrH AddrL + * - DCC FAST CLOCK: 0x01 0xF2 0xF3 + * - Command Tunnel: 0x3* ** ** [XOR] + * - BiDi messages: 0x7* ** ** [XOR] + * - Library Entry: 0xE9 0xF1 AddrH AddrL IDX SIZE [NAME][XOR] + * - Set function state: 0xE4 0x2* AddrH AddrL Group [XOR] +***************************************************************************** +*/ + +// include this library's description file +#include "XpressNetMaster.h" + +#if defined(__AVR__) +#include +XpressNetMasterClass *XpressNetMasterClass::active_object = 0; //Static +#elif defined(STM32F1) +XpressNetMasterClass *XpressNetMasterClass::active_object = 0; //Static +#endif + +// Local instantiation +XpressNetMasterClass XpressNet = XpressNetMasterClass(); + +// Constructor ///////////////////////////////////////////////////////////////// +// Function that handles the creation and setup of instances + +XpressNetMasterClass::XpressNetMasterClass() +{ + // initialize this instance's variables + XNetAdr = 0; //Startaddresse des ersten XNet Device + XNetSlaveMode = 0x00; //Start in MASTER MODE + XNetSlaveInit = true; //if change to Slave Mode make initialize + Railpower = csTrackVoltageOff; + Fahrstufe = Loco128; + + XNetBufferSend = 0; //start position to read data from the buffer + XNetBufferSend_data = 0; //position of byte that we are sending + XNetBufferStore = 0; //start position to store data in buffer + + for (byte s = 0; s < 32; s++) { //clear busy slots + SlotLokUse[s] = 0xFFFF; //slot is inactiv + } + + for (byte b = 0; b < XNetBufferSize; b++) { //clear send buffer + XNetBuffer[b].length = 0x00; + for (byte d = 0; d < XNetBufferMaxData; d++) { + XNetBuffer[b].data[d] = 0x00; + } + } + + XNetDataReady = false; //keine Daten empfangen! + XNet_state = XNet_get_callbyte; //set the start state + XNetclear(); //alte Nachricht l�schen + XNetMsgBuffer[XNetBufferlength] = 0x00; //reset buffer + + XNetCVAdr = 0; //no CV read + XNetCVvalue = 0; //no CV value +} + +//******************************************Serial******************************************* +void XpressNetMasterClass::setup(uint8_t FStufen, uint8_t XControl) //Initialisierung Serial +{ + Fahrstufe = FStufen; + MAX485_CONTROL = XControl; + +#if defined(RAW_DATA_MODE) + // LISTEN_MODE + pinMode(MAX485_CONTROL, OUTPUT); + digitalWrite (MAX485_CONTROL, LOW); //RECEIVE_MODE - SLAVE + +#elif defined(__AVR__) //Configuration for 8-Bit MCU + // LISTEN_MODE + pinMode(MAX485_CONTROL, OUTPUT); + digitalWrite (MAX485_CONTROL, LOW); //RECEIVE_MODE - SLAVE + + //Set up on 62500 Baud + cli(); //disable interrupts while initializing the USART + #if defined(__AVR_ATmega328p__) + UBRRH = 0; + UBRRL = 0x0F; + UCSRA = 0; + UCSRB = (1< 0xFF) { + XNetMsgCallByte = data & 0xFF; //only store data + XNetSlaveMode = XNetSlaveCycle; //reactivate SLAVE MODE + // XNetMsg[XNetlength] = 0x00; + if (XNetMsgCallByte == MY_ADDRESS) + XNetSendNext(); //start sending out by interrupt + } + else { + XNetMsgBuffer[XNetBufferlength]++; //weitere Nachrichtendaten + XNetMsgBuffer[XNetMsgBuffer[XNetBufferlength]] = data & 0xFF; // Zeichen aus UDR an Aufrufer zurueckgeben + } + + if (XNetMsgBuffer[XNetBufferlength] >= 2) { // header and one data byte or more received + //Check length - length is inside header but without header and xor! + if (((XNetMsgBuffer[1] & 0x0F) + 2) == XNetMsgBuffer[XNetBufferlength]) { //reach data length? + for (byte i = 0; i < XNetMsgBuffer[XNetBufferlength]; i++) { + XNetMsg[i] = XNetMsgBuffer[i+1]; + } + XNetDataReady = true; + XNetMsgBuffer[XNetBufferlength] = 0x00; //clear! + } + if (XNetMsgBuffer[XNetBufferlength] >= (XNetMaxDataLength) ) { //overflow!!! + XNetMsgBuffer[XNetBufferlength] = 0x00; //clear! + } + } +} +#endif + +//******************************************************************************************* +//Daten Auswerten +void XpressNetMasterClass::update(void) +{ + #if defined(ARDUINO_ESP8266_ESP01) || defined(ARDUINO_ESP8266_WEMOS_D1MINI) + XNetReceive(); + #endif + + uint32_t time_diff; + + //XpressNet State Machine: + switch (XNet_state) { + case XNet_send_callbyte: + //wait until data sending is ready. + break; + case XNet_wait_receive: //wait for client answer, max 120 microsekunden + if (micros() - XSendCount >= XNetTimeUntilNext) { //Timeout? + XNet_state = XNet_get_callbyte; + } + if (XNetMsgBuffer[XNetBufferlength] != 0x00) { + #if defined (XNetDEBUGTime) + XNetSerial.print("Reply after: "); + XNetSerial.println(micros() - XSendCount); + #endif + XNet_state = XNet_receive_data; + XSendCount = micros(); //save time for receive the first byte + } + break; + case XNet_receive_data: //read client data, max 500ms + time_diff = micros() - XSendCount; + if (time_diff >= XNetTimeReadData) { //Timeout? + XNet_state = XNet_get_callbyte; + XNetclear(); //alte Nachricht l�schen + XNetMsgBuffer[XNetBufferlength] = 0x00; //reset buffer + break; + } + if (!XNetDataReady) + break; + #if defined (XNetDEBUGTime) + XNetSerial.print("Paket time: "); + XNetSerial.println(micros() - XSendCount); + #endif + XNetDataReady = false; + if (XNetCheckXOR()) //Checks the XOR + XNetAnalyseReceived(); //Auswerten der empfangenen Daten + XNetclear(); //alte Nachricht l�schen + if (XNetSlaveMode == 0x00) { //MASTER MODE + XNet_state = XNet_send_data; + XNetSendNext(); //start sending out by interrupt + } + else XNet_state = XNet_get_callbyte; + break; + case XNet_send_data: + break; + case XNet_get_callbyte: //address the next client + if (XNetSlaveMode == 0x00) { //MASTER MODE + //we are not sending out more Data, get next CallByte + XNet_state = XNet_send_callbyte; + getNextXNetAdr(); //Send next CallByte + XNetSendNext(); //start sending out by interrupt + } + else { + //count cycles that we run in SLAVE MODE + XNetSlaveMode--; //stay only in SLAVE MODE if we receive CallBytes + XNet_state = XNet_receive_data; + //we need to initialize the SLAVE MODE? + if (XNetSlaveInit) { + XNetSlaveInit = false; + uint8_t Init[] = { 0x00, 0x21, 0x24, 0x05 }; + XNetsend(Init, 4); //send initsequence + #if defined (XNetDEBUG) + XNetSerial.print("Slave INIT: "); + XNetSerial.println(XNetSlaveMode); + #endif + } + if (XNetSlaveMode == 0x00) { //we will leave the Slave-Mode? + XNetSlaveInit = true; + XNetMsgBuffer[XNetBufferlength] = 0x00; //clear! + } + } + + XSendCount = micros(); //save time for callbyte send! + break; + } +} + +//-------------------------------------------------------------------------------------------- +//Checks the XOR +bool XpressNetMasterClass::XNetCheckXOR(void) { + byte rxXOR = 0x00; //store the read in XOR + for (byte i = 0; i < ((XNetMsg[XNetheader] & 0x0F) + 2); i++) { + rxXOR = rxXOR ^ XNetMsg[i]; + } + if (rxXOR == 0x00) { //XOR is 0x00? + return true; + } + //�bertragungsfehler: + if (XNetSlaveMode == 0x00) { //MASTER MODE + uint8_t ERR[] = {DirectedOps, 0x61, 0x80, 0xE1 }; + XNetsend(ERR, 4); + } + return false; +} + +//-------------------------------------------------------------------------------------------- +//Daten Auswerten +void XpressNetMasterClass::XNetAnalyseReceived(void) { //work on received data + + #if defined (XNetDEBUG) + if (XNetSlaveMode == 0x00) { + XNetSerial.print("MRX: 0x1"); + XNetSerial.print(CallByteInquiry, HEX); //MASTER MODE - CallByte 8 Bit only + } + else { + XNetSerial.print("SRX: 0x1"); + XNetSerial.print(XNetMsgCallByte, HEX); //SLAVE MODE - CallByte 8 Bit only + } + for (byte i = 0; i < ((XNetMsg[XNetheader] & 0x0F) + 2); i++) { + if (XNetMsg[i] < 0x10) + XNetSerial.print(" 0x0"); + else XNetSerial.print(" 0x"); + XNetSerial.print(XNetMsg[i], HEX); + } + XNetSerial.println(); + #endif + + #if defined(STM32F1) + printf("XN rx: 0x1%x", XNetMsgCallByte); + for (byte i = 0; i < ((XNetMsg[XNetheader] & 0x0F) + 2); i++) printf(" %x", XNetMsg[i]); + printf("\n"); + #endif + + switch (XNetMsg[XNetheader]) { + case 0x21: + if (XNetSlaveMode == 0x00) { + if (XNetMsg[XNetdata1] == 0x24 && XNetMsg[XNetdata2] == 0x05) { //Command station status indication response + /* + Bit 0: =1 - Command station is in emergency off (Nothalt) + Bit 1: =1 - Command station is in emergency stop (Notaus) + Bit 2: Command station-Start mode (0 = manual mode, 1 = automatic mode) + Automatic Mode: All locomotives start at their last known speed each + time the command station is powered up + Manual Mode: All locomotives have a speed of 0 and functions out on + command station power up + Bit 3: = 1 - The command station is in service mode + Bit 4: reserved + Bit 5: reserved + Bit 6: = 1 - The command station is performing a power up. + Bit 7: = 1 - There was a RAM check error in the command station + */ + byte status = 0x01; //csTrackVoltageOff = B1; + switch (Railpower) { + case csNormal: status = 0; break; + case csEmergencyStop: status = 0x01; break; + case csServiceMode: status = 0x08; break; + case csShortCircuit: status = 0x02; break; + } + uint8_t sendStatus[] = { DirectedOps, 0x62, 0x22, status, 0x00 }; + getXOR(sendStatus, 5); + XNetsend(sendStatus, 5); + } + if (XNetMsg[XNetdata1] == 0x21 && XNetMsg[XNetdata2] == 0x00) { //Command station softwareversion response + uint8_t sendVersion[] = { DirectedOps, 0x63, 0x21, XNetVersion, XNetID, 0x00 }; //63-21 36 0 74 + getXOR(sendVersion, 6); + XNetsend(sendVersion, 6); + } + if (XNetMsg[XNetdata1] == 0x80 && XNetMsg[XNetdata2] == 0xA1) { //Alles Aus (Notaus) + setPower(csTrackVoltageOff); + } + if (XNetMsg[XNetdata1] == 0x81 && XNetMsg[XNetdata2] == 0xA0) { //Alles An + setPower(csNormal); + } + if (XNetMsg[XNetdata1] == 0x10 && XNetMsg[XNetdata2] == 0x31) { //Request for Service Mode results + if (XNetCVAdr != 0) { + uint8_t sendStatus[] = { DirectedOps, 0x63, 0x14, XNetCVAdr, XNetCVvalue, 0x00}; //Service Mode response for Direct CV mode + getXOR(sendStatus, 6); + XNetsend(sendStatus, 6); + XNetCVAdr = 0; //reset CV read Adr + XNetCVvalue = 0;//reset CV value + } + else { + // Programming info. "Command station busy" + uint8_t sendStatus[] = { DirectedOps, 0x61, 0x1F, 0x00 }; + if (XNetCVvalue == 0xFF) { //"no ACK" + sendStatus[2] = 0x13; //Programming info. "Data byte not found" + XNetCVvalue = 0; //reset error! + } + // Programming info. "Command station ready " + //uint8_t sendStatus[] = { DirectedOps, 0x61, 0x11, 0x00 }; + getXOR(sendStatus, 4); + XNetsend(sendStatus, 4); + } + } + if (SlotLokUse[DirectedOps & 0x1F] == 0xFFFF) + SlotLokUse[DirectedOps & 0x1F] = 0; //mark Slot as activ + //XNetclear(); //alte Nachricht l�schen + } + break; + case 0x22: //Start Programming + if (XNetMsg[XNetdata1] == 0x11) { //Register Mode read request (Register Mode) + } + if (XNetMsg[XNetdata1] == 0x14) { //Paged Mode read request (Paged Mode) + } + if (XNetMsg[XNetdata1] == 0x15) { //Direct Mode CV read request (CV mode) + XNetCVAdr = 0; //no CV read + XNetCVvalue = 0; //no CV value + if (notifyXNetDirectReadCV) + notifyXNetDirectReadCV(XNetMsg[XNetdata2]-1); //try to read the CV 1..255 + break; + } + unknown(); //unbekannte Anfrage + //XNetclear(); //alte Nachricht l�schen + break; + case 0x23: + if (XNetMsg[XNetdata1] == 0x12) { //Register Mode write request (Register Mode) + } + if (XNetMsg[XNetdata1] == 0x16) { //Direct Mode CV write request (CV mode) + XNetCVAdr = 0; //no CV read + XNetCVvalue = 0; //no CV value + if (notifyXNetDirectCV) + notifyXNetDirectCV(XNetMsg[XNetdata2]-1, XNetMsg[XNetdata3]); + break; + } + if (XNetMsg[XNetdata1] == 0x17) { //0x23 0x17 CV DAT[XOR] - Paged Mode write request(Paged mode) + + } + unknown(); //unbekannte Anfrage + //XNetclear(); + break; + case 0xE6: { //POM CV write MultiMaus + if (XNetMsg[XNetdata1] == 0x30) { + uint16_t Adr = ((XNetMsg[XNetdata2] & 0x3F) << 8) + XNetMsg[XNetdata3]; + uint16_t CVAdr = ((XNetMsg[XNetdata4] & 0b11) << 8) + XNetMsg[XNetdata5]; + if ((XNetMsg[XNetdata4] & 0xFC) == 0xEC) { //set byte + if (notifyXNetPOMwriteByte) + notifyXNetPOMwriteByte (Adr, CVAdr, XNetMsg[XNetdata6]); + } + if ((XNetMsg[XNetdata4] & 0xFC) == 0xE8) { //set bit + if (notifyXNetPOMwriteBit) + notifyXNetPOMwriteBit (Adr, CVAdr, (XNetMsg[XNetdata6] & 0x0F)); + } + } + break; } + case 0x80: + if (XNetMsg[XNetdata1] == 0x80) { //EmStop + setPower(csEmergencyStop); + break; + } + unknown(); //unbekannte Anfrage + //XNetclear(); //alte Nachricht l�schen + break; + case 0xE3: { + if (XNetSlaveMode == 0x00) { + switch (XNetMsg[XNetdata1]) { + case 0x00: //Lokdaten anfordern & F0 bis F12 anfordern + if (notifyXNetgiveLocoInfo) + notifyXNetgiveLocoInfo(DirectedOps, word(XNetMsg[XNetdata2] & 0x3F, XNetMsg[XNetdata3])); + break; + case 0x07: { //Funktionsstatus F0 bis F12 anfordern (Funktion ist tastend oder nicht tastend) + //0x07, sonst ist LokMaus2 langsam! + uint8_t LocoFkt[] = { DirectedOps, 0xE3, 0x50, 0x00, 0x00, 0x00 }; + getXOR(LocoFkt, 6); + XNetsend(LocoFkt, 6); + break; + } + case 0x08: { //Funktionsstatus F13 bis F28 anfordern (Funktion ist tastend oder nicht tastend) + uint8_t LocoFkt[] = { DirectedOps, 0xE3, 0x51, 0x00, 0x00, 0x00 }; + getXOR(LocoFkt, 6); + XNetsend(LocoFkt, 6); + break; + } + case 0x09: //Funktionszustand F13 bis F28 anfordern + if (notifyXNetgiveLocoFunc) + notifyXNetgiveLocoFunc(DirectedOps, word(XNetMsg[XNetdata2] & 0x3F, XNetMsg[XNetdata3])); + break; + case 0xF0: //Lok und Funktionszustand MultiMaus anfordern + if (notifyXNetgiveLocoMM) + notifyXNetgiveLocoMM(DirectedOps, word(XNetMsg[XNetdata2] & 0x3F, XNetMsg[XNetdata3])); + break; + default: unknown(); //unbekannte Anfrage + } + } + //XNetclear(); //alte Nachricht l�schen + break; + } + case 0xE4: { //Fahrbefehle + AddBusySlot(DirectedOps, word(XNetMsg[XNetdata2] & 0x3F, XNetMsg[XNetdata3])); //set Busy + if (XNetMsg[XNetdata1] == 0x10) { //14 Fahrstufen + if (notifyXNetLocoDrive14) + notifyXNetLocoDrive14(word(XNetMsg[XNetdata2] & 0x3F, XNetMsg[XNetdata3]), XNetMsg[XNetdata4]); + } + else if (XNetMsg[XNetdata1] == 0x11) { //27 Fahrstufen + if (notifyXNetLocoDrive27) + notifyXNetLocoDrive27(word(XNetMsg[XNetdata2] & 0x3F, XNetMsg[XNetdata3]), XNetMsg[XNetdata4]); + } + else if (XNetMsg[XNetdata1] == 0x12) { //28 Fahrstufen + if (notifyXNetLocoDrive28) + notifyXNetLocoDrive28(word(XNetMsg[XNetdata2] & 0x3F, XNetMsg[XNetdata3]), XNetMsg[XNetdata4]); + } + else if (XNetMsg[XNetdata1] == 0x13) { //128 Fahrstufen + if (notifyXNetLocoDrive128) + notifyXNetLocoDrive128(word(XNetMsg[XNetdata2] & 0x3F, XNetMsg[XNetdata3]), XNetMsg[XNetdata4]); + } + else if (XNetMsg[XNetdata1] == 0x20) { //Funktionsbefehl Gruppe1 0 0 0 F0 F4 F3 F2 F1 + if (notifyXNetLocoFunc1) + notifyXNetLocoFunc1(word(XNetMsg[XNetdata2] & 0x3F, XNetMsg[XNetdata3]), XNetMsg[XNetdata4]); + } + else if (XNetMsg[XNetdata1] == 0x21) { //Funktionsbefehl Gruppe2 0000 F8 F7 F6 F5 + if (notifyXNetLocoFunc2) + notifyXNetLocoFunc2(word(XNetMsg[XNetdata2] & 0x3F, XNetMsg[XNetdata3]), XNetMsg[XNetdata4]); + } + else if (XNetMsg[XNetdata1] == 0x22) { //Funktionsbefehl Gruppe3 0000 F12 F11 F10 F9 + if (notifyXNetLocoFunc3) + notifyXNetLocoFunc3(word(XNetMsg[XNetdata2] & 0x3F, XNetMsg[XNetdata3]), XNetMsg[XNetdata4]); + } + else if (XNetMsg[XNetdata1] == 0x23 || XNetMsg[XNetdata1] == 0xF3) { //Funktionsbefehl Gruppe4 F20-F13 + //0xF3 = undocumented command is used when a mulitMAUS is controlling functions f20..f13. + if (notifyXNetLocoFunc4) + notifyXNetLocoFunc4(word(XNetMsg[XNetdata2] & 0x3F, XNetMsg[XNetdata3]), XNetMsg[XNetdata4]); + } + else if (XNetMsg[XNetdata1] == 0x28) { //Funktionsbefehl Gruppe5 F28-F21 + if (notifyXNetLocoFunc5) + notifyXNetLocoFunc5(word(XNetMsg[XNetdata2] & 0x3F, XNetMsg[XNetdata3]), XNetMsg[XNetdata4]); + } + else unknown(); //unbekannte Anfrage + break; + } + case 0x42: //Accessory Decoder information request + if (notifyXNetTrntInfo && XNetSlaveMode == 0x00) + notifyXNetTrntInfo(DirectedOps, XNetMsg[XNetdata1], XNetMsg[XNetdata2]); + //XNetclear(); //alte Nachricht l�schen + break; + case 0x52: //Accessory Decoder operation request + if (notifyXNetTrnt) + notifyXNetTrnt((XNetMsg[XNetdata1] << 2) | ((XNetMsg[XNetdata2] & 0b110) >> 1), XNetMsg[XNetdata2]); + //XNetMsg[XNetdata2] = 0000 ABBP + //A = Weichenausgang(Spulenspannung EIN/AUS) + //BB = Adresse des Dekoderport 1..4 + //P = Ausgang (Gerade = 0 / Abzweigen = 1) + //XNetclear(); //alte Nachricht l�schen + break; + default: //Befehl in Zentrale nicht vorhanden + unknown(); //unbekannte Anfrage + } + + if (XNetSlaveMode != 0x00) { //SLAVE-MODE + if (XNetMsgCallByte == GENERAL_BROADCAST) { //Central Station broadcast data + if (XNetMsg[XNetheader] == 0x61) { + if ((XNetMsg[XNetdata1] == 0x01) && (XNetMsg[XNetdata2] == 0x60)) { + // Normal Operation Resumed + Railpower = csNormal; + if (notifyXNetPower) + notifyXNetPower(Railpower); + } + else if ((XNetMsg[XNetdata1] == 0x00) && (XNetMsg[XNetdata2] == 0x61)) { + // Track power off + Railpower = csTrackVoltageOff; + if (notifyXNetPower) + notifyXNetPower(Railpower); + } + else if ((XNetMsg[XNetdata1] == 0x08)) { + // Track Short + Railpower = csShortCircuit; + if (notifyXNetPower) + notifyXNetPower(Railpower); + } + else if ((XNetMsg[XNetdata1] == 0x02) && (XNetMsg[XNetdata2] == 0x63)) { + // Service Mode Entry + Railpower = csServiceMode; + if (notifyXNetPower) + notifyXNetPower(Railpower); + } + } + else if (XNetMsg[XNetheader] == 0x81) { + if ((XNetMsg[XNetdata1] == 0x00) && (XNetMsg[XNetdata2] == 0x81)) { + //Emergency Stop + Railpower = csEmergencyStop; + if (notifyXNetPower) + notifyXNetPower(Railpower); + } + } + else if ((XNetMsg[XNetheader] & 0xF0) == 0x40) { + //R�ckmeldung Schaltinformation + byte len = (XNetMsg[XNetheader] & 0x0F) / 2; //each Adr and Data + for (byte i = 1; i <= len; i++) { + notifyXNetFeedback((XNetMsg[XNetheader+(i*2)-1] << 2) | ((XNetMsg[XNetheader+(i*2)] & 0b110) >> 1), XNetMsg[XNetheader+(i*2)]); + //XNetMsg[XNetdata2] = 0000 ABBP + //A = Weichenausgang(Spulenspannung EIN/AUS) + //BB = Adresse des Dekoderport 1..4 + //P = Ausgang (Gerade = 0 / Abzweigen = 1) + } + } + else if (XNetMsg[XNetheader] == 0x05 && XNetMsg[XNetdata1] == 0xF1) { + //DCC FAST CLOCK set request + /* 0x05 0xF1 TCODE1 TCODE2 TCODE3 TCODE4 [XOR] + 00mmmmmm TCODE1, mmmmmm = denotes minutes, range 0...59. + 100HHHHH TCODE2, HHHHH = denotes hours, range 0...23. + 01000www TCODE3, www = denotes day of week, 0=monday, 1=tuesday, a.s.o. + 110fffff TCODE4, fffff = denotes speed ratio, range 0..31. (0=stopped) + */ + } + } //Broadcast END + else if (XNetMsgCallByte == ACK_REQ) { //Central Station ask client for ACK? + uint8_t AckSeq[] = {0x00, 0x20, 0x20}; + XNetsend (AckSeq, 3); + } //ACK END + else /* if (XNetMsgCallByte == MY_ADDRESS*) */ { //Central Station send data to us? + switch (XNetMsg[XNetheader]) { + case 0x52: // Some other device asked for an accessory change + break; + case 0x61: //Zustand + break; + case 0x62: //Version + if (XNetMsg[XNetdata1] == 0x22) { + switch (XNetMsg[XNetdata2]) { + case 0x00: //csNormal + Railpower = csNormal; + if (notifyXNetPower) + notifyXNetPower(Railpower); + break; + case 0x02: //csTrackVoltageOff + Railpower = csTrackVoltageOff; + if (notifyXNetPower) + notifyXNetPower(Railpower); + break; + case 0x01: //csEmergencyStop + Railpower = csEmergencyStop; + if (notifyXNetPower) + notifyXNetPower(Railpower); + break; + case 0x08: //csServiceMode + Railpower = csServiceMode; + if (notifyXNetPower) + notifyXNetPower(Railpower); + break; + } + uint8_t commandVersionSequence[] = { 0x00, 0x21, 0x21, 0x00}; + XNetsend (commandVersionSequence, 4); + } + break; + case 0x63: + break; + case 0xE3: //Antwort abgefrage Funktionen F13-F28 + break; + case 0xE4: //Antwort der abgefragen Lok + break; + case 0x42: //Antwort Schaltinformation + break; + case 0xE1: //Err Lok control + break; + } //switch HEADER END + } //MY_ADDRESS END + } //ENDE SLAVE MODE +} + +//-------------------------------------------------------------------------------------------- +//Befehl in Zentrale nicht vorhanden +void XpressNetMasterClass::unknown(void) //unbekannte Anfrage +{ + if (XNetSlaveMode == 0x00) { //MASTER MODE + uint8_t NotIn[] = {DirectedOps, 0x61, 0x82, 0xE3 }; + XNetsend(NotIn, 4); + } +} + +//-------------------------------------------------------------------------------------------- +void XpressNetMasterClass::getNextXNetAdr(void) +{ + XNetAdr++; //n�chste Adresse im XNet + if (XNetAdr > 31) { //wenn letzte erreicht von Beginn! + //search for used slots to call them more then unused: + while((SlotLokUse[XNetAdr % 32] == 0xFFFF) && (XNetAdr > 31)) { //slot used an loco address? + XNetAdr++; + } + if ((XNetAdr % 32) == 0) + XNetAdr = 1; //start at the beginning + + } + /* + AAAAA = XNetAdr % 32 (Slot 1 to 31) + Call Byte Definitions: + Call_Response P11A AAAA + Call_Inquiry P10A AAAA + Call_Request_Ack_From_Device P00A AAAA + Call_Broadcast P110 0000 + Call_Transfer_Err P11A AAAA + */ + //Generate byte for the new window: + CallByteInquiry = callByteParity((XNetAdr % 32) | 0x40); // | 0x100; // the address for Call Byte Window + RequestAck = callByteParity((XNetAdr % 32)| 0x00); // | 0x100; // the address for a request acknowlegement sent + DirectedOps = callByteParity((XNetAdr % 32)| 0x60); // | 0x100; // the address when we are sending ops + + //Send CallByteInquiry for next Addr: + uint8_t NormalInquiry[] = { CallByteInquiry }; + XNetsend(NormalInquiry, 1); +} + +//-------------------------------------------------------------------------------------------- +//Zustand der Gleisversorgung setzten +void XpressNetMasterClass::setPower(byte Power) +{ + switch (Power) { + case csNormal: { + if (XNetSlaveMode == 0x00) { //MASTER MODE + uint8_t NormalPower[] = { GENERAL_BROADCAST, 0x61, 0x01, 0x60 }; + XNetsend(NormalPower, 4); + } + else { + uint8_t PowerAn[] = {0x00, 0x21, 0x81, 0xA0 }; + XNetsend(PowerAn, 4); + } + break; + } + case csEmergencyStop: { + if (XNetSlaveMode == 0x00) { //MASTER MODE + uint8_t EStopPower[] = { GENERAL_BROADCAST, 0x81, 0x00, 0x81 }; + XNetsend(EStopPower, 4); + } + else { + uint8_t EmStop[] = {0x00, 0x80, 0x80 }; + XNetsend(EmStop, 3); + } + break; + } + case csTrackVoltageOff: { + if (XNetSlaveMode == 0x00) { //MASTER MODE + uint8_t OffPower[] = { GENERAL_BROADCAST, 0x61, 0x00, 0x61 }; + XNetsend(OffPower, 4); + } + else { + uint8_t PowerAus[] = {0x00, 0x21, 0x80, 0xA1 }; + XNetsend(PowerAus, 4); + } + break; + } + case csShortCircuit: { + uint8_t ShortPower[] = { GENERAL_BROADCAST, 0x61, 0x08, 0x69 }; + XNetsend(ShortPower, 4); + break; + } + case csServiceMode: { + uint8_t ServicePower[] = { GENERAL_BROADCAST, 0x61, 0x02, 0x63 }; + XNetsend(ServicePower, 4); + break; + } + } + + if (Railpower != Power) { + Railpower = Power; //Save new Power State + if (notifyXNetPower) + notifyXNetPower(Railpower); + } + +} + +//-------------------------------------------------------------------------------------------- +//R�ckmeldung verteilen +void XpressNetMasterClass::setBCFeedback(byte data1, byte data2) +{ + uint8_t Feedback[] = { GENERAL_BROADCAST, 0x42, data1, data2, 0x00}; + XNetsend(Feedback, 5); +} + +//-------------------------------------------------------------------------------------------- +//Lok in use (Request/Vormerken) +void XpressNetMasterClass::ReqLocoBusy(uint16_t Adr) { + //SetLocoBusyAdr = Adr; + //BusyAdrCount = 0; + //AddBusySlot(0, Adr); + + for (byte s = 1; s < 32; s++) { + if (SlotLokUse[s] == Adr) { //if in use from X-Net device -> set busy + SetLocoBusy(callByteParity(s | 0x60), SlotLokUse[s]); + SlotLokUse[s] = 0; //clean slot + } + } + SlotLokUse[0] = Adr; +} + +//-------------------------------------------------------------------------------------------- +//Lok in use (Busy) +void XpressNetMasterClass::SetLocoBusy(uint8_t UserOps, uint16_t Adr) { + uint8_t LocoInfo[] = { UserOps, 0xE3, 0x40, 0x00, 0x00, 0x00 }; + if (Adr > 99) //Xpressnet long addresses (100 to 9999: AH/AL = 0xC064 to 0xE707) + LocoInfo[3] = (Adr >> 8) | 0xC0; + else LocoInfo[3] = Adr >> 8; //short addresses (0 to 99: AH = 0x0000 and AL = 0x0000 to 0x0063) + LocoInfo[4] = Adr & 0xFF; + getXOR(LocoInfo, 6); + XNetsend(LocoInfo, 6); +} + +//-------------------------------------------------------------------------------------------- +//Lokinfo an XNet Melden +void XpressNetMasterClass::SetLocoInfo(uint8_t UserOps, uint8_t Speed, uint8_t F0, uint8_t F1) { + SetLocoInfo(UserOps, Fahrstufe, Speed, F0, F1); +} + +void XpressNetMasterClass::SetLocoInfo(uint8_t UserOps, uint8_t Steps, uint8_t Speed, uint8_t F0, uint8_t F1) { + //0xE4 | 0000 BFFF | RVVV VVVV | 000F FFFF | FFFF FFFF | XOr + // B = 0 free; B = 1 controlled by another Device + // FFF -> 000 = 14; 001 = 27; 010 = 28; 100 = 128 + //LokInfo Identification Speed FA FB + byte v = Speed; + if (Steps == Loco28 || Steps == Loco27) { + v = (Speed & 0x0F) << 1; //Speed Bit + v |= (Speed >> 4) & 0x01; //Addition Speed Bit + v |= 0x80 & Speed; //Dir + } + uint8_t LocoInfo[] = {UserOps, 0xE4, Steps, v, F0, F1, 0x00 }; + if (SlotLokUse[UserOps & 0x1F] == 0x00) //has Slot a Address? + LocoInfo[2] |= 0x08; //set BUSY + getXOR(LocoInfo, 7); + XNetsend(LocoInfo, 7); +} + +//-------------------------------------------------------------------------------------------- +//LokFkt an XNet Melden +void XpressNetMasterClass::SetFktStatus(uint8_t UserOps, uint8_t F4, uint8_t F5) { + //F4 = F20-F13 + //F5 = F28-F21 + uint8_t LocoFkt[] = {UserOps, 0xE3, 0x52, F4, F5, 0x00 }; + getXOR(LocoFkt, 6); + XNetsend(LocoFkt, 6); +} + +//-------------------------------------------------------------------------------------------- +//Lokinfo for MultiMaus +void XpressNetMasterClass::SetLocoInfoMM(uint8_t UserOps, uint8_t Steps, uint8_t Speed, uint8_t F0, uint8_t F1, uint8_t F2, uint8_t F3) { + //E7 0x04=128 Steps||0x0C=128+Busy (Dir,Speed) (F0,F4,F3,F2,F1) F5-F12 F13-F20 0x00 0x00 CRC + byte v = Speed; + if (Steps == Loco28 || Steps == Loco27) { + v = (Speed & 0x0F) << 1; //Speed Bit + v |= (Speed >> 4) & 0x01; //Addition Speed Bit + v |= 0x80 & Speed; //Dir + } + uint8_t LocoInfo[] = {UserOps, 0xE7, /*0x04*/ Steps, v, 0x20, F1, F2, F3, 0x00, 0x00 }; + /*ERROR: Steps change form > 99 to 28steps only!!! why?? (This info comes from DCC:"notifyLokAll(..)"*/ + LocoInfo[4] |= F0; + getXOR(LocoInfo, 10); + XNetsend(LocoInfo, 10); +} + +//-------------------------------------------------------------------------------------------- +void XpressNetMasterClass::setSpeed(uint16_t Adr, uint8_t Steps, uint8_t Speed) { + //Locomotive speed and direction operation + // 0xE4 | Ident | AH | AL | RV | XOr + // Ident: 0x10 = F14; 0x11 = F27; 0x12 = F28; 0x13 = F128 + // RV = RVVV VVVV Dirction and Speed + byte v = Speed; + if (Steps == Loco28 || Steps == Loco27) { + v = (Speed & 0x0F) << 1; //Speed Bit + v |= (Speed >> 4) & 0x01; //Addition Speed Bit + v |= 0x80 & Speed; //Dir + } + uint8_t LocoInfo[] = {0x00, 0xE4, 0x13, 0x00, 0x00, v, 0x00 }; //default to 128 Steps! + switch (Steps) { + case 14: LocoInfo[2] = 0x10; break; + case 27: LocoInfo[2] = 0x11; break; + case 28: LocoInfo[2] = 0x12; break; + } + if (Adr > 99) //Xpressnet long addresses (100 to 9999: AH/AL = 0xC064 to 0xE707) + LocoInfo[3] = (Adr >> 8) | 0xC0; + else LocoInfo[3] = Adr >> 8; //short addresses (0 to 99: AH = 0x0000 and AL = 0x0000 to 0x0063) + LocoInfo[4] = Adr & 0xFF; + getXOR(LocoInfo, 7); + XNetsend(LocoInfo, 7); +} + +//-------------------------------------------------------------------------------------------- +//Gruppe 1: 0 0 0 F0 F4 F3 F2 F1 +void XpressNetMasterClass::setFunc0to4(uint16_t Adr, uint8_t G1) { + uint8_t LocoInfo[] = {0x00, 0xE4, 0x20, 0x00, 0x00, G1, 0x00 }; + if (Adr > 99) //Xpressnet long addresses (100 to 9999: AH/AL = 0xC064 to 0xE707) + LocoInfo[3] = (Adr >> 8) | 0xC0; + else LocoInfo[3] = Adr >> 8; //short addresses (0 to 99: AH = 0x0000 and AL = 0x0000 to 0x0063) + LocoInfo[4] = Adr & 0xFF; + getXOR(LocoInfo, 7); + XNetsend(LocoInfo, 7); +} + +//-------------------------------------------------------------------------------------------- +//Gruppe 2: 0 0 0 0 F8 F7 F6 F5 +void XpressNetMasterClass::setFunc5to8(uint16_t Adr, uint8_t G2) { + uint8_t LocoInfo[] = {0x00, 0xE4, 0x21, 0x00, 0x00, G2, 0x00 }; + if (Adr > 99) //Xpressnet long addresses (100 to 9999: AH/AL = 0xC064 to 0xE707) + LocoInfo[3] = (Adr >> 8) | 0xC0; + else LocoInfo[3] = Adr >> 8; //short addresses (0 to 99: AH = 0x0000 and AL = 0x0000 to 0x0063) + LocoInfo[4] = Adr & 0xFF; + getXOR(LocoInfo, 7); + XNetsend(LocoInfo, 7); +} + +//-------------------------------------------------------------------------------------------- +//Gruppe 3: 0 0 0 0 F12 F11 F10 F9 +void XpressNetMasterClass::setFunc9to12(uint16_t Adr, uint8_t G3) { + uint8_t LocoInfo[] = {0x00, 0xE4, 0x22, 0x00, 0x00, G3, 0x00 }; + if (Adr > 99) //Xpressnet long addresses (100 to 9999: AH/AL = 0xC064 to 0xE707) + LocoInfo[3] = (Adr >> 8) | 0xC0; + else LocoInfo[3] = Adr >> 8; //short addresses (0 to 99: AH = 0x0000 and AL = 0x0000 to 0x0063) + LocoInfo[4] = Adr & 0xFF; + getXOR(LocoInfo, 7); + XNetsend(LocoInfo, 7); +} + +//-------------------------------------------------------------------------------------------- +//Gruppe 4: F20 F19 F18 F17 F16 F15 F14 F13 +void XpressNetMasterClass::setFunc13to20(uint16_t Adr, uint8_t G4) { + uint8_t LocoInfoMM[] = {0x00, 0xE4, 0xF3, 0x00, 0x00, G4, 0x00 }; //normal: 0x23! + if (Adr > 99) //Xpressnet long addresses (100 to 9999: AH/AL = 0xC064 to 0xE707) + LocoInfoMM[3] = (Adr >> 8) | 0xC0; + else LocoInfoMM[3] = Adr >> 8; //short addresses (0 to 99: AH = 0x0000 and AL = 0x0000 to 0x0063) + LocoInfoMM[4] = Adr & 0xFF; + getXOR(LocoInfoMM, 7); + XNetsend(LocoInfoMM, 7); + + LocoInfoMM[0] = 0x00; + LocoInfoMM[1] = 0xE4; + LocoInfoMM[2] = 0x23; //MultiMaus only + LocoInfoMM[3] = Adr >> 8; + LocoInfoMM[4] = Adr & 0xFF; + LocoInfoMM[5] = G4; + getXOR(LocoInfoMM, 7); + XNetsend(LocoInfoMM, 7); +} + +//-------------------------------------------------------------------------------------------- +//Gruppe 5: F28 F27 F26 F25 F24 F23 F22 F21 +void XpressNetMasterClass::setFunc21to28(uint16_t Adr, uint8_t G5) { + uint8_t LocoInfo[] = {0x00, 0xE4, 0x28, 0x00, 0x00, G5, 0x00 }; + if (Adr > 99) //Xpressnet long addresses (100 to 9999: AH/AL = 0xC064 to 0xE707) + LocoInfo[3] = (Adr >> 8) | 0xC0; + else LocoInfo[3] = Adr >> 8; //short addresses (0 to 99: AH = 0x0000 and AL = 0x0000 to 0x0063) + LocoInfo[4] = Adr & 0xFF; + getXOR(LocoInfo, 7); + XNetsend(LocoInfo, 7); +} + +//-------------------------------------------------------------------------------------------- +//check if there are slots with the same loco, set them to busy +void XpressNetMasterClass::SetBusy(uint8_t slot) { + for (byte s = 1; s < 32; s++) { + if ((s != slot) && (SlotLokUse[s] == SlotLokUse[slot])) { //if in other Slot -> set busy + SetLocoBusy(callByteParity(s | 0x60), SlotLokUse[s]); + SlotLokUse[s] = 0; //clean slot that informed as busy & let it activ + } + } +} + +//-------------------------------------------------------------------------------------------- +//Add loco to slot. Slot 0 is reserved for non XpressNet Device +void XpressNetMasterClass::AddBusySlot(uint8_t UserOps, uint16_t Adr) { + if (Adr == 0 || SlotLokUse[UserOps & 0x1F] == Adr) //skip if already in store! + return; + SlotLokUse[UserOps & 0x1F] = Adr; //store loco that is used + SetBusy(UserOps & 0x1F); //make other busy +} + + +//-------------------------------------------------------------------------------------------- +//TrntPos request return +void XpressNetMasterClass::SetTrntStatus(uint8_t UserOps, uint8_t Address, uint8_t Data) { + if (XNetSlaveMode == 0x00) { //MASTER MODE + // data=ITTN ZZZZ Z=Weichenausgang1+2 (Aktive/Inaktive); + //0x42 AAAA AAAA ITTN ZZZZ + //I = is 1, the switching command requested has not been completed + //TT = turnout group (0-3) + //N = N=0 is the lower nibble, N=1 the upper nibble + //Z3 Z2 Z1 Z0 = (Z1,Z0|Z3,Z2) 01 "left", 10 "right" + uint8_t TrntInfo[] = {UserOps, 0x42, 0x00, 0x00, 0x00 }; + TrntInfo[2] = Address; + TrntInfo[3] = Data; + getXOR(TrntInfo, 5); + XNetsend(TrntInfo, 5); + } +} + +//-------------------------------------------------------------------------------------------- +//Trnt Change position +void XpressNetMasterClass::SetTrntPos(uint16_t Address, uint8_t state, uint8_t active) { + //Accessory Decoder operation request (0x52 | AAAA AAAA | 1000 ABBP | XOr) + //1. Byte = AAAA AAAA -> (Adresse >> 2) - ohne Stupenauswahl + //2. Byte = 1000 ABBP + //A = Weichenausgang(Spulenspannung EIN/AUS) + //BB = Adresse des Dekoderport 1..4 + //P = Ausgang (Gerade = 0 / Abzweigen = 1) + uint8_t TrntInfo[] = {0x00, 0x52, 0x00, 0x00, 0x00 }; + TrntInfo[2] = Address >> 2; + TrntInfo[3] = 0x80; + TrntInfo[3] |= (Address & 0x03) << 1; + TrntInfo[3] |= (active & 0x01) << 3; + TrntInfo[3] |= state & 0x01; + getXOR(TrntInfo, 5); + XNetsend(TrntInfo, 5); +} + +//-------------------------------------------------------------------------------------------- +//return a CV data read +void XpressNetMasterClass::setCVReadValue(uint8_t cvAdr, uint8_t value) { + //Save?: + if (cvAdr != 0xFF) { + XNetCVAdr = cvAdr+1; + XNetCVvalue = value; + } +} + +//-------------------------------------------------------------------------------------------- +//no ACK +void XpressNetMasterClass::setCVNack(void) { + XNetCVAdr = 0; + XNetCVvalue = 0xFF; +} + +//-------------------------------------------------------------------------------------------- +// send along a bunch of bytes to the Command Station +void XpressNetMasterClass::XNetsend(byte *dataString, byte byteCount) { + #if defined (XNetDEBUG) + if (byteCount > 1) { + XNetSerial.print("XTX: "); + } + #endif + + XNetBuffer[XNetBufferStore].length = byteCount; + + for (byte i = 0; i < byteCount; i++) { + XNetBuffer[XNetBufferStore].data[i] = *dataString; //add data to Buffer + dataString++; + + #if defined (XNetDEBUG) + if (byteCount > 1) { + if (XNetBuffer[XNetBufferStore].data[i] < 0x10) + XNetSerial.print(" 0x0"); + else XNetSerial.print(" 0x"); + if (i == 0 && XNetSlaveMode == 0x00) //MASTER-MODE + XNetSerial.print("1"); + XNetSerial.print(XNetBuffer[XNetBufferStore].data[i], HEX); + } + #endif + } + + XNetBufferStore++; + if (XNetBufferStore >= XNetBufferSize) + XNetBufferStore = 0; + + #if defined (XNetDEBUG) + if (byteCount > 1) + XNetSerial.println(); + #endif +} + +//-------------------------------------------------------------------------------------------- +// calculate the XOR +void XpressNetMasterClass::getXOR (uint8_t *data, byte length) { + byte XOR = 0x00; + data++; //without CallByte + for (byte i = 0; i < (length-2); i++) { + XOR = XOR ^ *data; + data++; + } + *data = XOR; +} + +//-------------------------------------------------------------------------------------------- +// calculate the parity bit in the call byte for this guy +byte XpressNetMasterClass::callByteParity (byte me) { + byte parity = (1==0); + byte vv; + me &= 0x7f; + vv = me; + + while (vv) { + parity = !parity; + vv &= (vv-1); + } + if (parity) me |= 0x80; + return me; +} + +//-------------------------------------------------------------------------------------------- +#if defined(__AVR__) +//Interrupt routine for writing via Serial +#if defined(__AVR_ATmega8__) +ISR(USART_TX_vect) { + XpressNetMasterClass::handle_TX_interrupt(); //weiterreichen an die Funktion +} +#elif defined(SERIAL_PORT_0) + ISR(USART_TX_vect) { + XpressNetMasterClass::handle_TX_interrupt(); //weiterreichen an die Funktion + } +#else + ISR(USART1_TX_vect) { + XpressNetMasterClass::handle_TX_interrupt(); //weiterreichen an die Funktion + } +#endif + +// Interrupt handling for receive Data +// static +inline void XpressNetMasterClass::handle_TX_interrupt() +{ + if (active_object) + { + active_object->XNetSendNext(); //n�chste Byte Senden + } +} +#endif + +#if defined(STM32F1) +extern "C" void usart2_isr (void) { + // check with which type of interrupt we are dealing + if (usart_get_flag(XN_USART_INST, USART_SR_RXNE)) + XpressNetMasterClass::handle_RX_interrupt(); + + if (usart_get_flag(XN_USART_INST, USART_SR_TC)) + XpressNetMasterClass::handle_TX_interrupt(); +} + +inline void XpressNetMasterClass::handle_TX_interrupt() { + if (active_object) active_object->XNetSendNext(); // send next byte +} +#endif + +//-------------------------------------------------------------------------------------------- +void XpressNetMasterClass::XNetSendNext(void) { + uint16_t data9 = XNetReadBuffer(); + + if (data9 > 0x1FF) { + //nothing less to send out. + digitalWrite(MAX485_CONTROL, LOW); //RECEIVE_MODE + #if defined(STM32F1) + // disable Tx empty interrupt, otherwise it goes haywire + //usart_disable_tx_interrupt(XN_USART_INST); + /* TODO we need another hal function, like + * usart_enable/disable_tc_interrupt(), the rx-empty + * interrupt messes with the last byte due to the switch to + * receive mode above. + */ + USART_CR1(XN_USART_INST) &= ~USART_CR1_TCIE; + #endif + if (XNet_state == XNet_send_data && XNetSlaveMode == 0x00) + XNet_state = XNet_get_callbyte; + else XNet_state = XNet_wait_receive; //wait for receive from client + return; + } + + #if defined(RAW_DATA_MODE) + digitalWrite(MAX485_CONTROL, HIGH); //SEND_MODE + if (XpressNet_RAW_send) + XpressNet_RAW_send(data9); + XNetSendNext(); + + #elif defined(__AVR__) + digitalWrite(MAX485_CONTROL, HIGH); //SEND_MODE + #ifdef __AVR_ATmega8__ + /* wait for empty transmit buffer */ + //while (!(UCSR0A & (1 << UDRE0))) {} + /* put the data into buffer, and send */ + UCSRB &= ~(1 << TXB8); + if (data9 & 0x100) //is there a 9th bit? + UCSRB |= (1 << TXB8); + UDR = data9; + #elif defined(SERIAL_PORT_0) + /* wait for empty transmit buffer */ + //while (!(UCSR0A & (1 << UDRE0))) {} + /* put the data into buffer, and send */ + UCSR0B &= ~(1 << TXB80); + if (data9 & 0x100) //is there a 9th bit? + UCSR0B |= (1 << TXB80); + UDR0 = data9; + #else + /* wait for empty transmit buffer */ + //while (!(UCSR1A & (1 << UDRE1))) {} + /* put the data into buffer, and send */ + UCSR1B &= ~(1 << TXB81); + if (data9 & 0x100) //is there a 9th bit? + UCSR1B |= (1 << TXB81); + UDR1 = data9; + #endif + + #elif defined(ARDUINO_ESP8266_ESP01) || defined(ARDUINO_ESP8266_WEMOS_D1MINI) + rs485.write(data9); + XNetSendNext(); + + #elif defined(STM32F1) + digitalWrite(MAX485_CONTROL, HIGH); //SEND_MODE + /* put the data into buffer, and send */ + usart_send(XN_USART_INST, data9); + // enable Tx empty interrupt + //usart_enable_tx_interrupt(XN_USART_INST); + // TODO see comment about miss hal function above + USART_CR1(XN_USART_INST) |= USART_CR1_TCIE; + + #endif + +} + +//-------------------------------------------------------------------------------------------- +uint16_t XpressNetMasterClass::XNetReadBuffer() { + if (XNetBuffer[XNetBufferSend].length == 0x00) + return 0xFFFF; //no data in Buffer! + + uint16_t data = XNetBuffer[XNetBufferSend].data[XNetBufferSend_data]; + if (XNetBufferSend_data == 0x00) { //it is a CALLBYTE and we are MASTER! + data |= 0x100; //add 9th bit + + if (XNetSlaveMode != 0x00) { //SLAVE-MODE? + XNetBufferSend_data++; //next data byte + return 0xFFFF; //stop here, just send one packet as answer! + } + } + + XNetBufferSend_data++; //next data byte + if (XNetBufferSend_data >= XNetBuffer[XNetBufferSend].length) { //any byte left to send? + XNetBufferSend_data = 0; //Reset data counter + XNetBuffer[XNetBufferSend].length = 0x00; //Reset Bufferstore + XNetBufferSend++; //next byte + if (XNetBufferSend >= XNetBufferSize) //overflow? + XNetBufferSend = 0; //start value + } + return data; +} + +#if defined(__AVR__) +//Interrupt reading data! +//-------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------- +//Interrupt routine for reading via Serial +#ifdef __AVR_ATmega8__ +ISR(USART_RX_vect) { + XpressNetMasterClass::handle_RX_interrupt(); //weiterreichen an die Funktion +} +#elif defined(SERIAL_PORT_0) + ISR(USART_RX_vect) { + XpressNetMasterClass::handle_RX_interrupt(); //weiterreichen an die Funktion + } + +#else + ISR(USART1_RX_vect) { + XpressNetMasterClass::handle_RX_interrupt(); //weiterreichen an die Funktion + } +#endif + +// Interrupt handling for receive Data +// static +inline void XpressNetMasterClass::handle_RX_interrupt() +{ + if (active_object) + { + active_object->XNetReceive(); //Byte lesen + } +} +#endif + +#if defined(STM32F1) +inline void XpressNetMasterClass::handle_RX_interrupt() { + if (active_object) active_object->XNetReceive(); // read byte +} +#endif + +//-------------------------------------------------------------------------------------------- +//Serial einlesen: +void XpressNetMasterClass::XNetReceive(void) +{ + #if defined(__AVR__) + + // if there's any serial available, read it: + #ifdef __AVR_ATmega8__ + // Filter the 9th bit, then return + if (UCSRB & (1 << RXB8)) { + XNetMsgCallByte = UDR; //only store data + XNetSlaveMode = XNetSlaveCycle; //reactivate SLAVE MODE + // XNetMsg[XNetlength] = 0x00; + if (XNetMsgCallByte == MY_ADDRESS) + XNetSendNext(); //start sending out by interrupt + } + else { + XNetMsgBuffer[XNetBufferlength]++; //weitere Nachrichtendaten + XNetMsgBuffer[XNetMsgBuffer[XNetBufferlength]] = UDR; // Zeichen aus UDR an Aufrufer zurueckgeben + } + #elif defined(SERIAL_PORT_0) + // Filter the 9th bit, then return + if (UCSR0B & (1 << RXB80)) { + XNetMsgCallByte = UDR0; //only store data + XNetSlaveMode = XNetSlaveCycle; //reactivate SLAVE MODE + // XNetMsg[XNetlength] = 0x00; + if (XNetMsgCallByte == MY_ADDRESS) + XNetSendNext(); //start sending out by interrupt + } + else { + XNetMsgBuffer[XNetBufferlength]++; //weitere Nachrichtendaten + XNetMsgBuffer[XNetMsgBuffer[XNetBufferlength]] = UDR0; // Zeichen aus UDR an Aufrufer zurueckgeben + } + #else + // Filter the 9th bit, then return + if (UCSR1B & (1 << RXB81)) { + XNetMsgCallByte = UDR1; //only store data + XNetSlaveMode = XNetSlaveCycle; //reactivate SLAVE MODE + // XNetMsg[XNetlength] = 0x00; + if (XNetMsgCallByte == MY_ADDRESS) + XNetSendNext(); //start sending out by interrupt + } + else { + XNetMsgBuffer[XNetBufferlength]++; //weitere Nachrichtendaten + XNetMsgBuffer[XNetMsgBuffer[XNetBufferlength]] = UDR1; // Zeichen aus UDR an Aufrufer zurueckgeben + } + #endif + #elif defined(ARDUINO_ESP8266_ESP01) || defined(ARDUINO_ESP8266_WEMOS_D1MINI) + if (rs485.available()) { + int data = rs485.read(); + if (data > 0xFF) { + XNetMsgCallByte = data; + XNetSlaveMode = XNetSlaveCycle; //reactivate SLAVE MODE + if (XNetMsgCallByte == MY_ADDRESS) + XNetSendNext(); //start sending out by interrupt + } + else { + XNetMsgBuffer[XNetBufferlength]++; //weitere Nachrichtendaten + XNetMsgBuffer[XNetMsgBuffer[XNetBufferlength]] = data; // Zeichen aus UDR an Aufrufer zurueckgeben + } + } + #elif defined(STM32F1) + // Filter the 9th bit, then return call byte + uint16_t rx_data_9b = usart_recv(XN_USART_INST); + if (rx_data_9b & (1 << 8)) { + XNetMsgCallByte = (uint8_t) (rx_data_9b & 0xFF); //only store data + XNetSlaveMode = XNetSlaveCycle; //reactivate SLAVE MODE + // XNetMsg[XNetlength] = 0x00; + if (XNetMsgCallByte == MY_ADDRESS) + XNetSendNext(); //start sending out by interrupt + } + else { + XNetMsgBuffer[XNetBufferlength]++; //weitere Nachrichtendaten + XNetMsgBuffer[XNetMsgBuffer[XNetBufferlength]] = (uint8_t) (rx_data_9b & 0xFF); // Zeichen aus UDR an Aufrufer zurueckgeben + } + #endif + + if (XNetMsgBuffer[XNetBufferlength] >= 2) { // header and one data byte or more received + //Check length - length is inside header but without header and xor! + if (((XNetMsgBuffer[1] & 0x0F) + 2) == XNetMsgBuffer[XNetBufferlength]) { //reach data length? + for (byte i = 0; i < XNetMsgBuffer[XNetBufferlength]; i++) { + XNetMsg[i] = XNetMsgBuffer[i+1]; + } + XNetDataReady = true; + XNetMsgBuffer[XNetBufferlength] = 0x00; //clear! + } + if (XNetMsgBuffer[XNetBufferlength] >= (XNetMaxDataLength) ) { //overflow!!! + XNetMsgBuffer[XNetBufferlength] = 0x00; //clear! + } + } +} + +//-------------------------------------------------------------------------------------------- +//L�schen des letzten gesendeten Befehls +void XpressNetMasterClass::XNetclear(void) +{ + //Reset Message + XNetMsg[XNetheader] = 0x00; + XNetMsg[XNetdata1] = 0x00; + XNetMsg[XNetdata2] = 0x00; + XNetMsg[XNetdata3] = 0x00; + XNetMsg[XNetdata4] = 0x00; + XNetMsg[XNetdata5] = 0x00; + XNetMsg[XNetdata6] = 0x00; + XNetMsg[XNetdata7] = 0x00; + XNetMsgCallByte = 0x00; //Reset CallByte +} diff --git a/c6021light/src/XpressNet/XpressNetMaster.h b/c6021light/src/XpressNet/XpressNetMaster.h new file mode 100644 index 0000000..e1afc34 --- /dev/null +++ b/c6021light/src/XpressNet/XpressNetMaster.h @@ -0,0 +1,330 @@ +/* + XpressNetMaster.h - library for XpressNet Master protocoll + Copyright (c) 2020 Philipp Gahtow All right reserved. + + Notice: + Support for XPressNet Version 3.0 or 3.6! + + Change History: + - timing issue when change to the next slot + - add busy request for loco + - narrow conversation compiler warning + - speed up transmission window (Callbyte) and adjust timing + - add MultiMaus full Support for F13 to F20 + - add unknown message return + - add UART RX interrupt + - add UART TX Buffer with interrupt control + - fix MultiMaus CV read and write in Master Mode + - fix speed step setting + - add ack request answer in slave mode + - add feedback return to all clients + - add POM write byte and bit + - fix slave ack return + - add support for RAW Data in-/output + - add inside 'AddBusySlot' a skip if already busy by this slot + - adjust init client mode power setting + - add support ESP8266 with RS485SoftwareSerial + - fix sending long Adresses missing the two highest bits of the High bytes are set to "1" (0xC000) + - fix hanging in CV-Prog + - fix Accessory Decoder operation request Byte 2 + - fix Locomotive speed and direction operation (0xE4) Speed Steps +*/ + +// ensure this library description is only included once +#ifndef XpressNetMaster_h +#define XpressNetMaster_h + +//CONFIG: +#define XNetVersion 0x36 //System Bus Version +#define XNetID 0x10 //Zentrale: +//0x00 = LZ100; +//0x01 = LH200; +//0x10 = ROCO MultiMaus; +//0x02 = other; + +// include types & constants of Wiring core API +#if defined(WIRING) + #include +#elif ARDUINO >= 100 + #include +#else + #include + extern "C" { + #include + } +#endif + +/* From the ATMega datasheet: */ +//-------------------------------------------------------------------------------------------- +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //Arduino MEGA +#define SERIAL_PORT_1 +#undef SERIAL_PORT_0 + +#elif defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644P__) //Sanguino (other pins!) +#define SERIAL_PORT_1 +#undef SERIAL_PORT_0 + +#elif defined(ARDUINO_ESP8266_ESP01) //ESP8266 +#include +// define RS485 TX pin +#ifndef XNetRS485_TX +#define XNetRS485_TX 4 +#endif +// define RS485 RX pin +#ifndef XNetRS485_RX +#define XNetRS485_RX 2 +#endif +//remove AVR statements: +#undef SERIAL_PORT_0 +#undef SERIAL_PORT_1 + +#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) //WeMos +#include +// define RS485 TX pin +#ifndef XNetRS485_TX +#define XNetRS485_TX 2 +#endif +// define RS485 RX pin +#ifndef XNetRS485_RX +#define XNetRS485_RX 0 +#endif +//remove AVR statements: +#undef SERIAL_PORT_0 +#undef SERIAL_PORT_1 + +#elif defined(STM32F1) // c6021light/Bluepill/STM32F1 +// HAL includes +#include +#include + +// define which USART instance to use +#define XN_USART_INST USART2 +#define XN_USART_BANK GPIOA +#define XN_USART_TX_PIN GPIO_USART2_TX +#define XN_USART_RX_PIN GPIO_USART2_RX + +#define XN_USART_IRQ NVIC_USART2_IRQ + +#define XN_TXEN_BANK GPIOA +#define XN_TXEN_PIN GPIO1 + +#else //others Arduino UNO +#define SERIAL_PORT_0 +#undef SERIAL_PORT_1 +#endif + +//-------------------------------------------------------------------------------------------- + +//#define RAW_DATA_MODE //kein Serial Interface - use external communication! + +//-------------------------------------------------------------------------------------------- +//only for Debug: +//#define XNetSerial Serial //Debugging Serial +//#define XNetDEBUG //Put out the messages +//#define XNetDEBUGTime //Put out the microseconds + + +//-------------------------------------------------------------------------------------------- +#define XNetTimeUntilNext 550 //value in microseconds until the next transmission windows starts! +/*An XpressNet device designed to work with XpressNet V3 and later systems must be designed so that it +begins its transmission within 110 microseconds of receiving its transmission window. (older X-Bus +based systems required this transmission to begin with in 40 microseconds.) Command stations must be +designed to accept transmissions received up to 120 microseconds after transmitting the window. The +difference is to provide a design tolerance between the different types of devices. +Under normal conditions an XpressNet device must be designed to be able to handle the receipt of its +next transmission window between 400 microseconds and 500 milliseconds after the receipt of the last window. */ + +#define XNetTimeReadData 6000 //max time to wait until paket is finish with correct XOR + +//XpressNet Send Buffer length: +#define XNetBufferSize 5 //max Data Pakets (max: 4 Bit = 16!) +#define XNetBufferMaxData 10 //max Bytes for each Paket (max: 15!) + +//XpressNet Mode (Master/Slave) +#define XNetSlaveCycle 0xFF //max (255) cycles to Stay in SLAVE MODE when no CallByte is received + +//Fahrstufen: +#define Loco14 0x00 //FFF = 000 = 14 speed step +#define Loco27 0x01 //FFF = 001 = 27 speed step +#define Loco28 0x02 //FFF = 010 = 28 speed step +#define Loco128 0x04 //FFF = 100 = 128 speed step + +// XPressnet Call Bytes. +// broadcast to everyone, we save the incoming data and process it later. +#define GENERAL_BROADCAST 0x60 //0x160 +#define FB_BROADCAST 0xA0 //0x1A0 +#define MY_ADDRESS 0x5A //only for SLAVE-MODE Adr=31 (0x5F) or Adr=26 (0x5A)! + +#define ACK_REQ 0x9A //Acknowledge-Anforderungen beantworten mit 0x20 0x20 + +// certain global XPressnet status indicators +#define csNormal 0x00 // Normal Operation Resumed ist eingeschaltet +#define csEmergencyStop 0x01 // Der Nothalt ist eingeschaltet +#define csTrackVoltageOff 0x02 // Die Gleisspannung ist abgeschaltet +#define csShortCircuit 0x04 // Kurzschluss +#define csServiceMode 0x08 // Der Programmiermodus ist aktiv - Service Mode + +#define XNet_get_callbyte 0 //prepare next client +#define XNet_send_callbyte 1 //wait until send out data for next client +#define XNet_wait_receive 2 //wait for client answer, max 120 microsekunden +#define XNet_receive_data 3 //read client data, max 500ms +#define XNet_send_data 4 + +//XpressNet Befehl, jedes gesendete Byte +#define XNetMaxDataLength 8 +#define XNetBufferlength 0 //Read Buffer length +#define XNetheader 0 //Messageheader +#define XNetdata1 1 //Databyte1 +#define XNetdata2 2 //Databyte2 +#define XNetdata3 3 //Databyte3 +#define XNetdata4 4 //Databyte4 +#define XNetdata5 5 //Databyte5 +#define XNetdata6 6 //Databyte6 +#define XNetdata7 7 //Databyte7 + +typedef struct //Antwort/Abfragespeicher +{ + byte length; //Speicher f�r Datenl�nge + byte data[XNetBufferMaxData]; //zu sendende Daten +} XSend; + +// library interface description +class XpressNetMasterClass +{ + // user-accessible "public" interface + public: + XpressNetMasterClass(void); //Constuctor + void setup(uint8_t FStufen, uint8_t XControl); //Initialisierung Serial + #if defined(RAW_DATA_MODE) + void RAW_serial_read(unsigned int data); + #endif + void update(void); //Set new Data on the Dataline + + void setPower(byte Power); //Zustand Gleisspannung Melden + void setBCFeedback(byte data1, byte data2); //R�ckmeldedaten an Clients verteilen + + //Control Loco + void ReqLocoBusy(uint16_t Adr); //Lok Adresse besetzt melden + void SetLocoBusy(uint8_t UserOps, uint16_t Adr); //Lok besetzt melden + void SetLocoInfo(uint8_t UserOps, uint8_t Speed, uint8_t F0, uint8_t F1); //Lokinfo an XNet Melden + void SetLocoInfo(uint8_t UserOps, uint8_t Steps, uint8_t Speed, uint8_t F0, uint8_t F1); //Lokinfo an XNet Melden + void SetFktStatus(uint8_t UserOps, uint8_t F4, uint8_t F5); //LokFkt an XNet Melden + void SetLocoInfoMM(uint8_t UserOps, uint8_t Steps, uint8_t Speed, uint8_t F0, uint8_t F1, uint8_t F2, uint8_t F3); + void SetTrntStatus(uint8_t UserOps, uint8_t Address, uint8_t Data); // data=0000 00AA A=Weichenausgang1+2 (Aktive/Inaktive); + void SetTrntPos(uint16_t Address, uint8_t state, uint8_t active); //�nderung der Weichenlage + + void setSpeed(uint16_t Adr, uint8_t Steps, uint8_t Speed); + void setFunc0to4(uint16_t Adr, uint8_t G1); //Gruppe 1: 0 0 0 F0 F4 F3 F2 F1 + void setFunc5to8(uint16_t Adr, uint8_t G2); //Gruppe 2: 0 0 0 0 F8 F7 F6 F5 + void setFunc9to12(uint16_t Adr, uint8_t G3); //Gruppe 3: 0 0 0 0 F12 F11 F10 F9 + void setFunc13to20(uint16_t Adr, uint8_t G4); //Gruppe 4: F20 F19 F18 F17 F16 F15 F14 F13 + void setFunc21to28(uint16_t Adr, uint8_t G5); //Gruppe 5: F28 F27 F26 F25 F24 F23 F22 F21 + + void setCVReadValue(uint8_t cvAdr, uint8_t value); //return a CV data read + void setCVNack(void); //no ACK + + // public only for easy access by interrupt handlers + static inline void handle_RX_interrupt(); //Serial RX Interrupt bearbeiten + static inline void handle_TX_interrupt(); //Serial TX Interrupt bearbeiten + + // library-accessible "private" interface + private: + //Variables: + byte XNet_state; //single state machine + uint8_t XNetSlaveMode; // > 0 then we are working in SLAVE MODE + bool XNetSlaveInit; //send initialize sequence + byte Railpower; //Data of the actual Power State + byte Fahrstufe; //Standard f�r Fahrstufe + byte MAX485_CONTROL; //Port for send or receive control + uint8_t XNetAdr; //Adresse des Abzufragenden XNet Device + unsigned long XSendCount; //Zeit: Call Byte ausgesendet + byte XNetMsgCallByte; //Received CallByte for Msg + byte XNetMsg[XNetMaxDataLength]; //Serial receive (Length, Header, Data1 to Data7) + byte XNetMsgBuffer[XNetMaxDataLength + 1]; //Read Buffer + + byte callByteParity (byte me); // calculate the parity bit + uint8_t CallByteInquiry; + uint8_t RequestAck; + uint8_t DirectedOps; + + uint16_t SlotLokUse[32]; //store loco to DirectedOps + void SetBusy(uint8_t Slot); //send busy message to slot that doesn't use + void AddBusySlot(uint8_t UserOps, uint16_t Adr); //add loco to slot + void XNetclear(void); //Clear a old Message + + //Functions: + void unknown(void); //unbekannte Anfrage + void getNextXNetAdr(void); //N�CHSTE Adr of XNet Device + bool XNetCheckXOR(void); //Checks the XOR + void XNetAnalyseReceived(void); //work on received data + + //Serial send and receive: + static XpressNetMasterClass *active_object; //aktuelle aktive Object for interrupt handler + XSend XNetBuffer[XNetBufferSize]; //Sendbuffer for data that needs to send out + byte XNetBufferSend; //position to read next data + byte XNetBufferSend_data; //position of byte we are sending + byte XNetBufferStore; //position to store the next data + + void XNetsend(byte *dataString, byte byteCount); //Sende Datenarray out NOW! + uint16_t XNetReadBuffer(void); //read out next Buffer Data + void getXOR (uint8_t *data, byte length); // calculate the XOR + void XNetSendNext(void); //Sendet Daten aus dem Buffer mittels Interrupt + void XNetReceive(void); //Speichern der eingelesenen Daten + bool XNetDataReady; //Daten Fertig empfangen! + + uint8_t XNetCVAdr; //CV Adr that was read + uint8_t XNetCVvalue; //read CV Value + + #if defined(ARDUINO_ESP8266_ESP01) || defined(ARDUINO_ESP8266_WEMOS_D1MINI) + RS485SoftwareSerial rs485; + #endif +}; + +extern XpressNetMasterClass XpressNet; + +#if defined (__cplusplus) + extern "C" { +#endif + #if defined(RAW_DATA_MODE) + extern void XpressNet_RAW_send(uint32_t data) __attribute__((weak)); + #endif + #if defined(STM32F1) + extern void notifyXNetGlobal() __attribute__((weak)); + #endif + + extern void notifyXNetPower(uint8_t State) __attribute__((weak)); + extern uint8_t getPowerState() __attribute__((weak)); //give Back Actual Power State + //Fahrbefehl: + extern void notifyXNetgiveLocoInfo(uint8_t UserOps, uint16_t Address) __attribute__((weak)); + extern void notifyXNetLocoDrive14(uint16_t Address, uint8_t Speed) __attribute__((weak)); + extern void notifyXNetLocoDrive27(uint16_t Address, uint8_t Speed) __attribute__((weak)); + extern void notifyXNetLocoDrive28(uint16_t Address, uint8_t Speed) __attribute__((weak)); + extern void notifyXNetLocoDrive128(uint16_t Address, uint8_t Speed) __attribute__((weak)); + //Funktionsbefehl: + extern void notifyXNetgiveLocoFunc(uint8_t UserOps, uint16_t Address) __attribute__((weak)); + extern void notifyXNetLocoFunc1(uint16_t Address, uint8_t Func1) __attribute__((weak));//Gruppe1 0 0 0 F0 F4 F3 F2 F1 + extern void notifyXNetLocoFunc2(uint16_t Address, uint8_t Func2) __attribute__((weak));//Gruppe2 0000 F8 F7 F6 F5 + extern void notifyXNetLocoFunc3(uint16_t Address, uint8_t Func3) __attribute__((weak));//Gruppe3 0000 F12 F11 F10 F9 + extern void notifyXNetLocoFunc4(uint16_t Address, uint8_t Func4) __attribute__((weak));//Gruppe4 F20-F13 + extern void notifyXNetLocoFunc5(uint16_t Address, uint8_t Func5) __attribute__((weak));//Gruppe5 F28-F21 + //Weichenbefehl: + extern void notifyXNetTrntInfo(uint8_t UserOps, uint8_t Address, uint8_t data) __attribute__((weak));// data=0000 000N N=Nibble N0-(0,1); N1-(2,3); + extern void notifyXNetTrnt(uint16_t Address, uint8_t data) __attribute__((weak));// data=0000 000A A=Weichenausgang (Aktive/Inaktive); + //R�ckmeldung: + extern void notifyXNetFeedback(uint16_t Address, uint8_t data) __attribute__((weak));// data=0000 000A A=Weichenausgang (Aktive/Inaktive); + //CV: + extern void notifyXNetDirectCV(uint8_t CV, uint8_t data) __attribute__((weak)); + extern void notifyXNetDirectReadCV(uint8_t cvAdr) __attribute__((weak)); + //POM: + extern void notifyXNetPOMwriteByte (uint16_t Adr, uint16_t CV, uint8_t data) __attribute__((weak)); + extern void notifyXNetPOMwriteBit (uint16_t Adr, uint16_t CV, uint8_t data) __attribute__((weak)); + //MultiMaus: + extern void notifyXNetgiveLocoMM(uint8_t UserOps, uint16_t Address) __attribute__((weak)); + +#if defined (__cplusplus) +} +#endif + + +#endif + diff --git a/c6021light/src/XpressNet/XpressNetMsg.cpp b/c6021light/src/XpressNet/XpressNetMsg.cpp new file mode 100644 index 0000000..4226471 --- /dev/null +++ b/c6021light/src/XpressNet/XpressNetMsg.cpp @@ -0,0 +1,44 @@ +#include "AtomicRingBuffer/ObjectRingBuffer.h" + +#include "XpressNetMsg.h" + +namespace XpressNetMsg { + +constexpr static const uint8_t kXNQueueSize = 5; + +using QueueType = AtomicRingBuffer::ObjectRingBuffer; + +QueueType XN_RxQueue; + +/* TODO I honestly don't fully understand how to use the AtomicRingBuffer + * and what the functions used here exactly do. + */ +XN_RxMsgPtr_t getXNMessage() { + return XN_RxMsgPtr_t{XN_RxQueue.peek().ptr, freeXNRXMessage}; +} + +void freeXNRXMessage(XN_MsgPtr_t msgPtr) { + XN_RxQueue.consume(QueueType::MemoryRange{msgPtr, 1}); +} + +void forwardRx(XNetMsg& msg) { + // Simply put the message into the queue + auto memory = XN_RxQueue.allocate(); + if (memory.ptr != nullptr) { + *memory.ptr = msg; + XN_RxQueue.publish(memory); + } +} + +} // namespace XpressNetMsg + +// TODO shall we move the notify functions to a separate file? +extern "C" void notifyXNetPower(uint8_t State) { + XpressNetMsg::XNetMsg XN_Msg; + XN_Msg.XN_message.header = XpressNetMsg::POWER; + XN_Msg.XN_message.data.powerData = State; + XpressNetMsg::forwardRx(XN_Msg); + + if (notifyXNetGlobal) + notifyXNetGlobal(); +} diff --git a/c6021light/src/XpressNet/XpressNetMsg.h b/c6021light/src/XpressNet/XpressNetMsg.h new file mode 100644 index 0000000..4392cb4 --- /dev/null +++ b/c6021light/src/XpressNet/XpressNetMsg.h @@ -0,0 +1,56 @@ +#ifndef __XPRESSNETMSG_H__ +#define __XPRESSNETMSG_H__ + +#ifdef ARDUINO +#include +#else +#include +#include +#endif + +#include "RR32Can/Types.h" +#include "XpressNetMaster.h" + +namespace XpressNetMsg { + +typedef enum { + POWER, + ACCESSORY_NOTIFY +} XN_Header_t; + +typedef union { + uint8_t powerData; + uint32_t accessoryData; +} XN_MsgBody_t; + +typedef struct { + XN_Header_t header; + XN_MsgBody_t data; +} XN_Msg_t; + +/** + * TODO I only created the class because I could not figure out + * how to properly use the AtomicRingBuffer instead of the ObjectRingBuffer. + * Probably this is way too convoluted and complicated for a simple message queue. + */ +class XNetMsg { + public: + + constexpr XNetMsg(){}; + + XN_Msg_t XN_message = {POWER, 0}; + + private: + +}; + +using XN_RxMsgPtr_t = std::unique_ptr; +using XN_MsgPtr_t = XNetMsg*; + +XN_RxMsgPtr_t getXNMessage(); +void forwardRx(XNetMsg& msg); +void freeXNRXMessage(XN_MsgPtr_t msgPtr); + +} // namespace XpressNetMsg + +#endif // __XPRESSNETMSG_H__ diff --git a/c6021light/src/hal/LibOpencm3Hal.cpp b/c6021light/src/hal/LibOpencm3Hal.cpp index ffcb5c0..97bcd11 100644 --- a/c6021light/src/hal/LibOpencm3Hal.cpp +++ b/c6021light/src/hal/LibOpencm3Hal.cpp @@ -10,6 +10,8 @@ #include +#include "XpressNet/XpressNetMaster.h" + namespace hal { void LibOpencm3Hal::led(bool on) { @@ -33,6 +35,7 @@ void LibOpencm3Hal::beginClock() { // Enable the UART clock rcc_periph_clock_enable(RCC_USART1); + rcc_periph_clock_enable(RCC_USART2); // Enable the I2C clock rcc_periph_clock_enable(RCC_I2C1); @@ -52,12 +55,18 @@ void LibOpencm3Hal::beginGpio() { gpio_set(GPIOA, GPIO0); // Set Idle High (TODO: Correct?) gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, - GPIO4 | GPIO5 | GPIO6); // Extra LED + GPIO4 | GPIO5 | GPIO6); // Debug I/O gpio_set(GPIOA, GPIO4 | GPIO5 | GPIO6); + + // Tx Ena Xpressnet + gpio_set_mode(XN_TXEN_BANK, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, XN_TXEN_PIN); + gpio_clear(XN_TXEN_BANK, XN_TXEN_PIN); } void LibOpencm3Hal::beginLocoNet() { LocoNet.init(PinNames::PB15); } +void LibOpencm3Hal::beginXpressNet() { XpressNet.setup(Loco128, PinNames::PA1); } + void LibOpencm3Hal::toggleLed() { gpio_toggle(GPIOC, GPIO13); } extern "C" { diff --git a/c6021light/src/hal/LibOpencm3Hal.h b/c6021light/src/hal/LibOpencm3Hal.h index 4c9fdf5..33ee61d 100644 --- a/c6021light/src/hal/LibOpencm3Hal.h +++ b/c6021light/src/hal/LibOpencm3Hal.h @@ -12,6 +12,7 @@ class LibOpencm3Hal { beginClock(); beginGpio(); beginLocoNet(); + beginXpressNet(); } void led(bool on); @@ -21,6 +22,7 @@ class LibOpencm3Hal { void beginClock(); void beginGpio(); void beginLocoNet(); + void beginXpressNet(); }; } // namespace hal diff --git a/c6021light/src/main.cpp b/c6021light/src/main.cpp index c20c55f..36edc8f 100644 --- a/c6021light/src/main.cpp +++ b/c6021light/src/main.cpp @@ -123,6 +123,9 @@ int main(void) { extern "C" void notifyLnByteReceived() { routingTask.notifyFromISRWithWake(); } +// single function that gets called from all relevant XNet notifiers +extern "C" void notifyXNetGlobal() { routingTask.notify(); } + namespace ConsoleManager { int run_ln_slot_server_dump(int argc, const char* const* argv, int argcMatched) { diff --git a/c6021light/src/tasks/ConsoleTask/ConsoleTask.cpp b/c6021light/src/tasks/ConsoleTask/ConsoleTask.cpp index b5999ff..1d5ec47 100644 --- a/c6021light/src/tasks/ConsoleTask/ConsoleTask.cpp +++ b/c6021light/src/tasks/ConsoleTask/ConsoleTask.cpp @@ -13,6 +13,11 @@ void ConsoleTask::TaskMain() { if (hal::pollSerial(receivedCharacter)) { microrl_insert_char(&ConsoleManager::microrl, receivedCharacter); } + /* TODO hacky solution for first XN trials + * It is doable to trigger a notifier whenever update() needs to be called, + * which then triggers a task which will call update(). + */ + XpressNet.update(); } } diff --git a/c6021light/src/tasks/ConsoleTask/ConsoleTask.h b/c6021light/src/tasks/ConsoleTask/ConsoleTask.h index 2820b79..3d5fdd4 100644 --- a/c6021light/src/tasks/ConsoleTask/ConsoleTask.h +++ b/c6021light/src/tasks/ConsoleTask/ConsoleTask.h @@ -4,6 +4,7 @@ #include #include "OsTask.h" +#include "XpressNet/XpressNetMaster.h" namespace tasks { namespace ConsoleTask { diff --git a/c6021light/src/tasks/RoutingTask/RoutingTask.cpp b/c6021light/src/tasks/RoutingTask/RoutingTask.cpp index 54de945..c12c54e 100644 --- a/c6021light/src/tasks/RoutingTask/RoutingTask.cpp +++ b/c6021light/src/tasks/RoutingTask/RoutingTask.cpp @@ -8,6 +8,8 @@ #include "hal/stm32can.h" #include "tasks/RoutingTask/LocoNetPrinter.h" +#include "XpressNet/XpressNetMsg.h" + #include "DataModel.h" namespace tasks { @@ -17,6 +19,7 @@ void RoutingTask::processCAN() { for (auto framePtr = hal::getCanMessage(); framePtr != nullptr; framePtr = hal::getCanMessage()) { i2cForwarder_.forward(*framePtr); lnForwarder_.forward(*framePtr); + xnForwarder_.forward(*framePtr); // Forward to self RR32Can::RR32Can.HandlePacket(*framePtr); // Explicitly reset framePtr so that getCanMessage can produce a new message @@ -45,6 +48,7 @@ void RoutingTask::processI2CMessages() { if (i2cForwarder_.MakeRR32CanMsg(*messagePtr, frame)) { RR32Can::RR32Can.SendPacket(frame); lnForwarder_.forward(frame); + xnForwarder_.forward(frame); // Forward to self RR32Can::RR32Can.HandlePacket(frame); } @@ -61,6 +65,7 @@ void RoutingTask::processI2CStopGo() { if (i2cForwarder_.MakeRR32CanPowerMsg(hal::getStopGoRequest(), frame)) { RR32Can::RR32Can.SendPacket(frame); lnForwarder_.forward(frame); + xnForwarder_.forward(frame); // Forward to self RR32Can::RR32Can.HandlePacket(frame); } @@ -75,6 +80,7 @@ void RoutingTask::processLocoNet() { // Convert to generic CAN representation if (lnForwarder_.MakeRR32CanMsg(*LnPacket, frame)) { i2cForwarder_.forward(frame); + xnForwarder_.forward(frame); // Forward to CAN RR32Can::RR32Can.SendPacket(frame); @@ -104,11 +110,35 @@ void RoutingTask::matchEnginesFromLocoNetAndCan() { } } +void RoutingTask::processXpressNet() { + for (auto messagePtr = XpressNetMsg::getXNMessage(); messagePtr != nullptr; + messagePtr = XpressNetMsg::getXNMessage()) { + //printf("XN RX: %d\n", messagePtr->XN_message.data.powerData); + + RR32Can::CanFrame frame; + + // Convert to generic CAN representation + if (xnForwarder_.MakeRR32CanMsg(*messagePtr, frame)) { + i2cForwarder_.forward(frame); + lnForwarder_.forward(frame); + // Forward to CAN + RR32Can::RR32Can.SendPacket(frame); + + // Forward to self + RR32Can::RR32Can.HandlePacket(frame); + } + + // Explicitly reset framePtr so that getXNMessage can produce a new message + messagePtr.reset(); + } +} + void RoutingTask::loop() { processI2CStopGo(); processCAN(); processI2CMessages(); processLocoNet(); + processXpressNet(); matchEnginesFromLocoNetAndCan(); } diff --git a/c6021light/src/tasks/RoutingTask/RoutingTask.h b/c6021light/src/tasks/RoutingTask/RoutingTask.h index ced7287..0a3885c 100644 --- a/c6021light/src/tasks/RoutingTask/RoutingTask.h +++ b/c6021light/src/tasks/RoutingTask/RoutingTask.h @@ -10,6 +10,7 @@ #include "CANForwarder.h" #include "I2CForwarder.h" #include "LocoNetForwarder.h" +#include "XpressNetForwarder.h" #include "CanEngineDB.h" #include "LocoNetSlotServer.h" @@ -23,6 +24,7 @@ class RoutingTask : public freertossupport::OsTask { void begin(DataModel& dataModel) { lnForwarder_.init(dataModel, slotServer_); + xnForwarder_.init(dataModel); i2cForwarder_.init(dataModel); slotServer_.init(dataModel); }; @@ -41,11 +43,13 @@ class RoutingTask : public freertossupport::OsTask { CANForwarder canForwarder_; I2CForwarder i2cForwarder_; LocoNetForwarder lnForwarder_; + XpressNetForwarder xnForwarder_; void processCAN(); void processI2CMessages(); void processI2CStopGo(); void processLocoNet(); + void processXpressNet(); void matchEnginesFromLocoNetAndCan(); }; diff --git a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp new file mode 100644 index 0000000..ebee039 --- /dev/null +++ b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp @@ -0,0 +1,84 @@ +#include "tasks/RoutingTask/XpressNetForwarder.h" + +#include "RR32Can/Constants.h" +#include "RR32Can/messages/S88Event.h" +#include "RR32Can/messages/SystemMessage.h" +#include "RR32Can/messages/TurnoutPacket.h" + +#include "XpressNet/XpressNetMaster.h" + +namespace tasks { +namespace RoutingTask { + +void XpressNetForwarder::forward(const RR32Can::CanFrame& frame) { + switch (frame.id.getCommand()) { + case RR32Can::Command::ACCESSORY_SWITCH: { + const RR32Can::TurnoutPacket turnoutPacket(const_cast(frame.data)); + if (!frame.id.isResponse()) { + // Send to XpressNet + // convert CAN turnout direction + uint8_t xn_direction; + if (turnoutPacket.getDirection() == RR32Can::TurnoutDirection::GREEN) + xn_direction = 1; + else + xn_direction = 0; + + XpressNet.SetTrntPos(turnoutPacket.getLocid().getNumericAddress().value(), xn_direction, turnoutPacket.getPower()); + } + break; + } + + case RR32Can::Command::SYSTEM_COMMAND: { + const RR32Can::SystemMessage systemMessage(const_cast(frame.data)); + switch (systemMessage.getSubcommand()) { + case RR32Can::SystemSubcommand::SYSTEM_STOP: + if (!frame.id.isResponse()) XpressNet.setPower(csTrackVoltageOff); + break; + case RR32Can::SystemSubcommand::SYSTEM_GO: + if (!frame.id.isResponse()) XpressNet.setPower(csNormal); + break; + default: + // Other messages not forwarded. + break; + } + break; + } + + default: + // Other messages not forwarded. + break; + } +} + +void XpressNetForwarder::forwardLocoChange(const RR32Can::LocomotiveData& loco, LocoDiff_t& diff) { + // nothing here yet.. +} + +bool XpressNetForwarder::MakeRR32CanMsg(const XpressNetMsg::XNetMsg& XnPacket, RR32Can::CanFrame& frame) { + // Decode the message type + switch(XnPacket.XN_message.header) { + case XpressNetMsg::POWER: { + frame.id.setCommand(RR32Can::Command::SYSTEM_COMMAND); + frame.id.setResponse(false); + RR32Can::SystemMessage systemMessage(frame.data); + systemMessage.initData(); + + if (XnPacket.XN_message.data.powerData == csNormal) { + systemMessage.setSubcommand(RR32Can::SystemSubcommand::SYSTEM_GO); + } else if (XnPacket.XN_message.data.powerData == csTrackVoltageOff) { + systemMessage.setSubcommand(RR32Can::SystemSubcommand::SYSTEM_STOP); + } + + return true; + break; + } + + default: + // Other packet types not handled for now. + return false; + break; + } +} + +} // namespace RoutingTask +} // namespace tasks diff --git a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h new file mode 100644 index 0000000..46aa980 --- /dev/null +++ b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h @@ -0,0 +1,37 @@ +#ifndef __TASKS__ROUTINGTASK__XPRESSNETFORWARDER_H__ +#define __TASKS__ROUTINGTASK__XPRESSNETFORWARDER_H__ + +#include "RoutingForwarder.h" + +#include "RR32Can/Locomotive.h" +#include "RR32Can/messages/Data.h" +#include "RR32Can/messages/Identifier.h" + +#include "XpressNet/XpressNetMsg.h" + +#include "DataModel.h" + +namespace tasks { +namespace RoutingTask { + +/* + * \brief Class XpressNetForwarder + */ +class XpressNetForwarder final : public RoutingForwarder { + public: + void init(DataModel& dataModel) { + this->dataModel_ = &dataModel; + } + + void forwardLocoChange(const RR32Can::LocomotiveData& loco, LocoDiff_t& diff) override; + void forward(const RR32Can::CanFrame& frame) override; + bool MakeRR32CanMsg(const XpressNetMsg::XNetMsg& XnPacket, RR32Can::CanFrame& frame); + + private: + DataModel* dataModel_ = nullptr; +}; + +} // namespace RoutingTask +} // namespace tasks + +#endif // __TASKS__ROUTINGTASK__XPRESSNETFORWARDER_H__ From 01e4ba3b466470cf272ba337bc495aa4f6934c17 Mon Sep 17 00:00:00 2001 From: Florian Glaser Date: Wed, 3 Feb 2021 23:17:24 +0100 Subject: [PATCH 02/14] Resolved PR comments --- .../XpressNetMaster}/XpressNetMaster.cpp | 0 .../XpressNetMaster}/XpressNetMaster.h | 2 +- c6021light/lib/XpressNetMaster/library.json | 8 ++++++++ c6021light/src/WProgram.cpp | 2 +- c6021light/src/XpressNet/XpressNetMsg.cpp | 2 +- c6021light/src/hal/LibOpencm3Hal.cpp | 3 +-- c6021light/src/main.cpp | 2 +- c6021light/src/tasks/ConsoleTask/ConsoleTask.h | 2 +- c6021light/src/tasks/RoutingTask/RoutingTask.cpp | 1 - c6021light/src/tasks/RoutingTask/RoutingTask.h | 1 - .../src/tasks/RoutingTask/XpressNetForwarder.cpp | 15 ++++++++++----- .../src/tasks/RoutingTask/XpressNetForwarder.h | 7 ++----- 12 files changed, 26 insertions(+), 19 deletions(-) rename c6021light/{src/XpressNet => lib/XpressNetMaster}/XpressNetMaster.cpp (100%) rename c6021light/{src/XpressNet => lib/XpressNetMaster}/XpressNetMaster.h (99%) create mode 100644 c6021light/lib/XpressNetMaster/library.json diff --git a/c6021light/src/XpressNet/XpressNetMaster.cpp b/c6021light/lib/XpressNetMaster/XpressNetMaster.cpp similarity index 100% rename from c6021light/src/XpressNet/XpressNetMaster.cpp rename to c6021light/lib/XpressNetMaster/XpressNetMaster.cpp diff --git a/c6021light/src/XpressNet/XpressNetMaster.h b/c6021light/lib/XpressNetMaster/XpressNetMaster.h similarity index 99% rename from c6021light/src/XpressNet/XpressNetMaster.h rename to c6021light/lib/XpressNetMaster/XpressNetMaster.h index e1afc34..6e9a1ef 100644 --- a/c6021light/src/XpressNet/XpressNetMaster.h +++ b/c6021light/lib/XpressNetMaster/XpressNetMaster.h @@ -48,7 +48,7 @@ #elif ARDUINO >= 100 #include #else - #include + #include "WProgram.h" extern "C" { #include } diff --git a/c6021light/lib/XpressNetMaster/library.json b/c6021light/lib/XpressNetMaster/library.json new file mode 100644 index 0000000..1724c43 --- /dev/null +++ b/c6021light/lib/XpressNetMaster/library.json @@ -0,0 +1,8 @@ +{ + "name": "XpressNetMaster", + "version": "2.9.1", + "dependencies": + { + "freertos" : "10.4.1" + } +} diff --git a/c6021light/src/WProgram.cpp b/c6021light/src/WProgram.cpp index d20ae6d..92fb5ae 100644 --- a/c6021light/src/WProgram.cpp +++ b/c6021light/src/WProgram.cpp @@ -56,4 +56,4 @@ void digitalWrite(PinNames pin, PinValue value) { gpio_clear(port, pinMask); break; } -} \ No newline at end of file +} diff --git a/c6021light/src/XpressNet/XpressNetMsg.cpp b/c6021light/src/XpressNet/XpressNetMsg.cpp index 4226471..169bf37 100644 --- a/c6021light/src/XpressNet/XpressNetMsg.cpp +++ b/c6021light/src/XpressNet/XpressNetMsg.cpp @@ -33,7 +33,7 @@ void forwardRx(XNetMsg& msg) { } // namespace XpressNetMsg // TODO shall we move the notify functions to a separate file? -extern "C" void notifyXNetPower(uint8_t State) { +void notifyXNetPower(uint8_t State) { XpressNetMsg::XNetMsg XN_Msg; XN_Msg.XN_message.header = XpressNetMsg::POWER; XN_Msg.XN_message.data.powerData = State; diff --git a/c6021light/src/hal/LibOpencm3Hal.cpp b/c6021light/src/hal/LibOpencm3Hal.cpp index 97bcd11..1cf0012 100644 --- a/c6021light/src/hal/LibOpencm3Hal.cpp +++ b/c6021light/src/hal/LibOpencm3Hal.cpp @@ -9,8 +9,7 @@ #include #include - -#include "XpressNet/XpressNetMaster.h" +#include namespace hal { diff --git a/c6021light/src/main.cpp b/c6021light/src/main.cpp index 36edc8f..fd8bcac 100644 --- a/c6021light/src/main.cpp +++ b/c6021light/src/main.cpp @@ -124,7 +124,7 @@ int main(void) { extern "C" void notifyLnByteReceived() { routingTask.notifyFromISRWithWake(); } // single function that gets called from all relevant XNet notifiers -extern "C" void notifyXNetGlobal() { routingTask.notify(); } +void notifyXNetGlobal() { routingTask.notify(); } namespace ConsoleManager { diff --git a/c6021light/src/tasks/ConsoleTask/ConsoleTask.h b/c6021light/src/tasks/ConsoleTask/ConsoleTask.h index 3d5fdd4..de57e77 100644 --- a/c6021light/src/tasks/ConsoleTask/ConsoleTask.h +++ b/c6021light/src/tasks/ConsoleTask/ConsoleTask.h @@ -4,7 +4,7 @@ #include #include "OsTask.h" -#include "XpressNet/XpressNetMaster.h" +#include namespace tasks { namespace ConsoleTask { diff --git a/c6021light/src/tasks/RoutingTask/RoutingTask.cpp b/c6021light/src/tasks/RoutingTask/RoutingTask.cpp index c12c54e..ab5278a 100644 --- a/c6021light/src/tasks/RoutingTask/RoutingTask.cpp +++ b/c6021light/src/tasks/RoutingTask/RoutingTask.cpp @@ -113,7 +113,6 @@ void RoutingTask::matchEnginesFromLocoNetAndCan() { void RoutingTask::processXpressNet() { for (auto messagePtr = XpressNetMsg::getXNMessage(); messagePtr != nullptr; messagePtr = XpressNetMsg::getXNMessage()) { - //printf("XN RX: %d\n", messagePtr->XN_message.data.powerData); RR32Can::CanFrame frame; diff --git a/c6021light/src/tasks/RoutingTask/RoutingTask.h b/c6021light/src/tasks/RoutingTask/RoutingTask.h index 0a3885c..b1298f5 100644 --- a/c6021light/src/tasks/RoutingTask/RoutingTask.h +++ b/c6021light/src/tasks/RoutingTask/RoutingTask.h @@ -24,7 +24,6 @@ class RoutingTask : public freertossupport::OsTask { void begin(DataModel& dataModel) { lnForwarder_.init(dataModel, slotServer_); - xnForwarder_.init(dataModel); i2cForwarder_.init(dataModel); slotServer_.init(dataModel); }; diff --git a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp index ebee039..c50555b 100644 --- a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp +++ b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp @@ -5,7 +5,6 @@ #include "RR32Can/messages/SystemMessage.h" #include "RR32Can/messages/TurnoutPacket.h" -#include "XpressNet/XpressNetMaster.h" namespace tasks { namespace RoutingTask { @@ -18,10 +17,11 @@ void XpressNetForwarder::forward(const RR32Can::CanFrame& frame) { // Send to XpressNet // convert CAN turnout direction uint8_t xn_direction; - if (turnoutPacket.getDirection() == RR32Can::TurnoutDirection::GREEN) + if (turnoutPacket.getDirection() == RR32Can::TurnoutDirection::GREEN) { xn_direction = 1; - else + } else { xn_direction = 0; + } XpressNet.SetTrntPos(turnoutPacket.getLocid().getNumericAddress().value(), xn_direction, turnoutPacket.getPower()); } @@ -31,12 +31,14 @@ void XpressNetForwarder::forward(const RR32Can::CanFrame& frame) { case RR32Can::Command::SYSTEM_COMMAND: { const RR32Can::SystemMessage systemMessage(const_cast(frame.data)); switch (systemMessage.getSubcommand()) { - case RR32Can::SystemSubcommand::SYSTEM_STOP: + case RR32Can::SystemSubcommand::SYSTEM_STOP: { if (!frame.id.isResponse()) XpressNet.setPower(csTrackVoltageOff); break; - case RR32Can::SystemSubcommand::SYSTEM_GO: + } + case RR32Can::SystemSubcommand::SYSTEM_GO: { if (!frame.id.isResponse()) XpressNet.setPower(csNormal); break; + } default: // Other messages not forwarded. break; @@ -67,6 +69,9 @@ bool XpressNetForwarder::MakeRR32CanMsg(const XpressNetMsg::XNetMsg& XnPacket, R systemMessage.setSubcommand(RR32Can::SystemSubcommand::SYSTEM_GO); } else if (XnPacket.XN_message.data.powerData == csTrackVoltageOff) { systemMessage.setSubcommand(RR32Can::SystemSubcommand::SYSTEM_STOP); + } else { + return false; + break; } return true; diff --git a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h index 46aa980..aa94d24 100644 --- a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h +++ b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h @@ -7,9 +7,9 @@ #include "RR32Can/messages/Data.h" #include "RR32Can/messages/Identifier.h" +#include #include "XpressNet/XpressNetMsg.h" -#include "DataModel.h" namespace tasks { namespace RoutingTask { @@ -19,16 +19,13 @@ namespace RoutingTask { */ class XpressNetForwarder final : public RoutingForwarder { public: - void init(DataModel& dataModel) { - this->dataModel_ = &dataModel; - } void forwardLocoChange(const RR32Can::LocomotiveData& loco, LocoDiff_t& diff) override; void forward(const RR32Can::CanFrame& frame) override; bool MakeRR32CanMsg(const XpressNetMsg::XNetMsg& XnPacket, RR32Can::CanFrame& frame); private: - DataModel* dataModel_ = nullptr; + }; } // namespace RoutingTask From cce843814af9b6b0f4e8e57f636276982de2cf8b Mon Sep 17 00:00:00 2001 From: Florian Glaser Date: Thu, 4 Feb 2021 21:20:04 +0100 Subject: [PATCH 03/14] Removed unnecessary XpressNet message class --- .../lib/XpressNetMaster/XpressNetMaster.cpp | 4 ++-- c6021light/platformio.ini | 2 +- c6021light/src/XpressNet/XpressNetMsg.cpp | 13 +++++------- c6021light/src/XpressNet/XpressNetMsg.h | 21 +++---------------- .../tasks/RoutingTask/XpressNetForwarder.cpp | 8 +++---- .../tasks/RoutingTask/XpressNetForwarder.h | 2 +- 6 files changed, 16 insertions(+), 34 deletions(-) diff --git a/c6021light/lib/XpressNetMaster/XpressNetMaster.cpp b/c6021light/lib/XpressNetMaster/XpressNetMaster.cpp index 3825a83..74a8a25 100644 --- a/c6021light/lib/XpressNetMaster/XpressNetMaster.cpp +++ b/c6021light/lib/XpressNetMaster/XpressNetMaster.cpp @@ -325,8 +325,8 @@ void XpressNetMasterClass::XNetAnalyseReceived(void) { //work on received data #endif #if defined(STM32F1) - printf("XN rx: 0x1%x", XNetMsgCallByte); - for (byte i = 0; i < ((XNetMsg[XNetheader] & 0x0F) + 2); i++) printf(" %x", XNetMsg[i]); + printf("XN rx: 0x1%02x", XNetMsgCallByte); + for (byte i = 0; i < ((XNetMsg[XNetheader] & 0x0F) + 2); i++) printf(" %02x", XNetMsg[i]); printf("\n"); #endif diff --git a/c6021light/platformio.ini b/c6021light/platformio.ini index c03982e..d45ded8 100644 --- a/c6021light/platformio.ini +++ b/c6021light/platformio.ini @@ -28,7 +28,7 @@ framework = libopencm3 board_build.ldscript = stm32f103c8.ld monitor_speed = 115200 -monitor_port = COM5 +monitor_port = COM4 monitor_flags = --raw ;upload_protocol = serial diff --git a/c6021light/src/XpressNet/XpressNetMsg.cpp b/c6021light/src/XpressNet/XpressNetMsg.cpp index 169bf37..a2b447e 100644 --- a/c6021light/src/XpressNet/XpressNetMsg.cpp +++ b/c6021light/src/XpressNet/XpressNetMsg.cpp @@ -6,13 +6,10 @@ namespace XpressNetMsg { constexpr static const uint8_t kXNQueueSize = 5; -using QueueType = AtomicRingBuffer::ObjectRingBuffer; +using QueueType = AtomicRingBuffer::ObjectRingBuffer; QueueType XN_RxQueue; -/* TODO I honestly don't fully understand how to use the AtomicRingBuffer - * and what the functions used here exactly do. - */ XN_RxMsgPtr_t getXNMessage() { return XN_RxMsgPtr_t{XN_RxQueue.peek().ptr, freeXNRXMessage}; } @@ -21,7 +18,7 @@ void freeXNRXMessage(XN_MsgPtr_t msgPtr) { XN_RxQueue.consume(QueueType::MemoryRange{msgPtr, 1}); } -void forwardRx(XNetMsg& msg) { +void forwardRx(XN_Msg_t& msg) { // Simply put the message into the queue auto memory = XN_RxQueue.allocate(); if (memory.ptr != nullptr) { @@ -34,9 +31,9 @@ void forwardRx(XNetMsg& msg) { // TODO shall we move the notify functions to a separate file? void notifyXNetPower(uint8_t State) { - XpressNetMsg::XNetMsg XN_Msg; - XN_Msg.XN_message.header = XpressNetMsg::POWER; - XN_Msg.XN_message.data.powerData = State; + XpressNetMsg::XN_Msg_t XN_Msg; + XN_Msg.header = XpressNetMsg::POWER; + XN_Msg.data.powerData = State; XpressNetMsg::forwardRx(XN_Msg); if (notifyXNetGlobal) diff --git a/c6021light/src/XpressNet/XpressNetMsg.h b/c6021light/src/XpressNet/XpressNetMsg.h index 4392cb4..4148b92 100644 --- a/c6021light/src/XpressNet/XpressNetMsg.h +++ b/c6021light/src/XpressNet/XpressNetMsg.h @@ -28,27 +28,12 @@ typedef struct { XN_MsgBody_t data; } XN_Msg_t; -/** - * TODO I only created the class because I could not figure out - * how to properly use the AtomicRingBuffer instead of the ObjectRingBuffer. - * Probably this is way too convoluted and complicated for a simple message queue. - */ -class XNetMsg { - public: - constexpr XNetMsg(){}; - - XN_Msg_t XN_message = {POWER, 0}; - - private: - -}; - -using XN_RxMsgPtr_t = std::unique_ptr; -using XN_MsgPtr_t = XNetMsg*; +using XN_RxMsgPtr_t = std::unique_ptr; +using XN_MsgPtr_t = XN_Msg_t*; XN_RxMsgPtr_t getXNMessage(); -void forwardRx(XNetMsg& msg); +void forwardRx(XN_Msg_t& msg); void freeXNRXMessage(XN_MsgPtr_t msgPtr); } // namespace XpressNetMsg diff --git a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp index c50555b..ff601cf 100644 --- a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp +++ b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp @@ -56,18 +56,18 @@ void XpressNetForwarder::forwardLocoChange(const RR32Can::LocomotiveData& loco, // nothing here yet.. } -bool XpressNetForwarder::MakeRR32CanMsg(const XpressNetMsg::XNetMsg& XnPacket, RR32Can::CanFrame& frame) { +bool XpressNetForwarder::MakeRR32CanMsg(const XpressNetMsg::XN_Msg_t& XnPacket, RR32Can::CanFrame& frame) { // Decode the message type - switch(XnPacket.XN_message.header) { + switch(XnPacket.header) { case XpressNetMsg::POWER: { frame.id.setCommand(RR32Can::Command::SYSTEM_COMMAND); frame.id.setResponse(false); RR32Can::SystemMessage systemMessage(frame.data); systemMessage.initData(); - if (XnPacket.XN_message.data.powerData == csNormal) { + if (XnPacket.data.powerData == csNormal) { systemMessage.setSubcommand(RR32Can::SystemSubcommand::SYSTEM_GO); - } else if (XnPacket.XN_message.data.powerData == csTrackVoltageOff) { + } else if (XnPacket.data.powerData == csTrackVoltageOff) { systemMessage.setSubcommand(RR32Can::SystemSubcommand::SYSTEM_STOP); } else { return false; diff --git a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h index aa94d24..5c6f1d8 100644 --- a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h +++ b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h @@ -22,7 +22,7 @@ class XpressNetForwarder final : public RoutingForwarder { void forwardLocoChange(const RR32Can::LocomotiveData& loco, LocoDiff_t& diff) override; void forward(const RR32Can::CanFrame& frame) override; - bool MakeRR32CanMsg(const XpressNetMsg::XNetMsg& XnPacket, RR32Can::CanFrame& frame); + bool MakeRR32CanMsg(const XpressNetMsg::XN_Msg_t& XnPacket, RR32Can::CanFrame& frame); private: From 367ad8c61d0125e0af186e39335e37c962223e0e Mon Sep 17 00:00:00 2001 From: Florian Glaser Date: Fri, 5 Feb 2021 10:00:37 +0100 Subject: [PATCH 04/14] Several fixes to XN library: The rx buffer now holds multiple entries and separate rd/wr pointers, before back-to-back messages could go lost depending on task timing. Send out data now actually only during our send slot (doh\!). Avoid rx errors during transmission caused by pulled-down rx line of RS485 transceiver due to poor-mans level shifter (resistive divider). --- .../lib/XpressNetMaster/XpressNetMaster.cpp | 109 +++++++++--------- .../lib/XpressNetMaster/XpressNetMaster.h | 19 +-- 2 files changed, 62 insertions(+), 66 deletions(-) diff --git a/c6021light/lib/XpressNetMaster/XpressNetMaster.cpp b/c6021light/lib/XpressNetMaster/XpressNetMaster.cpp index 74a8a25..b961716 100644 --- a/c6021light/lib/XpressNetMaster/XpressNetMaster.cpp +++ b/c6021light/lib/XpressNetMaster/XpressNetMaster.cpp @@ -65,10 +65,11 @@ XpressNetMasterClass::XpressNetMasterClass() XNetBuffer[b].data[d] = 0x00; } } - - XNetDataReady = false; //keine Daten empfangen! + XNet_state = XNet_get_callbyte; //set the start state - XNetclear(); //alte Nachricht l�schen + XNetRxMsgRd = 0; + XNetRxMsgWr = 0; + XNetMsg = XNetRxMsgBuf[0]; XNetMsgBuffer[XNetBufferlength] = 0x00; //reset buffer XNetCVAdr = 0; //no CV read @@ -171,11 +172,13 @@ void XpressNetMasterClass::RAW_serial_read(unsigned int data) //read data from soft_uart // Filter the 9th bit, then return if (data > 0xFF) { - XNetMsgCallByte = data & 0xFF; //only store data + XNetRxMsgBuf[XNetRxMsgWr][XNetMaxDataLength] = data & 0xFF; //only store data XNetSlaveMode = XNetSlaveCycle; //reactivate SLAVE MODE // XNetMsg[XNetlength] = 0x00; - if (XNetMsgCallByte == MY_ADDRESS) + if (XNetRxMsgBuf[XNetRxMsgWr][XNetMaxDataLength] == MY_ADDRESS) { + // the master called our send slot, send out whatever is in the tx buffer XNetSendNext(); //start sending out by interrupt + } } else { XNetMsgBuffer[XNetBufferlength]++; //weitere Nachrichtendaten @@ -186,9 +189,10 @@ void XpressNetMasterClass::RAW_serial_read(unsigned int data) //Check length - length is inside header but without header and xor! if (((XNetMsgBuffer[1] & 0x0F) + 2) == XNetMsgBuffer[XNetBufferlength]) { //reach data length? for (byte i = 0; i < XNetMsgBuffer[XNetBufferlength]; i++) { - XNetMsg[i] = XNetMsgBuffer[i+1]; - } - XNetDataReady = true; + XNetRxMsgBuf[XNetRxMsgWr][i] = XNetMsgBuffer[i+1]; + } + // Increase the write pointer + XNetRxMsgWr = (XNetRxMsgWr + 1) % XnetRxBufSize; XNetMsgBuffer[XNetBufferlength] = 0x00; //clear! } if (XNetMsgBuffer[XNetBufferlength] >= (XNetMaxDataLength) ) { //overflow!!! @@ -230,20 +234,24 @@ void XpressNetMasterClass::update(void) time_diff = micros() - XSendCount; if (time_diff >= XNetTimeReadData) { //Timeout? XNet_state = XNet_get_callbyte; - XNetclear(); //alte Nachricht l�schen XNetMsgBuffer[XNetBufferlength] = 0x00; //reset buffer break; } - if (!XNetDataReady) + // check if there is data in the receive buffer + if (XNetRxMsgRd == XNetRxMsgWr) { break; + } #if defined (XNetDEBUGTime) XNetSerial.print("Paket time: "); XNetSerial.println(micros() - XSendCount); #endif - XNetDataReady = false; + // Point the msg-analyze function to the correct part of the buffer + XNetMsg = XNetRxMsgBuf[XNetRxMsgRd]; + XNetMsgCallByte = XNetRxMsgBuf[XNetRxMsgRd][XNetMaxDataLength]; if (XNetCheckXOR()) //Checks the XOR XNetAnalyseReceived(); //Auswerten der empfangenen Daten - XNetclear(); //alte Nachricht l�schen + // increase read pointer + XNetRxMsgRd = (XNetRxMsgRd + 1) % XnetRxBufSize; if (XNetSlaveMode == 0x00) { //MASTER MODE XNet_state = XNet_send_data; XNetSendNext(); //start sending out by interrupt @@ -393,7 +401,6 @@ void XpressNetMasterClass::XNetAnalyseReceived(void) { //work on received data } if (SlotLokUse[DirectedOps & 0x1F] == 0xFFFF) SlotLokUse[DirectedOps & 0x1F] = 0; //mark Slot as activ - //XNetclear(); //alte Nachricht l�schen } break; case 0x22: //Start Programming @@ -409,7 +416,6 @@ void XpressNetMasterClass::XNetAnalyseReceived(void) { //work on received data break; } unknown(); //unbekannte Anfrage - //XNetclear(); //alte Nachricht l�schen break; case 0x23: if (XNetMsg[XNetdata1] == 0x12) { //Register Mode write request (Register Mode) @@ -425,7 +431,6 @@ void XpressNetMasterClass::XNetAnalyseReceived(void) { //work on received data } unknown(); //unbekannte Anfrage - //XNetclear(); break; case 0xE6: { //POM CV write MultiMaus if (XNetMsg[XNetdata1] == 0x30) { @@ -447,7 +452,6 @@ void XpressNetMasterClass::XNetAnalyseReceived(void) { //work on received data break; } unknown(); //unbekannte Anfrage - //XNetclear(); //alte Nachricht l�schen break; case 0xE3: { if (XNetSlaveMode == 0x00) { @@ -480,7 +484,6 @@ void XpressNetMasterClass::XNetAnalyseReceived(void) { //work on received data default: unknown(); //unbekannte Anfrage } } - //XNetclear(); //alte Nachricht l�schen break; } case 0xE4: { //Fahrbefehle @@ -528,7 +531,6 @@ void XpressNetMasterClass::XNetAnalyseReceived(void) { //work on received data case 0x42: //Accessory Decoder information request if (notifyXNetTrntInfo && XNetSlaveMode == 0x00) notifyXNetTrntInfo(DirectedOps, XNetMsg[XNetdata1], XNetMsg[XNetdata2]); - //XNetclear(); //alte Nachricht l�schen break; case 0x52: //Accessory Decoder operation request if (notifyXNetTrnt) @@ -537,7 +539,6 @@ void XpressNetMasterClass::XNetAnalyseReceived(void) { //work on received data //A = Weichenausgang(Spulenspannung EIN/AUS) //BB = Adresse des Dekoderport 1..4 //P = Ausgang (Gerade = 0 / Abzweigen = 1) - //XNetclear(); //alte Nachricht l�schen break; default: //Befehl in Zentrale nicht vorhanden unknown(); //unbekannte Anfrage @@ -604,6 +605,7 @@ void XpressNetMasterClass::XNetAnalyseReceived(void) { //work on received data uint8_t AckSeq[] = {0x00, 0x20, 0x20}; XNetsend (AckSeq, 3); } //ACK END + // TODO here the slave address actually needs to be checked!! else /* if (XNetMsgCallByte == MY_ADDRESS*) */ { //Central Station send data to us? switch (XNetMsg[XNetheader]) { case 0x52: // Some other device asked for an accessory change @@ -1106,11 +1108,14 @@ inline void XpressNetMasterClass::handle_TX_interrupt() #if defined(STM32F1) extern "C" void usart2_isr (void) { // check with which type of interrupt we are dealing - if (usart_get_flag(XN_USART_INST, USART_SR_RXNE)) + if (usart_get_flag(XN_USART_INST, USART_SR_RXNE)) { XpressNetMasterClass::handle_RX_interrupt(); + } - if (usart_get_flag(XN_USART_INST, USART_SR_TC)) + // only listen to the TC flag if we enabled the interrupt before.. + if ((USART_CR1(XN_USART_INST) & USART_CR1_TCIE) && usart_get_flag(XN_USART_INST, USART_SR_TC)) { XpressNetMasterClass::handle_TX_interrupt(); + } } inline void XpressNetMasterClass::handle_TX_interrupt() { @@ -1123,7 +1128,8 @@ void XpressNetMasterClass::XNetSendNext(void) { uint16_t data9 = XNetReadBuffer(); if (data9 > 0x1FF) { - //nothing less to send out. + //nothing left to send out. Re-enable receiver. + usart_set_mode(XN_USART_INST, USART_MODE_TX_RX); digitalWrite(MAX485_CONTROL, LOW); //RECEIVE_MODE #if defined(STM32F1) // disable Tx empty interrupt, otherwise it goes haywire @@ -1138,6 +1144,7 @@ void XpressNetMasterClass::XNetSendNext(void) { if (XNet_state == XNet_send_data && XNetSlaveMode == 0x00) XNet_state = XNet_get_callbyte; else XNet_state = XNet_wait_receive; //wait for receive from client + return; } @@ -1180,16 +1187,16 @@ void XpressNetMasterClass::XNetSendNext(void) { XNetSendNext(); #elif defined(STM32F1) + // disable UART receiver to avoid rx errors during tx (low rx line) + usart_set_mode(XN_USART_INST, USART_MODE_TX); digitalWrite(MAX485_CONTROL, HIGH); //SEND_MODE /* put the data into buffer, and send */ usart_send(XN_USART_INST, data9); - // enable Tx empty interrupt - //usart_enable_tx_interrupt(XN_USART_INST); - // TODO see comment about miss hal function above + // enable transfer complete interrupt + // TODO see comment about missing hal function above USART_CR1(XN_USART_INST) |= USART_CR1_TCIE; #endif - } //-------------------------------------------------------------------------------------------- @@ -1265,10 +1272,10 @@ void XpressNetMasterClass::XNetReceive(void) #ifdef __AVR_ATmega8__ // Filter the 9th bit, then return if (UCSRB & (1 << RXB8)) { - XNetMsgCallByte = UDR; //only store data + XNetRxMsgBuf[XNetRxMsgWr][XNetMaxDataLength] = UDR; //only store data XNetSlaveMode = XNetSlaveCycle; //reactivate SLAVE MODE // XNetMsg[XNetlength] = 0x00; - if (XNetMsgCallByte == MY_ADDRESS) + if (XNetRxMsgBuf[XNetRxMsgWr][XNetMaxDataLength] == MY_ADDRESS) XNetSendNext(); //start sending out by interrupt } else { @@ -1278,10 +1285,10 @@ void XpressNetMasterClass::XNetReceive(void) #elif defined(SERIAL_PORT_0) // Filter the 9th bit, then return if (UCSR0B & (1 << RXB80)) { - XNetMsgCallByte = UDR0; //only store data + XNetRxMsgBuf[XNetRxMsgWr][XNetMaxDataLength] = UDR0; //only store data XNetSlaveMode = XNetSlaveCycle; //reactivate SLAVE MODE // XNetMsg[XNetlength] = 0x00; - if (XNetMsgCallByte == MY_ADDRESS) + if (XNetRxMsgBuf[XNetRxMsgWr][XNetMaxDataLength] == MY_ADDRESS) XNetSendNext(); //start sending out by interrupt } else { @@ -1291,10 +1298,10 @@ void XpressNetMasterClass::XNetReceive(void) #else // Filter the 9th bit, then return if (UCSR1B & (1 << RXB81)) { - XNetMsgCallByte = UDR1; //only store data + XNetRxMsgBuf[XNetRxMsgWr][XNetMaxDataLength] = UDR1; //only store data XNetSlaveMode = XNetSlaveCycle; //reactivate SLAVE MODE // XNetMsg[XNetlength] = 0x00; - if (XNetMsgCallByte == MY_ADDRESS) + if (XNetRxMsgBuf[XNetRxMsgWr][XNetMaxDataLength] == MY_ADDRESS) XNetSendNext(); //start sending out by interrupt } else { @@ -1306,9 +1313,9 @@ void XpressNetMasterClass::XNetReceive(void) if (rs485.available()) { int data = rs485.read(); if (data > 0xFF) { - XNetMsgCallByte = data; + XNetRxMsgBuf[XNetRxMsgWr][XNetMaxDataLength] = data; XNetSlaveMode = XNetSlaveCycle; //reactivate SLAVE MODE - if (XNetMsgCallByte == MY_ADDRESS) + if (XNetRxMsgBuf[XNetRxMsgWr][XNetMaxDataLength] == MY_ADDRESS) XNetSendNext(); //start sending out by interrupt } else { @@ -1320,11 +1327,12 @@ void XpressNetMasterClass::XNetReceive(void) // Filter the 9th bit, then return call byte uint16_t rx_data_9b = usart_recv(XN_USART_INST); if (rx_data_9b & (1 << 8)) { - XNetMsgCallByte = (uint8_t) (rx_data_9b & 0xFF); //only store data + XNetRxMsgBuf[XNetRxMsgWr][XNetMaxDataLength] = (uint8_t) (rx_data_9b & 0xFF); // only store callbyte body XNetSlaveMode = XNetSlaveCycle; //reactivate SLAVE MODE - // XNetMsg[XNetlength] = 0x00; - if (XNetMsgCallByte == MY_ADDRESS) - XNetSendNext(); //start sending out by interrupt + if (XNetRxMsgBuf[XNetRxMsgWr][XNetMaxDataLength] == MY_ADDRESS) { + // the master called our send slot, send out whatever is in the tx buffer + XNetSendNext(); + } } else { XNetMsgBuffer[XNetBufferlength]++; //weitere Nachrichtendaten @@ -1335,10 +1343,13 @@ void XpressNetMasterClass::XNetReceive(void) if (XNetMsgBuffer[XNetBufferlength] >= 2) { // header and one data byte or more received //Check length - length is inside header but without header and xor! if (((XNetMsgBuffer[1] & 0x0F) + 2) == XNetMsgBuffer[XNetBufferlength]) { //reach data length? + // TODO use memcpy() for (byte i = 0; i < XNetMsgBuffer[XNetBufferlength]; i++) { - XNetMsg[i] = XNetMsgBuffer[i+1]; - } - XNetDataReady = true; + //XNetMsg[i] = XNetMsgBuffer[i+1]; + XNetRxMsgBuf[XNetRxMsgWr][i] = XNetMsgBuffer[i+1]; + } + // Increase the write pointer + XNetRxMsgWr = (XNetRxMsgWr + 1) % XnetRxBufSize; XNetMsgBuffer[XNetBufferlength] = 0x00; //clear! } if (XNetMsgBuffer[XNetBufferlength] >= (XNetMaxDataLength) ) { //overflow!!! @@ -1346,19 +1357,3 @@ void XpressNetMasterClass::XNetReceive(void) } } } - -//-------------------------------------------------------------------------------------------- -//L�schen des letzten gesendeten Befehls -void XpressNetMasterClass::XNetclear(void) -{ - //Reset Message - XNetMsg[XNetheader] = 0x00; - XNetMsg[XNetdata1] = 0x00; - XNetMsg[XNetdata2] = 0x00; - XNetMsg[XNetdata3] = 0x00; - XNetMsg[XNetdata4] = 0x00; - XNetMsg[XNetdata5] = 0x00; - XNetMsg[XNetdata6] = 0x00; - XNetMsg[XNetdata7] = 0x00; - XNetMsgCallByte = 0x00; //Reset CallByte -} diff --git a/c6021light/lib/XpressNetMaster/XpressNetMaster.h b/c6021light/lib/XpressNetMaster/XpressNetMaster.h index 6e9a1ef..ef71e4e 100644 --- a/c6021light/lib/XpressNetMaster/XpressNetMaster.h +++ b/c6021light/lib/XpressNetMaster/XpressNetMaster.h @@ -140,6 +140,8 @@ next transmission window between 400 microseconds and 500 milliseconds after the #define XNetBufferSize 5 //max Data Pakets (max: 4 Bit = 16!) #define XNetBufferMaxData 10 //max Bytes for each Paket (max: 15!) +#define XnetRxBufSize 4 // number of messages the rx buffer can hold + //XpressNet Mode (Master/Slave) #define XNetSlaveCycle 0xFF //max (255) cycles to Stay in SLAVE MODE when no CallByte is received @@ -229,7 +231,7 @@ class XpressNetMasterClass // library-accessible "private" interface private: - //Variables: + //Variables: byte XNet_state; //single state machine uint8_t XNetSlaveMode; // > 0 then we are working in SLAVE MODE bool XNetSlaveInit; //send initialize sequence @@ -238,8 +240,11 @@ class XpressNetMasterClass byte MAX485_CONTROL; //Port for send or receive control uint8_t XNetAdr; //Adresse des Abzufragenden XNet Device unsigned long XSendCount; //Zeit: Call Byte ausgesendet - byte XNetMsgCallByte; //Received CallByte for Msg - byte XNetMsg[XNetMaxDataLength]; //Serial receive (Length, Header, Data1 to Data7) + byte XNetMsgCallByte; // Received XN CallByte + byte XNetRxMsgBuf[XnetRxBufSize][XNetMaxDataLength+1]; // Receive buffer: 1 entry for receiving, one for processing. Callbyte at the end. + byte XNetRxMsgRd; // Read pointer into receive buffer + byte XNetRxMsgWr; // Write pointer into receive buffer + byte* XNetMsg; // Pointer to valid portion of RxMsgBuf (Length, Header, Data1 to Data7) byte XNetMsgBuffer[XNetMaxDataLength + 1]; //Read Buffer byte callByteParity (byte me); // calculate the parity bit @@ -250,15 +255,14 @@ class XpressNetMasterClass uint16_t SlotLokUse[32]; //store loco to DirectedOps void SetBusy(uint8_t Slot); //send busy message to slot that doesn't use void AddBusySlot(uint8_t UserOps, uint16_t Adr); //add loco to slot - void XNetclear(void); //Clear a old Message - //Functions: + //Functions: void unknown(void); //unbekannte Anfrage void getNextXNetAdr(void); //N�CHSTE Adr of XNet Device bool XNetCheckXOR(void); //Checks the XOR void XNetAnalyseReceived(void); //work on received data - //Serial send and receive: + //Serial send and receive: static XpressNetMasterClass *active_object; //aktuelle aktive Object for interrupt handler XSend XNetBuffer[XNetBufferSize]; //Sendbuffer for data that needs to send out byte XNetBufferSend; //position to read next data @@ -270,7 +274,6 @@ class XpressNetMasterClass void getXOR (uint8_t *data, byte length); // calculate the XOR void XNetSendNext(void); //Sendet Daten aus dem Buffer mittels Interrupt void XNetReceive(void); //Speichern der eingelesenen Daten - bool XNetDataReady; //Daten Fertig empfangen! uint8_t XNetCVAdr; //CV Adr that was read uint8_t XNetCVvalue; //read CV Value @@ -325,6 +328,4 @@ extern XpressNetMasterClass XpressNet; } #endif - #endif - From 74cc1ca15093d5bfa2dcf324a5c5fa8020f7e26e Mon Sep 17 00:00:00 2001 From: Florian Glaser Date: Mon, 15 Feb 2021 21:59:00 +0100 Subject: [PATCH 05/14] Make room for XpressNet messages with more than 8 bytes in the receive buffer. Fix a nasty bug that screws up the receive process in slave mode. --- c6021light/lib/XpressNetMaster/XpressNetMaster.cpp | 13 ++++++++----- c6021light/lib/XpressNetMaster/XpressNetMaster.h | 2 +- c6021light/src/hal/stm32I2C.cpp | 2 +- .../src/tasks/RoutingTask/XpressNetForwarder.cpp | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/c6021light/lib/XpressNetMaster/XpressNetMaster.cpp b/c6021light/lib/XpressNetMaster/XpressNetMaster.cpp index b961716..766a11f 100644 --- a/c6021light/lib/XpressNetMaster/XpressNetMaster.cpp +++ b/c6021light/lib/XpressNetMaster/XpressNetMaster.cpp @@ -232,7 +232,7 @@ void XpressNetMasterClass::update(void) break; case XNet_receive_data: //read client data, max 500ms time_diff = micros() - XSendCount; - if (time_diff >= XNetTimeReadData) { //Timeout? + if (XNetSlaveMode == 0x00 && time_diff >= XNetTimeReadData) { // Timeout? only check in master mode! XNet_state = XNet_get_callbyte; XNetMsgBuffer[XNetBufferlength] = 0x00; //reset buffer break; @@ -255,8 +255,9 @@ void XpressNetMasterClass::update(void) if (XNetSlaveMode == 0x00) { //MASTER MODE XNet_state = XNet_send_data; XNetSendNext(); //start sending out by interrupt + } else { + XNet_state = XNet_get_callbyte; } - else XNet_state = XNet_get_callbyte; break; case XNet_send_data: break; @@ -1129,8 +1130,8 @@ void XpressNetMasterClass::XNetSendNext(void) { if (data9 > 0x1FF) { //nothing left to send out. Re-enable receiver. - usart_set_mode(XN_USART_INST, USART_MODE_TX_RX); digitalWrite(MAX485_CONTROL, LOW); //RECEIVE_MODE + usart_set_mode(XN_USART_INST, USART_MODE_TX_RX); #if defined(STM32F1) // disable Tx empty interrupt, otherwise it goes haywire //usart_disable_tx_interrupt(XN_USART_INST); @@ -1141,9 +1142,11 @@ void XpressNetMasterClass::XNetSendNext(void) { */ USART_CR1(XN_USART_INST) &= ~USART_CR1_TCIE; #endif - if (XNet_state == XNet_send_data && XNetSlaveMode == 0x00) + if (XNet_state == XNet_send_data && XNetSlaveMode == 0x00) { XNet_state = XNet_get_callbyte; - else XNet_state = XNet_wait_receive; //wait for receive from client + } else { + XNet_state = XNet_wait_receive; //wait for receive from client + } return; } diff --git a/c6021light/lib/XpressNetMaster/XpressNetMaster.h b/c6021light/lib/XpressNetMaster/XpressNetMaster.h index ef71e4e..2553ee6 100644 --- a/c6021light/lib/XpressNetMaster/XpressNetMaster.h +++ b/c6021light/lib/XpressNetMaster/XpressNetMaster.h @@ -173,7 +173,7 @@ next transmission window between 400 microseconds and 500 milliseconds after the #define XNet_send_data 4 //XpressNet Befehl, jedes gesendete Byte -#define XNetMaxDataLength 8 +#define XNetMaxDataLength 10 #define XNetBufferlength 0 //Read Buffer length #define XNetheader 0 //Messageheader #define XNetdata1 1 //Databyte1 diff --git a/c6021light/src/hal/stm32I2C.cpp b/c6021light/src/hal/stm32I2C.cpp index 348e96d..f32e9e7 100644 --- a/c6021light/src/hal/stm32I2C.cpp +++ b/c6021light/src/hal/stm32I2C.cpp @@ -336,7 +336,7 @@ extern "C" void i2c1_ev_isr(void) { if (rxControl.bytesProcessed < 3) { switch (rxControl.bytesProcessed) { case 1: - rxControl.msgMemory.ptr->destination_ = i2c_get_data(I2C1); + rxControl.msgMemory.ptr->source_ = i2c_get_data(I2C1); break; case 2: rxControl.msgMemory.ptr->data_ = i2c_get_data(I2C1); diff --git a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp index ff601cf..b6dc38e 100644 --- a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp +++ b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp @@ -23,7 +23,7 @@ void XpressNetForwarder::forward(const RR32Can::CanFrame& frame) { xn_direction = 0; } - XpressNet.SetTrntPos(turnoutPacket.getLocid().getNumericAddress().value(), xn_direction, turnoutPacket.getPower()); + XpressNet.SetTrntPos(turnoutPacket.getLocid().getNumericAddress().value() + 4, xn_direction, turnoutPacket.getPower()); } break; } From 5b111411e92b152de663dc61ccd75fb618a3bfbb Mon Sep 17 00:00:00 2001 From: Damian Philipp Date: Fri, 19 Feb 2021 17:06:47 +0100 Subject: [PATCH 06/14] Made includes more test-friendly --- c6021light/src/XpressNet/XpressNetMsg.cpp | 1 + c6021light/src/XpressNet/XpressNetMsg.h | 1 - c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp | 2 ++ c6021light/src/tasks/RoutingTask/XpressNetForwarder.h | 1 - 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/c6021light/src/XpressNet/XpressNetMsg.cpp b/c6021light/src/XpressNet/XpressNetMsg.cpp index a2b447e..ed3d38f 100644 --- a/c6021light/src/XpressNet/XpressNetMsg.cpp +++ b/c6021light/src/XpressNet/XpressNetMsg.cpp @@ -1,5 +1,6 @@ #include "AtomicRingBuffer/ObjectRingBuffer.h" +#include "XpressNetMaster.h" #include "XpressNetMsg.h" namespace XpressNetMsg { diff --git a/c6021light/src/XpressNet/XpressNetMsg.h b/c6021light/src/XpressNet/XpressNetMsg.h index 4148b92..04ecfb8 100644 --- a/c6021light/src/XpressNet/XpressNetMsg.h +++ b/c6021light/src/XpressNet/XpressNetMsg.h @@ -9,7 +9,6 @@ #endif #include "RR32Can/Types.h" -#include "XpressNetMaster.h" namespace XpressNetMsg { diff --git a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp index b6dc38e..07fdeee 100644 --- a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp +++ b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp @@ -1,5 +1,7 @@ #include "tasks/RoutingTask/XpressNetForwarder.h" +#include "XpressNetMaster.h" + #include "RR32Can/Constants.h" #include "RR32Can/messages/S88Event.h" #include "RR32Can/messages/SystemMessage.h" diff --git a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h index 5c6f1d8..8474430 100644 --- a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h +++ b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h @@ -7,7 +7,6 @@ #include "RR32Can/messages/Data.h" #include "RR32Can/messages/Identifier.h" -#include #include "XpressNet/XpressNetMsg.h" From e163c0d89061f2573a7dc030bd981cac3a776d8a Mon Sep 17 00:00:00 2001 From: Florian Glaser Date: Tue, 2 Mar 2021 13:20:17 +0100 Subject: [PATCH 07/14] First try of adding XpressNet to UnitTests --- c6021light/CMakeLists.txt | 4 ++++ c6021light/src/XpressNet/XpressNetMsg.cpp | 3 +-- c6021light/src/XpressNet/XpressNetMsg.h | 2 ++ c6021light/test/mocks/RoutingTaskFixture.h | 4 ++++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/c6021light/CMakeLists.txt b/c6021light/CMakeLists.txt index 4b56221..7942ad4 100644 --- a/c6021light/CMakeLists.txt +++ b/c6021light/CMakeLists.txt @@ -95,12 +95,14 @@ add_executable(c6021lightTest "test/integration/StopGoRequest.cpp" "test/integration/CanEngineDBRequest.cpp" "test/mocks/LocoNet.cpp" + "test/mocks/XpressNetMaster.cpp" "test/mocks/RoutingForwarderMock.cpp" "test/integration/SlotServerProcessing.cpp" "test/integration/EngineRouting.cpp" "src/tasks/RoutingTask/CANForwarder.cpp" "src/tasks/RoutingTask/I2CForwarder.cpp" + "src/tasks/RoutingTask/XpressNetForwarder.cpp" "src/tasks/RoutingTask/LocoNetForwarder.cpp" "src/tasks/RoutingTask/LocoNetHelpers.cpp" "src/tasks/RoutingTask/LocoNetPrinter.cpp" @@ -110,6 +112,8 @@ add_executable(c6021lightTest "src/MarklinI2C/Messages/AccessoryMsg.cpp" + "src/XpressNet/XpressNetMsg.cpp" + "${RR32CAN_LIB_SRCS}" "${RR32CANENGINEDB_LIB_SRCS}" ) diff --git a/c6021light/src/XpressNet/XpressNetMsg.cpp b/c6021light/src/XpressNet/XpressNetMsg.cpp index ed3d38f..c5e1e0b 100644 --- a/c6021light/src/XpressNet/XpressNetMsg.cpp +++ b/c6021light/src/XpressNet/XpressNetMsg.cpp @@ -1,7 +1,6 @@ -#include "AtomicRingBuffer/ObjectRingBuffer.h" +#include "XpressNetMsg.h" #include "XpressNetMaster.h" -#include "XpressNetMsg.h" namespace XpressNetMsg { diff --git a/c6021light/src/XpressNet/XpressNetMsg.h b/c6021light/src/XpressNet/XpressNetMsg.h index 04ecfb8..3c07aeb 100644 --- a/c6021light/src/XpressNet/XpressNetMsg.h +++ b/c6021light/src/XpressNet/XpressNetMsg.h @@ -8,6 +8,8 @@ #include #endif +#include + #include "RR32Can/Types.h" namespace XpressNetMsg { diff --git a/c6021light/test/mocks/RoutingTaskFixture.h b/c6021light/test/mocks/RoutingTaskFixture.h index 0c5133b..7e2c8ee 100644 --- a/c6021light/test/mocks/RoutingTaskFixture.h +++ b/c6021light/test/mocks/RoutingTaskFixture.h @@ -5,6 +5,7 @@ #include "gtest/gtest.h" #include "mocks/LocoNet.h" +#include "mocks/XpressNetMaster.h" #include "mocks/RoutingForwarderMock.h" #include "RR32Can/RR32Can.h" @@ -24,6 +25,7 @@ class RoutingTaskFixture : public Test { public: void SetUp() { mocks::LocoNetInstance = &lnHal; + mocks::XpressNetMasterInstance = &xnHal; hal::canMock = &canHal; hal::i2cMock = &i2cHal; @@ -39,6 +41,7 @@ class RoutingTaskFixture : public Test { hal::canMock = nullptr; hal::i2cMock = nullptr; mocks::LocoNetInstance = nullptr; + mocks::XpressNetMasterInstance = nullptr; } DataModel dataModel; @@ -47,6 +50,7 @@ class RoutingTaskFixture : public Test { StrictMock canTx; StrictMock lnHal; StrictMock lnTx; + StrictMock xnHal; tasks::RoutingTask::RoutingTask routingTask; StrictMock stopGoTimer; StrictMock canEngineDBTimer; From 466de7f5e7deca9106d32c8db9878d16b7084c78 Mon Sep 17 00:00:00 2001 From: Florian Glaser Date: Tue, 2 Mar 2021 22:41:23 +0100 Subject: [PATCH 08/14] Actually add XN mock implementation --- c6021light/test/mocks/XpressNetMaster.cpp | 9 +++++++ c6021light/test/mocks/XpressNetMaster.h | 31 +++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 c6021light/test/mocks/XpressNetMaster.cpp create mode 100644 c6021light/test/mocks/XpressNetMaster.h diff --git a/c6021light/test/mocks/XpressNetMaster.cpp b/c6021light/test/mocks/XpressNetMaster.cpp new file mode 100644 index 0000000..112d88a --- /dev/null +++ b/c6021light/test/mocks/XpressNetMaster.cpp @@ -0,0 +1,9 @@ +#include "mocks/XpressNetMaster.h" + +namespace mocks { +XpressNetMasterClass* XpressNetMasterInstance; +} + +void notifyXNetGlobal(void) { + return; +} diff --git a/c6021light/test/mocks/XpressNetMaster.h b/c6021light/test/mocks/XpressNetMaster.h new file mode 100644 index 0000000..85f4a86 --- /dev/null +++ b/c6021light/test/mocks/XpressNetMaster.h @@ -0,0 +1,31 @@ +#ifndef __MOCKS__XPRESSNETMASTER_H__ +#define __MOCKS__XPRESSNETMASTER_H__ + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#define XpressNet (*mocks::XpressNetMasterInstance) + +#define csNormal 0x00 +#define csTrackVoltageOff 0x02 + +namespace mocks { + +/* + * \brief Class XpressNetMaster + */ +class XpressNetMasterClass { + public: + MOCK_METHOD(void, setPower, (uint8_t Power), ()); + MOCK_METHOD(void, SetTrntPos, (uint16_t Address, uint8_t state, uint8_t active), ()); +}; + +extern XpressNetMasterClass* XpressNetMasterInstance; + +} // namespace mocks + +void notifyXNetGlobal(void); + +#endif // __MOCKS__XPRESSNETMASTER_H__ From 90d2fa0b74bd2e075fa8c384fa0233028707b378 Mon Sep 17 00:00:00 2001 From: Florian Glaser Date: Tue, 9 Mar 2021 01:57:33 +0100 Subject: [PATCH 09/14] Finally got ctests to compile. Hoorray --- c6021light/CMakeLists.txt | 2 -- c6021light/src/XpressNet/XpressNetMsg.cpp | 10 ++++++---- c6021light/src/XpressNet/XpressNetMsg.h | 1 - c6021light/test/mocks/XpressNetMaster.cpp | 14 +++++++++++--- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/c6021light/CMakeLists.txt b/c6021light/CMakeLists.txt index 7942ad4..3a4f661 100644 --- a/c6021light/CMakeLists.txt +++ b/c6021light/CMakeLists.txt @@ -112,8 +112,6 @@ add_executable(c6021lightTest "src/MarklinI2C/Messages/AccessoryMsg.cpp" - "src/XpressNet/XpressNetMsg.cpp" - "${RR32CAN_LIB_SRCS}" "${RR32CANENGINEDB_LIB_SRCS}" ) diff --git a/c6021light/src/XpressNet/XpressNetMsg.cpp b/c6021light/src/XpressNet/XpressNetMsg.cpp index c5e1e0b..8f7a31b 100644 --- a/c6021light/src/XpressNet/XpressNetMsg.cpp +++ b/c6021light/src/XpressNet/XpressNetMsg.cpp @@ -8,16 +8,14 @@ constexpr static const uint8_t kXNQueueSize = 5; using QueueType = AtomicRingBuffer::ObjectRingBuffer; +void freeXNRXMessage(XN_MsgPtr_t msgPtr); + QueueType XN_RxQueue; XN_RxMsgPtr_t getXNMessage() { return XN_RxMsgPtr_t{XN_RxQueue.peek().ptr, freeXNRXMessage}; } -void freeXNRXMessage(XN_MsgPtr_t msgPtr) { - XN_RxQueue.consume(QueueType::MemoryRange{msgPtr, 1}); -} - void forwardRx(XN_Msg_t& msg) { // Simply put the message into the queue auto memory = XN_RxQueue.allocate(); @@ -27,6 +25,10 @@ void forwardRx(XN_Msg_t& msg) { } } +void freeXNRXMessage(XN_MsgPtr_t msgPtr) { + XN_RxQueue.consume(QueueType::MemoryRange{msgPtr, 1}); +} + } // namespace XpressNetMsg // TODO shall we move the notify functions to a separate file? diff --git a/c6021light/src/XpressNet/XpressNetMsg.h b/c6021light/src/XpressNet/XpressNetMsg.h index 3c07aeb..a5edf0b 100644 --- a/c6021light/src/XpressNet/XpressNetMsg.h +++ b/c6021light/src/XpressNet/XpressNetMsg.h @@ -35,7 +35,6 @@ using XN_MsgPtr_t = XN_Msg_t*; XN_RxMsgPtr_t getXNMessage(); void forwardRx(XN_Msg_t& msg); -void freeXNRXMessage(XN_MsgPtr_t msgPtr); } // namespace XpressNetMsg diff --git a/c6021light/test/mocks/XpressNetMaster.cpp b/c6021light/test/mocks/XpressNetMaster.cpp index 112d88a..89d06e8 100644 --- a/c6021light/test/mocks/XpressNetMaster.cpp +++ b/c6021light/test/mocks/XpressNetMaster.cpp @@ -1,9 +1,17 @@ #include "mocks/XpressNetMaster.h" +#include "XpressNet/XpressNetMsg.h" + namespace mocks { XpressNetMasterClass* XpressNetMasterInstance; } -void notifyXNetGlobal(void) { - return; -} +void notifyXNetGlobal(void) { return; } + +namespace XpressNetMsg { + +XN_RxMsgPtr_t getXNMessage() { return XN_RxMsgPtr_t{nullptr, nullptr}; } + +void forwardRx(XN_Msg_t& msg) { return; } + +} // namespace XpressNetMsg From 3ca9edd3eadaa9beada4648fb129560524999f71 Mon Sep 17 00:00:00 2001 From: Damian Philipp Date: Sat, 5 Jun 2021 20:19:56 +0200 Subject: [PATCH 10/14] Run clang-format on all files. --- c6021light/src/XpressNet/XpressNetMsg.cpp | 11 +++----- c6021light/src/XpressNet/XpressNetMsg.h | 6 +---- c6021light/src/hal/LibOpencm3Hal.cpp | 2 +- .../src/tasks/ConsoleTask/ConsoleTask.cpp | 4 +-- .../src/tasks/ConsoleTask/ConsoleTask.h | 2 +- .../src/tasks/RoutingTask/RoutingTask.cpp | 25 +++++++++---------- .../tasks/RoutingTask/XpressNetForwarder.cpp | 13 +++++----- .../tasks/RoutingTask/XpressNetForwarder.h | 3 --- c6021light/test/mocks/RoutingTaskFixture.h | 2 +- 9 files changed, 29 insertions(+), 39 deletions(-) diff --git a/c6021light/src/XpressNet/XpressNetMsg.cpp b/c6021light/src/XpressNet/XpressNetMsg.cpp index 8f7a31b..7f5113d 100644 --- a/c6021light/src/XpressNet/XpressNetMsg.cpp +++ b/c6021light/src/XpressNet/XpressNetMsg.cpp @@ -12,9 +12,7 @@ void freeXNRXMessage(XN_MsgPtr_t msgPtr); QueueType XN_RxQueue; -XN_RxMsgPtr_t getXNMessage() { - return XN_RxMsgPtr_t{XN_RxQueue.peek().ptr, freeXNRXMessage}; -} +XN_RxMsgPtr_t getXNMessage() { return XN_RxMsgPtr_t{XN_RxQueue.peek().ptr, freeXNRXMessage}; } void forwardRx(XN_Msg_t& msg) { // Simply put the message into the queue @@ -25,9 +23,7 @@ void forwardRx(XN_Msg_t& msg) { } } -void freeXNRXMessage(XN_MsgPtr_t msgPtr) { - XN_RxQueue.consume(QueueType::MemoryRange{msgPtr, 1}); -} +void freeXNRXMessage(XN_MsgPtr_t msgPtr) { XN_RxQueue.consume(QueueType::MemoryRange{msgPtr, 1}); } } // namespace XpressNetMsg @@ -38,6 +34,7 @@ void notifyXNetPower(uint8_t State) { XN_Msg.data.powerData = State; XpressNetMsg::forwardRx(XN_Msg); - if (notifyXNetGlobal) + if (notifyXNetGlobal) { notifyXNetGlobal(); + } } diff --git a/c6021light/src/XpressNet/XpressNetMsg.h b/c6021light/src/XpressNet/XpressNetMsg.h index a5edf0b..0d04757 100644 --- a/c6021light/src/XpressNet/XpressNetMsg.h +++ b/c6021light/src/XpressNet/XpressNetMsg.h @@ -14,10 +14,7 @@ namespace XpressNetMsg { -typedef enum { - POWER, - ACCESSORY_NOTIFY -} XN_Header_t; +typedef enum { POWER, ACCESSORY_NOTIFY } XN_Header_t; typedef union { uint8_t powerData; @@ -29,7 +26,6 @@ typedef struct { XN_MsgBody_t data; } XN_Msg_t; - using XN_RxMsgPtr_t = std::unique_ptr; using XN_MsgPtr_t = XN_Msg_t*; diff --git a/c6021light/src/hal/LibOpencm3Hal.cpp b/c6021light/src/hal/LibOpencm3Hal.cpp index 1cf0012..60050f6 100644 --- a/c6021light/src/hal/LibOpencm3Hal.cpp +++ b/c6021light/src/hal/LibOpencm3Hal.cpp @@ -54,7 +54,7 @@ void LibOpencm3Hal::beginGpio() { gpio_set(GPIOA, GPIO0); // Set Idle High (TODO: Correct?) gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, - GPIO4 | GPIO5 | GPIO6); // Debug I/O + GPIO4 | GPIO5 | GPIO6); // Debug I/O gpio_set(GPIOA, GPIO4 | GPIO5 | GPIO6); // Tx Ena Xpressnet diff --git a/c6021light/src/tasks/ConsoleTask/ConsoleTask.cpp b/c6021light/src/tasks/ConsoleTask/ConsoleTask.cpp index 4f9327a..a4fac9b 100644 --- a/c6021light/src/tasks/ConsoleTask/ConsoleTask.cpp +++ b/c6021light/src/tasks/ConsoleTask/ConsoleTask.cpp @@ -16,11 +16,11 @@ void ConsoleTask::TaskMain() { // Abuse polling ConsoleTask as a blocking LnTx handler lnTx_->DoBlockingSend(); - + /* TODO hacky solution for first XN trials * It is doable to trigger a notifier whenever update() needs to be called, * which then triggers a task which will call update(). - */ + */ XpressNet.update(); } } diff --git a/c6021light/src/tasks/ConsoleTask/ConsoleTask.h b/c6021light/src/tasks/ConsoleTask/ConsoleTask.h index b001279..da83abf 100644 --- a/c6021light/src/tasks/ConsoleTask/ConsoleTask.h +++ b/c6021light/src/tasks/ConsoleTask/ConsoleTask.h @@ -3,8 +3,8 @@ #include -#include "OsTask.h" #include +#include "OsTask.h" #include "LocoNetTx.h" diff --git a/c6021light/src/tasks/RoutingTask/RoutingTask.cpp b/c6021light/src/tasks/RoutingTask/RoutingTask.cpp index 04589d5..e65139c 100644 --- a/c6021light/src/tasks/RoutingTask/RoutingTask.cpp +++ b/c6021light/src/tasks/RoutingTask/RoutingTask.cpp @@ -125,22 +125,21 @@ void RoutingTask::matchEnginesFromLocoNetAndCan() { void RoutingTask::processXpressNet() { for (auto messagePtr = XpressNetMsg::getXNMessage(); messagePtr != nullptr; messagePtr = XpressNetMsg::getXNMessage()) { + RR32Can::CanFrame frame; - RR32Can::CanFrame frame; - - // Convert to generic CAN representation - if (xnForwarder_.MakeRR32CanMsg(*messagePtr, frame)) { - i2cForwarder_.forward(frame); - lnForwarder_.forward(frame); - // Forward to CAN - RR32Can::RR32Can.SendPacket(frame); + // Convert to generic CAN representation + if (xnForwarder_.MakeRR32CanMsg(*messagePtr, frame)) { + i2cForwarder_.forward(frame); + lnForwarder_.forward(frame); + // Forward to CAN + RR32Can::RR32Can.SendPacket(frame); - // Forward to self - RR32Can::RR32Can.HandlePacket(frame); - } + // Forward to self + RR32Can::RR32Can.HandlePacket(frame); + } - // Explicitly reset framePtr so that getXNMessage can produce a new message - messagePtr.reset(); + // Explicitly reset framePtr so that getXNMessage can produce a new message + messagePtr.reset(); } } diff --git a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp index 07fdeee..77e4f67 100644 --- a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp +++ b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp @@ -7,7 +7,6 @@ #include "RR32Can/messages/SystemMessage.h" #include "RR32Can/messages/TurnoutPacket.h" - namespace tasks { namespace RoutingTask { @@ -24,12 +23,13 @@ void XpressNetForwarder::forward(const RR32Can::CanFrame& frame) { } else { xn_direction = 0; } - - XpressNet.SetTrntPos(turnoutPacket.getLocid().getNumericAddress().value() + 4, xn_direction, turnoutPacket.getPower()); + + XpressNet.SetTrntPos(turnoutPacket.getLocid().getNumericAddress().value() + 4, xn_direction, + turnoutPacket.getPower()); } break; } - + case RR32Can::Command::SYSTEM_COMMAND: { const RR32Can::SystemMessage systemMessage(const_cast(frame.data)); switch (systemMessage.getSubcommand()) { @@ -58,9 +58,10 @@ void XpressNetForwarder::forwardLocoChange(const RR32Can::LocomotiveData& loco, // nothing here yet.. } -bool XpressNetForwarder::MakeRR32CanMsg(const XpressNetMsg::XN_Msg_t& XnPacket, RR32Can::CanFrame& frame) { +bool XpressNetForwarder::MakeRR32CanMsg(const XpressNetMsg::XN_Msg_t& XnPacket, + RR32Can::CanFrame& frame) { // Decode the message type - switch(XnPacket.header) { + switch (XnPacket.header) { case XpressNetMsg::POWER: { frame.id.setCommand(RR32Can::Command::SYSTEM_COMMAND); frame.id.setResponse(false); diff --git a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h index 8474430..9ca8d39 100644 --- a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h +++ b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.h @@ -9,7 +9,6 @@ #include "XpressNet/XpressNetMsg.h" - namespace tasks { namespace RoutingTask { @@ -18,13 +17,11 @@ namespace RoutingTask { */ class XpressNetForwarder final : public RoutingForwarder { public: - void forwardLocoChange(const RR32Can::LocomotiveData& loco, LocoDiff_t& diff) override; void forward(const RR32Can::CanFrame& frame) override; bool MakeRR32CanMsg(const XpressNetMsg::XN_Msg_t& XnPacket, RR32Can::CanFrame& frame); private: - }; } // namespace RoutingTask diff --git a/c6021light/test/mocks/RoutingTaskFixture.h b/c6021light/test/mocks/RoutingTaskFixture.h index 7e2c8ee..6a705c0 100644 --- a/c6021light/test/mocks/RoutingTaskFixture.h +++ b/c6021light/test/mocks/RoutingTaskFixture.h @@ -5,8 +5,8 @@ #include "gtest/gtest.h" #include "mocks/LocoNet.h" -#include "mocks/XpressNetMaster.h" #include "mocks/RoutingForwarderMock.h" +#include "mocks/XpressNetMaster.h" #include "RR32Can/RR32Can.h" From 1905f260cd4e0ec5521096914868b31910f31012 Mon Sep 17 00:00:00 2001 From: Damian Philipp Date: Sat, 5 Jun 2021 20:23:14 +0200 Subject: [PATCH 11/14] Make existing Unit Tests pass. Added expectations for XpressNet Output. --- .../test/integration/StatelessRouting.cpp | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/c6021light/test/integration/StatelessRouting.cpp b/c6021light/test/integration/StatelessRouting.cpp index 3b8ce7a..7a3d2e3 100644 --- a/c6021light/test/integration/StatelessRouting.cpp +++ b/c6021light/test/integration/StatelessRouting.cpp @@ -33,10 +33,13 @@ class TurnoutRoutingFixture : public mocks::RoutingTaskFixture, lnMsg LnPacket{Ln_Turnout(turnout, direction, power)}; }; -TEST_P(TurnoutRoutingFixture, TurnoutRequest_I2CtoCANandLocoNet) { +TEST_P(TurnoutRoutingFixture, TurnoutRequest_I2CtoCANandLocoNetandXn) { // Setup expectations EXPECT_CALL(canTx, SendPacket(canFrame)); EXPECT_CALL(lnTx, DoAsyncSend(LnPacket)); + EXPECT_CALL(xnHal, SetTrntPos(turnout.getNumericAddress().value() + 4, + TurnoutDirectionToIntegral(direction), + power)); // Addr, State, Active mocks::makeSequence(canHal); mocks::makeSequence(lnHal); @@ -48,9 +51,12 @@ TEST_P(TurnoutRoutingFixture, TurnoutRequest_I2CtoCANandLocoNet) { routingTask.loop(); } -TEST_P(TurnoutRoutingFixture, TurnoutRequest_CANtoLocoNetAndI2C) { +TEST_P(TurnoutRoutingFixture, TurnoutRequest_CANtoLocoNetAndI2CAndXn) { // Setup expectations EXPECT_CALL(lnTx, DoAsyncSend(LnPacket)); + EXPECT_CALL(xnHal, SetTrntPos(turnout.getNumericAddress().value() + 4, + TurnoutDirectionToIntegral(direction), + power)); // Addr, State, Active mocks::makeSequence(i2cHal); mocks::makeSequence(lnHal); @@ -62,10 +68,12 @@ TEST_P(TurnoutRoutingFixture, TurnoutRequest_CANtoLocoNetAndI2C) { routingTask.loop(); } -TEST_P(TurnoutRoutingFixture, TurnoutRequest_LocoNetToCANandI2C) { +TEST_P(TurnoutRoutingFixture, TurnoutRequest_LocoNetToCANandI2CandXn) { // Setup expectations EXPECT_CALL(canTx, SendPacket(canFrame)); - + EXPECT_CALL(xnHal, SetTrntPos(turnout.getNumericAddress().value() + 4, + TurnoutDirectionToIntegral(direction), + power)); // Addr, State, Active mocks::makeSequence(i2cHal); mocks::makeSequence(canHal); @@ -76,7 +84,7 @@ TEST_P(TurnoutRoutingFixture, TurnoutRequest_LocoNetToCANandI2C) { routingTask.loop(); } -TEST_P(TurnoutRoutingFixture, TurnoutRequest_I2CtoCANandLocoNet_WithGeneratedResponse) { +TEST_P(TurnoutRoutingFixture, TurnoutRequest_I2CtoCANandLocoNetandXn_WithGeneratedResponse) { // Configure data model dataModel.generateI2CTurnoutResponse = true; @@ -86,6 +94,9 @@ TEST_P(TurnoutRoutingFixture, TurnoutRequest_I2CtoCANandLocoNet_WithGeneratedRes MarklinI2C::Messages::AccessoryMsg::makeOutbound(turnout, direction, power); EXPECT_CALL(i2cHal, sendI2CMessage(i2cResponseMessage)); EXPECT_CALL(lnTx, DoAsyncSend(LnPacket)); + EXPECT_CALL(xnHal, SetTrntPos(turnout.getNumericAddress().value() + 4, + TurnoutDirectionToIntegral(direction), + power)); // Addr, State, Active mocks::makeSequence(canHal); mocks::makeSequence(lnHal); @@ -179,9 +190,10 @@ class PowerRoutingFixture : public mocks::RoutingTaskFixture, hal::StopGoRequest stopGoRequest; }; -TEST_P(PowerRoutingFixture, PowerRequest_CANtoLocoNet) { +TEST_P(PowerRoutingFixture, PowerRequest_CANtoLocoNetAndXn) { // Setup expectations EXPECT_CALL(lnTx, DoAsyncSend(LnPacket)); + EXPECT_CALL(xnHal, setPower(power ? csNormal : csTrackVoltageOff)); mocks::makeSequence(i2cHal); mocks::makeSequence(lnHal); @@ -208,9 +220,10 @@ TEST_P(PowerRoutingFixture, PowerResponse_CANtoLocoNet) { routingTask.loop(); } -TEST_P(PowerRoutingFixture, PowerRequest_LocoNetToCAN) { +TEST_P(PowerRoutingFixture, PowerRequest_LocoNetToCANAndXn) { // Setup expectations EXPECT_CALL(canTx, SendPacket(canFrame)); + EXPECT_CALL(xnHal, setPower(power ? csNormal : csTrackVoltageOff)); mocks::makeSequence(i2cHal); mocks::makeSequence(canHal); @@ -227,6 +240,7 @@ TEST_P(PowerRoutingFixture, PowerRequest_FromI2C) { // Expoected output packets EXPECT_CALL(canTx, SendPacket(canFrame)); EXPECT_CALL(lnTx, DoAsyncSend(LnPacket)); + EXPECT_CALL(xnHal, setPower(power ? csNormal : csTrackVoltageOff)); // Expected input packets mocks::makeSequence(i2cHal); From 2b373910c481ee6fdc1f32457b8999e286749f12 Mon Sep 17 00:00:00 2001 From: Damian Philipp Date: Sat, 5 Jun 2021 20:38:43 +0200 Subject: [PATCH 12/14] Update CodeCoverage.cmake --- c6021light/CodeCoverage.cmake | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/c6021light/CodeCoverage.cmake b/c6021light/CodeCoverage.cmake index 27e7d3d..fe9c780 100644 --- a/c6021light/CodeCoverage.cmake +++ b/c6021light/CodeCoverage.cmake @@ -66,6 +66,10 @@ # - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters # in EXCLUDEs, and remove manual escaping from gcovr targets # +# 2020-05-04, Mihchael Davis +# - Add -fprofile-abs-path to make gcno files contain absolute paths +# - Fix BASE_DIRECTORY not working when defined +# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines # USAGE: # # 1. Copy this file into your cmake modules path. @@ -139,7 +143,7 @@ elseif(NOT CMAKE_COMPILER_IS_GNUCXX) endif() endif() -set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage" +set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage -fprofile-abs-path" CACHE INTERNAL "") set(CMAKE_Fortran_FLAGS_COVERAGE @@ -209,7 +213,7 @@ function(setup_target_for_coverage_lcov) endif() # NOT GENHTML_PATH # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR - if(${Coverage_BASE_DIRECTORY}) + if(DEFINED Coverage_BASE_DIRECTORY) get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) else() set(BASEDIR ${PROJECT_SOURCE_DIR}) @@ -257,8 +261,7 @@ function(setup_target_for_coverage_lcov) ${Coverage_NAME}.capture ${Coverage_NAME}.total ${Coverage_NAME}.info - ${Coverage_NAME} # report directory - + ${Coverage_NAME}/index.html WORKING_DIRECTORY ${PROJECT_BINARY_DIR} DEPENDS ${Coverage_DEPENDENCIES} VERBATIM # Protect arguments to commands @@ -305,7 +308,7 @@ function(setup_target_for_coverage_gcovr_xml) endif() # NOT GCOVR_PATH # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR - if(${Coverage_BASE_DIRECTORY}) + if(DEFINED Coverage_BASE_DIRECTORY) get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) else() set(BASEDIR ${PROJECT_SOURCE_DIR}) @@ -377,7 +380,7 @@ function(setup_target_for_coverage_gcovr_html) endif() # NOT GCOVR_PATH # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR - if(${Coverage_BASE_DIRECTORY}) + if(DEFINED Coverage_BASE_DIRECTORY) get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) else() set(BASEDIR ${PROJECT_SOURCE_DIR}) @@ -413,7 +416,7 @@ function(setup_target_for_coverage_gcovr_html) --object-directory=${PROJECT_BINARY_DIR} -o ${Coverage_NAME}/index.html - BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME} # report directory + BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory WORKING_DIRECTORY ${PROJECT_BINARY_DIR} DEPENDS ${Coverage_DEPENDENCIES} VERBATIM # Protect arguments to commands From 87604070212d87cbc00be045725e31b258a8c6aa Mon Sep 17 00:00:00 2001 From: Damian Philipp Date: Sat, 5 Jun 2021 20:56:40 +0200 Subject: [PATCH 13/14] Add missing braces --- c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp index 77e4f67..197be66 100644 --- a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp +++ b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp @@ -34,11 +34,15 @@ void XpressNetForwarder::forward(const RR32Can::CanFrame& frame) { const RR32Can::SystemMessage systemMessage(const_cast(frame.data)); switch (systemMessage.getSubcommand()) { case RR32Can::SystemSubcommand::SYSTEM_STOP: { - if (!frame.id.isResponse()) XpressNet.setPower(csTrackVoltageOff); + if (!frame.id.isResponse()) { + XpressNet.setPower(csTrackVoltageOff); + } break; } case RR32Can::SystemSubcommand::SYSTEM_GO: { - if (!frame.id.isResponse()) XpressNet.setPower(csNormal); + if (!frame.id.isResponse()) { + XpressNet.setPower(csNormal); + } break; } default: From 734d4f0992a66b48336908d85a0d22d7f0984bc7 Mon Sep 17 00:00:00 2001 From: Damian Philipp Date: Sat, 5 Jun 2021 21:05:08 +0200 Subject: [PATCH 14/14] Switch to RR32Can utilities --- .../src/tasks/RoutingTask/XpressNetForwarder.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp index 197be66..e55eb06 100644 --- a/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp +++ b/c6021light/src/tasks/RoutingTask/XpressNetForwarder.cpp @@ -6,6 +6,7 @@ #include "RR32Can/messages/S88Event.h" #include "RR32Can/messages/SystemMessage.h" #include "RR32Can/messages/TurnoutPacket.h" +#include "RR32Can/util/constexpr.h" namespace tasks { namespace RoutingTask { @@ -67,15 +68,10 @@ bool XpressNetForwarder::MakeRR32CanMsg(const XpressNetMsg::XN_Msg_t& XnPacket, // Decode the message type switch (XnPacket.header) { case XpressNetMsg::POWER: { - frame.id.setCommand(RR32Can::Command::SYSTEM_COMMAND); - frame.id.setResponse(false); - RR32Can::SystemMessage systemMessage(frame.data); - systemMessage.initData(); - if (XnPacket.data.powerData == csNormal) { - systemMessage.setSubcommand(RR32Can::SystemSubcommand::SYSTEM_GO); + frame = RR32Can::util::System_Go(false); } else if (XnPacket.data.powerData == csTrackVoltageOff) { - systemMessage.setSubcommand(RR32Can::SystemSubcommand::SYSTEM_STOP); + frame = RR32Can::util::System_Stop(false); } else { return false; break;