STM32H743之GPIO外部中断实验

硬件连接

将 LED,独立按键五项摇杆按键的跳线(KEY JMP)接好。

操作与现象

按下 CTR(五项摇杆中间)按键,LED1灯状态变化一次。

相关知识准备

GPIO工作模式详细介绍
GPIO_MODE_INPUT //浮空输入
GPIO_MODE_OUTPUT_PP //推挽输出
GPIO_MODE_OUTPUT_OD //开漏输出
GPIO_MODE_AF_PP //复用推完输出
GPIO_MODE_AF_OD //复用开漏输出
GPIO_MODE_AF_INPUT //与GPIO_MODE_INPUT相同
GPIO_MODE_ANALOG //模拟输入
GPIO_MODE_IT_RISING //具有上升边缘触发检测的外部中断模式
GPIO_MODE_IT_FALLING //具有下降边缘触发检测的外部中断模式
GPIO_MODE_IT_RISING_FALLING //具有上升/下降边缘触发检测的外部中断模式
GPIO_MODE_EVT_RISING //具有上升边缘触发检测的外部事件模式
GPIO_MODE_EVT_FALLING //具有下降边缘触发检测的外部事件模式
GPIO_MODE_EVT_RISING_FALLING //具有上升/下降边缘触发检测的外部事件模式

  • 当GPIO采用浮空输入模式时,STM32的引脚状态是不确定的,此时STM32得到的电平状态完全取决于GPIO外部的电平状态,所以说在GPIO外部的引脚悬空时,读取该端口的电平状态是个不确定的值。
  • 模拟输入模式最常用的场合是ADC模拟输入,不像其他输入模式只有0和1,模拟输入模式可以读取到很细微变化的值。
  • 为什么要用带上拉或者下拉输入的模式呢?因为浮空模式时,在GPIO外部连接的电路未工作时,STM32读取的GPIO状态是不确定的,所以可以采用带上拉或者下拉输入的模式先给MCU一个确定的状态,当外部电路电平状态发生变化时,易于MCU的判断。这样可以增强MCU的抗干扰能力。
  • 推挽结构一般是指两个三极管分别受两个互补信号的控制,总是在一个三极管导通的时候另一个截止。这种结构既可以输出高电平,也可以输出低电平,可以用于连接数字器件。推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小,效率高。输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度。
  • 一般开漏输出模式时,如果外部不接上拉电阻时,只能输出低电平,所以要想输出高电平必须要外接上拉电阻。这样做的有一个好处,可以用来匹配不同的电平信号,也就是用于不同电压的系统之间的通信;另外,因为要输出高电平需要有外部的上拉电阻,所以在进行通信时,通信的速度也受到上拉电阻阻值的影响,阻值小时,通信速度可以很快,阻值大时,通信速度变慢,但也不能为了通信速度把上拉电阻用的很小,也要注意在电阻很小时,功耗会变大,所以要平衡好这个度。
  • 复用模式,可以理解为把GPIO配置为第二功能使用的时候的配置,并非单纯的用作IO输入或输出。比如使用外设IIC时,我们需要把GPIO配置为复用推挽输出,用于数据通信功能。再比如串口通信的TX,以及SPI外设的GPIO使用就要把引脚设置为复用开漏输出。

如何理解 “外部中断” 与 “外部事件” ?

  • 中断(IT)——能够触发中断,用于中断方式
  • 事件(EVT)——只设置中断标志位,不产生中断,用于查询方式

NVIC中断
NVIC 的全称是 Nested vectored interrupt controller,即嵌套向量中断控制器。
对于 M3/M4/M7 内核的 MCU,每个中断的优先级都是用寄存器中的 8 位来设置的。8 位的话就可以设置 2^8 = 256 级中断,实际中用不了这么多,所以芯片厂商根据自己生产的芯片做出了调整。比如
ST 的 STM32F1xx,F4xx 和 H7 只使用了这个 8 位中的高四位[7:4],低四位取零,这样 2^4=16,只能
表示 16 级中断嵌套。
对于这个 NVIC,有个重要的知识点就是优先级分组、抢占优先级和子优先级。STM32F1xx,F4xx 和 H7 都是只使用了这个 8 位寄存器的高四位[7:4]。

#define NVIC_PRIORITYGROUP_0         ((uint32_t)0x00000007) /*!< 0 bits for pre-emption priority
                                                                 4 bits for subpriority */
#define NVIC_PRIORITYGROUP_1         ((uint32_t)0x00000006) /*!< 1 bits for pre-emption priority
                                                                 3 bits for subpriority */
#define NVIC_PRIORITYGROUP_2         ((uint32_t)0x00000005) /*!< 2 bits for pre-emption priority
                                                                 2 bits for subpriority */
#define NVIC_PRIORITYGROUP_3         ((uint32_t)0x00000004) /*!< 3 bits for pre-emption priority
                                                                 1 bits for subpriority */
#define NVIC_PRIORITYGROUP_4         ((uint32_t)0x00000003) /*!< 4 bits for pre-emption priority
                                                                 0 bits for subpriority */

STM32 支持 5 种优先级分组。系统上电复位后,默认使用的是优先级分组 0,也就是没有抢占式优先级,只有子优先级。

  • 具有高抢占式优先级的中断可以在具有低抢占式优先级的中断服务程序执行过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以抢占低抢占式优先级的中断的执行。
  • 在抢占式优先级相同的情况下,有几个子优先级不同的中断同时到来,那么高子优先级的中断优
    先被响应。
  • 在抢占式优先级相同的情况下,如果有低子优先级中断正在执行,高子优先级的中断要等待已被
    响应的低子优先级中断执行结束后才能得到响应,即子优先级不支持中断嵌套。
  • Reset、NMI、Hard Fault 优先级为负数,高于普通中断优先级,且优先级不可配置。
  • 系统中断(比如:PendSV,SVC,SysTick)是不是一定比外部中断(比如 SPI,USART)要高?答案:不是的,它们是在同一个 NVIC 下面设置的。
  • 另外特别注意一点,配置抢占优先级和子优先级,它们合并成的4bit 数字的数值越小,优先级越高,这一点千万不要搞错了。

使用HAL库函数配置GPIO中断

① 初始化HAL

HAL_Init();

设置优先级分组4并开启SYSCFG时钟

HAL_StatusTypeDef HAL_Init(void)
{
  /* 设置中断优先级分组 */
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); //设置优先级分组4

  /* 使用systick作为时基源,并配置1ms刻度(重置后的默认时钟为HSI) */
  if(HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)
  {
    return HAL_ERROR;
  }

  /* 初始化底层硬件 */
  HAL_MspInit();

  /* 返回函数状态 */
  return HAL_OK;
}
void HAL_MspInit(void)
{
  __HAL_RCC_SYSCFG_CLK_ENABLE(); //开启SYSCFG时钟
}

② 系统时钟配置

SystemClock_Config();

③ GPIO初始化

MX_GPIO_Init();

其函数实现与GPIO输入输出实验类似,不同的是在GPIO输入输出中,PI11引脚即CTR配置为输入模式,PB6引脚即LED1配置为上拉电阻,在GPIO中断中,PI11引脚即CTR配置为具有下降沿触发检测的外部中断模式,PB6引脚即LED1不配置上拉/下拉电阻。

配置中断优先级,并使能中断。

void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* 使能GPIO端口时钟 */
  __HAL_RCC_GPIOI_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /* 配置GPIO引脚输出电平 */
  HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);

  /* 配置引脚PI11即CTR */
  GPIO_InitStruct.Pin = KEY_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; //具有下降边缘触发检测的外部中断模式
  GPIO_InitStruct.Pull = GPIO_PULLUP; //上拉电阻
  HAL_GPIO_Init(KEY_GPIO_Port, &GPIO_InitStruct);

  /* 配置引脚PB6即LED1 */
  GPIO_InitStruct.Pin = LED_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出模式
  GPIO_InitStruct.Pull = GPIO_NOPULL; //不设置上拉/下拉电阻
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; //高速
  HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0); //设置抢占优先级为0,子优先级为0
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); //使能中断

}

④ 中断服务函数

EXTI15_10_IRQHandler();

void EXTI15_10_IRQHandler(void)
{
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11);
}

⑤ 编写中断处理回调函数

HAL_GPIO_EXTI_Callback();

/* 编写中断处理回调函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
		i = ~i;
		if(i){
			HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
		}else{
			HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
	}
}

你可能感兴趣的:(STM32H743,单片机,嵌入式,stm32)