历史上的今天
返回首页

历史上的今天

今天是:2025年01月15日(星期三)

正在发生

2019年01月15日 | stm32之中断系统

2019-01-15 来源:eefocus

前言:这一节以及后面的定时器部分都是学习所有的单片机时的最重要的部分,我也花了很多时间去理解手册和程序,我争取尽量全面并且细致的记录我的体会。


一、中断的概念

这里就省略了,相信你学过单片机就会懂。


二、stm32里面的NVIC是什么

NVIC的中文意思是嵌套向量中断控制器,控制着中断的相关功能(其中包括中断源、抢占优先级、响应优先级、中断的使能与失能),具体的配置方法后面会提到。


三、stm32里面的优先级

在stm32中,一个中断的优先级由两部分决定,一个是抢占式优先级,还有一个是响应优先级。两个优先级组成了一个4位的控制字。 


如图: 


这里写图片描述


那么我们就会问,这有四位,那两个优先级各占多少位。就像图中所展示的,抢占式优先级(黄色)可以沾满四位,也可以一位都不占,如果占满四位,则抢占式优先级共有0-15这些等级,占满2位,就有0-3这些等级。同理响应优先级也是这样。分配他们各占多少位的函数为:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);后面这个数字代表的是抢占式优先级的位数


抢占式优先级和响应优先级分析


这两个优先级中,抢占式优先级占主导地位,抢占式优先级高的中断会优先打断主程序或者另外一个中断程序。 


1.如果两个中断的抢占式优先级一样高,当两个中断同时发生时,响应优先级高的先发生,完了再发生另一个。但是要注意,响应优先级高的中断不会打断低的中断,也就是当一个高响应优先级的中断来时,如果正在执行一个低的中断,他不会打断他,而是等待低的执行完在执行。也就是说只有高抢占式优先级可以阻断。


四,中断运用过程


1.中断的相关配置(其中包括很多很多步骤)


2.设置优先级分组,也就是前面提到的两个优先级的位数


3.书写中断服务函数


注意:第一个步骤对应一个总的函数my_exti_init();第二步骤单独写在主函数中,第三个步骤的中断服务函数直接写在头文件中不写在第一个函数中,不用写在主函数中。总的来说这三个步骤是指主函数中的步骤。 

这三个步骤都在后面详细分析


五、配置过程详细分析


首先需要知道,stm32f4中有23个外部中断,也就是有23个外部中断线路,但是只有0-15线是用来连到io口的(平时没有连上,需要用库函数来连上) 


这里写图片描述


到这里你一定会发现,0-15这有16条线,而stm32刚好每个端口有16个针脚,于是你自然也会想到阵脚的编号肯定是与中断线路编号刚好对应上的,事实上你很聪明,就是这样。至于是哪一个端口的针脚,就需要库函数配置。


配置步骤一:因为用到io口,所以首先要配置io口,就是打开时钟,设为输入模式等操作,至于为上啦还是下拉就看外设了,因为这里外设为按键,就好判断。


配置步骤二:像前面说的,要将中断线路和io口连接起来,用到的函数是:


SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);//这里就是将A端口的0管脚接在0线路上,用到几个线路就写多少个这个函数

1

但是SYSCFG是接在APB2总线上的,所以在SYSCFG_EXTILineConfig之前需要用RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);使能SYSCFG。


配置步骤三:配置NVIC(用到多少中断就写多少组)


NVIC_InitTypeDef NVIC_InitStructure;//声明一个结构体


NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;//中断通道,根据线路修改数字

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//设置抢占式优先级

NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;//设置响应优先级

NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//通道使能

NVIC_Init(&NVIC_InitStructure);//最后在申明下


配置步骤四:初始化外部中断EXTI(用到多少中断就写多少组)


EXTI_InitTypeDef EXTI_InitStructure;//声明结构体


EXTI_InitStructure.EXTI_Line=EXTI_Line0;//选择线路

EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//选模式,这里为中断模式

EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;//上升沿触发,下降沿为Falling

EXTI_InitStructure.EXTI_LineCmd=ENABLE;//使能

EXTI_Init(&EXTI_InitStructure);


配置步骤五:编写中断服务函数(写在头文件中) 

格式:


void EXTI0_IRQHandler(void)

{

        if(EXTI_GetITStatus(EXTI_Line0)==1)//看是否确实触发了中断,值为1就为触发了

        {

                delay_ms(10);//消抖   

                if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)//再一次确定,只不过这一次是确定按钮是否按下

                {

                        while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0));

                        GPIO_ResetBits(GPIOF,GPIO_Pin_10);//open the led

                }

        }

        EXTI_ClearITPendingBit(EXTI_Line0);//注意,这一步非常重要,经常容易忘记,写在中断服务函数的最后。用于清空中断的状态,以便于下次再次使用


注意:中断函数名不是自己取得,是有规定的


 这里写图片描述 


线路一用第一个,线路二用第二个,但是线路5到9用的是一样的倒数第二个,线路10-15用的最后一个。


完整程序: 

exti.c


#include "exti.h"



void my_exti_init()

{

        NVIC_InitTypeDef NVIC_InitStructure;

        EXTI_InitTypeDef EXTI_InitStructure;


        RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);


        SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);//ÖжÏÏß·µÄÓ³É䣬¾ÍÊǰÑÄĸö¶Ë¿Ú¿´×öÖжÏÊäÈë

        SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2);//ÖжÏÏß·µÄÓ³É䣬¾ÍÊǰÑÄĸö¶Ë¿Ú¿´×öÖжÏÊäÈë

      SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);//ÖжÏÏß·µÄÓ³É䣬¾ÍÊǰÑÄĸö¶Ë¿Ú¿´×öÖжÏÊäÈë

      SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);//ÖжÏÏß·µÄÓ³É䣬¾ÍÊǰÑÄĸö¶Ë¿Ú¿´×öÖжÏÊäÈë



        NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;

        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;

        NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;

        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;

        NVIC_Init(&NVIC_InitStructure);



      NVIC_InitStructure.NVIC_IRQChannel=EXTI2_IRQn;

        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;

        NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;

        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;

        NVIC_Init(&NVIC_InitStructure);



        NVIC_InitStructure.NVIC_IRQChannel=EXTI3_IRQn;

        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;

        NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;

        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;

        NVIC_Init(&NVIC_InitStructure);



        NVIC_InitStructure.NVIC_IRQChannel=EXTI4_IRQn;

        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;

        NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;

        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;

        NVIC_Init(&NVIC_InitStructure);


      //Line0


      EXTI_InitStructure.EXTI_Line=EXTI_Line0;

        EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//ģʽÅäÖÃΪÖжÏģʽ

        EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;

      EXTI_InitStructure.EXTI_LineCmd=ENABLE;

        EXTI_Init(&EXTI_InitStructure);


        //Line2

        EXTI_InitStructure.EXTI_Line=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);


        //Line3

        EXTI_InitStructure.EXTI_Line=EXTI_Line3;

        EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//ģʽÅäÖÃΪÖжÏģʽ

        EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;

      EXTI_InitStructure.EXTI_LineCmd=ENABLE;

        EXTI_Init(&EXTI_InitStructure);



        //Line4

        EXTI_InitStructure.EXTI_Line=EXTI_Line4;

        EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//ģʽÅäÖÃΪÖжÏģʽ

        EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;

      EXTI_InitStructure.EXTI_LineCmd=ENABLE;

        EXTI_Init(&EXTI_InitStructure);

}


void EXTI0_IRQHandler(void)//ÖжÏÏß·0

