当前位置:   article > 正文

HPM6750系列--第九篇 GPIO详解(中断操作)_hpm6750 can中断

hpm6750 can中断

一、目的

        在上篇中《HPM6750系列--第九篇 GPIO详解(基本操作)》我们讲解了GPIO的基本操作,本篇继续讲解GPIO的中断处理。

        

二、介绍

        支持的中断类型包括:

  • 边沿触发(上升沿、下降沿)
  • 电平触发(高电平、低电平)

        将一个引脚设置为中断涉及到以下几个步骤(此处我们以PZ02举例):

        1.设置IOC/PIOC/BIO中对应的引脚复用为gpio功能
  1. void init_gpio_pins(void)
  2. {
  3. uint32_t pad_ctl = IOC_PAD_PAD_CTL_PE_SET(1) | IOC_PAD_PAD_CTL_PS_SET(1);
  4. #ifdef USING_GPIO0_FOR_GPIOZ
  5. HPM_IOC->PAD[IOC_PAD_PZ02].FUNC_CTL = IOC_PZ02_FUNC_CTL_GPIO_Z_02; //②
  6. HPM_IOC->PAD[IOC_PAD_PZ02].PAD_CTL = pad_ctl; //③
  7. /* PZ port IO needs to configure BIOC as well */
  8. HPM_BIOC->PAD[IOC_PAD_PZ02].FUNC_CTL = IOC_PZ02_FUNC_CTL_SOC_PZ_02; //①
  9. #endif
  10. }

        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来进行后续的方向设置、中断配置        

        2.设置引脚为输入

        

  1. /**
  2. * @brief Set pin to input mode
  3. *
  4. * @param ptr GPIO base address
  5. * @param port Port index
  6. * @param pin Pin index
  7. */
  8. static inline void gpio_set_pin_input(GPIO_Type *ptr, uint32_t port, uint8_t pin)
  9. {
  10. ptr->OE[port].CLEAR = 1 << pin;
  11. }
  12. /**
  13. * @brief Interrupt trigger type
  14. */
  15. typedef enum gpio_interrupt_trigger {
  16. gpio_interrupt_trigger_level_high = 0,
  17. gpio_interrupt_trigger_level_low,
  18. gpio_interrupt_trigger_edge_rising,
  19. gpio_interrupt_trigger_edge_falling,
  20. #if defined(GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT) && (GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT == 1)
  21. gpio_interrupt_trigger_edge_both,
  22. #endif
  23. } gpio_interrupt_trigger_t;
  24. void gpio_config_pin_interrupt(GPIO_Type *ptr, uint32_t gpio_index, uint8_t pin_index, gpio_interrupt_trigger_t trigger)
  25. {
  26. switch (trigger) {
  27. case gpio_interrupt_trigger_level_high:
  28. case gpio_interrupt_trigger_level_low:
  29. ptr->TP[gpio_index].CLEAR = 1 << pin_index;
  30. if (trigger == gpio_interrupt_trigger_level_high) {
  31. ptr->PL[gpio_index].CLEAR = 1 << pin_index;
  32. } else {
  33. ptr->PL[gpio_index].SET = 1 << pin_index;
  34. }
  35. break;
  36. case gpio_interrupt_trigger_edge_falling:
  37. case gpio_interrupt_trigger_edge_rising:
  38. #if defined(GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT) && (GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT == 1)
  39. ptr->PD[gpio_index].CLEAR = 1 << pin_index;
  40. #endif
  41. ptr->TP[gpio_index].SET = 1 << pin_index;
  42. if (trigger == gpio_interrupt_trigger_edge_rising) {
  43. ptr->PL[gpio_index].CLEAR = 1 << pin_index;
  44. } else {
  45. ptr->PL[gpio_index].SET = 1 << pin_index;
  46. }
  47. break;
  48. #if defined(GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT) && (GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT == 1)
  49. case gpio_interrupt_trigger_edge_both:
  50. ptr->TP[gpio_index].SET = 1 << pin_index;
  51. ptr->PD[gpio_index].SET = 1 << pin_index;
  52. break;
  53. #endif
  54. default:
  55. return;
  56. }
  57. }
  58. void test_gpio_input_interrupt(void)
  59. {
  60. gpio_interrupt_trigger_t trigger;
  61. printf("input interrupt\n");
  62. #ifdef BOARD_LED_GPIO_CTRL
  63. printf("user led will be switched on off based on user switch\n");
  64. gpio_set_pin_output(BOARD_LED_GPIO_CTRL, BOARD_LED_GPIO_INDEX,
  65. BOARD_LED_GPIO_PIN);
  66. #endif
  67. gpio_set_pin_input(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX,
  68. BOARD_APP_GPIO_PIN); //①
  69. #if defined(GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT) && (GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT == 1)
  70. trigger = gpio_interrupt_trigger_edge_both;
  71. #else
  72. trigger = gpio_interrupt_trigger_edge_falling; //②
  73. #endif
  74. gpio_config_pin_interrupt(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX,
  75. BOARD_APP_GPIO_PIN, trigger); //②
  76. gpio_enable_pin_interrupt(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX,
  77. BOARD_APP_GPIO_PIN); //③
  78. intc_m_enable_irq_with_priority(BOARD_APP_GPIO_IRQ, 1); //④
  79. while (1) {
  80. __asm("wfi");
  81. }
  82. }

         

        ①HPM_GPIO0设置PZ02为输入引脚。 

        ②HPM_GPIO0设置PZ02边沿中断,下降沿触发

        ③HPM_GPIO0使能PZ02中断

        ④设置IRQn_GPIO0_Z中断优先级

        

