STM32F4编码器计数正在改变它不应该

时间:2016-09-12 12:43:28

标签: microcontroller stm32f4

我目前正在使用STM32F4和STM32F429ZI Nucleo-144板。我希望使用这个微控制器通过正交编码器接口评估旋转编码器的位置。查看文档,这是通过计时器完成的。我将微处理器上的A / B编码器输出连接到PA6和PC7,但我注意到计数似乎正在漂移。

在调试过程中,我注意到如果我断开其中一个编码器输出到微控制器并移动电机,即使只连接了一个编码器线,计数仍会递增/递减。由于我依靠TI1和TI2边沿,因此不应该发生这种情况。如果我正确读取下图,由于我的一条线路使用内部上拉保持高电平,另一条输入上的时钟脉冲应该上/下/上/下,实际上只是在两个不同的计数之间循环。但是,如果我正在旋转编码器,则计数会根据方向继续递增或递减。

为什么只连接一个编码器输入,编码器数量会发生变化?我还附加了范围跟踪,以证明只有一个计数是活动的,以及代码。

编辑:我也尝试将极性从BOTH EDGE改为RISING EDGE,没有任何明显的好处。

enter image description here

enter image description here

#include "stm32f4xx_hal.h"
#include "encoder_test.h"

GPIO_InitTypeDef  GPIO_InitStruct;
TIM_HandleTypeDef Timer_InitStruct;
TIM_Encoder_InitTypeDef Encoder_InitStruct;

void EncoderTest_Init()
{
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_TIM3_CLK_ENABLE();

    /**TIM3 GPIO Configuration
    PA6     ------> TIM3_CH1
    PC7     ------> TIM3_CH2
    */

    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    Timer_InitStruct.Instance = TIM3;
    Timer_InitStruct.Init.Period = 0xFFFF;
    Timer_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP;
    Timer_InitStruct.Init.Prescaler = 1;
    Timer_InitStruct.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

    Encoder_InitStruct.EncoderMode = TIM_ENCODERMODE_TI12;
    Encoder_InitStruct.IC1Filter = 0x00;
    Encoder_InitStruct.IC1Polarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
    Encoder_InitStruct.IC1Prescaler = TIM_ICPSC_DIV1;
    Encoder_InitStruct.IC1Selection = TIM_ICSELECTION_DIRECTTI;
    Encoder_InitStruct.IC2Filter = 0x00;
    Encoder_InitStruct.IC2Polarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
    Encoder_InitStruct.IC2Prescaler = TIM_ICPSC_DIV1;
    Encoder_InitStruct.IC2Selection = TIM_ICSELECTION_DIRECTTI;

    if (HAL_TIM_Encoder_Init(&Timer_InitStruct, &Encoder_InitStruct) != HAL_OK)
    {
        while (1);
    }

    if (HAL_TIM_Encoder_Start_IT(&Timer_InitStruct, TIM_CHANNEL_1) != HAL_OK)
    {
        while (1);
    }
}


void TIM3_IRQHandler()
{
    HAL_TIM_IRQHandler(&Timer_InitStruct);
}

1 个答案:

答案 0 :(得分:1)

经过进一步调查,该问题似乎是由预先决定的。提供偶数值时,预分频器在编码器模式下不起作用。由于预分频器是输入值+ 1,使用STM32F4 HAL,输入的预分频器必须是偶数。

我发现确认我不是唯一有此问题的人forum post。在帖子中有一些讨论,预分频器可能与编码器模式不兼容,但尚未得到证实。我已经发送了一封电子邮件给ST以了解它的底部。如果不支持,则输入预分频器值为0是安全的。

以下是以下工作代码:

#include "stm32f4xx_hal.h"
#include "encoder_test.h"

GPIO_InitTypeDef  GPIO_InitStruct;

TIM_HandleTypeDef Timer3_InitStruct;
TIM_Encoder_InitTypeDef EncoderTim3_InitStruct;

