当前位置:文档之家› S.D.Lu的STM32学习笔记(7)外部中断实验

S.D.Lu的STM32学习笔记(7)外部中断实验

S.D.Lu的STM32学习笔记(7)外部中断实验
S.D.Lu的STM32学习笔记(7)外部中断实验

S.D.Lu的STM32学习笔记(7)外部中断实验

概述:

中断的应用是单片机初学者遇到的第一道最难的障碍。个人认为,是否掌握中断的使用方法也可以作为是否已经入门的判断依据。

ARM Coetex-M3内核的中断系统很庞大,支持多达240个中断通道(不包含16个CM3的中断线)。但是STM32并没有使用CM3核的所有资源,目前STM32(非互联型)可支持60个中断通道,互联型支持68个。

本篇笔记将从较容易学习的外部中断入手,详细描述STM32的中断编程方法及步骤。

由于STM32外部中断的设置相对于51、AVR等单片机来说要复杂,所以在实验之前先沿着信号路径对相关寄存器进行介绍和分析。

STM32外部中断:

STM32有20个外部中断源,如下:

16个GPIO输入中断;

PVD输出;

RTC闹钟事件;

USB唤醒事件;

以太网唤醒事件(只适用于互联型产品)。

(本篇讨论的是16个GPIO输入中断。)

GPIO输入中断虽然有16个输入通道,但是只占用了7个中断向量。EXTI0~EXTI4各占用一个中断向量,EXTI5~9共用一个,EXTI10~15共用一个。所以在编程的时候EXTI5~9将共用一个中断函数,EXTI10~15共用一个中断函数。

这16个外部中断和GPIO的映射图如下。

由上图可以看出,这16个中断的输入引脚是可以由用户自己重新定义的。当然是有限制的,例如:EXTI0通道,只能选择PA0,PB0,PC0,PD0,PE0,PF0,PG0这7个引脚中的一个;而EXTI1只能选择名称为Px1的7个引脚中的一个(x可取A~G中的一个);依次类推,EXTI2只能选择Px2引脚;……。具体选择那个引脚,可由寄存器AFIO_EXTICRx(x可取1~4)的相应位设置。具体操作可参考本文后面的程序。

上图说明了EXTIx的输入通道选择方法,下面将介绍相关内部寄存器的作用及配置。

上图是外部中断/事件控制器的框图。该框图涉及6个寄存器,(按信号路径)分别是:下降沿触发选择寄存器(EXTI_FTSR);

上升沿触发选择寄存器(EXTI_RTSR);

软件中断事件寄存器(EXTI_SWIER);

挂起寄存器(EXTI_PR);

中断屏蔽寄存器(EXTI_IMR);

事件屏蔽寄存器(EXTI_EMR)。

为了便于理解,我们先将上图简化。

首先,左下角的“脉冲发生器”和“事件屏蔽寄存器”是用于产生事件脉冲信号的,为其它外设模块提供触发信号。在本篇笔记我们不涉及该内容,可将这部分模块忽略。欲了解中断和事件的区别,看参考《STM32中外部中断与外部事件》一文。

其次,中间的“软件中断事件寄存器”的作用是,为用户提供一个通过软件产生中断的途径,在此也可暂时忽略。

“请求挂起寄存器”其实就是中断标志寄存器。

简化后得到下图:

所以,在外部中断控制器部分我们只需要操作4个寄存器即可:

下降沿触发选择寄存器(EXTI_FTSR);

上升沿触发选择寄存器(EXTI_RTSR);

挂起寄存器(EXTI_PR);

中断屏蔽寄存器(EXTI_IMR)。

简化框图中,最左边的箭头“至NVIC中断控制器”,指向下一个中断处理模块。NVIC的全称是嵌套向量中断控制器。这是STM32外部中断设置最难理解的部分。NVIC对CM3核支持的所有240个通道的中断嵌套进行控制。

我们先了解NVIC的相关寄存器。打开固件库的头文件core_cm3.h,查找“NVIC”,找到如下结构体定义:

