w 24h

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ą UDPEthernetUDP Udp; // Instancja do przesyłania i odbierania pakietów UDP// Struktura dla pakietu komendstruct cmdPacket {uint8_t control; // Kontrola komendyuint8_t io; // Wejście/wyjścieuint16_t pwm[6]; // Tablica wartości PWM (do 6 wejść)int32_t pos[3]; // Pozycje dla 3 osifloat 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 zwrotnejstruct fbPacket {uint8_t control; // Kontrola informacji zwrotnejuint8_t io; // Stan wejść/wyjśćint32_t pos[3]; // Pozycje osifloat 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 stanuvolatile 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 osivolatile unsigned long ul_cmd_T[3]; // Zmienne do przechowywania czasów komendunsigned long ul_accelStep[3] = { 0, 0, 0 }; // Zmienne do przechowywania liczby kroków w przyspieszeniu// Zmienne do przechowywania różnych ważnych czasówvolatile unsigned long ul_T[3] = { 0, 0, 0 }; // Czas cykluvolatile unsigned long ul_TH[3] = { 0, 0, 0 }; // Czas wyższyvolatile unsigned long ul_TL[3] = { 0, 0, 0 }; // Czas niższyvolatile bool b_math[3] = { false, false, false }; // Flagi do obliczeń pozycjonowaniavolatile bool b_dirSignal[3] = { LOW, LOW, LOW }; // Flagi dla kierunkówvolatile bool b_dirChange[3] = { false, false, false }; // Flagi mówiące, że musimy zmienić kierunekconst uint8_t pwm_pin[6] = { OUT_00_PIN, OUT_01_PIN, OUT_02_PIN, OUT_03_PIN, OUT_04_PIN, OUT_05_PIN }; // Piny PWMbool 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 osihw_timer_t* stepGen_0 = NULL; // Timer dla osi 0hw_timer_t* stepGen_1 = NULL; // Timer dla osi 1hw_timer_t* stepGen_2 = NULL; // Timer dla osi 2/*==================================================================*/// Funkcja do szybkiego obliczania odwrotności pierwiastkafloat IRAM_ATTR fastInvSqrt(const float x) {const float xhalf = x * 0.5f; // Połowa wartości xunion {float x; // Typ floatuint32_t i; // Typ 32 bitowy} u = { .x = x }; // Inicjalizacja unii wartością xu.i = 0x5f3759df - (u.i >> 1); // Algorytm szybkiej odwrotności pierwiastkareturn u.x * (1.5f - xhalf * u.x * u.x); // Zwracanie wyniku}/*==================================================================*/// Obsługuje timer dla osi 0void IRAM_ATTR onTime_0() {static bool b_stepState = LOW; // Stan kroku (niski na początku)static bool b_dirState = LOW; // Stan kierunkuif (b_stepState == LOW) { // Koniec cyklu, wykonywanie impulsuif (ul_T[0]) { // Istnieje czas, więc należy zacząć impulsif (!b_dirChange[0]) { // Jeśli kierunek jest poprawnySTEP_0_H; // Ustaw HIGH na pinie STEP 0timerAlarmWrite(stepGen_0, ul_TH[0], true); // Ustaw alarm timerab_stepState = HIGH; // Zmień stan na wysokib_dirState ? fb.pos[0]++ : fb.pos[0]--; // Zmiana pozycji w zależności od kierunkub_math[0] = true; // Ustaw flagę do obliczeń} else { // Musimy zmienić kierunekif (b_dirSignal[0]) {DIR_0_H; // Ustaw HIGH na pinie kierunkub_dirState = HIGH;} else {DIR_0_L; // Ustaw LOW na pinie kierunkub_dirState = LOW;}timerAlarmWrite(stepGen_0, ul_dirSetup[0], true); // Ustaw nowy czas ustalony dla kierunkub_dirChange[0] = false; // Resetuj flagę zmiany kierunku}} else { // Nie wymaga impulsutimerAlarmWrite(stepGen_0, 4000ul, true); // Ustaw alarm timera na mały czasb_math[0] = true; // Flaga dla obliczeń}} else { // Środek cykluSTEP_0_L; // Ustaw LOW na pinie STEP 0timerAlarmWrite(stepGen_0, ul_TL[0], true); // Ustaw alarm na czas niższyb_stepState = LOW; // Zmień stan na niski}}/*==================================================================*/// Obsługuje timer dla osi 1void IRAM_ATTR onTime_1() {static bool b_stepState = LOW; // Stan krokustatic bool b_dirState = LOW; // Stan kierunkuif (b_stepState == LOW) { // Koniec cykluif (ul_T[1]) { // Istnieje czasif (!b_dirChange[1]) { // Kierunek poprawnySTEP_1_H; // Impuls HIGHtimerAlarmWrite(stepGen_1, ul_TH[1], true);b_stepState = HIGH; // Zmień stanb_dirState ? fb.pos[1]++ : fb.pos[1]--; // Zmiana pozycjib_math[1] = true; // Ustaw flagę} else { // Zmiana kierunkuif (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 impulsutimerAlarmWrite(stepGen_1, 4000ul, true);b_math[1] = true;}} else { // Środek cykluSTEP_1_L; // Ustaw LOWtimerAlarmWrite(stepGen_1, ul_TL[1], true);b_stepState = LOW;}}/*==================================================================*/// Obsługuje timer dla osi 2void 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 przyspieszenievoid IRAM_ATTR newT(const int i) {// Użycie algorytmu szybkiego odwrotności pierwiastka dla wydajnościul_T[i] = 40000000.0f * fastInvSqrt((float)ul_accelStep[i] * f_accel_x2[i]); // Sqrt wzórul_TH[i] = ul_T[i] >> 1; // Ustalanie połowyul_TL[i] = ul_T[i] - ul_TH[i]; // Czas niższy}/*==================================================================*/// Dekceleracja na podstawie liczby krokówvoid IRAM_ATTR deceleration(const int i) {if (ul_accelStep[i]) {ul_accelStep[i]--; // Decrement stepsif (ul_accelStep[i])newT(i); // Nowe przeliczenie czasuelseul_T[i] = 0; // Ustawienie zerowego czasu}}/*==================================================================*/// Przyspiesza w zależności od kontroli aktywnejvoid IRAM_ATTR acceleration(const int i) {if (cmd.control & CTRL_ENABLE) {ul_accelStep[i]++; // InkrementacjanewT(i); // Obliczenie nowego czasu} elsedeceleration(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łubool enable = cmd.control & CTRL_ENABLE; // Sprawdzamy, czy kontrola jest włączona// Obsługa wyjścia PWM dla pierwszego pinuif (pwm_enable[0]) {if (enable) {if (last_pwm[0] != cmd.pwm[0]) { // Sprawdza zmianę PWMlast_pwm[0] = cmd.pwm[0];ledcWrite(0, last_pwm[0]); // Zapisuje nową wartość PWM}} else {ledcWrite(0, 0); // Ustawienie PWM na 0last_pwm[0] = 0;}} else {if (enable)(cmd.io & IO_00) ? OUT_00_H : OUT_00_L; // Ustawia stan wyjścia na podstawie aktualnego IOelseOUT_00_L; // Gdy wyłączone, ustaw na niski}// Powtarzamy analogicznie dla kolejnych kanałów PWMif (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;elseOUT_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;elseOUT_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;elseOUT_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;elseOUT_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;elseOUT_05_L;}}/*==================================================================*/// Obsługuje sygnały wejściowevoid IRAM_ATTR inputHandler() {// Odczytuje stany wejść i ustawia flagi(IN_00) ? fb.io = IO_00 : fb.io = 0; // Ustawienie flags ioif (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ącevoid IRAM_ATTR commandHandler() {if (cmd.control & CTRL_READY) { // Sprawdzenie, czy kontrola jest gotowafor (int i = 0; i < 3; i++) {// Ustalanie czasu na podstawie podanej prędkościif (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]);elseul_cmd_T[i] = 40000000ul; // Domyślna wartość}}// Weryfikacja czy już jesteśmy gotowi na kolejne komendyif (!(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 kierunkufb.control |= CTRL_DIRSETUP; // Ustawienie flagifor (int i = 0; i < 3; i++)ul_dirSetup[i] = cmd.pos[i] / 25; // Ustalanie ustawień kierunku} else if (cmd.control & CTRL_ACCEL) { // Ustawienie przyspieszeniafb.control |= CTRL_ACCEL; // Ustawienie flagifor (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 PWMfb.control |= CTRL_PWMFREQ; // Ustawienie flagifor (int i = 0; i < 6; i++) {if (cmd.pwm[i]) { // Jeśli wartość PWM jest podanaledcAttachPin(pwm_pin[i], i * 2); // Dołączenie pinu PWMledcSetup(i * 2, cmd.pwm[i], 10); // Ustawienia PWMledcWrite(i * 2, 0); // Inicjalizacja wyjściapwm_enable[i] = true; // Włączenie flagi PWM} else {// Jeśli PWM nie jest wymagany, ustaw na LOWif (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 0void IRAM_ATTR loop_Core0(void* parameter) {delay(500); // Opóźnienie początkoweUdp.parsePacket(); /* Opróżnianie bufora */for (;;) {// Zmienna do monitorowaniastatic unsigned long ul_watchdog;if (Udp.parsePacket() == sizeof(cmd) + 1) { // Sprawdzenie, czy odebrano cały pakietchar packetBuffer[60]; // Bufor do odbierania i wysyłania danychUdp.read(packetBuffer, sizeof(cmd) + 1); // Odczytanie pakietu// Prosta kontrola integralności pakietuuint8_t chk = 71; // Prosta suma kontrolnafor (int i = 0; i < sizeof(cmd); i++) // Bitowe XORchk ^= packetBuffer[i];if (packetBuffer[sizeof(cmd)] == chk) { // Sprawdzanie sumy kontrolnejmemcpy(&cmd, &packetBuffer, sizeof(cmd)); // Kopiowanie danych do strukturycommandHandler(); // Obsługa komendyul_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ścielsefb.vel[i] = 0.0f; // Ustawienie prędkości na 0}memcpy(&packetBuffer, &fb, sizeof(fb)); // Kopiowanie danych zwrotnychUdp.beginPacket(Udp.remoteIP(), Udp.remotePort()); // Rozpoczęcie pakietu UDPUdp.write(packetBuffer, sizeof(fb)); // Wysyłanie bazy danych strukturyUdp.endPacket(); // Zakończenie pakietu UDPoutputHandler(); // Obsługuje wyjścia}// Resetowanie kontrolek po 10 msif (millis() - ul_watchdog > 10ul) {fb.control = 0; // Resetowanie kontrolicmd.control = 0; // Resetowanie komendyoutputHandler(); // Zapewnia wyjściaul_watchdog = millis(); // Resetowanie zegara watchdog}}}/*==================================================================*/// Inicjacja dla core 0void setup_Core0(void* parameter) {// Definiowanie pinów jako wejściowepinMode(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ęciapinMode(IN_05_PIN, INPUT); // Bez podciągnięciapinMode(IN_06_PIN, INPUT); // Bez podciągnięciapinMode(IN_07_PIN, INPUT); // Bez podciągnięciadelay(500); // Opóźnienie dla ustabilizowaniaEthernet.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 ustabilizowaniaUdp.begin(port); /* Rozpoczęcie UDP */delay(100); // Opóźnienie na ustabilizowanie systemuxTaskCreatePinnedToCore(loop_Core0, // Funkcja zadania."loopTask_Core0", // Nazwa zadania.4096, // Rozmiar stosu zadaniaNULL, // Parametr zadania0, // Priorytet zadaniaNULL, // Uchwyty do zadania0); // Przypisanie zadania do core 0disableCore0WDT(); // Dezaktywacja watchdog timervTaskDelete(NULL); // Usunięcie zadania (nikogo nie potrzebujemy w tym punkcie)}/*==================================================================*/// Konfiguracja początkowavoid setup() {// Inicjalizacja kroku i kierunku dla trzech osipinMode(STEP_0_PIN, OUTPUT);digitalWrite(STEP_0_PIN, 0); // Oznacz RTLpinMode(DIR_0_PIN, OUTPUT);digitalWrite(DIR_0_PIN, 0); // Oznacz RTLpinMode(STEP_1_PIN, OUTPUT);digitalWrite(STEP_1_PIN, 0); // Oznacz RTLpinMode(DIR_1_PIN, OUTPUT);digitalWrite(DIR_1_PIN, 0); // Oznacz RTLpinMode(STEP_2_PIN, OUTPUT);digitalWrite(STEP_2_PIN, 0); // Oznacz RTLpinMode(DIR_2_PIN, OUTPUT);digitalWrite(DIR_2_PIN, 0); // Oznacz RTL// Konfiguracja Prescalera// 80 000 000 / 2 = 40 000 000 tics/second ===> 25 ns/ticstepGen_0 = timerBegin(0, 2, true); // Rozpoczęcie timerastepGen_1 = timerBegin(1, 2, true);stepGen_2 = timerBegin(2, 2, true);timerAttachInterrupt(stepGen_0, &onTime_0, true); // Ustawienie przerwania dla osi 0timerAttachInterrupt(stepGen_1, &onTime_1, true); // Ustawienie przerwania dla osi 1timerAttachInterrupt(stepGen_2, &onTime_2, true); // Ustawienie przerwania dla osi 2timerAlarmWrite(stepGen_0, 40000000, true); // Alarm dla osi 0timerAlarmWrite(stepGen_1, 40000000, true); // Alarm dla osi 1timerAlarmWrite(stepGen_2, 40000000, true); // Alarm dla osi 2timerAlarmEnable(stepGen_0); // Włączenie alarmu dla osi 0timerAlarmEnable(stepGen_1); // Włączenie alarmu dla osi 1timerAlarmEnable(stepGen_2); // Włączenie alarmu dla osi 2// Tworzenie zadania dla setup_Core0xTaskCreatePinnedToCore(setup_Core0, // Funkcja zadania."setup_Core0Task", // Nazwa zadania.1024, // Rozmiar stosu zadaniaNULL, // Parametr zadania0, // Priorytet zadaniaNULL, // Uchwyty do zadania0); // Przypisanie zadania do core 0}/*==================================================================*/// Główna pętla sterownikavoid IRAM_ATTR loop() {for (int i = 0; i < 3; i++) { // Iteracja dla każdej osiif (b_math[i]) { // Sprawdzenie, czy obliczenia są potrzebneb_math[i] = false; // Reset flagiif (!ul_accelStep[i]) { // Oś jest w stanie spoczynkulong l_pos_error = cmd.pos[i] - fb.pos[i]; // Obliczanie błędu pozycjiif (l_pos_error) { // Jeśli istnieje błąd pozycjiif ((l_pos_error > 0 && b_dirSignal[i] == HIGH) || (l_pos_error < 0 && b_dirSignal[i] == LOW)) // Kierunek jest dobryacceleration(i); // Przyspieszelse { // Musimy zmienić kierunek(l_pos_error > 0) ? b_dirSignal[i] = HIGH : b_dirSignal[i] = LOW; // Ustal kierunekb_dirChange[i] = true; // Ustaw flagę zmiany kierunku}}} else { // Oś jest w ruchuif ((cmd.vel[i] > 0.0f && b_dirSignal[i] == HIGH) || (cmd.vel[i] < 0.0f && b_dirSignal[i] == LOW)) { // Dobry kierunekif (ul_T[i] > ul_cmd_T[i]) // Jeśli prędkość jest zbyt niskaacceleration(i); // Przyspieszelse if (ul_T[i] < ul_cmd_T[i]) // Jeśli prędkość jest zbyt wysokadeceleration(i); // Dekceleracja} else // Zły kierunek lub celowa prędkość wynosi zerodeceleration(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.