Programowanie modułów ESP32 w środowisku ESP-IDF (2). Podstawy obsługi portów I/O, ADC, PWM, UART

Programowanie modułów ESP32 w środowisku ESP-IDF (2). Podstawy obsługi portów I/O, ADC, PWM, UART

W tym artykule omówione zostaną podstawy programowania układów ESP32 za pomocą środowiska ESP-IDF. Nasza uwaga skupiona będzie na procedurach, od których zazwyczaj rozpoczyna się pracę z nowym mikrokontrolerem czy środowiskiem programistycznym: dostępie do portów wejścia/wyjścia, obsłudze przetworników ADC oraz wyjść PWM czy też komunikacji ze światem zewnętrznym poprzez interfejs UART.

Obsługa portów I/O

Porty I/O, czyli porty wejścia/wyjścia, są najwdzięczniejszym interfejsem, ponieważ pozwalają w prosty sposób przetestować związany z nimi fragment oprogramowania. Wystarczy do wybranego portu podłączyć poprzez opornik zwykłą diodę LED, a jej świecenie będzie sygnalizować stan danego wyprowadzenia. Na niektórych płytach rozwojowych z ESP32 producenci zamontowali już tzw. LED-y użytkownika, doskonale nadające się do eksperymentów. W takim przypadku wystarczy jedynie odszukać w dokumentacji płytki numer portu I/O, do którego taka dioda jest podłączona.

Stworzenie programu testowego rozpoczynamy od uruchomienia edytora ze środowiskiem ESP-IDF. W opisie domyślnie będzie to edytor oparty na Eclipse, ESP-IDF v.4.4.

Klikamy File → New → Espressif IDF Project. Jeżeli zaznaczymy „Create a project using one of the templates”, będzie można otworzyć któryś z gotowych przykładów. Tym razem jednak stworzymy program od podstaw. W polu „Project name” należy wpisać nazwę tworzonego programu. W przykładzie użyję nazwy test-io-esp32-s3. Na liście „Select project target:” trzeba wybrać wariant procesora zamontowanego na używanej płycie rozwojowej, w omawianym przykładzie będzie to ESP32-S3. Po kliknięciu Finish zostanie utworzony projekt z plikiem main.c i pozostałymi plikami potrzebnymi w strukturze projektu.

Rysunek 1. Widok okna głównego środowiska Espressif-IDE

Ewentualne komunikaty o błędach w wygenerowanym projekcie znikną po przeprowadzeniu kompilacji w tym celu należy wybrać Project → Build Project lub nacisnąć ikonę młotka na pasku skrótów (patrz rysunek 1, oznaczenie strzałką nr 1). Teraz w pliku main.c – na miejsce automatycznie utworzonego kodu – proszę wpisać ten z listingu 1. W linii #define BLINK_GPIO podajemy numer portu I/O, do którego podłączona jest dioda LED. Należy wykonać powtórną kompilację i usunąć ewentualne błędy powstałe przy wpisywaniu kodu z listingu 1. Następnie podłączamy płytkę ewaluacyjną kablem do gniazda USB. W kolejnym kroku za pomocą narzędzi systemowych odczytujemy przydzielony podłączonej płycie numer portu. W edytorze należy kliknąć edycję listy Launch Target i w polu listy rozwijanej Serial Port: (rysunek 1, strzałka nr 3) ustawić numer portu. Po naciśnięciu przycisku Launch (rysunek 1, strzałka nr 2) płyta powinna zostać zaprogramowana, a dioda zacznie migotać z częstotliwością 1 Hz.

#include <unistd.h> #include <stdio.h> #include "driver/gpio.h" #include "esp_log.h" #include "sdkconfig.h" static const char *TAG = "example"; /* wstaw w tej linii numer GPIO do którego * jest podłączona dioda LED */ #define BLINK_GPIO 21 static uint8_t led_stan = 0; static void configure_led(void) { ESP_LOGI(TAG, "Przykład konfiguracji wyjścia GPIO LED!"); gpio_reset_pin(BLINK_GPIO); /* Ustaw tryb pracy GPIO jako push/pull output */ gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); } static void blink_led(void) { /* Set the GPIO level according to the state (LOW or HIGH)*/ gpio_set_level(BLINK_GPIO, led_stan); led_stan =!led_stan; } void app_main(void) { ESP_LOGI(TAG, "Cześć tu program testowy GPIO LED!"); configure_led(); while (true) { ESP_LOGI(TAG, "Stan LED=%s!", led_stan == true ? "ON" : "OFF"); blink_led(); sleep(1); } } Listing 1. Program umożliwiający najprostsze miganie diodą LED

