版本:2022.03.08 Ver1.0.0
版本:2022.03.16 Ver1.1.0 完成查询模式函数部分
版本:2022.03.17 Ver1.2.0 完成中断模式函数部分
版本:2022.03.18 Ver1.3.0 完成DMA模式函数部分
对于STM32的调试来说,通过串口打印数据往往是一个不错的选择。
那么,就让小编我们来编写一个初步的串口通信。
博主使用的是自研制的STM32L431RCT6试作型板。
其中板载了CH340G模块,可以直接通过type-c口连接PC USB来进行通信。
如果核心板或开发板中没有CH340G/TTL转USB模块,需要备一个CH340G /TTL转USB模块。
一般USART1 板件都是 PA9发射端 PA10接收端
与串口模块的连接示意图:
这步跳过吧
STM32的USART1都是PA9发射端 PA10接收端
除非有特殊处理或者说明
配置USART1
配置 外部晶振 、下载方式 与 时钟
具体参考STM32集成开发环境 STM32CubeIDE 安装与配置指南
生成代码
首先建立USART.h 和 USART.h
具体参考STM32CubeIDE开发笔记 MK.II - ST-LINK调试 与 建立用户驱动库
使用Keil可以参考以下文章的重定向方法
在勾选Keil的Use MicroLib选项后即可
STM32的printf函数重定向
STM32 Uart及其配置
【STM32】HAL库 STM32CubeMX教程四—UART串口通信详解
由于GCC中没有MicroLib,不能对fputc()进行重定向
在参考以下文章后,得知需要对__write()进行重定向
重定向printf函数到串口输出的多种方法
【STM32Cube笔记】10-异步串口收发USART
首先引入huart1
extern UART_HandleTypeDef huart1; //引用main.c中的串口声明
再进行重定向
//
//CHANNEL A 重定向printf法
//
extern UART_HandleTypeDef huart1; //引用main.c中的串口声明
//重定向 printf
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&huart1,(uint8_t *)ptr,len, 0x10);
return len;
}
int _read(int file, char *ptr, int len)
{
HAL_UART_Receive(&huart1,(uint8_t *)ptr,len,0x10);
return len;
}
//
//CHANNEL A
//
在main中添加输出测试
#define user_buffersize 24
char user_buffer[user_buffersize]="";
//channel 1/
printf("\r\n");
printf("NND\r\n");
sprintf(user_buffer,"%s\r\n","WSM?");
printf("%s",user_buffer);
memset(user_buffer,0,user_buffersize);
scanf("%s",user_buffer);
printf("%s",user_buffer);
//channel 1/
这边建议别用这种方法,经测试printf可正常使用,scanf有点问题哈,用起来有点不对劲(读不到直接跳)(直接瞎读) 或者直接卡死(keil里面用MicroLib就很正常),如果只使用串口输出是一点问题都没有的。建议回去转KEIL
但是与其重定向这么麻烦,各个编译器之间还不同……我为什么不在HAL库基础上自己写一个通用呢???在前面浪费了那么多时间的我怕不是个傻子吧???
第二种方法不采用重定义,在USART.c/.h内编写相应改版换皮函数
首先我们来说件事
那个……先加个这个函数
void UART_FlagClear(UART_HandleTypeDef *huart_num)
{
//解决串口错误 恢复置位 清除flag
__HAL_UART_CLEAR_FLAG(huart_num, UART_FLAG_PE);
__HAL_UART_CLEAR_FLAG(huart_num, UART_FLAG_FE);
__HAL_UART_CLEAR_FLAG(huart_num, UART_FLAG_NE);
__HAL_UART_CLEAR_FLAG(huart_num, UART_FLAG_ORE);
}
事情的起因是这样的,按照普通的串口HAL_UART_Transmit和HAL_UART_Receive,在使用串口助手调试的时候,在Transmit未结束前,向mcu发送数据时,会卡死!会卡死!会卡死!这个问题困了我三天我是真的吐了。原因是这样做会使串口进入错误回馈函数,并且把相关标志位置位,这之后无论往串口塞什么数据,全都没反应!全都没反应!全都没反应!
这种情况下,就等超时结束。但是如果你把 Transmit或Receive 塞进了while里面……抱歉,你可能出不来了,会一直卡在里面。
感谢这位大佬,成功解决上述问题,即清除标志位。
STM32 HAL_UART_Receive HAL库串口接受清空错误标志
void USART_WriteData(UART_HandleTypeDef *huart_num, const uint8_t *wData)
{
int len = strlen((const char *)wData);
while(HAL_UART_Transmit(huart_num,wData,len, 0x10)!= HAL_OK)
{
//解决串口错误 恢复置位 清除flag
UART_FlagClear(huart_num);
}
}
void USART_ReadData(UART_HandleTypeDef *huart_num,uint8_t *rData, uint16_t len)
{
USART_WriteData(huart_num,(uint8_t *)"Ready to receive data!\r\n",1);
while(HAL_UART_Receive(huart_num,rData,len,0x10)!= HAL_OK)
{
//解决串口错误 恢复置位 清除flag
UART_FlagClear(huart_num);
//清空接收数组 重新接收
memset(rData,0,len);
}
}
之后在usart.c中添加下头文件(stdio.h,stm32l4xx_hal.h,string.h),在usart.h中声明下这两个函数
#define user_buffersize 24
uint8_t RXdata_size;
char user_buffer[user_buffersize]="";
//channel 2/
memset(user_buffer,0,user_buffersize);
sprintf(user_buffer,"\r\nStr:%s\r\n"," Test is ready!");
USART_WriteData(&huart1,(uint8_t *)user_buffer,1);
memset(user_buffer,0,user_buffersize);
sprintf(user_buffer,"%s\r\n","Ready to Receive!");
USART_WriteData(&huart1,(uint8_t *)user_buffer,1);
RXdata_size=12;
memset(user_buffer,0,user_buffersize);
USART_ReadData(&huart1,(uint8_t *)user_buffer,RXdata_size,1);
USART_WriteData(&huart1,(uint8_t *)user_buffer,1);
//channel 2/
正常收发的情况是这样的 唯一的缺点可能就是指定长度接收吧 = =
经压力测试,串口不会卡死
//user_buffersize 在main.h中 #define user_buffersize 26
extern char user_buffer[user_buffersize];
#define IT_MODE 0X01
uint8_t USART_TX_MODE;
uint8_t USART_RX_MODE;
void UART_IT_MODE(UART_HandleTypeDef *huart_num)
{
USART_RX_MODE=IT_MODE;
USART_TX_MODE=IT_MODE;
HAL_UART_Receive_IT(huart_num, (uint8_t *)user_buffer,user_buffersize);
HAL_UART_Transmit_IT(huart_num, (uint8_t *)"USART_IT_Mode_Ready!\r\n",22);
memset(user_buffer,0,user_buffersize);
}
HAL_UART_RxCpltCallback在hal库中被弱定义,在USART.c中编写这个函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
char IT_RecTMPbuffer[user_buffersize]="";
if(huart->Instance == USART1)
{
if(USART_RX_MODE== IT_MODE)
{
//IT RX-CALLBACK
strcpy(IT_RecTMPbuffer,user_buffer);
HAL_UART_Transmit_IT(huart, (uint8_t *)IT_RecTMPbuffer,user_buffersize);
//IT RX-CALLBACK
memset(user_buffer,0,sizeof(user_buffer));
//再次使能中断
HAL_UART_Receive_IT(huart, (uint8_t *)user_buffer,user_buffersize);
}
}
}
这里的IT_RecTMPbuffer是为了传递接受的user_buffer数据,随后清空user_buffer数据并使能下一次接收中断。
其实这个有没有都可以,一般来说
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
if(USART_RX_MODE== IT_MODE)
{
//IT RX-CALLBACK
HAL_UART_Transmit_IT(huart, (uint8_t *)user_buffer,user_buffersize);
//IT RX-CALLBACK
//再次使能中断
HAL_UART_Receive_IT(huart, (uint8_t *)user_buffer,user_buffersize);
}
}
}
这样已经足够
//channel 2B
UART_IT_MODE(&huart1);
//channel 2B
while(1)
{
}
//DMA//
void USART_WriteData_DMA(UART_HandleTypeDef *huart_num, const uint8_t *wData)
{
USART_RX_MODE=DMA_MODE;
HAL_UART_Transmit_DMA(huart_num,wData, strlen((const char *)wData));
return;
}
void USART_ReadData_DMA(UART_HandleTypeDef *huart_num,uint8_t *rData,uint8_t len)
{
HAL_UART_Receive_DMA(huart_num,rData,len);
return;
}
#define DMA_MODE 0X02
uint8_t USART_TX_MODE;
uint8_t USART_RX_MODE;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
char IT_RecTMPbuffer[user_buffersize]="";
if(huart->Instance == USART1)
{
if(USART_RX_MODE == DMA_MODE)
{
//DMA RX-CALLBACK
//DMA RX-CALLBACK
//回显接收内容
USART_WriteData_DMA(huart,(uint8_t*)user_buffer);
}
}
}
//channel 2C
strcpy(user_buffer,"DMA_Trans_Ready!\r\n");
USART_WriteData_DMA(&huart1,(uint8_t*)user_buffer);
USART_ReadData_DMA(&huart1,(uint8_t*)user_buffer,user_buffersize);
//channel 2C
while(1)
{
osDelay(100);
}
注意,完整部分与上面部分有所不同,不可无脑直接套入
/*
* USART.h
*
* Created on: Mar 8, 2022
* Author: KeeganKei
*/
#ifndef INC_USART_H_
#define INC_USART_H_
#define IT_MODE 0X01
#define DMA_MODE 0X02
//重定向
int _write(int file, char *ptr, int len);
int _read(int file, char *ptr, int len);
void UART_FlagClear(UART_HandleTypeDef *huart_num);
//EX 函数
void USART_WriteData(UART_HandleTypeDef *huart_num, const uint8_t *wData);
void USART_ReadData(UART_HandleTypeDef *huart_num,uint8_t *rData,uint8_t len);
void UART_IT_MODE(UART_HandleTypeDef *huart_num);
void USART_WriteData_DMA(UART_HandleTypeDef *huart_num, const uint8_t *wData);
void USART_ReadData_DMA(UART_HandleTypeDef *huart_num,uint8_t *rData,uint8_t len);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
#endif /* INC_USART_H_ */
/*
* USART.c
*
* Created on: Mar 8, 2022
* Author: KeeganKei
*/
#include
#include
#include //import HAL_UART_Transmit
#include
#include
//
// //CHANNEL A 重定向printf法
//
//extern UART_HandleTypeDef huart1; //引用main.c中的串口声明
//
// //重定向 printf
//int _write(int file, char *ptr, int len)
//{
// HAL_UART_Transmit(&huart1,(uint8_t *)ptr,len, 0x0A);
// return len;
//}
//
//int _read(int file, char *ptr, int len)
//{
// HAL_UART_Receive(&huart1,(uint8_t *)ptr,len,0x0A);
// return len;
//}
//
// //CHANNEL A
//
//
// //CHANNEL B
//
extern char User_Tx_Buffer[User_TX_BufferSize];
extern char User_Rx_Buffer[User_RX_BufferSize];
uint8_t USART_TX_MODE;
uint8_t USART_RX_MODE;
// FLAG_CLEAR
void UART_FlagClear(UART_HandleTypeDef *huart_num)
{
//解决串口错误 恢复置位 清除flag
__HAL_UART_CLEAR_FLAG(huart_num, UART_FLAG_PE);
__HAL_UART_CLEAR_FLAG(huart_num, UART_FLAG_FE);
__HAL_UART_CLEAR_FLAG(huart_num, UART_FLAG_NE);
__HAL_UART_CLEAR_FLAG(huart_num, UART_FLAG_ORE);
}
// //
// //
// //NORMAL//
void USART_WriteData(UART_HandleTypeDef *huart_num, const uint8_t *wData)
{
while(HAL_UART_Transmit(huart_num,wData,strlen((const char *)wData), 0x10)!= HAL_OK)
{
//解决串口错误 恢复置位 清除flag
UART_FlagClear(huart_num);
}
return;
}
void USART_ReadData(UART_HandleTypeDef *huart_num,uint8_t *rData, uint8_t len)
{
while(HAL_UART_Receive(huart_num,rData,len,0x10)!= HAL_OK)
{
//解决串口错误 恢复置位 清除flag
UART_FlagClear(huart_num);
//清空接收数组 重新接收
memset(rData,0,len);
}
return;
}
// IT
void UART_IT_MODE(UART_HandleTypeDef *huart_num)
{
USART_RX_MODE=IT_MODE;
USART_TX_MODE=IT_MODE;
HAL_UART_Receive_IT(huart_num, (uint8_t *)User_Rx_Buffer,User_RX_BufferSize);
HAL_UART_Transmit_IT(huart_num, (uint8_t *)"USART_IT_Mode_Ready!\r\n",22);
memset(User_Rx_Buffer,0,User_RX_BufferSize);
}
// //DMA//
void USART_WriteData_DMA(UART_HandleTypeDef *huart_num, const uint8_t *wData)
{
USART_TX_MODE=DMA_MODE;
HAL_UART_Transmit_DMA(huart_num,wData, strlen((const char *)wData));
return;
}
void USART_ReadData_DMA(UART_HandleTypeDef *huart_num,uint8_t *rData,uint8_t len)
{
USART_RX_MODE=DMA_MODE;
HAL_UART_Receive_DMA(huart_num,rData,len);
return;
}
// //
// //
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
if(USART_TX_MODE == IT_MODE)
{
// //IT TX-CALLBACK
// //IT TX-CALLBACK
}
else if(USART_TX_MODE == DMA_MODE)
{
// //DMA TX-CALLBACK
// //DMA TX-CALLBACK
}
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
char IT_RecTMPbuffer[User_RX_BufferSize]="";
if(huart->Instance == USART1)
{
if(USART_RX_MODE == IT_MODE)
{
// //IT RX-CALLBACK
//回显接收数据,有小bug
strcpy(IT_RecTMPbuffer,User_Rx_Buffer);
HAL_UART_Transmit_IT(huart, (uint8_t *)IT_RecTMPbuffer,User_RX_BufferSize);
// //IT RX-CALLBACK
memset(User_Rx_Buffer,0,User_RX_BufferSize);
HAL_UART_Receive_IT(huart, (uint8_t *)User_Rx_Buffer,User_RX_BufferSize);
}
else if(USART_RX_MODE == DMA_MODE)
{
// //DMA RX-CALLBACK
// //DMA RX-CALLBACK
//回显接收数据
USART_WriteData_DMA(huart,(uint8_t*)User_Rx_Buffer);
}
}
}
//
// //CHANNEL B
//
// 24 char +\r\n = 26
#define User_TX_BufferSize 26
#define User_RX_BufferSize 26
PART.A
#include
#include
#include
char User_Tx_Buffer[User_TX_BufferSize]="";
char User_Rx_Buffer[User_RX_BufferSize]="";
extern uint8_t USART_TX_MODE;
extern uint8_t USART_RX_MODE;
PART.B
测试时CHANNEL 1/2A/2B/2C 选择测试时注释掉其他三大块的内容
// ///channel 1/
//不推荐
// memset(User_Tx_Buffer,0,User_TX_BufferSize);
// printf("\r\n");
// printf("NND\r\n");
// sprintf(User_Tx_Buffer,"%s\r\n","WSM?");
// printf("%s",User_Tx_Buffer);
//
// memset(User_Rx_Buffer,0,User_RX_BufferSize);
// scanf("%s",User_Rx_Buffer);
// printf("%s",User_Rx_Buffer);
// ///channel 1/
// /channel 2A
//新手推荐,且足够一般情况使用
// memset(User_Tx_Buffer,0,User_TX_BufferSize);
// sprintf(User_Tx_Buffer,"\r\nStr:%s\r\n"," Test is ready!");
// USART_WriteData(&huart1,(uint8_t *)User_Tx_Buffer);
//
// memset(User_Tx_Buffer,0,User_TX_BufferSize);
// sprintf(User_Tx_Buffer,"%s\r\n","Ready to Receive!");
// USART_WriteData(&huart1,(uint8_t *)User_Tx_Buffer);
//
// uint8_t RXdata_size = 12;
// memset(User_Rx_Buffer,0,User_RX_BufferSize);
// USART_ReadData(&huart1,(uint8_t *)User_Rx_Buffer,RXdata_size);
// USART_WriteData(&huart1,(uint8_t *)User_Rx_Buffer);
// /channel 2A
// /channel 2B
//有小bug
// UART_IT_MODE(&huart1);
// while(1)
// {
// osDelay(100);
// }
// /channel 2B
// /channel 2C
//按指定长度输送不会有Bug
// USART_TX_MODE=DMA_MODE;
// USART_RX_MODE=DMA_MODE;
//
// memset(User_Tx_Buffer,0,User_TX_BufferSize);
// strcpy(User_Tx_Buffer,"DMA_Trans_Ready!\r\n");
//
// USART_WriteData_DMA(&huart1,(uint8_t*)User_Tx_Buffer);
// USART_ReadData_DMA(&huart1,(uint8_t*)User_Rx_Buffer,User_RX_BufferSize);
// while(1)
// {
// osDelay(100);
// }
// /channel 2C
存在部分细微BUG = =,待施工完毕
参考STM32CubeIDE开发笔记 MK.II - ST-LINK调试 与 建立用户驱动库进行debug与调试
搞着搞着,开始怀疑我到底会不会串口了= =
这部分类型转换的锅比较多 嗯。。。
测试了几天总算完毕了
那…就这样吧