江协科技STM32课程笔记(四)—定时器TIM(输入捕获)

一、定时器输入捕获

左半边CH引脚输入到捕获比较寄存器部分就是输入捕获,捕获比较寄存器右边为输出比较部分。4个输入捕获和输出比较通道,共用4个CCR寄存器,CH1到CH4,4个通道的引脚也是共用的。 所以对于同一个定时器,输入捕获和输出比较,只能使用其中一个,不能同时使用。

输入捕获模式下,当CH通道输入引脚出现指定电平跳变(上下沿)时,那输入滤波和边沿检测电路就会检测到这个上升沿,让输入捕获电路产生动作将当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数。

针对上图解析:

1、图98中输入捕获部分左上角的异或门是为了三相无刷电机设计的,当三个输入引脚的任何一个有电平翻转时,输出引脚就产生一次电平翻转,之后输出通道数据选择器,到达输入捕获通道1。

2、输入信号过来后经过输入滤波器和边沿检测器,输入滤波器可以对信号进行滤波,避免一些高频的毛刺信号误触发;然后边沿检测器,和外部中断一样,可以选择高电平触发或者低电平触发。滤波器由CCM1寄存器的ICF位控制,以采样频率对输入信号进行采样,当连续N个值都为高电平,输出才为高电平;连续N个值都为低电平,输出才为低电平。

3、其实是设计了两套滤波和边沿检测电路,第一套电路经过滤波和极性选择得到TI1FP1(TI1 Filter Polarity 1),输入给通道1的后续电路;第二套电路,经过另一个滤波和极性选择得到TI1FP2(TI1 Filter Polarity 2),输入给通道2的后续电路。同理,下面TI2信也会得到TI2FP1和TI2FP2,TI2FP1输入给上面,TI2FP2输入给下面。在这里两个信号进来,可以选择各走各的,也可以选择进行一个交叉,让CH2引脚输入给通道1,或者CH1引脚输入给通道2。一是用于灵活切换后续捕获电路的输入;二是可以把一个引脚的输入,同时映射到两个捕获单元,这也是PWMI模式的经典结构。第一个捕获通道使用上升沿触发,用来捕获周期,第二个通道使用下降沿触发,用来捕获占空比。CC1S位控制数据选择器。

4、预分频器,每个通道各有一个,可以选择对前面的信号进行分频,分频之后的触发信号,就可以触发捕获电路进行工作了,每来一个触发信号,CNT的值,就会向CCR转运一次,转运的同时,会发生一个捕获事件,这个事件会在状态寄存器置标志位,同时也可以产生中断。

二、主从模式

其中主模式可以将定时器内部的信号,映射到TRGO引脚,用于触发别的外设,所以这部分叫做主模式。从模式就是接收其它外设或者自身外设的一些信号。用于控制自身定时器的运行,也就是被别的信号控制,所以这部分叫从模式。触发源选择就是选择从模式的触发信号源的,可以认为它是从模式的一部分,触发源选择,选择指定的一个信号,得到TRGI,TRGI去触发从模式,从模式可以在这个列表里,选择一项操作来自动执行,若果想要完成TI1FP1信号自动触发CNT清零,那触发源选择,就可以选中这里的TI1FP1,从模式执行的操作,就可以选择执行Reset的操作。这样TI1FP1的信号就可以自动触发从模式,从模式清零CNT,实现硬件全自动测量。

CR2寄存器控制

SMCR寄存器控制

三、捕获结构

这边直接介绍PWMI结构,测频就是去掉下半部分

PWMI模式使用了两个通道同时捕获一个引脚,可以同时测量周期和占空比,上首先TI1FP1配置上升沿触发,触发捕获和清零CNT,正常地捕获周期,TI1FP2配置为下降沿触发,通过交叉通道去触发通道2的捕获单元。最开始上升沿,CCR1捕获,同时清零CNT,之后CNT一直++,然后在下降沿这个时刻,触发CCR2捕获,所以这是CCR2的值,就是CNT从上升沿到下降沿的计数值,就是高电平期间的计数值;CCR2捕获,并不触发CNT清零,所以CNT继续++,直到下一次上升沿,CCR1捕获周期,CNT清零,CCR1就是一整个周期的计数值,CCR2就是高电平期间的计数值,用CCR2/CCR1就是占空比了。另外这里,可以用两个通道同时捕获第一个引脚的输入,这样通道2的前面部分就没有用到了。

四、使用

测频法:一定时间内多少个上升沿,适合高频。fx=N/T

测周法:一个周期用标准频率下计数多少个,适合低频fx=fc/N

中界频率:测频法和测周法误差相等的频率点。fm = 根号下(fc/T)。利用上面两个等式的N相等获得。

cpp 复制代码
void TIM_DeInit(TIM_TypeDef* TIMx);
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
/*通过结构体配置输入捕获单元的函数,第一个参数选择定时器,第二个参数是包含各个配置的结构体
输入捕获和输出比较都有4个通道,ICInit4个通道共用一个函数,在结构体里会额外有一个参数,选择
具体配置哪个通道,因为可能有交叉通道的配置,所以函数合在一起比较方便*/
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
/*输入捕获的初始化函数,和上一个函数类似,都是用于初始化输入捕获单元的,但是上一个函数只是单一的配置
一个通道,这个函数可以快速配置两个通道,把外设电路配置成PPT里展示的PWMI模式*/
void TIM_BDTRConfig(TIM_TypeDef* TIMx, TIM_BDTRInitTypeDef *TIM_BDTRInitStruct);
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);
/*给输入捕获结构体赋一个初始值*/
void TIM_BDTRStructInit(TIM_BDTRInitTypeDef* TIM_BDTRInitStruct);
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
void TIM_GenerateEvent(TIM_TypeDef* TIMx, uint16_t TIM_EventSource);
void TIM_DMAConfig(TIM_TypeDef* TIMx, uint16_t TIM_DMABase, uint16_t TIM_DMABurstLength);
void TIM_DMACmd(TIM_TypeDef* TIMx, uint16_t TIM_DMASource, FunctionalState NewState);
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter);
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
                             uint16_t ExtTRGFilter);
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                             uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
                   uint16_t ExtTRGFilter);
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_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
/*选择输入触发源TRGI,从模式的触发源选择,调用函数选择从模式的触发源*/
void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,
                                uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);
void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_SelectCOM(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_SelectCCDMA(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_CCPreloadControl(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);
void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);
void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);
void TIM_UpdateDisableConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_UpdateRequestConfig(TIM_TypeDef* TIMx, uint16_t TIM_UpdateSource);
void TIM_SelectHallSensor(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_SelectOnePulseMode(TIM_TypeDef* TIMx, uint16_t TIM_OPMode);
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);
/*选择输出触发源TRGO,选择主模式输出的触发源*/
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);
/*选择从模式*/
void TIM_SelectMasterSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_MasterSlaveMode);
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
/*读写的CCR寄存器,输出比较模式下,CCR是只写的,要用SetCompare写入*/
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
/*上面4个函数分别配置通道1、2、3、4的分频器,这个参数结构体里也可以配置,是一样的效果*/
void TIM_SetClockDivision(TIM_TypeDef* TIMx, uint16_t TIM_CKD);
uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);
/*上面4个函数分别读取4个通道的CCR值,和上面TIM_SetCompare3是对应的,读写的都是CCR寄存器
输出比较模式下,CCR是只写的,要用SetCompare写入,输入捕获模式下,CCR是只读的,要用GetCapture读出*/
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);

测周法测频率

cpp 复制代码
uint16_t psc = 720;

