【注】STM32串口相关,总共分为三个部分:①发送接收基础性问题。②最优的串口使用方式及说明。③串口发送接收数据的一般算法。
串口中断无接收数据,但频繁卡中断
这个问题需要我们在中断中依次判断不同标志,使用串口中断程序中使用if (USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET)//注意!不能使用if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)来判断,然后无论是如果是接收或发送中断,那么读取数据,若是其他中断则直接丢掉数据,并且清除标志位
if(USART_GetFlagStatus(USART3, USART_FLAG_ORE) != RESET)
{//同 @arg USART_IT_ORE_ER : OverRun Error interrupt if the EIE bit is set
ushTemp = USART_ReceiveData(USART1); //取出来扔掉
USART_ClearFlag(USART3, USART_FLAG_ORE);
}
if(USART_GetFlagStatus(USART3, USART_FLAG_NE) != RESET)
{//同 @arg USART_IT_NE : Noise Error interrupt
USART_ClearFlag(USART3, USART_FLAG_NE);
}
if(USART_GetFlagStatus(USART3, USART_FLAG_FE) != RESET)
{//同 @arg USART_IT_FE : Framing Error interrupt
USART_ClearFlag(USART3, USART_FLAG_FE);
}
if(USART_GetFlagStatus(USART3, USART_FLAG_PE) != RESET)
{//同 @arg USART_IT_PE : Parity Error interrupt
USART_ClearFlag(USART3, USART_FLAG_PE);
}
if(USART_GetFlagStatus(USART3, USART_FLAG_RXNE) == SET)
{
//USART_SendData(USART2, USART_ReceiveData(USART2));
R_Dat =USART_ReceiveData(USART3); //读取接收到的数据
}
说明:USART_GetFlagStatus,USART_GetITStatus,前者返回值是中断标志位状态(读SR寄存器),后者返回值是中断发生与否的判断(读CR寄存器)
按照上述的程序写法,一般来说串口硬件错误是可以避免的。
空闲中断,顾名思义就是在接收完成数据线空闲的时候,会进入中断,俗称“帧中断”。
触发机制:无数据接收情况下,串口空闲中断是不会产生的。在有数据到达时,空闲中断开始检测,在第一帧数据接收完毕到第二帧数据开始接收期间存在一个空闲状态,此时空闲中断检测到该空闲并触发中断,此时表明一帧数据已经接收完毕。
为什么要使用空闲中断呢?
我们在数据量小的场合下,直接使用串口一般中断接收判断数据即可,但是在稍微复杂一点的系统中,通信可能包括各种传感器信息和动作信息,此时数据帧的长度不定,再加上帧头、帧尾校验位等,如果我们每接收一位数据就判断一次,那么对于需要较高实时性的系统来说无疑浪费了很多的资源。如果我们将一帧数据接收完之后再再空闲中断中去解析相关数据,那么在数据传输过程中就可以去执行其他任务,而不是频繁地打断。
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接收中断
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//开启串口空闲中断
void USART2_IRQHandler(void){ //串口1中断服务程序
int clear;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET){ //字符接收中断(接收到的数据必须是0x0d 0x0a结尾)
USART2_RX_BUF[length++] = USART2->DR & 0x0FF;
}else if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET){//空闲帧中断
if(USART2_RX_BUF[length - 1] == 0xff){
clear = USART2->DR;
clear = USART2->SR;//或者UART_ClearITPendingBit(UART3, UART_ICR_RXIDLE);
length = clear;
length = 0;
/*
数据处理
*/
USART2_RX_STA = 1;
}else{
;
}
}
}
这种方式下,主要是利用接收中断接收不定长数据,在接收完成后进入空闲中断,这个时候可以进行数据的解析。
这种方式省去了每接收一个字节就判断一次的资源浪费问题,但是仍然是频繁进入中断,打断主程序的运行。
空闲中断解决了在不定长数据接收中需要频繁判断数据的资源浪费问题,但是频繁进入中断也是资源浪费的一大问题。
所以这里我们使用DMA的方式,这也是网上主要的一种串口通信技巧。
DMA是什么?
DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。四种情况的数据传输如下:
外设 -> 内存
内存 -> 外设
内存 -> 内存
外设 -> 外设
UART_ITConfig(UART3, UART_IER_RXIDLE, ENABLE);
UART_DMACmd(UART3, UART_DMAReq_EN, ENABLE);
DMA_Cmd(DMA1_Channel3, ENABLE);
void UART3_IRQHandler(void)
{
if(UART_GetITStatus(UART3, UART_ISR_RXIDLE) != RESET)
{
clear = USART3->DR;
clear = USART3->SR;/*清除串口空闲中断标志位*/
//或UART_ClearITPendingBit(UART3, UART_ICR_RXIDLE);
DMA_Cmd(DMA1_Channel3, DISABLE);//停止DMA传输
/*
数据处理
*/
DMA_Cmd(DMA1_Channel3, ENABLE); //使能DMA传输
}
}
在配置相关结构体时,需要对应手册中的不同功能的不同DMA通道。