【裸机开发】IRQ 中断服务函数(二)—— 全局中断初始化

实现了 IRQ 中断服务函数的汇编部分以后,接下来我们要使用C代码实现IRQ中断服务函数的具体逻辑,主要包含初始化和中断处理两部分。

  • 全局中断初始化(全局中断使能、IRQ中断使能)
  • 具体中断处理逻辑实现

目录

一、全局中断初始化(理论流程)

二、IRQ 中断使能

1、认识中断ID

2、IRQ 中断使能实现

三、初始化 IRQ 中断服务函数表

1、中断服务函数指针

2、中断服务函数表声明

3、中断服务函数表初始化

四、总结:中断服务函数接口

1、通用中断服务函数(system_irqhandler)

2、中断服务函数注册接口

3、中断初始化接口


一、全局中断初始化(理论流程)

全局中断初始化的基本流程如下:

① GIC全局中断使能:相当于打开中断控制器

② IRQ中断使能:IRQ目前用于用户日常使用的有128种,我们需要手动指定具体哪一种中断使能

③ 初始化服务函数表:IRQ 一共160 种中断,我们需要为每一种中断分配一个默认的中断服务函数

④ 设置中断向量表:中断向量表默认保存在 0x00000000 的位置,但是 0x00000000 保存了boot rom 相关内容,所以我们有时需要把中断向量表换个地方保存。

二、IRQ 中断使能

IRQ 中断使能需要具体到某种具体的外设中断,即你要打开哪一种类型的中断,GPIO 的中断还是SPI 的中断。因此,在实现IRQ中断使能之前需要先了解中断ID

1、认识中断ID

IRQ 中断是我们最常用的一种中断,包含了所有外设可能产生的中断,如按键、I2C、SPI 等。为了区分这些外设产生的中断,IRQ 中断为这些外设产生的中断分配了中断号。

0~15号:这 16 个 ID 分配给 SGI(软件中断)。由软件触发引起的中断,通过向寄存器 GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信。

16~31号:这16个ID分配给 PPI(私有中断)。一个CPU可能包含多核,每个核有自己独有的中断,这些独有中断只能在指定的核中处理,我们称这种中断为“ 私有中断 ”。

32~1019号:剩余的分配给 SPI(共享中断)。一个CPU可能包含多核,共享中断在所有的核中都可以得到处理,不仅限于某个特定的核。(目前只用了 128 个)

2、IRQ 中断使能实现

IRQ 中断使能使用的是SDK中的接口。下面的 99 是中断ID,比如你要使用 GPIO1 的 0 -15 引脚,对应的中断ID就是66 + 32 = 98;如果要使用 GPIO1 的 16 - 31 引脚,对应的中断 ID 就是 67 + 32 = 99

注意:这里的 32 表示SGI 和 PPI 对应的32种中断,用户无法干涉。

#include "core_ca7.h"

/* 使能GIC 中 IRQ 的某个外设对应的中断 */
GIC_EnableIRQ(99);    

三、初始化 IRQ 中断服务函数表

为了区分IRQ中断具体来自于哪个外设,定义了 160 种中断(前32种中断有特殊用途),我们要做的第一件事就是给这些中断配上默认的中断函数。

1、中断服务函数指针

为了管理前面说到了的 160 种中断,我们要定义一个函数指针数组,数组中保存的是函数指针(也就是函数地址),因此我们需要确定函数指针的类型。

/* 中断服务函数的格式 */
typedef void (*irqhandler_t)(void* userParams);
  • 函数指针类型名:irqhandler_t
  • 函数格式:
    • 形参:void*,后续可能需要给中断服务函数传递参数
    • 返回值:void

2、中断服务函数表声明

为了管理每一种中断对应的中断服务函数,我们要创建一个数组,数组的下标就代表中断ID,下标对应的就是中断服务函数指针。

#define MAX_INT 160

/* 管理着所有的irq中断服务函数 */
irqhandler_t irqTable[MAX_INT];

3、中断服务函数表初始化

接下来我们要给这 160 个中断服务函数表逐一赋予默认中断服务函数,所以我们先简单定义一个默认中断服务函数。函数格式必须按照最开始的中断服务函数指针的格式来。

/* 默认中断服务函数 */
void default_irqhandler(void* userParams)
{
    return;
}

然后我们为中断服务函数表中的每一个元素进行初始化

/* 初始化 irq 中断服务函数表 */
void irqtable_init()
{
    unsigned int i = 0;
    for (; i < MAX_INT; i++)
    {
        irqTable[i] = default_irqhandler;
    }
}

四、总结:中断服务函数接口

1、通用中断服务函数(system_irqhandler)

在汇编部分,我们已经获取到了中断ID,在通用中断服务函数中,我们要根据这个中断ID执行对应的中断服务函数。

代码中 giccIar 就是中断ID,我们根据中断ID可以在中断服务函数表中找到对应的函数地址(函数指针),直接调用即可。

/* 通用中断服务函数 */
void system_irqhandler(unsigned int giccIar)
{
    // GICC_IAR 寄存器低10位用于存储中断ID
    unsigned int intID = giccIar & 0x3ff;            
    if (giccIar > MAX_INT)
    {
        return;
    }
    
    // 如果没有要传递的内容,那就传递 (void*)0
    irqTable[giccIar]((void *)0);
}

2、中断服务函数注册接口

方才我们只是给中断服务函数表中每一个中断ID赋予了默认中断服务函数指针,后续可能会在其他地方要修改中断ID对应的中断服务函数,因此我们这里定义一个“注册中断服务函数”

/* 注册中断服务函数 */
void register_irqhandler(unsigned int intID, irqhandler_t handler)
{
    irqTable[intID] = handler;
}

 

3、中断初始化接口

GIC 全局中断使能 和 IRQ 中断使能用的都是 SDK 中的接口。

#include "core_ca7.h"

