STM32F103C8芯片学习心得分享:外部中断介绍与软件编写(基于标准库开发固件函数库)
目录
【1】外部中断
一.GPIO外设的初始化
二.AFIO中断引脚的设置
三.EXTI的配置
1.EXTI_DeInit();
2.EXTI_Init()
【外设的初始化和设置】
3.EXTI_GenerateSWInterrupt()
4.EXTI_GetFlagStatus()
5.EXTI_ClearFlag()
6.EXTI_GetITStatus()
【EXTI_GetFlagStatus和EXTI_GetITStatus的区别】
7.EXTI_ClearITPendingBit()
8.软件代码配置
四、NVIC的配置
【NVIC的分组】
【抢占优先级和响应优先级的介绍】
1.NVIC_PrioritygRoupConfig()
2.NVIC_Init()
3.软件编写
1.先进行优先级分组
2.定义结构体变量
3.引出成员变量
第一个成员变量:
第二个成员变量:
第三个成员变量:
第四个成员变量:
4.NVIC初始化
5.中断函数
【2】实战练习
【1】外部中断
外部中断就是芯片识别外部的信号,进行判断,如果满足自己设置的中断条件就会自动跳转到中断
函数内部执行函数里面的内容。因为平时我们的程序都放在while下的,执行主要内容,但有时我
们有一些紧急的情况需要处理,这时候中断就会打断主程序先把紧急的事情干完了,再干自己的正常工作;
一.GPIO外设的初始化
初始化还是跟上节的初始化流程是一样的,如果忘记了可以看一下上一节,注意中断是检测的外部
信号脉冲,所以记得把这里的GPIO_Mode改成输入模式,输入模式有三种,忘记了的同学可以看
一下上一节,代码举例如下:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
二.AFIO中断引脚的设置
当然AFIO作为挂载在APB2上的外设第一步就是打开时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
STM32的外部中断的第二步就是中断引脚的设置,AFIO给出了16个通道给中断,而且就是对应
Pin0~15的端口;那么只给出了这么多的通道,而且有GPIOA或GPIOB或GPIOX等这么多外设容
易冲突,那么就有个规则:比如,当GPIOA_Pin_1即GPIOA已经占了通道1(通道几对应它的端口
几),通道1就已经用完了,那么这些GPIOB或GPIOC啥的GPIOX它们的Pin_1即它们的端口1就
不能在选择这个通道作为中断,只能用除了1之外的通道;也可以这么理解,相当于给了16条通道
线,每条通道线,对应连接端口0~15即Pin_X(0~15),但是每条线只能选择一个GPIO类型的外设。
这个函数就是用来配置这个AFIO的中断线的,虽然它的名字里面没有AFIO,但其实函数内部是操
作AFIO的,第一个参数就是GPIO_PortSourceGPIOx(x:A~G);–用来选择用哪一个GPIO外设
第二个参数就是GPIO_PinSourcex(x:0~15); –用来选择哪一个中断线
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);
三.EXTI的配置
首先这里先介绍一下有关EXTI(外部中断事件控制器)的库函数:
1.EXTI_DeInit();
这个函数说将EXTI外设的配置重新配置为缺省值,那么缺省值是什么呢,缺省值其实就是官方人
员配置的默认值,这个默认值呢,就是刚开始上电时的值,那么这个函数其实就是起到了一个清除
配置的作用;
2.EXTI_Init()
这个函数就是对EXTI外设的初始化,跟GPIO_Init有异曲同工之妙;
其实,看到这里会发现,STM32的外设的函数都有个惯用套路:
【外设的初始化和设置】
1.打开时钟XXX_Cmd
2.声明一个结构体变量:XXX_InitTypeDef 变量;
3.为结构体变量的成员变量填充值;
4.调用数 XXX_Init()来初始化外设
注:EXTI和NVIC的时钟是一直开启的,就不需要手动开启了
3.EXTI_GenerateSWInterrupt()
这个函数是用来程序触发中断的特殊类型中断的函数,比如你写了一些代码,在这个地方需要它进入中断,就用这个函数直接生成软件中断;
4.EXTI_GetFlagStatus()
当外部中断线已经达成了触发条件,那么这个EXTI的线路标志位就会被置1,那么这个函数就可以直接获取这个线路标志位的状态;
5.EXTI_ClearFlag()
外部中断的标志位需要手动清0,那么该函数就是用来清除外部中断触发中断条件标志位;
6.EXTI_GetITStatus()
【EXTI_GetFlagStatus和EXTI_GetITStatus的区别】
这个函数也是用来读取中断标志位的,区别于EXTI_GetFlagStatus(),它多了一个判断是否被特定
的寄存器进行了中断屏蔽,也就是说,当触发了中断条件时,这时单纯是满足条件而将标志位置
1,如果被特定寄存器进行中断屏蔽了,就是单纯的置位而不响应中断,那么EXTI就是单纯读取标
志位,而EXTI_GetITStatus还会判断是否被中断屏蔽,也就是这个函数进行读取中断是否响应时的
标志位,也就是读取中断状态;
7.EXTI_ClearITPendingBit()
这个函数就是对应EXTI_GetITStatus(),状态标志位的清除了
8.软件代码配置
如同上面介绍的外设的基本套路的配置这里也是这样做的
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line1;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
EXTI_Init(&EXTI_InitStructure);
这里解释一下上面的代码含义
1. EXTI_InitTypeDef EXTI_InitStructure; 这个很熟悉了,就是结构体变量的定义
2.第一个成员变量:
EXTI_Line这个是指的是你选择的哪一条中断线,
参数:EXTI_LineX(X:0~19),这个中断线就是我们上面AFIO选择的那一条中断线,这里同样也可
以进行或运算,选中多条中断线;当然这里总共二十条中断线,有四条特殊的中断线
3.第二个成员变量:
EXTI_LineCmd这个指的是否开启你选中的中断的线
参数:ENABLE或DISABLE
4.第三个成员变量:
EXTI_Mode指的是你要选择的中断方式,这里分别是中断方式和事件方式;中断方式就是字面意
思,选择触发中断进入中断函数;事件方式指的是就是当检测外部的信号时,此时的动作是触发某
个事件,比如说是当检测到外部信号后,信号是流入其他外设,让那些外设工作;
参数:EXTI_Mode_Interrupt(中断)或EXTI_Mode_Event(事件)
5.第四个成员变量:
EXTI_Trigger指的是触发方式,这里有三种触发方式分别是上升沿或下降沿或上升沿和下降沿触发
上升沿和下降沿触发自然是上升沿触发了,下降沿也要触发了
参数:EXTI_Trigger_Rising(上升)
EXTI_Trigger_Falling(下降)
EXTI_Trigger_Rising_Falling(上升沿和下降沿)
6.结构变量初始化EXTI_Init();
四、NVIC的配置
NVIC:嵌套中断向量列表控制器;
【NVIC的分组】
NVIC是用来设置中断优先级的,毕竟有这么多中断线输入进来,不可能同时发生,这时就要用中
断优先级给它们排个队,那么这里的优先级又分为抢占优先级和响应优先级,首先这里就给出了几
种排队分组的方式,你要先选择一种排队分组的方式,才能进行设置优先级去排列的先后顺序
【抢占优先级和响应优先级的介绍】
数值越小的优先级越高,这里从三种情况来解释抢占优先级(先占优先级)和响应优先级(从优先
级);比如,甲程序的抢占优先级和乙程序的抢占优先级相同时,当甲程序比乙程序的响应优先级
高,那么当甲和乙的中断程序同时到来时,系统会先响应甲的中断;当甲程序的响应优先级和乙程
序的响应优先级相同时或不同时,甲程序的抢占优先级比乙的抢占优先级高,那么这时候如果乙进
入了中断程序就会被甲的程序给打断,执行完甲的再回头来执行乙的;假如抢占优先级和响应优先
级都相同就按照系统排列的中断号进行排队,数字越小,中断优先级越高;
这里的第二列的数字优先级就是当抢占优先级和响应优先级相同时,这个数字越小,中断优先级越高;
这里先了解一些有关NVIC的有关函数:
1.NVIC_PrioritygRoupConfig()
这个函数就是首先给优先级进行分组的,上面介绍了有5组的形式可以选,值得注意的是你写程序的时候,只能给中断分一次组,然后写的这些中断都是根据这个组来写优先级,不能每写一个中断就分一次组;
2.NVIC_Init()
这个函数就是经典的配置外设初始化的函数了,这里NVIC的初始化就用这个来配置
3.软件编写
1.先进行优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //这里选择分组方式0
2.定义结构体变量
NVIC_InitTypeDef NVIC_InitStructure;
3.引出成员变量
第一个成员变量:
NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn;
这个指的是中断通道,这个该怎么选呢,根据上一节的双击右键跳转定义再全局搜索
再根据自己芯片的型号,上一节介绍选择启动文件那里,比如我用的芯片配对的是后缀为MD的文
件可以找到EXTI的通道有两个,EXTI9_5_IRQn和EXTI15_10_IRQn,选择一个;
第二个成员变量:
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
这个指的是要不要开启这个中断通道,参数就是ENABLE或者DISABLE
第三个成员变量:
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
这个就是抢占优先级,根据那个分组里的范围自行选择,注意分完组后,抢占优先级和响应优先级
就有取值范围了
第四个成员变量:
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
这个就是响应优先级
4.NVIC初始化
NVIC_Init(&NVIC_InitStructure);
这个就是将NVIC按照自己的配置初始化了
5.中断函数
当上面都配置好了,接下来就等外部信号触发后,执行中断函数里的内容了,那么这里中断函数号也是有特定的名称的,你的特定通道是有对应的中断函数名的,可以在启动文件(启动文件在上一节有介绍)里面找
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line6)==SET) //当检测到中断标志位置位了,也就是进入中断了
{
EXTI_ClearITPendingBit(EXTI_Line6); //这里必须要手动将标志位清0
}
}
【2】实战练习
按一下按键来改变led的亮暗状态
我这里分模块写,就不都放在main函数这一页了
init.c:
#include "stm32f10x.h" // Device header
uint16_t count;
void init_intial(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef InitStructure;
InitStructure.GPIO_Mode=GPIO_Mode_IPU; //这里采用上拉输入
InitStructure.GPIO_Pin=GPIO_Pin_6;
InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6); //选择中断通道6
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line6;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //这里只有一个中断函数可以虽然设置优先级
NVIC_InitTypeDef NVIC_initStruct;
NVIC_initStruct.NVIC_IRQChannel=EXTI9_5_IRQn;
NVIC_initStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_initStruct.NVIC_IRQChannelPreemptionPriority=1;
NVIC_initStruct.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_initStruct);
}
uint16_t return_num(void)
{
return count; //将计数标志位返回
}
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line6)==SET)
{
count++; //设置一个计数标志位,给led做判断使用
EXTI_ClearITPendingBit(EXTI_Line6);
}
}
init.h
#ifndef __INIT_H
#define __INIT_H
void init_intial(void);
uint16_t return_num(void);
#endif
led.c
#include "stm32f10x.h" // Device header
void led_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef InitStucture;
InitStucture.GPIO_Mode=GPIO_Mode_Out_PP;
InitStucture.GPIO_Pin=GPIO_Pin_0;
InitStucture.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&InitStucture);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
}
led.h
#ifndef __LED_H
#define __LED_H
void led_init(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "init.h"
#include "led.h"
uint16_t num;
int main()
{
init_intial(); //中断函数的初始化
led_init(); //led的初始化
while(1)
{
num=return_num(); //读取进入中断的计数标志位
if(num%2==1)
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
else
GPIO_SetBits(GPIOA,GPIO_Pin_0);
}
}
proteus仿真:
STM32中断仿真测试
作者:星空客