Stabilní a bezchybný přenos telemetrických dat je absolutní prioritou každého měřícího systému. Během vývoje softwaru pro obsluhu komunikačního modulu HC-12, připojeného přes rozhraní UART, jsem stál před rozhodnutím, jaký mechanismus pro zpracování příchozích zpráv zvolit. Porovnával jsem metodu neustálého dotazování (Polling) s pokročilou architekturou řízenou událostmi (Event-Driven), která využívá datové fronty.
Ve svém řešení jsem nakonec použil architekturu řízenou událostmi, ačkoliv by se začátečníkům mohlo zdát logičtější a programátorsky snazší použít metodu Polling. Při aplikaci Pollingu se procesor v nekonečné smyčce neustále dotazuje komunikačního portu: „Máš nová data?“. Tento přístup je z inženýrského hlediska extrémně neefektivní. Spotřebovává značné množství elektrické energie a zcela zbytečně blokuje výpočetní výkon procesoru, který by mohl být využit pro zpracování dat ze senzorů. Navíc při vysoké rychlosti přenosu hrozí, že procesor nestihne data přečíst včas.
Z toho důvodu jsem na implementoval sofistikovaný systém událostních front (QueueHandle_t). Můj komunikační Task pro obsluhu HC-12 v podstatě „spí“ a nevyužívá absolutně žádný strojový čas procesoru. Ve chvíli, kdy modul zachytí rádiový signál, vygeneruje se hardwarové přerušení. Následně systém automaticky vytvoří událost (například UART_DATA), vloží ji do fronty a Task bleskově probudí k jejímu zpracování. Tento přístup mi poskytl naprostou garanci, že nepřijdu o jediný bajt dat ani při extrémním komunikačním vytížení. Kód s ukázkou použití fronty může pak vypadat například takto:
#include "driver/uart.h"#include "freertos/queue.h"#define HC12_UART_PORT UART_NUM_1#define TXD_PIN (17)#define RXD_PIN (18)#define RX_BUF_SIZE (1024)// Fronta pro předávání událostí UARTu - mozek naší komunikacestatic QueueHandle_t uart_queue;void init_hc12_communication() { uart_config_t uart_config = { .baud_rate = 9600, .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_DEFAULT, }; // Instalace ovladače: definujeme velikost bufferu a odkaz na frontu událostí uart_driver_install(HC12_UART_PORT, RX_BUF_SIZE * 2, 0, 20, &uart_queue, 0); uart_param_config(HC12_UART_PORT, &uart_config); uart_set_pin(HC12_UART_PORT, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);}void hc12_event_task(void *pvParameters) { uart_event_t event; uint8_t* dtmp = (uint8_t*) malloc(RX_BUF_SIZE); for(;;) { // Klíčový moment: Task čeká na událost z fronty (neplýtvá CPU výkonem) if(xQueueReceive(uart_queue, (void *)&event, (TickType_t)portMAX_DELAY)) { if(event.type == UART_DATA) { // Čteme data z bufferu až ve chvíli, kdy víme, že tam skutečně jsou int len = uart_read_bytes(HC12_UART_PORT, dtmp, event.size, portMAX_DELAY); // Zde probíhá samotné zpracování přijatých telemetrických dat printf("Přijato přes HC-12: %.*s\n", len, dtmp); } } }}/* Poznámka pro zájemce o testování: Pro zprovoznění tohoto mechanismu je nutné v hlavní funkci app_main() nejprve zavolat init_hc12_communication() a následně vytvořit task pomocí xTaskCreate(hc12_event_task, ...). Tuto implementaci si již vyzkoušejte sami v rámci svého hlavního souboru. :D*/
Slovníček pojmů:
- Polling (Dotazování): Způsob programování, kdy systém neustále plýtvá časem tím, že dokola aktivně kontroluje stav určitého hardwaru.
- Event-Driven (Řízené událostmi): Architektura, kde program pasivně čeká, dokud mu sám hardware prostřednictvím přerušení neoznámí, že vyžaduje pozornost.
- Fronta (Queue): Bezpečná softwarová struktura, přes kterou si různé části programu organizovaně předávají data, aby nedošlo k jejich ztrátě či přepsání.
- UART: Sériový komunikační standard, přes který spolu čipy komunikují asynchronně pomocí dvou datových vodičů (TX pro odesílání, RX pro příjem).
- Přetečení paměti (Overflow): Kritický chybový stav, který nastává, když příchozí data plní paměťový prostor (buffer) rychleji, než je procesor stíhá číst.