void EncoderTest_Init_Tim3()
{
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_TIM3_CLK_ENABLE();

    /**TIM3 GPIO Configuration
    PA6     ------> TIM3_CH1
    PC7     ------> TIM3_CH2
    */

    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    Timer3_InitStruct.Instance = TIM3;
    Timer3_InitStruct.Init.Period = 0xFFFF;
    Timer3_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP;
    Timer3_InitStruct.Init.Prescaler = 10;
    Timer3_InitStruct.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

    EncoderTim3_InitStruct.EncoderMode = TIM_ENCODERMODE_TI12;
    EncoderTim3_InitStruct.IC1Filter = 0x00;
    EncoderTim3_InitStruct.IC1Polarity = TIM_INPUTCHANNELPOLARITY_RISING;
    EncoderTim3_InitStruct.IC1Prescaler = TIM_ICPSC_DIV4;
    EncoderTim3_InitStruct.IC1Selection = TIM_ICSELECTION_DIRECTTI;
    EncoderTim3_InitStruct.IC2Filter = 0x00;
    EncoderTim3_InitStruct.IC2Polarity = TIM_INPUTCHANNELPOLARITY_RISING;
    EncoderTim3_InitStruct.IC2Prescaler = TIM_ICPSC_DIV4;
    EncoderTim3_InitStruct.IC2Selection = TIM_ICSELECTION_DIRECTTI;

    if (HAL_TIM_Encoder_Init(&Timer3_InitStruct, &EncoderTim3_InitStruct) != HAL_OK)
    {
        while (1);
    }

    if (HAL_TIM_Encoder_Start_IT(&Timer3_InitStruct, TIM_CHANNEL_1) != HAL_OK)
    {
        while (1);
    }
}



void TIM3_IRQHandler()
{
    HAL_TIM_IRQHandler(&Timer3_InitStruct);
}

编辑: 在与ST技术支持部门交谈后,编码器接口无意与预分频器值一起使用,即使是OR奇数。我在下面粘贴了他们的响应,但即使使用看似有效的预分频器值,编码器也可能计算出随时间的漂移。

另一种解决方案是不使用预分频器,而是使用建议的here方法将16位值扩展到32位空间。我已经重新打印了这个方法,以防链接失效:

2013年11月19日来自ST&#39论坛的用户goosen.kobus.001:

根据我的经验,使用溢出中断来放大编码器是不可靠的,尤其是当您使用高分辨率编码器时:编码器值会在您输入中断的瞬间发生变化,从而导致如果编码器应该在0处停止,就像伺服电机被命令进入编码器位置0那样,系统在递增上部字时应该递减等。

我发现的最佳方法是手动完成。这是我的程序:

  1. 确保经常运行读取编码器值的控制回路(即,如果编码器全速旋转,编码器值在溢出之间仍然至少读取10-20次。对于我的伺服电机应用1ms的循环间隔就足够了。

  2. 跟踪最后读取的编码器值。

  3. 将当前和最后一个编码器值分成象限(最高有效2位)。即pos_now& = 0xC000; pos_last& = 0xC000;

  4. 检查编码器是否在最后一步中从象限0移动到象限3或3移动到0:

    4.1 if(pos_now == 0&& pos_last == 0xC000)upper_word ++;

    4.2 if(pos_now == 0xC000&& pos_last == 0)upper_word - ;

  5. 这就是为什么我说编码器读取循环需要经常运行;你需要确保经常读取该值,使得在读取之间不可能从象限0-> 1-> 2-> 3。 还应该可以将此逻辑放入另一个以10kHz运行的定时器中断。这样你就有了一个始终是最新的编码器值。

    ST响应:

    您好,

    我得到了设计师和计时器架构师的反馈。 编码器接口设计为无预分频器工作,以便不降低编码器的分辨率。

    正如您所观察到的,他们已经确认它不能用于预分频器值,只能用于奇数值。 我们有一个预分频器的子计数器,它是单向的,因此不受计数器方向的影响,并在定时器时钟的每个上升沿递增(没有预分频器)。 计数器方向在定时器时钟的每个上升沿(不使用预分频器)更新,但计数器仅在预分频器的子计数器达到编程值且根据方向位的值时递增。

    因此,在一种情况下,行为与没有预分频器的行为相同,因为计数器使用不同的方向(每个奇数时钟周期数)更新,但在另一种情况下,计数器的方向始终相同已更新,编码器界面无法正常工作。

    所以你可以使用预分频器但是有一个奇数值。

    推荐用例没有预分频器。

    祝你好运

    ST MCU技术支持