中断模式串口

中断模式串口

串口数据的发送接收可以藉由串口发送中断、接收中断实现。串口发送中断、接收中断可以同时开启。UART发送中断可以分为两种,UART发送缓冲区为空中断,数据从UART引脚发送出去中断。串口中断模式参见FM33L023D串口中断例程,该例程在初始化阶段开启UART0发送、接收中断,串口0收到数据后,会进入串口中断服务处理程序。串口发送中断、接收中断共用一个中断服务处理程序,发送中断触发需要修改中断服务例程。串口中断操作相对于轮询方式,操作更复杂。开发者需要先配置串口功能,再开启总中断,开启UART发送中断、接收中断。

中断使能

串口中断例程先设置中断触发模式,串口发送中断选择BUFFER空且移位寄存器空,相对于串口发送缓冲区为空,这种模式触发时机晚,以前用STM32系列芯片好像没有注意到这个细节。针对这个问题官方论坛有给出明确解释。

请问FM33LC0xx系列LPUART发送中断TXBE和TXSE的区别是什么? - FM33LC0xx系列 - 低功耗嵌入式软件开发者论坛 - Powered by Discuz! Archiverhttps://www.fmdevelopers.com.cn/archiver/?tid-1146.html

deepseek针对这两种模式给出了使用场景,我就直接搬运了。

  • 追求最高发送吞吐量(使用TXE/THRE):如果需要连续、大量地发送数据,且对CPU效率要求高,通常建议使用仅“发送缓冲区空”中断。因为可以在上一个字节还在“路上”(移位发送)时,CPU就提前把下一个字节填入缓冲区,充分利用硬件流水线,实现最高效的连续发送,中间几乎没有空闲间隔。

  • 需要精确控制RS485收发切换或流控(使用TC):如果涉及RS485通信,需要在发送完成后立即释放总线(将RE/DE引脚拉低),或者在半双工模式下切换方向,必须使用“BUFFER空且移位寄存器空”中断(即发送完成中断)。只有在TC标志置位时,才能确保总线上所有数据已发送完毕,此时切换收发状态才安全,否则会造成数据丢失或总线冲突。

接收中断选择接收缓冲区满,而不是数据接收完成产生中断。接收缓冲区,即RXREG寄存器有效长度1Byte,接收1Byte触发一次中断,这是最常见的模式。数据接收完成,更像是一帧接收完成触发一次中断。前者更常见,后者估计还要配置数据帧格式(帧头、帧尾、校验和等),暂时没看到明确的解释。接着通过FL_NVIC_Init函数设置中断优先级,底层实际调用NVIC库函数。接着就是配置UARTIE寄存器,使能发送中断、接收中断。

FL_UART_SetTxInterruptSelect(UART0, FL_UART_INTSEL_TXBE_TXSE); //发生中断选择BUFFER空且移位寄存器空 FL_UART_SetRxInterruptSelect(UART0, FL_UART_INTSEL_RX_FULL); //接收Buffer满中断 InterruptConfigStruct.preemptPriority = 0x02; FL_NVIC_Init(&InterruptConfigStruct, UART0_IRQn); FL_UART_ClearFlag_TXIF(UART0); FL_UART_ClearFlag_RXIF(UART0); FL_UART_EnableIT_TXIE(UART0); FL_UART_EnableIT_RXIE(UART0); /** * @brief Configure NVIC for specified Interrupt. * @param configStruct NVIC configuration. * @param irq Interrupt number. * @retval None */ void FL_NVIC_Init(FL_NVIC_ConfigTypeDef *configStruct, IRQn_Type irq) { /* Check parameter */ if(configStruct->preemptPriority > 3) { configStruct->preemptPriority = 3; } NVIC_DisableIRQ(irq); NVIC_ClearPendingIRQ(irq); NVIC_SetPriority(irq, configStruct->preemptPriority); NVIC_EnableIRQ(irq); }

中断接收

中断接收功能,相信使用过串口接收中断的开发人员都有一定的了解。UART0接收到1Byte数据后,进入中断接收逻辑。由于串口一直不断的接收到数据,程序将不断触发串口接收逻辑。开发者可以将接收到的数据存入数组。然后,在主循环中,对接收数据进行解析,如果符合数据帧格式,就执行相应的操作。为了防止数据溢出,可以使用环形数组。也就是记录接收数据的开始位置、结束位置,数据索引循环处理,例如((index++)%arrRecvLen)。

int indexTx = 0; //发送索引 int indexRx= 0; void UART0_IRQHandler(void) { if(FL_SET == FL_UART_IsEnabledIT_TXIE(UART0) && FL_SET == FL_UART_IsActiveFlag_TXIF(UART0)) { FL_UART_ClearFlag_TXIF(UART0); /*中断发送测试代码 for(indexTx = 0;indexTx < 9;indexTx++) { UART0->TXREG = arrSend[i]; } */ } if(FL_SET == FL_UART_IsEnabledIT_RXIE(UART0) && FL_SET == (FL_UART_IsActiveFlag_RXIF(UART0))) { arrRecv[indexRx++] = (uint8_t)FL_UART_ReadRXBuff(UART0); if(indexRx > 10) { } } }

中断发送

中断发送功能我了解的不多,只能给出自己的见解。我为了验证这个功能,就在中断使能函数的末尾添加了“UART4->TXREG = 'S'”,这样,系统初始化执行到末尾,便会主动发送'S'字符,接着进入串口发送中断,不断向串口发送缓冲区填充数据,直至数组内容发送完毕。至此,中断发送功能得到验证。但是真正使用的时候,还要检查发送缓冲区是否为空,不能直接发送。

最后,再给出中断发送与轮询发送的对比表格。

轮询发送(for循环)

中断发送(中断驱动)

for(i=0; i<13; i++) {
UART5->TXBUF = commSendBuf[i];
while(TXE==0);
}

主程序
start_index0 = 0;
end_index = 13;
UART5->TXBUF = commSendBuf[0];
开启中断使能;

中断服务程序(自动触发13次)
if(start_index0 < end_index)
UART5->TXBUF = commSendBuf[start_index0++];
else
关闭中断使能;

维度

轮询发送

中断发送

发送循环位置

主程序(for循环)

中断服务程序(ISR)

如何检测TXE

while(TXE==0);主动等待

由硬件中断自动触发,无需显式等待

是否有检测代码

有(显式while

有(ISR中的if判断索引,及硬件自动触发)

CPU占用

高(等待期间100%占用)

低(只在ISR中占用几微秒)

代码结构

简单、直观

复杂、需要管理状态和中断使能