void PWM_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟

	/*GPIO重映射*/
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);			//开启AFIO的时钟,重映射必须先开启AFIO的时钟
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);			//将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);		//将JTAG引脚失能,作为普通GPIO引脚使用

		
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA1引脚初始化为复用推挽输出	
																	//受外设控制的引脚,均需要配置为复用模式		
	/*对于普通的开漏/推挽输出,引脚的控制全是来自输出数据寄存器的,如果想用定时器来控制引脚,那就需要使用复用开漏/推挽输出的模式
	这里输出数据寄存器将被断开,输出控制全将转移给片上外设,只有把GPIO设置成复用推挽输出,引脚的控制权才能交给片上外设,PWM波形才能
	通过引脚输出*/
	/*配置时钟源*/
	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 = 100 - 1;					//计数周期,即ARR的值,决定频率和占空比
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;				//预分频器,即PSC的值,影响频率
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
	
	/*输出比较初始化*/
	TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量
	TIM_OCStructInit(&TIM_OCInitStructure);							//结构体初始化,若结构体没有完整赋值
																	//则最好执行此函数,给结构体所有成员都赋一个默认值
																	//避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				//输出比较模式,选择PWM模式1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		//输出极性,选择为高,若选择极性为低,则输出高低电平取反
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);						//将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

//影响占空比
void PWM_SetCompare1(int16_t Compare)
{
	TIM_SetCompare1(TIM2,Compare);
}

/**
  * 函    数:PWM设置PSC
  * 参    数:Prescaler 要写入的PSC的值,范围:0~65535
  * 返 回 值:无
  * 注意事项:PSC和ARR共同决定频率,此函数仅设置PSC的值,并不直接是频率
  *           频率Freq = CK_PSC / (PSC + 1) / (ARR + 1)
  */
void PWM_SetPrescaler(uint16_t Prescaler)
{
	/*
	@param  TIM_PSCReloadMode: specifies the TIM Prescaler Reload mode
  *   This parameter can be one of the following values:
  *     @arg TIM_PSCReloadMode_Update: The Prescaler is loaded at the update event.等待更新事件触发重装
  *     @arg TIM_PSCReloadMode_Immediate: The Prescaler is loaded immediately.立即重装
  * */
	TIM_PrescalerConfig(TIM2, Prescaler-1, TIM_PSCReloadMode_Immediate);		//设置PSC的值
}

/**
  * 函    数:输入捕获初始化
  * 参    数:无
  * 返 回 值:无
  */
void IC_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA6引脚初始化为上拉输入
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM3);		//选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;               //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;               //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元
	
	/*输入捕获初始化*/
	TIM_ICInitTypeDef TIM_ICInitStructure;							//定义结构体变量
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				//选择配置定时器通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;							//输入滤波器参数,可以过滤信号抖动
	/* ETF[3:0]:外部触发滤波 (External trigger filter)
    这些位定义了对ETRP信号采样的频率和对ETRP数字滤波的带宽。实际上,数字滤波器是一个
    事件计数器,它记录到N个事件后会产生一个输出的跳变。*/
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;		//极性,选择为上升沿触发捕获
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;			//捕获预分频,选择不分频,每次信号都触发捕获
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;	//输入信号交叉,选择直连通道,不交叉
	TIM_ICInit(TIM3, &TIM_ICInitStructure);							//将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道
	
	/*选择触发源及从模式*/
	/*利用TI1FP1触发在每次周期开始时来自动清除CNT计数器 */
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);					//触发源选择TI1FP1
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);					//从模式选择复位
																	//即TI1产生上升沿时,会触发CNT归零
	//见手册图123
	/*TIM使能*/
	TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定时器开始运行
}

uint32_t IC_GetFreq(void)
{
	return 1000000/TIM_GetCapture1(TIM3);//Freq = fc/N  fc=72M/(PSC+1)=1MHz
}

void Key_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB1和PB11引脚初始化为上拉输入
}
void Key_GetNum(void)
{
	uint8_t KeyNum = 0;		//定义变量,默认键码值为0
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)			//读PB1输入寄存器的状态,如果为0,则代表按键1按下
	{
		sysDelayms(20);											//延时消抖
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);	//等待按键松手
		sysDelayms(20);											//延时消抖
		KeyNum = 1;												//置键码为1
	}
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)			//读PB11输入寄存器的状态,如果为0,则代表按键2按下
	{
		sysDelayms(20);											//延时消抖
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);	//等待按键松手
		sysDelayms(20);											//延时消抖
		KeyNum = 2;												//置键码为2
	}
	
	switch(KeyNum)
	{
		case 1 : psc = 720 * 2;break;
		case 2 : psc = 720 / 2;break;
	}
}


