STM32 基础知识(探索者开发板)--61讲 EXTI

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
}

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值