概念
中断属于一种异常
保存现场、处理异常(中断)、恢复现场;
现场就是各种寄存器;
1、初始化、设置中断源、中断优先级
2、执行程序
3、产生中断,比如按下按键
4、cpu没执行完一条指令、都会检查有无中断、异常
5、发现异常、中断。跳去对应的地址去执行程序
CPU怎么直到中断/异常跳过来执行哪一个函数呢?
cortex M3、M4处理器
都是硬件来做的;
1、保存现场
2、硬件分辨异常/中断
3、跳转到向量表去执行
4、恢复:软件触发、硬件恢复
cortex A7处理器
硬件
1、切换模式,进入IRQ模式
2、跳转
3、执行代码
软件
4、保存现场
5、分辨中断
6、恢复现场
CPU的寄存器
R0和R3
调用者和被调用者之间传参;
现场:R0-R15, 以及PSR;
LR
LR并不是简单的存着一个返回地址,因为要恢复现场、其他保存好的寄存器也要恢复;
如果是简单的返回地址的话,那就直接返回了,其他保存的寄存器不会恢复;
LR = 特殊值;
调用完函数;
发现LR = 一个特殊的值;
M3、M4有两个sp,sp_mian、sp_process主栈寄存器和线程栈寄存器,一般用的主栈寄存器
对于A7处理器
中断硬件框架:
f103和157都是中断引脚只能配置一个;
对于F103这块芯片,如果要PA0发出中断,那要做的操作有?
实现按下按键产生中断,松开也产生中断,双边沿触发;PA0按键的引脚
1、外部中断寄存器,选择PA0发生中断;中断源
2、是上升沿、还是下降沿发生中断?
使能EXTI,以及配置中断触发方式
3、NVIC中断控制器
中断设置使能寄存器,里面有32bit,每一位对应一个寄存器;
把这个寄存器第0位设置位1,才能把对应的中断发给cpu
4、设置cpu是否处理中断
cpu内部的寄存器,bit0设置为0才可以处理中断;
GPIO中断编程
1、设置外部中断0,PA0
基地址+偏移地址
2、EXTI配置模式、以及使能
触发模式选择双边触发,也就是上升沿以及下降沿的两个寄存器都要设置;
找基地址
设置外部中断EXTI0,0就是第一个,第一位bit0,设置为上升沿触发bit0 = 1
使能EXTI,
这一位设置成1就是不屏蔽,也就是NVIC与EXTI0的通道打开了
3、NVIC
设置第6位为0:EXTI0
4、使能cpu中断
4、清除中断
清除外部中断控制器
写入1清除,基地址是EXTI的
清除NVIC中断控制器
设置为0;
具体代码
结构体对齐
直接用结构体来存这些寄存器的地址;
#include "string.h"
#include "exti.h"
#include "nvic.h"
/* stm32f103xe.h */
typedef struct
{
volatile unsigned int EVCR;
volatile unsigned int MAPR;
volatile unsigned int EXTICR[4];
volatile unsigned int RESERVED0;
volatile unsigned int MAPR2;
} AFIO_TypeDef;
void key_init(void)
{
AFIO_TypeDef *afio = (AFIO_TypeDef *)0x40010000;
unsigned int *pReg;
unsigned int *pRegA;
/* 1.初始化GPIO引脚PA0, 设置为输入引脚 */
/* 使能GPIOB, GPIOA */
pReg = (unsigned int *)(0x40021000 + 0x18);
*pReg |= (1<<3) | (1<<2);
/* 设置GPIOA0为输入引脚 */
pRegA = (unsigned int *)(0x40010800 + 0x00);
*pRegA &= ~(3); /* mode0 = 0b00 */
*pRegA &= ~(3<<2); /* cnf0 = 0b00 */
*pRegA |= (1<<2); /* cnf0 = 0b01 */
/* 2. 设置为EXTI0 */
afio->EXTICR[0] &= ~0xf; /* PA0 as EXTI0 */
}
void EXTI0_IRQHandler(void)
{
/* GPIOA input data register */
unsigned int * pRegA = (unsigned int *)(0x40010800 + 0x08);
if ((*pRegA & (1<<0)) == 0) /* KEY1被按下 */
{
puts("KEY1 pressed!\n\r");
}
else
{
puts("KEY1 released!\n\r");
}
/* 清除中断 */
exti_clear_int(0);
nvic_clear_int(6);
}
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_TypeDef;
void exti_init(void)
{
EXTI_TypeDef * exti = (EXTI_TypeDef *)0x40010400;
/* 1. 设置触发方式:双边沿触发 */
exti->RTSR |= (1<<0);
exti->FTSR |= (1<<0);
/* 2. 使能中断: 能够向NVIC发出中断 */
exti->IMR |= (1<<0);
}
void exti_clear_int(int bit)
{
EXTI_TypeDef * exti = (EXTI_TypeDef *)0x40010400;
exti->PR |= (1<<bit);
}
/* core_cm3.h */
typedef struct
{
volatile unsigned int ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */
volatile unsigned int RESERVED0[24];
volatile unsigned int ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */
volatile unsigned int RSERVED1[24];
volatile unsigned int ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */
volatile unsigned int RESERVED2[24];
volatile unsigned int ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */
volatile unsigned int RESERVED3[24];
volatile unsigned int IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */
volatile unsigned int RESERVED4[56];
volatile unsigned char IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */
volatile unsigned int RESERVED5[644];
volatile unsigned int STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */
} NVIC_Type;
void nvic_init(void)
{
NVIC_Type * nvic = (NVIC_Type *)0xE000E100;
/* 1. 使能EXTI0中断 */
nvic->ISER[0] |= (1<<6);
}
void nvic_clear_int(int bit)
{
NVIC_Type * nvic = (NVIC_Type *)0xE000E100;
if (bit <= 31)
nvic->ICPR[0] |= (1<<bit);
}