http://blog.sina.com.cn/s/blog_60cf05130100wrnb.html 2011 今天用按鍵中斷來控制LED燈閃爍。當(dāng)時有時候經(jīng)常會出現(xiàn)自己設(shè)置的LED閃爍是4下,但是結(jié)果卻是雙倍次數(shù),8下。很不解。源代碼如下。
Test3_Led1(void)
{
int K;
for(K=0;K<4;K++)
{
GPIO_SetBits(GPIOF,GPIO_Pin_6);
Mydelay_ms(1000);
GPIO_ResetBits(GPIOF,
GPIO_Pin_6);
Mydelay_ms(1000);
}
經(jīng)過研究,發(fā)現(xiàn)應(yīng)該是按鍵中斷沒做好,每次都產(chǎn)生2次的中斷。這是為什么?
中斷服務(wù)函數(shù)如下:
void EXTI0_IRQHandler(void)
{
//if (USART_GetITStatus(USART1, USART_IT_RXNE)
!= RESET)
if
(EXTI_GetITStatus( EXTI_Line0)!= RESET)
{
EXTI_ClearITPendingBit(EXTI_Line0);
//0
Led1_Flag++;//做測試用,發(fā)現(xiàn)每次都加了2.
Test3_Led1();
//EXTI_ClearITPendingBit(EXTI_Line0); //最好放在后面以免造成一清0后,因為按鍵延時又變1。
}
}
于是我繼續(xù)研究。發(fā)現(xiàn)原來是這樣的。
每次按鍵按下是一定有抖動的,抖動應(yīng)該會有很多下,5下以上是很正常的。按理論上將每次抖動都會長生一個脈沖,也就是一個中斷。應(yīng)該會有不下于5次中斷,為什么只響應(yīng)2次呢。
其實這個很簡單。因為相同管腳每次中斷都是相同優(yōu)先級的,所以即使產(chǎn)生中斷也不會響應(yīng)。只有在第一個中斷函數(shù)結(jié)束之后再來中斷才會響應(yīng)。雖然不響應(yīng),但是卻會改變中斷標志位。一定還有有人問,不是服務(wù)函數(shù)中把標志位清0了嗎?但是由于單片機執(zhí)行代碼速度很快,你請0之后,抖動如果還在那么就還會產(chǎn)生中斷號(雖然不響應(yīng))。只有在第一次中斷結(jié)束之后,再次去查看中斷號的時候才響應(yīng)。(注:響應(yīng)中斷首先是直接給總線發(fā)命令,不需要單片機查詢。因此采用中斷比采用查詢方式要好的多,減少了CPU的工作量。一旦中斷標志位變1,而且沒有優(yōu)先級更高的中斷在執(zhí)行,那么就會響應(yīng)中斷服務(wù)函數(shù))現(xiàn)在我們就是每次按鍵按下以后,雖然中斷標志經(jīng)過清0,但是由于抖動的存在,清0后很有可能再變1。當(dāng)時不會立刻執(zhí)行。只有當(dāng)這個中斷服務(wù)函數(shù)執(zhí)行完畢才執(zhí)行。所以每次都有可能調(diào)用2次中斷服務(wù)函數(shù)。
那么我們應(yīng)該怎么改變呢?
其實很簡單,最好在中斷服務(wù)函數(shù)開頭和結(jié)尾都把中斷標志位清0,那么即使第一次清0后再變1,但是第二次清0是一定會成功的?;蛘呤且淮吻?,但是在清0之前先延時,確保已經(jīng)沒有抖動。
我是選用第一種方案。
代碼如下:
void EXTI0_IRQHandler(void)
{
//if (USART_GetITStatus(USART1, USART_IT_RXNE)
!= RESET)
if
(EXTI_GetITStatus( EXTI_Line0)!= RESET)
{
Mydelay_ms(1000);
EXTI_ClearITPendingBit(EXTI_Line0);
//0
Led1_Flag++;//做測試用,發(fā)現(xiàn)每次都加了2.
Test3_Led1();
//EXTI_ClearITPendingBit(EXTI_Line0); //最好放在后面以免造成一清0后,因為按鍵延時又變1。
}
}
這樣即使有抖動每次也只會調(diào)用一次中斷服務(wù)函數(shù)。
全部代碼如下:
#include "stm32f10x.h"
//#include "stm32f10x_conf.h"
#include <stdio.h>
void delay(void)
{
__IO uint32_t i = 0;
for(i = 0xFF; i != 0;
i--)
{
}
}
Mydelay_ms(u16 a)
{
u16 i,j;
for(j=0;j<a;j++)
for(i=0;i<6000;i++);
}
Test1_GPIO(void)
{
GPIO_Write(GPIOC, 0xfc4f);
Mydelay_ms(1);
//delay();
GPIO_Write(GPIOC, 0xfd8f);
Mydelay_ms(1000);
GPIO_Write(GPIOC, 0xfd0f);
Mydelay_ms(1000);
GPIO_Write(GPIOC, 0xfe0f);
Mydelay_ms(1000);
}
Test2_GPIO(void)
{
GPIO_SetBits(GPIOF,GPIO_Pin_6);
Mydelay_ms(1000);
GPIO_ResetBits(GPIOF, GPIO_Pin_6) ;
Mydelay_ms(1000);
}
Test3_Led1(void)
{
int K;
for(K=0;K<4;K++)
{
GPIO_SetBits(GPIOF,GPIO_Pin_6);
Mydelay_ms(1000);
GPIO_ResetBits(GPIOF,
GPIO_Pin_6);
Mydelay_ms(1000);
}
}
Test3_Led2(void)
{
int t;
for(t=0;t<4;t++)
{
GPIO_SetBits(GPIOF,GPIO_Pin_7);
Mydelay_ms(1000);
GPIO_ResetBits(GPIOF,
GPIO_Pin_7);
Mydelay_ms(1000);
}
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
//GPIO_Init(GPIOA,&GPIO_InitStructure);
//GPIO_Init(GPIOB,&GPIO_InitStructure);
//GPIO_Init(GPIOC,&GPIO_InitStructure);
//GPIO_Init(GPIOD,&GPIO_InitStructure);
//GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOF,&GPIO_InitStructure);
}
//定時函數(shù)有有問題
void NVIC_Configuration(void)
{
NVIC_InitTypeDef
NVIC_InitStructure;
//中斷管理恢復(fù)默認參數(shù)
#ifdef
VECT_TAB_RAM //如果C/C++
Compiler\Preprocessor\Defined
symbols中的定義了VECT_TAB_RAM(見程序庫更改內(nèi)容的表格)
NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); //則在RAM調(diào)試
#else
//如果沒有定義VECT_TAB_RAM
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);//則在Flash里調(diào)試
#endif
//結(jié)束判斷語句
//以下為中斷的開啟過程,不是所有程序必須的。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//設(shè)置NVIC優(yōu)先級分組,方式。
//注:一共16個優(yōu)先級,分為搶占式和響應(yīng)式。兩種優(yōu)先級所占的數(shù)量由此代碼確定,NVIC_PriorityGroup_x可以是0、1、2、3、4,分別代表搶占優(yōu)先級有1、2、4、8、16個和響應(yīng)優(yōu)先級有16、8、4、2、1個。規(guī)定兩種優(yōu)先級的數(shù)量后,所有的中斷級別必須在其中選擇,搶占級別高的會打斷其他中斷優(yōu)先執(zhí)行,而響應(yīng)級別高的會在其他中斷執(zhí)行完優(yōu)先執(zhí)行。
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//中斷通道名;
//開中斷,中斷名稱見函數(shù)庫
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
//搶占優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelSubPriority =
1;
//響應(yīng)優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //啟動此通道的中斷
NVIC_Init(&NVIC_InitStructure);
//中斷初始化
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;//中斷通道名;
//開中斷,中斷名稱見函數(shù)庫
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//搶占優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelSubPriority =
0;
//響應(yīng)優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //啟動此通道的中斷
NVIC_Init(&NVIC_InitStructure);
}
void RCC_Configuration(void)
{
ErrorStatus HSEStartUpStatus;
// RCC_DeInit();
RCC_HSEConfig(RCC_HSE_ON);
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if(HSEStartUpStatus == SUCCESS)
{
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
FLASH_SetLatency(FLASH_Latency_2);
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);//Pll在最后設(shè)置
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while(RCC_GetSYSCLKSource() != 0x08)
{
}
}
////注:AHB主要負責(zé)外部存儲器時鐘。APB2負責(zé)AD,I/O,高級TIM,串口1。APB1負責(zé)DA,USB,SPI,I2C,CAN,串口2345,普通TIM?
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB
|RCC_APB2Periph_GPIOC
| RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE|
RCC_APB2Periph_GPIOF| RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN, ENABLE);
}
void GPIO_Configuration1(void)
{
GPIO_InitTypeDef
GPIO_InitStructure;
//GPIO狀態(tài)恢復(fù)默認參數(shù)
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_標號 | GPIO_Pin_標號
;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//輸出速度2MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOF, &GPIO_InitStructure);
//C組GPIO初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 |GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void EXTI_Configuration(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_ClearITPendingBit(EXTI_Line0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,
GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource8);
//GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource5);
//GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource6);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode =
EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger =
EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd =
ENABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line = EXTI_Line8;
EXTI_Init(&EXTI_InitStructure);
}
//下面這段是標準,一定要先配置中斷線,也就是管教選擇,才能進行線路選擇。
u16 Led1_Flag=0,Led2_Flag=0;
void EXTI0_IRQHandler(void);
int main(void)
{
SystemInit();
RCC_Configuration();
GPIO_Configuration1();
EXTI_Configuration();
NVIC_Configuration();
GPIO_SetBits(GPIOF,GPIO_Pin_6);
GPIO_SetBits(GPIOF,GPIO_Pin_7);
while (1)
{
//Mydelay_ms(1000);
//if(Led_Flag==1)
//{
//Mydelay_ms(1000);
//}
//GPIO_ResetBits(GPIOF,GPIO_Pin_6);
//Test2_GPIO();
}
}
void EXTI0_IRQHandler(void)
{
//if (USART_GetITStatus(USART1, USART_IT_RXNE)
!= RESET)
if
(EXTI_GetITStatus( EXTI_Line0)!= RESET)
{
EXTI_ClearITPendingBit(EXTI_Line0);
//0
Led1_Flag++;
Test3_Led1();
EXTI_ClearITPendingBit(EXTI_Line0); //最好放在后面以免造成一清0后,因為按鍵延時又變1。
}
}
void EXTI9_5_IRQHandler(void)
{
//if (USART_GetITStatus(USART1, USART_IT_RXNE)
!= RESET)
if
(EXTI_GetITStatus( EXTI_Line8)!= RESET)
{
EXTI_ClearITPendingBit(EXTI_Line8);
//0
Led2_Flag++;
Test3_Led2();
EXTI_ClearITPendingBit(EXTI_Line8); //最好放在后面以免造成一清0后,因為按鍵延時又變1。
}
}
//#ifdef USE_FULL_ASSERT
//void assert_failed(uint8_t* file, uint32_t line)
//{
// while (1)
//{
// }
//}
//#endif
|