硬汉嵌入式论坛

 找回密码
 立即注册
查看: 311|回复: 1
收起左侧

[技术讨论] 关于bsp_uart_fifo.c文件中的串口中断函数优化

[复制链接]

7

主题

45

回帖

66

积分

初级会员

积分
66
QQ
发表于 2026-1-24 14:11:19 | 显示全部楼层 |阅读模式
原来的中断接收函数,实际使用时使能了接收中断,发送是采用租塞形式的发送方式,调试运行的时候,频繁的进入串口中断,尽管实际上没有数据接收,和发送,以至使得程序不能正常执行,在ai的帮组下,做了优化,优化的代码如下:
1. 增加了发送标志清零操作;
2. 接收中断,发送缓冲区空中断、发送传输完成中断,三者的顺序做了一个结构性的优化,3者并行,且发送传输完成中断放在发送缓冲区空中断前面;
3. 增加了溢出中断处理;
4.一次性的读出串口状态寄存器;


[C] 纯文本查看 复制代码
static void UartIRQ(UART_T *_pUart)
{
	/* 处理接收中断  */
	if (USART_GetITStatus(_pUart->uart, USART_IT_RXNE) != RESET)
	{
		/* 从串口接收数据寄存器读取数据存放到接收FIFO */
		uint8_t ch;

		ch = USART_ReceiveData(_pUart->uart);
		_pUart->pRxBuf[_pUart->usRxWrite] = ch;
		if (++_pUart->usRxWrite >= _pUart->usRxBufSize)//重新覆盖写
		{
			_pUart->usRxWrite = 0;
		}
		if (_pUart->usRxCount < _pUart->usRxBufSize)//
		{
			_pUart->usRxCount++;
		}

		/* 回调函数,通知应用程序收到新数据,一般是发送1个消息或者设置一个标记 */
		//if (_pUart->usRxWrite == _pUart->usRxRead)
		//if (_pUart->usRxCount == 1)
		{
			if (_pUart->ReciveNew)
			{
				// _pUart->ReciveNew(ch);//ReciveNew(ch) 会在Modbus协议里面要用到
			}
		}
	}

	/* 处理发送缓冲区空中断 */
	if (USART_GetITStatus(_pUart->uart, USART_IT_TXE) != RESET)
	{
		//if (_pUart->usTxRead == _pUart->usTxWrite)
		if (_pUart->usTxCount == 0)
		{
			/* 发送缓冲区的数据已取完时, 禁止发送缓冲区空中断 (注意:此时最后1个数据还未真正发送完毕)*/
			USART_ITConfig(_pUart->uart, USART_IT_TXE, DISABLE);

			/* 使能数据发送完毕中断 */
			USART_ITConfig(_pUart->uart, USART_IT_TC, ENABLE);
		}
		else
		{
			/* 从发送FIFO取1个字节写入串口发送数据寄存器 */
			USART_SendData(_pUart->uart, _pUart->pTxBuf[_pUart->usTxRead]);
			if (++_pUart->usTxRead >= _pUart->usTxBufSize)
			{
				_pUart->usTxRead = 0;
			}
			_pUart->usTxCount--;
		}

	}
	/* 数据bit位全部发送完毕的中断 */
	else if (USART_GetITStatus(_pUart->uart, USART_IT_TC) != RESET)
	{
		//if (_pUart->usTxRead == _pUart->usTxWrite)
		if (_pUart->usTxCount == 0)
		{
			/* 如果发送FIFO的数据全部发送完毕,禁止数据发送完毕中断 */
			USART_ITConfig(_pUart->uart, USART_IT_TC, DISABLE);

			/* 回调函数, 一般用来处理RS485通信,将RS485芯片设置为接收模式,避免抢占总线 */
			if (_pUart->SendOver)
			{
				_pUart->SendOver();
			}
		}
		else
		{
			/* 正常情况下,不会进入此分支 */

			/* 如果发送FIFO的数据还未完毕,则从发送FIFO取1个数据写入发送数据寄存器 */
			USART_SendData(_pUart->uart, _pUart->pTxBuf[_pUart->usTxRead]);
			if (++_pUart->usTxRead >= _pUart->usTxBufSize)
			{
				_pUart->usTxRead = 0;
			}
			_pUart->usTxCount--;
		}
	}
}


