Bootstrap

STM32-HAL库电机开发编码器

编码器编程基于STM32F407IG,前置基础知识是定时器输入捕获。

原理详解

编码器的种类有增量式编码器、绝对式编码器、混合式绝对式编码器。
本实验使用增量式编码器
基本编码器的原理示意图如下图所示。旋转编码器内部大都由码盘、光电检测装置和信号处理电路等部分构成。码盘上刻了若干圈线槽, 线槽等距并且可透光,当码盘旋转时就会周期性的透过和遮挡来自光电检测装置的光线,这样检测装置就会周期性的生成若干电信号。 但是这些电信号通常比较微弱,需要加入一套处理电路对信号进行放大和整形,最后把信号整形为脉冲信号并向外输出。编码器基本原理图
在这里插入图片描述
增量式编码器都有A、B两通道信号输出,这是因为增量式编码器的码盘上有两圈线槽, 两圈线槽的之间会错开一定的角度,这个角度会使得光电检测装置输出的两相信号相差 1/4 周期(90°)。
加粗样式
目的是根据两相信号变化的先后顺序就可以判断运动方向,记录输出的脉冲个数可以知道位移量的大小,同时通过输出信号的频率就能得到速度
采集信号时使用四倍频技术是为了增加分辨率
分辨率:指编码器能够分辨的最小单位。
精度是指编码器每个读数与转轴实际位置间的最大误差,通常用角度、角分或角秒来表示。
在这里插入图片描述
分辨率通过分频提高了四倍,精度是不改变的,所以精确率增加了。精确率是由分辨率和精度共同决定的。
在这里插入图片描述
代码详解

/**
  * @brief  配置TIMx编码器模式
  * @param  无
  * @retval 无
  */
static void TIM_Encoder_Init(void)
{ 
  TIM_Encoder_InitTypeDef Encoder_ConfigStructure;
  
  /* 使能编码器接口时钟 */
  ENCODER_TIM_CLK_ENABLE();
  
  /* 定时器初始化设置 */
  TIM_EncoderHandle.Instance = ENCODER_TIM;
  //捕获预分频器的值为0时代表每捕获一个边沿便执行捕获
  TIM_EncoderHandle.Init.Prescaler = 0;
  //向上计数
  TIM_EncoderHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
  //捕获一个周期总次数为最大值65535
  TIM_EncoderHandle.Init.Period = 65535;
  //不分频
  TIM_EncoderHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  TIM_EncoderHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  
  /* 设置编码器倍频数 */
  Encoder_ConfigStructure.EncoderMode = ENCODER_MODE;
  /* 编码器接口通道1设置 上升沿开始*/
  Encoder_ConfigStructure.IC1Polarity = TIM_ICPOLARITY_RISING;
  //编码器极性设置,没有反方向就不用管
  Encoder_ConfigStructure.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  //不分频
  Encoder_ConfigStructure.IC1Prescaler = TIM_ICPSC_DIV1;
  Encoder_ConfigStructure.IC1Filter = 0;
  /* 编码器接口通道2设置 */
  Encoder_ConfigStructure.IC2Polarity = TIM_ICPOLARITY_RISING;
  Encoder_ConfigStructure.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  Encoder_ConfigStructure.IC2Prescaler = TIM_ICPSC_DIV1;
  Encoder_ConfigStructure.IC2Filter = 0;
  /* 初始化编码器接口 */
  HAL_TIM_Encoder_Init(&TIM_EncoderHandle, &Encoder_ConfigStructure);
  
  /* 清零计数器 */
  __HAL_TIM_SET_COUNTER(&TIM_EncoderHandle, 0);
  
  /* 清零中断标志位 */
  __HAL_TIM_CLEAR_IT(&TIM_EncoderHandle,TIM_IT_UPDATE);
  /* 使能定时器的更新事件中断 */
  __HAL_TIM_ENABLE_IT(&TIM_EncoderHandle,TIM_IT_UPDATE);
  /* 设置更新事件请求源为:计数器溢出 */
  __HAL_TIM_URS_ENABLE(&TIM_EncoderHandle);
  
  /* 设置中断优先级 */
  HAL_NVIC_SetPriority(ENCODER_TIM_IRQn, 0, 1);
  /* 使能定时器中断 */
  HAL_NVIC_EnableIRQ(ENCODER_TIM_IRQn);
  
  /* 使能编码器接口 */
  HAL_TIM_Encoder_Start(&TIM_EncoderHandle, TIM_CHANNEL_ALL);
}

