Bootstrap

STM32通用定时器-输入捕获-脉冲计数(编码器模式)及电机测速原理

一、知识点

1. 编码器
  两相编码器(正交编码器):两相编码器由 A 相和 B 相组成,相位差为 90 度。当旋转方向为顺时针时,A 相先变化,然后 B 相变化;当旋转方向为逆时针时,B 相先变化,然后 A 相变化。通过检测相位差变化,可以确定旋转的方向。STM32 单片机可以使用定时器的正交编码器模式(Encoder Mode)来实现两相编码器的接口。
在这里插入图片描述

2. 编码器接口
  编码器的两个输入引脚,就是每个定时器的CH1和CH2引脚,CH3和CH4不能接编码器。
  最终的实验现象,编码器有两个输出,一个是A相,一个是B相,然后接入到STM32,定时器的编码器接口,编码器的接口自动控制定时器时基单元中的CNT计数器,进行自增或自减。比如初始化之后,CNT初始值为0,然后编码器右转,CNT就++,右转产生一个脉冲,CNT就加一次,比如右转产生10个脉冲后,停下来,那么这个过程CNT就由0自增到10,停下来,编码器左转,CNT就–,左转产生一个脉冲,CNT就自减一次, 比如编码器再左转产生5个脉冲,那CNT就在原来10的基础上自减5,停下来。
  编码器接口,其实就相当于是一个带有方向控制的外部时钟,同时控制着CNT的计数时钟和计数方向,这样CNT的值就表示了编码器的位置。如果我们每隔一段时间取一次编码器的值,再把CNT清零,那么每次取出来的值就表示了编码器的速度。
  编码器测速实际上就是测频法测正交脉冲的频率,CNT计次,然后每隔一段时间取一次计次,这就是测频法的思路。编码器计次能根据旋转方向,不仅能自增计次还能自减计次,是一个带方向的测速。
每个高级定时器和通用定时器都拥有1个编码器接口。

二、框图

在这里插入图片描述
  
在这里插入图片描述
  

三、编码器计数操作

在这里插入图片描述
  解释:当Tl1先产生脉冲,Tl2后产生脉冲。(A相先变化,B相后变化)时电机正转,计数器开始计数CNT++。当产生毛刺时,CNT不计数。Tl2先产生脉冲,Tl1后产生脉冲。(B相先变化,A相后变化)时电机反转,计数器CNT- -。当产生毛刺时不计数。

  
在这里插入图片描述
  解释:在TL1和TL2上计数时:当Tl1FP1信号为上升沿,且相对信号(Tl2)为高电平状态,则为向下计数,即CNT- -。若Tl2FP2信号在上升沿,且相对信号(TL1)为高电平,则为向上计数,即CNT++。各类信号状态与计数关系请查看上表。

四、相关寄存器

1. 第①部分对应寄存器

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 第②部分对应寄存器

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

五、代码编写

1. 库函数编写

void Encoder_Init_TIM4(void)
{
	GPIO_InitTypeDef GPIO_InitStructure; //定义一个引脚初始化的结构体  
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//定义一个定时器初始化的结构体
	TIM_ICInitTypeDef TIM_ICInitStructure; //定义一个定时器编码器模式初始化的结构体
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能TIM4时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能CPIOB时钟
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;	//PB6、PB7
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
	GPIO_Init(GPIOB, &GPIO_InitStructure);	//根据GPIO_InitStructure的参数初始化GPIOB0
	
	TIM_TimeBaseStructure.TIM_Period = 0xffff; //设定计数器自动重装值
	TIM_TimeBaseStructure.TIM_Prescaler = 0; // 预分频器 
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //选择时钟分频:不分频
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct的参数初始化定时器TIM4
	
	TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3:CH1、CH2同时计数,四分频
	TIM_ICStructInit(&TIM_ICInitStructure); //把TIM_ICInitStruct 中的每一个参数按缺省值填入
	TIM_ICInitStructure.TIM_ICFilter = 10;  //设置滤波器长度
	TIM_ICInit(TIM4, &TIM_ICInitStructure); //根TIM_ICInitStructure参数初始化定时器TIM4编码器模式
	
	TIM_Cmd(TIM4, ENABLE); //使能定时器4
}

int16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp = TIM_GetCounter(TIM4); //获取编码器当前值
	TIM_SetCounter(TIM4, 0);  //将编码器计数器清0
	return Temp;
}

六、电机测速原理

1. 基本信息

  1. 需要知道电机的一些基本信息,如减速比、多少线的编码器(这些参数需要问电机的的商家,或者查看手册获得)。
  2. 知道在代码中使用的是编码器模式几。如编码器模式1、2、3。使用编码器模式1和2则为2倍频,使用编码器模式3则为4倍频。频数高的精度会更高。
    在这里插入图片描述

2. 计算电机一圈的脉冲数

假设电机减速比为30,500线的编码器,使用编码器模式3。
电机一圈脉冲数:30×500×4=60000

3. 计算电机的速度

   这是我们需要再使用一个定时器用于定时,在定时器中断中获取编码器的脉冲。如:我们将定时器2设置为10ms触发一次中断,则每过10ms我们获取一次脉冲,假设10ms测得脉冲数为600。则1ms的脉冲数为60(600/10),1s的脉冲数为60000(60*1000)。又因为电机一圈脉冲为60000,所以电机的速度为:60000/60000=1 圈/s 。代码如下所示:

float cnt=0;
float speed=0;
void Time2_Init(u16 arr,u16 psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = arr; //电机PWM频率要和定时器采样频率一致
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);

	TIM_Cmd(TIM2, ENABLE);
}

void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{     
		  cnt=Encoder_Get();
		  speed=cnt/10*1000/60000;
		  TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
//------------------------mian.c------------

int main()
{
	Time2_Init(7199,99);  //定时时间为7200*100/72 = 10000us =10ms
	while(1){}
}
;