STM32串口USART通讯

1. USART和UART

USART(Universal Synchronous Asynchronous Receiver and Transmitter)即通用同步异步收发器,它是一个串行通信设备,与外部设备可灵活进行全双工数据交换。在这之前我们常用到的是UART(Universal Asynchronous Receiver and Transmitter),它是在USART基础上裁剪掉了同步通信功能,只有异步通信。区分同步和异步最简单的方法就是看通信时需不需要对外提供时钟输出,我们平时使用的串口通信基本都是UART。关于串口通信其他基本概念,在上一篇文章http://blog.csdn.net/qq_29344757/article/details/75246263已有详细解析,这里不再赘述。

2. STM32中USART功能框图

这里写图片描述

2.1 功能引脚

(1)TX:输出引脚,用于发送数据
(2)RX:输入引脚,用于接收数据
(3)SW_RX:输入引脚,用于单线和智能卡模式,属于内部引脚,没有具体外部引脚引出
(4)IRDA_OUT:输出引脚,用于发送红外数据
(5)IRDA_IN:输入引脚,用于接收红外数据
(6)nRTS:请求以发送(Request To Send),n表低电平有效。若使能RTS硬件流控制,当USART接收器准备好接收数据时,nRTS有效,即它为低电平;当接收寄存器已满时,nRTS被设置为高电平
(7)nCTS:请求以发送(Clean To Send),n表低电平有效。若使能CTS硬件流控制,发送器在发送下一帧数据前会检测nCTS引脚状态,若为低电平表可以发送数据,若为高电平则在发送完当前数据帧之后停止发送。
(8)SCLK:发送器时钟输出引脚,仅适用于同步模式。

2.2 数据寄存器

USART数据寄存器(USART_DR)只有低9位有效,
STM32串口USART通讯_第1张图片
第9位数据是否有效要取决于USART_CR1(USART控制寄存器1)的M位设置,
STM32串口USART通讯_第2张图片
当M位为0时表示8位数据字长,当M位为1时表9位数据字长,一般我们使用8位数据字长。
USART_DR实际是包含了两个寄存器,一个专门用于发送的TDR,一个专门用于接收的RDR。进行发送数据操作时,往USART_DR写入数据会自动存储在TDR内,当进行读取数据操作时,向USART_DR读取数据会自动提取RDR数据。串行通信时一位一位传输的,所以TDR和RDR寄存器都是介于系统总线和移位寄存器间的:发送数据时把TDR内容转移到发送移位寄存器上,接收数据时则是把接收到的每一位顺序保存在接收移位寄存器内进而转移到RDR。

2.3 控制器

STM32的USART有专门用于控制发送、接收、唤醒单元和中断等的寄存器。例如:
(1)USART_CR1寄存器的UE位用于开启/关闭给串口的时钟源的,使用USART之前向该位写1用于使能USART。
(2)USART_CR1寄存器的M位用于控制发送/接收数据字长可选8位/9位。
(3)USART_CR1寄存器的TE位用于启停数据发送,向该位写1时发送移位寄存器上的数据会从TX引脚输出,低位在前,高位在后。如果设置USART为同步模式,SCLK引脚将会输出时钟信号。
(4)USART_CR1寄存器的RE位用于开启/关闭USART的接收,若为1,接收器在RX线开始接收数据帧的起始位,确定到起始位后就根据RX线电平状态把数据存放在接收移位寄存器内,接收完成后就要接收移位寄存器移到RDR内,并将USART_SR寄存器的RXNE位置1(若USART_CR2寄存器的RXNEIE置1的话此时可以产生中断)

一般在我们编程中较为重要的USART_CR1寄存器标志位有:
在发送数据时,

TE:发送使能
TXE:发送寄存器为空
TC: 发送完成
TXIE:发送完成中断使能

接收数据时,

RE:接收使能
RXNE:读数据寄存器非空
RXNEIE:发送完成中断使能

2.4 波特率设置

USART的发送器和接收器使用相同的波特率,计算公式为:

Tx/Rx波特率 = USART的时钟频率 / (16 * USARTDIV)

USARTDIV是一个存放波特率寄存器(USART_BRR)的无符号浮点数
STM32串口USART通讯_第3张图片
DIV_Mantissa[11:0]表示USARTDIV的整数部分
DIV_Fraction[3:0]表示USARTDIV的小数部分。
例如:DIV_Mantissa = 26(0x1a),DIV_Fraction = 12(0x0c),那么USART_CRR的值为0x26c。USARTDIV的小数位12 / 16 = 0.75,整数位26,最终USARTDIV的值为26.75。
假设知道USARTDIV的值为22.63,那么,DIV_Fraction = 16 * 0.63 = 10.08,最接近的整数位10,即DIV_Fraction = 0x0a,DIV_Mantissa = 22,即为0x16。
以USART1为例,它是挂接在APB2总线上的:
STM32串口USART通讯_第4张图片
即USART1的时钟源频率为72MHz,那么如果要得到115200的波特率,即:
115200 = 72000000 / (16 * USARTDIV),USARTDIV = 39.0625,
同理计算可得,DIV_Fraction = 0.0625 * 16 = 1,DIV_Mantissa = 39,USART_BRR的值设置为0x271。

2.5 校验控制

STM32F103系列单片机的USART支持奇偶校验,USART_CR1寄存器的PCE位置1就可以启动奇偶校验,奇偶检验由硬件自动完成。使能奇偶校验控制后,每个字符帧的格式变为:

起始位 + 数据帧 + 校验位 + 停止位

注意,当使用校验位时,USART_CR1寄存器的M位需要设置为1,即9位模式,因为串口传输的长度为8位数据帧加上1位校验位共9位。启动奇偶校验控制后,在发送数据帧时会自动添加校验位,接收数据帧时自动验证校验位。接收数据时若出现奇偶校验位验证失败,USART_SR寄存器的PE位会被置1并可以产生奇偶校验中断。

2.6 中断控制

USART有多个中断请求事件,常用中断请求有:
STM32串口USART通讯_第5张图片

3. USART初始化数据结构

有了标准库,我们的编程变得十分简单,若上面讲到的寄存器操作及原理不清晰,也不是我们要动手编程的阻碍了,不过了解下总归是好的。
初始化结构体定义在标准外设库的stm32f10x_usart.h文件中:

typedef struct
{
  uint32_t USART_BaudRate;            //波特率,标准库会根据此值计算得到USARTDIV的值,从而设置USART_BRR的值。
  uint16_t USART_WordLength;          //字长,可选8位或9位(多一位表带奇偶校验)
  uint16_t USART_StopBits;            //停止位,0.5、1、1.5、2个停止位可选。设置在USART_CR2的STOP[1:0]位
  uint16_t USART_Parity;              //校验位,可设置USART_Parity_No,USART_Parity_Even(偶校验),USART_Parity_Odd(奇校验)。
                                      //设置在USART_CR1的PCE、PS位
  uint16_t USART_Mode;                //USART模式,USART_Mode_Rx和USART_Mode_Tx可选,可或
  uint16_t USART_HardwareFlowControl; //硬件流控制,可使能RTS/使能CTS/同时使能RTS和CTS,不使能硬件流
} USART_InitTypeDef;

4. USART编程使用到的外设库函数

4.1 USART时钟使能

以USART1为例,它是挂接在APB2总线在的外设,使能函数为

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1);

4.2 USART复位

当外设出现异常时可通过复位设置实现对外设的复位,然后重新配置该外设使其重新工作。一般系统在刚开始配置外设时,都会先执行复位外设操作。USART的复位函数为

void USART_DeInit(USART_TypeDef* USARTx);

复位USART1时

USART_DeInit(USART1);

4.3 串口初始化

void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);

参数一为待初始化的串口标号
参数二为USART_InitTypeDef类型的结构体指针,原型及其意义在前面已讲

4.4 USART发送数据

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);

该函数向USART_DR写入一个数据,串口会自动发送。

4.5 接收数据

uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

该函数是操作USART_DR读取串口接收到的数据

4.6 串口状态

串口的状态是保存在USART_SR寄存器中,读取串口状态寄存器的函数为】

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

参数二表示我们要查看串口的哪种状态,可取值为:

USART_FLAG_LBD:  LIN Break detection flag
USART_FLAG_TXE:  Transmit data register empty flag(传输数据寄存器为空标志)
USART_FLAG_TC:   Transmission Complete flag(传输完成标志)
USART_FLAG_RXNE: Receive data register not empty flag
USART_FLAG_IDLE: Idle Line detection flag
USART_FLAG_ORE:  OverRun Error flag
arg USART_FLAG_NE:   Noise Error flag
arg USART_FLAG_FE:   Framing Error flag
arg USART_FLAG_PE:   Parity Error flag

4.7 串口使能

void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);

4.8 串口响应中断

void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);

参数二表示使能串口哪种中断。串口能产生中断的中断源有许多,取值为:

USART_IT_CTS:  CTS change interrupt (not available for UART4 and UART5)
USART_IT_LBD:  LIN Break detection interrupt
USART_IT_TXE:  Transmit Data Register empty interrupt
USART_IT_TC:   Transmission complete interrupt
USART_IT_RXNE: Receive Data register not empty interrupt(接收数据中断)
USART_IT_IDLE: Idle line detection interrupt
USART_IT_PE:   Parity Error interrupt
USART_IT_ERR:  Error interrupt(Frame error, noise error, overrun error)

4.9 获取相应中断状态

使能某中断后,当该中断发生了就会设置状态寄存器的某个标志位。在中断服务函数中我们经常要去判断是哪个中断源,

ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);

