解决STM32G474单片机_HAL_UART_Transmit_IT()连续发送之问题

在使用STM32G474单片机的HAL库时,使用"在中断服务程序中发送数据"和"在中断程序中接收数据",是一种很常用的方法,特别是RS485通讯中。首次使用,肯定会踩坑。要么出现第一个数据收不到,要么出现连续发送,要么发送象是乱码,总之怪象连篇,搜索网络,发现能解决问题的成功案例很少。

1、串口初始化

//将PA9复用为USART1_TX
//将PA10复用为USART1_RX

UART_HandleTypeDef HardwareUSART1;

uint8_t USART1_RX_Data;

uint8_t USART1_RX_BufferUSART1_RX_Buffer_Size;
//USART1接收缓冲区数组

uint8_t USART1_RX_Buffer_Load_Index;
//USART1_RX_Buffer\[\]的装载索引值

uint8_t USART1_TX_BufferUSART1_TX_Buffer_Size;
//USART1发送缓冲区数组

uint8_t USART1_TX_Buffer_Load_Index;
//USART1_TX_Buffer\[\]的装载索引值

uint8_t *pUSART1_TX_Buffer;

void USART1_Init(uint32_t baudrate);
void print_USART1_Receive_Data(void);
void Start_USART1_Send_Data(void);

void USART1_Init(uint32_t baudrate)

{

HardwareUSART1.Instance = USART1;

HardwareUSART1.Init.BaudRate = baudrate; //波特率

HardwareUSART1.Init.WordLength = UART_WORDLENGTH_8B; //字长为8位数据格式

HardwareUSART1.Init.StopBits = UART_STOPBITS_1; //一个停止位

HardwareUSART1.Init.Parity = UART_PARITY_NONE; //无奇偶校验位

HardwareUSART1.Init.Mode = UART_MODE_TX_RX; //收发模式

HardwareUSART1.Init.HwFlowCtl = UART_HWCONTROL_NONE; //无硬件流控

HardwareUSART1.Init.OverSampling = UART_OVERSAMPLING_16; //过采样率

HardwareUSART1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;

HardwareUSART1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;

HardwareUSART1.AdvancedInit.AutoBaudRateEnable = UART_ADVFEATURE_AUTOBAUDRATE_DISABLE;

HardwareUSART1.AdvancedInit.AutoBaudRateMode = UART_ADVFEATURE_AUTOBAUDRATE_ONSTARTBIT;

HardwareUSART1.Init.ClockPrescaler = UART_PRESCALER_DIV1;

HardwareUSART1.FifoMode=UART_FIFOMODE_DISABLE;

if (HAL_UART_Init(&HardwareUSART1) != HAL_OK)

{

Error_Handler();

}

__HAL_UART_CLEAR_FLAG(&HardwareUSART1, UART_CLEAR_TCF);

//Transmission Complete Clear Flag

__HAL_UART_ENABLE_IT(&HardwareUSART1,UART_IT_RXNE);
//使能USART1接收到数据时产生中断
//"UART read data register" not empty interruption

__HAL_UART_DISABLE_IT(&HardwareUSART1, UART_IT_TXE);
//串口发送数据时,不使能"串口发送数据寄存器为空"产生中断(位TXE=0)
//Disable the UART Transmit Complete Interrupt

__HAL_UART_DISABLE_IT(&HardwareUSART1,UART_IT_TC);
//串口发送数据时,不使能"串口发送完成"产生中断(位TC=1)
// __HAL_UART_ENABLE_IT(&HardwareUSART1, UART_IT_TXE);
// __HAL_UART_ENABLE_IT(&HardwareUSART1,UART_IT_TC);
//使能USART1发送完成时产生中断

HAL_NVIC_EnableIRQ(USART1_IRQn);

HAL_NVIC_SetPriority(USART1_IRQn,8,0);
//设置NVIC中断分组4:4位抢占优先级,0位响应优先级
//选择中断优先级组4,即抢占优先级为4位,取值为0~15,响应优先级组为0位,取值为0

USART1_RX_Buffer_Load_Index=0;

}

//HAL_UART_Init()执行时,会先调用HAL_UART_MspInit()
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)

{

GPIO_InitTypeDef GPIO_InitStruct = {0};

RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

if(uartHandle->Instance==USART1)

{

PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;

PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;

if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)

{ //HAL_RCCEx_PeriphCLKConfig()初始化USART1外设时钟
//Initializes the peripherals clocks

Error_Handler();

}

__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1外设时钟

__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA外设时钟

GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;//选择引脚编号9和10

GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; //复用功能推挽模式

GPIO_InitStruct.Pull = GPIO_NOPULL; //不用上拉

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; //引脚速度为低速

GPIO_InitStruct.Alternate = GPIO_AF7_USART1; //将引脚复用为USART1

HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}

}

//HAL_UART_DeInit()执行时,会先调用HAL_UART_MspDeInit()
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)

{

if(uartHandle->Instance==USART1)

{

__HAL_RCC_USART1_CLK_DISABLE();//不使能USART1外设时钟

HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

}

}

