Krokohrátky s Arduinem 4

Doposud jsme rychlost a směr otáčení krokových motorků řídili potenciometrem, tedy stejnosměrným napětím, přiváděným na analogový vstup Arduina. Dnes se podíváme, jak je možno krokový motor řídit modelářskými servopulsy.

Řízení modelářského servomechanismu:

Poloha výstupního hřídele serva odpovídá proporcionálně šířce řídícího impulzu. Řídící impulz je pozitivní s amplitudou od 3 V do napájecího napětí, aktivní dobou trvání proměnnou od 1 do 2 ms a opakovací frekvencí obvykle 50 Hz (Evropa) nebo 60 Hz (USA a Japonsko). U takzvaných digitálních serv ale může být opakovací frekvence i mnohem vyšší. Době trvání řídícího pulzu odpovídá rozsah polohy výstupního hřídele serva ±45°. Většina serv dovoluje zvětšit mechanický rozsah pohybu výstupní osy na ±90° zvětšením rozsahu řídících impulzů od 0,5 do 2,5 ms.

Schéma zapojení:

V tomto zapojení neřídíme rychlost otáčení krokového motoru potenciometrem (tedy analogovým signálem), ale digitálním pulsem s různou dobou trvání. Protože puls může přijít kdykoli v době běhu programu a mohli bychom ho zmeškat, případně naopak bychom si čekáním na jeho příchod blokovali činnost programu, je třeba použít přerušení. Ve vzorových programech je použita knihovna PinChangeInterrupt, která umožňuje sledovat události na všech pinech Arduina. Pro vstup servosignálu je v tomto případě použit pin D8, který tato knihovna testuje jako první a tím pádem má minimální časovou režii.

Program 1:

Jednosměrné řízení rychlosti otáčení krokového motoru servopulsem

/*
stepperServoPulseControl.ino
Jednosměrné řízení rychlosti otáčení krokového motoru servopulsem
Driver STEP / DIR (SMCB10), motor MEMA 17 generic
JeDe robot s.r.o., říjen 2019
www.edurobot.cz
www.robodoupe.cz
*/ 

#include <AccelStepper.h>
// Knihovna AccelStepper (https://www.airspayce.com/mikem/arduino/AccelStepper/)
#include <PinChangeInterrupt.h>
// Knihovna PinChangeInterrupt (https://github.com/NicoHood/PinChangeInterrupt)

AccelStepper stepper1(AccelStepper::DRIVER, 2, 3);
// STEP (2), DIR (3)
// Nastavení druhu řídícího rozhraní a přiřazení pracovních pinů
// Viz příručka knihovny AccelStepper

// přiřazení pinů
#define pulse_pin 8
// Při testu událostí je tento pin první na řadě

// konstanty
#define  MAX_SPEED 2000
#define  MIN_SPEED 0
// Nastavení maxima a minima rychlosti otáčení motoru v krocích za sekundu

#define  MIN_PULSE_WIDTH 750
#define  MAX_PULSE_WIDTH 2265
// Minimální a maximální délka servopulsu v mikrosekundách

// proměnné
float current_speed = 0.0;        // Okamžitá rychlost otáčení motoru v krocích za sekundu
float analog_value = 0.0;         // Převod délky pulsu na rozsah 0 až 1023
                                  // určuje rychlost otáčení motoru.
int duration;                     // Doba trvání servopulsu

volatile unsigned long start = 0;
volatile float pulse_length = 0;

// ZAČÁTEK PROGRAMU 
void setup()
{
  // Knihovně AccelStepper předáme maximální rychlost otáčení motoru
  stepper1.setMaxSpeed(MAX_SPEED);
  
  // nastavíme pin pro čtení servopulsu jako vstupní...
  pinMode(pulse_pin, INPUT);
  
  // ... a připojíme na něj obsluhu přerušení
  attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(pulse_pin), onRising, CHANGE);
}
 
