UART串口收发过程与配置
参考资料
《STM32Fx中文参考手册》第26章:通用同步异步收发器章节
开发板配套教程《STM32Fx开发指南》 串口实验章节
笔记基于正点原子官方视频
视频连接https://www.bilibili.com/video/BV1Wx411d7wT?p=71&spm_id_from=333.1007.top_right_bar_window_history.content.click
如有侵权,联系删除
一、串口发送过程配置流程概述
1.HAL库中串口寄存器定义文件
在stm32f429xx.h 文件(位置:工程文件-HALLIB-stm32f4xx_hal.c-stm32f429xx.h)中,就有关于串口寄存器定义的结构体
typedef struct
{
__IO uint32_t SR;
__IO uint32_t BRR;
__IO uint32_t CR1;
__IO uint32_t CR2;
__IO uint32_t CR3;
__IO uint32_t GTPR;
} USART_TypeDef
2.HAL库中串口函数声明定义文件
位置:工程文件-HALLIB-stm32f4xx_hal_uart.c和工程文件-HALLIB-stm32f4xx_hal_usart.c
stm32f4xx_hal_uart.c / stm32f4xx_hal_uart.c
stm32f4xx_hal_usart.c / stm32f4xx_hal_usart.c
3.串口字节发送流程
① 编程USARTx_CR1的M位来定义字长。
② 编程USARTx_CR2的STOP位来定义停止位位数。
③ 编程USARTx_BRR寄存器确定波特率。
④ 使能USARTx_CR1的UE位使能USARTx。
⑤ 如果进行多缓冲通信,配置USARTx_CR3的DMA使能(DMAT)。具体请参考后面DMA实验。
⑥ 使能USARTx_CR1的TE位使能发送器。
⑦ 向发送数据寄存器TDR写入要发送的数据(对于M3,发送和接收共用DR寄存器)。
⑧ 向TRD寄存器写入最后一个数据后,等待状态寄存器USARTx_SR(ISR)的TC位置1,传输完成。
注:①-⑥为配置步骤;⑦-⑧为发送数据步骤
- 配置步骤①~⑥:配置字长,停止位,奇偶校验位,波特率等:
位置:工程文件-HALLIB-stm32f4xx_hal_uart.c中
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
该函数内部会引用标识符__HAL_USART_ENABLE使能相应串口。
- 步骤⑦~⑧发送数据和等待发送完成:
位置:工程文件-HALLIB-stm32f4xx_hal_uart.c中
HAL_StatusTypeDef HAL_USART_Transmit(USART_HandleTypeDef *husart, uint8_t *pTxData, uint16_t Size, uint32_t Timeout);
二、串口发送预备
1.__Weak关键字
函数前面加__weak修饰符,我们称之为弱函数。对于弱函数,用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候会选择用户定义的函数。如果用户没有定义,那么函数内容就是弱函数定义的内容。
- 函数声明
void HAL_UART_MspInit(UART_HandleTypeDef *huart);
- 函数定义(弱函数)
函数内部为空,即我们可以在里面编写相关弱函数
__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
}
- 若函数被重新定义
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
…//内容
}
** __Weak关键字的好处**
- 对于事先已经定义好的一个流程,我们只希望修改流程中的某部分与用户相关的代码,这个时候我们可以采用弱函数定义一个空函数,然后让用户自行定义该函数。这样做的好处是我们不会对既有程序流程做任何修改。
- HAL库中大量使用__weak关键字修饰外设回调函数。
- 外设回调函数供用户编写MCU相关程序,大大提高程序的通用性移植性。
2.串口发送程序配置过程(HAL库):
① 初始化串口相关参数,使能串口:HAL_UART_Init();
② 串口相关IO口配置,复用配置:在HAL_UART_MspInit中调用HAL_GPIO_Init函数。
③ 发送数据,并等待数据发送完成:HAL_UART_Transmit()函数;
二、串口发送具体步骤
1.主函数
打开工程模板,把主函数清理成下图样子,并初始化延时函数
2.初始化串口参数
我定义一个uart1,在里面初始化HAL_UART_Init,因为HAL_UART_Init(UART_HandleTypeDef *huart)里面是一个结构体变量(UART_HandleTypeDef),我们查看结构体变量的内容如下图:
其中1号框内每一个参数都有自己的定义,里面定义了可以选择哪些数据区表示2号框里的内容,具体可以右键-查看定义来查看
typedef struct
{
USART_TypeDef *Instance;
UART_InitTypeDef Init;
uint8_t *pTxBuffPtr;
uint16_t TxXferSize;
uint16_t TxXferCount;
uint8_t *pRxBuffPtr;
uint16_t RxXferSize;
uint16_t RxXferCount;
DMA_HandleTypeDef *hdmatx;
DMA_HandleTypeDef *hdmarx;
HAL_LockTypeDef Lock;
__IO HAL_UART_StateTypeDef State;
__IO uint32_t ErrorCode;
}UART_HandleTypeDef
3.初始化串口参数步骤
1)初始化UART_HandleTypeDef的第一个变量usart1_handler.Instance
查看HAL_UART_Init下面关于Instance的定义,如下图
可见到Instance可以赋值的内容这里我们选择USART1,即写入代码
usart1_handler.Instance=USART1;
2)初始化usart1_handler.Init
查看Init相关定义,发现Init也是一个结构体,里面有7个变量,如图,也就说里面的7个变量也要进行逐一定义。
typedef struct
{
uint32_t BaudRate; //波特率
uint32_t WordLength; //字长
uint32_t StopBits; //
uint32_t Parity; //
uint32_t Mode; //
uint32_t HwFlowCtl; //
uint32_t OverSampling; //
}UART_InitTypeDef;
(1)定义波特率为115200,代码如下
usart1_handler.Init.BaudRate=115200; //定义波特率
(2)定义字长WordLength,查看定义如下
**小技巧:**这里问我们需要查看WordLength的定义,我们就可以把WordLength复制下来,在总的初始化函数里面查找。
然后查看编号4的定义就可以了,如下图
可以看到字长有8位和9位两种,这我们选择8位,代码如下:
usart1_handler.Init.WordLength=UART_WORDLENGTH_8B;
(3)定义停止位StopBits
可以看到,StopBits可以选择1位或2位,这里我们选择1位,代码如下
usart1_handler.Init.StopBits=UART_STOPBITS_1;
(4)定义硬件流HwFlowCtl
查看定义有如下选项,这里我们选择关闭,代码如下:
usart1_handler.Init.HwFlowCtl=UART_HWCONTROL_NONE;
(5)定义模式Mode(开启发送还是接收或者都打开)
查看定义如下
在这个文件夹下继续搜索Mode,这里选择都打开,代码如下
usart1_handler.Init.Mode=UART_MODE_TX_RX;
(6)定义奇偶校验位Parity
查看定义如下,选择不使用校验位,代码如下:
usart1_handler.Init.Parity=UART_HWCONTROL_NONE;
到这里usart1_handler.Init结构体初始化完毕!!
3)注释掉usart.c函数
位置在:工程模板-SYSTEM-usart.c,
因为usart.c库函数中已经对串口发送和接收定义好了,这里我们要使用自己写的程序,则需要把下面代码注释掉
4)对弱函数进行重新编写
找到弱函数位置,复制其函数名,在main函数中进行重新编写,
我们常把外设的复用映射
、GPIO串口初始化
和中断优先级设置
放在里面
代码如下:
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_Initure; //初始化GPIO结构体
if(huart->Instance ==USART1)
{
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟
GPIO_Initure.Pin=GPIO_PIN_9; //PA9
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速
GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为USART1
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9
GPIO_Initure.Pin=GPIO_PIN_10; //PA10
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10
}
5)串口发送程序编写
编写程序发送函数,如图,并查看定义:
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
其中UART_HandleTypeDef *huart为句柄(前文已解释)
uint8_t *pData为需要发送的数据
uint16_t Size为需要发送多少个字节的数据
uint32_t Timeout为超时时间
所以设置好的代码为
u8 data[]="-Apollo by wire- "; //定义一个数组,用于存放要发送的数据(数据为-Apollo by wire- )
while(1)
{
HAL_UART_Transmit(&usart1_handler,data,sizeof(data),1000);
delay_ms(300);
}
6)串口发送程序main.c代码
#include "sys.h"
#include "delay.h"
#include "usart.h"
UART_HandleTypeDef usart1_handler; //定义结构体变量
void uart1_init() //初始化uart1
{
usart1_handler.Instance=USART1;
usart1_handler.Init.BaudRate=115200; //定义波特率
usart1_handler.Init.WordLength=UART_WORDLENGTH_8B; //定义字长位8位
usart1_handler.Init.StopBits=UART_STOPBITS_1; //定义停止位
usart1_handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //定义硬件流
usart1_handler.Init.Mode=UART_MODE_TX_RX; //定义模式
usart1_handler.Init.Parity=UART_HWCONTROL_NONE; //定义奇偶校验位
HAL_UART_Init(&usart1_handler); //初始换刚刚定义的结构体变量
}
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_Initure; //初始化GPIO结构体
if(huart->Instance ==USART1)
{
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟
GPIO_Initure.Pin=GPIO_PIN_9; //PA9
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速
GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为USART1
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9
GPIO_Initure.Pin=GPIO_PIN_10; //PA10
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10
}
}
int main(void)
{
u8 data[]="-Apollo by wire- "; //定义一个数组,用于存放要发送的数据
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhz
delay_init(180); //初始化延时函数
uart1_init(); //调用前面写的初始化函数
while(1)
{
HAL_UART_Transmit(&usart1_handler,data,sizeof(data),1000);
delay_ms(300);
}
}
4.编译程序,烧录进开发板
编译程序并烧录进开发板,
打开串口调试助手,位置:H:\3.开发板学习\STM32WorkFile\000.串口调试助手\XCOM(正点原子推荐)
然后设置好接口和波特率,打开串口接收即可看到数据在持续输出。