STM32编码器的学习笔记

编码器

编码器参数:

     

  • 分辨率:指编码器能够分辨的最小单位。对于增量式编码器,其分辨率表示为编码器转轴旋转一圈所产生的脉冲数,即脉冲数/转 (Pulse Per Revolution 或 PPR)。码盘上透光线槽的数目其实就等于分辨率,也叫多少线,较为常见的有 5-6000 线。对于绝对式编码器,内部码盘所用的位数就是它的分辨率,单位是位 (bit),具体还分单圈分辨率和多圈分辨率。
  • 精度:首先明确一点,精度与分辨率是两个不同的概念。精度是指编码器每个读数与转轴实际位置间的最大误差,通常用角度、角分或角秒来表示。例如有些绝对式编码器参数表里会写 ±20′′,这个就表示编码器输出的读数与转轴实际位置之间存在正负 20 角秒的误差,精度由码盘刻线加工精度、转轴同心度、材料的温度特性、电路的响应时间等各方面因素共同决定。
  • 最大响应频率:指编码器每秒输出的脉冲数,单位是 Hz。计算公式:最大响应频率 = 分辨率 * 轴转速/60。
  • 信号输出形式:对于增量式编码器,每个通道的信号独立输出 上图就是常规信号输出,还有正弦信号输出形式,输出电路形式通常有集电极开路输出、推挽输出、差分输出等。对于绝对式编码器,由于是直接输出几十位的二进制数,为了确保传输速率和信号质量,一般采用串行输出或总线型输出,例如同步串行接口 (SSI)RS485CANopen 或 EtherCAT 等,也有一部分是并行输出,输出电路形式与增量式编码器相同。

STM32编码器接口介绍,

  • 如果计数器仅在 TI2 边沿处计数,在 TIMx_SMCR 寄存器中写入 SMS=001”;
  • 如果计数器仅在 TI1 边沿处计数,写入 SMS=010”;
  • 如果计数器在 TI1 TI2 边沿处均计数,则写入 SMS=011”。
  • 通过编程 TIMx_CCER 寄存器的 CC1P CC2P 位,选择 TI1 TI2 极性
  • 如果需要,还可 对输入滤波器进行编程。CC1NP CC2NP 必须保持低电平。
  • TI1 TI2 两个输入用于连接增量编码器。如果使能计数器(在 TIMx_CR1 存器的 CEN 位中写入“1”),则计数器的时钟由 TI1FP1 TI2FP2 上的每次有效信号转 换提供。

  • TI1FP1 TI2FP2 是进行输入滤波器和极性选择后 TI1 TI2 的信号,如果不进行 滤波和反相,则 TI1FP1=TI1TI2FP2=TI2

  • 将根据两个输入的信号转换序列,产生计数脉冲和方向信号
  • 根据该信号转换序列,计数器相应递增或递减计数,同时硬件对 TIMx_CR1 寄存器的 DIR 位进行相应修改

  • 任何输入(TI1 TI2)发生信号转换时,都会计算 DIR 位,无论计数器是仅在 TI1 TI2 边沿处计数,还是同时在 TI1 TI2 处计数。

  • 编码器接口模式就相当于带有方向选择的外部时钟。

  • 这意味着,计数器仅在 0 TIMx_ARR 寄存器中的自动重载值之间进行连续计数(根据具体方向,从 0 递增计数到 ARR,或从 ARR 递减计数到 0)。因此,在启动前必须先配置 TIMx_ARR

  • 同样,捕获、比较、预分频 器、重复计数器及触发输出功能继续正常工作。编码器模式和外部时钟模式 2 不兼容,因此 不能同时选择。

1. 下图就是电机正转的编码器输出波形,上表的红线计数方式

2. 通道A接TI1,通道B接TI2

3.TI1FP1的计数方向,需要参考TI2的电平

4. TI2FP2的计数方向,需要参考TI1的电平

顺时针旋转就是一个递增的计数器

逆时针旋转就是一个递减的计数器

STM32寄存器介绍

编码器的使用:

  1. 设置从模式寄存器的SMCR(SMS) 工作模式:编码器模式1,编码器模式2,编码器模式3;
  2. 设置输入引脚为TI1,TI2
  3. 设置触发的极性CCER(CC1P,CC2P)
  4. 设置滤波和分频 SCMR(IC1F,IC1PSC   IC2F,IC2PSC)
  5. 使能计数器 CR1(CEN)
  6. 硬件会自动设置CR1(DIR)方向位;

不完整代码:

#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 */

/**
  ******************************************************************************
  * @file    bsp_motor_control.c
  * @author  fire
  * @version V1.0
  * @date    2019-xx-xx
  * @brief   编码器接口
  ******************************************************************************
  * @attention
  *
  * 实验平台:野火  STM32 F407 开发板 
  * 论坛    :http://www.firebbs.cn
  * 淘宝    :http://firestm32.taobao.com
  *
  ******************************************************************************
  */ 

#include "./Encoder/bsp_encoder.h"

/* 定时器溢出次数 */
__IO int16_t Encoder_Overflow_Count = 0;

TIM_HandleTypeDef TIM_EncoderHandle;

/**
  * @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();
  
  /* 设置输入类型 */
  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  配置TIMx编码器模式
  * @param  无
  * @retval 无
  */
static void TIM_Encoder_Init(void)
{ 
  TIM_Encoder_InitTypeDef Encoder_ConfigStructure;
  
  /* 使能编码器接口时钟 */
  ENCODER_TIM_CLK_ENABLE();
  
  /* 定时器初始化设置 */
  TIM_EncoderHandle.Instance = ENCODER_TIM;
  TIM_EncoderHandle.Init.Prescaler = ENCODER_TIM_PRESCALER;
  TIM_EncoderHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
  TIM_EncoderHandle.Init.Period = ENCODER_TIM_PERIOD;
  TIM_EncoderHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  TIM_EncoderHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  
  /* 设置编码器倍频数 */
  Encoder_ConfigStructure.EncoderMode = ENCODER_MODE;
  /* 编码器接口通道1设置 */
  Encoder_ConfigStructure.IC1Polarity = ENCODER_IC1_POLARITY; //上升沿触发 有点不解,不是使用 编码器模式 3 在计数器在 TI1FP1 和 TI2FP2 的边沿计数,
  Encoder_ConfigStructure.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  Encoder_ConfigStructure.IC1Prescaler = TIM_ICPSC_DIV1;
  Encoder_ConfigStructure.IC1Filter = 0;
  /* 编码器接口通道2设置 */
  Encoder_ConfigStructure.IC2Polarity = ENCODER_IC2_POLARITY;
  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, 5, 1);
  /* 使能定时器中断 */
  HAL_NVIC_EnableIRQ(ENCODER_TIM_IRQn);
  
  /* 使能编码器接口 */
  HAL_TIM_Encoder_Start(&TIM_EncoderHandle, TIM_CHANNEL_ALL);
}

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

/**
  * @brief  定时器更新事件回调函数
  * @param  无
  * @retval 无
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* 判断当前计数器计数方向 方向代表电机方向 */
  if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&TIM_EncoderHandle))
    /* 下溢 */
    Encoder_Overflow_Count--;
  else
    /* 上溢 */
    Encoder_Overflow_Count++;
}

/*********************************************END OF FILE**********************/

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Car12

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值