一、STM32 的 USART 简介

        STM32 芯片具有多个 USART 外设用于串口通讯,它是 Universal SynchronousAsynchronous Receiver and Transmitter 的缩写,即通用同步异步收发器可以灵活地与外部设备进行全双工数据交换。有别于 USART,它还有具有 UART 外设(UniversalAsynchronousReceiver and Transmitter),它是在 USART 基础上裁剪掉了同步通信功能,只有异步通信简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是 UART。
        USART 满足外部设备对工业标准 NRZ 异步串行数据格式的要求,并且使用了小数波特率发生器,可以提供多种波特率,使得它的应用更加广泛。 USART 支持同步单向通信和半双工单线通信;还支持局域互连网络 LIN、智能卡(SmartCard)协议与 lrDA(红外线数据协会) SIR ENDEC 规范。
        USART 支持使用 DMA,可实现高速数据通信。
        USART 在 STM32 应用最多莫过于“打印”程序信息,一般在硬件设计时都会预留一个 USART 通信接口连接电脑,用于在调试程序是可以把一些调试信息“打印”在电脑端的串口调试助手工具上,从而了解程序运行是否正确、指出运行出错位置等等。STM32 的 USART 输出的是 TTL 电平信号,若需要 RS-232 标准的信号可使用MAX232 芯片进行转换。
 

二、协议层

1、数据包的组成:

2、波特率:

        主要使用的是串口异步通讯,异步通讯中由于没有时钟信号,所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码,常见的波特率为4800、 9600、 115200 等。

3、 通讯的起始和停止信号:

       串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑 0 的数据位表示,而数据包的停止信号可由 0.5、 1、 1.5 或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。
4、有效数据:
       在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、 6、 7 或 8 位长
5、数据校验:
       在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、 0 校验(space)、 1 校验(mark)以及无校验(noparity)

三、编程要点

1-初始化串口需要用到的GPIO, GPIO_InitTypeDef,GPIO_PinAFConfig();
2-初始化串口, USART_InitTypeDef
3-中断配置
4-使能串口
5-编写发送和接收函数
6-编写中断服务函数

四、编程时需要用到的固件库函数

1-配置GPIO为具体的第二功能
void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF)
2-中断配置函数
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState NewState)
3-串口使能函数
void USART_Cmd(USART_TypeDef* USARTx,FunctionalState NewState)
4-数据发送函数
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)

5-数据接收函数
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
6-中断状态位获取函数
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)

五、一些结构体

USART初始化结构体

typedef struct
{undefined
uint32_t USART_BaudRate; //波特率 BRR
uint16_t USART_WordLength; //字长 CR1_M
uint16_t USART_StopBits; //停止位 CR2_STOP
uint16_t USART_Parity; //校验控制 CR1_PCE、 CR1_PS
uint16_t USART_Mode; //模式选择CR1_TE、 CR1_RE
// 硬件流选择 CR3_CTSE、 CR3_RTSE
uint16_t USART_HardwareFlowControl;

} USART_InitTypeDef;


同步时钟初始化结构体

typedef struct
{undefined
uint16_t USART_Clock; // 同步时钟 CR2_CLKEN
uint16_t USART_CPOL; // 极性 CR2_CPOL
uint16_t USART_CPHA; // 相位 CR2_CPHA
uint16_t USART_LastBit; //最后一个位的时钟脉冲 CR2_LBC

} USART_ClockInitTypeDef;

六、代码片段

#ifndef __BSP_USART_H
#define	__BSP_USART_H
/**
  ******************************************************************************
  * @file    bsp.usart.c
  * @author  Sumjess
  * @version V1.0
  * @date    2019-09-10
  * @brief   MDK5.27
  ******************************************************************************
  * @attention
  *
  * 实验平台   :STM32 F429 
  * CSDN Blog  :https://blog.csdn.net/qq_38351824
  * 微信公众号 :Tech云
  *
  ******************************************************************************
  */
#include "stm32f4xx.h"
#include <stdio.h>
//---------------------------------------------------------------------------------------------//
//               STM32F429IGT6 芯片的 USART 引脚
//       APB2(最高 90MHz)            APB1(最高 45MHz)
//      USART1     USART6       USART2      USART3          UART4     UART5     UART7   UART8
//TX   PA9/PB6    PC6/PG14     PA2/PD5   PB10/PD8/PC10    PA0/PC10    PC12     PF7/PE8   PE1
//RX   PA10/PB7   PC7/PG9      PA3/PD6   PB11/PD9/PC11    PA1/PC11    PD2      PF6/PE7   PE0
//SCLK PA8        PG7/PC8      PA4/PD7   PB12/PD10/PC12                      
//nCTS PA11       PG13/PG15    PA0/PD3   PB13/PD11
//nRTS PA12       PG8/PG12     PA1/PD4   PB14/PD12
/
 
