特性:
(1)USART只能一位一位地发送和接受数据,在起始位期间,TX端处于低电平;当闲置时,TX端为高。
(2)发送和接受由一共用的波特率发生器驱动,当发送器和接收器的使能位分别置位时,分别为其产生时钟。
(3)发送器根据M位的状态发送8位或9位的数据字。当发送使能位TE被置位时,发送移位寄存器中的数据从TX上输出,如果需要用到时钟,相应的时钟脉冲将在SCLK脚上输出。
(4)一帧数据结构:
■起始位
■8位或9位的数据字
■可选的奇偶校验位
■停止位
对于上面这张图,从下至上,我们看到串口外设主要由三个部分组成,分别是波特率控制、收发控制和数据存储转移。
波特率控制
和USART_BRR寄存器相关,上一篇文章《STM32下波特率计算详解》中已经详细地讲解过了,这里不再赘述。在实际的库函数操作,只需要将实际的波特率大小赋予一个结构体变量中的一个波特率成员即可。
收发控制
围绕着发送器和接收器控制部分,有好多个寄存器 : CR1 、 CR2、 CR3 和 SR,即USART 的三个控制寄存器( Control Register)及一个状态寄存器( Status Register)。通过向寄存器写入 各种控制参数来控制发送和接收,如奇偶校验位、停止位等,还包括对USART 中断的控制 ;串口的状态在任何时候都可以从状态寄存器中查询得到。
(1)数据发送过程
当使能了发送使能位TE后,数据要发送出去,首先要将数据写进USART_DR寄存器(和51单片机SBUF一样,USART_DR寄存器实际包含了两个寄存器,给发送用的TDR和给接收用的RDR,在USART_DR寄存器下还有一个发送/接收移位寄存器,数据的发送接收最后都要通过移位寄存器),如果此时USART正在发送数据,写入的数据先保存在TDR寄存器中,传输完成后再把该数据复制进移位寄存器。如果此时USART没有在发送数据,对USART_DR寄存器的写操作,数据直接存放进移位寄存器。无论哪种方式,数据存进移位寄存器后,便开始发送,TXE被置位,当一帧数据发送完(停止位发送后),TC位被置位,并且如果USART_CR1的TCIE位被置位,将产生中断。数据的发送是从低位发送起。
(2)数据的接收过程
当USART_CR1的RE位被使能后,且接收器的RX端由高电平被拉为低电平,那么接收开始。数据的最低有效位首先从RX脚移进移位寄存器。
■当RXNE(接收数据寄存器非空标志位)被置位,表明移位寄存器的内容被转移到RDR,此时数据已经被接收并且可以被读出,USART_DR和相关的错误标志位。
■如果RENEIE位被置位,将产生中断,如果在接收期间,检测到侦错误、噪音、溢出错误,错误标志位将被置位。
■在多缓冲器通信时, RXNE在每个字节接收后被置起,并由DMA对数据寄存器的读操作而清零。
■在单缓冲器模式里,由软件读USART_DR寄存器完成对RXNE位清除。 RXNE标志也可以通过对它写0来清除。 RXNE位必须在下一字符接收结束前被清零,以避免溢出错误。
注:
TEX和RXNE是针对USART_DR寄存器的,而USART_DR实质上由两个寄存器组成TDR和RDR,当发送时,数据从TDR传输到移位寄存器,TXE被置位,表示TDR已为空,当接收数据时,数据从移位寄存器转移到RDR时,RXNE被置位,表示RDR非空;E有Empty的意思。
(3)什么是溢出错误
我们知道。接收数据时,数据从移位寄存器转移到RDR,RXNE被置位,表示数据已被接收可以读出了。清除RXNE标志位的方法是读取USART_DR寄存器自动清零或对RXNE写0手动清零。当RXNE仍然置位,又接收到数据,新数据将暂存入移位寄存器,此时产生溢出错误,RXNE位仍为置位,数据只有在RXNE清零后,才能从移位寄存器转移到RDR。
● 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读访问之间)接收到新的数据,此种情况也可能发生。
(4)什么是侦错误
在预期的时间里,没有接收到停止位,可能的原因:
●大量噪音存在
●没有同步上
当帧错误被检测到时:
● FE位被硬件置起
● 无效数据从移位寄存器传送到USART_DR寄存器。
● 在单字节通信时,没有中断产生。然而,这个位和RXNE位同时置起,后者将产生中断。在多缓冲器通信情况下,如果USART_CR3寄存器中EIE位被置位的
话,将产生中断。顺序执行对USART_SR和USART_DR寄存器的读操作,可复位FE位。
(5)几个重要的中断标志(USART_SR)
●TXE:当TXE为1时,表示发送数据寄存器为空;当为0时,表示发送数据寄存器不为空。
●RXNE:当RXNE为1,表示接收数据寄存器为非空,可以读取接收到的数据了(存于USART_DR中);当为0时,表示接收寄存器为空,此时移位寄存器接收到新数据,会立即传输到数据寄存器中。
●TC:当发送完成,即数据已经从移位寄存器发送出去后,TC将置位。
●ORE:检测到数据溢出
●PE:检测到奇偶检验错误
○CTS:当nCTS输入变化状态时,该位被硬件置位,由软件清零
○LBD:LIN断开检测标志,当检测到LIN断开时,该位由硬件置位,由软件清0
○IDLE:当检测到空闲总线时,该位由硬件置位,清零时先读USART_SR,再读USART_DR
○NE:噪声错误标志,在接收到的帧检测到噪音时,由硬件对该位置位。由软件序列对其清玲(先读USART_SR,再读USART_DR)。
○FE:帧错误,当检测到同步错位,过多的噪声或者检测到break符,该位被硬件置位。由软件序列将其清零(先读USART_SR,再读USART_DR)。
0:没有检测到帧错误;
1 :检测到帧错误或者break符。
注意:该位不会产生中断,因为它和RXNE一起出现,后者自己会在RXNE标志置位时产生中断。如果当前传输的数据既产生了帧错误,又产生了过载错误,还是会继续该数据的传输,并且只有ORE位会被置位。如果EIE位被置位,在多缓冲区通信模式下,随着FE标志被置位,中断产生。
这些中断都被连接到同一个中断向量,在中断函数中,要用USART_SR中相应标志位是否置位,来区分中断来源。
数据存储转移
收发控制器根据我们的寄存器配置,对数据存储转移部分的移位寄存器进行控制。当我们需要发送数据时,内核或 DMA 外设(一种数据传输方式,在后面介绍)把数据从内存(变量)写入到发送数据寄存器 TDR 后,发送控制器将适时地自动把数据从 TDR 加载到发送移位寄存器,然后通过串口线 Tx,把数据一位一位地发送出去,当数据从 TDR转移到移位寄存器时,会产生发送寄存器 TDR 已空事件 TXE,当数据从移位寄存器全部发送出去时,会产生数据发送完成事件 TC,这些事件以在状态寄存器中查询到。而接收数据则是一个逆过程,数据从串口线 Rx 一位一位地输入到接收移位寄存器,然后自动地转移到接收数据寄存器 RDR,最后用内核指令或 DMA 读取到内存(变量)中。
GPIO初始化
(1)USART引脚IO口位置及重映像问题
(2)USART引脚IO口的模式问题
所以如果使用的是USART 1,且没有重映射,那么GPIO口的配置是:
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA,ENABLE);
//初始化USART 1的TX和RX引脚
GPIO_InitTypeDef GPIO_Struct;
GPIO_Struct.GPIO_Speed = GPIO_Speed_50MHz;
//USART TX引脚使用复用推挽输出
GPIO_Struct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Struct.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOA,&GPIO_Struct);
//USART RX引脚使用浮空输入
GPIO_Struct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Struct.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA,&GPIO_Struct);
//GPIO、usart配置
void USART_GPIO_conf(void)
{
GPIO_InitTypeDef GPIO_Struct;
USART_InitTypeDef USART_Struct;
//设置之前一定要开启相应的外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1,ENABLE);
//初始化USART 1的TX和RX引脚
GPIO_Struct.GPIO_Speed = GPIO_Speed_50MHz;
//USART TX引脚使用复用推挽输出
GPIO_Struct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Struct.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOA,&GPIO_Struct);
//USART RX引脚使用浮空输入
GPIO_Struct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Struct.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA,&GPIO_Struct);
//设置USART的波特率为9600,接收发送使能,无奇偶校验,1位停止位,8位数据位,无硬件流控
USART_Struct.USART_BaudRate = 9600;
USART_Struct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Struct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Struct.USART_Parity = USART_Parity_No;
USART_Struct.USART_StopBits = USART_StopBits_1;
USART_Struct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART_Struct);
USART_Cmd(USART1, ENABLE);
}
接收和发送数据库函数
检测已收到数据代码
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET)
;
printf("已接受到\n");
USART_ClearFlag(USART1,USART_FLAG_RXNE);
/* 发送一个字节数据到USART1 */
USART_SendData(USART1, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
;