Działanie programu oparto na najprostszym sposobie sterowania portem I/O. W funkcji configure_led(void) port został ustawiony jako wyjściowy, a funkcja blink_led(void) zmienia poziom na wyjściu, do którego podłączona została dioda LED.

W celu przetestowania I/O pracującego w trybie wejścia należy wybrać drugi dostępny port na płycie rozwojowej. Ponieważ w czasie testu będzie on zwierany do masy, najlepiej wybrać taki, który nie ma przypisanej dodatkowej ważnej funkcji systemowej – czyli np. port o numeracji wyższej niż 17. Ponadto trzeba wprowadzić zmiany widoczne na listingu 2. Polegają one na zdefiniowaniu wybranego drugiego portu jako SW_GPIO, dodaniu funkcji ustawienia go jako wejściowego configure_sw(void) i modyfikacji funkcji main(). Po przesłaniu nowej wersji oprogramowania na płytę rozwojową i zwarciu do masy portu SW_GPIO, częstotliwość migotania diody LED zwiększa się do około 5 Hz.

(…) #define SW_GPIO 47 (…) static void configure_sw(void) { gpio_reset_pin(SW_GPIO); if (gpio_set_pull_mode ( SW_GPIO , GPIO_PULLUP_ONLY) !=ESP_OK) { ESP_LOGE(TAG, "SW LED nie został prawidłowo skonfigurowany!"); }ESP_LOGI(TAG, "Przykład konfiguracji wejścia SW_GPIO"); gpio_set_direction(SW_GPIO, GPIO_MODE_INPUT); } (…) void app_main(void) { uint8_t status_SW=true; ESP_LOGI(TAG, "Cześć tu program testowy GPIO LED!"); configure_led(); configure_sw(); while (true) { blink_led(); status_SW =gpio_get_level(SW_GPIO); ESP_LOGI(TAG, "Stan LED=%s, f=%s", led_stan == true ? "ON" : "OFF", status_SW ==true ? "1Hz" : "5Hz"); if (status_SW!=0){ sleep(1); }else usleep(200000); led_stan =!led_stan; } } Listing 2. Rozszerzenie programu z listingu 1 o odczyt stanu wejścia cyfrowego

Makra ESP_LOGI i ESP_LOGE powodują wysłanie odpowiednich komunikatów do terminalu wywoływanego z pulpitu edytora (rysunek 1, strzałka nr 4). Po naciśnięciu ikony terminalu wyświetla się tablica, na której w polu „Project Name:” trzeba ustawić nazwę projektu, a w polu „Serial port:” wybrać numer przydzielonego płycie portu. Po zatwierdzeniu powinna otworzyć się zakładka terminalu odbierającego komunikaty wysyłane przez nasze oprogramowanie. Takie zastosowanie terminalu pozwala na proste debugowanie programu.

W przykładzie pokazano najprostszy sposób sterowania portami I/O. API środowiska ESP-IDF umożliwia bardziej zaawansowane manipulacje, np. dołączanie wewnętrznych oporników podciągających, współpracę z systemem przerwań, zatrzaskiwanie ustawień portu nawet na czas resetu czy grupowe ustawianie parametrów wielu portów jednocześnie. Szczegółowy opis odpowiedni dla wersji IDF-4.4 można znaleźć w [1].

Obsługa przetworników ADC

W większości wersji ESP32 do dyspozycji użytkownika są dwa niezależne przetworniki analogowo-cyfrowe, każdy z kilkoma multipleksowanymi wejściami. Jako wejścia używa się portów I/O pracujących w trybie analogowym. W dokumentacji technicznej dotyczącej używanej wersji układu ESP32 opisane zostało przyporządkowanie dostępnych wejść przetwornika do portów I/O. Także w dokumentacji podane będą informacje o rozdzielczości bitowej, nieliniowości przetwarzania i ograniczeniach w użyciu przetworników ADC.

Na listingu 3 pokazany został prosty program, używający przetwornika ADC do odczytu bezpośredniego w trybie próbkowania. Listing oparto na przykładzie esp-idf-v4.4/examples/peripherals/adc/single_read/single_read. Klikamy File → New → Espressif IDF Project, zaznaczamy „Create a project using one of the templates” i na wyświetlonej liście klikamy przykład single_read.

