最近做Cortex-A53与M3集成研究,先将中断相关记录一下,文档主要参考的是正点原子 I.MX6U嵌入式Linux驱动开发指南V1.5.pdf
NVIC
STM32(Cortex-M)的中断控制器 1. 中断向量表 2. NVIC(内嵌向量中断控制器) 3. 中断使能 4. 中断服务函数
1、中断向量表
中断向量表是一个表,这个表里面存放的是中断向量。中断服务程序的入口地址或存放中断服务程序的首地址成为中断向量,因此中断向量表是一系列中断服务程序入口地址组成的表。这些中断服务程序(函数)在中断向量表中的位置是由半导体厂商定好的,当某个中断被触发以后就会自动跳转到中断向量表中对应的中断服务程序(函数)入口地址处。中断向量表在整个程序的最前面,比如 STM32F103 的中断向量表如下所示:
摘自startup_stm32f10x_hd.s
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect
DCD TAMPER_IRQHandler ; Tamper
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2
DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3
DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4
DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5
DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6
DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7
DCD ADC1_2_IRQHandler ; ADC1 & ADC2
DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX
DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD TIM1_BRK_IRQHandler ; TIM1 Break
DCD TIM1_UP_IRQHandler ; TIM1 Update
DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation
DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3
DCD TIM4_IRQHandler ; TIM4
DCD I2C1_EV_IRQHandler ; I2C1 Event
DCD I2C1_ER_IRQHandler ; I2C1 Error
DCD I2C2_EV_IRQHandler ; I2C2 Event
DCD I2C2_ER_IRQHandler ; I2C2 Error
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD USART3_IRQHandler ; USART3
DCD EXTI15_10_IRQHandler ; EXTI Line 15..10
DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line
DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend
DCD TIM8_BRK_IRQHandler ; TIM8 Break
DCD TIM8_UP_IRQHandler ; TIM8 Update
DCD TIM8_TRG_COM_IRQHandler ; TIM8 Trigger and Commutation
DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare
DCD ADC3_IRQHandler ; ADC3
DCD FSMC_IRQHandler ; FSMC
DCD SDIO_IRQHandler ; SDIO
DCD TIM5_IRQHandler ; TIM5
DCD SPI3_IRQHandler ; SPI3
DCD UART4_IRQHandler ; UART4
DCD UART5_IRQHandler ; UART5
DCD TIM6_IRQHandler ; TIM6
DCD TIM7_IRQHandler ; TIM7
DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1
DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2
DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3
DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
__Vectors_End
中断向量表都是链接到代码的最前面,比如一般 ARM 处理器都是从地址 0X00000000 开始执行指令的,那么中断向量表就是从 0X00000000 开始存放的。上面第 1 行的“__initial_sp”就是第一条中断向量,存放的是栈顶指针,接下来是第 2 行复位中断复位函数 Reset_Handler 的入口地址,依次类推,直到第 27 行的最后一个中断服务函数 DMA2_Channel4_5_IRQHandler 的入口地址,这样 STM32F103 的中断向量表就建好了。 我们说 ARM 处理器都是从地址 0X00000000 开始运行的,但是我们学习 STM32 的时候代码是下载到 0X8000000 开始的存储区域中。因此中断向量表是存放到 0X8000000 地址处的,而不是 0X00000000,这样不是就出错了吗?为了解决这个问题,Cortex-M 架构引入了一个新的概念——中断向量表偏移,通过中断向量表偏移就可以将中断向量表存放到任意地址处,中断向量表偏移配置在函数 SystemInit 中完成,通过向 SCB_VTOR 寄存器写入新的中断向量表首地址即可,
void SystemInit (void)
{
RCC->CR |= (uint32_t)0x00000001;
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET;
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
#endif
}
第 8 行和第 10 行就是设置中断向量表偏移,第 8 行是将中断向量表设置到 RAM 中,第10 行是将中断向量表设置到 ROM 中,基本都是将中断向量表设置到 ROM 中,也就是地址0X8000000 处。第 10 行用到了 FALSH_BASE 和 VECT_TAB_OFFSET,这两个都是宏,定义如下所示:
#define FLASH_BASE ((uint32_t)0x08000000) #define VECT_TAB_OFFSET 0x0
因此第 10 行的代码就是:SCB->VTOR=0X080000000,中断向量表偏移设置完成。通过上面的讲解我们了解了两个跟 STM32 中断有关的概念:中断向量表和中断向量表偏移,那么这个跟 I.MX6U 有什么关系呢?因为 I.MX6U 所使用的 Cortex-A7 内核也有中断向量表和中断向量表偏移,而且其含义和 STM32 是一模一样的!只是用到的寄存器不同而已,概念完全相同!
2、NVIC(内嵌向量中断控制器)
中断系统得有个管理机构,对于 STM32 这种 Cortex-M 3内核的单片机来说这个管理机构叫做 NVIC,全称叫做 Nested Vectored Interrupt Controller。关于 NVIC 本教程不作详细的讲解,既然 Cortex-M 内核有个中断系统的管理机构—NVIC,那么 I.MX6U 所使用的 Cortex-A7 内核是不是也有个中断系统管理机构?答案是肯定的,不过 Cortex-A 内核的中断管理机构不叫做NVIC,而是叫做 GIC,全称是 general interrupt controller,后面我们会详细的讲解 Cortex-A 内核的 GIC。
3、中断使能
要使用某个外设的中断,肯定要先使能这个外设的中断,以 STM32F103 的 USART1:
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
上述代码就是使能USART1中断,同理,如果要使用 I.MX6U 的某个中断的话也需要使能其对应的中断。
4、中断服务函数
我们使用中断的目的就是为了使用中断服务函数,当中断发生以后中断服务函数就会被调用,我们要处理的工作就可以放到中断服务函数中去完成。同样以 STM32F103 的 USART1 为例,其中断服务函数如下所示:
void USART1_IRQHandler(void)
{
u8 Res;
#if SYSTEM_SUPPORT_OS
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
Res =USART_ReceiveData(USART1);
if((USART_RX_STA&0x8000)==0)
{
if(USART_RX_STA&0x4000)
{
if(Res!=0x0a)USART_RX_STA=0;
else USART_RX_STA|=0x8000;
}
else
{
if(Res==0x0d)
USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
}
}
}
}
#if SYSTEM_SUPPORT_OS
OSIntExit();
#endif
}
其中USART有如下具体中断,都跳转到同一个中断服务函数,然后再获取具体的中断源头USART_GetITStatus
#define USART_IT_PE ((uint16_t)0x0028)
#define USART_IT_TXE ((uint16_t)0x0727)
#define USART_IT_TC ((uint16_t)0x0626)
#define USART_IT_RXNE ((uint16_t)0x0525)
#define USART_IT_IDLE ((uint16_t)0x0424)
#define USART_IT_LBD ((uint16_t)0x0846)
#define USART_IT_CTS ((uint16_t)0x096A)
#define USART_IT_ERR ((uint16_t)0x0060)
#define USART_IT_ORE ((uint16_t)0x0360)
#define USART_IT_NE ((uint16_t)0x0260)
#define USART_IT_FE ((uint16_t)0x0160)
延伸IAP
Reset_Handler
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
代码: IAP bootloader.
GICv2
1、GIC 控制器总览
GIC 是 ARM 公司给 Cortex-A/R 内核提供的一个中断控制器,类似 Cortex-M 内核中的 NVIC。目前 GIC 有 4 个版本:V1~V4,V1 是最老的版本,已经被废弃了。V2~V4 目前正在大量的使用。GIC V2 是给 ARMv7-A 架构使用的,比如 Cortex-A7、Cortex-A9、Cortex-A15 等,V3 和 V4 是给 ARMv8-A/R 架构使用的,也就是 64 位芯片使用的。I.MX6U 是 Cortex-A 内核的,因此我们主要讲解 GIC V2。GIC V2 最多支持 8 个核。ARM 会根据 GIC 版本的不同研发出不同的 IP 核,那些半导体厂商直接购买对应的 IP 核即可,比如 ARM 针对 GIC V2 就开发出了 GIC400 这个中断控制器 IP 核。当 GIC 接收到外部中断信号以后就会报给 ARM 内核,但是ARM 内核只提供了四个信号给 GIC 来汇报中断情况:VFIQ、VIRQ、FIQ 和 IRQ, GIC 接收众多的外部中断,然后对其进行处理,最终就只通过四个信号报给 ARM 内核,这四个信号的含义如下:
VFIQ:虚拟快速 FIQ VIRQ:虚拟快速 IRQ FIQ:快速中断 IRQ IRQ:外部中断 IRQ
VFIQ 和 VIRQ 是针对虚拟化的,我们讨论虚拟化,剩下的就是 FIQ 和 IRQ 了
左侧部分就是中断源,中间部分就是 GIC 控制器,最右侧就是中断控制器向处理器内核发送中断信息。我们重点要看的肯定是中间的 GIC 部分,GIC 将众多的中断源分为分为三类: 1、SPI(Shared Peripheral Interrupt),共享中断,顾名思义,所有 Core 共享的中断,这个是最常见的,那些外部中断都属于 SPI 中断(注意!不是 SPI 总线那个中断) 。比如按键中断、串口中断等等,这些中断所有的 Core 都可以处理,不限定特定 Core。 2、PPI(Private Peripheral Interrupt),私有中断,我们说了 GIC 是支持多核的,每个核肯定有自己独有的中断。这些独有的中断肯定是要指定的核心处理,因此这些中断就叫做私有中断。 3、SGI(Software-generated Interrupt),软件中断,由软件触发引起的中断,通过向寄存器GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信。
2、中断 ID
中断源有很多,为了区分这些不同的中断源肯定要给他们分配一个唯一 ID,这些 ID 就是中断 ID。每一个 CPU 最多支持 1020 个中断 ID,中断 ID 号为 ID0~ID1019。这 1020个 ID 包含了 PPI、SPI 和 SGI,那么这三类中断是如何分配这 1020 个中断 ID 的呢?这 1020 个 ID 分配如下: ID0~ID15:这 16 个 ID 分配给 SGI。 ID16~ID31:这 16 个 ID 分配给 PPI。 ID32~ID1019:这 988 个 ID 分配给 SPI,像 GPIO 中断、串口中断等这些外部中断 ,至于具体到某个 ID 对应哪个中断那就由半导体厂商根据实际情况去定义了。比如 I.MX6U的总共使用了 128 个中断 ID,加上前面属于 PPI 和 SGI 的 32 个 ID, I.MX6U 的中断源共有 128+32=160个,这 128 个中断 ID 对应的中断在《I.MX6ULL 参考手册》的“3.2 Cortex A7 interrupts”小节NXP 官方 SDK中的文件 MCIMX6Y2C.h,在此文件中定义了一个枚举类型 IRQn_Type,此枚举类型就枚举出了 I.MX6U 的所有中断,代码如下所示:
#define NUMBER_OF_INT_VECTORS 160
typedef enum IRQn {
NotAvail_IRQn = -128,
Software0_IRQn = 0,
Software1_IRQn = 1,
Software2_IRQn = 2,
Software3_IRQn = 3,
Software4_IRQn = 4,
Software5_IRQn = 5,
Software6_IRQn = 6,
Software7_IRQn = 7,
Software8_IRQn = 8,
Software9_IRQn = 9,
Software10_IRQn = 10,
Software11_IRQn = 11,
Software12_IRQn = 12,
Software13_IRQn = 13,
Software14_IRQn = 14,
Software15_IRQn = 15,
VirtualMaintenance_IRQn = 25,
HypervisorTimer_IRQn = 26,
VirtualTimer_IRQn = 27,
LegacyFastInt_IRQn = 28,
SecurePhyTimer_IRQn = 29,
NonSecurePhyTimer_IRQn = 30,
LegacyIRQ_IRQn = 31,
IOMUXC_IRQn = 32,
DAP_IRQn = 33,
SDMA_IRQn = 34,
TSC_IRQn = 35,
SNVS_IRQn = 36,
LCDIF_IRQn = 37,
RNGB_IRQn = 38,
CSI_IRQn = 39,
PXP_IRQ0_IRQn = 40,
SCTR_IRQ0_IRQn = 41,
SCTR_IRQ1_IRQn = 42,
WDOG3_IRQn = 43,
Reserved44_IRQn = 44,
APBH_IRQn = 45,
WEIM_IRQn = 46,
RAWNAND_BCH_IRQn = 47,
RAWNAND_GPMI_IRQn = 48,
UART6_IRQn = 49,
PXP_IRQ1_IRQn = 50,
SNVS_Consolidated_IRQn = 51,
SNVS_Security_IRQn = 52,
CSU_IRQn = 53,
USDHC1_IRQn = 54,
USDHC2_IRQn = 55,
SAI3_RX_IRQn = 56,
SAI3_TX_IRQn = 57,
UART1_IRQn = 58,
UART2_IRQn = 59,
UART3_IRQn = 60,
UART4_IRQn = 61,
UART5_IRQn = 62,
eCSPI1_IRQn = 63,
eCSPI2_IRQn = 64,
eCSPI3_IRQn = 65,
eCSPI4_IRQn = 66,
I2C4_IRQn = 67,
I2C1_IRQn = 68,
I2C2_IRQn = 69,
I2C3_IRQn = 70,
UART7_IRQn = 71,
UART8_IRQn = 72,
Reserved73_IRQn = 73,
USB_OTG2_IRQn = 74,
USB_OTG1_IRQn = 75,
USB_PHY1_IRQn = 76,
USB_PHY2_IRQn = 77,
DCP_IRQ_IRQn = 78,
DCP_VMI_IRQ_IRQn = 79,
DCP_SEC_IRQ_IRQn = 80,
TEMPMON_IRQn = 81,
ASRC_IRQn = 82,
ESAI_IRQn = 83,
SPDIF_IRQn = 84,
Reserved85_IRQn = 85,
PMU_IRQ1_IRQn = 86,
GPT1_IRQn = 87,
EPIT1_IRQn = 88,
EPIT2_IRQn = 89,
GPIO1_INT7_IRQn = 90,
GPIO1_INT6_IRQn = 91,
GPIO1_INT5_IRQn = 92,
GPIO1_INT4_IRQn = 93,
GPIO1_INT3_IRQn = 94,
GPIO1_INT2_IRQn = 95,
GPIO1_INT1_IRQn = 96,
GPIO1_INT0_IRQn = 97,
GPIO1_Combined_0_15_IRQn = 98,
GPIO1_Combined_16_31_IRQn = 99,
GPIO2_Combined_0_15_IRQn = 100,
GPIO2_Combined_16_31_IRQn = 101,
GPIO3_Combined_0_15_IRQn = 102,
GPIO3_Combined_16_31_IRQn = 103,
GPIO4_Combined_0_15_IRQn = 104,
GPIO4_Combined_16_31_IRQn = 105,
GPIO5_Combined_0_15_IRQn = 106,
GPIO5_Combined_16_31_IRQn = 107,
Reserved108_IRQn = 108,
Reserved109_IRQn = 109,
Reserved110_IRQn = 110,
Reserved111_IRQn = 111,
WDOG1_IRQn = 112,
WDOG2_IRQn = 113,
KPP_IRQn = 114,
PWM1_IRQn = 115,
PWM2_IRQn = 116,
PWM3_IRQn = 117,
PWM4_IRQn = 118,
CCM_IRQ1_IRQn = 119,
CCM_IRQ2_IRQn = 120,
GPC_IRQn = 121,
Reserved122_IRQn = 122,
SRC_IRQn = 123,
Reserved124_IRQn = 124,
Reserved125_IRQn = 125,
CPU_PerformanceUnit_IRQn = 126,
CPU_CTI_Trigger_IRQn = 127,
SRC_Combined_IRQn = 128,
SAI1_IRQn = 129,
SAI2_IRQn = 130,
Reserved131_IRQn = 131,
ADC1_IRQn = 132,
ADC_5HC_IRQn = 133,
Reserved134_IRQn = 134,
Reserved135_IRQn = 135,
SJC_IRQn = 136,
CAAM_Job_Ring0_IRQn = 137,
CAAM_Job_Ring1_IRQn = 138,
QSPI_IRQn = 139,
TZASC_IRQn = 140,
GPT2_IRQn = 141,
CAN1_IRQn = 142,
CAN2_IRQn = 143,
Reserved144_IRQn = 144,
Reserved145_IRQn = 145,
PWM5_IRQn = 146,
PWM6_IRQn = 147,
PWM7_IRQn = 148,
PWM8_IRQn = 149,
ENET1_IRQn = 150,
ENET1_1588_IRQn = 151,
ENET2_IRQn = 152,
ENET2_1588_IRQn = 153,
Reserved154_IRQn = 154,
Reserved155_IRQn = 155,
Reserved156_IRQn = 156,
Reserved157_IRQn = 157,
Reserved158_IRQn = 158,
PMU_IRQ2_IRQn = 159
} IRQn_Type;
3、GIC 逻辑分块
GIC 架构分为了两个逻辑块: Distributor 和 CPU Interface,也就是分发器端和 CPU 接口端。 这两个逻辑块的含义如下: Distributor(分发器端):从上图可以看出,此逻辑块负责处理各个中断事件的分发问题,也就是中断事件应该发送到哪个 CPU Interface 上去。分发器收集所有的中断源,可以控制每个中断的优先级,它总是将优先级最高的中断事件发送到 CPU 接口端。分发器端要做的主要工作如下: 1、全局中断使能控制。 2、控制每一个中断的使能或者关闭。 3、设置每个中断的优先级。 4、设置每个中断的目标处理器列表。 5、设置每个外部中断的触发模式:电平触发或边沿触发。 6、设置每个中断属于组 0 还是组 1。 CPU Interface(CPU 接口端):CPU 接口端听名字就知道是和 CPU Core 相连接的,因此在图 17.1.3.2 中每个 CPU Core 都可以在 GIC 中找到一个与之对应的 CPU Interface。CPU 接口端就是分发器和 CPU Core 之间的桥梁,CPU 接口端主要工作如下: 1、使能或者关闭发送到 CPU Core 的中断请求信号。 2、应答中断。 3、通知中断处理完成。 4、设置优先级掩码,通过掩码来设置哪些中断不需要上报给 CPU Core。 5、定义抢占策略。 6、当多个中断到来的时候,选择优先级最高的中断通知给 CPU Core。
文件 core_ca7.h 定义了 GIC 结构体,此结构体里面的寄存器分为了分发器端和 CPU 接口端,寄存器定义如下所示:
typedef struct
{
uint32_t RESERVED0[1024];
__IOM uint32_t D_CTLR;
__IM uint32_t D_TYPER;
__IM uint32_t D_IIDR;
uint32_t RESERVED1[29];
__IOM uint32_t D_IGROUPR[16];
uint32_t RESERVED2[16];
__IOM uint32_t D_ISENABLER[16];
uint32_t RESERVED3[16];
__IOM uint32_t D_ICENABLER[16];
uint32_t RESERVED4[16];
__IOM uint32_t D_ISPENDR[16];
uint32_t RESERVED5[16];
__IOM uint32_t D_ICPENDR[16];
uint32_t RESERVED6[16];
__IOM uint32_t D_ISACTIVER[16];
uint32_t RESERVED7[16];
__IOM uint32_t D_ICACTIVER[16];
uint32_t RESERVED8[16];
__IOM uint8_t D_IPRIORITYR[512];
uint32_t RESERVED9[128];
__IOM uint8_t D_ITARGETSR[512];
uint32_t RESERVED10[128];
__IOM uint32_t D_ICFGR[32];
uint32_t RESERVED11[32];
__IM uint32_t D_PPISR;
__IM uint32_t D_SPISR[15];
uint32_t RESERVED12[112];
__OM uint32_t D_SGIR;
uint32_t RESERVED13[3];
__IOM uint8_t D_CPENDSGIR[16];
__IOM uint8_t D_SPENDSGIR[16];
uint32_t RESERVED14[40];
__IM uint32_t D_PIDR4;
__IM uint32_t D_PIDR5;
__IM uint32_t D_PIDR6;
__IM uint32_t D_PIDR7;
__IM uint32_t D_PIDR0;
__IM uint32_t D_PIDR1;
__IM uint32_t D_PIDR2;
__IM uint32_t D_PIDR3;
__IM uint32_t D_CIDR0;
__IM uint32_t D_CIDR1;
__IM uint32_t D_CIDR2;
__IM uint32_t D_CIDR3;
__IOM uint32_t C_CTLR;
__IOM uint32_t C_PMR;
__IOM uint32_t C_BPR;
__IM uint32_t C_IAR;
__OM uint32_t C_EOIR;
__IM uint32_t C_RPR;
__IM uint32_t C_HPPIR;
__IOM uint32_t C_ABPR;
__IM uint32_t C_AIAR;
__OM uint32_t C_AEOIR;
__IM uint32_t C_AHPPIR;
uint32_t RESERVED15[41];
__IOM uint32_t C_APR0;
uint32_t RESERVED16[3];
__IOM uint32_t C_NSAPR0;
uint32_t RESERVED17[6];
__IM uint32_t C_IIDR;
uint32_t RESERVED18[960];
__OM uint32_t C_DIR;
} GIC_Type;
结构体 GIC_Type 就是 GIC 控制器,列举出了 GIC 控制器的所有寄存器,可以通过结构体 GIC_Type 来访问 GIC 的所有寄存器。 第 5 行是 GIC 的分发器端相关寄存器,其相对于 GIC 基地址偏移为 0X1000,因此我们获取到 GIC 基地址以后只需要加上 0X1000 即可访问 GIC 分发器端寄存器。 第 51 行是 GIC 的 CPU 接口端相关寄存器,其相对于 GIC 基地址的偏移为 0X2000,同样的,获取到 GIC 基地址以后只需要加上 0X2000 即可访问 GIC 的 CPU 接口段寄存器。
主要通过CP15协处理来操作GIC
中断使能
中断使能包括两部分,一个是 IRQ 或者 FIQ 总中断使能,另一个就是 ID0~ID1019 这 1020个中断源的使能。
1、IRQ 和 FIQ 总中断使能
IRQ 和 FIQ 分别是外部中断和快速中断的总开关,就类似家里买的进户总电闸,然后ID0~ID1019 这 1020 个中断源就类似家里面的各个电器开关。要想开电视,那肯定要保证进户总电闸是打开的,因此要想使用 I.MX6U 上的外设中断就必须先打开 IRQ 中断(本教程不使用FIQ)。在“6.3.2 程序状态寄存器”小节已经讲过了,寄存器 CPSR 的 I=1 禁止 IRQ,当 I=0 使能 IRQ;F=1 禁止 FIQ,F=0 使能 FIQ。我们还有更简单的指令来完成 IRQ 或者 FIQ 的使能和禁止:
指令 | 描述 |
---|
cpsid i | 禁止 IRQ 中断 | cpsie i | 使能 IRQ 中断 | cpsid f | 禁止 FIQ 中断 | cpsie f | 使能 FIQ 中断 |
2、ID0~ID1019 中断使能和禁止
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 中断的。
中断优先级设置
1、优先级数配置
学过 STM32 都知道 Cortex-M 的中断优先级分为抢占优先级和子优先级,两者是可以配置的。同样的 Cortex-A7 的中断优先级也可以分为抢占优先级和子优先级,两者同样是可以配置的。Cortex-A7 最多可以支持 256 个优先级,数字越小,优先级越高!半导体厂商自行决定选择多少个优先级。I.MX6U 选择了 32 个优先级。在使用中断的时候需要初始化 GICC_PMR 寄存器,此寄存器用来决定使用几级优先级 ,寄存器结构如图 17.1.6.1 所示: GICC_PMR 寄存器只有低 8 位有效,这 8 位最多可以设置 256 个优先级,其他优先级数设置如表 I.MX6U 支持 32 个优先级,所以 GICC_PMR 要设置为 0b11111000。
2、抢占优先级和子优先级位数设置
抢占优先级和子优先级各占多少位是由寄存器 GICC_BPR 来决定的 寄存器 GICC_BPR 只有低 3 位有效,其值不同,抢占优先级和子优先级占用的位数也不同,配置如表 为了简单起见,一般将所有的中断优先级位都配置为抢占优先级,比如 I.MX6U 的优先级位数为 5(32 个优先级),所以可以设置 Binary point 为 2,表示 5 个优先级位全部为抢占优先级。
3、优先级设置
前面已经设置好了 I.MX6U 一共有 32 个抢占优先级,数字越小优先级越高。具体要使用某个中断的时候就可以设置其优先级为0~31。某个中断 D的中断优先级设置由寄存器D_IPRIORITYR 来完成,前面说了 Cortex-A7 使用了 512 个中断 ID,每个中断 ID 配有一个优先级寄存器,所以一共有 512 个 D_IPRIORITYR 寄存器。如果优先级个数为32的话,使用寄存器 D_IPRIORITYR 的 bit7:4 来设置优先级,也就是说实际的优先级要左移 3 位。比如要设置ID40 中断的优先级为 5,示例代码如下: GICD_IPRIORITYR[40] = 5 << 3; 有关优先级设置的内容就讲解到这里,优先级设置主要有三部分:
1、设置寄存器 GICC_PMR,配置优先级个数,比如 I.MX6U 支持 32 级优先级。 2、设置抢占优先级和子优先级位数,一般为了简单起见,会将所有的位数都设置为抢占 优先级。 3、设置指定中断 ID 的优先级,也就是设置外设优先级。
代码: link.
参考 代码: I.MX6U嵌入式Linux驱动开发指南V1.5.pdf.
|