2021/10/14 智能家具 嵌入式实训 第四天 中断 (2)

 

外部中断概述

  STM32的每个IO都可以作为外部中断输入。
  STM32的中断控制器支持19个外部中断/事件请求
    线0~15:对应外部IO口的输入中断。
    线16:连接到PVD输出。
    线17:连接到RTC闹钟事件。
    线18:连接到USB唤醒事件。

  每个外部中断线可以独立的配置触发方式(上升沿,下降沿或者双边沿触发),触发/屏蔽,专用的状态位。

  

从上面可以看出,STM32供IO使用的中断线只有16个,但是STM32F10x系列的IO口多达上百个,STM32F103ZET6(112),
STM32F103RCT6(51),那么中断线怎么跟io口对应呢?

 

 

 

通过该图可知,每一个中断线都对应了7组中的对应编号的IO口(这边以ZET6:112个IO=16个 * 7组)。
    有如下关系:
      GPIOx.0映射到EXTI0
      GPIOx.1映射到EXTI1
      …
      GPIOx.15映射到EXTI15
注意:每个时间只能有一个I/O可映射到对应的中断线。

外部中断服务函数

是不是16个中断线就可以分配16个中断服务函数呢?
  IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数(下表少写了EXTI0)

 

 

 从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数外部中断线10 ~15分配一个中断向量,共用一个中断服务函数。

中断服务函数列表

  EXTI0_IRQHandler
  EXTI1_IRQHandler
  EXTI2_IRQHandler
  EXTI3_IRQHandler
  EXTI4_IRQHandler
  EXTI9_5_IRQHandler
  EXTI15_10_IRQHandler

外部中断设置步骤

  ①映射
  ②中断使能、触发模式等使能方式。
  ③编写中断服务函数

一般配置步骤:
  ①初始化IO口为输入。
    GPIO_Init();


  ② 开启IO口复用时钟。    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);


  ③设置IO口与中断线的映射关系。
    void GPIO_EXTILineConfig();


  ④初始化线上中断,设置触发条件等。
    EXTI_Init();


  ⑤配置中断分组(NVIC),并使能中断。
    NVIC_Init();


  ⑥编写中断服务函数。
    EXTIx_IRQHandler();


  ⑦清除中断标志位
    EXTI_ClearITPendingBit();

 

外部中断常用库函数

①void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
//设置IO口与中断线的映射关系
exp: GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);

②void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
//初始化中断线:触发方式等

③ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
//判断中断线中断状态,是否发生

④void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
//清除中断线上的中断标志位

 

* 接下来写外部中断的函数 exit.c

先把exit.h写完

#ifndef _EXTI_H_
#define _EXTI_H_

#include "stm32f10x.h"
#include "stdio.h"

#include "key.h"
#include "beep.h"

extern uint8_t key1_status;

/* 函数接口 */
void EXTI0_Config(void);

#endif

再写exit.c

①初始化IO口为输入。
    GPIO_Init();

就是使能

按键

可以直接引用 key,h led.h

    GPIO_InitTypeDef GPIO_InitStruce;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOC , ENABLE);
    GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPU;//上拉输入
    GPIO_InitStruce.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6;
    GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOE, &GPIO_InitStruce);
    
    GPIO_InitStruce.GPIO_Mode=GPIO_Mode_IPD;//下拉输入
    GPIO_InitStruce.GPIO_Pin=GPIO_Pin_0;
    GPIO_InitStruce.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruce);

>可省略

 

第二步:开启IO口复用时钟

增加:RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);

第三步:设置IO口与中断线的映射关系

该函数存在于:stm32f10x_gpio.h中
增加:GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);

看按键的连接口 这里是KEY1 PA0  映射线

//配置中断线和IO管脚的映射关系(PA0 -- 中断线0)

 

第四步:初始化线上中断,设置触发条件等。

该函数存在于:stm32f10x_exti.h中
增加:

    //2. 配置中断线
        EXTI_InitTypeDef EXTI_InitStructure;
        EXTI_InitStructure.EXTI_Line = EXTI_Line0;                      //中断线0
        EXTI_InitStructure.EXTI_LineCmd = ENABLE;                       //中断线使能
        EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;             //中断模式
        EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;         //下降沿触发
        EXTI_Init(&EXTI_InitStructure);

 

第五步:配置中断分组(NVIC),并使能中断。

该函数存在于:misc.h中
通道的参数是在stm32f10x,h中找到IRQn结尾的。
增加:

//3. 配置NVIC
        //NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);               //配置NVIC中断优先级组(一般写在主函数开始处)
                NVIC_InitTypeDef NVIC_InitStruct;                               
                NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;                                        //选择中断通道
                NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;                                        //中断通道使能
                NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;                    //占先优先级 0
                NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;                                    //次级优先级 0
        NVIC_Init(&NVIC_InitStruct);

 

 

这边要注意,在这同时要在main.c中先写下该函数NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置NVIC中断分组,2位抢占优先级,2位响应优先级,设置优先级后,这边填写的抢占优先级才有效。

 

 

下面写中断服务函数

编写中断服务函数&清除中断标志位

该函数在startup_stm32f10x_hd.s中:EXTI4_IRQHandler
故可在exti,c文件中编写一下程序:

先定义一下

uint8_t key1_status = 0; 

key1的初始值

 

基本的中断服务函数

void EXTI0_IRQHandler(void) //中断服务函数
{
  Delay_nop_nms(10);//延时10ms
    if(KEY1==0){ //如果KEY1按下
        BEEP_TOGGLE;
}
        EXTI_ClearITPendingBit(EXTI_Line0);  //清除中断标志位  

}

 

    void EXTI0_IRQHandler(void) //中断服务函数
{
            if(EXTI_GetITStatus(EXTI_Line0))      //判断中断是否发生(PR挂起寄存器)
            {
                EXTI_ClearITPendingBit(EXTI_Line0); //清除中断标志位
                if(KEY1)//按下
                    key1_status = 1;
                else//抬起
                {
                    BEEP_TOGGLE; 
                    //LED1_TOGGLE;
                    key1_status = 2;
                }
            }
        }

 

然后编写主函数

#include "main.h"

/***********************************************************************************
函数功能:main函数
形参:无
返回值:无
***********************************************************************************/
int main(void)
{
    Systick_Config(72000);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);               //配置NVIC中断优先级组
    LED_Config(ON); 
    Beep_Config(OFF);
    KEY_Config();
    EXTI0_Config();
    while(1)
    {       
        LED_Twinkle();
        if(key1_status==2)
        {
            key1_status=0;
            BEEP_TOGGLE;
        }
        
}
    }

就相当于key1按下 key1_status为1

抬起后    key1_status为2

这样

if(key1_status==2)
key1_status=0;

可以表示一个按下抬起的过程

 

 



 



posted @ 2021-10-16 21:31  halfup  阅读(106)  评论(1编辑  收藏  举报