根据我对STM32的了解,我有点不确定解决问题的最佳方法是什么。我想用一个集成的霍尔编码器测量电机的速度和位置,每个旋转6400个上升/下降沿,分成两个通道(一个CH给出3200个上升/下降沿)。
最好的方法是什么?
事情是......我有4台电机需要测量。 我考虑了很多选项,但是我希望在位置数据已经知道的情况下只生成中断(基本上,所以我不会在每个脉冲处增加一个位置变量,而是让计时器为我做这个)。
据我所知,一些计时器支持一种名为"编码器模式"的模式。我不知道有关此模式的详细信息,但我希望(如果可能的话)能够在固定的时间内(比如大约20ms)计算我的速度。 在编码器模式下,有一个定时器,是否可以知道上升沿/下降沿计数(我猜想会在CNT寄存器中)并让它在每20 ms触发一次中断,这样我就可以划分CNT寄存器需要20ms才能获得ISR内的计数/秒速度?
我的另一个选项是使用输入捕捉直接模式计数,每个定时器有两个通道(每个电机一个),还有另一个定时器,固定周期为20ms,并计算4个电机的所有速度。但它需要5个计时器......
如果还有其他问题,DMA是否可以帮助将其保留为4个定时器?例如,我们可以用DMA计算吗?
谢谢!
答案 0 :(得分:1)
STM32F407上的编码器接口模式支持定时器1& 8(高级控制定时器 - 16位)和定时器2至5(通用定时器 - 16/32位)。定时器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
调用此函数。
非周期方法可能更容易实现,并且适用于您想要了解经过时间段内平均值速度的应用程序。适用于例如可能相对较慢更新的人类可读显示器。
周期性方法更适合速度控制应用,在这些应用中,您使用反馈回路来控制电机的速度。如果反馈时间不稳定,你将得到很差的控制。
当然可以定期调用非周期函数,但是在delta时间是确定性的情况下会产生不必要的开销。
答案 1 :(得分:0)
计时器可以依靠一种类型的事件
它既可以计算传感器等外部信号,也可以计数时钟信号,但不能同时计算两者。如果你想每20分钟做一些事情,你需要某事,它依赖于稳定的时钟源。
DMA当然可以计算它正在进行的传输,但为了让它每隔20毫秒执行一次,它必须按固定的时间间隔触发。
因此,您需要第五个计时器。
幸运的是,有很多计时器可供选择
F407有14个硬件定时器。您不想使用超过4个,我假设其中10个在您的应用程序的其他地方使用。检查它们的用法。也许有一个可以依赖合适的时钟频率,并且可以生成一个适合于对编码器进行采样的频率的中断。
Cortex-M内核有一个名为SysTick的内部定时器。许多应用程序使用它来每1ms产生一次中断,用于计时和其他周期性任务。如果是这种情况,您可以在每20个SysTick中断中读取编码器值 - 这样做的好处是不需要额外的中断进入/退出开销。否则,您可以直接设置它以每20ms产生一次中断。请注意,您在参考手册中找不到SysTick,它已在STM32F4程序员手册中记录。
RTC具有周期性自动唤醒功能,可以每20 ms产生一次中断。
除非您使用全部6个UARTS,否则您可以将其中一个设置为非常慢的波特率(如1000波特),并继续传输虚拟数据(您不必为其分配物理引脚)。以1000波特率发送,具有8位,一个启动位和一个停止位,每10 ms发出一次中断。 (除非你的APB频率低于允许的最大值,否则它不会让你降到500波特率)