【踩坑日记】STM32 USART 串口与 FreeRTOS 冲突

news2024/11/23 15:54:07

文章目录

  • 问题描述
  • 问题出现的环境
  • 问题解决过程
    • 第一步
    • 第二步
    • 第三步
    • 第四步
    • 第五步
    • 第六步
    • 第七步
    • 第八步
  • 后续验证
  • 一些思考
  • 类似的问题
  • 后记

问题描述

笔者使用 FreeRTOS 创建了两个任务,使两颗 LED 以不同频率闪烁,但是在加入串口 USART 部分代码后, LED 不能正常工作了。

问题出现的环境

  • 硬件:STM32F103C8T6、ST-Link
  • 软件:KEIL5
  • 代码来源:野火 FreeRTOS 例程
    说明,野火的教程并不适用于 F103C8,笔者对其进行了移植,一定程度上是因为移植出现了冲突的问题。
    关于如何移植野火的 FreeRTOS 源码,请看笔者的这篇文章【学习日记】【FreeRTOS】FreeRTOS 移植到 STM32F103C8

问题解决过程

这个部分记录了笔者解决问题的思路,或许对你有一些启发,如果觉得太长可以直接按右边的目录跳到问题的解决方法
在这里插入图片描述

第一步

首先尝试屏蔽了重定向的 printf,但是未注释掉 USART 的初始化函数,问题没有解决;

第二步

尝试屏蔽 USART 的初始化函数,问题解决,此时可以确定问题出在 USART 的初始化函数中;

第三步

查看 USART 的初始化函数代码,如下:

 /**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置停止位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 配置校验位
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 配置硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = 
	USART_HardwareFlowControl_None;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USARTx, &USART_InitStructure);
	
	// 串口中断优先级配置
	NVIC_Configuration();
	
	// 使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}

可以将函数复制到 ChatGPT 中,让 AI 帮助分析函数的结构,这里分享一个我自制的 Prompt:

你是一个优秀的编程工程师,你的工作是分析我发送给你的函数,把函数执行的功能用较简洁的语言归纳为几步。需要进行分析的函数我将放置于中括号中,你只需要输出归纳出的函数功能,不需要输出其他提示语。为了防止我们的对话过长你忘记了你的职责,当你忘记时我将在发送的消息前加上 “/keep” 来提醒你。

这个函数的结构大致是:

  • 打开串口使用的 GPIO 端口的时钟和外设的时钟。
  • 配置 USART Tx 引脚为推挽复用模式,配置 USART Rx 引脚为浮空输入模式。
  • 配置串口的工作参数,包括波特率、数据字长、停止位、校验位和硬件流控制等。
  • 完成串口的初始化配置,将工作参数应用到 USART 外设上。
  • 配置串口的中断优先级。
  • 使能串口接收中断。
  • 使能串口。

由于我们不确定问题出在这个函数的哪个地方,所以笔者采用了逐层解屏蔽的方式进行排查,也就是先将这个函数中所有代码都注释掉,然后从上往下逐层去掉屏蔽,直到问题出现,就可以确定有问题的代码。

如果函数较长,可以采用类似二分法的方式进行排查,也就是每次都解除一半的注释,如果问题出现,那么有问题的代码就在刚刚接触注释的那一半代码中;如果问题没有出现,有问题的代码就在另一半代码中。

不过在我们对这个函数进行逐层解屏蔽的排查操作时,我们要注意从函数的结构可以看出,串口的生效操作在最后一行,如果这行被注释了意味着前面所有的操作实际上都没有生效,所以这行从始至终都不能被注释。

经过笔者的排查,发现问题就出在 “使能串口接收中断” 这部分代码中,只要这部分代码被屏蔽了,问题就解决了。
在这里插入图片描述
虽然注释掉串口接收中断就可以解决问题,但是这意味着我们只能接收的 STM32 发送给电脑的信息,而不能给 STM32 发送信息。笔者自然是对这个解决方式不满意的,接着往下看。

第四步

值得一提的是,我发现了野火的代码中另一个 bug:野火在板载硬件初始化和串口初始化函数中都对中断优先级分组进行了配置,而且配置的方式是冲突的:

  • 板载硬件初始化函数:
    在这里插入图片描述
  • 串口初始化函数中中断配置函数:
    在这里插入图片描述
    我们选择对串口初始化函数中中断配置函数进行修改以解决这个 bug:
 /**
  * @brief  配置嵌套向量中断控制器NVIC
  * @param  无
  * @retval 无
  */
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
//  /* 嵌套向量中断控制器组选择 */
//  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 15;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

第五步

  • 由于发现了注释掉串口接收中断就可以解决问题,笔者自然推断是不是进入了中断跳不出来导致任务得不到执行,所以决定修改串口中断的优先级,改为抢断优先级最低的 15。
  • 虽然说思路正确,但这实际上是不可行的。在 FreeRTOS 中,PendSV 的中断优先级配置为最低的 15,PendSV 进行任务切换。
  • 如果真的是由于卡在串口的中断中导致任务得不到执行,那么即使设置串口中断为最低优先级,由于和 PendSV 的中断优先级一样,那么也无法跳出串口中断。

第六步

  • 此时笔者决定先使用串口模块(实际上是 USB 转 TTL 模块)将 STM32 和计算机连接起来,看看能否正常接收 STM32 发送的消息。奇迹发生了,当接上串口模块后,即使是能了串口接收中断,模块也是正常工作的!真有点玄学的味道!
    USB 转 TTL 模块
  • 但是很快我就想到,变量就是我刚刚接入的串口模块。尝试插拔了几次模块,果然如此。
  • 于是笔者开始进行排除,这个模块需要接 3 根线,分别是 GND、TX 和 RX,笔者将其分别单独与 STM32 连接,最终确定问题出在串口模块的 TX 引脚上:当串口模块已经接入计算机,这时只要将串口模块的 TX 连接到 STM32 的 RX 引脚上(这里使用的是 USART1,RX 为 PA10),那么即使使能了串口接收中断, STM32 也能正常工作

第七步

  • 于是笔者测量了串口模块的 TX 电平,发现有 3.5V。推测出是不是当 STM32 的 RX 引脚需要一个类似于 3.5V 的高电平来确保其不会无故触发接收中断导致任务无法执行。所以笔者直接将 RX 引脚接入 STM32 板子自带的 3.3V 引脚,发现工作正常,猜想正确!

  • 笔者又想到,可能不一定需要一个确定的高电平,确定的电平即可。于是将 STM32 的 RX 引脚接地,工作也正常!

第八步

  • 笔者返回查看串口的引脚初始化代码,发现 RX 引脚确实配置为浮空输入,猜想是否因为浮空输入导致了电平的不稳定而无故触发中断卡死
    在这里插入图片描述
  • 这里配置的浮空输入是否有 bug 呢?于是查看数据手册,发现浮空输入或者上拉输入都是可以的:
    在这里插入图片描述
  • 于是改为上拉输入,问题解决
  // 将USART Rx的GPIO配置为上拉输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);

解决方法!!!

  • 最简单的解决方式就是将 STM32 USART 的 RX 引脚从浮空输入改为上拉输入
  // 将USART Rx的GPIO配置为上拉输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
  • 还有一种解决方式是即使暂时不使用 USART,但是只要在程序中初始化了串口(使能了接收中断),就要给串口的 RX 引脚一个确定的电平输入,可以是接地也可以是给 3.3V 的高电平

后续验证

  • 定义了串口中断函数后,使用 ST-Link 进入调试模式,发现的确卡在了串口中断函数中导致任务不能得到执行

  • 推测可能是串口的电平不稳定导致的

一些思考

  • 在一开始我们提到,一定程度上是因为移植野火的 FreeRTOS 例程才出现了冲突的问题。怎么理解呢?

  • 实际上,为了降低学习成本,笔者使用的是 STM32F103C8 的最小系统板,所以才需要外接 USB 转 TTL 电平模块与计算机通信。

  • 而野火的官方例程是适配其开发板的,肯定是经过了测试正常才发布的。那么为什么在野火的开发板上就不会出现类似的 bug 呢?因为野火的开发板已经集成了 USB 转 TTL 电平模块,一上电就能为 STM32 USART 的 RX 引脚提供一个确定的电平,规避了产生这个 bug 的条件。

类似的问题

笔者刚发现这个 bug 的时候,并不清楚是引脚的电平配置导致的问题,在网络上寻求解决方式的时候多半都是往 FreeRTOS 与 USART 冲突这个方向去搜索,导致并未找到正确的解决方法。

而在笔者写这篇文章的时候,就搜到了几篇类似的文章:
stm32 串口接收引脚配置为浮空输入问题
STM32F1频繁进入串口中断-串口直接连接到了接插件引脚上
USART3------------RXD----------PB11 悬空会导致程序频繁进入串口接收中断!!!

后记

如果您觉得本文写得不错,可以点个赞激励一下作者!
如果您发现本文的问题,欢迎在评论区或者私信共同探讨!
共勉!

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

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

相关文章

Shader 用到的数学函数总结及用法

在写shader相关的程序的时候往往需要用到 OpenGL内置的数学库,下面列举几个常用的函数用法和效果,下面的所有代码都是运行在shadertoy在线工具上的,推荐一个好用的数学工具desmosDesmos 目录: abs(x)step(edge,x)dot(uv1,uv2) …

MySQL双主架构、主从架构

为什么要对数据库做优化? MySQL官方说法: 单表2000万数据就达到瓶颈了。所以为了保证查询效率,要让每张表的大小得到控制。 MySQL主主架构 主数据库都负责增删改查。 比如有1000W的数据,有两个主数据库,就将数据分流给…

古巴勒索软件-CVE-2023-27532工具利用

黑莓捕获古巴勒索软件,其中一项工具有关CVE漏洞工具利用,利用有关CVE-2023-27532编号的漏洞利用工具进行投放。 MD5 9a3349f062130201a3dc16e78d5ab05d SHA-1 96e0a6a2aa12ed572fea9a1a858c903356bce1ab SHA-256 cf87a44c575d391df668123b05c207eef0…

代码随想录 (四)字符串

1.反转字符串 class Solution { public:void reverseString(vector<char>& s) {for (int i 0, j s.size() - 1; i < s.size()/2; i, j--) {swap(s[i],s[j]);}} };2.反转字符串II 直接用reverse&#xff08;是左闭右开的&#xff09; std::reverse 是 C 标准库中…

【2023深圳杯数学建模A题思路模型与代码分享】

2023深圳杯数学建模A题 A题 影响城市居民身体健康的因素分析解题思路第一问第二问第三问第四问 技术文档第一问完整代码写在最后 A题 影响城市居民身体健康的因素分析 以心脑血管疾病、糖尿病、恶性肿瘤以及慢性阻塞性肺病为代表的慢性非传染性疾病&#xff08;以下简称慢性病…

TCP性能机制

延迟应答 为什么有延迟应答 发送方如果长时间没有收到ACK应答&#xff0c;则会触发超时重传机制&#xff0c;重新发送数据包。但如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小&#xff0c;发送方一次只能发少量数据&#xff0c;效率较低。 举个例子理解一…

高性能服务器NodejsExpress快速构建Web

目录 1 Express基本使用1.1 托管静态资源 2 Express 路由3 Express 中间件3.1 全局中间件3.2 局部中间件3.3 中间件分类 4 CORS 跨域资源共享4.1 cors 中间件解决跨域4.2 CORS4.3 CORS 常见响应头4.4 CORS 请求分类 1 Express基本使用 官网传送门(opens new window) 基于 Node.…

简单计算器的实现(含转移表实现)

文章目录 计算器的一般实现使⽤函数指针数组的实现&#xff08;转移表&#xff09; 计算器的一般实现 通过函数的调用&#xff0c;实现加减乘除 # define _CRT_SECURE_NO_WARNINGS#include<stdio.h>int Add(int x, int y) {return x y; }int Sub(int x, int y) {retur…

计算机中找不到msvcp140.dll丢失怎么弄?程序员都是这样解决的

msvcp140.dll是Microsoft Visual C 2015 Redistributable Package的一部分&#xff0c;它是一个运行时库文件&#xff0c;提供了许多C标准库函数的实现。如果缺少这个文件&#xff0c;可能会导致一些程序无法正常运行&#xff0c;例如一些游戏、应用程序等。为了解决这个问题&a…

