STM32裸机开发转FreeRTOS教程

news2025/1/9 17:04:51

目录

  • 1. 简介
  • 2. RTOS设置
    • (1)分配内存
    • (2)查看任务剩余空间
    • (3)使用osDelay
  • 3. 队列的使用
    • (1)创建队列
    • (1)直接传值和指针传值
    • (2)发送/接收等待时间
    • (3)不要在硬件中断发送队列
  • 4. 数据传递和共享
    • (1)尽量用全局常量代替函数指针传参
    • (2)同一资源需要被多个线程访问的两种方法
  • 5. 开发调试
    • (1)修改任务名称前备份代码,否则都会被删除
    • (2)keil的字体和编码,vscode的使用
    • (3)DMA串口日志
    • (4)文档放在项目文件夹外面,以免被cube删除
  • 6. LCD乱码问题

1. 简介

之前都是用CubeMX+Keil裸机开发STM32,最近第一次启用了FreeRTOS,用它可以实现多线程,但是如果写代码不严谨,单片机容易卡死,非常头疼。

2. RTOS设置

(1)分配内存

config parameters选项卡里,有个totoal heap size,意思大概是freertos占用的总内存,这个数值的默认值是比较小的,后面线程和队列加多了可能会不够,可以手动增加。我设置成的8kB,STM32f103rct6有48kB的RAM,是很充足的:
在这里插入图片描述
可以在heap usage里面看到使用情况,"still available"和"used"加起来正好是上面设置的总大小:
在这里插入图片描述
还有个minimal stack size参数,这个相当于一个底线,分配给每个任务的空间大小不能小于这个值。注意这个是用Word(字)作单位,32位单片机的一个字占4字节。
在这里插入图片描述
下图是设置任务的界面,每个任务默认给了128个字(半个kB)这个大小是比较适中的, 足够大部分常规任务的应用,也不会太占用单片机内存。
在这里插入图片描述
如果想节省内存,可以把前面minimal stack size设为64 Words(不允许更小了),然后把那些变量比较少的线程空间大小设置为64 Word。调试期间可以用随后介绍的方法查看线程空间够不够。

(2)查看任务剩余空间

为了用uxTaskGetStackHighWaterMark()查看任务剩余空间,需要在cubemx中开启它对应的使能,如下图。
在FreeRTOSConfig.h里面改会和cubemx冲突。
在这里插入图片描述

(3)使用osDelay

所有线程(除了IDLE)的死循环里面都需要至少加个osDelay(1),否则容易卡死。
在cmsis_os.c里查看osDelay的函数体,可见它本质上就是vTaskDelay:
在这里插入图片描述

3. 队列的使用

(1)创建队列

在Cube的Tasks和Queues选项卡,添加队列:
在这里插入图片描述
Queue Size是队列长度,设置的别让队列溢出就行,可以用osMessageAvailableSpace()查询队列剩余长度。
Item Size是每个元素的长度,这个后面会讲。

生成代码之后,cube会在freertos.c里创建一个队列句柄:

osMessageQId ledQueHandle;

cube里面设置的item size,代表每个队列数据占用多少字节。但由于c语言属于初级语言,不能给函数传递不定长度的参数,添加队列元素的函数是:

osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec)

它的第二个参数info,始终是uint32_t类型的,占4个字节。那如何传递不同长度的数据呢?答案就是“指针传值”。

如果要传递的数据可以用4个字节表示,就用“直接传值”方法,item size设为4;如果单次数据量超过了4字节,可以把数据放在数组或结构体里面,用指针传值方法,item size为被传递的数组或结构体的大小。

(1)直接传值和指针传值

直接传值示例:

// 发送线程
void Task_Send(void const *arg)
{
	...
	int cmd;
	for(;;){
		...
		osMessagePut(ledQueHandle, (uint32_t)cmd, osWaitForever);// 参数是int或float等数值
		...
	}
}
// 接收线程:
void Task_Receive(void const * argument)
{
  /* USER CODE BEGIN Task_LED */
	osEvent evt;
	int cmd;
	for(;;)	{
		evt = osMessageGet(ledQueHandle,0); 
		if(evt.status==osEventMessage){
			cmd=(int)evt.value.v; 
			...
		}
		osDelay(1);		
	}
}

指针传值示例:

