linux uart应用开发(ttyS*设备)
写这篇文章的原因:因为在linux开发串口应用的时候,遇到了问题,让遇到相同问题的人少走点弯路:
①读串口数据的时,需要接受换行符才能返回。
②接受数据时,一个字节一个字节的返回。无法接受完多个字节在返回。
对于 linux的开发板来说,串口的驱动是不需要我们去开发,我目前是在内核4.9上开发,只需要修改一下设备树就可以了。所以直接对设备文件进访问就可以了。
linux使用串口的方法:
1.串口配置的头文件:
#include <termios.h> /*POSIX 终端控制定义*/
2.串口波特率的设置:
波特率 | |
B4800 | 4800 |
B9600 | 9600 |
B67500 | 67500 |
B115200 | 115200 |
修改波特率是使用cfsetispeed()和cfsetospeed()函数来操作。
例如:要将波特率修改为115200:cfsetispeed(&opts, B115200);
cfsetospeed(&opts,B115200);
3.串口属性配置:
串口的属性定义在结构体struct termios,其头文件#include <asm/termbits.h>,结构体定义如下:
#define NCCS 19struct termios {tcflag_t c_iflag; /* 输入参数 */tcflag_t c_oflag; /* 输出参数 */tcflag_t c_cflag; /* 控制参数*/tcflag_t c_lflag; /* 本地模式 */tcflag_t c_ispeed; /* 输入波特率 */tcflag_t c_ospeed; /* 输出波特率 */cc_t c_line; /* 线控制 */cc_t c_cc[NCCS]; /* 控制字符*/
};
对串口属性的获取和设置有两个:
① tcsetattr(int fd, int opt_DE, termios *ptr) ,返回值:成功返回0,失败返回-1。
fd:文件描述符
opt_DE:选项值,可供选择:
(1)TCSANOW: 不等数据传输完毕就立即改变属性
(2)TCSADRAIN:等待所有数据传输结束才改变属性
(3)TCSAFLUSH:清空输入输出缓冲区才改变属性
*ptr:指向termios结构的指针
②tcgetsttr(int fd, termios *ptr),返回值:成功返回0,失败返回-1。
fd:待操作的文件描述符
*ptr:指向termios结构的指针
4.属性描述:
①:c_iflag:
IGNBRK:忽略输入中的 BREAK 状态。
BRKINT:如果设置了 IGNBRK,将忽略 BREAK。如果没有设置,但是设置了 BRKINT,那么 BREAK 将使得输入和输出队列被刷新,如果终端是一个前台进程组的控制终端,这个进程组中所有进程将收到 SIGINT 信号。如果既未设置IGNBRK 也未设置 BRKINT,BREAK 将视为与 NUL 字符同义,除非设置了 PARMRK,这种情况下它被视为序列 /377 /0 /0。
IGNPAR:忽略桢错误和奇偶校验错。
PARMRK:如果没有设置 IGNPAR,在有奇偶校验错或桢错误的字符前插入 /377 /0。如果既没有设置 IGNPAR 也没有设置PARMRK,将有奇偶校验错或桢错误的字符视为 /0。
INPCK:启用输入奇偶检测。
ISTRIP:去掉第八位。
INLCR:将输入中的 NL 翻译为 CR。
IGNCR:忽略输入中的回车。
ICRNL:将输入中的回车翻译为新行 (除非设置了 IGNCR)。
IUCLC:(不属于 POSIX) 将输入中的大写字母映射为小写字母。
IXON:启用输出的 XON/XOFF 流控制。
IXANY:(不属于 POSIX.1;XSI) 允许任何字符来重新开始输出。(?)
IXOFF:启用输入的 XON/XOFF 流控制。
IMAXBEL:(不属于 POSIX) 当输入队列满时响零。Linux 没有实现这一位,总是将它视为已设置。
②c_oflag:
OPOST:启用具体实现自行定义的输出处理。其余 c_oflag 标志常量定义在 POSIX 1003.1-2001 中,除非另外说明。
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:
CBAUD:(不属于 POSIX) 波特率掩码 (4+1 位)。
CBAUDEX:(不属于 POSIX) 扩展的波特率掩码 (1 位),包含在 CBAUD 中。
(POSIX 规定波特率存储在 termios 结构中,并未精确指定它的位置,而是提供了函数 cfgetispeed() 和 cfsetispeed() 来存取它。一些系统使用 c_cflag 中 CBAUD 选择的位,其他系统使用单独的变量,例如 sg_ispeed 和 sg_ospeed 。)
CSIZE:字符长度掩码。取值为 CS5, 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:
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 信号时刷新输入和输出队列。
TOSTOP:向试图写控制终端的后台进程组发送 SIGTTOU 信号。
PENDIN:(不属于 POSIX; Linux 下不被支持) 在读入下一个字符时,输入队列中所有字符被重新输出。(bash 用它来处理 typeahead)
IEXTEN:启用实现自定义的输入处理。这个标志必须与 ICANON 同时使用,才能解释特殊字符 EOL2,LNEXT,REPRINT 和 WERASE,IUCLC 标志才有效。
⑤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 模式读的最小字符数。
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).
⑥:VIME 和VMIN需要配合使用,关系如下:
1、VTIME=0,VMIN=0:此时即使读取不到任何数据,函数read也会返回,返回值是0。
2、VTIME=0,VMIN>0:read调用一直阻塞,直到读到VMIN个字符后立即返回。
3、VTIME>0,VMIN=0:read调用读到数据则立即返回,否则将为每个字符最多等待 VTIME*100ms 时间。
4、VTIME>0,VMIN>0:read调用将保持阻塞直到读取到第一个字符,读到了第一个字符之后开始计时,此后若时间到了 VTIME*100ms 或者时间未到但已读够了VMIN个字符则会返回。若在时间未到之前又读到了一个字符(但此时读到的总数仍不够VMIN)则计时重新开始(即每个字符都有VTIME*100ms的超时时间)。
代码例程:
①设置波特率:
void set_baudrate(struct termios *set_serial, unsigned long int baud_rate)
{int baud = B115200;switch(baud_rate){case 2400:baud = B2400;break;case 4800:baud = B4800;break;case 9600:baud = B9600;break;case 19200:baud = B19200;break;case 38400:baud = B38400;break;case 57600:baud = B57600;break;case 115200:baud = B115200;break;default:baud = B115200;break;}cfsetispeed(set_serial, baud);cfsetospeed(set_serial, baud);
}
②设置数据位:
void set_databits(struct termios *set_serial, unsigned int data_bits)
{switch(data_bits){case 5:set_serial->c_cflag |= CS5;break;case 6:set_serial->c_cflag |= CS6;break;case 7:set_serial->c_cflag |= CS7;break;case 8:set_serial->c_cflag |= CS8;break;default:set_serial->c_cflag |= CS8;break;}
}
③设置校验位:
void set_parity(struct termios *set_serial, char parity)
{switch(parity){case 'N':set_serial->c_cflag &= ~PARENB; //no parity checkbreak;case 'O':set_serial->c_cflag |= PARENB; //odd checkset_serial->c_cflag &= ~PARODD;break;case 'E':set_serial->c_cflag |= PARENB; //even checkset_serial->c_cflag |= PARODD;break;default:set_serial->c_cflag &= ~PARENB;break;}
}
④停止位:
void set_stopbits(struct termios *set_serial, unsigned int stop_bits)
{if(stop_bits == 2){set_serial->c_cflag |= CSTOPB; //2 stop bits}else{set_serial->c_cflag &= ~CSTOPB; //1 stop bits}
}
⑤串口配置函数:
void set_option(unsigned int baud_rate, unsigned int data_bits, char parity, unsigned int stop_bits)
{struct termios opts;tcgetattr(m_dev, &opts);set_baudrate(&opts, baud_rate);opts.c_cflag |= CLOCAL|CREAD;set_parity(&opts, parity);set_stopbits(&opts, stop_bits);set_databits(&opts, data_bits);opts.c_cflag &= ~CRTSCTS;opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //raw inputopts.c_oflag &= ~OPOST; // raw outputopts.c_cc[VTIME]=1;opts.c_cc[VMIN]=1023;tcsetattr(m_dev, TCSANOW, &opts);
}
⑥发送数据:
int send_data(int fd, const char *data, int datalen)
{int len = 0;len = write(m_dev, data, datalen);if(len == datalen){return 0;}else{tcflush(m_dev, TCOFLUSH);return -1;}
}
7接收函数:
int receive(int fd, char *data, int datalen)
{int read_len;if((read_len = read(m_dev, data, datalen))>0){return read_len;}else{return -1;}
}
⑧主函数:
int main( int argc, char *argv[])
{int fd;int ret;char buff[1024];char senddata[] = "uart";fd= open("/dev/ttyS2", O_RDWR | O_NOCTTY | O_NONBLOCK);if(fd <= 0){printf("uart open fail\n");return -1;}fcntl(fd, F_SETFL, 0);set_option(115200, 8, 'N', 1);while (1){ret = receive(fd, buff, sizeof(buff));buff[ret] = 0;printf("receive data : %s\n", buff);send_data(fd, senddata, sizeof(senddata));}close(fd);return 0;
}
完整代码:https://download.csdn.net/download/jiafanluo/11231218
linux uart应用开发(ttyS*设备)相关推荐
- linux的驱动开发——字符设备驱动
1.字符设备驱动 \qquad字符设备驱动是最基本,最常用的设备.它将千差万别的硬件设备采用统一的接口封装起来,屏蔽了硬件的差异,简化了应用层的操作. 2.描述所有字符设备的结构体 \qquad描述所 ...
- Linux嵌入式驱动开发零基础入门集合(STM32过渡到Linux嵌入式)
Linux嵌入式驱动开发01--第一个驱动Hello World(附源码) Linux嵌入式驱动开发02--驱动编译到内核 Linux嵌入式驱动开发03--杂项设备驱动(附源码) Linux嵌入式驱动 ...
- Linux嵌入式驱动开发13——ioctl接口(gpio控制使用)
文章目录 全系列传送门 引言 什么是unlocked_ioctl接口? unlocked_ioctl和read/write函数有什么相同和不同 unlocked_ioctl接口命令规则 命令的合成宏与 ...
- Linux嵌入式驱动开发07——GPIO驱动过程记录(飞凌开发板)
文章目录 全系列传送门 1. 在/arch/arm/boot/dts/imx6q-pinfunc.h查找 2. 在设备树配置文件中添加设备节点定义以及其引脚定义 3. 修改设备树文件添加配置 4. d ...
- Linux嵌入式驱动开发02——驱动编译到内核
文章目录 全系列传送门 make menuconfig图形化配置界面 1. 怎么进入到make menuconfig图形化界面? 2. make menuconfig图形化界面的操作 3. 退出 4. ...
- Linux嵌入式驱动开发01——第一个驱动Hello World(附源码)
文章目录 全系列传送门 引言 驱动介绍 Hello World 1. 包含头文件 2. 驱动模块的入口和出口 3. 声明信息 4. 功能实现 完整代码 编译 第一种方法 第二种方法 编译成模块 第一步 ...
- 【Linux驱动开发】设备树详解(二)设备树语法详解
活动地址:CSDN21天学习挑战赛 [Linux驱动开发]设备树详解(一)设备树基础介绍 [Linux驱动开发]设备树详解(二)设备树语法详解 [Linux驱动开发]设备树详解(三)设备树Kern ...
- linux下echo /dev/ttys* 到字符设备文件,linux之tty pty pts
一.基本概念: 1> tty(终端设备的统称): tty一词源于Teletypes,或者teletypewriters,原来指的是电传打字机,是通过串行线用打印机键盘通过阅读和发送信息的东西,后 ...
- <Linux开发>--驱动开发-- 字符设备驱动(3) 过程详细记录
<Linux开发>–驱动开发-- 字符设备驱动(3) 过程详细记录 驱动开发是建立再系统之上的,前面作者也记录了系统移植的过程记录,如果有兴趣,可进入博主的主页查看相关文章,这里就不添加链 ...
最新文章
- android自定义差值器,如何创建自定义插值器以在android中应用翻译动画
- Erlang 之父去世,他留给程序员两点忠告
- 超适合新手的基础Linux命令
- ODS(Operational Data Store)定义
- 《北大学科》第一季:数学篇
- “五一”档总票房破13亿 《你的婚礼》高居榜首
- android 拖动进度,Android 可拖动的seekbar自定义进度值
- python编程入门必备知识-Python快速编程入门,打牢基础必须知道的11个知识点
- 光流(八)--总结篇
- Codeforces Round #568 (Div. 2)A
- 【解决】速达服务启动失败,文件无效
- 摩托罗拉为什么要限制自家linux手机,很明显,这是一款配备Linux系统的智能手机,但摩托罗拉将其变成了功能机...
- ICA算法的数学原理
- 后缀树系列三:后缀树的应用
- 工业大数据特征有哪些 大数据工程师来告诉你
- 蓝桥杯模拟赛第二场(web)
- YUV图解 (YUV444, YUV422, YUV420, YV12, NV12, NV21)
- Echarts图表中设置背景图片
- 作为程序员的我,常用的工具软件有这些
- java的三大体系分别是什么