Arduino: program do testowania napędu, pozwalający na uzyskanie wyższych częstotliwości PULs..

Program do Testowania Napędów z Arduino UNO

Program generuje sygnały sterujące dla napędów krokowych lub serwonapędów za pomocą Arduino, umożliwiając testowanie ich działania. Generuje impulsy (PUL) oraz sygnał kierunkowy (DIR) z konfigurowalną częstotliwością i czasem trwania impulsu.

Program Arduino


#define PUL_PIN 9        // Pin 9 (OC1A) – impulsy
#define DIR_PIN 10       // Pin 10 – kierunek
#define SZYBKOSC 100000  // Częstotliwość w Hz (np. 20 Hz)
#define TIME_US 8        // Czas trwania impulsu w mikrosekundach (>= 3 µs)
#define STEPS_PER_DIR 40000 // Liczba kroków przed zmianą kierunku

uint16_t step_counter = 0;  // Licznik kroków
uint8_t prev_state = 0;     // Poprzedni stan pinu 9

void setup() {
  pinMode(PUL_PIN, OUTPUT);  // Pin impulsów
  pinMode(DIR_PIN, OUTPUT);  // Pin kierunku
  digitalWrite(DIR_PIN, LOW);

  uint32_t clock_freq = 16000000UL;  // Częstotliwość zegara: 16 MHz
  uint32_t target_freq = SZYBKOSC;   // Docelowa częstotliwość w Hz
  uint8_t prescaler = 1;             // Domyślny preskaler
  uint16_t TOP;

  uint32_t period_ticks = clock_freq / target_freq;  // Liczba cykli na okres
  if (period_ticks > 65535) {  // Jeśli przekracza 16-bitowy rejestr
    prescaler = 8;
    period_ticks = clock_freq / (target_freq * prescaler);
    if (period_ticks > 65535) {
      prescaler = 64;
      period_ticks = clock_freq / (target_freq * prescaler);
      if (period_ticks > 65535) {
        prescaler = 256;
        period_ticks = clock_freq / (target_freq * prescaler);
        if (period_ticks > 65535) {
          prescaler = 1024;
          period_ticks = clock_freq / (target_freq * prescaler);
        }
      }
    }
  }

  TOP = (uint16_t)(period_ticks - 1);

  uint16_t pulse_cycles = TIME_US * (clock_freq / 1000000UL);  // Przeliczenie µs na cykle
  pulse_cycles = pulse_cycles / prescaler;
  if (pulse_cycles < 48 / prescaler) pulse_cycles = 48 / prescaler; // Minimum 3 µs
  if (pulse_cycles > TOP) pulse_cycles = TOP;

  TCCR1A = (1 << COM1A1) | (1 << WGM11);  // Clear OC1A on match, Fast PWM
  TCCR1B = (1 << WGM13) | (1 << WGM12);   // Fast PWM z ICR1
  
  switch (prescaler) {
    case 1:    TCCR1B |= (1 << CS10); break;
    case 8:    TCCR1B |= (1 << CS11); break;
    case 64:   TCCR1B |= (1 << CS11) | (1 << CS10); break;
    case 256:  TCCR1B |= (1 << CS12); break;
    case 1024: TCCR1B |= (1 << CS12) | (1 << CS10); break;
  }

  ICR1 = TOP;
  OCR1A = pulse_cycles;
}

void loop() {
  uint8_t current_state = PINB & (1 << PB1);

  if (current_state && !prev_state) {
    step_counter++;
    if (step_counter >= STEPS_PER_DIR) {
      step_counter = 0;
      PORTB ^= (1 << PB2);
    }
  }
  
  prev_state = current_state;
}
  

Główne funkcje programu

  • Generowanie impulsów (PUL): Sygnał PWM generowany jest na pinie 9 (OC1A) za pomocą Timer1 w trybie Fast PWM.
  • Sterowanie kierunkiem (DIR): Sygnał kierunkowy generowany jest na pinie 10.
  • Automatyczna zmiana kierunku: Po osiągnięciu STEPS_PER_DIR, kierunek jest zmieniany (toggle).
  • Dostosowanie preskalera Timer1: Program automatycznie dobiera odpowiedni preskaler, aby zapewnić poprawną generację sygnału.

Konfiguracja parametrów

Parametr Opis
PUL_PIN Pin 9 – Wyjście impulsów (PWM).
DIR_PIN Pin 10 – Wyjście kierunku (HIGH/LOW).
SZYBKOSC Częstotliwość impulsów w Hz (np. 5000 dla 5 kHz).
TIME_US Czas trwania impulsu w mikrosekundach (min. 3 µs).
STEPS_PER_DIR Liczba kroków przed zmianą kierunku (domyślnie 40 000).

Zastosowanie

Zastosowanie
Testowanie napędów krokowych (sterowników typu STEP/DIR).
Kalibracja serwomechanizmów.
Generowanie sygnałów testowych dla układów sterujących silnikami.

Uwagi

  • Program wykorzystuje Timer1 w trybie Fast PWM z ICR1, co zapewnia precyzyjne sterowanie częstotliwością i wypełnieniem.
  • Dla bardzo niskich częstotliwości (< 1 Hz) może być konieczna ręczna modyfikacja preskalera.
  • Minimalny czas impulsu to 3 µs (ograniczenie sprzętowe Arduino).

Schemat podłączenia

ArduinoSterownik napędu

Arduino Sterownik napędu
Pin 9 (PUL) PUL (STEP)
Pin 10 (DIR) DIR
GND GND

Program jest gotowy do użycia z popularnymi sterownikami, takimi jak A4988, DRV8825, TB6600. Można go łatwo dostosować do innych aplikacji wymagających generowania sygnałów STEP/DIR.

 

 

 

 

 

wersja nieco poprawiona....

 

Cel kodu:

Generować precyzyjny sygnał prostokątny (impulsy PUL) na pinie D9 (OC1A) o określonej częstotliwości (SZYBKOSC) i szerokości impulsu (TIME_US) za pomocą Timer1 w trybie Fast PWM z ICR1. Sterować również kierunkiem obrotów przez pin DIR_PIN.


🧠 Tło teoretyczne – Timer1 (16-bit) i tryb Fast PWM z ICR1

  • ATmega328P ma 3 timery:

    • Timer0 – 8-bit (używany przez millis())

    • Timer1 – 16-bit, idealny do generowania sygnałów

    • Timer2 – 8-bit

  • Tryb Fast PWM z ICR1 jako TOP:

    • Timer liczy od 0 do ICR1, potem wraca do 0

    • Pin OC1A (D9) przełącza stan zgodnie z wartością OCR1A

    • Dzięki ICR1 mamy kontrolę nad częstotliwością

    • Dzięki OCR1A mamy kontrolę nad długością impulsu (czas trwania stanu wysokiego)


📜 kod


// Definicje parametrów impulsów
#define PUL_PIN 9          // OC1A – wyjście PWM (pin D9)
#define DIR_PIN 10         // Kierunek obrotów (pin D10)
#define SZYBKOSC (25000UL) // Docelowa częstotliwość impulsów w Hz
#define TIME_US 5          // Szerokość impulsu w mikrosekundach
#define STEPS_PER_DIR 2500 // Liczba kroków przed zmianą kierunku

// Zmienne do zliczania kroków
uint16_t step_counter = 0;
uint8_t prev_state = 0;  // Do detekcji zbocza narastającego

