1.中断向量表
这个表里面存放的都是中断向量,中断服务程序的入口地址或存放中断服务程序的首地址成为中断向量。中断向量表是一系列中断服务程序入口地址组成的表,当某个中断触发的时候会自动跳转到中断向量表对应的中断服务程序的入口。
2.NVIC(内嵌向量中断控制器)
在IMU6U的中断管理机构叫做GIC。GIC接受到外部中断汇报给ARM内核,ARM内核提供了四个信号给GIC来回报中断情况。
中断源分为三个部分:
SPI(Shared Peripheral Interrupt),
共享中断,顾名思义,所有
Core
共享的中断,这个是最
常见的,那些外部中断都属于
SPI
中断
(
注意!不是
SPI
总线那个中断
)
。比如按键中断、串口
中断等等,这些中断所有的
Core
都可以处理,不限定特定
Core
。
PPI(Private Peripheral Interrupt)
,私有中断,我们说了
GIC
是支持多核的,每个核肯定
有自己独有的中断。这些独有的中断肯定是要指定的核心处理,因此这些中断就叫做私有中断。
SGI(Software-generated Interrupt)
,软件中断,由软件触发引起的中断,通过向寄存器
GICD_SGIR
写入数据来触发,系统会使用
SGI
中断来完成多核之间的通信。
ID0~ID15
:这
16
个
ID
分配给
SGI
。
ID16~ID31
:这
16
个
ID
分配给
PPI
。
ID32~ID1019
:这
988
个
ID
分配给
SPI。
GIC逻辑模块:
Distributor【分发器端】 和 CPU Interface【CPU接口端】
中断使能
IRQ和FIQ分别是外部中断和快速中断的总开关
中断使能
GIC 寄存器 GICD_ISENABLERn 和 GICD_ ICENABLERn 用来完成外部中断的使能和禁
止,对于 Cortex-A7 内核来说中断 ID 只使用了 512 个。一个 bit 控制一个中断 ID 的使能,那么
就需要 512/32=16 个 GICD_ISENABLER 寄存器来完成中断的使能。同理,也需要 16 个
GICD_ICENABLER 寄存器来完成中断的禁止。其中 GICD_ISENABLER0 的 bit[15:0]对应
ID15~0 的 SGI 中断,GICD_ISENABLER0 的 bit[31:16]对应 ID31~16 的 PPI 中断。剩下的
GICD_ISENABLER1~GICD_ISENABLER15 就是控制 SPI 中断的
中断优先级设置
中断优先级也可以分为抢占优先级和子优先级,根据GICC_PMR的低八位来控制优先级级数的范围,
GIC
控制器最多可以支持
256
个优先级,数字越小,优先级越高
I.MX6U 是 Cortex-A7 内核,所以支持 32 个优先级,因此 GICC_PMR 要设置为 0b11111000
抢占优先级和子优先级位数设置
I.MX6U
的优先级
位数为
5(32
个优先级
)
,所以可以设置
Binary point
为
2
,表示
5
个优先级位全部为抢占优先级。
这里解释一下:优先级位数如果是5,则代表的是2的5次方即32个不同的优先级级别,,所以才选择了Binary Point为2。全部为抢占优先级。
优先级设置
1.设置寄存器
GICC_PMR
,配置优先级个数,比如
I.MX6U
支持
32
级优先级。
2.设置抢占优先级和子优先级位数,一般为了简单起见,会将所有的位数都设置为抢占
优先级。
3.设置指定中断 ID 的优先级,也就是设置外设优先级。
代码段
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : bsp_exit.c
作者 : 左忠凯
版本 : V1.0
描述 : 外部中断驱动。
其他 : 配置按键对应的GPIP为中断模式
论坛 : www.wtmembed.com
日志 : 初版V1.0 2019/1/4 左忠凯创建
***************************************************************/
#include "bsp_exit.h"
#include "bsp_gpio.h"
#include "bsp_int.h"
#include "bsp_delay.h"
#include "bsp_beep.h"
/*
* @description : 初始化外部中断
* @param : 无
* @return : 无
*/
void exit_init(void)
{
gpio_pin_config_t key_config;
/* 1、设置IO复用 */
IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0); /* 复用为GPIO1_IO18 */
IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);
/* 2、初始化GPIO为中断模式 */
key_config.direction = kGPIO_DigitalInput;
key_config.interruptMode = kGPIO_IntFallingEdge;
key_config.outputLogic = 1;
gpio_init(GPIO1, 18, &key_config);
GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn); /* 使能GIC中对应的中断 */
system_register_irqhandler(GPIO1_Combined_16_31_IRQn, (system_irq_handler_t)gpio1_io18_irqhandler, NULL); /* 注册中断服务函数 */
gpio_enableint(GPIO1, 18); /* 使能GPIO1_IO18的中断功能 */
}
/*
* @description : GPIO1_IO18最终的中断处理函数
* @param : 无
* @return : 无
*/
void gpio1_io18_irqhandler(void)
{
static unsigned char state = 0;
/*
*采用延时消抖,中断服务函数中禁止使用延时函数!因为中断服务需要
*快进快出!!这里为了演示所以采用了延时函数进行消抖,后面我们会讲解
*定时器中断消抖法!!!
*/
delay(10);
if(gpio_pinread(GPIO1, 18) == 0) /* 按键按下了 */
{
state = !state;
beep_switch(state);
}
gpio_clearintflags(GPIO1, 18); /* 清除中断标志位 */
}
复用IO口,设置中断触发方式。使能GIC以及GPIO中断
EPIT
定时器实验
EPIT定时器:完成
周期性的中断定时方式【仅此一个功能!!!】,32位寄存器,
EPIT
是一个
向下计数器
EPIT 定时器有两种工作模式:set-and-forget 和 free-running
set-and-forget
模式
:EPITx_CR(x=1,
2)
寄存器的
RLD
位置
1
的时候
EPIT
工作在此模式
下,在此模式下
EPIT
的计数器从加载寄存器
EPITx_LR
中获取初始值,不能直接向计数器寄存
器写入数据。不管什么时候,只要计数器计数到
0
,那么就会从加载寄存器
EPITx_LR
中重新
加载数据到计数器中,周而复始。
free-running
模式
:
EPITx_CR
寄存器的
RLD
位清零的时候
EPIT
工作在此模式下,当计数
器计数到
0
以后会重新从
0XFFFFFFFF
开始计数,并不是从加载寄存器
EPITx_LR
中获取数据。
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : bsp_epittimer.c
作者 : 左忠凯
版本 : V1.0
描述 : EPIT定时器驱动文件。
其他 : 配置EPIT定时器,实现EPIT定时器中断处理函数
论坛 : www.wtmembed.com
日志 : 初版V1.0 2019/1/5 左忠凯创建
***************************************************************/
#include "bsp_epittimer.h"
#include "bsp_int.h"
#include "bsp_led.h"
/*
* @description : 初始化EPIT定时器.
* EPIT定时器是32位向下计数器,时钟源使用ipg=66Mhz
* @param - frac : 分频值,范围为0~4095,分别对应1~4096分频。
* @param - value : 倒计数值。
* @return : 无
*/
void epit1_init(unsigned int frac, unsigned int value)
{
if(frac > 0XFFF)
frac = 0XFFF;
EPIT1->CR = 0; /* 先清零CR寄存器 */
/*
* CR寄存器:
* bit25:24 01 时钟源选择Peripheral clock=66MHz
* bit15:4 frac 分频值
* bit3: 1 当计数器到0的话从LR重新加载数值
* bit2: 1 比较中断使能
* bit1: 1 初始计数值来源于LR寄存器值
* bit0: 0 先关闭EPIT1
*/
EPIT1->CR = (1<<24 | frac << 4 | 1<<3 | 1<<2 | 1<<1);
EPIT1->LR = value; /* 倒计数值 */
EPIT1->CMPR = 0; /* 比较寄存器,当计数器值和此寄存器值相等的话就会产生中断 */
/* 使能GIC中对应的中断 */
GIC_EnableIRQ(EPIT1_IRQn);
/* 注册中断服务函数 */
system_register_irqhandler(EPIT1_IRQn, (system_irq_handler_t)epit1_irqhandler, NULL);
EPIT1->CR |= 1<<0; /* 使能EPIT1 */
}
/*
* @description : EPIT中断处理函数
* @param : 无
* @return : 无
*/
void epit1_irqhandler(void)
{
static unsigned char state = 0;
state = !state;
if(EPIT1->SR & (1<<0)) /* 判断比较事件发生 */
{
led_switch(LED0, state); /* 定时器周期到,反转LED */
}
EPIT1->SR |= 1<<0; /* 清除中断标志位 */
}