Jak otwierać, odczytywać i zapisywać z portu szeregowego w C?
Jestem trochę zdezorientowany czytaniem i zapisem do portu szeregowego. Mam Urządzenie USB w Linuksie, które używa sterownika konwertera szeregowego USB FTDI. Kiedy go podłączam, tworzy: / dev / ttyUSB1.
Myślałem, że itd jest proste do otwarcia i odczytu/zapisu z niego w C. znam szybkość transmisji i informacje o parzystości, ale wygląda na to, że nie ma na to standardu?
Coś mi umyka, czy ktoś może wskazać mi właściwy kierunek?
2 answers
Napisałem to dawno temu (z lat 1985-1992, z kilkoma poprawkami od tego czasu ), i po prostu skopiuj i wklej potrzebne bity do każdego projektu.
Musisz zadzwonić cfmakeraw
na tty
uzyskany z tcgetattr
. Nie można wyzerować struct termios
, skonfigurować go, a następnie ustawić tty
za pomocą tcsetattr
. Jeśli użyjesz metody zero-out, doświadczysz niewyjaśnionych przerywanych awarii, zwłaszcza na BSD i OS X. "niewyjaśnione przerywane awarie" obejmują zawieszanie się w read(3)
.
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
int
set_interface_attribs (int fd, int speed, int parity)
{
struct termios tty;
if (tcgetattr (fd, &tty) != 0)
{
error_message ("error %d from tcgetattr", errno);
return -1;
}
cfsetospeed (&tty, speed);
cfsetispeed (&tty, speed);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~IGNBRK; // disable break processing
tty.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
tty.c_oflag = 0; // no remapping, no delays
tty.c_cc[VMIN] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
error_message ("error %d from tcsetattr", errno);
return -1;
}
return 0;
}
void
set_blocking (int fd, int should_block)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
error_message ("error %d from tggetattr", errno);
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
if (tcsetattr (fd, TCSANOW, &tty) != 0)
error_message ("error %d setting term attributes", errno);
}
...
char *portname = "/dev/ttyUSB1"
...
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
error_message ("error %d opening %s: %s", errno, portname, strerror (errno));
return;
}
set_interface_attribs (fd, B115200, 0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (fd, 0); // set no blocking
write (fd, "hello!\n", 7); // send 7 character greeting
usleep ((7 + 25) * 100); // sleep enough to transmit the 7 plus
// receive 25: approx 100 uS per char transmit
char buf [100];
int n = read (fd, buf, sizeof buf); // read up to 100 characters if ready to read
Wartości prędkości to B115200
, B230400
, B9600
, B19200
, B38400
, B57600
, B1200
, B2400
, B4800
, itd. Wartości dla parzystości to 0
(bez parzystości), PARENB|PARODD
(włącz parzystość i użyj parzystości), PARENB
(włącz parzystość i użyj parzystości), PARENB|PARODD|CMSPAR
(Oznacz parzystość) i PARENB|CMSPAR
(parzystość przestrzeni).
"Blocking" ustawia, czy read()
na porcie czeka na nadejście określonej liczby znaków. Ustawienie brak blokowania oznacza, że read()
zwraca jednak wiele znaki są dostępne bez czekania na więcej, aż do limitu bufora.
Dodatek:
CMSPAR
jest potrzebny tylko do wyboru znaku i parytetu przestrzeni, co jest rzadkością. W większości zastosowań można go pominąć. Mój plik nagłówkowy /usr/include/bits/termios.h
umożliwia zdefiniowanie CMSPAR
tylko wtedy, gdy zdefiniowany jest symbol preprocesora __USE_MISC
. Definicja ta występuje (w features.h
) z
#if defined _BSD_SOURCE || defined _SVID_SOURCE
#define __USE_MISC 1
#endif
Wstępny komentarz <features.h>
mówi:
/* These are defined by the user (or the compiler)
to specify the desired environment:
...
_BSD_SOURCE ISO C, POSIX, and 4.3BSD things.
_SVID_SOURCE ISO C, POSIX, and SVID things.
...
*/
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-25 04:46:11
Dla kodu demonstracyjnego zgodnego ze standardem POSIX opisanym w odpowiednie Ustawianie trybów terminala
i Serial Programming Guide for POSIX Operating Systems , Poniżej przedstawiono
Kod ten powinien być poprawnie wykonany przy użyciu Linuksa na procesorach x86 oraz arm (a nawet Cris).
Wynika to zasadniczo z drugiej odpowiedzi, ale niedokładne i wprowadzające w błąd komentarze zostały poprawione.
Ten program demonstracyjny otwiera i inicjalizuje terminal szeregowy w 115200 baud dla trybu niekanonicznego, który jest jak najbardziej przenośny.
Program przesyła zakodowany na twardo ciąg tekstowy do drugiego terminala i opóźnia wykonanie wyjścia.
Następnie program wchodzi w nieskończoną pętlę, aby odbierać i wyświetlać dane z terminala szeregowego.
Domyślnie odebrane dane są wyświetlane jako szesnastkowe wartości bajtów.
Aby program traktował otrzymane dane jako kody ASCII, skompiluj program za pomocą symbolu DISPLAY_STRING, np.
cc -DDISPLAY_STRING demo.c
Jeśli odebrane dane są tekstem ASCII (a nie danymi binarnymi) i chcesz je odczytać jako linie zakończone znakiem nowej linii, zobacz ta odpowiedź dla przykładowego programu.
#define TERMINAL "/dev/ttyUSB0"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
int set_interface_attribs(int fd, int speed)
{
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
cfsetospeed(&tty, (speed_t)speed);
cfsetispeed(&tty, (speed_t)speed);
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
/* setup for non-canonical mode */
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 1;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
void set_mincount(int fd, int mcount)
{
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error tcgetattr: %s\n", strerror(errno));
return;
}
tty.c_cc[VMIN] = mcount ? 1 : 0;
tty.c_cc[VTIME] = 5; /* half second timer */
if (tcsetattr(fd, TCSANOW, &tty) < 0)
printf("Error tcsetattr: %s\n", strerror(errno));
}
int main()
{
char *portname = TERMINAL;
int fd;
int wlen;
char *xstr = "Hello!\n";
int xlen = strlen(xstr);
fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
printf("Error opening %s: %s\n", portname, strerror(errno));
return -1;
}
/*baudrate 115200, 8 bits, no parity, 1 stop bit */
set_interface_attribs(fd, B115200);
//set_mincount(fd, 0); /* set to pure timed read */
/* simple output */
wlen = write(fd, xstr, xlen);
if (wlen != xlen) {
printf("Error from write: %d, %d\n", wlen, errno);
}
tcdrain(fd); /* delay for output */
/* simple noncanonical input */
do {
unsigned char buf[80];
int rdlen;
rdlen = read(fd, buf, sizeof(buf) - 1);
if (rdlen > 0) {
#ifdef DISPLAY_STRING
buf[rdlen] = 0;
printf("Read %d: \"%s\"\n", rdlen, buf);
#else /* display hex */
unsigned char *p;
printf("Read %d:", rdlen);
for (p = buf; rdlen-- > 0; p++)
printf(" 0x%x", *p);
printf("\n");
#endif
} else if (rdlen < 0) {
printf("Error from read: %d: %s\n", rdlen, strerror(errno));
} else { /* rdlen == 0 */
printf("Timeout from read\n");
}
/* repeat read to get full message */
} while (1);
}
Przykład efektywnego programu, który zapewnia buforowanie odebranych danych, ale pozwala na przekazanie danych wejściowych bajt po bajcie, zobacz ta odpowiedź.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-14 22:40:08