STM32-自学笔记(13.NVIC和外部中断)

1.NVIC,嵌套中断向量控制器。(通俗点理解就是,许多中断向量交织在一起,形成一个向量网)

和SysTick定时器一样,NVIC属于ARM Cortex-M3内核的内部设备之一,与基于此内核的控制器并无直接联系,就是说任何一款基于ARM Cortex-M3内核的微控制器都带有NVIC.

作用:用来管理中断嵌套的,主要在于优先级的管理。嵌套是什莫?,先回忆一下中断的几个概念。

  • 中断响应:当某个中断来临,会将相应的中断标志位置位。当CPU查询到这个置位的标志位时,将响应此中断,并执行相应的中断服务函数。
  • 中断优先级:每个中断都具有其优先级,其相互之间的优先关系一般以优先级编号较小者拥有较高优先级。而大家容易忽略的是,优先级又分为两种:查询优先级和执行优先级。
  • 查询优先级和执行优先级:当某一时刻有两个或以上中断处于挂起状态,这首先执行执行优先级较高的中断。若执行优先级一致,则首先执行查询优先级较高的中断。查询优先级一般以该中断向量在中断向量表中的位置决定。
  • 中断嵌套:当某个执行优先级较低的中断服务在执行时另一个执行优先级较高的中断来临,则当前优先级较低的中断被打断,CPU转而执行较高优先级的中断服务。
  • 中断挂起:当某个执行较高优先级的中断服务在执行时,另一个优先级较低的中断来临,则因为优先级的关系,较低优先级中断无法立即响应,则进入挂起状态(等待执行)。

2.外部中断(EXTI),STM32的外部中断资源是非常丰富的,其每一个GPIO口都可以设置为一个EXTI通道。每个输入通道可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入通道都可以被独立的屏蔽。挂起寄存器会保持着某个通道的中断请求。

STM32一共为GPIO配备了19个中断通道,但其中只有16个是由用户自由支配的,分别为EXTI0~EXTI15通道,而EXTI16~EXTI18通道分配给了STM32的RTC、PVD以及USB使用。

  • 每个中断事件都有独立的触发位和屏蔽位
  • 每个中断通道都有专用的状态位
  • 支持多达19个中断源请求
  • 可以检测到脉冲宽度低于APB2时钟宽度的外部信号

实验设计:

配置3个STM32的外部中断,分别为EXTI0、EXTI1、EXTI2,并分别赋予他们从低到高的先占优先级。首先触发EXTI0中断,并在其中断服务返回之前触发EXTI1中断,同样在EXTI1中断返回之前触发EXTI2中断。按照此流程,程序应该发生了2次中断嵌套,并且在EXTI2中断服务完成之后,依EXTI2---EXTI1---EXTI0的次序进行中断返回。以上过程使用串口向上位机打印信息。

硬件电路:

一个按键和STM32微控制器相连接。还有也需要USART的电平转换电路。

软件设计(程序设计)

设计要点:这个实验重点涉及NVIC 和 EXTI的初始化工作

  • 配置RCC寄存器组,开启GPIOA和AFIO时钟。
  • 配置GPIOA.0、GPIOA.1和GPIOA.2位浮空输入模式,并将齐分别设置为外部中断EXTI0、EXTI1、EXTI2的输入通道。
  • 配置NVIC,使用优先级分组2,并赋予:EXTI0,2级先占优先级,0级次占优先级;EXTI1,1级先占优先级,0级次占优先级;EXTI2,0级先占优先级,0级次占优先级;
  • 开启EXTI0、EXTI1、EXTI2中断,并在下降沿时触发中断。
  • 配置USART。

主函数  main.c

#include"stm32f10x_lib.h"

#include"stdio.h"

void RCC_Configuration (void);

void GPIO_Configuration (void);

void NVIC_Configuration (void);

void EXTI_Configuration (void);

void USART_Configuration (void);

int main (void)

{

RCC_Configuration ();                  //设置系统时钟
 
GPIO_Configuration ();                 //设置GPIO端口

NVIC_Configuration ();                 //设置NVIC

EXTI_Configuration ();                 //设置EXTI

USART_Configuration ();                //设置USART

while(1);

}

设置系统各部分时钟  RCC_Configuration

void RCC_Configuration(void)

{

ErrorStatus HSEStartUpStatus;      //定义枚举类型变量 HSEStartUpStatus

RCC_DeInit();                     //复位系统时钟设置

RCC_HSEConfig(RCC_HSE_ON);         //开启HSE

HSEStatrtUpStatus=RCC_WaitForHSEStartUp();   //等待HSE起振并稳定

if(HSEStatrtUpStatus==SUCCESS)     //判断HSE是否起振成功,是则进入if()内部

{

RCC_HCLKConfig(RCC_SYSCLK_Div1);   //选择HCLK(AHB)时钟源为SYSCLK分频

RCC_PCLK2Config(RCC_HCLK_Div1);    //选择PCLK2时钟源为HCLK(AHB)1分频

RCC_PCLK1Config(RCC_HCLK_Div2);    //选择PCLK1时钟源为HCLK(AHB)2分频

FLASH_SetLatency(FLASH_Latency_2);  //设置Flash延时周期数为2

FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);   //使能Flash预取缓存

//选择PLL时钟源为 HSE 1 分频,倍频数为9,则PLL=8MHz *9=72MHz

RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);

RCC_PLLCmd(ENABLE);                  //使能PLL

while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);  //等待PLL输出稳定

RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);    //选择SYSCLK时钟源为PLL

while(RCC_GetSYSCLKSource()!=0x08);      //等待PLL成为SYSCLK时钟源

}

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);    //打开APB2总线上的GPIOA、USART1、AFIO时钟

}

