【问题标题】:STM32F407 timers with hall encoders带霍尔编码器的 STM32F407 定时器
【发布时间】:2018-07-16 02:22:28
【问题描述】:

鉴于我对 STM32 的了解,我有点不确定解决问题的最佳方法是什么。我想用集成霍尔编码器测量电机的速度和位置,每转 6400 个上升/下降沿,分为两个通道(一个通道提供 3200 个上升/下降沿)。

最好的方法是什么?

问题是...我有 4 个电机要测量。 我考虑了很多选项,但我想要一个仅在已知位置数据时才生成中断的选项(基本上,所以我不会在每个脉冲处增加自己的位置变量,而是让计时器为我做)。

据我所知,一些计时器支持一种称为“编码器模式”的模式。我不知道有关此模式的详细信息,但我希望(如果可能的话)能够在固定的时间(比如大约 20 毫秒)内计算我的速度。 是否有可能在编码器模式下,使用一个计时器,同时知道上升/下降沿计数(我猜这将在 CNT 寄存器中)并让它在每 20 毫秒触发一次中断,这样我就可以划分将 CNT 寄存器延迟 20ms 以获得 ISR 内的计数/秒速度?

我的另一个选项是使用 Input Capture 直接模式计数,每个计时器上有两个通道(每个电机一个通道),并有另一个固定周期为 20 毫秒的计时器,并计算那里 4 个电机的所有速度.但它需要 5 个计时器...

如果还有其他问题,DMA 是否可以帮助将其保持在 4 个定时器?例如,我们可以用 DMA 来计算吗?

谢谢!

【问题讨论】:

  • 听起来您需要 RTC 与解码输入捕获定时器并行运行。

标签: timer embedded stm32 encoder stm32f4discovery


【解决方案1】:

计时器可以对一种类型的事件进行计数

它既可以依靠传感器等外部信号,也可以依靠时钟信号,但不能同时依靠这两种信号。如果您想在每 20 毫秒内执行一次操作,则需要某事依靠稳定的时钟源。

DMA 当然可以计算它正在执行的传输,但要让它每 20 毫秒执行一次,它必须以固定的时间间隔被某些东西触发。

因此,您需要第五个计时器。

幸运的是,有很多计时器可供选择

  • 还有 10 个计时器

F407 有 14 个硬件定时器。您不想使用超过 4 个,我假设其中 10 个在您的应用程序的其他地方使用。检查它们的使用情况。也许有一个依赖于合适的时钟频率,并且可以生成一个频率适合编码器采样的中断。

  • SysTick 计时器

Cortex-M 内核有一个称为 SysTick 的内部计时器。许多应用程序使用它每 1ms 生成一个中断,以进行计时和其他周期性任务。如果是这种情况,您可以在每 20 个 SysTick 中断中读取编码器值 - 这具有不需要额外的中断进入/退出开销的优点。否则,您可以直接将其设置为每 20ms 产生一次中断。请注意,您不会在参考手册中找到 SysTick,它记录在 STM32F4 程序员手册中。

  • 实时时钟

RTC 具有周期性自动唤醒功能,可以每 20 毫秒产生一次中断。

  • UART

除非您使用全部 6 个 UART,否则您可以将其中一个设置为非常慢的波特率,例如 1000 波特,并继续传输虚拟数据(您不必为其分配物理引脚)。以 1000 波特、8 位、一个开始和一个停止位发送,每 10 ms 产生一次中断。 (除非您的 APB 频率低于允许的最大值,否则它不会让您降至 500 波特)

【讨论】:

    【解决方案2】:

    定时器 1 和 8(高级控制定时器 - 16 位)和定时器 2 至 5(通用定时器 - 16/32 位)支持 STM32F407 上的编码器接口模式。定时器 9 到 14(也是通用的)不支持正交编码输入。

    重要的是,在这种模式下,定时器作为计数器而不是定时器运行。正交输入允许根据方向向上/向下计数,以便提供相对位置

    请注意,如果您的电机只会沿一个方向行驶,则不需要编码器模式,您可以简单地从单个通道为计时器计时,尽管这会显着降低分辨率,因此低速时的精度可能会受到影响.

    要确定速度,您需要计算相对位置的变化随着时间

    所有 ARM Cortex-M 设备都有一个 SYSTICK 定时器,它会产生周期性中断。您可以使用它来计算时间。

    那么你有两种可能:

    • 定期读取编码器计数器,其中计数的变化与速度成正比(因为时间的变化将是一个常数),
    • 不定期读取编码器并计算位置变化时间变化

    编码器接口模式的重载值是可配置的,对于这个应用程序(速度而不是位置),您应该将其设置为最大值(0xffff 或 0xffffffff),因为它使算法更简单,因为您不必处理带有环绕(只要它在读取之间不环绕两次)。

    对于非周期性方法,假设您在 32 位模式下使用定时器 2 到 5,例如,以下伪代码将以 RPM 为单位生成速度:

    int speedRPM_Aperiodic( int timer_id )
    {
        int rpm = 0 ;
    
        static struct
        {
            uint32_t count ;
            uint32_t time ;
        } previous[] = {{0,0},{0,0},{0,0},{0,0}} ;
    
        if( timer_id < sizeof(previous) / sizeof(*previous) )
        {
            uint32_t current_count = getEncoderCount( timer_id ) ;
            int delta_count = previous[timer_id].count - current_count ;
            previous[timer_id].count = current_count ;
    
            uint32_t current_time = getTick() ;
            int delta_time = previous[timer_id].time - current_time ;
            previous[timer_id].time = current_time ;
    
            rpm = (TICKS_PER_MINUTE * delta_count) / 
                  (delta_time * COUNTS_PER_REVOLUTION) ;
        }
    
        return rpm ;
    }
    

    该函数需要足够频繁地调用,以使计数不会重复多次,并且不能太快以至于计数太小而无法准确测量。

    这可以适用于delta_time 固定且非常准确的周期性方法(例如来自定时器中断或定时器处理程序):

    int speedRPM_Periodic( int timer_id )
    {
        int rpm = 0 ;
    
        uint32_t previous_count[] = {0,0,0,0} ;
    
        if( timer_id < sizeof(previous_count) / sizeof(*previous_count) )
        {
            uint32_t current_count = getEncoderCount( timer_id ) ;
            int delta_count = previous[timer_id].count - current_count ;
            previous_count[timer_id] = current_count ;
    
            rpm = (TICKS_PER_MINUTE * delta_count) / 
                  (SPEED_UPDATE_TICKS * COUNTS_PER_REVOLUTION) ;
        }
    
        return rpm ;
    }
    

    这个函数必须在每个SPEED_UPDATE_TICKS 被调用一次。

    非周期性方法可能更容易实现,并且适用于您想知道过去一段时间内的平均速度的应用程序。适合例如更新速度相对较慢的人类可读显示。

    周期性方法更适合速度控制应用,在这些应用中您使用反馈回路来控制电机的速度。如果反馈时间不恒定,您将无法控制。

    当然可以定期调用非周期函数,但在增量时间是确定性的情况下会产生不必要的开销。

    【讨论】:

    • 非常感谢您解除了我的疑虑。事实上,由于速度控制,我不想要非周期性计数。
    猜你喜欢
    • 2017-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多