32单片机学习记录4之串口通信
前置
STM32的GPIO口有通用模式,复用模式,模拟模式三种,加上输入输出就是有6中对应的模式。 我学习了通用模式,会使用GPIO口使用一些简单外设,如LED,独立按键,红外感应器等。但是一些复杂的外设,传递不是高低电平了,那么通用模式已经不够用了。如WiFi模块,蓝牙模块,这个时候就要学习复用模式了。而串口通信就是复用模式下某一类外设的统一通信标准是吗?
- GPIO模式确实包括三种基本模式:
- 通用模式(GPIO)
- 复用模式(Alternate Function)
- 模拟模式(Analog)
- 输入输出方向又进一步细分为:
- 输入
- 输出
- 推挽输出
- 开漏输出
- 通用模式适合简单的数字输入输出,如你提到的:
- LED控制
- 独立按键
- 简单的数字传感器(如红外感应器)
- 复用模式的确是为了连接更复杂的外设而设计的。这些外设需要特定的通信协议或信号传输方式,如:
- WiFi模块
- 蓝牙模块
- UART/USART串口通信
- SPI
- I2C
- CAN总线
- 以太网接口等
- 关于串口通信:
- 串口(UART/USART)是一种复用模式下的通信标准
- 它使用两根线进行双向通信:TX(发送)和RX(接收)
- 每个GPIO口可以配置为串口的发送或接收端口
- 这种模式下,数据不再是简单的高低电平,而是按照特定的波特率和协议传输数据
总的来说,你的理解是正确的。复用模式让STM32能够与更复杂的外设进行通信,不再局限于简单的高低电平控制。串口通信就是复用模式的一个典型应用,是微控制器与外部设备进行数据交换的重要方式。
内核也只知道机器码吧?GPIO的通用模式也要经过数字寄存器转换,复用模式和模拟模式也要经过特殊的控制器与内核交流?
内核与指令
- 内核确实只能直接执行机器码
- 对于任何GPIO操作,最终都会转换为特定的机器指令
GPIO操作的基本流程
- 通用模式:
- 通过配置寄存器设置GPIO的工作模式
- 数字寄存器负责控制输入/输出状态
- 使用类似 GPIOA->ODR 这样的寄存器地址来直接操作
- 复用模式:
- 需要额外的复用映射寄存器(AFRL/AFRH)
- 配置特定GPIO口的复用功能
- 由专门的复用控制器处理信号路由
- 转换为内核能识别的机器码
- 模拟模式:
- 通常用于ADC、DAC等模数转换
- 使用模拟控制寄存器
- 信号需要经过模拟前端电路处理
- 转换为内核能识别的机器码
寄存器与内核交互
- 所有模式最终都是通过映射到内存的寄存器地址来实现
- 内核通过总线直接读写这些寄存器
- 不同模式下,寄存器的配置和使用方式有所不同
所以复用模式需要经过如串口控制器与内核交流,模拟模式需要经过ADC或则DAC控制器与内核交流,这一些都属于单片机上的外设也就是片上外设。 所以与单片机外的外设交流时,选择对应的模式,到时候片上外设会把信息转换为内核认识的机器码的?
片上外设(On-Chip Peripherals)
- 这些外设集成在微控制器芯片内部
- 包括:
- 串口控制器(USART/UART)
- ADC控制器
- DAC控制器
- I2C控制器
- SPI控制器
- 定时器
- 等等
通信过程
- 片上外设作为内核和具体硬件功能之间的桥梁
- 负责将上层逻辑转换为底层硬件可识别的信号
- 将硬件返回的信号转换为内核可理解的数据
与外部设备通信
- 选择合适的通信模式(如复用模式)
- 片上外设控制器负责:
- 信号电平转换(如通用模式下的数字寄存器)
- 数字寄存器将内核的逻辑电平指令转换为实际的GPIO电平
- 控制输出高低电平
- 读取输入引脚的电平状态
- 处理电平的开关、翻转等基本操作
- 协议实现(如复用模式下的串口控制器)
- 实现特定通信协议的底层细节
- 串口控制器负责:
- 按照串口通信协议发送数据帧
- 处理起始位、数据位、校验位、停止位
- 管理波特率
- 处理数据的发送和接收缓冲区
- SPI控制器管理时钟同步、数据移位
- I2C控制器处理起始信号、从机地址、数据传输等
- 数据编解码
- 将内核的数据转换为通信接口能识别的格式
- 对数据进行打包、解包
- 处理数据的序列化和反序列化
- 执行必要的数据转换(如二进制到串行)
- 时序控制
- 管理通信的时间节奏
- 控制信号的定时和同步
- 处理通信协议中的时序要求
- 管理超时、延迟等时间相关的逻辑
- 信号电平转换(如通用模式下的数字寄存器)
这些职责使得片上外设控制器能够有效地在内核逻辑和具体硬件接口之间进行无缝转换,是微控制器实现复杂通信的关键。
不同外设通信模式
- 串口(UART/USART):使用复用模式
- 模拟传感器:使用模拟模式和ADC
- I2C/SPI:特定的复用模式
- CAN总线:专门的总线控制器模式
所以你的理解完全正确:片上外设就是内核与具体硬件功能之间的"翻译官",负责将抽象的指令转换为具体的硬件操作,并将硬件返回的信号转换为内核可理解的数据格式。
一句话总结串口通信是什么
串口通信是单片机在复用模式下实现的一种与单片机外部设备进行数据交换的通信标准和方式。
这个总结精准地概括了串口通信的本质:
- 使用复用模式
- 针对片外设备
- 是一种标准化的通信方式
GPIO口与串口以及ADC,DAC的关系
我看STM32的芯片内部框图,有串口通信专门端口,也有ADC,DAC的专门端口,为什么要说他们是复用模式和模拟模式下的通信方式?这意思是我还需要去GPIO口设置模式?可是他们并不在一个端口上啊?设置通用模式,是因为需要直接使用GPIO去输出或者检测高低电平
**在STM32微控制器中,很多引脚实际上是具有多功能的。虽然像USART(串口)、ADC、DAC看起来有专门的端口,但这些端口通常都复用在GPIO(通用输入/输出)引脚上。**这种设计的原因主要有以下几点:
- 引脚资源复用
- 芯片的物理引脚数是有限的
- 通过引脚复用,可以让同一个物理引脚在不同模式下具有不同的功能
- 这样可以在有限的引脚上实现更多的功能
- 模式切换
- GPIO模式不仅仅是简单的输入输出
- 它可以通过配置寄存器切换为多种功能:
- 数字输入
- 数字输出
- 复用功能(如串口、SPI、I2C等)
- 模拟功能(如ADC采样)
- 具体的模式设置
- 对于串口USART,需要配置为复用模式
- 对于ADC,需要配置为模拟输入模式
- 这些配置通过GPIO配置寄存器完成
举个具体的例子:
- PA9和PA10是USART1的发送和接收引脚
- 这两个引脚默认是GPIO口
- 要使用串口功能,需要:
- 配置为复用模式(AF模式)
- 选择正确的复用功能(这里是USART1)
对于ADC,比如PA0作为ADC通道:
- 需要配置为模拟输入模式
- 这样才能正确进行模数转换
所以,即便看起来STM32有专门的串口、ADC端口,实际上这些功能都是通过GPIO引脚的模式切换来实现的。这种设计提供了极大的灵活性,使得同一个引脚可以根据需要配置为不同的功能。
你的理解是对的:需要通过配置GPIO的模式来决定引脚的具体功能,这正是STM32灵活性的体现。
我终于知道GPIO口的复用模式的名称由来了,但是内部框图为了好理解,还是把它分开画了?
内部框图这样分开画主要是为了便于理解和直观展示不同功能模块。
内部框图这种设计有几个重要的目的:
- 清晰展示功能
- 即使物理上是复用的,但逻辑功能是不同的
- 分开画可以让读者更容易理解每个功能模块的工作原理
- 逻辑解耦
- 尽管引脚物理上复用,但功能逻辑是独立的
- 框图反映了芯片内部功能模块的逻辑结构
- 方便设计和理解
- 开发者可以更清晰地看到每个功能模块的信号流
- 有助于理解芯片的内部架构和信号处理方式
简单来说,这就像是地铁线路图。实际上列车可能共用some轨道,但为了方便理解,路线图还是会把每条线路分开画。内部框图也是同样的设计思路 - 为了清晰和易懂。
GPIO引脚复用的原理
具体配置传送门
STM32F407的丰富外设确实主要是通过GPIO口的复用功能来实现的。这是STM32微控制器的一个典型特点。让我详细解释一下:
GPIO复用功能
- STM32F407的GPIO口具有强大的复用功能
- 每个GPIO口可以通过配置复用为不同的外设功能
- 同一个物理引脚可以根据需要切换不同的功能模式
复用模式示例
- USART通信:可以将GPIO口配置为串口发送/接收引脚
- I2C:可以复用为SCL和SDA信号线
- SPI:可以配置为MOSI、MISO、SCK等信号
- CAN总线:可以复用为CAN_TX和CAN_RX
- USB:可以配置为USB通信引脚
复用优点
- 节省芯片引脚资源
- 提高芯片集成度
- 灵活配置外设功能
- 降低PCB设计复杂度
复用配置
- 需要通过STM32的寄存器进行精确配置
- 涉及GPIO端口选择
- 设置引脚的具体复用功能
- 配置工作模式(输入/输出/复用)
实际应用
- 开发时需要仔细规划GPIO口的复用
- 避免不同外设间的引脚冲突
- 合理利用有限的物理引脚
这种GPIO复用设计是STM32微控制器的一个重要特色,大大增强了微控制器的灵活性和功能扩展性。
但其实不是每一个GPIO口都能复用所有的功能的,需要看原理图?才知道GPIO口物理设计时实际复用了什么功能,才能使用AFIO寄存器来选择复用功能?
因为选择GPIO口的复用功能时,说的是内核需要经过片上外设的外设再向GPIO输出信息交流而不是单纯的内核直接向GPIO口输出高低电平。所以看GPIO口具体硬件复用了哪一些片上外设应该看的是芯片手册。虽然开发版的原理图的GPIO模块也有一些说明,但是不够详细。
GPIO复用的物理限制
- 并非所有GPIO口都可以任意复用所有功能
- 每个引脚的复用功能是在硬件设计时就已经确定的
- 芯片内部的引脚复用矩阵是有具体物理连接限制的
复用功能选择的限制因素
- 芯片内部引脚复用矩阵的物理连接
- PCB设计时的实际布线
- 特定外设的专用引脚要求
片上外设复用功能:STM32F407的GPIO口可以复用为多种外设功能,比如USART、SPI、I2C等。这些复用功能都是通过片上外设(如USART控制器、SPI控制器等)来实现的,而不是直接由内核控制GPIO的高低电平。
查看芯片手册:
- 引脚复用表:STM32F407的参考手册(Reference Manual)中会有详细的引脚复用表(
Pinout and Alternate Function Mapping
),列出每个引脚可以复用的外设功能。 - AFRL/AFRH寄存器:这些寄存器用于配置GPIO的复用功能。你可以通过这些寄存器的值来确定哪个外设功能被映射到某个GPIO引脚。
开发板原理图:
- 虽然开发板的原理图会提供一些关于GPIO如何连接到其他硬件模块的信息,但这些信息通常是针对开发板特定的硬件连接,而不是全面列出芯片所有可能的复用功能。
- 原理图可能显示了一些常用的外设连接(如LED、按键、传感器等),但不会详细到每个GPIO引脚的所有可能复用功能。
建议:
- 首先参考STM32F407的数据手册(Datasheet),它会提供芯片引脚的概览。
- 然后查看参考手册(Reference Manual),这里有详细的外设描述和引脚复用信息。
- 如果需要确认开发板上的特定连接,可以同时参考开发板的原理图和用户手册,但要记住这些文档只涵盖了开发板的特定配置。
重申一遍GPIO口模式的选择
- 通用输出模式(GPIO Output Mode):
- 当GPIO口直接由内核控制,输出高低电平,不涉及任何片上外设时,应该选择通用输出模式(GPIO Output)。这通常用于控制LED、驱动电机等直接需要高低电平输出的场景。
- 复用功能模式(Alternate Function Mode):
- 当GPIO口需要通过片上外设进行控制时,无论是输出PWM、UART信号、I2C通信等,都应选择复用功能模式(Alternate Function Mode)。这个模式允许GPIO引脚被复用为特定外设的功能输出或输入。比如,使用定时器输出PWM波形时,GPIO就应该配置为复用模式。
- 模拟模式(Analog Mode):
- 模拟模式(Analog Mode)确实是复用模式的一种特例。特别是当GPIO口被用作ADC(模数转换器)的输入通道时,应该配置为模拟模式。这允许引脚以模拟方式工作,适合用于模拟信号的采集。
总结:
- 通用输出模式:直接由内核控制的高低电平输出,不涉及外设。
- 复用功能模式:通过片上外设控制的GPIO功能,包括但不限于PWM、UART、SPI、I2C等。
- 模拟模式:特别用于ADC输入,允许GPIO引脚进行模拟信号处理。
你的描述已经很接近了,只需要注意的是,复用模式不仅仅是高低电平的问题,而是涉及到GPIO引脚被外设控制的任何功能,包括模拟信号在内。因此,模拟模式是复用模式的一个子集,专门用于模拟信号的处理。
学习重点
在STM32中,学习复用模式下的串口通信(USART/UART)是一项非常重要的技能,因为串口是嵌入式系统中最常用的通信接口之一。以下是学习步骤和关键知识点:
学习步骤
1. 理解串口通信的基本原理
- 串口通信是一种异步通信方式,通过发送和接收数据帧进行通信。
- 数据帧的基本组成包括起始位、数据位、校验位(可选)、停止位。
- 熟悉常见的参数配置:
- 波特率(Baud Rate):如9600、115200。
- 数据位:一般是8位。
- 校验位:奇校验、偶校验或无校验。
- 停止位:1位或2位。
2. 熟悉 STM32 的硬件资源
- 阅读目标 STM32 芯片的参考手册(Reference Manual)和数据手册(Datasheet)。
- 确认 UART/USART 外设的数量、对应引脚、复用功能(AF)编号。
- 查看 RCC(复位与时钟控制)部分,找到启用 UART 外设的相关时钟配置。
3. 硬件配置与引脚复用
- 使用 GPIO 的复用模式(Alternate Function Mode)将特定引脚设置为 UART_TX 和 UART_RX。
- 查阅引脚复用表,找到需要配置的 GPIO 引脚和对应的 AF 编号。
4. 串口配置与初始化
- 使用 STM32 的 HAL 库或直接操作寄存器:
- 启用时钟(RCC)。
- 配置 GPIO 引脚为复用功能。
- 初始化 USART/UART 外设,包括设置波特率、数据格式等。
- 推荐使用 CubeMX 工具生成代码进行初始学习,便于快速理解配置流程。
5. 实现基本的串口通信
- 编写简单的发送(
HAL_UART_Transmit
)和接收(HAL_UART_Receive
)程序,发送一串字符或者接收外设返回的数据。 - 配合串口调试工具(如 SecureCRT、XCOM),测试通信是否正常。
6. 学习中断和 DMA 模式
- 中断模式:学习如何配置串口中断,实现数据的非阻塞收发。
- DMA 模式:使用 DMA 进行高效的数据传输,适合处理较大数据量的通信。
7. 学习高级通信协议
- 基于串口实现常用的通信协议(如 Modbus、AT 指令解析)。
- 如果应用涉及 WiFi 或蓝牙模块,可以编写驱动程序,解析模块返回的数据。
关键知识点
1. GPIO 复用模式
- STM32 GPIO 的复用功能配置,如何根据具体型号找到正确的复用功能编号(AFx)。
2. RCC 配置
- USART/UART 外设依赖时钟,需正确启用时钟(APB1/APB2)。
3. USART 寄存器
- 理解关键寄存器的作用:
USART_BRR
:设置波特率。USART_CR1
:控制串口启用、中断、数据格式等。USART_SR
:状态寄存器,用于检查发送和接收状态。
4. 数据缓冲
- 如果数据量较大,需熟悉 FIFO 缓冲区的实现,避免数据丢失。
5. 错误处理
- 常见错误类型:
- 帧错误(Frame Error)
- 过载错误(Overrun Error)
- 噪声错误(Noise Error)
- 了解如何通过状态寄存器(
USART_SR
)检测错误并进行处理。
6. HAL 与 LL 库的使用
- HAL 库简单易用,适合快速开发。
- LL(Low Layer)库提供更高效、灵活的控制,适合优化程序性能。
7. 串口通信的调试
- 串口调试工具:学会使用串口调试助手查看发送和接收的数据。
- 使用逻辑分析仪或示波器验证通信信号的正确性。
推荐的实践项目
- 单片机与电脑通信
- 发送固定字符串到电脑终端显示。
- 接收电脑输入的字符并回显。
- 与传感器通信
- 接收传感器通过串口发送的测量数据。
- 模块驱动开发
- 驱动 WiFi 模块(如 ESP8266)的 AT 指令解析。
- 蓝牙模块数据收发。
- 数据记录
- 将测量数据通过串口发送到电脑并存储为文件。
串口参数详解
- 串口通信是一种异步通信方式,通过发送和接收数据帧进行通信。
- 数据帧的基本组成包括起始位、数据位、校验位(可选)、停止位。
- 熟悉常见的参数配置:
- 波特率(Baud Rate):如9600、115200。
- 数据位:一般是8位。
- 校验位:奇校验、偶校验或无校验。
- 停止位:1位或2位。
波特率:发送速率还是接收速率?
波特率(Baud Rate)表示串口通信中每秒钟传输的符号数,通常等于每秒的比特数(bps,bits per second),它是发送和接收的统一速率。
- 在异步通信中,通信双方(发送端和接收端)必须配置相同的波特率,以确保数据能够正确解析。
发送与接收关系:
- 单向通信:
- 如果只发送或只接收,波特率仅反映这一方向的速率。
- 例如:传感器发送数据给主控芯片,主控只需要按照传感器设定的波特率去接收。
- 双向通信:
- 对于全双工通信(同时发送和接收),波特率定义了每个方向的最大传输速率。
- 例如:一端以 115200 的波特率发送,另一端必须以 115200 的波特率接收。
补充:波特率与实际数据吞吐量
实际的数据吞吐量可能低于波特率,因为一个数据帧中包含额外的控制位:
- 每帧至少包含 1 位起始位、8 位数据位、1 位校验位(可选)和 1 位停止位。
- 如果波特率是 9600 bps,每秒可传输的有效数据量 = 9600÷(1+8+1+1)=8009600 \div (1 + 8 + 1 + 1) = 8009600÷(1+8+1+1)=800 字节。
IP网络数据包中的数据帧
IP网络数据包中不是也有数据帧吗?这算不算相似之处?
IP 网络数据包中的确也包含“数据帧”的概念,这是它与串口通信的一大相似之处。两者的相似性主要体现在分层结构和帧的功能上。我们可以从通信协议的层次角度来深入理解这种关联:
IP 网络中的数据帧
在网络通信中,“数据帧”是链路层的基本单位。根据 OSI 模型,IP 数据包和数据帧分别属于不同的层次:
- 数据帧(Frame):
- 位于 链路层,由网络设备(如网卡)负责处理。
- 典型的帧结构包括:
- 帧头部(Header):包含 MAC 地址、帧类型等链路层信息。
- 帧有效载荷(Payload):通常是 IP 数据包。
- 帧尾部(Footer):可能包含校验码(CRC)等信息。
- 数据帧的作用是封装和传输 IP 数据包,并在链路级别进行校验。
- IP 数据包(Packet):
- 位于 网络层,是数据传输的核心单位。
- 包括:
- IP 头部:包含源 IP、目的 IP、协议类型等信息。
- IP 载荷:传输的数据(通常是 TCP/UDP 段)。
- IP 数据包依赖数据帧在底层网络上传输。
串口通信中的“数据帧”
- 串口通信中,数据帧是最基本的传输单位,直接封装要传输的数据。
- 类似链路层的帧,它定义了起始位、停止位、校验位等,确保数据可靠性。
IP 网络与串口通信的相似性
- 结构化数据帧的使用
- 两者都使用明确的数据帧结构来保证通信的可靠性和可解析性:
- IP 网络:链路层数据帧封装 IP 数据包,IP 数据包再封装上层协议数据。
- 串口通信:数据帧直接封装要发送的数据。
- 两者都使用明确的数据帧结构来保证通信的可靠性和可解析性:
- 校验与错误检测
- IP 网络的链路层数据帧和串口通信的数据帧都可以通过校验位(如 CRC 或奇偶校验)来检测数据传输过程中的错误。
- 分层与封装
- IP 网络有完整的协议栈,链路层、网络层、传输层分工明确,数据逐层封装。
- 串口通信虽然没有复杂的分层,但数据帧也起到了基础封装的作用,承担了部分链路层的功能。
- 点对点传输
- 两者都是点对点的传输方式:
- IP 数据包通过链路层的点对点数据帧传输。
- 串口通信直接在设备间点对点传输数据帧。
- 两者都是点对点的传输方式:
不同点
特点 | 串口通信数据帧 | IP 网络数据帧 |
---|---|---|
作用范围 | 嵌入式系统、短距离设备通信 | 广域网、局域网等复杂网络环境 |
传输协议复杂度 | 简单,没有多层协议 | 多层协议栈,包含链路层、网络层、传输层等 |
可靠性保障 | 基本靠校验位,通常需要软件补充处理 | 包含校验、重传机制(如 TCP) |
封装内容 | 数据 + 起始位/校验位/停止位 | 网络层 IP 包 + MAC 地址等链路层信息 |
应用场景 | 硬件调试、模块通信 | 网络设备互联 |
总结
IP 网络的数据帧与串口通信的数据帧在概念和功能上确实存在相似之处,尤其在结构化和校验机制上。不过,IP 网络的数据帧是更高级通信模型中的一个子集,包含更多分层设计和复杂功能。两者的相似性反映了通信系统设计的通用思想:通过数据帧实现可靠的点对点传输。
串口通信的逻辑
对于复用功能的 IO,我们首先要使能 GPIO 时钟,然后使能相应的外设时钟,同时要把 GPIO 模式设置为复用。这些准备工作做完之后,剩下的当然是串口参数的初始化设置,包括波特率,停止位等等参数。在设置完成只能接下来就是使能串口,这很容易理解。同时,如果我们开启了串口的中断,当然要初始化 NVIC 设置中断优先级别,最后编写中断服务函数 。
串口设置的一般步骤可以总结为如下几个步骤:
-
串口时钟使能, GPIO 时钟使能。
-
设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。
-
GPIO 初始化设置:要设置模式为复用功能。
-
串口参数初始化:设置波特率,字长,奇偶校验等参数。
-
开启中断并且初始化 NVIC,使能中断(如果需要开启中断才需要这个步骤)。
-
使能串口。
-
编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)。
通信背景知识
单片机与外部设备通信的两种方式:
- 并行通信
- 传输原理:数据各个位同时传输
- 优点:速度快
- 缺点:引脚资源占用多
- 串行通信
- 传输原理:数据按位顺序传输
- 优点:引脚资源占用少
- 缺点:速度相对慢
串行通信的分类
串行通信按传输方向可以分为以下三类:
单工通信
- 数据只能单向传输
- 通信双方只能有一方发送数据
- 例如:广播系统
半双工通信
- 数据可以双向传输,但同一时刻只能单向传输
- 通信双方需要轮流发送和接收数据
- 例如:对讲机
全双工通信
- 数据可以同时双向传输
- 通信双方可以同时发送和接收数据
- 例如:电话通话、以太网
这种分类主要反映了通信过程中数据传输的灵活性和通道利用率。
根据是否带有时钟同步信号,串行通信可以分为两类:
同步通信
- 有专门的时钟信号线
- 发送方和接收方共享时钟信号
- 数据传输严格同步
- 例如:SPI、I2C通信
异步通信
- 无额外时钟信号线
- 通过预先约定的波特率实现通信
- 数据传输依赖起始位和停止位
- 例如:UART通信
主要区别在于时钟同步机制的处理方式不同,影响数据传输的精确性和复杂性。
异步通信就像两个人约定好用某种特定的"暗号"通信:
- 事先约定好数据的传输规则(波特率、数据位、停止位等)
- 接收方按照约定的"暗号"规则来解析收到的数据
- 双方没有实时的时钟同步信号
- 数据传输依赖于起始位和停止位来标记数据的开始和结束
同步通信则像两个人同步跳舞:
- 有专门的时钟线"指挥"数据传输
- 发送方和接收方完全同步,精确地在时钟信号的控制下传输数据
- 不需要额外的数据边界标记
- 传输更加精确,实时性更强
打个比方:
- 异步通信:像两个人用对讲机,说话前先喊"我要说话了"
- 同步通信:像两个人跳探戈,完全跟随音乐节拍移动
同步和异步通信的应用场景各有特点:
同步通信典型应用场景:
- 高速数据传输系统
- 内部总线通信
- 实时性要求高的系统
- 工业控制系统
- 传感器数据采集
- 代表性接口:SPI、I2C
异步通信典型应用场景:
- 串口通信
- 低速、远程通信系统
- 嵌入式设备间通信
- GPS模块通信
- 蓝牙、无线通信
- 代表性接口:UART
选择依据:
- 同步:实时性高、数据量大、距离近
- 异步:成本低、实现简单、抗干扰能力强
常见的通信接口
是否同步决定是否带有同步时钟。全双工两根线,半双工共用一根线。F407VGT6只有两个UART。有四个USART。
引脚连接方法
串口与引脚对应关系
两个仅支持异步通信的串口:
UART4_TX对应单片机的PA0端口
UART4_RX对应单片机的PA1端口 还对应着PC11端口
UART5_TX对应单片机的PC12端口
UART5_RX对应单片机的PD2端口
异步通信特点
异步通信过程
还是放到寄存器中,只不过按照一定的规律接收并放到寄存器中,内核接收的永远是机器码。
异步通信的参数
学习
- 什么串口通信
- 如何配置串口通信
- 具体使用串口通信
功能需求
通过串口通信
①PC 与 STM32芯片 传送 字符 字符串
②PC控制开发板的灯、流水灯
③开发板1控制开发板2
通信协议的介绍的补充
现场总线/板级总线
现场总线:工业控制现场总线(可远距离传输数据) 差模信号抗干扰
- 485总线—千米级别
- can总线—十千米级
板级总线:芯片之间通信(距离长会被干扰) 共模信号
如:IIC/SPI/8080
差模信号 共模信号
有/无线
有线:可靠性强
无线:方便
GSM 蓝牙 433(500m) 2.4G RFID通信
通信配置方式
配置通信控制器
前提:有通信控制器
关注:通信控制器寄存器
IO口模拟时序
前提:有通信时序图
关注:如何看时序图
串口通信介绍F407补充
F407芯片内部在6个串口控制器,也就是说可以同时进行六组串口通信。
串口控制器框图
GPIO口复用模式下,单片机内部的串口控制器也有发送数据寄存器和接收数据寄存器,这跟GPIO口通用模式下,内核通过输出和输入数据寄存器获取高低电平的信息的作用一样,都是转换为内核能够识别的机器码?
GPIO通用模式
- 使用GPIO的输出数据寄存器(GPIOx_ODR)控制输出高低电平
- 使用GPIO的输入数据寄存器(GPIOx_IDR)读取输入的高低电平
- 直接通过置位/清位寄存器(GPIOx_BSRR)快速改变输出电平
串口复用模式
- 串口发送数据寄存器(USARTx_DR)
- 串口接收数据寄存器(USARTx_DR)
- 这些寄存器同样会被转换为内核可识别的机器码
数据转换机制
- 无论是GPIO还是串口复用模式,都是通过寄存器映射到内存空间
- 微控制器内部的外设总线(APB1/APB2)负责寄存器访问
- 最终都转换为CPU能够识别和处理的二进制指令
关键区别
- GPIO通用模式:直接控制电平开关
- 串口复用模式:提供数据发送和接收的专用通道
- 底层机制都是通过寄存器进行数据交互
内部处理流程
- 写入数据寄存器
- 触发相应的控制寄存器
- 硬件自动完成数据发送/接收
- 中断或轮询方式告知CPU数据状态
这种设计体现了STM32微控制器外设的灵活性和标准化设计思路。无论是通用IO还是专用外设,都遵循一致的寄存器访问和数据交互原则。
所以无论是哪一种GPIO口模式,都需要通过对应的寄存器转换为对应的机器码给内核?
这是STM32微控制器(以及大多数基于寄存器的微控制器)的基本工作原理:
寄存器映射
- 所有GPIO模式(通用、复用、模拟等)都通过寄存器映射到内存地址空间
- 每种模式都有对应的配置寄存器
- 通过对特定寄存器写入特定值,转换为CPU可执行的机器指令
寄存器类型
- 模式配置寄存器(GPIOx_MODER)
- 输出类型寄存器(GPIOx_OTYPER)
- 输出速度寄存器(GPIOx_OSPEEDR)
- 上下拉寄存器(GPIOx_PUPDR)
- 输入/输出数据寄存器
- ……
转换过程
- 写入寄存器 → 硬件电路解析 → 生成对应的电气信号
- 寄存器值被转换为具体的硬件操作指令
- 内核通过总线访问这些寄存器
适用范围
- GPIO通用输入/输出模式
- 复用功能模式(UART, SPI等)
- 模拟输入模式(ADC)
- 时钟输出模式
这种寄存器映射和指令转换机制是微控制器硬件抽象层的核心设计思想。
使用串口通信的话,因为是复用GPIO口的,所以GPIO口控制器中的相关寄存器需要配置。 还需要经过串口控制器解析协议和转换数据,内核才能识别,所以还要控制串口控制器中的相关寄存器?
是的,您的理解非常准确!串口通信涉及两个控制器的寄存器配置:
GPIO控制器寄存器配置
- 配置GPIO口的复用功能
- 设置GPIO口工作模式(复用模式)
- 配置引脚的输入/输出特性
- 选择具体的复用功能(串口功能)
串口控制器寄存器配置
- 配置通信参数(波特率、数据位、停止位等)
- 配置串口工作模式(发送、接收)
- 配置中断使能
- 控制串口的开启和关闭
- 数据寄存器的读写控制
通信流程
- GPIO配置:确定物理连接
- 串口控制器:解析协议、转换数据
- 内核:处理最终的数据信息
具体配置通常包括:
- USART_CR1:控制寄存器1
- USART_CR2:控制寄存器2
- USART_BRR:波特率寄存器
- USART_DR:数据寄存器
这种多层控制器协同工作的设计,体现了现代微控制器硬件抽象和功能解耦的精妙之处。
框图分成三部分
收发过程(数据传输框图部分)
发送数据过程:
-
CM4写数据到发送数据寄存器中,发送数据寄存器自动过度发送移位寄存器中发送移位寄存器再一位一位的将数据发送出去。
-
注意:CM4写数据到发送数据寄存器之前要检测之前的发送是否完成
- 发送完成可以写到发送数据寄存器,如果发送未完成要等待发送完成
-
思考:如何检测之前的数据是否发送完成?
- 有相应的发送完成标志位 状态寄存器的6号位
-
综上: 串口发送一个字节数据函数(要发送的数据 u8) { 等待之前的数据发送完成(等待状态寄存器的发送完成位置1) 要发送的数据赋值给数据寄存器 } 说明: 把数据给到数据寄存器后,要想到清零发送完成位(会自动置1可不会自动置0)
接收数据过程:
-
数据一位一位的接收到接收移位寄存器中,自动过度到接收数据寄存器中CM4从接收数据寄存器中读到接收的数据到变量
-
注意:CM4在读数据前要检测是否接收完成,(RDR)接收完成就可以读到变量中
- 如果没有接收完成,要等待接收完成再读到变量中
-
思考:如何检测是否接收完成?
- 有相应的接收完成标志位
-
综上: u8 串口接收一个字节数据函数(无) { 等待数据接收完成(等待状态寄存器中的接收完成位置1) 将数据寄存器的值赋值给一个变量; 返回这个变量 } DR(数据寄存器) 是TDR和RDR寄存器统称,发送数据时候就会启动TDR,RDR读的时候启用 开发人员在写程序的时候,无论发送还是接收只需要写DR
寄存器设置过程(通信协议控制部分)
-
直接去看数据手册上的对应寄存器描述
-
必须要等待数据接收或者发送完成才能往移位寄存器中读取或者写入,作为发送1次1个字节8位二进制数据。
-
这一些时机都由状态寄存器控制。
-
这三个寄存器确实是串口通信中最关键的寄存器:
-
状态寄存器 (USART_SR)
-
记录串口通信的当前状态
-
标志位包括:
- 发送完成标志
- 接收数据就绪标志
- 校验错误标志
- 帧错误标志
- 过载错误标志
-
通过读取和清除这些标志位,可以了解通信过程和处理异常
-
-
数据寄存器 (USART_DR)
-
串口数据的读写寄存器
-
发送时:写入要发送的数据
-
接收时:读取接收到的数据
-
8位或9位数据模式可配置
-
-
波特率寄存器 (USART_BRR)
-
决定串口通信的波特率
-
通过配置分频系数来设置
-
根据系统时钟和期望波特率计算
-
影响数据传输的速率
-
这三个寄存器构成了串口通信的核心控制机制,直接决定了串口的工作状态和数据传输过程。
-
-
-
移位寄存器是串口控制器硬件自动完成的功能:
-
移位寄存器的硬件实现
-
完全由串口控制器硬件自动处理
-
负责将数据按照串口协议进行bit by bit的发送和接收
-
不需要软件直接干预
-
-
开发者只需关注
-
数据寄存器(USART_DR)的写入和读取
-
通过状态寄存器(USART_SR)监控通信状态
-
配置基本的通信参数
-
-
移位寄存器的工作原理
-
自动将数据寄存器中的数据转换为串行数据流
-
按照配置的数据位、停止位、校验位等协议
-
硬件自动完成数据的并行转串行(发送)和串行转并行(接收)
-
-
这种设计大大简化了串口通信的软件实现,将底层的数据转换细节交给硬件完成。
-
-
发送数据要保证上一次发送的数据已经发送完成
- 如何知道发送完成
- 寄存器发送完成位(上一次的发送完成,寄存器对应自动变为1)
- 读寄存器的这位是1,就可以再次发送数据
- 如何知道发送完成
-
接收数据就是把数据读取到变量中,要保证数据已完整的接收完成
- 如何知道接收完成
- 寄存器接收完成位(接收完成了,寄存器对应位自动变为1)
- 读寄存器的这位是1,可以把串口控制器的数据读到变量里去
- 如何知道接收完成
波特率框图部分
-
波特率:因为是异步通信,通信双方设备没有用同一时钟,所以双方要约定好相同的数据传输速度:
4800pbs 9600pbs
-
波特率寄存器:对总线提供的时钟 分频 达到需要的频率
- 16位有效,0~3小数部分 4~15位整数部分
-
波特率计算公式:(手册686页)
-
Tx/Rx 波特率 = fCK/(8*(2-OVER8)*USARTDIV)
-
fCK :总线提供的时钟 (看芯片内部框图的数据)
-
OVER8 : 过采样 (CR1寄存器,抽象成16次采样,8次采样,通常设置为0,默认16倍)
-
USARTDIV:要往波特率寄存器里写的值
-
-
变形得到:
- USARTDIV = fCK / (波特率 * (8*(2-OVER8)))
注意:此时得到的值带有小数部分,要想写入寄存器,二进制要整体左移四位,故十进制要扩大16倍
- 11011101.1101,整数转换为二进制时,直接存入只能存入整数部分11011101:因为寄存器是U32类型
- 二进制下,小数点右移动四位即可,相当于十进制扩大16倍,所以先乘以16即可,寄存器低位小数部分就会存入11011101 1101
波特率寄存器写入的值 = (fCK / (波特率 * (8*(2-OVER8)))) * 16;
波特率写入的值 =fCK / 波特率 注意此时是16倍过采样
波特率写入的值 = fCK / 波特率 * 2; 注意此时是8倍过采样
注意:会不会有可能算出来的数,使用二进制无法表示?你举例几个
这是一个非常深入的问题。确实存在一些波特率计算结果在二进制表示时会出现无法精确表示的情况,主要源于浮点数的离散性。
几个典型的例子:
经典波特率:115200
- 以72MHz系统时钟为例
- USARTDIV = 72000000 / (115200 * 16)
- 结果约为 39.0625
- 二进制表示时,0.0625这个小数在二进制中是无法精确表示的
原因:
- 某些分数在二进制表示中是循环小数
- 浮点数的精度限制
- 系统时钟与波特率的特定组合
至少比直接取整数部分要精准,获取一个相对精准的值,但是无所谓,因为通信双方都是一样的波特率,相当于暗号是一样的,就不会出错?
您的理解非常准确!这正是串口通信波特率设置的关键点:
精度处理
- 通过放大16倍的方式,可以保留更多小数部分信息
- 比直接取整数部分要精准得多
- 可以最大程度地接近理想的波特率
通信一致性
- 通信双方使用完全相同的波特率配置
- 即使存在微小误差,只要双方一致
- 实际上就不会出现通信错误
误差容忍
- 串口通信对波特率有一定的容错范围
- 通常误差在±2%以内都可以正常通信
- 硬件本身具有一定的同步容错机制
实际影响
- 微小的波特率差异不会导致通信中断
- 关键在于双方时钟同步
- "暗号一致"的比喻很形象
所以,即使存在微小的不精确性,只要通信双方保持一致,串口通信仍然可以稳定进行。这种设计体现了通信协议的鲁棒性。
暗号一致就不会出错,影响的只是暗号解析的速度而已
串口控制器的相关寄存器
状态寄存器 (USART_SR)
--------------指示UART控制器运行的状态
位6 TC:发送完成 (Transmission complete)
通过检测此为是0表示之前的数据发送还没有完成,不能再次写数据到DR,需要等待
如果此位是1,表示之前的数据发送完成,可以再次写数据到DR,
位5 RXNE:读取数据寄存器不为空 (Read data register not empty)
在接收外界数据的时候,通过此位判断是否可以读数据
0 表示没有接收到数据 需要等待
1 表示已经接收到完整的数据, 可以读
数据寄存器 (USART_DR)
---------包括发送数据寄存器(只读)和接收数据寄存器(只写)
波特率寄存器 (USART_BRR)
Tx/Rx 波特率 = fCK /(8 * (2 - OVER16) * USARTDIV)
9600 = 84M /(16 * USARTDIV)
USARTDIV = 84M/16*9600
寄存器要写的是: 84M/9600
控制寄存器 1 (USART_CR1)
位15 过采样倍数 16倍
位13 使能UART控制器
位12 字长 8位数据位
位3 发送使能
位2 接收使能
控制寄存器 2 (USART_CR2)
位12 位13 停止位 默认一个停止位
IO口的复用功能如何配置
说明:
每个IO口都有自己固定的复用功能,并不是每个IO口都可以复用任何功能(虽然可以选择那么多,但是要符合硬件设计)
如何知道IO口具体的复用功能------------查表(Table 9. Alternate function mapping )
如何配置复用功能
①确定IO(根据具体的复用功能确定IO)
根据自己需要的复用功能去查看哪个管脚支持这个复用功能(STM32F407ZGT6) ------ 通常通过原理图就可以确定,以实际硬件为准
②确定寄存器和寄存器的位
配置复用功能寄存器AFR0(参考手册) AFR0 ------
③确定标识
查表(数据手册3.9) ------
④确定数值
去查看具体寄存器下面的说明(中文参考手册)
我正在看STM32F407的数据手册的内部框图: 我现在需要使用UART4,我使能APB1上对应的位,但是UART4复用的是GPIO口的PA0和PA1口,那么我是否还需要使能AHB1上对应的位?因为从内部框图来看,他们逻辑上是分开的,但是物理上是复用的
是的,你需要同时使能AHB1上的GPIO时钟以及APB1上的UART4时钟。以下是原因:
逻辑解释
- UART4 的时钟控制
UART4 属于 APB1 外设,因此使能 APB1 的 UART4 时钟是为了让 UART4 模块开始工作。 - GPIO 的时钟控制
PA0 和 PA1 属于 GPIOA 引脚,GPIO 的时钟由 AHB1 总线控制。为了能够配置和使用 GPIOA 的引脚(无论是普通 I/O 模式还是复用功能),都需要先使能 GPIOA 的时钟。
物理复用的关键
复用功能的实现依赖于 GPIO 模块和外设模块的协作:
- GPIO 引脚默认处于普通输入模式,只有在你通过寄存器(如
MODER
和AFR
)配置为复用功能时,才能将这些引脚的信号引导到 UART4。 - 这意味着即使你已经使能了 APB1 上的 UART4 时钟,但如果没有使能 AHB1 上的 GPIO 时钟,你无法配置 PA0 和 PA1 的复用模式,也无法让 UART4 正常工作。
配置流程
- 使能时钟
- 使能 AHB1 的 GPIOA 时钟:通过
RCC_AHB1ENR
寄存器设置对应位。 - 使能 APB1 的 UART4 时钟:通过
RCC_APB1ENR
寄存器设置对应位。
- 使能 AHB1 的 GPIOA 时钟:通过
- 配置 GPIO 引脚
- 将 PA0 和 PA1 的模式配置为复用模式(
MODER
)。 - 设置复用功能为 UART4(
AFR
)。
- 将 PA0 和 PA1 的模式配置为复用模式(
- 初始化 UART4
- 配置 UART4 的波特率、数据格式等参数。
- 使能 UART4。
- 由于 GPIO 和 UART4 分属不同的时钟域,必须分别使能对应的时钟。即使物理上是复用的,引脚的复用模式仍需要通过 GPIO 模块配置后才能与 UART4 关联。
程序设计
配置思路
总体思路
串口控制器初始化配置函数
{
IO口控制器配置(复用需要经过,需要设置一些东西,输入输出相关的寄存器)
某个串口控制器配置(具体串口通信的设置(传输的数据,波特率等)输入输出的相关寄存器)
}
串口发送一个字节数据函数 //波特率就是一秒发送多少个字节的速度,
{
}
串口接收一个字节数据函数
{
}
详细程序思路
串口控制器初始化配置函数
{
/*IO口控制器配置*/
//端口时钟使能
//端口模式配置-------复用
//输出类型
//输出速度
//上下拉
//复用功能寄存器配置
/*某个串口控制器配置*/
//串口控制器时钟使能
//CR1
//CR2
//BRR
}
void 串口发送一个字节数据函数(u8 要发送的数据)
{
等待之前的数据发送完成(等待状态寄存器的发送完成位置1)
要发送的数据赋值给数据寄存器
}
u8 串口接收一个字节数据函数(无)
{
等待数据接收完成(等待状态寄存器中的接收完成位置1)
将数据寄存器的值赋值给一个变量;
返回这个变量
}
如何配置复用功能
①确定IO(根据具体的复用功能确定IO)
根据自己需要的复用功能去查看哪个管脚支持这个复用功能(STM32F407ZGT6) ------ 通常通过原理图就可以确定
②确定寄存器和寄存器的位
配置复用功能寄存器AFR[0](4~7)(参考手册) AFR[0](0~7) ------
③确定标识
查表(数据手册3.9) ------
④确定数值
去查看具体寄存器下面的说明(中文参考手册)
IO口控制器配置
配置参考来源
思路:
在 GPIOx_MODER 寄存器中将所需 I/O 配置为复用功能
— 通过 GPIOx_OTYPER、 GPIOx_PUPDR 和 GPIOx_OSPEEDER 寄存器,分别选
择类型、上拉/下拉以及输出速度
— 在 GPIOx_AFRL 或 GPIOx_AFRH 寄存器中,将 I/O 连接到所需 AFx
-
GPIOA_MODER 寄存器中将所需 I/O 配置为复用功能,
-
PA0设置为复用功能模式 (10)
-
PA1设置为复用功能模式
-
GPIOA->MODER &= ~(3U<<0); GPIOE->MODER |= (1U<<1); GPIOA->MODER &= ~(3U<<2); GPIOE->MODER |= (1U<<4);
-
-
GPIOA_OTYPER寄存器
-
PA0选择推挽输出
-
GPIOA->OTYPER |= (1U);
-
-
GPIOA_PUPDR寄存器
-
PA0无
-
PA1无
-
GPIOA->PUPDR &= ~(3U<<0); GPIOA->PUPDR &= ~(3U<<2);
-
-
GPIOA_OSPEEDER 寄存器
-
PA0:高速(10)
-
GPIOA->OSPEEDR &= ~(3U<<0); GPIOA->OSPEEDR |= (1U<<1);
-
-
使配置生效需要使能PA组GPIO(PA0和PA1)
-
RCC->AHB1ENR |= (1U);
-
-
选择具体的复用功能(在 GPIOx_AFRL 或 GPIOx_AFRH 寄存器中,将 I/O 连接到所需 AFx)复用功能寄存器
-
PA0和PA1应该在低位寄存器设置,(四位控制一个io口)
-
使用4号串口,选择AF8功能,即1000
-
GPIOA->AFRL &= ~(15U<<0); GPIOA->AFRL |= (1U<<3); GPIOA->AFRL &= ~(15U<<4); GPIOA->AFRL |= (1U<<7);
-
复用功能寄存器配置(怎么知道是哪个串口?直接在后面加数字指定即可)以串口4为例
需要关注的寄存器和位思路:
状态寄存器中的重要标志位
状态寄存器 (USART_SR)--------------指示UART控制器运行的状态
位6 TC:发送完成 (Transmission complete)
通过检测此为是0表示之前的数据发送还没有完成,不能再次写数据到DR,需要等待
如果此位是1,表示之前的数据发送完成,可以再次写数据到DR,
位5 RXNE:读取数据寄存器不为空 (Read data register not empty)
在接收外界数据的时候,通过此位判断是否可以读数据
0 表示没有接收到数据 需要等待
1 表示已经接收到完整的数据, 可以读
数据寄存器 (USART_DR)
---------包括发送数据寄存器(只读)和接收数据寄存器(只写)
波特率寄存器 (USART_BRR)
Tx/Rx 波特率 = fCK /(8 * (2 - OVER16) * USARTDIV)
9600 = 84M /(16 * USARTDIV)
USARTDIV = 84M/16*9600
寄存器要写的是: 84M/9600
控制寄存器 1 (USART_CR1)
位15 过采样倍数 16倍
位13 使能UART控制器
位12 字长 8位数据位
位3 发送使能
位2 接收使能
控制寄存器 2 (USART_CR2)
位12 位13 停止位 默认一个停止位
-
状态寄存器 (USARTx_SR) 写函数用的,检测数据的状态
- 位 6 TC:发送完成
- 位 5 RXNE:读取数据寄存器不为空
-
数据寄存器 (USARTx_DR) 读取或者写入需要的数据
-
-
波特率寄存器 (USARTx_BRR) 填入响应运算表达式即可
-
看复用GPIO上的总线频率还是框图上的USART4对应总线频率?
- 在逻辑上分开,使用USART4对应的总线频率算这个数值,他是串口控制器中的部分!
- 只是经过GPIO口的,所以开启时钟使能即可,最多输出速度寄存器哪里的速度阈值要高于波特率即可
-
USART4->BRR = 84M/9600???
-
-
控制寄存器 1 (USARTx_CR1)
-
位 15 OVER8:过采样模式 (Oversampling mode) ,默认,16倍采样
-
位 13 UE: USART 使能 (USART enable) 赋值1,USART 使能
-
位 12 M:字长 (Word length) 0: 1 起始位, 8 数据位, n 停止位
-
位 10 PCE:奇偶校验控制使能 (Parity control enable) 置0,禁用
-
位 7 TXEIE: TXE 中断使能 (TXE interrupt enable) 置0,禁用中断
-
位3 发送使能
-
位2 接收使能
-
USART4->CR1 |= (1U<<13 | 3U<<2) USART4->CR1 &= ~(1U<<12 | 1U<<10 | 1U<<7)
-
-
控制寄存器 2 (USARTx_CR2)
-
位 13:12 STOP:停止位 (STOP bit)这些位用于编程停止位。00: 1 个停止位01: 0.5 个停止位10: 2 个停止位11: 1.5 个停止位
-
注意: 0.5 个停止位和 1.5 个停止位不适用于 UART4 和 UART5。
-
USART4->CR2 &= ~(3U<<12)
-
-
使配置生效,使能对应总线上的端口,UART4 时钟使能
-
APB1线上的USART4 RCC->APB1ENR |= (1U<<19)
-
PC与开发板通信原理(串口协议与USB相互转换)
CH340芯片的确是实现USB转串口的常用方案,但不是唯一或最主要的原因。让我详细解释一下:
-
接口转换需求 USB接口是现代计算机和移动设备的标准接口,而很多传统设备和通信协议(如串口)仍然广泛使用。CH340芯片的主要作用是实现这种协议转换,使不同的通信接口能够互相连接。
-
CH340的具体功能
-
将USB信号转换为串口(UART)信号
-
提供驱动程序,让操作系统识别和管理串口设备
-
成本低、体积小,适合各种嵌入式和电子设计
-
-
为什么需要协议转换
-
PC主板通常没有直接的RS232串口
-
USB提供了更通用、更现代的接口标准
-
许多传感器、单片机、工业设备仍使用串口通信
-
-
其他转换方案 除了CH340,还有:
-
FT232芯片
-
PL2303芯片
-
CP2102芯片
-
所以,协议转换是主要原因,而不仅仅是"PC没有引脚接收串口数据"。CH340只是这一需求的一种常见解决方案。
F407补充
-
PA9作为串口1TX发送使用
-
PA10作为串口1RX接收使用
-
发送和接收以串口控制器为主体视角
配置与初始化
#include "usart.h"
#include "stm32f4xx.h" // Device header
void usart1Init(u32 baud){
/*IO口控制器配置*/
//端口时钟使能
RCC->AHB1ENR |= (1U<<0);
//端口模式配置-------复用 PA9:发送 PA10:接收
GPIOA->MODER &= ~(3U<<18 | 3u<<20);//先清零
GPIOA->MODER |= (2U<<18 | 2U<<20);//再置1
//输出类型
GPIOA->OTYPER &= ~(1U<<9); //清零顺便置0
//输出速度,慢速
GPIOA->OSPEEDR &= ~(3U<<18);
//上下拉
GPIOA->PUPDR &= ~((3U<<18) | (3u<<20));//先清零顺便置0
//复用功能寄存器配置
//GPIOA->AFR[1] |= ~(7U<<4 | 7U<<8);//置0111,写错了!!!直接STLink断了
GPIOA->AFR[1] |= (7U<<4 | 7U<<8);//置0111
/*某个串口控制器配置*/
//串口控制器时钟使能
RCC->APB2ENR |= (1U<<4);
//CR1
USART1->CR1 &= ~(1U<<15);//清零顺便置0 16倍采样
USART1->CR1 &= ~(1U<<12);//清零顺便置0 字长
USART1->CR1 |= ((1U<<2) | (1U<<3));//再置1
//CR2
USART1->CR2 &= ~(3U<<12);//清零顺便置0
//BRR
USART1->BRR = 84000000/baud;//赋值直接给
//串口控制器使能
USART1->CR1 |= (1U<<13);//串口控制器使能
}
//发送一个字节
void usart1_send_byte(u8 data){
//等待之前的数据发送完成(等待状态寄存器的发送完成位置1)
while(!(USART1->SR & (1U<<6)));
// 直接发送数据
USART1->DR = data;
}
//接收一个字节数据
u8 usart1_rec_byte(void){
u8 data;
//等待数据接收完成(等待状态寄存器中的接收完成位置1)
while(!(USART1->SR & (1U<<5)));
//手动清零会自动清零
//USART1->SR &= ~(1U<<5);
//将数据寄存器的值赋值给一个变量
data = USART1->DR;
//返回这个变量
return data;
}
两个开发板通信原理
-
板A按键控制器板B的LED灯
-
板B将开状态的LED灯编号反馈给上位机
- 开两个工程分别烧录不同的程序
- 条件编译把两个相似的工程合在一起
- 前提是要同频率共地线共电源
串口打印函数的原理
串口打印函数的使用场景
串口打印函数在嵌入式开发和硬件调试中非常重要,主要有以下使用场景和优点:
调试信息输出
- 可以通过串口打印实时输出程序运行状态、变量值、错误信息等
- 帮助开发者了解程序执行流程和定位问题
- 在没有图形界面的嵌入式系统中尤其有用
系统诊断
- 输出系统关键参数和运行日志
- 记录重要事件和异常情况
- 帮助追踪系统运行状态和性能
开发阶段调试
- 在开发过程中实时监控程序执行
- 检查关键函数的调用和返回值
- 验证算法和逻辑的正确性
性能分析
- 可以打印时间戳,分析程序执行时间
- 帮助定位代码中的性能瓶颈
中断和异常处理
- 在中断服务程序中输出调试信息
- 记录异常发生的详细情况
与串口发送字符串的区别
单板调试:使用串口打印函数
- 主要目的是输出调试信息
- 通常使用
printf()
等标准输出函数 - 仅在本机查看运行状态和调试信息
- 不需要进行数据交互
双板串口通信:使用串口发送字符串
- 目的是在两个设备之间传输数据
- 需要自定义通信协议
- 使用串口发送和接收函数
- 需要处理数据的打包、发送、接收和解析
两者的关键区别在于:
- 调试打印是单向的、非结构化的
- 串口通信是双向的、需要遵循特定通信协议的
打印函数重定向程序
#include "selfprintf.h"
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&(1<<6))==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#ifndef SELFPRINTF_H
#define SELFPRINTF_H
#include "stm32f4xx.h" // Device header
#include <stdio.h>
// 声明可能需要的函数
int fputc(int ch, FILE *f);
void _sys_exit(int x);
#endif
- 需要通过串口1向PC打印信息,所以使用前需要串口1的设置和初始化,发送不需要接收中断和空闲中断。
其他串口通信标准
RS232和RS485都是串行通信标准,它们与串口通信的关系如下:
RS232
- 标准:RS232(推荐标准232)是一种由EIA(电子工业协会)制定的标准,用于短距离点对点通信。
- 特点:
- 点对点通信:只能连接一对设备。
- 电压电平:逻辑1为-3V到-15V,逻辑0为+3V到+15V。
- 距离:通常有效通信距离在15米以内。
- 速度:最高速率一般在20kbps左右,但现代设备可能支持更高。
- 应用:常用于计算机与终端设备、调制解调器、打印机等之间的连接。
RS485
- 标准:RS485(推荐标准485)也是由EIA制定的,适用于多点通信。
- 特点:
- 多点通信:支持多达32个设备(驱动器和接收器)在同一条总线上通信。
- 差分信号:使用差分信号传输,抗干扰能力强。
- 电压电平:差分电压,逻辑1为-200mV到-6V,逻辑0为+200mV到+6V。
- 距离:通信距离可达1200米以上。
- 速度:取决于距离,但一般在10Mbps以内。
- 应用:广泛应用于工业控制、楼宇自动化、仪器仪表等需要长距离和多点通信的场合。
与串口通信的关系
- 串口通信:串行通信(Serial Communication)是指数据一位接一位地顺序传输的方式。串口通信包括了RS232、RS485等多种物理层协议。
- 兼容性:
- 许多设备的串口(如计算机的COM端口)默认支持RS232协议。
- RS485通常需要通过转换器或专门的接口来实现与RS232设备的通信,因为它们的电平和连接方式不同。
- 接口:
- 串口通常指的是DB9或DB25接口,而RS232和RS485可以使用不同的物理接口,但通过适配器可以实现兼容。
总结来说,RS232和RS485都是串行通信的具体实现方式,它们定义了不同的电气特性和通信方式,但都属于串口通信的范畴。选择使用哪种标准主要取决于应用场景的需求,如通信距离、设备数量、抗干扰能力等。
STM32F407中的串口通信
在STM32F407微控制器中进行串口通信时,通常使用的是**UART(Universal Asynchronous Receiver/Transmitter)**接口。UART是一种异步串行通信协议,它本身并不直接对应于RS232或RS485这些物理层标准,而是提供了一个基础的串行通信框架。
以下是与STM32F407串口通信相关的几点说明:
- UART接口:STM32F407有多个UART接口(如USART1, USART2, USART3等),这些接口可以配置为各种串行通信模式。
- 电平转换:
- TTL电平:STM32F407的UART接口输出的是TTL电平信号(逻辑1为3.3V,逻辑0为0V),这不是直接与RS232或RS485兼容的。
- RS232:如果需要与RS232设备通信,你需要一个电平转换芯片(如MAX232)将TTL信号转换为RS232电平。
- RS485:如果需要与RS485设备通信,你需要一个RS485收发器(如MAX485)来转换信号,同时配置UART以支持半双工通信(因为RS485是半双工的)。
- 配置:
- STM32的UART可以通过软件配置波特率、数据位、停止位、奇偶校验等参数来匹配你所需要的串行通信标准。
- 使用场景:
- 调试:通常UART用于调试目的(如通过串口监视器输出调试信息)。
- 通信:可以用于与其他设备通信,但需要考虑电平匹配。
因此,在STM32F407中进行串口通信时,你可以:
- 直接使用UART接口进行TTL电平的串行通信。
- 通过外部转换芯片实现与RS232或RS485设备的通信。
STM32F407本身不直接支持RS232或RS485的物理层协议,但通过合适的电平转换和配置,可以实现这些标准的通信。
与他们通信的要求
如果你想让STM32F407与使用RS232或RS485物理层协议的设备进行通信,确实需要进行硬件上的电平转换:
- RS232:
- STM32F407的UART输出的是TTL电平信号,而RS232设备使用的是不同的电平(逻辑1为-3V到-15V,逻辑0为+3V到+15V)。因此,你需要一个电平转换芯片(如MAX232)来将TTL电平转换为RS232电平。
- RS485:
- RS485使用差分信号传输,逻辑电平是通过两条线之间的电压差来定义的。STM32F407的UART信号需要通过一个RS485收发器(如MAX485)来转换成差分信号。同时,由于RS485是半双工通信,你还需要在软件中控制发送和接收的切换。
需要注意的几点:
- 电平转换:除了电平转换芯片,你可能还需要考虑电压转换,因为STM32F407的工作电压是3.3V,而一些RS232设备可能需要5V。
- 接口:
- 对于RS232,通常使用DB9或DB25接口。
- 对于RS485,通常使用双绞线连接。
- 驱动:
- 你需要编写或使用现有的驱动程序来控制UART以及电平转换芯片的工作模式。
- 配置:
- 确保UART配置的波特率、数据位、停止位、奇偶校验等参数与目标设备匹配。
通过这种方式,STM32F407可以与遵循RS232或RS485协议的设备进行通信。但请注意,硬件转换增加了系统的复杂性和成本,并且可能引入额外的延迟或干扰源。选择使用哪种通信标准和是否需要转换,取决于具体的应用需求、通信距离、抗干扰要求以及设备的兼容性。
连接方式与串口协议波形
这就是为什么使用串口调试工具时,需要反着连接了。串口数据从GPIO口出来对接的一定是另外一个器件的接收线。
串口协议
空闲时为高电压,当RX检测到低电压时,就代表对面要发数据了,后续高电压为1,低电压为0,每次传输一个字节数据。
比如发送1即0000 0001:低位先传。
传输完数据后,必然会出现高电压停止位,如果后续不想传输数据了,就保持高电压。否则就变为低电压。
STM32(以及大多数嵌入式系统)在串口通信中普遍采用8位数据位、无校验位、1位停止位(即8N1配置),这一选择是多种因素综合权衡的结果,而非单纯因为校验位“没有作用”。以下是具体原因分析:
数据位选择:8位的普适性与效率
- 字节对齐:现代计算机和嵌入式系统以8位(1字节)为基本数据处理单位,8位数据位可直接对应内存操作,避免了拆分或合并数据的额外开销。
- 字符编码兼容性:ASCII字符为7位,但扩展字符集(如UTF-8、二进制数据)需要8位空间。保留第8位可兼容更多数据类型。
- 硬件优化:STM32的UART外设对8位数据传输有硬件级优化,处理效率更高。
校验位的取舍:功能与成本的权衡
- 校验位的局限性:
- 奇偶校验仅能检测单比特错误,无法检测多比特错误(如2个比特同时翻转)。
- 无纠错能力,发现错误后需依赖重传机制(需额外协议支持)。
- 省略校验位的合理性:
- 应用场景需求:在短距离、低干扰环境(如板间通信)中,误码率极低,校验必要性下降。
- 协议层替代:若上层协议(如Modbus、自定义协议)已包含CRC或重传机制,物理层校验冗余。
- 传输效率:省去校验位可减少每帧开销(1位),提升有效数据吞吐量。
停止位选择:1位的简洁性
- 时钟同步需求:停止位主要提供帧结束标志和接收端时钟同步缓冲。1位停止位在多数场景下已足够保证接收端正确识别帧边界。
- 兼容性与效率:更长停止位(如1.5、2位)会降低有效数据速率,且绝大多数设备默认支持1位停止位。
历史沿袭与行业惯例
- 早期设备兼容性:8N1配置源于早期终端设备(如Teletype)的通信标准,成为事实上的默认配置。
- 开发便利性:库函数(如HAL库)、调试工具默认使用8N1,减少配置错误风险。
STM32的典型应用场景
- 调试与日志输出:UART常用于输出调试信息,短暂的数据错误对功能影响较小。
- 传感器数据传输:多数传感器数据更新频率高但容错性强(如温度值小幅波动可平滑处理)。
- 协议封装:若通信内容已包含校验字段(如HTTP包头、自定义帧尾CRC),物理层校验冗余。
何时需要启用校验位?
尽管8N1是默认配置,以下场景建议启用校验位:
- 高干扰环境:工业现场、电机控制等易受电磁干扰的场景。
- 无上层校验机制:简单点对点通信且无重传逻辑时,奇偶校验可提供基础错误检测。
- 特定外设要求:某些传感器或模块强制要求校验位(如某些GPS模块)。
串口控制器的物理引脚
发送自然需要GPIO口输出高低电平,所以串口对应的GPIO口选择复用推挽输出?
串口 TX 的 GPIO 配置
- 复用推挽输出(AF_PP,Alternate Function Push-Pull)
- 适用于标准的 UART/USART 发送模式。
- 发送高电平时,驱动为 VCC(通常是 3.3V 或 5V)。
- 发送低电平时,驱动为 GND。
- 适用于绝大多数情况下的 UART 发送。
- 复用开漏输出(AF_OD,Alternate Function Open-Drain)
- 适用于某些特殊场景,例如共用总线或者需要外部上拉电阻的场合。
- 发送低电平时,GPIO 输出低电平(GND)。
- 发送高电平时,GPIO 处于高阻态(需要外部上拉电阻将其拉高)。
- 常用于与不同电平设备通信(如 3.3V MCU 和 5V 设备)。
- 对于 STM32,在使用串口时,GPIO 需要配置为 复用推挽输出(AF_PP):
- TX 引脚:复用推挽输出(AF_PP)(一般默认)
- RX 引脚:浮空输入(Input Floating)或上拉输入(Input Pull-Up)
复用外设时GPIO口的配置
查参考手册的外设的GPIO口配置表(以F103为例)所以配置还是以手册为主
F103C8T6型号,串口的RX不用配置复用模式?而F407型号需要配置复用模式
在 STM32F103C8T6 和 STM32F407 上,串口 RX 引脚的 GPIO 配置方式有所不同,原因是它们的 GPIO 端口控制机制不同。
STM32F103C8T6(基于 GPIO 复用功能)
STM32F103 系列的 GPIO 复用功能比较简单,RX 引脚 不需要 配置为复用输入,只需要配置为 浮空输入 或 上拉输入:
GPIO_MODE_INPUT_FLOATING
(浮空输入)GPIO_MODE_INPUT_PULLUP
(上拉输入,适用于空闲状态为高电平的串口)
原因:STM32F103 的串口 RX 由 硬件自动接管,即使配置为普通输入模式,USART 硬件仍然可以正确接收数据。
STM32F407(基于更复杂的 GPIO 复用系统)
STM32F4 系列采用更先进的 AF(Alternate Function)复用模式,所有与外设相关的 GPIO 都必须显式配置为复用模式。因此,串口 RX 引脚必须设置为 复用输入模式:
GPIO_MODE_AF_PP
(复用推挽输出,适用于 TX)GPIO_MODE_AF_OD
(复用开漏输出,少用)GPIO_MODE_AF_INPUT
(实际上没有这个宏,但需要配置 AF)
此外,还要指定 复用功能编号,不同的外设对应不同的 AF 号。例如:
- USART1_TX(PA9)对应 AF7
- USART1_RX(PA10)对应 AF7
波特率与GPIO口最大输出速率的关系
GPIO 速度决定了引脚输出电平切换的速率(上升/下降时间),单位是 MHz。不同速度的适用情况如下:
- 低速(Low Speed):适用于低频信号、I²C 等时钟敏感应用。
- 中速(Medium Speed):适用于普通 GPIO 控制、较低速的通信(如 9600~115200bps 的串口)。
- 高速(High Speed):适用于高速通信(如 1~4Mbps 串口、SPI)。
- 超高速(Very High Speed,仅 F4 及以上支持):适用于更高速的数据传输,如 SDIO、USB、SPI(>10MHz 时钟)。
根据使用的波特率选择满足需求的最低速度,以节省功耗。
重映射串口引脚
状态寄存器中的重要标志位
STM32芯片,使用串口发送或者接收时,判断标志位时,使用移位寄存器相关还是数据寄存器相关为空或者不为空比较好?
在 STM32 使用串口(USART/UART)进行数据发送或接收时,判断标志位的选择应该基于数据寄存器(DR 或 RDR、TDR)是否为空或不为空,而不是直接依赖移位寄存器(Shift Register, SR)。这是因为:
发送数据时
发送数据时,应该检查**发送数据寄存器空(TXE, Transmit Data Register Empty)**标志,而不是移位寄存器状态:
TXE = 1
表示 TDR 寄存器(发送数据寄存器) 为空,可以写入新的数据。TXE = 0
表示 TDR 仍在传输数据,不能写新数据。
如果要确认数据完全发送完毕,包括移位寄存器的传输,应该检查:
TC = 1
(Transmission Complete,发送完成),表示 数据完全发送,包括移位寄存器内容。
最佳实践(发送判断)
while (!(USARTx->ISR & USART_ISR_TXE)); // 等待 TXE 置位
USARTx->TDR = data; // 发送数据
SART_ISR_TC 是一个偏移宏,用于访问 USARTx->ISR(状态寄存器 ISR)中的 TC(Transmission Complete,传输完成)标志位。
TXE(Transmit Data Register Empty,发送数据寄存器为空)
含义:表示 TDR(Transmit Data Register,发送数据寄存器)为空,可以写入新数据。
触发时机:当TDR 的数据已经被移位寄存器取走后,硬件会自动将 TXE 置 1,表示软件可以写入新的数据。
清除方式:
写入 TDR(USARTx->TDR = data) 后,TXE 自动清零,表示正在传输数据。
传输完成后,TXE 又会变成 1,等待下一次写入。
适用场景:
判断是否可以发送下一字节数据(适用于非 DMA 方式的连续发送)。
实现循环发送数据,确保每次写入 TDR 时前一字节已被移走。
如果要确保数据完全发送完毕:
while (!(USARTx->ISR & USART_ISR_TC)); // 等待 TC 置位
TC(Transmission Complete,发送完成)
含义:表示移位寄存器(Shift Register)中的数据已经发送完毕,即数据已真正从 TX 引脚输出。
触发时机:
整个数据帧(包括起始位、数据位、校验位、停止位)全部发送完毕后,TC 置 1。
清除方式:
读取 ISR 并写 0 到 TCCF(Transmission Complete Clear Flag),或写新数据到 TDR。
适用场景:
发送单个数据包后等待传输完成,确保数据全部发送完毕(如协议通信)。
在关闭串口、切换模式、禁用外设前,需要等待 TC = 1,避免数据丢失。
-
连续发送数据时,主要用
TXE
,在发送完所有数据后(如协议结束)用TC
。 -
如果你的目标是高效发送数据流,一般用
TXE
; 如果你需要确保最后一字节已发送(比如切换模式、关闭串口),就用TC
。
接收数据时
接收数据时,应该检查 RXNE(Receive Data Register Not Empty) 标志,而不是移位寄存器:
RXNE = 1
表示 数据寄存器(RDR) 有数据可读。RXNE = 0
表示 没有数据可读。
最佳实践(接收判断)
while (!(USARTx->ISR & USART_ISR_RXNE)); // 等待 RXNE 置位
uint8_t data = USARTx->RDR; // 读取数据
为什么不能直接依赖移位寄存器?
- 移位寄存器不可直接访问
STM32 的 USART/UART 硬件自动控制移位寄存器,软件无法直接访问其状态。 - 移位寄存器与数据寄存器协同工作
数据寄存器负责数据缓存,而移位寄存器负责实际的串行传输。 - 官方推荐使用 TXE、TC、RXNE
STM32 官方推荐使用TXE
、TC
进行发送状态判断,RXNE
进行接收状态判断,而非移位寄存器。
标志位触发中断
USART 状态寄存器(ISR)中的标志位可以触发中断,这样可以使用中断方式来处理串口收发,而不是一直轮询(轮询会浪费 CPU 资源)。
通过 ISR 标志位触发中断
STM32 的 USART 具有多个状态标志位,比如:
- RXNE(接收数据寄存器非空) → 触发接收中断
- TXE(发送数据寄存器为空) → 触发发送中断
- TC(传输完成) → 触发传输完成中断
- ORE(溢出错误)、PE(奇偶校验错误) → 触发错误中断
这些标志位可以配合 USART 控制寄存器(CR1、CR2、CR3)中的中断使能位,当特定事件发生时,自动触发中断。
串口接收中断(常见用法)
默认情况下,CPU 需要轮询 RXNE 标志位来判断是否有新数据:
while (!(USARTx->ISR & USART_ISR_RXNE)); // 轮询 RXNE,等待数据
uint8_t data = USARTx->RDR; // 读取数据
但这样会浪费 CPU 资源,因此可以使用中断方式,当 RXNE
置位时自动触发 USART 接收中断。
如何开启 USART 接收中断
- 使能USART 的 RXNE 中断
必须打开 USART 控制寄存器 CR1
中的 RXNEIE
(Receive Not Empty Interrupt Enable):
USARTx->CR1 |= USART_CR1_RXNEIE; // 使能接收中断
这样,当 RXNE = 1
(表示有数据可读)时,USART 硬件会触发中断。
- 使能 NVIC 中断
STM32 的 USART 中断信号需要在 NVIC(嵌套向量中断控制器)中开启:
NVIC_EnableIRQ(USARTx_IRQn); // 启用 USARTx 的 NVIC 中断
NVIC_SetPriority(USARTx_IRQn, 1); // 设置中断优先级(1 代表较高优先级)
- 编写 USART 中断处理函数
每个 USART 具有对应的中断处理函数,比如 USART1_IRQHandler()
:
void USART1_IRQHandler(void) {
if (USART1->ISR & USART_ISR_RXNE) { // 检测 RXNE 标志位
uint8_t data = USART1->RDR; // 读取数据(读取后 RXNE 自动清零)
// 处理接收到的数据,例如存入缓冲区
}
}
⚠ 注意:必须读取 RDR
寄存器,否则 RXNE
标志不会清零,中断会一直触发。
F103系统板串口连接
重写printf重定向到串口
- printf打印控制台的原理
- 有多少个字符就调用多少次fputc
- 把fputc重定向到串口的话,就可以显示到串口上了