CAN-FD与UART
文档版本:V1.0 | 适用系统:RT-Thread | 适用硬件:C-3576BQ V10 核心板 + M4-R2(ShimetaPi)底板 | 更新日期:2026-10-23
1. 概述
本文档详细说明 M4-R2(RK3576)平台上 RT-Thread 操作系统下 CAN-FD 和 UART 外设驱动的开发指南。
文档范围:
- CAN-FD 控制器:支持经典 CAN 和 CAN FD(Flexible Data-rate)双模式
- UART 控制器:16550A 兼容(参考),支持中断和 DMA 模式
适用硬件:
- 核心板:C-3576BQ V10(RK3576)
- 底板:M4-R2(ShimetaPi)
技术参数(参考说明,RK3576 具体能力以实际发布的 SDK/TRM 为准,待板上确认):
- CAN FD 数据速率:最高 8Mbps,最大 64 字节负载
- 经典 CAN 数据速率:最高 1Mbps,8 字节负载
- UART 波特率:上限以实际 RK3576 SDK 为准
参考文档:
- Rockchip_Developer_Guide_RT-Thread_CAN_CANFD_CN.pdf(V0.1.0, 2024-09-09)
- Rockchip_Developer_Guide_RT-Thread_UART.pdf(V1.2.0, 2024-08-15)
2. 硬件准备
2.1 CAN-FD 硬件要求
| 组件 | 规格 | 说明 |
|---|---|---|
| RK3576 SoC | 集成 CAN-FD 控制器 | 支持经典 CAN 和 CAN FD(以实际 SDK 为准) |
| CAN 收发器 | TJA1051 或同等 | 用于电平转换 |
| 终端电阻 | 120Ω | 总线两端各一个 |
| 连接线 | 双绞线 | CAN_H, CAN_L |
2.2 UART 硬件要求
| 组件 | 规格 | 说明 |
|---|---|---|
| RK3576 UART | 16550A 兼容(参考) | 具体波特率上限以实际 SDK 为准 |
| 电平转换 | RS232/RS485 | 根据外部设备选择 |
| 连接线 | TX, RX, GND | 基本三线连接 |
2.3 引脚配置
参考说明:以下引脚以实际发布的 RK3576 SDK/数据手册和底板原理图为准(待板上确认),不沿用 RK3588 参考引脚。
CAN-FD 引脚(M4-R2 需以实际原理图为准):
- CAN0:待确认(以实际 RK3576 SDK 与底板原理图为准)
- CAN1:待确认(以实际 RK3576 SDK 与底板原理图为准)
UART 引脚(M4-R2 需以实际原理图为准):
- 调试串口:底板 DEBUG 串口,网络标注
UART0_TX_M0_DEBUG(GPIO0_D4_D)/UART0_RX_M0_DEBUG(GPIO0_D5_D),3.3V 电平,波特率 1500000 - 其余 UART 引脚号:以实际 RK3576 SDK 与底板原理图为准
3. 软件准备
3.1 开发环境
- RT-Thread Studio 或 IAR/Keil MDK
- RT-Thread 内核版本:4.1.0+
- HAL 驱动包:RK3576 AMP SDK
3.2 驱动文件位置
CAN-FD 驱动:
- HAL 层:
components/drivers/can/hal_canfd.c - RT-Thread 接口:
rockchip_canfd_dev_init() - 位置:AMP SDK 的 HAL 层
UART 驱动:
- 核心层:
components/drivers/serial/serial.c - 串口核心:
serial_core.c - 位置:RT-Thread 标准驱动框架
3.3 依赖项
RT-Thread 内核
├── 设备框架 (rt_device)
├── CAN 设备驱动
│ ├── hal_canfd.c
│ └── rockchip_canfd_dev_init()
└── UART 设备驱动
├── serial.c
└── serial_core.c4. 操作步骤
4.1 CAN-FD 驱动初始化流程
配置引脚复用
- 调用
HAL_PINCTRL_SetIOMUX()设置 CAN 引脚 - 配置引脚功能为 CAN TX/RX
- 调用
初始化 CAN 控制器
- 调用
rockchip_canfd_dev_init()初始化 HAL 驱动 - 配置波特率、工作模式
- 调用
注册 RT-Thread 设备
- 设备自动注册为 "can0", "can1" 等
- 使用标准 RT-Thread 设备 API 访问
配置 CAN 过滤器
- 设置消息 ID 过滤规则
- 配置掩码和过滤模式
4.2 UART 驱动初始化流程
配置引脚复用
- 调用
HAL_PINCTRL_SetIOMUX()设置 UART 引脚 - 配置 TX/RX/CTS/RTS 引脚组
- 调用
配置引脚电气属性
- 设置上拉/下拉电阻
- 配置驱动强度
初始化 UART 控制器
- 16550A 控制器自动初始化(参考)
- 配置波特率、数据位、停止位、校验位
注册 RT-Thread 设备
- 设备自动注册为 "uart0", "uart1" 等
- 使用标准 RT-Thread 设备 API 访问
5. 代码详解
5.1 CAN-FD 驱动示例
5.1.1 CAN 初始化代码
#include <rtdevice.h>
#include <rtthread.h>
#define CAN_DEV_NAME "can0" // CAN 设备名称
static struct rt_can_msg rx_msg;
void can_init_example(void)
{
struct rt_device *can_dev;
struct rt_can_msg msg;
rt_err_t ret;
/* 查找 CAN 设备 */
can_dev = rt_device_find(CAN_DEV_NAME);
if (can_dev == RT_NULL)
{
rt_kprintf("find %s failed!\n", CAN_DEV_NAME);
return;
}
/* 打开 CAN 设备 */
ret = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
if (ret != RT_EOK)
{
rt_kprintf("open %s failed! ret=%d\n", CAN_DEV_NAME, ret);
return;
}
/* 配置 CAN 波特率(经典 CAN: 125K/250K/500K/1M, CAN FD: 参考最高 8M) */
rt_device_control(can_dev, RT_CAN_CMD_SET_BAUD, (void *)1000000); // 1Mbps
/* 配置 CAN 模式 (0: 经典 CAN, 1: CAN FD) */
rt_device_control(can_dev, RT_CAN_CMD_SET_MODE, (void *)1); // CAN FD 模式
rt_kprintf("CAN device %s initialized successfully\n", CAN_DEV_NAME);
}5.1.2 CAN 发送代码
/* CAN 发送示例 */
rt_err_t can_send_example(struct rt_device *can_dev)
{
struct rt_can_msg msg;
rt_err_t ret;
/* 配置发送消息 */
msg.id = 0x123; // 消息 ID
msg.ide = RT_CAN_STDID; // 标准帧 (RT_CAN_EXTID 为扩展帧)
msg.rtr = RT_CAN_DTR; // 数据帧 (RT_CAN_RTR 为远程帧)
msg.len = 8; // 数据长度 (经典 CAN 最大 8 字节,CAN FD 最大 64 字节)
/* 填充数据 */
msg.data[0] = 0x11;
msg.data[1] = 0x22;
msg.data[2] = 0x33;
msg.data[3] = 0x44;
msg.data[4] = 0x55;
msg.data[5] = 0x66;
msg.data[6] = 0x77;
msg.data[7] = 0x88;
/* 发送 CAN 消息 */
ret = rt_device_write(can_dev, 0, &msg, sizeof(msg));
if (ret == sizeof(msg))
{
rt_kprintf("CAN message sent successfully, ID=0x%03X\n", msg.id);
return RT_EOK;
}
else
{
rt_kprintf("CAN message send failed, ret=%d\n", ret);
return -RT_ERROR;
}
}5.1.3 CAN 接收代码
/* CAN 接收回调函数 */
static rt_err_t can_rx_indicate(rt_device_t dev, rt_size_t size)
{
struct rt_can_msg rx_msg;
if (size == sizeof(struct rt_can_msg))
{
/* 读取接收到的消息 */
rt_device_read(dev, -1, &rx_msg, sizeof(rx_msg));
rt_kprintf("CAN RX: ID=0x%03X, DLC=%d, Data:",
rx_msg.id, rx_msg.len);
for (int i = 0; i < rx_msg.len; i++)
{
rt_kprintf(" %02X", rx_msg.data[i]);
}
rt_kprintf("\n");
}
return RT_EOK;
}
/* CAN 接收线程 */
void can_rx_thread_entry(void *parameter)
{
struct rt_device *can_dev;
struct rt_can_msg rx_msg;
rt_err_t ret;
/* 查找并打开 CAN 设备 */
can_dev = rt_device_find(CAN_DEV_NAME);
if (can_dev == RT_NULL)
{
rt_kprintf("find %s failed!\n", CAN_DEV_NAME);
return;
}
ret = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_RX);
if (ret != RT_EOK)
{
rt_kprintf("open %s failed! ret=%d\n", CAN_DEV_NAME, ret);
return;
}
/* 设置接收回调函数 */
rt_device_set_rx_indicate(can_dev, can_rx_indicate);
/* 持续接收消息 */
while (1)
{
/* 阻塞等待接收 */
ret = rt_device_read(can_dev, -1, &rx_msg, sizeof(rx_msg));
if (ret == sizeof(rx_msg))
{
/* 处理接收到的消息 */
rt_kprintf("Received CAN message: ID=0x%03X\n", rx_msg.id);
}
rt_thread_mdelay(10);
}
}5.2 UART 驱动示例
5.2.1 UART 初始化代码
#include <rtdevice.h>
#include <rtthread.h>
#define UART_DEV_NAME "uartx" // UART 设备名称(以实际 BSP 注册名为准)
void uart_init_example(void)
{
struct rt_device *uart_dev;
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
rt_err_t ret;
/* 查找 UART 设备 */
uart_dev = rt_device_find(UART_DEV_NAME);
if (uart_dev == RT_NULL)
{
rt_kprintf("find %s failed!\n", UART_DEV_NAME);
return;
}
/* 打开 UART 设备 */
ret = rt_device_open(uart_dev, RT_DEVICE_FLAG_INT_RX);
if (ret != RT_EOK)
{
rt_kprintf("open %s failed! ret=%d\n", UART_DEV_NAME, ret);
return;
}
/* 配置 UART 参数(波特率上限以实际 RK3576 SDK 为准) */
config.baud_rate = BAUD_RATE_115200; // 波特率
config.data_bits = DATA_BITS_8; // 数据位: 8
config.stop_bits = STOP_BITS_1; // 停止位: 1
config.parity = PARITY_NONE; // 校验: 无
/* 应用配置 */
ret = rt_device_control(uart_dev, RT_DEVICE_CTRL_CONFIG, &config);
if (ret != RT_EOK)
{
rt_kprintf("config %s failed! ret=%d\n", UART_DEV_NAME, ret);
return;
}
rt_kprintf("UART device %s initialized successfully\n", UART_DEV_NAME);
}5.2.2 UART 发送代码
/* UART 发送示例 */
rt_err_t uart_send_example(struct rt_device *uart_dev, const char *str)
{
rt_size_t len;
rt_err_t ret;
/* 发送字符串 */
len = rt_device_write(uart_dev, 0, str, strlen(str));
if (len == strlen(str))
{
rt_kprintf("UART send success: %s\n", str);
return RT_EOK;
}
else
{
rt_kprintf("UART send failed, sent %d bytes\n", len);
return -RT_ERROR;
}
}
/* UART 发送二进制数据 */
rt_err_t uart_send_binary_example(struct rt_device *uart_dev)
{
uint8_t data[] = {0x01, 0x02, 0x03, 0x04, 0x05};
rt_size_t len;
len = rt_device_write(uart_dev, 0, data, sizeof(data));
if (len == sizeof(data))
{
rt_kprintf("UART binary send success, %d bytes\n", len);
return RT_EOK;
}
return -RT_ERROR;
}5.2.3 UART 接收代码
/* UART 接收回调函数 */
static rt_err_t uart_rx_indicate(rt_device_t dev, rt_size_t size)
{
uint8_t ch;
if (size > 0)
{
/* 逐字节读取 */
while (rt_device_read(dev, -1, &ch, 1) == 1)
{
rt_kprintf("UART RX: 0x%02X (%c)\n", ch, ch);
}
}
return RT_EOK;
}
/* UART 接收线程 */
void uart_rx_thread_entry(void *parameter)
{
struct rt_device *uart_dev;
uint8_t ch;
rt_err_t ret;
/* 查找并打开 UART 设备 */
uart_dev = rt_device_find(UART_DEV_NAME);
if (uart_dev == RT_NULL)
{
rt_kprintf("find %s failed!\n", UART_DEV_NAME);
return;
}
ret = rt_device_open(uart_dev, RT_DEVICE_FLAG_INT_RX);
if (ret != RT_EOK)
{
rt_kprintf("open %s failed! ret=%d\n", UART_DEV_NAME, ret);
return;
}
/* 设置接收回调函数 */
rt_device_set_rx_indicate(uart_dev, uart_rx_indicate);
/* 持续接收数据 */
while (1)
{
/* 阻塞等待接收单个字节 */
ret = rt_device_read(uart_dev, -1, &ch, 1);
if (ret == 1)
{
/* 处理接收到的数据 */
rt_kprintf("Received: 0x%02X\n", ch);
}
rt_thread_mdelay(10);
}
}5.2.4 UART 引脚配置示例
#include "hal_pinctrl.h"
void uart_pin_config_example(void)
{
/* 配置调试 UART 引脚(M4-R2 调试串口网络 UART0_M0_DEBUG,引脚号以 RK3576 SDK 为准) */
HAL_PINCTRL_SetIOMUX(GPIO_BANK0,
GPIO_PIN_D4 | GPIO_PIN_D5,
0x2); // UART0_TX_M0_DEBUG, UART0_RX_M0_DEBUG
/* 配置引脚电气属性 */
HAL_PINCTRL_SetPull(GPIO_BANK0,
GPIO_PIN_D4 | GPIO_PIN_D5,
PIN_PULL_UP); // 上拉
HAL_PINCTRL_SetDriveStrength(GPIO_BANK0,
GPIO_PIN_D4 | GPIO_PIN_D5,
DRIVE_STRENGTH_8MA);
}注意:以上引脚号以 S-06(C-3576BQ IO Domains)标注的调试串口网络为基础,具体 IOMUX 取值以 RK3576 SDK 为准。
6. 常见问题
6.1 CAN-FD 相关问题
Q1: CAN 设备找不到(find failed)
- 检查引脚复用配置是否正确
- 确认
rockchip_canfd_dev_init()已被调用 - 检查设备树中 CAN 节点是否启用
Q2: CAN 发送失败
- 检查 CAN 总线是否连接正确
- 确认终端电阻已连接(120Ω)
- 检查波特率配置是否匹配
- 确认 CAN 控制器已初始化
Q3: CAN 接收不到数据
- 检查过滤器配置是否正确
- 确认接收回调函数已设置
- 检查中断是否已使能
Q4: CAN FD 模式无法工作
- 确认对端设备也支持 CAN FD
- 检查波特率配置(数据段和仲裁段)
- 确认控制器支持 CAN FD 模式
6.2 UART 相关问题
Q1: UART 设备找不到
- 检查设备树中 UART 节点是否启用
- 确认 UART 驱动已编译到系统中
- 检查设备名称是否正确("uart0", "uart1" 等)
Q2: UART 数据乱码
- 检查波特率配置是否与对端设备匹配(调试串口固定 1500000)
- 确认数据位、停止位、校验位配置正确
- 检查引脚配置是否正确
- 确认时钟配置正确
Q3: UART 接收数据丢失
- 检查接收缓冲区大小
- 确认接收线程优先级足够高
- 考虑使用 DMA 模式提高吞吐量
Q4: UART 波特率受限
- 具体波特率上限以实际 RK3576 SDK 为准
- 检查时钟源配置
- 确认分频系数计算正确
6.3 通用问题
Q1: 驱动初始化失败
- 检查 HAL 驱动是否正确移植
- 确认 AMP SDK 版本兼容性
- 检查编译选项是否包含必要组件
Q2: 中断不工作
- 检查 NVIC 中断配置
- 确认中断优先级设置正确
- 检查中断向量表是否正确
7. 相关文档
| 文档 | 说明 |
|---|---|
| RT-Thread环境与工程入口 | AMP 架构、SDK 目录、构建流程入口 |
| BSP与启动 | 调试串口配置、menuconfig、finsh shell |
| I2C-SPI-USB | RT-Thread 外设驱动(I2C、SPI、USB) |
| CAN-CAN-FD驱动 | Linux 侧 CAN 驱动(对照参考) |
| UART串口驱动 | Linux 侧 UART 驱动(对照参考) |
| 核心板参数 | 硬件事实基线(调试串口、接口能力) |
| 底板接口定义 | 核心板接口能力(最多 7 路 UART) |
