带你模仿正点原子到寄存器编写–外部中断EXTI_驱动按键
前言
对于大家来说学习STM32中断是学的比较迷的,这里是我学中断做的一些笔记希望对大家有所作用。
一、我们先了解一些中断的一些基础知识。
要产生中断,必须先配置好并使能中断线。根据需要的边沿检测设置2个触发寄存器,同时在中断屏蔽寄存器的相应位写’1’允许中断请求。当外部中断线上发生了期待的边沿时,将产生一个中断请求,对应的挂起位也随之被置’1’。在挂起寄存器的对应位写’1’,将清除该中断请求。 如果需要产生事件,必须先配置好并使能事件线。根据需要的边沿检测通过设置2个触发寄存器,同时在事件屏蔽寄存器的相应位写’1’允许事件请求。当事件线上发生了需要的边沿时,将产生一个事件请求脉冲,对应的挂起位不被置’1’。 通过在软件中断/事件寄存器写’1’,也可以通过软件产生中断/事件请求。 这里我要强调一下一定要将挂起寄存器置’1’清除中断请求信号,不然在之后没有中断事件产生的时候它还一直将这个中断信号发给NVIC中断向量控制器使得MCU一直进行这个中断对应的中断服务程序。
这里我要提示一下EXTI0到EXRTI4中断线对应的中断服务子程序是EXTI0_IRQHandler到EXTI4_IRQHandler。而EXTI5到EXTI9中断线是共用一个中断服务子程序的EXTI9_5_IRQHandler 。而EXTI10到EXTI15中断线是共用一个中断服务子程序的EXTI15_10_IRQHandler。其它的你们可以看启动程序里的,这个不能自己随便命名的。
二、寄存器配置
STM32手册提供的配置外部中断: 总结配置思路:
- 时钟配置:GPIO时钟、AFIO时钟(中断线与IO形成映射)。
- GPIO配置:IO初始化和中断线映射。
- 屏蔽位(EXTI_IMR):这个我们一般不配,因为我们就是想外部出现事件后能发生中断响应。
- EXIT配置:触发方式(EXTI_RTSR上升沿触发、EXTI_FTSR下降沿触发)、中断使能。
- NVIC配置:优先级分组,中断使能。
- 中断服务子程序:判断标志、清除标志。
手册的映射地址图: 根据上图即可配置出下图程序:
EXTI.h:
#ifndef __EXIT_H
#define __EXIT_H
#include "gpio.h"
#include "nvic.h"
#include "key.h"
#define afio_base (0x40010000)
#define EXTI_BASE (0x40010400)
typedef struct
{
volatile unsigned int IMR;
volatile unsigned int EMR;
volatile unsigned int RTSR;
volatile unsigned int FTSR;
volatile unsigned int SWIER;
volatile unsigned int PR;
} EXTI_Type;
typedef struct
{
volatile unsigned int EVCR;
volatile unsigned int MAPR;
volatile unsigned int EXTICR[4];
} afio_type;
#define afio ((afio_type *) afio_base)
#define EXTI ((EXTI_Type *) EXTI_BASE)
void exti_init(void);
void ex_nvic_config(u8 gpio,u8 bit,u8 trim);
#endif
EXTI.c:
void ex_nvic_config(u8 gpio,u8 bit,u8 trim)
{
u8 extino;
u8 extioffset;
extino=bit/4;
extioffset=(bit%4)*4;
RCC->RCC_APB2ENR|=0x01;
afio->EXTICR[extino]&=~(0x000F<<extioffset);
afio->EXTICR[extino]|=gpio<<extioffset;
EXTI->IMR|=1<<bit;
if(trim&0x01) EXTI->FTSR|=1<<bit;
if(trim&0x02) EXTI->RTSR|=1<<bit;
}
大家可能对extioffset和extino这两个变量对寄存器进行配置不太懂,下图是这个配置的参考,大家看了应该就懂了。 配置完上面的我们需要将中断事件载入NVIC嵌套向量中断控制器中然后作出中断响应。 前面的外部中断配置的只是开启或者是禁止中断信号的进入内核罢了。而NVIC嵌套向量中断控制器是用于中断分组,而用于分配抢占优先级和响应优先级的。 还有中断分组功能不是在NVIC寄存器上的。 这里我先说明: 中断的使能与除能分别使用各自的寄存器来控制——这与传统的, 使用单一比特的两个 状态来表达使能与除能是不同的。 CM3 中可以有 240 对使能位/除能位,每个中断拥有一对。这 240 个对子分布在 8 对 32 位寄存器中(最后一对没有用完)。欲使能一个中断,你需要写 1 到对应 SETENA 的位中;欲除能一个中断,你需要写 1 到对应的 CLRENA 位中;如果往它们中写 0,不会有任何效果。通过这种方式,使能/除能中断时只需把“当事位”写成1,其它的位可以全部为零。再也不用像以前那样,害怕有些位被写入 0 而破坏其对应的中断设置(写 0 没有效果),从而实现每个中断都可以自顾地设置,而互不侵犯——只需单一的写指令,不再需要读‐改‐写。 含有60个IPR寄存器。 中断分组寄存器: 中文版的图: 有了上面的知识后:
nvic.h:
#ifndef __NVIC_H
#define __NVIC_H
#include "gpio.h"
#define scb_aircr (*(volatile unsigned int *)0xE000ED0C)
#define nvic_base (0xE000E100)
typedef struct
{
volatile unsigned int iser[8];
unsigned int RESERVED0[24];
volatile unsigned int icer[8];
unsigned int RSERVED1[24];
volatile unsigned int ispr[8];
unsigned int RESERVED2[24];
volatile unsigned int icpr[8];
unsigned int RESERVED3[24];
volatile unsigned int iabr[8];
unsigned int RESERVED4[56];
volatile unsigned char ipr[240];
unsigned int RESERVED5[644];
volatile unsigned int stir;
}nvic_type;
#define nvic ((nvic_type *) nvic_base)
void NVIC_Init(u8 p,u8 s,u8 no,u8 group);
#endif
nvic.c:
#include "nvic.h"
void NVIC_Init(u8 p,u8 s,u8 no,u8 group)
{
u32 t,t1,t2;
t1=(~group)&0x07;
t1<<=8;
t=scb_aircr;
t&=0X0000F8FF;
t|=0X05FA0000;
t|=t1;
scb_aircr=t;
t2=p<<(4-group);
t2|=s&(0x0f>>group);
t2&=0xf;
nvic->iser[no/32]|=(1<<no%32);
nvic->ipr[no]|=t2<<4;
}
上面配置完了我们就可以写发生中断后进行的中断响应了(中断服务子程序)
void EXTI0_IRQHandler(void)
{
if(GPIOA->GPIO_IDR&(1<<0))
{
delay_ms(20);
if(GPIOA->GPIO_IDR&(1<<0))
{
GPIOB->GPIO_ODR^=(1<<5);
}
}
EXTI->PR|=1<<0;
}
void EXTI2_IRQHandler(void)
{
if(!(GPIOE->GPIO_IDR&(1<<2)))
{
delay_ms(20);
if(!(GPIOE->GPIO_IDR&(1<<2)))
{
GPIOB->GPIO_ODR^=(1<<5);
}
}
EXTI->PR|=1<<2;
}
void EXTI3_IRQHandler(void)
{
if(!(GPIOE->GPIO_IDR&(1<<3)))
{
delay_ms(20);
if(!(GPIOE->GPIO_IDR&(1<<3)))
{
GPIOE->GPIO_ODR^=(1<<5);
}
}
EXTI->PR|=1<<3;
}
将GPIO口与EXTI外部中断联系起来,再将EXTI与NVIC联系起来:
void exti_init(void)
{
ex_nvic_config(0,0,2);
ex_nvic_config(4,2,1);
ex_nvic_config(4,3,1);
NVIC_Init(2,0,6,2);
NVIC_Init(2,1,8,2);
NVIC_Init(2,2,9,2);
}
这个GPIO的初始化:
void key_init(void)
{
RCC->RCC_APB2ENR|=1<<2;
RCC->RCC_APB2ENR|=1<<6;
GPIOE->GPIO_CRL&=0XFFF000FF;
GPIOE->GPIO_CRL|=0X00088800;
GPIOE->GPIO_ODR|=7<<2;
GPIOA->GPIO_CRL&=0XFFFFFFF0;
GPIOA->GPIO_CRL|=0X00000008;
}
完整的程序
exti.h
#ifndef __EXIT_H
#define __EXIT_H
#include "gpio.h"
#include "nvic.h"
#include "key.h"
#define afio_base (0x40010000)
#define EXTI_BASE (0x40010400)
typedef struct
{
volatile unsigned int IMR;
volatile unsigned int EMR;
volatile unsigned int RTSR;
volatile unsigned int FTSR;
volatile unsigned int SWIER;
volatile unsigned int PR;
} EXTI_Type;
typedef struct
{
volatile unsigned int EVCR;
volatile unsigned int MAPR;
volatile unsigned int EXTICR[4];
} afio_type;
#define afio ((afio_type *) afio_base)
#define EXTI ((EXTI_Type *) EXTI_BASE)
void exti_init(void);
void ex_nvic_config(u8 gpio,u8 bit,u8 trim);
void key_init(void);
#endif
exti.c
#include "exti.h"
#include "delay.h"
void EXTI0_IRQHandler(void)
{
if(GPIOA->GPIO_IDR&(1<<0))
{
delay_ms(20);
if(GPIOA->GPIO_IDR&(1<<0))
{
GPIOB->GPIO_ODR^=(1<<5);
}
}
EXTI->PR|=1<<0;
}
void EXTI2_IRQHandler(void)
{
if(!(GPIOE->GPIO_IDR&(1<<2)))
{
delay_ms(20);
if(!(GPIOE->GPIO_IDR&(1<<2)))
{
GPIOB->GPIO_ODR^=(1<<5);
}
}
EXTI->PR|=1<<2;
}
void EXTI3_IRQHandler(void)
{
if(!(GPIOE->GPIO_IDR&(1<<3)))
{
delay_ms(20);
if(!(GPIOE->GPIO_IDR&(1<<3)))
{
GPIOE->GPIO_ODR^=(1<<5);
}
}
EXTI->PR|=1<<3;
}
void ex_nvic_config(u8 gpio,u8 bit,u8 trim)
{
u8 extino;
u8 extioffset;
extino=bit/4;
extioffset=(bit%4)*4;
RCC->RCC_APB2ENR|=0x01;
afio->EXTICR[extino]&=~(0x000F<<extioffset);
afio->EXTICR[extino]|=gpio<<extioffset;
EXTI->IMR|=1<<bit;
if(trim&0x01) EXTI->FTSR|=1<<bit;
if(trim&0x02) EXTI->RTSR|=1<<bit;
}
void exti_init(void)
{
ex_nvic_config(0,0,2);
ex_nvic_config(4,2,1);
ex_nvic_config(4,3,1);
NVIC_Init(2,0,6,2);
NVIC_Init(2,1,8,2);
NVIC_Init(2,2,9,2);
}
void key_init(void)
{
RCC->RCC_APB2ENR|=1<<2;
RCC->RCC_APB2ENR|=1<<6;
GPIOE->GPIO_CRL&=0XFFF000FF;
GPIOE->GPIO_CRL|=0X00088800;
GPIOE->GPIO_ODR|=7<<2;
GPIOA->GPIO_CRL&=0XFFFFFFF0;
GPIOA->GPIO_CRL|=0X00000008;
}
nvic.h
#ifndef __NVIC_H
#define __NVIC_H
#include "gpio.h"
#define scb_aircr (*(volatile unsigned int *)0xE000ED0C)
#define nvic_base (0xE000E100)
typedef struct
{
volatile unsigned int iser[8];
unsigned int RESERVED0[24];
volatile unsigned int icer[8];
unsigned int RSERVED1[24];
volatile unsigned int ispr[8];
unsigned int RESERVED2[24];
volatile unsigned int icpr[8];
unsigned int RESERVED3[24];
volatile unsigned int iabr[8];
unsigned int RESERVED4[56];
volatile unsigned char ipr[240];
unsigned int RESERVED5[644];
volatile unsigned int stir;
}nvic_type;
#define nvic ((nvic_type *) nvic_base)
void NVIC_Init(u8 p,u8 s,u8 no,u8 group);
#endif
nvic.c
#include "nvic.h"
void NVIC_Init(u8 p,u8 s,u8 no,u8 group)
{
u32 t,t1,t2;
t1=(~group)&0x07;
t1<<=8;
t=scb_aircr;
t&=0X0000F8FF;
t|=0X05FA0000;
t|=t1;
scb_aircr=t;
t2=p<<(4-group);
t2|=s&(0x0f>>group);
t2&=0xf;
nvic->iser[no/32]|=(1<<no%32);
nvic->ipr[no]|=t2<<4;
}
感谢大家关注,希望对大家有所帮助。 下一篇写 “用寄存器方法写模拟串口程序”😁
|