历史上的今天

今天是:2024年11月12日(星期二)

2021年11月12日 | STM32 NVIC 中断

发布者:EternalBliss 来源: eefocus关键字:STM32  NVIC  中断 手机看文章 扫描二维码
随时随地手机看文章

***************************** STM32 NVIC 中断


一、STM32 的中断分组:STM32 将中断分为 5 个组,组 0~4。该分组的设

置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。具体的分配关系如表所示:

组…,…,. bit[7:4]分配情况 …,…,…,…分配结果

0 …,…,…,…,…,…0:4 …,…,…,…0 位抢占优先级,4 位响应优先级

1 …,…,…,…,…,…1:3 …,…,…,…1 位抢占优先级,3 位响应优先级

2 …,…,…,…,…,…2:2 …,…,…,…2 位抢占优先级,2 位响应优先级

3…,…,…,…,…,…3:1…,…,…,… ,3 位抢占优先级,1 位响应优先级

4…,…,…,…,…,…4:0…,…,…,… , 4位抢占优先级,0 位响应优先级

…,…,…,…,…,…,…,…,表 . AIRCR 中断分组设置表


例如组设置为 3,那么此时所有的 60 个中断,每个中断的中断优先寄存器的高四位中的最高 3 位是抢占优先级,低 1 位是响应优先级。每个中断,你可以设置抢占优先级为 0~7,响应优先级为 1 或 0。抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。


这里需要注意两点:第一,如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;第二,高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断。


二、如何使用库函数实现以上中断分组设置以及中断优先级管理

NVIC 中断管理函数主要在 misc.c 文件里面。

(1).首先是中断优先级分组函数 :void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);这个函数的作用是对中断的优先级进行分组,这个函数在系统中只能被调用一次,一旦分组确定就最好不要更改。(这个函数是放在主函数里的)


int main(void) {

delay_init();    

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //设置NVIC优先级分组为2

uart_init(115200);

  LED_Init();

TIM3_Int_Init(4999,7199);

    while(1){

LED0=!LED0;

delay_ms(200);    

}  

}  


(2)设置好了系统中断分组,下面设置中断初始化函数 NVIC_Init。

NVIC_InitTypeDef 结构体中间的三个成员变量的作用是:


1) NVIC_IRQChannel:定义初始化的是哪个中断,这个我们可以在 stm32f10x.h 中找到每个中断对应的名字。例如 USART1_IRQn、 TIM3_IRQn、EXTI9_5_IRQn、I2C1_EV_IRQn、SPI1_IRQn等等(还有好多)。


2) NVIC_IRQChannelPreemptionPriority:定义这个中断的抢占优先级别。

3) NVIC_IRQChannelSubPriority:定义这个中断的子优先级别。

4) NVIC_IRQChannelCmd:该中断是否使能。


三、 STM32 外部中断

(一)、重要概念

(1) *代码主要分布在固件库的 stm32f10x_exti.h 和 stm32f10x_exti.c 文件中。

STM32F103 的19 个外部中断为:

线 0~15:对应外部 IO 口的输入中断。

线 16:连接到 PVD 输出。

线 17:连接到 RTC 闹钟事件。

线 18:连接到 USB 唤醒事件。


(2)STM32 是怎么把 16 个中断线和 IO 口一一对应起来的呢?

(这个概念相当重要)

{STM32 就这样设计,GPIO 的管脚 GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G)分别对应中断线 0~15。这样每个中断线对应了最多 7 个 IO 口,以线 0 为例:它对应了 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0。而中断线每次只能连接到 1 个 IO 口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。}


(二)、程序编写

(1)主要步骤:

1)初始化 GPIO 口;

2)开启 AFIO 时钟;

3)设置好中断线和 GPIO 映射关系,配置 GPIO 与中断线的映射关系的函数 GPIO_EXTILineConfig();

4)设置好了中断的触发模式等初始化参数,中断线上中断的初始化是通过函数 EXTI_Init();

