分享

STM32编码器_cx

 myallmy 2023-07-31 发布于北京

之前一直白嫖编码器代码,这次花了一早上学习了编码器。

 

一、编码器简介

 

1.概述

编码器是一种将角位移或者角速度转换成一连串电数字脉冲的旋转式传感器。

可以用来测量位置,测量速度。

每转过单位的角度就发出一个脉冲信号,通常为A相、B相输出。A相、B相为相互延迟1/4周期的脉冲输出(即正交信号),根据延迟关系可以区别正反转,而且通过取A相、B相的上升和下降沿可以进行2或4倍频。

例:四倍频的方法是测量A相和B相的上升沿和下降沿

这里写图片描述

2.原理

这里写图片描述

编码器通过发送正交脉冲信号表示角度信息,如图为一个示例。(其中TI1和TI2分别对应编码器输出A、B项)

脉冲信号特性表示信息
两项先后关系旋转方向
脉冲个数转过角度

二、stm32控制编码器

stm32控制编码器可以采用Timer的编码器模式,Tim1~8的CH1和CH2分别对应A相和B相

(1)大致过程

1、stm32f407中定时器1、2、3、4、5、8提供编码器接口模式

             选择编码器接口模式的方法是:如果计数器只在TI2的边沿计数,则置TIMx_SMCR寄存器中的
             SMS=001;如果只在TI1边沿计数,则置SMS=010;如果计数器同时在TI1和TI2边沿计数,则
             置SMS=011。

            通过设置TIMx_CCER寄存器中的CC1P和CC2P位,可以选择TI1和TI2极性;如果需要,还可以
            对输入滤波器编程。

             两个输入TI1和TI2被用来作为增量编码器的接口。假定计数器已经启动(TIMx_CR1
           寄存器中的CEN=’1’),计数器由每次在TI1FP1或TI2FP2上的有效跳变驱动。

 2、可以对输入信号TI1,TI2进行滤波处理,数字滤波器由事件器组成,每N个事件才视为一个有效边沿,可以在TIMx_CCMR1、TIMx_CCMR2中的IC1F位域设置
 3、stm32提供了单项计数(只能测速度)和双项计数模式(可测速度&方向)

       计数模式和编码器的关系

      在这里插入图片描述


下图是一个计数器操作的实例,显示了计数信号的产生和方向控制。它还显示了当选择了双向计数模式时,输入抖动是如何被抑制的;抖动可能会在传感器的位置靠近一个转换点时产生。在这个例子中,我们假定配置如下:

  1. CC1S=’01’ (TIMx_CCMR1寄存器, IC1FP1映射到TI1)
  2. CC2S=’01’ (TIMx_CCMR2寄存器, IC2FP2映射到TI2)
  3. CC1P=’0’ (TIMx_CCER寄存器, IC1FP1不反相, IC1FP1=TI1)
  4. CC2P=’0’ (TIMx_CCER寄存器, IC2FP2不反相, IC2FP2=TI2)
  5. SMS=’011’ (TIMx_SMCR寄存器,所有的输入均在上升沿和下降沿有效).
  6. CEN=’1’ (TIMx_CR1寄存器,计数器使能)

在这里插入图片描述

在A、B中仅一项有毛刺时,计数值加减后保持不变,实现了抖动补偿

两张图结合来看

在这里插入图片描述

仅在TI1计数时 相对信号的电平其实就是TI2的电平(不考虑反向的情况)这样再看这张表就会比较容易理解了
在TI2为高电平的时候TI1为上升沿时脉冲计数减1,TI1位下降沿时脉冲计数加1
在TI2为低电平的时候TI1为上升沿时脉冲计数加1,TI1位下降沿时脉冲计数减1

后面以此类推

4、 编码器A、B相输入的信号TI1、TI2经滤波和反相后成为TI1FP1 或 TI2FP2 ,定时器的时钟由他们上的每次有效信号转换提供,也就是说最终计数值即反映转过角度

根据两个输入信号的跳变顺序,产生了计数脉冲和方向信号。依据两个输入信号的跳变顺序,计数器向上或向下计数,同时硬件对TIMx_CR1寄存器的DIR位进行相应的设置。不管计数器是依靠TI1计数、依靠TI2计数或者同时依靠TI1和TI2计数。在任一输入端(TI1或者TI2)的跳变都会重新计算DIR位。

5、定时器配置为编码器接口模式时,会提供传感器当前位置的相关信息。使用另一个配置为捕获模式的定时器测量两个编码器事件之间的周期,可获得动态信息(速度、加速度和减速度)。

6、计数溢出后,定时器会装载“重装载值”,并清零重新计数,此值可设置为编码器旋转一周的脉冲个数,这样既可利用溢出中断次数判断转了几圈。但若只要求旋转角度,此值可以任意。任意时刻角度为=溢出中断次数*重装载值+当前计数值

 

(2)代码配置

