STM32萌新学习笔记(四)

本文详细介绍了STM32中的NVIC嵌套向量中断控制器及其在中断控制中的作用,包括中断优先级设定、EXTI外部中断/事件控制器的配置以及SysTick系统定时器的基础知识。通过实例演示了如何配置GPIO和中断,以及实战部分的主函数和中断函数的编写。
摘要由CSDN通过智能技术生成

 一、NVIC嵌套向量中断控制器

         STM32的中断很强大,每个外设都可以产生中断。另外就是,中断还有另一个称呼——异常,其实异常就是终端,我们STM32官方手册上的异常其实就是中断。 

        中断优先级值越小,中断优先级越高。    系统异常体现在内核水平,而外部中断体现在外设水平。然后在我们的官方手册里呢,灰色的就是内核水平的,也就是系统异常,白色的是外设中断。  

        如下图所示:

        那么怎么实现中断的控制呢?
        首先是NVIC: 嵌套向量中断控制器(NVIC ) 和处理器核的接口紧密相连,可以实现低延迟的中断处理和高效地处理晚到的中断。嵌套向量中断控制器管理着包括内核异常等中断。
更多关于异常和 NVIC 编程的说明需要参考《STM32F10xxx Cortex-M3编程手册-英文版-PM0056-Rev6》这本资料,也在野火官方资料文件夹里。(不过都是英文   @_@  )
        下面是NVIC寄存器的简单注释:
        这个结构体其实在我们固件库里面一个头文件里就有,大概位置在这里:
        这些寄存器里,经常会用到的是中断优先级寄存器,一般涉及到中断编程的时候就涉及到优先级的配置。从上面也可以看到,其实NVIC里每一个寄存器都是一个数组,但我们一般用不完数组里面的东西,比如说这个中断优先级寄存器,我们内核加外设总共也才70来个,根本用不完这240个位置。
        
        在这个头文件里面可以找到关于NVIC的相关库函数。
        中断优先级的设定,需要使用到IPR这一寄存器,描述是英文的,以下两张图片是我用软件翻译后的结果,
        这里又一次提到了优先级的值越低,优先级越高。另外一个解读出来的信息就是它虽然有八位,但是低四位不使用,只需要用到高四位。在优先级的比较上,先比较主优先级,再比较次优先级,若两者都一样,则比较外设编号。
        举个例子:
这张图片第一列是外设的位置,其实就是编号,如果我们设置EXTI2和EXTI3的优先级相同,那么在同等条件下将会是编号低的EXTI2先响应。
        那么优先级的主次怎么分组呢?看下面:
        
        

        通过内核外设SCB的AIRCR寄存器的PRIGROUP位来完成。不过我也不太清楚这里到底什么意思,全英文看不懂。也没有搞清楚为什么要这样分组,又为什么分成这么五组。

        配置中断优先级分组的固件库函数在下面这里:

配置好优先级分组,之后下一步该初始化结构体了:

上面图片中的注释是这个结构体组成部分的作用。第一个部分的作用是指定中断源。

        下一步就是调用初始化函数

这个初始化函数将上面定义的结构体内的成员全部写到相应的寄存器里面。

        下一步是编写中断服务函数,当你中断服务函数写错时,不会报错,而是执行启动文件里的中断服务函数。所以中断服务函数名称要和向量表也就是启动文件里的函数名保持一致。

二、EXTI外部中断 / 事件控制器

1.理论部分:

        EXTI总共有20条事件/中断线,其中前十六条对应16个IO端口,后四条则有其特定的输出。

        那么这二十根输入线的配置是通过哪个寄存器实现的呢?

        我们之前提到了,EXTI有20根输入线,每一根都可以输入配置上升沿或者下降沿,上升沿只需要在上升沿触发选择寄存器的相应位里写入“1”即可,写0为静止上升沿;下降沿配置同理。

如图是上升沿触发选择寄存器:

上升沿捕捉按键按下时产生的脉冲,下降沿捕捉按弹开时产生的脉冲。

在软件中断事件寄存器相应的位写1,将会设置EXTI_PR也就是挂起寄存器中相应的挂起位。

