http://blog.sina.com.cn/s/blog_60cf05130100wrnb.html 2011 今天用按键中断来控制LED灯闪烁。当时有时候经常会出现自己设置的LED闪烁是4下,但是结果却是双倍次数,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);
}
经过研究,发现应该是按键中断没做好,每次都产生2次的中断。这是为什么?
中断服务函数如下:
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++;//做测试用,发现每次都加了2.
Test3_Led1();
//EXTI_ClearITPendingBit(EXTI_Line0); //最好放在后面以免造成一清0后,因为按键延时又变1。
}
}
于是我继续研究。发现原来是这样的。
每次按键按下是一定有抖动的,抖动应该会有很多下,5下以上是很正常的。按理论上将每次抖动都会长生一个脉冲,也就是一个中断。应该会有不下于5次中断,为什么只响应2次呢。
其实这个很简单。因为相同管脚每次中断都是相同优先级的,所以即使产生中断也不会响应。只有在第一个中断函数结束之后再来中断才会响应。虽然不响应,但是却会改变中断标志位。一定还有有人问,不是服务函数中把标志位清0了吗?但是由于单片机执行代码速度很快,你请0之后,抖动如果还在那么就还会产生中断号(虽然不响应)。只有在第一次中断结束之后,再次去查看中断号的时候才响应。(注:响应中断首先是直接给总线发命令,不需要单片机查询。因此采用中断比采用查询方式要好的多,减少了CPU的工作量。一旦中断标志位变1,而且没有优先级更高的中断在执行,那么就会响应中断服务函数)现在我们就是每次按键按下以后,虽然中断标志经过清0,但是由于抖动的存在,清0后很有可能再变1。当时不会立刻执行。只有当这个中断服务函数执行完毕才执行。所以每次都有可能调用2次中断服务函数。
那么我们应该怎么改变呢?
其实很简单,最好在中断服务函数开头和结尾都把中断标志位清0,那么即使第一次清0后再变1,但是第二次清0是一定会成功的。或者是一次清0,但是在清0之前先延时,确保已经没有抖动。
我是选用第一种方案。
代码如下:
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++;//做测试用,发现每次都加了2.
Test3_Led1();
//EXTI_ClearITPendingBit(EXTI_Line0); //最好放在后面以免造成一清0后,因为按键延时又变1。
}
}
这样即使有抖动每次也只会调用一次中断服务函数。
全部代码如下:
#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);
}
//定时函数有有问题
void NVIC_Configuration(void)
{
NVIC_InitTypeDef
NVIC_InitStructure;
//中断管理恢复默认参数
#ifdef
VECT_TAB_RAM //如果C/C++
Compiler\Preprocessor\Defined
symbols中的定义了VECT_TAB_RAM(见程序库更改内容的表格)
NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); //则在RAM调试
#else
//如果没有定义VECT_TAB_RAM
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);//则在Flash里调试
#endif
//结束判断语句
//以下为中断的开启过程,不是所有程序必须的。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//设置NVIC优先级分组,方式。
//注:一共16个优先级,分为抢占式和响应式。两种优先级所占的数量由此代码确定,NVIC_PriorityGroup_x可以是0、1、2、3、4,分别代表抢占优先级有1、2、4、8、16个和响应优先级有16、8、4、2、1个。规定两种优先级的数量后,所有的中断级别必须在其中选择,抢占级别高的会打断其他中断优先执行,而响应级别高的会在其他中断执行完优先执行。
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//中断通道名;
//开中断,中断名称见函数库
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =
1;
//响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //启动此通道的中断
NVIC_Init(&NVIC_InitStructure);
//中断初始化
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;//中断通道名;
//开中断,中断名称见函数库
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =
0;
//响应优先级
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在最后设置
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while(RCC_GetSYSCLKSource() != 0x08)
{
}
}
////注:AHB主要负责外部存储器时钟。APB2负责AD,I/O,高级TIM,串口1。APB1负责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状态恢复默认参数
//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
|