From 087010a47c2411d2f1b9f8b17be2d24ba57c16f9 Mon Sep 17 00:00:00 2001 From: "David J. Fiddes" <35607151+davefiddes@users.noreply.github.com> Date: Wed, 24 Sep 2025 14:51:53 +0100 Subject: [PATCH 1/3] Allow listing of errors via CANopen SDO (#37) This exports the list of errors and time at which they occurred as two CANopen SDO arrays. The sub- index is used as an index into the list of errors. Any invalid indices return zero. Clients can use the "lasterr" parameter database spot-value which is present in all libopeninv projects to map the error number to a string. Tests: - Verify with updated OpenInverter CAN Tool that errors can be listed. - Initiate an new error after a period of time and ensure the list tallies with the elapsed wall-clock time. - Check CANopen SDO decodes as expected in Wireshark --- include/errormessage.h | 2 ++ src/cansdo.cpp | 30 ++++++++++++++++++++++++++++++ src/errormessage.cpp | 21 +++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/include/errormessage.h b/include/errormessage.h index b9f0560..76269e8 100644 --- a/include/errormessage.h +++ b/include/errormessage.h @@ -48,6 +48,8 @@ class ErrorMessage static void PrintAllErrors(); static void PrintNewErrors(); static ERROR_MESSAGE_NUM GetLastError(); + static ERROR_MESSAGE_NUM GetErrorNum(uint8_t index); + static uint32_t GetErrorTime(uint8_t index); protected: private: static void PrintError(uint32_t time, ERROR_MESSAGE_NUM err); diff --git a/src/cansdo.cpp b/src/cansdo.cpp index 3241c6d..8e55263 100644 --- a/src/cansdo.cpp +++ b/src/cansdo.cpp @@ -18,6 +18,7 @@ */ #include "cansdo.h" #include "my_math.h" +#include "errormessage.h" #define SDO_REQ_ID_BASE 0x600U #define SDO_REP_ID_BASE 0x580U @@ -28,6 +29,9 @@ #define SDO_INDEX_MAP_RX 0x3001 #define SDO_INDEX_MAP_RD 0x3100 #define SDO_INDEX_STRINGS 0x5001 +#define SDO_INDEX_ERROR_NUM 0x5002 +#define SDO_INDEX_ERROR_TIME 0x5003 + #define PRINT_BUF_ENQUEUE(c) printBuffer[(printByteIn++) & (sizeof(printBuffer) - 1)] = c #define PRINT_BUF_DEQUEUE() printBuffer[(printByteOut++) & (sizeof(printBuffer) - 1)] @@ -196,6 +200,32 @@ void CanSdo::ProcessSDO(uint32_t data[2]) { ReadOrDeleteCanMap(sdo); } + else if (sdo->index == SDO_INDEX_ERROR_NUM) + { + if (sdo->cmd == SDO_READ) + { + sdo->data = ErrorMessage::GetErrorNum(sdo->subIndex); + sdo->cmd = SDO_READ_REPLY; + } + else + { + sdo->cmd = SDO_ABORT; + sdo->data = SDO_ERR_INVIDX; + } + } + else if (sdo->index == SDO_INDEX_ERROR_TIME) + { + if (sdo->cmd == SDO_READ) + { + sdo->data = ErrorMessage::GetErrorTime(sdo->subIndex); + sdo->cmd = SDO_READ_REPLY; + } + else + { + sdo->cmd = SDO_ABORT; + sdo->data = SDO_ERR_INVIDX; + } + } else { if (!ProcessSpecialSDOObjects(sdo)) diff --git a/src/errormessage.cpp b/src/errormessage.cpp index 313fd72..20195a9 100644 --- a/src/errormessage.cpp +++ b/src/errormessage.cpp @@ -106,6 +106,27 @@ ERROR_MESSAGE_NUM ErrorMessage::GetLastError() return lastError; } +ERROR_MESSAGE_NUM ErrorMessage::GetErrorNum(uint8_t index) +{ + if (index < ERROR_BUF_SIZE) + { + if (errorBuffer[index].time > 0) + return errorBuffer[index].msg; + } + + return ERROR_NONE; +} + +uint32_t ErrorMessage::GetErrorTime(uint8_t index) +{ + if (index < ERROR_BUF_SIZE) + { + return errorBuffer[index].time; + } + + return 0; +} + /** Print all errors currently in error memory */ void ErrorMessage::PrintAllErrors() { From 1708b751b8b6b12768efffc1cb416c3871bb1487 Mon Sep 17 00:00:00 2001 From: "David J. Fiddes" Date: Sat, 27 Sep 2025 14:20:02 +0100 Subject: [PATCH 2/3] Allow concurrent sending of CAN frames - partial fix for issue #40 Stm32Can::Send() can be called from multiple contexts in a typical libopeninv application (main loop, CAN RX interrupt or timer interrupt(s)). To avoid CAN TX frames being lost we should disable interrupts while we manipulate the TX mailbox and queue. Tests: - On a ZombieVerterVCU experiencing issue #40 repeatedly request clean parameter databases with: while true; do oic cache clean && oic -n 3 -t 300 read opmode; done - Verify databases are downloaded correctly every time - Verify ox100 and 0x300 CAN frames sent to VW SBox continue to be sent --- src/stm32_can.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/stm32_can.cpp b/src/stm32_can.cpp index 16d0435..16fff5b 100644 --- a/src/stm32_can.cpp +++ b/src/stm32_can.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "stm32_can.h" #define MAX_INTERFACES 2 @@ -198,6 +199,9 @@ void Stm32Can::SetBaudrate(enum baudrates baudrate) */ void Stm32Can::Send(uint32_t canId, uint32_t data[2], uint8_t len) { + bool irq_masked = cm_is_masked_interrupts(); + cm_disable_interrupts(); + can_disable_irq(canDev, CAN_IER_TMEIE); if (can_transmit(canDev, canId, canId > 0x7FF, false, len, (uint8_t*)data) < 0 && sendCnt < SENDBUFFER_LEN) @@ -214,8 +218,12 @@ void Stm32Can::Send(uint32_t canId, uint32_t data[2], uint8_t len) { can_enable_irq(canDev, CAN_IER_TMEIE); } -} + if (!irq_masked) + { + cm_enable_interrupts(); + } +} Stm32Can* Stm32Can::GetInterface(int index) { From 847eeab869c219bac43cefeb4b1ded44f55d57cb Mon Sep 17 00:00:00 2001 From: "David J. Fiddes" Date: Fri, 3 Oct 2025 18:05:46 +0100 Subject: [PATCH 3/3] Fix hard realtime performance and have reliable CAN send This changes uses the capability of ARM Cortex-M3 CPUs to raise the interrupt priority mask so that low priority interrupts do not interrupt CAN sending. High priority interrupts form PWM, overcurrent and emergency stop in applications like stm32-sine will be processed unimpeded. High priority interrupts, obviously, cannot send CAN messages. The change requires: - CAN_MAX_IRQ_PRIORITY to be defined in hwdefs.h - The priority of all interrupts that can send CAN frames be 1 or lower (bigger number, lower priority) Tests: - Patch code into Stm32-vcu and stm32-sine - Torture test database download with: while true; do oic cache clean && oic read opmode; done - On stm32-sine set canperiod to 10ms - Ensure stm32-sine is in Run. Check jitter on exciter output with a DSO with infinite perrsistence. The new code shows no jitter. The older code shows a jitter of around 5.2 uSec. --- include/cortex.h | 34 ++++++++++++++++++++++++++++++++++ src/stm32_can.cpp | 14 +++++++------- 2 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 include/cortex.h diff --git a/include/cortex.h b/include/cortex.h new file mode 100644 index 0000000..1586c8b --- /dev/null +++ b/include/cortex.h @@ -0,0 +1,34 @@ +/* + * This file is part of the libopeninv project. + * + * Copyright (C) 2025 David J. Fiddes + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CORTEX_H +#define CORTEX_H + +#include + +/** Do not mask any interrupts */ +#define CM_BASEPRI_ENABLE_INTERRUPTS 0 + +/** \brief Set the BASEPRI register to the given priority level */ +inline __attribute__((always_inline)) void cm_set_basepriority(uint32_t new_priority) +{ + __asm__ volatile("msr basepri, %0 " ::"r"(new_priority) : "memory"); +} + +#endif // CORTEX_H diff --git a/src/stm32_can.cpp b/src/stm32_can.cpp index 16fff5b..f35f4f8 100644 --- a/src/stm32_can.cpp +++ b/src/stm32_can.cpp @@ -27,8 +27,8 @@ #include #include #include -#include #include "stm32_can.h" +#include "cortex.h" #define MAX_INTERFACES 2 #define IDS_PER_BANK 4 @@ -38,6 +38,10 @@ #define CAN_PERIPH_SPEED 36 #endif // CAN_PERIPH_SPEED +#ifndef CAN_MAX_IRQ_PRIORITY +#warning "CAN_MAX_IRQ_PRIORITY the highest interrupt priority users of the CAN interface may use is not defined" +#endif // CAN_MAX_IRQ_PRIORITY + struct CANSPEED { uint32_t ts1; @@ -199,8 +203,7 @@ void Stm32Can::SetBaudrate(enum baudrates baudrate) */ void Stm32Can::Send(uint32_t canId, uint32_t data[2], uint8_t len) { - bool irq_masked = cm_is_masked_interrupts(); - cm_disable_interrupts(); + cm_set_basepriority(CAN_MAX_IRQ_PRIORITY); can_disable_irq(canDev, CAN_IER_TMEIE); @@ -219,10 +222,7 @@ void Stm32Can::Send(uint32_t canId, uint32_t data[2], uint8_t len) can_enable_irq(canDev, CAN_IER_TMEIE); } - if (!irq_masked) - { - cm_enable_interrupts(); - } + cm_set_basepriority(CM_BASEPRI_ENABLE_INTERRUPTS); } Stm32Can* Stm32Can::GetInterface(int index)