From 870f34160906ef0576583b24dab5e85ede360002 Mon Sep 17 00:00:00 2001 From: wdfk-prog <1425075683@qq.com> Date: Sat, 27 Sep 2025 09:42:02 +0800 Subject: [PATCH] feat[can]: Implement non-blocking send mechanism and enhance CAN driver functionality - Added support for non-blocking mode CAN message sending, including software ring buffer management and dynamic memory allocation options. - Improved related comments and error handling. - Updated example code to demonstrate the usage of both blocking and non-blocking sending modes, and corrected some structure field naming and macro definitions. --- .../libraries/HAL_Drivers/drivers/drv_can.c | 90 ++- components/drivers/can/Kconfig | 69 ++- components/drivers/can/dev_can.c | 335 +++++++++-- components/drivers/include/drivers/dev_can.h | 547 ++++++++++++------ 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 da748590b08..196cc94a529 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 c762a46d6b1..ccb4348db28 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 8f70a1ddfb1..73aee2f0943 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 aubrcool@qq.com 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 @@ -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<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<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 @@ rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *da 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<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 81ea0927edb..922ddcbb539 100644 --- a/components/drivers/include/drivers/dev_can.h +++ b/components/drivers/include/drivers/dev_can.h @@ -8,12 +8,14 @@ * 2015-05-14 aubrcool@qq.com 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 +#include #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 * - * Example + * Example: Demonstrating CAN RX, Filters, and Blocking/Non-Blocking TX * @code {.c} * #include - * #include "rtdevice.h" + * #include * - * #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);