实现功能:外部中断线0(PA0)与线15(PA15)分别连接到两个按钮,一个按钮用于产生上升沿,另一个用于产生下降沿,两个中断函数里均对PA8口连接的led灯取反,同时PD2连接的led灯随意延时取反指示程序运行。
这次我用了官方提供的例程来构建自己的工程,这样可以省下不少时间,而且减少了出错率,调试起来容易多了。
首先是stm32中断与外部中断的概念。
ARM Coetex-M3内核共支持256个中断,其中16个内部中断,240个外部中断和可编程的256级中断优先级的设置。STM32目前支持的中断共84个(16个内部+68个外部),还有16级可编程的中断优先级的设置,仅使用中断优先级设置8bit中的高4位。
STM32可支持68个中断通道,已经固定分配给相应的外部设备,每个中断通道都具备自己的中断优先级控制字节PRI_n(8位,但是STM32中只使用4位,高4位有效),每4个通道的8位中断优先级控制字构成一个32位的优先级寄存器。68个通道的优先级控制字至少构成17个32位的优先级寄存器。
4bit的中断优先级可以分成2部分,从高位看,前面定义的是抢占式优先级,后面是响应优先级。4bit一共可以分成5组。
第0组:所有4bit用于指定响应优先级;
第1组:最高1位用于指定抢占式优先级,后面3位用于指定响应优先级;
第2组:最高2位用于指定抢占式优先级,后面2位用于指定响应优先级;
第3组:最高3位用于指定抢占式优先级,后面1位用于指定响应优先级;
第4组:所有4位用于指定抢占式优先级。
所谓抢占式优先级和响应优先级,他们之间的关系是:具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套。
1、 当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后 才能被处理。
2、 如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;
3、 3如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。每一个中断源都必须定义2个优先级。
有几点需要注意的是:
1)如果指定的抢占式优先级别或响应优先级别超出了选定的优先级分组所限定的范围,将可能得到意想不到的结果;
2)抢占式优先级别相同的中断源之间没有嵌套关系;
3)如果某个中断源被指定为某个抢占式优先级别,又没有其它中断源处于同一个抢占式优先级别,则可以为这个中断源指定任意有效的响应优先级别。
STM32有20个外部中断线,其中EXTI0-EXTI15给I/O端口使用,对应如下图:
EXTI线16连接到PVD输出
EXTI线17连接到RTC闹钟事件
EXTI线18连接到USB唤醒事件
EXTI线19连接到以太网唤醒事件(只适用于互联型产品)。
其中,每个外部中断都具有对应的硬件机制,如下图:
STM32中,每一个GPIO都可以触发一个外部中断,但是,GPIO的中断是以组位一个单位的,同组间的外部中断同一时间只能使用一个。比如说,PA0,PB0,PC0,PD0,PE0,PF0,PG0这些为1组,如果我们使用PA0作为外部中断源,那么别的就不能够再使用了,在此情况下,我们只能使用类似于PB1,PC2这种末端序号不同的外部中断源。每一组使用一个中断标志EXTIx。EXTI0 – EXTI4这5个外部中断有着自己的单独的中断响应函数,EXTI5-9共用一个中断响应函数,EXTI10-15共用一个中断响应函数。但是在中断函数里面可以查询中断标志,从而判断是哪个外部中断,虽然共用同一个中断号,但是具有独立的中断标志位EXTI_LineX,X为0~15。
使用外部中断需要准备的工作如下:
1、初始化IO口为输入。
这一步设置你要作为外部中断输入的IO口的状态,可以设置为上拉/下拉输入,也可以设置为浮空输入,但浮空的时候外部一定要带上拉,或者下拉电阻。否则可能导致中断不停的触发。在干扰较大的地方,就算使用了上拉/下拉,也建议使用外部上拉/下拉电阻,这样可以一定程度防止外部干扰带来的影响。
2、开启IO口的复用时钟。
3、开启与该IO口相对应的线上中断/事件,设置触发条件。
4、配置NVIC,并使能中断。
5、编写相应中断函数。
下面是main.c文件源码
//main.c
#include "stm32f10x.h"
EXTI_InitTypeDef EXTI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
void GPIO_Config(void);
void EXTI0_Config(void);
void EXTI15_10_Config(void);
void delay(void);
int main(void)
{
GPIO_Config();
/* Configure PA.00 in interrupt mode */
EXTI0_Config();
/* Configure PA.15 in interrupt mode */
EXTI15_10_Config();
while (1)
{
GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_RESET);
delay();
GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_SET);
delay();
}
}
void delay(void)
{
u16 i,j;
for(i=0;i<1000;i++)
for(j=0;j<1000;j++)
;
}
void GPIO_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE);
/* Configure PA.08 pin as output */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure PD.02 pin as output */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
/**
* @brief Configure PA.00 in interrupt mode
* @param None
* @retval None
*/
void EXTI0_Config(void)
{
/* Enable GPIOA clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* Configure PA.00 pin as input*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable AFIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
/* Connect EXTI0 Line to PA.00 pin */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
/* Configure EXTI0 line */
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/* Enable and set EXTI0 Interrupt to the lowest priority */
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief Configure PA.15 in interrupt mode
* @param None
* @retval None
*/
void EXTI15_10_Config(void)
{
/* Enable GPIOA clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* Configure PA.15 pin as input*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable AFIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
/* Connect EXTI15 Line to PA.15 pin */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource15);
/* Configure EXTI15 line */
EXTI_InitStructure.EXTI_Line = EXTI_Line15;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/* Enable and set EXTI15_10 Interrupt to the lowest priority */
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0E;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
下面是中断函数的源码。
//stm32f10x_it.c
#include "stm32f10x_it.h"
u8 flag=0;
/**
* @brief This function handle External line 0 interrupt request.
* @param None
* @retval None
*/
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatu(EXTI_Line0) != RESET)
{
if(flag)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_RESET);
flag=0;
}
else
{
GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_SET);
flag=1;
}
/* Clear the EXTI line 0 pending bit */
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
/**
* @brief This function handle External lines 15 to 10 interrupt request.
* @param None
* @retval None
*/
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatu(EXTI_Line15) != RESET)
{
if(flag)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_RESET);
flag=0;
}
else
{
GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_SET);
flag=1;
}
/* Clear the EXTI line 15 pending bit */
EXTI_ClearITPendingBit(EXTI_Line15);
}
}
编程配置后,可以看到连接到PD2上的led不断闪烁,按下PA0和PA15上任意一个按钮均可取反连接到PA8上的led灯。
用户468998 2013-7-21 10:52
用户603378 2012-12-30 16:35