目录
中断是什么
轮询
中断
中断调用情况
中断的分类
内部中断(TIM、UART等)
tim.c
tim.h
外部中断EXTI
exti.c
exti.h
中断是什么
在处理事件的时候有两种方式:轮询和中断。
轮询
顾名思义,就是每轮都询问一次。比如while循环的每一次,就会执行检查,1.此处串口是否有数据到来。2.每次都检测一下引脚状态,是否为低电平。在有大量事件发生时,cpu按照事件到来的先后顺序依次执行,不考虑时间的紧急性和事件大小,也就是不考虑时间的优先级。
中断
中断也是一种对于数据获取的调度方式。通过中断源来提醒CPU,数据已经更新,可以读取新数据。给每个事件设置优先级,在有大量事件发生时,cpu按照事件的优先级率先完成紧急性事件和处理时间短的事件。
中断调用情况
CPU对于调度频次不高的任务(函数),或者特别紧急的任务(刹车制动这种) 给出的一种任务执行关系,这种关系的本质就是函数功能一旦出现,就要立即实施。这两种形式的任务一般都以中断形式调度,对于上一种任 务,优势在于提升系统运行速率,后一种提升了系统的实时性。 中断是一种芯片的内部的宝贵资源,数量是十分有限的。
中断的分类
内部资源根据硬件细节不同分出内部中断,和外部中断两种。
内部中断(TIM、UART等)
CPU内部有一个硬件结构叫做NVIC,也叫做中断管理控制器, NVIC管理着很多引脚,当特定事件发生时,会给NVIC发送一个信号,NVIC调用相应的中断函数传给CPU让其执行。
tim.c
#include "stm32f4xx.h" // Device header
#include "stdio.h"//定时器的初始化
void Time_Init()
{//时钟使能RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//1//参数配置,84M 84 000 000TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;//3TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//4TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;TIM_TimeBaseInitStruct.TIM_Period=(5000-1);//工作量(路程),每一趟执行一次中断TIM_TimeBaseInitStruct.TIM_Prescaler=(8400-1);//84M除以8400=10 000,工作速度(10 000/s)TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);//2//让定时器开始工作TIM_Cmd(TIM2,ENABLE);//定时器开启中断,看到TIM_Period,数到头,给NVIC说一声TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//中断分组//分组方式,2位给抢占优先级,2位给响应优先级NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//从此处判断核心需求数量(2)//NVIC参数配置NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;NVIC_Init(&NVIC_InitStruct);}
void TIM2_IRQHandler()
{//信号判断,中断进一步确认if(TIM_GetITStatus(TIM2,TIM_IT_Update)){printf("not apple tree!\n");}//中断信号清除TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
tim.h
#include "stm32f4xx.h" // Device header
void Time_Init();
外部中断EXTI
上图紫色线路为轮询机制。黑色线路部分为内部中断。红色线路部分为外部中断NVIC处理的是中断信号,不是电平信号。
为什么GPIO需要接入EXTI产生中断信号,而不直接连到NVIC上?
usart,TIM,IIC这些芯片 内部器件本身就可以产生中断信号,因此就可以直接与NVIC连接。但是,光突突的GPIO本身就是一根电线,肯定不具备产生中断信号的功能。因此连接 EXTI,依靠EXTI产生中断信号。 原因2:中断通达数量比较少,EXTI的多路复选的功能扩充了接口。一个EXTI可 以接16个GPIO引脚呢。
exti.c
#include "stm32f4xx.h" // Device header
#include "BitBand.h"
#include "stdio.h"
#include "delay.h"void exti_key_init()
{//时钟配置RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);//给GPIO参数配置GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN;GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;GPIO_InitStruct.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_2;GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP;GPIO_InitStruct.GPIO_Speed=GPIO_High_Speed;GPIO_Init(GPIOE,&GPIO_InitStruct);//把GPIOE组的第2个引脚连接到EXTI2SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2);//把GPIOE组的第3个引脚连接到EXTI3SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);//EXTI参数配置//告诉EXTI2、3,看见上升沿,就给NVIC发送中断信号EXTI_InitTypeDef EXTI_InitStruct;EXTI_InitStruct.EXTI_Line=EXTI_Line2|EXTI_Line3;EXTI_InitStruct.EXTI_LineCmd=ENABLE;EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Rising;EXTI_Init(&EXTI_InitStruct);//分组方式,2位给抢占优先级,2位给响应优先级NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//从此处判断核心需求数量(2)NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel=EXTI2_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;NVIC_Init(&NVIC_InitStruct);//分组方式,2位给抢占优先级,2位给响应优先级NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);//从此处判断核心需求数量(2)NVIC_InitStruct.NVIC_IRQChannel=EXTI3_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;NVIC_Init(&NVIC_InitStruct);
}//中断服务子函数,给EXTI2服务
void EXTI2_IRQHandler()
{if(EXTI_GetITStatus(EXTI_Line2)){printf("key2 apple tree\n");}EXTI_ClearITPendingBit(EXTI_Line2);
}//中断服务子函数,给EXTI3服务
void EXTI3_IRQHandler()
{if(EXTI_GetITStatus(EXTI_Line3)){printf("key3 apple tree\n");}EXTI_ClearITPendingBit(EXTI_Line3);
}
exti.h
#include "stm32f4xx.h" // Device headervoid exti_key_init();