// 发送线程
void Task_Send(void const *arg)
{
	...
	int cmd[4];//传递数组,队列的item size = 16
	// MyStructType cmd;//传递结构体,需要预先定义MyStructType类型,队列的item size = sizeof(MyStructType)
	for(;;){
		...
		osMessagePut(ledQueHandle, (uint32_t)cmd, osWaitForever);//传递数组指针
//		osMessagePut(ledQueHandle, (uint32_t)&cmd, osWaitForever);//传递结构体指针
		...
	}
}
// 接收线程:
void Task_Receive(void const * argument)
{
  /* USER CODE BEGIN Task_LED */
	osEvent evt;
	int* pcmd;//接收指针,需要和发送的指针类型一致
//	MyStructType * pcmd;
	for(;;)	{
		evt = osMessageGet(ledQueHandle,0); 
		if(evt.status==osEventMessage){
			pcmd = (int*)evt.value.p;//需要强制转型
//			pcmd = (MyStructType*)evt.value.p;			
			...
		}
		osDelay(1);
	}
}

由以上可见,直接传值就是把要传送数据直接放到队列里,接收的时候用evt.value.v;指针传值是把被传递数据的指针放在队列里,接收的时候用evt.value.p。

(2)发送/接收等待时间

osMessagePut()和osMessageGet()的最后一个参数都是等待时间,发送函数的可以设置成osWaitForever,表示阻塞线程直到把数据放入队列;
接收函数的等待时间最好设置为0,同时在循环里加个osDelay()释放主控资源。设置成osWaitForever会卡死。

(3)不要在硬件中断发送队列

cmsis_os.h开头注释有:
在这里插入图片描述
意思是osMessagePut可以放中断,但是经过实测,在硬件中断中调用osMessagePut()函数会卡死。
所以,只能在操作系统函数(线程,定时器)操作队列,中断函数传值可以用全局变量。

4. 数据传递和共享

(1)尽量用全局常量代替函数指针传参

用指针传递维度高、数据量大的变量,容易导致各种错误。可以定义成全局变量,在函数里直接用。
如果全局变量需要被多个文件调用,可以先在.c文件定义,再在.h文件用 extern 声明一下,这样其他的C文件只要#include这个.h文件就能用全局变量了。

(2)同一资源需要被多个线程访问的两种方法

①互斥锁:在读写函数里面,先获取Mutex,操作之后再释放Mutex。
②队列:其他线程请求压入队列,再由资源访问线程接收处理。如果是读取操作,可以在队列元素里放个接收变量的指针(没验证过!)
经过测试,即便是4字节的变量,也要避免不同线程直接访问,不然会出错。

5. 开发调试

(1)修改任务名称前备份代码,否则都会被删除

在cube里面修改任务名称和入口函数前千万记得备份代码,否则重新生成代码之后,之前写的代码都会被擦除。
在这里插入图片描述

(2)keil的字体和编码,vscode的使用

在菜单栏Edit最下面打开configuration窗口,设置编码和字体:
在这里插入图片描述
在这里插入图片描述
Editor选项卡里面,编码设置有两个选择:

①Courier字体方案(字体易读):编码改成UTF-8,这是为了适配Courier字体。同时为了让cube适配UTF-8,需要添加一个系统环境变量,变量名称:JAVA_TOOL_OPTIONS,变量值:-Dfile.encoding=UTF-8。如果不加环境变量,cube会把中文注释搞成乱码。
②Keil默认字体方案(较难阅读):保持GB2312编码,也不用设置全局变量了。

同时勾选右边的“Automatic reload of externally modified files”,避免每次都提示要不要重新加载:
在这里插入图片描述
如果选Courier字体方案,还需要在Colors & Fonts选项卡设置:
在这里插入图片描述
开发过程中,可以用vscode打开项目文件夹,在里面写代码,再在keil里面编译下载。VSC的代码辅助比Keil好多了,而且深色主题更护眼。

(3)DMA串口日志

启用日志打印串口的发送DMA可以最小的干预主程序的运行。方法是在cube里面添加一个tx的dma通道,DMA参数默认
在这里插入图片描述
在NVIC页面里面,可以把DMA的中断关上,因为日志打印要求不高,不需要在DMA终端里面判断数据有没有发送完:
在这里插入图片描述
代码里面,可以先定义个全局数组作为发送缓冲区,在函数里用sprintf格式化字符串,先调用DMAStop,再发送,不然只能发送一次:

char uart_buf[50]; // 日志发送缓冲区
void Timer_Callback() // 要发送日志的函数,例如软件定时器
{
	sprintf(uart_buf,"%.2f %.2f %.2f %.2f\r\n",Mot.spd_sv, Mot.spd_pv, Mot.pos_sv, Mot.pos_pv);
	HAL_UART_DMAStop(&huart3);
	HAL_UART_Transmit_DMA(&huart3,uart_buf,strlen(uart_buf));
}

这个方法适用于周期循环发送日志的情况,发送周期基本上大于一次发送用时就行了,偶尔一次数据覆盖也没关系。如果日志量比较大,可以提高串口波特率。

(4)文档放在项目文件夹外面,以免被cube删除

如果要在项目里新建一个文件夹用来放文档,需要用全英文,避免特殊符号,以防被cube搞坏。或者把文档放项目文件夹外面。

6. LCD乱码问题

调试期间发现写入数据到芯片内部Flash之后,显示屏会出现字符错误。
解决方法是把把Flash写入地址往后移,从0x0800A000移到0x0800B000后,问题就消失了。
应该是代码地址和参数写入地址冲突了。

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

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

相关文章

Golang的网络安全漏洞检测

Golang的网络安全漏洞检测 一、网络安全意识的重要性 在当今网络高度发达的环境下,网络安全问题变得异常突出。黑客利用各种手段对网络系统进行攻击,企图窃取数据、篡改信息、瘫痪服务等,因此网络安全成为全球关注的焦点。在这种环境下&#…

oscp备考 oscp系列——Kioptix Level 1靶场 古老的 Apache Vuln

目录 前言 1. 主机发现 2. 端口扫描 3. 指纹识别 4. 目录扫描 5. 漏洞搜索和利用 前言 oscp备考,oscp系列——Kioptix Level 1靶场 Kioptix Level 1难度为简单靶场,主要考察 nmap的使用已经是否会看输出,以及是否会通过应用查找对应漏…

git的rebase和merge的区别?

B分支从A分支拉出 1.git merge 处于A分支执行,git merge B分支:相当于将commit X、commit Y两次提交,作为了新的commit Z提交到了A分支上。能溯源它真正提交的信息。 2.git rebase 处于B分支,执行git rebase A分支,B分支那边复…

ansible-性能优化

一. 简述: 搞过运维自动化工具的人,肯定会发现很多运维伙伴们经常用saltstack和ansible做比较,单从执行效率上来说,ansible确实比不上saltstack(ansible使用的是ssh,salt使用的是zeromq消息队列[暂没深入了解]),但其实…

Pytest钩子函数,测试框架动态切换测试环境

在软件测试中,测试环境的切换是个令人头疼的问题。不同环境的配置不同,如何高效切换测试环境成为许多测试开发人员关注的重点。你是否希望在运行测试用例时,能够动态选择测试环境,而不是繁琐地手动修改配置? Pytest 测…

【RK3568笔记】Android修改开机动画

概述 Android 的开机动画是由一系列连续的 PNG 图片作为帧组成的动画形式,不是一张 GIF 图片。将各帧 PNG 图片以压缩方式进行保存(压缩方式要求是存储压缩),并将保存的文件名命名为 bootanimation.zip,这个 bootanim…

华为路由器、交换机、AC、新版本开局远程登录那些坑(Telnet、SSH/HTTP避坑指南)

关于华为设备远程登录配置开启的通用习惯1、HTTP/HTTPS相关服务 http secure-server enablehttp server enable 2、Telnet服务telnet server enable3、SSH服务stelnet server enablessh user admin authentication-type password 「模拟器、工具合集」复制整段内容 链接&…

spring boot学习第二十三篇:Spring Boot集成RocketMQ

前置条件先安装好RocketMQ 希望在Window10安装rocketMQ并简单使用&#xff0c;可以参考如下文章&#xff1a; Window10安装rocketMQ并简单使用-CSDN博客 1、pom.xml文件里面加上依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId&…

花生好坏缺陷识别数据集,7262张图片,支持yolo,coco json,pasical voc xml格式的标注,识别准确率在95.7%

花生好坏缺陷识别数据集,7262张图片&#xff0c;支持yolo&#xff0c;coco json&#xff0c;pasical voc xml格式的标注&#xff0c;识别准确率在95.7% 数据集分割 训练组87&#xff05; 6353图片 有效集8% 606图片 测试集4% 303图片 预处理 自动定…

在JavaScript开发中,如何判断对象自身为空?