{

        if(EXTI_GetITStatus(EXTI_Line0)==1)//¿´ÊÇ·ñȷʵ´¥·¢ÁËÖжϣ¬¼´¿´°´Å¥ÊÇ·ñÕæµÄ°´Ï£¬µ«×¢ÒâÕâÀïµÄ1²»ÊÇÖ¸°´Å¥µÄ״̬£¬ÊÇÖ¸ÖжϵÄ״̬

        {

                delay_ms(10);   

                if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)

                {

                        while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0));

                        GPIO_ResetBits(GPIOF,GPIO_Pin_10);//open the led

                }

        }

        EXTI_ClearITPendingBit(EXTI_Line0);

}




void EXTI2_IRQHandler(void)//ÖжÏÏß·¶þ

{

        if(EXTI_GetITStatus(EXTI_Line2)==1)//¿´ÊÇ·ñȷʵ´¥·¢ÁËÖжϣ¬¼´¿´°´Å¥ÊÇ·ñÕæµÄ°´Ï£¬µ«×¢ÒâÕâÀïµÄ1²»ÊÇÖ¸°´Å¥µÄ״̬£¬ÊÇÖ¸ÖжϵÄ״̬

        {

                delay_ms(10);   

                if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)==0)

                {

                        while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)));

                        GPIO_SetBits(GPIOF,GPIO_Pin_8);

                }

        }

        EXTI_ClearITPendingBit(EXTI_Line2);

}


void EXTI3_IRQHandler(void)//ÖжÏÏß·Èý

{

        if(EXTI_GetITStatus(EXTI_Line3)==1)//¿´ÊÇ·ñȷʵ´¥·¢ÁËÖжϣ¬¼´¿´°´Å¥ÊÇ·ñÕæµÄ°´Ï£¬µ«×¢ÒâÕâÀïµÄ1²»ÊÇÖ¸°´Å¥µÄ״̬£¬ÊÇÖ¸ÖжϵÄ״̬

        {

                delay_ms(10);   

                if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==0)

                {

                        while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)));

                        GPIO_SetBits(GPIOF,GPIO_Pin_10);//turn off the led

                }

        }

        EXTI_ClearITPendingBit(EXTI_Line3);

}



void EXTI4_IRQHandler(void)//ÖжÏÏß·ËÄ

{

        if(EXTI_GetITStatus(EXTI_Line4)==1)//¿´ÊÇ·ñȷʵ´¥·¢ÁËÖжϣ¬¼´¿´°´Å¥ÊÇ·ñÕæµÄ°´Ï£¬µ«×¢ÒâÕâÀïµÄ1²»ÊÇÖ¸°´Å¥µÄ״̬£¬ÊÇÖ¸ÖжϵÄ״̬

        {

                delay_ms(10);   

                if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==0)

                {

                        while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)));

                        GPIO_ResetBits(GPIOF,GPIO_Pin_8);

                }

        }

        EXTI_ClearITPendingBit(EXTI_Line4);

}


main.c


int main()

{

    RCC_HSE_Config(8,336,2,7);

    Beep_Init();

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

    SysTick_Init(168);

    key_init();

    LED_Init();

    my_exti_init();

    while(1)

    {


    }


}


说明:去前面一节的按钮实验现象虽然相同,但是原理不同,前面的按钮实验是通过不断检测按钮的状态,而这里是用的中断,满足触发条件自动执行。


推荐阅读

史海拾趣

General Electric Company公司的发展小趣事
可能是由于电路布局不合理或外部元件选择不当引起的。解决方案是优化电路布局、选择合适的外部元件并加强电源滤波。
Eureka Microelectronics Inc公司的发展小趣事

随着Eureka产品线的不断拓展,公司开始积极拓展海外市场。通过与国际知名电子制造商的合作,Eureka的产品逐渐打入国际市场。同时,公司也积极参与国际展会和技术交流活动,提升了品牌知名度和影响力。这些努力不仅帮助Eureka扩大了市场份额,也促进了公司与国际同行的交流与合作。

芯海科技(CHIPSEA)公司的发展小趣事

近年来,随着新能源汽车市场的快速崛起,BMS(电池管理系统)技术成为关键。芯海科技凭借在“模拟信号链+MCU”领域的技术积累,成功开发出应用于BMS锂电管理的高精度模拟信号链芯片。这一技术的突破使得芯海科技能够切入新能源汽车市场,为行业的发展提供了有力支持。

固驰(GUERTE)公司的发展小趣事

固驰电子深知产品质量是企业生存之本,因此始终将品质控制放在首位。公司不仅建立了完善的质量管理体系,还通过了ISO9001:2015质量管理体系认证和美国UL产品认证,这标志着固驰电子的产品质量达到了国际先进水平。此外,公司还积极申请专利,目前已拥有50项国家专利,进一步巩固了其在行业内的技术领先地位。

Gruner AG公司的发展小趣事
明确电路需要实现的功能,如信号传输、电气隔离、放大等。
Genisco Electronics公司的发展小趣事
选择高质量、高可靠性的ATSE、电源检测装置和控制器等设备。

问答坊 | AI 解惑

给初学单片机的40个经典实验!

本帖最后由 paulhyde 于 2014-9-15 03:36 编辑 初学单片机准备电子设计大赛的时候,这个资料对我帮助很大,也拿出来跟大家分享一下,同时也希望大家把资料拿出来一起分享!~  …

查看全部问答>

步进电机总结

本帖最后由 paulhyde 于 2014-9-15 09:23 编辑 这是我找到的 分享下 可能有错哦  …

查看全部问答>

我见到过的最好的模电电子书

我见到过的最好的模电电子书,结合现在公司能实用的,不是纯理论的。…

查看全部问答>

关于使用VS2005开发WINCE程序中,指针引用的问题

我把VC6下的程序向VS2005下转,使用自己编译的ARMV41 SDK,结果程序调试的时候发现走到一处需要指针引用传入的函数时,报错 函数如下:         _ADRS *_ADRS::find(_ADRS *&pHead)         {   ...…

查看全部问答>

急!急!请各位大侠进来给点意见.

我之前做过USB方面驱动,最近要做PCI方面驱动,是实现这样两个功能 1.PCI转2串口+1并口 2.PCI转8串口 想请教的问题是: 1.这两个驱动是不是都属于多功能驱动? 2.多功能驱动的书写需要注意那些问题? 请务必给点意见,十分着急...…

查看全部问答>

高手救命:关于向PCI9054中CPLD寄存器中写入数据的问题?

环境为VC++6.0 CPLD寄存器地址:0x1f000;(偏移量) 要写入值为:        0。 写入数据的具体地址为:总线地址+偏移量 我想知道用什么具体的语句执行这个!那位高手知道,指点我一下吧! 我这先谢谢了!…

查看全部问答>

关于定时器和串口的浅谈

由于自己对网络方面的PCB布线要求不是很了解,所以自己的第一版lm3s的pcb以失败告终。 只能先用开发板调试。周末花了两天的时间将8962的定时器和串口方面弄明白,以便开展后续工作。   其实,前人的帖子已经说的很到位了,在此我就谈谈自己 ...…

查看全部问答>

【挖电源】自制5V电源

    原理图:         220V交流市电通过电源变压器变换成交流低压,再经过桥式整流电路D1~D4和滤波电容C4的整流和滤波,在固定式三端稳压器LM7805的Vin和GND两端形成一个并不十分稳定的直流电压(该电压 ...…

查看全部问答>

大家一起学习PIC单片机之一

大家好,本人是PIC单片机新手,但是我很勤奋,我买的ND118-877APIC实验开发板,里面有实例,但是没有源代码,本人自己写代码大家一起交流,希望能陆续写下去,本人QQ476857290 希望大家学习指导 本节学习数码管动态显示0000-9999…

查看全部问答>