STM32学习笔记四(基于标准库学习)

一、EXTI中断

STM32有多个可屏蔽中断通道(中断源):

包含EXTI(外部中断)、TIM、ADC(模数转换器)、USART(串口)、SPI、I2C、RTC(实时时钟)等多个外设。(几乎所有模块都能申请中断)

STM32使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级。

NVIC就是STM32中用来管理中断、分配优先级的,NVIC的中断优先级共有16个等级

二、NVIC基本结构

NVIC(嵌套中断向量控制器),在stm32中,它是用来统一分配中断优先级和管理中断的,NVIC是一个内核外设,是CPU的小助手(如果把中断全接到cpu上,会很麻烦,毕竟CPU主要是用来运算的)

NVIC有很多输入口,下图中线上划了个斜杠上面写了n(这个意思是:一个外设可能会同时占用多个中断通道,所以这里有n条线),然后NVIC只有一个输出口,NVIC根据每个中断的优先级分配中断的先后顺序,之后通过右边这一输出口就告诉CPU该处理哪个中断,对于中断先后顺序分配的任务,CPU不需要知道

举个例子:

比如CPU是医生,如果医院只有一个医生时,当看病人(EXTI、TIM、ADC等就是各类病人)很多时,医生就得先安排一下先看谁后看谁

如果有紧急的病人,那还得让紧急的病人最先来,这个安排先后顺序的任务很繁琐会影响医生看病的效率,所以医院就安排了一个叫号系统(NVIC)

来病人了统一取号并且根据病人的等级,分配一个优先级,然后叫号系统看一下现在在排队的病人,优先叫号紧急的病人,最后叫号系统给医生输出的就是一个一个排好队的病人,医生就可以专心看病了。

三、NVIC优先级分组

为了处理不同形式的优先级,STM32的NVIC可以对优先级进行分组,分为抢占优先级和响应优先级,抢占优先级和响应优先级的区别:

例子理解:

一种是,病人1在看病,外面排队中的病人2比病人1更加紧急,病人2可以不等病人1看完直接冲到医生的屋里,病人1先等待,先给病人2看病,病人2看完病接着病人1看病,然后外面排队的病人再进来,这种形式的优先级就是中断嵌套,这种决定是不是可以中断嵌套的优先级,就叫抢占优先级,抢占优先级高的,可以进行中断嵌套

另一种是病人叫号的例子,对于紧急的病人,其实有两种形式的优先。一种是,病人1在看病,外面排队了很多病人,当病人1看完后,外面排队中病人最先进去看病即使这个病人是最后来的,这种在排队中的插队的就叫响应优先级,响应优先级高的可以插队提前看病。

为了将优先级区分为抢占优先级和响应优先级,就需要对这16个优先级优先级进行分组,NVIC的中断优先级由优先级寄存器的4位(0~15,4位二进制,对应16个优先级)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级

因为优先级总共是4位,所以就有(0,4)、(1.3)、(2,2)、(3,1)、(4、0)这五种分组方式,分组0,就是0位的抢占等级,取值为0,4位的响应等级,取值为0~15,分组1234雷同。这个分组方式是我们在程序中自己进行选择的,选好分组方式后,就要注意抢占优先级和响应优先级的取值范围了,不要超出这个表里规定的取值范围。

优先级数字越小,优先级越高

分组方式

抢占优先级NVIC_IRQChannelPreemptionPriority

响应优先级NVIC_IRQChannelSubPriority备注

分组0

0位,取值为0

4位,取值为0~15

0bit抢占优先级、4bit响应优先级

分组1

1位,取值为0~1

3位,取值为0~7

1bit抢占优先级、3bit响应优先级

分组2

2位,取值为0~3

2位,取值为0~3

2bit抢占优先级、2bit响应优先级

分组3

3位,取值为0~7

1位,取值为0~1

3bit抢占优先级、1bit响应优先级

分组4

4位,取值为0~15

0位,取值为0