前言 如何判断一个对象为空是我们在开发中经常会遇到的问题&#xff0c;今天我们来聊聊几种经常使用的方法&#xff0c;以及在不同的场景下我们如何去使用。 1. JSON.stringify JSON.stringify 方法可以使对象序列化&#xff0c;转为相应的 JSON 格式。 const obj {};cons…

现场展示deepseek VS openAI o1模型大对比

DeepSeek-V3 模型的发布在 AI 领域引起了广泛关注。作为一款拥有 6850 亿参数的混合专家&#xff08;MoE&#xff09;语言模型&#xff0c;DeepSeek-V3 在多个基准测试中表现出色&#xff0c;甚至超越了一些闭源模型。其在 Aider 代码能力排行榜上的正确率达到 48.4%&#xff0…

2010年IMO几何预选题第1题

在 △ A B C \triangle ABC △ABC 中, A D AD AD, B E BE BE, C F CF CF 为三条高线, 延长 E F EF EF 交 ( A B C ) (ABC) (ABC) 于 P P P, 延长 D F DF DF, B P BP BP 交于 Q Q Q. 求证: A P A Q APAQ APAQ. 证明: 设 D F DF DF 交 ( A P F ) (APF) (APF) 于点 …

SOLID原则学习,单一职责原则(Single Responsibility Principle)

文章目录 1. 定义2. 核心思想3. 优点4. 总结 1. 定义 单一职责原则是面向对象设计中的五大原则&#xff08;SOLID&#xff09;之一&#xff0c;由Robert C. Martin提出。它指出一个类应该只有一个引起它变化的原因&#xff0c;即一个类应该只负责一项职责。 2. 核心思想 职责…

对快速由表及里说拜拜/如何正确运用由表及里

你是不是还&#xff1a;看到一男子拖走一女子就以为小情侣吵架而已&#xff08;可能人贩子&#xff09;&#xff1b;看到男友对你好个几次就从此死心塌地&#xff08;可能有手就行&#xff0c;细节装装而已&#xff09;结果耽误终身&#xff1b;看到女同事对你微笑不排斥就以为…

【算法】八大排序算法

这篇文章是对数据结构中 八大经典排序算法 的详解&#xff0c;包括其原理、实现过程、时间复杂度、空间复杂度及其适用场景。最后两种排序不常见&#xff0c;但仍收录了进来保持文章结构的完整性。 排序(Sort)是将无序的记录序列&#xff08;或称文件&#xff09;调整成有序的…

【学习笔记】数据结构(十一)

外部排序 文章目录 外部排序11.1 外存信息的存取11.2 外部排序的方法11.3 多路平衡归并的实现 - 增加k11.4 置换-选择排序 - 减少m11.5 最佳归并树 外部排序 指的是大文件的排序&#xff0c;即待排序的记录存储在外存储器 上&#xff0c;在排序过程中需进行多次的内、外存之间的…

基于 Python 自动化接口测试(踩坑与实践)

文档&#xff1a;基于 Python 的自动化接口测试 目录 背景问题描述与解决思路核心代码修改点及其详细解释最终测试结果后续优化建议 1. 问题背景 本项目旨在使用 Python 模拟浏览器的请求行为&#xff0c;测试文章分页接口的可用性。测试目标接口如下&#xff1a; bashcoder…

单片机-外部中断

中断是指 CPU 在处理某一事件 A 时&#xff0c;发生了另一事件 B&#xff0c;请求 CPU 迅速去处理(中断发生)&#xff1b;CPU 暂时停止当前的工作(中断响应)&#xff0c; 转去处理事件 B(中断服务)&#xff1b;待 CPU 将事件 B 处理完毕后&#xff0c;再回到原来事件 A 被中断的…

vite5.x配置https

旧版的vite直接在config里面配置https&#xff1a;true即可&#xff0c;新版的麻烦一些。 1.准备工作 需要安装openssl 下载地址&#xff1a;Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions 找到合适的版本安装&#xff0c;配置好环境变量&#x…

Clip Studio Paint 报错:Running on unsupported Os. Clip Studio Paint will close.

问题描述 启动 Clip Studio Paint 报错&#xff1a;Running on unsupported Os. Clip Studio Paint will close. 原因 Clip Studio Paint 锁区&#xff0c;系统是中国大陆的无法使用。 解决方式 打开系统设置 时间和语言- 区域- 区域格式- 简体中文&#xff08;新加坡&am…