Saya agak bingung membaca dan menulis ke port serial. Saya memiliki perangkat USB di Linux yang menggunakan driver konverter perangkat serial USB FTDI. Ketika saya mencolokkannya, itu membuat: / dev / ttyUSB1.
Saya pikir itu akan mudah untuk membuka dan membaca / menulis dari itu di C. Saya tahu baud rate dan informasi paritas, tapi sepertinya tidak ada standar untuk ini?
Apakah saya melewatkan sesuatu, atau dapatkah seseorang mengarahkan saya ke arah yang benar?
c
linux
serial-port
gnychi
sumber
sumber
ioctl
danfcntl
.Jawaban:
Saya menulis ini sejak lama ( dari tahun 1985-1992, hanya dengan beberapa perubahan sejak saat itu ), dan cukup salin dan tempel bit yang diperlukan ke dalam setiap proyek.
Anda harus memanggil
cfmakeraw
padatty
diperoleh daritcgetattr
. Anda tidak dapat menghilangkan nolstruct termios
, mengkonfigurasinya, dan kemudian mengaturtty
dengantcsetattr
. Jika Anda menggunakan metode zero-out, maka Anda akan mengalami kegagalan intermiten yang tidak dapat dijelaskan, terutama pada BSD dan OS X. "Kegagalan intermiten yang tidak dapat dijelaskan" termasuk bertahanread(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
Nilai untuk kecepatan yang
B115200
,B230400
,B9600
,B19200
,B38400
,B57600
,B1200
,B2400
,B4800
, dll nilai untuk paritas0
(artinya tidak ada paritas),PARENB|PARODD
(memungkinkan paritas dan menggunakan aneh),PARENB
(memungkinkan paritas dan menggunakan bahkan),PARENB|PARODD|CMSPAR
(mark paritas), danPARENB|CMSPAR
( ruang paritas)."Blocking" mengatur apakah a
read()
di port menunggu sampai sejumlah karakter tertentu tiba. Menetapkan tanpa pemblokiran berarti bahwaread()
pengembalian berapa pun banyak karakter tersedia tanpa menunggu lebih lama, hingga batas buffer.Tambahan:
CMSPAR
hanya diperlukan untuk memilih paritas mark dan spasi, yang tidak umum. Untuk sebagian besar aplikasi, ini dapat dihilangkan. File header saya/usr/include/bits/termios.h
memungkinkan definisiCMSPAR
hanya jika simbol preprocessor__USE_MISC
didefinisikan. Definisi itu muncul (dalamfeatures.h
) dengan#if defined _BSD_SOURCE || defined _SVID_SOURCE #define __USE_MISC 1 #endif
Komentar pengantar
<features.h>
mengatakan:/* 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. ... */
sumber
lsusb
untuk melihat semua perangkat USB. Mereka dapat diberi nama berbeda jika sistem Anda memilikiudev
aturan khusus ; lihat/etc/udev/rules.d/
Mungkin dari sana Anda dapat memilih pelabuhan yang Anda cari. Tentu dengan mendaftar dan kemudian mencabut / mencolokkan port, Anda dapat mengidentifikasi perbedaannya.Untuk kode demo yang sesuai dengan standar POSIX seperti yang dijelaskan dalam Mengatur Mode Terminal dengan Benar dan Panduan Pemrograman Serial untuk Sistem Operasi POSIX , berikut ini yang ditawarkan.
Kode ini harus dijalankan dengan benar menggunakan Linux pada x86 serta prosesor ARM (atau bahkan CRIS).
Ini pada dasarnya berasal dari jawaban lain, tetapi komentar yang tidak akurat dan menyesatkan telah diperbaiki.
Program demo ini membuka dan menginisialisasi terminal serial pada 115200 baud untuk mode non-kanonik yang se-portabel mungkin.
Program mentransmisikan string teks hardcode ke terminal lain, dan penundaan saat output dijalankan.
Program kemudian memasuki loop tak terbatas untuk menerima dan menampilkan data dari terminal serial.
Secara default, data yang diterima ditampilkan sebagai nilai byte heksadesimal.
Untuk membuat program memperlakukan data yang diterima sebagai kode ASCII, kompilasi program dengan simbol DISPLAY_STRING, misalnya
Jika data yang diterima adalah teks ASCII (bukan data biner) dan Anda ingin membacanya sebagai baris yang diakhiri oleh karakter baris baru, lihat jawaban ini untuk contoh program.
#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); }
sumber
cfmakeraw
right?O_NDELAY
atauO_NONBLOCK
. The cmrr.umn.edu/~strupp/serial.html menyebutkan bahwa jika Anda membuka file descriptor dengan orang-bendera, makaVTIME
diabaikan. Lalu apa perbedaan antara menjalankan denganO_NONBLOCK
deskriptor file versus melakukannya denganVTIME
?