若使能了串口发送完成中断,判断是否产生该中断时用

USART_GetITStatus(USART1, USART_IT_TC);

返回值等于SET,说明发生串口发送完成中断。

5. 编程实践

硬件平台采用MiniSTM32开发板,要实现板载USART和计算机通讯,需要用到一个USB转USART的芯片,MiniSTM32开发板采用的转换芯片为CH340G。硬件接线图为:
STM32串口USART通讯_第6张图片
需要注意的是,STM32的USART1的引脚和CH340的接线。USART1的发送、接收引脚分别是PA9和PA10:
STM32串口USART通讯_第7张图片
通过跳线帽将PA9(U1_TXD)、PA10(U1_RXD)连接到CH340G的RXD、TXD。这个在配置GPIO功能的时候要注意是以STM32芯片原理图上的引脚功能为准。

程序实现功能:开发板上电后通过USART发送字符串到计算机(计算机用串口调试助手接收),进而开发板进入等待中断状态,若计算机通过串口调试助手向开发板发数据就会触发STM32的接收数据终中断,在中断服务函数中接收数据并将数据发回给计算机。

STM32的串口编程关键点在于:
(1)使能RX和TX引脚GPIO时钟和USART时钟
(2)初始化GPIO,并将GPIO复用到USART上。参照《STM32中文参考手册_V10.pdf》-P110 外设的GPIO配置-USART部分
STM32串口USART通讯_第8张图片
(3)配置USART初始化参数
(4)配置终端控制器NVIC并使能USART接收中断
(5)使能USART
(6)在USART1中断服务函数中实现数据接收和发送

编程环境采用MDK5,工程结构如图:
STM32串口USART通讯_第9张图片

BSP/BSP_USART.c中实现对USART的所有操作及NVIC初始化操作,相关声明在BSP/BSP_USART.h中:

//BSP_USART.h
#ifndef __BSP_UART_H__
#define __BSP_UART_H__

#include 

void NVIC_Configuration(void);
void USART_Configuration(void);
void USART_SendChar(USART_TypeDef* pUSARTx, uint8_t c);
void USART_SendString(USART_TypeDef* pUSARTx, char* str);

#endif /* __BSP_UART_H__ */

//BSP_USART.c
#include "BSP_USART.h"

//配置NVIC
void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStu;

    //设置中断分组寄存器
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    NVIC_InitStu.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStu.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStu.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStu.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStu);
}

void USART_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStu;
    USART_InitTypeDef USART_InitStu;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    //PA9用作串口1输入引脚,配置为浮空输入
    GPIO_InitStu.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStu.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStu.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStu);

    //PA10用作串口1输出引脚,配置为复用推挽输出
    GPIO_InitStu.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStu.GPIO_Pin = GPIO_Pin_10;
    GPIO_Init(GPIOA, &GPIO_InitStu);

    //设置USART初始化结构体
    USART_InitStu.USART_BaudRate = 115200;
    USART_InitStu.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStu.USART_Parity = USART_Parity_No;
    USART_InitStu.USART_StopBits = USART_StopBits_1;
    USART_InitStu.USART_WordLength = USART_WordLength_8b;
    USART_InitStu.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_Init(USART1, &USART_InitStu);

    //配置NVIC
    NVIC_Configuration();

    //使能串口接收中断
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

    //开启
    USART_Cmd(USART1, ENABLE);
}

//发送一个字符
void USART_SendChar(USART_TypeDef* pUSARTx, uint8_t c)
{
    USART_SendData(pUSARTx, c);

    //等待发送结束标志
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

//发送字符串
void USART_SendString(USART_TypeDef* pUSARTx, char* str)
{
    uint32_t n = 0;

    while (*(str + n) != '\0')
    {
        USART_SendChar(pUSARTx, *(str + n));
        n++;
    }
    //等待发送结束标志
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

USART1的接收数据产生的中断的中断服务函数在stm32f10x_it.c中实现:

void USART1_IRQHandler(void)
{
    uint16_t c;

    //判断是否为USART1的接收中断
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        c = USART_ReceiveData(USART1);
        USART_SendData(USART1, c);
    }   
}

在main函数中:

#include 

int main(void)
{

    USART_Configuration();
    USART_SendString(USART1, "HelloWorld\n");
    //USART_SendChar(USART1, 'h');
    while (1);  

    return 0;
}

发送完”HelloWorld\n”后进入等待中断中。

下载运行,注意若使用板载的串口下载方式下载程序,就会占用了USART1发送/接收数据用的串口,所以需要下载完程序后再用串口调试助手测试。另外,计算机串口调试助手的波特率、停止位、数据位等要跟在代码中的设置一致。运行如图:

STM32串口USART通讯_第10张图片

你可能感兴趣的:(STM32单片机)