STM32F407芯片支持82个可屏蔽中断
NVIC:嵌套向量中断控制器,控制芯片中断相关功能。由于ARM给NVIC预留了非常多的功能,但是对于使用M4内核设计芯片的公司可能就不需要这么多功能,于是就需要在NVIC上进行裁剪。ST公司的STM32F407芯片内部中断数量就是NVIC裁剪后的结果。
STM32F407芯片支持82个可屏蔽中断通道,每个中断通道都具备自己的中断优先级控制字节(8位,但是STM32F407中只使用4位,高4位有效),用于表达优先级的高4位又被为组成抢占式优先级和响应优先级,通常也把响应优先级称为“亚优先级”或“副优先级”,每个中断源都需要被指定这两种优先级。
高抢占式优先级的中断事件会打断当前的主程序或者中断程序运行,俗称中断嵌套。在抢占式优先级相同的情况下,高响应优先级的中断优先被响应。
当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理那一个。
STM32F407中指定中断优先级的寄存器位有4位,这4位的分组方式如下:
中断配置步骤:
//要使用中断我们就需要先配置它,通常都需经过这几步:
//(1)使能外设某个中断
//(2)设置中断优先级分组,初始化 NVIC_InitTypeDef 结构体
typedef struct
{
uint8_t NVIC_IRQChannel; //中断源
uint8_t NVIC_IRQChannelPreemptionPriority; //抢占优先级
uint8_t NVIC_IRQChannelSubPriority; //响应优先级
FunctionalState NVIC_IRQChannelCmd; //中断使能或失能
} NVIC_InitTypeDef;
//(3)编写中断服务函数
EXTI简介
STM32F4外部中断/事件控制器(EXTI)包含多达 23 个用于产生事件/中断请求的边沿检测器。EXTI的每根输入线都可单独进行配置,以选择类型(中断或事件)和相应的触发事件(上升沿触发、下降沿触发或边沿触发),还可单独屏蔽。
EXTI结构框图:
外部中断事件的映射
STM32F4的EXTI具有23个中断/事件线。
EXTI0-15:
外部中断配置步骤:
要使用外部中断我们就需要先配置它,通常都需经过这几步:
(1)使能IO口时钟,配置IO口模式为输入
(2)开启 SYSCFG 时钟,设置 IO 口与中断线的映射关系
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);//使能SYSCFG时钟
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex);
(3)配置中断分组(NVIC),使能中断
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//EXTI0中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
(4)初始化EXTI,选择触发方式
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
typedef struct
{
uint32_t EXTI_Line; //中断/事件线
EXTIMode_TypeDef EXTI_Mode; //EXTI模式
EXTITrigger_TypeDef EXTI_Trigger; //EXTI触发方式
FunctionalState EXTI_LineCmd; //中断线使能或失能
}EXTI_InitTypeDef;
(5)编写EXTI中断服务函数
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler (包含5、6、7、8、9)
EXTI15_10_IRQHandler(包含10、11、12、13、14、15)
使用外部中断来控制led亮灭
外部中断源文件:
exti.c
void My_EXTI_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure; //定义NVIC结构体
EXTI_InitTypeDef EXTI_InitStructure; //定义EXTI结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE); //使能IO口时钟
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0); //打开SYSCFG时钟 该函数定义位于stm32f4xx_syscfg.c中
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2); //打开SYSCFG时钟是配置GPIO中断线映射的前提
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);
//EXTI0 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//EXTI0中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//EXTI2 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;//EXTI2中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =2; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//EXTI3 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;//EXTI3中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =1; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//EXTI4 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;//EXTI4中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//初始化线上中断,设置KEY_UP触发条件
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);
//初始化线上中断,设置KEY_LEFT KEY_DOWN KEY_RIGHT触发条件
EXTI_InitStructure.EXTI_Line=EXTI_Line2|EXTI_Line3|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)
{
if(EXTI_GetITStatus(EXTI_Line0)==1) //读取中断标志位
{
myDelay_ms(10);
if(K_UP==1)
{
led2=0;
}
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除中断标志位
}
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)==1)
{
myDelay_ms(10);
if(K_DOWN==0)
{
led2=1;
}
}
EXTI_ClearITPendingBit(EXTI_Line3);
}
void EXTI2_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line2)==1)
{
myDelay_ms(10);
if(K_RIGHT==0)
{
led1=0;
}
}
EXTI_ClearITPendingBit(EXTI_Line2);
}
void EXTI4_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line4)==1)
{
myDelay_ms(10);
if(K_LEFT==0)
{
led1=1;
}
}
EXTI_ClearITPendingBit(EXTI_Line4);
}
主函数:
int main()
{
u8 i=0;
SysTick_Init(168);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组
LED_Init();
KEY_Init();
My_EXTI_Init(); //外部中断初始化
while(1)
{
led1 = 0;
myDelay_ms(300);
led1 = 1;
myDelay_ms(300);
}
}
定时器中断:
STM32F407定时器由两个基本定时器(TIM6、TIM7)、10个通用定时器(TIM2-TIM5,TIM9-TIM14)和两个高级定时器(TIM1、TIM8)组成。通用定时器在 基本定时器的基础上扩展而来,增加了输入捕获与输出比较等功能。高级定时器又是在通用定时器基础上扩展而来,增加了可编程死区互补输出,重复计数器,带刹车(断路)功能,这些功能主要针对工业电机控制。
通用定时器:
STM32F4的通用定时器包含一个 16 位或 32 位自动重载计数器(CNT),该计数器由可编程预分频器(PSC)驱动。STM32F4的通用定时器可用于多种用途,包括测量输入信号的脉冲宽度(输入捕获)或者生成输出波形(输出比较和PWM)等。 使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32F4 的每个通用定时器都是完全独立的,没有互相共享的任何资源。
通用定时器结构框图
通用定时器是总线时钟的两倍。
通用定时器配置步骤:
固件库中还有两个函数是用来读取状态标志位以及清除中断标志位,函数分别为TIM_GetFlagStatus和TIM_ClearFlag。
定时器时钟:
TIM4时钟:
由于TIM4挂接在APB2上,根据时钟树可以看出外部时钟8MHz经过PLL的M(8)分频,N(336)倍频和P分频(8)得到的42MHz频率,如果APB中的presc不为1所以需要2倍频所以TIM4的时钟为84MHz。
使用定时器中断控制灯闪烁:
#include "time.h"
#include "led.h"
#include "sysTick.h"
void TIM_Init(u16 per,u16 psc)
{
TIM_TimeBaseInitTypeDef TIMBaseInitStructure; //定义定时器结构体
NVIC_InitTypeDef NVIC_InitStructure; //定义NVIC结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); //注意TIM4的时钟是APB1时钟的两倍
TIMBaseInitStructure.TIM_Period =per; //自动重装载值 周期
TIMBaseInitStructure.TIM_Prescaler = psc; //预分频系数(时钟源)
TIMBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分割,时钟分频 通常不需要修改
TIMBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式选择向上计数
TIM_TimeBaseInit(TIM4,&TIMBaseInitStructure); //初始化定时器中断
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //设置定时器中断类型
TIM_ClearFlag(TIM4,TIM_IT_Update); // **保证中断标志位为空**
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//TIM4中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
TIM_Cmd(TIM4,ENABLE); //使能定时器
}
void TIM4_IRQHandler(void) //定时器4中断函数
{
if(TIM_GetITStatus(TIM4,TIM_IT_Update)){
led2 = !led2;
}
TIM_ClearFlag(TIM4,TIM_IT_Update);
}
主函数:
#include "led.h"
#include "system.h"
#include "SysTick.h"
#include "key.h"
#include "time.h"
int main()
{
SysTick_Init(168);
LED_Init();
TIM_Init(5000-1,8400-1); //从0开始所以-1;TIM4的时钟是84MHz,通过8400分频之后是10000Hz也就是0.1ms,所以500ms需要5000次计数
while(1)
{
led1 = 0;
}
}