Link

步进-方向接口

步进/方向通信是运行步进电机最常见的通信接口之一。它非常基础,使用两个数字信号:step(步进)和direction(方向)。step信号产生一个短脉冲,表示电机应该以预定义的长度执行一步,direction信号决定运动方向(例如,HIGH为正向,LOW为反向)。

如前所述,这种接口特别适合步进电机,因为它们的运动本质上就是以步为单位的。但该接口也可以用于许多与步进电机无关的场景。一般来说,步进/方向接口可以描述为一个计数器,其中direction信号决定计数方向,step提供要计数的脉冲:

// on rising edge of step signal 
if(direction == HIGH) counter++;
else counter--; 

最后,要获得你感兴趣的值,只需将当前计数器值乘以步长值:

received_value = counter*my_step;

如何使用步进 - 方向监听器

为了更简洁地实现这一功能,SimpleFOC库基于StepDirListener类实现了一个中断式的接口版本:

// StepDirListener(step, dir, counter_to_value)
// - step              - step pin number
// - dir               - dir pin number
// - step_per_rotation - transformation variable from step count to your variable (ex. motor angle in radians)
StepDirListener step_dir = StepDirListener( 2, 5, _2PI/200.0 );

定义StepDirListener类后,其硬件引脚会在init()函数中进行配置,该函数需要添加到setup()函数中。

// init step and dir pins
step_dir.init();

此外,为了实现实际的计数功能,库采用了基于中断的方法,因此StepDirListener提供了handle()函数,你只需对其进行封装,例如:

// static wrapper function
void onStep() { step_dir.handle(); }

最后,你可以通过将封装函数提供给enableInterrupt()函数来启用计数器:

// enable interrupts 
step_dir.enableInterrupt(onStep);

最终,用户有两种方式获取接收的值。可以通过调用getValue()函数读取:

float my_variable = step_dir.getValue();

第二种获取值的方式是,关联你希望StepDirListener在每次更新计数器时同步更新的变量:

// some variable user wants to update 
float my_value;
// attach the variable to be updated on each step (optional) 
step_dir.attach(&my_value);

⚠️ 注意:性能不是最优

像步进/方向这样的简单通信方式,设计上更适合用硬件处理,而基于软件中断的实现通常不是最优解决方案。它能为用户提供良好的测试基础,但难以保证长期的稳定性。

示例代码

这是一个简单的步进 - 方向监听器代码。更多示例请查看库的examples/utils/communication_test/step_dir文件夹。

/**
 * A simple example of reading step/dir communication 
 *  - this example uses interrupts
*/

#include <SimpleFOC.h>

// angle 
float received_angle = 0;

// StepDirListener( step_pin, dir_pin, counter_to_value)
StepDirListener step_dir = StepDirListener(2, 3, 360.0/200.0); // receive the angle in degrees
void onStep() { step_dir.handle(); }

void setup() {

  Serial.begin(115200);
  
  // init step and dir pins
  step_dir.init();
  // enable interrupts 
  step_dir.enableInterrupt(onStep);
  // attach the variable to be updated on each step (optional) 
  // the same can be done asynchronously by caling step_dir.getValue();
  step_dir.attach(&received_angle);
    
  Serial.println(F("Step/Dir listenning."));
  _delay(1000);
}

void loop() {
  Serial.print(received_angle);   // automatically updated by the StepDirListener class
  Serial.print("\t");
  Serial.println(step_dir.getValue()); // getter of the StepDirListener class
  _delay(500);
}