本帖最后由 御坂10032号 于 2024-5-25 14:13 编辑
题外话
大家好,今天给大家带来的测评是关于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();
}
数据手册对照异常信息如下:
而关于中断函数相关的的向量以及其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个中断线则被配置给了内部模块。
如果当产生一个中断,这个中断的请求会被发送到边缘检测寄存器中,根据配置的极性判断当前的中断是上升沿触发,还是下降沿,或者是双边沿,或者是不触发(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灯已经处于点亮状态。
如果当前不对程序做任何修改。 除非关闭电源。LED灯始终都会处于点亮状态。
接下来我们来配置一个用户按键。使其按下这个按键可以熄灭或者点亮这个LED灯(非中断)
根据评估板使用指南的4.4按键章节,我们可以得知,一共有三个按键可以供用户配置使用,分别是连接到了PA0的WakeUp(唤醒)按键,连接到了PC13的Temper(干预)按键,以及最后连接到PF8的User按键。但是PF8按键是一个复用的按键,你需要检查你是否已经将这个PIN复用成了其他的功能。
关于如何复用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
代码如下:
|