Skip to content

Commit d1c21da

Browse files
committed
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.
1 parent 42458e0 commit d1c21da

File tree

4 files changed

+548
-121
lines changed

4 files changed

+548
-121
lines changed

bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2006-2024 RT-Thread Development Team
2+
* Copyright (c) 2006-2025, RT-Thread Development Team
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*
@@ -14,6 +14,7 @@
1414
* 2021-02-02 YuZhe XU fix bug in filter config
1515
* 2021-8-25 SVCHAO The baud rate is configured according to the different APB1 frequencies.
1616
f4-series only.
17+
* 2025-09-20 wdfk_prog Implemented sendmsg_nonblocking op to support framework's async TX.
1718
*/
1819

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

488+
/**
489+
* @internal
490+
* @brief Low-level function to send a CAN message to a specific hardware mailbox.
491+
*
492+
* This function is part of the **blocking** send mechanism. It is called by
493+
* `_can_int_tx` after a hardware mailbox has already been acquired. Its role is
494+
* to format the message according to the STM32 hardware requirements and place
495+
* it into the specified mailbox for transmission.
496+
*
497+
* @param can A pointer to the CAN device structure.
498+
* @param buf A pointer to the `rt_can_msg` to be sent.
499+
* @param box_num The specific hardware mailbox index (0, 1, or 2) to use for this transmission.
500+
*
501+
* @return `RT_EOK` on success, or an error code on failure.
502+
*/
487503
static int _can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t box_num)
488504
{
489505
CAN_HandleTypeDef *hcan;
@@ -586,6 +602,53 @@ static int _can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t
586602
}
587603
}
588604

605+
/**
606+
* @internal
607+
* @brief Low-level, hardware-specific non-blocking function to send a CAN message.
608+
*
609+
* This function interacts directly with the STM32 HAL library to add a message
610+
* to a hardware TX mailbox. It returns immediately and does not wait for the
611+
* transmission to complete.
612+
*
613+
* @param can A pointer to the CAN device structure.
614+
* @param buf A pointer to the `rt_can_msg` to be sent.
615+
*
616+
* @return
617+
* - `RT_EOK` if the message was successfully accepted by the hardware.
618+
* - `-RT_EBUSY` if all hardware mailboxes are currently full.
619+
* - `-RT_ERROR` on other HAL failures.
620+
*/
621+
static rt_ssize_t _can_sendmsg_nonblocking(struct rt_can_device *can, const void *buf)
622+
{
623+
CAN_HandleTypeDef *hcan = &((struct stm32_can *) can->parent.user_data)->CanHandle;
624+
struct rt_can_msg *pmsg = (struct rt_can_msg *) buf;
625+
CAN_TxHeaderTypeDef txheader = {0};
626+
uint32_t tx_mailbox;
627+
628+
if ((hcan->State != HAL_CAN_STATE_READY) && (hcan->State != HAL_CAN_STATE_LISTENING))
629+
return -RT_ERROR;
630+
631+
if (HAL_CAN_GetTxMailboxesFreeLevel(hcan) == 0)
632+
return -RT_EBUSY;
633+
634+
txheader.DLC = pmsg->len;
635+
txheader.RTR = (pmsg->rtr == RT_CAN_RTR) ? CAN_RTR_REMOTE : CAN_RTR_DATA;
636+
txheader.IDE = (pmsg->ide == RT_CAN_STDID) ? CAN_ID_STD : CAN_ID_EXT;
637+
if (txheader.IDE == CAN_ID_STD)
638+
txheader.StdId = pmsg->id;
639+
else
640+
txheader.ExtId = pmsg->id;
641+
642+
HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(hcan, &txheader, pmsg->data, &tx_mailbox);
643+
if (status != HAL_OK)
644+
{
645+
LOG_W("can sendmsg nonblocking send error %d", status);
646+
return -RT_ERROR;
647+
}
648+
649+
return RT_EOK;
650+
}
651+
589652
static int _can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t fifo)
590653
{
591654
HAL_StatusTypeDef status;
@@ -645,10 +708,11 @@ static int _can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t fifo)
645708

646709
static const struct rt_can_ops _can_ops =
647710
{
648-
_can_config,
649-
_can_control,
650-
_can_sendmsg,
651-
_can_recvmsg,
711+
.configure = _can_config,
712+
.control = _can_control,
713+
.sendmsg = _can_sendmsg,
714+
.recvmsg = _can_recvmsg,
715+
.sendmsg_nonblocking = _can_sendmsg_nonblocking,
652716
};
653717