//------------------------------------------------------//
//串口1中断开关
/*******************************************************/
#define EXTI_KEY       0
/
//串口开关
/*******************************************************/
#define USART1_Switch  1
#define USART2_Switch  1
#define USART3_Switch  1
#define UART4_Switch   1
#define UART5_Switch   1
#define USART6_Switch  1
#define UART7_Switch   1
#define UART8_Switch   1
//!!!USART和UART区别:USART即可以同步通信,又可以异步通信;
//                    UART 只能异步通信;
//                    SCLK、nCTS、nRTS这些引脚UART没有。
//
//① 重定向c库函数printf到串口,重定向后可使用printf等函数
//② 重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
//选择使用串口被允许使用 ① ② 
#define DEBUG_USART_printf_choose   DEBUG_USART1
//
 
 
//------------------------------------------------------//
//引脚定义
/*******************************************************/
#if     USART1_Switch
 
#define DEBUG_USART1                             USART1
#define DEBUG_USART1_CLK                         RCC_APB2Periph_USART1  //注意!只有串口1和6是APB2为90M,其他均为APB1为45M
#define DEBUG_USART1_BAUDRATE                    115200  //串口波特率
 
#define DEBUG_USART1_RX_GPIO_PORT                GPIOA
#define DEBUG_USART1_RX_GPIO_CLK                 RCC_AHB1Periph_GPIOA
#define DEBUG_USART1_RX_PIN                      GPIO_Pin_10
#define DEBUG_USART1_RX_AF                       GPIO_AF_USART1  //映射
#define DEBUG_USART1_RX_SOURCE                   GPIO_PinSource10
//!!!一个引脚有许多功能,通过映射将引脚与你要选的功能连接起来。
#define DEBUG_USART1_TX_GPIO_PORT                GPIOA
#define DEBUG_USART1_TX_GPIO_CLK                 RCC_AHB1Periph_GPIOA
#define DEBUG_USART1_TX_PIN                      GPIO_Pin_9
#define DEBUG_USART1_TX_AF                       GPIO_AF_USART1  //映射
#define DEBUG_USART1_TX_SOURCE                   GPIO_PinSource9
 
//中断部分配置//
#define DEBUG_USART_IRQHandler                  USART1_IRQHandler
#define DEBUG_USART_IRQ                 				USART1_IRQn     //串口1中断
 
#endif
 
 
 
#if  USART2_Switch
/************************************************************/
//串口2引脚定义
/*******************************************************/
#define DEBUG_USART2                             USART2
#define DEBUG_USART2_CLK                         RCC_APB1Periph_USART2
#define DEBUG_USART2_BAUDRATE                    115200  //串口波特率
 
#define DEBUG_USART2_RX_GPIO_PORT                GPIOD
#define DEBUG_USART2_RX_GPIO_CLK                 RCC_AHB1Periph_GPIOD
#define DEBUG_USART2_RX_PIN                      GPIO_Pin_6
#define DEBUG_USART2_RX_AF                       GPIO_AF_USART2    //映射
#define DEBUG_USART2_RX_SOURCE                   GPIO_PinSource6
//!!!一个引脚有许多功能,通过映射将引脚与你要选的功能连接起来。
#define DEBUG_USART2_TX_GPIO_PORT                GPIOD
#define DEBUG_USART2_TX_GPIO_CLK                 RCC_AHB1Periph_GPIOD
#define DEBUG_USART2_TX_PIN                      GPIO_Pin_5
#define DEBUG_USART2_TX_AF                       GPIO_AF_USART2    //映射
#define DEBUG_USART2_TX_SOURCE                   GPIO_PinSource5
 
#endif
 
 
 
#if  USART3_Switch
/************************************************************/
//串口3引脚定义
/*******************************************************/
#define DEBUG_USART3                             USART3
#define DEBUG_USART3_CLK                         RCC_APB1Periph_USART3
#define DEBUG_USART3_BAUDRATE                    115200  //串口波特率
 
#define DEBUG_USART3_RX_GPIO_PORT                GPIOB
#define DEBUG_USART3_RX_GPIO_CLK                 RCC_AHB1Periph_GPIOB
#define DEBUG_USART3_RX_PIN                      GPIO_Pin_11
#define DEBUG_USART3_RX_AF                       GPIO_AF_USART3    //映射
#define DEBUG_USART3_RX_SOURCE                   GPIO_PinSource11
//!!!一个引脚有许多功能,通过映射将引脚与你要选的功能连接起来。
#define DEBUG_USART3_TX_GPIO_PORT                GPIOB
#define DEBUG_USART3_TX_GPIO_CLK                 RCC_AHB1Periph_GPIOB
#define DEBUG_USART3_TX_PIN                      GPIO_Pin_10
#define DEBUG_USART3_TX_AF                       GPIO_AF_USART3    //映射
#define DEBUG_USART3_TX_SOURCE                   GPIO_PinSource10
 