int main(void)
{
	OLED_Init();				//LED初始化
	PWM_Init();					//PWM初始化
	IC_Init();					//初始化输入捕获
	Key_Init();
	OLED_ShowString(1,1,"Freq=00000Hz");
	PWM_SetCompare1(50);        //Duty占空比=CCR/100
	PWM_SetPrescaler(720);    //freq = 72M/(psc+1)/100
	while(1)
	{
		Key_GetNum();
		TIM_PrescalerConfig(TIM2, psc-1, TIM_PSCReloadMode_Immediate);
		OLED_ShowNum(1,6,IC_GetFreq(),5);
	}


}

PWMI测频率和占空比,使用TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);初始化

cpp 复制代码
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "OLED.h"
#include "Delay.h"
uint16_t psc = 720;
uint16_t duty = 50;

void PWM_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟

	/*GPIO重映射*/
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);			//开启AFIO的时钟,重映射必须先开启AFIO的时钟
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);			//将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);		//将JTAG引脚失能,作为普通GPIO引脚使用

		
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA1引脚初始化为复用推挽输出	
																	//受外设控制的引脚,均需要配置为复用模式		
	/*对于普通的开漏/推挽输出,引脚的控制全是来自输出数据寄存器的,如果想用定时器来控制引脚,那就需要使用复用开漏/推挽输出的模式
	这里输出数据寄存器将被断开,输出控制全将转移给片上外设,只有把GPIO设置成复用推挽输出,引脚的控制权才能交给片上外设,PWM波形才能
	通过引脚输出*/
	/*配置时钟源*/
	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 = 100 - 1;					//计数周期,即ARR的值,决定频率和占空比
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;				//预分频器,即PSC的值,影响频率
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
	
	/*输出比较初始化*/
	TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量
	TIM_OCStructInit(&TIM_OCInitStructure);							//结构体初始化,若结构体没有完整赋值
																	//则最好执行此函数,给结构体所有成员都赋一个默认值
																	//避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				//输出比较模式,选择PWM模式1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		//输出极性,选择为高,若选择极性为低,则输出高低电平取反
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);						//将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

//影响占空比
void PWM_SetCompare1(int16_t Compare)
{
	TIM_SetCompare1(TIM2,Compare);
}

/**
  * 函    数:PWM设置PSC
  * 参    数:Prescaler 要写入的PSC的值,范围:0~65535
  * 返 回 值:无
  * 注意事项:PSC和ARR共同决定频率,此函数仅设置PSC的值,并不直接是频率
  *           频率Freq = CK_PSC / (PSC + 1) / (ARR + 1)
  */
void PWM_SetPrescaler(uint16_t Prescaler)
{
	/*
	@param  TIM_PSCReloadMode: specifies the TIM Prescaler Reload mode
  *   This parameter can be one of the following values:
  *     @arg TIM_PSCReloadMode_Update: The Prescaler is loaded at the update event.等待更新事件触发重装
  *     @arg TIM_PSCReloadMode_Immediate: The Prescaler is loaded immediately.立即重装
  * */
	TIM_PrescalerConfig(TIM2, Prescaler-1, TIM_PSCReloadMode_Immediate);		//设置PSC的值
}

/**
  * 函    数:输入捕获初始化
  * 参    数:无
  * 返 回 值:无
  */
void IC_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA6引脚初始化为上拉输入
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM3);		//选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;               //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;               //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元
	
	/*输入捕获初始化*/
	TIM_ICInitTypeDef TIM_ICInitStructure;							//定义结构体变量
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				//选择配置定时器通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;							//输入滤波器参数,可以过滤信号抖动
	/* ETF[3:0]:外部触发滤波 (External trigger filter)
    这些位定义了对ETRP信号采样的频率和对ETRP数字滤波的带宽。实际上,数字滤波器是一个
    事件计数器,它记录到N个事件后会产生一个输出的跳变。*/
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;		//极性,选择为上升沿触发捕获
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;			//捕获预分频,选择不分频,每次信号都触发捕获
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;	//输入信号交叉,选择直连通道,不交叉
	//TIM_ICInit(TIM3, &TIM_ICInitStructure);							//将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道
	TIM_PWMIConfig(TIM3,&TIM_ICInitStructure);
	//将结构体变量交给TIM_PWMIConfig,配置TIM3的输入捕获通道
	//此函数同时会把另一个通道配置为相反的配置,实现PWMI模式

	/*选择触发源及从模式*/
	/*利用TI1FP1触发在每次周期开始时来自动清除CNT计数器 */
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);					//触发源选择TI1FP1
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);					//从模式选择复位
																	//即TI1产生上升沿时,会触发CNT归零
	//见手册图123
	/*TIM使能*/
	TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定时器开始运行
}

uint32_t IC_GetFreq(void)
{
	return 1000000/(TIM_GetCapture1(TIM3)+1);//Freq = fc/N  fc=72M/(PSC+1)=1MHz,+1是因为误差,上升沿来正好有一个没记到
}

uint32_t IC_GetDuty(void)
{
	return (TIM_GetCapture2(TIM3)+1)*100 / (TIM_GetCapture1(TIM3)+1);//占空比Duty = CCR2 / CCR1 * 100
}


void Key_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB1和PB11引脚初始化为上拉输入
}
void Key_GetNum(void)
{
	uint8_t KeyNum = 0;		//定义变量,默认键码值为0
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)			//读PB1输入寄存器的状态,如果为0,则代表按键1按下
	{
		sysDelayms(20);											//延时消抖
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);	//等待按键松手
		sysDelayms(20);											//延时消抖
		KeyNum = 1;												//置键码为1
	}
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)			//读PB11输入寄存器的状态,如果为0,则代表按键2按下
	{
		sysDelayms(20);											//延时消抖
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);	//等待按键松手
		sysDelayms(20);											//延时消抖
		KeyNum = 2;												//置键码为2
	}
	
	switch(KeyNum)
	{
		case 1 : psc = 720 * 2;duty = 75;break;
		case 2 : psc = 720 / 2;duty = 25;break;
	}
}


int main(void)
{
	OLED_Init();				//LED初始化
	PWM_Init();					//PWM初始化
	IC_Init();					//初始化输入捕获
	Key_Init();
	OLED_ShowString(1,1,"Freq=00000Hz");
	OLED_ShowString(2, 1, "Duty:00%");
	PWM_SetCompare1(duty);        //Duty占空比=CCR/100
	PWM_SetPrescaler(psc);    //freq = 72M/(psc+1)/100
	while(1)
	{
		Key_GetNum();
		TIM_PrescalerConfig(TIM2, psc-1, TIM_PSCReloadMode_Update);
		PWM_SetCompare1(duty);
		OLED_ShowNum(1,6,IC_GetFreq(),5);
		OLED_ShowNum(2,6,IC_GetDuty(),2);
	}


}

五、编码器测速

编码器接口相当于一个带有方向控制的外部时钟,它同时控制值CNT的计数时钟和计数方向。每隔一段时间取一次CNT的值,再把CNT清零,每次取出来的值就表示编码器的速度。这个编码器测速实际上就是测频法测正交脉冲的频率,CNT计次,然后每隔一段时间取一次计次,就是测频法的思路。只不过这个编码器计次能根据旋转方向,自增或者自减计次。**每个高级定时器和通用定时器都拥有1个编码器接口。**编码器的两个输入引脚就是每个定时器的CH1和CH2引脚,CH3和CH4不能接编码器。

正交编码器一般可以测量位置,或者带有方向的速度值,它一般有两个信号输出引脚,一个是A相、一个是B相。当正转时,A相提前B相90度;反转时A相滞后B相90度。

编码器接口要接到编码器的A相和B相, 编码器接口的两个引脚借用了输入捕获单元的前两个通道,所以最终编码器的输入引脚,就是定时器的CH1和CH2这两个引脚。如图所示

输入捕获的前两个通道,通过GPIO接口编码器的A、B相, 然后通过滤波器和边沿检测极性选择,产生TI1FP1和TI2FP2,通向编码器接口,编码器接口通过预分频器控制CNT计数器的时钟,同时,编码器接口根据编码器的旋转方向控制CNT的计数方向。编码器正转时,CNT自增,编码器反转时,CNT自减。一般设置ARR为65535,最大量程,可以利用补码的特性得到负数。比如CNT初始为0,正转,CNT自增,0、1、2、3、4、5、6、7等等;反转的时候,CNT自减,0下一个数就是65535、然后是65534...但是没关系,会进行一个操作,直接把这个16位的无符号数转换为16位的有符号数,根据补码的定义,这个65535就对应-1。

如果TI1反相

cpp 复制代码
void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,
                                uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);
/*第一个参数选择定时器
第二个参数选择编码器模式,
然后后面两个参数分别选择通道1和通道2的电平极性*/

代码:

cpp 复制代码
/*编码器初始化函数*/
/*
第1步:RCC开启时钟,开启GPIO和定时器的时钟
第2步:配置GPIO,这里需要把PA6和PA7配置成输入模式
第3步:配置时基单元,这里预分频器我们一般选择不分频,自动重装,一般给最大65535,只需要CNT执行计数就行了
第4步:配置输入捕获单元,这里输入捕获单元只有滤波器和极性这两个参数有用
第5步:配置编码器接口模式
第6步:调用TIM_Cmd,启动定时器
电路初始化完成之后,CNT就会随着编码器旋转而自增自减,如果想测量编码器的值,直接读出CNT的值就行了;
如果想测量编码器的速度和方向,那就需要每隔一段固定的闸门时间,取出一次CNT,然后再把CNT清零。
*/
void Encoder_Init(void)
{
	/*第1步:RCC开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);               //开启挂载在APB1上的TIM3时钟作为输入捕获的定时器
	/*第2步:GPIO初始化*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);        
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;   
	/*上拉输入(可以选择上拉、下拉或者浮空:根据接在这个引脚的外部模块输出的默认电平,如果外部模块空闲默认输出高电平,就选择上拉输入,
	默认输入高电平,如果外部模块空闲默认输出低电平,配置下拉输入,默认输入低电平,和外部模块保持默认状态一致,防止默认电平打架;
	如果不确定外部模块输出的默认状态或者外部信号输出功率非常小,这时就尽量选择浮空输入,没有上拉电阻和下拉电阻去影响外部信号,缺点就是
	当引脚悬空时,没有默认的电平了,输入就会受噪声干扰,来回不断地跳变)*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	GPIO_SetBits(GPIOA,GPIO_Pin_6 | GPIO_Pin_7); 
	/*第3步:选择时基单元的时钟源*/
	//TIM_InternalClockConfig(TIM3); //不需要了,编码器接口会托管时钟,编码器接口就是一个带方向的外部时钟,所以这个内部时钟就没有用了          
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;        // 指定时钟划分为1分频
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;    //这个参数也没有作用了,因为计数方向也是被编码器接口托管了
	TIM_TimeBaseInitStructure.TIM_Period = 65536-1;                    //指定ARR自动重装器的值,设置大一些,计数范围最大,而且方便换算为负数
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1-1;                     //这里不分频,编码器时钟直接驱动计数器   
	/*配置上面两个参数使得定时1s,也就是定时频率为1Hz,然后它们的取值都在0~65535之间*/
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;                //指定重复计数器的值,这个是高级定时器才有的,这里不需要用,直接给0
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	/*第4步:配置输入捕获单元*/
	TIM_ICInitTypeDef TIM_ICInitStruct;
	TIM_ICStructInit(&TIM_ICInitStruct); //因为有两个参数用不到,所以先默认初始化一下
	TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;             //选择TIM3的通道1
	TIM_ICInitStruct.TIM_ICFilter = 0xF;                      //选择输入捕获的滤波器,滤除高频噪声,使信号更平缓
//	TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;  
//	/*这里的上升沿参数代表的是高电平极性不反转
//	和下面TIM_EncoderInterfaceConfig配置的是同一个寄存器,所以可以删除*/
//	TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;        //这个参数与编码器无关
//	TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;   //这个参数与编码器无关
	TIM_ICInit(TIM3,&TIM_ICInitStruct);
	TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;             //选择TIM3的通道2
	TIM_ICInitStruct.TIM_ICFilter = 0xF;                      //选择输入捕获的滤波器,滤除高频噪声,使信号更平缓
//	TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;  
//	/*这里的上升沿参数代表的是高电平极性不反转
//	和下面TIM_EncoderInterfaceConfig配置的是同一个寄存器,所以可以删除*/
	TIM_ICInit(TIM3,&TIM_ICInitStruct);
	/*第5步:配置编码器接口模式*/
	TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising); 
	/*第二个参数使用TI1和TI2都计数,第3和第4个参数配置通道不反相,如果极性与自己想要的不一致,更改第3或第4个参数其中一个,
	比如将第3个改为TIM_ICPolarity_Falling*/
	/*第6步:启动定时器*/
	TIM_Cmd(TIM3,ENABLE);	              	
}

///*获取计数器的值*/
//int16_t Encoder_Get(void)
//{
//	return (TIM_GetCounter(TIM3));
//}

/*测速:使用闸门时间测速*/
int16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp = TIM_GetCounter(TIM3);    
	TIM_SetCounter(TIM3,0);       //给CNT清零
	return Temp;
}


/**
  * 函    数:定时中断初始化
  * 参    数:无
  * 返 回 值:无
  */
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,定时器开始运行
}

int16_t Speed;

int main(void)
{
	OLED_Init();                                 // 初始化OLED屏幕
	Timer_Init();                                // 初始化定时器
	Encoder_Init();
	OLED_ShowString(1,1,"Speed:");                 // 在1行3列显示字符串

	while(1)
	{	
		OLED_ShowSignedNum(1,7,Speed,5);
//		Delay_ms(1000);              //闸门时间,不建议这么做,会堵塞程序,因此使用中断去做
	}
}


/*TIM2的中断函数*/
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)      //判断一下中断标志位状态
	{
		Speed = Encoder_Get();    //每隔1s读取一下速度
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);      //清除标志位
	}
}
相关推荐
RainCity4 天前
Java Swing 自定义组件库分享(十二)
java·笔记·后端
LinXunFeng12 天前
Obsidian - 使用 Share Note 分享笔记并自部署
前端·笔记·github
✎ ﹏梦醒͜ღ҉繁华落℘16 天前
单片机基础知识---stm32单片机的优先级
stm32·单片机·mongodb
闪闪发亮的小星星16 天前
高斯光以及高斯光公式解释
笔记
CNNACN电商经济16 天前
纸价波动加速中小产能出清,包装印刷板块龙头份额提升与议价能力重估
科技·生活
cqbzcsq16 天前
CellFlow虚拟细胞论文阅读
论文阅读·人工智能·笔记·学习·生物信息
牛根生同志16 天前
SPI数据收发的时候 TXE与RXNE标志位置位的时机
stm32·spi·transfer
绿算技术16 天前
Mooncake 与绿算ForinnBase GroundPool如何联手打破推理僵局?
科技·算法·架构
阿米亚波16 天前
【Windows】QEMU 启动 openEuler aarch64/arm64 架构系统 + 离线软件源
linux·windows·经验分享·笔记·架构·arm
自传.16 天前
尚硅谷 Vibe Coding|第三章(1) Claude Code深度使用与进阶技巧 学习笔记
笔记·学习·尚硅谷·vibecoding