【前言】
在前一篇:【武汉芯源CW32】OTA升级系列之三串口驱动-电子产品世界论坛 (eepw.com.cn)
我分享了如何驱动串口,如果需要用到升级固件,或者是AT等非固定长度的数据交互,那收就需要实现不定长度的接收,并实现串口空闲中断来实现功能。由于CW32L083串口外设中没有串口空闲的中断,所以需要使用普通定时器+接收中断来实现空闲中断。
【程序设计】
1、开启串口中断,在接收到一个Byte后产生中断回调。
2、配置一个普通定时器中断,中断触发为10ms,在串口中断回调函数中启动定时器中断,如果产生溢出,说明产生了空闲中断。
3、在定时器中断回调函数中,把接收到缓冲区的位置处理并记录。来实现各个帧数据的标志。
【代码实现】
1、创建一个数组,用于存放串口接收的Buff。
2、创建两个结构体,一个为存放一帧数据的起始位与结束位置。
/* 用于存放一帧数据指向的起始位置,与结束位置 */ typedef struct{ uint8_t start; uint8_t end; }UCB_URxBuffptr; typedef struct{ uint32_t URxCounter; UCB_URxBuffptr URxDataPtr[U1_RX_NUM]; uint8_t IN; uint8_t OUT; uint8_t MAX; }UCB_CB; extern volatile uint8_t U1_RxBuff[U1_RX_SIZE]; extern UCB_CB UCB_UART1;
3、创更新串口初始化函数,初始化接收结构体,接收标志位初始到接收缓冲的首位,同时清空接收缓冲。使能串口接收中断。
/* 接收Buff初始化 */ //memset(&UCB_UART1,0,sizeof(UCB_UART1)); UCB_UART1.IN = 0; /* 将接收初始体指定接收缓冲区地始地址 */ UCB_UART1.OUT = 0; /* 将处理初始体指定接收缓冲区地始地址 */ UCB_UART1.MAX = U1_RX_NUM; UCB_UART1.URxCounter = 0; //优先级,无优先级分组 NVIC_SetPriority(UART1_UART4_IRQn, 0); //UARTx中断使能 NVIC_EnableIRQ(UART1_UART4_IRQn); UART_ITConfig(CW_UART1, UART_IT_RC, ENABLE);
4、编写串口接收回调函数,接收到一个字节后,把他存入缓存,并称位接收标志,同时开启定时器标志。
void UART1_Call_Back(void) { uint8_t rx_data; if(UART_GetITStatus(CW_UART1, UART_IT_RC) !=RESET) { rx_data=UART_ReceiveData_8bit(CW_UART1); if(UCB_UART1.URxCounter<U1_RX_SIZE) { U1_RxBuff[UCB_UART1.URxCounter] = rx_data; } else { UCB_UART1.URxCounter = 0; } CW_GTIM1->CR0_f.EN=0; CW_GTIM1->ARR=40000-1; CW_GTIM1->CR0_f.EN=1; UCB_UART1.URxCounter++; UART_ClearITPendingBit(CW_UART1, UART_IT_RC); } }
5、编写定时器中断,当定时器中断到达时,表示串口接收一帧数据到位,把接收到的结束地址放入结构体中,同时更新下一个开始接收的地址。
void GTM_UART1_Call_Back(void) { if(GTIM_GetITStatus(CW_GTIM1, GTIM_IT_OV)) { GTIM_ClearITPendingBit(CW_GTIM1, GTIM_IT_OV); UCB_UART1.URxDataPtr[UCB_UART1.IN].end = UCB_UART1.URxCounter-1; UCB_UART1.IN ++; if(UCB_UART1.IN == UCB_UART1.MAX){ UCB_UART1.IN = 0; } } if(U1_RX_SIZE - UCB_UART1.URxCounter >= U1_RX_MAX) { UCB_UART1.URxDataPtr[UCB_UART1.IN].start = UCB_UART1.URxCounter; } else{ // __NVIC_DisableIRQ(UART1_UART4_IRQn); UCB_UART1.URxDataPtr[UCB_UART1.IN].start =0; UCB_UART1.URxCounter = 0; // NVIC_EnableIRQ(UART1_UART4_IRQn); } }
6、测试代码,在测试代码中,首先判断接收与处理标志位是否一致,如果不一样,说明还有未处理的数据,使用串口回调输出:
if(UCB_UART1.OUT != UCB_UART1.IN) { CW_U1_printf("本次接收到了%d字节数据\r\n",UCB_UART1.URxDataPtr[UCB_UART1.OUT].end - UCB_UART1.URxDataPtr[UCB_UART1.OUT].start +1); for(i=0;i<UCB_UART1.URxDataPtr[UCB_UART1.OUT].end - UCB_UART1.URxDataPtr[UCB_UART1.OUT].start +1;i++) { CW_U1_printf("%c",U1_RxBuff[UCB_UART1.URxDataPtr[UCB_UART1.OUT].start+i]); } CW_U1_printf("\r\n"); UCB_UART1.OUT ++; if(UCB_UART1.OUT == UCB_UART1.MAX) { UCB_UART1.OUT = 0; } } }
使用串口发送,进行测试,结果如下:
【讨论】
CW32L083没有串口空闲中断,使用定时器中断进行配置可以实现“空闲中断”,能如期实现即定的需求。