写这篇文章的原因:因为在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:回车延时掩码。取值为 CR0CR1CR2, 或 CR3

TABDLY:水平跳格延时掩码。取值为 TAB0TAB1TAB2TAB3 (或 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*设备)相关推荐

  1. linux的驱动开发——字符设备驱动

    1.字符设备驱动 \qquad字符设备驱动是最基本,最常用的设备.它将千差万别的硬件设备采用统一的接口封装起来,屏蔽了硬件的差异,简化了应用层的操作. 2.描述所有字符设备的结构体 \qquad描述所 ...

  2. Linux嵌入式驱动开发零基础入门集合(STM32过渡到Linux嵌入式)

    Linux嵌入式驱动开发01--第一个驱动Hello World(附源码) Linux嵌入式驱动开发02--驱动编译到内核 Linux嵌入式驱动开发03--杂项设备驱动(附源码) Linux嵌入式驱动 ...

  3. Linux嵌入式驱动开发13——ioctl接口(gpio控制使用)

    文章目录 全系列传送门 引言 什么是unlocked_ioctl接口? unlocked_ioctl和read/write函数有什么相同和不同 unlocked_ioctl接口命令规则 命令的合成宏与 ...

  4. Linux嵌入式驱动开发07——GPIO驱动过程记录(飞凌开发板)

    文章目录 全系列传送门 1. 在/arch/arm/boot/dts/imx6q-pinfunc.h查找 2. 在设备树配置文件中添加设备节点定义以及其引脚定义 3. 修改设备树文件添加配置 4. d ...

  5. Linux嵌入式驱动开发02——驱动编译到内核

    文章目录 全系列传送门 make menuconfig图形化配置界面 1. 怎么进入到make menuconfig图形化界面? 2. make menuconfig图形化界面的操作 3. 退出 4. ...

  6. Linux嵌入式驱动开发01——第一个驱动Hello World(附源码)

    文章目录 全系列传送门 引言 驱动介绍 Hello World 1. 包含头文件 2. 驱动模块的入口和出口 3. 声明信息 4. 功能实现 完整代码 编译 第一种方法 第二种方法 编译成模块 第一步 ...

  7. 【Linux驱动开发】设备树详解(二)设备树语法详解

    ​ 活动地址:CSDN21天学习挑战赛 [Linux驱动开发]设备树详解(一)设备树基础介绍 [Linux驱动开发]设备树详解(二)设备树语法详解 [Linux驱动开发]设备树详解(三)设备树Kern ...

  8. linux下echo /dev/ttys* 到字符设备文件,linux之tty pty pts

    一.基本概念: 1> tty(终端设备的统称): tty一词源于Teletypes,或者teletypewriters,原来指的是电传打字机,是通过串行线用打印机键盘通过阅读和发送信息的东西,后 ...

  9. <Linux开发>--驱动开发-- 字符设备驱动(3) 过程详细记录

    <Linux开发>–驱动开发-- 字符设备驱动(3) 过程详细记录 驱动开发是建立再系统之上的,前面作者也记录了系统移植的过程记录,如果有兴趣,可进入博主的主页查看相关文章,这里就不添加链 ...

最新文章

  1. android自定义差值器,如何创建自定义插值器以在android中应用翻译动画
  2. Erlang 之父去世,他留给程序员两点忠告
  3. 超适合新手的基础Linux命令
  4. ODS(Operational Data Store)定义
  5. 《北大学科》第一季:数学篇
  6. “五一”档总票房破13亿 《你的婚礼》高居榜首
  7. android 拖动进度,Android 可拖动的seekbar自定义进度值
  8. python编程入门必备知识-Python快速编程入门,打牢基础必须知道的11个知识点
  9. 光流(八)--总结篇
  10. Codeforces Round #568 (Div. 2)A
  11. 【解决】速达服务启动失败,文件无效
  12. 摩托罗拉为什么要限制自家linux手机,很明显,这是一款配备Linux系统的智能手机,但摩托罗拉将其变成了功能机...
  13. ICA算法的数学原理
  14. 后缀树系列三:后缀树的应用
  15. 工业大数据特征有哪些 大数据工程师来告诉你
  16. 蓝桥杯模拟赛第二场(web)
  17. YUV图解 (YUV444, YUV422, YUV420, YV12, NV12, NV21)
  18. Echarts图表中设置背景图片
  19. 作为程序员的我,常用的工具软件有这些
  20. java的三大体系分别是什么

热门文章

  1. 高清青龙壁纸 桌面壁纸
  2. 整理2004-2018年地级市进出口贸易总额数据
  3. tkinter中分割线Separator、frame控件pack_propagate(0)、frame滚动条的用法
  4. Dubbo学习之DubboService
  5. 牛客:爬塔(STL)
  6. 西安电子科技大学计算机复试题目,西安电子科技大学考研复试 微机原理练习题...
  7. ppt如何查看加载宏
  8. 自学前端第一天:认识前端工程与网页
  9. 水果店快开业怎么发朋友圈,水果店开业怎么发朋友圈图片
  10. 华为mate20 pro 专业模式拍照