中断、异常

发布于:2024-08-08 ⋅ 阅读:(131) ⋅ 点赞:(0)

概念

中断属于一种异常
image.png
保存现场、处理异常(中断)、恢复现场;
现场就是各种寄存器;
1、初始化、设置中断源、中断优先级
2、执行程序
3、产生中断,比如按下按键
4、cpu没执行完一条指令、都会检查有无中断、异常
5、发现异常、中断。跳去对应的地址去执行程序

CPU怎么直到中断/异常跳过来执行哪一个函数呢?

cortex M3、M4处理器
都是硬件来做的;
1、保存现场
2、硬件分辨异常/中断
3、跳转到向量表去执行
4、恢复:软件触发、硬件恢复

cortex A7处理器
硬件
1、切换模式,进入IRQ模式
2、跳转
3、执行代码
软件
4、保存现场
5、分辨中断
6、恢复现场
image.png
CPU的寄存器
R0和R3
调用者和被调用者之间传参;
现场:R0-R15, 以及PSR;

LR

LR并不是简单的存着一个返回地址,因为要恢复现场、其他保存好的寄存器也要恢复;
如果是简单的返回地址的话,那就直接返回了,其他保存的寄存器不会恢复;
LR = 特殊值;
调用完函数;
发现LR = 一个特殊的值;
image.png
M3、M4有两个sp,sp_mian、sp_process主栈寄存器和线程栈寄存器,一般用的主栈寄存器
对于A7处理器
image.png

中断硬件框架:

f103和157都是中断引脚只能配置一个;
image.png
image.png

对于F103这块芯片,如果要PA0发出中断,那要做的操作有?

实现按下按键产生中断,松开也产生中断,双边沿触发;PA0按键的引脚

1、外部中断寄存器,选择PA0发生中断;中断源
image.png
2、是上升沿、还是下降沿发生中断?
使能EXTI,以及配置中断触发方式
image.png
3、NVIC中断控制器
中断设置使能寄存器,里面有32bit,每一位对应一个寄存器;
把这个寄存器第0位设置位1,才能把对应的中断发给cpu
4、设置cpu是否处理中断
cpu内部的寄存器,bit0设置为0才可以处理中断;
image.png

GPIO中断编程

1、设置外部中断0,PA0
image.png
基地址+偏移地址
image.png
image.png
image.png

2、EXTI配置模式、以及使能
触发模式选择双边触发,也就是上升沿以及下降沿的两个寄存器都要设置;
找基地址
image.png
设置外部中断EXTI0,0就是第一个,第一位bit0,设置为上升沿触发bit0 = 1
image.png
使能EXTI,
这一位设置成1就是不屏蔽,也就是NVIC与EXTI0的通道打开了
image.png

3、NVIC
image.png
设置第6位为0:EXTI0
image.png

4、使能cpu中断
image.png
image.png

4、清除中断
清除外部中断控制器
写入1清除,基地址是EXTI的
image.png
清除NVIC中断控制器
设置为0;
image.png

具体代码

结构体对齐
image.png
直接用结构体来存这些寄存器的地址;
image.png

#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);
}


网站公告

今日签到

点亮在社区的每一天
去签到