#endif
 
 
 
#if  UART4_Switch
/************************************************************/
//串口4引脚定义
/*******************************************************/
#define DEBUG_UART4                             UART4
#define DEBUG_UART4_CLK                         RCC_APB1Periph_UART4
#define DEBUG_UART4_BAUDRATE                    115200  //串口波特率
 
#define DEBUG_UART4_RX_GPIO_PORT                GPIOC
#define DEBUG_UART4_RX_GPIO_CLK                 RCC_AHB1Periph_GPIOC
#define DEBUG_UART4_RX_PIN                      GPIO_Pin_11
#define DEBUG_UART4_RX_AF                       GPIO_AF_UART4    //映射
#define DEBUG_UART4_RX_SOURCE                   GPIO_PinSource11
//!!!一个引脚有许多功能,通过映射将引脚与你要选的功能连接起来。
#define DEBUG_UART4_TX_GPIO_PORT                GPIOC
#define DEBUG_UART4_TX_GPIO_CLK                 RCC_AHB1Periph_GPIOC
#define DEBUG_UART4_TX_PIN                      GPIO_Pin_10
#define DEBUG_UART4_TX_AF                       GPIO_AF_UART4    //映射
#define DEBUG_UART4_TX_SOURCE                   GPIO_PinSource10
 
#endif
 
 
 
#if  UART5_Switch
/************************************************************/
//串口5引脚定义
/*******************************************************/
#define DEBUG_UART5                             UART5
#define DEBUG_UART5_CLK                         RCC_APB1Periph_UART5
#define DEBUG_UART5_BAUDRATE                    115200  //串口波特率
 
#define DEBUG_UART5_RX_GPIO_PORT                GPIOD
#define DEBUG_UART5_RX_GPIO_CLK                 RCC_AHB1Periph_GPIOD
#define DEBUG_UART5_RX_PIN                      GPIO_Pin_2
#define DEBUG_UART5_RX_AF                       GPIO_AF_UART5    //映射
#define DEBUG_UART5_RX_SOURCE                   GPIO_PinSource2
//!!!一个引脚有许多功能,通过映射将引脚与你要选的功能连接起来。
#define DEBUG_UART5_TX_GPIO_PORT                GPIOC
#define DEBUG_UART5_TX_GPIO_CLK                 RCC_AHB1Periph_GPIOC
#define DEBUG_UART5_TX_PIN                      GPIO_Pin_12
#define DEBUG_UART5_TX_AF                       GPIO_AF_UART5    //映射
#define DEBUG_UART5_TX_SOURCE                   GPIO_PinSource12
 
#endif
 
 
 
#if  USART6_Switch
/************************************************************/
//串口6引脚定义
/*******************************************************/
#define DEBUG_USART6                             USART6
#define DEBUG_USART6_CLK                         RCC_APB2Periph_USART6  //注意!只有串口1和6是APB2为90M,其他均为APB1为45M
#define DEBUG_USART6_BAUDRATE                    115200  //串口波特率
 
#define DEBUG_USART6_RX_GPIO_PORT                GPIOC
#define DEBUG_USART6_RX_GPIO_CLK                 RCC_AHB1Periph_GPIOC
#define DEBUG_USART6_RX_PIN                      GPIO_Pin_7
#define DEBUG_USART6_RX_AF                       GPIO_AF_USART6    //映射
#define DEBUG_USART6_RX_SOURCE                   GPIO_PinSource7
//!!!一个引脚有许多功能,通过映射将引脚与你要选的功能连接起来。
#define DEBUG_USART6_TX_GPIO_PORT                GPIOC
#define DEBUG_USART6_TX_GPIO_CLK                 RCC_AHB1Periph_GPIOC
#define DEBUG_USART6_TX_PIN                      GPIO_Pin_6
#define DEBUG_USART6_TX_AF                       GPIO_AF_USART6    //映射
#define DEBUG_USART6_TX_SOURCE                   GPIO_PinSource6
 
#endif
 
 
 
#if  UART7_Switch
/************************************************************/
//串口7引脚定义
/*******************************************************/
#define DEBUG_UART7                             UART7
#define DEBUG_UART7_CLK                         RCC_APB1Periph_UART7
#define DEBUG_UART7_BAUDRATE                    115200  //串口波特率
 
