STM32F103_LL库+寄存器学习笔记06 - 梳理串口与串行发送“Hello,World“

news2025/7/13 19:24:13

导言


USART是嵌入式非常重要的通讯方式,它的功能强大、灵活性高且用途广泛。只停留在HAL库层面上用USART只能算是入门,要加深对USART的理解,必须从寄存器层面入手。接下来,先从最简单的USART串行发送开始。

另外,在接下来的几个章节里,我会逐步地完成一个能在实际产品上使用,可靠的、健壮的串口收发的驱动程序。 比如,为了实现高效地收发程序,肯定要用DMA。然后配合DMA的传输过半中断、传输完成中断来实现高效的串口数据发送与接收。比如,时刻监控USART的健康状态,及时发现通讯异常,解决问题。比如,增加超时通讯机制,进一步提高系统的稳定性等等。
如果你只停留在USART串行发送 + USART接收中断的实践上,那么你真的很有必要再重新梳理一遍怎样完成一个高效且稳定的串口收发程序。废话不多说,开始。

效果如下所示:
每隔1是发送一条字符串"Hello,Wrold.\r\n"。
在这里插入图片描述
项目地址:https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_ll_library06_usart

一、CubeMX


在这里插入图片描述
在这里插入图片描述

二、代码(LL库)


2.1、main.c

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.1.1、MX_USART1_UART_Init()

在这里插入图片描述

2.1.1.1、USART1_TX(PA9)

在这里插入图片描述
复习一下GPIO的寄存器CRL/CRH,如下:
在这里插入图片描述

2.1.1.2、USART1_RX(PA10)

在这里插入图片描述
复习一下GPIO的寄存器CRL/CRH,如下:
在这里插入图片描述

2.1.1.3、为什么USART模式下,PA9与PA10要这样设置GPIO???

在这里插入图片描述
如上所示,《STM32F1参考手册》章节8.1.11看到,当GPIO用在USART功能时,TX引脚需要设置推挽复用输出,RX引脚需要设置浮空输入或者带上拉输入。
在这里插入图片描述
如上所示,从这张表里看到另外一个很常用的通讯方式CAN总线,TX引脚也是设置推挽复用输出,RX引脚也是设置浮空输入或者带上拉输入。

2.1.1.4、USART1

在这里插入图片描述

三、寄存器梳理


3.1、发送器

在这里插入图片描述
《STM32F1参考手册》的章节25.3.2-发送器一定要多看几遍,总的来说:

  1. 将要发送的字节放入USART_DR寄存器之前,一定要等待USART_SR的位TXE = 1。代码如下:
void USART1_SendChar_Reg(uint8_t c) {  
	// 等待TXE置位(发送缓冲区空)  
	while (!(USART1->SR & (1 << 7)));  
	// 写入数据到DR  
	USART1->DR = c;  
}
  1. USART_TXE是发送的”第一级缓冲“状态,表示数据从USART_DR到移位寄存器的转移完成,并不意味着数据已经完全发送出去。如下图所示:
    在这里插入图片描述
  2. USART_TC才是代表数据完成发送出去,TC = 1时,表明硬件发送通道完全空闲。比如在低功耗场景中,通过TC = 1的判断,进入低功耗模式。
  3. 在单字节发送流程里,TXE会先置1,TC后置1。用户写入USART_DR->TXE清零。接着,数据转移到移位寄存器->TXE置1,此时可以写入新数据。最后,移位寄存器逐位发送数据->发送完成,TC置1。

3.2、时钟RCC_APB2ENR

在这里插入图片描述
启动外设之前,先启动对应的外设时钟。 如上所示,寄存器RCC_APB2ENR的位14-USART1EN是USART的时钟,置1相当于开启USART时钟。

RCC->APB2ENR |= (0x01UL << 14UL); // 使能USART1时钟(位14)
RCC->APB2ENR |= (0x01UL << 2UL);  // 使能GPIOA(打开该时钟是因为USART1的TX、RX使用PA9与PA10

