赞
踩
在上篇中《HPM6750系列--第九篇 GPIO详解(基本操作)》我们讲解了GPIO的基本操作,本篇继续讲解GPIO的中断处理。
支持的中断类型包括:
将一个引脚设置为中断涉及到以下几个步骤(此处我们以PZ02举例):
- void init_gpio_pins(void)
- {
- uint32_t pad_ctl = IOC_PAD_PAD_CTL_PE_SET(1) | IOC_PAD_PAD_CTL_PS_SET(1);
-
- #ifdef USING_GPIO0_FOR_GPIOZ
- HPM_IOC->PAD[IOC_PAD_PZ02].FUNC_CTL = IOC_PZ02_FUNC_CTL_GPIO_Z_02; //②
- HPM_IOC->PAD[IOC_PAD_PZ02].PAD_CTL = pad_ctl; //③
- /* PZ port IO needs to configure BIOC as well */
- HPM_BIOC->PAD[IOC_PAD_PZ02].FUNC_CTL = IOC_PZ02_FUNC_CTL_SOC_PZ_02; //①
- #endif
- }
PZ02是电池备份域的IO,由HPM_BIOC设置复用功能,如果需要GPIO0来控制的话,需要先映射到HPM_IOC,再由HPM_IOC来复用成GPIO。
①HPM_BIOC将PZ02复用为SOC_PZ_02,也就是由HPM_IOC来决定其复用功能
②HPM_IOC将PZ02复用为GPIO
③HPM_IOC将PZ02设置为内部上拉
经过上面的设置之后,PZ02就由GPIO0来进行后续的方向设置、中断配置
-
- /**
- * @brief Set pin to input mode
- *
- * @param ptr GPIO base address
- * @param port Port index
- * @param pin Pin index
- */
- static inline void gpio_set_pin_input(GPIO_Type *ptr, uint32_t port, uint8_t pin)
- {
- ptr->OE[port].CLEAR = 1 << pin;
- }
-
-
- /**
- * @brief Interrupt trigger type
- */
- typedef enum gpio_interrupt_trigger {
- gpio_interrupt_trigger_level_high = 0,
- gpio_interrupt_trigger_level_low,
- gpio_interrupt_trigger_edge_rising,
- gpio_interrupt_trigger_edge_falling,
- #if defined(GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT) && (GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT == 1)
- gpio_interrupt_trigger_edge_both,
- #endif
- } gpio_interrupt_trigger_t;
-
- void gpio_config_pin_interrupt(GPIO_Type *ptr, uint32_t gpio_index, uint8_t pin_index, gpio_interrupt_trigger_t trigger)
- {
- switch (trigger) {
- case gpio_interrupt_trigger_level_high:
- case gpio_interrupt_trigger_level_low:
- ptr->TP[gpio_index].CLEAR = 1 << pin_index;
- if (trigger == gpio_interrupt_trigger_level_high) {
- ptr->PL[gpio_index].CLEAR = 1 << pin_index;
- } else {
- ptr->PL[gpio_index].SET = 1 << pin_index;
- }
- break;
- case gpio_interrupt_trigger_edge_falling:
- case gpio_interrupt_trigger_edge_rising:
- #if defined(GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT) && (GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT == 1)
- ptr->PD[gpio_index].CLEAR = 1 << pin_index;
- #endif
- ptr->TP[gpio_index].SET = 1 << pin_index;
- if (trigger == gpio_interrupt_trigger_edge_rising) {
- ptr->PL[gpio_index].CLEAR = 1 << pin_index;
- } else {
- ptr->PL[gpio_index].SET = 1 << pin_index;
- }
- break;
- #if defined(GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT) && (GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT == 1)
- case gpio_interrupt_trigger_edge_both:
- ptr->TP[gpio_index].SET = 1 << pin_index;
- ptr->PD[gpio_index].SET = 1 << pin_index;
- break;
- #endif
- default:
- return;
- }
- }
-
- void test_gpio_input_interrupt(void)
- {
- gpio_interrupt_trigger_t trigger;
-
- printf("input interrupt\n");
- #ifdef BOARD_LED_GPIO_CTRL
- printf("user led will be switched on off based on user switch\n");
- gpio_set_pin_output(BOARD_LED_GPIO_CTRL, BOARD_LED_GPIO_INDEX,
- BOARD_LED_GPIO_PIN);
- #endif
- gpio_set_pin_input(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX,
- BOARD_APP_GPIO_PIN); //①
- #if defined(GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT) && (GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT == 1)
- trigger = gpio_interrupt_trigger_edge_both;
- #else
- trigger = gpio_interrupt_trigger_edge_falling; //②
- #endif
- gpio_config_pin_interrupt(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX,
- BOARD_APP_GPIO_PIN, trigger); //②
- gpio_enable_pin_interrupt(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX,
- BOARD_APP_GPIO_PIN); //③
- intc_m_enable_irq_with_priority(BOARD_APP_GPIO_IRQ, 1); //④
- while (1) {
- __asm("wfi");
- }
- }
①HPM_GPIO0设置PZ02为输入引脚。
②HPM_GPIO0设置PZ02边沿中断,下降沿触发
③HPM_GPIO0使能PZ02中断
④设置IRQn_GPIO0_Z中断优先级
用户可以通过 GPIO 控制器的 IE 寄存器打开 GPIO 中断,IE 寄存器内的对应位置 1 就可以使能对应 IO 的中断。
用户可以通过 GPIO 控制器的 TP 寄存器来指定中断的类型,对应位置 1,表示中断由边沿触发,对应位置0,表示中断由电平触发
用户可以通过 GPIO 控制器的 PL 寄存器来指定中断的极性,对应位置 1,表示中断由下降沿或者低电平触发,对应位置 0,表示中断由上升沿或高电平触发。
用户可以通过 GPIO 控制器的 IF 寄存器来查询中断的状态,对应标志位置 1,表示对应 IO 有中断待处理;写1清零。
SDK_DECLARE_EXT_ISR_M宏注册isr_gpio函数为IRQn_GPIO0_Z的中断函数。
-
- /**
- * @brief Clear specific pin interrupt flag
- *
- * @param ptr GPIO base address
- * @param port Port index
- * @param pin Pin index
- */
- static inline void gpio_clear_pin_interrupt_flag(GPIO_Type *ptr, uint32_t port, uint8_t pin)
- {
- ptr->IF[port].VALUE = 1 << pin;
- }
-
- /**
- * @brief Check specific pin interrupt status
- *
- * @param ptr GPIO base address
- * @param port Port index
- * @param pin Pin index
- *
- * @return true if interrupt flag is set
- */
- static inline bool gpio_check_pin_interrupt_flag(GPIO_Type *ptr, uint32_t port, uint8_t pin)
- {
- return ptr->IF[port].VALUE & (1 << pin);
- }
-
- void isr_gpio(void)
- {
- gpio_clear_pin_interrupt_flag(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX,
- BOARD_APP_GPIO_PIN);
- #ifdef BOARD_LED_GPIO_CTRL
- gpio_toggle_pin(BOARD_LED_GPIO_CTRL, BOARD_LED_GPIO_INDEX,
- BOARD_LED_GPIO_PIN);
- printf("toggle led pin output\n");
- #else
- #if defined(GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT) && (GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT == 1)
- if (gpio_read_pin(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX, BOARD_APP_GPIO_PIN) == false) {
- printf("user key pressed\n");
- } else {
- printf("user key released\n");
- }
- #else
- printf("user key pressed\n");
- #endif
- #endif
-
- }
- SDK_DECLARE_EXT_ISR_M(BOARD_APP_GPIO_IRQ, isr_gpio)
在isr_gpio中断函数中清除HPM_GPIO0.PZ.02中断标志位。正确的做法我们应该检查HPM_GPIO0.PZ.02的值,然后再清除(因为我们可以注册PZ上多个IO引脚的中断)。
- if (gpio_check_pin_interrupt_flag(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX,
- BOARD_APP_GPIO_PIN)) {
- gpio_clear_pin_interrupt_flag(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX,
- BOARD_APP_GPIO_PIN);
- }
SDK_DECLARE_EXT_ISR_M宏用于注册中断函数,这个后面我们讲解中断系统时再讲。
- #define SDK_DECLARE_EXT_ISR_M(irq_num,isr) void isr(void) __attribute__((section(".isr_vector")));HPM_EXTERN_C void ISR_NAME_M(irq_num)(void) __attribute__((section(".isr_vector")));void ISR_NAME_M(irq_num)(void) { SAVE_CALLER_CONTEXT(); ENTER_NESTED_IRQ_HANDLING_M(); __asm volatile("la t1, %0\n\t" : : "i" (isr) : ); __asm volatile("jalr t1\n"); COMPLETE_IRQ_HANDLING_M(irq_num); EXIT_NESTED_IRQ_HANDLING_M(); RESTORE_CALLER_CONTEXT(); __asm volatile("fence io, io"); __asm volatile("mret\n");}
- 扩展到:
-
- void isr_gpio(void) __attribute__((section(".isr_vector"))); void default_isr_9(void) __attribute__((section(".isr_vector")));void default_isr_9(void) { { __asm volatile("addi sp, sp, %0" : : "i"(-(4 * (16 + 4))) :); __asm volatile("\n c.swsp ra, 0*4(sp) \n c.swsp t0, 1*4(sp) \n c.swsp t1, 2*4(sp) \n c.swsp t2, 3*4(sp) \n c.swsp s0, 4*4(sp) \n c.swsp s1, 5*4(sp) \n c.swsp a0, 6*4(sp) \n c.swsp a1, 7*4(sp) \n c.swsp a2, 8*4(sp) \n c.swsp a3, 9*4(sp) \n c.swsp a4, 10*4(sp) \n c.swsp a5, 11*4(sp) \n c.swsp a6, 12*4(sp) \n c.swsp a7, 13*4(sp) \n c.swsp s2, 14*4(sp) \n c.swsp s3, 15*4(sp) \n c.swsp t3, 16*4(sp) \n c.swsp t4, 17*4(sp) \n c.swsp t5, 18*4(sp) \n c.swsp t6, 19*4(sp)"); ; }; { __asm volatile("\n csrr s2, mepc \n csrr s3, mstatus \n"); ; ; __asm volatile("csrsi mstatus, 8"); }; __asm volatile("la t1, %0\n\t" : : "i" (isr_gpio) : ); __asm volatile("jalr t1\n"); { __asm volatile("csrci mstatus, 8"); __asm volatile("lui a4, 0xe4200"); __asm volatile("li a3, %0" : : "i" (9) :); __asm volatile("sw a3, 4(a4)"); }; { __asm volatile("\n csrw mstatus, s3 \n csrw mepc, s2 \n"); ; ; }; { __asm volatile("\n c.lwsp ra, 0*4(sp) \n c.lwsp t0, 1*4(sp) \n c.lwsp t1, 2*4(sp) \n c.lwsp t2, 3*4(sp) \n c.lwsp s0, 4*4(sp) \n c.lwsp s1, 5*4(sp) \n c.lwsp a0, 6*4(sp) \n c.lwsp a1, 7*4(sp) \n c.lwsp a2, 8*4(sp) \n c.lwsp a3, 9*4(sp) \n c.lwsp a4, 10*4(sp) \n c.lwsp a5, 11*4(sp) \n c.lwsp a6, 12*4(sp) \n c.lwsp a7, 13*4(sp) \n c.lwsp s2, 14*4(sp) \n c.lwsp s3, 15*4(sp) \n c.lwsp t3, 16*4(sp) \n c.lwsp t4, 17*4(sp) \n c.lwsp t5, 18*4(sp) \n c.lwsp t6, 19*4(sp) \n"); ; __asm volatile("addi sp, sp, %0" : : "i"((4 * (16 + 4))) :);}; __asm volatile("fence io, io"); __asm volatile("mret\n");}
首先拷贝gpio工程
- cp -r $HPM_SDK_BASE/samples/drivers/gpio ~/workspace/work/hpm
- cd ~/workspace/work/hpm/gpio
- cp -r ~/workspace/work/hpm/hello_world/.vscode ~/workspace/work/hpm/gpio/
- code .
选择flash_xip编译并进入调试窗口,然后我们按一下开发板上的PBUTN按键,就会触发中断,我们在中断中添加断点。
通过查看IF[GPIOZ]我们发现其值为0x4,也就是bit2为1,说明中断触发了。
然后我们继续执行发现IF[GPIOZ]变成了0x0,因为中断处理函数中我们需要清除中断标志。
另外串口打印中也有中断触发的打印信息。
至此本篇的内容就结束了,主要讲解了IO映射、IO中断的类型、中断注册以及中断使能。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。