RT-Thread studio上创建一个STM32F103的CAN通讯功能

前言

(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-ThreadFinSH是默认使用的串口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标准版模板工程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/709698.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1.1 编程环境的安装

汇编语言 汇编语言环境部署 第二个运行程序直接双击安装一直下一步即可MASM文件复制到D盘路径下找到dosbox安装路径&#xff1a;C:\Program Files (x86)\DOSBox-0.74找到该文件双击打开它&#xff0c;修改一下窗口大小 把这两行改成如下所示 运行dos&#xff0c;黑框中输入mou…

C#,数值计算,求解微分方程的吉尔(Gear)四阶方法与源代码

1 微分方程 微分方程&#xff0c;是指含有未知函数及其导数的关系式。解微分方程就是找出未知函数。 微分方程是伴随着微积分学一起发展起来的。微积分学的奠基人Newton和Leibniz的著作中都处理过与微分方程有关的问题。微分方程的应用十分广泛&#xff0c;可以解决许多与导数…

【Web安全靶场】sqli-labs-master 21-37 Advanced-Injection

sqli-labs-master 21-37 Advanced-Injection 第一关到第二十关请见专栏 文章目录 sqli-labs-master 21-37 Advanced-Injection第二十一关-Cookie注入第二十二关-Cookie注入第二十三关-注释符过滤的报错注入第二十四关-二次注入第二十五关-过滤OR、AND双写绕过第二十五a关-过滤…

【嵌入式——QT】日期与定时器

日期 QTime&#xff1a;时间数据类型&#xff0c;仅表示时间&#xff0c;如 16:16:16&#xff1b;QDate&#xff1a;日期数据类型&#xff0c;仅表示日期&#xff0c;如2024-1-22&#xff1b;QDateTime&#xff1a;日期时间数据类型&#xff0c;表示日期和时间&#xff0c;如2…

多个版本的Python如何不冲突?

转载文章&#xff0c;防止忘记或删除 转载于&#xff1a;电脑中存在多个版本的Python如何不冲突&#xff1f; - 知乎 (zhihu.com) 如何安装多版本的Python并与之共存&#xff1f; 如果你的工作涉及到Python多版本之间开发或测试&#xff0c;那么请收藏本文&#xff0c; 如果你…

【python】Python Turtle绘制流星雨动画效果【附源码】

在这篇技术博客中&#xff0c;我们将学习如何使用 Python 的 Turtle 模块绘制一个流星雨的动画效果。通过简单的代码实现&#xff0c;我们可以在画布上展现出流星闪耀的场景&#xff0c;为视觉带来一丝神秘与美感。 一、效果图&#xff1a; 二、准备工作 &#xff08;1)、导入…

每日一题——LeetCode1544.整理字符串

方法一 字符串转数组删除元素 将字符串转为数组&#xff0c;遍历数组&#xff0c;如果碰到同一字母大写小写连续出现就原地删除这两个元素&#xff0c;最后把数组转回字符串并返回 var makeGood function(s) {let arrs.split()for(let i0;i<s.length-1;i){if(arr[i]!arr[…

【程序员的金三银四求职宝典】《春风拂面,代码在手:程序员的金三银四求职指南》

《春风拂面&#xff0c;代码在手&#xff1a;程序员的金三银四求职指南》 随着春风的轻拂&#xff0c;大地复苏&#xff0c;万物更新。在这个生机勃勃的季节&#xff0c;不仅自然界在迎接新生&#xff0c;对于广大的程序员朋友们而言&#xff0c;这也是一个全新的开始——金三…

windows U盘不能识别

windows U盘不能识别 1、问题描述2、问题分析解决3、把U盘插到windows电脑上试试能不能识别 1、问题描述 windwos u盘不能识别 u盘被拿到mac电脑上做了启动盘之后&#xff0c;就不能被windows识别了。题主很奇怪里面被mac电脑的同学放了什么&#xff0c;因此想到把优盘挂载到L…

免费的Git图形界面工具sourceTree介绍

阅读本文同时请参阅-----代码库管理工具Git介绍 sourceTree是一款免费的Git图形界面工具&#xff0c;它简化了Git的使用过程&#xff0c;使得开发者可以更加方便地下载代码、更新代码、提交代码和处理冲突。下面我将详细介绍如何使用sourceTree进行这些操作。 1.下载和…

05|Mysql锁分类

1. 锁分类 1.1根据性能 乐观锁 ● 版本号 ● 读多场景 ● 第二次循环需要读取到最新的数据统计 示例 while{ // 1.调用方法获取当前版本号 getCurrentBalanceAndVersion(accountId); // 2.Java运算newBalance Balance 500; updateAccountBalance(account…

【习题——菱形的打印】

一、打印下面的图形&#xff08;菱形&#xff09; 我们可以先来看一个简单点的菱形&#xff1a; 输入描述&#xff1a; 输入一个char类型字符 输出描述&#xff1a; 输出一个用这个字符填充的对角线长5个字符&#xff0c;倾斜放置的菱形&#xff1a; 1、思路&#xff1a; 我…

黑马c++ STL部分 笔记(3) deque容器

双端数组&#xff0c;可以对头端进行插入删除操作 deque与vector区别&#xff1a; vector对于头部的插入删除效率低&#xff0c;数据量越大&#xff0c;效率越低&#xff08;每次头插&#xff0c;后面的元素就往后移&#xff09; deque相对而言&#xff0c;对头部的插入删除速…

【论文综述+多模态】腾讯发布的多模态大语言模型(MM-LLM)综述(2024.02)

论文链接&#xff1a;24.02.MM-LLMs: Recent Advances in MultiModal Large Language | 国内-链接 实时网站&#xff1a;https://mm-llms.github.io 参考说明1-readpaper:https://mp.weixin.qq.com/s/ESUVe1aTYFLVJ10S9c1dBg 一、什么是MM-LLM ? 多模态大语言模型&#xff…

我写了个ImageWindow应用

文章目录 0 引言1 应用简介2 主要功能和特点2.1 多图像同/异步像素级对比2.2 支持多达30种图像格式2.3 高效率的图像处理性能 3 简明使用教程3.1 软件下载安装与更新3.1.1 软件下载与安装3.1.2 软件更新 3.2 多视窗添加并自动最优排列3.3 多样化图像导入方式3.4 自动切换显示模…

node 之 模块化

1.什么是模块化 模块化是指解决一个复杂问题时&#xff0c;自顶向下逐层把系统划分成若干模块的过程。对于整个系统来说&#xff0c;模块是可组合、分解和更换的单元。 编程领域中的模块化 编程领域中的模块化&#xff0c;就是遵守固定的规则&#xff0c;把一个大文件拆成独立并…

怎么调用文心一言的api接口生成一个简单的聊天机器人(python代码)

寒假在学习大模型&#xff0c;但也没弄出多少眉目&#xff0c;电脑性能还有点小问题&#xff0c;大模型总跑不起来&#xff0c;只会简单调用一下现有的大模型的接口&#xff0c;例如&#xff1a;文心一言&#xff0c;下面展示一下代码&#xff1a; import tkinter as tk impor…

Mysql安装教程

一、下载 点开下面的链接&#xff1a;https://dev.mysql.com/downloads/mysql/ 点击Download 就可以下载对应的安装包了, 安装包如下: 二、解压 下载完成后我们得到的是一个压缩包&#xff0c;将其解压&#xff0c;我们就可以得到MySQL 8.0.31 的软件本体了(就是一个文件夹…

第三节-docker-cs架构分析

一、组成 docker engine&#xff1a;docker-client、rest-api、dockerd containerd&#xff1a; 1、管理容器生命周期 2、拉取/推送镜像 3、存储管理 4、调用runc 5、管理网络 containerd-shim&#xff1a;相当于一个驱动&#xff0c;containerd通过containerd-shim驱使…

关于静态应用程序安全测试(SAST)的自动修复(AutoFix)

SAST&#xff08;Static Application Security Testing&#xff0c;静态应用程序安全测试&#xff09;具是一种在软件工程中使用的安全解决方案&#xff0c;它主要用于在程序员编写好源代码后&#xff0c;无需经过编译器编译&#xff0c;直接对源代码进行扫描&#xff0c;以找出…