3.2、GPIO_CRH

在这里插入图片描述
如上所示,PA9是USART1_TX,PA10是USART1_RX。

// PA9,复用功能推挽输出模式
GPIOA->CRH &= ~(0xFUL << 4UL); // 清除CNF9与MODE9
GPIOA->CRH |= (0xAUL << 4UL);  // 复用功能推挽输出模式(CNF9 = 10,MODE9 = 11),最大速度50MHz

// PA10,浮空输入模式
GPIOA->CRH &= ~(0xFUL << 8UL); // 清除CNF10与MODE10
GPIOA->CRH |= (0x4UL << 8UL);  // 浮空输入模式(CNF10 = 01,MODE10 = 00)

3.3、USART1

3.3.1、USART_BRR

在这里插入图片描述
摘自《STM32F1参考手册》章节25.3.4,波特率(BBR) = fck / 16 * USARTDIV。

计算过程:

  1. SystemClock设置72M,APB2没有分频,所以USART1的fck = 72MHz。
  2. BBR = fck / (16 * BaudRate) = 72000000 / (16 * 115200) = 39.0625。 整数部分39,小数部分0.0625 * 16 = 1。
  3. 写入USART1->BBR = 0x271(39 << 4 | 1)。 如下所示,39 << 4UL的目的是设置DIV_Mantissa,最后|1是设置DIV_Fraction。
    在这里插入图片描述

3.3.2、配置数据帧格式 - USART_CR1与USART_CR2

一般情况下UART通讯的数据帧格式是:8位数据 + 1位停止位 + 无奇偶校验。
在这里插入图片描述
在这里插入图片描述
如上所示,USART_CR1的M(字长)置0与PCE(校验控制使能)置0。

USART1->CR1 &= ~(0x01UL << 12UL); // 位M清0,数据一共8位
USART1->CF1 &= ~(0x01UL << 10UL); // 位PCE清0,禁止校验控制

在这里插入图片描述
如上所示,USART_CR2的段STOP置0。

USART1->CR2 &= ~(0x3UL << 12UL); // 位12、13置0,1个停止位

3.3.3、配置传输方向 - USART_CR1

在这里插入图片描述
在这里插入图片描述
如上所示,USART_CR1的TE与RE都置1。

USART1->CR1 |= (1UL << 3UL); // 使能发送
USART1->CR1 |= (1UL << 2UL); // 使能接收

3.3.4、禁用硬件流控 - USART_CR3

在这里插入图片描述
如上所示,USART_CR3的RTSE置0。

USART->CR3 &= ~(0x01UL << 8UL); // RTSE = 0,禁用硬件控流

3.3.6、清除无关模式位 - USART_CR2与USART_CR3

在这里插入图片描述
如上所示:

  • 禁止CK引脚输出时钟。
  • 禁止LIN模式。
    在这里插入图片描述
    如上所示:
  • 不使能红外模式。
  • 不选择半双工模式。
  • 禁止智能卡模式。
// (6) 配置异步模式 (清除无关模式位)
USART1->CR2 &= ~(1UL << 14);      // LINEN 位 = 0, 禁用 LIN 模式
USART1->CR2 &= ~(1UL << 11);      // CLKEN 位 = 0, 禁用时钟输出
USART1->CR3 &= ~(1UL << 5);       // SCEN 位 = 0, 禁用智能卡模式
USART1->CR3 &= ~(1UL << 1);       // IREN 位 = 0, 禁用 IrDA 模式
USART1->CR3 &= ~(1UL << 3);       // HDSEL 位 = 0, 禁用半双工

3.3.7、启用USART

在这里插入图片描述
如上所示,USART_CR1的UE置1,让USART模块使能。

USART1->CR1 |= (1UL << 13UL); // USART模块使能

四、代码(寄存器方式)


4.1、main.c

