当前位置: 首页 > news >正文

基于RT-Thread的STM32开发第十讲——CAN通讯

基于RT-Thread的STM32开发第十讲——CAN通讯

文章目录

    • 基于RT-Thread的STM32开发第十讲——CAN通讯
  • 前言
  • 一、工程创建
  • 二、代码开发
    • 1.user_can.c
    • 2.user_can.h
    • 3.main.c
  • 三、效果展示


前言

本章是基于RT-Thread studio实现CAN通讯,使用的MCU是STM32F103C8T6,使用的RT-Thread驱动是5.1.0。
首先还是需要得到5.1.0的不报错工程,这部分有疑问可以去看我的往期文章,也可以直接使用4系列的稳定驱动。关于CAN的介绍的配置可以看这篇文章CAN的构成介绍及CubeMX配置说明
强烈建议先看这篇文章在看本文,看完这篇文章后对本文很多配置就非常容易理解


一、工程创建

STM32F103C8T6是有一对CAN通讯的引脚,但是创建的工程不管是驱动5还是驱动4都没有把CAN的配置文件添加进去,我又试了一些F4系类的,发现能够自动添加,看样子像是疏漏了。所以我们需要自行添加。
在RT-ThreadStudio的安装目录找到HAL_Drivers文件夹,类似地址如下
F:\RT-ThreadStudio\repo\Extract\RT-Thread_Source_Code\RT-Thread\5.1.0\bsp\stm32\libraries\HAL_Drivers
其中5.1.0可换成你用的版本,在HAL_Drivers文件夹下包含了所以驱动文件,我们把drv_can.cdrv_can.h复制到自己创建的工程内,
drv_can.c放在工程drivers文件夹下,drv_can.h放在工程drivers\include文件夹下,然后重新构建项目。CAN的驱动文件就添加进来了。
在这里插入图片描述
同样,在board.c中也没有CAN的宏定义,也许自行添加,如图
在这里插入图片描述
然后去setting中开启CAN的使能
在这里插入图片描述
CANFD是传统CAN总线协议的扩展版本,主要改进在于:
更高传输速率更高:仲裁段(控制信号)保持传统 CAN 的速率(最高 1Mbps),但数据段速率可大幅提升(最高 8Mbps 甚至更高)
数据帧更长:传统 CAN 数据长度最大 8 字节,而 CANFD 可支持最大 64 字节
无特殊说明,一半不开启
接着去CubeMX配置好CAN的功能,并将void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)复制到board.c下方
在这里插入图片描述
到这CAN的工程就配置完成了,下面就可以写通讯函数了

二、代码开发

因为CAN的数据包构成比较复杂,比其他一般外设配置复杂,强烈建议仔细阅读官方文档的CAN设备讲解。

1.user_can.c

#include "ads_can.h"
#define DBG_TAG "ads_can"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>#define     CAN_name    "can1"static rt_device_t can_dev;
static rt_sem_t can_sem;static void can_rx_entry(void *parameter);
int can_init(void)
{can_dev = rt_device_find(CAN_name);if(can_dev == RT_NULL){LOG_D("failed to fine %s device\n",CAN_name);return -1;}can_sem = rt_sem_create("can_sem", 0, RT_IPC_FLAG_FIFO);if(can_sem == RT_NULL){LOG_D("failed to create can sem\n");return -1;}rt_err_t can_dev_flag;can_dev_flag = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX);RT_ASSERT(can_dev_flag == RT_EOK);rt_thread_t can_rx_thread;can_rx_thread = rt_thread_create("can_rx", can_rx_entry, RT_NULL, 1024, 20, 1);if(can_rx_thread != RT_NULL){rt_thread_startup(can_rx_thread);}return 0;
}
rt_err_t can_rx_call(rt_device_t dev,rt_size_t size)
{rt_sem_release(can_sem);return 0;
}
static void can_rx_entry(void *parameter)
{struct rt_can_msg rxmsg = {0};rt_device_set_rx_indicate(can_dev,can_rx_call);
#ifdef RT_CAN_USING_HDRrt_device_control(can_dev, RT_CAN_CMD_SET_BAUD,(void *)CAN500kBaud);//波特率设置为500Krt_device_control(can_dev, RT_CAN_CMD_SET_MODE,(void *)RT_CAN_MODE_LOOPBACKANLISTEN);//设置为环回静默模式struct rt_can_filter_item items[1] ={RT_CAN_FILTER_ITEM_INIT(0x00f8,0,0,0,0x0030,RT_NULL,RT_NULL),};struct rt_can_filter_config cfg = {1,1,items};rt_err_t res;res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg);RT_ASSERT(res == RT_EOK);
#endifwhile(1){rxmsg.hdr_index = -1;rt_sem_take(can_sem, RT_WAITING_FOREVER);rt_thread_mdelay(10);rt_device_read(can_dev, 0, &rxmsg, sizeof(rxmsg));rt_kprintf("ID:%x\n",rxmsg.id);for (int var = 0; var < 8; ++var) {rt_kprintf("%x ",rxmsg.data[var]);}rt_kprintf("\n");}
}
void can_sample(int argc,char **argv)
{struct rt_can_msg txmsg = {0};txmsg.id = strtol(argv[1], 0, 16);txmsg.ide = 0;txmsg.rtr = 0;txmsg.len = 4;txmsg.data[0] = 0x01;txmsg.data[1] = 0x02;txmsg.data[2] = 0x04;txmsg.data[3] = 0x08;int size;size = rt_device_write(can_dev, 0, &txmsg, sizeof(txmsg));if(size == sizeof(txmsg)){rt_kprintf("can dev write data success\n");}
}
MSH_CMD_EXPORT(can_sample,can device sample);

