提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、CUBE-MX可视化配置
- 二、发送数据和接收数据
- 1.HAL库串口的阻塞模式和非阻塞模式
- 2.HAL库串口阻塞模式数据发送函数
- 3.HAL库串口阻塞模式数据接收函数
- 4.HAL库串口非阻塞模式数据发送函数
- 5.HAL库串口非阻塞模式数据接收函数
- 6.HAL库串口处理流程和标准库函数的处理流程对比
- 三、接收不定长数据以及DMA数据的发送和接收
- 四、printf重定向
前言
本文讲述了利用HAL库进行串口数据的发送和接收
提示:以下是本篇文章正文内容,下面案例可供参考
一、CUBE-MX可视化配置
二、发送数据和接收数据
1.HAL库串口的阻塞模式和非阻塞模式
阻塞模式:
在阻塞模式下,发送和接收函数会一直等待直到数据发送或接收完成。在HAL库中,一些典型的阻塞模式的串口操作函数有:
发送数据:HAL_UART_Transmit 或 HAL_UART_Transmit_DMA。
接收数据:HAL_UART_Receive 或 HAL_UART_Receive_DMA。
这些函数会一直阻塞程序的执行,直到数据完全发送或接收
非阻塞模式:
在非阻塞模式下,发送和接收函数是非阻塞的,它们会立即返回,允许程序在等待数据传输的同时执行其他任务。在HAL库中,使用的是带有IT(中断)或DMA(直接存储器访问)的函数。
发送数据:HAL_UART_Transmit_IT、HAL_UART_Transmit_DMA等。
接收数据:HAL_UART_Receive_IT、HAL_UART_Receive_DMA等。
两种方式的优缺点:
阻塞模式简单直观,但可能导致程序在等待数据时无法执行其他任务。非阻塞模式可以提高系统的响应性,但需要更复杂的程序设计,以处理异步的数据传输。
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);//非阻塞方式发送数据
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
2.HAL库串口阻塞模式数据发送函数
实现了每隔500ms向串口发送一个字符串hello world
HAL_UART_Transmit函数解析
函数:HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
入口参数:UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1,注意的是传入的是地址例如:&huart1
*pData 需要发送的数据
Size 发送的字节数
Timeout 最大发送时间,发送数据超过该时间退出发送
返回值: HAL_OK = 0x00U,
HAL_ERROR = 0x01U,
HAL_BUSY = 0x02U,
HAL_TIMEOUT = 0x03U
功能:发送一字节数据
3.HAL库串口阻塞模式数据接收函数
实现了将串口发送的数据进行接收,存放到receiveData中,并利用HAL_UART_Transmit()将接收到的数据发送到串口
HAL_UART_Receive函数解析
函数:HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
参数:同HAL_UART_Transmit()函数一样,这个函数也有四个参数
第一个参数是要使用的串口句柄地址,比如要使用U(S)ART1,参数就设置为U(S)ART1的句柄地址&huart1
第二个参数是接受数据的缓冲区首地址
第三个参数是接受的数据长度,这里可以直接用sizeof()函数获取接受缓冲区的长度
第四个参数是超时时间,单位是ms,如果超过设置的时间,则函数返回HAL_TIMEOUT,如果设置为HAL_MAX_DELAY,处理器就会一直等到接受到设置好的数据数量再执行下一条语句。
返回值: HAL_OK = 0x00U,
HAL_ERROR = 0x01U,
HAL_BUSY = 0x02U,
HAL_TIMEOUT = 0x03U
功能:接收数据
4.HAL库串口非阻塞模式数据发送函数
首先要使能中断
函数:HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
参数:前三个参数和阻塞方式完全一致,为什么没有超时时间了呢?因为中断(IT)方式配置完成寄存器之后不需要再占用CPU,会在接受完成后触发中断。
5.HAL库串口非阻塞模式数据接收函数
重写HAL_UART_RxCpltCallback回调函数,接收数据完成后将数据以非阻塞方式进行发送
实现效果,注意再发送数据后想要再次接收数据要再次使用HAL_UART_Receive_IT(&huart1, receiveData, 2);
6.HAL库串口处理流程和标准库函数的处理流程对比
MX_USART1_UART_Init(); 对应 USART_Config();NVIC_Configuration();
HAL_UART_Init(&huart1) 对应 USART_Init(DEBUG_USARTx, &USART_InitStructure)
HAL_UART_Receive_IT(&huart1, (uint8_t *)Buffer, 1); 对应 USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
在看HAL库代码时疑问点是在开启HAL_UART_Receive_IT()非阻塞接收数据,具体实现的源代码在哪里?
首先在使用标准库时,一般直接在某个串口的中断服务函数中利用USART_ReceiveData整个库函数来接收数据。
void USART1_IRQHandler(void)
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
Res =USART_ReceiveData(USART1);
}
}
而在HAL库中开发者不用再写f(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)判断寄存器的状态了,因为HAL_UART_IRQHandler(&huart1);这个函数已经帮你写好了。
点进HAL_UART_IRQHandler(&huart1);这里面HAL库已经帮你写了判断寄存器值的函数这里相当于if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
点进去UART_Receive_IT这里面实现的功能和标准库函数中的USART_ReceiveData实现功能是一样的
这里是标准库函数中的USART_ReceiveData(),经过比较可以看出HAL库将底层对寄存器的操作进行了封装因此用户可以更加方便的使用HAL库函数
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
{
/* Check the parameters */
assert_param(IS_USART_ALL_PERIPH(USARTx));
/* Receive Data */
return (uint16_t)(USARTx->DR & (uint16_t)0x01FF);
}
三、接收不定长数据以及DMA数据的发送和接收
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
/* Prevent unused argument(s) compilation warning */
if(huart==&huart1)
{
// HAL_UART_Transmit_DMA(&huart1, receiveData, Size);
// HAL_UARTEx_ReceiveToIdle_DMA(&huart1, receiveData, sizeof(receiveData));
HAL_UART_Transmit_IT(&huart1, receiveData, Size);
HAL_UARTEx_ReceiveToIdle_IT(&huart1, receiveData, sizeof(receiveData));
}
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_UARTEx_RxEventCallback can be implemented in the user file.
*/
}
利用DMA进行数据的传输
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
/* Prevent unused argument(s) compilation warning */
if(huart==&huart1)
{
HAL_UART_Transmit_DMA(&huart1, receiveData, Size);
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, receiveData, sizeof(receiveData));
//HAL_UART_Transmit_IT(&huart1, receiveData, Size);
//HAL_UARTEx_ReceiveToIdle_IT(&huart1, receiveData, sizeof(receiveData));
}
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_UARTEx_RxEventCallback can be implemented in the user file.
*/
}
四、printf重定向
1.在main函数前面加上以下代码
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
//STM32cubeIDE下
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1,0xFFFF);
return ch;
}
//keil下 //没在keil下尝试过 一直使用软件stm32cubeIDE没有取keil验证
//UART_HandleTypeDef huart1; //UART句柄
int fputc(int ch,FILE *f)
{
HAL_UART_Transmit(&huart1,(uint8_t*)&ch,1,0xffff);
return ch;
}
2.在CUBEIDE中按照图中所示修改
实现效果