void setup() {
  pinMode(PUL_PIN, OUTPUT);    // Konfiguracja pinu impulsów
  pinMode(DIR_PIN, OUTPUT);    // Konfiguracja pinu kierunku
  digitalWrite(DIR_PIN, LOW);  // Początkowy kierunek

  // Parametry zegara systemowego
  uint32_t clock_freq = 16000000UL;  // 16 MHz – standard dla Arduino UNO
  uint32_t target_freq = SZYBKOSC;   // Częstotliwość docelowa PWM
  uint16_t prescaler = 1;            // Początkowy preskaler timera
  uint16_t TOP;                      // Górna granica licznika (ICR1)

  // Oblicz liczbę cykli zegara potrzebnych na jeden okres fali PWM
  uint32_t period_ticks = clock_freq / target_freq;

  // Automatyczne dobranie preskalera, jeśli TOP > 65535 (zakres 16-bit)
  if (period_ticks > 65535) {
    prescaler = 8;
    period_ticks = clock_freq / (target_freq * prescaler);
    if (period_ticks > 65535) {
      prescaler = 64;
      period_ticks = clock_freq / (target_freq * prescaler);
      if (period_ticks > 65535) {
        prescaler = 256;
        period_ticks = clock_freq / (target_freq * prescaler);
        if (period_ticks > 65535) {
          prescaler = 1024;
          period_ticks = clock_freq / (target_freq * prescaler);
        }
      }
    }
  }

  TOP = (uint16_t)(period_ticks - 1); // Ustawienie wartości ICR1

  // Obliczenie szerokości impulsu OCR1A (w cyklach zegara)
  uint16_t pulse_cycles = TIME_US * (clock_freq / 1000000UL);  // µs → cykle
  pulse_cycles = pulse_cycles / prescaler;

  // Wymuszenie minimalnego impulsu (np. dla drivera)
  if (pulse_cycles < 48 / prescaler) pulse_cycles = 48 / prescaler;

  // Zapewnienie, że impuls nie jest dłuższy niż cały okres
  if (pulse_cycles > TOP) pulse_cycles = TOP;

  // Konfiguracja Timer1 w trybie Fast PWM z ICR1 jako TOP
  TCCR1A = (1 << COM1A1) | (1 << WGM11);  // WGM11=1, COM1A1=1: OC1A zmienia stan
  TCCR1B = (1 << WGM13) | (1 << WGM12);   // WGM13=1, WGM12=1 → tryb 14 (Fast PWM)

  // Ustawienie preskalera (CSx bity)
  switch (prescaler) {
    case 1:    TCCR1B |= (1 << CS10); break;
    case 8:    TCCR1B |= (1 << CS11); break;
    case 64:   TCCR1B |= (1 << CS11) | (1 << CS10); break;
    case 256:  TCCR1B |= (1 << CS12); break;
    case 1024: TCCR1B |= (1 << CS12) | (1 << CS10); break;
  }

  ICR1 = TOP;          // Rejestr górnej granicy – ustala częstotliwość
  OCR1A = pulse_cycles; // Długość stanu wysokiego – szerokość impulsu
}

void loop() {
  // Czytaj stan pin D9 (OC1A – impuls)
  uint8_t current_state = PINB & (1 << PB1); // PB1 to pin 9 (OC1A)

  // Detekcja zbocza narastającego – początek impulsu
  if (current_state && !prev_state) {
    step_counter++;
    if (step_counter >= STEPS_PER_DIR) {
      step_counter = 0;
      // Zmiana kierunku – zmiana stanu DIR_PIN
      PORTB ^= (1 << PB2); // PB2 to pin 10
    }
  }

  prev_state = current_state;
}

 

📘 Szersze omówienie elementów:

🕒 Tryb Fast PWM z ICR1 jako TOP (WGM = 14)

Rejestr Rola
ICR1 określa wartość końcową licznika (czyli długość okresu = częstotliwość)
OCR1A określa moment przełączenia stanu na pinie OC1A = szerokość impulsu
TCCR1A/B konfigurują tryb pracy timera i preskaler
CS10/11/12 wybierają preskaler (dzielnik częstotliwości zegara)