GIC 控制器简介
1、GIC 控制器总览
- VFIQ:虚拟快速 FIQ。
- VIRQ:虚拟外部 IRQ。
- FIQ:快速中断 IRQ。
- IRQ:外部中断 IRQ。
- ①、SPI(Shared Peripheral Interrupt),共享中断,顾名思义,所有 Core 共享的中断,这个是最常见的,那些外部中断都属于 SPI 中断(注意!不是 SPI 总线那个中断) 。比如按键中断、串口中断等等,这些中断所有的 Core 都可以处理,不限定特定 Core。
- ②、PPI(Private Peripheral Interrupt),私有中断,我们说了 GIC 是支持多核的,每个核肯定 有自己独有的中断。这些独有的中断肯定是要指定的核心处理,因此这些中断就叫做私有中断。
- ③、SGI(Software-generated Interrupt),软件中断,由软件触发引起的中断,通过向寄存器GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信。
2、中断 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 CortexA7 interrupts”小节,中断源如表 17.1.3.1 所示:
/** Interrupt Number Definitions */
#define NUMBER_OF_INT_VECTORS 160 /**< Number of interrupts in the Vector table */typedef enum IRQn {/* Auxiliary constants */NotAvail_IRQn = -128, /**< Not available device specific interrupt *//* Core interrupts */Software0_IRQn = 0, /**< Cortex-A7 Software Generated Interrupt 0 */Software1_IRQn = 1, /**< Cortex-A7 Software Generated Interrupt 1 */Software2_IRQn = 2, /**< Cortex-A7 Software Generated Interrupt 2 */Software3_IRQn = 3, /**< Cortex-A7 Software Generated Interrupt 3 */Software4_IRQn = 4, /**< Cortex-A7 Software Generated Interrupt 4 */Software5_IRQn = 5, /**< Cortex-A7 Software Generated Interrupt 5 */Software6_IRQn = 6, /**< Cortex-A7 Software Generated Interrupt 6 */Software7_IRQn = 7, /**< Cortex-A7 Software Generated Interrupt 7 */Software8_IRQn = 8, /**< Cortex-A7 Software Generated Interrupt 8 */Software9_IRQn = 9, /**< Cortex-A7 Software Generated Interrupt 9 */Software10_IRQn = 10, /**< Cortex-A7 Software Generated Interrupt 10 */Software11_IRQn = 11, /**< Cortex-A7 Software Generated Interrupt 11 */Software12_IRQn = 12, /**< Cortex-A7 Software Generated Interrupt 12 */Software13_IRQn = 13, /**< Cortex-A7 Software Generated Interrupt 13 */Software14_IRQn = 14, /**< Cortex-A7 Software Generated Interrupt 14 */Software15_IRQn = 15, /**< Cortex-A7 Software Generated Interrupt 15 */VirtualMaintenance_IRQn = 25, /**< Cortex-A7 Virtual Maintenance Interrupt */HypervisorTimer_IRQn = 26, /**< Cortex-A7 Hypervisor Timer Interrupt */VirtualTimer_IRQn = 27, /**< Cortex-A7 Virtual Timer Interrupt */LegacyFastInt_IRQn = 28, /**< Cortex-A7 Legacy nFIQ signal Interrupt */SecurePhyTimer_IRQn = 29, /**< Cortex-A7 Secure Physical Timer Interrupt */NonSecurePhyTimer_IRQn = 30, /**< Cortex-A7 Non-secure Physical Timer Interrupt */LegacyIRQ_IRQn = 31, /**< Cortex-A7 Legacy nIRQ Interrupt *//* Device specific interrupts */IOMUXC_IRQn = 32, /**< General Purpose Register 1 from IOMUXC. Used to notify cores on exception condition while boot. */DAP_IRQn = 33, /**< Debug Access Port interrupt request. */SDMA_IRQn = 34, /**< SDMA interrupt request from all channels. */TSC_IRQn = 35, /**< TSC interrupt. */SNVS_IRQn = 36, /**< Logic OR of SNVS_LP and SNVS_HP interrupts. */LCDIF_IRQn = 37, /**< LCDIF sync interrupt. */RNGB_IRQn = 38, /**< RNGB interrupt. */CSI_IRQn = 39, /**< CMOS Sensor Interface interrupt request. */PXP_IRQ0_IRQn = 40, /**< PXP interrupt pxp_irq_0. */SCTR_IRQ0_IRQn = 41, /**< SCTR compare interrupt ipi_int[0]. */SCTR_IRQ1_IRQn = 42, /**< SCTR compare interrupt ipi_int[1]. */WDOG3_IRQn = 43, /**< WDOG3 timer reset interrupt request. */Reserved44_IRQn = 44, /**< Reserved */APBH_IRQn = 45, /**< DMA Logical OR of APBH DMA channels 0-3 completion and error interrupts. */WEIM_IRQn = 46, /**< WEIM interrupt request. */RAWNAND_BCH_IRQn = 47, /**< BCH operation complete interrupt. */RAWNAND_GPMI_IRQn = 48, /**< GPMI operation timeout error interrupt. */UART6_IRQn = 49, /**< UART6 interrupt request. */PXP_IRQ1_IRQn = 50, /**< PXP interrupt pxp_irq_1. */SNVS_Consolidated_IRQn = 51, /**< SNVS consolidated interrupt. */SNVS_Security_IRQn = 52, /**< SNVS security interrupt. */CSU_IRQn = 53, /**< CSU interrupt request 1. Indicates to the processor that one or more alarm inputs were asserted. */USDHC1_IRQn = 54, /**< USDHC1 (Enhanced SDHC) interrupt request. */USDHC2_IRQn = 55, /**< USDHC2 (Enhanced SDHC) interrupt request. */SAI3_RX_IRQn = 56, /**< SAI3 interrupt ipi_int_sai_rx. */SAI3_TX_IRQn = 57, /**< SAI3 interrupt ipi_int_sai_tx. */UART1_IRQn = 58, /**< UART1 interrupt request. */UART2_IRQn = 59, /**< UART2 interrupt request. */UART3_IRQn = 60, /**< UART3 interrupt request. */UART4_IRQn = 61, /**< UART4 interrupt request. */UART5_IRQn = 62, /**< UART5 interrupt request. */eCSPI1_IRQn = 63, /**< eCSPI1 interrupt request. */eCSPI2_IRQn = 64, /**< eCSPI2 interrupt request. */eCSPI3_IRQn = 65, /**< eCSPI3 interrupt request. */eCSPI4_IRQn = 66, /**< eCSPI4 interrupt request. */I2C4_IRQn = 67, /**< I2C4 interrupt request. */I2C1_IRQn = 68, /**< I2C1 interrupt request. */I2C2_IRQn = 69, /**< I2C2 interrupt request. */I2C3_IRQn = 70, /**< I2C3 interrupt request. */UART7_IRQn = 71, /**< UART-7 ORed interrupt. */UART8_IRQn = 72, /**< UART-8 ORed interrupt. */Reserved73_IRQn = 73, /**< Reserved */USB_OTG2_IRQn = 74, /**< USBO2 USB OTG2 */USB_OTG1_IRQn = 75, /**< USBO2 USB OTG1 */USB_PHY1_IRQn = 76, /**< UTMI0 interrupt request. */USB_PHY2_IRQn = 77, /**< UTMI1 interrupt request. */DCP_IRQ_IRQn = 78, /**< DCP interrupt request dcp_irq. */DCP_VMI_IRQ_IRQn = 79, /**< DCP interrupt request dcp_vmi_irq. */DCP_SEC_IRQ_IRQn = 80, /**< DCP interrupt request secure_irq. */TEMPMON_IRQn = 81, /**< Temperature Monitor Temperature Sensor (temperature greater than threshold) interrupt request. */ASRC_IRQn = 82, /**< ASRC interrupt request. */ESAI_IRQn = 83, /**< ESAI interrupt request. */SPDIF_IRQn = 84, /**< SPDIF interrupt. */Reserved85_IRQn = 85, /**< Reserved */PMU_IRQ1_IRQn = 86, /**< Brown-out event on either the 1.1, 2.5 or 3.0 regulators. */GPT1_IRQn = 87, /**< Logical OR of GPT1 rollover interrupt line, input capture 1 and 2 lines, output compare 1, 2, and 3 interrupt lines. */EPIT1_IRQn = 88, /**< EPIT1 output compare interrupt. */EPIT2_IRQn = 89, /**< EPIT2 output compare interrupt. */GPIO1_INT7_IRQn = 90, /**< INT7 interrupt request. */GPIO1_INT6_IRQn = 91, /**< INT6 interrupt request. */GPIO1_INT5_IRQn = 92, /**< INT5 interrupt request. */GPIO1_INT4_IRQn = 93, /**< INT4 interrupt request. */GPIO1_INT3_IRQn = 94, /**< INT3 interrupt request. */GPIO1_INT2_IRQn = 95, /**< INT2 interrupt request. */GPIO1_INT1_IRQn = 96, /**< INT1 interrupt request. */GPIO1_INT0_IRQn = 97, /**< INT0 interrupt request. */GPIO1_Combined_0_15_IRQn = 98, /**< Combined interrupt indication for GPIO1 signals 0 - 15. */GPIO1_Combined_16_31_IRQn = 99, /**< Combined interrupt indication for GPIO1 signals 16 - 31. */GPIO2_Combined_0_15_IRQn = 100, /**< Combined interrupt indication for GPIO2 signals 0 - 15. */GPIO2_Combined_16_31_IRQn = 101, /**< Combined interrupt indication for GPIO2 signals 16 - 31. */GPIO3_Combined_0_15_IRQn = 102, /**< Combined interrupt indication for GPIO3 signals 0 - 15. */GPIO3_Combined_16_31_IRQn = 103, /**< Combined interrupt indication for GPIO3 signals 16 - 31. */GPIO4_Combined_0_15_IRQn = 104, /**< Combined interrupt indication for GPIO4 signals 0 - 15. */GPIO4_Combined_16_31_IRQn = 105, /**< Combined interrupt indication for GPIO4 signals 16 - 31. */GPIO5_Combined_0_15_IRQn = 106, /**< Combined interrupt indication for GPIO5 signals 0 - 15. */GPIO5_Combined_16_31_IRQn = 107, /**< Combined interrupt indication for GPIO5 signals 16 - 31. */Reserved108_IRQn = 108, /**< Reserved */Reserved109_IRQn = 109, /**< Reserved */Reserved110_IRQn = 110, /**< Reserved */Reserved111_IRQn = 111, /**< Reserved */WDOG1_IRQn = 112, /**< WDOG1 timer reset interrupt request. */WDOG2_IRQn = 113, /**< WDOG2 timer reset interrupt request. */KPP_IRQn = 114, /**< Key Pad interrupt request. */PWM1_IRQn = 115, /**< hasRegInstance(`PWM1`)?`Cumulative interrupt line for PWM1. Logical OR of rollover, compare, and FIFO waterlevel crossing interrupts.`:`Reserved`) */PWM2_IRQn = 116, /**< hasRegInstance(`PWM2`)?`Cumulative interrupt line for PWM2. Logical OR of rollover, compare, and FIFO waterlevel crossing interrupts.`:`Reserved`) */PWM3_IRQn = 117, /**< hasRegInstance(`PWM3`)?`Cumulative interrupt line for PWM3. Logical OR of rollover, compare, and FIFO waterlevel crossing interrupts.`:`Reserved`) */PWM4_IRQn = 118, /**< hasRegInstance(`PWM4`)?`Cumulative interrupt line for PWM4. Logical OR of rollover, compare, and FIFO waterlevel crossing interrupts.`:`Reserved`) */CCM_IRQ1_IRQn = 119, /**< CCM interrupt request ipi_int_1. */CCM_IRQ2_IRQn = 120, /**< CCM interrupt request ipi_int_2. */GPC_IRQn = 121, /**< GPC interrupt request 1. */Reserved122_IRQn = 122, /**< Reserved */SRC_IRQn = 123, /**< SRC interrupt request src_ipi_int_1. */Reserved124_IRQn = 124, /**< Reserved */Reserved125_IRQn = 125, /**< Reserved */CPU_PerformanceUnit_IRQn = 126, /**< Performance Unit interrupt ~ipi_pmu_irq_b. */CPU_CTI_Trigger_IRQn = 127, /**< CTI trigger outputs interrupt ~ipi_cti_irq_b. */SRC_Combined_IRQn = 128, /**< Combined CPU wdog interrupts (4x) out of SRC. */SAI1_IRQn = 129, /**< SAI1 interrupt request. */SAI2_IRQn = 130, /**< SAI2 interrupt request. */Reserved131_IRQn = 131, /**< Reserved */ADC1_IRQn = 132, /**< ADC1 interrupt request. */ADC_5HC_IRQn = 133, /**< ADC_5HC interrupt request. */Reserved134_IRQn = 134, /**< Reserved */Reserved135_IRQn = 135, /**< Reserved */SJC_IRQn = 136, /**< SJC interrupt from General Purpose register. */CAAM_Job_Ring0_IRQn = 137, /**< CAAM job ring 0 interrupt ipi_caam_irq0. */CAAM_Job_Ring1_IRQn = 138, /**< CAAM job ring 1 interrupt ipi_caam_irq1. */QSPI_IRQn = 139, /**< QSPI1 interrupt request ipi_int_ored. */TZASC_IRQn = 140, /**< TZASC (PL380) interrupt request. */GPT2_IRQn = 141, /**< Logical OR of GPT2 rollover interrupt line, input capture 1 and 2 lines, output compare 1, 2 and 3 interrupt lines. */CAN1_IRQn = 142, /**< Combined interrupt of ini_int_busoff,ini_int_error,ipi_int_mbor,ipi_int_txwarning and ipi_int_waken */CAN2_IRQn = 143, /**< Combined interrupt of ini_int_busoff,ini_int_error,ipi_int_mbor,ipi_int_txwarning and ipi_int_waken */Reserved144_IRQn = 144, /**< Reserved */Reserved145_IRQn = 145, /**< Reserved */PWM5_IRQn = 146, /**< Cumulative interrupt line. OR of Rollover Interrupt line, Compare Interrupt line and FIFO Waterlevel crossing interrupt line */PWM6_IRQn = 147, /**< Cumulative interrupt line. OR of Rollover Interrupt line, Compare Interrupt line and FIFO Waterlevel crossing interrupt line */PWM7_IRQn = 148, /**< Cumulative interrupt line. OR of Rollover Interrupt line, Compare Interrupt line and FIFO Waterlevel crossing interrupt line */PWM8_IRQn = 149, /**< Cumulative interrupt line. OR of Rollover Interrupt line, Compare Interrupt line and FIFO Waterlevel crossing interrupt line */ENET1_IRQn = 150, /**< ENET1 interrupt */ENET1_1588_IRQn = 151, /**< ENET1 1588 Timer interrupt [synchronous] request. */ENET2_IRQn = 152, /**< ENET2 interrupt */ENET2_1588_IRQn = 153, /**< MAC 0 1588 Timer interrupt [synchronous] request. */Reserved154_IRQn = 154, /**< Reserved */Reserved155_IRQn = 155, /**< Reserved */Reserved156_IRQn = 156, /**< Reserved */Reserved157_IRQn = 157, /**< Reserved */Reserved158_IRQn = 158, /**< Reserved */PMU_IRQ2_IRQn = 159 /**< Brown-out event on either core, gpu or soc regulators. */
} IRQn_Type;
3、GIC 逻辑分块
- ①、全局中断使能控制。
- ②、控制每一个中断的使能或者关闭。
- ③、设置每个中断的优先级。
- ④、设置每个中断的目标处理器列表。
- ⑤、设置每个外部中断的触发模式:电平触发或边沿触发。
- ⑥、设置每个中断属于组 0 还是组 1。
- ①、使能或者关闭发送到 CPU Core 的中断请求信号。
- ②、应答中断。
- ③、通知中断处理完成。
- ④、设置优先级掩码,通过掩码来设置哪些中断不需要上报给 CPU Core。
- ⑤、定义抢占策略。
- ⑥、当多个中断到来的时候,选择优先级最高的中断通知给 CPU Core。
/** GIC寄存器描述结构体,* GIC分为分发器端和CPU接口端*/
typedef struct
{uint32_t RESERVED0[1024];__IOM uint32_t D_CTLR; /*!< Offset: 0x1000 (R/W) Distributor Control Register */__IM uint32_t D_TYPER; /*!< Offset: 0x1004 (R/ ) Interrupt Controller Type Register */__IM uint32_t D_IIDR; /*!< Offset: 0x1008 (R/ ) Distributor Implementer Identification Register */uint32_t RESERVED1[29];__IOM uint32_t D_IGROUPR[16]; /*!< Offset: 0x1080 - 0x0BC (R/W) Interrupt Group Registers */uint32_t RESERVED2[16];__IOM uint32_t D_ISENABLER[16]; /*!< Offset: 0x1100 - 0x13C (R/W) Interrupt Set-Enable Registers */uint32_t RESERVED3[16];__IOM uint32_t D_ICENABLER[16]; /*!< Offset: 0x1180 - 0x1BC (R/W) Interrupt Clear-Enable Registers */uint32_t RESERVED4[16];__IOM uint32_t D_ISPENDR[16]; /*!< Offset: 0x1200 - 0x23C (R/W) Interrupt Set-Pending Registers */uint32_t RESERVED5[16];__IOM uint32_t D_ICPENDR[16]; /*!< Offset: 0x1280 - 0x2BC (R/W) Interrupt Clear-Pending Registers */uint32_t RESERVED6[16];__IOM uint32_t D_ISACTIVER[16]; /*!< Offset: 0x1300 - 0x33C (R/W) Interrupt Set-Active Registers */uint32_t RESERVED7[16];__IOM uint32_t D_ICACTIVER[16]; /*!< Offset: 0x1380 - 0x3BC (R/W) Interrupt Clear-Active Registers */uint32_t RESERVED8[16];__IOM uint8_t D_IPRIORITYR[512]; /*!< Offset: 0x1400 - 0x5FC (R/W) Interrupt Priority Registers */uint32_t RESERVED9[128];__IOM uint8_t D_ITARGETSR[512]; /*!< Offset: 0x1800 - 0x9FC (R/W) Interrupt Targets Registers */uint32_t RESERVED10[128];__IOM uint32_t D_ICFGR[32]; /*!< Offset: 0x1C00 - 0xC7C (R/W) Interrupt configuration registers */uint32_t RESERVED11[32];__IM uint32_t D_PPISR; /*!< Offset: 0x1D00 (R/ ) Private Peripheral Interrupt Status Register */__IM uint32_t D_SPISR[15]; /*!< Offset: 0x1D04 - 0xD3C (R/ ) Shared Peripheral Interrupt Status Registers */uint32_t RESERVED12[112];__OM uint32_t D_SGIR; /*!< Offset: 0x1F00 ( /W) Software Generated Interrupt Register */uint32_t RESERVED13[3];__IOM uint8_t D_CPENDSGIR[16]; /*!< Offset: 0x1F10 - 0xF1C (R/W) SGI Clear-Pending Registers */__IOM uint8_t D_SPENDSGIR[16]; /*!< Offset: 0x1F20 - 0xF2C (R/W) SGI Set-Pending Registers */uint32_t RESERVED14[40];__IM uint32_t D_PIDR4; /*!< Offset: 0x1FD0 (R/ ) Peripheral ID4 Register */__IM uint32_t D_PIDR5; /*!< Offset: 0x1FD4 (R/ ) Peripheral ID5 Register */__IM uint32_t D_PIDR6; /*!< Offset: 0x1FD8 (R/ ) Peripheral ID6 Register */__IM uint32_t D_PIDR7; /*!< Offset: 0x1FDC (R/ ) Peripheral ID7 Register */__IM uint32_t D_PIDR0; /*!< Offset: 0x1FE0 (R/ ) Peripheral ID0 Register */__IM uint32_t D_PIDR1; /*!< Offset: 0x1FE4 (R/ ) Peripheral ID1 Register */__IM uint32_t D_PIDR2; /*!< Offset: 0x1FE8 (R/ ) Peripheral ID2 Register */__IM uint32_t D_PIDR3; /*!< Offset: 0x1FEC (R/ ) Peripheral ID3 Register */__IM uint32_t D_CIDR0; /*!< Offset: 0x1FF0 (R/ ) Component ID0 Register */__IM uint32_t D_CIDR1; /*!< Offset: 0x1FF4 (R/ ) Component ID1 Register */__IM uint32_t D_CIDR2; /*!< Offset: 0x1FF8 (R/ ) Component ID2 Register */__IM uint32_t D_CIDR3; /*!< Offset: 0x1FFC (R/ ) Component ID3 Register */__IOM uint32_t C_CTLR; /*!< Offset: 0x2000 (R/W) CPU Interface Control Register */__IOM uint32_t C_PMR; /*!< Offset: 0x2004 (R/W) Interrupt Priority Mask Register */__IOM uint32_t C_BPR; /*!< Offset: 0x2008 (R/W) Binary Point Register */__IM uint32_t C_IAR; /*!< Offset: 0x200C (R/ ) Interrupt Acknowledge Register */__OM uint32_t C_EOIR; /*!< Offset: 0x2010 ( /W) End Of Interrupt Register */__IM uint32_t C_RPR; /*!< Offset: 0x2014 (R/ ) Running Priority Register */__IM uint32_t C_HPPIR; /*!< Offset: 0x2018 (R/ ) Highest Priority Pending Interrupt Register */__IOM uint32_t C_ABPR; /*!< Offset: 0x201C (R/W) Aliased Binary Point Register */__IM uint32_t C_AIAR; /*!< Offset: 0x2020 (R/ ) Aliased Interrupt Acknowledge Register */__OM uint32_t C_AEOIR; /*!< Offset: 0x2024 ( /W) Aliased End Of Interrupt Register */__IM uint32_t C_AHPPIR; /*!< Offset: 0x2028 (R/ ) Aliased Highest Priority Pending Interrupt Register */uint32_t RESERVED15[41];__IOM uint32_t C_APR0; /*!< Offset: 0x20D0 (R/W) Active Priority Register */uint32_t RESERVED16[3];__IOM uint32_t C_NSAPR0; /*!< Offset: 0x20E0 (R/W) Non-secure Active Priority Register */uint32_t RESERVED17[6];__IM uint32_t C_IIDR; /*!< Offset: 0x20FC (R/ ) CPU Interface Identification Register */uint32_t RESERVED18[960];__OM uint32_t C_DIR; /*!< Offset: 0x3000 ( /W) Deactivate Interrupt Register */
} GIC_Type;
CP15 协处理器
- MRC: 将 CP15 协处理器中的寄存器数据读到 ARM 寄存器中。
- MCR: 将 ARM 寄存器的数据写入到 CP15 协处理器寄存器中。
- MRC 就是读 CP15 寄存器,MCR 就是写 CP15 寄存器,MCR 指令格式如下:
MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>
- cond:指令执行的条件码,如果忽略的话就表示无条件执行。
- opc1:协处理器要执行的操作码。
- Rt:ARM 源寄存器,要写入到 CP15 寄存器的数据就保存在此寄存器中。
- CRn:CP15 协处理器的目标寄存器。
- CRm:协处理器中附加的目标寄存器或者源操作数寄存器,如果不需要附加信息就将 CRm 设置为 C0,否则结果不可预测。
- opc2:可选的协处理器特定操作码,当不需要的时候要设置为 0。
MRC p15, 0, r0, c0, c0, 0
1、c0 寄存器
2、c1 寄存器
- bit13:V , 中断向量表基地址选择位,为 0 的话中断向量表基地址为 0X00000000,软件可以使用 VBAR 来重映射此基地址,也就是中断向量表重定位。为 1 的话中断向量表基地址为0XFFFF0000,此基地址不能被重映射。
- bit12:I,I Cache 使能位,为 0 的话关闭 I Cache,为 1 的话使能 I Cache。
- bit11:Z,分支预测使能位,如果开启 MMU 的话,此位也会使能。
- bit10:SW,SWP 和 SWPB 使能位,当为 0 的话关闭 SWP 和 SWPB 指令,当为 1 的时候就使能 SWP 和 SWPB 指令。
- bit9:3:未使用,保留。
- bit2:C,D Cache 和缓存一致性使能位,为 0 的时候禁止 D Cache 和缓存一致性,为 1 时使能。
- bit1:A,内存对齐检查使能位,为 0 的时候关闭内存对齐检查,为 1 的时候使能内存对齐检查。
- bit0:M,MMU 使能位,为 0 的时候禁止 MMU,为 1 的时候使能 MMU。
MRC p15, 0, <Rt>, c1, c0, 0 ;读取 SCTLR 寄存器,数据保存到 Rt 中。
MCR p15, 0, <Rt>, c1, c0, 0 ;将 Rt 中的数据写到 SCTLR(c1)寄存器中。
3、c12 寄存器
ldr r0, =0X87800000 ; r0=0X87800000
MCR p15, 0, r0, c12, c0, 0 ;将 r0 里面的数据写入到 c12 中,即 c12=0X87800000
4、c15 寄存器
MRC p15, 4, r1, c15, c0, 0 ; 获取 GIC 基础地址,基地址保存在 r1 中。
MRC p15, 4, r1, c15, c0, 0 ;获取 GIC 基地址
ADD r1, r1, #0X2000 ;GIC 基地址加 0X2000 得到 CPU 接口端寄存器起始地址
LDR r0, [r1, #0XC] ;读取 CPU 接口端起始地址+0XC 处的寄存器值,也就是寄存器;GIC_IAR 的值
中断使能
1、IRQ 和 FIQ 总中断使能
2、ID0~ID1019 中断使能和禁止
中断优先级设置
1、优先级数配置
2、抢占优先级和子优先级位数设置
3、优先级设置
GICD_IPRIORITYR[40] = 5 << 3;
硬件原理分析
本试验我们用到的硬件有:
1) LED 灯 LED0。
2)蜂鸣器。
3)1 个按键 KEY0。
按键 KEY0 的原理图如图所示
按键 KEY0 是连接到 I.MX6U 的 UART1_CTS 这个 IO 上的,KEY0接了一个 10K 的上拉电阻,因此 KEY0 没有按下的时候 UART1_CTS 应该是高电平,当 KEY0按下以后 UART1_CTS 就是低电平。
试验程序编写
1、移植 SDK 包中断相关文件
#include "core_ca7.h"
2 、重新编写 start.S 文件
.global _start /* 全局标号 *//** 描述: _start函数,首先是中断向量表的创建* 参考文档:ARM Cortex-A(armV7)编程手册V4.0.pdf P42,3 ARM Processor Modes and Registers(ARM处理器模型和寄存器)* ARM Cortex-A(armV7)编程手册V4.0.pdf P165 11.1.1 Exception priorities(异常)*/
_start:ldr pc, =Reset_Handler /* 复位中断 */ ldr pc, =Undefined_Handler /* 未定义中断 */ldr pc, =SVC_Handler /* SVC(Supervisor)中断 */ldr pc, =PrefAbort_Handler /* 预取终止中断 */ldr pc, =DataAbort_Handler /* 数据终止中断 */ldr pc, =NotUsed_Handler /* 未使用中断 */ldr pc, =IRQ_Handler /* IRQ中断 */ldr pc, =FIQ_Handler /* FIQ(快速中断)未定义中断 *//* 复位中断 */
Reset_Handler:cpsid i /* 关闭全局中断 *//* 关闭I,DCache和MMU * 采取读-改-写的方式。*/mrc p15, 0, r0, c1, c0, 0 /* 读取CP15的C1寄存器到R0中 */bic r0, r0, #(0x1 << 12) /* 清除C1寄存器的bit12位(I位),关闭I Cache */bic r0, r0, #(0x1 << 2) /* 清除C1寄存器的bit2(C位),关闭D Cache */bic r0, r0, #0x2 /* 清除C1寄存器的bit1(A位),关闭对齐 */bic r0, r0, #(0x1 << 11) /* 清除C1寄存器的bit11(Z位),关闭分支预测 */bic r0, r0, #0x1 /* 清除C1寄存器的bit0(M位),关闭MMU */mcr p15, 0, r0, c1, c0, 0 /* 将r0寄存器中的值写入到CP15的C1寄存器中 */#if 0/* 汇编版本设置中断向量表偏移 */ldr r0, =0X87800000dsbisbmcr p15, 0, r0, c12, c0, 0dsbisb
#endif/* 设置各个模式下的栈指针,* 注意:IMX6UL的堆栈是向下增长的!* 堆栈指针地址一定要是4字节地址对齐的!!!* DDR范围:0X80000000~0X9FFFFFFF*//* 进入IRQ模式 */mrs r0, cpsrbic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */orr r0, r0, #0x12 /* r0或上0x13,表示使用IRQ模式 */msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */ldr sp, =0x80600000 /* 设置IRQ模式下的栈首地址为0X80600000,大小为2MB *//* 进入SYS模式 */mrs r0, cpsrbic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */orr r0, r0, #0x1f /* r0或上0x13,表示使用SYS模式 */msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */ldr sp, =0x80400000 /* 设置SYS模式下的栈首地址为0X80400000,大小为2MB *//* 进入SVC模式 */mrs r0, cpsrbic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */ldr sp, =0X80200000 /* 设置SVC模式下的栈首地址为0X80200000,大小为2MB */cpsie i /* 打开全局中断 */
#if 0/* 使能IRQ中断 */mrs r0, cpsr /* 读取cpsr寄存器值到r0中 */bic r0, r0, #0x80 /* 将r0寄存器中bit7清零,也就是CPSR中的I位清零,表示允许IRQ中断 */msr cpsr, r0 /* 将r0重新写入到cpsr中 */
#endifb main /* 跳转到main函数 *//* 未定义中断 */
Undefined_Handler:ldr r0, =Undefined_Handlerbx r0/* SVC中断 */
SVC_Handler:ldr r0, =SVC_Handlerbx r0/* 预取终止中断 */
PrefAbort_Handler:ldr r0, =PrefAbort_Handler bx r0/* 数据终止中断 */
DataAbort_Handler:ldr r0, =DataAbort_Handlerbx r0/* 未使用的中断 */
NotUsed_Handler:ldr r0, =NotUsed_Handlerbx r0/* IRQ中断!重点!!!!! */IRQ_Handler:push {lr} /* 保存lr地址 */push {r0-r3, r12} /* 保存r0-r3,r12寄存器 */mrs r0, spsr /* 读取spsr寄存器 */push {r0} /* 保存spsr寄存器 */mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中* 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49* Cortex-A7 Technical ReferenceManua.pdf P68 P138*/ add r1, r1, #0X2000 /* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */ldr r0, [r1, #0XC] /* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,* GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据* 这个中断号来绝对调用哪个中断服务函数*/push {r0, r1} /* 保存r0,r1 */cps #0x13 /* 进入SVC模式,允许其他中断再次进去 */push {lr} /* 保存SVC模式的lr寄存器 */ldr r2, =system_irqhandler /* 加载C语言中断处理函数到r2寄存器中*/blx r2 /* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */pop {lr} /* 执行完C语言中断服务函数,lr出栈 */cps #0x12 /* 进入IRQ模式 */pop {r0, r1} str r0, [r1, #0X10] /* 中断执行完成,写EOIR */pop {r0} msr spsr_cxsf, r0 /* 恢复spsr */pop {r0-r3, r12} /* r0-r3,r12出栈 */pop {lr} /* lr出栈 */subs pc, lr, #4 /* 将lr-4赋给pc *//* FIQ中断 */
FIQ_Handler:ldr r0, =FIQ_Handler bx r0
第 110 到 144 行是中断服务函数 IRQ_Handler,这个是本章的重点,因为所有的外部中断最终都会触发 IRQ 中断,所以 IRQ 中断服务函数主要的工作就是区分当前发生的什么中断(中断 ID)?然后针对不同的外部中断做出不同的处理。第 111 到 115 行是保存现场,第 117 到 122行是获取当前中断号,中断号被保存到了 r0 寄存器中。第 131 和 132 行才是中断处理的重点,这两行相当于调用了函数 system_irqhandler,函数 system_irqhandler 是一个 C 语言函数,此函数有一个参数,这个参数中断号,所以我们需要传递一个参数。汇编中调用 C 函数如何实现参数传递呢?根据 ATPCS(ARM-Thumb Procedure Call Standard)定义的函数参数传递规则,在汇编调用 C 函数的时候建议形参不要超过 4 个,形参可以由 r0~r3 这四个寄存器来传递,如果形参大于 4 个,那么大于 4 个的部分要使用堆栈进行传递。所以给 r0 寄存器写入中断号就可以了函数 system_irqhandler 的参数传递,在 136 行已经向 r0 寄存器写入了中断号了。中断的真正处理过程其实是在函数 system_irqhandler 中完成,稍后需要编写函数 system_irqhandler。
第 137 行向 GICC_EOIR 寄存器写入刚刚处理完成的中断号,当一个中断处理完成以后必须向 GICC_EOIR 寄存器写入其中断号表示中断处理完成。
0X2000 MOV R1, R0 ;执行
0X2004 MOV R2, R3 ;译指
0X2008 MOV R4, R5 ;取值 PC
3、通用中断驱动文件编写
#ifndef _BSP_INT_H
#define _BSP_INT_H
#include "imx6ul.h"/* 中断服务函数形式 */
typedef void (*system_irq_handler_t) (unsigned int giccIar, void *param);/* 中断服务函数结构体*/
typedef struct _sys_irq_handle
{system_irq_handler_t irqHandler; /* 中断服务函数 */void *userParam; /* 中断服务函数参数 */
} sys_irq_handle_t;/* 函数声明 */
void int_init(void);
void system_irqtable_init(void);
void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam);
void system_irqhandler(unsigned int giccIar);
void default_irqhandler(unsigned int giccIar, void *userParam); #endif
#include "bsp_int.h"/* 中断嵌套计数器 */
static unsigned int irqNesting;/* 中断服务函数表 */
static sys_irq_handle_t irqTable[NUMBER_OF_INT_VECTORS];/** @description : 中断初始化函数* @param : 无* @return : 无*/
void int_init(void)
{GIC_Init(); /* 初始化GIC */system_irqtable_init(); /* 初始化中断表 */__set_VBAR((uint32_t)0x87800000); /* 中断向量表偏移,偏移到起始地址 */
}/** @description : 初始化中断服务函数表 * @param : 无* @return : 无*/
void system_irqtable_init(void)
{unsigned int i = 0;irqNesting = 0;/* 先将所有的中断服务函数设置为默认值 */for(i = 0; i < NUMBER_OF_INT_VECTORS; i++){system_register_irqhandler((IRQn_Type)i,default_irqhandler, NULL);}
}/** @description : 给指定的中断号注册中断服务函数 * @param - irq : 要注册的中断号* @param - handler : 要注册的中断处理函数* @param - usrParam : 中断服务处理函数参数* @return : 无*/
void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam)
{irqTable[irq].irqHandler = handler;irqTable[irq].userParam = userParam;
}/** @description : C语言中断服务函数,irq汇编中断服务函数会调用此函数,此函数通过在中断服务列表中查找指定中断号所对应的中断处理函数并执行。* @param - giccIar : 中断号* @return : 无*/
void system_irqhandler(unsigned int giccIar)
{uint32_t intNum = giccIar & 0x3FFUL;/* 检查中断号是否符合要求 */if ((intNum == 1023) || (intNum >= NUMBER_OF_INT_VECTORS)){return;}irqNesting++; /* 中断嵌套计数器加一 *//* 根据传递进来的中断号,在irqTable中调用确定的中断服务函数*/irqTable[intNum].irqHandler(intNum, irqTable[intNum].userParam);irqNesting--; /* 中断执行完成,中断嵌套寄存器减一 */}/** @description : 默认中断服务函数* @param - giccIar : 中断号* @param - usrParam : 中断服务处理函数参数* @return : 无*/
void default_irqhandler(unsigned int giccIar, void *userParam)
{while(1) {}
}
4、修改 GPIO 驱动文件
#ifndef _BSP_GPIO_H
#define _BSP_GPIO_H
#define _BSP_KEY_H
#include "imx6ul.h"/* * 枚举类型和结构体定义 */
typedef enum _gpio_pin_direction
{kGPIO_DigitalInput = 0U, /* 输入 */kGPIO_DigitalOutput = 1U, /* 输出 */
} gpio_pin_direction_t;/** GPIO中断触发类型枚举*/
typedef enum _gpio_interrupt_mode
{kGPIO_NoIntmode = 0U, /* 无中断功能 */kGPIO_IntLowLevel = 1U, /* 低电平触发 */kGPIO_IntHighLevel = 2U, /* 高电平触发 */kGPIO_IntRisingEdge = 3U, /* 上升沿触发 */kGPIO_IntFallingEdge = 4U, /* 下降沿触发 */kGPIO_IntRisingOrFallingEdge = 5U, /* 上升沿和下降沿都触发 */
} gpio_interrupt_mode_t; /** GPIO配置结构体*/
typedef struct _gpio_pin_config
{gpio_pin_direction_t direction; /* GPIO方向:输入还是输出 */uint8_t outputLogic; /* 如果是输出的话,默认输出电平 */gpio_interrupt_mode_t interruptMode; /* 中断方式 */
} gpio_pin_config_t;/* 函数声明 */
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config);
int gpio_pinread(GPIO_Type *base, int pin);
void gpio_pinwrite(GPIO_Type *base, int pin, int value);
void gpio_intconfig(GPIO_Type* base, unsigned int pin, gpio_interrupt_mode_t pinInterruptMode);
void gpio_enableint(GPIO_Type* base, unsigned int pin);
void gpio_disableint(GPIO_Type* base, unsigned int pin);
void gpio_clearintflags(GPIO_Type* base, unsigned int pin);#endif
#include "bsp_gpio.h"/** @description : GPIO初始化。* @param - base : 要初始化的GPIO组。* @param - pin : 要初始化GPIO在组内的编号。* @param - config : GPIO配置结构体。* @return : 无*/
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
{base->IMR &= ~(1U << pin);if(config->direction == kGPIO_DigitalInput) /* GPIO作为输入 */{base->GDIR &= ~( 1 << pin);}else /* 输出 */{base->GDIR |= 1 << pin;gpio_pinwrite(base,pin, config->outputLogic); /* 设置默认输出电平 */}gpio_intconfig(base, pin, config->interruptMode); /* 中断功能配置 */
}/** @description : 读取指定GPIO的电平值 。* @param - base : 要读取的GPIO组。* @param - pin : 要读取的GPIO脚号。* @return : 无*/int gpio_pinread(GPIO_Type *base, int pin){return (((base->DR) >> pin) & 0x1);}/** @description : 指定GPIO输出高或者低电平 。* @param - base : 要输出的的GPIO组。* @param - pin : 要输出的GPIO脚号。* @param - value : 要输出的电平,1 输出高电平, 0 输出低低电平* @return : 无*/
void gpio_pinwrite(GPIO_Type *base, int pin, int value)
{if (value == 0U){base->DR &= ~(1U << pin); /* 输出低电平 */}else{base->DR |= (1U << pin); /* 输出高电平 */}
}/** @description : 设置GPIO的中断配置功能* @param - base : 要配置的IO所在的GPIO组。* @param - pin : 要配置的GPIO脚号。* @param - pinInterruptMode: 中断模式,参考枚举类型gpio_interrupt_mode_t* @return : 无*/
void gpio_intconfig(GPIO_Type* base, unsigned int pin, gpio_interrupt_mode_t pin_int_mode)
{volatile uint32_t *icr;uint32_t icrShift;icrShift = pin;base->EDGE_SEL &= ~(1U << pin);if(pin < 16) /* 低16位 */{icr = &(base->ICR1);}else /* 高16位 */{icr = &(base->ICR2);icrShift -= 16;}switch(pin_int_mode){case(kGPIO_IntLowLevel):*icr &= ~(3U << (2 * icrShift));break;case(kGPIO_IntHighLevel):*icr = (*icr & (~(3U << (2 * icrShift)))) | (1U << (2 * icrShift));break;case(kGPIO_IntRisingEdge):*icr = (*icr & (~(3U << (2 * icrShift)))) | (2U << (2 * icrShift));break;case(kGPIO_IntFallingEdge):*icr |= (3U << (2 * icrShift));break;case(kGPIO_IntRisingOrFallingEdge):base->EDGE_SEL |= (1U << pin);break;default:break;}
}/** @description : 使能GPIO的中断功能* @param - base : 要使能的IO所在的GPIO组。* @param - pin : 要使能的GPIO在组内的编号。* @return : 无*/
void gpio_enableint(GPIO_Type* base, unsigned int pin)
{ base->IMR |= (1 << pin);
}/** @description : 禁止GPIO的中断功能* @param - base : 要禁止的IO所在的GPIO组。* @param - pin : 要禁止的GPIO在组内的编号。* @return : 无*/
void gpio_disableint(GPIO_Type* base, unsigned int pin)
{ base->IMR &= ~(1 << pin);
}/** @description : 清除中断标志位(写1清除)* @param - base : 要清除的IO所在的GPIO组。* @param - pin : 要清除的GPIO掩码。* @return : 无*/
void gpio_clearintflags(GPIO_Type* base, unsigned int pin)
{base->ISR |= (1 << pin);
}
- gpio_intconfig:配置 GPIO 的中断功能。
- gpio_enableint:GPIO 中断使能函数。
- gpio_disableint:GPIO 中断禁止函数。
- gpio_clearintflags:GPIO 中断标志位清除函数。
5、按键中断驱动文件编写
#ifndef _BSP_EXIT_H
#define _BSP_EXIT_H
#include "imx6ul.h"/* 函数声明 */
void exit_init(void); /* 中断初始化 */
void gpio1_io18_irqhandler(void); /* 中断处理函数 */#endif
#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); /* 清除中断标志位 */
}
6、编写 main.c 文件
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_exit.h"/** @description : main函数* @param : 无* @return : 无*/
int main(void)
{unsigned char state = OFF;int_init(); /* 初始化中断(一定要最先调用!) */imx6u_clkinit(); /* 初始化系统时钟 */clk_enable(); /* 使能所有的时钟 */led_init(); /* 初始化led */beep_init(); /* 初始化beep */key_init(); /* 初始化key */exit_init(); /* 初始化按键中断 */while(1) { state = !state;led_switch(LED0, state);delay(500);}return 0;
}
编译下载验证
1、编写 Makefile 和链接脚本
CROSS_COMPILE ?= arm-linux-gnueabihf-
TARGET ?= intCC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdumpINCDIRS := imx6ul \bsp/clk \bsp/led \bsp/delay \bsp/beep \bsp/gpio \bsp/key \bsp/exit \bsp/intSRCDIRS := project \bsp/clk \bsp/led \bsp/delay \bsp/beep \bsp/gpio \bsp/key \bsp/exit \bsp/intINCLUDE := $(patsubst %, -I %, $(INCDIRS))SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))SFILENDIR := $(notdir $(SFILES))
CFILENDIR := $(notdir $(CFILES))SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS := $(SOBJS) $(COBJS)VPATH := $(SRCDIRS).PHONY: clean$(TARGET).bin : $(OBJS)$(LD) -Timx6ul.lds -o $(TARGET).elf $^$(OBJCOPY) -O binary -S $(TARGET).elf $@$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis$(SOBJS) : obj/%.o : %.S$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<$(COBJS) : obj/%.o : %.c$(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ $<clean:rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)
2、编译下载
使用 Make 命令编译代码,编译成功以后使用软件 imxdownload2 将编译完成的 bsp.bin 文件生成可执行的img文件,命令如下:
make
./imxdownload2 int.bin
如果 imxdownload2无权限,可用以下命令添加权限
chmod 777 imxdownload2
利用Win32DiskImager软件将load.img执行文件写入SD卡,SD卡插入开发板上即可正常运行。如果代码运行正常的话 LED0 会以大约 500ms 周期闪烁, 按下开发板上的 KEY0 按键,蜂鸣器打开,再按下 KEY0按键,蜂鸣器关闭。
例程
【免费】Linux学习笔记13-GPIO中断实验例程资源-CSDN文库