STM32串口通信:USART

一、通信接口背景知识

设备之间通信的方式

1、一般情况下,设备之间的通信方式可以分成并行通信和串行通信两种。它们的区别是:

串行通信:数据按位顺序传输。优点:占用引脚资源少,缺点:速度相对较慢。

并行通信:数据各个位同时传输。优点:速度快,缺点:占用引脚资源多。

串口通信一般是以帧格式传输数据,即一帧一帧传输,每帧包含有起始信号、数据信息、停止信息,可能还有校验信息。

2、串行通信的分类
(1)按照数据传送方向,分为:

单工:数据传输只支持数据在一个方向上传输;
半双工:允许数据在两个方向上传输。但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;它不需要独立的接收端和发送端,两者可以合并一起使用一个端口。
全双工:允许数据同时在两个方向上传输。因此,全双工通信是两个单工通信方式的结合,需要独立的接收端和发送端。

(2)按照通信方式,分为:

同步通信:带时钟同步信号传输。比如:SPI,IIC通信接口。
异步通信:不带时钟同步信号。比如:UART(通用异步收发器),单总线。
在同步通讯中,收发设备上方会使用一根信号线传输信号,在时钟信号的驱动下双方进行协调,同步数据。例如,通讯中通常双方会统一规定在时钟信号的上升沿或者下降沿对数据线进行采样。

在异步通讯中不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些用于同步的信号位,或者将主题数据进行打包,以数据帧的格式传输数据。通讯中还需要双方规约好数据的传输速率(也就是波特率)等,以便更好地同步。常用的波特率有4800bps、9600bps、115200bps等。

在同步通讯中,数据信号所传输的内容绝大部分是有效数据,而异步通讯中会则会包含数据帧的各种标识符,所以同步通讯效率高,但是同步通讯双方的时钟允许误差小,稍稍时钟出错就可能导致数据错乱,异步通讯双方的时钟允许误差较大。

常见的串口通信接口:

二、通用同步/异步收发器(USART)

1、通用同步异步收发器是一个串行通信设备,可以灵活的与外部设备进行全双工数据交换。有别与USART,还有一个UART,它在USART基础上裁剪掉了同步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是 UART。
USART 支持同步单向通信和半双工单线通信;还支持局域互连网络 LIN、智能卡(SmartCard)协议与 lrDA(红外线数据协会) SIR ENDEC规范。
USART支持使用 DMA,可实现高速数据通信。

2、STM32的UART特点
全双工异步通信;
分数波特率发生器系统,提供精确的波特率。发送和接受共用的可编程波特率,最高可达4.5Mbits/s;
可编程的数据字长(8位或者9位);
可配置的停止位(支持1或者2位停止位);
可配置的使用DMA多缓冲器通信;
单独的发送器和接收器使能位;
检测标志:① 接受缓冲器  ②发送缓冲器空 ③传输结束标志;
多个带标志的中断源,触发中断;
其他:校验控制,四个错误检测标志。

3、UART引脚连接方法

  • RXD:数据输入引脚,数据接受;
  • TXD:数据发送引脚,数据发送。

对于两个芯片之间的连接,两个芯片GND共地,同时TXD和RXD交叉连接。这里的交叉连接的意思就是,芯片1的RxD连接芯片2的TXD,芯片2的RXD连接芯片1的TXD。这样,两个芯片之间就可以进行TTL电平通信了

4、USART串口通信的详细过程

UART(USART)框图

这个框图分成上、中、下三个部分。

框图的上部分,数据从RX进入到接收移位寄存器,后进入到接收数据寄存器,最终供CPU或者DMA来进行读取;数据从CPU或者DMA传递过来,进入发送数据寄存器,后进入发送移位寄存器,最终通过TX发送出去。

然而,UART的发送和接收都需要波特率来进行控制的,波特率是怎样控制的呢?

这就到了框图的下部分,在接收移位寄存器、发送移位寄存器都还有一个进入的箭头,分别连接到接收器控制、发送器控制。而这两者连接的又是接收器时钟、发送器时钟。也就是说,异步通信尽管没有时钟同步信号,但是在串口内部,是提供了时钟信号来进行控制的。而接收器时钟和发送器时钟有是由什么控制的呢?

可以看到,接收器时钟和发送器时钟又被连接到同一个控制单元,也就是说它们共用一个波特率发生器。

说到时钟,则必须了解:UART1的时钟是挂载在APB2总线上的,所以时钟源为PCLK2(高速);而USART2~5挂载在APB1上,所以时钟源为PCLK1

总结,串口外设分为数据存储转移部分收发控制部分波特率控制部分