4bit抢占优先级、0bit响应优先级

四、实现EXTI外部中断

EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序。(简单说:引脚电平变化,申请中断,进入中断程序)

支持的触发方式(引脚电平的变化类型)

1.上升沿(电平从低电平变到高电平的瞬间触发中断)

EXTI_Trigger_Rising 

2.下降沿(电平从高电平变到低电平的瞬间触发中断)

EXTI_Trigger_Falling

3.双边沿(上升沿和下降沿都可以触发中断)

EXTI_Trigger_Rising_Fulling 

4.软件触发(程序执行代码就能触发中断)

外部中断模式

1.中断模式:AFIO调用EXTI的0-15中断线对GPIO的电平变化进行中断判断

EXTI_Mode_Interrupt

2.事件模式:不会进入中断嵌套程序,会进行事件判断

EXTI线16连接到PVD输出

EXTI线17连接到RTC闹钟事件

EXTI线18连接到USB唤醒事件

●EXTI线19连接到以太网唤醒事件(只适用于互联型产品)

EXTI_Mode_Event 

五、EXTI中断常用函数

EXTI初始化函数
//建立结构体
EXTI_InitTypeDef EXTI_InitStructure;

//配置结构体
EXTI_InitStructure.EXTI_Line= EXTI_LineXX;  //指定中断线  XX=0-15;
EXTI_InitStructure.EXTI_LineCmd= ENABLE/DISABLE; //使能中断 
EXTI_InitStructure.EXTI_Mode= EXTI_Mode_Interrput/EXTI_Mode_event;  //指定外部中断的模式;
EXTI_InitStructure.EXTI_Trigger= 指定触发信号的有效边沿; //触发模式,参考上文
EXTI_Init(&EXTI_InitStructure) //调用创建的结构体地址
EXTI配置清除函数

void EXTI_DeInit(void);

EXTI恢复缺省值(默认值)函数

void EXTI_StructInit(EXTI_InitTypeDef *EXTI_InitStructure;)

EXTI_Line= EXTI_LineNone; 
EXTI_InitStructure.EXTI_LineCmd= EDISABLE; 
EXTI_InitStructure.EXTI_Mode= EXTI_Mode_Interrput;  
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI软件中断函数

用于软件触发中断,与中断/事件模式不同,不需要外部电平触发就可以使EXTI_Line置位

EXTI_GenerateSWInterrupt(u32 EXTI_LineX)X=0~15
EXTI线路标志位设置与否

用于检测线路的配置是否设置完成

FlagStatus EXTI_GetFlagStatus(u32 EXTI_LineX);  X=0~15
//返回值为 SET/RESET
EXTI清除线路标志位的返回值

使用FlagStatus EXTI_GetFlagStatus(u32 EXTI_Line)后标志位会有返回值

该函数用于清除返回值

void EXTI_ClearFlag(u32 EXTI_LineX)
EXTI_ClearFlag(EXTI_LineX)
  X=0~15
EXTI中断触发标志位

EXTI线路触发中断的时候标志位置1,函数用于检测该标志位是否置1

无返回值,常配合IF函数使用

ITStatus EXTI_GetITStatus(u32 EXTI_Line X)  
EXTI_GetITStatus(EXTI_Line X) ;
 X=0~15
清除EXTI中断触发标志位

EXTI线路中断后标志位不会自动复位,用该函数使标志位复位

void EXTI_ClearITPendingBit(u32 EXTI_Line X) 
EXTI_ClearITPendingBit( EXTI_Line X);
X=0~15

六、NVIC常用函数

优先级分组设定

可考虑第三大点的“NVIC优先级分组”的信息

分组方式,整个芯片只能用一种。如放在模块中进行分组,要确保每个模块分组都选的是同一个;或者将这个代码放在主函数的最开始

整个系统执行过程中,只设置一次中断分组。(NVIC_PriorityGroupConfig只是对一个寄存器操作。多次操作以最后一次设置为准。)

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_X); X=0\1\2\3\4

