LINUX串口相关设置详解
fd=open(dev,O_RDWR|O_NOCTTY|O_NDELAY); //fd为打开的终端文件描述符
if(fd < 0)
cout << dev <<"open error\n" <<endl;
fcntl(fd, F_SETFL, FNDELAY); //标志FNDELAY可以保证read函数在端口上读不到字符的时候返回0
//fcntl(fd, F_SETFL, 0); //回到正常(阻塞)模式
————————————————
版权声明:本文为CSDN博主「花洛兮灬」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq413886183/article/details/108242610
Linux串口数据收发代码
编译生成a.out 后执行
./a.out /dev/ttySTM1 38400 8 0 1 0
代表 打开/dev/ttySTM1 以波特率:38400 数据位8 校验位无 停止位1 接受数据的方式(1代表发送,0代表接收)
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <termio.h> #include <time.h> #define MAX_BUF_SIZE 2048 char buf[MAX_BUF_SIZE+2]; #define MY_END_CHAR 0x13 int setup_port(int fd, int baud, int databits, int parity, int stopbits); int reset_port(int fd); int read_data(int fd, void *buf, int len); int write_data(int fd, void *buf, int len); void print_usage(char *program_name); int main(int argc, char *argv[]) //./a.out /dev/ttyS3 38400 8 0 1 0 { int fd; int baud; int len; int count; int i; int databits; int stopbits; int parity; int read_or_write; if (argc != 7) { print_usage(argv[0]); return 1; } baud = atoi(argv[2]); if ((baud < 0) || (baud > 921600)) { fprintf(stderr, "Invalid baudrate!\n"); return 1; } databits = atoi(argv[3]); if ((databits < 5) || (databits > 8)) { fprintf(stderr, "Invalid databits!\n"); return 1; } parity = atoi(argv[4]); if ((parity < 0) || (parity > 2)) { fprintf(stderr, "Invalid parity!\n"); return 1; } stopbits = atoi(argv[5]); if ((stopbits < 1) || (stopbits > 2)) { fprintf(stderr, "Invalid stopbits!\n"); return 1; } read_or_write = atoi(argv[6]); fd = open(argv[1], O_RDWR, 0); if (fd < 0) { fprintf(stderr, "open <%s> error %s\n", argv[1], strerror(errno)); return 1; } if (setup_port(fd, baud, databits, parity, stopbits)) { fprintf(stderr, "setup_port error %s\n", strerror(errno)); close(fd); return 1; } count = 0; if (read_or_write) { fprintf(stderr, "Begin to send:\n"); while ( (len = read(0, buf, MAX_BUF_SIZE)) > 0 ) { if (len == 1) { buf[0] = MY_END_CHAR; buf[1] = 0; write_data(fd, buf, len); break; } i = write_data(fd, buf, len); if (i == 0) { fprintf(stderr, "Send data error!\n"); break; } //count += len; //fprintf(stderr, "Send %d bytes\n", len); } } else { fprintf(stderr, "Begin to recv:\n"); int x = 0; len = MAX_BUF_SIZE; while (1) { i = read_data(fd, buf, len); if (i > 0) { //count += i; //fprintf(stderr, "Recv %d byte\n", i); for(x=0;x<i;x++) fprintf(stderr,"%02X", buf[x]); if (buf[i-1] == MY_END_CHAR) { break; } } } } reset_port(fd); close(fd); return 0; } static int baudflag_arr[] = { B921600, B460800, B230400, B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1800, B1200, B600, B300, B150, B110, B75, B50 }; static int speed_arr[] = { 921600, 460800, 230400, 115200, 57600, 38400, 19200, 9600, 4800, 2400, 1800, 1200, 600, 300, 150, 110, 75, 50 }; int speed_to_flag(int speed) { int i; for (i = 0; i < sizeof(speed_arr)/sizeof(int); i++) { if (speed == speed_arr[i]) { return baudflag_arr[i]; } } fprintf(stderr, "Unsupported baudrate, use 9600 instead!\n"); return B9600; } static struct termio oterm_attr; int setup_port(int fd, int baud, int databits, int parity, int stopbits) { struct termio term_attr; if (ioctl(fd, TCGETA, &term_attr) < 0) { return -1; } memcpy(&oterm_attr, &term_attr, sizeof(struct termio)); term_attr.c_iflag &= ~(INLCR | IGNCR | ICRNL | ISTRIP); term_attr.c_oflag &= ~(OPOST | ONLCR | OCRNL); term_attr.c_lflag &= ~(ISIG | ECHO | ICANON | NOFLSH); term_attr.c_cflag &= ~CBAUD; term_attr.c_cflag |= CREAD | speed_to_flag(baud); term_attr.c_cflag &= ~(CSIZE); switch (databits) { case 5: term_attr.c_cflag |= CS5; break; case 6: term_attr.c_cflag |= CS6; break; case 7: term_attr.c_cflag |= CS7; break; case 8: default: term_attr.c_cflag |= CS8; break; } switch (parity) { case 1: term_attr.c_cflag |= (PARENB | PARODD); break; case 2: term_attr.c_cflag |= PARENB; term_attr.c_cflag &= ~(PARODD); break; case 0: default: term_attr.c_cflag &= ~(PARENB); break; } switch (stopbits) { case 2: term_attr.c_cflag |= CSTOPB; break; case 1: default: term_attr.c_cflag &= ~CSTOPB; break; } term_attr.c_cc[VMIN] = 1; term_attr.c_cc[VTIME] = 0; if (ioctl(fd, TCSETAW, &term_attr) < 0) { return -1; } if (ioctl(fd, TCFLSH, 2) < 0) { return -1; } return 0; } int read_data(int fd, void *buf, int len) { int count; int ret; ret = 0; count = 0; //while (len > 0) { ret = read(fd, (char*)buf + count, len); if (ret < 1) { fprintf(stderr, "Read error %s\n", strerror(errno)); //break; } count += ret; len = len - ret; //} *((char*)buf + count) = 0; return count; } int write_data(int fd, void *buf, int len) { int count; int ret; ret = 0; count = 0; while (len > 0) { ret = write(fd, (char*)buf + count, len); if (ret < 1) { fprintf(stderr, "Write error %s\n", strerror(errno)); break; } count += ret; len = len - ret; } return count; } void print_usage(char *program_name) { fprintf(stderr, "*************************************\n" " A Simple Serial Port Test Utility\n" "*************************************\n\n" "Usage:\n %s <device> <baud> <databits> <parity> <stopbits> <read_or_write>\n" " databits: 5, 6, 7, 8\n" " parity: 0(None), 1(Odd), 2(Even)\n" " stopbits: 1, 2\n" " read_or_write: 0(read), 1(write)\n" "Example:\n %s /dev/ttyS0 9600 8 0 1 0\n\n", program_name, program_name ); } int reset_port(int fd) { if (ioctl(fd, TCSETAW, &oterm_attr) < 0) { return -1; } return 0; }
Linux下串口的设置命令
查看串口波特率等信息:
stty -F /dev/ttyS0 -a #ttyS0为要查看的串口
设置串口参数:
stty -F /dev/ttyS0 ispeed 115200 ospeed 115200 cs8
该命令将串口1(/dev/ttyS0)设置成115200波特率,8位数据模式。
一般情况下设置这两个参数就可以了,如果显示数据乱码,可能还需要设置其它参数,使用man查看stty其它设置选项。
查看串口接收数据:
cat /dev/ttyS0
16进制查看串口接收数据:
hexdump -C /dev/ttyO1
向串口发数据:
echo "test" > /dev/ttyS0
注意:并不是所有的Linux系统都支持使用echo cat 指令测试串口
查看串口信息:
setserial -g /dev/ttyS2
/dev/ttyS2, UART: unknown, Port: 0x03e8, IRQ: 4 1 #!/usr/bin/expect 2 #-------------------------------------------------- about us
使用shell脚本实现数据收发
#!/usr/bin/expect # set port /dev/ttyUSB3 set port [lindex $argv 0] set cmd [lindex $argv 1] set timeout 1 spawn -noecho microcom $port expect { timeout {send $cmd\r} } set timeout 3 expect { "\r\n" {exit 0} timeout {} } expect eof
1 #!/bin/sh 2 3 stty -F /dev/ttyUSB0 115200 #CONFIGURE SERIAL PORT 4 exec 3</dev/ttyUSB0 #REDIRECT SERIAL OUTPUT TO FD 3 5 cat <&3 > /tmp/ttyDump.dat & #REDIRECT SERIAL OUTPUT TO FILE 6 PID=$! #SAVE PID TO KILL CAT 7 echo "R" > /dev/ttyUSB0 #SEND COMMAND STRING TO SERIAL PORT 8 sleep 0.2s #WAIT FOR RESPONSE 9 kill $PID #KILL CAT PROCESS 10 wait $PID 2>/dev/null #SUPRESS "Terminated" output 11 12 exec 3<&- #FREE FD 3 13 cat /tmp/ttyDump.dat #DUMP CAPTURED DATA
1 #!/bin/sh 2 3 exec 99<>/dev/ttyUSB0 4 printf "QPI\r" >&99 5 while read -t 3 line <&99;do echo $line;done
写一个linux的串口程序,发现大多数情况下数据接收没问题,但是有时却有问题。主要是接收的字符串中包含有0x03这个字符,会造成与它相邻的字符同时也接收不到,搞了好久才发现这个错误。查找资料后发现许多ARM板也存着这个问题,存在问题的字符串还包括0x13、0x0D等特殊含义的字符。
解决方法比较简单,在接收数据前,对串口的文件描述符fd进行如下设置,
struct termios options; if ( tcgetattr( fd,&options) != 0) { perror("SetupSerial 1"); return(FALSE); } options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); options.c_oflag &= ~OPOST; options.c_cflag |= CLOCAL | CREAD; options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); tcsetattr(fd,TCSAFLUSH,&options);
现在问题应该可以解决了。如果你还想了解的更深入的话,可以接着往下看看。
一、数据成员
termios 函数族提供了一个常规的终端接口,用于控制非同步通信端口。 这个结构包含了至少下列成员:
1 tcflag_t c_iflag; /* 输入模式 */ 2 tcflag_t c_oflag; /* 输出模式 */ 3 tcflag_t c_cflag; /* 控制模式 */ 4 tcflag_t c_lflag; /* 本地模式 */ 5 cc_t c_cc[NCCS]; /* 控制字符 */ 6 7 struct termios 8 {unsigned short c_iflag; /* 输入模式标志*/ 9 unsigned short c_oflag; /* 输出模式标志*/ 10 unsigned short c_cflag; /* 控制模式标志*/ 11 unsigned short c_lflag; /*区域模式标志或本地模式标志或局部模式*/ 12 unsigned char c_line; /*行控制line discipline */ 13 unsigned char c_cc[NCC]; /* 控制字符特性*/ 14 };
二、作用
这个变量被用来提供一个健全的线路设置集合, 如果这个端口在被用户初始化前使用. 驱动初始化这个变量使用一个标准的数值集, 它拷贝自 tty_std_termios 变量. tty_std_termos 在 tty 核心被定义为:
1 struct termios tty_std_termios = { 2 .c_iflag = ICRNL | IXON, 3 .c_oflag = OPOST | ONLCR, 4 .c_cflag = B38400 | CS8 | CREAD | HUPCL, 5 .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |ECHOCTL | ECHOKE | IEXTEN, 7 .c_cc = INIT_C_CC 8 };
这个 struct termios 结构用来持有所有的当前线路设置, 给这个 tty 设备的一个特定端口. 这些线路设置控制当前波特率, 数据大小, 数据流控设置, 以及许多其他值.
三、成员的值
(一)c_iflag 标志常量:
Input mode ( 输入模式)
input mode可以在输入值传给程序之前控制其处理的方式。其中输入值可能是由序列埠或键盘的终端驱动程序所接收到的字元。 我们可以利用termios结构的c_iflag的标志来加以控制,其定义的方式皆以OR来加以组合。
IGNBRK :忽略输入中的 BREAK 状态。 (忽略命令行中的中断)
BRKINT :(命令行出现中断时,可产生一插断)如果设置了 IGNBRK,将忽略 BREAK。如果没有设置,但是设置了 BRKINT,那么 BREAK 将使得输入和输出队列被刷新,如果终端是一个前台进程组的控制终端,这个进程组中所有进程将收到 SIGINT 信号。如果既未设置 IGNBRK 也未设置 BRKINT,BREAK 将视为与 NUL 字符同义,除非设置了 PARMRK,这种情况下它被视为序列 377。
IGNPAR :忽略桢错误和奇偶校验错。
PARMRK :如果没有设置 IGNPAR,在有奇偶校验错或桢错误的字符前插入 377 。如果既没有设置 IGNPAR 也没有设置 PARMRK,将有奇偶校验错或桢错误的字符视为。
INPCK :启用输入奇偶检测。
ISTRIP :去掉第八位。
INLCR :将输入中的 NL 翻译为 CR。(将收到的换行符号转换为Return)
IGNCR :忽略输入中的回车。
ICRNL :将输入中的回车翻译为新行 (除非设置了 IGNCR)(否则当输入信号有 CR 时不会终止输入)。
IUCLC :(不属于 POSIX) 将输入中的大写字母映射为小写字母。
IXON :启用输出的 XON/XOFF 流控制。
IXANY :(不属于 POSIX.1;XSI) 允许任何字符来重新开始输出。(?)
IXOFF :启用输入的 XON/XOFF 流控制。
IMAXBEL:(不属于 POSIX) 当输入队列满时响零。Linux 没有实现这一位,总是将它视为已设置。
(二) c_oflag 标志常量:Output mode ( 输出模式)
Output mode主要负责控制输出字元的处理方式。输出字元在传送到序列埠或显示器之前是如何被程序来处理。
输出模式是利用termios结构的c_oflag的标志来加以控制,其定义的方式皆以OR来加以组合。
OPOST :启用具体实现自行定义的输出处理。
OLCUC :(不属于 POSIX) 将输出中的小写字母映射为大写字母。
ONLCR :(XSI) 将输出中的新行符映射为回车-换行。
OCRNL :将输出中的回车映射为新行符
ONOCR :不在第 0 列输出回车。
ONLRET :不输出回车。
OFILL :发送填充字符作为延时,而不是使用定时来延时。
OFDEL :(不属于 POSIX) 填充字符是 ASCII DEL (0177)。如果不设置,填充字符则是 ASCII NUL。
NLDLY :新行延时掩码。取值为 NL0 和 NL1。
CRDLY :回车延时掩码。取值为 CR0, CR1, CR2, 或 CR3。
TABDLY :水平跳格延时掩码。取值为 TAB0, TAB1, TAB2, TAB3 (或 XTABS)。取值为 TAB3,即 XTABS,将扩展跳格为空格 (每个跳格符填充 8 个空格)。(?)
BSDLY :回退延时掩码。取值为 BS0 或 BS1。(从来没有被实现过)
VTDLY :竖直跳格延时掩码。取值为 VT0 或 VT1。
FFDLY :进表延时掩码。取值为 FF0 或 FF1。
(三)c_cflag 标志常量:Control mode ( 控制模式)
Control mode主要用于控制终端设备的硬件设置。利用termios结构的c_cflag的标志来加以控制。控制模式用在序列线连接到数据设备,也可以用在与终端设备的交谈。
一般来说,改变终端设备的组态要比使用termios的控制模式来改变行(lines)的行为来得容易。
CBAUD :(不属于 POSIX) 波特率掩码 (4+1 位)。
CBAUDEX :(不属于 POSIX) 扩展的波特率掩码 (1 位),包含在 CBAUD 中。
(POSIX 规定波特率存储在 termios 结构中,并未精确指定它的位置,而是提供了函数 cfgetispeed() 和 cfsetispeed() 来存取它。一些系统使用 c_cflag 中 CBAUD 选择的位,其他系统使用单独的变量,例如 sg_ispeed 和 sg_ospeed 。)
CSIZE:字符长度掩码(传送或接收字元时用的位数)。取值为 CS5(传送或接收字元时用5bits), CS6, CS7, 或 CS8。
CSTOPB :设置两个停止位,而不是一个。
CREAD :打开接受者。
PARENB :允许输出产生奇偶信息以及输入的奇偶校验(启用同位产生与侦测)。
PARODD :输入和输出是奇校验(使用奇同位而非偶同位)。
HUPCL :在最后一个进程关闭设备后,降低 modem 控制线 (挂断)。(?)
CLOCAL :忽略 modem 控制线。
LOBLK :(不属于 POSIX) 从非当前 shell 层阻塞输出(用于 shl )。(?)
CIBAUD :(不属于 POSIX) 输入速度的掩码。CIBAUD 各位的值与 CBAUD 各位相同,左移了 IBSHIFT 位。
CRTSCTS :(不属于 POSIX) 启用 RTS/CTS (硬件) 流控制。
(四)c_lflag 标志常量:Local mode ( 局部模式)
Local mode主要用来控制终端设备不同的特色。利用termios结构里的c_lflag的标志来设定局部模式。
在巨集中有两个比较重要的标志:
1.ECHO:它可以让你阻止键入字元的回应。
2.ICANON(正规模式)标志,它可以对所接收的字元在两种不同的终端设备模式之间来回切换。
ISIG:当接受到字符 INTR, QUIT, SUSP, 或 DSUSP 时,产生相应的信号。
ICANON:启用标准模式 (canonical mode)。允许使用特殊字符 EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, 和 WERASE,以及按行的缓冲。
XCASE:(不属于 POSIX; Linux 下不被支持) 如果同时设置了 ICANON,终端只有大写。输入被转换为小写,除了有前缀的字符。输出时,大写字符被前缀(某些系统指定的特定字符) ,小写字符被转换成大写。
ECHO :回显输入字符。
ECHOE :如果同时设置了 ICANON,字符 ERASE 擦除前一个输入字符,WERASE 擦除前一个词。
ECHOK :如果同时设置了 ICANON,字符 KILL 删除当前行。
ECHONL :如果同时设置了 ICANON,回显字符 NL,即使没有设置 ECHO。
ECHOCTL :(不属于 POSIX) 如果同时设置了 ECHO,除了 TAB, NL, START, 和 STOP 之外的 ASCII 控制信号被回显为 ^X, 这里 X 是比控制信号大 0x40 的 ASCII 码。例如,字符 0x08 (BS) 被回显为 ^H。
ECHOPRT :(不属于 POSIX) 如果同时设置了 ICANON 和 IECHO,字符在删除的同时被打印。
ECHOKE :(不属于 POSIX) 如果同时设置了 ICANON,回显 KILL 时将删除一行中的每个字符,如同指定了 ECHOE 和 ECHOPRT 一样。
DEFECHO :(不属于 POSIX) 只在一个进程读的时候回显。
FLUSHO :(不属于 POSIX; Linux 下不被支持) 输出被刷新。这个标志可以通过键入字符 DISCARD 来开关。
NOFLSH :禁止在产生 SIGINT, SIGQUIT 和 SIGSUSP 信号时刷新输入和输出队列,即关闭queue中的flush。
TOSTOP :向试图写控制终端的后台进程组发送 SIGTTOU 信号(传送欲写入的信息到后台处理)。
PENDIN :(不属于 POSIX; Linux 下不被支持) 在读入下一个字符时,输入队列中所有字符被重新输出。(bash 用它来处理 typeahead)
IEXTEN :启用实现自定义的输入处理。这个标志必须与 ICANON 同时使用,才能解释特殊字符 EOL2,LNEXT,REPRINT 和 WERASE,IUCLC 标志才有效。
(五)c_cc 数组:特殊控制字元
可提供使用者设定一些特殊的功能, 如Ctrl+C的字元组合。特殊控制字元主要是利用termios结构里c_cc的阵列成员来做设定。
c_cc阵列主要用于正规与非正规两种环境,但要注意的是正规与非正规不可混为一谈。
其定义了特殊的控制字符。符号下标 (初始值) 和意义为:
VINTR:(003, ETX, Ctrl-C, or also 0177, DEL, rubout) 中断字符。发出 SIGINT 信号。当设置 ISIG 时可被识别,不再作为输入传递。
VQUIT :(034, FS, Ctrl-) 退出字符。发出 SIGQUIT 信号。当设置 ISIG 时可被识别,不再作为输入传递。
VERASE :(0177, DEL, rubout, or 010, BS, Ctrl-H, or also #) 删除字符。删除上一个还没有删掉的字符,但不删除上一个 EOF 或行首。当设置 ICANON 时可被识别,不再作为输入传递。
VKILL :(025, NAK, Ctrl-U, or Ctrl-X, or also @) 终止字符。删除自上一个 EOF 或行首以来的输入。当设置 ICANON 时可被识别,不再作为输入传递。
VEOF :(004, EOT, Ctrl-D) 文件尾字符。更精确地说,这个字符使得 tty 缓冲中的内容被送到等待输入的用户程序中,而不必等到 EOL。如果它是一行的第一个字符,那么用户程序的 read() 将返回 0,指示读到了 EOF。当设置 ICANON 时可被识别,不再作为输入传递。
VMIN :非 canonical 模式读的最小字符数(MIN主要是表示能满足read的最小字元数)。
VEOL :(0, NUL) 附加的行尾字符。当设置 ICANON 时可被识别。
VTIME :非 canonical 模式读时的延时,以十分之一秒为单位。
VEOL2 :(not in POSIX; 0, NUL) 另一个行尾字符。当设置 ICANON 时可被识别。
VSWTCH :(not in POSIX; not supported under Linux; 0, NUL) 开关字符。(只为 shl 所用。)
VSTART :(021, DC1, Ctrl-Q) 开始字符。重新开始被 Stop 字符中止的输出。当设置 IXON 时可被识别,不再作为输入传递。
VSTOP :(023, DC3, Ctrl-S) 停止字符。停止输出,直到键入 Start 字符。当设置 IXON 时可被识别,不再作为输入传递。
VSUSP :(032, SUB, Ctrl-Z) 挂起字符。发送 SIGTSTP 信号。当设置 ISIG 时可被识别,不再作为输入传递。
VDSUSP :(not in POSIX; not supported under Linux; 031, EM, Ctrl-Y) 延时挂起信号。当用户程序读到这个字符时,发送 SIGTSTP 信号。当设置 IEXTEN 和 ISIG,并且系统支持作业管理时可被识别,不再作为输入传递。
VLNEXT :(not in POSIX; 026, SYN, Ctrl-V) 字面上的下一个。引用下一个输入字符,取消它的任何特殊含义。当设置 IEXTEN 时可被识别,不再作为输入传递。
VWERASE :(not in POSIX; 027, ETB, Ctrl-W) 删除词。当设置 ICANON 和 IEXTEN 时可被识别,不再作为输入传递。
VREPRINT :(not in POSIX; 022, DC2, Ctrl-R) 重新输出未读的字符。当设置 ICANON 和 IEXTEN 时可被识别,不再作为输入传递。
VDISCARD :(not in POSIX; not supported under Linux; 017, SI, Ctrl-O) 开关:开始/结束丢弃未完成的输出。当设置 IEXTEN 时可被识别,不再作为输入传递。
VSTATUS :(not in POSIX; not supported under Linux; status request: 024, DC4, Ctrl-T).
这些符号下标值是互不相同的,除了 VTIME,VMIN 的值可能分别与 VEOL,VEOF 相同。 (在 non-canonical 模式下,特殊字符的含义更改为延时含义。MIN 表示应当被读入的最小字符数。TIME 是以十分之一秒为单位的计时器。如果同时设置了它们,read 将等待直到至少读入一个字符,一旦读入 MIN 个字符或者从上次读入字符开始经过了 TIME 时间就立即返回。如果只设置了 MIN,read 在读入 MIN 个字符之前不会返回。如果只设置了 TIME,read 将在至少读入一个字符,或者计时器超时的时候立即返回。如果都没有设置,read 将立即返回,只给出当前准备好的字符。)
MIN与TIME组合有以下四种:
1、 MIN = 0 , TIME =0
有READ立即回传
否则传回 0 ,不读取任何字元
2、 MIN = 0 , TIME >0
READ 传回读到的字元,或在十分之一秒后传回TIME
若来不及读到任何字元,则传回0
3、 MIN > 0 , TIME =0
READ 会等待,直到MIN字元可读
4、 MIN > 0 , TIME > 0
每一格字元之间计时器即会被启动
READ 会在读到MIN字元,传回值或TIME的字元计时(1/10秒)超过时将值传回
四、与此结构体相关的函数
(一)tcgetattr()
1.原型
int tcgetattr(int fd,struct termois & termios_p);
2.功能
取得终端介质(fd)初始值,并把其值 赋给temios_p;函数可以从后台进程中调用;但是,终端属性可能被后来的前台进程所改变。
(二)tcsetattr()
1.原型
int tcsetattr(int fd,int actions,const struct termios *termios_p);
2.功能
设置与终端相关的参数 (除非需要底层支持却无法满足),使用 termios_p 引用的 termios 结构。optional_actions (tcsetattr函数的第二个参数)指定了什么时候改变会起作用:
TCSANOW:改变立即发生
TCSADRAIN:改变在所有写入 fd 的输出都被传输后生效。这个函数应当用于修改影响输出的参数时使用。(当前输出完成时将值改变)
TCSAFLUSH :改变在所有写入 fd 引用的对象的输出都被传输后生效,所有已接受但未读入的输入都在改变发生前丢弃(同TCSADRAIN,但会舍弃当前所有值)。
(三)tcsendbreak()
传送连续的 0 值比特流,持续一段时间,如果终端使用异步串行数据传输的话。如果 duration 是 0,它至少传输 0.25 秒,不会超过 0.5 秒。如果 duration 非零,它发送的时间长度由实现定义。
如果终端并非使用异步串行数据传输,tcsendbreak() 什么都不做。
(四)tcdrain()
等待直到所有写入 fd 引用的对象的输出都被传输。
(五)tcflush()
丢弃要写入 引用的对象,但是尚未传输的数据,或者收到但是尚未读取的数据,取决于 queue_selector 的值:
TCIFLUSH :刷新收到的数据但是不读
TCOFLUSH :刷新写入的数据但是不传送
TCIOFLUSH :同时刷新收到的数据但是不读,并且刷新写入的数据但是不传送
(六)tcflow()
挂起 fd 引用的对象上的数据传输或接收,取决于 action 的值:
TCOOFF :挂起输出
TCOON :重新开始被挂起的输出
TCIOFF :发送一个 STOP 字符,停止终端设备向系统传送数据
TCION :发送一个 START 字符,使终端设备向系统传输数据
打开一个终端设备时的默认设置是输入和输出都没有挂起。
(七)波特率函数
被用来获取和设置 termios 结构中,输入和输出波特率的值。新值不会马上生效,直到成功调用了 tcsetattr() 函数。
设置速度为 B0 使得 modem "挂机"。与 B38400 相应的实际比特率可以用 setserial(8) 调整。
输入和输出波特率被保存于 termios 结构中。
cfmakeraw 设置终端属性如下:
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
termios_p->c_cflag &= ~(CSIZE|PARENB);
termios_p->c_cflag |= CS8;
1.cfgetospeed() 返回 termios_p 指向的 termios 结构中存储的输出波特率
2.cfsetospeed() 设置 termios_p 指向的 termios 结构中存储的输出波特率为 speed。取值必须是以下常量之一:
B0 B50 B75 B110 B134 B150 B200 B300 B600 B1200 B1800 B2400 B4800 B9600 B19200 B38400 B57600 B115200 B230400
其中:零值 B0 用来中断连接。如果指定了 B0,不应当再假定存在连接。通常,这样将断开连接。CBAUDEX 是一个掩码,指示高于 POSIX.1 定义的速度的那一些 (57600 及以上)。因此,B57600 & CBAUDEX 为非零。
3.cfgetispeed() 返回 termios 结构中存储的输入波特率。
4.cfsetispeed() 设置 termios 结构中存储的输入波特率为 speed。如果输入波特率被设为0,实际输入波特率将等于输出波特率。
五、RETURN VALUE 返回值
1.cfgetispeed() 返回 termios 结构中存储的输入波特率。
2.cfgetospeed() 返回 termios 结构中存储的输出波特率。
3.其他函数返回:
(1)0:成功
(2)-1:失败,
并且为 errno 置值来指示错误。
注意 tcsetattr() 返回成功,如果任何所要求的修改可以实现的话。因此,当进行多重修改时,应当在这个函数之后再次调用 tcgetattr() 来检测是否所有修改都成功实现。
六、NOTES 注意
Unix V7 以及很多后来的系统有一个波特率的列表,在十四个值 B0, ..., B9600 之后可以看到两个常数 EXTA, EXTB ("External A" and "External B")。很多系统将这个列表扩展为更高的波特率。
tcsendbreak 中非零的 duration 有不同的效果。SunOS 指定中断 duration*N 秒,其中 N 至少为 0.25,不高于 0.5 。Linux, AIX, DU, Tru64 发送 duration 微秒的 break 。FreeBSD, NetBSD, HP-UX 以及 MacOS 忽略 duration 的值。在 Solaris 和 Unixware 中, tcsendbreak 搭配非零的 duration 效果类似于 tcdrain。
SEE ALSO 参见
stty(1), setserial(8)
————————————————
版权声明:本文为CSDN博主「tiny丶」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/vevenlcf/article/details/51096122
属性获取
函数:int tcgetattr(int fd, struct termios *termios_p);成功返回零;失败返回非零,发生失败接口将设置errno错误标识。
参数说明:
键 值
|
说 明
|
IGNBRK
|
忽略BREAK键输入
|
BRKINT
|
如果设置了IGNBRK,BREAK键输入将被忽略
|
IGNPAR
|
忽略奇偶校验错误
|
PARMRK
|
标识奇偶校验错误
|
INPCK
|
允许输入奇偶校验
|
ISTRIP
|
去除字符的第8个比特
|
INLCR
|
将输入的NL(换行)转换成CR(回车)
|
IGNCR
|
忽略输入的回车
|
ICRNL
|
将输入的回车转化成换行(如果IGNCR未设置的情况下)
|
IUCLC
|
将输入的大写字符转换成小写字符(非POSIX)
|
IXON
|
允许输出时对XON/XOFF流进行控制
|
IXANY
|
输入任何字符将重启停止的输出
|
IXOFF
|
允许输入时对XON/XOFF流进行控制
|
IMAXBEL
|
当输入队列满的时候开始响铃
|
键 值
|
说 明
|
OPOST
|
处理后输出
|
OLCUC
|
将输入的小写字符转换成大写字符(非POSIX)
|
ONLCR
|
将输入的NL(换行)转换成CR(回车)及NL(换行)
|
OCRNL
|
将输入的CR(回车)转换成NL(换行)
|
ONOCR
|
第一行不输出回车符
|
ONLRET
|
不输出回车符
|
OFILL
|
发送填充字符以延迟终端输出
|
OFDEL
|
以ASCII码的DEL作为填充字符,如果未设置该参数,填充字符为NUL
|
NLDLY
|
换行输出延时,可以取NL0(不延迟)或NL1(延迟0.1s)
|
CRDLY
|
回车延迟,取值范围为:CR0、CR1、CR2和 CR3
|
TABDLY
|
水平制表符输出延迟,取值范围为:TAB0、TAB1、TAB2和TAB3
|
BSDLY
|
空格输出延迟,可以取BS0或BS1
|
VTDLY
|
垂直制表符输出延迟,可以取VT0或VT1
|
FFDLY
|
换页延迟,可以取FF0或FF1
|
键 值
|
说 明
|
CBAUD
|
波特率(4+1位)(非POSIX)
|
CBAUDEX
|
附加波特率(1位)(非POSIX)
|
CSIZE
|
字符长度,取值范围为CS5、CS6、CS7或CS8
|
CSTOPB
|
设置两个停止位
|
CREAD
|
使用接收器
|
PARENB
|
使用奇偶校验
|
PARODD
|
对输入使用奇偶校验,对输出使用偶校验
|
HUPCL
|
关闭设备时挂起
|
CLOCAL
|
忽略调制解调器线路状态
|
CRTSCTS
|
使用RTS/CTS流控制
|
键 值
|
说 明
|
ISIG
|
当输入INTR、QUIT、SUSP或DSUSP时,产生相应的信号
|
ICANON
|
使用标准输入模式
|
XCASE
|
在ICANON和XCASE同时设置的情况下,终端只使用大写。
|
ECHO
|
显示输入字符
|
ECHOE
|
如果ICANON同时设置,ERASE将删除输入的字符
|
ECHOK
|
如果ICANON同时设置,KILL将删除当前行
|
ECHONL
|
如果ICANON同时设置,即使ECHO没有设置依然显示换行符
|
ECHOPRT
|
如果ECHO和ICANON同时设置,将删除打印出的字符(非POSIX)
|
TOSTOP
|
向后台输出发送SIGTTOU信号
|
宏
|
说 明
|
宏
|
说 明
|
VINTR
|
Interrupt字符
|
VEOL
|
附加的End-of-file字符
|
VQUIT
|
Quit字符
|
VTIME
|
非规范模式读取时的超时时间
|
VERASE
|
Erase字符
|
VSTOP
|
Stop字符
|
VKILL
|
Kill字符
|
VSTART
|
Start字符
|
VEOF
|
End-of-file字符
|
VSUSP
|
Suspend字符
|
VMIN
|
非规范模式读取时的最小字符数
|
属性设置
tcsetattr函数用于设置终端参数。函数在成功的时候返回0,失败的时候返回-1,并设置errno的值。
参数说明:参数fd为打开的终端文件描述符,参数optional_actions用于控制修改起作用的时间,而结构体termios_p中保存了要修改的参数。optional_actions可以取如下的值。
示例
#include <stdio.h> #include <termios.h> #include <unistd.h> #include <errno.h> int main(void){ //term用于存储获得的终端参数信息 struct termios term; int err; //获得标准输入的终端参数,将获得的信息保存在term变量中 if(tcgetattr(STDIN_FILENO,&term)==-1){ perror("Cannot get standard input description"); return 1; } //修改获得的终端信息的结束控制字符 term.c_cc[VEOF]=(cc_t)0x07; //使用tcsetattr函数将修改后的终端参数设置到标准输入中 //err用于保存函数调用后的结果 err=tcsetattr(STDIN_FILENO,TCSAFLUSH,&term); //如果err为-1或是出现EINTR错误(函数执行被信号中断), //给出相关出错信息 if(err==-1 && err==EINTR){ perror("Failed to change EOF character"); return 1; } return 0; }用gcc编译程序,得到可执行程序。在执行程序前,按“Ctrl+D”可以使终端结束。执行程序后,按“Ctrl+D”失去了作用,而输入“Ctrl+G”实现了原来“Ctrl+D”的功能。
INUX 使用tcgetattr与tcsetattr函数控制终端
转载
为了便于通过程序来获得和修改终端参数,Linux还提供了tcgetattr函数和tcsetattr函数。tcgetattr用于获取终端的相关参数,而tcsetattr函数用于设置终端参数。这两个函数的具体信息如表6.2所示。
表6.2 tcgetattr函数和tcsetattr函数
头文件 |
|
||
函数形式 |
int tcgetattr(int fd, struct termios *termios_p); int tcsetattr(int fd, int optional_actions, const struct termios *termios_p); |
||
返回值 |
成功 |
失败 |
是否设置errno |
0 |
−1 |
是 |
说明:tcgetattr函数用于获取与终端相关的参数。参数fd为终端的文件描述符,返回的结果保存在termios结构体中,该结构体一般包括如下的成员:
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_cc[NCCS];
- 1.
- 2.
- 3.
- 4.
- 5.
其具体意义如下。
c_iflag:输入模式标志,控制终端输入方式,具体参数如表6.3所示。
表6.3 c_iflag参数表
键 值 |
说 明 |
IGNBRK |
忽略BREAK键输入 |
BRKINT |
如果设置了IGNBRK,BREAK键的输入将被忽略,如果设置了BRKINT ,将产生SIGINT中断 |
IGNPAR |
忽略奇偶校验错误 |
PARMRK |
标识奇偶校验错误 |
INPCK |
允许输入奇偶校验 |
ISTRIP |
去除字符的第8个比特 |
INLCR |
将输入的NL(换行)转换成CR(回车) |
IGNCR |
忽略输入的回车 |
ICRNL |
将输入的回车转化成换行(如果IGNCR未设置的情况下) |
IUCLC |
将输入的大写字符转换成小写字符(非POSIX) |
IXON |
允许输入时对XON/XOFF流进行控制 |
IXANY |
输入任何字符将重启停止的输出 |
IXOFF |
允许输入时对XON/XOFF流进行控制 |
IMAXBEL |
当输入队列满的时候开始响铃,Linux在使用该参数而是认为该参数总是已经设置 |
c_oflag:输出模式标志,控制终端输出方式,具体参数如表6.4所示。
表6.4 c_oflag参数
键 值 |
说 明 |
OPOST |
处理后输出 |
OLCUC |
将输入的小写字符转换成大写字符(非POSIX) |
ONLCR |
将输入的NL(换行)转换成CR(回车)及NL(换行) |
OCRNL |
将输入的CR(回车)转换成NL(换行) |
ONOCR |
第一行不输出回车符 |
ONLRET |
不输出回车符 |
OFILL |
发送填充字符以延迟终端输出 |
OFDEL |
以ASCII码的DEL作为填充字符,如果未设置该参数,填充字符将是NUL(‘\0’)(非POSIX) |
NLDLY |
换行输出延时,可以取NL0(不延迟)或NL1(延迟0.1s) |
CRDLY |
回车延迟,取值范围为:CR0、CR1、CR2和 CR3 |
TABDLY |
水平制表符输出延迟,取值范围为:TAB0、TAB1、TAB2和TAB3 |
BSDLY |
空格输出延迟,可以取BS0或BS1 |
VTDLY |
垂直制表符输出延迟,可以取VT0或VT1 |
FFDLY |
换页延迟,可以取FF0或FF1 |
c_cflag:控制模式标志,指定终端硬件控制信息,具体参数如表6.5所示。
LINUX 使用tcgetattr函数与tcsetattr函数控制终端二
2009-11-24 15:30
表6.5 c_oflag参数 c_lflag:本地模式标志,控制终端编辑功能,具体参数如表6.6所示。
表6.6 c_lflag参数 c_cc[NCCS]:控制字符,用于保存终端驱动程序中的特殊字符,如输入结束符等。c_cc中定义了如表6.7所示的控制字符。
表6.7 c_cc支持的控制字符 tcsetattr函数用于设置终端的相关参数。参数fd为打开的终端文件描述符,参数optional_actions用于控制修改起作用的时间,而结构体termios_p中保存了要修改的参数。 错误信息:
实例演练: //p6.2.c 修改终端控制字符示例
#include #include #include #include int main(void){ //term用于存储获得的终端参数信息 struct termios term; int err; //获得标准输入的终端参数,将获得的信息保存在term变量中 if(tcgetattr(STDIN_FILENO,&term)==-1){ perror("Cannot get standard input description"); return 1; } //修改获得的终端信息的结束控制字符 term.c_cc[VEOF]=(cc_t)0x07; //使用tcsetattr函数将修改后的终端参数设置到标准输入中 //err用于保存函数调用后的结果 err=tcsetattr(STDIN_FILENO,TCSAFLUSH,&term); //如果err为-1或是出现EINTR错误(函数执行被信号中断), //给出相关出错信息 if(err==-1 && err==EINTR){ perror("Failed to change EOF character"); return 1; } return 0; }
使用gcc编译p6.2.c程序,得到名为p6.2的可执行程序。在执行p6.2程序前,按“Ctrl+D”可以使终端结束。执行p6.2程序后,按“Ctrl+D”失去了作用,而输入“Ctrl+G”实现了原来“Ctrl+D”的功能
|
键 值 |
说 明 |
CBAUD |
波特率(4+1位)(非POSIX) |
CBAUDEX |
附加波特率(1位)(非POSIX) |
CSIZE |
字符长度,取值范围为CS5、CS6、CS7或CS8 |
CSTOPB |
设置两个停止位 |
CREAD |
使用接收器 |
PARENB |
使用奇偶校验 |
PARODD |
对输入使用奇偶校验,对输出使用偶校验 |
HUPCL |
关闭设备时挂起 |
CLOCAL |
忽略调制解调器线路状态 |
CRTSCTS |
使用RTS/CTS流控制 |
======
串口操作需要的头文件
#include /*标准函数库定义*/
#include /*Unix 标准函数定义*/
#include
#include
#include /*文件控制定义*/
#include /*PPSIX 终端控制定义*/
#include /*错误号定义*/
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
1.打开串口
在前面已经提到linux下的串口访问是以设备文件形式进行的,所以打开串口也即是打开文件的操作。函数原型可以如下所示:
- 1.
参数说明:
(1)DE_name:要打开的设备文件名
比如要打开串口1,即为/dev/ttyS0。
(2)open_Status:文件打开方式,可采用下面的文件打开模式:
O_RDONLY:以只读方式打开文件
O_WRONLY:以只写方式打开文件
O_RDWR:以读写方式打开文件
O_APPEND:写入数据时添加到文件末尾
O_CREATE:如果文件不存在则产生该文件,使用该标志需要设置访问权限位mode_t
O_EXCL:指定该标志,并且指定了O_CREATE标志,如果打开的文件存在则会产生一个错误
O_TRUNC:如果文件存在并且成功以写或者只写方式打开,则清除文件所有内容,使得文件长度变为0
O_NOCTTY:如果打开的是一个终端设备,这个程序不会成为对应这个端口的控制终端,如果没有该标志,任何一个输入,例如键盘中止信号等,都将影响进程。
O_NONBLOCK:该标志与早期使用的O_NDELAY标志作用差不多。程序不关心DCD信号线的状态,如果指定该标志,进程将一直在休眠状态,直到DCD信号线为0。
函数返回值:
成功返回文件描述符,如果失败返回-1
例如:
在 Linux 下串口文件是位于 /dev 下的。串口一 为 /dev/ttyS0,串口二 为 /dev/ttyS1。打开串口是通过使用标准的文件打开函数操作:
/*以读写方式打开串口*/
fd = open( "/dev/ttyS0", O_RDWR);
if (fd==-1)
{
/* 不能打开串口一*/
perror(" 提示错误!");
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
2.设置串口
最基本的设置串口包括波特率设置,效验位和停止位设置。串口的设置主要是设置
struct termios 结构体的各成员值。
{ unsigned short c_iflag; /* 输入模式标志 */
unsigned short c_oflag; /* 输出模式标志 */
unsigned short c_cflag; /* 控制模式标志*/
unsigned short c_lflag; /* local mode flags */
unsigned char c_line; /* line discipline */
unsigned char c_cc[NCC]; /* control characters */
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
设置这个结构体很复杂,我这里就只说说常见的一些设置:
2.1 波特率设置
波特率的设置定义在,其包含在头文件里。
常用的波特率常数如下:
B0-------à0 B1800-------à1800
B50-----à50 B2400------à2400
B75-----à75 B4800------à4800
B110----à110 B9600------à9600
B134----à134.5 B19200-----à19200
B200----à200 B38400------à38400
B300----à300 B57600------à57600
B600----à600 B76800------à76800
B1200---à1200 B115200-----à115200
假定程序中想要设置通讯的波特率,使用cfsetispeed( )和cfsetospeed( )函数来操作,获取波特率信息是通过cfgetispeed()和cfgetospeed()函数来完成的。
比如可以这样来指定串口通讯的波特率:
........
.......
struct termios opt; /*定义指向termios 结构类型的指针opt*/
/***************以下设置通讯波特率****************/
cfsetispeed(&opt,B9600 ); /*指定输入波特率,9600bps*/
cfsetospeed(&opt,B9600);/*指定输出波特率,9600bps*/
/************************************************/
.........
..........
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
一般来说,输入、输出的波特率应该是一致的。
下面是另一个修改波特率的代码:
tcgetattr(fd, &Opt);
cfsetispeed(&Opt,B19200); /*设置为19200Bps*/
cfsetospeed(&Opt,B19200);
tcsetattr(fd,TCANOW,&Opt);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
设置波特率的例子函数:
*@brief 设置串口通信速率
*@param fd 类型 int 打开串口的文件句柄
*@param speed 类型 int 串口速度
*@return void
*/
int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400,
19200, 9600, 4800, 2400, 1200, 300, };
void set_speed(int fd, int speed){
int i;
int status;
struct termios Opt;
tcgetattr(fd, &Opt);
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++) {
if (speed == name_arr[i]) {
tcflush(fd, TCIOFLUSH);
cfsetispeed(&Opt, speed_arr[i]);
cfsetospeed(&Opt, speed_arr[i]);
status = tcsetattr(fd1, TCSANOW, &Opt);
if (status != 0) {
perror("tcsetattr fd1");
return;
}
tcflush(fd,TCIOFLUSH);
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
2.2 设置效验的函数:
*@brief 设置串口数据位,停止位和效验位
*@param fd 类型 int 打开的串口文件句柄
*@param databits 类型 int 数据位 取值 为 7 或者8
*@param stopbits 类型 int 停止位 取值为 1 或者2
*@param parity 类型 int 效验类型 取值为N,E,O,,S
*/
int set_Parity(int fd,int databits,int stopbits,int parity)
{
struct termios options;
if ( tcgetattr( fd,&options) != 0) {
perror("SetupSerial 1");
return(FALSE);
}
options.c_cflag &= ~CSIZE;
switch (databits) /*设置数据位数*/
{
case 7:
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
fprintf(stderr,"Unsupported data sizen"); return (FALSE);
}
switch (parity)
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* 转换为偶效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;break;
default:
fprintf(stderr,"Unsupported parityn");
return (FALSE);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
2.3 设置停止位
{
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
fprintf(stderr,"Unsupported stop bitsn");
return (FALSE);
}
/* Set input parity option */
if (parity != 'n')
options.c_iflag |= INPCK;
tcflush(fd,TCIFLUSH);
options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return (FALSE);
}
return (TRUE);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
在上述代码中,有两句话特别重要:
options.c_cc[VTIME] = 0; /* 设置超时0 seconds*/
options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/
这两句话决定了对串口读取的函数read()的一些功能。我将着重介绍一下他们对read()函数的影响。
对串口操作的结构体是
tcflag_t c_iflag; /*输入模式标记*/
tcflag_t c_oflag; /*输出模式标记*/
tcflag_t c_cflag; /*控制模式标记*/
tcflag_t c_lflag; /*本地模式标记*/
cc_t c_line; /*线路规程*/
cc_t c_cc[NCCS]; /*控制符号*/
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
其中cc_t, c_line只有在一些特殊的系统程序(比如,设置通过tty设备来通信的网络协议)中才会用。在数组c_cc中有两个下标(VTIME和VMIN)对应的元素不是控制符,并且只是在原始模式下有效。只有在原始模式下,他们决定了read()函数在什么时候返回。在标准模式下,除非设置了O_NONBLOCK选项,否则只有当遇到文件结束符或各行的字符都已经编辑完毕后才返回。
控制符VTIME和VMIN之间有着复杂的关系。VTIME定义要求等待的零到几百毫秒的时间量(通常是一个8位的unsigned char变量,取值不能大于cc_t)。 VMIN定义了要求等待的最小字节数(不是要求读的字节数——read()的第三个参数才是指定要求读的最大字节数),这个字节数可能是0。
l) 如果VTIME取0,VMIN定义了要求等待读取的最小字节数。函数read()只有在读取了VMIN个字节的数据或者收到一个信号的时候才返回。
2) 如果VMIN取0,VTIME定义了即使没有数据可以读取,read()函数返回前也要等待几百毫秒的时间量。这时,read()函数不需要像其通常情况那样要遇到一个文件结束标志才返回0。
3) 如果VTIME和VMIN都不取0,VTIME定义的是当接收到第一个字节的数据后开始计算等待的时间量。如果当调用read函数时可以得到数据,计时器马上开始计时。如果当调用read函数时还没有任何数据可读,则等接收到第一个字节的数据后,计时器开始计时。函数read可能会在读取到VMIN个字节的数据后返回,也可能在计时完毕后返回,这主要取决于哪个条件首先实现。不过函数至少会读取到一个字节的数据,因为计时器是在读取到第一个数据时开始计时的。
4) 如果VTIME和VMIN都取0,即使读取不到任何数据,函数read也会立即返回。同时,返回值0表示read函数不需要等待文件结束标志就返回了。
这就是这两个变量对read函数的影响。
2.4 串口属性配置
在程序中,很容易配置串口的属性,这些属性定义在结构体struct termios中。为在程序中使用该结构体,需要包含文件,该头文件定义了结构体struct termios。该结构体定义如下:
#define NCCS 19
tcflag_t c_iflag; /* 输入参数 */
tcflag_t c_oflag; /* 输出参数 */
tcflag_t c_cflag; /* 控制参数*/
tcflag_t c_ispeed; /* 输入波特率 */
tcflag_t c_ospeed; /* 输出波特率 */
cc_t c_line; /* 线控制 */
cc_t c_cc[NCCS]; /* 控制字符*/
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
其中成员c_line在POSIX(Portable Operating System Interface for UNIX)系统中不使用。对于支持POSIX终端接口的系统中,对于端口属性的设置和获取要用到两个重要的函数是:
(1).int tcsetattr(int fd,int opt_DE,*ptr)
该函数用来设置终端控制属性,其参数说明如下:
fd:待操作的文件描述符
opt_DE:选项值,有三个选项以供选择:
TCSANOW: 不等数据传输完毕就立即改变属性
TCSADRAIN:等待所有数据传输结束才改变属性
TCSAFLUSH:清空输入输出缓冲区才改变属性
*ptr:指向termios结构的指针
函数返回值:成功返回0,失败返回-1。
(2).int tcgetattr(int fd,*ptr)
该函数用来获取终端控制属性,它把串口的默认设置赋给了termios数据数据结构,其参数说明如下:
fd:待操作的文件描述符
*ptr:指向termios结构的指针
函数返回值:成功返回0,失败返回-1。
2.5 注意的问题:
如果不是开发终端之类的,只是串口传输数据,而不需要串口来处理,那么使用原始模式(Raw Mode)方式来通讯,设置方式如下:
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
options.c_oflag &= ~OPOST; /*Output*/
3.读写串口
3.1 串口读操作(接收端)
用open函数打开设备文件,函数返回一个文件描述符(file descriptors,fd),通过文件描述符来访问文件。读串口操作是通过read函数来完成的。函数原型如下:
int read(int fd, *buffer,length);
参数说明:
(1).int fd:文件描述符
(2).*buffer:数据缓冲区
(3).length:要读取的字节数
函数返回值:
读操作成功读取返回读取的字节数,失败则返回-1。
3.2 串口写操作(发送端)
写串口操作是通过write函数来完成的。函数原型如下:
write(int fd, *buffer,length);
参数说明:
(1).fd:文件描述符
(2).*buffer:存储写入数据的数据缓冲区
(3).length:写入缓冲去的数据字节数
函数返回值:
成功返回写入数据的字节数,该值通常等于length,如果写入失败返回-1。
例如:向终端设备发送初始化命令
设置好串口之后,读写串口就很容易了,把串口当作文件读写就是。
·发送数据
char buffer[1024];
int Length;int nByte;
nByte = write(fd, buffer ,Length)
4.关闭串口
关闭串口就是关闭文件。
close(fd);
5.例子
下面是一个简单的读取串口数据的例子,使用了上面定义的一些函数和头文件
代码说明:使用串口二测试的,发送的数据是字符,
但是没有发送字符串结束符号,所以接收到后,后面加上了结束符号。
我测试使用的是单片机发送数据到第二个串口,测试通过。
**********************************************************************/
#define FALSE -1
#define TRUE 0
/*********************************************************************/
int OpenDev(char *Dev)
{
int fd = open( Dev, O_RDWR );
//| O_NOCTTY | O_NDELAY
if (-1 == fd)
{
perror("Can't Open Serial Port");
return -1;
}
else
return fd;
}
int main(int argc, char **argv){
int fd;
int nread;
char buff[512];
char *dev = "/dev/ttyS1"; //串口二
fd = OpenDev(dev);
set_speed(fd,19200);
if (set_Parity(fd,8,1,'N') == FALSE) {
printf("Set Parity Errorn");
exit (0);
}
while (1) //循环读取数据
{
while((nread = read(fd, buff, 512))>0)
{
printf("nLen %dn",nread);
buff[nread+1] = '';
printf( "n%s", buff);
}
}
//close(fd);
// exit (0);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
posted on 2023-02-18 11:10 bailinjun 阅读(1856) 评论(0) 编辑 收藏 举报