Linux设备程序设计(四)------Linux下串口通讯

串口通信协议和Linux下的串口编程 一、串口通信介绍: 串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节,尽管比按位字节(byte)的并行通信慢,但是串口可以使用一根线发送数据的同时用另一根线接收数据。串口通信属于异步串行通信方式。串口是一种接口标准,它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议。 二、串口接头: 常见的串口接头有两种,一种是9针串口(简单DB-9),一种是25针串口(简称DB-25)。 以DB9为例为例,如图: 母头:泛指所有带孔状的接头(5针朝下,从左到 阅读详情

用过开发板的朋友们都知道,UART在一个芯片中是很珍贵的资源,它可以被配置为485功能,也可以配置232功能,而在工业通讯中,485,232都是常用的通讯方式。

这里多说一句题外话:一直没有找到linux比较好的编辑工具,vim虽然功能强大,但是没有鼠标,总感觉不爽,gedit虽然有鼠标,但是缩进功能实在是不行,这里推荐一款本人一直用的,感觉用的不错,就是大家熟悉的eclipse for c++,这个有Linux版本,用起来虽然没有windows下那么爽,但是比大多数(至少笔者用过的)都好用。

首先说,Linux的串口通讯和我们逻辑编程的编程方法是不同的:

裸机下需要配置寄存器,然后如果中断方式,设置中断函数,如果查询方式,就查询状态位,判断是否有数据过来,如果要发送数据则就是往发送寄存器发送数据,然后就自动发出去了。

Linux下可不同,那些所谓的驱动都已经写好了,我们需要调用的是Linux统一封装的数据结构和函数来操作串口,对于不同的芯片,只要是Linux操作系统,都可以用这一类型的东西操作串口。

在Linux中所有串口配置数据都可以用struct termios来描述,里面有许多的成员函数,像我们配置波特率,数据位长度和奇偶校验,具体配置方法可以参考这篇程:http://wenku.baidu.com/view/114854b069dc5022aaea00b7.html

下面是笔者写的程序,大家可以拿来当做模板来使用,主要实现对UART0的操作:

#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>

//为了保证用户输入的波特率是个正确的值,所以需要这两个数组验证,对于设置波特率时候,前面要加个B
int speed_arr[] = { B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300,
	B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300, };

int name_arr[] = {115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300,
	115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300, };

/*-----------------------------------------------------------------------------
  函数名:      set_speed
  参数:        int fd ,int speed
  返回值:      void
  描述:        设置fd表述符的串口波特率
 *-----------------------------------------------------------------------------*/
void set_speed(int fd ,int speed)
{
	struct termios opt;
	int i;
	int status;

	tcgetattr(fd,&opt);
	for(i = 0;i < sizeof(speed_arr)/sizeof(int);i++)
	{
		if(speed == name_arr[i])						//找到标准的波特率与用户一致
		{
			tcflush(fd,TCIOFLUSH);						//清除IO输入和输出缓存
			cfsetispeed(&opt,speed_arr[i]);			//设置串口输入波特率
			cfsetospeed(&opt,speed_arr[i]);			//设置串口输出波特率

			status = tcsetattr(fd,TCSANOW,&opt);	//将属性设置到opt的数据结构中,并且立即生效
			if(status != 0)
				perror("tcsetattr fd:");				//设置失败
			return ;
		}
		tcflush(fd,TCIOFLUSH);							//每次清除IO缓存
	}
}
/*-----------------------------------------------------------------------------
  函数名:      set_parity
  参数:        int fd
  返回值:      int
  描述:        设置fd表述符的奇偶校验
 *-----------------------------------------------------------------------------*/
int set_parity(int fd)
{
	struct termios opt;

	if(tcgetattr(fd,&opt) != 0)					//或许原先的配置信息
	{
		perror("Get opt in parity error:");
		return -1;
	}

	/*通过设置opt数据结构,来配置相关功能,以下为八个数据位,不使能奇偶校验*/
	opt.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
				| INLCR | IGNCR | ICRNL | IXON);
	opt.c_oflag &= ~OPOST;
	opt.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
	opt.c_cflag &= ~(CSIZE | PARENB);
	opt.c_cflag |= CS8;

	tcflush(fd,TCIFLUSH);							//清空输入缓存

	if(tcsetattr(fd,TCSANOW,&opt) != 0)
	{
		perror("set attr parity error:");
		return -1;
	}

	return 0;
}
/*-----------------------------------------------------------------------------
  函数名:      serial_init
  参数:        char *dev_path,int speed,int is_block
  返回值:      初始化成功返回打开的文件描述符
  描述:        串口初始化,根据串口文件路径名,串口的速度,和串口是否阻塞,block为1表示阻塞
 *-----------------------------------------------------------------------------*/