在这里插入图片描述
如上所示,函数USART1_Configure(void)用于初始化外设USART1。

  1. 错误修复: 第80行代码,改为GPIOA->CRH |= (0x09UL << 4UL)才对,速度10MHz的话,MODE9 = 01。所以应该是1001 = 0x09。

在这里插入图片描述
如上所示,函数USART1_SendString(const char \*str)用于串行发送字符串。
在这里插入图片描述
如上所示,在main()的while(1)里每隔1S执行一次函数USART1_SendString()发送字符串"Hello,World.\r\n"
在这里插入图片描述
如上所示,代码编译成功。
在这里插入图片描述

五、细节补充


5.1、为什么要清除无关模式位?

《3.3.6-清除无关模式位 - USART_CR2与USART_CR3》讲到怎样清除无关模式位,都是将某些位清0。实际上从《STM32F1参考手册》了解到,这些位(比如位14-LINEN)在系统初始化的时候默认就是0的。为什么我的代码还要再一次清0呢?多此一举吗?

首先,ST的LL库代码也会显式清除这些位。MX_USART1_UART_Init()函数里的LL_USART_ConfigAsyncMode(),它的内容如下:
在这里插入图片描述
既然LL库也会这样做,肯定有原因的。我的理解是:

  • 之前的状态可能非复位值:
    如果 USART 之前被其他程序或中断配置过(例如在 Bootloader 或其他初始化代码中),这些位可能被设置为 1。因此,即使复位值是 0,显式清除可以确保当前状态明确为禁用状态,避免遗留问题。
  • 代码可读性和可维护性:
    显式清除这些位(即使默认是 0)可以增强代码的可读性,让读者清楚地知道这些功能被故意禁用,而不是依赖复位值。这种做法在嵌入式开发中很常见,属于“防御性编程”。

5.2、为什么外设USART的校验功能一般都是关闭?

  1. 硬件奇偶校验的局限性
    • 只能检测单比特错误:硬件奇偶校验仅能检测数据传输中的单个比特错误,无法检测或纠正多比特错误。对于高可靠性应用(如工业通信、航空航天),这种检测能力不足。
    • 性能开销:启用硬件奇偶校验会增加通信帧长度(例如 8 位数据 + 1 位校验 + 1 停止位 = 10 位),从而降低有效数据吞吐量,尤其在高速通信中影响较大。
    • 复杂性增加:硬件奇偶校验需要发送端和接收端严格匹配配置(奇/偶、数据长度等),否则会导致错误(PE 标志),增加调试难度。
  2. 软件校验的灵活性
    • 更强大的错误检测:软件可以实现更复杂的校验算法,如 CRC-8、CRC-16、CRC-32,甚至自定义校验和。这些方法能检测多比特错误并提供更高的可靠性。
    • 协议层整合:在通信协议(如 Modbus、CAN、I2C)中,通常已经定义了标准校验机制(如 Modbus 的 LRC 或 CRC),硬件奇偶校验可能重复或冲突,不如直接在协议层实现。
    • 可扩展性:软件校验可以根据需要动态调整(如改变校验算法或长度),而硬件奇偶校验固定在硬件层面,缺乏灵活性。
  3. 工程实践的习惯
    • 简单性优先:在低成本或简单应用中(如串口调试、传感器通信),开发者往往选择关闭硬件奇偶校验,简化配置(使用 8-N-1 格式:8 位数据、无校验、1 停止位),并通过软件实现必要校验。
    • 兼容性:许多设备和工具(如串口调试助手)默认使用无校验配置,启用硬件奇偶校验可能导致不兼容。

5.3、USART1默认是PA9、PA19,怎样复用到PB6、PB7?

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如上所示,从寄存器AFIO_MAPR的bit2-USART1_REMAP可以设置USART1所使用的引脚。代码例子如下:
在这里插入图片描述

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

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

相关文章

硬件基础--14_电功率

电功率 电功率:指电流在单位时间内做的功(表示用电器消耗电能快慢的一个物理量)。 单位:瓦特(W)&#xff0c;简称瓦。 公式:PUI(U为电压&#xff0c;单位为V&#xff0c;i为电流&#xff0c;单位为A&#xff0c;P为电功率&#xff0c;单位为W)。 单位换算:进位为1000&#xff…