654718
static void _can_rx_isr(struct rt_can_device *can, rt_uint32_t fifo)
@@ -788,6 +852,21 @@ static void _can_sce_isr(struct rt_can_device *can)
788852
hcan->Instance->MSR |= CAN_MSR_ERRI;
789853
}
790854

855+
/**
856+
* @internal
857+
* @brief The low-level ISR for CAN TX events on STM32.
858+
*
859+
* This function's sole responsibility is to check the hardware status flags
860+
* to determine which mailbox completed a transmission and whether it was
861+
* successful or failed. It then reports the specific event to the upper
862+
* framework layer via `rt_hw_can_isr()`.
863+
*
864+
* @note This ISR contains NO framework-level logic (e.g., buffer handling).
865+
* It is a pure hardware event reporter, ensuring a clean separation
866+
* of concerns between the driver and the framework.
867+
*
868+
* @param can A pointer to the CAN device structure.
869+
*/
791870
static void _can_tx_isr(struct rt_can_device *can)
792871
{
793872
CAN_HandleTypeDef *hcan;

components/drivers/can/Kconfig

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,76 @@
11
config RT_USING_CAN
22
bool "Using CAN device drivers"
33
default n
4+
help
5+
Enable this option to include the CAN device driver framework.
46

57
if RT_USING_CAN
8+
69
config RT_CAN_USING_HDR
7-
bool "Enable CAN hardware filter"
10+
bool "Enable CAN hardware filter support"
811
default n
9-
12+
help
13+
If your CAN controller supports hardware filtering, and you want to
14+
use the framework to configure these filters, enable this option.
15+
1016
config RT_CAN_USING_CANFD
11-
bool "Enable CANFD support"
17+
bool "Enable CAN-FD support"
1218
default n
13-
19+
help
20+
Enable this to support CAN with Flexible Data-Rate. This will
21+
increase the size of the rt_can_msg structure.
22+
1423
config RT_CANMSG_BOX_SZ
15-
int "CAN message box size"
24+
int "Software RX message box size (in messages)"
1625
default 16
1726
help
18-
Set the size of the CAN message box.
19-
27+
This sets the capacity of the software buffer (FIFO) for incoming
28+
CAN messages. It defines how many messages can be buffered by the
29+
driver before the application must read them.
30+
2031
config RT_CANSND_BOX_NUM
21-
int "Number of CAN send queues"
32+
int "Number of mailboxes for blocking send"
2233
default 1
2334
help
24-
Set the number of CAN send queues.
25-
35+
This sets the number of concurrent blocking send operations that
36+
can be in flight. It is typically matched to the number of
37+
hardware transmission mailboxes on the CAN controller.
38+
2639
config RT_CANSND_MSG_TIMEOUT
27-
int "CAN send message timeout"
40+
int "Timeout for blocking send (in OS ticks)"
2841
default 100
2942
help
30-
Set the timeout for CAN send messages.
43+
This sets the default time a thread will wait for a hardware
44+
mailbox to become available when sending in blocking mode.
45+
46+
config RT_CAN_NB_TX_FIFO_SIZE
47+
int "Non-blocking send buffer size (in bytes)"
48+
default 256
49+
help
50+
This defines the size of the software ring buffer used for
51+
non-blocking and ISR-based transmissions. When the hardware
52+
mailboxes are full, outgoing messages are temporarily stored
53+
in this buffer.
54+
55+
To calculate a suitable size, use:
56+
(number of messages to buffer) * sizeof(struct rt_can_msg).
57+
For standard CAN, sizeof(struct rt_can_msg) is typically 16 bytes.
58+
The default of 256 bytes can buffer 16 standard CAN messages.
59+
If using CAN-FD, you will need to increase this size significantly.
60+
61+
config RT_CAN_MALLOC_NB_TX_BUFFER
62+
bool "Dynamically allocate the non-blocking send buffer"
63+
depends on RT_USING_HEAP
64+
default n
65+
help
66+
If this option is enabled (y), the non-blocking send buffer will
67+
be allocated from the system heap at runtime when the CAN device
68+
is opened (using rt_malloc). This saves static RAM but requires
69+
a heap to be available.
70+
71+
If this option is disabled (n), the buffer will be allocated
72+
as a static array within the rt_can_device structure. This
73+
consumes static RAM but guarantees the memory is always available
74+
and avoids heap fragmentation.
75+
3176
endif

0 commit comments

Comments
 (0)