[MCU] 兆易GD32H759I-EVAL 外部中断

御坂10032号   2024-5-25 01:57 楼主

题外话

 

大家好,今天给大家带来的测评是关于GD32的外部中断

本文参考如下资料:

1- GD32H759I-EVAL-V1.3.pdf

2- GD32H759I-EVAL评估板使用指南_Rev1.2.pdf

3- GD32H737_757_759_User_Manual_Rev1.3_CN.pdf

4- GD32H759xx_Datasheet_Rev1.5.pdf

 

下期:ADC

正文

 

在GD32H759I-EVAL SDK内,根据数据手册得知当前的Arm Cortex®-M7一共支持了217种可屏蔽的中断。 本章我们将根据数据手册探索SDK中中断的使用。

在GD32H737_757_759_User_Manual_Rev1.3_CN.pdf (后统称用户手册)的第309页,可以看到当前芯片支持的所有中断的类型。如果你烧录的是官方的Demo的话,你可以看到项目目录下有一个命名为gd32h7xx_it.h的一个头文件和一个命名为gd32h7xx_it.c的源文件。这两个文件重写了启动文件startup_gd32h7xx.s中的部分weak方法实现了对部分中断的处理。

#include "gd32h7xx_it.h"
#include "main.h"
#include "systick.h"

/*!
    \brief      this function handles NMI exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void NMI_Handler(void)
{
    /* if NMI exception occurs, go to infinite loop */
    while(1) {
    }
}

/*!
    \brief      this function handles HardFault exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void HardFault_Handler(void)
{
    /* if Hard Fault exception occurs, go to infinite loop */
    while(1) {
    }
}

/*!
    \brief      this function handles MemManage exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void MemManage_Handler(void)
{
    /* if Memory Manage exception occurs, go to infinite loop */
    while(1) {
    }
}

/*!
    \brief      this function handles BusFault exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void BusFault_Handler(void)
{
    /* if Bus Fault exception occurs, go to infinite loop */
    while(1) {
    }
}

/*!
    \brief      this function handles UsageFault exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void UsageFault_Handler(void)
{
    /* if Usage Fault exception occurs, go to infinite loop */
    while(1) {
    }
}

/*!
    \brief      this function handles DebugMon exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void DebugMon_Handler(void)
{
    /* if DebugMon exception occurs, go to infinite loop */
    while(1) {
    }
}

/*!
    \brief      this function handles SVC exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void SVC_Handler(void)
{
    /* if SVC exception occurs, go to infinite loop */
    while(1) {
    }
}

/*!
    \brief      this function handles PendSV exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void PendSV_Handler(void)
{
    /* if PendSV exception occurs, go to infinite loop */
    while(1) {
    }
}

/*!
    \brief      this function handles SysTick exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void SysTick_Handler(void)
{
    delay_decrement();
}

数据手册对照异常信息如下:

image.png  

 

而关于中断函数相关的的向量以及其Weak函数可以在startup_gd32h7xx.s中找到, 如下所示

 

__Vectors       DCD     __initial_sp                      ; Top of Stack
                DCD     Reset_Handler                     ; Reset Handler
                DCD     NMI_Handler                       ; NMI Handler
                DCD     HardFault_Handler                 ; Hard Fault Handler
                DCD     MemManage_Handler                 ; MPU Fault Handler
                DCD     BusFault_Handler                  ; Bus Fault Handler
                DCD     UsageFault_Handler                ; Usage Fault Handler
                DCD     0                                 ; Reserved
                DCD     0                                 ; Reserved
                DCD     0                                 ; Reserved
                DCD     0                                 ; Reserved
                DCD     SVC_Handler                       ; SVCall Handler
                DCD     DebugMon_Handler                  ; Debug Monitor Handler
                DCD     0                                 ; Reserved
                DCD     PendSV_Handler                    ; PendSV Handler
                DCD     SysTick_Handler                   ; SysTick Handler

 

如下是启动文件中定义的部分Weak函数,通过对这些函数的重写,用户可以处理各种异常情况(中断)

 

NMI_Handler\
                PROC
                EXPORT  NMI_Handler                       [WEAK]
                B       .
                ENDP
HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler                 [WEAK]
                B       .
                ENDP
MemManage_Handler\
                PROC
                EXPORT  MemManage_Handler                 [WEAK]
                B       .
                ENDP
BusFault_Handler\
                PROC
                EXPORT  BusFault_Handler                  [WEAK]
                B       .
                ENDP
UsageFault_Handler\
                PROC
                EXPORT  UsageFault_Handler                [WEAK]
                B       .
                ENDP
SVC_Handler     PROC
                EXPORT  SVC_Handler                       [WEAK]
                B       .
                ENDP
DebugMon_Handler\
                PROC
                EXPORT  DebugMon_Handler                  [WEAK]
                B       .
                ENDP
PendSV_Handler  PROC
                EXPORT  PendSV_Handler                    [WEAK]
                B       .
                ENDP
SysTick_Handler PROC
                EXPORT  SysTick_Handler                   [WEAK]
                B       .
                ENDP    

 

对于外部中断而言,一共具有38个中断线。但是来自IO管脚的中断线只有16个,其他剩余的22个中断线则被配置给了内部模块。

image.png  

如果当产生一个中断,这个中断的请求会被发送到边缘检测寄存器中,根据配置的极性判断当前的中断是上升沿触发,还是下降沿,或者是双边沿,或者是不触发(GD32支持这四种模式)具体的定义可以查看gd32h7xx_it.h的337行到382行。之后这个中断信号经过一个或门。或门的另一个输入则是来自于软件触发。说明GD32的中断分别支持软件触发或者是硬件触发。之后会根据这个中断的类型(外部中断,或者是事件中断)进入对应的屏蔽控制寄存器。 如果对应的屏蔽控制寄存器屏蔽了这个中断。那么这个中断将不会产生。 如果没有被屏蔽的话。那么事件正常触发。

 

接下来我们来验证一下实际中断的使用。使用两种中断的方式来熄灭一个LED灯。

在上节的代码上稍作修改来快速点亮一个LED灯(LED1)。

#include "gd32h7xx.h"
#include "systick.h"


static void cache_enable(void)
{
    /* Enable I-Cache */
    SCB_EnableICache();

    /* Enable D-Cache */
    SCB_EnableDCache();
}