由NVIC的结构体定义可以知道,其中有7个可用的寄存器组:

__IO uint32_t ISER[8]:中断使能设置寄存器组;

__IO uint32_t ICER[8]:中断使能清除寄存器组;

__IO uint32_t ISPR[8]:中断挂起设置寄存器组;

__IO uint32_t ICPR[8]:中断挂起清除寄存器组;

__IO uint32_t IABR[8]:中断激活位寄存器组;

__IO uint8_t IP[240]:中断优先级寄存器组,8bit宽度;

__O uint32_t STIR:软件触发中断寄存器组。

这些寄存器的功能描述可参考《Cortex-M3技术参考手册》(广州周立功公司)第8章。STM32最多只有68个中断通道,所以没有使用上述的全部寄存器。

对各个中断的优先级设置就是通过写这些寄存器组完成的。需要说明一点,STM32的中断优先级寄存器只用了8位宽度的高4位,而这4位的意义则由寄存器AIRCR的[10:8]这3个位的设置决定。AIRCR寄存器的说明请参考《Cortex-M3技术参考手册》正文第83~84页。AIRCR被定义在SCB_Type结构体中,在core_cm3.h第160行。

关于中断优先级设置和中断过程的更多详细说明,可参考《STM32中断优先级相关概念与使用笔记》(华东师大)和《Cortex-M3 权威指南》(宋岩译)第八章的“中断建立全过程的演示”一节。

由上述介绍和分析,可以总结出STM32外部中断要经过3个部分模块设置处理,然后才进入到中断服务程序的处理,其框图如下:

通过上面的介绍和分析,我们对STM32的外部中断有了清楚的了解,在编程的时候按照其信号路径,根据需要设置相关的寄存器即可。

实验目的:

通过实验掌握外部中断的编程方法。

本篇将在前一篇的基础上进行,使用相同的硬件,且实现的功能相同,都是用按键控制LED的亮灭。为了简化笔记的描述,对多余的串口代码并没有进行删减。

步骤1:修改main.c文件

a)、将函数KEY_Scan()删除。

b)、添加中断初始化函数:

//函数功能:外部中断初始化=====

void EXTIX_Init(void)

{

//定义EXTI和NVIC结构体变量

EXTI_InitTypeDef EXTI_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

KEY_PORT_Init(); //初始化按键I/O端口

//步骤1:将EXTI线路的引脚重新映射

//AFIO-> EXTICR[3] = 0x00000000;//若使用的是PA口,此句可以省略,默认映射到PA口

//步骤2:设置EXTI线路的模式参数

EXTI_InitStructure.EXTI_Line = EXTI_Line13; //选中要设置的EXTI线路

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//设置EXTI线路为中断请求

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//设置输入线路下降沿为中断请求

EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能EXTI线路中断

EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化EXTI的寄存器

//步骤3:设置优先级分组

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 1bit抢占优先级,3bit响应优先级

//步骤4:设置EXTI线路的优先级,并使能中断线路

NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //选择中断线通道

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //设置抢占优先级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //设置响应优先级

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断线路

NVIC_Init(&NVIC_InitStructure);//根据NVIC_InitStruct中指定的参数初始化NVIC寄存器}

c)、添加中断服务函数:

//外部中断15~10服务程序

void EXTI15_10_IRQHandler(void)

{

Delay_ms(10); //消抖

if(0==GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_13)) //按键确认

{

if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_8))//根据LED当前输出状态进行控制

{

GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_RESET); //PA8 = 0

}

else

{

GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_SET); //PA8 = 1

}

while(0==GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_13)); //等待按键释放

}

EXTI_ClearFlag(EXTI_Line13); //清除EXTI_LINE13上的中断标志位

}

d)、将main函数改为如下内容:

//函数功能:主函数

int main(void)

{

RCC_Configuration(); //初始化外设时钟

PORT_Init(); //初始化I/O端口

SysTick_Init(); //初始化SysTick定时器

USART_Configuration(); //初始化串口

EXTIX_Init(); //初始化外部中断

while(1)

{

}

}