int serial_init(char *dev_path,int speed,int is_block)
{
	int fd;
	int flag;

	flag = 0;
	flag |= O_RDWR;						//设置为可读写的串口属性文件
	if(is_block == 0)
		flag |=O_NONBLOCK;				//若为0则表示以非阻塞方式打开

	fd = open(dev_path,flag);				//打开设备文件
	if(fd < 0)
	{
		perror("Open device file err:");
		close(fd);
		return -1;
	}

	/*打开设备文件后,下面开始设置波特率*/
	set_speed(fd,speed);				//考虑到波特率可能被单独设置,所以独立成函数

	/*设置奇偶校验*/
	if(set_parity(fd) != 0)
	{
		perror("set parity error:");
		close(fd);						//一定要关闭文件,否则文件一直为打开状态
		return -1;
	}

	return fd;
}
/*-----------------------------------------------------------------------------
  函数名:      serial_send
  参数:        int fd,char *str,unsigned int len
  返回值:      发送成功返回发送长度,否则返回小于0的值
  描述:        向fd描述符的串口发送数据,长度为len,内容为str
 *-----------------------------------------------------------------------------*/
int serial_send(int fd,char *str,unsigned int len)
{
	int ret;

	if(len > strlen(str))					//判断长度是否超过str的最大长度
		len = strlen(str);

	ret = write(fd,str,len);
	if(ret < 0)
	{
		perror("serial send err:");
		return -1;
	}

	return ret;
}

/*-----------------------------------------------------------------------------
  函数名:      serial_read
  参数:        int fd,char *str,unsigned int len,unsigned int timeout
  返回值:      在规定的时间内读取数据,超时则退出,超时时间为ms级别
  描述:        向fd描述符的串口接收数据,长度为len,存入str,timeout 为超时时间
 *-----------------------------------------------------------------------------*/
int serial_read(int fd, char *str, unsigned int len, unsigned int timeout)
{
	fd_set rfds;
	struct timeval tv;
	int ret;								//每次读的结果
	int sret;								//select监控结果
	int readlen = 0;						//实际读到的字节数
	char * ptr;

	ptr = str;							//读指针,每次移动,因为实际读出的长度和传入参数可能存在差异

	FD_ZERO(&rfds);						//清除文件描述符集合
	FD_SET(fd,&rfds);					//将fd加入fds文件描述符,以待下面用select方法监听

	/*传入的timeout是ms级别的单位,这里需要转换为struct timeval 结构的*/
	tv.tv_sec  = timeout / 1000;
	tv.tv_usec = (timeout%1000)*1000;

	/*防止读数据长度超过缓冲区*/
	//if(sizeof(&str) < len)
	//	len = sizeof(str);


	/*开始读*/
	while(readlen < len)
	{
		sret = select(fd+1,&rfds,NULL,NULL,&tv);		//检测串口是否可读

		if(sret == -1)								//检测失败
		{
			perror("select:");
			break;
		}
		else if(sret > 0)							//检测成功可读
		{
			ret = read(fd,ptr,1);
			if(ret < 0)
			{
				perror("read err:");
				break;
			}
			else if(ret == 0)
				break;

			readlen += ret;								//更新读的长度
			ptr     += ret;								//更新读的位置
		}
		else													//超时
		{
			printf("timeout!\n");
			break;
		}
	}

	return readlen;
}
int main()
{
	int fd;
	int ret;
	char str[]="hello linux serial!";
	char buf[100];


	fd =  serial_init("/dev/ttyS1",115200,1);
	if(fd < 0)
	{
		perror("serial init err:");
		return -1;
	}

	ret = serial_send(fd,str,22);
	printf("send %d bytes!\n",ret);
	serial_read(fd,buf,100,5000);
	printf("the buf is :%s\n",buf);
	close(fd);
	return 0;
}
做几点简要的说明:

1.高级的一点芯片,如ARM9一般有个UART口专门用于打印调试信息,其对应的设备文件是ttyS0,然后UART0对应    ttyS1,以此类推,笔者这里打开的是ttyS1那就是UART0

2.可以看到一点,我定义了一个struct termios opt,对UART的所有配置信息都是在这个结构体中完成。

3.还是那句,发送和接受数据就和读写文件一样简单,发送就是write操作,读就是read操作

4.在读的时候,笔者用的是循环查询的方式,在Linux中,一般用的方法可以创建一个线程,用这个线程来循环调用select方法,来反复查询UART对应的设备文件是否可读(对应于是否有数据从串口传入),这种阻塞方式不是盲目等待,后来有调度机制,可以代替裸机编程中的中断方式。

下面是运行结果:

 

Linux应用层开发(4):串口通讯与终端设备 串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式, 因为它简单便捷,大部分电子设备都支持,电子工程师在调试设备时也经常使用串口来输出调试信息, 在计算机的上古时代,键盘、鼠标、MODEM以及终端都是使用串口通讯的。另外,为了方便使用,开发板上常常添加了USB转串口芯片, 使得我们可以直接用USB线连接主机和开发板的串口。串口在旧式的台式计算机中一般会以RS-232标准的COM口出现(也称DB9接口),见下图。 阅读详情

相关推荐

linux串口通讯

在当今的科技世界中,串口通讯虽然不像一些新兴的高速通信技术那般夺目,但它依然在众多领域有着不可替代的地位,尤其是在嵌入式系统开发、工业自动化控制等场景。而 Linux 系统,凭借其开源、稳定且强大的特性,为串口通讯提供了出色的支持。

道亦无名 253

Linux驱动侧手动添加485收发支持

Linux驱动侧 适配半双工485的收发支持

我不姓周,不唱朋友 2076

Linux 串口类

使用C++ 封装了linux 的串口操作接口,统一了外部调用接口,隐藏了对串口的细节,简单易用。 另外,为了提高响应的实时性,和数据的吞吐量,内部使用了线程和环形缓冲区,经过测试,暂时未发现bug。

嵌入式Linux开发---RS485通信驱动硬件编程

RS485的使用与UART串口的使用基本相同,差别在于使用485时需要手动切换485芯片的收发引脚模式。 实际上,使用RS485通信的本质还是在使用UART串口通信,只是在程序编写时,需要对read、write多一步的处理。即,进行写485操作时,通过控制引脚切换485芯片为发送模式,读操作时,通过控制引脚切换485芯片为接收模式。485处理芯片相对于串口,就是需要将TTL逻辑电平信号转换为RS485差分电平信号。

weixin_49337111的博客 1万+

linux 串口类(c/c++)

串口设备无论是在工控领域,还是在嵌入式设备领域,应用都非常广泛。而串口编程也就显得必不可少。 偶然的一次机会,需要使用串口,而且操作系统还要求是Linux,因此,趁着这次机会,综合别人的代码, 进行了一次整理和封装。具体的封装格式为C代码,这样做是为了很好的移植性,使它可以在C和C++环境下, 都可以编译和使用

Linux c++多线程串口编程demo

使用多线程进行串口编程,获取串口数据,利用互斥锁和信号量在不同的线程中安全地操作数据,希望该demo能帮助你快速理解并掌握上述知识。

Linux下的串口通信(RS485)

n";// RTS这里头文件就不做过多解释,set_opt函数主要是用来定义串口的配置,uart_send主要是串口的发送函数,uart_recv_timeout函数是为了防止串口通信超时。变量fd,ret,i是简单的变量定义,char *uart3 = "/dev/ttyTHS0";定义了发送数据的串口,*buffer是发送的数据。

2301_80471066的博客 3973

Linux平台下的串口通信类的实现