5)设置 NVIC 中断优先级,并使能中断;

6)编写中断服务函数。(这里需要说明一下,STM32 的 IO 口外部中断函数只有 6 个,分别为:

EXPORT EXTI0_IRQHandler

EXPORT EXTI1_IRQHandler

EXPORT EXTI2_IRQHandler

EXPORT EXTI3_IRQHandler

EXPORT EXTI4_IRQHandler

EXPORT EXTI9_5_IRQHandler

EXPORT EXTI15_10_IRQHandler

中断线 0-4 每个中断线对应一个中断函数,中断线 5-9 共用中断函数 EXTI9_5_IRQHandler,中断线 10-15 共用中断函数 EXTI15_10_IRQHandler。)


(2)中断服务函数会经常使用到两个函数:

第一个函数是判断某个中断线上的中断是否发生(标志位是否置位):ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);这个函数一般使用在中断服务函数的开头判断中断是否发生。

另一个函数是清除某个中断线上的中断标志位:void EXTI_ClearITPendingBit(uint32_t EXTI_Line);这个函数一般应用在中断服务函数结束之前,清除中断标志位。


void KEY_Init(void){         //GPIO初始化程序

  GPIO_InitTypeDef GPIO_InitStructure;

 

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);


GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_2;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 

  GPIO_Init(GPIOE, &GPIO_InitStructure);

}


//外部中断0服务程序

void EXTIX_Init(void){

  EXTI_InitTypeDef EXTI_InitStructure;

  NVIC_InitTypeDef NVIC_InitStructure;

    // 1)初始化 GPIO 口

    KEY_Init();  

    

    //  2)开启 AFIO 时钟;使能复用功能时钟

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);


    //  3)将中断线 2 与 GPIOE 映射起来,那么很显然是 GPIOE.2 与 EXTI2 中断线连接了 

  GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);


    //  4)设置好了中断的   触发模式   等初始化参数

  EXTI_InitStructure.EXTI_Line=EXTI_Line2; //第一个参数是中断线的标号,取值范围0~15

  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//第二个参数是中断模式,可选值为中断 EXTI_Mode_Interrupt 和事件 EXTI_Mode_Event

  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//第三个参数是触发方式,可以是下降沿触发 EXTI_Trigger_Falling,上升沿触发 EXTI_Trigger_Rising,或者任意电平(上升沿和下降沿)触发EXTI_Trigger_Rising_Falling

  EXTI_InitStructure.EXTI_LineCmd = ENABLE;

  EXTI_Init(&EXTI_InitStructure);  


    //  5)设置 NVIC 中断优先级,并使能中断

  NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;

  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;

  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;

  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&NVIC_InitStructure); 

}



//6)编写中断2服务函数

void EXTI2_IRQHandler(void){

delay_ms(10); //按键 消抖

if(KEY2==0){   //第一个函数,按下按键   即判断某个中断线上的中断是否发生,也可用函数:ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)

LED0=!LED0;

}  

EXTI_ClearITPendingBit(EXTI_Line2);  //另一个函数是  清除某个中断线上的中断标志位

}


//note:具体源码,在正点原子,实验5  外部中断实验,那边一下设置了好几个外部中断


四、 STM32 定时器中断

(一)、重要概念

(1)、STM32F1 的定时器功能十分强大,有 TIME1 和 TIME8 等高级定时器,也有 TIME2~TIME5 等通用定时器,还有 TIME6和 TIME7 等基本定时器。

(2)、STM32 通用定时器简介

STM32F1 的通用定时器是一个通过可编程预分频器(PSC)驱动的 16 位自动装载计数器(CNT)构成。可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。 使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。


(3)、STM3F1 的通用 TIMx (TIM2、TIM3、TIM4 和 TIM5)定时器功能包括:

1)16 位向上、向下、向上/向下自动装载计数器(TIMx_CNT)。

2)16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为 1~65535 之间的任意数值。

3)4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:

A.输入捕获;

B.输出比较;