#include <stdio.h> #include <stdlib.h> #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/adc.h" #include "esp_adc_cal.h" //ADC Channels #if CONFIG_IDF_TARGET_ESP32 #define ADC1_EXAMPLE_CHAN0 ADC_CHANNEL_6//ADC1_CHANNEL_6 #define ADC2_EXAMPLE_CHAN0 ADC2_CHANNEL_0 static const char *TAG_CH[2][10] = {{"ADC1_CH6"}, {"ADC2_CH0"}}; #else #define ADC1_EXAMPLE_CHAN0 ADC1_CHANNEL_2 //#define ADC2_EXAMPLE_CHAN0 ADC2_CHANNEL_0 static const char *TAG_CH[2][10] = {{"ADC1_CH2"}, {"ADC2_CH0"}}; #endif //ADC Attenuation #define ADC_EXAMPLE_ATTEN ADC_ATTEN_DB_11 //ADC Calibration #if CONFIG_IDF_TARGET_ESP32 #define ADC_EXAMPLE_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_VREF #elif CONFIG_IDF_TARGET_ESP32S2 #define ADC_EXAMPLE_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP #elif CONFIG_IDF_TARGET_ESP32C3 #define ADC_EXAMPLE_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP #elif CONFIG_IDF_TARGET_ESP32S3 #define ADC_EXAMPLE_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP_FIT #endif static int adc_raw[2][10]; //static const char *TAG = "ADC SINGLE"; static esp_adc_cal_characteristics_t adc1_chars; static esp_adc_cal_characteristics_t adc2_chars; static bool adc_calibration_init(void) { esp_err_t ret; bool cali_enable = false; ret = esp_adc_cal_check_efuse(ADC_EXAMPLE_CALI_SCHEME); if (ret == ESP_ERR_NOT_SUPPORTED) { ESP_LOGW(TAG, "Calibration scheme not supported, skip software calibration"); } else if (ret == ESP_ERR_INVALID_VERSION) { ESP_LOGW(TAG, "eFuse not burnt, skip software calibration"); } else if (ret == ESP_OK) { cali_enable = true; esp_adc_cal_characterize(ADC_UNIT_1, ADC_EXAMPLE_ATTEN, ADC_WIDTH_BIT_DEFAULT, 0, &adc1_chars); // esp_adc_cal_characterize(ADC_UNIT_2, ADC_EXAMPLE_ATTEN, ADC_WIDTH_BIT_DEFAULT, 0, &adc2_chars); } else { ESP_LOGE(TAG, "Invalid arg"); } return cali_enable; } void app_main(void) { // esp_err_t ret = ESP_OK; uint32_t voltage = 0; bool cali_enable = adc_calibration_init(); //ADC1 config ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_DEFAULT)); ESP_ERROR_CHECK(adc1_config_channel_atten(ADC1_EXAMPLE_CHAN0, ADC_EXAMPLE_ATTEN)); //ADC2 config // ESP_ERROR_CHECK(adc2_config_channel_atten(ADC2_EXAMPLE_CHAN0, ADC_EXAMPLE_ATTEN)); vTaskDelay(pdMS_TO_TICKS(5000)); while (1) { adc_raw[0][0] = adc1_get_raw(ADC1_EXAMPLE_CHAN0); ESP_LOGI(TAG_CH[0][0], "raw data: %d", adc_raw[0][0]); if (cali_enable) { voltage = esp_adc_cal_raw_to_voltage(adc_raw[0][0], &adc1_chars); ESP_LOGI(TAG_CH[0][0], "cali data: %d mV", voltage); } vTaskDelay(pdMS_TO_TICKS(1000)); /* do { ret = adc2_get_raw(ADC2_EXAMPLE_CHAN0, ADC_WIDTH_BIT_DEFAULT, &adc_raw[1][0]); } while (ret == ESP_ERR_INVALID_STATE); ESP_ERROR_CHECK(ret); ESP_LOGI(TAG_CH[1][0], "raw data: %d", adc_raw[1][0]); if (cali_enable) { voltage = esp_adc_cal_raw_to_voltage(adc_raw[1][0], &adc2_chars); ESP_LOGI(TAG_CH[1][0], "cali data: %d mV", voltage); } vTaskDelay(pdMS_TO_TICKS(1000)); */ } } Listing 3. Prosty przykład obsługi przetwornika ADC

