Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 85 additions & 5 deletions bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2006-2024 RT-Thread Development Team
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
Expand All @@ -14,6 +14,7 @@
* 2021-02-02 YuZhe XU fix bug in filter config
* 2021-8-25 SVCHAO The baud rate is configured according to the different APB1 frequencies.
f4-series only.
* 2025-09-20 wdfk_prog Implemented sendmsg_nonblocking op to support framework's async TX.
*/

#include "drv_can.h"
Expand Down Expand Up @@ -484,6 +485,21 @@ static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg)
return RT_EOK;
}

/**
* @internal
* @brief Low-level function to send a CAN message to a specific hardware mailbox.
*
* This function is part of the **blocking** send mechanism. It is called by
* `_can_int_tx` after a hardware mailbox has already been acquired. Its role is
* to format the message according to the STM32 hardware requirements and place
* it into the specified mailbox for transmission.
*
* @param[in] can A pointer to the CAN device structure.
* @param[in] buf A pointer to the `rt_can_msg` to be sent.
* @param[in] box_num The specific hardware mailbox index (0, 1, or 2) to use for this tran
*
* @return `RT_EOK` on success, or an error code on failure.
*/
static int _can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t box_num)
{
CAN_HandleTypeDef *hcan;
Expand Down Expand Up @@ -586,6 +602,53 @@ static int _can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t
}
}

/**
* @internal
* @brief Low-level, hardware-specific non-blocking function to send a CAN message.
*
* This function interacts directly with the STM32 HAL library to add a message
* to a hardware TX mailbox. It returns immediately and does not wait for the
* transmission to complete.
*
* @param[in] can A pointer to the CAN device structure.
* @param[in] buf A pointer to the `rt_can_msg` to be sent.
*
* @return
* - `RT_EOK` if the message was successfully accepted by the hardware.
* - `-RT_EBUSY` if all hardware mailboxes are currently full.
* - `-RT_ERROR` on other HAL failures.
*/
static rt_ssize_t _can_sendmsg_nonblocking(struct rt_can_device *can, const void *buf)
{
CAN_HandleTypeDef *hcan = &((struct stm32_can *) can->parent.user_data)->CanHandle;
struct rt_can_msg *pmsg = (struct rt_can_msg *) buf;
CAN_TxHeaderTypeDef txheader = {0};
uint32_t tx_mailbox;

if ((hcan->State != HAL_CAN_STATE_READY) && (hcan->State != HAL_CAN_STATE_LISTENING))
return -RT_ERROR;

if (HAL_CAN_GetTxMailboxesFreeLevel(hcan) == 0)
return -RT_EBUSY;

txheader.DLC = pmsg->len;
txheader.RTR = (pmsg->rtr == RT_CAN_RTR) ? CAN_RTR_REMOTE : CAN_RTR_DATA;
txheader.IDE = (pmsg->ide == RT_CAN_STDID) ? CAN_ID_STD : CAN_ID_EXT;
if (txheader.IDE == CAN_ID_STD)
txheader.StdId = pmsg->id;
else
txheader.ExtId = pmsg->id;

HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(hcan, &txheader, pmsg->data, &tx_mailbox);
if (status != HAL_OK)
{
LOG_W("can sendmsg nonblocking send error %d", status);
return -RT_ERROR;
}

return RT_EOK;
}

static int _can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t fifo)
{
HAL_StatusTypeDef status;
Expand Down Expand Up @@ -645,10 +708,11 @@ static int _can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t fifo)

static const struct rt_can_ops _can_ops =
{
_can_config,
_can_control,
_can_sendmsg,
_can_recvmsg,
.configure = _can_config,
.control = _can_control,
.sendmsg = _can_sendmsg,
.recvmsg = _can_recvmsg,
.sendmsg_nonblocking = _can_sendmsg_nonblocking,
};

static void _can_rx_isr(struct rt_can_device *can, rt_uint32_t fifo)
Expand Down Expand Up @@ -788,6 +852,22 @@ static void _can_sce_isr(struct rt_can_device *can)
hcan->Instance->MSR |= CAN_MSR_ERRI;
}

/**
* @internal
* @brief The low-level ISR for CAN TX events on STM32.
*
* This function's sole responsibility is to check the hardware status flags
* to determine which mailbox completed a transmission and whether it was
* successful or failed. It then reports the specific event to the upper
* framework layer via `rt_hw_can_isr()`.
*
* @note This ISR contains NO framework-level logic (e.g., buffer handling).
* It is a pure hardware event reporter, ensuring a clean separation
* of concerns between the driver and the framework.
*
* @param[in] can A pointer to the CAN device structure.
* @return void
*/
static void _can_tx_isr(struct rt_can_device *can)
{
CAN_HandleTypeDef *hcan;
Expand Down
69 changes: 57 additions & 12 deletions components/drivers/can/Kconfig
Original file line number Diff line number Diff line change
@@ -1,31 +1,76 @@
config RT_USING_CAN
bool "Using CAN device drivers"
default n
help
Enable this option to include the CAN device driver framework.

if RT_USING_CAN

config RT_CAN_USING_HDR
bool "Enable CAN hardware filter"
bool "Enable CAN hardware filter support"
default n

help
If your CAN controller supports hardware filtering, and you want to
use the framework to configure these filters, enable this option.

config RT_CAN_USING_CANFD
bool "Enable CANFD support"
bool "Enable CAN-FD support"
default n

help
Enable this to support CAN with Flexible Data-Rate. This will
increase the size of the rt_can_msg structure.

config RT_CANMSG_BOX_SZ
int "CAN message box size"
int "Software RX message box size (in messages)"
default 16
help
Set the size of the CAN message box.

This sets the capacity of the software buffer (FIFO) for incoming
CAN messages. It defines how many messages can be buffered by the
driver before the application must read them.

config RT_CANSND_BOX_NUM
int "Number of CAN send queues"
int "Number of mailboxes for blocking send"
default 1
help
Set the number of CAN send queues.

This sets the number of concurrent blocking send operations that
can be in flight. It is typically matched to the number of
hardware transmission mailboxes on the CAN controller.

config RT_CANSND_MSG_TIMEOUT
int "CAN send message timeout"
int "Timeout for blocking send (in OS ticks)"
default 100
help
Set the timeout for CAN send messages.
This sets the default time a thread will wait for a hardware
mailbox to become available when sending in blocking mode.

config RT_CAN_NB_TX_FIFO_SIZE
int "Non-blocking send buffer size (in bytes)"
default 256
help
This defines the size of the software ring buffer used for
non-blocking and ISR-based transmissions. When the hardware
mailboxes are full, outgoing messages are temporarily stored
in this buffer.

To calculate a suitable size, use:
(number of messages to buffer) * sizeof(struct rt_can_msg).
For standard CAN, sizeof(struct rt_can_msg) is typically 16 bytes.
The default of 256 bytes can buffer 16 standard CAN messages.
If using CAN-FD, you will need to increase this size significantly.

config RT_CAN_MALLOC_NB_TX_BUFFER
bool "Dynamically allocate the non-blocking send buffer"
depends on RT_USING_HEAP
default n
help
If this option is enabled (y), the non-blocking send buffer will
be allocated from the system heap at runtime when the CAN device
is opened (using rt_malloc). This saves static RAM but requires
a heap to be available.

If this option is disabled (n), the buffer will be allocated
as a static array within the rt_can_device structure. This
consumes static RAM but guarantees the memory is always available
and avoids heap fragmentation.

endif
Loading
Loading