#define DEBUG_UART7_RX_GPIO_PORT                GPIOF
#define DEBUG_UART7_RX_GPIO_CLK                 RCC_AHB1Periph_GPIOF
#define DEBUG_UART7_RX_PIN                      GPIO_Pin_6
#define DEBUG_UART7_RX_AF                       GPIO_AF_UART7    //映射
#define DEBUG_UART7_RX_SOURCE                   GPIO_PinSource6
//!!!一个引脚有许多功能,通过映射将引脚与你要选的功能连接起来。
#define DEBUG_UART7_TX_GPIO_PORT                GPIOF
#define DEBUG_UART7_TX_GPIO_CLK                 RCC_AHB1Periph_GPIOF
#define DEBUG_UART7_TX_PIN                      GPIO_Pin_7
#define DEBUG_UART7_TX_AF                       GPIO_AF_UART7    //映射
#define DEBUG_UART7_TX_SOURCE                   GPIO_PinSource7
 
#endif
 
 
 
#if  UART8_Switch
/************************************************************/
//串口8引脚定义
/*******************************************************/
#define DEBUG_UART8                             UART8
#define DEBUG_UART8_CLK                         RCC_APB1Periph_UART8
#define DEBUG_UART8_BAUDRATE                    115200  //串口波特率
 
#define DEBUG_UART8_RX_GPIO_PORT                GPIOE
#define DEBUG_UART8_RX_GPIO_CLK                 RCC_AHB1Periph_GPIOE
#define DEBUG_UART8_RX_PIN                      GPIO_Pin_0
#define DEBUG_UART8_RX_AF                       GPIO_AF_UART8    //映射
#define DEBUG_UART8_RX_SOURCE                   GPIO_PinSource0
//!!!一个引脚有许多功能,通过映射将引脚与你要选的功能连接起来。
#define DEBUG_UART8_TX_GPIO_PORT                GPIOE
#define DEBUG_UART8_TX_GPIO_CLK                 RCC_AHB1Periph_GPIOE
#define DEBUG_UART8_TX_PIN                      GPIO_Pin_1
#define DEBUG_UART8_TX_AF                       GPIO_AF_UART8    //映射
#define DEBUG_UART8_TX_SOURCE                   GPIO_PinSource1
 
#endif
 
 
 
 
 
/*****************************************串口相关函数***************************************/
 
 
void Debug_USART_Config(void);                                 //串口初始化
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);     //发送一个字符
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);//发送一个16位数
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);    //发送字符串
int fputc(int ch, FILE *f);                                    //重定向c库函数printf到串口,重定向后可使用printf函数
int fgetc(FILE *f);                                            //重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
 
 
#endif  /* __BSP_DEBUG_USART_H */

 七、USART 中断请求

中断事件 事件标志 使能控制位
发送数据寄存器为空 TXE TXEIE
CTS 标志 CTS CTSIE
发送完成 TC TCIE
准备好读取接收到的数据 RXNE RXNEIE
检测到上溢错误 ORE
检测到空闲线路 IDLE IDLEIE
奇偶校验错误 PE PEIE
断路标志 LBD LBDIE
多缓冲区通信中的噪声标志、上溢错误和帧错误 NF 或 ORE 或 FE EIE

八、常用函数总结

(1)发送字符串

/*****************  发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  /* 等待发送完成 */
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  {}
}

(2)发送一个字符

/*****************  发送一个字符 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,ch);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

(3)发送一个16位数

/*****************  发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
	uint8_t temp_h, temp_l;
	
	/* 取出高八位 */
	temp_h = (ch&0XFF00)>>8;
	/* 取出低八位 */
	temp_l = ch&0XFF;
	
	/* 发送高八位 */
	USART_SendData(pUSARTx,temp_h);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
	
	/* 发送低八位 */
	USART_SendData(pUSARTx,temp_l);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

(4)字符接收函数

//字符接收函数:
//uint8_t Usart_Sum_rec( USART_TypeDef * pUSARTx, uint8_t * rev)
//返回值:1为获取数据成功   0为获取数据失败
//USART_TypeDef * pUSARTx   选择串口输出,注意必须先初始化、使能该串口
//uint8_t * rev  利用指针获取数据
uint8_t Usart_Sum_rec( USART_TypeDef * pUSARTx, uint8_t * rev)
{
	 if(USART_GetFlagStatus(pUSARTx,USART_FLAG_RXNE)!=RESET)
	 {	
		 *rev = USART_ReceiveData(pUSARTx);
		 Delay_ms(1);
		 return 1;
	 }
	 return 0;
 }