«

按键中断控制LED灯

Sweetiey 发布于 阅读:206 软件笔记


按键控制灯

#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种分法:

按键中断控制LED灯

所以,在设置NVIC_SetPriorityGrouping(   )参数时,有以下几种可以填进去:

按键中断控制LED灯

通过注释可以看出,前面的代码使用的是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的中断

按键中断控制LED灯

前面的代码使用的是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:

按键中断控制LED灯

PA1是按钮端口,需要把模式配置为“上升沿的外部中断”

按键中断控制LED灯

PC10是LED引脚,选择Push Pull推挽输出

设置好后,在KEIL里面打开工程,设置


void EXTI1_IRQHandler(void)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_10);
}


STM32


扫描二维码,在手机上阅读