2.19 编码器接口测速代码

编码器接口测速接线图

参考引脚定义表,PA6和PA7是TIM3的通道1和通道2,我们计划用TIM3接编码器。

编码器计数代码

第一步,RCC开启时钟,开启GPIO和定时器的时钟。

第二步,配置GPIO,这里需要把PA6和PA7配置成输入模式。

第三步,配置时基单元,这里预分频器我们一般选择不分频。自动重装器一般给最大65535,只需要CNT执行计数就行了。

第四步,配置输入捕获单元。不过这里输入捕获单元只有滤波器和极性这两个参数有用。后面的参数没有用到,与编码器无关。

第五步,配置编码器接口模式。这个直接调用一个库函数就可以了。

第六步,调用TIM_Cmd()函数,启动定时器。

电路初始化完成之后,CNT就会随着编码器的旋转而自增自减了。如果想要测量编码器的位置,直接读出CNT的值就行了,如果想要测量编码器的速度和方向,那就需要每隔一段固定的闸门时间取出一次CNT,然后再把CNT清零。这样就是测频法测量速度了。

流程看完了,我们再看一下库函数:

定时器编码器接口配置

TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);

第一个参数是选择定时器。

第二个参数选择编码器模式。

后面两个参数,分别选择通道1和通道2的电平极性。

void Encoder_Init()
{
	//开启TIM3、GPIOA的时钟。
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//配置GPIO,PA6/PA7改成上拉输入。
	GPIO_InitTypeDef GPIO_InitStruture;
	GPIO_InitStruture.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStruture.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_6;
	GPIO_Init(GPIOA,&GPIO_InitStruture);
	
	//不需要选择时基单元时钟,因为编码器接口会托管时钟。
	//编码器接口本身就是一个带方向控制的外部时钟。
	//TIM_InternalClockConfig(TIM3);
	
	//配置时基单元。
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStruture;
	TIM_TimeBaseStruture.TIM_ClockDivision=TIM_CKD_DIV1;
	//计数器模式目前没有作用,因为计数方向也是被编码器接口托管了。
	TIM_TimeBaseStruture.TIM_CounterMode=TIM_CounterMode_Up;
	//这里给最大值65536-1,这样计数范围是最大的,而且方便换算为负数。
	TIM_TimeBaseStruture.TIM_Period=65536 -1; 
	//预分频器给0,不分频。
	TIM_TimeBaseStruture.TIM_Prescaler=1-1;
	TIM_TimeBaseStruture.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStruture);
	
	//初始化输入捕获单元。
	//编码器接口只用到了滤波器和极性选择,剩下的不用写。
	TIM_ICInitTypeDef TIM_ICInitStruture;
	/* 因为结构体是不完整的,为了防止结构体出现不确定的值可能会造成的问题,
	我们最好使用StrucInit给结构体赋一个初始值。*/
	TIM_ICStructInit(&TIM_ICInitStruture);
	//选择通道,选择TIM3的通道1.
	TIM_ICInitStruture.TIM_Channel=TIM_Channel_1;
	//配置输入捕获的滤波器,范围是0X0~0XF。越大滤波效果越好。
	TIM_ICInitStruture.TIM_ICFilter=0XF;
	/*边沿检测、极性选择。我们选择上升沿触发。
	我们说过,这里的上升沿并不代表上升沿有效,因为编码器接口始终都是上升沿、下降沿都有效的。
	这里的上升沿参数代表的是高低电平极性不反转。其实这里的极性参数等会我们配置编码器接口的
	时候也有,属于重复配置了,这里这个其实也可以删掉。*/
	TIM_ICInitStruture.TIM_ICPolarity=TIM_ICPolarity_Rising;
	TIM_ICInit(TIM3,&TIM_ICInitStruture);//这样通道1就配置好了
	//我们接着配置通道2,和通道1的逻辑一样。
	TIM_ICInitStruture.TIM_Channel=TIM_Channel_2;
	TIM_ICInitStruture.TIM_ICFilter=0XF;
	TIM_ICInitStruture.TIM_ICPolarity=TIM_ICPolarity_Rising;
	TIM_ICInit(TIM3,&TIM_ICInitStruture);
	
	//配置编码器接口
	TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);

	
	//使能定时器
	TIM_Cmd(TIM3,ENABLE);

}

//返回TIM3计数器的值
uint16_t Encoder_Get()
{
	return TIM_GetCounter(TIM3);
}

 

 

本套件使用的编码器是有这个段落感的,每转一格,它输出的波形实际上是这样的:,A、B相各出现一个下降沿和上升沿,所以计次总共加了4次,当然如果你是电机的编码器,那就不会有这个段落感了。

上述代码我们代入到实物中,会发现旋转编码器正转,CNT的值正常增加,但反转,会出现65535,这时我们就需要用到之前所说的补码小技巧,把uint16_t的类型强转成int16_t,这样就把65535强转成-1了。(STM32是16位系统,所以-1的补码正好是65535)

表现在代码上就是:

//返回TIM3计数器的值
int16_t Encoder_Get()
{
	return TIM_GetCounter(TIM3);
}

把返回值强转成了int16_t类型的有符号数。

 

接下来我们可以再来研究一下极性的问题,如果这个方向和你想要的不一致的话,可以修改一下极性。

在硬件层面上,我们可以直接把A、B相的引脚换一下位置。

在软件层面上,我们可以把任意一个极性反转一下,方向就会反过来。如果两个极性都反转,那极性还是会保持不变。

TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Falling);

 

编码器测速代码

如果我们想用这个编码器来测速的话,就可以在固定的闸门时间,读一次CNT,然后把CNT清零。

//读取CNT,清零CNT。
int16_t Encoder_Get()
{
	int16_t Temp;
	Temp = TIM_GetCounter(TIM3);
	TIM_SetCounter(TIM3,0);
	return Temp;
}