Zmiany w programie polegają na uproszczeniu kodu i odczycie tylko z kanału 6 przetwornika nr 1. Na listingu 3 niepotrzebne fragmenty kodu zostały ujęte w komentarze.

Jako pierwsza wywoływana jest funkcja adc_calibration_init(), której procedury służą do kalibracji przetwornika ADC. W ESP32 niektóre stałe kalibracyjne mogą być zapisywane na etapie produkcji układu w specjalnym obszarze pamięci „eFuse bits”. Jeżeli procedura esp_adc_cal_check_efuse nie odnajdzie poszukiwanej wartości kalibracyjnej, do korekty odczytów z przetwornika zostaną użyte wartości domyślne. Funkcja esp_adc_cal_characterize() mierzy napięcie Vref i temperaturę, czyli parametry używane do korekty obliczeń. Kolejne wywoływane funkcje: adc1_config_width() i adc1_config_channel_atten(), ustawiają parametry pracy wybranego kanału przetwornika 1 zgodnie z definicjami umieszczonymi na początku listingu 3. Potem w nieskończonej pętli następuje odczyt surowych danych z przetwornika i ich konwersja na wartość napięcia w mV, z zastosowaniem obliczonej wcześniej korekcji (funkcje adc1_get_raw(), esp_adc_cal_raw_to_voltage()). Obie wartości – opakowane w stosowny komunikat – po każdym odczycie wysyłane są do terminalu.

Opis bardziej rozbudowanych funkcji API do obsługi przetwornika ADC można znaleźć w dokumentacji [2].

LEDC – sterowanie jasnością LED w trybie PWM

Funkcje z grupy API LEDC przeznaczone są do sterowania jasnością diod LED przez zmianę wypełnienia impulsu PWM. Można je także zastosować ogólnie – jako sposób generowania impulsów o zmiennym współczynniku wypełnienia. Sposób użycia funkcji pokazany zostanie na przykładzie, w którym jasność świecenia diody LED będzie zmieniana proporcjonalnie do wartości napięcia podawanego na wejście przetwornika ADC. W tym celu zmodyfikujemy przykład z listingu 3, dodając funkcje LEDC. Dodatkowe linie programu zawiera listing 4.

(…) //LEDC PWM #include "driver/ledc.h" (…) //LEDC PWM #define LEDC_TIMER LEDC_TIMER_0 #define LEDC_MODE LEDC_LOW_SPEED_MODE #define LEDC_OUTPUT_IO (2) // Define the output GPIO #define LEDC_CHANNEL LEDC_CHANNEL_0 #define LEDC_DUTY_RES LEDC_TIMER_13_BIT // Set duty resolution to 13 bits #define LEDC_FREQUENCY (5000) // Frequency in Hertz. Set frequency at 5 kHz (…) //LEDC PWM static void example_ledc_init(void) { // Prepare and then apply the LEDC PWM timer configuration ledc_timer_config_t ledc_timer = { .speed_mode = LEDC_MODE, .timer_num = LEDC_TIMER, .duty_resolution = LEDC_DUTY_RES, .freq_hz = LEDC_FREQUENCY, // Set output frequency at 5 kHz .clk_cfg = LEDC_AUTO_CLK }; ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); // Prepare and then apply the LEDC PWM channel configuration ledc_channel_config_t ledc_channel = { .speed_mode = LEDC_MODE, .channel = LEDC_CHANNEL, .timer_sel = LEDC_TIMER, .intr_type = LEDC_INTR_DISABLE, .gpio_num = LEDC_OUTPUT_IO, .duty = 0, // Set duty to 0% .hpoint = 0 }; ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); } void app_main(void) { (…) example_ledc_init(); while(1) { (…) //LEDC PWM // Set duty to 50% uint32_t ledc_duty=(8192)-1; ledc_duty *=voltage; ledc_duty /=3300; ESP_LOGI(TAG_CH[0][0],"LEDC PWM %d",ledc_duty); ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, ledc_duty/*LEDC_DUTY*/)); // Update duty to apply the new value ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL)); } } Listing 4. Rozbudowa przykładu z listingu 3 o obsługę wyjścia PWM

