江协科技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);      //清除标志位
	}
}
相关推荐
koo3641 天前
李宏毅机器学习笔记24
人工智能·笔记·机器学习
xyx-3v1 天前
SPI四种工作模式
stm32·单片机·嵌入式硬件·学习
老虎06271 天前
黑马点评学习笔记02(Mabatis—plus)
笔记·学习
BreezeJuvenile1 天前
实验二 呼吸灯功能实验
stm32·单片机·嵌入式系统·流水灯·实验
摇滚侠1 天前
Spring Boot 3零基础教程,Spring Boot 日志级别,笔记19
java·spring boot·笔记
烧冻鸡翅QAQ1 天前
考研数学笔记(概率统计篇)
笔记·考研
~黄夫人~1 天前
Ubuntu系统快速上手命令(详细)
linux·运维·笔记·ubuntu·postgresql
2401_885405511 天前
定位守护童年,科技构筑安全屏障
科技·物联网·安全·小程序·宠物·web app·智能手表
隐匿7811 天前
nacos网站
笔记