在Linux下实现的串口通讯类,提供了非常简单足够用的接口。当初是为解决MCU升级时,实现的一个通用串口通信类。

记录Linux开发板串口调试、静态ip

记录连接开发板串口调试的方法

x1010121的博客 1826

linux c++ 简单串口通信

一个linux下利用com口 本机发送本机接受数据的简单例子。运行程序前要先切换本机com。本文件只包含src部分。

瑞芯微RK3568开发板实战:Android11系统parameter.txt文件配置全解析(附常见问题排查)

本文深入解析了瑞芯微RK3568开发板在Android 11系统下parameter.txt文件的配置要点与实战技巧。作为BootLoader启动阶段的核心蓝图,该文件决定了硬件初始化、分区布局及电源管理。文章详细拆解了身份标识、GPIO控制(PWR_HLD)、命令行参数及分区表等关键字段的配置逻辑,并针对RK3568平台与Android 11特性提供了专项适配策略,最后附带了启动失败、分区异常等常见问题的深度排查方法。

php55的博客 897

DM8168 + Linux RS485控制

1: 485通信对软件来说,就是比串口232通信多了一个方向控制,因为485是半双工,有主从的总线通信方式,而232是点对点的通信,在Linux与ARM的串口通信已经很完备了,因此进行485通信只要增加方向控制即可。 2: 如何保证方向及时切换是最难的,这里的方向当然是发送数据完后,马上切换为接收状态,如果在应用层作方向切换 的控制,则很难保证把从机返回的数据接收完整,因为

mianhuantang848989的专栏 1684

FreeRTOS CLI实战:5分钟搞定GD32串口终端移植(附LED控制源码)

本文详细介绍了如何在GD32芯片上快速移植FreeRTOS CLI(Command Line Interface)终端,实现串口交互式调试。通过DMA+空闲中断配置和LED控制命令实战,开发者可在5分钟内构建高效嵌入式调试工具,显著提升现场维护效率。

weixin_28707365的博客 450

第十九节 串口通讯与终端设备

串口通讯与终端设备

picassocao的博客 2321

YOLOv12改进策略【注意力机制篇】| 添加SE、CBAM、ECA、CA、Swin Transformer等注意力和多头注意力机制

这篇文章带来一个经典注意力模块的汇总,虽然有些模块已经发布很久了,但后续的注意力模块也都是在此基础之上进行改进的,对于初学者来说还是有必要去学习了解一下,以加深对模块,模型的理解。

Limiiiing的博客 1511

全新YOLO26有效涨点改进专栏目录 | 本专栏持续更新500+篇内容 | 包含各种卷积、主干网络、各种注意力机制、检测头、损失函数、小目标检测改进、二次创新模块、独家创新等几百种创新点改进

🔥《2026年YOLO26改进专栏》🔥 🚀持续更新500+最前沿创新改进点(顶会顶刊论文/独家创新策略),涵盖主干网络、各种卷积、注意力机制、检测头等模块,助力科研论文发表与模型性能提升,支持检测/分割/分类任务。订阅即获完整可运行项目,提供专业QQ答疑群,适合科研发论文(SCI/CCF/核心)及工业应用(PCB/医学/遥感检测)。专栏价格将逐步上涨,早订更优惠! ⭐特色:非计算机专业友好、模块可自由组合、全网独家创新;📌适用:研究生大论文和小论文创新、小目标检测、多领域科研应用,满足不同研究需求。

Ai缝合怪、助力小伙伴高效发论文 5166

Linux下C语言实现串口通讯例程

我们的目标,是在Linux系统中实现一个C语言的串口通讯程序,能与插入的串口通讯设备进行通讯。所以代码层面的目标可以拆解为三个: 1.正确配置串口。 2.实现写入功能。 3.实现读取功能 在分化需求之后,我们来从代码层面逐个实现。

QQ2061144211的博客 4477

制作一款精美的 Qt IFW 安装程序

对于 Qt IFW 来说,默认的安装界面并不吸引人。倘若要制作一款精美的安装程序,这显然是不够的,需要在此基础上进行一系列定制。

青春不老,奋斗不止! 5261

树莓派通过蓝牙进行串口通讯

树莓派通过蓝牙进行串口通讯 今天做毕设,想用树莓派进行蓝牙串口通讯,搜了很多网页,终于成功了。趁热打铁记录一下。 一、树莓派进行蓝牙连接 1、蓝牙配对 有图形界面的直接点右上角的蓝牙然后点Add Device添加自己要连接的蓝牙即可。 没有的只能通过指令了,首先通过各种方式进入终端,然后当然先检查更新一下软件源、软件包吧 sudo apt-get update sudo apt-get upgrade 查看蓝牙信息 sudo bluetoothctl 然后输入 scan on 扫描蓝牙 找到自己要连

qq_42747705的博客 6732

1-Wire 单总线硬件结构及软件时序分析(实测波形+C代码分析)

1根线就可以通信,是不是很神奇?因为其节省I/O口的优点而被广泛使用!

记得诚 1万+

自定义配方画面的具体方法示例 - 嵌入式

自定义配方画面的具体方法示例 - 嵌入式在TIA博途WinCC中,您可以通过编程来创建自定义配方画面。这些画面可以用于显示和编辑您的嵌入式系统中的配方参数。下面是一个示例,展示了如何在WinCC中实现自定义配方画面的方法。首先,您需要创建一个新的画面,并添加必要的控件来显示和编辑配方参数。在WinCC中,您可以使用文本框、按钮和标签等控件来实现这一目的。以下是一个简单的示例,展示了如何创建一个具有文本框和按钮的配方画面:创建新画面:添加控件:编辑控件属性:编写脚本: 在这个示例中,我们定义了两个变量和来存储

CyberBytezZ的博客 1039

[转]详解linux下的串口通讯开发

详解linux下的串口通讯开发<br />串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使用。常用的串口是RS-232-C接口(又称EIA RS-232-C)它是在1970年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。串口通讯指的是计算机依次以位(bit)为单位来传送数据,串行通讯使用的范围很广,在嵌入式系统开发过程中串口通讯也经常用到通讯方式之一。<br />Linux对所有设备的访问是通过设备文件来进行的,串口也是这样,为了

saokeliu的专栏 1346

使用再生龙备份还原liunx系统(精确每一步)

再生龙镜像可自行到官网下载(ISO/ZIP格式均可),然后使用tuxboot烧录工具制作启动盘,另外准备一个U盘用来存储备份的镜像文件,即再生龙盘和存储盘是两个分开的。当然也可以用分区工具把同一个硬盘分区来使用,比较方便,先学会基本的吧,只是告诉有个捷径,省个U盘。

米可的博客! 8379

linux系统如何连接串口,Linux下如何测试及使用USB转串口线

1、将设备u口插入pc2、输入#lsmod 先看看能否检测到这个设备,就看有没有pl2303字眼可以了。如果有,则不需要再装驱动。另外如果有的话最好再用dmesg | grep usb查找如果看到:“drivers/usb/serial/usb-serial.c: USB Serial support registered for Generic”就说明设备已被识别,然后往下看drivers/us...

weixin_34430692的博客 4354

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Linux串口通讯

一、串口简介 串口是计算机上的串行通讯的物理接口。计算机历史上,串口曾经被广泛用于连接计算机和终端设备和各种外部设备。虽然以太网接口和USB接口也是以一个串行流进行数据传送的,但是串口连接通常特指那些与RS-232标准兼容的硬件或者调制解调器的接口。虽然现在在很多个人计算机上,原来用以连接外部设备的串口已经广泛的被USB和Firewire替代;而原来用以连接网络的串口则被以太网替代,还有用以

BOKE 2264

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

.NET Core 跨平台 串口通讯 ,Windows/Linux 串口通讯

1,前言开发环境:在 Visual Studio 2017,.NET Core 2.x串口通讯用于设备之间,传递数据,物联网设备中广泛使用串口方式连接通讯,物联网通讯协议...

dotNET跨平台 5868

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

上一篇: Linux设备驱动程序设计(三)----Linux的特殊字符设备:混杂设备,依旧让LED闪烁起来
下一篇: Linux设备驱动学习(五)------Linux下定时器操作
memoryjs
博客等级 码龄18年 240粉丝 113原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值