From 1e9ab611301285feb0c3f8e16c912d86201e19ef Mon Sep 17 00:00:00 2001 From: Tilen Date: Wed, 9 Sep 2026 22:51:41 +0200 Subject: [PATCH 1/3] fix: prepare STM32 driver for later rework to support more FDCAN peripherals --- CANopenNode_STM32/CO_app_STM32.c | 8 +- CANopenNode_STM32/CO_app_STM32.h | 53 ++- CANopenNode_STM32/CO_driver_STM32.c | 703 +++++++++++++++------------- 3 files changed, 418 insertions(+), 346 deletions(-) diff --git a/CANopenNode_STM32/CO_app_STM32.c b/CANopenNode_STM32/CO_app_STM32.c index ad93aa8..9bec2fd 100644 --- a/CANopenNode_STM32/CO_app_STM32.c +++ b/CANopenNode_STM32/CO_app_STM32.c @@ -27,8 +27,8 @@ #include "CO_app_STM32.h" #include "CANopen.h" #include "main.h" -#include #include +#include #include "CO_storageBlank.h" #include "OD.h" @@ -122,7 +122,7 @@ canopen_app_resetCommunication() { CO->CANmodule->CANnormal = false; /* Enter CAN configuration. */ - CO_CANsetConfigurationMode((void*)canopenNodeSTM32); + CO_CANsetConfigurationMode(canopenNodeSTM32); CO_CANmodule_disable(CO->CANmodule); /* initialize CANopen */ @@ -176,7 +176,7 @@ canopen_app_resetCommunication() { } /* Configure Timer interrupt function for execution every 1 millisecond */ - HAL_TIM_Base_Start_IT(canopenNodeSTM32->timerHandle); //1ms interrupt + HAL_TIM_Base_Start_IT(canopenNodeSTM32->timerHandle); // 1ms interrupt /* Configure CAN transmit and receive interrupt */ @@ -218,7 +218,7 @@ canopen_app_process() { if (reset_status == CO_RESET_COMM) { /* delete objects from memory */ - HAL_TIM_Base_Stop_IT(canopenNodeSTM32->timerHandle); + HAL_TIM_Base_Stop_IT(canopenNodeSTM32->timerHandle); CO_CANsetConfigurationMode((void*)canopenNodeSTM32); CO_delete(CO); log_printf("CANopenNode Reset Communication request\n"); diff --git a/CANopenNode_STM32/CO_app_STM32.h b/CANopenNode_STM32/CO_app_STM32.h index 16ef037..52320b8 100644 --- a/CANopenNode_STM32/CO_app_STM32.h +++ b/CANopenNode_STM32/CO_app_STM32.h @@ -11,11 +11,13 @@ #include "CANopen.h" #include "main.h" -/* CANHandle : Pass in the CAN Handle to this function and it wil be used for all CAN Communications. It can be FDCan or CAN +/* CANHandle : Pass in the CAN Handle to this function and it wil be used for all CAN Communications. It can be FDCan or + CAN * and CANOpenSTM32 Driver will take of care of handling that * HWInitFunction : Pass in the function that initialize the CAN peripheral, usually MX_CAN_Init * timerHandle : Pass in the timer that is going to be used for generating 1ms interrupt for tmrThread function, - * please note that CANOpenSTM32 Library will override HAL_TIM_PeriodElapsedCallback function, if you also need this function + * please note that CANOpenSTM32 Library will override HAL_TIM_PeriodElapsedCallback function, if you also need this + function * in your codes, please take required steps */ @@ -24,28 +26,32 @@ extern "C" { #endif +#if CO_STM32_FDCAN_Driver +typedef FDCAN_HandleTypeDef can_periph_handle_t; +#else /* CO_STM32_FDCAN_Driver */ +typedef CAN_HandleTypeDef can_periph_handle_t; +#endif /* CO_STM32_FDCAN_Driver */ typedef struct { - uint8_t - desiredNodeID; /*This is the Node ID that you ask the CANOpen stack to assign to your device, although it might not always - * be the final NodeID, after calling canopen_app_init() you should check ActiveNodeID of CANopenNodeSTM32 structure for assigned Node ID. - */ - uint8_t activeNodeID; /* Assigned Node ID */ + uint8_t desiredNodeID; /*This is the Node ID that you ask the CANOpen stack to assign to your device, although it + * might not always be the final NodeID, after calling canopen_app_init() you should check + * ActiveNodeID of CANopenNodeSTM32 structure for assigned Node ID. + */ + uint8_t activeNodeID; /* Assigned Node ID */ uint16_t baudrate; /* This is the baudrate you've set in your CubeMX Configuration */ - TIM_HandleTypeDef* - timerHandle; /*Pass in the timer that is going to be used for generating 1ms interrupt for tmrThread function, - * please note that CANOpenSTM32 Library will override HAL_TIM_PeriodElapsedCallback function, if you also need this function in your codes, please take required steps - */ + + /* + * Pass in the timer that is going to be used for generating 1ms interrupt for tmrThread function, + * please note that CANOpenSTM32 Library will override HAL_TIM_PeriodElapsedCallback function, if + * you also need this function in your codes, please take required steps + */ + TIM_HandleTypeDef* timerHandle; /* Pass in the CAN Handle to this function and it wil be used for all CAN Communications. It can be FDCan or CAN - * and CANOpenSTM32 Driver will take of care of handling that*/ -#ifdef CO_STM32_FDCAN_Driver - FDCAN_HandleTypeDef* CANHandle; -#else - CAN_HandleTypeDef* CANHandle; -#endif + * and CANOpenSTM32 Driver will take of care of handling that*/ + can_periph_handle_t* CANHandle; - void (*HWInitFunction)(); /* Pass in the function that initialize the CAN peripheral, usually MX_CAN_Init */ + void (*HWInitFunction)(void); /* Pass in the function that initialize the CAN peripheral, usually MX_CAN_Init */ uint8_t outStatusLEDGreen; // This will be updated by the stack - Use them for the LED management uint8_t outStatusLEDRed; // This will be updated by the stack - Use them for the LED management @@ -53,16 +59,17 @@ typedef struct { } CANopenNodeSTM32; - -// In order to use CANOpenSTM32, you'll have it have a canopenNodeSTM32 structure somewhere in your codes, it is usually residing in CO_app_STM32.c +// In order to use CANOpenSTM32, you'll have it have a canopenNodeSTM32 structure somewhere in your codes, it is usually +// residing in CO_app_STM32.c extern CANopenNodeSTM32* canopenNodeSTM32; - -/* This function will initialize the required CANOpen Stack objects, allocate the memory and prepare stack for communication reset*/ +/* This function will initialize the required CANOpen Stack objects, allocate the memory and prepare stack for + * communication reset*/ int canopen_app_init(CANopenNodeSTM32* canopenSTM32); /* This function will reset the CAN communication periperhal and also the CANOpen stack variables */ int canopen_app_resetCommunication(); -/* This function will check the input buffers and any outstanding tasks that are not time critical, this function should be called regurarly from your code (i.e from your while(1))*/ +/* This function will check the input buffers and any outstanding tasks that are not time critical, this function should + * be called regurarly from your code (i.e from your while(1))*/ void canopen_app_process(); /* Thread function executes in constant intervals, this function can be called from FreeRTOS tasks or Timers ********/ void canopen_app_interrupt(void); diff --git a/CANopenNode_STM32/CO_driver_STM32.c b/CANopenNode_STM32/CO_driver_STM32.c index ed9d972..f3bf156 100644 --- a/CANopenNode_STM32/CO_driver_STM32.c +++ b/CANopenNode_STM32/CO_driver_STM32.c @@ -31,8 +31,14 @@ #include "301/CO_driver.h" #include "CO_app_STM32.h" -/* Local CAN module object */ -static CO_CANmodule_t* CANModule_local = NULL; /* Local instance of global CAN module */ +/** + * \brief We use the local can module pointer, + * that is later used in the callbacks. + * + * We assume only one canopen instance is used at all times. + * It is declared as volatile, invoked from callbacks and interrupts + */ +static CO_CANmodule_t* volatile CANModule_local = NULL; /* Local instance of global CAN module */ /* CAN masks for identifiers */ #define CANID_MASK 0x07FF /*!< CAN standard ID mask */ @@ -49,48 +55,316 @@ static CO_CANmodule_t* CANModule_local = NULL; /* Local instance of global CAN m #warning "FDCAN_BUFFER_INDEXES not defined" #endif #endif +#endif /* CO_STM32_FDCAN_Driver */ + +/** + * \brief Returns the HAL instance pointer for the [FD]CAN instance for the MCU + * hardware implementation from the CO_CanHandle CanOpen instance + * \note It returns the pointer to the hardware peripheral instance, + * of type `can_periph_handle_t*` which is set depending on the CAN or FDCAN availability in the + * product silicon + */ +#define GET_CAN_PERIPH_HANDLE(_co_can_module_instance_) \ + (((CANopenNodeSTM32*)(_co_can_module_instance_)->CANptr)->CANHandle) + +/** + * \brief Convert buffer DLC number to the FDCAN peripheral marcos + * \param dlc: DLC number between 0 to 8 + */ +#define FDCAN_DLC_NUM_TO_PERIPH_VALUE(dlc) \ + ((dlc) == 0) \ + ? FDCAN_DLC_BYTES_0 \ + : (((dlc) == 1) \ + ? FDCAN_DLC_BYTES_1 \ + : (((dlc) == 2) \ + ? FDCAN_DLC_BYTES_2 \ + : (((dlc) == 3) \ + ? FDCAN_DLC_BYTES_3 \ + : (((dlc) == 4) ? FDCAN_DLC_BYTES_4 \ + : (((dlc) == 5) ? FDCAN_DLC_BYTES_5 \ + : (((dlc) == 6) ? FDCAN_DLC_BYTES_6 \ + : (((dlc) == 7) ? FDCAN_DLC_BYTES_7 \ + : ((dlc) == 8) ? FDCAN_DLC_BYTES_8 \ + : (0)))))))) + +/** + * \brief Converts an FDCAN peripheral DLC value (FDCAN_DLC_BYTES_x) back into + * the actual number of data bytes it represents + */ +#define FDCAN_DLC_PERIPH_TO_NUM(dlc) \ + ((dlc) == FDCAN_DLC_BYTES_0) \ + ? 0 \ + : (((dlc) == FDCAN_DLC_BYTES_1) \ + ? 1 \ + : (((dlc) == FDCAN_DLC_BYTES_2) \ + ? 2 \ + : (((dlc) == FDCAN_DLC_BYTES_3) \ + ? 3 \ + : (((dlc) == FDCAN_DLC_BYTES_4) \ + ? 4 \ + : (((dlc) == FDCAN_DLC_BYTES_5) \ + ? 5 \ + : (((dlc) == FDCAN_DLC_BYTES_6) \ + ? 6 \ + : (((dlc) == FDCAN_DLC_BYTES_7) ? 7 \ + : ((dlc) == FDCAN_DLC_BYTES_8) ? 8 \ + : (0)))))))) + +#ifdef CO_STM32_FDCAN_Driver +static void prv_fdcan_bus_off_check_reset(FDCAN_HandleTypeDef* hfdcan); +#endif /* CO_STM32_FDCAN_Driver */ + +/** + * \brief Send CAN message to network + * This function must be called with atomic access. + * + * \param[in] CANmodule: CAN module instance + * \param[in] buffer: Pointer to buffer to transmit + * \return `1` on success, `0` otherwise + */ +static uint8_t +prv_send_can_message(CO_CANmodule_t* CANmodule, CO_CANtx_t* buffer) { + uint8_t success = 0; + + /* Check if TX FIFO is ready to accept more messages */ +#ifdef CO_STM32_FDCAN_Driver + static FDCAN_TxHeaderTypeDef tx_hdr; + + if (HAL_FDCAN_GetTxFifoFreeLevel(GET_CAN_PERIPH_HANDLE(CANmodule)) > 0) { + /* + * RTR flag is part of identifier value + * hence it needs to be properly decoded + */ + tx_hdr.Identifier = buffer->ident & CANID_MASK; + tx_hdr.TxFrameType = (buffer->ident & FLAG_RTR) ? FDCAN_REMOTE_FRAME : FDCAN_DATA_FRAME; + tx_hdr.IdType = FDCAN_STANDARD_ID; + tx_hdr.FDFormat = FDCAN_CLASSIC_CAN; + tx_hdr.BitRateSwitch = FDCAN_BRS_OFF; + tx_hdr.MessageMarker = 0; + tx_hdr.ErrorStateIndicator = FDCAN_ESI_ACTIVE; + tx_hdr.TxEventFifoControl = FDCAN_NO_TX_EVENTS; + + tx_hdr.DataLength = FDCAN_DLC_NUM_TO_PERIPH_VALUE(buffer->DLC); + + /* Now add message to FIFO. Should not fail */ + success = HAL_FDCAN_AddMessageToTxFifoQ(GET_CAN_PERIPH_HANDLE(CANmodule), &tx_hdr, buffer->data) == HAL_OK; + } +#else + static CAN_TxHeaderTypeDef tx_hdr; + /* Check if TX FIFO is ready to accept more messages */ + if (HAL_CAN_GetTxMailboxesFreeLevel(GET_CAN_PERIPH_HANDLE(CANmodule)) > 0) { + uint32_t TxMailboxNum = 0; + + /* + * RTR flag is part of identifier value + * hence it needs to be properly decoded + */ + tx_hdr.ExtId = 0u; + tx_hdr.IDE = CAN_ID_STD; + tx_hdr.DLC = buffer->DLC; + tx_hdr.StdId = buffer->ident & CANID_MASK; + tx_hdr.RTR = (buffer->ident & FLAG_RTR) ? CAN_RTR_REMOTE : CAN_RTR_DATA; + + /* Now add message to FIFO. Should not fail */ + success = + HAL_CAN_AddTxMessage(GET_CAN_PERIPH_HANDLE(CANmodule), &tx_hdr, buffer->data, &TxMailboxNum) == HAL_OK; + } #endif + return success; +} + +/** + * \brief Read message from RX FIFO + * \param CANmodule: CAN module instance, typically from the local pointer + * \param[in] fifo: Fifo number to use for read + * \param[in] fifo_isrs: List of interrupts for respected FIFO + */ +static void +prv_read_can_received_msg(CO_CANmodule_t* CANmodule, uint32_t fifo, uint32_t fifo_isrs) { + CO_CANrxMsg_t rcvMsg; + CO_CANrx_t* buffer = NULL; /* receive message buffer from CO_CANmodule_t object. */ + uint16_t index; /* index of received message */ + uint32_t rcvMsgIdent; /* identifier of the received message */ + uint8_t messageFound = 0; + can_periph_handle_t* mcu_canhandle = GET_CAN_PERIPH_HANDLE(CANmodule); + +#ifdef CO_STM32_FDCAN_Driver + /* + * Write received message to the temporary 64-bytes buffer. + * This is to ensure that the CAN nodes that do not comply with the newer CAN standards + * don't send wrong message with the wrong DLC value. This is a safety measure to avoid buffer overflow. + * + * Check the FDCAN implementation for STM32 in their respective reference manual. + */ + static FDCAN_RxHeaderTypeDef rx_hdr; + static uint8_t rx_data[64]; + + /* Read received message from FIFO */ + if (HAL_FDCAN_GetRxMessage(mcu_canhandle, fifo, &rx_hdr, rx_data) != HAL_OK) { + return; + } + + /* Setup identifier (with RTR) and length */ + rcvMsg.ident = rx_hdr.Identifier | (rx_hdr.RxFrameType == FDCAN_REMOTE_FRAME ? FLAG_RTR : 0x00); + rcvMsg.dlc = FDCAN_DLC_PERIPH_TO_NUM(rx_hdr.DataLength); /* Invalid length (more than 8) resolves to 0 */ + if (rcvMsg.dlc > 0) { + memcpy(rcvMsg.data, rx_data, rcvMsg.dlc); + } + rcvMsgIdent = rcvMsg.ident; +#else + static CAN_RxHeaderTypeDef rx_hdr; + + /* Read received message from FIFO */ + if (HAL_CAN_GetRxMessage(mcu_canhandle, fifo, &rx_hdr, rcvMsg.data) != HAL_OK) { + return; + } + /* Setup identifier (with RTR) and length */ + rcvMsg.ident = rx_hdr.StdId | (rx_hdr.RTR == CAN_RTR_REMOTE ? FLAG_RTR : 0x00); + rcvMsg.dlc = rx_hdr.DLC; + rcvMsgIdent = rcvMsg.ident; +#endif + + /* + * Hardware filters are not used for the moment + * \todo: Implement hardware filters... + */ + if (CANmodule->useCANrxFilters) { + __BKPT(0); + } else { + /* + * We are not using hardware filters, hence it is necessary + * to manually match received message ID with all buffers + */ + buffer = CANmodule->rxArray; + for (index = CANmodule->rxSize; index > 0U; --index, ++buffer) { + if (((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U) { + messageFound = 1; + break; + } + } + } + + /* Call specific function, which will process the message */ + if (messageFound && buffer != NULL && buffer->CANrx_callback != NULL) { + buffer->CANrx_callback(buffer->object, &rcvMsg); + } +} + +/** + * \brief TX buffer has been well transmitted callback + * \param[in] hcan: pointer to an CAN_HandleTypeDef structure that contains + * the configuration information for the specified CAN. + * \param[in] MailboxNumber: the mailbox number that has been transmitted + */ +static void +prv_process_tx_complete(CO_CANmodule_t* CANmodule, uint32_t MailboxNumber) { + CANmodule->firstCANtxMessage = false; /* First CAN message (bootup) was sent successfully */ + CANmodule->bufferInhibitFlag = false; /* Clear flag from previous message */ + if (CANmodule->CANtxCount > 0U) { /* Are there any new messages waiting to be send */ + CO_CANtx_t* buffer = &CANmodule->txArray[0]; /* Start with first buffer handle */ + + /* + * Try to send more buffers, process all empty ones + * + * This function is always called from interrupt, + * however to make sure no preemption can happen, interrupts are anyway locked + * (unless you can guarantee no higher priority interrupt will try to access to CAN instance and send data, + * then no need to lock interrupts..) + */ + CO_LOCK_CAN_SEND(CANmodule); + for (size_t idx = CANmodule->txSize; idx > 0U; --idx, ++buffer) { + /* Try to send message */ + if (buffer->bufferFull) { + if (prv_send_can_message(CANmodule, buffer)) { + buffer->bufferFull = false; + CANmodule->CANtxCount--; + CANmodule->bufferInhibitFlag = buffer->syncFlag; + } else { + break; // if we could not send the message, break out of the loop (the tx buffers are full) + } + } + } + CO_UNLOCK_CAN_SEND(CANmodule); + } +} /******************************************************************************/ +/* CanOpenNode specific implementation functions */ +/******************************************************************************/ + +/** + * \brief Put the CAN in the configuration mode. + * + * \param CANptr: Custom \ref CANopenNodeSTM32 structure pointer + */ void CO_CANsetConfigurationMode(void* CANptr) { + CANopenNodeSTM32* customhandle = CANptr; + /* Put CAN module in configuration mode */ - if (CANptr != NULL) { + if (customhandle != NULL) { + can_periph_handle_t* periphhandle = customhandle->CANHandle; #ifdef CO_STM32_FDCAN_Driver - HAL_FDCAN_Stop(((CANopenNodeSTM32*)CANptr)->CANHandle); + HAL_FDCAN_Stop(periphhandle); #else - HAL_CAN_Stop(((CANopenNodeSTM32*)CANptr)->CANHandle); + HAL_CAN_Stop(periphhandle); #endif } } /******************************************************************************/ + +/** + * \brief Set CAN peripheral to the normal mode + * + * \param CANmodule: CanOpen module, where we store also the pointer + * to our custom STM32 structure + */ void CO_CANsetNormalMode(CO_CANmodule_t* CANmodule) { /* Put CAN module in normal mode */ - if (CANmodule->CANptr != NULL) { + if (CANmodule != NULL && CANmodule->CANptr != NULL) { + can_periph_handle_t* periphhandle = GET_CAN_PERIPH_HANDLE(CANmodule); + HAL_StatusTypeDef status; + #ifdef CO_STM32_FDCAN_Driver - if (HAL_FDCAN_Start(((CANopenNodeSTM32*)CANmodule->CANptr)->CANHandle) == HAL_OK) + status = HAL_FDCAN_Start(periphhandle); #else - if (HAL_CAN_Start(((CANopenNodeSTM32*)CANmodule->CANptr)->CANHandle) == HAL_OK) + status = HAL_CAN_Start(periphhandle); #endif - { + + if (status == HAL_OK) { CANmodule->CANnormal = true; } } } /******************************************************************************/ + +/** + * \brief Initialize the CANmodule object + * + * \param CANmodule + * \param CANptr: CANptr is the same variable we pass to the CO_CANinit and should be + * of the type \ref CANopenNodeSTM32 for this implementation + * \param rxArray + * \param rxSize + * \param txArray + * \param txSize + * \param CANbitRate + * \return + */ CO_ReturnError_t CO_CANmodule_init(CO_CANmodule_t* CANmodule, void* CANptr, CO_CANrx_t rxArray[], uint16_t rxSize, CO_CANtx_t txArray[], uint16_t txSize, uint16_t CANbitRate) { + CANopenNodeSTM32* canstm32handle = CANptr; /* verify arguments */ - if (CANmodule == NULL || rxArray == NULL || txArray == NULL) { + if (CANmodule == NULL || rxArray == NULL || txArray == NULL || canstm32handle == NULL) { return CO_ERROR_ILLEGAL_ARGUMENT; } - /* Hold CANModule variable */ + /* CANptr is our STM32 type */ CANmodule->CANptr = CANptr; /* Keep a local copy of CANModule */ @@ -123,7 +397,9 @@ CO_CANmodule_init(CO_CANmodule_t* CANmodule, void* CANptr, CO_CANrx_t rxArray[], /***************************************/ /* STM32 related configuration */ /***************************************/ - ((CANopenNodeSTM32*)CANptr)->HWInitFunction(); + if (canstm32handle->HWInitFunction != NULL) { + canstm32handle->HWInitFunction(); + } /* * Configure global filter that is used as last check if message did not pass any of other filters: @@ -134,19 +410,18 @@ CO_CANmodule_init(CO_CANmodule_t* CANmodule, void* CANptr, CO_CANrx_t rxArray[], * Accept non-matching standard ID messages * Reject non-matching extended ID messages */ - #ifdef CO_STM32_FDCAN_Driver - if (HAL_FDCAN_ConfigGlobalFilter(((CANopenNodeSTM32*)CANptr)->CANHandle, FDCAN_ACCEPT_IN_RX_FIFO0, FDCAN_REJECT, + if (HAL_FDCAN_ConfigGlobalFilter(canstm32handle->CANHandle, FDCAN_ACCEPT_IN_RX_FIFO0, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK) { return CO_ERROR_ILLEGAL_ARGUMENT; } -#else +#else /* CO_STM32_FDCAN_Driver */ CAN_FilterTypeDef FilterConfig; #if defined(CAN) FilterConfig.FilterBank = 0; #else - if (((CAN_HandleTypeDef*)((CANopenNodeSTM32*)CANmodule->CANptr)->CANHandle)->Instance == CAN1) { + if ((GET_CAN_PERIPH_HANDLE(CANmodule))->Instance == CAN1) { FilterConfig.FilterBank = 0; } else { FilterConfig.FilterBank = 14; @@ -163,14 +438,15 @@ CO_CANmodule_init(CO_CANmodule_t* CANmodule, void* CANptr, CO_CANrx_t rxArray[], FilterConfig.FilterActivation = ENABLE; FilterConfig.SlaveStartFilterBank = 14; - if (HAL_CAN_ConfigFilter(((CANopenNodeSTM32*)CANptr)->CANHandle, &FilterConfig) != HAL_OK) { + if (HAL_CAN_ConfigFilter(canstm32handle->CANHandle, &FilterConfig) != HAL_OK) { return CO_ERROR_ILLEGAL_ARGUMENT; } -#endif +#endif /* !CO_STM32_FDCAN_Driver */ + /* Enable notifications */ /* Activate the CAN notification interrupts */ #ifdef CO_STM32_FDCAN_Driver - if (HAL_FDCAN_ActivateNotification(((CANopenNodeSTM32*)CANptr)->CANHandle, + if (HAL_FDCAN_ActivateNotification(canstm32handle->CANHandle, 0 | FDCAN_IT_RX_FIFO0_NEW_MESSAGE | FDCAN_IT_RX_FIFO1_NEW_MESSAGE | FDCAN_IT_TX_COMPLETE | FDCAN_IT_TX_FIFO_EMPTY | FDCAN_IT_BUS_OFF | FDCAN_IT_ARB_PROTOCOL_ERROR | FDCAN_IT_DATA_PROTOCOL_ERROR @@ -180,9 +456,9 @@ CO_CANmodule_init(CO_CANmodule_t* CANmodule, void* CANptr, CO_CANrx_t rxArray[], return CO_ERROR_ILLEGAL_ARGUMENT; } #else - if (HAL_CAN_ActivateNotification(((CANopenNodeSTM32*)CANptr)->CANHandle, CAN_IT_RX_FIFO0_MSG_PENDING - | CAN_IT_RX_FIFO1_MSG_PENDING - | CAN_IT_TX_MAILBOX_EMPTY) + if (HAL_CAN_ActivateNotification(canstm32handle->CANHandle, CAN_IT_RX_FIFO0_MSG_PENDING + | CAN_IT_RX_FIFO1_MSG_PENDING + | CAN_IT_TX_MAILBOX_EMPTY) != HAL_OK) { return CO_ERROR_ILLEGAL_ARGUMENT; } @@ -194,13 +470,19 @@ CO_CANmodule_init(CO_CANmodule_t* CANmodule, void* CANptr, CO_CANrx_t rxArray[], /******************************************************************************/ void CO_CANmodule_disable(CO_CANmodule_t* CANmodule) { + /* Disable the module */ if (CANmodule != NULL && CANmodule->CANptr != NULL) { -#ifdef CO_STM32_FDCAN_Driver - HAL_FDCAN_Stop(((CANopenNodeSTM32*)CANmodule->CANptr)->CANHandle); + can_periph_handle_t* periphhandle = GET_CAN_PERIPH_HANDLE(CANmodule); + HAL_StatusTypeDef status; +#ifdef CO_STM32_FDCAN_Driver + status = HAL_FDCAN_Stop(periphhandle); #else - HAL_CAN_Stop(((CANopenNodeSTM32*)CANmodule->CANptr)->CANHandle); + status = HAL_CAN_Stop(periphhandle); #endif + if (status != HAL_OK) { + (void)status; /* might be unused */ + } } } @@ -254,97 +536,6 @@ CO_CANtxBufferInit(CO_CANmodule_t* CANmodule, uint16_t index, uint16_t ident, bo return buffer; } -/** - * \brief Send CAN message to network - * This function must be called with atomic access. - * - * \param[in] CANmodule: CAN module instance - * \param[in] buffer: Pointer to buffer to transmit - */ -static uint8_t -prv_send_can_message(CO_CANmodule_t* CANmodule, CO_CANtx_t* buffer) { - - uint8_t success = 0; - - /* Check if TX FIFO is ready to accept more messages */ -#ifdef CO_STM32_FDCAN_Driver - static FDCAN_TxHeaderTypeDef tx_hdr; - if (HAL_FDCAN_GetTxFifoFreeLevel(((CANopenNodeSTM32*)CANmodule->CANptr)->CANHandle) > 0) { - /* - * RTR flag is part of identifier value - * hence it needs to be properly decoded - */ - tx_hdr.Identifier = buffer->ident & CANID_MASK; - tx_hdr.TxFrameType = (buffer->ident & FLAG_RTR) ? FDCAN_REMOTE_FRAME : FDCAN_DATA_FRAME; - tx_hdr.IdType = FDCAN_STANDARD_ID; - tx_hdr.FDFormat = FDCAN_CLASSIC_CAN; - tx_hdr.BitRateSwitch = FDCAN_BRS_OFF; - tx_hdr.MessageMarker = 0; - tx_hdr.ErrorStateIndicator = FDCAN_ESI_ACTIVE; - tx_hdr.TxEventFifoControl = FDCAN_NO_TX_EVENTS; - - switch (buffer->DLC) { - case 0: - tx_hdr.DataLength = FDCAN_DLC_BYTES_0; - break; - case 1: - tx_hdr.DataLength = FDCAN_DLC_BYTES_1; - break; - case 2: - tx_hdr.DataLength = FDCAN_DLC_BYTES_2; - break; - case 3: - tx_hdr.DataLength = FDCAN_DLC_BYTES_3; - break; - case 4: - tx_hdr.DataLength = FDCAN_DLC_BYTES_4; - break; - case 5: - tx_hdr.DataLength = FDCAN_DLC_BYTES_5; - break; - case 6: - tx_hdr.DataLength = FDCAN_DLC_BYTES_6; - break; - case 7: - tx_hdr.DataLength = FDCAN_DLC_BYTES_7; - break; - case 8: - tx_hdr.DataLength = FDCAN_DLC_BYTES_8; - break; - default: /* Hard error... */ - break; - } - - /* Now add message to FIFO. Should not fail */ - success = - HAL_FDCAN_AddMessageToTxFifoQ(((CANopenNodeSTM32*)CANmodule->CANptr)->CANHandle, &tx_hdr, buffer->data) - == HAL_OK; - } -#else - static CAN_TxHeaderTypeDef tx_hdr; - /* Check if TX FIFO is ready to accept more messages */ - if (HAL_CAN_GetTxMailboxesFreeLevel(((CANopenNodeSTM32*)CANmodule->CANptr)->CANHandle) > 0) { - /* - * RTR flag is part of identifier value - * hence it needs to be properly decoded - */ - tx_hdr.ExtId = 0u; - tx_hdr.IDE = CAN_ID_STD; - tx_hdr.DLC = buffer->DLC; - tx_hdr.StdId = buffer->ident & CANID_MASK; - tx_hdr.RTR = (buffer->ident & FLAG_RTR) ? CAN_RTR_REMOTE : CAN_RTR_DATA; - - uint32_t TxMailboxNum; // Transmission MailBox number - - /* Now add message to FIFO. Should not fail */ - success = HAL_CAN_AddTxMessage(((CANopenNodeSTM32*)CANmodule->CANptr)->CANHandle, &tx_hdr, buffer->data, - &TxMailboxNum) - == HAL_OK; - } -#endif - return success; -} - /******************************************************************************/ CO_ReturnError_t CO_CANsend(CO_CANmodule_t* CANmodule, CO_CANtx_t* buffer) { @@ -414,32 +605,26 @@ CO_CANclearPendingSyncPDOs(CO_CANmodule_t* CANmodule) { } /******************************************************************************/ -/* Get error counters from the module. If necessary, function may use - * different way to determine errors. */ -static uint16_t rxErrors = 0, txErrors = 0, overflow = 0; void CO_CANmodule_process(CO_CANmodule_t* CANmodule) { uint32_t err = 0; + can_periph_handle_t* periphhandle = GET_CAN_PERIPH_HANDLE(CANmodule); // CANOpen just care about Bus_off, Warning, Passive and Overflow // I didn't find overflow error register in STM32, if you find it please let me know #ifdef CO_STM32_FDCAN_Driver - - err = ((FDCAN_HandleTypeDef*)((CANopenNodeSTM32*)CANmodule->CANptr)->CANHandle)->Instance->PSR - & (FDCAN_PSR_BO | FDCAN_PSR_EW | FDCAN_PSR_EP); - + err = periphhandle->Instance->PSR & (FDCAN_PSR_BO | FDCAN_PSR_EW | FDCAN_PSR_EP); if (CANmodule->errOld != err) { - uint16_t status = CANmodule->CANerrorStatus; CANmodule->errOld = err; - if (err & FDCAN_PSR_BO) { status |= CO_CAN_ERRTX_BUS_OFF; - // In this driver we expect that the controller is automatically handling the protocol exceptions. + /* FDCAN does not auto start bus-off, start it here */ + prv_fdcan_bus_off_check_reset(periphhandle); } else { /* recalculate CANerrorStatus, first clear some flags */ status &= 0xFFFF @@ -454,25 +639,18 @@ CO_CANmodule_process(CO_CANmodule_t* CANmodule) { status |= CO_CAN_ERRRX_PASSIVE | CO_CAN_ERRTX_PASSIVE; } } - CANmodule->CANerrorStatus = status; } #else - - err = ((CAN_HandleTypeDef*)((CANopenNodeSTM32*)CANmodule->CANptr)->CANHandle)->Instance->ESR - & (CAN_ESR_BOFF | CAN_ESR_EPVF | CAN_ESR_EWGF); - - // uint32_t esrVal = ((CAN_HandleTypeDef*)((CANopenNodeSTM32*)CANmodule->CANptr)->CANHandle)->Instance->ESR; Debug purpose + err = periphhandle->Instance->ESR & (CAN_ESR_BOFF | CAN_ESR_EPVF | CAN_ESR_EWGF); if (CANmodule->errOld != err) { - uint16_t status = CANmodule->CANerrorStatus; CANmodule->errOld = err; - if (err & CAN_ESR_BOFF) { status |= CO_CAN_ERRTX_BUS_OFF; - // In this driver, we assume that auto bus recovery is activated ! so this error will eventually handled automatically. - + // In this driver, we assume that auto bus recovery is activated ! so this error will eventually handled + // automatically. This has to be enabled in the settings } else { /* recalculate CANerrorStatus, first clear some flags */ status &= 0xFFFF @@ -490,124 +668,31 @@ CO_CANmodule_process(CO_CANmodule_t* CANmodule) { CANmodule->CANerrorStatus = status; } - #endif } -/** - * \brief Read message from RX FIFO - * \param hfdcan: pointer to an FDCAN_HandleTypeDef structure that contains - * the configuration information for the specified FDCAN. - * \param[in] fifo: Fifo number to use for read - * \param[in] fifo_isrs: List of interrupts for respected FIFO - */ -#ifdef CO_STM32_FDCAN_Driver -static void -prv_read_can_received_msg(FDCAN_HandleTypeDef* hfdcan, uint32_t fifo, uint32_t fifo_isrs) -#else -static void -prv_read_can_received_msg(CAN_HandleTypeDef* hcan, uint32_t fifo, uint32_t fifo_isrs) -#endif -{ - - CO_CANrxMsg_t rcvMsg; - CO_CANrx_t* buffer = NULL; /* receive message buffer from CO_CANmodule_t object. */ - uint16_t index; /* index of received message */ - uint32_t rcvMsgIdent; /* identifier of the received message */ - uint8_t messageFound = 0; +/******************************************************************************/ +/* Hardware specific callback functions implemented here */ +/******************************************************************************/ #ifdef CO_STM32_FDCAN_Driver - /* - * Write received message to the temporary 64-bytes buffer. - * This is to ensure that the CAN nodes that do not comply with the newer CAN standards - * don't send wrong message with the wrong DLC value. This is a safety measure to avoid buffer overflow. - * - * Check the FDCAN implementation for STM32 in their respective reference manual. - */ - static FDCAN_RxHeaderTypeDef rx_hdr; - static uint8_t rx_data[64]; - /* Read received message from FIFO */ - if (HAL_FDCAN_GetRxMessage(hfdcan, fifo, &rx_hdr, rx_data) != HAL_OK) { - return; - } - /* Setup identifier (with RTR) and length */ - rcvMsg.ident = rx_hdr.Identifier | (rx_hdr.RxFrameType == FDCAN_REMOTE_FRAME ? FLAG_RTR : 0x00); - switch (rx_hdr.DataLength) { - case FDCAN_DLC_BYTES_0: - rcvMsg.dlc = 0; - break; - case FDCAN_DLC_BYTES_1: - rcvMsg.dlc = 1; - break; - case FDCAN_DLC_BYTES_2: - rcvMsg.dlc = 2; - break; - case FDCAN_DLC_BYTES_3: - rcvMsg.dlc = 3; - break; - case FDCAN_DLC_BYTES_4: - rcvMsg.dlc = 4; - break; - case FDCAN_DLC_BYTES_5: - rcvMsg.dlc = 5; - break; - case FDCAN_DLC_BYTES_6: - rcvMsg.dlc = 6; - break; - case FDCAN_DLC_BYTES_7: - rcvMsg.dlc = 7; - break; - case FDCAN_DLC_BYTES_8: - rcvMsg.dlc = 8; - break; - default: - rcvMsg.dlc = 0; - break; /* Invalid length when more than 8 */ - } - if (rcvMsg.dlc > 0) { - memcpy(rcvMsg.data, rx_data, rcvMsg.dlc); - } - rcvMsgIdent = rcvMsg.ident; -#else - static CAN_RxHeaderTypeDef rx_hdr; - /* Read received message from FIFO */ - if (HAL_CAN_GetRxMessage(hcan, fifo, &rx_hdr, rcvMsg.data) != HAL_OK) { - return; - } - /* Setup identifier (with RTR) and length */ - rcvMsg.ident = rx_hdr.StdId | (rx_hdr.RTR == CAN_RTR_REMOTE ? FLAG_RTR : 0x00); - rcvMsg.dlc = rx_hdr.DLC; - rcvMsgIdent = rcvMsg.ident; -#endif +/** + * \brief Check for BUS-OFF and reinitialize the FDCAN peripheral instance + * IP will then wait for recessive bits before resuming the operation + * + * \param hfdcan + */ +static void +prv_fdcan_bus_off_check_reset(FDCAN_HandleTypeDef* hfdcan) { + FDCAN_ProtocolStatusTypeDef protocolStatus = {0}; - /* - * Hardware filters are not used for the moment - * \todo: Implement hardware filters... - */ - if (CANModule_local->useCANrxFilters) { - __BKPT(0); - } else { - /* - * We are not using hardware filters, hence it is necessary - * to manually match received message ID with all buffers - */ - buffer = CANModule_local->rxArray; - for (index = CANModule_local->rxSize; index > 0U; --index, ++buffer) { - if (((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U) { - messageFound = 1; - break; - } - } - } - - /* Call specific function, which will process the message */ - if (messageFound && buffer != NULL && buffer->CANrx_callback != NULL) { - buffer->CANrx_callback(buffer->object, (void*)&rcvMsg); + HAL_FDCAN_GetProtocolStatus(hfdcan, &protocolStatus); + if (protocolStatus.BusOff) { + CLEAR_BIT(hfdcan->Instance->CCCR, FDCAN_CCCR_INIT); } } -#ifdef CO_STM32_FDCAN_Driver /** * \brief Rx FIFO 0 callback. * \param[in] hfdcan: pointer to an FDCAN_HandleTypeDef structure that contains @@ -617,7 +702,7 @@ prv_read_can_received_msg(CAN_HandleTypeDef* hcan, uint32_t fifo, uint32_t fifo_ void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef* hfdcan, uint32_t RxFifo0ITs) { if (RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) { - prv_read_can_received_msg(hfdcan, FDCAN_RX_FIFO0, RxFifo0ITs); + prv_read_can_received_msg(CANModule_local, FDCAN_RX_FIFO0, RxFifo0ITs); } } @@ -630,7 +715,7 @@ HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef* hfdcan, uint32_t RxFifo0ITs) { void HAL_FDCAN_RxFifo1Callback(FDCAN_HandleTypeDef* hfdcan, uint32_t RxFifo1ITs) { if (RxFifo1ITs & FDCAN_IT_RX_FIFO1_NEW_MESSAGE) { - prv_read_can_received_msg(hfdcan, FDCAN_RX_FIFO1, RxFifo1ITs); + prv_read_can_received_msg(CANModule_local, FDCAN_RX_FIFO1, RxFifo1ITs); } } @@ -642,37 +727,41 @@ HAL_FDCAN_RxFifo1Callback(FDCAN_HandleTypeDef* hfdcan, uint32_t RxFifo1ITs) { */ void HAL_FDCAN_TxBufferCompleteCallback(FDCAN_HandleTypeDef* hfdcan, uint32_t BufferIndexes) { - CANModule_local->firstCANtxMessage = false; /* First CAN message (bootup) was sent successfully */ - CANModule_local->bufferInhibitFlag = false; /* Clear flag from previous message */ - if (CANModule_local->CANtxCount > 0U) { /* Are there any new messages waiting to be send */ - CO_CANtx_t* buffer = &CANModule_local->txArray[0]; /* Start with first buffer handle */ - uint16_t i; + prv_process_tx_complete(CANModule_local, BufferIndexes); +} - /* - * Try to send more buffers, process all empty ones - * - * This function is always called from interrupt, - * however to make sure no preemption can happen, interrupts are anyway locked - * (unless you can guarantee no higher priority interrupt will try to access to FDCAN instance and send data, - * then no need to lock interrupts..) - */ - CO_LOCK_CAN_SEND(CANModule_local); - for (i = CANModule_local->txSize; i > 0U; --i, ++buffer) { - /* Try to send message */ - if (buffer->bufferFull) { - if (prv_send_can_message(CANModule_local, buffer)) { - buffer->bufferFull = false; - CANModule_local->CANtxCount--; - CANModule_local->bufferInhibitFlag = buffer->syncFlag; - } else { - break; // if we could not send the message, break out of the loop (the tx buffers are full) - } - } - } - CO_UNLOCK_CAN_SEND(CANModule_local); - } +/** + * \brief Error callback. + * \param[in] hfdcan: pointer to an FDCAN_HandleTypeDef structure that contains + * the configuration information for the specified FDCAN + */ +void +HAL_FDCAN_ErrorCallback(FDCAN_HandleTypeDef* hfdcan) { + (void)hfdcan; + + /* Do we do it here or in process only? */ + /* prv_fdcan_bus_off_check_reset(hfdcan); */ } -#else + +/** + * \brief Error status callback + * + * \param[in] hfdcan: pointer to an FDCAN_HandleTypeDef structure that contains + * the configuration information for the specified FDCAN. + * \param[in] ErrorStatusITs: indicates which Error Status interrupts are signaled. + * This parameter can be any combination of \arg FDCAN_Error_Status_Interrupts + */ +void +HAL_FDCAN_ErrorStatusCallback(FDCAN_HandleTypeDef* hfdcan, uint32_t ErrorStatusITs) { + (void)hfdcan; + (void)ErrorStatusITs; + + /* Do we do it here or in process only? */ + /* prv_fdcan_bus_off_check_reset(hfdcan); */ +} + +#else /* CO_STM32_FDCAN_Driver */ + /** * \brief Rx FIFO 0 callback. * \param[in] hcan: pointer to an CAN_HandleTypeDef structure that contains @@ -680,7 +769,7 @@ HAL_FDCAN_TxBufferCompleteCallback(FDCAN_HandleTypeDef* hfdcan, uint32_t BufferI */ void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef* hcan) { - prv_read_can_received_msg(hcan, CAN_RX_FIFO0, 0); + prv_read_can_received_msg(CANModule_local, CAN_RX_FIFO0, 0); } /** @@ -690,61 +779,37 @@ HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef* hcan) { */ void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef* hcan) { - prv_read_can_received_msg(hcan, CAN_RX_FIFO1, 0); + prv_read_can_received_msg(CANModule_local, CAN_RX_FIFO1, 0); } /** - * \brief TX buffer has been well transmitted callback - * \param[in] hcan: pointer to an CAN_HandleTypeDef structure that contains - * the configuration information for the specified CAN. - * \param[in] MailboxNumber: the mailbox number that has been transmitted + * \brief TX mailbox 0 message sent + * + * \param hcan */ -void -CO_CANinterrupt_TX(CO_CANmodule_t* CANmodule, uint32_t MailboxNumber) { - - CANmodule->firstCANtxMessage = false; /* First CAN message (bootup) was sent successfully */ - CANmodule->bufferInhibitFlag = false; /* Clear flag from previous message */ - if (CANmodule->CANtxCount > 0U) { /* Are there any new messages waiting to be send */ - CO_CANtx_t* buffer = &CANmodule->txArray[0]; /* Start with first buffer handle */ - uint16_t i; - - /* - * Try to send more buffers, process all empty ones - * - * This function is always called from interrupt, - * however to make sure no preemption can happen, interrupts are anyway locked - * (unless you can guarantee no higher priority interrupt will try to access to CAN instance and send data, - * then no need to lock interrupts..) - */ - CO_LOCK_CAN_SEND(CANmodule); - for (i = CANmodule->txSize; i > 0U; --i, ++buffer) { - /* Try to send message */ - if (buffer->bufferFull) { - if (prv_send_can_message(CANmodule, buffer)) { - buffer->bufferFull = false; - CANmodule->CANtxCount--; - CANmodule->bufferInhibitFlag = buffer->syncFlag; - } else { - break; // if we could not send the message, break out of the loop (the tx buffers are full) - } - } - } - CO_UNLOCK_CAN_SEND(CANmodule); - } -} - void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef* hcan) { - CO_CANinterrupt_TX(CANModule_local, CAN_TX_MAILBOX0); + prv_process_tx_complete(CANModule_local, CAN_TX_MAILBOX0); } +/** + * \brief TX mailbox 1 message sent + * + * \param hcan + */ void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef* hcan) { - CO_CANinterrupt_TX(CANModule_local, CAN_TX_MAILBOX0); + prv_process_tx_complete(CANModule_local, CAN_TX_MAILBOX1); } +/** + * \brief TX mailbox 2 message sent + * + * \param hcan + */ void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef* hcan) { - CO_CANinterrupt_TX(CANModule_local, CAN_TX_MAILBOX0); + prv_process_tx_complete(CANModule_local, CAN_TX_MAILBOX2); } -#endif + +#endif /* !CO_STM32_FDCAN_Driver */ From 1152858050206caca4a67f210575ce9adacbd2ee Mon Sep 17 00:00:00 2001 From: Tilen Date: Fri, 11 Sep 2026 19:45:02 +0200 Subject: [PATCH 2/3] fix: use local variables for loops --- CANopenNode_STM32/CO_driver_STM32.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/CANopenNode_STM32/CO_driver_STM32.c b/CANopenNode_STM32/CO_driver_STM32.c index f3bf156..bad55d6 100644 --- a/CANopenNode_STM32/CO_driver_STM32.c +++ b/CANopenNode_STM32/CO_driver_STM32.c @@ -384,14 +384,18 @@ CO_CANmodule_init(CO_CANmodule_t* CANmodule, void* CANptr, CO_CANrx_t rxArray[], CANmodule->errOld = 0U; /* Reset all variables */ - for (uint16_t i = 0U; i < rxSize; i++) { - rxArray[i].ident = 0U; - rxArray[i].mask = 0xFFFFU; - rxArray[i].object = NULL; - rxArray[i].CANrx_callback = NULL; + for (uint16_t idx = 0U; idx < rxSize; idx++) { + CO_CANrx_t* const msg = &rxArray[idx]; + + msg->ident = 0U; + msg->mask = 0xFFFFU; + msg->object = NULL; + msg->CANrx_callback = NULL; } - for (uint16_t i = 0U; i < txSize; i++) { - txArray[i].bufferFull = false; + for (uint16_t idx = 0U; idx < txSize; idx++) { + CO_CANtx_t* const msg = &txArray[idx]; + + msg->bufferFull = false; } /***************************************/ @@ -585,9 +589,8 @@ CO_CANclearPendingSyncPDOs(CO_CANmodule_t* CANmodule) { } /* delete also pending synchronous TPDOs in TX buffers */ if (CANmodule->CANtxCount > 0) { - uint16_t i; CO_CANtx_t* buffer = &CANmodule->txArray[0]; - for (i = CANmodule->txSize; i > 0U; i--) { + for (uint16_t idx = CANmodule->txSize; idx > 0U; idx--) { if (buffer->bufferFull) { if (buffer->syncFlag) { buffer->bufferFull = false; From 9a0e187a718e1946946d0a591184ab9350dcd527 Mon Sep 17 00:00:00 2001 From: Tilen Date: Fri, 11 Sep 2026 19:46:48 +0200 Subject: [PATCH 3/3] fix: enter critical before checking the TX length --- CANopenNode_STM32/CO_driver_STM32.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/CANopenNode_STM32/CO_driver_STM32.c b/CANopenNode_STM32/CO_driver_STM32.c index bad55d6..88107d1 100644 --- a/CANopenNode_STM32/CO_driver_STM32.c +++ b/CANopenNode_STM32/CO_driver_STM32.c @@ -258,20 +258,20 @@ prv_read_can_received_msg(CO_CANmodule_t* CANmodule, uint32_t fifo, uint32_t fif */ static void prv_process_tx_complete(CO_CANmodule_t* CANmodule, uint32_t MailboxNumber) { - CANmodule->firstCANtxMessage = false; /* First CAN message (bootup) was sent successfully */ - CANmodule->bufferInhibitFlag = false; /* Clear flag from previous message */ + CANmodule->firstCANtxMessage = false; /* First CAN message (bootup) was sent successfully */ + CANmodule->bufferInhibitFlag = false; /* Clear flag from previous message */ + + /* + * Try to send more buffers, process all empty ones + * + * This function is always called from interrupt, + * however to make sure no preemption can happen, interrupts are anyway locked + * (unless you can guarantee no higher priority interrupt will try to access to CAN instance and send data, + * then no need to lock interrupts..) + */ + CO_LOCK_CAN_SEND(CANmodule); if (CANmodule->CANtxCount > 0U) { /* Are there any new messages waiting to be send */ CO_CANtx_t* buffer = &CANmodule->txArray[0]; /* Start with first buffer handle */ - - /* - * Try to send more buffers, process all empty ones - * - * This function is always called from interrupt, - * however to make sure no preemption can happen, interrupts are anyway locked - * (unless you can guarantee no higher priority interrupt will try to access to CAN instance and send data, - * then no need to lock interrupts..) - */ - CO_LOCK_CAN_SEND(CANmodule); for (size_t idx = CANmodule->txSize; idx > 0U; --idx, ++buffer) { /* Try to send message */ if (buffer->bufferFull) { @@ -284,8 +284,8 @@ prv_process_tx_complete(CO_CANmodule_t* CANmodule, uint32_t MailboxNumber) { } } } - CO_UNLOCK_CAN_SEND(CANmodule); } + CO_UNLOCK_CAN_SEND(CANmodule); } /******************************************************************************/