STM32的中断系统(按键控灯:基于HAL库+寄存器)

1. 中断的概念

在主程序运行过程中,出现了特定事件,使得CPU暂停当前正在运行的程序,转而去处理这个事件,等这个事件处理完成之后,CPU再回到刚才被打断的位置继续处理,这就是中断

那个打断CPU执行的特定事件,我们一般称之为中断源。被中断源打断的位置我们称为断点。处理特定事件的过程,我们称为执行中断处理程序

标题

2. 中断嵌套概念

正在执行中断程序的时候,这个时候有可能被另外一个中断源给中断,CPU转而去执行另外一个中断源的中断处理程序,这叫中断嵌套

中断B能否打断中断A,要看他们的优先级,优先级高的可以打断优先级低的,优先级低的无法打断优先级高的。中断源可以是外部的,也可以是内部的。外部的叫外部中断源,内部的叫内部中断源(有时候也叫异常)。

2.1. 为什么需要中断

2.1.1. STM32的中断体系架构

STM32有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。

STM32F103系列70个中断(咱们目前使用的芯片)有10个内部中断和60个可编程的外部中断。

3. NVIC嵌套向量中断控制器

嵌套向量中断控制器管理着包括内核异常,外部中断等所有中断。由NVIC决定哪个中断的处理程序交给CPU来执行。

标题

3.1.1. 中断优先级

NVIC为了方便管理中断,可以通过软件给每个中断设置优先级。

NVIC用4个位来控制优先级,值小的优先级高。把优先级分为两种:抢占优先级响应优先级

3.1.2. 中断优先级规则

Ø 优先级值越小,优先级越高。

Ø 如果不设置优先级,则默认优先级为0。

Ø 先比较抢占优先级。抢占优先级高的可以打断抢占优先级低的。

Ø 若抢占优先级一样,再比较响应优先级。但是响应优先级不会导致中断嵌套

Ø 若抢占优先级一样的同时挂起,则优先处理响应抢占优先级高的。

Ø 若挂起的优先级(抢占和响应)都一样,则查找中断向量表,值小的先响应。

分组

抢占优先级

响应优先级

0

0位 取值范围:0

4位 取值范围:0-15

1

1位 取值范围:0-1

3位 取值范围:0-7

2

2位 取值范围:0-3

2位 取值范围:0-3

3

3位 取值范围:0-7

1位 取值范围:0-1

4

4位 取值范围:0-15

0位 取值范围:0

3.1. 外部中断控制器 (EXTI)

3.1.1. 按键控灯(基于寄存器开发)

#include "Driver_LED.h"
#include "Delay.h"
#include "Driver_Key.h"

int main()
{
    
    /* 1. 初始化LED */
    Driver_LED_Init();

    /* 2. 初始化按键 */
    Driver_Key_Init();

    while (1)
    {
       
    }
}
#include "Driver_Key.h"
#include "Driver_LED.h"
#include "Delay.h"

/**
 * @description: 初始化按键.
 *  1. 给按键对应的io口设置工作模式: 下拉输入
 *  2. 配置复用为外部中断
 *  3. 配置外部中断控制器 EXTI
 *  4. 配置NVIC
 */
void Driver_Key_Init(void)
{
    /* 1. 开启时钟 */
    /* 1.1  GPIOF*/
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    /* 1.2  AFIO*/
    RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;

    /* 2. 配置   PA0  为下拉输入: MODE=00 CNF=10  ODR=0 */
    GPIOA->CRL &= ~GPIO_CRL_MODE0;
    GPIOA->CRL |= GPIO_CRL_CNF0_1;
    GPIOA->CRL &= ~GPIO_CRL_CNF0_0;   
    GPIOA->ODR &= ~GPIO_ODR_ODR0;

    /* 3. 配置AFIO 配置 PA0 引脚为外部中断  EXTICR3  0101*/
    //AFIO->EXTICR[0] &= ~AFIO_EXTICR1_EXTI0;
    AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI0_PA;

    /* 4. 配置EXTI */
    /* 4.1. 配置上升沿触发 RTSR TR10=1*/
    EXTI->RTSR |= EXTI_RTSR_TR0;
    /* 4.2 开启 LINE10, 配置的中断屏蔽寄存器 */
    EXTI->IMR |= EXTI_IMR_MR0;

    /* 5. 配置 NVIC */
    /* 5.1 配置优先级组 (3-7) 配置3表示4个二进制位全部用于表示抢占优先级*/
    NVIC_SetPriorityGrouping(3);
    /* 5.2 配置优先级 参数1:中断号*/
    NVIC_SetPriority(EXTI0_IRQn, 3);
    /* 5.3 使能Line10 */
    NVIC_EnableIRQ(EXTI0_IRQn);
}

