按键中断控制LED灯
按键控制灯
#include "stm32f10x.h" // 包含STM32F10x的标准外设库头文件
int main(void)
{
// 使能GPIOA, GPIOC和AFIO时钟(复用功能时钟)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO, ENABLE);
// 设置中断优先级分组为NVIC_PriorityGroup_1
NVIC_SetPriorityGrouping(NVIC_PriorityGroup_1);
GPIO_InitTypeDef GPIO_InitStructure; // 定义一个GPIO初始化结构体
// 配置GPIOC的Pin10为输出模式,推挽输出,速度为50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOC, &GPIO_InitStructure); // 初始化GPIOC的Pin10(LED引脚)
// 配置GPIOA的Pin1为上拉输入模式,速度为50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // IPU表示内部上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA的Pin1,引脚上的按钮
EXTI_InitTypeDef EXTI_InitStructure; // 定义一个外部中断初始化结构体
// 配置外部中断线1,为中断模式,触发方式为上升沿触发
EXTI_InitStructure.EXTI_Line = EXTI_Line1;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); // 初始化外部中断线1
// 选择GPIOA的Pin1作为外部中断线1的输入源
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);
NVIC_InitTypeDef NVIC_InitStructure; // 定义一个NVIC初始化结构体
// 配置外部中断线1的中断优先级和使能中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); // 初始化NVIC的外部中断线1
while(1)
{
// 主循环,空实现,等待中断
}
}
// 宏定义,用于设置GPIO引脚为高电平
#define digitalHi(p,i) {p->BSRR=i;}
// 宏定义,用于设置GPIO引脚为低电平
#define digitalLo(p,i) {p->BRR=i;}
// 宏定义,用于切换GPIO引脚的电平状态
#define digitalToggle(p,i) {p->ODR ^=i;}
// 外部中断线1的中断服务函数
void EXTI1_IRQHandler(void)
{
// 检查外部中断线1的中断标志位,如果置位则处理
if(EXTI_GetITStatus(EXTI_Line1) != RESET)
{
// 切换GPIOC的Pin10的电平状态(用于控制LED)
digitalToggle(GPIOC, GPIO_Pin_10);
// 清除外部中断线1的中断挂起位
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
1.设置中断优先组:
在使用中断时,需要先设置中断优先组NVIC_SetPriorityGrouping(NVIC_PriorityGroup_1);
STM32F103使用4位二进制表示中断优先级
这4位又被分为抢占优先级(Pre-emption priority)和子优先级(Sub priority)
共有5种分法:
所以,在设置NVIC_SetPriorityGrouping( )参数时,有以下几种可以填进去:
通过注释可以看出,前面的代码使用的是NVIC_PriorityGroup_1,也就是1位强占优先级,3位子优先级。抢占优先级的大小会影响中断嵌套和中断排队,子优先级大小只影响中断排队。
发生中断排队的条件:新中断抢占优先级更高(二进制00-11数字越小,优先级越高)
发生中断排队:优先级越高,排队越靠前,优先级相同,先来后到。
2.设置GPIO(General Purpose Input Output,即通用输入/输出端口):
设置GPIO时,首先要开启相应引脚的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO, ENABLE);
这句话就开启了GPIOC和GPIOA的时钟。
开启时钟后,需要配置GPIO具体引脚的属性
配置引脚属性,需要通过一个结构体
先定义一个结构体:
GPIO_InitTypeDef GPIO_InitStructure; // 定义一个GPIO初始化结构体
定义好后,开始配置(上面定义结构体的名字是“GPIO_InitStructure“,这个名字可以随便改,但是下面结构体名字也要改)
// 配置GPIOC的Pin10为输出模式,推挽输出,速度为50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOC, &GPIO_InitStructure); // 初始化GPIOC的Pin10(LED引脚)
// 配置GPIOA的Pin1为上拉输入模式,速度为50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // IPU表示内部上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA的Pin1,按钮引脚
下面开始配置中断,也是先定义一个结构体
EXTI_InitTypeDef EXTI_InitStructure; // 定义一个外部中断初始化结构体
// 配置外部中断线1,为中断模式,触发方式为上升沿触发(EXTI_Trigger_Rising)
也可以设置下降沿(EXTI_Trigger_Falling)
(这里因为我们按钮设置的是GPIOA1,A的第一个引脚,所以必须选EXTI_Line1,如果是其他比如GPIOA2引脚,则下面要改成EXTI_Line2)
EXTI_InitStructure.EXTI_Line = EXTI_Line1;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); // 初始化外部中断线1
// 选择GPIOA的Pin1作为外部中断线1的输入源
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);
3.配置NVIC (Nested Vectored Interrupt Controller,嵌套向量中断控制器)
NVIC_InitTypeDef NVIC_InitStructure; // 定义一个NVIC初始化结构体
// 配置外部中断线1的中断优先级和使能中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
前面选的是Lin1,所以要选Lin1的中断
前面的代码使用的是NVIC_PriorityGroup_1,也就是1位强占优先级,3位子优先级。
用1位二进制来表示抢占优先级,取值只有0和1所以,下面配置这个按钮的抢占优先级PreemptionPriority只有0和1两个选择()
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
3位子优先级。
用3位二进制来表示子优先级,取值只有000到111,也就是0-7所以,下面配置这个按钮的子优先级SubPriority只能是0-7的数,因为没有其他中断的事件了,这里可以随便填一个。
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); // 初始化NVIC的外部中断线1
4.配置中断函数
// 宏定义,用于设置GPIO引脚为高电平
#define digitalHi(p,i) {p->BSRR=i;}
// 宏定义,用于设置GPIO引脚为低电平
#define digitalLo(p,i) {p->BRR=i;}
// 宏定义,用于切换GPIO引脚的电平状态
#define digitalToggle(p,i) {p->ODR ^=i;}
上面是设置引脚的宏定义,对着抄就行。
// 外部中断线1的中断服务函数,前面选的是Lin1,也就是中断线1,下面中断函数名字必须和中断线数字对上,选的中断线1就要是EXTI1的中断函数EXTI1_IRQHandler
void EXTI1_IRQHandler(void)
{
// 检查外部中断线1的中断标志位,如果置位则处理
if(EXTI_GetITStatus(EXTI_Line1) != RESET)
{
// 翻转GPIOC的Pin10的电平状态(用于控制LED)
digitalToggle(GPIOC, GPIO_Pin_10);
//如果只要点亮灯,就
// digitalHi(GPIOC, GPIO_Pin_10);
// 清除外部中断线1的中断挂起位
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
CubeMX:
PA1是按钮端口,需要把模式配置为“上升沿的外部中断”
PC10是LED引脚,选择Push Pull推挽输出
设置好后,在KEIL里面打开工程,设置
void EXTI1_IRQHandler(void)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_10);
}