一种基于STM32F1 MCU的增量型编码器测速的方法

遇到的问题

1. 编码器信号,用来计量长度,如果需要同时测量当前实时速度,在不增加接口的情况下,实现较为准确实时的测速

2. 简单的M法速度,对于低速信号,测量精度较差,实时性也不高。

测速方法

之前的文章提到,测速的三种方法:

(1)在规定时间内测量所产生的脉冲个数来获得被测速度,称为M法测速;

(2)测量相邻两个脉冲的时间来测量速度,称为T法测速;

(3)同时测量检测时间和在此时间内脉冲发生器发出的脉冲个数来测量速度,称为M/T法测速。以上三中测速方法中,M法适合于测量较高的速度,能获得较高分辨率(最容易想到);T法适合于测量较低的速度,这时能获得较高的分辨率;而M/T法则无论高速低速都适合测量(详细介绍)。

本文的方法是M/T法在增量编码器信号的实现和应用。

一. 增量编码器介绍

增量型编码器:就是每转过单位的角度就发出一个脉冲信号(也有发正余弦信号,然后对其进行细分,斩波出频率更高的脉冲),通常为A相、B相、Z相输出,A相、B相为相互延迟1/4周期的脉冲输出,根据延迟关系可以区别正反转,而且通过取A相、B相的上升和下降沿可以进行2或4倍频;Z相为单圈脉冲,即每圈发出一个脉冲。

因此编码器不仅能测量脉冲,而且可以累计运动的长度(带方向),实际使用还是很多的。

正常使用 A相 B相,对应下图的 TI1 和 TI2 信号(应用STM32F系列ARM的编程参考手册)

一种基于STM32F1 MCU的增量型编码器测速的方法_第1张图片

二. 实现方法

1. 底层配置编码器和定时器

             一种基于STM32F1 MCU的增量型编码器测速的方法_第2张图片

2.状态机描述

                                          一种基于STM32F1 MCU的增量型编码器测速的方法_第3张图片

 

3. 状态机说明

状态机外部中断EXITX和定时器中实现,或其回调函数中,中断配置的上升沿检测(和编码器接口配置一致)

①  记录当前编码器器的数值(位置)X1,并记录当前时间 T1, 关闭EXITX中断,开始倒计时T200tick  (20ms)  ,进入延时状             态(EXITX中断中)

② 定时器T200tick--,当 T200tick == 1 时,进入计时到达状态 (定时器中)

③ 开启EXITX中断

④ 记录当前编码器数值(位置)X2,并记录当前时间 T2 (EXITX中断中)

速度Speed = (X2 - X1)/(T2 - T1),即可测得实时速度

在3状态中,还需要添加超时判断函数,当没有编码信号后,进行超时处理。

实际使用中,该方法耗费资源较少,精度较高,延时可控(最大为设定的超时时间),能够满足一般的编码器测速需求。

 

部分代码如下

初始化:

/*编码器初始化*/
/* TIM3 init function */
void MX_TIM3_Init(void)
{
  TIM_Encoder_InitTypeDef sConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 0;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = ENCODER_TIM_PERIOD - 1;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
  sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC1Filter = 15;
  sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC2Filter = 15;
  if (HAL_TIM_Encoder_Init(&htim3, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
}


/*引脚和中断配置*/
void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* tim_encoderHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(tim_encoderHandle->Instance==TIM3)
  {
  /* USER CODE BEGIN TIM3_MspInit 0 */

  /* USER CODE END TIM3_MspInit 0 */
    /* TIM3 clock enable */
    __HAL_RCC_TIM3_CLK_ENABLE();
  
    __HAL_RCC_GPIOC_CLK_ENABLE();
    /**TIM3 GPIO Configuration    
    PC6     ------> TIM3_CH1
    PC7     ------> TIM3_CH2 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;  /*重新配置为上升沿中断*/
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    __HAL_AFIO_REMAP_TIM3_ENABLE();

  /* USER CODE BEGIN TIM3_MspInit 1 */
	HAL_NVIC_SetPriority(EXTI9_5_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
  /* USER CODE END TIM3_MspInit 1 */
  }
}

 中断回调函数中的状态机实现

void SpeedCacu(void)
{
	if(myspeed.sta == SP_REC)						
	 {
		HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);			/*开启编码器引脚外部中断*/
        myspeed.sta = SP_CALU;						/*开始检测*/
		LEDC_OFF();
		
	 }
    if(myspeed.sta > SP_REC)
	{
        myspeed.sta--;
		LEDC_ON();
	}
    
    if(Def_Times(myspeed.lasttim,u32Systim100us) > 2000 )    /*如果计量超时200ms,速度更新为 0*/
    {
        myspeed.lasttim = u32Systim100us;
		myspeed.lastcount = __HAL_TIM_GET_COUNTER(&htim3); 
        myspeed.speed = 0;
        myspeed.acce = 0;
        myspeed.sta = SP_DELAY100MS;
		HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
        myspeed.readyflg = 1;
    }
}




/*100us定时器*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM4)
	{
		u32Systim100us++;
		SpeedCacu();
	}
}


/*编码器引脚改外部中断引脚*/
uint32_t sp_protect;		/*保护计数器*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{	
	uint16_t counta;
	uint32_t timesa;
	int32_t  mspeeda;
	
	/***********************************第一路用于触发测速*************************************/
	if(GPIO_Pin == GPIO_PIN_7)										/*T1中断*/
	{
		if(myspeed.sta == SP_INIT)
		{
			myspeed.lasttim = u32Systim100us;    					//读取当前时间
            myspeed.lastcount = __HAL_TIM_GET_COUNTER(&htim3);  	/*当前编码器计数值*/
			
			HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);
            myspeed.sta = SP_DELAY100MS;							/*正常测量周期 0.1秒*/
			
		}
		else if(myspeed.sta == SP_CALU)
        {
            counta = __HAL_TIM_GET_COUNTER(&htim3);
            timesa = u32Systim100us; 
            
            myspeed.pertim = Def_Times(myspeed.lasttim,timesa);		/*脉冲间隔时间*/	
            
            myspeed.pertick = Deta_Count(myspeed.lastcount,counta);  /*脉冲增量*/
			
            mspeeda = myspeed.pertick * 25000 / myspeed.pertim;      //计算出来的速度  * 10  
            
			/*与上一次的速度,计算加速度*/
            myspeed.acce = 100 * (mspeeda - myspeed.speed)/myspeed.pertim;   // ticks / (s * s)   1ms per tick

			myspeed.speed = mspeeda;						/*此处计算出来的是硬件速度  */
            
            myspeed.readyflg = 1;
			
			myspeed.lasttim = timesa;    					//读取当前时间
            myspeed.lastcount = counta;  					/*当前编码器计数值*/
			
			HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);
            myspeed.sta = SP_DELAY100MS;					/*正常测量周期 0.1秒  直接重新开始测量了*/
			
			myspeed.totalcount += Enc_GetCount();
        }
	}
}

 

你可能感兴趣的:(嵌入式软件)