NVIC初始化函数

1.NVIC_InitStructure.NVIC_IRQChannel

选择的通IRQ通道

2.NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority

选择IRQ通道的抢占优先级

3.NVIC_InitStructure.NVIC_IRQChannelSubPriority

选择IRQ通道的响应优先级

针对每个中断,设置对应的抢占优先级和响应优先级。

如果不设置中断优先级分组,则中断优先级分组默认为0,即0位抢占优先级,4位响应优先级。

4.NVIC_InitStructure.NVIC_IRQChannelCmd

选择IRQ通道的使能位

初始化函数示例:

    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_X); X=0\1\2\3\4
    NVIC_InitTypeDef NVIC_InitStructure;

	NVIC_InitStructure.NVIC_IRQChannel = XXXXXXXX;
    //视分组规划参数范围,可参考NVIC优先级分组
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = X; 
    //视分组规划参数范围,可参考NVIC优先级分组
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =X ;

	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE\DISABLE;
	NVIC_Init(&NVIC_InitStructure);	

七、使用实际案例

对射式红外传感器中断测试

当我们的挡光片或者编码盘在对射式红外传感器中间经过时,DO就会输出电平跳变的信号,电平跳变的信号触发STM32 PB14号口的中断,在中断函数中,执行变量++的程序,然后主循环里用OLED显示这个变量

main.c

#include "stm32f10x.h" 
#include "Delay.h"
#include "OLED.h"
#inclued "red-countsensor.h"

int main(void)
{
  red-countsensor(); //红外对射传感器初始化
  OLED_Init(); //OLED初始化
  OLED_ShowString(1,2,"Count:");//第一行第三列开始显示字符串


	while(1)
	{
		OLED_ShowNum(1,8,red_countsersor_get(),5);//显示red_countsersor_get的返回值,长度为5
	}

}

red-countsensor.h

#ifndef __RED_COUNTSENSOR_H
#define __RED_COUNTSENSOR_H


void red_countsensor_Init(void) ; //红外传感器初始化
uint16_t red_countsersor_get(void); //红外传感器中断读数





#endif


red-countsensor.c

#include "stm32f10x.h"                  // Device header

uint16_t red_countsensor_num;

/***
AFIO GPIO初始化函数
***/

void red_countsensor_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//GPIO时钟口打开
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //AFIO时钟口初始化
    //需要开启AFIO时钟后才可进入EXTI和NVIC的中断执行层且EXTI NCIC的时钟为常开

    //配置GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 ;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//设置AFIO调用口,接收GPIO的端口
	//表示GPIOB的14端口在EXTI的第14中断线口
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
	
	//配置AFIO,接受GPIO的中断需求
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;  //中断模式
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;  //选择第14个中断线口
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;  //中断打开
	EXTI_Init(&EXTI_InitStructure);
	
	//配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //分组方式,整个芯片只能用一种。如放在模块中进行分组,要确保每个模块分组都选的是同一个;或者将这个代码放在主函数的最开始
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//因为EXTI选择的是GPIO14对应的Line14,所以这里选择EXTI15_10IRQn的IRQ中断通道,具体可参考NVIC_InitStructure.NVIC_IRQChannel的中断通道选择
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);	
	
		
	
}


//STM32中断函数无参无返回值,有标志位,不需要声明自动执行
	//因为EXTI选择的是GPIO14对应的Line14,所以这里选择EXTI15_10的IRQ中断通道,具体可参考NVIC_InitStructure.NVIC_IRQChannel的中断通道选择
	void EXTI15_10_IRQHandler()
	{
     //中断触发
       if(EXTI_GetFlagStatus(EXTI_Line14) == SET)
     { 
		 red_countsensor_num++;  //全局变量增加
		 EXTI_ClearITPendingBit(EXTI_Line14); //中断触发标志位复位
     }


       }
	
  uint16_t red_countsersor_get(void)
{
	return red_countsensor_num;
 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值