diff --git a/include/canmap.h b/include/canmap.h index a344111..b075b72 100644 --- a/include/canmap.h +++ b/include/canmap.h @@ -59,7 +59,7 @@ class CanMap: CanCallback uint8_t next; }; - CanMap(CanHardware* hw, bool loadFromFlash = true); + explicit CanMap(CanHardware* hw, bool loadFromFlash = true); CanHardware* GetHardware() { return canHardware; } void HandleClear() override; void HandleRx(uint32_t canId, uint32_t data[2], uint8_t dlc) override; @@ -95,7 +95,6 @@ class CanMap: CanCallback CANIDMAP canSendMap[MAX_MESSAGES]; CANIDMAP canRecvMap[MAX_MESSAGES]; CANPOS canPosMap[MAX_ITEMS + 1]; //Last item is a "tail" - uint32_t lastRxTimestamp; void ClearMap(CANIDMAP *canMap); int Add(CANIDMAP *canMap, Param::PARAM_NUM param, uint32_t canId, uint8_t offsetBits, int8_t length, float gain, int8_t offset); diff --git a/include/cansdo.h b/include/cansdo.h index 65efe76..af62027 100644 --- a/include/cansdo.h +++ b/include/cansdo.h @@ -52,9 +52,9 @@ class CanSdo: CanCallback, public IPutChar } __attribute__((packed)); /** Default constructor */ - CanSdo(CanHardware* hw, CanMap* cm = 0); + explicit CanSdo(CanHardware* hw, CanMap* cm = 0); CanHardware* GetHardware() { return canHardware; } - void HandleClear(); + void HandleClear() override; void HandleRx(uint32_t canId, uint32_t data[2], uint8_t dlc) override; void SDOWrite(uint8_t nodeId, uint16_t index, uint8_t subIndex, uint32_t data); void SDORead(uint8_t nodeId, uint16_t index, uint8_t subIndex); @@ -64,7 +64,8 @@ class CanSdo: CanCallback, public IPutChar int GetPrintRequest() { return printRequest; } SdoFrame* GetPendingUserspaceSdo() { return pendingUserSpaceSdo ? &pendingUserSpaceSdoFrame : 0; } void SendSdoReply(SdoFrame* sdoFrame); - void PutChar(char c); + void PutChar(char c) override; + void TriggerTimeout(int callingFrequency); private: CanHardware* canHardware; @@ -78,6 +79,7 @@ class CanSdo: CanCallback, public IPutChar volatile char printBuffer[64]; //Must be a power of 2 for efficient modulo calculation volatile uint32_t printByteIn; volatile uint32_t printByteOut; + volatile int printTimeout; //remaining time to wait Param::PARAM_NUM mapParam; uint32_t mapId; CanMap::CANPOS mapInfo; 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/include/stm32_can.h b/include/stm32_can.h index 0487978..ed47682 100644 --- a/include/stm32_can.h +++ b/include/stm32_can.h @@ -51,7 +51,6 @@ class Stm32Can: public CanHardware void SetFilterBank(int& idIndex, int& filterId, uint16_t* idList); void SetFilterBankMask(int& idIndex, int& filterId, uint16_t* idMaskList); void SetFilterBank29(int& idIndex, int& filterId, uint32_t* idList); - uint32_t GetFlashAddress(); static Stm32Can* interfaces[]; }; diff --git a/src/cansdo.cpp b/src/cansdo.cpp index 79431d0..8873ffa 100644 --- a/src/cansdo.cpp +++ b/src/cansdo.cpp @@ -36,6 +36,7 @@ #define PRINT_BUF_ENQUEUE(c) printBuffer[(printByteIn++) & (sizeof(printBuffer) - 1)] = c #define PRINT_BUF_DEQUEUE() printBuffer[(printByteOut++) & (sizeof(printBuffer) - 1)] #define PRINT_BUF_EMPTY() ((printByteOut - printByteIn) == sizeof(printBuffer)) +#define PRINT_TIMEOUT 1000 /** \brief * @@ -44,7 +45,10 @@ * */ CanSdo::CanSdo(CanHardware* hw, CanMap* cm) - : canHardware(hw), canMap(cm), nodeId(1), remoteNodeId(255), printRequest(-1) + : canHardware(hw), canMap(cm), nodeId(1), remoteNodeId(255), printRequest(-1), + printByteIn(0), printByteOut(sizeof(printBuffer)), printTimeout(PRINT_TIMEOUT), + mapParam(Param::PARAM_INVALID), mapId(0), sdoReplyValid(false), sdoReplyData(0), + pendingUserSpaceSdo(false) { canHardware->AddCallback(this); HandleClear(); @@ -234,10 +238,31 @@ void CanSdo::ProcessSDO(uint32_t data[2]) canHardware->Send(0x580 + nodeId, data); } +/** \brief count down PutChar character send timeout + * + * \param callingFrequency in ms. This is subtracted from the remaining wait time + * \return void + * + */ +void CanSdo::TriggerTimeout(int callingFrequency) +{ + if (printTimeout > 0) + { + printTimeout -= callingFrequency; + } + if (printTimeout < 0) + { + printTimeout = 0; + } +} + void CanSdo::PutChar(char c) { + if (printTimeout == 0) return; //last call to PutChar resulted in a timeout. Do not recover until the next burst + + printTimeout = PRINT_TIMEOUT; //When print buffer is full, wait - while (printByteIn == printByteOut); + while (printByteIn == printByteOut && printTimeout > 0); PRINT_BUF_ENQUEUE(c); printRequest = -1; //We can clear the print start trigger as we've obviously started printing @@ -257,6 +282,7 @@ bool CanSdo::ProcessSpecialSDOObjects(SdoFrame* sdo) { sdo->data = 65535; //this should be the size of JSON but we don't know this in advance. Hmm. sdo->cmd = SDO_RESPONSE_UPLOAD | SDO_SIZE_SPECIFIED; + printTimeout = PRINT_TIMEOUT; printByteIn = 0; printByteOut = sizeof(printBuffer); //both point to the beginning of the physical buffer but virtually they are 64 bytes apart printRequest = sdo->subIndex; diff --git a/src/stm32_can.cpp b/src/stm32_can.cpp index 16d0435..20c709d 100644 --- a/src/stm32_can.cpp +++ b/src/stm32_can.cpp @@ -28,6 +28,14 @@ #include #include #include "stm32_can.h" +#include "cortex.h" + +//Some functions use the "register" keyword which C++ doesn't like +//We can safely ignore that as we don't even use those functions +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wregister" +#include +#pragma GCC diagnostic pop #define MAX_INTERFACES 2 #define IDS_PER_BANK 4 @@ -37,6 +45,21 @@ #define CAN_PERIPH_SPEED 36 #endif // CAN_PERIPH_SPEED +// To allow concurrent sending of CAN frames from different contexts we need to +// disable interrupts. Some projects have hard realtime requirements which mean +// we cannot disable all interrupts. These projects should define the highest +// interrupt priority users of the CAN interface here. If not defined we assume +// that all interrupts can be disabled. +// +// CAN_MAX_IRQ_PRIORITY should match the priority passed to nvic_set_priority() +#ifdef CAN_MAX_IRQ_PRIORITY +#define DISABLE_CAN_USER_INTERRUPTS() cm_set_basepriority(CAN_MAX_IRQ_PRIORITY); +#define ENABLE_CAN_USER_INTERRUPTS() cm_set_basepriority(CM_BASEPRI_ENABLE_INTERRUPTS); +#else +#define DISABLE_CAN_USER_INTERRUPTS() cm_disable_interrupts() +#define ENABLE_CAN_USER_INTERRUPTS() cm_enable_interrupts() +#endif // CAN_MAX_IRQ_PRIORITY + struct CANSPEED { uint32_t ts1; @@ -198,6 +221,8 @@ void Stm32Can::SetBaudrate(enum baudrates baudrate) */ void Stm32Can::Send(uint32_t canId, uint32_t data[2], uint8_t len) { + DISABLE_CAN_USER_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 +239,9 @@ void Stm32Can::Send(uint32_t canId, uint32_t data[2], uint8_t len) { can_enable_irq(canDev, CAN_IER_TMEIE); } -} + ENABLE_CAN_USER_INTERRUPTS(); +} Stm32Can* Stm32Can::GetInterface(int index) {