STM32单片机开发:按键中断实验
- STM32单片机开发
- 11天前
- 157热度
- 0评论
实验目标
前面一节课我们了解了STM32的中断机制,并学会了如何配置与中断相关的的寄存器,也了解了开启外部中断的具体配置步骤。
本节课我们应用前面学过的中断知识,通过按键触发中断来点亮发光二极管。两只发光二极管通过限流电阻接到PA8和PA9引脚,按键一端接地一端接到PC0引脚,PC0引脚默认高电平。当按键被按下后,触发EXTI0线中断,在EXTI0线中断处理函数内改变发光二极管的状态。具体实验电路见下图。
配置中断使能寄存器
STM32的所有GPIOx都引入到EXTI外部中断线上,所有的GPIO端口都能作为外部中断的输入源,GPIO与EXTI的连接方式见下图。
当前电路原理图的按键连接到PC0引脚,观察上图PA0~PG0引脚共同使用EXTI0中断线。我们需要从中断向量表中找到EXTI0中断线对应的中断号。打开startup_stm32f10x_hd.s文件(不同的单片机型号,其文件名称可能不同),查找EXTI0中断线对应的中断函数名称。
EXTI0中断线对应的中断号是6,中断处理函数为EXTI0_IRQHandler。NVIC_ISERx寄存器组为中断使能寄存器组,NVIC_ISER0配置0~31号中断的使能,要使能6号中断,需要配置NVIC_ISER0寄存器的第6个二进制位为1。下面是使能6号中断的代码:
// NVIC_ISER0寄存器的第6位写入1,启用6号中断
NVIC->ISER[0] |= NVIC_ISER_SETENA_6;
NVIC是一个NVIC_Type结构体指针,该结构体在core_cm3.h文件定义:
typedef struct
{
__IO uint32_t ISER[8]; /*Interrupt Set Enable Register*/
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; /*Interrupt Clear Enable Register*/
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; /*nterrupt Set Pending Register*/
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8]; /*Interrupt Clear Pending Register*/
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; /*Interrupt Active bit Register*/
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; /*Interrupt Priority Register (8Bit wide) */
uint32_t RESERVED5[644];
__O uint32_t STIR; /*Software Trigger Interrupt Register*/
} NVIC_Type;
它封装了NVIC中断控制器的相关寄存器,开发者通过该结构体可以直接访问ISERx、IP等寄存器。
NVIC_ISER_SETENA_0~NVIC_ISER_SETENA_31在stm32f10x.h文件定义,它是NVIC_ISER 寄存器的位定义,使用这些宏定义,可以简化对NVIC_ISER 寄存器位的赋值操作。
配置外部中断
外设接口通过控制中断屏蔽寄存器、请求挂起寄存器、软件中断事件寄存器、上升沿触发选择寄存器、下降沿触发选择寄存器进行中断处理。
配置中断屏蔽寄存器EXTI_IMR
中断屏蔽寄存器EXTI_IMR用于开放和屏蔽来自线x的中断请求,它是一个32位的寄存器,EXTI_IMR寄存器使用了0~19位,分别为MR0~MR19,MRx(x=0~19)置1,开放x线的中断请求,MRx置0,屏蔽该线的中断请求。
开放EXTI0中断线的代码为:
// 开放中断线EXTI0
EXTI->IMR |= EXTI_IMR_MR0;
宏定义EXTI_IMR_MR0~EXTI_IMR_MR19在stm32f103x6.h文件定义,它是EXTI_IMR寄存器的位定义,使用这些宏定义,可以简化对EXTI_IMR寄存器位的赋值操作。
配置下降沿触发选择寄存器
实验电路图按键一端接地,一端接PC0引脚,按键按下后PC0引脚接地,PCO引脚由高电平变换为低电平,因此需要配置下降沿触发选择寄存器。下降沿触发选择寄存器为EXTI_FTSR,它是一个32位的寄存器,0~19位有效,对应20条外部中断线。
配置EXTI0中断线下降沿触发选择寄存器的代码为:
// 中断线EXTI0采用下降沿触发
EXTI->FTSR |= EXTI_FTSR_TR0;
宏定义EXTI_FTSR_TR0~EXTI_FTSR_TR19在stm32f103x6.h文件定义,它是EXTI_FTSR寄存器的位定义,使用这些宏定义,可以简化对EXTI_FTSR寄存器位的赋值操作。
配置EXTI_PR寄存器
当中断发生后,EXTI_PR寄存器的中断位被置1,对应的中断处理函数被调用,中断处理函数需要对中断位再次写入1,以清除该位的中断状态。
EXTI_PR是一个32位的寄存器,0~19位有效,PRx(x=0~19)位为0表示对应线路没有触发请求,1表示有触发请求。
配置EXTI_PR寄存器的代码如下:
void EXTI0_IRQHandler(void)
{
if( flag )
ControlLed(0);
else
ControlLed(1);
//清出中断状态
EXTI->PR |= 0x00001;
}
EXTI0_IRQHandler是EXTI0中断处理函数,EXTI0对应EXTI_PR寄存器的第0位,对第0位进行写1操作。
配置AFIO时钟
要让中断正常工作,需要使能AFIO时钟,使能AFIO寄存器主要是复用PC0引脚作为中断源。使能AFIO时钟的代码如下:
// 使能AFIO时钟
RCC->APB2ENR=1<<0;
APB2ENR寄存器的第0位置1,使能AFIO时钟。
配置有效的GPIO中断引脚
当前电路原理图的按键连接到PC0引脚,而PA0~PG0引脚共同使用EXTI0中断线,因此需要配置PC0引脚为中断引脚。AFIO_EXTRCR0寄存器管理0—3号中断线,用于配置有效的GPIO中断引脚。
AFIO_EXTRCR0寄存器配置代码如下:
// PC0引脚有效
AFIO->EXTICR[0] = 0x02;
配置中断优先级
STM32的中断优先级由中断分组、主优先级和次优先级构成,中断分组的配置决定着主优先级与次优先级的配置,在没有配置中断分组的情况下,就配置主优先级和次优先级是无意义的。
STM32的中断提供了五个优先级分组:
组0: 0位主优先级,4位次优先级
组1: 1位主优先级,3位次优先级
组2: 2位主优先级,2位次优先级
组3: 3位主优先级,1位次优先级
组4: 4位主优先级,0位次优先级
SCB_AIRCR寄存器用于配置中断优先级分组,配置代码如下:
// 配置中断优先级分组,8:10位
// 分组模式:100 主:次=3:1
SCB->AIRCR = 0x05FA0000 | 0x400;
配置优先级代码如下:
NVIC->IP[0] = 0xf0;
IP[0]对应EXTI0中断线,IP是8位的寄存器,但只使用了高4位。
构建工程
构建STM32CubeIDE工程
启动STM32CubeIDE,新建STM32工程,工程名称为“STM32InterruptLED”,单片机选择STM32F103R6。
构建Proteus8工程
启动proteus8建立STM32仿真工程,添加STM32F103R6芯片,设计电路原理图:
编写代码
将下面的代码复制到main.c文件内。
void InitGPIO();
void ControlLed(int open);
void InitNVIC();
// 二极管点亮标志
int flag = 0;
// 延迟函数
void delay(unsigned int millisecond)
{
unsigned int i,j;
for(i=millisecond;i>0;i--)
for(j=120;j>0;j--);
}
// 程序主函数
int main()
{
// 配置中断寄存器
InitNVIC();
// 配置GPIO寄存器
InitGPIO();
// 关闭发光二极管
ControlLed(0);
while(1)
{
}
}
// 控制LED
void ControlLed(int open)
{
// 打开LED
if( open )
{
// PA9设置为低电平
GPIOA->ODR &= ~(1<<9);
// PA8设置为低电平
GPIOA->ODR &= ~(1<<8);
flag = 1;
}
else
{
// PA9设置为高电平
GPIOA->ODR |= 1<<9;
// PA8设置为高电平
GPIOA->ODR |= 1<<8;
flag = 0;
}
}
void InitGPIO()
{
// 使能GPIOA
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
// 使能GPIOC
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
// 0到7位清零
GPIOA->CRH &= 0xffffff00;
// 位0置1,位4置1
GPIOA->CRH |= 0x00000011;
//PC0端口配置为上拉输入模式
GPIOC->CRL &= 0xfffffff0;
GPIOC->CRL |= 0x00000008;
GPIOC->ODR |= 0x0001;
}
void InitNVIC()
{
// 开启AFIO时钟
RCC->APB2ENR=1<<0;
// NVIC_ISER0寄存器的第6位写入1,启用6号中断
NVIC->ISER[0] |= NVIC_ISER_SETENA_6;
// 配置中断优先级分组, 8:10位 分组模式:100 抢占:响应=3:1
SCB->AIRCR = 0x05FA0000 | 0x400;
NVIC->IP[0] = 0xf0;
// 开放中断线EXTI0
EXTI->IMR |= EXTI_IMR_MR0;
// 中断线EXTI0采用下降沿触发
EXTI->FTSR |= EXTI_FTSR_TR0;
// PC0引脚有效
AFIO->EXTICR[0] = 0x02;
}
void SystemInit()
{
}
//---------中断服务程序----------
void EXTI0_IRQHandler(void)
{
if( flag )
ControlLed(0);
else
ControlLed(1);
//解挂
EXTI->PR |= 0x00001;
}
编译和运行程序
构建成功的HEX程序存储在项目的Debug目录下,启动Proteus8,打开《STM32单片机的电源配置》一节建立的仿真工程,为STM32F103R6单片机设置运行程序。程序运行效果如下图所示: