EXTI 外部和芯片外设中断控制器
NVIC 内核中断控制器
中断NVIC的特点
a.优先级数值越小,优先级越高
b.抢占优先级:高抢占优先级可以打断正在执行的低抢占优先级中断
c.响应优先级:抢占优先级相同时,响应优先级高的先执行,但不能互相打断
d.自然优先级:抢占和响应优先级相同时,自然优先级高的先执行,也不能打断
EXTI配置步骤:
1.使能GPIO时钟
2.设置GPIO输入模式 上/下拉/浮空输入
3.使能AFIO/SYSCFG时钟 设置AFIO(F1)/SYSCFG(F4,F7)开启寄存器
4.设置EXTI和IO对应关系 AFIO_EXTICR/SYSCFG_EXTICR
5.设置EXTI屏蔽,上/下降沿 设置IMR,RTSR/FTSR上升/下降沿触发器
6.设置NVIC 设置优先级分组,设置优先级,使能NVIC中断
7.设计终端服务函数
具体操作——HAL库设置步骤
1.使能GPIO __HAL_RCC_GPIOx_CLK_ENABLE()
2.初始化 HAL_GPIO_Init(GPIOx, &gpio_init_struct)
3.设置中断hal.c中已配置好 ,且分好组
4.NVIC设置优先级 HAL_NVIC_SetPriority(EXTI4_IRQn,2,0) //(中断号,抢占,响应)
5.NVIC使能中断 HAL_NVIC_EnableIRQ(EXTI4_IRQn)
6.设置中断服务函数 void EXTI4_IRQHandler(void)
EXTI配置步骤中NVIC设置步骤
1.设置中断分组(AIRCR寄存器,在hal.c文件中已默认分配好)
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2) 分组为2
2.设置中断优先级(IPRx)
HAL_NVIV_SetPriority(中断号,抢占优先级,响应优先级)
3.使能中断(ISERx)
HAL_NVIC_EnableIRQ(中断号)
EXTI配置步骤中设计终端服务函数流程
每一次中断都会产生回调函数,就相当于信号槽的关系,产生中断是信号,回调函数是槽,在回调函数中写逻辑判断并实现功能
EXTI中断代码
//exti.c
#include "./BSP/EXTI/exti.h"
#include "./SYSTEM/delay/delay.h"
void exti_init(void)
{
//1.GPIO时钟使能
GPIO_InitTypeDef gpio_init_struct; //定义GPIO结构体
__HAL_RCC_GPIOE_CLK_ENABLE();
//2.HAL_GPIO引脚初始化
gpio_init_struct.Pin = GPIO_PIN_4;
gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; //模式选择,EXTI的输入
gpio_init_struct.Pull = GPIO_PULLUP; //上下拉输入
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
//3.设置中断分组 此处在hal.c中已经分配,不用管
//4.设置中断优先级
HAL_NVIC_SetPriority(EXTI4_IRQn,2,0);//在hal_cortex.c找,因为pin是4所以选择该中断号,因为分组为2,所以抢占优先级和响应优先级范围都是0~4
//5.使能中断
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}
//6.设计中断服务函数
void EXTI4_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);//公共处理函数,在hal.c文件中
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4);//清除中断标志位
}
//下面这个回调函数属于上面的公共处理函数的成员
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(20); //消抖
if(GPIO_Pin == GPIO_PIN_4)
{
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==0) //按键引脚
{
HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_9); //LED翻转,这里是探索者DS0灯
}
}
}
//exti.h
#ifndef __EXTI_H
#define __EXTI_H
#include "./SYSTEM/sys/sys.h"
void exti_init(void);
//另外两个函数在hal.c库中已经声明好,所以不用再声明
#endif
main.c代码
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/EXTI/exti.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
led_init(); /* 初始化LED */
exti_init(); /* 初始化exti */
while(1)
{
LED1(1); /* LED1 灭 */
delay_ms(500);
LED1(0); /* LED1 亮 */
delay_ms(500);
}
}
定时器中断实验:
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能TIM2时钟
TIM_InternalClockConfig(TIM2); //可不要,选择时钟为TIM2
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //创建定时器结构体
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//一分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//时钟向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //重装载值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //分频系数
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级计数器才用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //综上,定时器每个一秒触发一次中断
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //因为清除更新标志位
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //绑定时钟更新中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //配置中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级1
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE); //使能定时器
}
void TIM2_IRQHandler(void) //中断函数会一直遍历,等待中断触发
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断中断触发标志位
{
Num ++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //中断清除中断标志位
}
}
外部触发中断
void Timer_Init(void)
{
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0引脚初始化为上拉输入
/*外部时钟配置*/
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);
//选择外部时钟模式2,时钟从TIM_ETR引脚输入
//注意TIM2的ETR引脚固定为PA0,无法随意更改
//最后一个滤波器参数加到最大0x0F,可滤除时钟信号抖动
/*时基单元初始化*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; //计数周期,即ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //预分频器,即PSC的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
/*中断输出配置*/
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定时器更新标志位
//TIM_TimeBaseInit函数末尾,手动产生了更新事件
//若不清除此标志位,则开启中断后,会立刻进入一次中断
//如果不介意此问题,则不清除此标志位也可
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启TIM2的更新中断
/*NVIC中断分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2
//即抢占优先级范围:0~3,响应优先级范围:0~3
//此分组配置在整个工程中仅需调用一次
//若有多个中断,可以把此代码放在main函数内,while循环之前
//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
/*NVIC配置*/
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择配置NVIC的TIM2线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC线路的抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设
/*TIM使能*/
TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断是否是TIM2的更新事件触发的中断
{
Num ++; //Num变量自增,用于测试定时中断
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}
/**
* 函 数:返回定时器CNT的值
* 参 数:无
* 返 回 值:定时器CNT的值,范围:0~65535
*/
uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2); //返回定时器TIM2的CNT
}