STM32F103 UART 第一层复习:USART1 收发、回显与默写测试
芯片/板卡:STM32F103C8T6 最小系统板
开发方式:Keil + STM32F10x 标准外设库 V3.5.0
对照工程:
程序源码/STM32Project-有注释版/9-1 串口发送、9-2 串口发送+接收前置复习:已经完成 RCC、GPIO 第一层
整理日期:2026-09-11
本节边界:用标准库完成 USART1 轮询发送、轮询接收和单字节回显。寄存器只理解作用和库函数对应关系,不直接用寄存器写驱动;中断、DMA、环形缓冲区留到通过本测试以后。
1. 这一步只完成一个目标
不看原例程,允许查手册,使用标准库把 USART1 配置为 9600、8 位数据、无校验、1 位停止位、无硬件流控、收发模式 。上电向电脑发送
UART READY;电脑发送测试字节0x55,单片机接收后原样回发,并在 OLED 上显示接收值以及PASS/FAIL。
完整流程如下,图故意按从上到下排列:
#mermaid-svg-de1j9iNEX6BbVJMw{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-de1j9iNEX6BbVJMw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-de1j9iNEX6BbVJMw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-de1j9iNEX6BbVJMw .error-icon{fill:#552222;}#mermaid-svg-de1j9iNEX6BbVJMw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-de1j9iNEX6BbVJMw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-de1j9iNEX6BbVJMw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-de1j9iNEX6BbVJMw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-de1j9iNEX6BbVJMw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-de1j9iNEX6BbVJMw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-de1j9iNEX6BbVJMw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-de1j9iNEX6BbVJMw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-de1j9iNEX6BbVJMw .marker.cross{stroke:#333333;}#mermaid-svg-de1j9iNEX6BbVJMw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-de1j9iNEX6BbVJMw p{margin:0;}#mermaid-svg-de1j9iNEX6BbVJMw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-de1j9iNEX6BbVJMw .cluster-label text{fill:#333;}#mermaid-svg-de1j9iNEX6BbVJMw .cluster-label span{color:#333;}#mermaid-svg-de1j9iNEX6BbVJMw .cluster-label span p{background-color:transparent;}#mermaid-svg-de1j9iNEX6BbVJMw .label text,#mermaid-svg-de1j9iNEX6BbVJMw span{fill:#333;color:#333;}#mermaid-svg-de1j9iNEX6BbVJMw .node rect,#mermaid-svg-de1j9iNEX6BbVJMw .node circle,#mermaid-svg-de1j9iNEX6BbVJMw .node ellipse,#mermaid-svg-de1j9iNEX6BbVJMw .node polygon,#mermaid-svg-de1j9iNEX6BbVJMw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-de1j9iNEX6BbVJMw .rough-node .label text,#mermaid-svg-de1j9iNEX6BbVJMw .node .label text,#mermaid-svg-de1j9iNEX6BbVJMw .image-shape .label,#mermaid-svg-de1j9iNEX6BbVJMw .icon-shape .label{text-anchor:middle;}#mermaid-svg-de1j9iNEX6BbVJMw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-de1j9iNEX6BbVJMw .rough-node .label,#mermaid-svg-de1j9iNEX6BbVJMw .node .label,#mermaid-svg-de1j9iNEX6BbVJMw .image-shape .label,#mermaid-svg-de1j9iNEX6BbVJMw .icon-shape .label{text-align:center;}#mermaid-svg-de1j9iNEX6BbVJMw .node.clickable{cursor:pointer;}#mermaid-svg-de1j9iNEX6BbVJMw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-de1j9iNEX6BbVJMw .arrowheadPath{fill:#333333;}#mermaid-svg-de1j9iNEX6BbVJMw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-de1j9iNEX6BbVJMw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-de1j9iNEX6BbVJMw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-de1j9iNEX6BbVJMw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-de1j9iNEX6BbVJMw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-de1j9iNEX6BbVJMw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-de1j9iNEX6BbVJMw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-de1j9iNEX6BbVJMw .cluster text{fill:#333;}#mermaid-svg-de1j9iNEX6BbVJMw .cluster span{color:#333;}#mermaid-svg-de1j9iNEX6BbVJMw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-de1j9iNEX6BbVJMw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-de1j9iNEX6BbVJMw rect.text{fill:none;stroke-width:0;}#mermaid-svg-de1j9iNEX6BbVJMw .icon-shape,#mermaid-svg-de1j9iNEX6BbVJMw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-de1j9iNEX6BbVJMw .icon-shape p,#mermaid-svg-de1j9iNEX6BbVJMw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-de1j9iNEX6BbVJMw .icon-shape .label rect,#mermaid-svg-de1j9iNEX6BbVJMw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-de1j9iNEX6BbVJMw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-de1j9iNEX6BbVJMw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-de1j9iNEX6BbVJMw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
否
开启 GPIOA 和 USART1 时钟
PA9 配置为复用推挽输出 TX
PA10 配置为上拉输入 RX
配置 USART1 为 9600 8N1 收发
使能 USART1
发送 UART READY
RXNE 是否置 1
读取 DR 得到接收字节
把该字节写回 DR
OLED 显示接收值
是否等于 0x55
显示 PASS
显示 FAIL
这份主代码比 RCC、GPIO 多两个小函数:UART1_SendByte() 和 UART1_SendString()。这是串口最必要的封装,不涉及复杂协议。
2. 第一层与第二层的边界
| 第一层现在必须会 | 第二层以后再做 |
|---|---|
| 会接 TX、RX、GND | 依据手册直接配置 USART 寄存器 |
| 会配置 9600 8N1 | 手算并直接写 BRR |
| 会用标准库初始化 USART1 | 单步进入 USART_Init() 研究位操作 |
| 会发送一个字节和字符串 | 用 TXE 中断或 DMA 连续发送 |
| 会轮询 RXNE 并读出一个字节 | 中断接收、环形缓冲区、数据包状态机 |
| 会区分 TXE、TC、RXNE | 系统处理 ORE、FE、NE、PE 等异常 |
第一层可以看寄存器图、说出每个关键位的作用,但不要花大量时间背地址、偏移和全部位定义。当前评价标准是:能独立写、能接线、能看到数据、能定位常见故障。
3. 硬件连接与通信参数

图 1 串口的 TX 与 RX 必须交叉连接,并且两端必须共地。
本次使用 USART1 默认引脚:
| STM32F103C8T6 | USB 转串口模块 | 方向 | GPIO 模式 |
|---|---|---|---|
| PA9 / USART1_TX | RXD | STM32 → 电脑 | GPIO_Mode_AF_PP |
| PA10 / USART1_RX | TXD | 电脑 → STM32 | GPIO_Mode_IPU |
| GND | GND | 公共参考地 | 必须连接 |
必须背会:
text
发送接收要交叉:PA9(TX) 接模块 RXD,PA10(RX) 接模块 TXD。
两端必须共地。
使用 3.3 V TTL 串口电平,不能把 PA9/PA10 直接接传统 RS-232 电平。

图 2 异步串口一帧由起始位、数据位、可选校验位和停止位组成。
本实验统一使用:
text
波特率:9600
数据位:8
校验位:无
停止位:1
硬件流控:无
简称:9600 8N1
9600 波特时,每一位约为 1 / 9600 = 104.17 μs。发送一个 8N1 字节实际包含 1 个起始位、8 个数据位、1 个停止位,共 10 位,约需 1.04 ms。
4. USART1 的硬件数据通路

图 3 发送与接收各有数据寄存器和移位寄存器;软件通过统一的 USART_DR 地址访问它们。
4.1 发送发生了什么
#mermaid-svg-250E8xJ8PBsXLvvo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-250E8xJ8PBsXLvvo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-250E8xJ8PBsXLvvo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-250E8xJ8PBsXLvvo .error-icon{fill:#552222;}#mermaid-svg-250E8xJ8PBsXLvvo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-250E8xJ8PBsXLvvo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-250E8xJ8PBsXLvvo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-250E8xJ8PBsXLvvo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-250E8xJ8PBsXLvvo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-250E8xJ8PBsXLvvo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-250E8xJ8PBsXLvvo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-250E8xJ8PBsXLvvo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-250E8xJ8PBsXLvvo .marker.cross{stroke:#333333;}#mermaid-svg-250E8xJ8PBsXLvvo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-250E8xJ8PBsXLvvo p{margin:0;}#mermaid-svg-250E8xJ8PBsXLvvo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-250E8xJ8PBsXLvvo .cluster-label text{fill:#333;}#mermaid-svg-250E8xJ8PBsXLvvo .cluster-label span{color:#333;}#mermaid-svg-250E8xJ8PBsXLvvo .cluster-label span p{background-color:transparent;}#mermaid-svg-250E8xJ8PBsXLvvo .label text,#mermaid-svg-250E8xJ8PBsXLvvo span{fill:#333;color:#333;}#mermaid-svg-250E8xJ8PBsXLvvo .node rect,#mermaid-svg-250E8xJ8PBsXLvvo .node circle,#mermaid-svg-250E8xJ8PBsXLvvo .node ellipse,#mermaid-svg-250E8xJ8PBsXLvvo .node polygon,#mermaid-svg-250E8xJ8PBsXLvvo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-250E8xJ8PBsXLvvo .rough-node .label text,#mermaid-svg-250E8xJ8PBsXLvvo .node .label text,#mermaid-svg-250E8xJ8PBsXLvvo .image-shape .label,#mermaid-svg-250E8xJ8PBsXLvvo .icon-shape .label{text-anchor:middle;}#mermaid-svg-250E8xJ8PBsXLvvo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-250E8xJ8PBsXLvvo .rough-node .label,#mermaid-svg-250E8xJ8PBsXLvvo .node .label,#mermaid-svg-250E8xJ8PBsXLvvo .image-shape .label,#mermaid-svg-250E8xJ8PBsXLvvo .icon-shape .label{text-align:center;}#mermaid-svg-250E8xJ8PBsXLvvo .node.clickable{cursor:pointer;}#mermaid-svg-250E8xJ8PBsXLvvo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-250E8xJ8PBsXLvvo .arrowheadPath{fill:#333333;}#mermaid-svg-250E8xJ8PBsXLvvo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-250E8xJ8PBsXLvvo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-250E8xJ8PBsXLvvo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-250E8xJ8PBsXLvvo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-250E8xJ8PBsXLvvo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-250E8xJ8PBsXLvvo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-250E8xJ8PBsXLvvo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-250E8xJ8PBsXLvvo .cluster text{fill:#333;}#mermaid-svg-250E8xJ8PBsXLvvo .cluster span{color:#333;}#mermaid-svg-250E8xJ8PBsXLvvo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-250E8xJ8PBsXLvvo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-250E8xJ8PBsXLvvo rect.text{fill:none;stroke-width:0;}#mermaid-svg-250E8xJ8PBsXLvvo .icon-shape,#mermaid-svg-250E8xJ8PBsXLvvo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-250E8xJ8PBsXLvvo .icon-shape p,#mermaid-svg-250E8xJ8PBsXLvvo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-250E8xJ8PBsXLvvo .icon-shape .label rect,#mermaid-svg-250E8xJ8PBsXLvvo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-250E8xJ8PBsXLvvo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-250E8xJ8PBsXLvvo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-250E8xJ8PBsXLvvo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 程序调用 USART_SendData
数据写入 USART_DR/TDR
TXE 清 0
数据转入发送移位寄存器
TXE 置 1:可以写下一个字节
硬件从 PA9 逐位发送
停止位发送结束
TC 置 1:整个字节真正发完
TXE = 1:发送数据寄存器已经空,可以交付下一个字节。TC = 1:数据寄存器和发送移位寄存器都空,最后一个停止位也已发完。- 连续发送字符串时通常等待
TXE;如果马上要关闭串口、切换半双工方向或进入低功耗,最后一个字节后应等待TC。
4.2 接收发生了什么
#mermaid-svg-tcs50a7OMVYeFzfz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tcs50a7OMVYeFzfz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tcs50a7OMVYeFzfz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tcs50a7OMVYeFzfz .error-icon{fill:#552222;}#mermaid-svg-tcs50a7OMVYeFzfz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tcs50a7OMVYeFzfz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tcs50a7OMVYeFzfz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tcs50a7OMVYeFzfz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tcs50a7OMVYeFzfz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tcs50a7OMVYeFzfz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tcs50a7OMVYeFzfz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tcs50a7OMVYeFzfz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tcs50a7OMVYeFzfz .marker.cross{stroke:#333333;}#mermaid-svg-tcs50a7OMVYeFzfz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tcs50a7OMVYeFzfz p{margin:0;}#mermaid-svg-tcs50a7OMVYeFzfz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tcs50a7OMVYeFzfz .cluster-label text{fill:#333;}#mermaid-svg-tcs50a7OMVYeFzfz .cluster-label span{color:#333;}#mermaid-svg-tcs50a7OMVYeFzfz .cluster-label span p{background-color:transparent;}#mermaid-svg-tcs50a7OMVYeFzfz .label text,#mermaid-svg-tcs50a7OMVYeFzfz span{fill:#333;color:#333;}#mermaid-svg-tcs50a7OMVYeFzfz .node rect,#mermaid-svg-tcs50a7OMVYeFzfz .node circle,#mermaid-svg-tcs50a7OMVYeFzfz .node ellipse,#mermaid-svg-tcs50a7OMVYeFzfz .node polygon,#mermaid-svg-tcs50a7OMVYeFzfz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tcs50a7OMVYeFzfz .rough-node .label text,#mermaid-svg-tcs50a7OMVYeFzfz .node .label text,#mermaid-svg-tcs50a7OMVYeFzfz .image-shape .label,#mermaid-svg-tcs50a7OMVYeFzfz .icon-shape .label{text-anchor:middle;}#mermaid-svg-tcs50a7OMVYeFzfz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tcs50a7OMVYeFzfz .rough-node .label,#mermaid-svg-tcs50a7OMVYeFzfz .node .label,#mermaid-svg-tcs50a7OMVYeFzfz .image-shape .label,#mermaid-svg-tcs50a7OMVYeFzfz .icon-shape .label{text-align:center;}#mermaid-svg-tcs50a7OMVYeFzfz .node.clickable{cursor:pointer;}#mermaid-svg-tcs50a7OMVYeFzfz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tcs50a7OMVYeFzfz .arrowheadPath{fill:#333333;}#mermaid-svg-tcs50a7OMVYeFzfz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tcs50a7OMVYeFzfz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tcs50a7OMVYeFzfz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tcs50a7OMVYeFzfz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tcs50a7OMVYeFzfz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tcs50a7OMVYeFzfz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tcs50a7OMVYeFzfz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tcs50a7OMVYeFzfz .cluster text{fill:#333;}#mermaid-svg-tcs50a7OMVYeFzfz .cluster span{color:#333;}#mermaid-svg-tcs50a7OMVYeFzfz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tcs50a7OMVYeFzfz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tcs50a7OMVYeFzfz rect.text{fill:none;stroke-width:0;}#mermaid-svg-tcs50a7OMVYeFzfz .icon-shape,#mermaid-svg-tcs50a7OMVYeFzfz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tcs50a7OMVYeFzfz .icon-shape p,#mermaid-svg-tcs50a7OMVYeFzfz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tcs50a7OMVYeFzfz .icon-shape .label rect,#mermaid-svg-tcs50a7OMVYeFzfz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tcs50a7OMVYeFzfz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tcs50a7OMVYeFzfz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tcs50a7OMVYeFzfz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} PA10 检测到低电平起始位
USART 按波特率采样数据位
完整字节进入接收数据寄存器 RDR
RXNE 置 1
程序发现 RXNE 为 1
调用 USART_ReceiveData 读取 DR
RXNE 清 0
等待下一个字节
必须形成条件反射:
text
发送:写 DR,等 TXE。
接收:看 RXNE,读 DR。
5. 必须理解的寄存器
第一层不直接写这些寄存器,但要知道标准库最终改了什么。
| 寄存器 | 本次关键位/字段 | 简单作用 | 对应代码 |
|---|---|---|---|
USART_SR |
TXE、TC、RXNE |
报告发送和接收状态 | USART_GetFlagStatus() |
USART_DR |
DR[8:0] |
写入时发送,读取时取接收数据 | USART_SendData()、USART_ReceiveData() |
USART_BRR |
整数和小数分频字段 | 根据外设时钟产生目标波特率 | USART_InitStructure.USART_BaudRate |
USART_CR1 |
UE、TE、RE、M、PCE、PS、RXNEIE |
总使能、收发、字长、校验和接收中断 | USART_Init()、USART_Cmd()、USART_ITConfig() |
USART_CR2 |
STOP |
选择停止位数量 | USART_StopBits_1 |
USART_CR3 |
RTSE、CTSE、DMAT、DMAR |
硬件流控与 DMA | 本次选择无流控,不使用 DMA |
5.1 USART_SR 中最需要认识的标志
| 标志 | 含义 | 第一层如何处理 |
|---|---|---|
TXE |
发送数据寄存器空 | 每发一个字节后等待它置 1 |
TC |
整帧发送完成 | 认识即可;最后必须真正发完时使用 |
RXNE |
接收数据寄存器非空 | 置 1 后立即读取 DR |
ORE |
接收溢出 | 前一字节未及时读取,新字节又来了;当前先会判断原因 |
FE |
帧错误 | 常见于波特率、停止位或线路异常 |
NE |
噪声错误 | 常见于干扰或接线质量差 |
PE |
奇偶校验错误 | 两端校验设置不一致或数据错误 |
5.2 库函数如何落到寄存器
#mermaid-svg-bvSCKPeoplPgyAUN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bvSCKPeoplPgyAUN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bvSCKPeoplPgyAUN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bvSCKPeoplPgyAUN .error-icon{fill:#552222;}#mermaid-svg-bvSCKPeoplPgyAUN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bvSCKPeoplPgyAUN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bvSCKPeoplPgyAUN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bvSCKPeoplPgyAUN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bvSCKPeoplPgyAUN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bvSCKPeoplPgyAUN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bvSCKPeoplPgyAUN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bvSCKPeoplPgyAUN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bvSCKPeoplPgyAUN .marker.cross{stroke:#333333;}#mermaid-svg-bvSCKPeoplPgyAUN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bvSCKPeoplPgyAUN p{margin:0;}#mermaid-svg-bvSCKPeoplPgyAUN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bvSCKPeoplPgyAUN .cluster-label text{fill:#333;}#mermaid-svg-bvSCKPeoplPgyAUN .cluster-label span{color:#333;}#mermaid-svg-bvSCKPeoplPgyAUN .cluster-label span p{background-color:transparent;}#mermaid-svg-bvSCKPeoplPgyAUN .label text,#mermaid-svg-bvSCKPeoplPgyAUN span{fill:#333;color:#333;}#mermaid-svg-bvSCKPeoplPgyAUN .node rect,#mermaid-svg-bvSCKPeoplPgyAUN .node circle,#mermaid-svg-bvSCKPeoplPgyAUN .node ellipse,#mermaid-svg-bvSCKPeoplPgyAUN .node polygon,#mermaid-svg-bvSCKPeoplPgyAUN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bvSCKPeoplPgyAUN .rough-node .label text,#mermaid-svg-bvSCKPeoplPgyAUN .node .label text,#mermaid-svg-bvSCKPeoplPgyAUN .image-shape .label,#mermaid-svg-bvSCKPeoplPgyAUN .icon-shape .label{text-anchor:middle;}#mermaid-svg-bvSCKPeoplPgyAUN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bvSCKPeoplPgyAUN .rough-node .label,#mermaid-svg-bvSCKPeoplPgyAUN .node .label,#mermaid-svg-bvSCKPeoplPgyAUN .image-shape .label,#mermaid-svg-bvSCKPeoplPgyAUN .icon-shape .label{text-align:center;}#mermaid-svg-bvSCKPeoplPgyAUN .node.clickable{cursor:pointer;}#mermaid-svg-bvSCKPeoplPgyAUN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bvSCKPeoplPgyAUN .arrowheadPath{fill:#333333;}#mermaid-svg-bvSCKPeoplPgyAUN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bvSCKPeoplPgyAUN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bvSCKPeoplPgyAUN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bvSCKPeoplPgyAUN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bvSCKPeoplPgyAUN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bvSCKPeoplPgyAUN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bvSCKPeoplPgyAUN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bvSCKPeoplPgyAUN .cluster text{fill:#333;}#mermaid-svg-bvSCKPeoplPgyAUN .cluster span{color:#333;}#mermaid-svg-bvSCKPeoplPgyAUN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bvSCKPeoplPgyAUN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bvSCKPeoplPgyAUN rect.text{fill:none;stroke-width:0;}#mermaid-svg-bvSCKPeoplPgyAUN .icon-shape,#mermaid-svg-bvSCKPeoplPgyAUN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bvSCKPeoplPgyAUN .icon-shape p,#mermaid-svg-bvSCKPeoplPgyAUN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bvSCKPeoplPgyAUN .icon-shape .label rect,#mermaid-svg-bvSCKPeoplPgyAUN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bvSCKPeoplPgyAUN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bvSCKPeoplPgyAUN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bvSCKPeoplPgyAUN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 填写 USART_InitTypeDef
USART_Init
配置 BRR 波特率
配置 CR1 字长/校验/收发
配置 CR2 停止位
配置 CR3 硬件流控
USART_Cmd ENABLE
置 CR1.UE 使能外设
注意:USART_Init() 完成参数配置,但最后仍要调用 USART_Cmd(USART1, ENABLE) 打开 USART。
6. 必须练习的标准库函数
| 函数 | 作用 | 本次必须会到什么程度 |
|---|---|---|
RCC_APB2PeriphClockCmd() |
开启 GPIOA 和 USART1 时钟 | 能写出两个外设按位或后一起使能 |
GPIO_Init() |
配置 PA9、PA10 | 能区分 TX 的 AF_PP 与 RX 的输入模式 |
USART_Init() |
按结构体参数初始化 USART | 能独立填写 9600 8N1、无流控、收发模式 |
USART_Cmd() |
使能或关闭 USART | 知道应在配置完成后调用 |
USART_SendData() |
向 DR 写一个待发送数据 |
会写单字节发送函数 |
USART_GetFlagStatus() |
查询状态标志 | 会查询 TXE 和 RXNE,会解释 TC |
USART_ReceiveData() |
从 DR 读取接收数据 |
会在 RXNE 置位后读取,并转为 uint8_t |
本次只认识、不要求默写:
USART_StructInit():给初始化结构体填写默认值;USART_ITConfig():开启或关闭 USART 中断源,例如 RXNE 中断;USART_GetITStatus():在中断服务函数中判断是哪一个中断源;USART_ClearITPendingBit():清除部分中断挂起位;对于 RXNE,正常读取DR已会清除 RXNE,不要机械地重复清除;USART_ClearFlag():清除允许由软件清除的状态标志,不能把所有标志都当作同一种清除方式;USART_DeInit():恢复外设寄存器到复位状态,普通上电初始化不需要先调用。
7. 初始化模板:按六步记忆
text
1. 开 GPIOA 和 USART1 时钟
2. PA9 配置为复用推挽输出
3. PA10 配置为上拉输入
4. 填写 USART_InitTypeDef:9600 8N1、无流控、收发
5. USART_Init 把配置写入寄存器
6. USART_Cmd 使能 USART1
在课程标准工程中,启动文件会在进入 main() 之前调用 SystemInit(),因此本例不在 main() 中重复调用。若以后换了工程模板,应先确认系统时钟是否正确;系统时钟错误会让 USART_Init() 计算出的实际波特率偏离目标,常见现象就是乱码。
7.1 为什么 USART1 和 GPIOA 都在 APB2
本芯片上 USART1 与 GPIOA 都挂在 APB2,因此写:
c
RCC_APB2PeriphClockCmd(
RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1,
ENABLE
);
如果以后迁移到 USART2,要改为 PA2/PA3,并用 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE) 开 USART2 时钟;GPIOA 时钟仍由 APB2 开启。这个迁移题可以检验 RCC 是否真正理解。
7.2 为什么 PA9 是 GPIO_Mode_AF_PP
PA9 的电平由 USART1 外设控制,而不是由普通 GPIO 输出寄存器直接控制,所以使用"复用功能";串口 TX 需要主动输出高、低电平,所以使用推挽输出。
7.3 为什么 PA10 使用输入模式
PA10 接收外部 USB 转串口模块送来的电平,必须配置为输入。课程例程使用 GPIO_Mode_IPU,本复习代码保持一致。输入模式下 GPIO_Speed 不决定输入速度,结构体中给它赋值只是避免成员遗留旧值。
8. 第一层完整默写代码:轮询收发回显
使用前提:工程已经包含课程 OLED 驱动;串口助手选择 USART1 对应的 USB 转串口端口,参数为 9600 8N1;发送模式选择 HEX,并发送
55。
c
#include "stm32f10x.h" // STM32F10x 标准外设库总头文件
#include "OLED.h" // 课程 OLED 显示驱动
#define UART_TEST_BYTE 0x55U // 本次规定的测试字节
static void UART1_Init(void); // USART1 初始化函数声明
static void UART1_SendByte(uint8_t data); // 发送一个字节函数声明
static void UART1_SendString(const char *str); // 发送字符串函数声明
int main(void)
{
uint8_t rx_data; // 保存最近收到的一个字节
OLED_Init(); // 初始化 OLED,供观察结果
UART1_Init(); // 初始化 PA9、PA10 和 USART1
OLED_ShowString(1, 1, "UART1 9600"); // 第1行显示串口参数
OLED_ShowString(2, 1, "RX:00"); // 第2行预留接收值
OLED_ShowString(3, 1, "EXP:55"); // 第3行显示期望字节
OLED_ShowString(4, 1, "STATE:WAIT"); // 尚未收到数据
UART1_SendString("UART READY\r\n"); // 上电后向电脑发送启动提示
while (1)
{
// RXNE=1 表示接收数据寄存器中已有一个未读取字节
if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
{
// 读取 DR 取走数据;正常读取后 RXNE 会被清除
rx_data = (uint8_t)USART_ReceiveData(USART1);
// 把收到的数据原样发回电脑,构成单字节回显
UART1_SendByte(rx_data);
// 在 OLED 上用两位十六进制显示本次接收值
OLED_ShowHexNum(2, 4, rx_data, 2);
// 收到规定测试字节 0x55 才判定本轮通过
if (rx_data == UART_TEST_BYTE)
{
OLED_ShowString(4, 1, "STATE:PASS");
}
else
{
OLED_ShowString(4, 1, "STATE:FAIL");
}
}
}
}
static void UART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; // GPIO 配置结构体
USART_InitTypeDef USART_InitStructure; // USART 配置结构体
// GPIOA 与 USART1 都挂在 APB2,总线时钟必须先开启
RCC_APB2PeriphClockCmd(
RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1,
ENABLE
);
// PA9 是 USART1_TX,由外设控制并主动输出高低电平
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// PA10 是 USART1_RX,课程例程采用内部上拉输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置 9600 8N1、无硬件流控,同时开启发送和接收
USART_InitStructure.USART_BaudRate = 9600;
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_Tx | USART_Mode_Rx;
USART_Init(USART1, &USART_InitStructure);
// 最后置 CR1.UE,真正使能 USART1
USART_Cmd(USART1, ENABLE);
}
static void UART1_SendByte(uint8_t data)
{
// 把一个字节写入 USART1 数据寄存器
USART_SendData(USART1, data);
// 等待发送数据寄存器变空,之后才能安全交付下一个字节
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
{
// 硬件发送期间在此等待
}
}
static void UART1_SendString(const char *str)
{
// C 字符串以 '\0' 结尾,逐字节调用发送函数
while (*str != '\0')
{
UART1_SendByte((uint8_t)*str);
str++;
}
}
9. 逐段讲解:每行代码为什么存在
9.1 头文件、宏和函数声明
| 代码 | 含义 |
|---|---|
#include "stm32f10x.h" |
获得 RCC、GPIO、USART 类型、常量和库函数声明 |
#include "OLED.h" |
获得 OLED 初始化与显示函数声明 |
#define UART_TEST_BYTE 0x55U |
把测试值集中定义;以后改成 0xA5U 只改一处 |
static void UART1_Init(void); |
声明当前文件内部使用的初始化函数 |
static void UART1_SendByte(...) |
声明最小发送单位:一个字节 |
static void UART1_SendString(...) |
声明字符串发送函数,它会重复调用单字节发送 |
9.2 主函数
| 代码 | 必须能说出的解释 |
|---|---|
uint8_t rx_data; |
一个串口数据位宽为 8 位,所以用 8 位无符号变量保存 |
OLED_Init(); |
初始化观察工具;不是 USART 必需步骤 |
UART1_Init(); |
一次完成时钟、引脚、参数和 USART 使能 |
UART1_SendString("UART READY\r\n"); |
上电提示;\r\n 让多数串口终端换到下一行 |
while (1) |
裸机程序持续运行,不从 main 返回 |
USART_GetFlagStatus(...RXNE) |
轮询判断硬件是否收到新字节,不读取时不处理 |
USART_ReceiveData(USART1) |
从 DR 取数据;函数返回类型较宽,显式转成 uint8_t |
UART1_SendByte(rx_data) |
原样回发,使电脑能验证接收路径和发送路径都工作 |
OLED_ShowHexNum(...) |
显示真正收到的十六进制值,便于判断乱码或配置错误 |
rx_data == UART_TEST_BYTE |
软件比较实际值与期望值,输出明确 PASS/FAIL |
9.3 初始化函数
| 代码/配置 | 必须能说出的解释 |
|---|---|
RCC_APB2Periph_GPIOA |
没有 GPIOA 时钟,PA9/PA10 配置不会生效 |
RCC_APB2Periph_USART1 |
没有 USART1 时钟,USART 寄存器和收发硬件不能工作 |
PA9 + GPIO_Mode_AF_PP |
USART1_TX 的复用推挽输出 |
PA10 + GPIO_Mode_IPU |
USART1_RX 的上拉输入 |
USART_BaudRate = 9600 |
库函数根据 USART1 的外设时钟计算 BRR |
USART_WordLength_8b |
一帧包含 8 个有效数据位;本次不使用校验 |
USART_StopBits_1 |
使用 1 位停止位 |
USART_Parity_No |
不添加奇偶校验位 |
USART_HardwareFlowControl_None |
不使用 RTS/CTS,两根数据线即可 |
| `USART_Mode_Tx | USART_Mode_Rx` |
USART_Init(...) |
把结构体内容转换为 BRR、CR1、CR2、CR3 配置 |
USART_Cmd(...ENABLE) |
最后使能 USART;漏写会导致完全不能收发 |
9.4 发送函数
| 代码 | 必须能说出的解释 |
|---|---|
USART_SendData(USART1, data) |
将数据写入 DR/TDR,硬件开始安排发送 |
while (...TXE == RESET) |
DR 尚未空时等待,防止过快写入覆盖数据 |
查询 TXE 而不是 RXNE |
TXE 属于发送路径,RXNE 属于接收路径 |
9.5 字符串发送函数
| 代码 | 必须能说出的解释 |
|---|---|
while (*str != '\0') |
当前字符不是字符串结束符时继续发送 |
UART1_SendByte((uint8_t)*str) |
发送当前字符 |
str++ |
指针移动到下一个字符;漏写会无限发送同一字符 |
10. 结构体六个成员的背诵模板
不要背散乱单词,按"速、长、停、校、流、向"记忆:
c
USART_InitStructure.USART_BaudRate = 9600; // 速:波特率
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_Tx | USART_Mode_Rx;// 向:收发方向
口令:
text
九千六,八位长,一位停,无校验,无流控,收加发。
11. 完整测试方法
11.1 测试准备
- USB 转串口模块使用 3.3 V TTL 电平;
- PA9 接模块 RXD,PA10 接模块 TXD,GND 接 GND;
- 串口助手选择正确 COM 口;
- 参数选择 9600、8 数据位、无校验、1 停止位;
- 为了发送
0x55,串口助手选择 HEX 发送,而不是文本发送。
文本发送
55实际发送两个 ASCII 字节0x35 0x35;HEX 发送55才是一个字节0x55。
11.2 正常测试流程
#mermaid-svg-mXhzFnY8mIV6Zlcc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mXhzFnY8mIV6Zlcc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mXhzFnY8mIV6Zlcc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mXhzFnY8mIV6Zlcc .error-icon{fill:#552222;}#mermaid-svg-mXhzFnY8mIV6Zlcc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mXhzFnY8mIV6Zlcc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mXhzFnY8mIV6Zlcc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mXhzFnY8mIV6Zlcc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mXhzFnY8mIV6Zlcc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mXhzFnY8mIV6Zlcc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mXhzFnY8mIV6Zlcc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mXhzFnY8mIV6Zlcc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mXhzFnY8mIV6Zlcc .marker.cross{stroke:#333333;}#mermaid-svg-mXhzFnY8mIV6Zlcc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mXhzFnY8mIV6Zlcc p{margin:0;}#mermaid-svg-mXhzFnY8mIV6Zlcc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mXhzFnY8mIV6Zlcc .cluster-label text{fill:#333;}#mermaid-svg-mXhzFnY8mIV6Zlcc .cluster-label span{color:#333;}#mermaid-svg-mXhzFnY8mIV6Zlcc .cluster-label span p{background-color:transparent;}#mermaid-svg-mXhzFnY8mIV6Zlcc .label text,#mermaid-svg-mXhzFnY8mIV6Zlcc span{fill:#333;color:#333;}#mermaid-svg-mXhzFnY8mIV6Zlcc .node rect,#mermaid-svg-mXhzFnY8mIV6Zlcc .node circle,#mermaid-svg-mXhzFnY8mIV6Zlcc .node ellipse,#mermaid-svg-mXhzFnY8mIV6Zlcc .node polygon,#mermaid-svg-mXhzFnY8mIV6Zlcc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mXhzFnY8mIV6Zlcc .rough-node .label text,#mermaid-svg-mXhzFnY8mIV6Zlcc .node .label text,#mermaid-svg-mXhzFnY8mIV6Zlcc .image-shape .label,#mermaid-svg-mXhzFnY8mIV6Zlcc .icon-shape .label{text-anchor:middle;}#mermaid-svg-mXhzFnY8mIV6Zlcc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mXhzFnY8mIV6Zlcc .rough-node .label,#mermaid-svg-mXhzFnY8mIV6Zlcc .node .label,#mermaid-svg-mXhzFnY8mIV6Zlcc .image-shape .label,#mermaid-svg-mXhzFnY8mIV6Zlcc .icon-shape .label{text-align:center;}#mermaid-svg-mXhzFnY8mIV6Zlcc .node.clickable{cursor:pointer;}#mermaid-svg-mXhzFnY8mIV6Zlcc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mXhzFnY8mIV6Zlcc .arrowheadPath{fill:#333333;}#mermaid-svg-mXhzFnY8mIV6Zlcc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mXhzFnY8mIV6Zlcc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mXhzFnY8mIV6Zlcc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mXhzFnY8mIV6Zlcc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mXhzFnY8mIV6Zlcc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mXhzFnY8mIV6Zlcc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mXhzFnY8mIV6Zlcc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mXhzFnY8mIV6Zlcc .cluster text{fill:#333;}#mermaid-svg-mXhzFnY8mIV6Zlcc .cluster span{color:#333;}#mermaid-svg-mXhzFnY8mIV6Zlcc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mXhzFnY8mIV6Zlcc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mXhzFnY8mIV6Zlcc rect.text{fill:none;stroke-width:0;}#mermaid-svg-mXhzFnY8mIV6Zlcc .icon-shape,#mermaid-svg-mXhzFnY8mIV6Zlcc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mXhzFnY8mIV6Zlcc .icon-shape p,#mermaid-svg-mXhzFnY8mIV6Zlcc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mXhzFnY8mIV6Zlcc .icon-shape .label rect,#mermaid-svg-mXhzFnY8mIV6Zlcc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mXhzFnY8mIV6Zlcc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mXhzFnY8mIV6Zlcc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mXhzFnY8mIV6Zlcc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 下载程序并复位
电脑收到 UART READY
串口助手以 HEX 发送 55
STM32 RXNE 置位
程序读到 0x55
STM32 回发 0x55
电脑接收区显示 55
OLED 显示 RX:55
OLED 显示 STATE:PASS
一次通过必须同时看到:
- 电脑上电后收到
UART READY; - 电脑发送 HEX
55后,又收到 HEX55; - OLED 显示
RX:55; - OLED 显示
STATE:PASS。
这四项分别验证:初始化与发送、接收与回显、实际数据观察、软件比较。
11.3 反向测试
电脑再发送 HEX A5:
- 电脑应收到回显
A5,证明通信仍正常; - OLED 应显示
RX:A5; - 因为期望值仍为
0x55,状态应显示FAIL。
这个 FAIL 是正确结果,它证明比较逻辑不是永远显示 PASS。
12. "20% 看、80% 调"的重复复习安排
每轮不要重新看完整视频。先写、先接、先观察,再针对错误查资料。
第 1 轮:开卷抄写并跑通
目标:认识完整链路,不追求背诵。
- 对照本笔记输入代码;
- 在每一段旁边说出"时钟、TX 引脚、RX 引脚、参数、使能、发送、接收";
- 完成
55 → 回显 55 → PASS; - 主动发送
A5,看到FAIL。
第 2 轮:只看六步提示默写
只允许看以下提示:
text
时钟 → PA9 → PA10 → USART结构体 → USART_Init → USART_Cmd
发送:SendData → 等TXE
接收:看RXNE → ReceiveData
写完再与完整代码比较,记录漏项,不要边写边翻原例程。
第 3 轮:完全脱离原代码
允许查官方手册和函数声明,但不看 9-1、9-2 原例程,也不看本笔记的完整代码。要求在 30~45 分钟内完成接线、编码、下载与验证。
第 4 轮:改参数,证明不是死记
- 把测试值改为
0xA5U; - 把波特率改为
115200; - 同步修改串口助手参数;
- 完成
A5 → 回显 A5 → PASS; - 故意让电脑仍保持 9600,观察乱码或通信失败,再恢复一致。
第 5 轮:故障注入与定位
每次只制造一个错误,然后依据现象定位:
| 人为错误 | 可能现象 | 应想到的原因 |
|---|---|---|
| 拔掉 GND | 不稳定、乱码或完全不通 | 两端没有共同电平参考 |
| TX 与 TX、RX 与 RX 相接 | 电脑收不到或不能回显 | TX/RX 未交叉 |
| 串口助手改成错误波特率 | 乱码 | 两端位时间不一致 |
| 注释掉 USART1 时钟 | 完全不能收发 | 外设无时钟 |
| PA9 改为普通输入 | 电脑收不到 STM32 发送 | TX 引脚模式错误 |
| PA10 改为输出 | STM32 收不到,且可能产生电气冲突 | RX 引脚方向错误 |
漏掉 USART_Cmd() |
完全不能收发 | CR1.UE 未使能 |
| 发送后不等 TXE 并快速连续写 | 字符可能丢失 | 写 DR 节奏错误 |
每修复一个问题,要用一句话记录:
text
现象 → 我检查了什么 → 根因是什么 → 改哪一行恢复
13. 第一层通过标准
满足以下条件后,可以认为 UART 第一层的"第一步"通过:
- 不看课程原例程,能独立写出 USART1 初始化;
- 能正确写出 PA9
AF_PP、PA10 输入模式; - 能写出 9600 8N1、无流控、收发模式六项参数;
- 能写出
USART_SendData()加等待TXE的单字节发送函数; - 能用
RXNE判断收到数据,并调用USART_ReceiveData(); - 能完成上电字符串、单字节接收、回显和 OLED PASS/FAIL;
- 能解释 TXE、TC、RXNE 的区别;
- 能解释标准库配置最终落到 SR、DR、BRR、CR1、CR2、CR3;
- 遇到无数据或乱码时,会按"接线 → 共地 → 参数 → 时钟 → GPIO → USART 使能"顺序排查;
- 能把测试值和波特率改掉后重新跑通,而不是只能复现固定答案。
如果只能背出初始化、实际不能回显和定位问题,还不能算通过。第一层要求的是:独立写、调、改、查资料和定位问题。
14. 通过后的小升级:先认识中断,不急着背
轮询代码必须由 CPU 反复查看 RXNE。通过本节测试后,可以再看课程 9-2 串口发送+接收 的 RXNE 中断版本:
#mermaid-svg-bVss7G6wSYN5fx1A{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bVss7G6wSYN5fx1A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bVss7G6wSYN5fx1A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bVss7G6wSYN5fx1A .error-icon{fill:#552222;}#mermaid-svg-bVss7G6wSYN5fx1A .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bVss7G6wSYN5fx1A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bVss7G6wSYN5fx1A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bVss7G6wSYN5fx1A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bVss7G6wSYN5fx1A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bVss7G6wSYN5fx1A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bVss7G6wSYN5fx1A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bVss7G6wSYN5fx1A .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bVss7G6wSYN5fx1A .marker.cross{stroke:#333333;}#mermaid-svg-bVss7G6wSYN5fx1A svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bVss7G6wSYN5fx1A p{margin:0;}#mermaid-svg-bVss7G6wSYN5fx1A .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bVss7G6wSYN5fx1A .cluster-label text{fill:#333;}#mermaid-svg-bVss7G6wSYN5fx1A .cluster-label span{color:#333;}#mermaid-svg-bVss7G6wSYN5fx1A .cluster-label span p{background-color:transparent;}#mermaid-svg-bVss7G6wSYN5fx1A .label text,#mermaid-svg-bVss7G6wSYN5fx1A span{fill:#333;color:#333;}#mermaid-svg-bVss7G6wSYN5fx1A .node rect,#mermaid-svg-bVss7G6wSYN5fx1A .node circle,#mermaid-svg-bVss7G6wSYN5fx1A .node ellipse,#mermaid-svg-bVss7G6wSYN5fx1A .node polygon,#mermaid-svg-bVss7G6wSYN5fx1A .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bVss7G6wSYN5fx1A .rough-node .label text,#mermaid-svg-bVss7G6wSYN5fx1A .node .label text,#mermaid-svg-bVss7G6wSYN5fx1A .image-shape .label,#mermaid-svg-bVss7G6wSYN5fx1A .icon-shape .label{text-anchor:middle;}#mermaid-svg-bVss7G6wSYN5fx1A .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bVss7G6wSYN5fx1A .rough-node .label,#mermaid-svg-bVss7G6wSYN5fx1A .node .label,#mermaid-svg-bVss7G6wSYN5fx1A .image-shape .label,#mermaid-svg-bVss7G6wSYN5fx1A .icon-shape .label{text-align:center;}#mermaid-svg-bVss7G6wSYN5fx1A .node.clickable{cursor:pointer;}#mermaid-svg-bVss7G6wSYN5fx1A .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bVss7G6wSYN5fx1A .arrowheadPath{fill:#333333;}#mermaid-svg-bVss7G6wSYN5fx1A .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bVss7G6wSYN5fx1A .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bVss7G6wSYN5fx1A .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bVss7G6wSYN5fx1A .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bVss7G6wSYN5fx1A .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bVss7G6wSYN5fx1A .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bVss7G6wSYN5fx1A .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bVss7G6wSYN5fx1A .cluster text{fill:#333;}#mermaid-svg-bVss7G6wSYN5fx1A .cluster span{color:#333;}#mermaid-svg-bVss7G6wSYN5fx1A div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bVss7G6wSYN5fx1A .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bVss7G6wSYN5fx1A rect.text{fill:none;stroke-width:0;}#mermaid-svg-bVss7G6wSYN5fx1A .icon-shape,#mermaid-svg-bVss7G6wSYN5fx1A .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bVss7G6wSYN5fx1A .icon-shape p,#mermaid-svg-bVss7G6wSYN5fx1A .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bVss7G6wSYN5fx1A .icon-shape .label rect,#mermaid-svg-bVss7G6wSYN5fx1A .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bVss7G6wSYN5fx1A .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bVss7G6wSYN5fx1A .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bVss7G6wSYN5fx1A :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} USART_ITConfig 开启 RXNE 中断
NVIC_Init 允许 USART1_IRQn
收到字节后硬件置 RXNE
CPU 进入 USART1_IRQHandler
USART_GetITStatus 判断来源
USART_ReceiveData 读取 DR
保存数据并置软件标志
主循环读取软件标志并处理
当前只要能讲出流程即可,不把中断代码加入本次闭卷默写,避免 UART 第一步与 NVIC、共享变量、缓冲区问题混在一起。
15. 常见问题速查
| 现象 | 优先检查 |
|---|---|
电脑完全收不到 UART READY |
COM 口、共地、PA9→RXD、GPIOA/USART1 时钟、PA9 模式、USART_Cmd |
能收到启动信息,但发 55 没回显 |
PA10←TXD、PA10 输入模式、USART 是否包含 Rx、是否检查 RXNE |
| 收到乱码 | 两端波特率/数据位/校验/停止位、系统时钟是否正确、电平标准 |
OLED 显示 35 而不是 55 |
串口助手用了文本发送;字符 '5' 的 ASCII 值是 0x35 |
| 只收到一次,之后收不到 | 是否读取 DR 清 RXNE,是否发生 ORE,代码是否长期阻塞 |
| 回显正确但 OLED 显示 FAIL | 检查期望宏、HEX/文本发送方式和比较值 |
| 字符串不换行 | 终端换行设置,或是否发送 \r\n |
推荐排查顺序:
#mermaid-svg-jDyRctDFk6wSAMgz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jDyRctDFk6wSAMgz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jDyRctDFk6wSAMgz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jDyRctDFk6wSAMgz .error-icon{fill:#552222;}#mermaid-svg-jDyRctDFk6wSAMgz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jDyRctDFk6wSAMgz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jDyRctDFk6wSAMgz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jDyRctDFk6wSAMgz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jDyRctDFk6wSAMgz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jDyRctDFk6wSAMgz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jDyRctDFk6wSAMgz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jDyRctDFk6wSAMgz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jDyRctDFk6wSAMgz .marker.cross{stroke:#333333;}#mermaid-svg-jDyRctDFk6wSAMgz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jDyRctDFk6wSAMgz p{margin:0;}#mermaid-svg-jDyRctDFk6wSAMgz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jDyRctDFk6wSAMgz .cluster-label text{fill:#333;}#mermaid-svg-jDyRctDFk6wSAMgz .cluster-label span{color:#333;}#mermaid-svg-jDyRctDFk6wSAMgz .cluster-label span p{background-color:transparent;}#mermaid-svg-jDyRctDFk6wSAMgz .label text,#mermaid-svg-jDyRctDFk6wSAMgz span{fill:#333;color:#333;}#mermaid-svg-jDyRctDFk6wSAMgz .node rect,#mermaid-svg-jDyRctDFk6wSAMgz .node circle,#mermaid-svg-jDyRctDFk6wSAMgz .node ellipse,#mermaid-svg-jDyRctDFk6wSAMgz .node polygon,#mermaid-svg-jDyRctDFk6wSAMgz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jDyRctDFk6wSAMgz .rough-node .label text,#mermaid-svg-jDyRctDFk6wSAMgz .node .label text,#mermaid-svg-jDyRctDFk6wSAMgz .image-shape .label,#mermaid-svg-jDyRctDFk6wSAMgz .icon-shape .label{text-anchor:middle;}#mermaid-svg-jDyRctDFk6wSAMgz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jDyRctDFk6wSAMgz .rough-node .label,#mermaid-svg-jDyRctDFk6wSAMgz .node .label,#mermaid-svg-jDyRctDFk6wSAMgz .image-shape .label,#mermaid-svg-jDyRctDFk6wSAMgz .icon-shape .label{text-align:center;}#mermaid-svg-jDyRctDFk6wSAMgz .node.clickable{cursor:pointer;}#mermaid-svg-jDyRctDFk6wSAMgz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jDyRctDFk6wSAMgz .arrowheadPath{fill:#333333;}#mermaid-svg-jDyRctDFk6wSAMgz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jDyRctDFk6wSAMgz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jDyRctDFk6wSAMgz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jDyRctDFk6wSAMgz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jDyRctDFk6wSAMgz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jDyRctDFk6wSAMgz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jDyRctDFk6wSAMgz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jDyRctDFk6wSAMgz .cluster text{fill:#333;}#mermaid-svg-jDyRctDFk6wSAMgz .cluster span{color:#333;}#mermaid-svg-jDyRctDFk6wSAMgz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jDyRctDFk6wSAMgz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jDyRctDFk6wSAMgz rect.text{fill:none;stroke-width:0;}#mermaid-svg-jDyRctDFk6wSAMgz .icon-shape,#mermaid-svg-jDyRctDFk6wSAMgz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jDyRctDFk6wSAMgz .icon-shape p,#mermaid-svg-jDyRctDFk6wSAMgz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jDyRctDFk6wSAMgz .icon-shape .label rect,#mermaid-svg-jDyRctDFk6wSAMgz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jDyRctDFk6wSAMgz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jDyRctDFk6wSAMgz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jDyRctDFk6wSAMgz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 确认串口助手和 COM 口
确认 9600 8N1
确认共地和 TX/RX 交叉
确认 GPIOA/USART1 时钟
确认 PA9/PA10 模式
确认 USART_Init 六项
确认 USART_Cmd 已使能
观察 TXE/RXNE 和实际接收值
16. 官方手册定位与具体页码
以下页码均指本地 PDF 阅读器显示的 PDF 页码,不是纸质文档页脚编号。
16.1 《STM32F10xxx参考手册(中文)》
| 要查的内容 | PDF 页码 | 本节用处 |
|---|---|---|
| USART 章节、主要特性和基本结构 | 516~523 | 理解异步收发器、发送器、接收器和数据帧 |
| 波特率产生与计算 | 525 | 理解外设时钟与波特率关系 |
| USART 中断请求 | 538 | 通过轮询测试后学习 RXNE 中断时查阅 |
USART_SR 状态寄存器 |
540~541 | 查 TXE、TC、RXNE、ORE、FE、NE、PE |
USART_DR 数据寄存器 |
541~542 | 理解发送写 DR、接收读 DR |
USART_BRR 波特率寄存器 |
542 | 理解 USART_Init() 如何生成波特率 |
USART_CR1 控制寄存器 1 |
542~544 | 查 UE、M、PCE、PS、TE、RE、RXNEIE |
USART_CR2 控制寄存器 2 |
544~545 | 查 STOP 停止位字段 |
USART_CR3 控制寄存器 3 |
545~546 | 查 RTS/CTS、DMA 和错误中断配置 |
16.2 《STM32F103x8B数据手册(中文)》
| 要查的内容 | PDF 页码 | 本节用处 |
|---|---|---|
| LQFP48 引脚图和引脚定义表 | 17~20 | 核对 PA9/USART1_TX、PA10/USART1_RX 复用功能 |
16.3 《STM32F103xx固件函数库用户手册》
| 要查的内容 | PDF 页码 | 本节用处 |
|---|---|---|
| USART 函数总览 | 344~345 | 快速确认库中有哪些 USART 函数 |
USART_Init() 与 USART_InitTypeDef |
346~348 | 查初始化函数和结构体成员合法值 |
USART_StructInit()、USART_Cmd() |
349 | 查默认配置和外设使能函数 |
USART_ITConfig() |
350 | 后续开启 RXNE 中断时使用 |
USART_SendData()、USART_ReceiveData() |
354 | 查单字节发送、接收函数原型和作用 |
USART_GetFlagStatus() |
359 | 查 TXE、TC、RXNE 等状态标志 |
USART_GetITStatus() |
360~361 | 后续在中断函数中判断中断来源 |
推荐查阅顺序不是从头读完,而是:
text
数据手册查引脚 → 参考手册查硬件和寄存器 → 库手册查函数 → 回到代码验证
17. 与课程原例程的对应关系
| 本笔记内容 | 课程代码位置 | 本笔记做的取舍 |
|---|---|---|
| USART1 初始化和发送 | 9-1 串口发送/Hardware/Serial.c |
保留 USART1、PA9、9600 和 TXE 等待 |
| USART1 收发参数 | 9-2 串口发送+接收/Hardware/Serial.c |
保留 PA10、`Tx |
| 接收数据并回显 | 9-2 串口发送+接收/User/main.c |
改成轮询 RXNE,暂不引入 NVIC 和 ISR |
| OLED 显示十六进制接收值 | 9-2 串口发送+接收/User/main.c |
增加固定测试值比较和 PASS/FAIL |
课程 9-2 使用中断接收,本笔记先使用轮询,是为了单独验证 USART 本身。先把"收到字节 → 读 DR → 回显"练熟,再加入中断,问题更容易定位。
18. 最小背诵卡
text
USART1:PA9发,PA10收,都在GPIOA;USART1挂APB2。
接线:TX接RX,RX接TX,GND接GND。
参数:9600,8位,1停止,无校验,无流控,收发。
发送:USART_SendData,等待TXE。
接收:RXNE置位,USART_ReceiveData读DR。
初始化最后:USART_Cmd使能。
一句话心智模型:
RCC 给 USART1 和 GPIOA 时钟,GPIO 把 PA9/PA10 接到外设,
USART_Init配好帧格式和波特率,发送时写 DR 并等 TXE,接收时看 RXNE 再读 DR,最后用回显和实际值比较证明整个链路工作。