int can_init(void)
里面创建了一个线程、一个信号量。找到CAN设备再打开CAN设备,开启接收中断的发送中断。信号量用于CAN设备收到了一个数据进来提醒线程进行数据解析的作用。

static void can_rx_entry(void *parameter)
这是线程的入口函数,在里面设置了CAN接收回调函数,一旦有信息进来,则进入回调函数,类似中断,在回调函数来释放信号量,从而使其他线程能够获得信号量。
然后设置了CAN通讯的波特率和工作模式,波特率按实际情况而定,不宜太高。我这里工作模式设置成了回环静默模式,如果没有CAN转USB模块,很难验证通讯效果,而设置成回环静默模式就没问题了,能够自发自收。一般工作下设置成普通模式。
下面这一步就是对CAN数据包的配置了,按官方模板来就行

struct rt_can_filter_item items[1] ={RT_CAN_FILTER_ITEM_INIT(0x00f8,0,0,0,0x0030,RT_NULL,RT_NULL),};struct rt_can_filter_config cfg = {1,1,items};

RT_CAN_FILTER_ITEM_INIT函数的定义如下

#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 = 0x00f8 标识符设置
ide = 0 IDE 位为标准和扩展选择位,0:标准,1:扩展。这里使用标准数据帧
rtr = 0 RTR用标注该帧是数据帧还是远程帧,0:标准;1:远程。这里使用数据帧,远程帧也就是遥控帧
mode = 0 过滤表模式,0 表示标识符屏蔽模式,1 表示标识符扩展位模式,这里使用屏蔽模式。
(2025年10月5日,当前官方文章关于这一步的注释有误,屏蔽模式和扩展位模式弄反了)
mask = 0x0030 ID掩码,0表示对应的位不关心,1表示对应的位必须匹配,仅在标识符屏蔽模式下有效
在标识符扩展位模式下,ID掩码和ID码是唯二能够接收的标识符。
while循环就是将接收到的数据输出到屏幕上,很好理解

void can_sample(int argc,char **argv)
这是一个自定义finsh命令的语句,用来主动发送CAN数据包,前四位数据设置为1,2,3,4了。自定义ID

2.user_can.h

代码如下(示例):

#ifndef APPLICATIONS_ADS_CAN_H_
#define APPLICATIONS_ADS_CAN_H_#include <board.h>
#include <rtdevice.h>int can_init(void);#endif /* APPLICATIONS_ADS_CAN_H_ */

3.main.c

int main(void)
{can_init();while(1){rt_thread_mdelay(800);}return RT_EOK;
}

三、效果展示

下面是标识符扩展位模式,只允许0xf8和0x30通过,其他ID的数据包会被过滤掉
在这里插入图片描述
下面是标识符屏蔽模式,只允许xx11 xxxx类ID通过,其他ID的数据包会被过滤掉
在这里插入图片描述

http://www.dtcms.com/a/446444.html

相关文章:

  • Transformer时序预测模型对比传统LSTM的优劣
  • 随机试验中异质性处理效应的通用机器学习因果推断
  • ClaudeCode真经第七章:未来发展与技术展望
  • 利用DeepSeek辅助给duckdb_pgwire插件添加psql终端输出int128功能
  • 做网站在百度云盘登录
  • 亿企邦网站建设服务器租用免费试用
  • Coze源码分析-资源库-编辑知识库-后端源码-应用/领域/数据访问层
  • 做移动网站点击软件吗网站后台管理系统安装
  • 网站统计排名哪家网站雅虎全球购做的好
  • 福彩体彩10月5号数据分享
  • Java——文件相关知识
  • Hadess入门到实战(7) - 如何管理Pypi(Python)制品
  • RDMA 技术解析(1):RDMA 技术三国志 ——IB、RoCE、iWARP 的选型博弈
  • wordpress文章提交江苏网站快速排名优化
  • 【自记】MaxCompute中的冒烟测试
  • Linux : 动静态库制作、ELF格式
  • 如何制作电脑公司网站关键词怎么优化到百度首页
  • seo的全称是什么广州关于进一步优化
  • 南京设计网站免费做网站tk
  • YOLO算法原理详解系列 第004期-YOLOv4 算法原理详解
  • 高层次综合的基础-vivado_hls第二章
  • 市面上常见显示屏接口与对应的引脚
  • 天津网站建设方案维护网站开发实践教程
  • 网站源码出售wordpress 3.2.1
  • 西安网站注册个人建设网站成本
  • 初识Protobuf
  • 网站排名首页前三位什么网站百度收录好
  • 现在做什么网站好易搭应用快速开发平台
  • 站长之家商城佛山网站建设十年乐云seo
  • 网站挂黑链工具适合做网站开发的电脑配置