2、启动发送

void Start_USART1_Send_Data(void)

{

char i;

uint16_t k;

for(i=0;i<USART1_RX_Buffer_Size;i++)//串口接送测试

{

if(USART1_RX_Bufferi-1=='\r'&&USART1_RX_Bufferi=='\n')//收到结束符号

{

USART1_RX_Buffer_Load_Index = 0;

memset(USART1_TX_Buffer,0,USART1_TX_Buffer_Size);

strcpy((char*)USART1_TX_Buffer,(char*)USART1_RX_Buffer);

memset(USART1_RX_Buffer,0,USART1_RX_Buffer_Size);

k=strlen((char*)USART1_TX_Buffer);

USART1_TX_Buffer_Load_Index = k;

__HAL_UART_ENABLE_IT(&HardwareUSART1,UART_IT_TC);

pUSART1_TX_Buffer=USART1_TX_Buffer;//启动发送

}

}

}

3、接收函数

//UART_RxISR_8BIT()会调用HAL_UART_RxCpltCallback()处理接收到的数据
//HAL_UART_Receive_IT()会调用UART_RxISR_8BIT()
//在中断中调用HAL_UART_Receive_IT()
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{

USART1_RX_BufferUSART1_RX_Buffer_Load_Index=USART1_RX_Data;

USART1_RX_Buffer_Load_Index++;

if(USART1_RX_Buffer_Load_Index>=USART1_RX_Buffer_Size-2)

{//USART1_RX_Buffer\[\]防止溢出

USART1_RX_BufferUSART1_RX_Buffer_Load_Index='\0';

USART1_RX_Buffer_Load_Index=0;

}

if(USART1_RX_BufferUSART1_RX_Buffer_Load_Index-2=='\r'&&USART1_RX_BufferUSART1_RX_Buffer_Load_Index-1=='\n')

{//收到"\r\n"结束符号

USART1_RX_BufferUSART1_RX_Buffer_Load_Index='\0';

USART1_RX_Buffer_Load_Index++;

}

}

4、接收中断函数

void USART1_IRQHandler(void)

{

HAL_UART_IRQHandler(&HardwareUSART1);

HAL_UART_Receive_IT(&HardwareUSART1, &USART1_RX_Data, sizeof(USART1_RX_Data));

//从USART1接收一个字节

HAL_UART_Transmit_IT(&HardwareUSART1, pUSART1_TX_Buffer, USART1_TX_Buffer_Load_Index);

pUSART1_TX_Buffer=NULL;//USART1_TX_Buffer\[\]被发送完成后,停止发送
// USART1_TX_Buffer_Load_Index=0;//USART1_TX_Buffer\[\]被发送完成后,停止发送

//实测,发现HAL_UART_Transmit_IT()会在中断中将**USART1_TX_Buffer\[\]**全部发完,才会退出HAL_UART_Transmit_IT();

}

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

在STM32G474的HAL库中,HAL_UART_Transmit_IT()会将所有数据发送完了,才会退出中断,具体HAL库是怎么做到的,不必浪费时间分析。若要防止HAL_UART_Transmit_IT()连续发送,要么将pData设置为NULL,要么将Size设置为0。否则,串口会不停往外发送数据,十分恼火。搞了好几天才搞好。

总结:HAL库是在没有办法的情况下,不得不用。让HAL_UART_Transmit_IT()停下来,有点耍流氓,还有没有更好的办法?

测试结果如下:

相关推荐
嵌入式阿蔡6 小时前
CAN总线入门:从协议帧到STM32实战
c语言·stm32·单片机·嵌入式硬件·嵌入式实时数据库
黑科技iOS上架6 小时前
Qwen3.8-27B日常量化版在 Mac M5 32G配置上表现如何
经验分享·ai编程
依然鸣6 小时前
PTA团体程序设计天梯赛L1真题讲解L1-085-088
数据结构·c++·经验分享·算法·深度优先·pat考试
恒锐丰科技韩生6 小时前
率能 SS8847E|2.7~15V 双通道 H 桥电机驱动 ETSSOP16 散热封装 家电 / 打印 / 舞台灯光专用
嵌入式硬件·硬件工程
离凌寒6 小时前
一、对stm32mp157配置freertos后,任务挂掉无法启动问题总结
stm32·单片机·嵌入式硬件
luj_17687 小时前
大航海时代:沉浸式财商实战沙盒
c语言·开发语言·网络·经验分享·算法
LuminousCPP8 小时前
数据结构基础篇(二):顺序表与链表全方位对比|从内存布局到 CPU 缓存理解底层差异
c语言·数据结构·经验分享·链表·缓存
Regentsoft丽晶软件9 小时前
3C数码行业条码相同、序列号不同——POS系统怎么用“一物一码”实现从入库到售后的全链路追踪?
经验分享·零售·b2b
Hello_Damon_Nikola9 小时前
CH573从入门到精通
c语言·开发语言·单片机·嵌入式硬件
oshan20129 小时前
华大小华HC32F460单片机在线烧录
单片机·嵌入式硬件