一、前提說明
????????一開始寫單片機程序的時候不太清楚空閑中斷這個東西,每次用串口接收數據,都要再開一個定時器,在定時器內進行倒計時,每次接收數據就重置計時時間,計時結束就觸發中斷,再判斷所有接收的數據,當然這種方法也并不過時,因為不是所有單片機都有空閑中斷這個東西的,空閑中斷實際是為開發者串口接收數據提供了部分便利而已,無論是用定時器還是空閑中斷原理實際都是一樣的。另外我們要知道很多32單片機都是使用的Arm核心,而外設部分是單片機廠商自己設計的,所以各家的設計思想和實現方式也各有不同,切勿以固定思維去判斷。
????????這里我只對比兩款常用單片機的空閑中斷,只是為了說明不同品牌實現空閑中斷的方式也不同,但作用都是差不多的。
1、GD32單片機
設計思想:只在接收完一組數據之后觸發一次空閑
用法1:
? ? ? ? 初始化直接打開接收中斷和空閑中斷,在一組串口數據接收完成就會觸發一次空閑中斷,然后在空閑中斷內對數據進行處理,特別注意不但要清標志位還要再額外讀一下數據寄存器。
用法2:
? ? ? ? 初始化關閉空閑中斷,打開接收中斷,在串口接收中斷內開啟空閑中斷,在空閑中斷內關閉空閑中斷,這樣就可以保證不會一直進入空閑中斷,然后在空閑中斷內對數據進行處理。
2、STM32單片機
設計思想:只在接收完一組數據之后觸發一次空閑
用法1:
? ? ? ? 初始化直接打開接收中斷和空閑中斷,在一組串口數據接收完成就會觸發一次空閑中斷,然后在空閑中斷內對數據進行處理,特別注意不但要清標志位還要再額外讀一下數據寄存器。
注意:gd32單片機的方法二并不適用與stm32
3、其他單片機
另外還有一些單片機品牌連空閑中斷都沒有設計的,完全不考慮開發者的感受,這里抨擊一下,我就不點名了。
二、示例代碼
1、GD32
//串口初始化
void DW_PortCfg(void)
{rcu_periph_clock_enable(RCU_GPIOB);rcu_periph_clock_enable(RCU_AF);gpio_init(DW_MODULE_PORT,DW_URXD_MODE,GPIO_OSPEED_50MHZ,DW_URXD_PIN);gpio_init(DW_MODULE_PORT,DW_UTXD_MODE,GPIO_OSPEED_50MHZ,DW_UTXD_PIN);rcu_periph_clock_enable(DW_UART_RCU);usart_deinit(DW_UART);usart_baudrate_set(DW_UART,DW_BUAD_RATE);usart_stop_bit_set(DW_UART,USART_STB_1BIT);usart_word_length_set(DW_UART,USART_WL_8BIT);usart_parity_config(DW_UART,USART_PM_NONE);usart_hardware_flow_rts_config(DW_UART, USART_RTS_DISABLE);usart_hardware_flow_cts_config(DW_UART, USART_CTS_DISABLE);usart_receive_config(DW_UART, USART_RECEIVE_ENABLE);usart_transmit_config(DW_UART, USART_TRANSMIT_ENABLE);usart_enable(DW_UART);usart_interrupt_enable(DW_UART,USART_INT_RBNE);nvic_irq_enable(DW_UART_IRQn,0,1);
}
//中斷服務函數
void USART2_IRQHandler(void){if(usart_interrupt_flag_get(DW_UART,USART_INT_FLAG_RBNE)==SET){dwRecvChar(usart_data_receive(DW_UART));usart_interrupt_flag_clear(DW_UART,USART_INT_FLAG_RBNE);usart_interrupt_enable(DW_UART,USART_INT_IDLE);}else if(usart_interrupt_flag_get(DW_UART,USART_INT_FLAG_IDLE)){dwRecvFinish();usart_interrupt_flag_clear(DW_UART,USART_INT_FLAG_IDLE);usart_interrupt_disable(DW_UART,USART_INT_IDLE);}
}
2、STM32
void uart_init(u32 bound){//GPIO端口設置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA時鐘//USART1_TX GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9//USART1_RX GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 //Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶占優先級3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先級3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器//USART 初始化設置USART_InitStructure.USART_BaudRate = bound;//串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數據格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數據流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發模式USART_Init(USART1, &USART_InitStructure); //初始化串口1USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟串口接受中斷USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//開啟空閑中斷USART_Cmd(USART1, ENABLE);
}void USART1_IRQHandler(void) //串口1中斷服務程序
{uint16_t data; if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET) { USART2_RX_BUF[length++] = USART2->DR & 0x0FF; //接收數據}if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET) { data = USART1->SR; //清空空閑中斷標志位操作data = USART1->DR; }
}