BufferedSerial
nad seriovým portem (realizovaným USB), s rychlostí nastavenou na 115200 baudů.
i
a zhasne LED.
s
- rozsvícení LED. Nucleo pošle zpět na seriový port znak a
(ack).
e
- zhasnutí LED. Nucleo pošle zpět na seriový port znak a
(ack).
f
- změní stav LED. Nucleo pošle zpět na seriový port znak a
(ack).
!
(fail).
*
. (Toto nastane v případě neblokovaného režimu.)
GTKterm
, cutecom
nebo jerm
. Terminál připojte k portu \dev\ttyACM0
.
#include "mbed.h" DigitalOut myled(LED1); BufferedSerial serial (USBTX, USBRX, 115200); void blink(uint32_t i, Kernel::Clock::duration_u32 t = 50ms){ for(; i > 0; i--){ myled = true; ThisThread::sleep_for(t); myled = false; ThisThread::sleep_for(t); } } int main() { serial.set_blocking(true); const char init_msg = 'i'; serial.write(&init_msg, 1); blink(5); while(true){ char received_value; ssize_t received_in_fact = serial.read(&received_value, 1); char acknowledge_msg = 'a'; if(received_in_fact == 1){ switch(received_value){ case 's': myled = true; break; case 'e': myled = false; break; case 'f': myled = !myled; break; default: acknowledge_msg = '!'; } }else{ acknowledge_msg = '*'; } serial.write(&acknowledge_msg, 1); } }
V rámci operačního systému je zpravidla sériový port zpřístupňěn jako soubor (tzv. blokového zařízení) v adresáři /dev
, např. /dev/ttyUSB0
, /dev/cuaU0
nebo /dev/ttyACM0
, v závislosti na konkrétním operačním systému a příslušném ovladači. (Po připojení Nuclea k počítači například spusťte příkaz dmesg
, který zobrazí jak bylo Nucleo rozpoznáno.) Možností otevření a konfigurace sériového rozhraní je několik. Pro jednoduchost lze využít funkce z modulu prg_serial
, který je dostupný na návodné stránce.
S využítím funkcí z prg_serial.h
nahraďte virtuální termínál použitý v minulé úloze (GTKterm
apod.) vlastní aplikaci pro komunikaci s Nucleo boardem. Aplikaci rozšiřte například popisem jednotlivých akcí výpisem na terminal. Uvědomte si, se kterými všemi soubory se pracuje (stdin, stdout, sériové rozhraní).
#include <stdio.h> #include <stdbool.h> #include <stdlib.h> #include "prg_serial.h" int main(int argc, char *argv[]){ int ret = 0; const char *serial = argc > 1 ? argv[1] : "/dev/ttyACM0"; fprintf(stderr, "INFO: open serial port at %s\n", serial); int fd = serial_open(serial); if (fd != -1) { // read from serial port _Bool quit = false; while (!quit) { int c = getchar(); _Bool write_read_response = false; switch (c) { case 'q': printf("Quit the program\n"); quit = true; break; case 's': printf("Send 's' - LED on\n"); write_read_response = true; break; case 'e': printf("Send 'e' - LED off\n"); write_read_response = true; break; case 'f': printf("Send 'f' - LED switch\n"); write_read_response = true; break; default: printf("Ignoring char '%d'\n", c); break; } if (write_read_response) { int r = serial_putc(fd, c); if (r != -1) { fprintf(stderr, "DEBUG: Received response '%d'\n", r); } else { fprintf(stderr, "ERROR: Error in received responses\n"); } } } serial_close(fd); } else { fprintf(stderr, "ERROR: Cannot open device %s\n", serial); } return ret; }
Načítání stisku klávesy vyžaduje z důvodu bufferování standardního vstupu potvrzení koncem řádku (stisknutí klávesy enter), což není příliš šikovné. Proto nastate terminál do tzv. “raw” režimu např. prostřednictvím funkce cfmakeraw()
nebo stty raw -echo
, viz 6. přednáška. Po skončení programu je vhodné terminál opět přepnout do běžného režimu, proto implementujte funkce pro přepínání do/z raw řežimu např.
void set_raw(_Bool set) { static struct termios tio, tioOld; tcgetattr(STDIN_FILENO, &tio); if (set) { // put the terminal to raw tioOld = tio; //backup cfmakeraw(&tio); tio.c_lflag &= ~ECHO; // assure echo is disabled tio.c_oflag |= OPOST; // enable output postprocessing tcsetattr(STDIN_FILENO, TCSANOW, &tio); } else { // set the previous settingsreset tcsetattr(STDIN_FILENO, TCSANOW, &tioOld); } }
Nebo využitím volání externího procesu funkcí system()
, jejímž argumentem je jméno programu spolu s příslušnými argumenty, např.
void set_raw(_Bool set) { if (set) { system("stty raw -echo"); // enable raw, disable echo } else { system("stty -raw echo"); // disable raw, enable echo } }
S využitím raw režimu terminálu zjednodušíme aplikaci pro ovládání LED na Nucleo desce
Program vyzkoušejte a také otestujte chování při náhlem odpojení Nucleo desky při běhu programu nebo resetu Nuclea desky.
Pokud program nastaví terminál do režimu “raw” a skončí (například příkazem kill
, nebo-li signálem SIGINT
), terminál si může zachovat s původní nastavení.
V takovém případě je možné zkusit příkaz reset
, nebo program vybavit handlerem signálu:
void handler(int signal_code) { set_raw_1(0); exit(0); } int main(){ signal(SIGINT, intHandler); ... }
Oba programy rozšiřte pro ovládání blikání LED zasláním znaků
1
- nastaví periodu blikání LED na 50 ms
2
- nastaví periodu blikání LED na 100 ms
3
- nastaví periodu blikání LED na 200 ms
4
- nastaví periodu blikání LED na 500 ms
5
- nastaví periodu blikání LED na 1000 ms.
0
- zastaví blikání, nechá LED ve stavu v jakém zrovna je.
Nucleo pošle po spuštění znak i
a každé přijetí příkazu pro nastavení periody potvrdí znakem a
. Aplikace na straně počítače bude fungovat v režimu “raw” a bude zobrazovat zprávy zaslané Nucleem.
Blikání LED v programu pro Nucleo je možno realizovat třemi způsoby:
main
v kombinaci s neblokující verzí serial.read
za použití funkce ThisThread::sleep_for(…)
.
Ticker
a metody attach
, která nastaví obsluhu přerušení podobně jako u tlačítka v minulém cvičení v kombinaci s blokující variantou sériového příjmu.
f
. Program pro Nucleo nebude nijak modifikovaný. (Tento způsob není ideální - pokud bychom měli pro Nucleo externí napájení, předchozí řešení by oproti tomuto zvládly blikat i po odpojení USB.)