void loop()
{
  analog_value = map(pulse_length, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH, 0, 1023);
  // změřenou délku servopulsu převedeme na rozsah 0 – 1023...
  
  current_speed = (((analog_value/1023) * (MAX_SPEED - MIN_SPEED)) + MIN_SPEED);
  // ... a přepočteme ji na okamžitou rychlost otáčení motoru

  stepper1.setSpeed(current_speed);
  // Nastavíme novou rychlost otáčení...
  
  stepper1.runSpeed();
  // ... a předáme driveru
}

// FUNKCE
void onRising()
{
  processPin();
}

void processPin()
{
  uint8_t trigger = getPinChangeInterruptTrigger(digitalPinToPCINT(pulse_pin));

if(trigger == RISING)
  {
    start = micros();
  }
else if(trigger == FALLING)
  {
    pulse_length = micros() - start;
  }
}

Program 2:

Obousměrné řízení rychlosti otáčení krokového motoru servopulsem

/*
StepperServoPulseControlBidir.ino
Obousměrné řízení rychlosti otáčení krokového motoru servopulsem
Driver STEP / DIR (SMCB10), motor MEMA 17 generic
JeDe robot s.r.o., říjen 2019
www.edurobot.cz
www.robodoupe.cz
*/ 

#include <AccelStepper.h>
// Knihovna AccelStepper
#include <PinChangeInterrupt.h>
// Knihovna PinChangeInterrupt

AccelStepper stepper1(AccelStepper::DRIVER, 2, 3);
// STEP (2), DIR (3)
// Nastavení druhu řídícího rozhraní a přiřazení pracovních pinů
// Viz příručka knihovny AccelStepper

// přiřazení pinů
#define pulse_pin 8
 
// konstanty
#define  MAX_SPEED 2000
#define  MIN_SPEED 0
// Nastavení maxima a minima rychlosti otáčení motoru v krocích za sekundu

#define  MIN_PULSE_WIDTH 750
#define  MAX_PULSE_WIDTH 2265
// Minimální a maximální délka servopulsu v us

// proměnné
float current_speed = 0.0;        // Okamžitá rychlost otáčení motoru v krocích za sekundu
float analog_value = 0.0;         // Převod délky pulsu na rozsah 0 až 1023
                                  // určuje rychlost otáčení motoru.
int duration;                     // Doba trvání servopulsu

volatile unsigned long start = 0;
volatile float pulse_length = 0;

// ZAČÁTEK PROGRAMU 
void setup()
{
  // Knihovně AccelStepper předáme maximální rychlost otáčení motoru
  stepper1.setMaxSpeed(MAX_SPEED);
  pinMode(pulse_pin, INPUT);
  attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(pulse_pin), onRising, CHANGE);
}
  
void loop()
{
  analog_value = map(pulse_length, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH, 0, 1023);

  if (analog_value < 460)
  // Je-li přečtená hodnota menší než 460...
  {
    current_speed = map(analog_value, 0, 460, MAX_SPEED, MIN_SPEED);
    // ... změníme ji z rozsahu 0 až 460 na rozsah
    // daný konstantami MAX_SPEED a MIN_SPEED
  }
  else if (analog_value > 563)
  // Je-li přečtená hodnota větší než 563...
  {
    current_speed = (map(analog_value, 563, 1023, MIN_SPEED, MAX_SPEED))* -1;
    // ... změníme ji z rozsahu 563 až 1023 na rozsah
    // daný konstantami MIN_SPEED a MAX_SPEED
    // Vynásobením konstantou -1 se hodnota current_speed stane zápornou
    // a směr otáčení motoru se změní
  }
  else
  // je-li přečtená hodnota mimo výše uvedené rozsahy...
  {
    current_speed = 0;
    // ... motor zastavíme
  }

  stepper1.setSpeed(current_speed);
  // Nastavíme novou rychlost otáčení…
  
  stepper1.runSpeed();
  // … a předáme driveru
}

// FUNKCE
void onRising()
{
  processPin();
}

void processPin()
{
  uint8_t trigger = getPinChangeInterruptTrigger(digitalPinToPCINT(pulse_pin));

if(trigger == RISING)
  {
    start = micros();
  }
else if(trigger == FALLING)
  {
    pulse_length = micros() - start;
  }
}