设置各GPIO端口功能  GPIO_Configuration

void GPIO_Configuration(void)

{

GPIO_InitTypeDef GPIO_InitStructure;
//设置PA.0、PA.1、PA.2为浮空输入

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;

GPIO_Init(GPIOA,&GPIO_InitStructure);

//定义PA.0为外部中断0输入通道(EXTI)

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);

//定义PA.1为外部中断0输入通道(EXTI1)

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);

//定义PA.2为外部中断2输入通道(EXTI2)

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource2);

//设置USART1的Tx脚(PA.9)为第二功能推挽输出,最大翻转频率为50MHz

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;

GPIO_Init(GPIOA,&GPIO_InitStructure);

//设置USART1的Rx脚(PA.10)为浮空输入脚

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;

GPIO_Init(GPIOA,&GPIO_InitStructure);

}

设置NVIC参数   NVIC_Configuration

void NVIC_Configuration (void)

{

//定义NVIC初始化结构体NVIC_InitStructure

NVIC_InitTypeDef NVIC_InitSturcture;

//#ifdef、#else、#endif结构的作用是根据预编译条件决定中断向量表起始地址

#ifdef  

//中断向量表起始地址从0x20000000开始

NVIC_SetVectorTable(NVIC_VectTab_RAM,0x0);

#else

//中断向量表起始地址从0x80000000开始

NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x0);

#endif

//选择NVIC优先级分组2

NVIC_PriorotyGroupConfig(NVIC_PriorotyGroup_2);

//使能EXTI0通道,2级先占优先级,0级次占优先级

NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQChannel;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;

NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;

NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;

NVIC_Init(&NVIC_InitStructure);


//使能EXTI1通道,1级先占优先级,0级次占优先级

NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQChannel;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;

NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;

NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;

NVIC_Init(&NVIC_InitStructure);

//使能EXTI2通道,0级先占优先级,0级次占优先级

NVIC_InitStructure.NVIC_IRQChannel=EXTI2_IRQChannel;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;

NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;

NVIC_Init(&NVIC_InitStructure);

}

设置EXTI参数  EXTI_Configuration

void EXTI_Configuration (void)

{

//定义EXTI初始化结构体EXTI_InitStructure

EXTI_InitTypeDef EXTI_InitStructure;

//设置外部中断0、1、2通道在下降沿是触发中断

EXTI_InitStructure.EXTI_Line=EXTI_Line0 | EXTI_Line1 | EXTI_Line2;

EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;

EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;

EXTI_InitStructure.EXTI_LineCmd=ENABLE;

EXTI_Init(&EXTI_InitStructure);

}

设置USART1  USART_Configuration

void USART_Configuration(void)

{

USART_InitTypeDef USART_InitStructure;       //定义USART初始化结构体USART_InitStructure

USART_ClockInitTypeDef USART_ClockInitStructure;  //定义USART初始化结构体USART_ClockInitStructure

//波特率为9600bps;8位数据长度,1个停止位,无检验位;禁用硬件流控制;禁止USART时钟;时钟极性低;在第2个边沿捕获数据;最后一位数据的时钟脉冲不从SCLK输出

USART_InitStructure.USART_BaudRate=9600;

USART_InitStructure.USART_WordLength=USART_WordLength_8b;

USART_InitStructure.USART_StopBits=USART_StopBits_1;

USART_InitStructure.USART_Parity=USART_Parity_NO;

USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;

USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;

USART_Init(USART1,&USART_InitStructure);

USART_Cmd(USART1,ENABLE);         //使能USART1

}

将printf函数重定位到USART1   fputc

int fputc (int ch,FILE*f)

UASRT_SendData(USART1,(u8)ch);

while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);

return ch;

中断服务程序

头文件

#include "stm32f10x_it.h"

#include"stdio.h"

外部中断0中断服务函数       EXTI0_IRQHandler

void EXTI0_IRQHandler (void)

{

printf("\r\nEXTI IRQHandler enter.\r\n");

//触发外部中断1

EXTI_GenerateSWInterrupt(EXTI_Line1);

printf("\r\nEXTI IRQHandler return.\r\n");

EXTI_ClearFlag(EXTI_Line0);

}

外部中断1中断服务函数      EXTI1_IRQHandler

void EXTI0_IRQHandler (void)

{

printf("\r\nEXTI1 IRQHandler enter.\r\n");

//触发外部中断2

EXTI_GenerateSWInterrupt(EXTI_Line2);

printf("\r\nEXTI1 IRQHandler return.\r\n");

EXTI_ClearFlag(EXTI_Line1);

}

外部中断2中断服务函数      EXTI2_IRQHandler

void EXTI0_IRQHandler (void)

{

printf("\r\nEXTI2 IRQHandler enter.\r\n");

printf("\r\nEXTI2 IRQHandler return.\r\n");

EXTI_ClearFlag(EXTI_Line2);

}

注意事项

  1. 程序中EXTI0使用手动触发的方式,但EXTI1和EXTI2使用了软件触发的方式。
  2. 请一定要打开AFIO时钟。
  3. 虽然EXTI1和EXTI2使用了软件触发的方式,但是他们所对应的引脚设置以及中断触发方式的设置仍然是不可缺少的。
  4. 本程序中使用了优先级分组2,一共可以支持4个先占优先级,四个次占优先级。谨记,在设置设备中断优先级的时候,不要越出优先组所能分配的最大数量,否则将会产生不可预知的问题。
  5. 进入EXTI中断之后,要在其退出之前手动清除中断标志,否则中断会一直请求。

下一篇写本篇用到的库函数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值