GPIO中断使能寄存器

        用户可以通过 GPIO 控制器的 IE 寄存器打开 GPIO 中断,IE 寄存器内的对应位置 1 就可以使能对应 IO 的中断。

        

GPIO中断类型寄存器

        用户可以通过 GPIO 控制器的 TP 寄存器来指定中断的类型,对应位置 1,表示中断由边沿触发,对应位置0,表示中断由电平触发

        

GPIO中断极性寄存器

        用户可以通过 GPIO 控制器的 PL 寄存器来指定中断的极性,对应位置 1,表示中断由下降沿或者低电平触发,对应位置 0,表示中断由上升沿或高电平触发。

        

GPIO中断状态寄存器

        用户可以通过 GPIO 控制器的 IF 寄存器来查询中断的状态,对应标志位置 1,表示对应 IO 有中断待处理;写1清零。

        3.注册中断函数

        SDK_DECLARE_EXT_ISR_M宏注册isr_gpio函数为IRQn_GPIO0_Z的中断函数。 

​​​​​​​       

  1. /**
  2. * @brief Clear specific pin interrupt flag
  3. *
  4. * @param ptr GPIO base address
  5. * @param port Port index
  6. * @param pin Pin index
  7. */
  8. static inline void gpio_clear_pin_interrupt_flag(GPIO_Type *ptr, uint32_t port, uint8_t pin)
  9. {
  10. ptr->IF[port].VALUE = 1 << pin;
  11. }
  12. /**
  13. * @brief Check specific pin interrupt status
  14. *
  15. * @param ptr GPIO base address
  16. * @param port Port index
  17. * @param pin Pin index
  18. *
  19. * @return true if interrupt flag is set
  20. */
  21. static inline bool gpio_check_pin_interrupt_flag(GPIO_Type *ptr, uint32_t port, uint8_t pin)
  22. {
  23. return ptr->IF[port].VALUE & (1 << pin);
  24. }
  25. void isr_gpio(void)
  26. {
  27. gpio_clear_pin_interrupt_flag(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX,
  28. BOARD_APP_GPIO_PIN);
  29. #ifdef BOARD_LED_GPIO_CTRL
  30. gpio_toggle_pin(BOARD_LED_GPIO_CTRL, BOARD_LED_GPIO_INDEX,
  31. BOARD_LED_GPIO_PIN);
  32. printf("toggle led pin output\n");
  33. #else
  34. #if defined(GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT) && (GPIO_SOC_HAS_EDGE_BOTH_INTERRUPT == 1)
  35. if (gpio_read_pin(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX, BOARD_APP_GPIO_PIN) == false) {
  36. printf("user key pressed\n");
  37. } else {
  38. printf("user key released\n");
  39. }
  40. #else
  41. printf("user key pressed\n");
  42. #endif
  43. #endif
  44. }
  45. SDK_DECLARE_EXT_ISR_M(BOARD_APP_GPIO_IRQ, isr_gpio)

        在isr_gpio中断函数中清除HPM_GPIO0.PZ.02中断标志位。正确的做法我们应该检查HPM_GPIO0.PZ.02的值,然后再清除(因为我们可以注册PZ上多个IO引脚的中断)。

  1. if (gpio_check_pin_interrupt_flag(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX,
  2. BOARD_APP_GPIO_PIN)) {
  3. gpio_clear_pin_interrupt_flag(BOARD_APP_GPIO_CTRL, BOARD_APP_GPIO_INDEX,
  4. BOARD_APP_GPIO_PIN);
  5. }

        SDK_DECLARE_EXT_ISR_M宏用于注册中断函数,这个后面我们讲解中断系统时再讲。

  1. #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");}
  2. 扩展到:
  3. 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工程

  1. cp -r $HPM_SDK_BASE/samples/drivers/gpio ~/workspace/work/hpm
  2. cd ~/workspace/work/hpm/gpio
  3. cp -r ~/workspace/work/hpm/hello_world/.vscode ~/workspace/work/hpm/gpio/
  4. code .

         

        选择flash_xip编译并进入调试窗口,然后我们按一下开发板上的PBUTN按键,就会触发中断,我们在中断中添加断点。

        通过查看IF[GPIOZ]我们发现其值为0x4,也就是bit2为1,说明中断触发了。

        然后我们继续执行发现IF[GPIOZ]变成了0x0,因为中断处理函数中我们需要清除中断标志。

        另外串口打印中也有中断触发的打印信息。

        至此本篇的内容就结束了,主要讲解了IO映射、IO中断的类型、中断注册以及中断使能。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/982610
推荐阅读
  

闽ICP备14008679号