1、编码器引脚初始化

  1. static void Encoder_GPIO_Init(void)
  2. {
  3. GPIO_InitTypeDef GPIO_InitStruct = {0};
  4. /* 定时器引脚端口使能 */
  5. ENCODER_TIM_CH1_GPIO_CLK_ENABLE();
  6. ENCODER_TIM_CH2_GPIO_CLK_ENABLE();
  7. /* 设置输入类型*/
  8. GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
  9. /* 设置上拉 */
  10. GPIO_InitStruct.Pull = GPIO_PULLUP;
  11. /* 设置引脚速率 */
  12. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  13. /* 选择要控制的GPIO引脚 */
  14. GPIO_InitStruct.Pin = ENCODER_TIM_CH1_PIN;
  15. /* 设置复用 */
  16. GPIO_InitStruct.Alternate = ENCODER_TIM_CH1_GPIO_AF;
  17. HAL_GPIO_Init(ENCODER_TIM_CH1_GPIO_PORT, &GPIO_InitStruct);
  18. /* 选择要控制的GPIO引脚 */
  19. GPIO_InitStruct.Pin = ENCODER_TIM_CH2_PIN;
  20. /* 设置复用 */
  21. GPIO_InitStruct.Alternate = ENCODER_TIM_CH2_GPIO_AF;
  22. HAL_GPIO_Init(ENCODER_TIM_CH2_GPIO_PORT, &GPIO_InitStruct);
  23. }

2、配置编码器模式

  1. static void TIM_Encoder_Init(void)
  2. {
  3. TIM_Encoder_InitTypeDef Encoder_ConfigStructure;
  4. /* 使能编码器接口时钟 */
  5. ENCODER_TIM_CLK_ENABLE();
  6. /* 定时器初始化设置 */
  7. TIM_EncoderHandle.Instance = ENCODER_TIM;
  8. TIM_EncoderHandle.Init.Prescaler = ENCODER_TIM_PRESCALER;
  9. TIM_EncoderHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
  10. TIM_EncoderHandle.Init.Period = ENCODER_TIM_PERIOD;
  11. TIM_EncoderHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  12. TIM_EncoderHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  13. /* 设置编码器倍频数*/
  14. Encoder_ConfigStructure.EncoderMode = ENCODER_MODE;
  15. /* 编码器接口通道1设置 */
  16. Encoder_ConfigStructure.IC1Polarity = ENCODER_IC1_POLARITY;
  17. Encoder_ConfigStructure.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  18. Encoder_ConfigStructure.IC1Prescaler = TIM_ICPSC_DIV1;
  19. Encoder_ConfigStructure.IC1Filter = 0;
  20. /* 编码器接口通道2设置 */
  21. Encoder_ConfigStructure.IC2Polarity = ENCODER_IC2_POLARITY;
  22. Encoder_ConfigStructure.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  23. Encoder_ConfigStructure.IC2Prescaler = TIM_ICPSC_DIV1;
  24. Encoder_ConfigStructure.IC2Filter = 0;
  25. /* 初始化编码器接口 */
  26. HAL_TIM_Encoder_Init(&TIM_EncoderHandle, &Encoder_ConfigStructure);
  27. /* 清零计数器 */
  28. __HAL_TIM_SET_COUNTER(&TIM_EncoderHandle, 0);
  29. /* 清零中断标志*/
  30. __HAL_TIM_CLEAR_IT(&TIM_EncoderHandle,TIM_IT_UPDATE);
  31. /* 使能定时器跟新中断 */
  32. __HAL_TIM_ENABLE_IT(&TIM_EncoderHandle,TIM_IT_UPDATE);
  33. /* 设置跟新事件请求:计数器溢出 */
  34. __HAL_TIM_URS_ENABLE(&TIM_EncoderHandle);
  35. /* 设置中断优先级 */
  36. HAL_NVIC_SetPriority(ENCODER_TIM_IRQn, 5, 1);
  37. /* 使能定时器中断 */
  38. HAL_NVIC_EnableIRQ(ENCODER_TIM_IRQn);
  39. /* 使能编码器接口 */
  40. HAL_TIM_Encoder_Start(&TIM_EncoderHandle, TIM_CHANNEL_ALL);
  41. }

3、圈数计算

  1. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  2. {
  3. /* 判断当前计数器的方向*/
  4. if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&TIM_EncoderHandle))
  5. /* 下溢 */
  6. Encoder_Overflow_Count--;
  7. else
  8. /* 上溢 */
  9. Encoder_Overflow_Count++;
  10. }

4、最终计算

  1. void HAL_SYSTICK_Callback(void)
  2. {
  3. static uint16_t i = 0;
  4. i++;
  5. if(i == 100)/* 100ms计算一次 */
  6. {
  7. /* 电机旋转方向 = 计数器计数方向 */
  8. Motor_Direction = __HAL_TIM_IS_TIM_COUNTING_DOWN(&TIM_EncoderHandle);
  9. /* 当前时刻的总计数值 = 计数器值 + 计数溢出次数 * ENCODER_TIM_PERIOD */
  10. Capture_Count =__HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (Encoder_Overflow_Count * ENCODER_TIM_PERIOD);
  11. /* 转轴转数 = 单位时间内的计数值 / 编码器的总分辨率 * 时间系数 */
  12. Shaft_Speed = (float)(Capture_Count - Last_Count) / ENCODER_TOTAL_RESOLUTION * 10 ;
  13. /* 记录当前总计数值 */
  14. Last_Count = Capture_Count;
  15. i = 0;
  16. }
  17. }

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多