定时器 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 被调用一次。
非周期性方法可能更容易实现,并且适用于您想知道过去一段时间内的平均速度的应用程序。适合例如更新速度相对较慢的人类可读显示。
周期性方法更适合速度控制应用,在这些应用中您使用反馈回路来控制电机的速度。如果反馈时间不恒定,您将无法控制。
当然可以定期调用非周期函数,但在增量时间是确定性的情况下会产生不必要的开销。