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 操作…

串口流控

流控作用 这里讲到的“流”&#xff0c;指的是数据流。数据在两个串口之间传输时&#xff0c;当通讯双方速率不匹配时&#xff0c;常常会出现丢失数据的现象。如台式机与单片机之间的通讯&#xff0c;若接收端数据缓冲区已满&#xff0c;继续发送来的数据就会丢失。在早期网络…

电容的分类及作用

旁路 旁路电容是为本地器件提供能量的储能器件&#xff0c;它能使稳压器的输出均匀化&#xff0c;降低负载需求。就像小型可充电电池一样&#xff0c;旁路电容能够被充电&#xff0c;并向器件进行放电。为尽量减少阻抗&#xff0c;旁路电容要尽量靠近负载器件的供电电源管脚和…

快速解决 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 主机。配件实例可能包含机器人控制器、扩展坞、诊断和音乐设备、售货亭、…

安卓USB开发教程 二 USB Host

USB Host&#xff08;主机模式&#xff09; 当 Android 设备处于 USB 主机模式时&#xff0c;它充当 USB 主机&#xff0c;为总线供电&#xff0c;并枚举连接的 USB 设备。Android 3.1 及更高版本支持 USB 主机模式。 API 概述 在开始之前&#xff0c;理解需要使用的类是很重…

安卓USB开发教程 三 USB Accessory

USB Accessory&#xff08;配件模式&#xff09; USB 配件模式允许用户连接专为 Android 设备设计的 USB 主机硬件。配件必须遵守 Android Accessory Development Kit 文档中列出的 Android 配件协议。 这使得 Android 设备无法充当 USB 主机时仍然可以与 USB 硬件交互。 当 A…

Android ADB

Android 调试桥 本文内容 adb 的工作方式在您的设备上启用 adb 调试通过 WLAN 连接到设备查询设备将命令发送至特定设备安装应用设置端口转发将文件复制到设备/从设备复制文件停止 adb 服务器adb 命令参考发出 shell 命令 调用 Activity Manager (am)调用软件包管理器 (pm)进行…

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

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

安卓系统源代码下载(官方教程)

下载源代码 Android 源代码树位于由 Google 托管的 Git 代码库中。Git 代码库中包含 Android 源代码的元数据&#xff0c;其中包括与对源代码进行的更改以及更改日期相关的元数据。本文档介绍了如何下载特定 Android 代码流水线的源代码树。 要从特定设备的出厂映像开始&#x…

Cygwin 下载极速源推荐

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

安卓USB开发教程 四 安卓 AOA

Android 开放性配件协议&#xff08;AOA&#xff09; Android 开放性配件协议&#xff08;AOA&#xff09;支持允许外部 USB 硬件&#xff08;Android USB 配件&#xff09;与工作在配件模式下的 Android 设备进行交互。当处于配件模式的 Android 设备被供电&#xff0c;所连接…

安卓USB开发教程 五 安卓 AOA 1.0

Android Open Accessory Protocol 1.0&#xff08;AOA 协议 1.0&#xff09; Android USB 配件必须遵从 Android Open Accessory&#xff08;AOA&#xff09;协议&#xff0c;该协议定义了配件如何检测和建立与 Android 设备的通信。配件应执行以下步骤&#xff1a; 1. 等待并…

安卓USB开发教程 六 安卓 AOA 2.0

Android Open Accessory Protocol 2.0 目录 Detecting AOAv2 support Audio support HID support Interoperability with AOAv1 Connecting AOAv2 without an Android app This document describes changes in the Android Open Accessory (AOA) protocol since its initial re…

Linux 驱动编译报错:error: macro __DATE__ might prevent reproducible builds [-Werror=date-time]

编译驱动时遇到这个错误提示&#xff0c;表示当前编译环境中将关于 DATE 以及 TIME 的警告也作为错误来进行处理的。有如下几种方法可以参考&#xff1a; 1. 在编译驱动的相应 Makefile 中增加一行&#xff1a;CFLAGS -Wno-errordate-time&#xff0c;然后保存重新 make&…

Java 结构体之 JavaStruct 使用教程一 初识 JavaStruct

Javastruct 是什么 简而言之&#xff0c;Javastruct 是一个第三方库&#xff0c;用于像处理 C 或者 C 结构体那样处理 java 对象。也即利用 Javastruct 可以在 java 上实现类似于结构体的功能和操作。 Javastruct 的用途 在 java 或者 Android 应用程序与一些嵌入式设备通讯…

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. 在…