STM32F103外部中断编程
STM32F103外部中断编程
中断,顾名思义就是停下手头的活,去干另外一件急活,干完急活然后回来继续干手头的活。
单片机和人一样,有时候也有更急的程序需要执行,执行完之后再回来执行之前正在执行的程序。今天就来试试如何让单片机中断当前任务,执行其他任务。
先看看我的实验板上做外部中断要用的资源:
如上图,板子的下面有三个独立按键,右下边有三个贴片的LED,其中这两部分的电路图如下图所示:
1、独立按键电路:
由上图可知,三个独立按键分别接在STM32的PB7、PB8、PB9引脚上。
2、LED电路:
由上图可知,三个LED分别接在STM32的PC1,、PC3、PC13引脚上,
我们规划是当按下按键K2的时候,就让灯D2闪烁一会儿。这时候K2就是外部中断,当按下后,触发中断服务函数,灯D2闪烁的程序就是中断服务函数。
现在打开Keil V5进行编程:
先在Manage Run-time Environment界面勾选以下选项:
勾选完成后,就开始进行编程:
1、中断初始化函数的编写:
void Exit_Configuration(void)
{
/*
*定义结构体
*/
EXTI_InitTypeDef EXTI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/*
*时钟配置(要打开AFIO时钟)
*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO,ENABLE);
/*
*外部中断使用的GPIO的配置使用PB口的9引脚
*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*
*外部中断使用的GPIO引脚也就是PB口的9引脚连接到EXTILine9
*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource9);
/*
*配置外部中断
*/
EXTI_InitStructure.EXTI_Line =EXTI_Line9;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/*
*配置NVIC
*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
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);
}
2、中断初始化函数写好了之后,就要编写中断服务函数了:
void EXTI9_5_IRQHandler(void)
{
u16 aa=10;
if(EXTI_GetITStatus(EXTI_Line9)!=RESET){
//初始化灯所在的GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
//通过循环让灯闪烁
while (aa){
GPIO_SetBits(GPIOC,GPIO_Pin_3);
delay_us(100000);
GPIO_ResetBits(GPIOC,GPIO_Pin_3);
delay_us(100000);
aa--;
}
//清除中断标志
EXTI_ClearITPendingBit(EXTI_Line9);
//使灯的状态为灭
GPIO_SetBits(GPIOC,GPIO_Pin_3);
}
}
3、当然延时函数的实现使用的是系统滴答定时器,函数实现如下:
/*******************************************************************************
* 函 数 名 : delay_us
* 函数功能 : 延时函数,延时us
* 输 入 : i
* 输 出 : 无
*******************************************************************************/
void delay_us(u32 i)
{
u32 temp;
SysTick->LOAD=9*i; //设置重装数值, 72MHZ时
SysTick->CTRL=0X01; //使能,减到零是无动作,采用外部时钟源
SysTick->VAL=0; //清零计数器
do
{
temp=SysTick->CTRL; //读取当前倒计数值
}
while((temp&0x01)&&(!(temp&(1<<16)))); //等待时间到达
SysTick->CTRL=0; //关闭计数器
SysTick->VAL=0; //清空计数器
}
4、以上都完成后我们就可以编写main函数了,main函数非常简单,只要初始化外部中断,然后就可以空循环等待中断。当然在正常项目中主程序一般不会空循环。main函数如下;
int main(){
Exit_Configuration();
while(1)
{
;
}
}
当程序编写完成之后我们下载到板子上进行试验,按下按键Key2,发现灯并没有像我们想象的那样闪烁,为什么呢?回头查看程序:
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;这句的意思在官方库函数文档中的解释是:
也就是说可以设置中断为上升沿中断还是下降沿中断还是上升沿和下降沿都产生中断。回头看看我们的硬件电路:
PB9通过一个按键接地,也就是当按键按下去之后,PB9的输入电平为低电平,而原本我们程序中初始化GPIO的语句如下:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO的模式配置是浮空输入,这时候没有信号输入的时候相当于是低电平,而我们再按下按键的时候,仍然是低电平,所以并没有产生上升沿或者下降沿,这样的话我们就需要对电路进行修改,怎么修改呢,在PB9上连接一个大的电阻连接到VCC,这样当按键没有按下去的时候,PB9输入高电平,当按键按下去之后,PB9输入低电平,相当于触发了外部中断。这个我们添加的电阻,就是上拉电阻,在我之前的电子元器件的使用之电阻的使用中有讲解。
通过以上的操作就可以看到,当你按下按键之后,灯如你所愿的闪烁起来了。这就是STM32的外部中断使用过程。
总结:
要使用STM32的外部中断,需要以下几个步骤:
1、使用RCC_APB2PeriphClockCmd()函数配置时钟
2、使用GPIO_EXTILineConfig()函数连接中断产生的引脚
3、使用EXTI_Init( )函数配置外部中断
4、使用NVIC_Init()配置中断优先级等
5、编写中断服务函数EXTI9_5_IRQHandler()
其中大家可能会疑惑,中断服务函数的名字中的9_5是什么意思呢?
PxN管脚共用外部中断线EXTIN和外部中断向量EXTIN_IRQn和中断服务程序入口EXTIN_IRQHandler,其中[9...5]共用EXTI9_5_IRQn和EXTI9_5_IRQHandler、[15...10]共用EXTI15_10_IRQn和EXTI15_10_IRQHandler,这样的话不管我们用PA9还是PB9,都是用EXTI9_5_IRQHandler这个函数。
从上面这张图可以看出上面讲的是什么意思了
而EXTI9_5_IRQn用在什么地方呢?大家返回去看配置NVIC的程序:
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
如果觉得有用,请关注微信公众号: