一. 通用中断驱动
本文是 IMX6ULL 裸机篇---中断实验。旨在用 C 语言编写一套简单的中断驱动框架代码。
在
start.S
文件中,我们在中断服务函数
IRQ_Handler
中调用了
C
函数
system_irqhandler
来处
理具体的中断。
本实验会认识中断控制器: GIC控制器。
I.MX6U(Cortex-A)的中断控制器,关于 GIC 的详细内容请参考开发板光盘中的文档《ARM Generic Interrupt Controller(ARM GIC 控制器)V2.0.pdf》
GIC 是 ARM 公司给 Cortex-A/R 内核提供的一个中断控制器,类似 Cortex-M 内核中的
NVIC。GIC V2 是给 ARMv7-A 架构使用的,比如 Cortex-A7、Cortex-A9、Cortex-A15 等,
V3 和 V4 是给 ARMv8-A/R 架构使用的,也就是 64 位芯片使用的。
I.MX6U 是 Cortex-A 内核
的,因此我们主要讲解 GIC V2。
下面介绍一下大体工作如下:
1. 移植 SDK 包中断相关文件 。
主要是文件 core_ca7.h 的更改以及移植。因为core_ca7.h文件中定义了 GIC 结构体以及GIC控制器的操作接口。涉及主要接口如下:
2. 中断初始化。
其中包括GIC初始化,初始化中断向量表,设置中断向量偏移。
3. 初始化中断向量表。
4. 默认中断服务函数。
这是一个空函数,主要用来给初始化中断函数处理表。
5. IRQ中断服务函数。
在 start.S 中调用的 system_irqhandler 函数,此函数根据中断号在中断处理函数表 irqTable 中取出对应的中断处理函数并执行。
二. 代码实现
1. bsp.int.h 头文件
在 bsp 目录下新建名为 "int" 的文件夹,在 bsp/int 文件夹里面创建 bsp_int.c 和 bsp_int.h 两个文件。
bsp_int.h头文件需要创建一个函数指针和结构体。bsp_int.h文件代码实现如下:
#ifndef __BSP_INT_H_
#define __BSP_INT_H_
#include "imx6ull.h"
/* 定义中断函数指针 */
typedef void (*system_irq_handle_t)(unsigned int gicciar, void* param);
/* 中断处理函数结构体 */
typedef struct _sys_irq_handle {
system_irq_handle_t irq_handle;
void* user_param;
}sys_irq_handle_t;
void int_init(void);
void default_irqhandler(unsigned int gicciar, void* user_param);
void system_register_irqhandler(IRQn_Type irq, system_irq_handle_t handler_func, void* param);
#endif
2. 中断函数实现:
bsp_int.c 文件代码实现如下:
#include "bsp_int.h"
//中断嵌套计数器
static int irqNesting;
//中断处理函数表
static sys_irq_handle_t irq_table[NUMBER_OF_INT_VECTORS];
/* 中断初始化函数 */
void int_init(void)
{
GIC_Init(); //GIC初始化
system_irqtable_init(); //中断向量表初始化
//中断向量偏移设置
__set_VBAR(0x87800000);
}
/* 初始化中断处理函数表 */
void system_irqtable_init(void)
{
int i = 0;
irqNesting = 0;
for(i =0; i<NUMBER_OF_INT_VECTORS; i++)
{
irq_table[i].irq_handle = default_irqhandler;
irq_table[i].user_param = NULL;
}
}
/* 默认中断处理函数 */
void default_irqhandler(unsigned int gicciar, void* user_param)
{
while(1)
{
}
}
/* 注册中断处理函数*/
void system_register_irqhandler(IRQn_Type irq, system_irq_handle_t handler_func, void* param)
{
irq_table[irq].irq_handle = handler_func;
irq_table[irq].user_param = param;
}
/* 具体的中断处理函数, IRQ_handler会调用此函数*/
//gicciar: 触发IRQ中断的中断ID
void system_irqhandler(unsigned int gicciar)
{
uint32_t index = gicciar & 0x3ff;
if(index >= NUMBER_OF_INT_VECTORS) //检查中断ID值
{
return;
}
irqNesting++;
//根据中断ID号,选择中断函数-->执行
irq_table[index].irq_handle(index, irq_table[index].user_param);
irqNesting--;
}
总结:
在上述代码实现中,启动文件 start.S 文件中的中断服务函数 IRQ_Handler 中,调用了上面的 C 函数 system_irqhandler。start.S汇编中提供了函数参数(即中断ID号)。
system_irqhandler() 函数 来处理具体的中断。但是函数 system_irqhandler 的具体内容还
没有实现,所以需要实现函数 system_irqhandler 的具体内容。
不同的中断源对应不同的中断处理函数,I.MX6U 有 160 个中断源,所以需要 160 个中断处理函数。
我们可以将这些中断处理函数放到一个数组里面,中断处理函数在数组中的标号,就是其对应的中断号。
当中断发生以后,system_irqhandler函数 根据中断号从中断处理函数数组中找到对应的中断处理函数并执行即可。