1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
一、STM32的中断系统
代码从0x00000000运行。32设置连接首地址0x8000000的地方存放了中断向量表,因此要从0x8000000的地方开始运行。需要告诉soc内核,要设置中断向量偏移。设置SCB的VTOC寄存器为新的中断向量表起始地址,内部指向各种中断的函数名。 但是Cotex-A的中断向量表只有几个,我们主要使用的是IRQ。 二、6ULL中断系统 1.GIC控制器 GIC提供了开关中断,设置中断优先级等等。6ULL有1020个中断号,用于向CPU interface发送信号,从而通知IRQ中断。其中32-1019号ID用于SPI(共享外设中断)。其他暂时不管它。 GIC由Distributor与CPU interface构成。Distributor将中断事件发送给CPU interface。 Distributor 其中Distributor所做的事情是:SPI中断使能,设置优先级,设置到中断目标处理器列表中,设置外部中断的触发模式,设置中断属于组0还是组1。 CPU interface 作为内核与Distributor的桥梁,主要工作是:向CPU发送请求信号,应答,状态等,设置优先级掩码(从而设置哪些中断无需上报给CPU),定义抢占策略:当多个中断来时进行分配给CPU。 2.CP15协处理器 协处理器中有16个32bit寄存器。对于指令填写依照下图。 格式: MRC{cond} sp15 其中 RT 作为数据的中间人。 r0寄存器 我们要关闭ID Cache和MMU,找到手册中的SCTLR寄存,bit0关闭MMU,bit1控制对齐,控制为打开关闭MMU,bit2控制 D Cache的打开和关闭,bit11用于控制分支预测,bit12用于控制 I Cache。 c12寄存器 用于中断向量偏移设置,将新的向量表首地址写入到CP15协处理器中的VBAR。 c15寄存器 我们需要读取CBAR寄存器,保存了GIC控制器的寄存器首地址。GIC寄存器组偏移0x1000-0x1fff为GIC的Distributor。0x2000-0x3fff为CPU Interface。意味我们可以访问GIC控制器了。 MRC sp15 0 r0 c12 c0 0 MCR sp15 0 r0 c12 c0 0 1 2 三、编写外部中断 1.复位中断函数 向量表中有8个中断,需要用户自己定义。 修改汇编程序,添加中断向量表。编写复位中断函数与IRQ中断服务函数。 1.关闭L.D Cache 和MMU。 2.设置sp指针,使用外部中断必须设置IRQ模式。 3.清除bss段 4.调到C语言中 5.中断处理结束以后,必须要向GICC_EOIR寄存器写入中断号,表示完成 start.S .global _start _start: ldr pc ,=Reset_Handler ldr pc ,=Undefined_Handler ldr pc ,=SVC_Handler ldr pc ,=PrefAbort_Handler ldr pc ,=DataAbort_Handler ldr pc ,=NotUsed_Handler ldr pc ,=IRQ_Handler ldr pc ,=FIQ_Handler Reset_Handler: cpsid i /*close up IRQ */ /*关闭ID Cache 修改SCTLR寄存器,才用读改写的方式 */ mrc p15,0,r0,c1,c0,0 /*读取SCTLR寄存器的数据到r0中*/ bic r0 ,r0 , #(1<<12) /*关闭I Cache*/ bic r0 ,r0 , #(1<<11) /*关闭分支预测*/ bic r0 ,r0 , #(1<<2) /*关闭D Cache*/ bic r0 ,r0 , #(1<<1) /*关闭对齐*/ bic r0 ,r0 , #(1<<0) /*关闭MMU*/ mcr p15,0,r0,c1,c0,0 /*写到SCTLR寄存器中*/ .global _bss_start _bss_start: .word __bss_start .global _bss_end _bss_end: .word __bss_end /*设置中断向量偏移*/ /* ldr r0 ,=0x87800000 d*** i*** mcr p15 ,0 ,r0 ,c12 ,c0 ,0 d*** i*** */ /*清除BSS段*/ ldr r0, _bss_start ldr r1, _bss_end mov r2, #0 bss_loop: stmia r0!, {r2} cmp r0, r1 /* 比较R0和R1里面的值 */ ble bss_loop /*如果r0地址小于等于r1,继续清除bss段*/ /*进入SYS*/ mrs r0 , cpsr bic r0 ,r0 ,#0x1f orr r0 ,r0 ,#0x1f msr cpsr ,r0 ldr sp , =0x80600000 /*4M大小的地方*/ /*进入IRQ*/ mrs r0 , cpsr bic r0 ,r0 ,#0x1f orr r0 ,r0 ,#0x12 msr cpsr ,r0 ldr sp , =0x80400000 /*4M大小的地方*/ /*设置处理器进入SVC模式下的sp*/ mrs r0, cpsr /* 读取cpsr到r0*/ bic r0, r0, #0x1f /* 清除cpsr的bit4-0*/ orr r0, r0, #0x13 /* 使用SVC模式*/ msr cpsr, r0 /* 将r0写入到cpsr*/ /* 设置SP指针 */ ldr sp, =0x80200000 cpsie i /*open IRQ */ b main /* 跳转到C语言main函数*/ Undefined_Handler: ldr r0 ,=Undefined_Handler bx r0 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_Handler: FIQ_Handler: ldr r0 ,=FIQ_Handler bx r0 2.IRQ中断函数 1.首先lr,r0-r3 r12入栈。保存spsr的值。 2.将CPU Interface的GICC_IAR寄存器的中断号ID读到r0中。 3.入栈r0,r1,进入SVC模式,从而运行C语言系统中断函数。 4.出栈SVC , 进入IRQ模式,将ID好写入到EOIR位。 5.出栈r0 --ID, 恢复spsr寄存器,再回到中断之前的SYS模式。 IRQ_Handler: IRQ_Handler: 读取spsr寄存器*/ push {r0} /*保存spsr寄存器*/ mrc p15 ,4 ,r1 ,c15 ,c0 ,0/*从CP15的C0寄存器内的值读到寄存器中*/ add r1 , r1 ,#0x2000 /*r1保存了GIC控制器CPU接口的基地址0x2000*/ ldr r0 ,[r1 , #0xC] /*偏移0x0C为GICC_IAR寄存器保存到r0寄存器 ,可以从GICC_IAR的bit9-0读取中断ID,读取中断ID的目的就是为了得到对应的中断处理函数*/ push {r0 ,r1} /*保存r0 ,r1*/ cps #0x13 /*进入SVC模式,允许其他中断再次进入*/ push {lr} /*保存SVC的lr寄存器*/ ldr r2 , =system_irqhandler/*加载C语言中断处理函数到r2寄存器内 ,压栈的r0会成为它的参数*/ blx r2 /*运行C语言中断处理函数,带有一个参数,保存到*/ pop {lr} /*执行完中断后,lr出栈*/ cps #0x12 /*进入IRQ模式*/ pop {r0, r1} str r0 , [r1 , #0x10]/*中断执行完,r0的中断ID写到EOIR*/ pop {r0} msr spsr_cxsf , r0 /*recover spsr*/ pop {r0-r3 ,r12} pop {lr} subs pc , lr ,#4 /*将两lr-4能给pc*/ 四、编写中断函数 1.初始化GIC ,由于GIC控制器提供了中断的各种开关,优先级之类的。 2.初始化中断向量表 bsp_int.c #include "stdio.h" #include "bsp_int.h" static uint8_t irqNesting; //中断嵌套值 /*中断表定义*/ static sys_irq_handler_t irqTable[NUMBER_OF_INT_VECTORS]; /*中断向量表初始化*/ void System_IrqTable_Init(void){ uint8_t i = 0 ; irqNesting = 0; for(i = 0 ; i < NUMBER_OF_INT_VECTORS ; i++){ System_Register_IrqHandler((IRQn_Type)i, handler, NULL); } } void System_Register_IrqHandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam) { irqTable[irq].irqHandler = handler; irqTable[irq].userParam = userParam; } /*中断初始化函数*/ void int_init(void) { GIC_Init(); Sytem_IrqTable_Init(); /*中断向量偏移设置*/ __set_VBAR(0x87800000); } /*汇编IRQ模式进入的函数 *将传入的参数是是r0寄存,就是获取GICC_IAR寄存器的中断ID号 */ void system_irqhandler(unsigned int gicciar){ uint32_t intNum = gicciar; if(gicciar >= 160)return ; irqNesting++; irqTable[intNum].irqHandler(intNum , irqTable[intNum].userParam); } void Default_IrqHandler(uint8_t gicciar ,void *param){ while(1){} } bsp_int.h #ifndef __BSP_INT_H__ #define __BSP_INT_H__ #include "imx6ull.h" /*定义中断函数*/ typedef void (*system_irq_handler_t)(unsigned int gicciar , void *param); /*中断函数结构体*/ typedef struct sys_irq_handler{ system_irq_handler_t irqHandler; void *userParam; }sys_irq_handler_t; void int_init(void); void system_irqhandler(unsigned int gicciar); #endif 五、GPIO按键中断 1.GPIO中断设置 1.设置GPIO5_IO01为低电平触发为高电平触发 2.设置使能GPIO中断掩码寄存器,GPIO_IMR寄存器,用于使能中断。 3.GPIO_ICR中断设置寄存器,可以设置不同的触发信号。 3.设置EDGE位被设置以后,ICR位就没有用了。置位可以任意电平信号进行触发。 3.使能ISR中断状态寄存器,处理完以后,需要清除中断标志位。清除ISR寄存器的位。写1清0。 bsp_gpio.c #include "bsp_gpio.h" void gpio_init(GPIO_Type *base , int pin , gpio_pin_cfg_t *config) { if(config->Direction == KGPIO_DigitalInput){ base->GDIR &= ~(1< else{ base->GDIR |= (1< Gpio_IntConfig(base ,pin ,config->interruptMode); } /*Clear Interrupt FLag bit*/ void Gpio_ClearInterFlags(GPIO_Type *base , unsigned int pin){ base->ISR |= (1< /*GPIO INterrupt Iiit*/ 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 &= ~(1< if(pin<<16){ icr = &(base->ICR1); } else{ icr = &(base->ICR2); icrShift -= 16; } switch(pin_int_mode) { case KGPIO_IntLowLevel: *icr &= ~(3 << (2*icrShift)); break; case KGPIO_IntHighLevel: *icr &= ~(3 << (2*icrShift)); *icr |= (1 << (2*icrShift)); break; case KGPIO_IntRisingEdge: *icr &= ~(3 << (2*icrShift)); *icr |= (2 << (2*icrShift)); break; case KGPIO_IntFallingEdge: *icr &= ~(3 << (2*icrShift)); *icr |= (3 << (2*icrShift)); break; case KGPIO_IntRisingOrFallingEdge: base->EDGE_SEL |= (1 << pin); break; default: break; } } bsp_gpio.h #ifndef __BSP_GPIO_H__ #define __BSP_GPIO_H__ #include "imx6ull.h" /*Interrupt edge type*/ 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 struct*/ typedef enum Gpio_Pin{ KGPIO_DigitalInput = 0U, KGPIO_DigitalOutput = 1U, }gpio_pin_dir_t; typedef struct Gpio_Pin_Config{ gpio_pin_dir_t Direction; uint8_t OutputLogic; gpio_interrupt_mode_t interruptMode; }gpio_pin_cfg_t; int gpio_pinread(GPIO_Type *base , int pin); void gpio_pinwrite(GPIO_Type *base , int pin , int value); void gpio_init(GPIO_Type *base , int pin , gpio_pin_cfg_t *config); void Gpio_EnableInt(GPIO_Type *base , unsigned int pin); void Gpio_DisableInt(GPIO_Type *base , unsigned int pin); void Gpio_ClearInterFlags(GPIO_Type *base , unsigned int pin); void Gpio_IntConfig(GPIO_Type *base ,unsigned int pin ,gpio_interrupt_mode_t ping_int_mode); #endif 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 六、外部中断设置 1.初始化gpio 2.使能GIC控制器 3.注册中断函数 4.使能中断 GIC配置 1.使能相应的中断IO对应的ID位位106 GPIO5_Combined_0_15_IRQn = 106, 1 2.设置中断优先级 3.注册GPIO的中断处理函数 4.GPIO_IMR寄存器进行使能 #include "bsp_exti.h" #include "bsp_gpio.h" #include "bsp_int.h" #include "bsp_delay.h" #include "bsp_beep.h" #include "bsp_led.h" void exti_init(void) { gpio_pin_cfg_t key_config; IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01 , 0); IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01 , 0xf080); /* GPIO初始化 */ key_config.Direction = KGPIO_DigitalInput; key_config.interruptMode = KGPIO_IntHighLevel; gpio_init(GPIO5 , 1 , &key_config); GIC_EnableIRQ(GPIO5_Combined_0_15_IRQn); system_register_irqhandler(GPIO5_Combined_0_15_IRQn , (system_irq_handler_t)GPIO5_IO01_irqhandler , NULL); Gpio_EnableInt(GPIO5 , 1); //IMR } /*Interrupt handler*/ void GPIO5_IO01_irqhandler(unsigned int gicciar,void *param){ delay(10); if(gpio_pinread(GPIO5 , 1) == 0){ led_switch(LED_RED , LED_ON); beep_switch(BEEP_ON); delay(1000); led_switch(LED_RED , LED_OFF); beep_switch(BEEP_OFF); delay(1000); } /*clear flags*/ Gpio_ClearInterFlags(GPIO5 , 1); } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1804 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1629 浏览 1 评论
1097 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
736 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1686 浏览 2 评论
1944浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
747浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
580浏览 3评论
602浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
565浏览 3评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-28 09:38 , Processed in 0.756651 second(s), Total 74, Slave 58 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号