/**
  * @brief  编码器接口引脚初始化
  * @param  无
  * @retval 无
  */
static void Encoder_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  
  /* 定时器通道引脚端口时钟使能 */
  ENCODER_TIM_CH1_GPIO_CLK_ENABLE();
  ENCODER_TIM_CH2_GPIO_CLK_ENABLE();
  
  /**TIM3 GPIO Configuration    
  PC6     ------> TIM3_CH1
  PC7     ------> TIM3_CH2 
  */
  /* 设置输入类型 */
  GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
  /* 设置上拉 */
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  /* 设置引脚速率 */
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  
  /* 选择要控制的GPIO引脚 */	
  GPIO_InitStruct.Pin = ENCODER_TIM_CH1_PIN;
  /* 设置复用 */
  GPIO_InitStruct.Alternate = ENCODER_TIM_CH1_GPIO_AF;
  /* 调用库函数,使用上面配置的GPIO_InitStructure初始化GPIO */
  HAL_GPIO_Init(ENCODER_TIM_CH1_GPIO_PORT, &GPIO_InitStruct);
  
  /* 选择要控制的GPIO引脚 */	
  GPIO_InitStruct.Pin = ENCODER_TIM_CH2_PIN;
  /* 设置复用 */
  GPIO_InitStruct.Alternate = ENCODER_TIM_CH2_GPIO_AF;
  /* 调用库函数,使用上面配置的GPIO_InitStructure初始化GPIO */
  HAL_GPIO_Init(ENCODER_TIM_CH2_GPIO_PORT, &GPIO_InitStruct);
}

/**
  * @brief  编码器接口初始化
  * @param  无
  * @retval 无
  */
void Encoder_Init(void)
{
  Encoder_GPIO_Init();    /* 引脚初始化 */
  TIM_Encoder_Init();     /* 配置编码器接口 */
}

.h文件

#ifndef __BSP_ENCOEDER_H
#define	__BSP_ENCOEDER_H

#include "stm32f4xx.h"

/* 定时器选择 */
#define ENCODER_TIM                            TIM3
#define ENCODER_TIM_CLK_ENABLE()  				     __HAL_RCC_TIM3_CLK_ENABLE()

/* 定时器溢出值 */		
#define ENCODER_TIM_PERIOD                     65535
/* 定时器预分频值 */
#define ENCODER_TIM_PRESCALER                  0      

/* 定时器中断 */
#define ENCODER_TIM_IRQn                       TIM3_IRQn
#define ENCODER_TIM_IRQHandler                 TIM3_IRQHandler

/* 编码器接口引脚 */
#define ENCODER_TIM_CH1_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOC_CLK_ENABLE()
#define ENCODER_TIM_CH1_GPIO_PORT              GPIOC
#define ENCODER_TIM_CH1_PIN                    GPIO_PIN_6
#define ENCODER_TIM_CH1_GPIO_AF                GPIO_AF2_TIM3

#define ENCODER_TIM_CH2_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOC_CLK_ENABLE()
#define ENCODER_TIM_CH2_GPIO_PORT              GPIOC
#define ENCODER_TIM_CH2_PIN                    GPIO_PIN_7
#define ENCODER_TIM_CH2_GPIO_AF                GPIO_AF2_TIM3

/* 编码器接口倍频数 */
#define ENCODER_MODE                           TIM_ENCODERMODE_TI12

/* 编码器接口输入捕获通道相位设置 */
#define ENCODER_IC1_POLARITY                   TIM_ICPOLARITY_RISING
#define ENCODER_IC2_POLARITY                   TIM_ICPOLARITY_RISING

/* 编码器物理分辨率 */
#define ENCODER_RESOLUTION                     16

/* 经过倍频之后的总分辨率 */
#if (ENCODER_MODE == TIM_ENCODERMODE_TI12)
  #define ENCODER_TOTAL_RESOLUTION             (ENCODER_RESOLUTION * 4)  /* 4倍频后的总分辨率 */
#else
  #define ENCODER_TOTAL_RESOLUTION             (ENCODER_RESOLUTION * 2)  /* 2倍频后的总分辨率 */
#endif

/* 减速电机减速比 */
#define REDUCTION_RATIO                        30

extern __IO int16_t Encoder_Overflow_Count;
extern TIM_HandleTypeDef TIM_EncoderHandle;

void Encoder_Init(void);

#endif   /* __BSP_ENCODER_H */


;