【Unity】如何制作小地图

我们为什么要制作小地图呢&#xff1f; 原因很简单&#xff1a; 导航和定位&#xff1a;小地图可以显示玩家当前位置以及周围环境的概览。这使得玩家能够更好地导航和定位自己在游戏中的位置&#xff0c;找到目标或避开障碍物。场景了解&#xff1a;通过小地图&#xff0c;玩…

Presto 介绍

目录 Presto 介绍Presto 架构Coordinator协调器节点发现服务&#xff08;discovery service&#xff09;工作节点基于连接器的架构查询执行模型查询优化JVM 配置Presto Web UIPresto 内存管理Presto 任务并发性Presto 工作节点调度Presto 网络数据交换Presto 资源组 Presto 介绍…

公司电脑文件数据加密系统

电脑文件透明加密软件是一种可以对文件进行加密的软件&#xff0c;这类软件能够在用户无感知的情况下对文件进行加密&#xff0c;使得文件的安全性得到保障。 PC访问地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 智能透明加…

Java抽象类详解

抽象类 抽象类的概念 在面向对象的概念中&#xff0c;所有的对象都是通过类来描绘的&#xff0c;但是反过来&#xff0c;并不是所有的类都是来描绘对象的&#xff0c;如果一个类中没有包含足够的信息来描绘一个具体的对象&#xff0c;这样的类就是抽象类。比如&#xff1a; 说…

C语言刷题(13)

第一题 第二题 第三题 第四题 第五题 第六题 第七题 注意 1.nsqrt(n)&#xff0c;sqrt本身不会将n开根 2.初始化已经令sumn了&#xff0c;故相加的个数为m-1次

机器学习在大数据分析中的应用

文章目录 机器学习在大数据分析中的原理机器学习在大数据分析中的应用示例预测销售趋势客户细分和个性化营销 机器学习在大数据分析中的前景和挑战前景挑战 总结 &#x1f389;欢迎来到AIGC人工智能专栏~探索机器学习在大数据分析中的应用 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&…

Java面试题(2) 两个线程,交替打印100次A和B

目录 一、实现方式一实现思路&#xff1a;代码实现&#xff1a;执行结果&#xff1a; 二、实现方式二实现思路&#xff1a;代码实现&#xff1a;执行结果&#xff1a; 三、思考 一、实现方式一 实现思路&#xff1a; 代码实现&#xff1a; ThreadTest.java package com.demo…

Elasticsearch算分优化方案之rescore_query

简介 今天来说一说Elasticsearch 的重新评分&#xff0c;即在检索出来一次结果的基础上在进行检索提升数据排序效果&#xff0c;但是仅对查询或者post_filter阶段返回的前多少条进行二次查询。在每个分片上进行二次检索的文档数量时可以通过window_size 控制的&#xff0c;该参…

PHP8中自定义函数-PHP8知识详解

1、什么是函数&#xff1f; 函数&#xff0c;在英文中的单词是function&#xff0c;这个词语有功能的意思&#xff0c;也就是说&#xff0c;使用函数就是在编程的过程中&#xff0c;实现一定的功能。即函数就是实现一定功能的一段特定代码。 在前面的教学中&#xff0c;我们已…

甜椒叶病害识别(Python代码,pyTorch框架,深度卷积网络模型,很容易替换为其它模型,带有GUI识别界面)

代码运行要求&#xff1a;Torch>1.13.1即可 1.数据集介绍&#xff1a; 第一个文件夹是细菌斑叶&#xff08;3460张&#xff09; 第二个文件夹是 健康&#xff08;4024张&#xff09; 2.整个文件夹 data文件夹存放的是未被划分训练集和测试集的原始照片 picture文件夹存放的…

系统架构:数据库

文章目录 数据库设计关系代数规范化理论求候选键特殊函数依赖Armstrong公理 数据库设计 步骤产出说明1.根据数据要求和处理要求进行需求分析数据流图、数据字典、需求说明书等分析数据流向、数据详细含义等&#xff0c;分析具体需求2.对现实世界进行抽象&#xff0c;进行概念结…