http://blog.sina.com.cn/s/blog_60cf05130100wrnb.html
2011
今天用按键中断来控制LED灯闪烁。当时有时候经常会出现自己设置的LED闪烁是4下,但是结果却是双倍次数,8下。很不解。源代码如下。
Test3_Led1(void)
{
经过研究,发现应该是按键中断没做好,每次都产生2次的中断。这是为什么?
中断服务函数如下:
于是我继续研究。发现原来是这样的。
每次按键按下是一定有抖动的,抖动应该会有很多下,5下以上是很正常的。按理论上将每次抖动都会长生一个脉冲,也就是一个中断。应该会有不下于5次中断,为什么只响应2次呢。
其实这个很简单。因为相同管脚每次中断都是相同优先级的,所以即使产生中断也不会响应。只有在第一个中断函数结束之后再来中断才会响应。虽然不响应,但是却会改变中断标志位。一定还有有人问,不是服务函数中把标志位清0了吗?但是由于单片机执行代码速度很快,你请0之后,抖动如果还在那么就还会产生中断号(虽然不响应)。只有在第一次中断结束之后,再次去查看中断号的时候才响应。(注:响应中断首先是直接给总线发命令,不需要单片机查询。因此采用中断比采用查询方式要好的多,减少了CPU的工作量。一旦中断标志位变1,而且没有优先级更高的中断在执行,那么就会响应中断服务函数)现在我们就是每次按键按下以后,虽然中断标志经过清0,但是由于抖动的存在,清0后很有可能再变1。当时不会立刻执行。只有当这个中断服务函数执行完毕才执行。所以每次都有可能调用2次中断服务函数。
那么我们应该怎么改变呢?
其实很简单,最好在中断服务函数开头和结尾都把中断标志位清0,那么即使第一次清0后再变1,但是第二次清0是一定会成功的。或者是一次清0,但是在清0之前先延时,确保已经没有抖动。
我是选用第一种方案。
代码如下:
Mydelay_ms(1000);
这样即使有抖动每次也只会调用一次中断服务函数。
全部代码如下:
#include "stm32f10x.h"
//#include "stm32f10x_conf.h"
#include <stdio.h>
void delay(void)
{
}
Mydelay_ms(u16 a)
{
}
Test1_GPIO(void)
{
}
Test2_GPIO(void)
{
}
Test3_Led1(void)
{
}
Test3_Led2(void)
void GPIO_Configuration(void)
{
}
//定时函数有有问题
{
NVIC_InitTypeDefNVIC_InitStructure;
#ifdef
NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); //则在RAM调试
#else
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_IRQChannelPreemptionPrio
NVIC_InitStructure.NVIC_IRQChannelSubPriority =1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //启动此通道的中断
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;//中断通道名;//开中断,中断名称见函数库
NVIC_InitStructure.NVIC_IRQChannelPreemptionPrio
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)
{
}
////注: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_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN, ENABLE);
}
void GPIO_Configuration1(void)
{
GPIO_InitTypeDefGPIO_InitStructure;
//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)
{
GPIO_SetBits(GPIOF,GPIO_Pin_6);
//}
}
//#ifdef
//void assert_failed(uint8_t* file, uint32_t line)
//{
//}
//#endif
联系客服