Vue.js 完全指南:从入门到精通

1. Vue.js 简介 1.1 什么是 Vue.js? Vue.js(通常简称为 Vue)是一个用于构建用户界面的渐进式 JavaScript 框架。所谓"渐进式",意味着 Vue 的设计是由浅入深的,你可以根据自己的需求选择使用它的一部分或全部功能。 Vue 最初由尤雨溪(Evan You)在 2014 年创…

在Git仓库的Readme上增加目录页

一般在编写Readme时想要增加像文章那样的目录&#xff0c;方便快速跳转&#xff0c;但是Markdown语法并没有提供这样的方法&#xff0c;但是可以通过超链接结合锚点的方式来实现&#xff0c;如下图是我之前一个项目里写的Readme&#xff1a; 例如有下面几个Readme内容&#xff…

C# SolidWorks 二次开发 -各种菜单命令增加方式

今天给大家讲一讲solidworks中各种菜单界面&#xff0c;如下图&#xff0c;大概有13处&#xff0c;也许还不完整哈。 1.CommandManager选项卡2.下拉选项卡3.菜单栏4.下级菜单5.浮动工具栏6.快捷方式工具栏7.FeatureManager工具栏区域8.MontionManager区域 ModelView?9.任务窗…

【RocketMQRocketMQ Dashbord】Springboot整合RocketMQ

【RocketMQ&&RocketMQ Dashbord】Springboot整合RocketMQ 【一】Mac安装RocketMQ和RocketMQ Dashbord【1】安装RocketMQ&#xff08;1&#xff09;下载&#xff08;2&#xff09;修改 JVM 参数&#xff08;3&#xff09;启动测试&#xff08;4&#xff09;关闭测试&…

《白帽子讲 Web 安全》之跨站请求伪造

引言 在数字化时代&#xff0c;网络已深度融入人们生活的方方面面&#xff0c;Web 应用如雨后春笋般蓬勃发展&#xff0c;为人们提供着便捷高效的服务。然而&#xff0c;繁荣的背后却潜藏着诸多安全隐患&#xff0c;跨站请求伪造&#xff08;CSRF&#xff09;便是其中极为隐蔽…

K8S学习之基础五十:k8s中pod时区问题并通过kibana查看日志

k8s中pod默认时区不是中国的&#xff0c;挂载一个时区可以解决 vi pod.yaml apiVersion: v1 kind: Pod metadata:name: counter spec:containers:- name: countimage: 172.16.80.140/busybox/busybox:latestimagePullPolicy: IfNotPresentargs: [/bin/sh,-c,i0;while true;do …

nginx代理前端请求

一&#xff0c;项目配置 我在 ip 为 192.168.31.177 的机器上使用 vue3 开发前端项目&#xff0c;项目中使用 axios 调用后端接口。 这是 axios 的配置&#xff1a; import axios from axios;const request axios.create({baseURL: http://192.168.31.177:8001,// 设置请求…

Android生态大变革,谷歌调整开源政策,核心开发不再公开

“开源”这个词曾经是Android的护城河&#xff0c;如今却成了谷歌的烫手山芋。最近谷歌宣布调整Android的开源政策&#xff0c;核心开发将全面转向私有分支。翻译成人话就是&#xff1a;以后Android的核心更新&#xff0c;不再公开共享了。 这操作不就是开源变节吗&#xff0c;…

银行分布式新核心的部署架构(两地三中心)

银行的核心系统对可用性和性能要求均非常严苛&#xff0c;所以一般都采用两地三中心部署模式。 其中&#xff1a; 同城两个主数据中心各自部署一套热备&#xff0c;平时两个中心同时在线提供服务&#xff0c;进行负载均衡假如其中一个数据中心出现异常&#xff0c;则由另外一个…

MantisBT在Windows10上安装部署详细步骤