Najpierw należy dodać plik nagłówkowy biblioteki ledc.h. Następnie w sekcji definicji dodajemy nowe definicje: LEDC_TIMER – numer timera (0...3), LEDC_MODE – tryb pracy (wolny lub szybki), LEDC_OUTPUT_IO – numer portu I/O, na który ma być wyprowadzony przebieg PWM i do którego będzie podłączona dioda LED, LEDC_CHANNEL – numer kanału (0...7), LEDC_DUTY_RES – rozdzielczość (w bitach) generowanego przebiegu PWM, LEDC_FREQUENCY – częstotliwość generowanego przebiegu PWM. Należy także dodać funkcję example_ledc_init(), która inicjuje generowanie przebiegu PWM, używając do tego zdefiniowanych powyżej parametrów. Funkcje inicjujące mogą wygenerować komunikat błędu, jeżeli podane parametry przekraczają dopuszczalne zakresy rozdzielczości i częstotliwości. I tak dla niskich częstotliwości można ustawić wysoką rozdzielczość (do 20 bitów), natomiast dla najwyższej częstotliwości (40 MHz) rozdzielczość może być tylko 1-bitowa.

Wywołanie funkcji example_ledc_init() powinno zostać umieszczone na początku funkcji main(). Dalej – w pętli while – odczytana z przetwornika wartość napięcia voltage stosowana jest do obliczenia proporcjonalnej do niej wartości wypełnienia, umieszczanej w zmiennej ledc_duty. W każdym przebiegu pętli while funkcje ledc_set_duty() i ledc_update_duty() aktualizują wypełnienie przebiegu PWM. Opis funkcji i parametrów interfejsu LEDC można znaleźć w [3]. Na rysunku 2 pokazano schemat podłączenia do płyty rozwojowej dodatkowych elementów, takich jak dioda LED i potencjometr.

Rysunek 2. Schemat podłączenia dodatkowych elementów zewnętrznych do płyty rozwojowej

UART – obsługa interfejsu transmisji szeregowej

Protokół UART zapewnia obsługę asynchronicznych interfejsów komunikacji szeregowej, takich jak RS232, RS422 czy RS485. Układ ESP32 ma trzy identyczne kontrolery UART (UART0, UART1 i UART2). W przypadku płyt rozwojowych jeden z nich, zazwyczaj UART0, używany jest do przesyłania danych w czasie zapisu programu do pamięci FLASH, pozostałe mogą zostać użyte do komunikacji z urządzeniami zewnętrznymi.

Pokazany na listingu 5 program jest przykładem dostępnym w katalogu esp-idf-v4.4/examples/peripherals/uart/uart_echo. Program działa na zasadzie odpytywania (pollingu) danych wejściowych interfejsu UART. Po odebraniu jakichkolwiek danych zostaną one natychmiast odesłane tym samym kanałem UART. Procedura odpytywania działa w osobnym wątku echo_task(). Na początku wywoływane są procedury konfigurujące interfejs UART. Do konfiguracji używa się parametrów zdefiniowanych w pliku sdkonfig, znajdującym się w drzewie projektu. Plik sdkonfig można edytować po zaznaczeniu jego pozycji w zakładce Project Explorer i dwukrotnym kliknięciu lewym przyciskiem myszy. Po otwarciu okna konfiguratora i wybraniu pozycji Echo Example Configuration można ustawić takie parametry, jak: numer używanego interfejsu UART, wstępnie ustawiana szybkość transmisji, przypisanie wybranych portów I/O jako wyprowadzeń RXD i TXD. Procedura uart_driver_install() instaluje sterownik wybranego interfejsu UART, w przykładzie określa ona rozmiar pierścieniowego bufora odbiorczego na BUF_SIZE * 2 i wyłącza bufor nadawczy. Procedura uart_param_config() ustawia parametry komunikacji UART-a przekazywane strukturą uart_config. Z kolei procedura uart_set_pin() konfiguruje fizyczne piny GPIO, do których będzie podłączony interfejs UART. Jeżeli jakieś wyprowadzenia nie będą używane (tak jak w przykładzie RTS i CTS), należy zamiast numeru użyć makra UART_PIN_NO_CHANGE.

