一、串口
记录使用HAL库开发串口
1.1 简介
USART中文意思是通用同步异步收发器,常用串口是异步串口,简记为UART,是内部集成的硬件外设。使用两根通信线,发送端TX和接收端RX,工作时RX和TX交叉连接。由于计算机的USB接口协议与单片机的TTL协议不相容,需要用到USB转TTL模块
F407内部集成4个通用同步/异步收发器(USART1,USART2,USART3和USART6),2个通用异步收发器(UART4和UART5)。
1.2 串口硬件框图
这里给出参考手册里面的串口硬件框图
由图可以得出以下信息
- TX和RX接口分别用于数据的发送和接收
- SW_RX接口用于智能卡模式下接收数据
- IRAD_OUT和IRAD_IN接口用于IRAD模式下的数据发送和接收
- RST和CTS接口用于硬件流控制
- USART_BRR用于生成波特率
发送过程和接收过程如下
- 发送过程
数据先进入USART_TDR,同时TXE置零;再由USART_TDR进入USART_TSR并置位TXE;最后由USART_TSR经由TX发送出去 - 接收过程
数据先经由RX进入USART_RSR;再由USART_RSR进入USART_RDR,同时RXE置零;最后由USART_RDR进入总线被接受并置位RXE
1.3 串口参数及数据帧
数据帧具体包括以下参数
- 波特率:串口通信速率
- 起始位:固定为低电平
- 数据位:1为高电平,0为低电平,低位先行,可配置数据位长度(8/9)
- 校验位:数据位的最后一位
- 停止位:固定为高电平,可设置停止位长度(0.5/1/1.5/2)
这里给出江协科教程中的串口时序图
1.4 串口发送时序图
这里给出串口发送时序图,以理解TC和TXE行为
该图解读如下
- 软件使能USART,USART外设就绪,TXE=1
- 等待TXE=1标志,将F1数据写入DR同时TXE=0,数据F2就绪
- DR中数据F1进入TX line发送出去同时TXE=1
- 数据F2和数据F3按以上流程进行发送
- 数据F3发送完成后,TC=1标志发送完成
1.5 串口接收
- 起始位检测
当识别到特定序列时才能检测起始位,该序列是1110X0X0X0000
,在一位时间内进行16位采样,如下图所示
由图可以看出,先检测RX线的下降沿,检测到下降沿后的三个采样点中至少有2个为0能确定起始位,之后的三个采样点中至少有2个为0才能确定当前起始位有效。 - 数据采样
当一个数据帧接收完毕,配置RXNE=1,表明RSR为空,RDR正常接收到数据,可继续接收下一帧数据
二、串口的HAL库驱动
2.1 串口原理分析
根据stm32f4xx_hal_uart.c
这一文件,给出USART外设的使用步骤
- 通过
HAL_UART_MspInit()
进行UART外设硬件配置 - 通过
UART_HandleTypeDef
进行UART外设配置
注意:这里使用的是USART1,该外设是复用在GPIOA上,需要映射复用输入7(AF7)。串口发射脚需要产生高低电平,应设置为推挽输出模式;串口空闲帧为高电平,开发板RX脚未接上拉电阻,因此接收脚设置为上拉模式。
2.2 HAL库代码编写
uart.c
文件
/*
*********************************************************************************************************
* Module Description
*
* 串口模块,开发板串口接口为PA9和PA10引脚,分别为USART1_TX和USART1_RX
*********************************************************************************************************
*/
#include "bsp.h"
/* private function prototypes */
void uart_Config();
/*
**********************************************************************************
* @brief 板载串口初始化
* @param None
* @return None
* @note
**********************************************************************************
*/
void bsp_InitUart()
{
uart_Config();
}
/*
**********************************************************************************
* @brief 将USART1外设配置为UART
* -数据位8字节
* -1字节停止位
* -无校验位
* -波特率9600
* -禁用硬件流控制
* @param None
* @return None
* @note
**********************************************************************************
*/
void uart_Config()
{
UART_HandleTypeDef UART_HandleStructure;
UART_HandleStructure.Instance = USART_DEBUG; // UART寄存器基址
UART_HandleStructure.Init.BaudRate = USART_BAUDRATE; // UART波特率
UART_HandleStructure.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 禁用硬件流控制
UART_HandleStructure.Init.Mode = UART_MODE_TX_RX; // 收发模式
UART_HandleStructure.Init.Parity = UART_PARITY_NONE; // 禁用校验位
UART_HandleStructure.Init.StopBits = UART_STOPBITS_1; // 1字节停止位
UART_HandleStructure.Init.WordLength = UART_WORDLENGTH_8B; // 8字节数据位
UART_HandleStructure.Init.OverSampling = UART_OVERSAMPLING_16; // 过采样
if(HAL_UART_Init(&UART_HandleStructure) != HAL_OK)
{
Error_Handler();
}
}
/*
**********************************************************************************
* @brief 配置UART底层硬件参数,完成时钟使能、引脚配置和中断配置
* @param hurt:UART寄存器基址
* @return None
* @note 此函数原为弱定义,经重定义后即为实际被
* HAL_UART_Init(UART_HandleTypeDef *huart)函数调用
**********************************************************************************
*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_InitStructure;
if(huart->Instance == USART_DEBUG)
{
/* 开启硬件时钟 */
USART1_CLK_ENABLE();
USART1_TX_GPIO_CLK_ENABLE();
USART1_RX_GPIO_CLK_ENABLE();
/* 配置引脚 */
GPIO_InitStructure.Pin = USART1_TX_PIN; // TX引脚
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; // 复用推挽
GPIO_InitStructure.Pull = GPIO_PULLUP; // 上拉模式
GPIO_InitStructure.Alternate = USART1_TX_AF; // 复用映射
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
HAL_GPIO_Init(USART1_TX_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.Pin = USART1_RX_PIN; // RX引脚
GPIO_InitStructure.Alternate = USART1_RX_AF; // 复用映射
HAL_GPIO_Init(USART1_RX_GPIO_PORT, &GPIO_InitStructure);
}
}
/*
**********************************************************************************
* @brief 重定向c库函数fputc,可以使用printf函数从串口1打印输出
* @param None
* @return None
* @note
**********************************************************************************
*/
int fputc(int ch, FILE *f)
{
while((USART1->SR & 0X40) == 0); // 等待上一个字符发送完成
USART1->DR = (uint8_t)ch; // 将要发送的字符写入DR
return ch;
}
uart.h
文件
#ifndef __BSP_UART_FIFO_H
#define __BSP_UART_FIFO_H
/* private define */
#define USART_DEBUG USART1
#define USART_BAUDRATE 115200
#define USART1_CLK_ENABLE() do{__HAL_RCC_USART1_CLK_ENABLE();}while(0)
#define USART1_TX_GPIO_CLK_ENABLE() do{__HAL_RCC_GPIOA_CLK_ENABLE();}while(0)
#define USART1_TX_GPIO_PORT GPIOA
#define USART1_TX_PIN GPIO_PIN_9
#define USART1_TX_AF GPIO_AF7_USART1
#define USART1_RX_GPIO_CLK_ENABLE() do{__HAL_RCC_GPIOA_CLK_ENABLE();}while(0)
#define USART1_RX_GPIO_PORT GPIOA
#define USART1_RX_PIN GPIO_PIN_10
#define USART1_RX_AF GPIO_AF7_USART1
/* public statement */
void bsp_InitUart();
#endif
以上即完成了USART1作UART的配置,在main
函数中即可使用printf
函数经串口打印数据。