前言
(1)如果有嵌入式企业需要招聘湖南区域日常实习生,任何区域的暑假Linux驱动实习岗位,可C站直接私聊,或者邮件:zhangyixu02@gmail.com,此消息至2025年1月1日前均有效
(2)学习本文之前,建议先在裸机上实现CAN通讯,这样测试一下通讯是否硬件是否正常。
前期准备
创建RT-Thread标准版工程
(1)打开项目资源管理器。
(2)在项目资源管理器中右键 —> 新建 —> 项目。
(3)选择
RT-Thread
项目 —> 下一步。
(4)选择所需要的芯片型号
(5)选择这个项目,进行编译。
进行STM32CubeMX适配
(1)打开
STM32CubeMX
。
(2)因为我是
STlink
下载器,所以进行如下配置
(3)我开发板上是使用的外部无源晶振(陶瓷晶振),因此
RCC
的外部高速时钟配置为Crystal/Ceramic Resonator
。
(4)因为
RT-Thread
的FinSH
是默认使用的串口1,因此,我们需要打开串口1。
(5)官方推荐
STM32F103
系统主频为72MHZ
,因此这里设置为72MHZ
。
(6)对外设初始化的文件单独生成
.c/.h
文件,最终生成代码,并且关闭STM32CubeMX
。
(7)打开RT-Thread studio,出现如下弹框,点击确定。
注意:上面的STM32CubeMX
必须关闭,否则这个弹窗将不会出现。
(8)此时编译烧录即可
(9)此时上机打开串口工具测试,波特率115200。即可看到如下打印信息。
(10)在某些教程中,可能会需要你找到
cubemx/Src/main.c
,将生成的SystemClock_Config()
函数复制到drivers/drv_clk.c
中。但是我当前版本的RT-Thread studio
成功的避免了这个问题,工程中存在两个main.c
也可以正常编译,cubemx/Src/main.c
中的main()
函数也被__WEAK
因此不会产生冲突。
增加板级LED支持
(1)因为我开发板上有两个LED,一个是PB5引脚,一个是PE5引脚。为了知道程序在正常运行,同时打印信息太多影响FinSH使用,因此我加上LED闪烁程序,把
Hello RT-Thread!
打印信息删除。
/** Copyright (c) 2006-2024, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2024-02-21 RT-Thread first version*/#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <rtdevice.h>
#include <drv_common.h>#define LED1_PIN GET_PIN(B, 5)
#define LED2_PIN GET_PIN(E, 5)int main(void)
{int count = 1;rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);rt_pin_mode(LED2_PIN, PIN_MODE_OUTPUT);while (count++){rt_pin_write(LED1_PIN, PIN_LOW);rt_pin_write(LED2_PIN, PIN_HIGH);rt_thread_mdelay(1000);rt_pin_write(LED1_PIN, PIN_HIGH);rt_pin_write(LED2_PIN, PIN_LOW);rt_thread_mdelay(1000);}return RT_EOK;
}
适配CAN通讯功能
(1)通过上面的步骤,我们已经成功的创建了一个基础模板工程了。现在再增加CAN通讯的驱动部分。
增加CAN驱动
使能CAN驱动
(1)按照如下步骤使能
RT-Thread
中的CAN
驱动。
(2)配置完成之后,按
Ctrl+S
保存,出现一下弹框,等待完成。
(3)进入
board.h
文件,添加如下代码。
/*-------------------------- CAN CONFIG BEGIN --------------------------*/#define BSP_USING_CAN
#define BSP_USING_CAN1
/*#define BSP_USING_CAN2*//*-------------------------- CAN CONFIG END --------------------------*/
STM32CubeMX使能CAN设备
(1)双击打开
STM32CubeMX
,按照下图使能CAN
设备。
切记,配置完STM32CubeMX
之后,一定一定记得将STM32CubeMX
关闭!
(2)找到
cubemx/Src/can.c
文件,按照如下步骤添加构建。
<1>
<2>
<3>
增加CAN驱动代码
(1)此时我们需要找到
RT-Thread
官方的驱动代码。首先按照如下方式找到RT-Thread Studio
的安装路径。
(2)例如,现在我们
RT-Thread Studio
的安装路径为D:\RT-Thread_Studio\soft\RT-ThreadStudio
,那么我们只需要再这个路径后面加上\repo\Extract\RT-Thread_Source_Code\RT-Thread\4.0.3\bsp\stm32\libraries\HAL_Drivers
。在这个路径中找到drv_can.c
文件,然后复制到当前的工程drivers
目录下。
(3)然后再在
\repo\Extract\RT-Thread_Source_Code\RT-Thread\4.0.3\bsp\stm32\libraries\HAL_Drivers
路径中找到drv_can.h
文件,然后复制到当前的工程drivers\include
目录下。
(4)此时进入
RT-Thread studio
,点击当前工程,右键,选择刷新,即可出现刚刚复制过来的两个驱动文件。
上机实操
(1)编译烧录,上机测试。输入
list_device
命令,出现can1
设备。表面CAN
驱动已经成功编译进入。
增加CAN命令使用例程
(1)通过上面的步骤,我们成功将
CAN
驱动编译进入了RT-Thread
中。但是还没有完成真正意义上的CAN
设备使用。此时我们可以增加一个CAN
设备使用命令,用于测试。
添加CAN命令测试代码
(1)我们先在
applications
目录下增加一个can_test.c
的文件
<1>
<2>
(2)将如下代码编写进入
can_test.c
文件中。
/** 程序清单:这是一个 CAN 设备使用例程* 例程导出了 can_test 命令到控制终端* 命令调用格式:can_test can1* 命令解释:命令第二个参数是要使用的 CAN 设备名称,为空则使用默认的 CAN 设备* 程序功能:通过 CAN 设备发送一帧,并创建一个线程接收数据然后打印输出。
*/#include <rtthread.h>
#include "rtdevice.h"#define CAN_DEV_NAME "can1" /* CAN 设备名称 */static struct rt_semaphore rx_sem; /* 用于接收消息的信号量 */
static rt_device_t can_dev; /* CAN 设备句柄 *//* 接收数据回调函数 */
static rt_err_t can_rx_call(rt_device_t dev, rt_size_t size)
{/* CAN 接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */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};/* 设置接收回调函数 */rt_device_set_rx_indicate(can_dev, can_rx_call);#ifdef RT_CAN_USING_HDRstruct rt_can_filter_item items[5] ={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 号过滤表 */};struct rt_can_filter_config cfg = {5, 1, items}; /* 一共有 5 个过滤表 *//* 设置硬件过滤表 */res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg);RT_ASSERT(res == RT_EOK);
#endifwhile (1){/* hdr 值为 - 1,表示直接从 uselist 链表读取数据 */rxmsg.hdr = -1;/* 阻塞等待接收信号量 */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++){rt_kprintf("%2x", rxmsg.data[i]);}rt_kprintf("\n");}
}int can_test(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];if (argc == 2){rt_strncpy(can_name, argv[1], RT_NAME_MAX);}else{rt_strncpy(can_name, CAN_DEV_NAME, RT_NAME_MAX);}/* 查找 CAN 设备 */can_dev = rt_device_find(can_name);if (!can_dev){rt_kprintf("find %s failed!\n", can_name);return RT_ERROR;}/* 初始化 CAN 接收信号量 */rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);/* 以中断接收及中断发送方式打开 CAN 设备 */res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);RT_ASSERT(res == RT_EOK);/* 设置 CAN 通信的波特率为 500kbit/s*/res = rt_device_control(can_dev, RT_CAN_CMD_SET_BAUD, (void *)CAN500kBaud);/* 创建数据接收线程 */thread = rt_thread_create("can_rx", can_rx_thread, RT_NULL, 1024, 25, 10);if (thread != RT_NULL){rt_thread_startup(thread);}else{rt_kprintf("create can_rx thread failed!\n");}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 dev write data failed!\n");}// 更改后再发送十次for(rt_uint8_t send_ind = 0; send_ind < 10; send_ind++){rt_thread_mdelay(1000);msg.data[0] = msg.data[0] + 0x01;msg.data[1] = msg.data[1] + 0x01;msg.data[2] = msg.data[2] + 0x01;msg.data[3] = msg.data[3] + 0x01;msg.data[4] = msg.data[4] + 0x01;msg.data[5] = msg.data[5] + 0x01;msg.data[6] = msg.data[6] + 0x01;msg.data[7] = msg.data[7] + 0x01;/* 发送一帧 CAN 数据 */size = rt_device_write(can_dev, 0, &msg, sizeof(msg));if (size == 0){rt_kprintf("can dev write data failed!\n");}}return res;
}/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(can_test, can device sample);
上机测试
(1)打开CAN分析仪,将波特率设置为
500Kbps
。
(2)输入
can_test
命令,CAN
分析仪上出现11次数据打印。
可能会遇到的问题
To initialize device:can1 failed
(1)在测试过程中,有可能会出现如下问题。
(2)处理办法很简单,将
cubemx/Src/can.c
加入构建。
can dev write data failed!
(1)如果测试出现这个错误,就说明你的
CAN
分析仪没有打开,或者是CAN
分析仪波特率设置错误。
(2)处理办法很简单,打开
CAN
分析仪,设置正确的波特率,然后板子复位即可。
assertion failed at function:rt_object_init
(1)注意,每次
can_test
命令只能执行一次,因为第二次执行can_test
命令就会出现这个报错,之后程序卡死。
(2)处理办法很简单,板子重新复位启动。
参考
(1)C站:RT-Thread系列08——CAN设备(CAN收发)
(2)C站:RT-Thread studio 添加CAN通信功能
(3)C站:STM32CubeMX创建CAN通讯工程+图莫斯UTA0403使用
(4)RT-Thread官方文档:CAN 设备
(5)C站:RT-Thread studio创建一个STM32F103ZE的RT-Thread标准版模板工程