C.PWM 生成(边缘或中间对齐模式) ;

D.单脉冲模式输出。

4)可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。

5)如下事件发生时能产生中断/DMA:

A.更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)

B.触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)

C.输入捕获

D.输出比较

E.支持针对定位的增量(正交)编码器和霍尔传感器电路

F.触发输入作为外部时钟或者按周期的电流管理


(4)、通用定时器的寄存器

1)首先是控制寄存器 1(TIMx_CR1),TIMx_CR1 的最低位,也就是计数器使能位,该位必须置 1,才能让定时器开始计数。

2)第二个与我们这 章密切相关的寄存器:DMA/中断使能寄存器(TIMx_DIER)。同样仅关心它的第 0 位,该位是更新中断允许位,本章用到的是定时器的更新中断,所以该位要设置为 1,来允许由于更新事件所产生的中断。

3)第三个与我们这章有关的寄存器:预分频寄存器(TIMx_PSC)。该寄存器用

设置对时钟进行分频,然后提供给计数器,作为计数器的时钟。

4)最后,要介绍的寄存器是:状态寄存器(TIMx_SR)。该寄存器用来标记当前与定时器相关的各种事件/中断是否发生。

顺带介绍一下

5)TIMx_CNT 寄存器,该寄存器是定时器的计数器,该寄存器存储了当前

定时器的计数值。

6)自动重装载寄存器(TIMx_ARR)。根据 TIMx_CR1 寄存器中 APRE 位的设置:APRE=0 时,预装载寄存器的内容可以随时传送到影子寄存器,此时 2者是连通的;而 APRE=1 时,在每一次更新事件(UEV)时,才把预装在寄存器的内容传送到影子寄存器。


(5)、定时器的时钟来源有 4 个:

1)内部时钟(CK_INT)

2)外部时钟模式 1:外部输入脚(TIx)

3)外部时钟模式 2:外部触发输入(ETR)

4)内部触发输入(ITRx):使用 A 定时器作为 B 定时器的预分频器(A 为 B 提供时钟)。

这些时钟,具体选择哪个可以通过 TIMx_SMCR 寄存器的相关位来设置。


这里的 CK_INT时钟是从 APB1 倍频的来的,即通用定时器 TIMx 的时钟是 APB1 时钟的 2 倍;除非 APB1 的时钟分频数设置为 1,即 APB1 的时钟不分频的时候,通用定时器 TIMx 的时钟就等于 APB1的时钟。这里还要注意的就是高级定时器的时钟不是来自 APB1,而是来自 APB2 的。


(6)、如何设置分频系数(psc)、自动重载计数周期值(arr)

在前面时钟系统部分我们讲解过,系统初始化的时候在默认的系统初始化函数 SystemInit 函数里面已经初始化 APB1 的时钟为 2 分频,所以 APB1 的时钟为 36M,而从 STM32 的内部时钟树图得知:

1.当 APB1 的时钟分频数为 1(即psc=1时) 的时候,TIM2 ~ 7 的时钟为 APB1 的时钟**(即36M);

2.而如果 APB1 的时钟分频数不为 1(psc!= 1时),那么 TIM2~7 的时钟频率将为 APB1 时钟的两倍(即72M)**。

因此,TIM3 的时钟为 72M,再根据我们设计的 arr 和 psc 的值,就可以计算中断时间了。


计算公式如下:

Tout= ((arr+1) * (psc+1))/Tclk。

例如 Tout= ((4999+1) * ( 7199+1))/72=500000us=500ms。

其中:

计数器时钟频率的分频系数(即psc)为 1~65535 之间的任意数值。

Tclk:TIM3 的输入时钟频率(单位为 Mhz)。

Tout:TIM3 溢出时间(单位为 us)。


int main(void) {

delay_init();    

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 

uart_init(115200);

  LED_Init();

TIM3_Int_Init(4999,7199);  //当 定时器计数的值 (即TIM3_CNT)等于 4999 的值的时候,就会产生 TIM3 的更新中断

    while(1){

LED0=!LED0;

delay_ms(200);    

}  

}  


