完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
UART空闲中断 在实际项目中经常用到串口接收一些不定长的数据,此时必须面对一个问题:怎么判断这一帧数据接收完成了呢?通常使用UART非空中断配合简单的数据协议,在数据中加入帧头、帧尾,在程序中判断是否接收到帧尾来确定数据接收完毕,因为对每个数据都要进行判断,比较消耗系统资源,尤其是在一些实时性要求较高的场合。而串口空闲中断可以大大简化数据接收过程的判断,在这一块起到非常重要的作用。 空闲中断(IDLE),俗称帧中断,即第一帧数据接收完毕到第二帧数据开始接收期间存在一个空闲状态(每接收一帧数据后空闲标志位置1),检测到此空闲状态后即执行中断程序。空闲中断的优点在于省去了帧头帧尾的检测,进入中断程序即意味着已经接收到一组完整数据,仅需即时对数据处理或将数据转移出缓冲区即可。 串口空闲中断在串口无数据接收的情况下,是不会产生的,产生的条件是当清除空闲标志位后,必须有接收到第一个数据后,才开始触发,一旦接收的数据断流,没有接收到数据,即产生空闲中断。 MM32F0270系列的UART支持空闲中断功能。相关的寄存器包括UART中断状态寄存器(UART_ISR)、 UART中断使能寄存器(UART_IER)、 UART中断清除寄存器(UART_ICR),对应的位如下: UART中断状态寄存器(UART_ISR)
UART中断使能寄存器(UART_IER) UART中断清除寄存器(UART_ICR) UART DMA方式 MM32F0270 UART使用DMA方式接收数据可以减小CPU的开销。 对于接收定长数据,可以将DMA接收缓冲区的长度设定为待接收数据的长度,这样利用DMA的传输完成中断就可以知道已经接收了一帧数据。 对于接收不定长数据,由于内核在串口接收数据到空闲这段时间,是不受理串口数据的,所以可以使用DMA来协助我们把数据传送到指定的地方,当数据传输完成后,通知内核去处理。 截取MM32F0270的DMA各通道请求映像表如下,其中UART1_RX对应通道3。 各通道DMA请求一览表(部分) 实验 本次实验使用UART空闲中断功能,通过DMA方式把数据传送到指定的缓存区。当UART接收完一帧数据后,会产生一个空闲中断。这个中断在UART其他任何状态都不产生,只会在接收完一帧数据后才会产生,一帧数据可以是1个字节或者多个字节。因此,我们可以在这个空闲中断函数中,设置一个接收完成标志位。那么,我们只需要在主程序中检测这个标志位就知道数据是否接收完成了,如果数据接收完成,将数据发送到上位机显示。程序部分 程序参考官网UART空闲中断代码,并在此基础上修改。 UART初始化 void UART1_NVIC_Init(u32 baudrate) { UART_InitTypeDef UART_InitStruct; NVIC_InitTypeDef NVIC_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2ENR_UART1, ENABLE); //UART1 NVIC NVIC_InitStruct.NVIC_IRQChannel = UART1_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); //Baud rate UART_StructInit(&UART_InitStruct); UART_InitStruct.BaudRate = baudrate; //The word length is in 8-bit data format. UART_InitStruct.WordLength = UART_WordLength_8b; UART_InitStruct.StopBits = UART_StopBits_1; //No even check bit. UART_InitStruct.Parity = UART_Parity_No; //No hardware data flow control. UART_InitStruct.HWFlowControl = UART_HWFlowControl_None; UART_InitStruct.Mode = UART_Mode_Rx | UART_Mode_Tx; UART_Init(UART1, &UART_InitStruct); UART_Cmd(UART1, ENABLE); UART1_GPIO_Init(); } DMA初始化 void DMA_Config_Init(DMA_Channel_TypeDef* dam_chx, u32 cpar, u32 cmar, u16 cndtr) { DMA_InitTypeDef DMA_InitStruct; RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1, ENABLE); DMA_DeInit(dam_chx); DMA_StructInit(&DMA_InitStruct); DMA_InitStruct.DMA_PeripheralBaseAddr = cpar; DMA_InitStruct.DMA_MemoryBaseAddr = cmar; DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStruct.DMA_BufferSize = cndtr; DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; DMA_InitStruct.DMA_Priority = DMA_Priority_Low; DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; DMA_InitStruct.DMA_Auto_reload = DMA_Auto_Reload_Disable; DMA_Init(dam_chx, &DMA_InitStruct); UART_DMACmd(UART1, UART_DMAReq_EN, ENABLE); DMA_Cmd(dam_chx, ENABLE); } 在程序中调用DMA初始化函数配置DMA1_Channel3,将UART1接收寄存器的数据通过DMA搬运到接收缓冲区sRecvBuf[RECVBUFLENGTH],数据单元数目为RECVBUFLENGTH。DMA_Config_Init(DMA1_Channel3, (u32)&UART1->RDR, (u32)sRecvBuf, RECVBUFLENGTH); UART中断服务函数 void UART1_IRQHandler(void) { /* Omit the code related to sending. */ // Recv packet if (UART_GetITStatus(UARTx, UART_ISR_RX) != RESET) { UART_ClearITPendingBit(UARTx, UART_ICR_RX); if(Flag_RecvComplete == 0) { if(RecvCnt < RECVBUFLENGTH) { //(1) RecvCnt++; } else { RecvCnt = 0; Flag_RecvComplete = 1; //(2) } } } //Idle interrupt if (UART_GetITStatus(UARTx, UART_ISR_RXIDLE) != RESET) { receive_len = RECVBUFLENGTH - DMA_GetCurrDataCounter(DMA1_Channel3); //(3) if(receive_len > 0){ Flag_RecvComplete = 1; //(4) } UART_ClearITPendingBit(UARTx, UART_ICR_RXIDLE); } } (1)UART接收到数据进入中断,通过变量RecvCnt累加记录接收的数据量。限制RecvCnt不能超出缓冲区范围。 (2)对超出缓冲区数据处理,此处将超出缓冲区的数据舍去,将Flag_RecvComplete置1。 (3)进入UART空闲中断,通过receive_len记录当前DMA传输的数据单元数量。 (4)接收到数据,则将Flag_RecvComplete置1。 在中断外通过判断Flag_RecvComplete标志进行数据处理。 UART收发函数 void UART1_RxTx_Transceiving(void) { UART_ITConfig(UART1, UART_IT_RXIEN, ENABLE); //(1) UART_ITConfig(UART1, UART_IER_RXIDLE, ENABLE); //(2) while(1) { if(Flag_RecvComplete == 1) { //(3) Flag_RecvComplete = 0; memcpy((u8*)&sSendBuf[0], (u8*)&sRecvBuf[0], receive_len); //(4) //DMA reset to reinitialize DMA_Cmd(DMA1_Channel3, DISABLE); DMA_Config_Init(DMA1_Channel3, (u32)&UART1->RDR, (u32)sRecvBuf, RECVBUFLENGTH); //(5) DMA_Cmd(DMA1_Channel3, ENABLE); //Send data UART1_Send_Packet_Interrupt((u8*)&sSendBuf[0], receive_len); // while(1) { if((SendLen) == UART1_Check_Send_Finish()) { break; } } } } } (1)和(2)配置UART_IT_RXIEN中断和UART_IER_RXIDLE中断。 (3)判断标志位,该标志位在进入空闲中断置1。 (4)将接收缓冲区sRecvBuf的数据复制到发送缓冲区sSendBuf。 (5)复位DMA,重新设置DMA传输的数据单元数目为RECVBUFLENGTH(接收缓冲区范围)。传输数量寄存器DMA_CNDTRx只能在通道关闭时写入,通道开启后该寄存器变为只读。 其余和发送相关的代码是将数据发送到上位机。 演示 仿真运行程序,可以在程序的①、②位置设置断点,并在Watch窗口观测receive_len的值。 打开串口调试助手发送一帧数据,如:0123456789,程序运行至断点①,UART中断状态寄存器UART_ISR的RXIDLE_INTF位置1,表示进入UART空闲中断。 重复进行上述操作,每帧数据发送完成都会进入空闲中断。 串口调试助手显示如下: 实验简单演示了使用MM32F0270的UART空闲中断+ DMA方式接收不定长数据,运行结果和预期一致。 参考Demo程序可登录MindMotion的官网下载MM32F0270库函数和例程: https://www.mindmotion.com.cn/pr ... instream/MM32F0270/ 工程路径如下: ~MM32F0270_SamplesLibSamplesUARTUART_idleframe_Interrupt |
|
相关推荐
|
|
只有小组成员才能发言,加入小组>>
2249个成员聚集在这个小组
加入小组灵动微电子MM32全系列MCU产品应用手册,库函数和例程和选型表
11703 浏览 3 评论
【MM32 eMiniBoard试用连载】+基于OLED12864的GUI---U8G2
5930 浏览 1 评论
【MM32 eMiniBoard试用连载】移植RT-Thread至MM32L373PS
10965 浏览 0 评论
【MM32 eMiniBoard测评报告】+ 开箱 + 初探
4577 浏览 1 评论
灵动微课堂(第106讲) | MM32 USB功能学习笔记 —— WinUSB设备
4302 浏览 1 评论
[MM32软件] MM32F002使用内部flash存储数据怎么操作?
977浏览 1评论
805浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-27 09:28 , Processed in 0.448525 second(s), Total 36, Slave 29 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号