GPIO外部中断处理—基于I.MX6UL嵌入式SoC
1、前言
在前面的文章《GPIO按键输入—基于I.MX6UL嵌入式SoC》中,链接如下:
https://www.cnblogs.com/Cqlismy/p/12500760.html
实现了GPIO的通用输入功能,还介绍了GPIO实现按键功能的实例,在该实例中,按键是否按下,是通过不断读取GPIO的电平来进行判断的,如果该GPIO读取到低电平,也就是按键处于按下状态了,GPIO读取到高电平,则按键是处于松开状态的,但是,在实际项目开发中,并不会使用这样的按键驱动,对于GPIO按键功能的实现,一般是使用GPIO的外部中断原理去实现,GPIO除了能实现基本的输入/输出功能以外,还能够产生ARM Core中断。
本篇文章将使用GPIO的外部中断功能来实现一个简单的按键驱动,按键的另一端接入到I.MX6UL嵌入式SoC的GPIO上,当按键处于松开状态时,GPIO处于高电平状态,当按键按下后,GPIO处于低电平状态,并且触发ARM Core中断,然后处理器跳转到按键的中断服务处理函数执行,进行按键按下的事件上报。
关于ARM GIC中断相关的知识,可以参考文章《ARM Cortex-A7中断系统基础知识》,链接如下:
https://www.cnblogs.com/Cqlismy/p/12549087.html
本文对这方面的知识就不再详细介绍了。
2、GPIO外部中断处理程序
GPIO外部中断处理实例的大致编程思路如下所示:
- 使能相应的按键GPIO引脚外设时钟;
- GIC中断控制器初始化;
- 设置IO口的复用模式为GPIO,设置IO口引脚的电气属性;
- 设置GPIO的方向为输入,配置GPIO的中断触发方式为低电平触发,并使能GPIO中断功能;
- 实现GPIO外部中断的中断服务处理程序,进行按键事件的处理。
由于在GPIO外部中断处理程序中需要使用到GIC中断控制器,先移植NXP官方为I.MX6UL提供的SDK包中的core_ca7.h文件,该文件主要是访问CP15协处理器寄存器和GIC中断控制的一些底层驱动函数,需要拷贝到工程目录的imx6ul目录下,修改后的文件内容如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* Copyright (c) 2009 - 2015 ARM LIMITED Copyright (c) 2016, Freescale Semiconductor, Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of ARM nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------*/ #ifndef __CORE_CA7_H #define __CORE_CA7_H #include "types.h" #define FORCEDINLINE __attribute__((always_inline)) #define __ASM __asm /*!< asm keyword for GNU Compiler */ #define __INLINE inline /*!< inline keyword for GNU Compiler */ #define __STATIC_INLINE static inline /* following defines should be used for structure members */ #define __IM volatile const /*! Defines 'read only' structure member permissions */ #define __OM volatile /*! Defines 'write only' structure member permissions */ #define __IOM volatile /*! Defines 'read / write' structure member permissions */ #define __STRINGIFY(x) #x #define __MCR(coproc, opcode_1, src, CRn, CRm, opcode_2) \ __ASM volatile ("MCR " __STRINGIFY(p##coproc) ", " __STRINGIFY(opcode_1) ", " \ "%0, " __STRINGIFY(c##CRn) ", " __STRINGIFY(c##CRm) ", " \ __STRINGIFY(opcode_2) \ : : "r" (src) ) #define __MRC(coproc, opcode_1, CRn, CRm, opcode_2) \ ({ \ uint32_t __dst; \ __ASM volatile ("MRC " __STRINGIFY(p##coproc) ", " __STRINGIFY(opcode_1) ", " \ "%0, " __STRINGIFY(c##CRn) ", " __STRINGIFY(c##CRm) ", " \ __STRINGIFY(opcode_2) \ : "=r" (__dst) ); \ __dst; \ }) __attribute__( ( always_inline ) ) __STATIC_INLINE void __set_APSR(uint32_t apsr) { __ASM volatile ("MSR apsr, %0" : : "r" (apsr) : "cc"); } __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_CPSR(void) { uint32_t result; __ASM volatile ("MRS %0, cpsr" : "=r" (result) ); return(result); } __attribute__( ( always_inline ) ) __STATIC_INLINE void __set_CPSR(uint32_t cpsr) { __ASM volatile ("MSR cpsr, %0" : : "r" (cpsr) : "cc"); } __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FPEXC(void) { uint32_t result; __ASM volatile ("VMRS %0, fpexc" : "=r" (result) ); return result; } __attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FPEXC(uint32_t fpexc) { __ASM volatile ("VMSR fpexc, %0" : : "r" (fpexc)); } /******************************************************************************* * Register Abstraction Core Register contain: - CPSR - CP15 Registers ******************************************************************************/ /* Core Register CPSR */ typedef union { struct { uint32_t M:5; /*!< bit: 0.. 4 Mode field */ uint32_t T:1; /*!< bit: 5 Thumb execution state bit */ uint32_t F:1; /*!< bit: 6 FIQ mask bit */ uint32_t I:1; /*!< bit: 7 IRQ mask bit */ uint32_t A:1; /*!< bit: 8 Asynchronous abort mask bit */ uint32_t E:1; /*!< bit: 9 Endianness execution state bit */ uint32_t IT1:6; /*!< bit: 10..15 If-Then execution state bits 2-7 */ uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ uint32_t _reserved0:4; /*!< bit: 20..23 Reserved */ uint32_t J:1; /*!< bit: 24 Jazelle bit */ uint32_t IT0:2; /*!< bit: 25..26 If-Then execution state bits 0-1 */ uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ uint32_t C:1; /*!< bit: 29 Carry condition code flag */ uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ uint32_t N:1; /*!< bit: 31 Negative condition code flag */ } b; /*!< Structure used for bit access */ uint32_t w; /*!< Type used for word access */ } CPSR_Type; /* CPSR Register Definitions */ #define CPSR_N_Pos 31U /*!< CPSR: N Position */ #define CPSR_N_Msk (1UL << CPSR_N_Pos) /*!< CPSR: N Mask */ #define CPSR_Z_Pos 30U /*!< CPSR: Z Position */ #define CPSR_Z_Msk (1UL << CPSR_Z_Pos) /*!< CPSR: Z Mask */ #define CPSR_C_Pos 29U /*!< CPSR: C Position */ #define CPSR_C_Msk (1UL << CPSR_C_Pos) /*!< CPSR: C Mask */ #define CPSR_V_Pos 28U /*!< CPSR: V Position */ #define CPSR_V_Msk (1UL << CPSR_V_Pos) /*!< CPSR: V Mask */ #define CPSR_Q_Pos 27U /*!< CPSR: Q Position */ #define CPSR_Q_Msk (1UL << CPSR_Q_Pos) /*!< CPSR: Q Mask */ #define CPSR_IT0_Pos 25U /*!< CPSR: IT0 Position */ #define CPSR_IT0_Msk (3UL << CPSR_IT0_Pos) /*!< CPSR: IT0 Mask */ #define CPSR_J_Pos 24U /*!< CPSR: J Position */ #define CPSR_J_Msk (1UL << CPSR_J_Pos) /*!< CPSR: J Mask */ #define CPSR_GE_Pos 16U /*!< CPSR: GE Position */ #define CPSR_GE_Msk (0xFUL << CPSR_GE_Pos) /*!< CPSR: GE Mask */ #define CPSR_IT1_Pos 10U /*!< CPSR: IT1 Position */ #define CPSR_IT1_Msk (0x3FUL << CPSR_IT1_Pos) /*!< CPSR: IT1 Mask */ #define CPSR_E_Pos 9U /*!< CPSR: E Position */ #define CPSR_E_Msk (1UL << CPSR_E_Pos) /*!< CPSR: E Mask */ #define CPSR_A_Pos 8U /*!< CPSR: A Position */ #define CPSR_A_Msk (1UL << CPSR_A_Pos) /*!< CPSR: A Mask */ #define CPSR_I_Pos 7U /*!< CPSR: I Position */ #define CPSR_I_Msk (1UL << CPSR_I_Pos) /*!< CPSR: I Mask */ #define CPSR_F_Pos 6U /*!< CPSR: F Position */ #define CPSR_F_Msk (1UL << CPSR_F_Pos) /*!< CPSR: F Mask */ #define CPSR_T_Pos 5U /*!< CPSR: T Position */ #define CPSR_T_Msk (1UL << CPSR_T_Pos) /*!< CPSR: T Mask */ #define CPSR_M_Pos 0U /*!< CPSR: M Position */ #define CPSR_M_Msk (0x1FUL << CPSR_M_Pos) /*!< CPSR: M Mask */ /* CP15 Register SCTLR */ typedef union { struct { uint32_t M:1; /*!< bit: 0 MMU enable */ uint32_t A:1; /*!< bit: 1 Alignment check enable */ uint32_t C:1; /*!< bit: 2 Cache enable */ uint32_t _reserved0:2; /*!< bit: 3.. 4 Reserved */ uint32_t CP15BEN:1; /*!< bit: 5 CP15 barrier enable */ uint32_t _reserved1:1; /*!< bit: 6 Reserved */ uint32_t B:1; /*!< bit: 7 Endianness model */ uint32_t _reserved2:2; /*!< bit: 8.. 9 Reserved */ uint32_t SW:1; /*!< bit: 10 SWP and SWPB enable */ uint32_t Z:1; /*!< bit: 11 Branch prediction enable */ uint32_t I:1; /*!< bit: 12 Instruction cache enable */ uint32_t V:1; /*!< bit: 13 Vectors bit */ uint32_t RR:1; /*!< bit: 14 Round Robin select */ uint32_t _reserved3:2; /*!< bit:15..16 Reserved */ uint32_t HA:1; /*!< bit: 17 Hardware Access flag enable */ uint32_t _reserved4:1; /*!< bit: 18 Reserved */ uint32_t WXN:1; /*!< bit: 19 Write permission implies XN */ uint32_t UWXN:1; /*!< bit: 20 Unprivileged write permission implies PL1 XN */ uint32_t FI:1; /*!< bit: 21 Fast interrupts configuration enable */ uint32_t U:1; /*!< bit: 22 Alignment model */ uint32_t _reserved5:1; /*!< bit: 23 Reserved */ uint32_t VE:1; /*!< bit: 24 Interrupt Vectors Enable */ uint32_t EE:1; /*!< bit: 25 Exception Endianness */ uint32_t _reserved6:1; /*!< bit: 26 Reserved */ uint32_t NMFI:1; /*!< bit: 27 Non-maskable FIQ (NMFI) support */ uint32_t TRE:1; /*!< bit: 28 TEX remap enable. */ uint32_t AFE:1; /*!< bit: 29 Access flag enable */ uint32_t TE:1; /*!< bit: 30 Thumb Exception enable */ uint32_t _reserved7:1; /*!< bit: 31 Reserved */ } b; /*!< Structure used for bit access */ uint32_t w; /*!< Type used for word access */ } SCTLR_Type; #define SCTLR_TE_Pos 30U /*!< SCTLR: TE Position */ #define SCTLR_TE_Msk (1UL << SCTLR_TE_Pos) /*!< SCTLR: TE Mask */ #define SCTLR_AFE_Pos 29U /*!< SCTLR: AFE Position */ #define SCTLR_AFE_Msk (1UL << SCTLR_AFE_Pos) /*!< SCTLR: AFE Mask */ #define SCTLR_TRE_Pos 28U /*!< SCTLR: TRE Position */ #define SCTLR_TRE_Msk (1UL << SCTLR_TRE_Pos) /*!< SCTLR: TRE Mask */ #define SCTLR_NMFI_Pos 27U /*!< SCTLR: NMFI Position */ #define SCTLR_NMFI_Msk (1UL << SCTLR_NMFI_Pos) /*!< SCTLR: NMFI Mask */ #define SCTLR_EE_Pos 25U /*!< SCTLR: EE Position */ #define SCTLR_EE_Msk (1UL << SCTLR_EE_Pos) /*!< SCTLR: EE Mask */ #define SCTLR_VE_Pos 24U /*!< SCTLR: VE Position */ #define SCTLR_VE_Msk (1UL << SCTLR_VE_Pos) /*!< SCTLR: VE Mask */ #define SCTLR_U_Pos 22U /*!< SCTLR: U Position */ #define SCTLR_U_Msk (1UL << SCTLR_U_Pos) /*!< SCTLR: U Mask */ #define SCTLR_FI_Pos 21U /*!< SCTLR: FI Position */ #define SCTLR_FI_Msk (1UL << SCTLR_FI_Pos) /*!< SCTLR: FI Mask */ #define SCTLR_UWXN_Pos 20U /*!< SCTLR: UWXN Position */ #define SCTLR_UWXN_Msk (1UL << SCTLR_UWXN_Pos) /*!< SCTLR: UWXN Mask */ #define SCTLR_WXN_Pos 19U /*!< SCTLR: WXN Position */ #define SCTLR_WXN_Msk (1UL << SCTLR_WXN_Pos) /*!< SCTLR: WXN Mask */ #define SCTLR_HA_Pos 17U /*!< SCTLR: HA Position */ #define SCTLR_HA_Msk (1UL << SCTLR_HA_Pos) /*!< SCTLR: HA Mask */ #define SCTLR_RR_Pos 14U /*!< SCTLR: RR Position */ #define SCTLR_RR_Msk (1UL << SCTLR_RR_Pos) /*!< SCTLR: RR Mask */ #define SCTLR_V_Pos 13U /*!< SCTLR: V Position */ #define SCTLR_V_Msk (1UL << SCTLR_V_Pos) /*!< SCTLR: V Mask */ #define SCTLR_I_Pos 12U /*!< SCTLR: I Position */ #define SCTLR_I_Msk (1UL << SCTLR_I_Pos) /*!< SCTLR: I Mask */ #define SCTLR_Z_Pos 11U /*!< SCTLR: Z Position */ #define SCTLR_Z_Msk (1UL << SCTLR_Z_Pos) /*!< SCTLR: Z Mask */ #define SCTLR_SW_Pos 10U /*!< SCTLR: SW Position */ #define SCTLR_SW_Msk (1UL << SCTLR_SW_Pos) /*!< SCTLR: SW Mask */ #define SCTLR_B_Pos 7U /*!< SCTLR: B Position */ #define SCTLR_B_Msk (1UL << SCTLR_B_Pos) /*!< SCTLR: B Mask */ #define SCTLR_CP15BEN_Pos 5U /*!< SCTLR: CP15BEN Position */ #define SCTLR_CP15BEN_Msk (1UL << SCTLR_CP15BEN_Pos) /*!< SCTLR: CP15BEN Mask */ #define SCTLR_C_Pos 2U /*!< SCTLR: C Position */ #define SCTLR_C_Msk (1UL << SCTLR_C_Pos) /*!< SCTLR: C Mask */ #define SCTLR_A_Pos 1U /*!< SCTLR: A Position */ #define SCTLR_A_Msk (1UL << SCTLR_A_Pos) /*!< SCTLR: A Mask */ #define SCTLR_M_Pos 0U /*!< SCTLR: M Position */ #define SCTLR_M_Msk (1UL << SCTLR_M_Pos) /*!< SCTLR: M Mask */ /* CP15 Register ACTLR */ typedef union { struct { uint32_t _reserved0:6; /*!< bit: 0.. 5 Reserved */ uint32_t SMP:1; /*!< bit: 6 Enables coherent requests to the processor */ uint32_t _reserved1:3; /*!< bit: 7.. 9 Reserved */ uint32_t DODMBS:1; /*!< bit: 10 Disable optimized data memory barrier behavior */ uint32_t L2RADIS:1; /*!< bit: 11 L2 Data Cache read-allocate mode disable */ uint32_t L1RADIS:1; /*!< bit: 12 L1 Data Cache read-allocate mode disable */ uint32_t L1PCTL:2; /*!< bit:13..14 L1 Data prefetch control */ uint32_t DDVM:1; /*!< bit: 15 Disable Distributed Virtual Memory (DVM) transactions */ uint32_t _reserved3:12; /*!< bit:16..27 Reserved */ uint32_t DDI:1; /*!< bit: 28 Disable dual issue */ uint32_t _reserved7:3; /*!< bit:29..31 Reserved */ } b; /*!< Structure used for bit access */ uint32_t w; /*!< Type used for word access */ } ACTLR_Type; #define ACTLR_DDI_Pos 28U /*!< ACTLR: DDI Position */ #define ACTLR_DDI_Msk (1UL << ACTLR_DDI_Pos) /*!< ACTLR: DDI Mask */ #define ACTLR_DDVM_Pos 15U /*!< ACTLR: DDVM Position */ #define ACTLR_DDVM_Msk (1UL << ACTLR_DDVM_Pos) /*!< ACTLR: DDVM Mask */ #define ACTLR_L1PCTL_Pos 13U /*!< ACTLR: L1PCTL Position */ #define ACTLR_L1PCTL_Msk (3UL << ACTLR_L1PCTL_Pos) /*!< ACTLR: L1PCTL Mask */ #define ACTLR_L1RADIS_Pos 12U /*!< ACTLR: L1RADIS Position */ #define ACTLR_L1RADIS_Msk (1UL << ACTLR_L1RADIS_Pos) /*!< ACTLR: L1RADIS Mask */ #define ACTLR_L2RADIS_Pos 11U /*!< ACTLR: L2RADIS Position */ #define ACTLR_L2RADIS_Msk (1UL << ACTLR_L2RADIS_Pos) /*!< ACTLR: L2RADIS Mask */ #define ACTLR_DODMBS_Pos 10U /*!< ACTLR: DODMBS Position */ #define ACTLR_DODMBS_Msk (1UL << ACTLR_DODMBS_Pos) /*!< ACTLR: DODMBS Mask */ #define ACTLR_SMP_Pos 6U /*!< ACTLR: SMP Position */ #define ACTLR_SMP_Msk (1UL << ACTLR_SMP_Pos) /*!< ACTLR: SMP Mask */ /* CP15 Register CPACR */ typedef union { struct { uint32_t _reserved0:20; /*!< bit: 0..19 Reserved */ uint32_t cp10:2; /*!< bit:20..21 Access rights for coprocessor 10 */ uint32_t cp11:2; /*!< bit:22..23 Access rights for coprocessor 11 */ uint32_t _reserved1:6; /*!< bit:24..29 Reserved */ uint32_t D32DIS:1; /*!< bit: 30 Disable use of registers D16-D31 of the VFP register file */ uint32_t ASEDIS:1; /*!< bit: 31 Disable Advanced SIMD Functionality */ } b; /*!< Structure used for bit access */ uint32_t w; /*!< Type used for word access */ } CPACR_Type; #define CPACR_ASEDIS_Pos 31U /*!< CPACR: ASEDIS Position */ #define CPACR_ASEDIS_Msk (1UL << CPACR_ASEDIS_Pos) /*!< CPACR: ASEDIS Mask */ #define CPACR_D32DIS_Pos 30U /*!< CPACR: D32DIS Position */ #define CPACR_D32DIS_Msk (1UL << CPACR_D32DIS_Pos) /*!< CPACR: D32DIS Mask */ #define CPACR_cp11_Pos 22U /*!< CPACR: cp11 Position */ #define CPACR_cp11_Msk (3UL << CPACR_cp11_Pos) /*!< CPACR: cp11 Mask */ #define CPACR_cp10_Pos 20U /*!< CPACR: cp10 Position */ #define CPACR_cp10_Msk (3UL << CPACR_cp10_Pos) /*!< CPACR: cp10 Mask */ /* CP15 Register DFSR */ typedef union { struct { uint32_t FS0:4; /*!< bit: 0.. 3 Fault Status bits bit 0-3 */ uint32_t Domain:4; /*!< bit: 4.. 7 Fault on which domain */ uint32_t _reserved0:2; /*!< bit: 8.. 9 Reserved */ uint32_t FS1:1; /*!< bit: 10 Fault Status bits bit 4 */ uint32_t WnR:1; /*!< bit: 11 Write not Read bit */ uint32_t ExT:1; /*!< bit: 12 External abort type */ uint32_t CM:1; /*!< bit: 13 Cache maintenance fault */ uint32_t _reserved1:18; /*!< bit:14..31 Reserved */ } b; /*!< Structure used for bit access */ uint32_t w; /*!< Type used for word access */ } DFSR_Type; #define DFSR_CM_Pos 13U /*!< DFSR: CM Position */ #define DFSR_CM_Msk (1UL << DFSR_CM_Pos) /*!< DFSR: CM Mask */ #define DFSR_Ext_Pos 12U /*!< DFSR: Ext Position */ #define DFSR_Ext_Msk (1UL << DFSR_Ext_Pos) /*!< DFSR: Ext Mask */ #define DFSR_WnR_Pos 11U /*!< DFSR: WnR Position */ #define DFSR_WnR_Msk (1UL << DFSR_WnR_Pos) /*!< DFSR: WnR Mask */ #define DFSR_FS1_Pos 10U /*!< DFSR: FS1 Position */ #define DFSR_FS1_Msk (1UL << DFSR_FS1_Pos) /*!< DFSR: FS1 Mask */ #define DFSR_Domain_Pos 4U /*!< DFSR: Domain Position */ #define DFSR_Domain_Msk (0xFUL << DFSR_Domain_Pos) /*!< DFSR: Domain Mask */ #define DFSR_FS0_Pos 0U /*!< DFSR: FS0 Position */ #define DFSR_FS0_Msk (0xFUL << DFSR_FS0_Pos) /*!< DFSR: FS0 Mask */ /* CP15 Register IFSR */ typedef union { struct { uint32_t FS0:4; /*!< bit: 0.. 3 Fault Status bits bit 0-3 */ uint32_t _reserved0:6; /*!< bit: 4.. 9 Reserved */ uint32_t FS1:1; /*!< bit: 10 Fault Status bits bit 4 */ uint32_t _reserved1:1; /*!< bit: 11 Reserved */ uint32_t ExT:1; /*!< bit: 12 External abort type */ uint32_t _reserved2:19; /*!< bit:13..31 Reserved */ } b; /*!< Structure used for bit access */ uint32_t w; /*!< Type used for word access */ } IFSR_Type; #define IFSR_ExT_Pos 12U /*!< IFSR: ExT Position */ #define IFSR_ExT_Msk (1UL << IFSR_ExT_Pos) /*!< IFSR: ExT Mask */ #define IFSR_FS1_Pos 10U /*!< IFSR: FS1 Position */ #define IFSR_FS1_Msk (1UL << IFSR_FS1_Pos) /*!< IFSR: FS1 Mask */ #define IFSR_FS0_Pos 0U /*!< IFSR: FS0 Position */ #define IFSR_FS0_Msk (0xFUL << IFSR_FS0_Pos) /*!< IFSR: FS0 Mask */ /* CP15 Register ISR */ typedef union { struct { uint32_t _reserved0:6; /*!< bit: 0.. 5 Reserved */ uint32_t F:1; /*!< bit: 6 FIQ pending bit */ uint32_t I:1; /*!< bit: 7 IRQ pending bit */ uint32_t A:1; /*!< bit: 8 External abort pending bit */ uint32_t _reserved1:23; /*!< bit:14..31 Reserved */ } b; /*!< Structure used for bit access */ uint32_t w; /*!< Type used for word access */ } ISR_Type; #define ISR_A_Pos 13U /*!< ISR: A Position */ #define ISR_A_Msk (1UL << ISR_A_Pos) /*!< ISR: A Mask */ #define ISR_I_Pos 12U /*!< ISR: I Position */ #define ISR_I_Msk (1UL << ISR_I_Pos) /*!< ISR: I Mask */ #define ISR_F_Pos 11U /*!< ISR: F Position */ #define ISR_F_Msk (1UL << ISR_F_Pos) /*!< ISR: F Mask */ /* Mask and shift a bit field value for use in a register bit range. */ #define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) /* Mask and shift a register value to extract a bit filed value. */ #define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) /******************************************************************************* * CP15 Access Functions ******************************************************************************/ FORCEDINLINE __STATIC_INLINE uint32_t __get_SCTLR(void) { return __MRC(15, 0, 1, 0, 0); } FORCEDINLINE __STATIC_INLINE void __set_SCTLR(uint32_t sctlr) { __MCR(15, 0, sctlr, 1, 0, 0); } FORCEDINLINE __STATIC_INLINE uint32_t __get_ACTLR(void) { return __MRC(15, 0, 1, 0, 1); } FORCEDINLINE __STATIC_INLINE void __set_ACTLR(uint32_t actlr) { __MCR(15, 0, actlr, 1, 0, 1); } FORCEDINLINE __STATIC_INLINE uint32_t __get_CPACR(void) { return __MRC(15, 0, 1, 0, 2); } FORCEDINLINE __STATIC_INLINE void __set_CPACR(uint32_t cpacr) { __MCR(15, 0, cpacr, 1, 0, 2); } FORCEDINLINE __STATIC_INLINE uint32_t __get_TTBR0(void) { return __MRC(15, 0, 2, 0, 0); } FORCEDINLINE __STATIC_INLINE void __set_TTBR0(uint32_t ttbr0) { __MCR(15, 0, ttbr0, 2, 0, 0); } FORCEDINLINE __STATIC_INLINE uint32_t __get_TTBR1(void) { return __MRC(15, 0, 2, 0, 1); } FORCEDINLINE __STATIC_INLINE void __set_TTBR1(uint32_t ttbr1) { __MCR(15, 0, ttbr1, 2, 0, 1); } FORCEDINLINE __STATIC_INLINE uint32_t __get_TTBCR(void) { return __MRC(15, 0, 2, 0, 2); } FORCEDINLINE __STATIC_INLINE void __set_TTBCR(uint32_t ttbcr) { __MCR(15, 0, ttbcr, 2, 0, 2); } FORCEDINLINE __STATIC_INLINE uint32_t __get_DACR(void) { return __MRC(15, 0, 3, 0, 0); } FORCEDINLINE __STATIC_INLINE void __set_DACR(uint32_t dacr) { __MCR(15, 0, dacr, 3, 0, 0); } FORCEDINLINE __STATIC_INLINE uint32_t __get_DFSR(void) { return __MRC(15, 0, 5, 0, 0); } FORCEDINLINE __STATIC_INLINE void __set_DFSR(uint32_t dfsr) { __MCR(15, 0, dfsr, 5, 0, 0); } FORCEDINLINE __STATIC_INLINE uint32_t __get_IFSR(void) { return __MRC(15, 0, 5, 0, 1); } FORCEDINLINE __STATIC_INLINE void __set_IFSR(uint32_t ifsr) { __MCR(15, 0, ifsr, 5, 0, 1); } FORCEDINLINE __STATIC_INLINE uint32_t __get_DFAR(void) { return __MRC(15, 0, 6, 0, 0); } FORCEDINLINE __STATIC_INLINE void __set_DFAR(uint32_t dfar) { __MCR(15, 0, dfar, 6, 0, 0); } FORCEDINLINE __STATIC_INLINE uint32_t __get_IFAR(void) { return __MRC(15, 0, 6, 0, 2); } FORCEDINLINE __STATIC_INLINE void __set_IFAR(uint32_t ifar) { __MCR(15, 0, ifar, 6, 0, 2); } FORCEDINLINE __STATIC_INLINE uint32_t __get_VBAR(void) { return __MRC(15, 0, 12, 0, 0); } FORCEDINLINE __STATIC_INLINE void __set_VBAR(uint32_t vbar) { __MCR(15, 0, vbar, 12, 0, 0); } FORCEDINLINE __STATIC_INLINE uint32_t __get_ISR(void) { return __MRC(15, 0, 12, 1, 0); } FORCEDINLINE __STATIC_INLINE void __set_ISR(uint32_t isr) { __MCR(15, 0, isr, 12, 1, 0); } FORCEDINLINE __STATIC_INLINE uint32_t __get_CONTEXTIDR(void) { return __MRC(15, 0, 13, 0, 1); } FORCEDINLINE __STATIC_INLINE void __set_CONTEXTIDR(uint32_t contextidr) { __MCR(15, 0, contextidr, 13, 0, 1); } FORCEDINLINE __STATIC_INLINE uint32_t __get_CBAR(void) { return __MRC(15, 4, 15, 0, 0); } /******************************************************************************* * GIC Functions ******************************************************************************/ 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; /* For simplicity, we only use group0 of GIC */ FORCEDINLINE __STATIC_INLINE void GIC_Init(void) { uint32_t i; uint32_t irqRegs; GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL); /* GIC Base address */ irqRegs = (gic->D_TYPER & 0x1FUL) + 1; /* maximum number of interrupts */ /* On POR, all SPI is in group 0, level-sensitive and using 1-N model */ /* Disable all PPI, SGI and SPI */ for (i = 0; i < irqRegs; i++) gic->D_ICENABLER[i] = 0xFFFFFFFFUL; /* Make all interrupts have higher priority */ gic->C_PMR = (0xFFUL << (8 - __GIC_PRIO_BITS)) & 0xFFUL; /* No subpriority, all priority level allows preemption */ gic->C_BPR = 7 - __GIC_PRIO_BITS; /* Enable group0 distribution */ gic->D_CTLR = 1UL; /* Enable group0 signaling */ gic->C_CTLR = 1UL; } FORCEDINLINE __STATIC_INLINE void GIC_EnableIRQ(IRQn_Type IRQn) { GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL); gic->D_ISENABLER[((uint32_t)(int32_t)IRQn) >> 5] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); } FORCEDINLINE __STATIC_INLINE void GIC_DisableIRQ(IRQn_Type IRQn) { GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL); gic->D_ICENABLER[((uint32_t)(int32_t)IRQn) >> 5] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); } /* Return IRQ number (and CPU source in SGI case) */ FORCEDINLINE __STATIC_INLINE uint32_t GIC_AcknowledgeIRQ(void) { GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL); return gic->C_IAR & 0x1FFFUL; } /* value should be got from GIC_AcknowledgeIRQ() */ FORCEDINLINE __STATIC_INLINE void GIC_DeactivateIRQ(uint32_t value) { GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL); gic->C_EOIR = value; } FORCEDINLINE __STATIC_INLINE uint32_t GIC_GetRunningPriority(void) { GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL); return gic->C_RPR & 0xFFUL; } FORCEDINLINE __STATIC_INLINE void GIC_SetPriorityGrouping(uint32_t PriorityGroup) { GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL); gic->C_BPR = PriorityGroup & 0x7UL; } FORCEDINLINE __STATIC_INLINE uint32_t GIC_GetPriorityGrouping(void) { GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL); return gic->C_BPR & 0x7UL; } FORCEDINLINE __STATIC_INLINE void GIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL); gic->D_IPRIORITYR[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8UL - __GIC_PRIO_BITS)) & (uint32_t)0xFFUL); } FORCEDINLINE __STATIC_INLINE uint32_t GIC_GetPriority(IRQn_Type IRQn) { GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL); return(((uint32_t)gic->D_IPRIORITYR[((uint32_t)(int32_t)IRQn)] >> (8UL - __GIC_PRIO_BITS))); } #endif /* __CORE_CA7_H */
core_ca7.h文件中比较重点的函数如下列表所示:
函数名 | 函数功能 |
GIC_Init
|
GIC控制器初始化 |
GIC_EnableIRQ
|
使能指定的IRQ外设中断 |
GIC_DisableIRQ
|
禁止指定的IRQ外设中断 |
GIC_AcknowledgeIRQ
|
返回IRQ外设中断号 |
GIC_DeactivateIRQ
|
使指定的IRQ外设中断失效 |
GIC_GetRunningPriority
|
获取当前运行的中断优先级 |
GIC_SetPriorityGrouping
|
设置抢占优先级位数 |
GIC_GetPriorityGrouping
|
获取抢占优先级位数 |
GIC_SetPriority
|
设置指定IRQ外设中断优先级 |
GIC_GetPriority
|
获取指定IRQ外设中断优先级 |
core_ca7.h文件的内容比较复杂,想要了解GIC驱动的实现,可以仔细研究其源码。
接下来,需要修改汇编源文件start.S,添加中断向量表,并实现相应的中断服务处理函数,start.S的内容如下所示:
.global _start /** * _start函数,程序先在这开始执行,用来设置C语言运行环境 */ _start: /* 创建中断向量表 */ ldr pc, =Reset_Handler /* 复位中断 */ ldr pc, =Undefined_Handler /* 未定义指令中断 */ ldr pc, =SVC_Handler /* 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 /* 关闭全局IRQ中断 */ /* 关闭I/D Cache以及MMU */ mrc p15, 0, r0, c1, c0, 0 /* 读取CP15协处理器的c1寄存器值到R0中 */ bic r0, r0, #(1 << 12) /* 清除c1的I位,关闭I Cache */ bic r0, r0, #(1 << 2) /* 清除c1的C位,关闭D Cache */ bic r0, r0, #(1 << 1) /* 清除c1的A位,关闭对齐检查 */ bic r0, r0, #(1 << 11) /* 清除c1的Z位,关闭分支预测 */ bic r0, r0, #(1 << 0) /* 清除c1的M位,关闭MMU */ mcr p15, 0, r0, c1, c0, 0 /* 将R0的值写入到CP15协处理器的c1寄存器中 */ #if 0 /* 汇编版本设置中断向量表偏移 */ ldr r0, =0x87800000 dsb isb mcr p15, 0, r0, c12, c0, 0 /* 将R0的值写入到CP15协处理器的c12寄存器中 */ dsb isb #endif /* SoC进入IRQ运行模式 */ mrs r0, cpsr /* 将cpsr寄存器的值读取到R0中 */ bic r0, r0, #0x1f /* 将cpsr寄存器的M[4:0]清0 */ orr r0, r0, #0x12 /* SoC进入到IRQ运行模式 */ msr cpsr, r0 /* 将R0的值回写到cpsr寄存器 */ ldr sp, =0x80600000 /* 设置IRQ模式下栈指针0x80600000,大小为2MB */ /* SoC进入System运行模式 */ mrs r0, cpsr /* 将cpsr寄存器的值读取到R0中 */ bic r0, r0, #0x1f /* 将cpsr寄存器的M[4:0]清0 */ orr r0, r0, #0x1f /* SoC进入到SYS运行模式 */ msr cpsr, r0 /* 将R0的值回写到cpsr寄存器 */ ldr sp, =0x80400000 /* 设置IRQ模式下栈指针0x80400000,大小为2MB */ /* SoC进入Supervisor运行模式 */ mrs r0, cpsr /* 将cpsr寄存器的值读取到R0中 */ bic r0, r0, #0x1f /* 将cpsr寄存器的M[4:0]清0 */ orr r0, r0, #0x13 /* SoC进入到SVC运行模式 */ msr cpsr, r0 /* 将R0的值回写到cpsr寄存器 */ ldr sp, =0x80200000 /* 设置IRQ模式下栈指针0x80200000,大小为2MB */ cpsie i /* 打开全局IRQ中断 */ #if 0 /* 使能IRQ中断 */ mrs r0, cpsr /* 将cpsr寄存器的值读取到R0中 */ bic r0, r0, #0x80 /* 将R0寄存器中的bit7清0,允许IRQ中断 */ msr cpsr, r0 /* 将R0的值回写到cpsr寄存器中 */ #endif b main /* 跳转到main函数运行 */ /* 未定义指令中断函数 */ Undefined_Handler: ldr r0, =Undefined_Handler bx r0 /* Supervisor中断服务函数 */ SVC_Handler: ldr r0, =SVC_Handler bx r0 /* 预取终止中断服务函数 */ PrefAbort_Handler: ldr r0, =PrefAbort_Handler bx r0 /* 数据终止中断服务函数 */ DataAbort_Handler: ldr r0, =DataAbort_Handler bx r0 /* 未使用的中断服务函数 */ NotUsed_Handler: ldr r0, =NotUsed_Handler bx r0 /* IRQ中断服务函数 */ IRQ_Handler: /* 中断发生时保护现场 */ push {lr} /* 保存lr寄存器(中断发生时保存PC的值) */ push {r0-r3, r12} /* 保存r0-r3,r12寄存器中的值 */ mrs r0, spsr /* 读取spsr寄存器的值到R0寄存器中 */ push {r0} /* 保存spsr寄存器 */ mrc p15, 4, r1, c15, c0, 0 /* 读取CP15协处理器的c15寄存器值(保存GIC基地址) */ /* r1=r1+0x2000,得到GIC的CPU Interface端基地址 */ add r1, r1, #0x2000 /** * CPU Interface端基地址加上0x0C得到 * GICC_IAR寄存器地址,该寄存器保存着当前 * 发生IRQ中断的中断号,需要根据这个中断号 * 调用相应的中断服务函数。 */ ldr r0, [r1, #0x0C] /* 将GICC_IAR寄存器中的值保存到R0 */ push {r0, r1} /* 保存r0(发生IRQ的中断号),r1(GIC的CPU Interface基地址) */ cps #0x13 /* SoC进入到SVC模式,允许其它中断再次进入 */ push {lr} /* 保存SVC模式下的lr寄存器 */ ldr r2, =system_irqhandler /* 将C中断服务处理函数地址加载到R2寄存器 */ blx r2 /* 调用C中断服务处理函数(参数为r0) */ pop {lr} /* 执行完C中断服务处理函数,lr出栈 */ cps #0x12 /* SoC进入到IRQ模式 */ pop {r0, r1} str r0, [r1, #0x10] /* 中断执行完成,写GICC_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
在start.S文件中,重点关注复位中断函数Reset_Handler和IRQ中断服务函数IRQ_Handler,I.MX6UL嵌入式SoC上电复位后进入到复位中断服务函数Reset_Handler处执行,该函数用来完成一些初始化工作,搭建好C运行环境,最后跳转到C版本的main函数执行,Reset_Handler进来后,先使用cpsid i指令将全局IRQ中断关闭,然后通过配置CP15协处理器的相关寄存器,将处理器的I/D Cache、MMU等功能关闭,接下来,初始化不同运行模式下的SP指针,不同运行模式下的栈大小都是2MB,最后,使用cpsie i指令将全局IRQ中断使能,并跳转到C版本的main函数处执行。
接下来,就是IRQ中断服务函数IRQ_Handler,当I.MX6UL芯片的外设产生中断后,最终处理器会进入IRQ模式,并执行IRQ_Handler中断服务函数,该函数主要的工作就是进行中断现场保护,然后去判断当前发生的外设中断(通过中断ID),并针对不同的外设中断做出相应的处理,中断处理完成后,需要恢复中断现场,返回到断点处继续执行原来的程序,GICC_IAR寄存器保存了发生IRQ中断的中断号,读取出来后,将中断号保存到r0寄存器,然后带参调用C函数system_irqhandler,参数为r0的值,也就是中断号,system_irqhandler函数就是通过得到的中断号,进行相应的外设中断处理的,中断处理完成后,需要向GICC_EOIR寄存器写入处理完成的中断号,以表示外设中断已经处理完成。
中断处理完成后,需要将中断现场恢复,返回到断点处继续执行。
接下来,需要实现通用中断的驱动函数,进入到bsp目录下,新创建int目录:
$ cd bsp/bsp $ mkdir int $ cd int $ touch bsp_int.h $ touch bsp_int.c
bsp_int.h文件的内容如下所示:
#ifndef __BSP_INT_H #define __BSP_INT_H #include "imx6ul.h" /* 中断处理函数形式 */ typedef void (*system_irq_handler_t)(unsigned int giccIar, void *param); /* 中断处理函数结构体 */ struct _sys_irq_handle { system_irq_handler_t irqHandler; /* 中断处理函数指针 */ void *userParam; /* 中断处理函数参数 */ }; typedef struct _sys_irq_handle sys_irq_handle_t; /* 相关函数声明 */ void system_irqhandler(unsigned int giccIar); void default_irqhandler(unsigned int giccIar, void *userParam); void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam); void system_irqtable_init(void); void interrupt_init(void); #endif
sys_irq_handle_t定义了中断处理函数结构体,system_irq_handler_t是定义的函数指针,为外设中断的具体处理,函数有两个,第一参数为中断号,第二个为用户参数,该函数的具体实现过程需要用户自己具体实现。
bsp_int.c则是通用中断的驱动函数的实现,文件的内容如下所示:
#include "bsp_int.h" /* 中断嵌套计数器 */ static unsigned int irqNesting; /* 中断处理函数表 */ static sys_irq_handle_t irqTable[NUMBER_OF_INT_VECTORS]; /** * default_irqhandler() - 默认的中断服务处理函数 * * @giccIar: 中断号 * @userParam: 中断服务处理函数参数 * * @return: 无 */ void default_irqhandler(unsigned int giccIar, void *userParam) { while (1) { } } /** * system_irqhandler() - 中断服务处理函数 * 汇编中的IRQ中断服务函数将调用该函数,该 * 函数通过在中断服务列表中查找指定中断号所 * 对应的中断处理函数并执行 * * @giccIar: 中断号 * * @return: 无 */ void system_irqhandler(unsigned int giccIar) { unsigned int intNum = giccIar & 0x3FFUL; /* 获取中断号 */ /* 判断中断号是否符合要求 */ if (intNum >= NUMBER_OF_INT_VECTORS) return; irqNesting++; /* 中断嵌套计数器加1 */ /* 根据传递的中断号,在中断列表中调用对应的外设中断处理函数 */ irqTable[intNum].irqHandler(intNum, irqTable[intNum].userParam); irqNesting--; /* 中断嵌套计数器减1 */ } /** * system_register_irqhandler() - 注册中断服务函数 * * @irq: 要注册的中断号 * @handler: 要注册的中断处理函数 * @userParam: 中断处理函数参数 * * @return: 无 */ void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam) { irqTable[irq].irqHandler = handler; irqTable[irq].userParam = userParam; } /** * system_irqtable_init() - 初始化中断服务函数列表 * * @param: 无 * @return: 无 */ void system_irqtable_init(void) { unsigned int i; irqNesting = 0; /* 将所有的中断处理服务函数设置为默认值 */ for (i = 0; i < NUMBER_OF_INT_VECTORS; i++) { system_register_irqhandler((IRQn_Type)i, default_irqhandler, NULL); } } /** * interrupt_init() - 中断初始化函数 * * @param: 无 * @return: 无 */ void interrupt_init(void) { GIC_Init(); /* 初始化GIC中断控制器 */ system_irqtable_init(); /* 初始化中断表 */ __set_VBAR((unsigned int)0x87800000); /* 设置中断向量表偏移地址 */ }
$ cd bsp/gpio
$ cd gpio
$ vim bsp_gpio.h
$ vim bsp_gpio.c
bsp_gpio.h文件的内容如下所示:
#ifndef __BSP_GPIO_H #define __BSP_GPIO_H #include "imx6ul.h" /* GPIO输入方向定义 */ typedef enum _gpio_pin_direction { kGPIO_DigitalInput = 0U, /* 表示GPIO方向输入 */ kGPIO_DigitalOutput = 1U, /* 表示GPIO方向输出 */ } 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的方向 */ unsigned char value; /* GPIO输出时默认引脚电平值 */ gpio_interrupt_mode_t interruptmode; /* 中断触发方式 */ } gpio_pin_config_t; /* GPIO操作函数相关声名 */ void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config); int gpio_pin_read(GPIO_Type *base, int pin); void gpio_pin_write(GPIO_Type *base, int pin, int value); /* GPIO中断相关操作函数声明 */ void gpio_interrupt_config(GPIO_Type *base, int pin, gpio_interrupt_mode_t interruptmode); void gpio_enable_interrupt(GPIO_Type *base, int pin); void gpio_disable_interrupt(GPIO_Type *base, int pin); void gpio_clear_int_flags(GPIO_Type *base, int pin); #endif
gpio_interrupt_mode_t为枚举类型,枚举了GPIO所支持的所有中断触发类型,在GPIO引脚配置结构体gpio_pin_config_t添加interruptmode成员变量,用来表示GPIO中断的触发类型。
bsp_gpio.c为通用GPIO驱动模块的具体实现,该文件的内容如下所示:
#include "bsp_gpio.h" /** * gpio_init() - GPIO初始化函数 * * @base: 要初始化的GPIO组,例如:GPIO1、GPIO2 * @pin: 要初始化的GPIO组的pin编号 * @config: gpio引脚配置结构体 * * @return: 无 */ void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config) { if (config->direction == kGPIO_DigitalInput) /* GPIO方向为输入 */ base->GDIR &= ~(1 << pin); else { base->GDIR |= (1 << pin); gpio_pin_write(base, pin, config->value); } gpio_interrupt_config(base, pin, config->interruptmode); /* GPIO中断配置 */ } /** * gpio_pin_read() - 读取GPIO引脚的电平 * * @base: 要读取的GPIO组,例如:GPIO1、GPIO2 * @pin: 要读取的GPIO组的pin编号 * * @return: 0表示低电平,1表示高电平 */ int gpio_pin_read(GPIO_Type *base, int pin) { return (((base->DR) >> pin) & 0x1); } /** * gpio_pin_write() - 设置GPIO引脚的电平 * * @base: 要设置的GPIO组,例如:GPIO1、GPIO2 * @pin: 要设置的GPIO组的pin编号 * @value: 引脚要设置的电平值:0->低电平,1->高电平 * * @return: 无 */ void gpio_pin_write(GPIO_Type *base, int pin, int value) { if (0 == value) base->DR &= ~(1 << pin); /* 引脚输出低电平 */ else base->DR |= (1 << pin); /* 引脚输出高电平 */ } /** * gpio_interrupt_config() - 设置GPIO中断触发配置 * * @base: 要设置的GPIO组,例如:GPIO1、GPIO2 * @pin: 要设置的GPIO组的pin编号 * @interruptmode: 要设置的GPIO中断触发方式 * * @return: 无 */ void gpio_interrupt_config(GPIO_Type *base, int pin, gpio_interrupt_mode_t interruptmode) { volatile unsigned int *icr; unsigned int icrshift = pin; base->EDGE_SEL &= ~(1 << pin); /* 双边沿触发寄存器清0 */ base->IMR &= ~(1 << pin); /* 禁止GPIO中断 */ if (pin < 16) { icr = &(base->ICR1); /* GPIOx_IO00~GPIOx_IO15 */ } else { icr = &(base->ICR2); /* GPIOx_IO16~GPIOx_IO31 */ icrshift -= 16; } *icr &= ~(3 << (2 * icrshift)); switch (interruptmode) { case (kGPIO_IntLowLevel): *icr |= (0 << (2 * icrshift)); break; case (kGPIO_IntHighLevel): *icr |= (1 << (2 * icrshift)); break; case (kGPIO_IntRisingEdge): *icr |= (2 << (2 * icrshift)); break; case (kGPIO_IntFallingEdge): *icr |= (3 << (2 * icrshift)); break; case (kGPIO_IntRisingOrFallingEdge): base->EDGE_SEL |= (1 << pin); break; default: break; } } /** * gpio_enable_interrupt() - 使能GPIO引脚中断 * * @base: 要设置的GPIO组,例如:GPIO1、GPIO2 * @pin: 要设置的GPIO组的pin编号 * * @return: 无 */ void gpio_enable_interrupt(GPIO_Type *base, int pin) { base->IMR |= (1 << pin); } /** * gpio_disable_interrupt() - 禁止GPIO引脚中断 * * @base: 要设置的GPIO组,例如:GPIO1、GPIO2 * @pin: 要设置的GPIO组的pin编号 * * @return: 无 */ void gpio_disable_interrupt(GPIO_Type *base, int pin) { base->IMR &= ~(1 << pin); } /** * gpio_clear_int_flags() - 清除GPIO中断标志位 * * @base: 要设置的GPIO组,例如:GPIO1、GPIO2 * @pin: 要设置的GPIO组的pin编号 * * @return: 无 */ void gpio_clear_int_flags(GPIO_Type *base, int pin) { base->ISR |= (1 << pin); /* 写1清除中断标志位 */ }
需要添加GPIO外部中断的驱动函数实现,和GPIO中断相关的函数功能如下:
函数 | 实现功能 |
gpio_interrupt_config
|
对GPIO中断进行配置 |
gpio_enable_interrupt
|
GPIO中断使能 |
gpio_disable_interrupt
|
GPIO中断禁止 |
gpio_clear_int_flags
|
清除GPIO中断标志位 |
实现按键驱动模块,按键驱动就是GPIO外部中断的简单实例,使用到的是CSI_DATA02这个引脚,需要将该引脚复用为GPIO功能,复用后,该引脚的标签为GPIO4_IO23,新创建bsp/exit目录,用来保存按键驱动模块的文件:
$ cd bsp $ mkdir exit $ cd exit $ touch bsp_exit.h $ touch bsp_exit.c
bsp_exit.h文件的内容如下所示:
#ifndef __BSP_EXIT_H #define __BSP_EXIT_H #include "imx6ul.h" void gpio_exit_init(void); void gpio4_io23_irqhander(unsigned int giccIar, void *userParam); #endif
该文件主要是按键中断函数的相关声明,bsp_exit.c文件的内容如下:
#include "bsp_exit.h" #include "bsp_gpio.h" #include "bsp_int.h" #include "bsp_delay.h" #include "bsp_led.h" /** * gpio_exit_init() - GPIO外部中断初始化 * * @param: 无 * @return: 无 */ void gpio_exit_init(void) { gpio_pin_config_t key_config; /* 设置CSI_DATA02引脚IO口复用为GPIO4_IO23 */ IOMUXC_SetPinMux(IOMUXC_CSI_DATA02_GPIO4_IO23, 0); /** * 配置GPIO4_IO23引脚电气属性 * bit[16]: 0 关闭HYS * bit[15:14]: 11 pull up 22k * bit[13]: 1 pull * bit[12]: 1 pull/keeper使能 * bit[11]: 0 禁止开路输出 * bit[10:8]: 000 resered * bit[7:6]: 10 速度为100MHz * bit[5:3]: 000 关闭输出 * bit[2:1]: 00 resered * bit[0]: 0 低摆率 */ IOMUXC_SetPinConfig(IOMUXC_CSI_DATA02_GPIO4_IO23, 0xF080); /* 将按键相关的GPIO方向设置为输入,并配置中断 */ key_config.direction = kGPIO_DigitalInput; key_config.interruptmode = kGPIO_IntFallingEdge; key_config.value = 1; gpio_init(GPIO4, 23, &key_config); /* 使能GIC中断控制器,注册GPIO中断服务函数并使能中断 */ GIC_EnableIRQ(GPIO4_Combined_16_31_IRQn); system_register_irqhandler(GPIO4_Combined_16_31_IRQn, (system_irq_handler_t)gpio4_io23_irqhander, NULL); gpio_enable_interrupt(GPIO4, 23); } /** * gpio4_io23_irqhander() - GPIO4_IO23中断服务处理函数 * * @giccIar: 中断号 * @userParam: 用户参数 * * @return: 无 */ void gpio4_io23_irqhander(unsigned int giccIar, void *userParam) { static unsigned char led2_state = OFF; /** * 采用简单延时消抖,但是中断服务处理函数中 * 禁止使用延时函数,因为中断服务处理需要快进 * 快出。 */ delay(10); if (gpio_pin_read(GPIO4, 23) == 0) { led2_state = !led2_state; led_switch(LED2, led2_state); } gpio_clear_int_flags(GPIO4, 23); /* 清除GPIO4_IO23中断标志位 */ }
#include "bsp_clk.h" #include "bsp_delay.h" #include "bsp_gpio.h" #include "bsp_led.h" #include "bsp_key.h" #include "bsp_int.h" #include "bsp_exit.h" /** * main() - 主函数 */ int main(void) { unsigned char led1_state = OFF; interrupt_init(); /* 中断初始化 */ imx6ul_clk_init(); /* 初始化相关时钟 */ system_clk_enable(); /* 系统外设时钟使能 */ led_init(); /* LED灯初始化 */ gpio_exit_init(); /* GPIO外部中断初始化 */ while (1) { led1_state = !led1_state; led_switch(LED1, led1_state); delay(500); } return 0; }
main()函数比较简单,函数调用后,将相关的外设进行初始化后即可。
3、小结
本文主要简单介绍了I.MX6UL嵌入式SoC中的GPIO外部中断功能的使用,通过一个简单的按键驱动实例进行介绍。