此段代码对 TIM3 进行初始化之后,进入死循环等待 TIM3溢出中断,当 TIM3_CNT 的值等于 TIM3_ARR 的值的时候,就会产生 TIM3 的更新中断,然后在中断里面取反 LED1,TIM3_CNT 再从 0 开始计数。根据上面的公式,我们可以算出中断溢出时间为 500ms,即 Tout= ((4999+1)*( 7199+1))/72=500000us=500ms。


(二)、程序编写

(1)主要步骤:

1)TIM3 时钟使能。(比较 1)和 6),都是关于时钟的设置)RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

2)初始化定时器参数,设置自动重装值,分频系数,计数方式等。

3) 定时器的初始化参数。

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

4)指明使能的定时器中断的类型。

TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE );

在 中断服务函数(void TIM3_IRQHandler(void))中,用于 读取中断状态寄存器的值 判断是否有中断发生。

5)TIM3 中断优先级设置。

NVIC_InitStructure

6)允许 TIM3 工作,也就是使能 TIM3。

TIM_Cmd(TIM3, ENABLE);


7)编写中断服务函数。

void TIM3_IRQHandler(void)

(这里还需要补充说明一下,固件库还提供了两个函数用来判断定时器状态以及清除定时器状态标志位的函数 TIM_GetFlagStatus 和 TIM_ClearFlag)


void TIM3_Int_Init(u16 arr,u16 psc){

    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

NVIC_InitTypeDef NVIC_InitStructure;

    // 1)TIM3 时钟使能。

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//TIM3 是挂载在 APB1 之下,所以我们通过 APB1 总线下的使能使能函数来使能 TIM3

// 2)初始化定时器参数,设置自动重装值,分频系数,计数方式等。

TIM_TimeBaseStructure.TIM_Period = arr; //是设置  自动重载计数周期值

TIM_TimeBaseStructure.TIM_Prescaler =psc;  // TIM_Prescaler 是用来设置  分频系数的

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //是用来设置时钟分频因子

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //数 TIM_CounterMode 是用来设置计数方式,可以设置为向上计数,向下计数方式还有中央对齐计数方式。

    // 3) 定时器的初始化参数是通过初始化函数 TIM_TimeBaseInit 实现

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 第一个参数是确定是哪个定时器。第二个参数是定时器初始化参数结构体指针 ,结构体类型为 TIM_TimeBaseInitTypeDef。这个结构体一共有 5 个成员变量,要说明的是,对于通用定时器只有前面四个参数有用,最后一个参数 TIM_RepetitionCounter 是高级定时器才有用的,这里不多解释。

    // 4)指明使能的定时器中断的类型

TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //第二个参数非常关键,是用来指明使能的定时器中断的类型,定时器中断的类型有很多种,包括 TIM_IT_Update,TIM_IT_Trigger,输入捕获中断等等。

    // 5)TIM3 中断优先级设置。

NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 

NVIC_Init(&NVIC_InitStructure); 

   // 6)允许 TIM3 工作,也就是使能 TIM3。

TIM_Cmd(TIM3, ENABLE);  

}


// 7)编写中断服务函数。

void TIM3_IRQHandler(void){

if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET){   //读取中断状态寄存器的值判断中断类型的函数

TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );    //清除定时器 TIMx 的中断 TIM_IT 标志位

LED1=!LED1;

}

}

关键字:STM32  NVIC  中断 引用地址:STM32 NVIC 中断

上一篇:STM32中断及NVIC概述
下一篇:电位器 ADC stm32f10x开发

推荐阅读

        虽然刚刚发布了One Plus 6T,但一加仍然是忙碌的,因为要成为首批提供5G手机的厂商,而之前该公司掌门人刘作虎已经确认了这个消息,一加将会率先推出支持5G网络的旗舰产品,预计在明年上半年亮相。  据外媒CNET报道称,一加将在明年年初就发布5G手机,不过它的名称可能不会冠以One Plus 7的名称,换句话说,一加希望这款5G手机开拓...
