本章将实现如下功能:STM32通过串口和上位机对话,STM32在收到上位机发过来的字符串后,原原本本返回给上位机。
STM32 串口简介
串口作为MCU的重要外部接口,同时也是软件开发重要的调试手段,其重要性不言而喻。现在基本上所有的MCU都会带有串口,STM32自然也不例外。
通过USB串口和电脑通信
串口最基本的设置,就是波特率的设置。只要开启了串口时钟,设置相应IO口的模式,然后配置一下波特率,数据位长度,奇偶校验位等信息,就可以使用。
- 串口时钟使能。串口作为STM32的一个外设,其时钟由外设时钟使能寄存器控制,串口1是在APB2ENR寄存器的第14位。除了串口1的时钟使能在APB2ENR寄存器,其它串口的时钟使能位都在APB1ENR寄存器,而APB2的频率一般是APB1的一倍。
- 串口复位。当外设出现异常的时候可以通过复位寄存器里面的对应位设置,实现该外设的复位,然后重新配置这个外设让其重新工作。一般在系统刚开始配置外设的时候,都会先执行复位该外设的操作。
串口1的复位设置位在APB2RSTR的第14位,通过向该位写1复位串口1,写0结束复位。
串口设置的一般步骤
- 串口时钟使能,GPIO时钟使能。
- 设置引脚复用器映射:调用GPIO_PinAFConfig函数。
- GPIO初始化设置:要设置模式为复用功能。
- 串口参数初始化:设置波特率,字长,奇偶校验等参数。
- 开启中断并且初始化NVIC,使能中断。
- 使能串口。
- 编写中断处理函数:函数名格式为USART1IRQHandler。
在HAL库中,串口相关的函数和定义主要在文件stm32f1xx_hal_uart.c和stm32f1xx_hal_uart.h中。
串口参数初始化
串口作为STM32的一个外设,HAL库为其配置了串口初始化函数。
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
typedef struct
{
USART_TypeDef *Instance;
UART_InitTypeDef Init;
uint8_t *pTxBuffPtr;
uint16_t TxXferSize;
__IO uint16_t TxXferCount;
uint8_t *pRxBuffPtr;
uint16_t RxXferSize;
__IO uint16_t RxXferCount;
DMA_HandleTypeDef *hdmatx;
DMA_HandleTypeDef *hdmarx;
HAL_LockTypeDef Lock;
__IO HAL_UART_StateTypeDef gState;
__IO HAL_UART_StateTypeDef RxState;
__IO uint32_t ErrorCode;
}UART_HandleTypeDef;
Instance是结构体指针类型变量,如果是串口11,取值为 USART1 即可。
Init 是 UART_InitTypeDef 结构体类型变量,它是用来设置串口的各个参数,包括波特率,停止位等。
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
该结构体第一个参数 BaudRate 为串口波特率,波特率可以说是串口最重要的参数了,它用来确定串口通信的速率。第二个参数 WordLength 为字长,可以设置为 8 位字长或者 9 位字长,这里我们设置为 8 位字长数据格式 UART_WORDLENGTH_8B。第三个参数 StopBits 为停止位设置,可以设置为 1 个停止位或者 2 个停止位,这里我们设置为 1 位停止位 UART_STOPBITS_1。第四个参数 Parity 设定是否需要奇偶校验,我们设定为无奇偶校验位。第五个参数 Mode 为串口模式,可以设置为只收模式,只发模式,或者收发模式。这里我们设置为全双工收发模式。第六个参数 HwFlowCtl 为是否支持硬件流控制,我们设置为无硬件流控制。第七个参数OverSampling 用来设置过采样为 16 倍还是 8 倍。
pTxBuffPtr,TxXferSize 和 TxXferCount 三个变量分别用来设置串口发送的数据缓存指针,发送的数据量和还剩余的要发送的数据量。而接下来的三个变量pRxBuffPtr,RxXferSize 和RxXferCount 则是用来设置接收的数据缓存指针,接收的最大数据量以及还剩余的要接收的数据量。
UART_HandleTypeDef UART1_Handler; //UART 句柄
UART1_Handler.Instance=USART1; //USART1
UART1_Handler.Init.BaudRate=115200; //波特率
UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为 8 位格式
UART1_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
UART1_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
UART1_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式
HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能 UART1
函数HAL_UART_Init内部会调用串口使能函数使能相应串口,所以调用了该函数之后就不需要重复使能串口了。
在调用初始化函数HAL_UART_Init内部,会先调用MSP初始化回调函数进行MCU相关初始化,函数为:
void HAL_UART_MspInit(UART_HandleTypeDef *huart);
该函数内部用来编写IO口初始化,时钟使能以及NVIC配置。
使能串口和GPIO口时钟
要使用串口,必须使能串口时钟和使用到的GPIO口时钟。
例如我们要使用串口 1,所以我们必须使能串口 1 时钟和 GPIOA 时钟(串口 1 使用的是 PA9 和 PA10)。
__HAL_RCC_USART1_CLK_ENABLE(); //使能 USART1 时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能 GPIOA 时钟
GPIO口初始化设置以及复用映射配置
在HAL库中IO口初始化参数设置和复用映射配置是在函数HAL_GPIO_Init中一次性完成的。
GPIO_Initure.Pin=GPIO_PIN_9; //PA9
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化 PA9
GPIO_Initure.Pin=GPIO_PIN_10; //PA10
GPIO_Initure.Mode=GPIO_MODE_AF_INPUT; //模式要设置为复用输入模式!
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化 PA10
开启串口相关中断,配置串口中断优先级
HAL库中定义了一个使能串口中断的标识符__HAL_UART_ENABLE_IT。
使能接收完成中断
__HAL_UART_ENABLE_IT(huart,UART_IT_RXNE);
第一个参数为串口句柄,第二个参数为要开启的中断类型值。
HAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道
HAL_NVIC_SetPrioority(USART1_IRQn,3,3); //抢占优先级3,子优先级3.
编写中断服务函数
void USART1_IRQHandler(void) ;
串口数据接收和发送
STM32F1 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是一个双寄存器,包含了 TDR 和 RDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);