优化后的代码:
[C] 纯文本查看 复制代码
void UartIRQ(UART_T *_pUart)
{
	uint32_t sr = _pUart->huart->Instance->SR;  // 读取一次状态寄存器
	// 先检查ORE错误,并清除
    /* 1. 处理ORE溢出错误 - 必须先处理 */
    if(sr & USART_SR_ORE)
    {
        // 清除ORE标志的正确方法
        volatile uint32_t tmp = _pUart->huart->Instance->SR;  // 读SR
        tmp = _pUart->huart->Instance->DR;                    // 读DR
        (void)tmp;
        
        // 重新读取SR,因为清除ORE后状态可能变化
        sr = _pUart->huart->Instance->SR;
    }
	
	/* 处理接收中断  */
	/*  RXNE (Read Data Register Not Empty) - 接收数据寄存器非空 */
	/* 2. 处理接收中断 */
    if(sr & USART_SR_RXNE)
    {
		/* 从串口接收数据寄存器读取数据存放到接收FIFO */
		uint8_t ch = (uint8_t)(_pUart->huart->Instance->DR & 0xFF);		
		
		_pUart->pRxBuf[_pUart->usRxWrite] = ch;
		if (++_pUart->usRxWrite >= _pUart->usRxBufSize)//重新覆盖写
		{
			_pUart->usRxWrite = 0;
		}
		if (_pUart->usRxCount < _pUart->usRxBufSize)//
		{
			_pUart->usRxCount++;
		}

		/* 回调函数,通知应用程序收到新数据,一般是发送1个消息或者设置一个标记 */
		//if (_pUart->usRxWrite == _pUart->usRxRead)
		//if (_pUart->usRxCount == 1)
		{
			if (_pUart->ReciveNew)
			{
				// _pUart->ReciveNew(ch);//ReciveNew(ch) 会在Modbus协议里面要用到
			}
		}
	}	
	/* 数据bit位全部发送完毕的中断 */
	/* 处理传输完成中断 TC (Transmission Complete) - 传输完成标志 */
    /* 3. 处理TC传输完成中断 - 必须放在TXE前面处理 
	在同时使能了TXE和TC中断的情况下,建议在中断处理函数中先处理TC中断,
	再处理TXE中断。因为TC表示一次传输的结束,而TXE表示可以发送下一个数据。
	如果先处理TXE,可能会在TC之前就写入新的数据,这样TC标志可能会被清除
	(因为写入DR寄存器是清除TC的条件之一),导致TC中断无法正常触发。*/
    if(sr & USART_SR_TC)
    {
		//if (_pUart->usTxRead == _pUart->usTxWrite)
		if (_pUart->usTxCount == 0)
		{
			// 必须清除 TC 标志,只有当DR寄存器进行写操作,才会自动清零,否则手动清零
			__HAL_UART_CLEAR_FLAG(_pUart->huart, UART_FLAG_TC); // 清除TC标志
			/* 如果发送FIFO的数据全部发送完毕,禁止数据发送完毕中断 */
			__HAL_UART_DISABLE_IT(_pUart->huart, UART_IT_TC);// 禁止TC中断

			/* 回调函数, 一般用来处理RS485通信,将RS485芯片设置为接收模式,避免抢占总线 */
			if (_pUart->SendOver)
			{
				_pUart->SendOver();
			}
		}
		else
		{
			/* 正常情况下,不会进入此分支 */

			/* 如果发送FIFO的数据还未完毕,则从发送FIFO取1个数据写入发送数据寄存器 */
			_pUart->huart->Instance->DR = _pUart->pTxBuf[_pUart->usTxRead];
			if (++_pUart->usTxRead >= _pUart->usTxBufSize)
			{
				_pUart->usTxRead = 0;
			}
			_pUart->usTxCount--;
		}
	}
	/* 处理发送缓冲区空中断 */
	/* TXE (Transmit Data Register Empty) - 发送数据寄存器空*/
	/* 4. 处理TXE发送空中断 */
    if(sr & USART_SR_TXE)
    {
		//if (_pUart->usTxRead == _pUart->usTxWrite)
		if (_pUart->usTxCount == 0)
		{
			/* 发送缓冲区的数据已取完时, 禁止发送缓冲区空中断 (注意:此时最后1个数据还未真正发送完毕)*/
			//禁止TXE中断、使能TC中断
			__HAL_UART_DISABLE_IT(_pUart->huart, UART_IT_TXE);
			
			
			/* 使能数据发送完毕中断 */
			__HAL_UART_ENABLE_IT(_pUart->huart, UART_IT_TC);

		}
		else
		{
			/* 从发送FIFO取1个字节写入串口发送数据寄存器 */
			/* 发送下一个字节 */
            _pUart->huart->Instance->DR = _pUart->pTxBuf[_pUart->usTxRead];
            if (++_pUart->usTxRead >= _pUart->usTxBufSize) {
                _pUart->usTxRead = 0;
            }
            _pUart->usTxCount--;
		}

	}
}


回复

使用道具 举报

1万

主题

7万

回帖

12万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
121123
QQ
发表于 2026-1-26 01:02:04 | 显示全部楼层
谢谢楼主分享。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2026-2-24 11:02 , Processed in 0.050981 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表