/* 中断初始化 */
void interrupt_init()
{
    /* GIC 全局中断使能初始化 */
    GIC_Init();

    /* irq 中断服务函数表初始化 */
    irqtable_init();

    /* 设置中断向量表偏移 */
    __set_VBAR((uint32_t)0x87800000);

    /* 使能GIC 中 IRQ 的某个外设对应的中断 */
    GIC_EnableIRQ(99);    
}
<think>好的,我现在需要帮助用户解决关于Zynq 7020 AMP架构下CPU1的私有定时器中断裸机开发的问题。首先,我需要回忆一下Zynq 7020的双核结构。Zynq-7000系列有两个Cortex-A9核,AMP模式下两个核可以独立运行不同的操作系统或裸机程序。用户的问题集中在CPU1的私有定时器设置,我需要确保自己了解私有定时器的相关寄存器配置。 首先,私有定时器(Private Timer)是每个CPU核心独有的,地址应该从0xF8F00600开始。需要配置加载值、控制寄存器,并启用中断中断号方面,私有定时器的中断属于PPI(私有外设中断),CPU0的PPI中断号是29,而CPU1的应该是对应的。不过需要确认一下,可能用户在使用CPU1时,中断号是否相同或者不同,这里可能需要查文档。 接下来,中断控制器的配置。Zynq使用的是GIC(通用中断控制器),在裸机开发中需要配置GIC的Distributor和CPU Interface。对于CPU1来说,可能需要确保中断分配到对应的CPU,并且优先级和使能位设置正确。同时,中断服务函数注册需要将中断处理函数地址写入向量表,或者通过设置VBR(向量基址寄存器)来指向自定义的向量表。 然后,代码结构方面,用户可能需要初始化定时器,配置中断控制器,编写中断服务函数,并启用全局中断。需要注意的是,在AMP模式下,两个核之间的资源访问需要协调,比如共享内存或者使用核间通信机制,但用户的问题主要聚焦在CPU1的私有定时器,可能暂时不需要处理核间通信部分,但可能需要确保内存分配正确,避免冲突。 此外,启动流程方面,在AMP环境中,CPU1通常由CPU0启动,通过写入CPU1的启动地址并释放复位。因此,用户可能需要将CPU1的代码编译成不同的可执行文件,并确保其加载到正确的内存地址。同时,在代码中需要设置好CPU1的堆栈和异常向量表。 可能还需要考虑缓存和MMU的配置,确保定时器相关寄存器的访问是有效的。在裸机开发中,通常需要禁用MMU,但缓存可能需要根据情况进行配置,比如是否启用数据缓存来加速访问,但需要确保寄存器映射的内存区域配置正确,避免缓存导致的问题。 最后,测试和调试部分,用户可能需要使用JTAG调试器分别连接到两个核,或者在代码中加入日志输出到共享内存,通过串口或其他方式查看CPU1的中断触发情况。同时,需要确保中断处理函数正确清除中断标志,避免重复进入中断。 可能遇到的问题包括中断未触发,原因可能是中断号配置错误、GIC未正确使能、定时器控制寄存器的配置有误,或者中断处理函数没有正确清除中断标志。需要逐步排查这些方面,并参考Xilinx的官方文档和例程,确保寄存器的配置值正确。</think>### Zynq 7020 AMP 裸机开发中 CPU1 私有定时器中断配置步骤 #### 1. **硬件基础** - Zynq 7020 包含双 Cortex-A9 核(CPU0 和 CPU1),私有定时器(Private Timer)是每个核的独立外设,地址为 $0xF8F00600$(CPU0)和 $0xF8F01600$(CPU1)[^1]。 - 私有定时器中断属于 PPI(私有外设中断),CPU1 的私有定时器中断号为 **30**。 #### 2. **关键寄存器配置** ##### a. **定时器初始化** ```c // 设置定时器加载值(32 位) *(volatile uint32_t*)(0xF8F01600 + 0x00) = LOAD_VALUE; // Timer Load Register // 控制寄存器配置 *(volatile uint32_t*)(0xF8F01600 + 0x08) = 0x07; // Timer Control Register: // 0x01: 使能定时器 // 0x02: 自动重载模式 // 0x04: 中断使能 ``` ##### b. **中断控制器(GIC)配置** ```c // GIC Distributor 配置(使能中断分配) *(volatile uint32_t*)(0xF8F01000 + 0x100) |= (1 << 30); // GICD_ISENABLERn(使能 PPI 30) // GIC CPU Interface 配置(CPU1 侧) *(volatile uint32_t*)(0xF8F00100 + 0x0C) = 0x1F0; // ICCPMR(优先级掩码) *(volatile uint32_t*)(0xF8F00100 + 0x00) = 0x01; // ICCICR(使能 CPU Interface) ``` #### 3. **中断服务函数(ISR)** ```c void __attribute__((interrupt)) timer_isr(void) { // 清除定时器中断标志 *(volatile uint32_t*)(0xF8F01600 + 0x0C) = 0x01; // Timer Interrupt Status Register // 用户自定义操作(如翻转 GPIO) } ``` #### 4. **向量表与中断绑定** - 在启动代码中修改向量表,将 IRQ 入口指向通用中断处理函数: ```assembly .section .vectors ldr pc, [pc, #24] // IRQ 跳转指令 .word irq_handler // IRQ 处理函数地址 ``` - 在 C 代码中绑定具体中断: ```c void irq_handler(void) { uint32_t int_id = *(volatile uint32_t*)(0xF8F00100 + 0x0C); // ICCIAR if (int_id == 30) timer_isr(); *(volatile uint32_t*)(0xF8F00100 + 0x10) = int_id; // ICCEOIR } ``` #### 5. **启动流程(AMP 模式)** - CPU0 需通过写 **SLCR.ACP_CTRL** 寄存器释放 CPU1 复位,并设置其启动地址: ```c *(volatile uint32_t*)(0xF8000000 + 0x244) = CPU1_ENTRY_ADDR; // 设置 CPU1 启动地址 *(volatile uint32_t*)(0xF8000000 + 0x8) |= 0x1; // 释放 CPU1 复位 ``` #### 6. **注意事项** - **内存隔离**:在 AMP 模式下需通过 MPU 或 MMU 配置隔离 CPU0 和 CPU1 的内存访问。 - **共享资源同步**:若操作共享外设(如 GPIO),需使用原子操作或软件锁。 - **调试方法**:可通过 UART 打印日志或 LED 指示灯验证中断触发。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值