/**
 * @description: line 15-10的中断服务函数.
 *  一旦按键下按键1,则会执行一次这个函数
 * @return {*}
 */
void EXTI0_IRQHandler(void)
{
    /* 务必一定必须要清除中断标志位 */
    EXTI->PR |= EXTI_PR_PR0;

    Delay_ms(5);
    if ((GPIOA->IDR & GPIO_IDR_IDR0) != 0)
    {
        Drviver_LED_Toggle(LED_1);
        Drviver_LED_Toggle(LED_2);
    }
}

#ifndef __DRIVER_KEY_H
#define __DRIVER_KEY_H

#include "stm32f10x.h"
void Driver_Key_Init(void);

#endif
#include "Driver_LED.h"

/**
 * @description: 对LED进行初始化
 */
void Driver_LED_Init(void)
{
    /* 1. 打开GPIOA的时钟 */
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;

    /* 2. 给用到的端口的所有 PIN (PA0 PA1 PA8) 设置工作模式: 通用推挽输出 MODE:11  CNF:00 */
    GPIOB->CRL |= (GPIO_CRL_MODE0 | GPIO_CRL_MODE1);
    GPIOB->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_CNF1);



    /* 3. 关闭所有灯  */
    Drviver_LED_Off(LED_1);
    Drviver_LED_Off(LED_2);
 
}

/**
 * @description: 点亮指定的LED
 * @param {uint32_t} led 要点亮的LED
 */
void Drviver_LED_On(uint32_t led)
{
    GPIOB->ODR &= ~led;
}

/**
 * @description: 关闭指定的LED
 * @param {uint32_t} led 要关闭的LED
 */
void Drviver_LED_Off(uint32_t led)
{
    GPIOB->ODR |= led;
}

/**
 * @description: 翻转LED的状态
 * @param {uint32_t} led 要翻转的LED
 */
void Drviver_LED_Toggle(uint32_t led)
{
    /* 1. 读取引脚的电平,如果是1(目前是关闭), 打开, 否则就关闭 */
    if ((GPIOB->IDR & led) == 0)
    {
        Drviver_LED_Off(led);
    }
    else
    {
        Drviver_LED_On(led);
    }
}
/**
 * @description: 打开数组中所有的灯
 * @param {uint32_t} leds 所有灯
 * @param {uint8_t} size 灯的个数
 */
void Drviver_LED_OnAll(uint32_t leds[], uint8_t size)
{

    for (uint8_t i = 0; i < size; i++)
    {
        Drviver_LED_On(leds[i]);
    }
}

/**
 * @description: 关闭数组中所有的灯
 * @param {uint32_t} leds 所有灯
 * @param {uint8_t} size 灯的个数
 */
void Drviver_LED_OffAll(uint32_t leds[], uint8_t size)
{
    for (uint8_t i = 0; i < size; i++)
    {
        Drviver_LED_Off(leds[i]);
    }
}
#ifndef __DRIVER_LED_H
#define __DRIVER_LED_H

#include "stm32f10x.h"

#define LED_1 GPIO_ODR_ODR0
#define LED_2 GPIO_ODR_ODR1


void Driver_LED_Init(void);

void Drviver_LED_On(uint32_t led);

void Drviver_LED_Off(uint32_t led);

void Drviver_LED_Toggle(uint32_t led);

void Drviver_LED_OnAll(uint32_t leds[], uint8_t size);

void Drviver_LED_OffAll(uint32_t leds[], uint8_t size);

#endif
#include "Delay.h" // Device header

void Delay_us(uint16_t us)
{
    /* 定时器重装值 */
    SysTick->LOAD = 72 * us;
    /* 清除当前计数值 */
    SysTick->VAL = 0;
    /*设置内部时钟源(2位->1),不需要中断(1位->0),并启动定时器(0位->1)*/
    SysTick->CTRL = 0x5;
    /*等待计数到0, 如果计数到0则16位会置为1*/
    while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG));
    /* 关闭定时器 */
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE; 
}

void Delay_ms(uint16_t ms)
{
    while (ms--)
    {
        Delay_us(1000);
    }
}

void Delay_s(uint16_t s)
{
    while (s--)
    {
        Delay_ms(1000);
    }
}
#ifndef __delay_h
#define __delay_h
#include "stm32f10x.h"                  // Device header

void Delay_us(uint16_t us);
void Delay_ms(uint16_t ms);
void Delay_s(uint16_t s);

#endif

3.1.2. 按键控灯(基于HAL库开发)

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == KEY0_Pin)
  {
      HAL_Delay(15);
  // 防抖: 延迟15ms之后再次检测是否仍然是高电平,
      if (HAL_GPIO_ReadPin(GPIOA, GPIO_Pin) == GPIO_PIN_SET)
    {
        HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
    }
  }
}

  • 28
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值