w 24h
samopoczucie?
Poniżej znajduje się przetłumaczony i obszernie skomentowany kod main.cpp
projektu HAL2UDP. Komentarze wyjaśniają zarówno poszczególne sekcje kodu, jak i dotykają ogólnych koncepcji używanych w tym projekcie.
https://github.com/jzolee/HAL2UDP/blob/main/src/main.cpp
/*
* Zewnętrzny generator kroków i interfejs IO dla LinuxCNC poprzez Ethernet
* z użyciem dwurdzeniowych modułów ESP32 i W5500.
*
* Copyright 2022 Juhász Zoltán
*
* wersja: 20220330
*
* Ten program jest wolnym oprogramowaniem; możesz go rozpowszechniać i/lub modyfikować
* na warunkach GNU General Public License opublikowanej przez Fundację Wolnego Oprogramowania;
* albo w wersji 2 Licencji, albo (według twojego wyboru) w dowolnej późniejszej wersji.
*
* Ten program jest dystrybuowany w nadziei, że będzie użyteczny,
* ale BEZ JAKIEJKOLWIEK GWARANCJI; bez nawet domyślnej gwarancji
* przydatności handlowej lub przydatności do określonego celu. Zobacz GNU General Public License
* po więcej szczegółów.
*
* Powinieneś otrzymać kopię GNU General Public License
* razem z tym programem; jeśli nie, napisz do Fundacji Wolnego Oprogramowania, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* Sprzęt:
*
* 3v3 -- 3.3v W5500 (zasilanie)
* GND -- GND W5500 (uziemienie)
* GND -- (DB15- 7)
* GND -- (DB15- 8)
*
* GPIO 1 -> OUT-03 lub PWM-03
* GPIO 2 -> OUT-00 lub PWM-00 -[100R]-(DB15 - 9) & onboard blue LED
* GPIO 4 -> OUT-01 lub PWM-01 -[100R]-(DB15 -10)
* GPIO 5 -> W5500 SCS (Chip Select)
* GPIO 12 -> step-0 -[100R]-(DB15 - 1)
* GPIO 13 -> dir-0 -[100R]-(DB15 - 2)
* GPIO 14 -> OUT-04 lub PWM-04
* GPIO 15 -> OUT-05 lub PWM-05
* (RX2) GPIO 16 -> step-1 -[100R]-(DB15 - 3)
* (TX2) GPIO 17 -> dir-1 -[100R]-(DB15 - 4)
* GPIO 18 -> W5500 SCLK (Clock)
* GPIO 19
* GPIO 21 -> step-2 -[100R]-(DB15 - 5)
* GPIO 22 -> dir-2 -[100R]-(DB15 - 6)
* GPIO 23 -> W5500 MOSI (Master Out Slave In)
* GPIO 25 -> OUT-02 lub PWM-02 -[100R]-(DB15 -11)
* GPIO 26
* GPIO 27
* GPIO 32
* GPIO 33
* GPIO 34
* GPIO 35
* (VP) GPIO 36
* (VN) GPIO 39
*/
/*
* NOTATKI GPIO WROOM 32
*
* https://randomnerdtutorials.com/esp32-pinout-reference-gpios/
*
* GPIO Wejście Wyjście Notatki
*
* 0 ? podciągnięty ? ? OK ? generuje sygnał PWM przy uruchamianiu
* 1 !! pin TX !! ? OK ? dane debugowe przy uruchamianiu
* 2 OK OK połączony z diodą LED na płytce
* 3 ? OK ? !! pin RX !! stan wysoki przy uruchamianiu
* 4 OK OK
* 5 OK OK generuje sygnał PWM przy uruchamianiu
* 6-11 X X połączone z wbudowaną pamięcią SPI flash
* 12 ? OK ? OK błąd uruchomienia, jeśli wysokie
* 13 OK OK
* 14 OK OK generuje sygnał PWM przy uruchamianiu
* 15 OK OK generuje sygnał PWM przy uruchamianiu
* 16 OK OK
* 17 OK OK
* 18 OK OK
* 19 OK OK
* 21 OK OK
* 22 OK OK
* 23 OK OK
* 25 OK OK
* 26 OK OK
* 27 OK OK
* 32 OK OK
* 33 OK OK
* 34 OK -- tylko wejście, brak podciągnięcia
* 35 OK -- tylko wejście, brak podciągnięcia
* 36 OK -- tylko wejście, brak podciągnięcia
* 39 OK -- tylko wejście, brak podciągnięcia
*/
#include // Biblioteka Arduino, zapewniająca podstawowe funkcje
#include // Biblioteka do obsługi Ethernetu
#include // Biblioteka do obsługi komunikacji UDP przez Ethernet
/*==================================================================*/
// Adres MAC dla Ethernetu (zwykle unikalny składający się z 6 bajtów)
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// Statyczny adres IP, który będzie używany przez naszą aplikację
IPAddress ip(192, 168, 96, 54); // Adres IP naszego ESP32
// Port, na którym nasz serwer UDP będzie nasłuchiwał
unsigned int port = 58427;
/*==================================================================*/
// Zdefiniowanie pinu CS (Chip Select) dla Ethernetu
#define SPI_CS_PIN 5 // Zastępując wartość w Ethernet_mod/src/utility/w5100.h
/*==================================================================*/
// Definicje pinów dla kroków i kierunków dla trzech osi
#define STEP_0_PIN 12
#define STEP_0_H REG_WRITE(GPIO_OUT_W1TS_REG, BIT12) // Ustawienie pinu STEP 0 w stan wysoki
#define STEP_0_L REG_WRITE(GPIO_OUT_W1TC_REG, BIT12) // Ustawienie pinu STEP 0 w stan niski
#define DIR_0_PIN 13
#define DIR_0_H REG_WRITE(GPIO_OUT_W1TS_REG, BIT13) // Ustawienie pinu DIR 0 w stan wysoki
#define DIR_0_L REG_WRITE(GPIO_OUT_W1TC_REG, BIT13) // Ustawienie pinu DIR 0 w stan niski
#define STEP_1_PIN 16
#define STEP_1_H REG_WRITE(GPIO_OUT_W1TS_REG, BIT16)
#define STEP_1_L REG_WRITE(GPIO_OUT_W1TC_REG, BIT16)
#define DIR_1_PIN 17
#define DIR_1_H REG_WRITE(GPIO_OUT_W1TS_REG, BIT17)
#define DIR_1_L REG_WRITE(GPIO_OUT_W1TC_REG, BIT17)
#define STEP_2_PIN 21
#define STEP_2_H REG_WRITE(GPIO_OUT_W1TS_REG, BIT21)
#define STEP_2_L REG_WRITE(GPIO_OUT_W1TC_REG, BIT21)
#define DIR_2_PIN 22
#define DIR_2_H REG_WRITE(GPIO_OUT_W1TS_REG, BIT22)
#define DIR_2_L REG_WRITE(GPIO_OUT_W1TC_REG, BIT22)
/*==================================================================*/
// Definicje pinów dla wyjść (OUT) oraz PWM
#define OUT_00_PIN 2
#define OUT_00_H REG_WRITE(GPIO_OUT_W1TS_REG, BIT2)
#define OUT_00_L REG_WRITE(GPIO_OUT_W1TC_REG, BIT2)
#define OUT_01_PIN 4
#define OUT_01_H REG_WRITE(GPIO_OUT_W1TS_REG, BIT4)
#define OUT_01_L REG_WRITE(GPIO_OUT_W1TC_REG, BIT4)
#define OUT_02_PIN 25
#define OUT_02_H REG_WRITE(GPIO_OUT_W1TS_REG, BIT25)
#define OUT_02_L REG_WRITE(GPIO_OUT_W1TC_REG, BIT25)
#define OUT_03_PIN 1
#define OUT_03_H REG_WRITE(GPIO_OUT_W1TS_REG, BIT1)
#define OUT_03_L REG_WRITE(GPIO_OUT_W1TC_REG, BIT1)
#define OUT_04_PIN 14
#define OUT_04_H REG_WRITE(GPIO_OUT_W1TS_REG, BIT14)
#define OUT_04_L REG_WRITE(GPIO_OUT_W1TC_REG, BIT14)
#define OUT_05_PIN 15
#define OUT_05_H REG_WRITE(GPIO_OUT_W1TS_REG, BIT15)
#define OUT_05_L REG_WRITE(GPIO_OUT_W1TC_REG, BIT15)
/*==================================================================*/
// Definicje pinów wejściowych (IN)
#define IN_00_PIN 26
#define IN_00 REG_READ(GPIO_IN_REG) & BIT26 // Zmienne do odczytu stanów wejść
#define IN_01_PIN 27
#define IN_01 REG_READ(GPIO_IN_REG) & BIT27
#define IN_02_PIN 32
#define IN_02 REG_READ(GPIO_IN1_REG) & BIT0
#define IN_03_PIN 33
#define IN_03 REG_READ(GPIO_IN1_REG) & BIT1
#define IN_04_PIN 34
#define IN_04 REG_READ(GPIO_IN1_REG) & BIT2
#define IN_05_PIN 35
#define IN_05 REG_READ(GPIO_IN1_REG) & BIT3
#define IN_06_PIN 36
#define IN_06 REG_READ(GPIO_IN1_REG) & BIT4
#define IN_07_PIN 39
#define IN_07 REG_READ(GPIO_IN1_REG) & BIT7
/*==================================================================*/
// Definicje kontrolne bitów do zarządzania różnymi opcjami
#define CTRL_DIRSETUP 0b00000001 // Bit do ustawiania kierunku
#define CTRL_ACCEL 0b00000010 // Bit do aktywacji przyspieszenia
#define CTRL_PWMFREQ 0b00000100 // Bit do definiowania częstotliwości PWM
#define CTRL_READY 0b01000000 // Bit wskazujący, że system jest gotowy
#define CTRL_ENABLE 0b10000000 // Bit włączający/wyłączający funkcję
// Definicje wejść/wyjść IO
#define IO_00 0b00000001 // Definicja dla IO 0
#define IO_01 0b00000010 // Definicja dla IO 1
#define IO_02 0b00000100 // Definicja dla IO 2
#define IO_03 0b00001000 // Definicja dla IO 3
#define IO_04 0b00010000 // Definicja dla IO 4
#define IO_05 0b00100000 // Definicja dla IO 5
#define IO_06 0b01000000 // Definicja dla IO 6
#define IO_07 0b10000000 // Definicja dla IO 7
/*==================================================================*/
// Tworzenie instancji klasy EthernetUDP do zarządzania komunikacją UDP
EthernetUDP Udp; // Instancja do przesyłania i odbierania pakietów UDP
// Struktura dla pakietu komend
struct cmdPacket {
uint8_t control; // Kontrola komendy
uint8_t io; // Wejście/wyjście
uint16_t pwm[6]; // Tablica wartości PWM (do 6 wejść)
int32_t pos[3]; // Pozycje dla 3 osi
float vel[3]; // Prędkości dla 3 osi
} cmd = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0f, 0.0f, 0.0f }; // Inicjalizacja do wartości domyślnych
// Struktura dla pakietu informacji zwrotnej
struct fbPacket {
uint8_t control; // Kontrola informacji zwrotnej
uint8_t io; // Stan wejść/wyjść
int32_t pos[3]; // Pozycje osi
float vel[3]; // Prędkości osi
} fb = { 0, 0, 0, 0, 0, 0.0f, 0.0f, 0.0f }; // Inicjalizacja wartości domyślnych
/*==================================================================*/
// Zmienne volatile do przechowywania ustawień i stanu
volatile unsigned long ul_dirSetup[3] = { 1000, 1000, 1000 }; // Ustawienia kierunku (czas w ns)
volatile float f_accel_x2[3] = { 1000.0, 1000.0, 1000.0 }; // Przyspieszenie * 2 w krokach na sekundę kwadrat
/*==================================================================*/
// Zmienne drobne do cech osi
volatile unsigned long ul_cmd_T[3]; // Zmienne do przechowywania czasów komend
unsigned long ul_accelStep[3] = { 0, 0, 0 }; // Zmienne do przechowywania liczby kroków w przyspieszeniu
// Zmienne do przechowywania różnych ważnych czasów
volatile unsigned long ul_T[3] = { 0, 0, 0 }; // Czas cyklu
volatile unsigned long ul_TH[3] = { 0, 0, 0 }; // Czas wyższy
volatile unsigned long ul_TL[3] = { 0, 0, 0 }; // Czas niższy
volatile bool b_math[3] = { false, false, false }; // Flagi do obliczeń pozycjonowania
volatile bool b_dirSignal[3] = { LOW, LOW, LOW }; // Flagi dla kierunków
volatile bool b_dirChange[3] = { false, false, false }; // Flagi mówiące, że musimy zmienić kierunek
const uint8_t pwm_pin[6] = { OUT_00_PIN, OUT_01_PIN, OUT_02_PIN, OUT_03_PIN, OUT_04_PIN, OUT_05_PIN }; // Piny PWM
bool pwm_enable[6] = { false, false, false, false, false, false }; // Flagi włączające PWM dla każdego kanału
/*==================================================================*/
// Timery do generacji kroków dla trzech osi
hw_timer_t* stepGen_0 = NULL; // Timer dla osi 0
hw_timer_t* stepGen_1 = NULL; // Timer dla osi 1
hw_timer_t* stepGen_2 = NULL; // Timer dla osi 2
/*==================================================================*/
// Funkcja do szybkiego obliczania odwrotności pierwiastka
float IRAM_ATTR fastInvSqrt(const float x) {
const float xhalf = x * 0.5f; // Połowa wartości x
union {
float x; // Typ float
uint32_t i; // Typ 32 bitowy
} u = { .x = x }; // Inicjalizacja unii wartością x
u.i = 0x5f3759df - (u.i >> 1); // Algorytm szybkiej odwrotności pierwiastka
return u.x * (1.5f - xhalf * u.x * u.x); // Zwracanie wyniku
}
/*==================================================================*/
// Obsługuje timer dla osi 0
void IRAM_ATTR onTime_0() {
static bool b_stepState = LOW; // Stan kroku (niski na początku)
static bool b_dirState = LOW; // Stan kierunku
if (b_stepState == LOW) { // Koniec cyklu, wykonywanie impulsu
if (ul_T[0]) { // Istnieje czas, więc należy zacząć impuls
if (!b_dirChange[0]) { // Jeśli kierunek jest poprawny
STEP_0_H; // Ustaw HIGH na pinie STEP 0
timerAlarmWrite(stepGen_0, ul_TH[0], true); // Ustaw alarm timera
b_stepState = HIGH; // Zmień stan na wysoki
b_dirState ? fb.pos[0]++ : fb.pos[0]--; // Zmiana pozycji w zależności od kierunku
b_math[0] = true; // Ustaw flagę do obliczeń
} else { // Musimy zmienić kierunek
if (b_dirSignal[0]) {
DIR_0_H; // Ustaw HIGH na pinie kierunku
b_dirState = HIGH;
} else {
DIR_0_L; // Ustaw LOW na pinie kierunku
b_dirState = LOW;
}
timerAlarmWrite(stepGen_0, ul_dirSetup[0], true); // Ustaw nowy czas ustalony dla kierunku
b_dirChange[0] = false; // Resetuj flagę zmiany kierunku
}
} else { // Nie wymaga impulsu
timerAlarmWrite(stepGen_0, 4000ul, true); // Ustaw alarm timera na mały czas
b_math[0] = true; // Flaga dla obliczeń
}
} else { // Środek cyklu
STEP_0_L; // Ustaw LOW na pinie STEP 0
timerAlarmWrite(stepGen_0, ul_TL[0], true); // Ustaw alarm na czas niższy
b_stepState = LOW; // Zmień stan na niski
}
}
/*==================================================================*/
// Obsługuje timer dla osi 1
void IRAM_ATTR onTime_1() {
static bool b_stepState = LOW; // Stan kroku
static bool b_dirState = LOW; // Stan kierunku
if (b_stepState == LOW) { // Koniec cyklu
if (ul_T[1]) { // Istnieje czas
if (!b_dirChange[1]) { // Kierunek poprawny
STEP_1_H; // Impuls HIGH
timerAlarmWrite(stepGen_1, ul_TH[1], true);
b_stepState = HIGH; // Zmień stan
b_dirState ? fb.pos[1]++ : fb.pos[1]--; // Zmiana pozycji
b_math[1] = true; // Ustaw flagę
} else { // Zmiana kierunku
if (b_dirSignal[1]) {
DIR_1_H;
b_dirState = HIGH;
} else {
DIR_1_L;
b_dirState = LOW;
}
timerAlarmWrite(stepGen_1, ul_dirSetup[1], true);
b_dirChange[1] = false; // Reset flagi
}
} else { // Nie wymaga impulsu
timerAlarmWrite(stepGen_1, 4000ul, true);
b_math[1] = true;
}
} else { // Środek cyklu
STEP_1_L; // Ustaw LOW
timerAlarmWrite(stepGen_1, ul_TL[1], true);
b_stepState = LOW;
}
}
/*==================================================================*/
// Obsługuje timer dla osi 2
void IRAM_ATTR onTime_2() {
static bool b_stepState = LOW;
static bool b_dirState = LOW;
if (b_stepState == LOW) {
if (ul_T[2]) {
if (!b_dirChange[2]) {
STEP_2_H;
timerAlarmWrite(stepGen_2, ul_TH[2], true);
b_stepState = HIGH;
b_dirState ? fb.pos[2]++ : fb.pos[2]--;
b_math[2] = true;
} else {
if (b_dirSignal[2]) {
DIR_2_H;
b_dirState = HIGH;
} else {
DIR_2_L;
b_dirState = LOW;
}
timerAlarmWrite(stepGen_2, ul_dirSetup[2], true);
b_dirChange[2] = false;
}
} else {
timerAlarmWrite(stepGen_2, 4000ul, true);
b_math[2] = true;
}
} else {
STEP_2_L;
timerAlarmWrite(stepGen_2, ul_TL[2], true);
b_stepState = LOW;
}
}
/*==================================================================*/
// Oblicza czas periodyczny w oparciu o przyspieszenie
void IRAM_ATTR newT(const int i) {
// Użycie algorytmu szybkiego odwrotności pierwiastka dla wydajności
ul_T[i] = 40000000.0f * fastInvSqrt((float)ul_accelStep[i] * f_accel_x2[i]); // Sqrt wzór
ul_TH[i] = ul_T[i] >> 1; // Ustalanie połowy
ul_TL[i] = ul_T[i] - ul_TH[i]; // Czas niższy
}
/*==================================================================*/
// Dekceleracja na podstawie liczby kroków
void IRAM_ATTR deceleration(const int i) {
if (ul_accelStep[i]) {
ul_accelStep[i]--; // Decrement steps
if (ul_accelStep[i])
newT(i); // Nowe przeliczenie czasu
else
ul_T[i] = 0; // Ustawienie zerowego czasu
}
}
/*==================================================================*/
// Przyspiesza w zależności od kontroli aktywnej
void IRAM_ATTR acceleration(const int i) {
if (cmd.control & CTRL_ENABLE) {
ul_accelStep[i]++; // Inkrementacja
newT(i); // Obliczenie nowego czasu
} else
deceleration(i); // Dekceleracja w przeciwnym razie
}
/*==================================================================*/
// Obsługuje wyjścia (wyjście dla PWM i innych sygnałów)
void IRAM_ATTR outputHandler() {
static int last_pwm[6] = { 0, 0, 0, 0, 0, 0 }; // Ostatnie wartości PWM dla każdego kanału
bool enable = cmd.control & CTRL_ENABLE; // Sprawdzamy, czy kontrola jest włączona
// Obsługa wyjścia PWM dla pierwszego pinu
if (pwm_enable[0]) {
if (enable) {
if (last_pwm[0] != cmd.pwm[0]) { // Sprawdza zmianę PWM
last_pwm[0] = cmd.pwm[0];
ledcWrite(0, last_pwm[0]); // Zapisuje nową wartość PWM
}
} else {
ledcWrite(0, 0); // Ustawienie PWM na 0
last_pwm[0] = 0;
}
} else {
if (enable)
(cmd.io & IO_00) ? OUT_00_H : OUT_00_L; // Ustawia stan wyjścia na podstawie aktualnego IO
else
OUT_00_L; // Gdy wyłączone, ustaw na niski
}
// Powtarzamy analogicznie dla kolejnych kanałów PWM
if (pwm_enable[1]) {
if (enable) {
if (last_pwm[1] != cmd.pwm[1]) {
last_pwm[1] = cmd.pwm[1];
ledcWrite(2, last_pwm[1]);
}
} else {
ledcWrite(2, 0);
last_pwm[1] = 0;
}
} else {
if (enable)
(cmd.io & IO_01) ? OUT_01_H : OUT_01_L;
else
OUT_01_L;
}
if (pwm_enable[2]) {
if (enable) {
if (last_pwm[2] != cmd.pwm[2]) {
last_pwm[2] = cmd.pwm[2];
ledcWrite(4, last_pwm[2]);
}
} else {
ledcWrite(4, 0);
last_pwm[2] = 0;
}
} else {
if (enable)
(cmd.io & IO_02) ? OUT_02_H : OUT_02_L;
else
OUT_02_L;
}
if (pwm_enable[3]) {
if (enable) {
if (last_pwm[3] != cmd.pwm[3]) {
last_pwm[3] = cmd.pwm[3];
ledcWrite(6, last_pwm[3]);
}
} else {
ledcWrite(6, 0);
last_pwm[3] = 0;
}
} else {
if (enable)
(cmd.io & IO_03) ? OUT_03_H : OUT_03_L;
else
OUT_03_L;
}
if (pwm_enable[4]) {
if (enable) {
if (last_pwm[4] != cmd.pwm[4]) {
last_pwm[4] = cmd.pwm[4];
ledcWrite(8, last_pwm[4]);
}
} else {
ledcWrite(8, 0);
last_pwm[4] = 0;
}
} else {
if (enable)
(cmd.io & IO_04) ? OUT_04_H : OUT_04_L;
else
OUT_04_L;
}
if (pwm_enable[5]) {
if (enable) {
if (last_pwm[5] != cmd.pwm[5]) {
last_pwm[5] = cmd.pwm[5];
ledcWrite(10, last_pwm[5]);
}
} else {
ledcWrite(10, 0);
last_pwm[5] = 0;
}
} else {
if (enable)
(cmd.io & IO_05) ? OUT_05_H : OUT_05_L;
else
OUT_05_L;
}
}
/*==================================================================*/
// Obsługuje sygnały wejściowe
void IRAM_ATTR inputHandler() {
// Odczytuje stany wejść i ustawia flagi
(IN_00) ? fb.io = IO_00 : fb.io = 0; // Ustawienie flags io
if (IN_01)
fb.io |= IO_01;
if (IN_02)
fb.io |= IO_02;
if (IN_03)
fb.io |= IO_03;
if (IN_04)
fb.io |= IO_04;
if (IN_05)
fb.io |= IO_05;
if (IN_06)
fb.io |= IO_06;
if (IN_07)
fb.io |= IO_07;
}
/*==================================================================*/
// Obsługuje komendy przychodzące
void IRAM_ATTR commandHandler() {
if (cmd.control & CTRL_READY) { // Sprawdzenie, czy kontrola jest gotowa
for (int i = 0; i < 3; i++) {
// Ustalanie czasu na podstawie podanej prędkości
if (cmd.vel[i] > 0.0f)
ul_cmd_T[i] = (unsigned long)(40000000.0f / cmd.vel[i]);
else if (cmd.vel[i] < 0.0f)
ul_cmd_T[i] = (unsigned long)(40000000.0f / -cmd.vel[i]);
else
ul_cmd_T[i] = 40000000ul; // Domyślna wartość
}
}
// Weryfikacja czy już jesteśmy gotowi na kolejne komendy
if (!(fb.control & CTRL_READY)) {
if ((fb.control & CTRL_DIRSETUP)
&& (fb.control & CTRL_ACCEL)
&& (fb.control & CTRL_PWMFREQ)) {
fb.control |= CTRL_READY; // Ustawienie gotowości
//log_printf("READYn"); // Logowanie gotowości (jeśli jest funkcja logowania)
} else if (cmd.control & CTRL_DIRSETUP) { // Ustawienie kierunku
fb.control |= CTRL_DIRSETUP; // Ustawienie flagi
for (int i = 0; i < 3; i++)
ul_dirSetup[i] = cmd.pos[i] / 25; // Ustalanie ustawień kierunku
} else if (cmd.control & CTRL_ACCEL) { // Ustawienie przyspieszenia
fb.control |= CTRL_ACCEL; // Ustawienie flagi
for (int i = 0; i < 3; i++)
f_accel_x2[i] = (float)cmd.pos[i] * 2.0; // Podwajanie wartości przyspieszenia
} else if (cmd.control & CTRL_PWMFREQ) { // Ustalanie częstotliwości PWM
fb.control |= CTRL_PWMFREQ; // Ustawienie flagi
for (int i = 0; i < 6; i++) {
if (cmd.pwm[i]) { // Jeśli wartość PWM jest podana
ledcAttachPin(pwm_pin[i], i * 2); // Dołączenie pinu PWM
ledcSetup(i * 2, cmd.pwm[i], 10); // Ustawienia PWM
ledcWrite(i * 2, 0); // Inicjalizacja wyjścia
pwm_enable[i] = true; // Włączenie flagi PWM
} else {
// Jeśli PWM nie jest wymagany, ustaw na LOW
if (i == 0) {
pinMode(OUT_00_PIN, OUTPUT);
digitalWrite(OUT_00_PIN, 0);
} else if (i == 1) {
pinMode(OUT_01_PIN, OUTPUT);
digitalWrite(OUT_01_PIN, 0);
} else if (i == 2) {
pinMode(OUT_02_PIN, OUTPUT);
digitalWrite(OUT_02_PIN, 0);
} else if (i == 3) {
pinMode(OUT_03_PIN, OUTPUT);
digitalWrite(OUT_03_PIN, 0);
} else if (i == 4) {
pinMode(OUT_04_PIN, OUTPUT);
digitalWrite(OUT_04_PIN, 0);
} else if (i == 5) {
pinMode(OUT_05_PIN, OUTPUT);
digitalWrite(OUT_05_PIN, 0);
}
}
}
}
}
}
/*==================================================================*/
// Główna pętla dla core 0
void IRAM_ATTR loop_Core0(void* parameter) {
delay(500); // Opóźnienie początkowe
Udp.parsePacket(); /* Opróżnianie bufora */
for (;;) {
// Zmienna do monitorowania
static unsigned long ul_watchdog;
if (Udp.parsePacket() == sizeof(cmd) + 1) { // Sprawdzenie, czy odebrano cały pakiet
char packetBuffer[60]; // Bufor do odbierania i wysyłania danych
Udp.read(packetBuffer, sizeof(cmd) + 1); // Odczytanie pakietu
// Prosta kontrola integralności pakietu
uint8_t chk = 71; // Prosta suma kontrolna
for (int i = 0; i < sizeof(cmd); i++) // Bitowe XOR
chk ^= packetBuffer[i];
if (packetBuffer[sizeof(cmd)] == chk) { // Sprawdzanie sumy kontrolnej
memcpy(&cmd, &packetBuffer, sizeof(cmd)); // Kopiowanie danych do struktury
commandHandler(); // Obsługa komendy
ul_watchdog = millis(); // Resetowanie watchdog
}
inputHandler(); // Sprawdź stan wejść
for (int i = 0; i < 3; i++) {
unsigned long ul_t = ul_T[i];
if (ul_t)
b_dirSignal[i] ? fb.vel[i] = 40000000.0f / (float)ul_t : fb.vel[i] = -40000000.0f / (float)ul_t; // Obliczanie prędkości
else
fb.vel[i] = 0.0f; // Ustawienie prędkości na 0
}
memcpy(&packetBuffer, &fb, sizeof(fb)); // Kopiowanie danych zwrotnych
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); // Rozpoczęcie pakietu UDP
Udp.write(packetBuffer, sizeof(fb)); // Wysyłanie bazy danych struktury
Udp.endPacket(); // Zakończenie pakietu UDP
outputHandler(); // Obsługuje wyjścia
}
// Resetowanie kontrolek po 10 ms
if (millis() - ul_watchdog > 10ul) {
fb.control = 0; // Resetowanie kontroli
cmd.control = 0; // Resetowanie komendy
outputHandler(); // Zapewnia wyjścia
ul_watchdog = millis(); // Resetowanie zegara watchdog
}
}
}
/*==================================================================*/
// Inicjacja dla core 0
void setup_Core0(void* parameter) {
// Definiowanie pinów jako wejściowe
pinMode(IN_00_PIN, INPUT_PULLUP);
pinMode(IN_01_PIN, INPUT_PULLUP);
pinMode(IN_02_PIN, INPUT_PULLUP);
pinMode(IN_03_PIN, INPUT_PULLUP);
pinMode(IN_04_PIN, INPUT); // Bez podciągnięcia
pinMode(IN_05_PIN, INPUT); // Bez podciągnięcia
pinMode(IN_06_PIN, INPUT); // Bez podciągnięcia
pinMode(IN_07_PIN, INPUT); // Bez podciągnięcia
delay(500); // Opóźnienie dla ustabilizowania
Ethernet.init(SPI_CS_PIN); /* Możesz użyć Ethernet.init(pin) aby skonfigurować pin CS */
Ethernet.begin(mac, ip); /* Rozpoczęcie Ethernetu */
delay(500); // Opóźnienie dla ustabilizowania
Udp.begin(port); /* Rozpoczęcie UDP */
delay(100); // Opóźnienie na ustabilizowanie systemu
xTaskCreatePinnedToCore(
loop_Core0, // Funkcja zadania.
"loopTask_Core0", // Nazwa zadania.
4096, // Rozmiar stosu zadania
NULL, // Parametr zadania
0, // Priorytet zadania
NULL, // Uchwyty do zadania
0); // Przypisanie zadania do core 0
disableCore0WDT(); // Dezaktywacja watchdog timer
vTaskDelete(NULL); // Usunięcie zadania (nikogo nie potrzebujemy w tym punkcie)
}
/*==================================================================*/
// Konfiguracja początkowa
void setup() {
// Inicjalizacja kroku i kierunku dla trzech osi
pinMode(STEP_0_PIN, OUTPUT);
digitalWrite(STEP_0_PIN, 0); // Oznacz RTL
pinMode(DIR_0_PIN, OUTPUT);
digitalWrite(DIR_0_PIN, 0); // Oznacz RTL
pinMode(STEP_1_PIN, OUTPUT);
digitalWrite(STEP_1_PIN, 0); // Oznacz RTL
pinMode(DIR_1_PIN, OUTPUT);
digitalWrite(DIR_1_PIN, 0); // Oznacz RTL
pinMode(STEP_2_PIN, OUTPUT);
digitalWrite(STEP_2_PIN, 0); // Oznacz RTL
pinMode(DIR_2_PIN, OUTPUT);
digitalWrite(DIR_2_PIN, 0); // Oznacz RTL
// Konfiguracja Prescalera
// 80 000 000 / 2 = 40 000 000 tics/second ===> 25 ns/tic
stepGen_0 = timerBegin(0, 2, true); // Rozpoczęcie timera
stepGen_1 = timerBegin(1, 2, true);
stepGen_2 = timerBegin(2, 2, true);
timerAttachInterrupt(stepGen_0, &onTime_0, true); // Ustawienie przerwania dla osi 0
timerAttachInterrupt(stepGen_1, &onTime_1, true); // Ustawienie przerwania dla osi 1
timerAttachInterrupt(stepGen_2, &onTime_2, true); // Ustawienie przerwania dla osi 2
timerAlarmWrite(stepGen_0, 40000000, true); // Alarm dla osi 0
timerAlarmWrite(stepGen_1, 40000000, true); // Alarm dla osi 1
timerAlarmWrite(stepGen_2, 40000000, true); // Alarm dla osi 2
timerAlarmEnable(stepGen_0); // Włączenie alarmu dla osi 0
timerAlarmEnable(stepGen_1); // Włączenie alarmu dla osi 1
timerAlarmEnable(stepGen_2); // Włączenie alarmu dla osi 2
// Tworzenie zadania dla setup_Core0
xTaskCreatePinnedToCore(
setup_Core0, // Funkcja zadania.
"setup_Core0Task", // Nazwa zadania.
1024, // Rozmiar stosu zadania
NULL, // Parametr zadania
0, // Priorytet zadania
NULL, // Uchwyty do zadania
0); // Przypisanie zadania do core 0
}
/*==================================================================*/
// Główna pętla sterownika
void IRAM_ATTR loop() {
for (int i = 0; i < 3; i++) { // Iteracja dla każdej osi
if (b_math[i]) { // Sprawdzenie, czy obliczenia są potrzebne
b_math[i] = false; // Reset flagi
if (!ul_accelStep[i]) { // Oś jest w stanie spoczynku
long l_pos_error = cmd.pos[i] - fb.pos[i]; // Obliczanie błędu pozycji
if (l_pos_error) { // Jeśli istnieje błąd pozycji
if ((l_pos_error > 0 && b_dirSignal[i] == HIGH) || (l_pos_error < 0 && b_dirSignal[i] == LOW)) // Kierunek jest dobry
acceleration(i); // Przyspiesz
else { // Musimy zmienić kierunek
(l_pos_error > 0) ? b_dirSignal[i] = HIGH : b_dirSignal[i] = LOW; // Ustal kierunek
b_dirChange[i] = true; // Ustaw flagę zmiany kierunku
}
}
} else { // Oś jest w ruchu
if ((cmd.vel[i] > 0.0f && b_dirSignal[i] == HIGH) || (cmd.vel[i] < 0.0f && b_dirSignal[i] == LOW)) { // Dobry kierunek
if (ul_T[i] > ul_cmd_T[i]) // Jeśli prędkość jest zbyt niska
acceleration(i); // Przyspiesz
else if (ul_T[i] < ul_cmd_T[i]) // Jeśli prędkość jest zbyt wysoka
deceleration(i); // Dekceleracja
} else // Zły kierunek lub celowa prędkość wynosi zero
deceleration(i); // Dekceleracja
}
}
}
}
Header: W początku kodu znajduje się meta-informacja o jego prawach autorskich oraz warunkach użytkowania.
Sprzęt: Osprzęt projektowych dla używanego układu ESP32 i W5500, szczegóły napięcia i pinów między płytką a urządzenia peryferyjnego.
GPIO: Szczegółowe rozpisanie pinów GPIO ESP32, ich zastosowań oraz ich stanów.
Funkcjonalność: Kod pozwala na wysyłanie i odbieranie pakietów przez Ethernet, generowanie kroków w odpowiedzi na dane komendy oraz obsługę PWM. Prostsze obliczenia przyspieszenia oraz zmiany stanu kierunku.
Główna pętla: Obsługuje wykonywanie zadań w iteracyjnym cyklu, jaki jest kluczowy do działania kroków.
Powyższe komentarze powinny pomóc w zrozumieniu działania i struktury kodu w ramach projektu HAL2UDP.