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("rnEXTI IRQHandler enter.rn");
//触发外部中断1
EXTI_GenerateSWInterrupt(EXTI_Line1);
printf("rnEXTI IRQHandler return.rn");
EXTI_ClearFlag(EXTI_Line0);
}
外部中断1中断服务函数 EXTI1_IRQHandler
void EXTI0_IRQHandler (void)
{
printf("rnEXTI1 IRQHandler enter.rn");
//触发外部中断2
EXTI_GenerateSWInterrupt(EXTI_Line2);
printf("rnEXTI1 IRQHandler return.rn");
EXTI_ClearFlag(EXTI_Line1);
}
外部中断2中断服务函数 EXTI2_IRQHandler
void EXTI0_IRQHandler (void)
{
printf("rnEXTI2 IRQHandler enter.rn");
printf("rnEXTI2 IRQHandler return.rn");
EXTI_ClearFlag(EXTI_Line2);
}
注意事项:
程序中EXTI0使用手动触发的方式,但EXTI1和EXTI2使用了软件触发的方式。
请一定要打开AFIO时钟。
虽然EXTI1和EXTI2使用了软件触发的方式,但是他们所对应的引脚设置以及中断触发方式的设置仍然是不可缺少的。
本程序中使用了优先级分组2,一共可以支持4个先占优先级,四个次占优先级。谨记,在设置设备中断优先级的时候,不要越出优先组所能分配的最大数量,否则将会产生不可预知的问题。
进入EXTI中断之后,要在其退出之前手动清除中断标志,否则中断会一直请求。
上一篇:STM32自学笔记(1.什么是STM32)
下一篇:STM32-自学笔记(14.NVIC和外部中断,程序用到的库函数介绍)
推荐阅读最新更新时间:2024-11-16 22:40
设计资源 培训 开发板 精华推荐
- 【涂鸦智能】遥控器
- 使用灌/源电压调节器创建虚拟接地
- 黄淮学院创客空间立创杯电子设计大赛-刘向强-1002040A
- MC33074ADR2G 有源带通滤波器运算放大器的典型应用
- 步进电机驱动板V2.0(5V)
- 使用 NXP Semiconductors 的 MPXV5050GP 的参考设计
- LTC3526BEDC 1 节电池至 2.85V 升压转换器的典型应用电路
- AD8626ARZ精密放大器用于8极Sallen-Key低通滤波器的典型应用电路
- 【涂鸦智能】多参数环境空气质量监测记录仪
- DC2425A-B,使用 LTC2310IMSE-16 16 位、2Msps 真差分输入串行 SAR ADC 的演示板
- 三星 Exynos 2600 芯片前景堪忧:良率挑战严峻,有被取消量产风险
- 苹果搁置反垄断报告的请求遭印度监管机构拒绝,案件将继续推进
- 2024年Automechanika Shanghai海量同期活动刷新历届记录,汇聚行业智慧,共谋未来发展
- 企业文化分享 如何培养稀缺的硅IP专业人员?SmartDV开启的个人成长与团队协作之旅
- 恩智浦发布首个超宽带无线电池管理系统解决方案
- 北交大本科生探秘泰克先进半导体开放实验室,亲历前沿高科技魅力
- 新帅上任:杜德森博士(Dr. Torsten Derr)将于2025年1月1日出任肖特集团首席执行官
- 边缘 AI 如何提升日常体验
- 苹果要首发!台积电宣布2nm已准备就绪
- AMD有望用上全新芯片堆叠技术:延迟大幅减少、性能显著提升