summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c90
-rw-r--r--components/drivers/can/Kconfig69
-rw-r--r--components/drivers/can/dev_can.c335
-rw-r--r--components/drivers/include/drivers/dev_can.h547
4 files changed, 778 insertions, 263 deletions
diff --git a/bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c b/bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c
index da748590b0..196cc94a52 100644
--- a/bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c
+++ b/bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c
@@ -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
*
@@ -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"
@@ -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;
@@ -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;
@@ -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)
@@ -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;
diff --git a/components/drivers/can/Kconfig b/components/drivers/can/Kconfig
index c762a46d6b..ccb4348db2 100644
--- a/components/drivers/can/Kconfig
+++ b/components/drivers/can/Kconfig
@@ -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 \ No newline at end of file
diff --git a/components/drivers/can/dev_can.c b/components/drivers/can/dev_can.c
index 8f70a1ddfb..73aee2f094 100644
--- a/components/drivers/can/dev_can.c
+++ b/components/drivers/can/dev_can.c
@@ -7,6 +7,7 @@
* Date Author Notes
* 2015-05-14 [email protected] first version
* 2015-07-06 Bernard code cleanup and remove RT_CAN_USING_LED;
+ * 2025-09-20 wdfk_prog Implemented non-blocking, ISR-safe send logic unified under rt_device_write.
*/
#include <rthw.h>
@@ -40,8 +41,19 @@ static rt_err_t rt_can_init(struct rt_device *dev)
return result;
}
-/*
- * can interrupt routines
+/**
+ * @internal
+ * @brief Handles reading messages from the software RX FIFO into a user buffer.
+ *
+ * This function is called by the public `rt_can_read()` API when the device
+ * is opened with interrupt-driven reception enabled. It safely transfers
+ * messages from the internal `uselist` to the user-provided buffer.
+ *
+ * @param[in] can A pointer to the CAN device.
+ * @param[out] data A pointer to the destination buffer for the received messages.
+ * @param[in] msgs The total size in bytes of the destination buffer.
+ *
+ * @return The number of bytes actually read from the FIFO.
*/
rt_inline rt_ssize_t _can_int_rx(struct rt_can_device *can, struct rt_can_msg *data, rt_ssize_t msgs)
{
@@ -63,7 +75,7 @@ rt_inline rt_ssize_t _can_int_rx(struct rt_can_device *can, struct rt_can_msg *d
struct rt_can_msg_list *listmsg = RT_NULL;
/* disable interrupt */
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
#ifdef RT_CAN_USING_HDR
hdr = data->hdr_index;
@@ -97,22 +109,22 @@ rt_inline rt_ssize_t _can_int_rx(struct rt_can_device *can, struct rt_can_msg *d
else
{
/* no data, enable interrupt and break out */
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
break;
}
}
/* enable interrupt */
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
if (listmsg != RT_NULL)
{
rt_memcpy(data, &listmsg->data, sizeof(struct rt_can_msg));
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
rt_list_insert_before(&rx_fifo->freelist, &listmsg->list);
rx_fifo->freenumbers++;
RT_ASSERT(rx_fifo->freenumbers <= can->config.msgboxsz);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
listmsg = RT_NULL;
}
@@ -127,6 +139,25 @@ rt_inline rt_ssize_t _can_int_rx(struct rt_can_device *can, struct rt_can_msg *d
return (size - msgs);
}
+/**
+ * @internal
+ * @brief Handles the blocking (synchronous) transmission of CAN messages.
+ *
+ * This function is the core of the blocking send mechanism. It iterates through
+ * a buffer of messages to be sent. For each message, it:
+ * 1. Acquires a hardware mailbox resource using a semaphore.
+ * 2. Submits the message to the low-level driver for transmission.
+ * 3. Blocks the calling thread by waiting on a completion object.
+ * 4. Is woken up by the TX complete ISR (`rt_hw_can_isr`) when the transmission is finished.
+ *
+ * @note This function will block the calling thread and must not be called from an ISR.
+ *
+ * @param[in] can A pointer to the CAN device.
+ * @param[in] data A pointer to the source buffer of messages to be sent.
+ * @param[in] msgs The total size in bytes of the source buffer.
+ *
+ * @return The number of bytes successfully sent.
+ */
rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *data, int msgs)
{
int size;
@@ -146,11 +177,11 @@ rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *da
struct rt_can_sndbxinx_list *tx_tosnd = RT_NULL;
rt_sem_take(&(tx_fifo->sem), RT_WAITING_FOREVER);
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
tx_tosnd = rt_list_entry(tx_fifo->freelist.next, struct rt_can_sndbxinx_list, list);
RT_ASSERT(tx_tosnd != RT_NULL);
rt_list_remove(&tx_tosnd->list);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
no = ((rt_ubase_t)tx_tosnd - (rt_ubase_t)tx_fifo->buffer) / sizeof(struct rt_can_sndbxinx_list);
tx_tosnd->result = RT_CAN_SND_RESULT_WAIT;
@@ -158,9 +189,9 @@ rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *da
if (can->ops->sendmsg(can, data, no) != RT_EOK)
{
/* send failed. */
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
rt_sem_release(&(tx_fifo->sem));
goto err_ret;
}
@@ -168,29 +199,29 @@ rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *da
can->status.sndchange |= 1<<no;
if (rt_completion_wait(&(tx_tosnd->completion), RT_CANSND_MSG_TIMEOUT) != RT_EOK)
{
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
can->status.sndchange &= ~ (1<<no);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
rt_sem_release(&(tx_fifo->sem));
goto err_ret;
}
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
result = tx_tosnd->result;
if (!rt_list_isempty(&tx_tosnd->list))
{
rt_list_remove(&tx_tosnd->list);
}
rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
rt_sem_release(&(tx_fifo->sem));
if (result == RT_CAN_SND_RESULT_OK)
{
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->status.sndpkg++;
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
data ++;
msgs -= sizeof(struct rt_can_msg);
@@ -199,9 +230,9 @@ rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *da
else
{
err_ret:
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->status.dropedsndpkg++;
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
break;
}
}
@@ -209,6 +240,20 @@ err_ret:
return (size - msgs);
}
+/**
+ * @internal
+ * @brief Handles blocking transmission in "private mode".
+ *
+ * This is a specialized version of `_can_int_tx` where the target hardware mailbox
+ * for each message is specified by the user in the `priv` field of the `rt_can_msg`
+ * structure, rather than being acquired dynamically from a pool.
+ *
+ * @param[in] can A pointer to the CAN device.
+ * @param[in] data A pointer to the source buffer of messages.
+ * @param[in] msgs The total size in bytes of the source buffer.
+ *
+ * @return The number of bytes successfully sent.
+ */
rt_inline int _can_int_tx_priv(struct rt_can_device *can, const struct rt_can_msg *data, int msgs)
{
int size;
@@ -230,16 +275,16 @@ rt_inline int _can_int_tx_priv(struct rt_can_device *can, const struct rt_can_ms
break;
}
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
if ((tx_fifo->buffer[no].result != RT_CAN_SND_RESULT_OK))
{
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
rt_completion_wait(&(tx_fifo->buffer[no].completion), RT_WAITING_FOREVER);
continue;
}
tx_fifo->buffer[no].result = RT_CAN_SND_RESULT_WAIT;
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
if (can->ops->sendmsg(can, data, no) != RT_EOK)
{
@@ -255,18 +300,18 @@ rt_inline int _can_int_tx_priv(struct rt_can_device *can, const struct rt_can_ms
result = tx_fifo->buffer[no].result;
if (result == RT_CAN_SND_RESULT_OK)
{
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->status.sndpkg++;
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
data ++;
msgs -= sizeof(struct rt_can_msg);
if (!msgs) break;
}
else
{
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->status.dropedsndpkg++;
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
break;
}
}
@@ -274,6 +319,76 @@ rt_inline int _can_int_tx_priv(struct rt_can_device *can, const struct rt_can_ms
return (size - msgs);
}
+/**
+ * @internal
+ * @brief Internal implementation of non-blocking CAN transmission.
+ *
+ * This function iterates through a buffer of CAN messages and attempts to send each one
+ * using a non-blocking strategy. It first tries to send directly via hardware (fast path).
+ * If the hardware is busy, it enqueues the message into a software ring buffer (slow path).
+ * This function is thread-safe and ISR-safe due to the use of critical sections for
+ * accessing the shared ring buffer.
+ *
+ * @param[in] can A pointer to the CAN device.
+ * @param[in] pmsg A pointer to the buffer of `rt_can_msg` structures.
+ * @param[in] size The total size of the buffer in bytes.
+ *
+ * @return The number of bytes successfully sent or enqueued for later transmission.
+ */
+static rt_ssize_t _can_nonblocking_tx(struct rt_can_device *can, const struct rt_can_msg *pmsg, rt_size_t size)
+{
+ rt_ssize_t sent_size = 0;
+ rt_base_t level;
+
+ if (can->ops->sendmsg_nonblocking == RT_NULL)
+ {
+ return -RT_EINVAL;
+ }
+
+ while (sent_size < size)
+ {
+ if (can->ops->sendmsg_nonblocking(can, pmsg) == RT_EOK)
+ {
+ pmsg++;
+ sent_size += sizeof(struct rt_can_msg);
+ continue;
+ }
+
+ level = rt_hw_local_irq_disable();
+ if (rt_ringbuffer_space_len(&can->nb_tx_rb) >= sizeof(struct rt_can_msg))
+ {
+ rt_ringbuffer_put(&can->nb_tx_rb, (rt_uint8_t *)pmsg, sizeof(struct rt_can_msg));
+ rt_hw_local_irq_enable(level);
+
+ pmsg++;
+ sent_size += sizeof(struct rt_can_msg);
+ }
+ else
+ {
+ /* Buffer is full, cannot process this message or subsequent ones. */
+ can->status.dropedsndpkg += (size - sent_size) / sizeof(struct rt_can_msg);
+ rt_hw_local_irq_enable(level);
+ break;
+ }
+ }
+
+ return sent_size;
+}
+
+/**
+ * @internal
+ * @brief Opens the CAN device and initializes its resources.
+ *
+ * This function is called when `rt_device_open()` is invoked on a CAN device.
+ * It allocates and initializes software FIFOs for reception and transmission,
+ * sets up semaphores for the blocking send mechanism, configures the non-blocking
+ * send buffer, and starts the periodic status timer.
+ *
+ * @param[in] dev A pointer to the device to be opened.
+ * @param[in] oflag The open flags, e.g., `RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX`.
+ *
+ * @return `RT_EOK` on successful opening, or an error code on failure.
+ */
static rt_err_t rt_can_open(struct rt_device *dev, rt_uint16_t oflag)
{
struct rt_can_device *can;
@@ -369,6 +484,12 @@ static rt_err_t rt_can_open(struct rt_device *dev, rt_uint16_t oflag)
}
#endif
+#ifdef RT_CAN_MALLOC_NB_TX_BUFFER
+ can->nb_tx_rb_pool = (rt_uint8_t *)rt_malloc(RT_CAN_NB_TX_FIFO_SIZE);
+ RT_ASSERT(can->nb_tx_rb_pool != RT_NULL);
+#endif /* RT_CAN_MALLOC_NB_TX_BUFFER */
+ rt_ringbuffer_init(&can->nb_tx_rb, can->nb_tx_rb_pool, RT_CAN_NB_TX_FIFO_SIZE);
+
if (!can->timerinitflag)
{
can->timerinitflag = 1;
@@ -397,6 +518,14 @@ static rt_err_t rt_can_close(struct rt_device *dev)
return RT_EOK;
}
+#ifdef RT_CAN_MALLOC_NB_TX_BUFFER
+ if (can->nb_tx_rb_pool)
+ {
+ rt_free(can->nb_tx_rb_pool);
+ can->nb_tx_rb_pool = RT_NULL;
+ }
+#endif
+
if (can->timerinitflag)
{
can->timerinitflag = 0;
@@ -461,7 +590,7 @@ static rt_ssize_t rt_can_read(struct rt_device *dev,
struct rt_can_device *can;
RT_ASSERT(dev != RT_NULL);
- if (size == 0) return 0;
+ if (size == 0) return -RT_EINVAL;
can = (struct rt_can_device *)dev;
@@ -470,22 +599,64 @@ static rt_ssize_t rt_can_read(struct rt_device *dev,
return _can_int_rx(can, buffer, size);
}
- return 0;
+ return -RT_ENOSYS;
}
+/**
+ * @brief Write data to the CAN device.
+ *
+ * This function serves as the unified entry point for sending CAN messages.
+ * It intelligently routes the request to either a blocking or non-blocking
+ * transmission function based on:
+ * 1. The calling context (thread or ISR).
+ * 2. A user-specified flag (`nonblocking`) in the message structure.
+ *
+ * @param[in] dev A pointer to the device object.
+ * @param[in] pos This parameter is ignored for CAN devices.
+ * @param[in] buffer A pointer to the buffer containing one or more `rt_can_msg` structures.
+ * @param[in] size The total size of the buffer in bytes. Must be a multiple of `sizeof(struct rt_can_msg)`.
+ *
+ * @return The number of bytes successfully written. For non-blocking sends, this means
+ * the data was either sent directly or enqueued in the buffer.
+ */
static rt_ssize_t rt_can_write(struct rt_device *dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
struct rt_can_device *can;
+ const struct rt_can_msg *pmsg;
RT_ASSERT(dev != RT_NULL);
- if (size == 0) return 0;
+ RT_ASSERT(buffer != RT_NULL);
+
+ if (size == 0) return -RT_EINVAL;
+
+ /* Ensure size is a multiple of the message size for buffer operations */
+ if (size % sizeof(struct rt_can_msg) != 0)
+ {
+ return -RT_EINVAL;
+ }
can = (struct rt_can_device *)dev;
+ pmsg = (const struct rt_can_msg *)buffer;
+
+ if(dev->ref_count == 0)
+ {
+ return -RT_ENOSYS;
+ }
- if ((dev->open_flag & RT_DEVICE_FLAG_INT_TX) && (dev->ref_count > 0))
+ /*
+ * Routing to the non-blocking send scenario:
+ * 1. Called from within an interrupt context.
+ * 2. Called from a thread, but the user explicitly set the nonblocking flag on the first message.
+ */
+ if (rt_interrupt_get_nest() > 0 || pmsg->nonblocking)
+ {
+ return _can_nonblocking_tx(can, pmsg, size);
+ }
+
+ if (dev->open_flag & RT_DEVICE_FLAG_INT_TX)
{
if (can->config.privmode)
{
@@ -496,7 +667,7 @@ static rt_ssize_t rt_can_write(struct rt_device *dev,
return _can_int_tx(can, buffer, size);
}
}
- return 0;
+ return -RT_ENOSYS;
}
static rt_err_t rt_can_control(struct rt_device *dev,
@@ -542,7 +713,7 @@ static rt_err_t rt_can_control(struct rt_device *dev,
{
for (i = 0; i < can->config.sndboxnumber; i++)
{
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
if(rt_list_isempty(&tx_fifo->buffer[i].list))
{
rt_sem_release(&(tx_fifo->sem));
@@ -551,7 +722,7 @@ static rt_err_t rt_can_control(struct rt_device *dev,
{
rt_list_remove(&tx_fifo->buffer[i].list);
}
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
}
}
@@ -559,12 +730,12 @@ static rt_err_t rt_can_control(struct rt_device *dev,
{
for (i = 0; i < can->config.sndboxnumber; i++)
{
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
if (tx_fifo->buffer[i].result == RT_CAN_SND_RESULT_OK)
{
rt_list_insert_before(&tx_fifo->freelist, &tx_fifo->buffer[i].list);
}
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
}
}
}
@@ -603,18 +774,18 @@ static rt_err_t rt_can_control(struct rt_device *dev,
continue;
}
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
if (!can->hdr[pitem->hdr_bank].connected)
{
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
rt_memcpy(&can->hdr[pitem->hdr_bank].filter, pitem,
sizeof(struct rt_can_filter_item));
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->hdr[pitem->hdr_bank].connected = 1;
can->hdr[pitem->hdr_bank].msgs = 0;
rt_list_init(&can->hdr[pitem->hdr_bank].list);
}
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
count--;
pitem++;
@@ -630,7 +801,7 @@ static rt_err_t rt_can_control(struct rt_device *dev,
pitem++;
continue;
}
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
if (can->hdr[pitem->hdr_bank].connected)
{
@@ -640,13 +811,13 @@ static rt_err_t rt_can_control(struct rt_device *dev,
{
rt_list_remove(can->hdr[pitem->hdr_bank].list.next);
}
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
rt_memset(&can->hdr[pitem->hdr_bank].filter, 0,
sizeof(struct rt_can_filter_item));
}
else
{
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
}
count--;
pitem++;
@@ -675,8 +846,17 @@ static rt_err_t rt_can_control(struct rt_device *dev,
return res;
}
-/*
- * can timer
+/**
+ * @internal
+ * @brief Periodic timer callback for the CAN device.
+ *
+ * This function is executed periodically by a system timer. Its main purposes are:
+ * 1. To query the current status of the CAN controller (e.g., error counters, bus state).
+ * 2. To invoke a user-registered status indicator callback, if any.
+ * 3. To call a user-registered bus hook function for periodic tasks, if any.
+ *
+ * @param[in] arg The argument passed to the callback, which is a pointer to the `rt_can_device`.
+ * @return void
*/
static void cantimeout(void *arg)
{
@@ -740,6 +920,10 @@ rt_err_t rt_hw_can_register(struct rt_can_device *can,
can->bus_hook = RT_NULL;
#endif /*RT_CAN_USING_BUS_HOOK*/
+#ifdef RT_CAN_MALLOC_NB_TX_BUFFER
+ can->nb_tx_rb_pool = RT_NULL;
+#endif
+
#ifdef RT_USING_DEVICE_OPS
device->ops = &can_device_ops;
#else
@@ -770,6 +954,17 @@ rt_err_t rt_hw_can_register(struct rt_can_device *can,
}
/* ISR for can interrupt */
+/**
+ * @brief The framework-level ISR handler for CAN devices.
+ *
+ * This function is called by the low-level BSP ISR and acts as the central
+ * dispatcher for all CAN-related interrupt events. It handles both receive
+ * events and transmission-complete events.
+ *
+ * @param[in] can A pointer to the CAN device structure.
+ * @param[in] event The interrupt event mask, indicating the cause of the interrupt.
+ * @return void
+ */
void rt_hw_can_isr(struct rt_can_device *can, int event)
{
switch (event & 0xff)
@@ -777,9 +972,9 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
case RT_CAN_EVENT_RXOF_IND:
{
rt_base_t level;
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->status.dropedrcvpkg++;
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
}
case RT_CAN_EVENT_RX_IND:
{
@@ -803,7 +998,7 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
if (ch == -1) break;
/* disable interrupt */
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->status.rcvpkg++;
can->status.rcvchange = 1;
if (!rt_list_isempty(&rx_fifo->freelist))
@@ -836,12 +1031,12 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
#endif
}
/* enable interrupt */
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
if (listmsg != RT_NULL)
{
rt_memcpy(&listmsg->data, &tmpmsg, sizeof(struct rt_can_msg));
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
rt_list_insert_before(&rx_fifo->uselist, &listmsg->list);
#ifdef RT_CAN_USING_HDR
hdr = tmpmsg.hdr_index;
@@ -857,7 +1052,7 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
}
#endif
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
}
/* invoke callback */
@@ -867,9 +1062,9 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
rt_size_t rx_length;
RT_ASSERT(hdr < can->config.maxhdr && hdr >= 0);
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
rx_length = can->hdr[hdr].msgs * sizeof(struct rt_can_msg);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
if (rx_length)
{
can->hdr[hdr].filter.ind(&can->parent, can->hdr[hdr].filter.args, hdr, rx_length);
@@ -882,10 +1077,10 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
{
rt_size_t rx_length;
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
/* get rx length */
rx_length = rt_list_len(&rx_fifo->uselist)* sizeof(struct rt_can_msg);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
if (rx_length)
{
@@ -904,6 +1099,7 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
no = event >> 8;
tx_fifo = (struct rt_can_tx_fifo *) can->can_tx;
RT_ASSERT(tx_fifo != RT_NULL);
+
if (can->status.sndchange&(1<<no))
{
if ((event & 0xff) == RT_CAN_EVENT_TX_DONE)
@@ -916,6 +1112,37 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
}
rt_completion_done(&(tx_fifo->buffer[no].completion));
}
+
+ if (can->ops->sendmsg_nonblocking != RT_NULL)
+ {
+ while (RT_TRUE)
+ {
+ struct rt_can_msg msg_to_send;
+ rt_base_t level;
+ rt_bool_t msg_was_present = RT_FALSE;
+
+ level = rt_hw_local_irq_disable();
+ if (rt_ringbuffer_data_len(&can->nb_tx_rb) >= sizeof(struct rt_can_msg))
+ {
+ rt_ringbuffer_get(&can->nb_tx_rb, (rt_uint8_t *)&msg_to_send, sizeof(struct rt_can_msg));
+ msg_was_present = RT_TRUE;
+ }
+ rt_hw_local_irq_enable(level);
+
+ if (!msg_was_present)
+ {
+ break;
+ }
+
+ if (can->ops->sendmsg_nonblocking(can, &msg_to_send) != RT_EOK)
+ {
+ level = rt_hw_local_irq_disable();
+ rt_ringbuffer_put_force(&can->nb_tx_rb, (rt_uint8_t *)&msg_to_send, sizeof(struct rt_can_msg));
+ rt_hw_local_irq_enable(level);
+ break;
+ }
+ }
+ }
break;
}
}
diff --git a/components/drivers/include/drivers/dev_can.h b/components/drivers/include/drivers/dev_can.h
index 81ea0927ed..922ddcbb53 100644
--- a/components/drivers/include/drivers/dev_can.h
+++ b/components/drivers/include/drivers/dev_can.h
@@ -8,12 +8,14 @@
* 2015-05-14 [email protected] first version
* 2015-07-06 Bernard remove RT_CAN_USING_LED.
* 2022-05-08 hpmicro add CANFD support, fixed typos
+ * 2025-09-20 wdfk_prog Added non-blocking send mechanism APIs and data structures.
*/
#ifndef __DEV_CAN_H_
#define __DEV_CAN_H_
#include <rtthread.h>
+#include <rtdevice.h>
#ifndef RT_CANMSG_BOX_SZ
#define RT_CANMSG_BOX_SZ 16
@@ -71,77 +73,88 @@ enum CANBAUD
* @brief CAN driver api
* @ingroup group_device_driver
*
- * <b>Example</b>
+ * <b>Example: Demonstrating CAN RX, Filters, and Blocking/Non-Blocking TX</b>
* @code {.c}
* #include <rtthread.h>
- * #include "rtdevice.h"
+ * #include <rtdevice.h>
*
- * #define CAN_DEV_NAME "can1" // CAN 设备名称
+ * #define CAN_DEV_NAME "can1" // The name of the CAN device
*
- * static struct rt_semaphore rx_sem; // 用于接收消息的信号量
- * static rt_device_t can_dev; // CAN 设备句柄
+ * static rt_device_t can_dev; // CAN device handle
+ * static struct rt_semaphore rx_sem; // Semaphore for message reception
*
- * // 接收数据回调函数
- * static rt_err_t can_rx_call(rt_device_t dev, rt_size_t size)
+ * // Callback function for CAN reception
+ * static rt_err_t can_rx_callback(rt_device_t dev, rt_size_t size)
* {
- * // CAN 接收到数据后产生中断,调用此回调函数,然后发送接收信号量
+ * // The CAN interrupt calls this callback when data is received.
+ * // Release the semaphore to notify the receiving thread.
* rt_sem_release(&rx_sem);
- *
* return RT_EOK;
* }
*
* static void can_rx_thread(void *parameter)
* {
- * int i;
* rt_err_t res;
- * struct rt_can_msg rxmsg = {0};
+ * struct rt_can_msg rx_msg = {0};
*
- * // 设置接收回调函数
- * rt_device_set_rx_indicate(can_dev, can_rx_call);
+ * // Set the receive callback function
+ * rt_device_set_rx_indicate(can_dev, can_rx_callback);
*
* #ifdef RT_CAN_USING_HDR
- * struct rt_can_filter_item items[5] =
+ * // Example of configuring multiple hardware filters
+ * struct rt_can_filter_item items[] =
* {
- * RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, 0, 0x700, RT_NULL, RT_NULL), // std,match ID:0x100~0x1ff,hdr 为 - 1,设置默认过滤表
- * RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, 0, 0x700, RT_NULL, RT_NULL), // std,match ID:0x300~0x3ff,hdr 为 - 1
- * RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, 0, 0x7ff, RT_NULL, RT_NULL), // std,match ID:0x211,hdr 为 - 1
- * RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL), // std,match ID:0x486,hdr 为 - 1
- * {0x555, 0, 0, 0, 0x7ff, 7,} // std,match ID:0x555,hdr 为 7,指定设置 7 号过滤表
+ * // Filter 1: Match standard frames with IDs from 0x100 to 0x1FF. hdr_index will be -1.
+ * RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, 0, 0x700, RT_NULL, RT_NULL),
+ * // Filter 2: Match standard frames with IDs from 0x300 to 0x3FF. hdr_index will be -1.
+ * RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, 0, 0x700, RT_NULL, RT_NULL),
+ * // Filter 3: Exactly match standard frame with ID 0x211. hdr_index will be -1.
+ * RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, 0, 0x7FF, RT_NULL, RT_NULL),
+ * // Filter 4: Exactly match standard frame with ID 0x486 using a helper macro. hdr_index will be -1.
+ * RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL),
+ * // Filter 5: Exactly match standard frame with ID 0x555 and explicitly assign it to filter bank #7.
+ * // This uses direct struct initialization: {id, ide, rtr, mode, mask, hdr_bank}.
+ * {0x555, 0, 0, 0, 0x7FF, 7}
* };
- * struct rt_can_filter_config cfg = {5, 1, items}; // 一共有 5 个过滤表
- * // 设置硬件过滤表
+ * // Create the filter configuration structure with 5 active filters.
+ * struct rt_can_filter_config cfg = {sizeof(items)/sizeof(items[0]), 1, items};
+ * // Set the hardware filters for the CAN device.
* res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg);
* RT_ASSERT(res == RT_EOK);
* #endif
- * res = RT_TRUE;
- * res = rt_device_control(can_dev, RT_CAN_CMD_START, &res);
+ *
+ * // Some drivers might require an explicit start command.
+ * // This is driver-specific.
+ * rt_uint32_t cmd_arg = 1; // Argument to enable the controller
+ * res = rt_device_control(can_dev, RT_CAN_CMD_START, &cmd_arg);
+ * RT_ASSERT(res == RT_EOK);
+ *
* while (1)
* {
- * // hdr 值为 - 1,表示直接从 uselist 链表读取数据
- * rxmsg.hdr = -1;
- * // 阻塞等待接收信号量
+ * // Block and wait for the semaphore, which is released by the receive callback.
* rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
- * // 从 CAN 读取一帧数据
- * rt_device_read(can_dev, 0, &rxmsg, sizeof(rxmsg));
- * // 打印数据 ID 及内容
- * rt_kprintf("ID:%x", rxmsg.id);
- * for (i = 0; i < 8; i++)
+ *
+ * // Read one frame of data from the CAN device's general message queue.
+ * rx_msg.hdr_index = -1;
+ * rt_device_read(can_dev, 0, &rx_msg, sizeof(rx_msg));
+ *
+ * // Print the received message's ID and data.
+ * rt_kprintf("Received a message. ID: 0x%x, Data: ", rx_msg.id);
+ * for (int i = 0; i < rx_msg.len; i++)
* {
- * rt_kprintf("%2x", rxmsg.data[i]);
+ * rt_kprintf("%02x ", rx_msg.data[i]);
* }
- *
* rt_kprintf("\n");
* }
* }
*
* int can_sample(int argc, char *argv[])
* {
- * struct rt_can_msg msg = {0};
* rt_err_t res;
- * rt_size_t size;
* rt_thread_t thread;
* char can_name[RT_NAME_MAX];
*
+ * // Allow specifying the CAN device name from the command line, e.g., "can_sample can2"
* if (argc == 2)
* {
* rt_strncpy(can_name, argv[1], RT_NAME_MAX);
@@ -150,21 +163,23 @@ enum CANBAUD
* {
* rt_strncpy(can_name, CAN_DEV_NAME, RT_NAME_MAX);
* }
- * // 查找 CAN 设备
+ *
+ * // Find the CAN device by name
* can_dev = rt_device_find(can_name);
* if (!can_dev)
* {
- * rt_kprintf("find %s failed!\n", can_name);
+ * rt_kprintf("find device %s failed!\n", can_name);
* return -RT_ERROR;
* }
*
- * // 初始化 CAN 接收信号量
+ * // Initialize the receive semaphore
* rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
*
- * // 以中断接收及发送方式打开 CAN 设备
+ * // Open the CAN device in interrupt-driven TX/RX mode
* res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
* RT_ASSERT(res == RT_EOK);
- * // 创建数据接收线程
+ *
+ * // Create and start the data receiving thread
* thread = rt_thread_create("can_rx", can_rx_thread, RT_NULL, 1024, 25, 10);
* if (thread != RT_NULL)
* {
@@ -173,36 +188,61 @@ enum CANBAUD
* else
* {
* rt_kprintf("create can_rx thread failed!\n");
+ * return -RT_ERROR;
* }
*
- * msg.id = 0x78; // ID 为 0x78
- * msg.ide = RT_CAN_STDID; // 标准格式
- * msg.rtr = RT_CAN_DTR; // 数据帧
- * msg.len = 8; // 数据长度为 8
- * // 待发送的 8 字节数据
- * msg.data[0] = 0x00;
- * msg.data[1] = 0x11;
- * msg.data[2] = 0x22;
- * msg.data[3] = 0x33;
- * msg.data[4] = 0x44;
- * msg.data[5] = 0x55;
- * msg.data[6] = 0x66;
- * msg.data[7] = 0x77;
- * // 发送一帧 CAN 数据
- * size = rt_device_write(can_dev, 0, &msg, sizeof(msg));
- * if (size == 0)
+ * rt_kprintf("CAN device %s opened successfully.\n", can_name);
+ *
+ * // --- Demonstrate Blocking Send ---
+ * struct rt_can_msg blocking_msg = {0};
+ * blocking_msg.id = 0x78;
+ * blocking_msg.ide = RT_CAN_STDID;
+ * blocking_msg.rtr = RT_CAN_DTR;
+ * blocking_msg.len = 8;
+ * // The `nonblocking` flag is 0 by default for blocking mode.
+ * for(int i = 0; i < 8; i++) blocking_msg.data[i] = i;
+ *
+ * rt_kprintf("Attempting to send a message in BLOCKING mode...\n");
+ * if (rt_device_write(can_dev, 0, &blocking_msg, sizeof(blocking_msg)) == sizeof(blocking_msg))
+ * {
+ * rt_kprintf("Blocking message sent successfully.\n");
+ * }
+ * else
+ * {
+ * rt_kprintf("Blocking message send failed.\n");
+ * }
+ *
+ * rt_thread_mdelay(100); // Wait a moment for clarity in the log
+ *
+ * // --- Demonstrate Non-Blocking Send ---
+ * struct rt_can_msg nonblocking_msg = {0};
+ * nonblocking_msg.id = 0x79;
+ * nonblocking_msg.ide = RT_CAN_STDID;
+ * nonblocking_msg.rtr = RT_CAN_DTR;
+ * nonblocking_msg.len = 4;
+ * nonblocking_msg.data[0] = 0xDE;
+ * nonblocking_msg.data[1] = 0xAD;
+ * nonblocking_msg.data[2] = 0xBE;
+ * nonblocking_msg.data[3] = 0xEF;
+ * nonblocking_msg.nonblocking = 1; // <-- Key: Set the non-blocking flag
+ *
+ * rt_kprintf("Attempting to send a message in NON-BLOCKING mode...\n");
+ * if (rt_device_write(can_dev, 0, &nonblocking_msg, sizeof(nonblocking_msg)) == sizeof(nonblocking_msg))
* {
- * rt_kprintf("can dev write data failed!\n");
+ * rt_kprintf("Non-blocking message was accepted (sent or enqueued).\n");
+ * }
+ * else
+ * {
+ * rt_kprintf("Non-blocking send failed (buffer was full).\n");
* }
*
* return res;
* }
- * // 导出到 msh 命令列表中
- * MSH_CMD_EXPORT(can_sample, can device sample);
+ * // Export the function to the MSH command line
+ * MSH_CMD_EXPORT(can_sample, can device usage example);
* @endcode
*/
-
/*!
* @addtogroup group_drivers_can
* @{
@@ -211,27 +251,46 @@ enum CANBAUD
#define CAN_RX_FIFO1 (0x00000001U) /*!< CAN receive FIFO 1 */
/**
- * @brief CAN filter item
+ * @brief CAN filter item structure
*/
struct rt_can_filter_item
{
- rt_uint32_t id : 29;
- rt_uint32_t ide : 1;
- rt_uint32_t rtr : 1;
- rt_uint32_t mode : 1;
- rt_uint32_t mask;
- rt_int32_t hdr_bank;/*Should be defined as:rx.FilterBank,which should be changed to rt_int32_t hdr_bank*/
- rt_uint32_t rxfifo;/*Add a configuration item that CAN_RX_FIFO0/CAN_RX_FIFO1*/
+ rt_uint32_t id : 29; /**< The CAN ID to be filtered. */
+ rt_uint32_t ide : 1; /**< Identifier type. 0 for Standard ID, 1 for Extended ID. */
+ rt_uint32_t rtr : 1; /**< Frame type. 0 for Data Frame, 1 for Remote Frame. */
+ rt_uint32_t mode : 1; /**< Filter mode. 0 for Mask Mode, 1 for List Mode. */
+ rt_uint32_t mask; /**< The filter mask. In Mask Mode, a '1' bit means the corresponding ID bit must match. */
+ rt_int32_t hdr_bank; /**< The specific hardware filter bank index to use. A value of -1 allows the driver to auto-assign. */
+ rt_uint32_t rxfifo; /**< The target RX FIFO for matched messages (CAN_RX_FIFO0 or CAN_RX_FIFO1). */
#ifdef RT_CAN_USING_HDR
+ /**
+ * @brief Callback function for a specific filter.
+ * @param[in] dev The CAN device that triggered the callback.
+ * @param[in] args User-provided arguments.
+ * @param[in] hdr The hardware filter index that matched the message.
+ * @param[in] size The size of the received data in bytes.
+ * @return The operation status.
+ */
rt_err_t (*ind)(rt_device_t dev, void *args , rt_int32_t hdr, rt_size_t size);
- void *args;
+ void *args; /**< User arguments for the indication callback. */
#endif /*RT_CAN_USING_HDR*/
};
-
+/**
+ * @def RT_CAN_FILTER_ITEM_INIT
+ * @brief A helper macro to initialize a `rt_can_filter_item` structure for Mask Mode.
+ *
+ * @param[in] _id The CAN ID for the filter.
+ * @param[in] _ide Identifier type (0 for Standard, 1 for Extended).
+ * @param[in] _rtr Frame type (0 for Data, 1 for Remote).
+ * @param[in] _mode Filter mode (0 for Mask, 1 for List).
+ * @param[in] _mask The mask to be applied.
+ * @param[in] _ind Optional callback function (can be RT_NULL).
+ * @param[in] _args Optional arguments for the callback (can be RT_NULL).
+ */
#ifdef RT_CAN_USING_HDR
#define RT_CAN_FILTER_ITEM_INIT(id,ide,rtr,mode,mask,ind,args) \
- {(id), (ide), (rtr), (mode),(mask), -1, CAN_RX_FIFO0,(ind), (args)}/*0:CAN_RX_FIFO0*/
+ {(id), (ide), (rtr), (mode),(mask), -1, CAN_RX_FIFO0,(ind), (args)}
#define RT_CAN_FILTER_STD_INIT(id,ind,args) \
RT_CAN_FILTER_ITEM_INIT(id,0,0,0,0xFFFFFFFF,ind,args)
#define RT_CAN_FILTER_EXT_INIT(id,ind,args) \
@@ -245,9 +304,8 @@ struct rt_can_filter_item
#define RT_CAN_EXT_RMT_DATA_FILTER_INIT(id,ind,args) \
RT_CAN_FILTER_ITEM_INIT(id,1,0,1,0xFFFFFFFF,ind,args)
#else
-
#define RT_CAN_FILTER_ITEM_INIT(id,ide,rtr,mode,mask) \
- {(id), (ide), (rtr), (mode), (mask), -1, CAN_RX_FIFO0 }/*0:CAN_RX_FIFO0*/
+ {(id), (ide), (rtr), (mode), (mask), -1, CAN_RX_FIFO0 }
#define RT_CAN_FILTER_STD_INIT(id) \
RT_CAN_FILTER_ITEM_INIT(id,0,0,0,0xFFFFFFFF)
#define RT_CAN_FILTER_EXT_INIT(id) \
@@ -264,65 +322,65 @@ struct rt_can_filter_item
/**
- * @brief CAN filter configuration
+ * @brief CAN hardware filter configuration structure.
+ * This structure is passed to the driver via `rt_device_control` with the `RT_CAN_CMD_SET_FILTER` command.
*/
struct rt_can_filter_config
{
- rt_uint32_t count;
- rt_uint32_t actived;
- struct rt_can_filter_item *items;
+ rt_uint32_t count; /**< The number of filter items in the `items` array. */
+ rt_uint32_t actived; /**< A flag to activate (1) or deactivate (0) the filters. */
+ struct rt_can_filter_item *items; /**< A pointer to an array of filter items. */
};
/**
- * @brief CAN timing configuration
+ * @brief CAN bit-timing parameters structure.
*/
struct rt_can_bit_timing
{
- rt_uint16_t prescaler; /* Pre-scaler */
- rt_uint16_t num_seg1; /* Bit Timing Segment 1, in terms of Tq */
- rt_uint16_t num_seg2; /* Bit Timing Segment 2, in terms of Tq */
- rt_uint8_t num_sjw; /* Synchronization Jump Width, in terms of Tq */
- rt_uint8_t num_sspoff; /* Secondary Sample Point Offset, in terms of Tq */
+ rt_uint16_t prescaler; /**< Baud rate prescaler. */
+ rt_uint16_t num_seg1; /**< Bit Timing Segment 1, in terms of Time Quanta (Tq). */
+ rt_uint16_t num_seg2; /**< Bit Timing Segment 2, in terms of Time Quanta (Tq). */
+ rt_uint8_t num_sjw; /**< Synchronization Jump Width, in terms of Time Quanta (Tq). */
+ rt_uint8_t num_sspoff; /**< Secondary Sample Point Offset, in terms of Time Quanta (Tq) for CAN-FD. */
};
/**
- * @brief CAN bit timing configuration list
- * @note
- * items[0] always for CAN2.0/CANFD Arbitration Phase
- * items[1] always for CANFD (if it exists)
+ * @brief CAN bit timing configuration list structure.
+ * @note items[0] is for CAN 2.0 / CAN-FD Arbitration Phase.
+ * items[1] is for CAN-FD Data Phase (if applicable).
*/
struct rt_can_bit_timing_config
{
- rt_uint32_t count;
- struct rt_can_bit_timing *items;
+ rt_uint32_t count; /**< The number of bit-timing configurations (typically 1 for CAN, 2 for CAN-FD). */
+ struct rt_can_bit_timing *items; /**< A pointer to an array of bit-timing structures. */
};
/**
- * @brief CAN configuration
+ * @brief CAN device configuration structure.
*/
struct can_configure
{
- rt_uint32_t baud_rate;
- rt_uint32_t msgboxsz;
- rt_uint32_t sndboxnumber;
- rt_uint32_t mode : 8;
- rt_uint32_t privmode : 8;
- rt_uint32_t reserved : 16;
- rt_uint32_t ticks;
+ rt_uint32_t baud_rate; /**< The baud rate for the arbitration phase (e.g., CAN500kBaud). */
+ rt_uint32_t msgboxsz; /**< The size of the software receive buffer (in number of messages). */
+ rt_uint32_t sndboxnumber; /**< The number of hardware mailboxes used for blocking send operations. */
+ rt_uint32_t mode : 8; /**< The CAN operation mode (e.g., RT_CAN_MODE_NORMAL, RT_CAN_MODE_LOOPBACK). */
+ rt_uint32_t privmode : 8; /**< Private mode flag. If set, the `priv` field of `rt_can_msg` specifies the hardware mailbox. */
+ rt_uint32_t reserved : 16; /**< Reserved for future use. */
+ rt_uint32_t ticks; /**< The period in OS ticks for the status-checking timer. */
#ifdef RT_CAN_USING_HDR
- rt_uint32_t maxhdr;
+ rt_uint32_t maxhdr; /**< The maximum number of hardware filters supported by the controller. */
#endif
#ifdef RT_CAN_USING_CANFD
- rt_uint32_t baud_rate_fd; /* CANFD data bit rate*/
- rt_uint32_t use_bit_timing: 8; /* Use the bit timing for CAN timing configuration */
- rt_uint32_t enable_canfd : 8; /* Enable CAN-FD mode */
- rt_uint32_t reserved1 : 16;
+ rt_uint32_t baud_rate_fd; /**< The baud rate for the CAN-FD data phase. */
+ rt_uint32_t use_bit_timing: 8; /**< A flag to indicate that `can_timing` and `canfd_timing` should be used instead of `baud_rate`. */
+ rt_uint32_t enable_canfd : 8; /**< A flag to enable CAN-FD functionality. */
+ rt_uint32_t reserved1 : 16; /**< Reserved for future use. */
/* The below fields take effect only if use_bit_timing is non-zero */
- struct rt_can_bit_timing can_timing; /* CAN bit-timing /CANFD bit-timing for arbitration phase */
- struct rt_can_bit_timing canfd_timing; /* CANFD bit-timing for datat phase */
+ struct rt_can_bit_timing can_timing; /**< Custom bit-timing for the arbitration phase. */
+ struct rt_can_bit_timing canfd_timing; /**< Custom bit-timing for the data phase. */
#endif
};
@@ -368,66 +426,130 @@ enum RT_CAN_BUS_ERR
};
/**
- * @brief CAN status
+ * @brief CAN device status and error statistics structure.
*/
struct rt_can_status
{
- rt_uint32_t rcverrcnt;
- rt_uint32_t snderrcnt;
- rt_uint32_t errcode;
- rt_uint32_t rcvpkg;
- rt_uint32_t dropedrcvpkg;
- rt_uint32_t sndpkg;
- rt_uint32_t dropedsndpkg;
- rt_uint32_t bitpaderrcnt;
- rt_uint32_t formaterrcnt;
- rt_uint32_t ackerrcnt;
- rt_uint32_t biterrcnt;
- rt_uint32_t crcerrcnt;
- rt_uint32_t rcvchange;
- rt_uint32_t sndchange;
- rt_uint32_t lasterrtype;
+ rt_uint32_t rcverrcnt; /**< Receive Error Counter (REC). */
+ rt_uint32_t snderrcnt; /**< Transmit Error Counter (TEC). */
+ rt_uint32_t errcode; /**< The current bus error code (see `enum RT_CAN_BUS_ERR`). */
+ rt_uint32_t rcvpkg; /**< Total number of successfully received packages. */
+ rt_uint32_t dropedrcvpkg; /**< Number of received packages dropped due to full buffers. */
+ rt_uint32_t sndpkg; /**< Total number of successfully sent packages. */
+ rt_uint32_t dropedsndpkg; /**< Number of sent packages dropped due to full buffers or errors. */
+ rt_uint32_t bitpaderrcnt; /**< Bit stuffing error count. */
+ rt_uint32_t formaterrcnt; /**< Format error count. */
+ rt_uint32_t ackerrcnt; /**< Acknowledgment error count. */
+ rt_uint32_t biterrcnt; /**< Bit error (recessive/dominant) count. */
+ rt_uint32_t crcerrcnt; /**< CRC error count. */
+ rt_uint32_t rcvchange; /**< A flag indicating that the RX buffer status has changed. */
+ rt_uint32_t sndchange; /**< A bitmask indicating which TX mailboxes have changed status. */
+ rt_uint32_t lasterrtype; /**< The type of the last error that occurred. */
};
#ifdef RT_CAN_USING_HDR
+/**
+ * @brief CAN hardware filter list entry.
+ * @internal
+ */
struct rt_can_hdr
{
- rt_uint32_t connected;
- rt_uint32_t msgs;
- struct rt_can_filter_item filter;
- struct rt_list_node list;
+ rt_uint32_t connected; /**< Flag indicating if the filter is connected to a specific list. */
+ rt_uint32_t msgs; /**< The number of messages currently buffered for this filter. */
+ struct rt_can_filter_item filter; /**< A copy of the filter configuration item. */
+ struct rt_list_node list; /**< The list head for messages matched by this filter. */
};
#endif
struct rt_can_device;
-typedef rt_err_t (*rt_canstatus_ind)(struct rt_can_device *, void *);
+/**
+ * @brief Typedef for the CAN status indication callback function.
+ * @param[in] can A pointer to the CAN device.
+ * @param[in] args User-provided arguments.
+ * @return The operation status.
+ */
+typedef rt_err_t (*rt_canstatus_ind)(struct rt_can_device *can, void *args);
+/**
+ * @brief Structure to hold the status indication callback and its arguments.
+ */
typedef struct rt_can_status_ind_type
{
- rt_canstatus_ind ind;
- void *args;
+ rt_canstatus_ind ind; /**< Pointer to the status indication callback function. */
+ void *args; /**< Pointer to user arguments for the callback. */
} *rt_can_status_ind_type_t;
-typedef void (*rt_can_bus_hook)(struct rt_can_device *);
+
+/**
+ * @brief Typedef for the periodic bus hook function.
+ * @param[in] can A pointer to the CAN device.
+ * @return void
+ */
+typedef void (*rt_can_bus_hook)(struct rt_can_device *can);
+
+/**
+ * @brief The CAN message structure.
+ */
+struct rt_can_msg
+{
+ rt_uint32_t id : 29; /**< CAN ID (Standard or Extended). */
+ rt_uint32_t ide : 1; /**< Identifier type: 0=Standard ID, 1=Extended ID. */
+ rt_uint32_t rtr : 1; /**< Frame type: 0=Data Frame, 1=Remote Frame. */
+ rt_uint32_t rsv : 1; /**< Reserved bit. */
+ rt_uint32_t len : 8; /**< Data Length Code (DLC) from 0 to 8. */
+ rt_uint32_t priv : 8; /**< Private data, used to specify the hardware mailbox in private mode. */
+ rt_int32_t hdr_index : 8; /**< For received messages, the index of the hardware filter that matched the message. */
+#ifdef RT_CAN_USING_CANFD
+ rt_uint32_t fd_frame : 1; /**< CAN-FD frame indicator. */
+ rt_uint32_t brs : 1; /**< Bit-rate switching indicator for CAN-FD. */
+ rt_uint32_t rxfifo : 2; /**< The RX FIFO where the message was received. */
+ rt_uint32_t reserved : 3;
+#else
+ rt_uint32_t rxfifo : 2; /**< The RX FIFO where the message was received. */
+ rt_uint32_t reserved : 5;
+#endif
+ rt_uint32_t nonblocking : 1; /**< Send mode: 0=Blocking (default), 1=Non-blocking. */
+#ifdef RT_CAN_USING_CANFD
+ rt_uint8_t data[64]; /**< CAN-FD message payload (up to 64 bytes). */
+#else
+ rt_uint8_t data[8]; /**< CAN message payload (up to 8 bytes). */
+#endif
+};
+typedef struct rt_can_msg *rt_can_msg_t;
+
+#ifndef RT_CAN_NB_TX_FIFO_SIZE
+#define RT_CAN_NB_TX_FIFO_SIZE (RT_CANMSG_BOX_SZ * sizeof(struct rt_can_msg))
+#endif
+
+/**
+ * @brief The core CAN device structure.
+ */
struct rt_can_device
{
- struct rt_device parent;
+ struct rt_device parent; /**< Inherits from the base RT-Thread device structure. */
- const struct rt_can_ops *ops;
- struct can_configure config;
- struct rt_can_status status;
+ const struct rt_can_ops *ops; /**< A pointer to the low-level driver operations. */
+ struct can_configure config; /**< The current configuration of the CAN device. */
+ struct rt_can_status status; /**< The current status and error statistics of the CAN device. */
- rt_uint32_t timerinitflag;
- struct rt_timer timer;
+ rt_uint32_t timerinitflag; /**< A flag to indicate if the status timer has been initialized. */
+ struct rt_timer timer; /**< A timer for periodically checking the CAN bus status. */
- struct rt_can_status_ind_type status_indicate;
+ struct rt_can_status_ind_type status_indicate; /**< The user-registered status indication callback. */
#ifdef RT_CAN_USING_HDR
- struct rt_can_hdr *hdr;
+ struct rt_can_hdr *hdr; /**< A pointer to an array of hardware filter list entries. */
#endif
#ifdef RT_CAN_USING_BUS_HOOK
- rt_can_bus_hook bus_hook;
+ rt_can_bus_hook bus_hook; /**< The user-registered periodic bus hook function. */
#endif /*RT_CAN_USING_BUS_HOOK*/
- struct rt_mutex lock;
- void *can_rx;
- void *can_tx;
+ struct rt_mutex lock; /**< A mutex for thread-safe access to the device. */
+ void *can_rx; /**< A pointer to the software receive FIFO structure (`rt_can_rx_fifo`). */
+ void *can_tx; /**< A pointer to the software transmit FIFO structure (`rt_can_tx_fifo`). */
+
+ struct rt_ringbuffer nb_tx_rb; /**< The ring buffer for non-blocking transmissions. */
+#ifdef RT_CAN_MALLOC_NB_TX_BUFFER
+ rt_uint8_t *nb_tx_rb_pool; /**< A pointer to the dynamically allocated pool for the non-blocking TX ring buffer. */
+#else
+ rt_uint8_t nb_tx_rb_pool[RT_CAN_NB_TX_FIFO_SIZE]; /**< The statically allocated pool for the non-blocking TX ring buffer. */
+#endif /* RT_CAN_MALLOC_NB_TX_BUFFER */
};
typedef struct rt_can_device *rt_can_t;
@@ -438,49 +560,30 @@ typedef struct rt_can_device *rt_can_t;
typedef struct rt_can_status *rt_can_status_t;
-struct rt_can_msg
-{
- rt_uint32_t id : 29;
- rt_uint32_t ide : 1;
- rt_uint32_t rtr : 1;
- rt_uint32_t rsv : 1;
- rt_uint32_t len : 8;
- rt_uint32_t priv : 8;
- rt_int32_t hdr_index : 8;/*Should be defined as:rx.FilterMatchIndex,which should be changed to rt_int32_t hdr_index : 8*/
-#ifdef RT_CAN_USING_CANFD
- rt_uint32_t fd_frame : 1;
- rt_uint32_t brs : 1;
- rt_uint32_t rxfifo : 2;/*Redefined to return :CAN RX FIFO0/CAN RX FIFO1*/
- rt_uint32_t reserved : 4;
-#else
- rt_uint32_t rxfifo : 2;/*Redefined to return :CAN RX FIFO0/CAN RX FIFO1*/
- rt_uint32_t reserved : 6;
-#endif
-#ifdef RT_CAN_USING_CANFD
- rt_uint8_t data[64];
-#else
- rt_uint8_t data[8];
-#endif
-};
-typedef struct rt_can_msg *rt_can_msg_t;
-
+/**
+ * @internal
+ * @brief List node for a single CAN message in a software FIFO.
+ */
struct rt_can_msg_list
{
- struct rt_list_node list;
+ struct rt_list_node list; /**< List node to link into a free/used list. */
#ifdef RT_CAN_USING_HDR
- struct rt_list_node hdrlist;
- struct rt_can_hdr *owner;
+ struct rt_list_node hdrlist; /**< List node to link into a specific hardware filter's list. */
+ struct rt_can_hdr *owner; /**< Pointer to the hardware filter that owns this message. */
#endif
- struct rt_can_msg data;
+ struct rt_can_msg data; /**< The actual CAN message data. */
};
+/**
+ * @internal
+ * @brief Software receive FIFO structure.
+ */
struct rt_can_rx_fifo
{
- /* software fifo */
- struct rt_can_msg_list *buffer;
- rt_uint32_t freenumbers;
- struct rt_list_node freelist;
- struct rt_list_node uselist;
+ struct rt_can_msg_list *buffer; /**< A pointer to the buffer pool of message list nodes. */
+ rt_uint32_t freenumbers; /**< The number of free nodes in the buffer pool. */
+ struct rt_list_node freelist; /**< The list of free message nodes. */
+ struct rt_list_node uselist; /**< The list of used message nodes (containing received messages). */
};
#define RT_CAN_SND_RESULT_OK 0
@@ -493,40 +596,94 @@ struct rt_can_rx_fifo
#define RT_CAN_EVENT_RX_TIMEOUT 0x05 /* Rx timeout */
#define RT_CAN_EVENT_RXOF_IND 0x06 /* Rx overflow */
+/**
+ * @internal
+ * @brief List node for a blocking send operation, corresponding to one hardware mailbox.
+ */
struct rt_can_sndbxinx_list
{
- struct rt_list_node list;
- struct rt_completion completion;
- rt_uint32_t result;
+ struct rt_list_node list; /**< List node to link into the free list. */
+ struct rt_completion completion;/**< A completion object to block the sending thread. */
+ rt_uint32_t result; /**< The result of the transmission (OK, ERR, WAIT). */
};
+/**
+ * @internal
+ * @brief Software transmit FIFO structure for blocking sends.
+ */
struct rt_can_tx_fifo
{
- struct rt_can_sndbxinx_list *buffer;
- struct rt_semaphore sem;
- struct rt_list_node freelist;
+ struct rt_can_sndbxinx_list *buffer; /**< A pointer to the buffer of sendbox nodes. */
+ struct rt_semaphore sem; /**< A counting semaphore representing available hardware mailboxes. */
+ struct rt_list_node freelist; /**< The list of free sendbox nodes. */
};
/**
- * @brief CAN operators
+ * @brief The CAN device driver operations structure.
+ *
+ * This structure contains pointers to the low-level functions that implement the CAN functionality.
+ * It must be provided when registering a CAN device.
*/
struct rt_can_ops
{
+ /**
+ * @brief Configures the CAN controller with the specified settings.
+ * @param[in] can A pointer to the CAN device structure.
+ * @param[in] cfg A pointer to the configuration structure.
+ * @return `RT_EOK` on success, or a negative error code on failure.
+ */
rt_err_t (*configure)(struct rt_can_device *can, struct can_configure *cfg);
+ /**
+ * @brief Sends control commands to the CAN device.
+ * @param[in] can A pointer to the CAN device structure.
+ * @param[in] cmd The control command (e.g., RT_CAN_CMD_SET_FILTER).
+ * @param[in] arg A pointer to the arguments for the command.
+ * @return `RT_EOK` on success, or a negative error code on failure.
+ */
rt_err_t (*control)(struct rt_can_device *can, int cmd, void *arg);
+ /**
+ * @brief Low-level blocking function to send a CAN message.
+ * @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] boxno The hardware mailbox number to use for transmission.
+ * @return The number of bytes sent on success, or a negative error code on failure.
+ */
rt_ssize_t (*sendmsg)(struct rt_can_device *can, const void *buf, rt_uint32_t boxno);
- rt_ssize_t (*recvmsg)(struct rt_can_device *can, void *buf, rt_uint32_t boxno);
+ /**
+ * @brief Low-level function to receive a CAN message.
+ * @param[in,out] can A pointer to the CAN device structure.
+ * @param[out] buf A pointer to the buffer to store the received `rt_can_msg`.
+ * @param[in] fifo The hardware FIFO number to read from.
+ * @return The number of bytes received on success, or a negative error code on failure.
+ */
+ rt_ssize_t (*recvmsg)(struct rt_can_device *can, void *buf, rt_uint32_t fifo);
+ /**
+ * @brief Low-level, hardware-specific function to send a CAN message non-blockingly.
+ *
+ * This function attempts to place a message into a hardware transmission mailbox
+ * and returns immediately without waiting 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.
+ * - Other negative error codes for different failures.
+ */
+ rt_ssize_t (*sendmsg_nonblocking)(struct rt_can_device *can, const void *buf);
};
/**
- * @brief Register a CAN device to device list
+ * @brief This function registers a CAN device with the device framework.
*
- * @param can the CAN device object
- * @param name the name of CAN device
- * @param ops the CAN device operators
- * @param data the private data of CAN device
+ * @param[in] can A pointer to the CAN device object to be registered.
+ * @param[in] name The name that the device will be registered with.
+ * @param[in] ops A pointer to the structure containing the low-level CAN driver operations.
+ * @param[in] data A pointer to a user-defined data structure, which can be accessed
+ * via `can->parent.user_data`.
*
- * @return the error code, RT_EOK on successfully
+ * @return `RT_EOK` on successful registration, or a negative error code on failure.
*/
rt_err_t rt_hw_can_register(struct rt_can_device *can,
const char *name,
@@ -534,10 +691,16 @@ rt_err_t rt_hw_can_register(struct rt_can_device *can,
void *data);
/**
- * @brief CAN interrupt service routine
+ * @brief The framework-level ISR handler for CAN devices.
+ *
+ * This function is called by the low-level BSP ISR and acts as the central
+ * dispatcher for all CAN-related interrupt events. It handles both receive
+ * events and transmission-complete events.
+ *
+ * @param[in] can A pointer to the CAN device structure.
+ * @param[in] event The interrupt event mask, indicating the cause of the interrupt.
*
- * @param can the CAN device
- * @param event the event mask
+ * @return void
*/
void rt_hw_can_isr(struct rt_can_device *can, int event);