数据存储转移部分:(1)当需要发送数据时,内核或DMA外设把数据从内存写入发送数据寄存器TDR后,数据会自动加载到发送移位寄存器(此过程产生TDR已空时间TXE),最终通过串口线TX一位一位地发送出去(全部发送出去后产生数据发送完成事件TC)。产生的事件会影响状态寄存器的值。
                                (2) 而接受数据时一个逆过程,数据从串口线RX输入到接受数据移位寄存器,然后自动转移到接受数据寄存器RDR,最后用内核指令或DMA读取到内存中。

收发控制部分:通过改写USART的三个控制寄存器(CR1、CR2、CR3)和一个状态寄存器(SR)的控制参数来控制发送和接收,如奇偶校验位、停止位等,还包括对USART中断的控制。

波特率控制部分:通过对时钟的控制可以改变波特率,而波特率是每秒传输的二进制码元符号的个数,即代表了数据传输的速率。配置波特率实际上就是修改波特率寄存器USART_BRR中的参数USARTDIV(串口时钟的分频值)。

5、波特率的计算

说到波特率就必须要先了解波特率寄存器

通过修改USARTDIV的值来改变波特率的公式如下:

上面的公式中,f PCLKx是串口的时钟频率,我们只要得到USARTDIV的值,就可以计算出波特率。

而USARTDIV在波特率寄存器的定义中包括了两个部分:DIV_Mantissa【15:4】(整数部分)和DIV_Fraction【3:0】(小数部分)。

计算公式为:USARTDIV    =   DIV_Mantis   + (DIV_Fraction/16)

注意:在配置串口时使用库函数则不需要计算寄存器的值,设置波特率时直接写即可。

6、串口操作相关库函数

  • 1个初始化函数
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);

作用:用于串口波特率,数据字长,奇偶校验,硬件流控以及收发使能等配置的初始化。

  • 2个使能函数
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);

作用:前者使能串口,后者使能串口的相关中断。

  • 2个数据收发函数
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

作用:前者发送数据到串口,后者从串口接收数据。

  • 4个状态位函数
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);

作用:前两者获取(或清除)状态标志位,后两者为获取(或清除)中断状态标志位。

7、串口操作的一般步骤

(1)时钟使能

调用函数:RCC_APBxPeriphClockCmd(),

包括GPIO时钟使能串口时钟使能,如果引脚复用还要使能AFIO的时钟

待完善

(2)配置GPIO引脚

调用函数:GPIO_Init()

USART外设引脚复用

当使用USART的时候,GPIO有时需要引脚复用(重映射),那么该如何判断是否需要重映射呢?请参考引脚复用配置的相关内容(待补充)

下图介绍了USART的引脚设置:

在教材中,复用引脚初始化的参数是:TX引脚为复用推挽输出,切换速率为50MHZ;RX引脚为浮空输入,不设置切换速率:

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;    //TX引脚为PA9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(USART1,&GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;   //RX引脚为P10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //浮空输入
GPIO_Init(USART1,&GPIO_InitStructure);

(3)配置中断优先级并使能中断(如果需要开启中断才需要这个步骤)

调用函数:NVIC_Init();USART_ITConfig();

待完善

注意:如果相关设备需要使用到中断功能,则必须在设备使能之前先配置中断优先级,并使能设备的中断。

(4)串口参数初始化,使能串口设备

调用函数:USART_Cmd();

待完善

(5)编写中断处理函数

调用函数:USARTx_IRQHandler();

串口数据收发。调用函数:USART_SendData();USART_ReceiveData();
串口传输状态获取。调用函数:USART_GetFlagStatus();USART_ClearITPendingBit();

待完善

在编写中断服务函数时,常常会获取相关状态位的数据,判断是否产生中断。因此要对状态寄存器有一定的了解。

状态寄存器(USART_SR)


状态寄存器适用于检测串口此时所处的状态。它能够检测到的状态有:发送寄存器空位、发送完成位、读数据寄存器非空位、检测到主线空闲位、过载错误为等等。

这边主要关注两个位:RXNE和TC(第5、6两位)。

RXNE(读数据寄存器非空)当该位被置1的时候,就是提示已经有数据被接收到了,并且可以读出来了(即RDR移位寄存器中的数据被转移到USART_DR寄存器中)。这时候要做的就是尽快读取USART_DR,从而将该位清零,也可以向该位写0,直接清除。
TC(发送完成)当该位被置1的时候,表示USART_DR内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:读USART_SR,写USART_DR;直接向该位写0。

 

 


 

猜你喜欢

转载自blog.csdn.net/San_a_fish_of_dream/article/details/115033933