导读:11月12日,国巨同意将以12亿美元全现金收购美资被动器件厂商KEMET。图:台媒报道外媒称,国巨同意以美股27.2美元高于市值的溢价,全现金收购美资被动元件厂商KEMET(基美),含债务在内交易价值为18亿美元。KEMET将成为国巨旗下子公司,预计2020年下半年可完成交易。KEMET周一股价跌2.1%,报收23.02美元。2018年4月,就传出包括日商太阳诱电、美商KE...
双十一已经结束了,魅族虽然没有发布特别惊艳的战报,但是却发表名为《只为初心,步履不停》的长文,谈到了它对双十一的看法。魅族称今年的双十一,有特别的意义,拥抱了老朋友,收获了新朋友,发现科技与艺术不分群体,而魅族也与最初的自己,唤起了共鸣。魅族感谢所有选择魅族17系列的消费者,魅族表示每一份认可都让他们更加坚定高端精品战略。以下是魅...
这几天把以前买的几十片STM8S005K6T6翻出来玩玩,使用STVD+COSMIC编写,点了一下流水灯.适合初学者单片机源程序如下:/* MAIN.C file** Copyright (c) 2002-2005 STMicroelectronics*/#include<stm8s005k6.h>#define uchar unsigned char#define uint unsigned intvoid delay_ms(unsigned int x) //默认时钟16m/8 1ms{ unsigne...

史海拾趣

问答坊 | AI 解惑

HT1380串行时钟芯片

HT1380串行时钟芯片一般来说,HT系列的芯片在串行口的应用一直以来就是很好的在单片机 这个领域,串行时钟一直占着很重要的位置…

查看全部问答∨

Introduction to NI VeriStand

Introduction to NI VeriStand…

查看全部问答∨

工程师们看过来——电气测量时,聪明人所做的10件糊涂事

本人搜集到的一篇很好的文章,因此转载给各位大虾,希望对工程师们有所帮助: 何靠与电打交道来谋生的人很快都会对任何“带电”的物体生出理性的敬意,哪怕是“带电”的机会很小。然而,需要按时完成一项工作或者使某个关键设备恢复联机的紧迫压力 ...…

查看全部问答∨

evc托盘编程

使用evc4.0编写一个系统托盘,就是我的程序起来之后,不显示对话框,自动出现一个图标到右下角,通过双机图标,可以弹出对话框。 右键单击右下角图标,可以弹出一个菜单。 不知道如何实现这些功能,请兄弟姐妹们帮个忙!…

查看全部问答∨

请高手帮忙~~~在EVC下的2个函数没定义.

error C2065: \'GetRunningObjectTable\' : undeclared identifier error C2065: \'CreateItemMoniker\' : undeclared identifier 我用的是EVC 4.0+SP4…

查看全部问答∨

si4010有没有人用过,求探讨

si4010有没有人用过,求探讨,开发软件,调试软件,烧录工具等等…

查看全部问答∨

STM32的驱动库好用吗?效率高吗?优化的吗?准备上STM32

    N多年没买过开发板了,也没用过仿真器了。都是用软件编译调试好,直接ISP,然后通过串口命令开启调试信息的输出。    昨天买了块STM32F103全功能型开发板,他们的办事效率真低(深圳到广州通常当 ...…

查看全部问答∨

求IAR Embedded Workbench for MCS-51 完整版

各位高手,小弟求IAR Embedded Workbench for MCS-51完整版,有哪位高人有,能否分享一下。我的邮箱jinghongchen@126.com…

查看全部问答∨

猎头招聘

世界500强招聘压力传感器SAE,如有兴趣可发简历至:aerie.song@seek-jobs.com.cn 要求:至少五年以上相关工作经验,英文流利。…

查看全部问答∨

串口调试可能用到的小工具

串口调试精灵 串口调试时可以用的到…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved