Linux 串口编程四 串口设备程序开发

Linux 串口编程和程序相对来说是很简单的,之所以用博客连载来展示,主要是想在学会使用的基础上掌握相关背景,原理以及注意事项。相信在遇到问题的时候,我们就不会对于技术的概念和 API 的使用浅尝辄止了。下面进入具体应用案例,由于现在很多电脑已经没有引出串口以及波特率范围会受到限制,这里我以 CH340 USB 转串口芯片制作的模块为基础讲解串口应用程序开发,关于该芯片在 Linux 系统的使用以及驱动加载可以参考:CH340 Linux驱动使用教程。

设备的打开与关闭

1. int libtty_open(const char *devname);

函数功能:根据传入的串口设备名打开相应的设备。成功返回设备句柄,失败返回-1。

2. int libtty_close(int fd);

函数功能:关闭打开的设备句柄。成功返回0,失败返回负值。

设备的配置

1. int libtty_setopt(int fd, int speed, char databits, char stopbits, char parity);

函数功能:配置串口设备,传入参数依次为波特率设置、数据位设置、停止位设置、检验设置。

Notes:设备打开前,可以通过 ls /dev 确认自己的硬件设备名,对于 USB 转串口 IC,在系统下名称为 "ttyUSBx",设备序号是根据插入主机的先后顺序自动分配的,这里我的为 "ttyUSB0",读者根据自己的需要修改。

/*** libtty_open - open tty device* @devname: the device name to open** In this demo device is opened blocked, you could modify it at will.*/
int libtty_open(const char *devname)
{int fd = open(devname, O_RDWR | O_NOCTTY | O_NDELAY); int flags = 0;if (fd == -1) {                        perror("open device failed");return -1;            }flags = fcntl(fd, F_GETFL, 0);flags &= ~O_NONBLOCK;if (fcntl(fd, F_SETFL, flags) < 0) {printf("fcntl failed.\n");return -1;}if (isatty(fd) == 0){printf("not tty device.\n");return -1;}elseprintf("tty device test ok.\n");return fd;
}

Note:
  • 传入的 devname 参数为设备绝对路径;
  • O_NOCTTY标志用于通知系统,这个程序不会成为对应这个设备的控制终端。如果没有指定这个标志,那么任何一个输入(如SIGINT等)都将会影响用户的进程;
  • O_NDELAY标志与O_NONBLOCK 等效,但这里不仅仅是设置为非阻塞,还用于通知系统,这个程序不关心 DCD 信号线所处的状态(即与设备相连的另一端是否激活或者停止)。如果用户指定了这一标志,则进程将会一直处在休眠状态,直到 DCD 信号线被激活;
  • 使用 fcntl 函数恢复设备状态为阻塞状态,在数据收发时就会进行等待;
  • 使用 isatty函数测试当前打开的设备句柄是否关联到终端设备,如果不是 tty 设备,那么也返回出错;
/*** libtty_setopt - config tty device* @fd: device handle* @speed: baud rate to set* @databits: data bits to set* @stopbits: stop bits to set* @parity: parity set** The function return 0 if success, or -1 if fail.*/
int libtty_setopt(int fd, int speed, int databits, int stopbits, char parity)
{struct termios newtio;struct termios oldtio;int i;bzero(&newtio, sizeof(newtio));bzero(&oldtio, sizeof(oldtio));if (tcgetattr(fd, &oldtio) != 0) {perror("tcgetattr");    return -1; }newtio.c_cflag |= CLOCAL | CREAD;newtio.c_cflag &= ~CSIZE;/* set tty speed */for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {if (speed == name_arr[i]) {      cfsetispeed(&newtio, speed_arr[i]); cfsetospeed(&newtio, speed_arr[i]);   } }/* set data bits */switch (databits) {case 5:                newtio.c_cflag |= CS5;break;case 6:                newtio.c_cflag |= CS6;break;case 7:                newtio.c_cflag |= CS7;break;case 8:    newtio.c_cflag |= CS8;break;  default:   fprintf(stderr, "unsupported data size\n");return -1; }/* set parity */switch (parity) {  case 'n':case 'N':newtio.c_cflag &= ~PARENB;    /* Clear parity enable */newtio.c_iflag &= ~INPCK;     /* Disable input parity check */break; case 'o':  case 'O':    newtio.c_cflag |= (PARODD | PARENB); /* Odd parity instead of even */newtio.c_iflag |= INPCK;     /* Enable input parity check */break; case 'e': case 'E':  newtio.c_cflag |= PARENB;    /* Enable parity */   newtio.c_cflag &= ~PARODD;   /* Even parity instead of odd */  newtio.c_iflag |= INPCK;     /* Enable input parity check */break;default:  fprintf(stderr, "unsupported parity\n");return -1; } /* set stop bits */ switch (stopbits) {  case 1:   newtio.c_cflag &= ~CSTOPB; break;case 2:   newtio.c_cflag |= CSTOPB; break;default:   perror("unsupported stop bits\n"); return -1;}newtio.c_cc[VTIME] = 0;	  /* Time-out value (tenths of a second) [!ICANON]. */newtio.c_cc[VMIN] = 0;    /* Minimum number of bytes read at once [!ICANON]. */tcflush(fd, TCIOFLUSH);  if (tcsetattr(fd, TCSANOW, &newtio) != 0)  {perror("tcsetattr");return -1;}return 0;
}
Note:
  • 首先保存了原先串口配置,为了方便演示,这里保存为局部变量,实际使用时是需要把配置保存到全局 termios 结构体中的。使用tcgetattr还可以测试配置是否正确、串口是否可用等。返回值参见 man 手册;
  • 使用 CLOCAL 用于忽略所有 MODEM 状态信号线,CREAD 标志用于使能接收。CSIZE 为数据位掩码;
  • 调用 cfsetispeedcfsetospeed参数设置波特率,函数中引用了两个数组,在后面的完整代码中会看到,这样书写可以简化设置代码;
  • 通过控制 c_cflag 与 c_iflag 配置串口数据位、停止位以及校验设置等;
  • VTIMEVMIN作用已经讲解多次,不再赘述,值得注意的是,TIME 值的单位是十分之一秒
  • 通过 tcflush清空输入和输出缓冲区,根据实际需要修改;
  • 最后通过 tcsetattr 函数对将配置实际作用于串口;
数据读写直接使用 readwrite 函数接口就可以了,因此没有列举出来。下面给出完整的串口读写测试代码:
/* TTY testing utility (using tty driver)* Copyright (c) 2017* This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License.** Cross-compile with cross-gcc -I /path/to/cross-kernel/include*/#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>  
#include <termios.h>  
#include <errno.h>   
#include <string.h>
#include <sys/types.h> 
#include <sys/stat.h>  int speed_arr[] = {B115200,B57600,B38400,B19200,B9600,B4800,B2400,B1200,B300
};int name_arr[] = {115200,57600,38400,19200,9600,4800,2400,1200,300
};/*** libtty_setopt - config tty device* @fd: device handle* @speed: baud rate to set* @databits: data bits to set* @stopbits: stop bits to set* @parity: parity set** The function return 0 if success, or -1 if fail.*/
int libtty_setopt(int fd, int speed, int databits, int stopbits, char parity)
{struct termios newtio;struct termios oldtio;int i;bzero(&newtio, sizeof(newtio));bzero(&oldtio, sizeof(oldtio));if (tcgetattr(fd, &oldtio) != 0) {perror("tcgetattr");    return -1; }newtio.c_cflag |= CLOCAL | CREAD;newtio.c_cflag &= ~CSIZE;/* set tty speed */for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {if (speed == name_arr[i]) {      cfsetispeed(&newtio, speed_arr[i]); cfsetospeed(&newtio, speed_arr[i]);   } }/* set data bits */switch (databits) {case 5:                newtio.c_cflag |= CS5;break;case 6:                newtio.c_cflag |= CS6;break;case 7:                newtio.c_cflag |= CS7;break;case 8:    newtio.c_cflag |= CS8;break;  default:   fprintf(stderr, "unsupported data size\n");return -1; }/* set parity */switch (parity) {  case 'n':case 'N':newtio.c_cflag &= ~PARENB;    /* Clear parity enable */newtio.c_iflag &= ~INPCK;     /* Disable input parity check */break; case 'o':  case 'O':    newtio.c_cflag |= (PARODD | PARENB); /* Odd parity instead of even */newtio.c_iflag |= INPCK;     /* Enable input parity check */break; case 'e': case 'E':  newtio.c_cflag |= PARENB;    /* Enable parity */   newtio.c_cflag &= ~PARODD;   /* Even parity instead of odd */  newtio.c_iflag |= INPCK;     /* Enable input parity check */break;default:  fprintf(stderr, "unsupported parity\n");return -1; } /* set stop bits */ switch (stopbits) {  case 1:   newtio.c_cflag &= ~CSTOPB; break;case 2:   newtio.c_cflag |= CSTOPB; break;default:   perror("unsupported stop bits\n"); return -1;}newtio.c_cc[VTIME] = 0;	  /* Time-out value (tenths of a second) [!ICANON]. */newtio.c_cc[VMIN] = 0;    /* Minimum number of bytes read at once [!ICANON]. */tcflush(fd, TCIOFLUSH);  if (tcsetattr(fd, TCSANOW, &newtio) != 0)  {perror("tcsetattr");return -1;}return 0;
}/*** libtty_open - open tty device* @devname: the device name to open** In this demo device is opened blocked, you could modify it at will.*/
int libtty_open(const char *devname)
{int fd = open(devname, O_RDWR | O_NOCTTY | O_NDELAY); int flags = 0;if (fd == -1) {                        perror("open device failed");return -1;            }flags = fcntl(fd, F_GETFL, 0);flags &= ~O_NONBLOCK;if (fcntl(fd, F_SETFL, flags) < 0) {printf("fcntl failed.\n");return -1;}if (isatty(fd) == 0){printf("not tty device.\n");return -1;}elseprintf("tty device test ok.\n");return fd;
}/*** libtty_close - close tty device* @fd: the device handle**/
int libtty_close(int fd)
{return close(fd);
}void tty_test(int fd)
{int nwrite, nread;char buf[100];memset(buf, 0x32, sizeof(buf));while (1) {nwrite = write(fd, buf, sizeof(buf));printf("wrote %d bytes already.\n", nwrite);nread = read(fd, buf, sizeof(buf));printf("read %d bytes already.\n", nread);sleep(2);}}int main(int argc, char *argv[])
{int fd;int ret;fd = libtty_open("/dev/ttyUSB0");if (fd < 0) {printf("libtty_open error.\n");exit(0);}ret = libtty_setopt(fd, 9600, 8, 1, 'n');if (ret != 0) {printf("libtty_setopt error.\n");exit(0);}tty_test(fd);ret = libtty_close(fd);if (ret != 0) {printf("libtty_close error.\n");exit(0);}
}
执行成功的话,会在终端屏幕上看到每隔两秒输出串口成功发送和接收的字节数,测试时可以直接短接串口的发送和接收引脚进行测试。以下为成功测试截图:


关于 Linux 串口编程的其他文章,可以移步至以下链接:

  1. 《Linux 串口编程<一> 一些背景》
  2. 《Linux 串口编程<二> 深入了解 termios》
  3. 《Linux 串口编程<三> 使用termios与API 进行串口程序开发》
  4. 《Linux 串口编程<四> 串口设备程序开发》

有疑问的读者可以给我邮件或者评论,觉得对你有帮助就点赞吧~:-D


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/403069.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

POSIX 串口编程指南

介绍 POSIX 串口编程指南将教会你在你的 UNIX 工作站或者 PC 上面如何成功、有效以及可移植性的对串口编程。每一章提供了使用 POSIX 终端控制函数的编程例程&#xff0c;可以基本不经修改地工作在 IRIX, HP-UX, SunOS, Solaris, Digital UNIX, Linux 以及其他大多数 UNIX 操作…

快速解决 Android SDK Manager 无法下载或者下载速度慢

在这里以 Windows 下的 Android SDK Manager 为例&#xff0c;其他系统下与此类似&#xff0c;只会存在部分工具栏名称不同的情况&#xff0c;不明之处可以追问。下面就进入具体配置流程&#xff1a; 1. 选择 Tools->Options 进入代理设置。 代理设置选项&#xff0c;在 H…

安卓之USB主机(Host)与配件(Accessory)模式

安卓设备与USB硬件通讯时有两种模式可以选择&#xff1a;USB Host 模式与 USB Accessory 模式。从 USB 逻辑角色来说&#xff0c;USB Host 模式是指安卓设备作为 USB 主机&#xff0c;所有活动均由安卓设备发起&#xff1b;USB Accessory 模式是指安卓设备作为 USB 设备&#x…

安卓USB开发教程 一 USB Host 与 Accessory

安卓通过两种模式&#xff1a;USB Accessory 与 USB Host 模式支持多种 USB 外设与安卓 USB 配件&#xff08;实现安卓配件协议的硬件&#xff09;。在 USB 配件模式下&#xff0c;外部 USB 硬件充当 USB 主机。配件实例可能包含机器人控制器、扩展坞、诊断和音乐设备、售货亭、…

轻松访问 Android 系统源码与下载

有时研究 Android 某个特性或者协议的时候需要参阅安卓系统源代码中代码实现或者协议文档等。通过正常的建立 repo&#xff0c;git 获取十分耗时&#xff0c;并且速度很慢&#xff0c;除非是需要重新编译系统&#xff0c;定制系统才需要这样做。因此&#xff0c;推荐一个 Andro…

Cygwin 下载极速源推荐

Cygwin 默认列表中的源下载速度太慢&#xff0c;国内使用时常用以下几个源&#xff0c;经过实际使用速度很快&#xff0c;下载时不妨几者都尝试下&#xff1a; 如上图所示&#xff0c;下载时在此栏填写 URL 地址即可&#xff0c;推荐地址&#xff1a; http://mirrors.sohu.com…

Java 结构体之 JavaStruct 使用教程二 JavaStruct 用例分析

使用环境 前一篇在介绍 JavaStruct 类时指定了使用库使用环境为 Java 5 及以上&#xff0c;也即开发我们使用的 JDK 版本为1.5及以上就可以了。以下讲解的用例可以直接将 code 直接粘贴到 java 的 main 函数中执行就可以了&#xff0c;后面会给出测试用例和结果。 使用方法 Jav…

Java 结构体之 JavaStruct 使用教程三 JavaStruct 数组进阶

经过前面两篇博客的介绍&#xff0c;相信对于 JavaStruct 的认识以及编程使用&#xff0c;读者已经有一定的基础了。只要理解和实践结合起来&#xff0c;掌握还是很容易的。下面进行一些数组使用方面的实例说明及演示。 在结构体类中使用数组有几种方式&#xff0c;可以使用静…

Android开发如何使用JNA

1. JNA&#xff08;Java Native Access&#xff09;项目已经迁移到 github&#xff0c;最新的项目链接&#xff1a;https://github.com/java-native-access/jna 。首先前往该地址下载使用 JNA 需要的两个 jar 库文件&#xff0c;jna.jar&#xff0c;jna-platform.jar 。 2. 在…

JAVA循环队列

关于自定义循环队列的实现原理和要点可以参见之前的博文系列&#xff1a;循环队列及C语言实现。这里主要对JAVA下的具体实现方式与原理进行说明。 一、JAVA 中已经自带了 Queue、DQueue、ArrayList、LinkedList 等常用的数据结构&#xff0c;为什么还要单独实现循环队列&#…

VMware 虚拟机占用磁盘空间

使用VMware创建的虚拟机尽管已经设定分配的磁盘大小&#xff0c;但仍然会发现虚拟机占用的磁盘空间会越来越大&#xff0c;而直观体现就是虚拟机系统文件 vmdk 不断增大。因此下面介绍一个简单的方法&#xff0c;使用 VMware 自带的工具对 vmdk 文件进行压缩以节省磁盘空间。拿…

frameworks/av/media/CedarX-Projects/CedarAndroidLib/LIB_KK44_/Android.mk: No such file or directory

在安卓系统编译过程中如果遇到上述或者与之类似的错误&#xff0c;可以采取相同的处理方法进行解决。直接进入到 CedarAndroidLib 目录下&#xff0c;也即此例中的 frameworks/av/media/CedarX-Projects/CedarAndroidLib。看一下当前文件&#xff1a; 注意第9行为包含标题中报错…

Windows与Linux下tftp服务的使用

tftp 协议是基于 udp 的&#xff0c;轻量小巧&#xff0c;用在局域网和嵌入式上很顺手。大部分帖子把在 linux 上配置的过程描述的过于复杂&#xff0c;其实只是个工具而已。研究协议抓下包对比协议内容也可以满足需求了&#xff0c;下面进入正文。分别讲下在 linux 以及 windo…

饥荒Mod 开发(二二):显示物品信息

饥荒Mod 开发(二一)&#xff1a;超大便携背包&#xff0c;超大物品栏&#xff0c;永久保鲜 饥荒中的物品没有详细信息&#xff0c;基本上只有一个名字&#xff0c;所以很多物品的功能都不知道&#xff0c;比如浆果吃了也不知道恢复什么&#xff0c; 采集的胡萝卜也不知道什么功…

安卓BLE开发教程(一) BLE基础

我试图以一种简单的方式去把重要的事情讲清楚。目的是希望BLE协议栈和基础概念简单化&#xff0c;让自己及类似的安卓开发者可以在较短的时间内把握住BLE的核心及使用方法。BLE本身很复杂&#xff0c;但对于安卓开发而言只要抓住一些核心点&#xff0c;便已足够。如果你想全面了…

Linux驱动如何在不同版本上快速迭代升级

As well known&#xff0c;Linux内核版本更新很快&#xff0c;有些内核版本的迭代升级可能会导致在使用的驱动版本存在编译失败或使用的兼容性问题&#xff0c;如何快速定位到内核版本间变更的地方&#xff0c;并处理掉该问题&#xff0c;列一下我常用的解决方法。&#xff08;…

苹果MacOS系统上安装第三方驱动失败/无效

近期不少用户在苹果系统上安装一些第三方驱动时反馈没有作用&#xff0c;但是驱动安装提示是完成的&#xff0c;并拷贝到了系统的驱动路径下&#xff1b;造成该问题的原因可参见如下苹果官方说明&#xff1a; User-Approved Kernel Extension Loading 引用下第一段官网说明 …

OpenWrt 之 MT7628 移植第三方SPI驱动

1、在OpenWrt系统上移植SPI驱动前&#xff0c;首先要确保SPI相关引脚未被复用为其他功能&#xff0c;比如GPIO&#xff1b;以下操作已假定该条件成立&#xff0c;否则请修改相关dts和c文件中复用配置&#xff1b; 2、打开dts配置文件进行修改&#xff0c;这里我是用的硬件为WR…

CH9102 USB转串口应用体验

近期使用CH9102 USB转串口芯片成功用在原有使用CP2102的产品板上&#xff0c;整个替换和验证过程还是很顺利的&#xff0c;顺带写个blog做个记录。 原项目上使用CP2102搭载ESP32实现Arduino物联网应用&#xff0c;采用USB转串口芯片实现串口下载&#xff0c;代码的Debug调试&am…

CH9101 USB转串口替换FT232R和FT230XQ

学生党一枚&#xff0c;前段时间跟着导师做的项目因为上面用到USB转串口芯片FT232R迟迟买不到&#xff0c;所以打算更换成国产USB转串口芯片CH340&#xff0c;对CH340的认识也很早了&#xff0c;很多年前开始直到现在各种开发板上基本都会标配一颗CH340&#xff0c;像某宝上的S…