下面来看一下框架图:

        首先从输入线输入上升沿或下降沿,被上升沿触发选择寄存器或下降沿触发选择寄存器捕捉,输出1或0;若是上升沿,边沿检测电路输出为1,触发选择寄存器输出为1,经过或门,输出仍为1,此时分两步走,一是向上进入中断,二时向下进入事件。

        向上进入中断后首先进入挂起寄存器,由于前面的软件中断事件寄存器置1,所以相应的挂起寄存器也置1,后面是个与门,需要两个都为1,才输出1;那么就需要看中断屏蔽寄存器了,

这个寄存器相当于中断的一个大开关,置1才会打开中断,因此经过与门后输出为1;

然后这个1就送到了NVIC中断控制器里,也就是内核里。

        如果向下进入事件里,面前也是一个与门,这里有个事件屏蔽寄存器,如果置1,与门输出1,就会进入脉冲发生器,产生一个脉冲,触发ADC的转换,起到一个信号的作用,也可以触发定时器的定时。

2.实战部分:

下面是我们的主函数部分和一部分函数功能的注释

int main(void)
{
	/* LED 端口初始化 */
	LED_GPIO_Config();
	  	
	/* 初始化EXTI中断,按下按键会触发中断,
  *  触发中断会进入stm32f4xx_it.c文件中的函数
	*  KEY1_IRQHandler和KEY2_IRQHandler,处理中断,反转LED灯。
	*/
	EXTI_Key_Config(); 
	
	/* 等待中断,由于使用中断方式,CPU不用轮询按键 */
	while(1)                            
	{
	}
}

我们这个主函数初始化这个按键中断函数,内部包含开启GPIO外设的时钟、中断函数的配置、中断外设的结构体、GPIO外设的结构体、GPIO的配置、EXTI模式的选择、数据的配置等等。

下面是我们的这个中断函数的内容和注释。

void EXTI_Key_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure; 
	EXTI_InitTypeDef EXTI_InitStructure;

	/*开启按键GPIO口的时钟*/
	RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK,ENABLE);
  RCC_APB2PeriphClockCmd(KEY2_INT_GPIO_CLK,ENABLE);
												
	/* 配置 NVIC 中断*/
	NVIC_Configuration();
	
/*--------------------------KEY1配置-----------------------------*/
	/* 选择按键用到的GPIO */	
  GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;
  /* 配置为浮空输入 */	
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);

	/* 选择EXTI的信号源 */
  GPIO_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, KEY1_INT_EXTI_PINSOURCE); 
  EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;
	
	/* EXTI为中断模式 */
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	/* 上升沿中断 */
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
  /* 使能中断 */	
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
	
  /*--------------------------KEY2配置-----------------------------*/
	/* 选择按键用到的GPIO */	
  GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;
  /* 配置为浮空输入 */	
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);

	/* 选择EXTI的信号源 */
  GPIO_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE, KEY2_INT_EXTI_PINSOURCE); 
  EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;
	
	/* EXTI为中断模式 */
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	/* 下降沿中断 */
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
  /* 使能中断 */	
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
}
/*********************************************END OF FILE**********************/

这个中断函数解释的位置在:

运行的结果是LED灯的反转。

        我们主函数里面的这个LED初始化函数其实是我们之前学过的,就在点亮LED灯和按键检测控制LED里面都有讲过。

就是下面这个:

void LED_GPIO_Config(void)
{		
		/*定义一个GPIO_InitTypeDef类型的结构体*/
		GPIO_InitTypeDef GPIO_InitStructure;

		/*开启LED相关的GPIO外设时钟*/
		RCC_APB2PeriphClockCmd( LED1_GPIO_CLK | LED2_GPIO_CLK | LED3_GPIO_CLK, ENABLE);
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;	

		/*设置引脚模式为通用推挽输出*/
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

		/*设置引脚速率为50MHz */   
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 

		/*调用库函数,初始化GPIO*/
		GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);	
		
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;

		/*调用库函数,初始化GPIO*/
		GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
		
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = LED3_GPIO_PIN;

		/*调用库函数,初始化GPIOF*/
		GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStructure);

		/* 关闭所有led灯	*/
		GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
		
		/* 关闭所有led灯	*/
		GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);	 
    
    /* 关闭所有led灯	*/
		GPIO_SetBits(LED3_GPIO_PORT, LED3_GPIO_PIN);
}

位置在:

三、SysTick系统定时器

定时器这一模块学习的还不太熟悉,姑且贴一些我用软件翻译的一些官方手册的图片。

下一周发定时器和串口通信的学习笔记,敬请期待喔~~~~~~~~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值