本帖最后由 liuyuliuyuliuyu 于 2015-12-22 10:11 编辑
开发环境:MDK5.14 和STM32CubeMX4.11。
在上一个帖子STM32CubeMX按键控制流水灯(https://bbs.21ic.com/forum.html?mo ... peid%26typeid%3D350)的基础上,继续学习串口通信。
使用STM32CubeMX配置串口通信,需要用到HAL库。HAL库中实现串口通信有三种方式:轮询、中断和DMA。和串口通信相关的初始化部分通过STM32CubeMX软件配置,具体过程在下面的例子中说明。 轮询模式:为堵塞模式,使用超时管理机制。 发送函数: HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
参数: huart: 指向串口结构体(通过STM32CubeMX软件配置串口时会生成,包含串口通信相关的信息)的指针。 pData: 指向发送数据块的指针 Size: 发送数据的数量 Timeout: 超时周期 返回值:HAL status:HAL_OK ,HAL_ERROR,HAL_BUSY ,HAL_TIMEOUT 接收函数: HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
参数: huart: 指向串口结构体的指针。 pData: 指向接收数据块的指针 Size: 接收数据的数量 Timeout: 超时周期 返回值:HAL status,HAL_OK ,HAL_ERROR,HAL_BUSY ,HAL_TIMEOUT 必须在指定的时间内接收到指定数量的数据才会返回HAL_OK。 中断模式: 非堵塞模式。和UART相关的中断:发生完成中断,结束中断和错误中断。 发送函数: 用于开启中断发送 HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
参数: huart: 指向串口结构体的指针。 pData: 指向发送数据块的指针 Size: 发送数据的数量 返回值:HAL status:HAL_OK ,HAL_ERROR,HAL_BUSY 接收函数:用于开启中断接收 HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
参数: huart: 指向串口结构体的指针。 pData: 指向接收数据块的指针 Size: 接收数据的数量 返回值:HAL status,HAL_OK ,HAL_ERROR,HAL_BUSY ,HAL_TIMEOUT 必须接收到指定数量的数据才会触发中断。 相关的回调函数: HAL_UART_TxCpltCallback():发送完成后,通过中断处理函数调用。 HAL_UART_RxCpltCallback():接收完成后,通过中断处理函数调用。 HAL_UART_ErrorCallback():传输过程中出现错误时,通过中断处理函数调用。 可以在回调函数里定制自己的代码。 DMA模式:非堵塞模式。 发送函数: 用于开启DMA发送 HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
参数: huart: 指向串口结构体的指针。 pData: 指向发送数据块的指针 Size: 发送数据的数量 返回值:HAL status,HAL_OK ,HAL_ERROR,HAL_BUSY 接收函数:用于开启DMA接收 HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
参数: huart: 指向串口结构体的指针。 pData: 指向接收数据块的指针 Size: 接收数据的数量 返回值:HAL status,HAL_OK ,HAL_ERROR,HAL_BUSY ,HAL_TIMEOUT 必须接收到指定数量的数据才会完成一次DMA传输。 相关的回调函数(使用回调函数需要开启串口中断): HAL_UART_TxHalfCpltCallback():一半数据(half transfer)发送完成后,通过中断处理函数调用。 HAL_UART_TxCpltCallback():发送完成后,通过中断处理函数调用。 HAL_UART_RxHalfCpltCallback():一半数据(half transfer)接收完成后,通过中断处理函数调用。 HAL_UART_RxCpltCallback():接收完成后,通过中断处理函数调用。 HAL_UART_ErrorCallback():传输过程中出现错误时,通过中断处理函数调用。 可以在回调函数里定制自己的代码。 暂停DMA传输: HAL_StatusTypeDef HAL_UART_DMAPause(UART_HandleTypeDef *huart)
恢复DMA传输: HAL_StatusTypeDef HAL_UART_DMAResume(UART_HandleTypeDef *huart)
停止DMA传输: HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)
参数:huart: 指向串口结构体的指针。 返回值:HAL status,HAL_OK
下面通过简单的例子说明: 发板的USART3使用的是PB10和PB11引脚,但是使能USART3的时候默认引脚是PC10和PC11。因此需要先配置PB10和PB11引脚,在使能USART。相关的电路图如下:
轮询模式: 在STM32CubeMX软件中,分别配置PB10和PB11为串口收发,接着使能USART3,最后配置串口通信相关的参数: 生成MDK工程并打开,在main.c文件中,定义全局变量用于收发: uint8_t aTxBuffer[] = "** UART__ComPolling ** \r\n";
uint8_t aRxBuffer[32];
在主函数的while循环之前,发送一个字符串,如果发送失败,点亮LED: HAL_GPIO_WritePin(GPIOG,GPIO_PIN_6,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_4,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_5,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOK,GPIO_PIN_3,GPIO_PIN_SET);
if(HAL_UART_Transmit(&huart3,(uint8_t *)aTxBuffer,32,5000)!= HAL_OK)
{
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_4,GPIO_PIN_RESET);
}
在while循环中,等待接收到指定数量的字符,然后将接收的字符发送出去,超时时间设置为1秒(单位为ms): while(HAL_UART_Receive(&huart3, (uint8_t *)aRxBuffer, 5, 1000) != HAL_OK)
{
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_5);
}
if(HAL_UART_Transmit(&huart3,(uint8_t *)aRxBuffer,5,1000) == HAL_OK)
{
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_6);
}
HAL_Delay(1000);
编译下载,使用串口调试助手,调试过程如下: 中断模式: 在上面配置的基础上,使能串口中断,并配置优先级,也可以改变优先级分组: 在生成的MDK工程中,定义全局变量用于收发: uint8_t aTxBuffer[] = "** UART__IT ** \r\n";
uint8_t aRxBuffer[32];
uint8_t Rx_flag = 0;
通过Rx_flag控制发送接收的数据。 在主函数的while循环之前开启发送和接收中断:HAL_GPIO_WritePin(GPIOG,GPIO_PIN_6,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_4,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_5,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOK,GPIO_PIN_3,GPIO_PIN_SET);
HAL_UART_Transmit_IT(&huart3,(uint8_t *)aTxBuffer,32);
HAL_UART_Receive_IT(&huart3,(uint8_t *)aRxBuffer,12);
在while循环中将接收的数据发送出去: if(Rx_flag == 1)
{
HAL_UART_Transmit_IT(&huart3,(uint8_t *)aRxBuffer,32);
Rx_flag = 0;
}
在usart.c文件中声明外部变量: extern uint8_t aRxBuffer[32];
extern uint8_t Rx_flag;
加入收发回调函数,在接收回调函数里将Rx_flag置1控制发送,并在此开始中断接收: void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle)
{
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_4);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
Rx_flag = 1;
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_5);
HAL_UART_Receive_IT(&huart3,(uint8_t *)aRxBuffer,12);
}
编译下载,使用串口调试助手,调试过程如下: DMA模式: 在上面配置的基础上,使能DMA,并为发送和接收选择DMA通道: 在生成的MDK工程中定义如下全局变量用于收发:uint8_t aTxBuffer[] = "** UART__DMA ** \r\n";
uint8_t aRxBuffer[32];
uint8_t Rx_flag = 0;
通过Rx_flag控制发送接收的数据。在主函数的while循环之前开启DMA发送和接收:
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_6,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_4,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_5,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOK,GPIO_PIN_3,GPIO_PIN_SET);
HAL_UART_Transmit_DMA(&huart3,(uint8_t *)aTxBuffer,32);
HAL_UART_Receive_DMA(&huart3,(uint8_t *)aRxBuffer,12);
在while循环中将接收的数据发送出去:
if(Rx_flag == 1)
{
HAL_UART_Transmit_DMA(&huart3,(uint8_t *)aRxBuffer,32);
Rx_flag = 0;
}
if(Rx_flag == 2)
{
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_6);
Rx_flag = 0;
}
在usart.c文件中声明外部变量:
extern uint8_t aRxBuffer[32];
extern uint8_t Rx_flag;
加入收发回调函数,在接收回调函数里将Rx_flag置1控制发送,并在此开始中断接收,通过控制LED简单的验证发送和接收一半数据的回调函数:
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle)
{
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_4);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
Rx_flag = 1;
HAL_UART_Receive_DMA(&huart3,(uint8_t *)aRxBuffer,12);
}
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{
HAL_GPIO_TogglePin(GPIOK,GPIO_PIN_3);
}
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)
{
Rx_flag = 2;
}
编译下载,使用串口调试助手,调试过程如下:
最后,关于串口重定向可以参考一下代码,需要包含stdio.h,可以替换成中断或DMA模式,这样就可以使用printf和scanf函数:
int fputc(int ch,FILE *fp)
{
HAL_UART_Transmit(&huart3,(uint8_t *)&ch,1,5000);
return ch;
}
int fgetc(FILE *fp)
{
uint8_t ch;
HAL_UART_Receive(&huart3,(uint8_t *)ch,1,5000);
return ch;
}
|