步骤2:添加stm32f10x_exti.c文件到工程

因为要用到EXTI的相关库函数,所以需要将EXTI相关的文件添加到工程中。

步骤3:编译,下载。

将工程进行编译,下载到芯片上,实现的功能和前一篇一样,按一下按键,LED的状态就发生一次翻转。至此实验完成。

实验总结

通过实验,我们学习了如何使用STM32的GPIO外部中断功能。

其实GPIO外部中断使用方法比较容易掌握,只要沿着中断信号线一路设置遇到的寄存器,最后编写中断服务程序即可,当然还要设置中断优先级。

使用STM32的GPIO外部中断初始化基本操作步骤如下:(中断处理步骤见中断函数) 初始化中断引脚为输入;(GPIOx)

重新映射中断引脚到中断线上;(AFIO_EXTICRx)

设置中断线的触发模式;(EXTI_RTSR和EXTI_FTSR)

使能中断线;(EXTI_IMR)

设置优先级分组方式;(SCB_AIRCR)

设置中断优先级;(NVIC_IPx)

使能中断通道。(NVIC_ISERx)

本篇笔记因为使用了库函数,很多操作都被封装起来了,所以在程序中看不出具体对哪些寄存器进行了操作。本文后面的附录,将给出直接操作寄存器设置外部中断的程序,从代码中可以清晰的看到相关寄存器的设置过程。为了简化笔记描述,对GPIO 的操作仍然沿用库函数。

最后需要说明的一点是,和其它单片机的编程不同,STM32的中断函数名称已经由系统指定,用户不能更改。其格式在文件stm32f10x_it.c的后面给出,如下图。

void PPP_IRQHandler(void)

{

}

其中PPP是外设名称,根据函数的说明部分,可参考启动文件startup_stm32f10x_xx.s 了解各个外设中断的命名。在本系列学习笔记中,我们一直使用的启动文件是startup_stm32f10x_md.s。打开该文件会发现,在第62~121行按次序列出了所有中断函数的名称。所以,以后写中断函数的时候,只要照着写就行了。

附录:

//函数功能:外部中断初始化=====

void EXTIX_Init(void)

{

KEY_PORT_Init(); //初始化按键I/O端口

//步骤1:将EXTI线路的引脚重新映射

AFIO-> EXTICR[3] = 0x00000000;//若使用的是PA口,此句可以省略,默认映射到PA口

//步骤2:设置EXTI线路的模式参数

EXTI->FTSR|=1<<13; //设置输入线路下降沿为中断请求

EXTI->IMR|=1<<13; //使能EXTI线路中断

//步骤3:设置优先级分组

SCB->AIRCR = (uint32_t)0x05FA0000 | (uint32_t)0x600; // 1bit抢占优先级,3bit响应优先级

/*对AIRCR 写操作时,须在高16位写入0x05FA,操作才会有效*/

//步骤4:设置EXTI15_10中断通道的优先级,并使能中断线路

/*在此说明一下,EXTI15_10的中断号是40,请参考《STM32数据手册》中文版(对应英文的RM0008文档),第9章表55*/

NVIC->IP[40] = 0x00; //抢占优先级0,响应优先级0

NVIC->ISER[40/32] = 1<<(40%32); //使能中断线路

/*第40号中断的使能位在ISER[1]的BIT8位*/

}

//外部中断15~10服务程序

void EXTI15_10_IRQHandler(void)

{

Delay_ms(10); //消抖

if(0==GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_13)) //按键确认

{

if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_8))//根据LED当前输出状态进行控制

GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_RESET); //PA8 = 0

else

GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_SET); //PA8 = 1

while(0==GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_13)); //等待按键释放

}

EXTI->PR = 1<<13; //清除EXTI_LINE13上的中断标志位

}

作者:S.D.Lu

深圳

2013-2-20

相关主题
相关文档 最新文档