一、设置分组
stm32f1xx_hal_cortex.c
查看分组
五个形参,分组0~4
stm32f1xx_hal.c
设置了分组为2, 此工程就不需要再设置了
再回到stm32f1xx_hal_cortex.c
查看NVIC_SetPriorityGrouping的定义,若无法跳转,先编译一下,
继续找定义
这段代码是用于设置 NVIC 的优先级分组的内联函数。关键部分:
-
SCB->AIRCR = reg_value;
: 最后,将更新后的寄存器值写入 SCB_AIRCR 寄存器,完成对 NVIC 优先级分组的设置。
总的来说,这个函数的目的是安全地设置 NVIC 的优先级分组,确保在进行任何更改之前正确地处理了旧的配置值,并且将写入操作限制在允许的范围内。
二、设置优先级
stm32f1xx_hal_cortex.c
没有返回值,三个形参
这段代码是用于设置特定中断的优先级的函数。
-
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
: 这是一个函数声明,用于设置特定中断的优先级。它接受三个参数,分别是中断号IRQn
,抢占优先级PreemptPriority
,以及响应优先级SubPriority
。 -
uint32_t prioritygroup = 0x00U;
: 这一行定义了一个变量prioritygroup
并初始化为0x00U
,该变量用于存储当前 NVIC 的优先级分组。 -
assert_param(IS_NVIC_SUB_PRIORITY(SubPriority));
和assert_param(IS_NVIC_PREEMPTION_PRIORITY(PreemptPriority));
: 这两行是参数检查,确保传递给函数的SubPriority
和PreemptPriority
在有效范围内。如果参数不在有效范围内,这些断言将会触发。 -
prioritygroup = NVIC_GetPriorityGrouping();
: 这一行调用了NVIC_GetPriorityGrouping()
函数,获取当前 NVIC 的优先级分组。 -
NVIC_SetPriority(IRQn, NVIC_EncodePriority(prioritygroup, PreemptPriority, SubPriority));
: 这一行调用了NVIC_SetPriority()
函数,设置指定中断的优先级。它使用了NVIC_EncodePriority()
函数将抢占优先级和子优先级编码为一个优先级值,并将其传递给NVIC_SetPriority()
函数来设置中断的优先级。
这个函数的目的是设置特定中断的优先级,确保传递给函数的优先级参数在有效范围内,并将优先级值编码后传递给 NVIC。
查看IRQn_Type定义
跳转到stm32f103xe.h
举个例子
RTC_IRQn
中断号是3
NVIC_SetPriority
看定义
跳转到这里,再去看定义
跳转到:
用于设置特定中断的优先级的内联函数。每个部分的作用:
-
__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
: 这是一个内联函数的声明,用于设置特定中断的优先级。它接受两个参数,一个是中断号IRQn
,另一个是优先级priority
。 -
if ((int32_t)(IRQn) >= 0)
: 这个条件语句检查中断号是否为非负值,如果是非负值,则说明是可编程中断,需要设置NVIC->IP
寄存器的相应位置;如果是负值,则说明是固定优先级中断,需要设置SCB->SHP
寄存器的相应位置。 -
NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
: 如果是可编程中断,就将优先级值左移以便正确放置到NVIC->IP
寄存器相应位置。这里通过移位来确保优先级值的正确性,同时保留了__NVIC_PRIO_BITS
指定的位数,这是由硬件定义的。 -
SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
: 如果是固定优先级中断,则将优先级值左移并设置到SCB->SHP
寄存器的相应位置。由于固定优先级中断的索引不是从零开始的,因此需要进行一些偏移和掩码操作。
这个函数的目的是根据中断类型设置对应中断的优先级,确保优先级值在正确的范围内,并将其正确地写入到相应的寄存器中。
stm32f1xx_hal_cortex.c对cortex_cm3.h文件里的函数进行了封装
这段代码是一个内联函数,用于检查特定中断是否已经被使能。让我们逐步解释每个部分的作用:
-
__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn)
: 这是一个内联函数的声明,用于获取特定中断是否已经被使能。它接受一个参数,即中断号IRQn
。 -
if ((int32_t)(IRQn) >= 0)
: 这个条件语句检查中断号是否为非负值,如果是非负值,则说明是可编程中断,需要检查NVIC->ISER
寄存器的相应位;如果是负值,则说明是固定优先级中断,这些中断无法被使能,直接返回 0。 -
return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
: 如果是可编程中断,则检查NVIC->ISER
寄存器中相应位是否被置位,如果被置位则返回 1,否则返回 0。这里通过对中断号进行移位和掩码操作来确定具体的位位置。 -
return(0U);
: 如果是固定优先级中断,则直接返回 0,表示该中断无法被使能。
这个函数的目的是根据中断类型检查特定中断是否已经被使能,并返回相应的结果。
三、EXTI
p60
P61
两种中断
四、GPIO外部中断
复制跑马灯实验,BSP新建EXTI文件夹,keil5中新建exti.c与exti.h并添加到EXTI文件夹,添加到工程里
复制拿过来用
若无法跳转定义,参考这份博客
http://t.csdnimg.cn/RCJyL
重新编译程序
再次执行Go To Definition of ''
的操作,可以了
参考
需选用外部中断下降沿触发,且输入上拉
#define GPIO_MODE_IT_RISING 0x10110000u /*!< External Interrupt Mode with Rising edge trigger detection */
#define GPIO_MODE_IT_FALLING 0x10210000u /*!< External Interrupt Mode with Falling edge trigger detection */
#define GPIO_MODE_IT_RISING_FALLING 0x10310000u /*!< External Interrupt Mode with Rising/Falling edge trigger detection */
#define GPIO_MODE_EVT_RISING 0x10120000u /*!< External Event Mode with Rising edge trigger detection */
#define GPIO_MODE_EVT_FALLING 0x10220000u /*!< External Event Mode with Falling edge trigger detection */
#define GPIO_MODE_EVT_RISING_FALLING 0x10320000u /*!< External Event Mode with Rising/Falling edge trigger detection */
这些宏定义了用于 GPIO(通用输入/输出)引脚的不同模式,用于处理外部中断和事件,根据触发边沿的不同来触发。具体而言:
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 配置函数一起使用,以设置 GPIO 引脚的所需模式,以便根据特定边沿触发来处理外部中断或事件。
上下拉配置
#define GPIO_NOPULL 0x00000000u /*!< No Pull-up or Pull-down activation */
#define GPIO_PULLUP 0x00000001u /*!< Pull-up activation */
#define GPIO_PULLDOWN 0x00000002u /*!< Pull-down activation */
这些宏定义了用于 GPIO 引脚的不同上拉和下拉配置选项。具体来说:
GPIO_NOPULL
:表示不启用上拉或下拉。GPIO_PULLUP
:表示启用上拉。GPIO_PULLDOWN
:表示启用下拉。
这些选项通常用于配置 GPIO 引脚的电气特性,以确保引脚在特定条件下的正确操作,如减少干扰、提高信号质量等。
选择输入上拉
中断优先级:HAL_NVIC_SetPriority()
go to definition
选择中断号
笔者选用的是B1,选择EXTI1_IRQn
设置抢断优先级与响应优先级
笔者设置的是2和0
使能中断:HAL_NVIC_EnableIRQ()
笔者的是EXTI1_IRQn
设计中断服务函数:
去到配置文件查找
选择EXTI1_IRQHandler
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
这段代码是一个 HAL(Hardware Abstraction Layer,硬件抽象层)的 GPIO(通用输入/输出)外部中断处理函数。让我来解释它:
-
HAL_GPIO_EXTI_IRQHandler
是一个函数,用于处理外部中断事件。它接收一个参数GPIO_Pin
,表示触发了外部中断的 GPIO 引脚。 -
在函数内部,首先检测是否检测到了 EXTI(外部中断)线的中断事件。
-
如果
GPIO_Pin
对应的 EXTI 中断事件标志被置位(不为 0),则说明该 GPIO 引脚触发了外部中断。 -
接着,通过
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin)
清除相应的中断标志。 -
最后,调用
HAL_GPIO_EXTI_Callback(GPIO_Pin)
,这是一个回调函数,用于在外部中断触发时执行用户定义的操作。这个函数的实现应该由用户在程序的其他地方提供。
用于在外部中断事件发生时执行相应的操作,如清除中断标志并调用用户定义的回调函数。
回调函数
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(GPIO_Pin);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/
}
用于处理 GPIO 外部中断回调的弱定义函数 HAL_GPIO_EXTI_Callback
。
-
__weak
关键字表示这是一个弱定义的函数,它可以被用户在程序的其他地方重新定义以满足特定需求。 -
这个函数的目的是处理 GPIO 外部中断事件。它接收一个参数
GPIO_Pin
,表示触发了外部中断的 GPIO 引脚。 -
函数内部通过调用
UNUSED(GPIO_Pin)
来避免编译器产生未使用参数的警告。这是因为在某些情况下,用户可能不需要使用该参数。 -
注释部分指出,如果需要在外部中断发生时执行特定的操作,用户应该在自己的代码文件中实现
HAL_GPIO_EXTI_Callback
函数,并在其中编写所需的处理逻辑。
这个函数提供了一个默认的外部中断处理回调函数,并允许用户根据需要进行修改或扩展。
中断信号到来,去翻转led引脚的信号,在led.h中查看引脚是B5
4.1使能GPIO时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
4.2 HAL_GPIO_init
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_GPIOB_CLK_ENABLE();
gpio_init_struct.Pin = LED0_GPI1_PIN; /* 引脚B1 */
gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(GPIOB, &gpio_init_struct); /* 初始化 */
4.3 设置中断分组
配置文件已设置分组为2
4.4 设置中断优先级
HAL_NVIC_SetPriority(EXTI1_IRQn,2,0);
4.5 使能中断
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
4.6 设计中断服务函数
void EXTI1_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(20);
if(GPIO_Pin=GPIO_Pin_1)
{
if(HAL_GPIO_ReadPin(GPIOB,GPIO_Pin_1) ==0)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_Pin_5);
}
}
}
exti.h
函数声明
void exti_init(void);
EXTI1_IRQHandler与HAL_GPIO_EXTI_Callback已经在配置文件里声明过
startup_stm32f103xe.s
stm32f1xx_hal_gpio.h
五、源码
exti.h
#ifndef _EXTI_H
#define _EXTI_H
#include "./SYSTEM/sys/sys.h"
void exti_init(void);
#endif
exit.c
#include "./BSP/EXTI/exti.h"
void exti_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_GPIOB_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_1; /* 引脚B1 */
gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(GPIOB, &gpio_init_struct); /* 初始化 */
HAL_NVIC_SetPriority(EXTI1_IRQn,2,0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
}
void EXTI1_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(20);
if(GPIO_Pin == GPIO_PIN_1)
{
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) ==0)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
}
}
}
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/EXTI/exti.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟,72M */
delay_init(72); /* 初始化延时函数 */
led_init(); /* 初始化LED */
exti_init();
while(1)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
delay_ms(5000);
}
}
参考
【【正点原子】手把手教你学STM32 HAL库开发全集【真人出镜】STM32入门教学视频教程 单片机 嵌入式】https://www.bilibili.com/video/BV1bv4y1R7dp?p=56&vd_source=be33b1553b08cc7b94afdd6c8a50dc5a