MantisBT 是一款基于 Web 的开源缺陷跟踪系统&#xff0c;以下是在 Windows 10 上安装部署 MantisBT 的详细步骤&#xff1a; 1. 安装必要的环境 MantisBT 是一个基于 PHP 的 Web 应用程序&#xff0c;因此需要安装 Web 服务器&#xff08;如 Apache&#xff09;、PHP 和数据…

9.4分漏洞!Next.js Middleware鉴权绕过漏洞安全风险通告

今日&#xff0c;亚信安全CERT监控到安全社区研究人员发布安全通告&#xff0c;Next.js 存在一个授权绕过漏洞&#xff0c;编号为 CVE-2025-29927。攻击者可能通过发送精心构造的 x-middleware-subrequest 请求头绕过中间件安全控制&#xff0c;从而在未授权的情况下访问受保护…

OpenCV图像拼接(5)图像拼接模块的用于创建权重图函数createWeightMap()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::detail::createWeightMap 是 OpenCV 库中用于图像拼接模块的一个函数&#xff0c;主要用于创建权重图。这个权重图在图像拼接过程中扮演着重…

CTF类题目复现总结-[MRCTF2020]ezmisc 1

一、题目地址 https://buuoj.cn/challenges#[MRCTF2020]ezmisc二、复现步骤 1、下载附件&#xff0c;得到一张图片&#xff1b; 2、利用010 Editor打开图片&#xff0c;提示CRC值校验错误&#xff0c;flag.png应该是宽和高被修改了&#xff0c;导致flag被隐藏掉&#xff1b;…

linux打包前端vue,后端springboot项目

第一步先对整个项目进行通过maven进行clean在进行compile 第二步直接进行打包package和install都可以 第三部把对应的jar放到服务器上 把jar包放到服务器上某个地址下&#xff0c;然后cd到这个目录下&#xff0c;然后执行命令 nohup java -jar ruoyi-admin.jar > springbo…

Elasticsearch:使用 AI SDK 和 Elastic 构建 AI 代理

作者&#xff1a;来自 Elastic Carly Richmond 你是否经常听到 AI 代理&#xff08;AI agents&#xff09;这个词&#xff0c;但不太确定它们是什么&#xff0c;或者如何在 TypeScript&#xff08;或 JavaScript&#xff09;中构建一个&#xff1f;跟我一起深入了解 AI 代理的概…

Docker 快速入门指南

Docker 快速入门指南 1. Docker 常用指令 Docker 是一个轻量级的容器化平台&#xff0c;可以帮助开发者快速构建、测试和部署应用程序。以下是一些常用的 Docker 命令。 1.1 镜像管理 # 搜索镜像 docker search <image_name># 拉取镜像 docker pull <image_name>…

自顶向下学习K8S--部署Agones

本文在本人博客&#xff0c;原文地址&#xff1a;http://viogami.tech/index.php/blog/346/ 我是gopher&#xff0c;离不开云原生&#xff0c;自然也逃不了理解docker和K8S这俩。今天抽空想玩下agones&#xff0c;进而对K8S有实践性的理解。 学一个新事物从底层理论学肯定是最…

unity中Xcharts图表鼠标悬浮表现异常

鼠标悬浮在面板附近&#xff0c;只显示单独的一个项目 而且无论鼠标如何移动&#xff0c;根本没有效果。 解决方案&#xff1a; 需要在对应的Canvas上绑定主相机才可以 鼠标移动到项目上就有信息展示了

【Java SE】包装类 Byte、Short、Integer、Long、Character、Float、Double、Boolean

参考笔记&#xff1a;java 包装类 万字详解&#xff08;通俗易懂)_java包装类-CSDN博客 目录 1.简介 2.包装类的继承关系图 3.装箱和拆箱 3.1 介绍 3.2 手动拆装箱 3.3. 自动拆装箱 ​4.关于String类型的转化问题 4.1 String类型和基本类型的相互转化 4.1.1 String —…