【WCH蓝牙系列芯片】-基于CH582开发板-串口中断识别寄存器的中断标志位识别
------------------------------------------------------------------------------------------------------------------------------------
在使用CH582芯片过程中,经常用的是UART串口外设。从芯片手册中可以看到中断识别寄存器(R8_UARTx_IIR)的每一位对应的描述。
在中断识别寄存器中第0位到第3位是表示是否有串口中断发生,并且触发的中断类型是什么,这从手册中的也可以看出来。
现在可以通过例程的串口中断函数去验证一个问题,在这么多中断类型中,如果同时发生不同的中断类型,产生不同的中断标志时,IIR寄存器的标志位会如何置位,会不会出现丢失标志位情况。比如,当“接收数据可用”和“THR寄存器空”同时发生,会出现什么情况。
通过程序复现这个问题的思路:
1、 通过串口发送数据
2、 串口接收到的字节数发到FIF0的触发点,产生接收数据可用的中断标志位,但是串口不接收数据
3、 利用GPIO中断,将RB_IER_THR_EMPTY 位从 0 变 1 触发,强制产生THR寄存器空的标志位,
4、 此时接收串口发送的数据,再看THR寄存器空的标志位是否被触发。
在CH582的EVT例程中进行修改为这个思路,在主函数添加串口1的配置,中断方式的数据接收和发送,GPIO中断引脚的配置参数。
int main() { uint8_t len; SetSysClock(CLK_SOURCE_PLL_60MHz); /* 配置串口1:先配置IO口模式,再配置串口 */ GPIOA_SetBits(GPIO_Pin_9); GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); // RXD-配置上拉输入 GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); // TXD-配置推挽输出,注意先让IO口输出高电平 UART1_DefInit(); //打印串口初始化 GPIOB_SetBits(GPIO_Pin_7); GPIOB_ModeCfg(GPIO_Pin_4, GPIO_ModeIN_PU); // RXD-配置上拉输入 GPIOB_ModeCfg(GPIO_Pin_7, GPIO_ModeOut_PP_5mA); // TXD-配置推挽输出,注意先让IO口输出高电平 UART0_DefInit(); printf("hello\r\n"); #if 1 // 测试串口发送字符串 UART1_SendString(TxBuff, sizeof(TxBuff)); #endif #if 0 // 查询方式:接收数据后发送出去 while(1) { len = UART1_RecvString(RxBuff); if(len) { UART1_SendString(RxBuff, len); } } #endif #if 1 // 中断方式:接收数据后发送出去 UART1_ByteTrigCfg(UART_7BYTE_TRIG); trigB = 7; UART1_INTCfg(ENABLE, RB_IER_RECV_RDY | RB_IER_LINE_STAT); PFIC_EnableIRQ(UART1_IRQn); #endif #if 1 /* 配置唤醒源为 GPIO - PA5 */ GPIOA_ModeCfg(GPIO_Pin_5, GPIO_ModeIN_PU); GPIOA_ITModeCfg(GPIO_Pin_5, GPIO_ITMode_LowLevel); //低电平触发 PFIC_EnableIRQ(GPIO_A_IRQn); GPIOA_ModeCfg(GPIO_Pin_4, GPIO_ModeOut_PP_5mA); //点灯使用的IO口 GPIOA_ResetBits(GPIO_Pin_4); #endif while(1); } /********************************************************************* * @fn UART1_IRQHandler * * @brief UART1中断函数 * * @return none */ __attribute__((interrupt("WCH-Interrupt-fast"))) __attribute__((section(".highcode"))) void UART1_IRQHandler(void) { volatile uint8_t i; switch(UART1_GetITFlag()) { case UART_II_LINE_STAT: // 线路状态错误 { UART1_GetLinSTA(); break; } case UART_II_RECV_RDY: // 数据达到设置触发点 if (R8_UART1_IER & RB_IER_THR_EMPTY) //判断THR寄存器空这个中断是否被设置 { printf("UART_II_RECV_RDY\r\n"); for(i = 0; i != trigB; i++) { RxBuff[i] = UART1_RecvByte(); UART1_SendByte(RxBuff[i]); } } else { return; } break; case UART_II_RECV_TOUT: // 接收超时,暂时一帧数据接收完成 printf("UART_II_RECV_TOUT\r\n"); // i = UART1_RecvString(RxBuff); // UART1_SendString(RxBuff, i); if (R8_UART1_IER & RB_IER_THR_EMPTY) { i = UART1_RecvString(RxBuff); UART1_SendString(RxBuff, i); } else { return; } break; case UART_II_THR_EMPTY: // 发送缓存区空,可继续发送 printf("UART_II_THR_EMPTY\r\n"); GPIOA_SetBits(GPIO_Pin_4); break; case UART_II_MODEM_CHG: // 只支持串口0 break; default: break; } }
在GPIO的中断服务函数中,先清标志位,利用UART1_INTCfg(ENABLE, RB_IER_THR_EMPTY);使能发送中断,会置发送缓存空标志,触发中断。
__attribute__((interrupt("WCH-Interrupt-fast"))) __attribute__((section(".highcode"))) void GPIOA_IRQHandler(void) { GPIOA_ClearITFlagBit(GPIO_Pin_5); // printf("GPIO_IN*******************\r\n"); UART1_INTCfg(ENABLE, RB_IER_THR_EMPTY); //使能发送中断,会置发送缓存空标志,触发中断 }
在串口服务函数中,串口接收数据可用中断 UART_II_RECV_RDY中,加入判断,如果串口发送的“THR 寄存器空”中断 UART_II_THR_EMPTY被触发中断,才开始接收数据,否则就一直不接收数据。
在串口接收数据超时中断 UART_II_RECV_TOUT中,也添加相同的程序,这样可以当串口发送超过触发点时,也可以判断串口接收数据超时中断是否被触发。
在串口发送的“THR 寄存器空”中断 UART_II_THR_EMPTY中,添加一些提示信息,以便触发这个中断标志时,能够做以区别。
通过串口调试助手,发送数据来观察,先发送七个字节的数据,然后通过按键触发GPIO中断,可以看到串口1作为数据收发口,也接收到全部数据。串口0的打印来看,先触发UART_II_RECV_RDY中断,再触发UART_II_THR_EMPTY中断。这表明当“接收数据可用”和“THR寄存器空”同时发生后,不仅中断标志位没有丢失,还根据中断优先级来先后处理不同的中断标志。
再次发送八个数据,然后通过按键触发GPIO中断,可以看出串口1能将全部的数据都接收到,串口0那边依次打印UART_II_RECV_RDY中断,再打印UART_II_RECV_TOUT中断,最后是UART_II_THR_EMPTY中断。
这也表明,同时产生多个中断标志位时,是不会丢失的,并且会根据中断优先级的顺序来触发中断的.