文章目录
前言
在上一篇博客里面写了串口通信的理论知识,在这一篇中将讲述串口通信在STM32CubeMx里面的配置,以及在函数里面怎么使用。
对于串口发送信息,分为三种方法:串口阻塞方式收发、串口中断方式收发、串口DMA方式收发。(DMA方式在之后的DMA章节讲解)
一、串口阻塞方式收发
STM32CubeMx配置
关于STM32CubeMx的基础配置讲解可以参考这篇博客STM32 CubeMx教程 – 基础知识及配置使用教程
配置RCC时钟,选择外部晶振模式:
配置SYS,debug模式
然后配置串口,首先选择一种模式,我使用的是 USB转TTL,(VCC GND RXD TXD)没有使用时钟线(CLK),所以选择使用异步通信。Mode 选择 Asynchronous ;Hardware Flow Control 选择disable
Mode:
Asynchronous:全双工异步通信
Synchronous:发送方为同步传输提供时钟的同步通信。在USART_CR2寄存器上写CLKEN位选择同步模式,用户可以以主模式方式控制双向同步串行通信,增加CK引脚作为USART发送器时钟的输出。
Single Wire:单线半双工通信。 单线半双工模式通过设置USART_CR3寄存器的HDSEL位选择。RX引脚不再被使用,TX和RX引脚在芯片内部互联,通过单线半双工协议与对侧交互数据。
Multiprocessor Communication:多处理器通信。 可以将几个USART连载一个网络里。
IrDA: (红外线数据协会) SIR ENDEC规范 。通过设置USART_CR3寄存器的IREN位选择IrDA模式。
LIN:局域互联网模式。 通过设置USART_CR2寄存器的LINEN位选择LIN模式。
Smart Card:智能卡模拟功能。 设置USART_CR3寄存器的SCEN位选择智能卡模式。智能卡是一个单线半双工通信协议。
Smart Card with Card Clock: CLKEN位可以被设置,以提供时钟给智能卡。
Hardware Flow Control :
Disable: 只通过RX和TX实现基本的串口通信
CTS Only: 在RX和TX的基础上增加CTS引脚,发送清除信号。若为高电平,在当前数据传输结束时阻断下一次的数据发送。
RTS Only: 在RX和TX的基础上增加RTS引脚,发送请求信号。若为低电平,表明USART准备好接收数据。在485通信中,需要使用该引脚。
CTS/RTS: 在RX和TX的基础上增加CTS和RTS引脚。
配置串口详细参数:
如果有自己喜欢的波特率可以改一改,大部分情况下是默认就行,不用改;
Parameter Settings:
Baud Rate:波特率。 通过波特率寄存器(USART_BRR)设置。
Word Length:字长。 数据位可选8位或9位 ,通过控制寄存器1(USART_CR1)中的M位设置。
Parity:奇偶校验选择。 校验位可选无校验(None)、偶校验(Even)、奇校验(Odd)。通过控制寄存器1(USART_CR1)中的PCE位和PS位设置。
Stop Bits:停止位。 停止位可选1位、2位。通过控制寄存器2(USART_CR2)中的STOP位设置。
Data Direction:数据方向。 可选收发(Receive and Transmit)、只接收(Receive Only)、只发送(Transmit Only)。通过控制寄存器1(USART_CR1)中的TE和RE位设置。
然后配置时钟树
配置项目设置:
然后生成工程。
串口发送/接收函数
HAL_UART_Transmit();串口发送数据,使用超时管理机制
HAL_UART_Receive();串口接收数据,使用超时管理机制
HAL_UART_Transmit_IT();串口中断模式发送
HAL_UART_Receive_IT();串口中断模式接收
HAL_UART_Transmit_DMA();串口DMA模式发送
HAL_UART_Transmit_DMA();串口DMA模式接收
阻塞发送函数
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
功能:
串口发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT)。
参数:
huart: 指向uart _ handletypedef结构的huart指针,该结构包含指定uart模块的配置信息。
UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
PData: 指向数据缓冲区的pData指针(U8或u16数据元素)。
Size: 要发送的数据元素( u8或U16 )的大小
Timeout: 超时持续时间,单位ms,0就是0ms超时,数据最大发送时间,超过则返回异常
示例:HAL_UART_Transmit(&huart1,"lu shi jun \r\n",strlen("lu shi jun \r\n"),0xFFFF); //先串口一 发送一段字符串 时间是0xFFFF,
代码实现:
要求:使用阻塞模式发送一段数据
流程:在主函数之前,在写定义的特定区域定义一些数据;然后在主函数里面利用阻塞模式发送函数发送这段数据
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
uint8_t tx_buf[] = "hello lu shi jun \r\n ";
uint8_t rx_buf[4];
/* USER CODE END PD */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* 使用串口阻塞模式发送数据 */
HAL_Delay(500);
HAL_UART_Transmit(&huart1,tx_buf,sizeof(tx_buf),100);
}
阻塞接受函数
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
功能:
串口接收指定长度的数据。如果超时没接收完成,则不再接收,返回超时标志(HAL_TIMEOUT)。
huart: 指向uart _ handletypedef结构的huart指针,该结构包含指定uart模块的配置信息。
pData: 指向数据缓冲区的指针( u8或U16数据元素)。
Size: 要接收的数据元素数量( u8或U16 )。
Timeout: 超时持续时间,单位ms,0就是0ms超时,数据接收最大等待时间,超出则异常
示例:if(HAL_UART_Receive(&huart1,rx_buf,sizeof(rx_buf),1000)==HAL_OK){ }
代码实现:
要求:使用串口阻塞模式,将收到的数据再发出去
流程:在主函数之前,在写定义的特定区域定义一些数据;然后在主函数里面,利用阻塞模式接收函数先判断是否接收到数据,如果接收到了,就将接收到的数据发出
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
uint8_t tx_buf[] = "hello lu shi jun \r\n ";
uint8_t rx_buf[4];
/* USER CODE END PD */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* 使用串口阻塞模式接收数据,如果接收到数据就发送接收到的数据 */
/* 注意阻塞模式只能接收定长的数据,这个长度是之前设置的数组长度,注意只能接收定长! */
/* 我之前设置的数组大小为4,所以这里发送四个数据就可以返回相应的数据 */
/* 可以自己设置发送的数据,这里是发送接收的数据,也可以发送一段特殊数据 */
if(HAL_UART_Receive(&huart1,rx_buf,sizeof(rx_buf),1000) == HAL_OK)
{
HAL_UART_Transmit(&huart1,rx_buf,sizeof(rx_buf),100);
}
}
二、串口中断方式收发
STM32CubeMx 配置
基本配置都和上面 串口阻塞方式 配置一样
串口中断模式多配置一个中断模式,在NVIC Setting 勾选Enabled
如果更改优先级,可以在NVIC 界面进行修改
串口中断函数
HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中断处理函数
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //串口发送中断回调函数
HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); //串口发送一半中断回调函数(用的较少)
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //串口接收中断回调函数
HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回调函数(用的较少)
HAL_UART_ErrorCallback();//串口接收错误函数
中断发送函数
HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能: 串口中断发送,以中断方式发送指定长度数据。
参数:
UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
pData: 指向数据缓冲区的指针(u8或u16数据元素)。
Size: 需要接收的数据元素(u8或u16)的数量。
示例:HAL_UART_Transmit_IT(&huart1,tx_buf,sizeof(tx_buf));
中断接收函数
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能: 串口中断接收,以中断方式接收指定长度数据。
过程: 设置数据存放位置、接收数据长度,然后使能串口接收中断。接收到数据时,会触发串口中断。串口中断函数处理,直到接收到指定长度数据,而后关闭中断,进入中断接收回调函数,不再触发接收中断。(只触发一次中断)
参数:
UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
pData: 指向数据缓冲区的指针(u8或u16数据元素)。
Size: 需要接收的数据元素(u8或u16)的数量。
示例:HAL_UART_Receive_IT(&huart1,rx_buf,sizeof(rx_buf));
中断处理函数
HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
功能: 对接收到的数据进行判断和处理 判断是发送中断还是接收中断,然后进行数据的发送和接收,在中断服务函数中使用;如果接收数据,则会进行接收中断处理函数;如果发送数据,则会进行发送中断处理函数。
接收中断回调函数:
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
功能: HAL库的中断进行完之后,并不会直接退出,而是会进入中断回调函数中,用户可以在其中设置代码,串口中断接收完成之后,会进入该函数,该函数为空函数,用户需自行修改,
参数:
UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
举例: HAL_UART_RxCpltCallback(&huart1){ //自定义处理代码 }
中断代码实现:
要求:利用串口中断模式,将接收到的内容发送出去
流程:在函数特定位置定义要使用的数组;在main中调用接收中断函数;接收中断,接收完数据 进入中断回调函数;回调函数中要调用一次HAL_UART_Receive_IT函数,使得程序可以重新触发接收中断
函数流程图:
HAL_UART_Receive_IT(中断接收函数) -> USART2_IRQHandler(void)(中断服务函数) -> HAL_UART_IRQHandler(UART_HandleTypeDef *huart)(中断处理函数) -> UART_Receive_IT(UART_HandleTypeDef *huart) (接收函数) -> HAL_UART_RxCpltCallback(huart);(中断回调函数)
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
uint8_t tx_buf[] = "hello lu shi jun \r\n ";
uint8_t rx_buf[4];
/* USER CODE END PD */
/* USER CODE BEGIN 2 */
// 在main函数里面调用
HAL_UART_Receive_IT(&huart1,rx_buf,sizeof(rx_buf));
/* USER CODE END 2 */
/* USER CODE BEGIN 4 */
/* 中断模式主要在中断回调模式里面进行相关操作 */
/* 注意:在激活一次中断以后,就会关闭中断,如果要继续使用中断,需要手动开启中断模式 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1) //判断串口号
{
HAL_UART_Transmit_IT(&huart1,rx_buf,sizeof(rx_buf)); //发送
HAL_UART_Receive_IT(&huart1,rx_buf,sizeof(rx_buf)); //开启一次中断
}
}
/* USER CODE END 4 */
三、printf 重定向
说到串口,肯定为了调试程序,使用HAL_UART_Transmit发送字符串很不方便,经常会用printf()函数输出一些调试信息;
具体可以参考这篇博客:STM32 HAL库 使用printf函数 Use MicroLIB配置
如果使用到串口的时候,可以在usart.c里面进行串口重定向
//printf重定向代码,修改其底层fputc
#if 1
#include <stdio.h>
/* 告知连接器不从C库链接使用半主机的函数 */
#pragma import(__use_no_semihosting)
/* 定义 _sys_exit() 以避免使用半主机模式 */
void _sys_exit(int x)
{
x = x;
}
/* 标准库需要的支持类型 */
struct __FILE
{
int handle;
};
FILE __stdout;
/* */
int fputc(int ch, FILE *stream)
{
/* 堵塞判断串口是否发送完成 */
/* 不同芯片的串口标志位不一定相同! */
while((USART1->SR & 0X40) == 0);
/* 串口发送完成,将该字符发送 */
USART1->DR = (uint8_t) ch;
return ch;
}
#endif
示例:
/* 串口重定向,一般用printf()比较多 */
// printf("hello lu shi jun \r\n");
// HAL_Delay(500);