目录
3.6硬件数据流控 (Hardware Flow Control)
3.8SCLK控制 (Synchronous Clock Control)
#USART串口协议#
一、通信接口
- 通信目的:将一个设备的数据传送到另一个设备,扩展硬件系统
STM32芯片内部集成许多功能模块(eg.定时器计数,PWM输出,AD采集……)电路所配置的寄存器,数据寄存器都在芯片,操作芯片直接读写即可
STM内部芯片没有的功能需要外挂芯片完成,其数据在STM32外部,获取方式是:在俩个设备之间连接一根或多根通讯线,通过通讯线路发送或者接收数据,完成数据交换从而实现控制外挂模块和读取外挂模块数据。
- 通信方式:设备与设备之间的连接方式
串行 : 一根或几根线,一次传1个比特(bit)。 节省引脚,适合远距离(相对并行),是主流。UART, I2C, SPI, USB, CAN都是串行。
并行: 多根线(如8根, 16根),一次传多个比特(一个字节或字)。 速度快,但引脚占用多,线间干扰大,距离短。STM32内部总线常用,外部用得少了(如老式打印机接口)。
有线 vs 无线:STM32本身通常处理有线协议,无线(如WiFi, BLE)常通过额外模块(如ESP8266, HC-05)用UART/SPI连接STM32。
- 通信协议:制定通信的规则,通信双方按照协议规则进行数据收发
举个例子:考试的时候想给别人传答案,就可以和对方约定一个通信协议。比如先咳嗽一声,代表通信开始,然后竖一个手指,代表发生a,竖两个手指代表发生b,竖三个手指代表发送c,然后挥一挥手,代表通讯结束。这就是一种通信方式。
通信的目的是进行信息传递。双方约定的规则,就是通信协议。
名称 |
引脚 |
双工 |
时钟 |
电平 |
设备 |
USART |
TX、RX |
全双工 |
异步 |
单端 |
点对点 |
I2C |
SCL、SDA |
半双工 |
同步 |
单端 |
多设备 |
SPI |
SCLK、MOSI、MISO、CS |
全双工 |
同步 |
单端 |
多设备 |
CAN |
CAN_H、CAN_L |
半双工 |
异步 |
差分 |
多设备 |
USB |
DP、DM |
半双工 |
异步 |
差分 |
点对点 |
以上通信接口STM32F103C8T6都支持,表只列举最典型参数和配置。
1.1通信接口(5个)
- UART (Universal Asynchronous Receiver/Transmitter): 最基础的点对点串行协议。像两个人面对面聊天。
TX (Transmit): 发数据的引脚。STM32的嘴巴。
RX (Receive): 收数据的引脚。STM32的耳朵。
(可选) RTS/CTS: 硬件流控引脚,用于协调发送速度(防止数据淹没对方)。
(uart是通用异步收/发器,usart通用同步/异步收/发器,相较于UART多一个时钟输出,无时钟输入)
- I2C (Inter-Integrated Circuit): 多设备共享“总线”的低速串行协议。像一个小组讨论,大家共用一条电话线,靠“名字”(地址)区分。
SCL (Serial Clock): 时钟线。由主设备产生,像打拍子指挥节奏。大家共用。
SDA (Serial Data): 数据线。主从设备都通过它收发数据。大家共用。
注意: I2C总线需要上拉电阻(通常4.7KΩ)连接到电源(3.3V),让总线在空闲时保持高电平。
- SPI (Serial Peripheral Interface): 高速全双工串行协议,通常点对点或点对少量设备。像两个人用两条专用电话线同时说和听。
SCK (Serial Clock): 时钟线。由主设备产生,指挥节奏。
MOSI (Master Out Slave In): 主设备发,从设备收的数据线。
MISO (Master In Slave Out): 主设备收,从设备发的数据线。
NSS / CS (Slave Select / Chip Select): 片选线。由主设备控制,低电平有效。拉低哪条线,就选中哪个从设备进行对话。主设备可以有多个CS引脚连接不同从设备。
- USB (Universal Serial Bus): 复杂但强大的通用串行总线协议。
DP (Data Positive)
DM (Data Negative)
专用差分信号线对,需要符合USB规范的硬件设计。
- CAN (Controller Area Network): 主要用于汽车、工业的可靠网络协议。
CAN_H (CAN High)
CAN_L (CAN Low)
专用差分信号线对,需要终端电阻(120Ω)。
1.2双工模式
比喻:对话的方向限制。是只能一方说(单工)?还是能轮流说(半双工)?还是能同时说和听(全双工)?
单工: 数据只能单向流动。例如:只读传感器、广播(电台发送,收音机只能接收)。STM32较少纯粹单工。
半双工: 数据可以双向流动,但同一时间只能一个方向。就像对讲机,说完“Over”才能听。I2C是半双工 (SDA线同一时刻只能一个设备驱动)。CAN也是半双工 (总线竞争)。
全双工:数据可以同时双向流动。就像打电话,能边说边听。UART、SPI是典型全双工 (有独立的TX/RX线或MOSI/MISO线)。注意: SPI虽然是物理全双工,但实际应用中,有时从设备在收到主设备命令前并不知道要发什么数据,所以主设备发的可能是命令,从设备发的可能是数据,看起来像半双工,但硬件上具备同时收发的能力。
1.3时钟特性
比喻:对话的语速或节拍器。
同步通信 : 通信双方共用同一个时钟信号来同步数据的发送和接收。主设备提供时钟(SCK/SCL),像指挥拍子。SPI,I2C是同步通信。
优点: 速度快,抗干扰相对好(因为有时钟边沿采样)。
缺点: 需要额外的时钟线。时钟频率限制通信距离。
异步通信: 没有共享的时钟线。双方事先约定好一个速度(波特率),依靠数据帧内部的起始位、停止位来同步每个字节。UART是异步通信。
优点: 只需要两根数据线(TX/RX),适合远距离(相对)。
缺点: 需要精确的波特率发生器,双方波特率误差不能太大(通常<3%),速度相对同步通信慢。更容易受干扰(没有时钟边沿约束)。
波特率 (Baud Rate): 衡量异步通信速度的单位,表示每秒传输的符号数(1个符号通常代表1个比特bit)。常用值:9600, 19200, 38400, 115200等。波特率越高,速度越快,但对时钟精度和信号质量要求越高。
时钟频率 (SCK/SCL): 衡量同步通信速度的单位(Hz)。SPI可达几十MHz,I2C标准模式100KHz,快速模式400KHz,高速模式3.4MHz等。
1.4信号传输方式
- 单端信号
用1根信号线 + 公共地线(GND)传输数据。
逻辑状态由信号线对地(GND)的电压差决定:
逻辑1:信号线电压 > 高电平阈值(如3.3V系统为2.0V)
逻辑0:信号线电压 < 低电平阈值(如0.8V)
通俗比喻:像一个人用嗓门音量大小传递信息:
大喊(高电平) = 1
小声(低电平) = 0
参考点是环境背景噪音(相当于GND)。
优点 |
缺点 |
电路简单,成本低(少1根线) |
抗干扰能力差 |
功耗较低 |
传输距离短(通常<1米) |
适合低速通信 |
地线噪声直接影响信号 |
- 差分信号
用2根相位相反的信号线传输数据(无地线依赖):
D+(正向信号线)
D-(反向信号线)
逻辑状态由两根线之间的电压差决定:
逻辑1:D+电压 - D-电压 > +阈值(如+200mV)
逻辑0:D+电压 - D-电压 < -阈值(如-200mV)
通俗比喻:像两个人用反义词对暗号:
A说“高”,B说“低” → 表示1
A说“低”,B说“高” → 表示0
外界干扰同时影响两人(如噪音+10dB),但暗号差值不变。
优点 |
缺点 |
超强抗干扰(抑制共模噪声) |
电路复杂(多1根线) |
传输距离远(可达百米) |
功耗较高 |
高速传输(GHz级) |
成本更高 |
降低电磁辐射(EMI) |
需阻抗匹配 |
特性 |
单端信号 |
差分信号 |
信号线数 |
1根 + GND |
2根(D+和D-) |
抗干扰 |
弱(依赖GND质量) |
极强(抵消共模噪声) |
传输距离 |
短(<1米) |
长(可达100米+) |
速度 |
低速(通常<10Mbps) |
高速(可达Gbps) |
成本 |
低 |
高(多1根线+专用芯片) |
噪声辐射 |
高(电流经地线形成环路) |
低(磁场相互抵消) |
1.5设备特性
点对点:俩个设备直接传输数据
多设备:需要寻址以确定通信对象
二、串口通信
串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力
USB转串口模块:芯片型号CH340(将串口协议转换为USB协议),实现串口和电脑通信。
陀螺仪传感器模块:可测量角速度,加速度等姿态参数,左右各四个引脚,一边串口引脚,一边I2C引脚。
蓝牙串口模块:四个角是串口通信引脚,芯片可以和手机互联,实现手机遥控单片机功能。
2.1硬件电路
简单双向串口通信有两根通信线(发送端TX和接收端RX)
TX与RX要交叉连接
当只需单向的数据传输时,可以只接一根通信线
一般串口通信的模块有四个引脚,VCC和GND供电,TX和RX通信且为单端信号,它们的高低电平是相对于GND,因此GND严格上也是通信线。所以串口通信的RX、TX、GND必须连接。如果两个设备都有独立供电,VCC可以不接。
如果其中一个设备没有供电,比如设备1是STM32,设备2是蓝牙串口模块。STM32有独立供电,蓝牙串口没有独立供电,就需要把蓝牙串口的VCC和STM32的VCC接在一起。STM32通过VCC-VCC这根线向右边的子模块供电。供电电压需要注意,是按照子模块要求。
当电平标准不一致时,需要加电平转换芯片
3.3V器件 ↔ 5V器件:
5V输出到3.3V输入:需分压电阻或电平转换芯片。
3.3V输出到5V输入:通常可直接连接。
TTL ↔ RS232:必须使用电平转换芯片(如MAX3232)。
2.2电平标准
电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
TTL电平:+3.3V或+5V表示1,0V表示0
RS232电平:-3~-15V表示1,+3~+15V表示0
一般在大型机器使用,由于环境可能恶劣,静电干扰较大,因此电瓶电压比较大,允许波动范围也大。
RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号)
差分信号抗干扰能力强。其通信距离可达上千米。
2.3串口参数
波特率:串口通信的速率
串口一般异步通信,需要双方确定通信速率(波特率,每秒传输码元的个数,二进制情况下,一个码元为一个比特,此时波特率=比特率)
每位比特(bit)的持续时间 T = 1 / 波特率。
优先选用 11.0592MHz 晶振(可被常用波特率整除)
72MHz系统时钟下,9600波特率误差仅0.14%(推荐)
起始位:标志一个数据帧的开始,固定为低电平
数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
若发送数据超过设定位数,高位会被截断。
校验位:用于数据验证,根据数据位计算得来
三种方式:无校验,奇校验(数据位+校验位=奇数个1),偶校验
但是若俩个数据发送过程都出错,奇偶校验就检测不出来错误。
串口助手中,数据位为有效载荷,校验位是独立的1位
停止位:用于数据帧间隔,固定为高电平
2.4串口数据帧整体结构(传输数据最小单位)
串口发送的字节格式,由串口协议规定。
串口每个字节都装载在一个数据帧,每个数据帧都由起始位,数据位和停止位组成。数据位8个代表一个字节8位。
数据帧结构:
空闲态:
通信未开始时,TX/RX线保持高电平(逻辑1)。
起始位(Start Bit):
1个比特的低电平(0),向接收方宣告“数据开始传输!”
关键作用:实现帧同步(接收方检测到下降沿即启动计时)。
数据位(Data Bits):
5~9个比特(通常选8位,兼容ASCII)
传输顺序:LSB(最低位)先传!
例如发送字符 'A' (0x41 = 二进制 01000001):
实际传输顺序为 1→0→0→0→0→0→1→0(从D0到D7)
校验位(Parity Bit):
可选,1个比特,用于检错(不能纠错):
奇校验(Odd):数据位+校验位的“1”总数=奇数
偶校验(Even):数据位+校验位的“1”总数=偶数。
例:0x41 (01000001)有2个“1”(偶数),若用偶校验则校验位=0
停止位(Stop Bit):
1~2个比特的高电平(1) ,表示“本帧结束”
强制拉高,为下一帧起始位的下降沿做准备。
数据位8位,无校验位
数据位9位,有校验位
2.5串口通信实际波形
第一个波形,发送字节数据0x55,波特率9600,每位时间是1/9600,大概104us。没发送数据时为空闲状态-高电平。数据帧开始先发送起始位,产生下降沿代表数据帧开始。数据0x55转为二进制,低位先行(依次发送1010 1010)。这个参数是八位数据,1位停止,无校验。没有校验位,后面的停止位把引脚置回高电平,数据帧完成发送。
STM32中,根据字节数据反转高低电平是USART外设自动完成,若软件模拟产生此波形,定时器104us+GPIO_WriteBit置高低电平。TX引脚发送是置高低电平,RX引脚接收为读取高低电平,也可以是USART外设自动完成,若软件模拟,定时调用GPIO_ReadInputDataBit读取每一位+接收使用外部中断(起始位下降沿触发)+对齐采样位采取8次。
其余波形也是如此(+校验位)
右下角俩个波形可以看出:串口停止位可以配置(1/1.5/2位)中间没有空闲状态
总结:串口通信——TX引脚输出定时翻转的高低电平,RX引脚定时读取引脚的高低电平。每个字节的数据加上起始位、停止位、可选的校验位,打包成数据帧依次输出在TX引脚,另一端RX引脚依次接收,就完成字节数据的传递。
三、USART
3.1USART简介
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器
相较于UART模式,多一个时钟输出。
USART=UART+同步通信扩展功能
USART 与 UART 的本质区别
特性
UART
USART
通信模式
仅异步
同步 + 异步
时钟线
无
同步模式需SCLK引脚
协议支持
基础串行协议
支持LIN、智能卡、IrDA等
硬件复杂度
简单
更复杂(寄存器更多)
STM32资源
部分型号仅有UART
主流型号均有USART
USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里
自带波特率发生器,最高达4.5Mbits/s
波特率发生器相当于一个分频器。比如APB2总线72MHz,波特率发生器极性分频,得到波特率时钟(通信波特率),在此时钟下进行接收发送波形。波特率常用9600和115200.
可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)
可选校验位(无校验/奇校验/偶校验)
支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
STM32F103C8T6 USART资源: USART1(APB2)、 USART2、 USART3
3.2USART六大高级功能
1.多处理器通信(地址标记)
用途:一主多从通信(类似I2C寻址)
原理:
发送特殊地址帧唤醒目标从机
从机忽略非自身地址的数据帧
配置:USART_CR2 寄存器的 ADD0~3 设置自身地址
2. 硬件流控(RTS/CTS)
引脚:
RTS:输出,指示“本机准备好接收”
CTS:输入,检测“对方是否允许发送”
作用:防止接收缓存溢出(尤其高速传输)
3. LIN总线支持
LIN Break检测:自动识别13位低电平起始符
同步场生成:硬件自动发送 0x55 同步字节
配置:USART_CR2 寄存器的 LINEN 使能
4. 智能卡模式(ISO 7816)
支持T=0/T=1协议(SIM卡、金融IC卡)
自动生成ETU(Elementary Time Unit)时钟
硬件校验奇偶错误并重传
5. IrDA红外编码
内置编解码器:将数据转为3/16位脉宽红外信号
支持SIR(115.2kbps)和MIR(1.152Mbps)速率
6. DMA联动
解放CPU:自动搬运大量数据到USART缓存
3.3USART框图
3.3.1STM32 USART寄存器精要
寄存器 |
作用 |
关键位域 |
USART_SR |
状态寄存器 |
RXNE(接收缓存非空), TC(发送完成), PE(校验错) |
USART_DR |
数据寄存器(读写合一) (TDR发送+RDR接收) |
写入数据启动发送,读取获取数据 |
USART_BRR |
波特率寄存器(16位) |
存储 USARTDIV 值 |
USART_CR1 |
控制寄存器1 |
UE(使能), M(字长), PCE(校验使能) |
USART_CR2 |
控制寄存器2 |
STOP(停止位), CLKEN(同步时钟使能) |
3.3.2发送接收控制器—数据寄存器(DR)
两个数据寄存器——发送数据寄存器TDR(Transmit DR)和接收数据寄存器RDR(Receive DR)。两个寄存器占用同一个地址(与51单片机串口的SBUF寄存器相似)。在程序上只表现为一个寄存器——数据寄存器DR。但实际硬件中是分成两个寄存器,一个用于发送TDR,一个用于接收RDR。TDR只写,RDR只读。当进行写操作时,数据就写到TDR,当进行读操作时,数据就从RDR读出。
发送器控制
控制发送移位寄存器工作
负责把CPU准备好的数据通过TX引脚一位一位地发送出去(比如发送一个字节0x55)
工作流程(发快递)=发送数据流程
发送数据流程
发送数据寄存器TDR:类似于快递站“打包台”(临时存放待发送的快递)
发送移位寄存器:快递站的“传送带”(把包裹逐个送出)
关键状态标志位(USART_SR寄存器)
TXE(Transmit Data Register Empty):
TXE=1:打包台空着,可以放新快递(CPU可写入新数据)
TXE=1:TDR为空(数据已转移到移位寄存器),可写入新数据
写入USART_DR后自动清零
TXE=0:打包台被占用(正在发送)
TC(Transmission Complete):
TC=1:所有快递已发出(包括最后的包装盒——停止位)
TC=1:移位寄存器发送完毕(包括停止位),一帧完成
读SR寄存器 + 写USART_DR可清零
TC=0:还在发送中
移位寄存器:像传送带一样,把数据从低位到高位逐个比特推出去
发送数据流程文字描述
在某时刻给TDR写入0x55,在寄存器中二进制存储0101 0101。此时,硬件检测到写入的数据并检查当前移位寄存器是否有数据正在移位。如果没有,0101 0101全部移动到发送移位寄存器,准备发送。
当数据从TDR移动到移位寄存器时,会置一个标志位TXE(TX Empty),发送寄存器空。我们检查这个标志位,如果置1了,我们就可以在TDR写入下一个数据了。若TXE=1,数据没有发送,但是数据已经从TDR转移到发送移位寄存器,从而可写入新数据。此时,发送移位寄存器会在发送器控制的驱动下向右移位,一位一位地把数据输出到TX引脚。这里是向右移位的,正好和串口协议规定的低位先行一致。
数据移位完成后,新的数据会再次自动地从TDR转移到发送移位寄存器。如果当前移位寄存器移位还没有完成,TDR的数据就会进行等待,一旦移位完成,就会立刻转移过来。
TDR和移位寄存器的双重缓存,可保证在连续发送数据时,数据帧之间不会有空闲,提高工作效率。简单来说,数据一旦从TDR转移到移位寄存器了,不管有没有移位完成,都立刻把下一个数据放在TDR里等着,一旦移位完成,新的数据就会立刻跟上。
接受控制器
控制接收移位寄存器工作
负责把RX引脚一位一位地接收数据,并组装成完整的字节交给CPU(比如接收0XAA)
工作流程(收快递)=接收数据流程
接收数据流程
接收移位寄存器:快递站的“扫描仪”(逐个检查收到的快递)
接收数据寄存器RDR:快递站的“暂存架”(存放已扫描的完整快递)
关键状态标志位(USART_SR寄存器)
RXNE=1:暂存架上有快递待取(CPU需及时读取)
移位寄存器:像扫描仪一样,从低位到高位逐个比特组装数据
错误处理:
停止位不对 → 标记为"破损快递"(帧错误FE)
暂存架未取又来新快递 → 标记"爆仓"(溢出错误ORE)
数据从RX引脚通向接收移位寄存器,在接收器控制的驱动下一位一位的读取RX电平,先放在最高位,然后往右移,移位八次之后就能接收一个字节。由于串口协议规定低位先行,因此接收移位器从高位往低位方向移动。当一个字节移位完成,一整个字节就会转移到接收数据寄存器RDR。在转移过程中产生标志位RXNE,接收数据寄存器非空。当RXNE=1时,可将数据读取。同样俩个寄存器进行缓存,当数据从移位寄存器转移到RDR时,可直接移位接收下一帧数据。
发送有帧头和帧尾,接收需要剔除它们,电路内部自动执行。
双缓冲机制的优势
为什么需要两个寄存器?
场景 |
单寄存器方案 |
双寄存器方案(TDR+RDR) |
发送时 |
必须等全部发完才能写新数据 |
数据装上传送带后立刻可写下一包 |
接收时 |
必须立刻取走否则丢数据 |
前一包在暂存架时仍能收下一包 |
效率 |
低 |
高(实现"流水线"作业) |
类比:就像快递站有两个工作台:
一个在打包新快递(TDR),另一个在送出已打包的快递(移位寄存器)
互不干扰,效率翻倍!
常见问题:
Q1: 为什么发送时要先检查TXE?
A:就像快递站——只有打包台空着(TXE=1),才能放新快递,否则会覆盖未处理的快递
Q2: 数据为什么要从低位(LSB)开始传?
A:就像写数字"123"——先写个位"3",再十位"2",最后百位"1",接收方反过来组装即可还原
Q3: 移位寄存器怎么知道何时停止?
A:根据配置的"数据位数"(如8位)——就像扫描仪知道快递单号有几位数字。
Q4:为什么发送时要用 TXE 和 TC 两个标志?
TXE=1(打包台空着) 只表示可以放新数据到TDR,但可能还有数据在移位寄存器没发完。
TC=1 (所有快递发送完毕)表示所有数据(包括停止位)已经全部发出。
Q5:接收数据时为什么会丢数据?
原因1:CPU没有及时读取 USART_DR(导致 ORE 溢出错误)。
原因2:波特率不匹配(比如STM32设成115200,但对方发的是9600)。
解决方法:
- 使用DMA自动接收数据。
- 确保双方波特率一致。
Q6:发送和接收能同时进行吗?
可以! USART是 全双工 的,发送和接收完全独立(有各自的寄存器和引脚)。
发送 vs 接收控制器对比
功能 |
发送控制器 |
接收控制器 |
核心寄存器 |
TDR(发送数据寄存器) |
RDR(接收数据寄存器) |
移位寄存器 |
把数据逐位推到TX引脚 |
从RX引脚逐位读取数据 |
关键标志 |
TXE(可写新数据)、TC(发送完成) |
RXNE(有数据可取)、FE/PE/ORE(错误) |
数据顺序 |
LSB(最低位)先发 |
LSB(最低位)先收 |
触发方式 |
CPU写入 USART_DR 启动发送 |
检测起始位(下降沿)自动启动接收 |
发送控制器:把数据从 TDR → 移位寄存器 → TX引脚 逐位发出(LSB先发)。
接收控制器:从RX引脚 → 移位寄存器 → RDR 逐位接收(LSB先收)。
状态寄存器(USART_SR) 就像 仪表盘,告诉你当前状态(能不能发/有没有数据/是否出错)。
3.3.3状态寄存器(SR)
状态寄存器(USART_SR) 就像是USART模块的“工作状态仪表盘”,实时显示发送、接收、错误等各种状态。它的每个标志位都像是一个小灯泡,亮起(=1)时表示某种状态发生。
状态寄存器核心标志位
标志位 |
名称(英文全称) |
作用(通俗解释) |
谁负责点亮它? |
如何熄灭它? |
TXE |
Transmit Data Register Empty |
发送快递台空啦! (可以放新数据了) |
当TDR数据转移到移位寄存器时 |
写入新数据到USART_DR自动熄灭 |
TC |
Transmission Complete |
所有快递发完啦! (包括最后的包装盒-停止位) |
移位寄存器发完最后一比特时 |
读SR寄存器 + 写USART_DR |
RXNE |
Receive Data Register Not Empty |
暂存架上有快递! (快来取数据) |
当移位寄存器存满一帧数据到RDR时 |
读取USART_DR自动熄灭 |
ORE |
Overrun Error |
爆仓啦! (新快递把旧快递挤掉了) |
RDR数据未读又收到新数据时 |
读SR + 读USART_DR |
FE |
Framing Error |
快递包装破损! (停止位不对) |
检测到停止位为低电平时 |
读SR + 读USART_DR |
PE |
Parity Error |
快递单号对不上! (校验错误) |
奇偶校验失败时 |
读SR + 读USART_DR |
工作状态实时演示
状态寄存器的三大关键作用
通知CPU该干啥
亮TXE灯:提醒CPU"可以发下一个数据了"
亮RXNE灯:提醒CPU"快来取数据"
报告错误情况
亮FE灯:警告"对方发来的数据格式不对"
亮ORE灯:警告"你取数据太慢了!"
控制DMA传输
TXE/RXNE标志可直接触发DMA请求,实现"自动搬运数据"
状态寄存器使用避坑指南
TXE vs TC的区别
TXE=1:仅表示可以放新数据(可能还有数据在移位寄存器未发完)
TC=1:表示所有数据(含停止位)已完全发出
清除标志的玄机
TXE:写入DR自动清除
TC:需要读SR + 写DR(两步操作!)
错误标志(ORE/FE/PE):需要读SR + 读DR
DMA模式下的特殊行为
启用DMA发送时,TXE标志不会置1(由DMA自动维护)
但TC标志仍会正常置位,可用于判断发送完成
标志位更新时序
TXE置位时刻:刚好在数据从TDR加载到移位寄存器时
TC置位时刻:停止位发送完成后立即发生
RXNE置位时刻:停止位采样正确的瞬间
事件 |
标志位变化 |
延迟周期(72MHz系统) |
写入USART_DR |
TXE立即清零 |
0 |
TDR→移位寄存器 |
TXE置位 |
1-2 |
停止位发送完成 |
TC置位 |
1 |
RX引脚采样完成 |
RXNE置位 |
1 |
硬件连接关系
USART_SR ← 发送控制器/接收控制器/错误检测单元
USART_SR → 中断控制器/NVIC
USART_SR ↔DMA控制器(通过TXE/RXNE触发请求)
3.4USART工作原理
1.异步模式(UART模式)
无时钟线,靠起始位/停止位同步
数据帧结构:起始位 + 数据位(8~9) + 校验位 + 停止位
波特率由内部定时器生成(依赖APB总线时钟)
2. 同步模式
有时钟线(SCLK),由主设备(通常是STM32)产生
数据在时钟边沿采样(类似SPI),支持全双工
可配置时钟极性(CPOL)和相位(CPHA)
3. 数据收发引擎
发送器:并行数据 → 移位寄存器 → 按比特串行输出
接收器:采样RX信号 → 移位寄存器 → 数据校验 → 存入缓存
3.5波特率发生器
USART模块的“心跳控制器”,决定数据发送和接收的速度节奏,确保通信双方以相同的速率传输数据
定义:每秒传输的符号数(1符号=1比特),单位是bps(比特/秒)
常见值:9600、115200(数值越大速度越快)
关键公式:
每位持续时间(秒) = 1 / 波特率
例如:115200波特率 → 每位持续 8.68μs
波特率发生器结构
时钟源:来自STM32的APB总线(如APB1=72MHz)
分频器:16位寄存器(USART_BRR)计算分频系数
输出:生成发送和接收所需的精确时钟
计算公式:
USARTDIV = f_PCLK / (16 * 波特率)
f_PCLK1/2:APB总线时钟频率(如72MHz)
USARTDIV:写入USART_BRR的值
分频值拆分:
整数部分:USART_BRR[15:4]
例:39 → 0x27小数部分:USART_BRR[3:0](步长0.0625)
例:0.0625 → 0x1, 0.125 → 0x2
工作流程
1.发送时钟控制
2.接收采样时钟
3次采样抗干扰:在每位中点附近采样3次,取多数值
问题:为什么波特率通信不稳定?
1.APB时钟配置错误(可能误用HSI内部时钟)
2.BRR计算未四舍五入(小数部分处理不当)
3.6硬件数据流控 (Hardware Flow Control)
USART通信的“交通警察”,通过nRTS(Request To Send请求发送)和nCTS(Clear To Send清除发送)俩跟信号线,防止数据拥堵丢失
硬件流控核心概念
1. 两根关键信号线
信号线 |
方向 |
作用(低电平有效) |
等效比喻 |
nRTS |
输出 |
告诉对方:“我的接收缓冲区有空,可以发数据” |
快递仓库的“有空位”指示灯 |
nCTS |
输入 |
检测对方:“是否允许我发送数据?” |
快递员的“绿灯通行”信号 |
2.工作逻辑
3.完整工作流程
硬件流控的三大核心机制
1. nRTS触发阈值
- STM32自动控制:当接收缓冲区达到预设阈值时自动拉高/拉低nRTS
- 阈值配置(通过USART_RTOR寄存器):USART1->RTOR = 0x10;
- // 当空闲空间<16字节时拉高nRTS
2. nCTS响应时间
- 实时检测:发送前检查nCTS,若为高电平则阻塞发送
- 典型响应延迟:<2个波特周期(115200波特率下约17μs)
3. 错误恢复
错误类型 |
触发条件 |
处理方式 |
CTS超时 |
nCTS持续高电平>1ms |
触发中断,重新初始化通信 |
RTS冲突 |
nRTS被意外拉高 |
检查缓冲区是否溢出 |
3.7中断控制
通信系统的智能警报器,当事件发生时(数据送达/发送完成/出现错误),立即通知CPU处理。
中断触发条件
中断类型 |
触发标志位 |
典型应用场景 |
比喻解释 |
TXE |
USART_SR.TXE |
发送寄存器空,可填充新数据 |
快递员喊:“箱子空了,快装货!” |
TC |
USART_SR.TC |
一帧数据完全发送完毕 |
快递站通知:“包裹已全部发出!” |
RXNE |
USART_SR.RXNE |
接收到新数据 |
收件人收到短信:“快递到货!” |
ORE |
USART_SR.ORE |
数据溢出(旧数据被覆盖) |
仓库报警:“货物堆积爆仓了!” |
IDLE |
USART_SR.IDLE |
检测到总线空闲(无数据) |
快递员报告:“今天没货送了” |
中断优先级:可配置4个抢占级别
发送中断流程
接收中断流程
TXE要数据,TC报完成,RXNE催取件,ORE喊救命
3.8SCLK控制 (Synchronous Clock Control)
SCLK基础概念
1. 同步 vs 异步模式
特性 |
异步模式(UART) |
同步模式(USART+SCLK) |
时钟线 |
无 |
需SCLK引脚 |
速率 |
较低(通常<1Mbps) |
更高(可达SPI级速度) |
适用场景 |
简单设备间通信 |
需精确时钟同步的设备 |
2.SCLK引脚作用
主模式:STM32输出时钟信号(驱动从设备)
从模式:STM32接收外部时钟(需外部主设备)
3.工作流程
同步发送时序(主模式)
同步接受时序(从模式)
SCLK三大核心参数:
1. 时钟极性(CPOL) - 空闲状态电平
2. 时钟相位(CPHA) - 数据采样边沿
3. 频率控制(BRR) - 通过波特率寄存器调节
调试口诀:
“SCLK不正常,先查CPOL/CPHA,再量时钟频率”
四种时钟模式(类似SPI)
模式 |
CPOL |
CPHA |
数据采样边沿 |
数据变化边沿 |
0 |
0 |
0 |
上升沿 |
下降沿 |
1 |
0 |
1 |
下降沿 |
上升沿 |
2 |
1 |
0 |
下降沿 |
上升沿 |
3 |
1 |
1 |
上升沿 |
下降沿 |
3.9TE使能
USART发送功能总开关,控制整个发送通道的激活与关闭
TE使能的核心作用
行为 |
TE=0(关闭) |
TE=1(使能) |
TX引脚状态 |
高阻态或保持最后电平 |
正常输出数据 |
数据发送 |
禁止发送(忽略写入DR) |
允许发送 |
移位寄存器操作 |
冻结 |
正常工作 |
中断/DMA触发 |
不产生TXE/TC中断 |
正常触发 |
TE使能的四大关键特性
硬件自动管理空闲状态
TE使能后,TX引脚自动拉高(空闲状态),直到发送第一个起始位。
首次写入DR的同步作用
第一次写入USART_DR会触发:起始位(低电平);后续数据位按LSB-first发出
关闭时的缓冲保护
若TE由1→0时移位寄存器仍在发送:会完成当前帧传输(包括停止位),之后TX引脚进入高阻态
与DMA的联动
即使TE=1,如果DMA未配置,TXE不会自动触发DMA请求
3.10唤醒单元
作用:实现串口挂载多设备
功能:给串口分配地址。串口一般时点对点通信,仅支持俩个设备互相通信。多设备在一条线上可以接都哦个设备,每个设备分配一个地址,想跟某个设备通信,先寻址确定通信对象在进行数据收发。当发送指定地址时,设备唤醒开始工作。没有接收到的地址保持沉默。
3.11USART基本结构
串口数据收发过程
波特率发生器用于产生约定的通信速率。时钟来源是PCLK2或1。经过波特率发生器分频后,产生的时钟通向发送控制器和接收控制器,发送控制器和接收控制器用来控制发送移位和接收移位。
之后由发送数据寄存器和发送移位寄存器这两个寄存器的配合,将数据一位一位地移出去,通过GPIO口的复用输出(引脚图),输出到TX引脚,产生串口协议规定的波形。
当数据由数据寄存器转移到移位寄存器时,会置TXE标志位,判断标志位就可以知道是否可以写下一个数据。
接收部分与之类似,RX引脚的波形通过GPIO口输入,在接收控制器的控制下,一位一位地移入接收移位寄存器。由于低位先行,因此数据要从左边开始移进来,移完一帧数据后,数据就会统一转运到接收寄存器,在转移的同时置RXNE标志位,检查标志位就可以知道是否收到数据。同时这个标志位也可以去申请中断,这样就可以在收到数据时直接进入中断函数,之后快速地读取和保存数据。
四、数据帧
4.1字节设置
字长=数据位长度(包含校验位)
9位字长波形:
第一条时序是TX发送或者RX接收的数据帧格式。空闲高电平,起始为零,接着根据写入的数据置1或置0,依次发送位0到位8,最后停止位1,数据帧结束。一般选择8位数据位(一个字节)+1位校验位
第二条时钟是同步时钟输出功能。每个数据位的中间都有一个时钟上升沿,时钟的频率和数据速率一样。接收端可以在时钟上升沿进行采样,就可以精准定位每一位数据。时钟的最后一位可以通过BCL位控制要不要输出。时钟的相位极性可通过配置寄存器配置。
第三条时空闲帧1
第四条时断开帧0
三四俩条时局域网协议使用。
8位字长波形:
一般8位数据位(一个字节)+无校验位
4.2配置停止位
STM32的串口可以配置停止位长度为0.5、1、1.5、2四种。
1个停止位,停止位的时长=数据位的1位时长
1.5个停止位,停止位时长=1.5倍数据位1位时长
2个停止位的,停止位时长=2倍数据位1位时长
0.5个停止位,停止位时长=0.5倍数据位1位时长
一般选择1位停止位
对于串口来说,串口的输出TX比输入RX简单很多。
输出只要定时翻转TX引脚高低电平即可。
输入不仅要保证输入的采样频率和波特率一致,还要保证每次输入采样的位置,正好处于每一位的正中间。只有在每一位的正中间采样,高低电平读进来才是最可靠的。如果采样点过于靠前或靠后,有可能高低电平还正在翻转,电平还不稳定,或者稍有误差,数据容易采样错。
此外,输入还要对噪声有一定的判断能力。
输入RX采样方法:
4.3起始位侦测
当输入电路侦测到一个数据帧的起始位后,就会以波特率的频率连续采样一帧数据。同时从起始位开始采样位置就要对齐位的正中间。
为了实现这一功能,输入对电路采样时钟进行了细分,以波特率的16倍频率进行采样。
最开始空闲状态高电平,采样为1,在某个位置采样到0(出现下降沿)且没有噪音,就为起始位。在起始位进行16次采样,没有噪音位0,为了采集准确,根据手册描述,接收电路还会在下降沿之后的第三次、五次、七次进行一批采样。
在第八次、九次、十次再进行一批采样,且这两批采样都要要求每三位里面至少应该有两个零。
①没有噪声置0,满足情况。
②有一些轻微的噪声,导致起始位中三位里有两个0,另一个是1,符合情况,并且在状态寄存器置NE(Noise Error)噪声标志位。若起始位中三位里有一个零,不符合情况。可猜测下降沿是噪声导致,电路需要重新捕捉下降沿。
若起始位侦测正确,接收状态从空闲变为接收起始位。同时,第八、九、十次采样的位置,正好是起始位的正中间,之后接收数据位时,就都在第八、九、十、次进采样。由此保证采样位置在位的正中间。
4.4数据采样
数据位的时间长度为16。一个数据位有十六个采样时钟,由于起始位侦测已经对齐了采样时钟,因此可直接在第八、九、十、次采样数据位,为保证数据的可靠性,连续采样三次。
没有噪声的理想情况下,这三次全为1或者全为0。
全为1就认为收到了1,全为0就认为收到了0。
如果有噪声导致三次采样不是全为1或者全为0,就按照2:1的规则确定:两次为1就认为收到了1,两次为0就认为收到了0。在这种情况下,噪声标志位NE也会置1。
4.5波特率发生器
- 发送器和接收器的波特率由波特率寄存器BRR里的DIV确定
- 计算公式:波特率 = fPCLK2/1 / (16 * DIV)
五、USART初始化
5.1初始化流程
①开启时钟,需要GPIO时钟和USART时钟
②GPIO初始化,把TX配置成复用输出,RX配置成输入
③配置USART,使用结构体
④若只需要发送功能,直接开启USART
⑤若需要接收功能,需要配置中断,(NVIC+ITConfig),再开启USART
初始化完成
发送数据:调用发送函数
接收数据:调用接收函数
获取发送和接受状态:调用标志位函数
5.2USART库函数
USART基本库函数
①USART恢复省配置(恢复默认初始状态)
void USART_DeInit(USART_TypeDef* USARTx);
②USART初始化
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
③USART结构体初始化
void USART_StructInit(USART_InitTypeDef* USART_InitStruct);
④配置同步时钟输出
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct);
void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct);
⑤USART使能
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
⑥USART中断输出配置
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
⑦开启USART到DMA的触发通道
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState);
⑧发送数据(写DR寄存器)
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
⑨接收数据(读DR寄存器)
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
⑩标志位相关函数
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
5.3USART初始化代码
#include "stm32f10x.h" // Device header
void serial_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
GPIO_InitTypeDef GPIO_Initstructure;
GPIO_Initstructure.GPIO_Mode=GPIO_Mode_AF_PP;//TX使用复用推挽输出
GPIO_Initstructure.GPIO_Pin=GPIO_Pin_9;
GPIO_Initstructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstructure);
USART_InitTypeDef USART_Initstructure;
USART_Initstructure.USART_BaudRate=9600;//波特率
USART_Initstructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件流控制
/*
#define USART_HardwareFlowControl_None 不使用流控
#define USART_HardwareFlowControl_RTS 只用CTS
#define USART_HardwareFlowControl_CTS 只用RTS
#define USART_HardwareFlowControl_RTS_CTS CTSRTS都使用
*/
USART_Initstructure.USART_Mode=USART_Mode_Tx;//USART模式
/*
#define USART_Mode_Rx 发送模式
#define USART_Mode_Tx 接收模式
*/
USART_Initstructure.USART_Parity=USART_Parity_No;//校验位
/*
#define USART_Parity_No 无校验
#define USART_Parity_Even 偶校验
#define USART_Parity_Odd 奇校验
*/
USART_Initstructure.USART_StopBits=USART_StopBits_1;//停止位
/*
#define USART_StopBits_1
#define USART_StopBits_0_5
#define USART_StopBits_2
#define USART_StopBits_1_5
*/
USART_Initstructure.USART_WordLength=USART_WordLength_8b;//字长
/*
#define USART_WordLength_8b
#define USART_WordLength_9b
*/
USART_Init(USART1,&USART_Initstructure);
USART_Cmd(USART1 ,ENABLE);
}
5.4发送数据
void serial_sendbyte(uint8_t byte)
{
USART_SendData(USART1,byte);//等待数据转移到移位寄存器,再进行数据传递,为了防止新发送的数据数据覆盖原先数据,等待标志位
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
//标志位是否手动清除
/*
TXE:发送数据寄存器空 (Transmit data register empty)
当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。如果USART_CR1
寄存器中的TXEIE为1,则产生中断。对USART_DR的写操作,将该位清零。
0:数据还没有被转移到移位寄存器;
1:数据已经被转移到移位寄存器。
注意:单缓冲器传输中使用该位。
*/
//不需要手动清除标志位,在下一次的senddata时,标志位会自动清0
}
标志位是否手动清零?
TXE 标志位通常不需要(也不应该)手动清除。写入新数据到 USART_DR
寄存器是清除它的唯一且正确的方式,并且这个清除操作是自动发生的。
以下是关键点的解释
1.硬件置位 (Set by Hardware):
当硬件完成将
TDR
(发送数据寄存器) 中的数据转移到移位寄存器(准备开始通过 TX 线一位一位地发送出去)时,硬件会自动将 TXE 标志位置为 1。这个动作表示
TDR
现在空了,可以接收新的数据了。如果此时
TXEIE
(TXE 中断使能) 位为 1,这个置位操作还会触发一个中断。
2.清零机制 (Clearing Mechanism):
文档明确说明:“对 USART_DR 的写操作,将该位清零”。
这是唯一的清除 TXE 标志位的方式。
当你向
USART_DR
(数据寄存器,通常通过写这个寄存器来发送数据) 写入一个新的字节时:硬件首先将这个新字节加载到
TDR
寄存器中。然后,硬件自动地、立即地将 TXE 标志位清零 (0)。
你不需要(也无法)通过直接向标志位寄存器写 0 或 1 来手动清除 TXE 标志位。 尝试这样做要么无效,要么可能影响其他标志位。
5.5主函数程序
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "serial.h"
int main(void)
{
OLED_Init();
serial_init();
serial_sendbyte(0x44);
while(1)
{
}
}
5.6数据模式
- HEX模式/十六进制模式/二进制模式:以原始数据的形式显示
- 文本模式/字符模式:以原始数据编码后的形式显示
字符和数据在发送和接收的转换关系
5.7SendByte函数分装
5.7.1发送数组
//发送数组
void Serial_SendArray(uint8_t *Array,uint16_t Length)
{
uint16_t i;
for(i=0;i<Length;i++)
{
serial_sendbyte(Array[i]);
}
}
//main函数中的代码编写
uint8_t arr[]={0x41,0x42,0x43,0x44};
Serial_SendArray(arr,4);
5.7.2发送数字
//次方函数
uint32_t serial_pow(uint32_t x,uint32_t y)
{
uint32_t result=1;
while(y--)
{
result *=x;
}
return result ;
}
//发送数字
void serial_sendnumber(uint32_t number,uint8_t length)
{
uint8_t i;
for(i=0;i<length;i++)
{
serial_sendbyte(number/serial_pow(10,length-1-i)%10+0x30);//ASCII码中字符0对应0x30
}
}
//主函数代码
serial_sendnumber(12345,5);
5.7.3printf函数分装
//printf函数移植方法
printf("num=%d\r\n",1234);//搭配fputc函数
int fputc(int ch,FILE *f)
{
serial_sendbyte(ch);
return ch;
}
//此方法printf只能有一个,重定向到串口1,串口2无法使用
//若每个串口想使用printf,可使用sprintf
//sprintf可以把格式化字符传输到一个字符串里
char string[100];
sprintf(string "num=%d\r\n",1234);
serial_sendstring(string);
//封装printf函数
//在serial.c文件添加头文件
#include "stdarg.h"
void serial_printf(char*format,...)
{
char string[100];
va_list arg;//定义参数列表变量
va_start(arg,format);//从format位置开始接收参数表,放在arg里
vsprintf(string,format,arg);//打印位置string 格式化字符串format,参数表为arg
serial_sendstring(string);
}
5.8串口发送和接收
serial.c函数修改不多
①GPIO口加PA10引脚,使用上拉输入
②USART结构体中模式加入接收模式
③接收串口可以是查询或者中断俩种方法
查询方法
serial.c文件中
需要改变的地方
Ⅰ
GPIO_InitTypeDef GPIO_Initstructure;
GPIO_Initstructure.GPIO_Mode=GPIO_Mode_AF_PP;//TX使用复用推挽输出
GPIO_Initstructure.GPIO_Pin=GPIO_Pin_9;
GPIO_Initstructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstructure);
GPIO_Initstructure.GPIO_Mode=GPIO_Mode_IPU;//RX使用上拉输入
GPIO_Initstructure.GPIO_Pin=GPIO_Pin_10;
GPIO_Initstructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstructure);
Ⅱ
USART_Initstructure.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;//USART模式
/*
#define USART_Mode_Rx 发送模式
#define USART_Mode_Tx 接收模式
*/
uint16_t serial_getbyte(void)
{
uint16_t RXdata;
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET)
{
RXdata=USART_ReceiveData(USART1);
//是否需要清除标志位-不需要,DR寄存器清除
}
return RXdata;
}
main.c文件中
int main(void)
{
OLED_Init();
serial_init();
while(1)
{
OLED_ShowHexNum(1,1,serial_getbyte(),2);
}
}
中断方法
在serial.c文件中
添加相关中断函数
USART_ITConfig(USART1 ,USART_IT_RXNE,ENABLE );
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(USART1 ,ENABLE);
中断函数配置
uint8_t serial_getRxfalg(void)
{
if(Serial_RXFlag==1)
{
Serial_RXFlag=0;
return 1;
}
return 0;
}
uint8_t serial_getRXdata(void)
{
return Serial_RXData;
}
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)
{
Serial_RXData=USART_ReceiveData(USART1);//读取后置标志位
Serial_RXFlag=1;
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
main.c文件中
uint16_t Rxdata;
int main(void)
{
OLED_Init();
serial_init();
OLED_ShowString(1,1,"RXdata:");
while(1)
{
if(serial_getRxfalg()==1)
{
Rxdata=serial_getRXdata();
serial_sendbyte(Rxdata);
}
OLED_ShowHexNum(1,8,Rxdata,2);
}
}