EXTI中断、TIM定时器

一.中断系统

  • 1.中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行。

  • 2.中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源。

  • 3.中断嵌套:当一个程序正在运行时,又有新的更高优先级的中断源申请终端,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回。

二.中断执行流程

三.STM32外设中断

四.NVIC基本结构


使用NVIC统一管理中断,每个中断都有16个可编写的优先等级,可对优先级分组,进一步设置抢占优先级和响应优先级

其中NVIC只管分配中断优先级,反馈给CPU

五.优先级分组


优先级的值越小,优先级就越高。

抢占优先级可以打断CPU正在的操作,优先处理
响应优先级可以插队,排在NVIC的优先级1的位置上。

六.EXTI


中断响应:申请中断,让CPU执行中断函数
事件中断:外部中断检测到引脚变化时,中断信号不会通向CPU,而是通向其他外设,来触发其他外设的操作

事件响应不会触发中断,会触发外设操作

七.EXTI基本结构

AFIO:在PA0,PB0,PC0中选择其中一个,PA1,PB1,PC1中选择一个······共16个连接到EXTI,即相同的Pin不能同时触发中断

八.AFIO复用IO口

九.EXTI框图


此为或门,可由多个输入,仅有一个输出,输入端只要有一个高电平,则输出高电平


此为与门,可由多个输入,仅有一个输出,输入端只要有一个低电平,则输出低电平

初始化例子:
配置过程:

  1. 配置RCC,把涉及的外设时钟都打开,如GPIO,AFIO,EXTI,NVIC等
  2. 配置GPIO,选择端口为输出模式
  3. 配置AFIO,选择我们用的GPIO连接到EXTI上
  4. 配置EXTI,选择边沿触发方式和相应方式
  5. 配置NVIC,选择合适的优先级
void CountSensor_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		
        //开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		
        //开启AFIO的时钟,外部中断必须开启AFIO的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						
        //将PB14引脚初始化为上拉输入
	
	/*AFIO选择中断引脚*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//将外部中断的14号线映射到GPIOB,即选择PB14为外部中断引脚
	
	/*EXTI初始化*/
	EXTI_InitTypeDef EXTI_InitStructure;						//定义结构体变量
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;					//选择配置外部中断的14号线
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;					//指定外部中断线使能
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			//指定外部中断线为中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		//指定外部中断线为下降沿触发
	EXTI_Init(&EXTI_InitStructure);								//将结构体变量交给EXTI_Init,配置EXTI外设
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;		//选择配置NVIC的EXTI15_10线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
}
void Encoder_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//开启AFIO的时钟,外部中断必须开启AFIO的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB0和PB1引脚初始化为上拉输入
	
	/*AFIO选择中断引脚*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//将外部中断的1号线映射到GPIOB,即选择PB1为外部中断引脚
	
	/*EXTI初始化*/
	EXTI_InitTypeDef EXTI_InitStructure;						//定义结构体变量
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;		//选择配置外部中断的0号线和1号线
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;					//指定外部中断线使能
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			//指定外部中断线为中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		//指定外部中断线为下降沿触发
	EXTI_Init(&EXTI_InitStructure);								//将结构体变量交给EXTI_Init,配置EXTI外设
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;			//选择配置NVIC的EXTI0线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设

	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;			//选择配置NVIC的EXTI1线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;			//指定NVIC线路的响应优先级为2
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
}

注意,中断标志位要恢复:

    void EXTI15_10_IRQHandler(void)//中断函数的格式
{
	if (EXTI_GetITStatus(EXTI_Line14)==SET)//判断是否为EXTI14进来,返回值为 SET or RESET
	{
		EXTI_ClearITPendingBit(EXTI_Line14);//清除中断函数的标志位,否则程序将一直卡在中断函数中
	}
	
}

十.TIM简介

十一.TIM类型


STM32F103C8T6:TIM1.TIM2.TIM3.TIM4;

APB2总线性能更高

十二.基本定时器

PSC预分频器   写0,则为不分频或者1分频。
             写1,那就是2分频。输出频率=输入频率/2=36MHz
             写2,那就是3分频,输出频率=输入频率/3=24MHz
             ...

十三.基本定时器框图

自动重装寄存器存储写入的记数目标,CNT计数器不断自增,当计数值=自动重装值时,计时时间到,产生中断信号,清零计数器

十四.通用定时器框图

基本定时器仅支持向上自增这一种计数模式。
通用定时器支持:
1.向上自增:从0自增至自动重装值后中断,清零
2.向下自减:从自动重装值减少至0后中断,恢复自动重装值
3.中央对齐:从0自增至重装值,中断,再从重装值减少到0,中断

外部时钟模式1的输入可以是ETR引脚、其他定时器、CH1引脚的边沿、CH1引脚和CH2引脚。

十五.高级定时器框图

重复次数计数器:每隔N个周期再更新一次

十六.定时中断基本结构

十七.预分频器时序

CNT_EN:高电平计数器运行,低电平停止
预分频控制寄存器:防止中途更改数值造成错误
重装计数值:FC

十八.计数器时序

初始化例子:
配置顺序:

  1. RCC开启时钟,此时基准时钟和整个外设时钟都应打开
  2. 选择时基单元的时钟源
  3. 配置时基单元
  4. 配置输出中断控制,允许更新中断输出到NVIC
  5. 配置NVIC,打开中断通道,并配置中断优先级

void Timer_Init(void)
{
/开启时钟/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟

/*配置时钟源*/
TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟

/*时基单元初始化*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				//计数周期,即ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 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更新事件的中断标志位
		//中断标志位必须清除否则中断将连续不断地触发,导致主程序卡死
	}
}

十九.输出比较简介

同样的,IC(Input Capture):输入捕获
CCR(Capture/Compare Register)捕获/比较寄存器
框图如下:

在输出比较时,这块电路会比较CNT和CCR的值,CNT是计数自增,当CNT大于、小于、等于CCR时,OC输出会对应的置1,置0,置1,置0.

二十.PWM简介


LED闪烁频率足够快,可以约为发出不同的亮度(视觉暂留)
占空比越大,等效的模拟电压越趋近于高电平。
占空比越小,等效的模拟电压越趋近于低电平。

二十一.输出比较通道


模式由TIMx_CCMR1配置

二十二.PWM基本结构


蓝色线:CNT 黄色线:ARR
红色线:CCR
通过调整CCR设置的值,来控制输出的占空比

二十三.PWM参数计算

posted @   Acloudyskye  阅读(190)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示