Stm32f103学习记录:TIM和EXTI代码实例详解

一、EXTI配置

打通这一路的电路即可

GPIO             ——》       AFIO        ——》    EXTI        —–》      NVIC

step1:打开RCC时钟

注意:RCC管的是外面的,NVIC住在皇宫里面,RCC管不着,可参照stm32f103c8t6芯片原

           理图

step2:配置GPIO,输入模式

step3:AFIO,选择这一路的GPIO

注意:相同的pin不能同时申请中断如PA1和PB1

step4:配置EXTI,选择边沿触发方式or触发响应方式

step5:配置NVIC,给中断配置抢占优先级和响应优先级

1.打开RCC时钟

rcc.h

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
//使用
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE)

 AFIO也在APB2上


RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE)

2.配置GPIO,输入模式

/*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);						//将PA1和PA2引脚初始化为推挽输出

GPIO_Mode_AIN 模拟输入

。。。IN_FLOATING 浮空输入

。。。IPD    下拉输入 

。。。IPU   上拉输入

。。。Out_OD   开漏输出

。。。Out_PP    推挽输出

。。。AF_OD     复用开漏输出

。。。AF_PP     复用推挽输出

推挽输出时:寄存器为1,输出1,为0,输出0

开漏输出时:寄存器为1,输出断开(高阻模式),为0,输出0

(作用:1.可作为通信协议输出,输出更高电平2.只需在IO口外接上拉电阻到5V )

库函数(gpio.h):

读取某引脚输入的电平(0为低,1为高)
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
读取整个输入寄存器的数据
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
读取某引脚输出的电平(0为低,1为高)
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
读取整个输出寄存器的数据
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
设为高电平
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
设为低电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
写某个引脚的数据
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
写数据
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

锁定引脚配置,放止意外修改
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
引脚重映射
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
配置AFIO数据选择器,选择要配置中断的引脚
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
以太网相关
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);

 3.AFIO,选择这一路的GPIO

void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
选择GPIO pin作为外部中断线
GPIO_PortSource 要选择的GPIO pin
GPIO_PinSource 哪个引脚

以PB14为例:
GPIO_PortSourceGPIOB
GPIO_PinSource14
4.配置EXTI,选择边沿触发方式or触发响应方式 
/*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_Mode_Event事件模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		//指定外部中断线为下降沿触发
            Trigger_Rising上升沿
            Trigger_Falling下降沿
            Trigger_Rising_Falling双边沿
EXTI_Init(&EXTI_InitStructure);								//将结构体变量交给EXTI_Init,配置EXTI外设
 库函数(exti.h): 
清除EXTI配置
void EXTI_DeInit(void);
配置EXTI外设
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
用结构体配置EXTI外设
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
软件触发中断
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);


这两个一般在主函数用,get完flag一定要clear,否则重复触发中断!!!

看看挂起寄存器的标志位是否为1,
9-15会触发同一个中断,这样可以判断哪个线触发了中断
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
清除flag(必须!!!)
void EXTI_ClearFlag(uint32_t EXTI_Line);


这两个一般在中断中用!!!

看看挂起寄存器的标志位是否为1,
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
清除flag(必须!!!)
void EXTI_ClearITPendingBit(uint32_t EXTI_Line); 

 

5. 配置NVIC,给中断配置抢占优先级和响应优先级

	/*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外设
库函数(misc.h): 
 The table below gives the allowed values of the pre-emption priority and subpriority according
 to the Priority Grouping configuration performed by NVIC_PriorityGroupConfig function
  ============================================================================================================================
    NVIC_PriorityGroup   | NVIC_IRQChannelPreemptionPriority | NVIC_IRQChannelSubPriority  | Description
  ============================================================================================================================
   NVIC_PriorityGroup_0  |                0                  |            0-15             |   0 bits for pre-emption priority
                         |                                   |                             |   4 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------
   NVIC_PriorityGroup_1  |                0-1                |            0-7              |   1 bits for pre-emption priority
                         |                                   |                             |   3 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_2  |                0-3                |            0-3              |   2 bits for pre-emption priority
                         |                                   |                             |   2 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_3  |                0-7                |            0-1              |   3 bits for pre-emption priority
                         |                                   |                             |   1 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_4  |                0-15               |            0                |   4 bits for pre-emption priority
                         |                                   |                             |   0 bits for subpriority                       
  ============================================================================================================================


中断分组
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
设置中断向量表
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
低功耗配置
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);
时钟源
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);

6.写中断函数

stm32中中断函数名称是固定的,名字参照启动文件startup_stm32f10x_md.s,名字以IQRHandler结尾的,就是中断函数

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                ; External Interrupts
                DCD     WWDG_IRQHandler            ; Window Watchdog
                DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
                DCD     TAMPER_IRQHandler          ; Tamper
                DCD     RTC_IRQHandler             ; RTC
                DCD     FLASH_IRQHandler           ; Flash
                DCD     RCC_IRQHandler             ; RCC
                DCD     EXTI0_IRQHandler           ; EXTI Line 0
                DCD     EXTI1_IRQHandler           ; EXTI Line 1
                DCD     EXTI2_IRQHandler           ; EXTI Line 2
                DCD     EXTI3_IRQHandler           ; EXTI Line 3
                DCD     EXTI4_IRQHandler           ; EXTI Line 4
                DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1
                DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2
                DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3
                DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4
                DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5
                DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6
                DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7
                DCD     ADC1_2_IRQHandler          ; ADC1_2
                DCD     USB_HP_CAN1_TX_IRQHandler  ; USB High Priority or CAN1 TX
                DCD     USB_LP_CAN1_RX0_IRQHandler ; USB Low  Priority or CAN1 RX0
                DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1
                DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE
                DCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5

例如(以下代码来自B站江科大):

/**
  * 函    数:EXTI0外部中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET)		//判断是否是外部中断0号线触发的中断
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)		//PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向
			{
				Encoder_Count --;					//此方向定义为反转,计数变量自减
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line0);			//清除外部中断0号线的中断标志位
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}

二、TIM配置

分频:输入信号时会以一定频率采样多次,若这N个采样点电平相同,就代表信号稳定了,输出采样值,否则保持上一个值,这个频率与分频值和时钟频率相关。而N的值是和ExtTRGFilter相关的


定时频率(CK_CNT_OV,计数器溢出频率)= CK_CNT(72MHz)/【(PSC+1)*(ARR+1)】

1.System下新建Timer.h Timer.c

2.放止重复引头文件,我们这样处理

#ifndef __TIMER_H
#define __TIMER_H

#endif

Timer.c这边还是#include "stm32f10x.h"    

我们只要让硬件电路打通,就可以让TIM工作 

库函数(tim.h,拖到最后) 
void TIM_DeInit(TIM_TypeDef* TIMx);

时基单元初始化
TIMx选定时器
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

时基单元初始化
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

使能计数器
FunctionalState NewState(使能或失能)
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);

中断输出配置
uint16_t TIM_IT          用哪个中断
FunctionalState NewState(使能或失能)
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

选择内部时钟
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);

选择ITRx其他定时器的时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);

选择TIX捕获通道的时钟
uint16_t TIM_TIxExternalCLKSource   TIX的引脚
uint16_t TIM_ICPolarity, uint16_t ICFilter  极性选择和滤波器  
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter);

选择ETR外部时钟模式1输入的时钟
uint16_t TIM_ExtTRGPrescaler预分频
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);

选择ETR外部时钟模式2输入的时钟
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                             uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);

单独配置ETR引脚的预分频器、极性、滤波器等
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);



有些数值常常修改,要改是在用Init太麻烦,这里有几个修改单个参数的:

单独写预分频值
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
改变计数模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
自动重装计时器预装功能
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
给计数器写入一个值
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
手动给一个自动重装值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
过去当前计数器的值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
预分频值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);

FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

初始化函数
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_CKD_DIV1 不分频
TIM_CKD_DIV2 2分频
TIM_CKD_DIV4 4分频
    //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_CounterMode_Down向下
TIM_CounterMode_CenterAligned1中央对齐模式
    //计数周期,即ARR自动重装的值

计几个数字再触发中断
改成1000的话,触发中断的频率会快10倍
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
    //预分频器,即PSC的值			
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;	
以多快的速度计数
改成720就会以原来的10倍计数

这样处理后频率为10K,在10K频率下记10000个数,时间就是1s	
    //重复计数器,高级定时器才会用到			
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
    //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元				
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				
	


	/*中断输出配置*/
	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,定时器开始运行
	
}
此处用到的中断函数是TIM2_IQRHandler 
/* 定时器中断函数,可以复制到使用它的地方
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)  //看看中断是不是从TIM2进来的
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
*/

但是注意!!!刚上电后会触发中断,原因在注释中解释了

三、外部时钟源选择

硬件电路

对射式红外传感器,Do接PA0(PA0是TIM2的外部时钟引脚)

程序编写

TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted,0x00)

TIM_ExtTRGPSC_OFF:关闭分频

TIM_ExtTRGPolarity_Inverted:反向,低电平或下降沿有效

TIM_ExtTRGPolarity_NonInverted:不反向,反之

0x00: 不用滤波器

GPIO这边我们用上拉输入,此处不再赘述。一般输入功率小的时候用浮空,防止上拉的影响,否则不建议用浮空,电平会跳来跳去

 术语表:

NVIC Nested Vectored Interrupt Controller 嵌套向量中断控制器

EXTI  External Interrupt                               外部中断

AFIO Alternate function I/O 复用IO,此处是中断引脚选择器

GPIO General-purpose input/output             通用型之输入输出

作者:python_use

物联沃分享整理
物联沃-IOTWORD物联网 » Stm32f103学习记录:TIM和EXTI代码实例详解

发表评论