Linux串口编程

这篇具有很好参考价值的文章主要介绍了Linux串口编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

本篇文章将讲解如何在Linux下使用串口。

一、Linux下的TTY体系介绍

在Linux系统中,tty(Teletypewriter)是指一种终端设备,它提供了用户与操作系统之间的交互界面。在较早的计算机系统中,tty是以打字机作为输入输出设备的终端系统,而现代的Linux系统中,tty则对应着虚拟终端。

Linux下的tty体系是由多个虚拟终端组成的,每个虚拟终端都对应着一个tty设备文件。在Linux系统中,tty设备文件位于/dev目录下,以tty开头,后面跟随一个数字,如tty1、tty2等。

对于用户来说,tty设备提供了一个字符设备节点,用于输入和输出字符数据。用户可以通过tty设备读取输入的字符,并将输出字符发送到tty设备。这些字符可以是用户输入的命令、系统的输出信息等。

用户可以在Linux系统中使用特殊的快捷键(如Ctrl+Alt+F1至Ctrl+Alt+F6)在各个终端之间切换,并在各个tty设备上独立地登录和执行命令。TTY设备为用户提供了与操作系统进行交互的接口,用户可以在不同的虚拟终端上同时工作,执行不同的任务。

在终端下通过ls命令可以查看到非常多的tty设备节点,这些节点都分别对应了不同的功能。
linux串口编程,Linux,linux,运维,服务器

这里引用百问网的一张图片:
linux串口编程,Linux,linux,运维,服务器

二、行规层

行规层(Line Discipline)是指在Unix-like操作系统中提供的一种机制,用于处理终端设备或网络连接中的数据流。它位于终端设备驱动程序和用户进程之间,负责对输入和输出的字符数据进行处理和转换。
行规层在传统的串行终端设备上起着重要的作用。它可以执行以下功能:

输入处理:行规层可以处理终端设备上的输入字符流。它可以执行字符的缓冲、编辑和回显等操作。例如,当用户在终端上输入字符时,行规层可以提供行缓冲,使得用户可以逐行输入,并在按下回车键后将整行字符发送给用户进程。

输出处理:行规层可以对输出字符进行处理。它可以执行转义序列的解释和字符的替换等操作。例如,当用户进程向终端设备发送输出字符流时,行规层可以将特定字符序列转换为控制终端的命令,以调整光标位置、修改显示属性等。

规范模式和原始模式:行规层支持规范模式和原始模式的切换。在规范模式下,行规层提供行缓冲、按行输入和回显等功能。这是默认的终端模式,适用于大多数交互式应用。在原始模式下,行规层关闭缓冲和编辑功能,字符逐个传递给用户进程,适用于特定的应用需求,如串口通信等。
控制字符处理:行规层负责处理特殊控制字符的输入和输出。例如,CTRL+C用于中断正在运行的程序,CTRL+D用于表示输入的结束等。行规层将检测和处理这些特殊字符,并执行相应的操作。

行规层的具体实现在不同的操作系统中会有所不同。在Linux系统中,行规层是通过终端设备驱动程序和TTY子系统来实现的。Linux提供了多个行规层的实现,如原始(line discipline)、终端(line discipline)、网络(line discipline)等。用户可以根据需要选择不同的行规层来完成特定的终端和网络通信任务。

总结来说,行规层是位于终端设备驱动程序和用户进程之间的一层软件机制,用于处理终端设备和网络连接中的数据流。它提供了输入处理、输出处理、规范模式和原始模式的切换以及控制字符处理等功能。通过行规层,用户可以更加灵活地操控和控制终端设备和网络连接。

三、Linux串口编程步骤

在ARM Linux系统上进行串口编程可以按照以下步骤进行:

1.打开串口设备:
首先,需要打开要使用的串口设备文件,一般在Linux系统中,串口设备文件位于/dev/ttySx或/dev/ttyUSBx,其中x是串口号或USB串口号。可以使用系统调用open()函数以读写方式打开串口设备文件。

2.配置串口参数:
在打开串口设备后,需要配置串口的通信参数,如波特率、数据位、停止位、校验位等。可以使用termios数据结构和相关的函数来进行设置。可以使用函数tcgetattr()获取当前串口参数,然后修改相关参数,最后通过tcsetattr()函数将修改后的参数应用到串口设备。

termios结构体:

struct termios {
	tcflag_t c_iflag;		/* input mode flags */
	tcflag_t c_oflag;		/* output mode flags */
	tcflag_t c_cflag;		/* control mode flags */
	tcflag_t c_lflag;		/* local mode flags */
	cc_t c_cc[NCCS];		/* control characters */
	cc_t c_line;			/* line discipline (== c_cc[19]) */
	speed_t c_ispeed;		/* input speed */
	speed_t c_ospeed;		/* output speed */
};

各个参数介绍:
c_iflag(输入模式标志位):这个字段包含一系列用于控制输入模式的标志位。例如,可以使用IGNBRK来忽略BREAK键输入,使用INPCK来启用奇偶校验等。

c_oflag(输出模式标志位):这个字段包含一系列用于控制输出模式的标志位。例如,可以使用OPOST来启用输出处理(执行输出转义字符的功能),使用ONLCR来将输出中的换行符转换为回车换行等。

c_cflag(控制模式标志位):这个字段包含一系列用于控制串口硬件特性的标志位。例如,可以使用CBAUD来设置波特率,使用CS8来设置数据位为8位等。

c_lflag(本地模式标志位):这个字段包含一系列用于控制本地模式(终端行为)的标志位。例如,可以使用ICANON来启用规范模式,使用ECHO来启用回显等。

c_cc(控制字符数组):这个数组存储了一些特殊控制字符的值,如终端驱动程序使用的信号字符,输入和输出的起始字符等。

c_line(线路规程):这个字段存储了用于处理数据的线路规程(line discipline)的标识符。一般情况下,可以将其设置为0。

c_ispeed(输入波特率):这个字段指定输入的波特率,表示从串口接收数据的速度。

c_ospeed(输出波特率):这个字段指定输出的波特率,表示向串口发送数据的速度。

3.读取和写入数据:
一旦串口打开且参数配置完成,就可以进行数据的读取和写入。可以使用read()函数从串口设备读取数据,使用write()函数将数据写入串口设备。

4.清理并关闭串口:
在使用完串口后,需要进行清理和关闭。可以使用tcsetattr()函数将串口参数恢复到默认值,并使用close()函数关闭串口设备文件。

四、代码编写

代码的话也是使用的是百问网提供的代码:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>

/* set_opt(fd,115200,8,'N',1) */
int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
	struct termios newtio,oldtio;
	
	if ( tcgetattr( fd,&oldtio) != 0) { 
		perror("SetupSerial 1");
		return -1;
	}
	
	bzero( &newtio, sizeof( newtio ) );
	newtio.c_cflag |= CLOCAL | CREAD; 
	newtio.c_cflag &= ~CSIZE; 

	newtio.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
	newtio.c_oflag  &= ~OPOST;   /*Output*/

	switch( nBits )
	{
	case 7:
		newtio.c_cflag |= CS7;
	break;
	case 8:
		newtio.c_cflag |= CS8;
	break;
	}

	switch( nEvent )
	{
	case 'O':
		newtio.c_cflag |= PARENB;
		newtio.c_cflag |= PARODD;
		newtio.c_iflag |= (INPCK | ISTRIP);
	break;
	case 'E': 
		newtio.c_iflag |= (INPCK | ISTRIP);
		newtio.c_cflag |= PARENB;
		newtio.c_cflag &= ~PARODD;
	break;
	case 'N': 
		newtio.c_cflag &= ~PARENB;
	break;
	}

	switch( nSpeed )
	{
	case 2400:
		cfsetispeed(&newtio, B2400);
		cfsetospeed(&newtio, B2400);
	break;
	case 4800:
		cfsetispeed(&newtio, B4800);
		cfsetospeed(&newtio, B4800);
	break;
	case 9600:
		cfsetispeed(&newtio, B9600);
		cfsetospeed(&newtio, B9600);
	break;
	case 115200:
		cfsetispeed(&newtio, B115200);
		cfsetospeed(&newtio, B115200);
	break;
	default:
		cfsetispeed(&newtio, B9600);
		cfsetospeed(&newtio, B9600);
	break;
	}
	
	if( nStop == 1 )
		newtio.c_cflag &= ~CSTOPB;
	else if ( nStop == 2 )
		newtio.c_cflag |= CSTOPB;
	
	newtio.c_cc[VMIN]  = 1;  /* 读数据时的最小字节数: 没读到这些数据我就不返回! */
	newtio.c_cc[VTIME] = 0; /* 等待第1个数据的时间: 
	                         * 比如VMIN设为10表示至少读到10个数据才返回,
	                         * 但是没有数据总不能一直等吧? 可以设置VTIME(单位是10秒)
	                         * 假设VTIME=1,表示: 
	                         *    10秒内一个数据都没有的话就返回
	                         *    如果10秒内至少读到了1个字节,那就继续等待,完全读到VMIN个数据再返回
	                         */

	tcflush(fd,TCIFLUSH);
	
	if((tcsetattr(fd,TCSANOW,&newtio))!=0)
	{
		perror("com set error");
		return -1;
	}
	//printf("set done!\n");
	return 0;
}

int open_port(char *com)
{
	int fd;
	//fd = open(com, O_RDWR|O_NOCTTY|O_NDELAY);
	fd = open(com, O_RDWR|O_NOCTTY);
    if (-1 == fd){
		return(-1);
    }
	
	  if(fcntl(fd, F_SETFL, 0)<0) /* 设置串口为阻塞状态*/
	  {
			printf("fcntl failed!\n");
			return -1;
	  }
  
	  return fd;
}


/*
 * ./serial_send_recv <dev>
 */
int main(int argc, char **argv)
{
	int fd;
	int iRet;
	char c;

	/* 1. open */

	/* 2. setup 
	 * 115200,8N1
	 * RAW mode
	 * return data immediately
	 */

	/* 3. write and read */
	
	if (argc != 2)
	{
		printf("Usage: \n");
		printf("%s </dev/ttySAC1 or other>\n", argv[0]);
		return -1;
	}

	fd = open_port(argv[1]);
	if (fd < 0)
	{
		printf("open %s err!\n", argv[1]);
		return -1;
	}

	iRet = set_opt(fd, 115200, 8, 'N', 1);
	if (iRet)
	{
		printf("set port err!\n");
		return -1;
	}

	printf("Enter a char: ");
	while (1)
	{
		scanf("%c", &c);
		iRet = write(fd, &c, 1);
		iRet = read(fd, &c, 1);
		if (iRet == 1)
			printf("get: %02x %c\n", c, c);
		else
			printf("can not get data\n");
	}

	return 0;
}

代码测试:
linux串口编程,Linux,linux,运维,服务器
这里输入a后不会马上返回数据只有按下回车后才会打印出数据。
linux串口编程,Linux,linux,运维,服务器
这是因为有行规层的限制,输入a后行规层并不会唤醒scanf,只有当输入回车后行规层才会将scanf唤醒。

总结

本篇文章就讲解到这里。文章来源地址https://www.toymoban.com/news/detail-704487.html

到了这里,关于Linux串口编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux本地部署1Panel服务器运维管理面板并实现公网访问

    1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器,包括主机监控、文件管理、数据库管理、容器管理等 下面我们介绍在Linux 本地安装1Panel 并结合cpolar 内网穿透工具实现远程访问1Panel 管理界面 执行如下命令一键安装 1Panel: 安

    2024年02月04日
    浏览(98)
  • Linux服务器常见运维性能测试(1)综合跑分unixbench、superbench

    最近需要测试一批服务器的相关硬件性能,以及在常规环境下的硬件运行稳定情况,需要持续拷机测试稳定性。所以找了一些测试用例。本次测试包括在服务器的高低温下性能记录及压力测试,高低电压下性能记录及压力测试,常规环境下CPU满载稳定运行的功率记录。 这个系

    2024年02月04日
    浏览(83)
  • linux并发服务器 —— linux网络编程(七)

    C/S结构 - 客户机/服务器;采用两层结构,服务器负责数据的管理,客户机负责完成与用户的交互;C/S结构中,服务器 - 后台服务,客户机 - 前台功能; 优点 1. 充分发挥客户端PC处理能力,先在客户端处理再提交服务器,响应速度快; 2. 操作界面好看,满足个性化需求; 3.

    2024年02月09日
    浏览(75)
  • [1Panel]开源,现代化,新一代的 Linux 服务器运维管理面板

    本期测评试用一下1Panel这款面板。1Panel是国内飞致云旗下开源产品。整个界面简洁清爽,后端使用GO开发,前端使用VUE的Element-Plus作为UI框架,整个面板的管理都是基于docker的,想法很先进。官方还提供了视频的使用教程,本期为大家按照本专栏的基本内容进行多方面的测评。

    2024年02月07日
    浏览(95)
  • 【Linux高性能服务器编程】——高性能服务器框架

      hello !大家好呀! 欢迎大家来到我的Linux高性能服务器编程系列之高性能服务器框架介绍,在这篇文章中, 你将会学习到高效的创建自己的高性能服务器,并且我会给出源码进行剖析,以及手绘UML图来帮助大家来理解,希望能让大家更能了解网络编程技术!!! 希望这篇

    2024年04月25日
    浏览(62)
  • Linux服务器常见运维性能测试(3)CPU测试super_pi、sysbench

    最近需要测试一批服务器的相关硬件性能,以及在常规环境下的硬件运行稳定情况,需要持续拷机测试稳定性。所以找了一些测试用例。本次测试包括在服务器的高低温下性能记录及压力测试,高低电压下性能记录及压力测试,常规环境下CPU满载稳定运行的功率记录。 这个系

    2024年02月02日
    浏览(55)
  • 华为云云耀云服务器L实例评测 | Linux系统宝塔运维部署H5游戏

    本章节内容,我们主要介绍华为云耀服务器L实例,从云服务的优势讲起,然后讲解华为云耀服务器L实例资源面板如何操作,如何使用宝塔运维服务,如何使用运维工具可视化安装nginx,最后部署一个自研的H5的小游戏(6岁的小朋友玩的很开心😁)。 前端的同学如果想把自己

    2024年02月07日
    浏览(58)
  • 《Linux高性能服务器编程》笔记01

    本文是读书笔记,如有侵权,请联系删除。 参考 Linux高性能服务器编程源码: https://github.com/raichen/LinuxServerCodes 豆瓣: Linux高性能服务器编程 □socket地址API。socket最开始的含义是一个IP地址和端口对(ip,port)。它唯一地 表示了使用TCP通信的一端。本书称其为socket地址。 □s

    2024年01月22日
    浏览(66)
  • 《Linux高性能服务器编程》笔记04

    本文是读书笔记,如有侵权,请联系删除。 参考 Linux高性能服务器编程源码: https://github.com/raichen/LinuxServerCodes 豆瓣: Linux高性能服务器编程 I/O复用使得程序能同时监听多个文件描述符,这对提高程序的性能至关重要。通常,网络程序在下列情况下需要使用I/0复用技术: 客户

    2024年01月21日
    浏览(75)
  • 【阅读笔记】Linux 高性能服务器编程

    原文地址以及最新代码参考:https://github.com/EricPengShuai/Interview/tree/main/Linux Ch.5 Linux 网络编程基础 API 5.1 socket 地址 API 5.1.1 主机字节序和网络字节序 大端字节序(网络字节序):高位低地址 小端字节序(主机字节序):高位高地址 参考代码:5-1byteorder.cpp 一般网络编程中,发

    2024年02月06日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包