Search
Cílem úlohy je rozšířit si znalosti komunikace s Nucleo aplikací sériovou komunikací s vícebajtovými zprávy a zpracováním dalšího zdroje asynchronních událostí, tj. reakce na stisk tlačítka na Nucleo desce.
Binární obrazy aplikací (Nucleo i ovládací počítač) pro testování jsou součástí balíku prg-hw10.zip.
Pro korektní ukončení programu (po stisku klávesy 'q') je nutné ukončit načítání znaku ze seriového portu, proto realizujte načítání v tzv. non-bloking režimu s využitím poll funkce, implementace můžete založit na modulu prg_serial_nonblocking, např. viz serial_nonblock.
poll
prg_serial_nonblocking
Realizujte programy pro desku Nucleo STM32F446RE a program pro ovládací počítač. Komunikaci mezi Nucleo deskou a ovládací aplikací realizujte seriovou komunikací (115200 bps, 8 bitů, bez parity). Úloha se skládá ze dvou částí: 1) programu pro Nucleo, který přímo odpovídá řešení úlohy na cvičení lab10 a 2) programu pro ovládací počítač.
mbed.org
MSG_STARTUP
'PRG-HW 10
MSG_GET_VERSION
MSG_VERSION
MSG_COMPUTE
MSG_ERROR
MSG_OK
MSG_COMPUTE_DATA
MSG_DONE
MSG_ABORT
Ovládací aplikace čte klávesy bez nutnosti stisku enter (tj. terminálový vstup je v raw režimu), na které reaguje zasláním odpovídající zprávy Nucleo programu. Program reaguje na následující znaky
nbr_tasks
chunk_id
Komunikační zprávy odpovídají zprávám z úlohy na cvičení lab10. V případě ovládací aplikace lze využít definice datových struktur pro zprávy v souboru messages.h
messages.h
/* * File name: messages.h */ #ifndef __MESSAGES_H__ #define __MESSAGES_H__ #include <stdint.h> #include <stdbool.h> // Definition of the communication messages typedef enum { MSG_OK, // ack of the received message MSG_ERROR, // report error on the previously received command MSG_ABORT, // abort - from user button or from serial port MSG_DONE, // report the requested work has been done MSG_GET_VERSION, // request version of the firmware MSG_VERSION, // send version of the firmware as major,minor, patch level, e.g., 1.0p1 MSG_STARTUP, // init of the message (id, up to 8 bytes long string, cksum MSG_COMPUTE, // request computation of a batch of tasks (chunk_id, nbr_tasks) MSG_COMPUTE_DATA, // computed result (chunk_id, result) MSG_NBR } message_type; #define STARTUP_MSG_LEN 9 typedef struct { uint8_t major; uint8_t minor; uint8_t patch; } msg_version; typedef struct { uint8_t message[STARTUP_MSG_LEN]; } msg_startup; typedef struct { uint16_t chunk_id; uint16_t nbr_tasks; } msg_compute; typedef struct { uint16_t chunk_id; uint16_t task_id; uint8_t result; } msg_compute_data; typedef struct { uint8_t type; // message type union { msg_version version; msg_startup startup; msg_compute compute; msg_compute_data compute_data; } data; uint8_t cksum; // message command } message; #endif /* end of messages.h */
kde jsou dále deklarovány hlavičky pomocných funkcí délky zprávy, marshalling a unmarshalling zpráv.
// return the size of the message in bytes bool get_message_size(uint8_t msg_type, int *size); // fill the given buf by the message msg (marhaling); bool fill_message_buf(const message *msg, uint8_t *buf, int size, int *len); // parse the message from buf to msg (unmarshaling) bool parse_message_buf(const uint8_t *buf, int size, message *msg);
Načtení zprávy ze sériového portu může být podobné tomu na Nucleo desce, ale v případě dedikovaného vlákna stačí načítat data do bufferu a následně vytvořit novou zprávu (např. malloc(sizeof(message)) ) voláním parse_message_buf(). Takto vytvořenou zprávu je pak možné v architektuře aplikace boss/worker předat do fronty událostí, kde po vyčtení z fronty bude zpráva zpracována hlavním vláknem a následně bude paměť uvolněna. Samotné načtení je možné realizovat neblokovaným čtením, např. pro jednoduchost lze použít funkci serial_getc_timeout() z prg_serial_nonblock.
malloc(sizeof(message))
parse_message_buf()
serial_getc_timeout()
V případě vytvoření zprávy pak stačí připravenou zprávu typu message převést na posloupnost bytů (buffer) funkcí fill_message_buf() a příslušný buffer zapsat na seriový port např. funkcí write().
message
fill_message_buf()
write()
Možností implementace ovládací aplikace je několik. Jednou z nich je dedikovat jedno vlákno na zpracování vstupu z klávesnice a druhé vlákno dedikovat na čtení ze sériového portu. Tato vlákna představují zdroje událostí, které mohou být přeposílány do hlavního vlákna prostřednictvím fronty zpráv. Komunikace směrem od hlavního vlákna je pouze při ukončení aplikace, tj. ukončení vláken. V našem případě aplikaci ukončujeme po stisknutí klávesy 'q', proto je postačující blokované čtení klávesnice v samostatném vlákně, které zašle zprávu s žádostí o ukončení aplikace hlavnímu vláknu a následně ukončí vyčítání kláves.
Frontu zpráv realizujeme jako kruhový buffer o definované kapacitě zpráv a synchronizačními primitivy zajistíme čekání vlákna při vložení zprávy do plné fronty, neboť předpokládáme, že dříve nebo později dojde k vyčtení zprávy hlavním vláknem.
Zprávy mohou být opět definovány různě, např. můžeme rozlišit zdroj zprávy (klávesnice, seriový port). Příklad zpráv je uveden v adresáři inspiration v podkladech pro domácí úkol. Dále je možná ukázka použití uvedena v komentářích přiloženého souboru hw10-main.c.
hw10-main.c
V ovládací aplikaci vypisujte dílčí stavy a události na standardní výstup. Rozlište mezi úrovni výpisu DEBUG, ERROR, INFO a WARN. Např.
DEBUG
ERROR
INFO
WARN
fprintf(stderr, "ERROR: Cannot open serial port %s\n", serial); fprintf(stderr, "INFO: Create thread '%s' %s\r\n", threads_names[i], ( r == 0 ? "OK" : "FAIL") ); fprintf(stderr, "INFO: Get version requested\r\n"); fprintf(stderr, "INFO: New computation chunk id: %d no. of tasks: %d\n\r", msg.data.compute.chunk_id, msg.data.compute.nbr_tasks); fprintf(stderr, "WARN: New computation requested but it is discarded due on ongoing computation\n\r"); fprintf(stderr, "INFO: Chunk reset request\n\r"); fprintf(stderr, "WARN: Chunk reset request discarded, it is currently computing\n\r"); fprintf(stderr, "WARN: Abort requested but it is not computing\n\r"); fprintf(stderr, "ERROR: send_message() does not send all bytes of the message!\n\r"); fprintf(stderr, "INFO: Nucleo restarted - '%s'\r\n", str); fprintf(stderr, "INFO: Receive ok from Nucleo\r\n"); fprintf(stderr, "INFO: Nucleo firmware ver. %d.%d-p%d\r\n", msg->data.version.major, msg->data.version.minor, msg->data.version.patch); fprintf(stderr, "INFO: Nucleo firmware ver. %d.%d\r\n", msg->data.version.major, msg->data.version.minor); fprintf(stderr, "WARN: Receive error from Nucleo\r\n"); fprintf(stderr, "INFO: Abort from Nucleo\r\n"); fprintf(stderr, "INFO: New data chunk id: %d, task id: %d - results %d\r\n", msg->data.compute_data.chunk_id, msg->data.compute_data.task_id, msg->data.compute_data.result); fprintf(stderr, "WARN: Nucleo sends new data without computing \r\n"); fprintf(stderr, "INFO: Nucleo reports the computation is done computing: %d\r\n", computation.computing); fprintf(stderr, "INFO: Call join to the thread %s\r\n", threads_names[i]); fprintf(stderr, "INFO: Joining the thread %s has been %s\r\n", threads_names[i], (r == 0 ? "OK" : "FAIL")); fprintf(stderr, "INFO: Exit input thead %p\r\n", pthread_self()); fprintf(stderr, "DEBUG: computing: %d\r\n", computation.computing); fprintf(stderr, "ERROR: Unknown message type has been received 0x%x\n - '%c'\r", c, c); fprintf(stderr, "ERROR: Cannot parse message type %d\n\r", msg_buf[0]); fprintf(stderr, "WARN: the packet has not been received discard what has been read\n\r"); fprintf(stderr, "ERROR: Cannot receive data from the serial port\r\n"); fprintf(stderr, "INFO: Exit serial_rx_thread %p\r\n", pthread_self()); fprintf(stderr, "DEBUG: Write message: ");
INFO: Create thread 'Input' OK INFO: Create thread 'Serial In' OK INFO: Nucleo restarted - 'PRG-HW 10' INFO: Get version requested INFO: Nucleo firmware ver. 0.9 INFO: New computation chunk id: 0 no. of tasks: 10 INFO: Receive ok from Nucleo INFO: New data chunk id: 0, task id: 0 - results 238 INFO: New data chunk id: 0, task id: 1 - results 222 INFO: New data chunk id: 0, task id: 2 - results 184 INFO: New data chunk id: 0, task id: 3 - results 95 INFO: New data chunk id: 0, task id: 4 - results 243 INFO: New data chunk id: 0, task id: 5 - results 101 INFO: New data chunk id: 0, task id: 6 - results 60 INFO: New data chunk id: 0, task id: 7 - results 12 INFO: New data chunk id: 0, task id: 8 - results 132 INFO: New data chunk id: 0, task id: 9 - results 184 INFO: Nucleo reports the computation is done computing: 1 INFO: New computation chunk id: 1 no. of tasks: 20 INFO: Receive ok from Nucleo INFO: New data chunk id: 1, task id: 0 - results 213 INFO: New data chunk id: 1, task id: 1 - results 22 INFO: New data chunk id: 1, task id: 2 - results 9 INFO: New data chunk id: 1, task id: 3 - results 3 INFO: New data chunk id: 1, task id: 4 - results 237 WARN: Nucleo sends new data without computing INFO: Receive ok from Nucleo INFO: New computation chunk id: 2 no. of tasks: 30 INFO: Receive ok from Nucleo INFO: New data chunk id: 2, task id: 0 - results 191 INFO: New data chunk id: 2, task id: 1 - results 251 INFO: New data chunk id: 2, task id: 2 - results 144 INFO: New data chunk id: 2, task id: 3 - results 178 INFO: Abort from Nucleo INFO: Chunk reset request INFO: New computation chunk id: 0 no. of tasks: 10 INFO: Receive ok from Nucleo INFO: New data chunk id: 0, task id: 0 - results 30 INFO: New data chunk id: 0, task id: 1 - results 187 INFO: New data chunk id: 0, task id: 2 - results 250 INFO: New data chunk id: 0, task id: 3 - results 200 INFO: New data chunk id: 0, task id: 4 - results 44 INFO: New data chunk id: 0, task id: 5 - results 249 INFO: New data chunk id: 0, task id: 6 - results 167 INFO: New data chunk id: 0, task id: 7 - results 207 INFO: New data chunk id: 0, task id: 8 - results 43 INFO: New data chunk id: 0, task id: 9 - results 202 INFO: Nucleo reports the computation is done computing: 1 INFO: New computation chunk id: 1 no. of tasks: 20 INFO: Receive ok from Nucleo INFO: New data chunk id: 1, task id: 0 - results 250 INFO: New data chunk id: 1, task id: 1 - results 3 INFO: New data chunk id: 1, task id: 2 - results 26 INFO: Get version requested INFO: New data chunk id: 1, task id: 3 - results 216 INFO: Nucleo firmware ver. 0.9 INFO: New data chunk id: 1, task id: 4 - results 208 INFO: New data chunk id: 1, task id: 5 - results 74 INFO: New data chunk id: 1, task id: 6 - results 46 INFO: New data chunk id: 1, task id: 7 - results 174 INFO: Abort from Nucleo INFO: Exit input thead 0x801016500 INFO: Exit serial_rx_thread 0x801016a00 INFO: Call join to the thread Input INFO: Joining the thread Input has been OK INFO: Call join to the thread Serial In INFO: Joining the thread Serial In has been OK
Q: Co vlastně má nucleo počítat?
Tato úloha je přípravou na semestrální práci, tedy výpočet na nucleu je zejména ilustrativní/k otestování komunikace. V semestrální práci pak na nucleu poběží výpočet části Juliovy množiny.
Program pokud možno realizujte na cvičení, nahrajte do odevzdávacího systému. Funkčnost programu ověří učitel na cvičení nebo na dalším cvičení.
mbed
hw10-mbed.cpp
hw10-cross.c
main()
Makefile