目录
|
实验要求
- 使用开发板上的串口向PC发送信息
- PC通过串口向开发板发送数据,CPU在接收到后,确认信息,并通过串口返回数据
- 例如:开发板先发送一个字符‘c’,然后PC发送一个字符‘a’,开发板接收到后,再发送一个字符‘b’
实验目的
- 学习和掌握STM32的USART模块的工作原理和使用方法
- 学习和掌握USART固件库的使用
- 掌握串口中断的使用方法
实验分析
-
硬件分析:
- USART的工作原理
-
软件分析:
- USART固件库
- USART实例
开发板原理图设计
-
MAX3232
-
与主芯片的连接
硬件知识点
详见STM32F10XXX英文版参考手册RM0008-Reference Manual
USART
通用同步异步收发器(USART)提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换。USART利用小数波特率发生器提供宽范围的波特率选择。
它支持同步单向通信和半双工单线通信,也支持LIN(局部互连网),智能卡协议和IrDA(红外数据组织)SIR ENDEC规范,以及调制解调器(CTS/RTS)操作。它还允许多处理器通信。
使用多缓冲器配置的DMA方式,可以实现高速数据通信。
-
(表) USART模式支持
USART内部结构
引脚定义
任何USART双向通信至少需要两个引脚:接收数据输入(RX)和发送数据输出(TX)。
- RX:接收数据输入。通过过采样技术来区别数据和噪音,从而恢复数据。
- TX:发送数据输出。当发送器被禁止时,输出引脚恢复到它的I/O端口配置。当发送器被激活,并且不发送数据时,TX引脚处于高电平。在单线和智能卡模式里,此I/O口被同时用于数据的发送和接收。
在同步模式中需要下列引脚:
- CK:发送器时钟输出。此引脚输出用于同步传输的时钟, (在起始位和停止位上没有时钟脉冲,软件可选地,可以在最后一个数据位送出一个时钟脉冲)。数据可以在RX上同步被接收。这可以用来控制带有移位寄存器的外部设备(例如LCD驱动器)。时钟相位和极性都是软件可编程的。在智能卡模式里,CK可以为智能卡提供时钟。
在IrDA模式里需要下列引脚:
- IrDA_RDI: IrDA模式下的数据输入。
- IrDA_TDO: IrDA模式下的数据输出。
在硬件流控模式中需要下列引脚:
- nCTS: 清除发送,若是高电平,在当前数据传输结束时阻断下一次的数据发送。
- nRTS: 发送请求,若是低电平,表明USART准备好接收数据
寄存器
- 一个状态寄存器(USART_SR)
- 一个数据寄存器(USART_DR)
- 三个控制寄存器(USART_CR1,USART_CR2,USART_CR3)
- 一个波特率寄存器(USART_BRR)
- 一个智能卡模式下的保护时间寄存器(USART_GTPR)
关于以上寄存器中每个位的具体定义,请参考USART寄存器描述。
控制逻辑
- 发送控制
- 接收控制
- 时钟控制
- 硬件流控制
- 波特率控制(发送器和接收器)
- 中断控制
- 唤醒单元
USART寄存器描述
下表列出了USART寄存器的地址映射和复位值。可以以半字(16位)或字(32位)的方式访问这些外设寄存器。
USART_SR(Status register)状态寄存器
rc_w0:软件可以读此位,也可以通过写’0’清除此位,写’1’对此位无影响。
位31:10 保留位,由硬件强制为0
位9 CTS:CTS 标志
- 如果设置了CTSE位,当nCTS输入状态变化时,该位由硬件置’1’。由软件清’0’(向该位写’0’)。如果USART_CR3寄存器中的CTSIE = 1,则产生中断。
- 0:nCTS状态线上没有变化;
- 1:nCTS状态线上发生变化。
- 在UART4和UART5上,该位不可用。
位8 LBD:LIN断开检测标志
- 当检测到LIN断开时,该位由硬件置’1’。由软件清’0’(向该位写’0’)。如果USART_CR2寄存器中的LBDIE = 1,则产生中断。
- 0:没有检测到LIN断开;
- 1:检测到LIN断开。
- 注意:若LBDIE=1,当LBD = 1时产生中断。
位7 TXE:发送数据寄存器(TDR)空
- 当TDR寄存器中的数据被转移到移位寄存器时,该位由硬件置’1’。如果USART_CR1寄存器中的TXEIE = 1,则产生中断。写USART_DR寄存器,将该位清’0’。
- 0:数据还没有被转移到移位寄存器;
- 1:数据已经被转移到移位寄存器。
- 注意:该位被用在单缓冲发送中。
位6 TC:发送完成
- 当包含有数据的一帧发送完成后,并且TXE=1时,该位由硬件置’1’。如果USART_CR1寄存器中的TCIE = 1,则产生中断。该位由软件序列清’0’ (先读一次USART_SR寄存器,后写一次USART_DR寄存器)。TC位也可以通过向该位写’0’来清除,此清零方式只在多缓冲通信中推荐使用。
- 0:发送还未完成;
- 1:发送完成。
位5 RXNE:读数据寄存器(RDR)非空 (Read data register not empty)
- 当RDR移位寄存器中的数据被转移到USART_DR寄存器时,该位由硬件置位。如果USART_CR1寄存器中的RXNEIE = 1,则产生中断。读USART_DR寄存器,将该位清零。RXNE位也可以通过向该位写’0’来清除,此清零方式只在多缓冲通信中推荐使用。
- 0:没收到数据;
- 1:收到数据,可以读出。
位4 IDLE:监测到总线空闲 (IDLE line detected)
- 当检测到总线空闲时,该位被硬件置位。如果USART_CR1寄存器中的IDLEIE = 1,则产生中断。该位由软件序列清除 (先读USART_SR,后读USART_DR)。
- 0:没有检测到空闲总线;
- 1:检测到空闲总线。
- 注意:IDLE位不会再次被置位,直到RXNE位被置位 (即又检测到一次空闲总线)
位3 ORE:溢出错误 (Overrun error)
- RXNE = 1的同时,移位寄存器中接收到的当前数据,准备转移至RDR寄存器时,该位被硬件置位。如果USART_CR1中的RXNEIE = 1,则产生中断。该位由软件序列清零(先读USART_SR,后读USART_CR)。
- 0:没有溢出错误;
- 1:检测到溢出错误。
- 注意:该位被置位时,RDR寄存器中的数据不会丢失,但是移位寄存器中的数据会被覆盖。在多缓冲通信模式下,如果设置了EIE位,则ORE置位会产生中断。
位2 NE:噪声错误标志 (Noise error)
- 在接收到的帧中检测到噪声时,该位由硬件置位。该位由软件序列清除 (先读USART_SR,再读USART_DR)。
- 0:没有检测到噪声;
- 1:检测到噪声。
- 注意:该位不会产生中断,因为它和RXNE一起出现,硬件会在设置RXNE标志时产生中断。在多缓冲通信模式下,如果设置了EIE位,则NE置位会产生中断。
位1 FE:帧错误 (Framing error)
- 当检测到同步错位,过多的噪声或者断开符,该位被硬件置位。由软件序列将其清零(先读USART_SR,再读USART_DR)。
- 0:没有检测到帧错误;
- 1:检测到帧错误或者break符。
- 注意:该位不会产生中断,因为它和RXNE一起出现,硬件会在设置RXNE标志时产生中断。如果当前传输的数据既产生了帧错误,又产生了溢出错误,硬件还是会继续该数据的传输,并且只设置ORE位。在多缓冲通信模式下,如果设置了EIE位,则FE置位会产生中断。
位0 PE:校验错误 (Parity error)
- 在接收模式下,如果出现奇偶校验错误,该位被硬件置位。由软件序列对其清零(先读USART_SR,再读USART_DR)。在清除PE位前,软件必须等待RXNE标志位被置’1’。如果USART_CR1中的PEIE = 1,则产生中断。
- 0:没有奇偶校验错误;
- 1:奇偶校验错误。
USART_DR (Data Register)数据寄存器
位31:9 保留位,由硬件强制为0
位8:0 DR[8:0]:数据值 (Data value)
- 包含了发送或接收的数据。由于它是由两个寄存器组成的,一个给发送用(TDR),一个给接收用(RDR),该寄存器兼具读和写的功能。TDR寄存器提供了内部总线和输出移位寄存器之间的并行接口。RDR寄存器提供了输入移位寄存器和内部总线之间的并行接口。(参见USART内部结构框图)
- 如果USART_CR1中的校验控制使能位PCE=1,在发送数据时,写到MSB的值(根据数据的长度不同,MSB是第7位或者第8位)会被后来的校验位取代。接收数据时,读到的MSB位是接收到的校验位。
USART_BRR(Baud Rate Register)波特率寄存器
位31:16 保留位,硬件强制为0
位15:4 DIV_Mantissa[11:0]:USARTDIV的整数部分
- 这12位定义了USART分频器除法因子(USARTDIV)的整数部分。
位3:0 DIV_Fraction[3:0]:USARTDIV的小数部分
- 这4位定义了USART分频器除法因子(USARTDIV)的小数部分。
USART_CR1(Control Register 1)控制寄存器1
位31:14 保留位,硬件强制为0
位13 UE:USART使能 (USART enable)
- 当该位被清零,在当前字节传输完成后USART的分频器和输出停止工作,以减少功耗。该位由软件设置和清零。
- 0:USART分频器和输出被禁止;
- 1:USART模块使能。
位12 M:字长 (Word length)
- 该位定义了数据字的长度,由软件对其设置和清零
- 0:一个起始位,8个数据位,n个停止位;
- 1:一个起始位,9个数据位,n个停止位。
- 注意:在数据传输过程中(发送或者接收时),不能修改这个位。
位11 WAKE:唤醒的方法 (Wakeup method)
- 这位决定了把USART唤醒的方法,由软件对该位设置和清零。
- 0:被空闲总线唤醒;
- 1:被地址标记唤醒。
位10 PCE:检验控制使能 (Parity control enable)
- 用该位选择是否进行硬件校验控制(对于发送来说就是校验位的产生;对于接收来说就是校验位的检测)。当使能了该位,在发送数据的最高位(如果M=1,最高位就是第9位;如果M=0,最高位就是第8位)插入校验位;对接收到的数据检查其校验位。软件对它置’1’或清’0’。一旦设置了该位,当前字节传输完成后,校验控制才生效。
- 0:禁止校验控制;
- 1:使能校验控制。
位9 PS:校验选择 (Parity selection)
- 当校验控制使能后,该位用来选择是采用偶校验还是奇校验。软件对它置’1’或清’0’。当前字节传输完成后,该选择生效。
- 0:偶校验;
- 1:奇校验。
位8 PEIE:PE中断使能 (PE interrupt enable)
- 该位由软件设置或清除。
- 0:禁止产生中断;
- 1:当USART_SR中的PE为’1’时,产生USART中断。
位7 TXEIE:发送缓冲区空中断使能 (TXE interrupt enable)
- 该位由软件设置或清除。
- 0:禁止产生中断;
- 1:当USART_SR中的TXE为’1’时,产生USART中断。
位6 TCIE:发送完成中断使能 (Transmission complete interrupt enable)
- 该位由软件设置或清除。
- 0:禁止产生中断;
- 1:当USART_SR中的TC为’1’时,产生USART中断。
位5 RXNEIE:接收缓冲区非空中断使能 (RXNE interrupt enable)
- 该位由软件设置或清除。
- 0:禁止产生中断;
- 1:当USART_SR中的ORE或者RXNE为’1’时,产生USART中断。
位4 IDLEIE:IDLE中断使能 (IDLE interrupt enable)
- 该位由软件设置或清除。
- 0:禁止产生中断;
- 1:当USART_SR中的IDLE为’1’时,产生USART中断。
位3 TE:发送使能 (Transmitter enable)
- 该位使能发送器。该位由软件设置或清除。
- 0:禁止发送;
- 1:使能发送。
-
注意:
- 在数据传输过程中,除了在智能卡模式下,如果TE位上有个0脉冲(即设置为’0’之后再设置为’1’),会在当前数据字传输完成后,发送一个“前导符”(空闲总线)。
- 当TE被设置后,在真正发送开始之前,有一个比特时间的延迟。
位2 RE:接收使能 (Receiver enable)
- 该位由软件设置或清除。
- 0:禁止接收;
- 1:使能接收,并开始搜寻RX引脚上的起始位。
位1 RWU:接收唤醒 (Receiver wakeup)
- 该位用来决定是否把USART置于静默模式。该位由软件设置或清除。当唤醒序列到来时,硬件也会将其清零。
- 0:接收器处于正常工作模式;
- 1:接收器处于静默模式。
-
注意:
- 在把USART置于静默模式(设置RWU位)之前,USART要已经先接收了一个数据字节。否则在静默模式下,不能被空闲总线检测唤醒。
- 当配置成地址标记检测唤醒(WAKE位=1),在RXNE位被置位时,不能用软件修改RWU位。
位0 SBK:发送断开帧 (Send break)
- 使用该位来发送断开字符。该位可以由软件设置或清除。操作过程应该是软件设置位它,然后在断开帧的停止位时,由硬件将该位复位。
- 0:没有发送断开字符;
- 1:将要发送断开字符。
USART_CR2(Control Register 2)控制寄存器2
位31:15 保留位,硬件强制为0。
位14 LINEN:LIN模式使能 (LIN mode enable)
- 该位由软件设置或清除。
- 0:禁止LIN模式;
- 1:使能LIN模式。
- 在LIN模式下,可以用USART_CR1寄存器中的SBK位发送LIN同步断开符(低13位),以及检测LIN同步断开符。
位13:12 STOP:停止位 (STOP bits)
- 这2位用来设置停止位的位数
- 00:1个停止位;
- 01:0.5个停止位;
- 10:2个停止位;
- 11:1.5个停止位;
- 注:UART4和UART5不能用0.5停止位和1.5停止位。
位11 CLKEN:时钟使能 (Clock enable)
- 该位用来使能CK引脚
- 0:禁止CK引脚;
- 1:使能CK引脚。
- 注:UART4和UART5上不存在这一位。
位10 CPOL:时钟极性 (Clock polarity)
- 在同步模式下,可以用该位选择SLCK引脚上时钟输出的极性。和CPHA位一起配合来产生需要的时钟/数据的采样关系
- 0:总线空闲时CK引脚上保持低电平;
- 1:总线空闲时CK引脚上保持高电平。
- 注:UART4和UART5上不存在这一位。
位9 CPHA: 时钟相位 (Clock phase)
- 在同步模式下,可以用该位选择SLCK引脚上时钟输出的相位。和CPOL位一起配合来产生需要的时钟/数据的采样关系(参见图259 和图260)。 544/754
- 0:在时钟的第一个边沿进行数据捕获;
- 1:在时钟的第二个边沿进行数据捕获。
- 注:UART4和UART5上不存在这一位。
位8 LBCL:最后一位时钟脉冲 (Last bit clock pulse)
- 在同步模式下,使用该位来控制是否在CK引脚上输出最后发送的那个数据字节(MSB)对应的时钟脉冲
- 0:最后一位数据的时钟脉冲不从CK输出;
- 1:最后一位数据的时钟脉冲会从CK输出。
-
注意:
- 最后一个数据位就是第8或者第9个发送的位(根据USART_CR1寄存器中的M位所定义的8或者9位数据帧格式)。
- UART4和UART5上不存在这一位。
位7 保留位,硬件强制为0
位6 LBDIE:LIN断开符检测中断使能 (LIN break detection interrupt enable)
- 断开符中断屏蔽(使用断开分隔符来检测断开符)
- 0:禁止中断;
- 1:只要USART_SR寄存器中的LBD为’1’就产生中断。
位5 LBDL:LIN断开符检测长度 (LIN break detection length)
- 该位用来选择是11位还是10位的断开符检测
- 0:10位的断开符检测;
- 1:11位的断开符检测。
位4 保留位,硬件强制为0
位3:0 ADD[3:0]:本设备的USART节点地址
- 该位域给出本设备USART节点的地址。
- 这是在多处理器通信下的静默模式中使用的,使用地址标记来唤醒某个USART设备。
- 注意:在使能发送后不能改写这三个位(CPOL、CPHA、LBCL)。
USART_CR3(Control Register 3)控制寄存器3
位31:11 保留位,硬件强制为0
位10 CTSIE:CTS中断使能 (CTS interrupt enable)
- 0:禁止中断;
- 1:USART_SR寄存器中的CTS为’1’时产生中断。
- 注:UART4和UART5上不存在这一位。
位9 CTSE:CTS使能 (CTS enable)
- 0:禁止CTS硬件流控制;
- 1:CTS模式使能,只有nCTS输入信号有效(拉成低电平)时才能发送数据。如果在数据传输的过程中,nCTS信号变成无效,那么发完这个数据后,传输就停止下来。如果当nCTS为无效时,往数据寄存器里写数据,则要等到nCTS有效时才会发送这个数据。
- 注:UART4和UART5上不存在这一位。
位8 RTSE:RTS使能 (RTS enable)
- 0:禁止RTS硬件流控制;
- 1:RTS中断使能,只有接收缓冲区内有空余的空间时才请求下一个数据。当前数据发送完成后,发送操作就需要暂停下来。如果可以接收数据了,将nRTS输出置为有效(拉至低电平)。
- 注:UART4和UART5上不存在这一位。
位7 DMAT:DMA使能发送 (DMA enable transmitter)
- 该位由软件设置或清除。
- 0:禁止发送时的DMA模式。
- 1:使能发送时的DMA模式;
- 注:UART4和UART5上不存在这一位。
位6 DMAR: DMA使能接收 (DMA enable receiver)
- 该位由软件设置或清除。
- 0:禁止接收时的DMA模式。
- 1:使能接收时的DMA模式;
- 注:UART4和UART5上不存在这一位。
位5 SCEN: 智能卡模式使能 (Smartcard mode enable)
- 该位用来使能智能卡模式
- 0:禁止智能卡模式;
- 1:使能智能卡模式。
- 注:UART4和UART5上不存在这一位。
位4 NACK:智能卡NACK使能 (Smartcard NACK enable)
- 0:校验错误出现时,不发送NACK;
- 1:校验错误出现时,发送NACK。
- 注:UART4和UART5上不存在这一位。
位3 HDSEL:半双工选择 (Half-duplex selection)
- 选择单线半双工模式
- 0:不选择半双工模式;
- 1:选择半双工模式。
位2 IRLP:红外低功耗 (IrDA low-power)
- 该位用来选择普通模式还是低功耗红外模式
- 0:通常模式;
- 1:低功耗模式。
位1 IREN:红外模式使能 (IrDA mode enable)
- 该位由软件设置或清除。
- 0:不使能红外模式;
- 1:使能红外模式。
位0 EIE:错误中断使能 (Error interrupt enable)
- 在多缓冲通信模式下,如果该位为’1’,当有帧错误、过载错误或者噪声错误时(USART_SR中的FE=1,或者ORE=1,或者NE=1),则产生中断。
- 0:禁止中断;
- 1:只要USART_CR3中的DMAR=1,并且USART_SR中的FE=1,或者ORE=1,或者NE=1,则产生中断
USART_GTPR(Guard Time and Prescaler Register)保护时间和预分频寄存器
位31:16 保留位,由硬件强制为0
位15:8 GT[7:0]:保护时间值 (Guard time value)
- 该位域规定了以波特时钟为单位的保护时间。
- 在智能卡模式下,需要这个功能。当保护时间过去后,才会设置发送完成标志(TC)。
- 注:UART4和UART5上不存在这一位。
位7:0 PSC[7:0]:预分频器值 (Prescaler value)
-
在红外(IrDA)低功耗模式下:
- PSC[7:0] = 红外低功耗波特率
- 对系统时钟分频以获得低功耗模式下的频率:
- 源时钟被寄存器中的值(仅有8位有效)分频
- 00000000:保留 – 不要写入该值;
- 00000001:对源时钟1分频;
- 00000010:对源时钟2分频;
- ……
- 在红外(IrDA)的正常模式下:PSC只能设置为00000001
-
在智能卡模式下:
- PSC[4:0]:预分频值
- 对系统时钟进行分频,给智能卡提供时钟。
- 寄存器中给出的值(低5位有效)乘以2后,作为对源时钟的分频因子
- 00000:保留 – 不要写入该值;
- 00001:对源时钟进行2分频;
- 00010:对源时钟进行4分频;
- 00011:对源时钟进行6分频;
- ……
-
- 位[7:5]在智能卡模式下没有意义。
- 注:UART4和UART5上不存在这一位。
USART帧格式
数据帧
由一个低电平的起始位,8位或9位数据位,和0.5/1/1.5/2个高电平的停止位组成。
其中,数据位长度(字长)取决于USART_CR1寄存器中的M位;发送和接收时,最低有效位在前;当USART_CR1寄存器中的校验控制使能位PCE=1时,最高有效位被校验位代替;在同步模式下,CK引脚是否为最高有效位提供时钟脉冲取决于USART_CR2中的LBCL位 ;停止位数取决于USART_CR2寄存器中的STOP[1:0]。
空闲帧
在一个帧周期内,全部为高电平。
断开帧
在一个帧周期内,全部为低电平。在断开帧结束后,再发送1或2个高电平的停止位,以识别下一个低电平的起始位。
小数波特率
发送和接收由一共用的波特率发生器驱动,当发送器和接收器的使能位分别(TE和RE)置位时,分别为其产生时钟。接收器和发送器的波特率应设置成相同。
- Tx / Rx 波特率 = PCLKx / (16 * USARTDIV)
PCLKx可以是APB1的时钟PCLK1(用于USART1),也可以是APB2的时钟PCLK2(用于USART2、3、4、5) 。
- USARTDIV = DIV_Maintissa[11:0] + DIV_Fraction[3:0] / 16
USARTDIV是一个无符号的定点数,转换成对应的二进制小数后,整数部分存放在USART_BRR寄存器中的DIV_Maintissa[11:0],小数部分存放在USART_BRR寄存器中的DIV_Fraction[3:0]。
注:在写入USART_BRR之后,波特率计数器会被波特率寄存器的新值替换。因此,不要在通信进行中改变波特率寄存器的数值。
配置流程
- 波特率:USART_BRR寄存器
- 字长:USART_CR1中的M位
- 停止位数:USART_CR2中的STOP[1:0]
- 校验控制:USART_CR1中的PCE位和PS位
- 收发模式:USART_CR1中的TE和RE
- 硬件流控制:USART_CR3中的CTSE位和RTSE位
- 使能USART:USART_CR1寄存器中的UE=1
如果采用多缓冲器通信,需配置USART_CR3中的DMA使能位(DMAT)。按多缓冲器通信中的描述配置DMA寄存器。
如果需要产生中断,则还要设置相应中断的使能位。
完成上述配置后,就可以进行数据的发送和接收了。
发送过程
如果在收发模式配置中,发送器被使能了,即TE=1,则可以进行数据的发送。
空闲帧
置位TE将使得USART在第一个数据帧前发送一空闲帧。
数据帧
- 开始时,发送数据寄存器TDR为空,TXE=1,如果TXEIE=1,则产生中断。
- 向USART_DR中写入数据后,TXE=0。
- 由于发送移位缓冲器也为空,硬件就会将TDR中的数据发送到发送移位寄存器中。
- TDR再次为空,TXE=1,如果TXEIE=1,则产生中断。
- 再次向USART_DR中写入数据。不断地重复上述步骤,直到发送完所有数据。
- 在USART_DR寄存器中写入最后一个数据字后,要等待TC=1,如果TCIE=1,则产生中断,它表示最后一个数据帧的传输结束。当需要关闭USART或需要进入停机模式之前,需要确认传输结束,避免破坏最后一次传输。
- 发送到发送移位寄存器中的数据在时钟脉冲的激励下按照数据帧的格式从TX脚上串行输出。
断开帧
如果设置SBK=1,在完成当前数据发送后,将在TX线上发送一个断开帧。断开帧发送完成时(在断开帧的停止位时)SBK被硬件复位。USART在最后一个断开帧的结束处插入一逻辑‘1’,以保证能识别下一帧的起始位。
注意:如果在开始发送断开帧之前,软件又复位了SBK位,断开符号将不被发送。如果要发送两个连续的断开帧,SBK位应该在前一个断开符号的停止位之后置起。
在数据传输期间不能复位TE位,否则将破坏TX脚上的数据,因为波特率计数器停止计数。正在传输的当前数据将丢失。
接收过程
如果在收发模式配置中,接收被使能了,即RE=1,则可以进行数据的接收。
起始位侦测
在USART中,如果辨认出一个特殊的采样序列,那么就认为侦测到一个起始位。
该序列为:1 1 1 0 X 0 X 0 X 0 0 0 0
注意:
- 如果该序列不完整,那么接收端将退出起始位侦测并回到空闲状态(不设置标志位)等待下降沿。
- 如果3个采样点都为‘0’(在第3、5、7位的第一次采样,和在第8、9、10的第二次采样都为’0’),则确认收到起始位,这时设置RXNE标志位,如果RXNEIE=1,则产生中断。
- 如果两次3个采样点上仅有2个是‘0’(第3、5、7位的采样点和第8、9、10位的采样点),那么起始位仍然是有效的,但是会设置NE噪声标志位。如果不能满足这个条件,则中止起始位的侦测过程,接收器会回到空闲状态(不设置标志位)。
- 如果有一次3个采样点上仅有2个是’0’(第3、5、7位的采样点或第8、9、10位的采样点),那么起始位仍然是有效的,但是会设置NE噪声标志位。
空闲帧
由于发送器在发送第一个数据帧前会发送一个空闲帧,因此接收器会接收到一个空闲帧。当一空闲帧被检测到时,其处理步骤和接收到普通数据帧一样,但如果IDLEIE位被设置将产生一个中断。
数据帧
- 开始时,接收数据寄存器RDR为空,RXNE=0。
- 接收器不断地对RX脚上输入的信号进行侦测,一旦侦测到起始位,就将输入的数据移进接收移位寄存器中。
- 当接收移位寄存器接收到数据后,硬件就会将接收移位寄存器中的数据转移到RDR中。
- RDR非空,RXNE=1,如果RXNEIE=1,则产生中断。
在接收期间如果检测到帧错误,噪声或溢出错误,错误标志将被置起,
在多缓冲器通信时,RXNE在每个字节接收后被置起,并由DMA对数据寄存器的读操作而清零。
在单缓冲器模式里,由软件读USART_DR寄存器完成对RXNE位清除。RXNE标志也可以通过对它写0来清除。RXNE位必须在下一字符接收结束前被清零,以避免溢出错误。
注意:在接收数据时,RE位不应该被复位。如果RE位在接收时被清零,当前接收的 数据会丢失。
断开帧
当接收到一个断开帧时,USART像处理帧错误一样处理它。
溢出错误
如果RXNE还没有被复位,又接收到一个字符,则发生溢出错误。数据只有当RXNE位被清零后才能从移位寄存器转移到RDR寄存器。
RXNE标记是接收到每个字节后被置位的。如果下一个数据已被收到或先前DMA请求还没被服务时,RXNE标志仍是置起的,溢出错误产生。当溢出错误产生时:
- ORE位被置位。
- RDR内容将不会丢失。读USART_DR寄存器仍能得到先前的数据。
- 移位寄存器中以前的内容将被覆盖。随后接收到的数据都将丢失。
- 如果RXNEIE位被设置或EIE和DMAR位都被设置,中断产生。
- 顺序执行对USART_SR和USART_DR寄存器的读操作,可复位ORE位
注意: 当ORE位置位时,表明至少有1个数据已经丢失。有两种可能性:
- 如果RXNE=1,上一个有效数据还在接收寄存器RDR上,可以被读出。
- 如果RXNE=0,这意味着上一个有效数据已经被读走,RDR已经没有东西可读。当上一个有效数据在RDR中被读取的同时又接收到新的(也就是丢失的)数据时,此种情况可能发生。在读序列期间(在USART_SR寄存器读访问和USART_DR读访问之间)接收到新的数据,此种情况也可能发生。
噪音错误
使用过采样技术(同步模式除外),通过区别有效输入数据和噪声来进行数据恢复。
如果三个采样数据为000,并且接收到的数据位为0,则认为是有效数据。
如果三个采样数据为111,并且接收到的数据位为1,也认为是有效数据。
其他情况都认为是噪声。当在接收帧中检测到噪声时:
- 在RXNE位的上升沿设置NE标志。
- 无效数据从移位寄存器传送到USART_DR寄存器。
- 在单个字节通信情况下,没有中断产生。然而,因为NE标志位和RXNE标志位是同 时被设置,RXNE将产生中断。在多缓冲器通信情况下,如果已经设置了USART_CR3 寄存器中EIE位,将产生一个中断。
顺序执行对USART_SR和USART_DR寄存器的读操作,可复位NE位。
帧错误
当以下情况发生时检测到帧错误:
由于没有同步上或大量噪音的原因,停止位没有在预期的时间内识别出来。
当帧错误被检测到时:
- FE位被硬件置起
- 无效数据从移位寄存器传送到USART_DR寄存器。
- 在单字节通信时,没有中断产生。然而,这个位和RXNE位同时置起,后者将产生中断。在多缓冲器通信情况下,如果USART_CR3寄存器中EIE位被置位的话,将产生中断。
顺序执行对USART_SR和USART_DR寄存器的读操作,可复位FE位。
校验错误
如果USART_CR1中的校验控制使能位PCE=1,发送时,写进数据寄存器的数据的MSB位被校验位替换后发送出去(PS=0,选择偶校验偶数个’1’;PS=1,选择奇校验奇数个’1’)。接收时,如果奇偶校验失败,USART_SR寄存器中的PE标志被置’1’,如果USART_CR1寄存器的PEIE=1,则中断产生。
USART中断
USART的各种中断事件被连接到同一个中断向量(见下图),有以下各种中断事件:
- 发送期间:发送完成(TC)、清除发送(CTS)、发送数据寄存器空(TXE)。
- 接收期间:空闲总线检测(IDLE)、溢出错误(ORE)、接收数据寄存器非空(RXNE)、校验错误(PE)、LIN断开符号检测(LBD)、噪音错误(NE)(仅在多缓冲器通信)和帧错误(FE)(仅在多缓冲器通信)。
如果设置了对应的使能控制位,这些事件就可以产生各自的中断。
(图) USART中断映射图
USART初始化结构类型定义
\stm32f10x_usart.h文件中
typedef struct { uint32_t USART_BaudRate; //波特率 uint16_t USART_WordLength; //字长(数据位数) uint16_t USART_StopBits; //停止位 uint16_t USART_Parity; //校验方式 uint16_t USART_Mode; //收发模式 uint16_t USART_HardwareFlowControl; //硬件流控制 } USART_InitTypeDef; /* USART_BaudRate 用于配置USART_BRR */ /* USART_WordLength 用于配置M位(USART_CR1[12]) */ #define USART_WordLength_8b ((uint16_t)0x0000) #define USART_WordLength_9b ((uint16_t)0x1000) /* USART_StopBits 用于配置STOP[1:0](USART_CR2[13:12]) */ #define USART_StopBits_1 ((uint16_t)0x0000) #define USART_StopBits_0_5 ((uint16_t)0x1000) #define USART_StopBits_2 ((uint16_t)0x2000) #define USART_StopBits_1_5 ((uint16_t)0x3000) /* USART_Parity 用于配置PCE位(USART_CR1[10])和PS位(USART_CR1[9]) */ #define USART_Parity_No ((uint16_t)0x0000) #define USART_Parity_Even ((uint16_t)0x0400) #define USART_Parity_Odd ((uint16_t)0x0600) /* USART_Mode 用于配置RE位(USART_CR1[2])和TE位(USART_CR1[3]) */ #define USART_Mode_Rx ((uint16_t)0x0004) #define USART_Mode_Tx ((uint16_t)0x0008) /* USART_HardwareFlowControl 用于配置RTSE位(USART_CR3[8])和CTSE位(USART_CR3[9]) */ #define USART_HardwareFlowControl_None ((uint16_t)0x0000) #define USART_HardwareFlowControl_RTS ((uint16_t)0x0100) #define USART_HardwareFlowControl_CTS ((uint16_t)0x0200) #define USART_HardwareFlowControl_RTS_CTS ((uint16_t)0x0300)
USART库函数
此处介绍的库函数只是一些比较常用的,详见\stm32f10x_usart.c文件
函数USART_DeInit
/*----------------------------------------------------------------------------------------------------- // 功能描述 将外设USARTx寄存器重设为缺省值 // 输入参数 USARTx:选择USART或 UART 外设 // 可以是下列值之一: // USART1, USART2, USART3, UART4 或 UART5 // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void USART_DeInit(USART_TypeDef* USARTx) { /* 检查输入参数 */ assert_param(IS_USART_ALL_PERIPH(USARTx)); if (USARTx == USART1) { /* 强制或者释放APB2外设复位 */ RCC_APB2PeriphResetCmd(RCC_APB2Periph_USART1, ENABLE); RCC_APB2PeriphResetCmd(RCC_APB2Periph_USART1, DISABLE); } /* 强制或者释放APB1外设复位 */ else if (USARTx == USART2) { RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2, ENABLE); RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2, DISABLE); } else if (USARTx == USART3) { RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART3, ENABLE); RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART3, DISABLE); } else if (USARTx == UART4) { RCC_APB1PeriphResetCmd(RCC_APB1Periph_UART4, ENABLE); RCC_APB1PeriphResetCmd(RCC_APB1Periph_UART4, DISABLE); } else { if (USARTx == UART5) { RCC_APB1PeriphResetCmd(RCC_APB1Periph_UART5, ENABLE); RCC_APB1PeriphResetCmd(RCC_APB1Periph_UART5, DISABLE); } } }
函数USART_Init
/*----------------------------------------------------------------------------------------------------- // 功能描述 根据USART_InitStruct中指定的参数初始化外设USARTx寄存器 // 输入参数 USARTx:选择USART或 UART 外设 // 可以是下列值之一: // USART1, USART2, USART3, UART4 或 UART5 // 输入参数 USART_InitStruct:指向结构USART_InitTypeDef的指针,包含了外设USART的配置信息 // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct) { uint32_t tmpreg = 0x00, apbclock = 0x00; uint32_t integerdivider = 0x00; uint32_t fractionaldivider = 0x00; uint32_t usartxbase = 0; RCC_ClocksTypeDef RCC_ClocksStatus; /* 检查输入参数 */ assert_param(IS_USART_ALL_PERIPH(USARTx)); assert_param(IS_USART_BAUDRATE(USART_InitStruct->USART_BaudRate)); assert_param(IS_USART_WORD_LENGTH(USART_InitStruct->USART_WordLength)); assert_param(IS_USART_STOPBITS(USART_InitStruct->USART_StopBits)); assert_param(IS_USART_PARITY(USART_InitStruct->USART_Parity)); assert_param(IS_USART_MODE(USART_InitStruct->USART_Mode)); assert_param(IS_USART_HARDWARE_FLOW_CONTROL(USART_InitStruct->USART_HardwareFlowControl)); /* 硬件流控制只对USART1, USART2 和 USART3 有效 */ if (USART_InitStruct->USART_HardwareFlowControl != USART_HardwareFlowControl_None) { assert_param(IS_USART_123_PERIPH(USARTx)); } usartxbase = (uint32_t)USARTx; /*---------------------------- USART CR2 配置-----------------------*/ tmpreg = USARTx->CR2; /* 清除STOP[13:12] */ tmpreg &= CR2_STOP_CLEAR_Mask; /* 配置USART 停止位,时钟,时钟极性,时钟相位和最后一位------------*/ /* 根据USART_StopBits设置STOP[13:12] */ tmpreg |= (uint32_t)USART_InitStruct->USART_StopBits; /* 写USART CR2 */ USARTx->CR2 = (uint16_t)tmpreg; /*---------------------------- USART CR1 配置-----------------------*/ tmpreg = USARTx->CR1; /* 清除M, PCE, PS, TE 和 RE */ tmpreg &= CR1_CLEAR_Mask; /* 配置USART 字长,校验和模式 ----------------------- */ /* 根据USART_WordLength设置M */ /* 根据USART_Parity设置PCE和PS */ /* 根据USART_Mode设置TE和RE */ tmpreg |= (uint32_t)USART_InitStruct->USART_WordLength | USART_InitStruct->USART_Parity | USART_InitStruct->USART_Mode; /* 写USART CR1 */ USARTx->CR1 = (uint16_t)tmpreg; /*---------------------------- USART CR3 配置 -----------------------*/ tmpreg = USARTx->CR3; /* 清除CTSE和RTSE */ tmpreg &= CR3_CLEAR_Mask; /* 配置USART硬件流控制 -------------------------------------------------*/ /* 根据USART_HardwareFlowControl设置CTSE和RTSE */ tmpreg |= USART_InitStruct->USART_HardwareFlowControl; /* 写USART CR3 */ USARTx->CR3 = (uint16_t)tmpreg; /*---------------------------- USART BRR 配置 -----------------------*/ /* 配置USART波特率 -------------------------------------------*/ RCC_GetClocksFreq(&RCC_ClocksStatus); if (usartxbase == USART1_BASE) { apbclock = RCC_ClocksStatus.PCLK2_Frequency; } else { apbclock = RCC_ClocksStatus.PCLK1_Frequency; } /* 整数部分 */ if ((USARTx->CR1 & CR1_OVER8_Set) != 0) { /* 在过采样模式为8次采样下的整数部分的计算 */ integerdivider = ((25 * apbclock) / (2 * (USART_InitStruct->USART_BaudRate))); } else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */ { /*在过采样模式为16次采样下的整数部分的计算 */ integerdivider = ((25 * apbclock) / (4 * (USART_InitStruct->USART_BaudRate))); } tmpreg = (integerdivider / 100) << 4; /* 小数部分 */ fractionaldivider = integerdivider - (100 * (tmpreg >> 4)); /* Implement the fractional part in the register */ if ((USARTx->CR1 & CR1_OVER8_Set) != 0) { tmpreg |= ((((fractionaldivider * 8) + 50) / 100)) & ((uint8_t)0x07); } else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */ { tmpreg |= ((((fractionaldivider * 16) + 50) / 100)) & ((uint8_t)0x0F); } /* 写USART BRR */ USARTx->BRR = (uint16_t)tmpreg; }
函数USART_StructInit
/*----------------------------------------------------------------------------------------------------- // 功能描述 把USART_InitStruct中的每一个参数按缺省值填入 // 输入参数 USART_InitStruct:指向结构USART_InitTypeDef的指针,待初始化 // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void USART_StructInit(USART_InitTypeDef* USART_InitStruct) { /* USART_InitStruct 成员缺省值 */ USART_InitStruct->USART_BaudRate = 9600; //波特率9600 USART_InitStruct->USART_WordLength = USART_WordLength_8b; //字长8位 USART_InitStruct->USART_StopBits = USART_StopBits_1; //停止位1位 USART_InitStruct->USART_Parity = USART_Parity_No ; //不校验 USART_InitStruct->USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_InitStruct->USART_HardwareFlowControl = USART_HardwareFlowControl_None; //没有硬件流控制 }
函数USART_Cmd
/*----------------------------------------------------------------------------------------------------- // 功能描述 使能或者失能USART外设 // 输入参数 USARTx:选择USART或 UART 外设 // 可以是下列值之一: // USART1, USART2, USART3, UART4 或 UART5 // 输入参数 NewState:外设USARTx的新状态 // 可以是:ENABLE或DISABLE // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState) { /* 检查输入参数 */ assert_param(IS_USART_ALL_PERIPH(USARTx)); assert_param(IS_FUNCTIONAL_STATE(NewState)); if (NewState != DISABLE) { /* 通过设置CR1中的UE使能选择的USART */ USARTx->CR1 |= CR1_UE_Set; } else { /* 通过设置CR1中的UE失能选择的USART */ USARTx->CR1 &= CR1_UE_Reset; } }
函数USART_ITConfig
/*----------------------------------------------------------------------------------------------------- // 功能描述 使能或失能指定的USART中断 // 输入参数 USARTx:选择USART或 UART 外设 // 可以是下列值之一: // USART1,USART2,USART3,UART4 或 UART5 // 输入参数 USART_IT:待使能或者失能的USART中断源 // 可以是下列值之一: // USART_IT_CTS: CTS 变化中断(UART4 和 UART5不可用) // USART_IT_LBD: LIN 断开检测中断 // USART_IT_TXE: 发送数据寄存器空中断 // USART_IT_TC: 发送完成中断 // USART_IT_RXNE: 接收数据寄存器非空中断 // USART_IT_IDLE: 空闲帧检测中断 // USART_IT_PE: 校验错误中断 // USART_IT_ERR: 错误中断(帧错误,噪声错误,上溢错误) // 输入参数 NewState:外设USARTx的新状态 // 可以是:ENABLE或DISABLE // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState) { uint32_t usartreg = 0x00, itpos = 0x00, itmask = 0x00; uint32_t usartxbase = 0x00; /* 检查输入参数 */ assert_param(IS_USART_ALL_PERIPH(USARTx)); assert_param(IS_USART_CONFIG_IT(USART_IT)); assert_param(IS_FUNCTIONAL_STATE(NewState)); /* CTS中断对UART4和UART5是无效的 */ if (USART_IT == USART_IT_CTS) { assert_param(IS_USART_123_PERIPH(USARTx)); } usartxbase = (uint32_t)USARTx; /* 获得USART寄存器索引 */ usartreg = (((uint8_t)USART_IT) >> 0x05); /* 获得中断位置 */ itpos = USART_IT & IT_Mask; itmask = (((uint32_t)0x01) << itpos); if (usartreg == 0x01) /* The IT is in CR1 register */ { usartxbase += 0x0C; } else if (usartreg == 0x02) /* The IT is in CR2 register */ { usartxbase += 0x10; } else /* 在CR3中的中断 */ { usartxbase += 0x14; } if (NewState != DISABLE) { *(__IO uint32_t*)usartxbase |= itmask; } else { *(__IO uint32_t*)usartxbase &= ~itmask; } }
函数USART_SendData
/*----------------------------------------------------------------------------------------------------- // 功能描述 通过外设USARTx发送单个数据 // 输入参数 USARTx:选择USART或 UART 外设 // 可以是下列值之一: // USART1, USART2, USART3, UART4 或 UART5 // 输入参数 Data:待发送的数据 // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void USART_SendData(USART_TypeDef* USARTx, uint16_t Data) { /* 检查输入参数 */ assert_param(IS_USART_ALL_PERIPH(USARTx)); assert_param(IS_USART_DATA(Data)); /* 发送数据 */ USARTx->DR = (Data & (uint16_t)0x01FF); }
函数USART_ReceiveData
/*----------------------------------------------------------------------------------------------------- // 功能描述 返回USARTx最近接收到的数据 // 输入参数 USARTx:选择USART或 UART 外设 // 可以是下列值之一: // USART1, USART2, USART3, UART4 或 UART5 // 返回值 接收到的数据 -----------------------------------------------------------------------------------------------------*/ uint16_t USART_ReceiveData(USART_TypeDef* USARTx) { /* 检查输入参数 */ assert_param(IS_USART_ALL_PERIPH(USARTx)); /* 接收数据 */ return (uint16_t)(USARTx->DR & (uint16_t)0x01FF); }
函数USART_GetFlagStatus
/*----------------------------------------------------------------------------------------------------- // 功能描述 检查指定的USART标志位设置与否 // 输入参数 USARTx:选择USART或 UART 外设 // 可以是下列值之一: // USART1,USART2,USART3,UART4 或 UART5 // 输入参数 USART_FLAG:待检查的USART标志位 // 可以是下列值之一: // USART_ FLAG _CTS: CTS 变化标志(UART4 和 UART5不可用) // USART_ FLAG _LBD: LIN 断开检测标志 // USART_ FLAG _TXE: 发送数据寄存器空标志 // USART_ FLAG _TC: 发送完成标志 // USART_ FLAG _RXNE: 接收数据寄存器非空标志 // USART_ FLAG _IDLE: 空闲帧检测标志 // USART_ FLAG _ORE: 上溢错误标志 // USART_ FLAG _NE: 噪声错误标志 // USART_ FLAG _FE: 帧错误标志 // USART_ FLAG _PE: 校验错误标志 // 返回值 USART_FLAG的新状态(SET或者RESET) -----------------------------------------------------------------------------------------------------*/ FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG) { FlagStatus bitstatus = RESET; /* 检查输入参数 */ assert_param(IS_USART_ALL_PERIPH(USARTx)); assert_param(IS_USART_FLAG(USART_FLAG)); /* CTS标志对UART4和UART5是无效的 */ if (USART_FLAG == USART_FLAG_CTS) { assert_param(IS_USART_123_PERIPH(USARTx)); } if ((USARTx->SR & USART_FLAG) != (uint16_t)RESET) { bitstatus = SET; } else { bitstatus = RESET; } return bitstatus; }
函数USART_ClearFlag
/*----------------------------------------------------------------------------------------------------- // 功能描述 清除USARTx的挂起标志 // 输入参数 USARTx:选择USART或 UART 外设 // 可以是下列值之一: // USART1,USART2,USART3,UART4 或 UART5 // 输入参数 USART_FLAG:待清除的USART标志 // 可以是下列值的任意组合: // USART_ FLAG _CTS: CTS 变化标志(UART4 和 UART5不可用) // USART_ FLAG _LBD: LIN 断开检测标志 // USART_ FLAG _TC: 发送完成标志 // USART_ FLAG _RXNE: 接收数据寄存器非空标志 // // 注意 // - PE(校验错误),FE(帧错误),NE(噪声错误),ORE(上溢错误) // 和IDLE(空闲帧检测)标志通过软件时序被清除: // 一个对USART_SR 寄存器的读操作(USART_GetFlagStatus()) // 后跟一个对USART_DR 寄存器的读操作 (USART_ReceiveData())。 // - RXNE 标志也可以通过读USART_DR 寄存器被清除(USART_ReceiveData())。 // - TC 标志也可以通过软件时序被清除: // 一个对USART_SR 寄存器的读操作(USART_GetFlagStatus()) // 后跟一个对USART_DR 寄存器的写操作(USART_SendData())。 // - TXE标志只能通过写USART_DR 寄存器被清除(USART_SendData())。 // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG) { /* 检查输入参数 */ assert_param(IS_USART_ALL_PERIPH(USARTx)); assert_param(IS_USART_CLEAR_FLAG(USART_FLAG)); /* CTS标志对UART4和UART5是无效的 */ if ((USART_FLAG & USART_FLAG_CTS) == USART_FLAG_CTS) { assert_param(IS_USART_123_PERIPH(USARTx)); } USARTx->SR = (uint16_t)~USART_FLAG; }
函数USART_GetITStatus
/*----------------------------------------------------------------------------------------------------- // 功能描述 检查指定的USART中断发生与否 // 输入参数 USARTx:选择USART或 UART 外设 // 可以是下列值之一: // USART1,USART2,USART3,UART4 或 UART5 // 输入参数 USART_IT:待检查的USART中断源 // 可以是下列值之一: // USART_ IT _CTS: CTS 变化中断(UART4 和 UART5不可用) // USART_ IT _LBD: LIN 断开检测中断 // USART_ IT _TXE: 发送数据寄存器空中断 // USART_ IT _TC: 发送完成中断 // USART_ IT _RXNE: 接收数据寄存器非空中断 // USART_ IT _IDLE: 空闲帧检测中断 // USART_ IT _ORE: 上溢错误中断 // USART_ IT _NE: 噪声错误中断 // USART_ IT _FE: 帧错误中断 // USART_ IT _PE: 校验错误中断 // 返回值 USART_FLAG的新状态(SET或者RESET) -----------------------------------------------------------------------------------------------------*/ ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT) { uint32_t bitpos = 0x00, itmask = 0x00, usartreg = 0x00; ITStatus bitstatus = RESET; /* 检查输入参数 */ assert_param(IS_USART_ALL_PERIPH(USARTx)); assert_param(IS_USART_GET_IT(USART_IT)); /* CYS中断对UART4和UART5是无效的 */ if (USART_IT == USART_IT_CTS) { assert_param(IS_USART_123_PERIPH(USARTx)); } /* 获得USART寄存器索引 */ usartreg = (((uint8_t)USART_IT) >> 0x05); /* 获得中断位置 */ itmask = USART_IT & IT_Mask; itmask = (uint32_t)0x01 << itmask; if (usartreg == 0x01) /* The IT is in CR1 register */ { itmask &= USARTx->CR1; } else if (usartreg == 0x02) /* The IT is in CR2 register */ { itmask &= USARTx->CR2; } else /* 在CR3中的中断 */ { itmask &= USARTx->CR3; } bitpos = USART_IT >> 0x08; bitpos = (uint32_t)0x01 << bitpos; bitpos &= USARTx->SR; if ((itmask != (uint16_t)RESET)&&(bitpos != (uint16_t)RESET)) { bitstatus = SET; } else { bitstatus = RESET; } return bitstatus; }
函数USART_ClearITPendingBit
/*----------------------------------------------------------------------------------------------------- // 功能描述 清除USARTx的中断挂起位 // 输入参数 USARTx:选择USART或 UART 外设 // 可以是下列值之一: // USART1,USART2,USART3,UART4 或 UART5 // 输入参数 USART_IT:待清除的USART中断挂起位 // 可以是下列值之一: // USART_ IT _CTS: CTS 变化中断(UART4 和 UART5不可用) // USART_ IT _LBD: LIN 断开检测中断 // USART_ IT _TC: 发送完成中断 // USART_ IT _RXNE: 接收数据寄存器非空中断 // // 注意 // - PE(校验错误),FE(帧错误),NE(噪声错误),ORE(上溢错误) // 和IDLE(空闲帧检测)挂起位通过软件时序被清除: // 一个对USART_SR 寄存器的读操作(USART_GetFlagStatus()) // 后跟一个对USART_DR 寄存器的读操作 (USART_ReceiveData())。 // - RXNE 挂起位也可以通过读USART_DR 寄存器被清除(USART_ReceiveData())。 // - TC 挂起位也可以通过软件时序被清除: // 一个对USART_SR 寄存器的读操作(USART_GetFlagStatus()) // 后跟一个对USART_DR 寄存器的写操作(USART_SendData())。 // - TXE标志只能通过写USART_DR 寄存器被清除(USART_SendData())。 // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT) { uint16_t bitpos = 0x00, itmask = 0x00; /* 检查输入参数 */ assert_param(IS_USART_ALL_PERIPH(USARTx)); assert_param(IS_USART_CLEAR_IT(USART_IT)); /* CTS中断对UART4和UART5是无效的 */ if (USART_IT == USART_IT_CTS) { assert_param(IS_USART_123_PERIPH(USARTx)); } bitpos = USART_IT >> 0x08; itmask = ((uint16_t)0x01 << (uint16_t)bitpos); USARTx->SR = (uint16_t)~itmask; }
USART实例
实例USART_HyperTerminal_Interrupt
main.c
main函数
int main(void) { /* NVIC 配置 */ NVIC_Configuration(); /* USARTx 配置 ------------------------------------------------------*/ /* USARTx 配置如下: - 波特率9600 - 字长8位 - 停止位2位 - 奇校验 - 不使用硬件流控制 (RTS 和 CTS 信号) - 收发使能 */ USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_2; USART_InitStructure.USART_Parity = USART_Parity_Odd; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; STM_EVAL_COMInit(COM1, &USART_InitStructure); /* 使能 EVAL_COM1 发送中断:当EVAL_COM1 发送数据寄存器空,产生该中断 */ USART_ITConfig(EVAL_COM1, USART_IT_TXE, ENABLE); /* 使能 EVAL_COM1 接收中断:当EVAL_COM1 接收数据寄存器非空,产生该中断 */ USART_ITConfig(EVAL_COM1, USART_IT_RXNE, ENABLE); while (1) { } }
NVIC配置函数NVIC_Configuration
/*----------------------------------------------------------------------------------------------------- // 功能描述 配置NVIC // 输入参数 无 // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* 使能 USARTx 中断 */ NVIC_InitStructure.NVIC_IRQChannel = USARTx_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
stm32100b_eval.h
#define COMn 2 // 描述 COM1口的定义,连接到 USART1 #define EVAL_COM1 USART1 #define EVAL_COM1_CLK RCC_APB2Periph_USART1 #define EVAL_COM1_TX_PIN GPIO_Pin_9 #define EVAL_COM1_TX_GPIO_PORT GPIOA #define EVAL_COM1_TX_GPIO_CLK RCC_APB2Periph_GPIOA #define EVAL_COM1_RX_PIN GPIO_Pin_10 #define EVAL_COM1_RX_GPIO_PORT GPIOA #define EVAL_COM1_RX_GPIO_CLK RCC_APB2Periph_GPIOA #define EVAL_COM1_IRQn USART1_IRQn // 描述 COM2口的定义,连接到 USART2 (USART2 引脚被映射到 GPIOD) #define EVAL_COM2 USART2 #define EVAL_COM2_CLK RCC_APB1Periph_USART2 #define EVAL_COM2_TX_PIN GPIO_Pin_5 #define EVAL_COM2_TX_GPIO_PORT GPIOD #define EVAL_COM2_TX_GPIO_CLK RCC_APB2Periph_GPIOD #define EVAL_COM2_RX_PIN GPIO_Pin_6 #define EVAL_COM2_RX_GPIO_PORT GPIOD #define EVAL_COM2_RX_GPIO_CLK RCC_APB2Periph_GPIOD #define EVAL_COM2_IRQn USART2_IRQn
stm32100b_eval.c
串口初始化函数STM_EVAL_COMInit
USART_TypeDef* COM_USART[COMn] = {EVAL_COM1, EVAL_COM2}; GPIO_TypeDef* COM_TX_PORT[COMn] = {EVAL_COM1_TX_GPIO_PORT, EVAL_COM2_TX_GPIO_PORT}; GPIO_TypeDef* COM_RX_PORT[COMn] = {EVAL_COM1_RX_GPIO_PORT, EVAL_COM2_RX_GPIO_PORT}; const uint32_t COM_USART_CLK[COMn] = {EVAL_COM1_CLK, EVAL_COM2_CLK}; const uint32_t COM_TX_PORT_CLK[COMn] = {EVAL_COM1_TX_GPIO_CLK, EVAL_COM2_TX_GPIO_CLK}; const uint32_t COM_RX_PORT_CLK[COMn] = {EVAL_COM1_RX_GPIO_CLK, EVAL_COM2_RX_GPIO_CLK}; const uint16_t COM_TX_PIN[COMn] = {EVAL_COM1_TX_PIN, EVAL_COM2_TX_PIN}; const uint16_t COM_RX_PIN[COMn] = {EVAL_COM1_RX_PIN, EVAL_COM2_RX_PIN}; /*----------------------------------------------------------------------------------------------------- // 功能描述 配置COM口 // 输入参数 COM:指定待配置的COM口 // 该参数可以是下列参数之一: // COM1 // COM2 // 输入参数 USART_InitStruct:指向结构USART_InitTypeDef的指针,包含USART外设的配置信息 // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void STM_EVAL_COMInit(COM_TypeDef COM, USART_InitTypeDef* USART_InitStruct) { GPIO_InitTypeDef GPIO_InitStructure; /* 使能 GPIO 时钟 */ RCC_APB2PeriphClockCmd(COM_TX_PORT_CLK[COM] | COM_RX_PORT_CLK[COM] | RCC_APB2Periph_AFIO, ENABLE); /* 使能 UART 时钟 */ if (COM == COM1) { RCC_APB2PeriphClockCmd(COM_USART_CLK[COM], ENABLE); } else { /* 使能 USART2 引脚软件重映射 */ GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE); RCC_APB1PeriphClockCmd(COM_USART_CLK[COM], ENABLE); } /* 配置 USART Tx 为复用推挽输出模式 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = COM_TX_PIN[COM]; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(COM_TX_PORT[COM], &GPIO_InitStructure); /* 配置 USART Rx 为浮空输入模式 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = COM_RX_PIN[COM]; GPIO_Init(COM_RX_PORT[COM], &GPIO_InitStructure); /* USART 配置 */ USART_Init(COM_USART[COM], USART_InitStruct); /* 使能 USART */ USART_Cmd(COM_USART[COM], ENABLE); }
stm32f10x_it.c
USART 中断服务程序USARTx_IRQHandler
#ifdef USE_STM3210C_EVAL #define USARTx_IRQHandler USART2_IRQHandler #else #define USARTx_IRQHandler USART1_IRQHandler #endif #define TxBufferSize (countof(TxBuffer) - 1) #define RxBufferSize 0x20 #define countof(a) (sizeof(a) / sizeof(*(a))) uint8_t TxBuffer[] = "\n\rUSART Hyperterminal Interrupts Example: USART-Hyperterminal\ communication using Interrupt\n\r"; uint8_t RxBuffer[RxBufferSize]; uint8_t NbrOfDataToTransfer = TxBufferSize; uint8_t NbrOfDataToRead = RxBufferSize; uint8_t TxCounter = 0; uint16_t RxCounter = 0; /*----------------------------------------------------------------------------------------------------- // 功能描述 该函数处理 USARTx 总的中断请求 // 输入参数 无 // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void USARTx_IRQHandler(void) { if(USART_GetITStatus(EVAL_COM1, USART_IT_RXNE) != RESET) { /* 读一个字节从数据寄存器 */ RxBuffer[RxCounter++] = (USART_ReceiveData(EVAL_COM1) & 0x7F); if(RxCounter == NbrOfDataToRead) { /* 关闭 EVAL_COM1 接收中断 */ USART_ITConfig(EVAL_COM1, USART_IT_RXNE, DISABLE); } } if(USART_GetITStatus(EVAL_COM1, USART_IT_TXE) != RESET) { /* 写一个字节到数据寄存器 */ USART_SendData(EVAL_COM1, TxBuffer[TxCounter++]); if(TxCounter == NbrOfDataToTransfer) { /* 关闭 EVAL_COM1 发送中断 */ USART_ITConfig(EVAL_COM1, USART_IT_TXE, DISABLE); } } }
系统结构设计
程序设计分析
数据结构和算法分析
-
USART1初始化
USART1的初始化大致分为下面4个步骤: 1、RCC配置
-
使用RCC_APB2PeriphClockCmd函数:
- 使能GPIO时钟
- 使能AFIO时钟
- 使能USART1时钟(USART1是APB2的外设)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
2、GPIO配置
-
使用GPIO_Init函数:
- 配置 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); ::配置 USART1 Rx 为浮空输入模式 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_Init(GPIOA, &GPIO_InitStructure);
3、NVIC配置
- 使用NVIC_Init函数使能 USART1 中断
NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);
4、USART1配置
-
使用USART_Init函数配置USART1如下:
- 波特率9600
- 字长8位
- 停止位1位
- 不使用校验
- 不使用硬件流控制 (RTS 和 CTS 信号)
- 收发使能
USART_InitTypeDef USART_InitStructure; 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_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure);
-
使用USART_IT_Config函数:
- 使能TXE中断
USART_ITConfig(USART1, USART_IT_TXE, ENABLE); ::使能RXNE中断 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
-
- 使用USART_Cmd函数使能USART1
USART_Cmd(USART1, ENABLE);
-
USART1中断服务程序
实验要求开发板先向PC发送数据,表示开发板已经做好了接收数据的准备。PC接收后,就可以向开发板发送数据了。开发板接收到PC发来的数据后,立即将数据再发回给PC,以实现回显功能。当PC向开发板发送的数据是回车(\r')时,PC端要实现回车的功能(回到下一行行首),开发板就要发回'\n'和'\r'。只有在开始时,开发板向PC发送数据时,使用了TXE中断,在回显时,并没有使用到TXE中断,因此,在开发板发送完提示信息后,必须将TXE中断关闭。使用LED3指示数据正在传输。
程序流程图
-
主程序
-
中断服务程序
代码实现及分析
main.c
main函数
int main(void) { /* 初始化TB开发板上的LED3 */ TB_LEDInit(LED3); /* 初始化TB开发板上的COM(USART1) */ TB_COMInit(); while (1) { } }
TB_COM.c
串口初始化函数TB_COMInit
/*----------------------------------------------------------------------------------------------------- // 功能描述 初始化TB开发板上的COM口(USART1) // 输入参数 无 // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void TB_COMInit(void) { GPIO_InitTypeDef GPIO_InitStructure; /* RCC 配置 -----------------------------------------------------------------*/ /* 使能 GPIOA 和 AFIO 时钟,USART1 Tx 和 PA9 复用,USART1 Rx 和 PA10 复用 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); /* 使能 USART1 时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); /* GPIO 配置 ----------------------------------------------------------------*/ /* 配置 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); /* 配置 USART1 Rx 为浮空输入模式 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_Init(GPIOA, &GPIO_InitStructure); /* NVIC 配置 ----------------------------------------------------------------*/ /* 使能 USART1 中断 */ NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* USART1 配置 ---------------------------------------------------------------*/ /* USART1 配置如下: - 波特率9600 - 字长8位 - 停止位1位 - 不使用校验 - 不使用硬件流控制 (RTS 和 CTS 信号) - 收发使能 */ USART_InitTypeDef USART_InitStructure; 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_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); /* 使能 USART1 发送中断:当 USART1 发送数据寄存器空,产生该中断 */ USART_ITConfig(USART1, USART_IT_TXE, ENABLE); /* 使能 USART1 接收中断:当 USART1 接收数据寄存器非空,产生该中断 */ USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); /* 使能 USART1 */ USART_Cmd(USART1, ENABLE); }
vectors.c
USART1中断服务程序
uint8_t TX[] = "Connection is successful! Please input:\n\r"; uint8_t Counter = 0; void Delay(__IO uint32_t nCount); /*----------------------------------------------------------------------------------------------------- // 功能描述 该函数处理 USART1 总的中断请求 // 输入参数 无 // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { uint16_t c; TB_LEDOn(LED3); c = USART_ReceiveData(USART1); if(c == '\r') { USART_SendData(USART1, '\n'); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } USART_SendData(USART1, c); Delay(0xffff); TB_LEDOff(LED3); } if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) { TB_LEDOn(LED3); USART_SendData(USART1, TX[Counter++]); if(Counter == sizeof(TX)) USART_ITConfig(USART1, USART_IT_TXE, DISABLE); Delay(0xffff); EM_TB_LEDOff(LED3); } } /*----------------------------------------------------------------------------------------------------- // 功能描述 插入一段延迟 // 输入参数 nCount:指定延迟时间长度 // 返回值 无 -----------------------------------------------------------------------------------------------------*/ void Delay(__IO uint32_t nCount) { for(; nCount != 0; nCount--); }