int main(void)
{
    /* enable the CPU Cache */
    cache_enable();
    /* configure systick */
    systick_config();
	
    rcu_periph_clock_enable(RCU_GPIOF);
		
    gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_10);
    gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_10);
		
		gpio_bit_set(GPIOF, GPIO_PIN_10);
	
    while(1) {
      
    }
}

 此时可以观察到LED灯已经处于点亮状态。

 

微信图片_20240525020106.jpg  

如果当前不对程序做任何修改。 除非关闭电源。LED灯始终都会处于点亮状态。

 

接下来我们来配置一个用户按键。使其按下这个按键可以熄灭或者点亮这个LED灯(非中断)

 

根据评估板使用指南的4.4按键章节,我们可以得知,一共有三个按键可以供用户配置使用,分别是连接到了PA0的WakeUp(唤醒)按键,连接到了PC13的Temper(干预)按键,以及最后连接到PF8的User按键。但是PF8按键是一个复用的按键,你需要检查你是否已经将这个PIN复用成了其他的功能。

image.png  

 

关于如何复用GPIO PIN成为其他的功能。具体请参考gd32h7xx_gpio.c 的第413行。然后查询数据手册,对应的PIN具有什么功能。由于本章节是讲解中断,所以在这里不做过多展开。

 

简单的修改一下使其程序在主循环中可以根据UserKey的按下来熄灭LED(需要注意的是,如果需要使用UserKey,需要把JP42跳帽接到UserKey上,同时将JP50也接到右侧)

 

#include "gd32h7xx.h"
#include "systick.h"

static void cache_enable(void)
{
    /* Enable I-Cache */
    SCB_EnableICache();

    /* Enable D-Cache */
    SCB_EnableDCache();
}

int main(void)
{
    /* Enable the CPU Cache */
    cache_enable();

    /* Configure systick */
    systick_config();

    /* Enable GPIOF clock */
    rcu_periph_clock_enable(RCU_GPIOF);

    /* LED 输出模式 */
    gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_10);
    gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_10);

    /* 点亮 LED */
    gpio_bit_set(GPIOF, GPIO_PIN_10);

    /* 按键 输入模式 */
    gpio_mode_set(GPIOF, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_8);

    while(1) {
        if(gpio_input_bit_get(GPIOF, GPIO_PIN_8) == RESET) {
            gpio_bit_reset(GPIOF, GPIO_PIN_10);
        }
    }
}

 

但是上述的代码是在循环中重复读取IO的变化。可以对上述代码做简单的修改来使用中断实现。

 

步骤如下:

1- NVIC设置开启中断。并且设置中断优先级

2- 配置中断线到具体的某一个GPIO pin

3- 设置中断的触发方式

4- 配置中断回调函数到gd32h7xx_it.h 并且在主函数引入

 

#include "gd32h7xx.h"
#include "systick.h"
#include "gd32h7xx_it.h"
static void cache_enable(void)
{
    /* Enable I-Cache */
    SCB_EnableICache();

    /* Enable D-Cache */
    SCB_EnableDCache();
}




int main(void)
{
    /* Enable the CPU Cache */
    cache_enable();

    /* Configure systick */
    systick_config();

    /* Enable GPIOF clock */
    rcu_periph_clock_enable(RCU_GPIOF);

    /* LED 输出模式 */
    gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_10);
    gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_10);

    /* 点亮 LED */
    gpio_bit_set(GPIOF, GPIO_PIN_10);

		
    /* 按键 输入模式 */
    gpio_mode_set(GPIOF, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_8);
		
		//开始中断
	  nvic_irq_enable(EXTI5_9_IRQn, 2U, 0U);
	
		syscfg_exti_line_config(EXTI_SOURCE_GPIOF,EXTI_SOURCE_PIN8);
		
    exti_init(EXTI_8, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
		
		exti_interrupt_flag_clear(EXTI_8);

    while(1) {
       
    }
}

 

回调函数配置如下:

 

/*
*@desc: This function cope with exit line 5-9
*@return  :  None
*/
void EXTI5_9_IRQHandler(void)
{
    if(exti_interrupt_flag_get(EXTI_8) != RESET)
    {
        /* 处理中断(例如,切换 LED 状态) */
        gpio_bit_write(GPIOF, GPIO_PIN_10, 
                       (bit_status)(1 - gpio_input_bit_get(GPIOF, GPIO_PIN_10)));

        /* 清除中断标志 */
        exti_interrupt_flag_clear(EXTI_8);
    }
}

 

 

效果演示:

2a758c73ba3fb8dd52b676074583b4f3

 

 

 

代码如下:

02_Interruption.zip (44.75 KB)
(下载次数: 0, 2024-5-25 14:02 上传)

 

 

本帖最后由 御坂10032号 于 2024-5-25 14:13 编辑

回复评论 (3)

一步一步分析对GPIO的输入操作,非常详细,这帖子有质量!
点赞  2024-6-1 11:25

如果有时间,排版与序号再修改一下,我给你精华帖

点赞  2024-6-1 11:26
引用: lugl4313820 发表于 2024-6-1 11:26 如果有时间,排版与序号再修改一下,我给你精华帖

点赞  2024-6-1 13:47
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复