#include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/uart.h" #include "driver/gpio.h" #include "sdkconfig.h" /** * This is an example which echos any data it receives on configured UART back to the sender, * with hardware flow control turned off. It does not use UART driver event queue. * * – Port: configured UART * – Receive (Rx) buffer: on * – Transmit (Tx) buffer: off * – Flow control: off * – Event queue: off * – Pin assignment: see defines below (See Kconfig) */ #define ECHO_TEST_TXD (CONFIG_EXAMPLE_UART_TXD) #define ECHO_TEST_RXD (CONFIG_EXAMPLE_UART_RXD) #define ECHO_TEST_RTS (UART_PIN_NO_CHANGE) #define ECHO_TEST_CTS (UART_PIN_NO_CHANGE) #define ECHO_UART_PORT_NUM (CONFIG_EXAMPLE_UART_PORT_NUM) #define ECHO_UART_BAUD_RATE (CONFIG_EXAMPLE_UART_BAUD_RATE) #define ECHO_TASK_STACK_SIZE (CONFIG_EXAMPLE_TASK_STACK_SIZE) #define BUF_SIZE (1024) static void echo_task(void *arg) { /* Configure parameters of an UART driver, * communication pins and install the driver */ uart_config_t uart_config = { .baud_rate = ECHO_UART_BAUD_RATE, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, .source_clk = UART_SCLK_APB, }; int intr_alloc_flags = 0; #if CONFIG_UART_ISR_IN_IRAM intr_alloc_flags = ESP_INTR_FLAG_IRAM; #endif ESP_ERROR_CHECK(uart_driver_install(ECHO_UART_PORT_NUM, BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags)); ESP_ERROR_CHECK(uart_param_config(ECHO_UART_PORT_NUM, &uart_config)); ESP_ERROR_CHECK(uart_set_pin(ECHO_UART_PORT_NUM, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS)); // Configure a temporary buffer for the incoming data uint8_t *data = (uint8_t *) malloc(BUF_SIZE); while (1) { // Read data from the UART int len = uart_read_bytes(ECHO_UART_PORT_NUM, data, BUF_SIZE, 20 / portTICK_RATE_MS); // Write data back to the UART uart_write_bytes(ECHO_UART_PORT_NUM, (const char *) data, len); if (len !=0)uart_write_bytes(ECHO_UART_PORT_NUM, (const char *) "echo ok\r\n", 9); } } void app_main(void) { xTaskCreate(echo_task, "uart_echo_task", ECHO_TASK_STACK_SIZE, NULL, 10, NULL); } Listing 5. Program do przetestowania interfejsu UART w trybie echa

Od tej chwili możliwy jest odbiór przesyłanych portem UART danych, które można odczytywać cyklicznie wywoływaną funkcją uart_read_bytes(). Funkcja przepisuje odebrane dane z pierścieniowego bufora odbiorczego – do utworzonego w programie bufora data[], do którego wskaźnik podany został jako parametr w wywołaniu funkcji. Funkcja zwraca także liczbę odebranych bajtów danych, wartość 0 oznacza brak nowych danych. W programie przykładowym odebrane bajty zapisane w buforze data[] odsyłane są tym samym UART-em jako echo, przy użyciu funkcji uart_write_bytes(). Następnie realizowany jest kolejny cykl odpytywania w pętli while(1).

W przykładzie pokazano najprostszy sposób korzystania z interfejsu UART. Informacje o bardziej wyrafinowanych metodach, opartych między innymi na przerwaniach, można znaleźć w dokumentacji [4] oraz w innych przykładach.

Ryszard Szymaniak, EP

Literatura:

  1. https://docs.espressif.com/projects/esp-idf/en/release-v4.4/esp32/api-reference/peripherals/gpio.html
  2. https://docs.espressif.com/projects/esp-idf/en/release-v4.4/esp32/api-reference/peripherals/adc.html
  3. https://docs.espressif.com/projects/esp-idf/en/release-v4.4/esp32/api-reference/peripherals/ledc.html
  4. https://docs.espressif.com/projects/esp-idf/en/release-v4.4/esp32/api-reference/peripherals/uart.html
Artykuł ukazał się w
Elektronika Praktyczna
lipiec 2024
Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik kwiecień 2025

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio marzec - kwiecień 2025

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka, Podzespoły, Aplikacje marzec 2025

Automatyka, Podzespoły, Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna kwiecień 2025

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich kwiecień 2025

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów

Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik kwiecień 2025

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio marzec - kwiecień 2025

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka, Podzespoły, Aplikacje marzec 2025

Automatyka, Podzespoły, Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna kwiecień 2025

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich kwiecień 2025

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów

Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik kwiecień 2025

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio marzec - kwiecień 2025

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka, Podzespoły, Aplikacje marzec 2025

Automatyka, Podzespoły, Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna kwiecień 2025

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich kwiecień 2025

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów