8.2 KiB
uart
实现重定向, printf
添加 usart.h usart.c
在调用的初始化函数 HAL_UART_Init 内部,会先调用 MSP 初始化回调函数进行 MCU 相关的初始化,函数为: void HAL_UART_MspInit(UART_HandleTypeDef *huart) 以上函数在 stm32f1xx_hal_msp.c 定义,无需重复定义 (CubeMX会自己定义) 操作过程中,只需要开启何种类型中断,关闭何种类型中断
HAL_NVIC_EnableIRQ(USART1_IRQn); //使能 USART1 中断通道 HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级 3,子优先级 3
__HAL_UART_ENABLE_IT(huart,UART_IT_RXNE); //开启接收完成中断 __HAL_UART_DISABLE_IT(huart,UART_IT_RXNE); //关闭接收完成中断
编写中断服务函数 USART1_IRQHandler 以上函数在 stm32f1xx_hal_it.c 定义, 其引用了实际的中断服务函数HAL_UART_IRQHandler(), 该函数非弱函数 用户可以注销 stm32f1xx_hal_it.c 中的定义,自己在自己定义的usart.c 中处理 如使用系统默认的, 需要在不同中端回调函数处理中断 如果不使用系统默认中断服务函数,需要自己定义
中断回调函数 例如: 接收完成回调 HAL_UART_RxCpltCallback() 该函数定义的时候是__weak ,可以直接重写,无需去原文件注销
开启中断 用__HAL_UART_ENABLE_IT()
printf
uart1 为例子 引入 stdio.h 使用printf处也需#include "stdio.h"
重写_write()函数,正对arm-gcc编译器
int _write(int fd, char *ch, int len)
{
HAL_UART_Transmit(&huart2, (uint8_t*)ch, len, 0xFFFF);
return len;
}
在mdk下的printf函数重定向:只需要重写fputc
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
调用
extern UART_HandleTypeDef huart2; MX_USART1_UART_Init(115200); MX_USART2_UART_Init(115200); MX_USART3_UART_Init(115200);
测试发送
注意后面要有 \n, 否则可能需要等到换行或缓冲区满后一起输出,不能及时看到数据 uint8_t Data[] = "HELLO WORLD !\n";
//参数1:使用的串口,2:要发送的数据,3:数据大小,4:发送的超时时间 HAL_UART_Transmit(&huart1,Data,sizeof(Data),1000);
测试接收
https://blog.csdn.net/qq_42652838/article/details/107776265?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-107776265-blog-120872001.235%5Ev36%5Epc_relevant_default_base3&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-107776265-blog-120872001.235%5Ev36%5Epc_relevant_default_base3&utm_relevant_index=1 中断类型及开启方法 开启中断 __HAL_UART_ENABLE_IT(huart,UART_IT_RXNE); //开启接收完成中断 __HAL_UART_DISABLE_IT(huart,UART_IT_RXNE); //关闭接收完成中断
在中断函数中直接判断
void USART3_IRQHandler(void){
uint8_t temp;
if(huart3.Instance->SR & UART_FLAG_RXNE){
Rx_uart3_signal=2;
temp=huart3.Instance->DR;
Rx_uart3_buf[Rx_uart3_cnt++]=temp;
}
else if(huart3.Instance->SR & UART_FLAG_IDLE){
temp=huart3.Instance->DR;
Rx_uart3_signal=0;
}
}
五种回调函数 void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);//发送完成回调函数 void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);//发送完成过半 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//接收完成回调函数 void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//接收完成过半 void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);//错误处理回调函数
main 函数实例
while(1)
{
if(USART_RX_STA&0x8000) // 接收区是否有数据
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("\r\n 您发送的消息为:\r\n");
HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000);
//发送接收到的数据
while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET); //等待发送结束
printf("\r\n\r\n");//插入换行
USART_RX_STA=0;
}else
{
times++;
if(times%5000==0)
{
printf("\r\nALIENTEK 战舰 STM32 开发板 串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n\r\n");
}
if(times%200==0)printf("请输入数据,以回车键结束\r\n");
if(times%30==0)LED0=!LED0;//闪烁 LED,提示系统正在运行.
delay_ms(10);
}
}
测试DMA
半满中断
接收发送
轮询模式要采用轮询模式接收, 中断模式要采用中断模式接收,DMA同理
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_UART_GetState(); 串口状态查询函数, 判断UART的接收是否结束,或者发送数据是否忙碌 代替方法 (&huart1)->gState == HAL_UART_STATE_READY
轮询
发送
// main
uint8_t Data[] = "HELLO WORLD !\n";
uint8_t Cmd[] = "\x23\x00\x00\x80\xA8\x00\x81\x01";
HAL_UART_Transmit(&huart2,(uint8_t*)Data,len,1000); // 需要超时参数
接收 -- 一般用于指定长度
// main
uint8_t Rxbuf[4] ={0};
while (1)
{
if ( HAL_UART_Receive(&huart2, Rxbuf, sizeof(Rxbuf), 1000) == HAL_OK)
{
HAL_UART_Transmit(&huart2, Rxbuf,sizeof(Rxbuf),100);
}
}
中断模式
main while循环之外 开启中断接收 HAL_UART_Receive_IT(&huart2, Rxbuf, sizeof(Rxbuf));
重写函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART2)
{
HAL_UART_Transmit_IT(&huart2, ACKbuf, sizeof(ACKbuf));
HAL_UART_Receive_IT(&huart2, Rxbuf, sizeof(Rxbuf)); // 重新使用串口接收中断
}
}
处理空闲中断
中断里面增加用户自定义中断处理 ****_it.h
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART1_IRQn 1 */
USER_UART_IRQHandler(&huart2); //新添加的函数,用来处理串口空闲中断
/* USER CODE END USART1_IRQn 1 */
}
自定义中断处理函数
void USER_UART_IRQHandler(UART_HandleTypeDef *huart)
{
if(USART2 == huart2.Instance) //判断是否是串口1
{
if(RESET != __HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) //判断是否是空闲中断
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2); //清楚空闲中断标志(否则会一直不断进入中断)
USER_UART_IDLECallback(huart); //调用中断处理函数
}
}
}
最终处理方法
void USAR_UART_IDLECallback(UART_HandleTypeDef *huart)
{
HAL_UART_DMAStop(&huart2); //停止本次DMA传输
uint8_t data_length = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx); //计算接收到的数据长度
HAL_UART_Transmit_DMA(&huart2,receive_buff,data_length); //DMA发送函数:将接收到的数据打印出去
data_length = 0;
}
DMA方式
HAL_UART_Transmit_DMA();串口DMA模式发送 HAL_UART_Transmit_DMA();串口DMA模式接收
HAL_UART_Receive_DMA(&huart2, Rxbuf, sizeof(Rxbuf)); // 不需要将接收函数放到 whilel里面,自动在后台干活 UART一旦开启DMA之后,DMA收发中断都是强制开启的,所以DMA收发函数也可以编写回调函数。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART2)
{
HAL_UART_Transmit_DMA(&huart2, ACKbuf, sizeof(ACKbuf));
// HAL_UART_Receive_IT(&huart2, Rxbuf, sizeof(Rxbuf)); // 重新使用串口接收中断
}
}
DMA 回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);//发送完成回调函数 void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);/发送一半回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//接收完成回调函数 void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//接收一半回调函数 void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);//传输出错回调函数