V souvislosti se synkovo projektem RC tanku jsem se nechal vtáhnout do problematiky řízení některých funkcí tanku, dost času mi (jako samoukovi amatérovi) zabralo řízení krokového motoru pro (kontinuální) otáčení věže.
Po řadě víceméně (spíš méně) fungujících pokusů jsem konečně z jedné
stránky o Arduinu plagiátorsky použil příklady ohledně interních časovačů, konkrétně se mi líbí tahle
kalkulačka pro CTC režim časovačů.
Trocha mordování a výsledekem je konečně celkem uspokojivě fungující sketch pro řízení krokového motoru klasickým servosignálem:
Kód:
// impulzy pro step driver @ Arduino nano
// rizeni otaceni veze tanku RC impulzy PWM
// AVR Timer CTC Interrupts Calculator
// v. 8
// http://www.arduinoslovakia.eu/application/timer-calculator
namespace {
#define pin_PWM 3 // vstup PWM @ pin
#define pin 4 // vystup
#define pin_smer 6 // pin DIR
#define pin_krok 7 // pin STEP
#define pin_en 8 // pin ENABLE
#define pin_Vcc 9 // pin napajeni pro driver
int hystereze = 30;
int neutral_uS = 1500;
int min_uS = 1000;
int max_uS = 2000;
int cislo;
int timer;
int min_cislo = 40;
int max_cislo = 6000;
int inp_uS = 1500;
long int new_uS;
}
void setupTimer1() {
noInterrupts();
// Clear registers
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
TIMSK1 = 0; // Clear the interrupt mask
// Hz (16000000/((OCR1A + 1) * 256))
OCR1A = 1249;
// CTC
TCCR1B |= (1 << WGM12);
// Prescaler 256
TCCR1B |= (1 << CS12);
// Output Compare Match A Interrupt Enable
TIMSK1 |= (1 << OCIE1A);
interrupts();
}
void setup() {
pinMode(pin_krok, OUTPUT); // signál STEP
pinMode(pin_smer, OUTPUT); // signál DIR
pinMode(pin_PWM, INPUT_PULLUP);
pinMode(pin_en, OUTPUT);
pinMode(pin, OUTPUT);
pinMode(pin_Vcc, OUTPUT);
digitalWrite(pin_Vcc, LOW);
// Serial.begin(115200);
delay(500);
attachInterrupt(1, puls_start, RISING);
delay(500);
setupTimer1();
digitalWrite(pin_en, LOW);
}
void loop(){
set_timer();
// tisk();
}
void puls_start() {
new_uS = micros();
attachInterrupt(1, puls_end, FALLING);
}
void puls_end() {
new_uS = micros() - new_uS;
attachInterrupt(1, puls_start, RISING);
new_uS = min(new_uS, max_uS);
new_uS = max(new_uS, min_uS);
cislo = abs(new_uS - neutral_uS);
if (new_uS > neutral_uS){ // načtení směru
digitalWrite(pin_smer, 1);
}
else {
digitalWrite(pin_smer, 0);
}
}
void set_timer() {
timer = 6000 - 12 * cislo;
timer = min(timer, max_cislo);
timer = max(timer, min_cislo);
}
ISR(TIMER1_COMPA_vect) {
if (cislo > hystereze) {
OCR1A = timer;
digitalWrite(13, HIGH);
digitalWrite(pin_krok, HIGH);
delayMicroseconds(5);
digitalWrite(pin_krok, LOW);
}
else {
digitalWrite(13, LOW);
}
}
void tisk() {
// Serial.print(inp_uS);
// Serial.print(" ");
Serial.println(timer);
}
Načítání délky PWM servoimpulzu jsem měl již dříve přes přerušení na vstupu, časovač přinesl inteligentnější časování kroků pro driver. Kolem je pár výpočtů, ty možná ještě doladím, týkají se hlavně omezení max. a min. rychlosti otáčení a přepočtu délky impulzu na čas mezi kroky; lineární převod není úplně to malinové, i když to funguje již uspokojivě. Zkusím asi nějakou např. expo funkci/vzoreček.
Co mi ještě včera hrabalo do slušného fungování bylo ošetření nekorektní délky PWM impulzu. Původně tam byla řádka
Kód:
if (new_uS > min_uS and new_uS < max_uS) inp_uS = new_uS; // osetreni delky
a to nevím proč dost zlobilo; převod vynechával, nešlo přejít rychle z jedné polohy kniplu na jinou, muselo se pomalu.
Nahradil jsem to jinými funkcemi a najednou se to umoudřilo a funguje to jak má:
Kód:
new_uS = min(new_uS, max_uS);
new_uS = max(new_uS, min_uS);