w 24h

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.
#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;
}
| 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 |
|---|
| Testowanie napędów krokowych (sterowników typu STEP/DIR). |
| Kalibracja serwomechanizmów. |
| Generowanie sygnałów testowych dla układów sterujących silnikami. |
Arduino → Sterownik 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.
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.
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)
// 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;
}
| 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) |