Linux下的串口通信

串口通信


基础知识:

  1. 什么是串口?

alt text

串口全称串行通信接口,是一种常用于电子设备之间通信的异步,全双工接口,典型的串口通信只需要 3 根线,分别是地线 (GND),发送线(TX),接收线(RX)。如下图:

alt text

2.什么是波特率?
串口的通信速率称之为波特率(bandrate),波特率也可以叫码元速率,定义: 单位时间内通过信道传输的码元个数就是波特率,单位: 波特
在数字信道中,一个脉冲信号就是一个码元,码元速率指的是在 1 秒钟内能发送多少个码元
也就是说在数字信道中 1 秒钟内可以发送多少个脉冲信号

alt text

需要注意的是,串口的波特率并不能随意设置,因为通信的双方必须设置相同的波特率才可
以成功通信。如果双方的波特率设置不一样则不能通信成功。另外波特率的值一般在常用的
里面选择,约定俗成。而特殊的波特率有可能需要额外设置。

  1. 什么是比特率?
    比特率:每秒钟传送的比特数,单位是 bit/s
    比特率和波特率的关系:
    比特率=波特率*log2M,M 表示每个码元承载的信息量。
    M 如何理解?
    一个码元就是一个脉冲信号!一个脉冲信号有可能携带 1bit 数据,也有可能携带 2bit 数据,4bit 数据。在二进制系统中,比特率就等于波特率

问题: 🤚
假如串口的波特率是 9600,在二进制系统中一秒钟可以传送多少个字节。
分析:一个字节等于 8 个 bit,也就是 8 个高低电平。在二进制系统中,比特率就等于波特率所以就是 9600/8=1200 个字节

串口通信的格式

  1. 格式 📌
    串口通信的格式分为两种:标准格式非标准格式
    标准格式:起始位(1bit) + 数据位(8bit) + 奇偶校验位(1bit) + 停止位(1bit)

alt text

起始位:数据线上空闲时为 1,拉低代表开始传输数据
数据位:需要发送或者接收的数据。
奇偶校验位:通过对数据中的 1的个数(奇数/偶数) 来校验数据传输是否准确。
停止位:数据传输完成。数据线恢复成 1的状态。

  1. 校验 🖇
    奇校验 (odd parity): 如果数据中有奇数个 1,校验位为 1,否则为 0
    偶校验 (even parity): 如果数据中有偶数个 1,校验位为 1,否则为 0
    0 校验(space parity): 校验位恒为 0,如果为 1表述错误。
    1校验(mark parity):校验位恒为 1,如果为0表示错误

串口的通讯接口

  • 串口只对数据格式有定义,并没有规定接口的电器特性,
  • 如果用高电平代表 1,用低电平代表0,那高电平是多少v呢?低电平又是多少v 呢?所以串口的通信接口类型有很多。
  • 在举个例子:🍐
    如果在串口通信中直接使用处理器引出的接口,电平是 TTL 电平。但是处理器的电平也有可能存在差异,所有某些情况下并不能直接连接。这时就要进行电平转换。
    TTL:transistor transistor logic (我们学习时用的单片机时常常用的TTL电平的,就是那些普通的杜邦线做连接)但是 TTL 的抗干扰能力比较弱,在数据传输的时候很容易出错,所以通信距离也短。往往只用在一个电路板中的俩个不同的芯片通信。既然串口没有规定电器特性,那是不是就可以通过电器特性入手来解决 TTL 的缺点呢?

RS232/协议

RS232 协议是 1970 年美国电子工业协会联合各个厂家共同制定的串行通信标准。这个标准
规定了在串口通信中采用一个标准的连接器,如下图所示,并且在标准中对每个连接器的引
脚和电平也做了规定。

alt text

DB9 引脚说明👆

1脚: 载波检测(DCD)
2脚: 接收数据(RXD)
3 脚: 发出数据(TXD)
4 脚: 数据终端准备好(DTR)
5 脚:信号地线(SG)
6脚: 数据准备好(DSR)
7 脚: 请求发送(RTS)
8 脚: 清除发送(CTS)
9 脚: 振铃指示(RI)

: Recommand Standard 即 RS,推荐标准
特点:逻辑1的电平为-5v 到-15v,逻辑 0的电平为+5v 到+15v。所以抗干扰能力有所增强。通信距离一般可达 15m。

RS485/协议

RS232 通信速度并不快,而且传输距离也不是很远(15m),相对于 TTL 电平来说确实提高
了抗干扰性,但是容易产生共模干扰。

RS485 标准是由电信行业协会和电子工业联盟制定。主要是用来解决超远距离(1200m)以
及更好的抗干扰性能。并且 RS485 具有多站能力,可以利用 RS485 组网。

电平特性
RS485 使用差分信号(抗共模干扰能力强) 进行数据传输,俩线之间的电压差为+2v 到+6v
表示逻辑 1,俩线之间的电压差为-2v 到-6v 表示逻辑 0。

差分信号是用俩跟线来描述是 0 还是 1,所以本质虽然也是串口,但是 485 是半双工,在软件编程中多了一个切换接收或者发送的操作,其他同串口编程一样。

串口子系统框架

alt text

使能内核驱动程序

#默认都是开着的
Device Drivers --->Character devices --->Serial drivers --->8250/16550 and compatible serial support

串口驱动8250🏎,8250通用的串口程序,不光在在ARM中使用,在x86中也有使用,是一个非常完善的驱动程序,相当于一个“汽车轮子”,而我们在造车时就不需要再重复造车轮了。

alt text

串口的应用编程

串口不像SPIIIC的注册流程,不需要在driver层注册驱动,完善控制器的相关结构体和必要的配置信息,而是直接在应用层使用驱动函数即可。

串口的操作流程

了解一下串口的配置结构体: termios 用于控制非同步通信端口。 这个结构包含了至少下列成员:

tcflag_t c_iflag;      /* 输入模式 */
tcflag_t c_oflag;      /* 输出模式 */
tcflag_t c_cflag;      /* 控制模式 */
tcflag_t c_lflag;      /* 本地模式 */
cc_t c_cc[NCCS];       /* 控制字符 */struct termios
{
unsigned short c_iflag; /* 输入模式标志*/
unsigned short c_oflag; /* 输出模式标志*/
unsigned short c_cflag; /* 控制模式标志*/
unsigned short c_lflag; /*区域模式标志或本地模式标志或局部模式*/
unsigned char c_line; /*行控制line discipline */
unsigned char c_cc[NCC]; /* 控制字符特性*/
};
  1. 步骤一:
    保存原来的串口配置,使用 tcgetattr 函数获取原来的 termio .结构体。
struct termios newtio, oldtio;
tcgetattr(fd, &oldtio);
  1. 步骤二:
    设置 c_cflag,打开 CLOCAL(使能本地连接) 和 CREAD(使能接收) 并清空 CSIZE(数据位)
  2. 步骤三:
    设置 c_cflag 中的数据位。

alt text

  1. 步骤四:
    设置奇偶校验位。
    奇验位:
newtio.c_cflag|= PARENB             //使能奇偶校验
newtio.c_cflag|= PARODD;            //使能奇校验    
newtio.c_iflag|=(INPCK|ISTRIP);      

ISTRIP 的作用是在串口接收数据时,自动剥离每个字符的最高位(第八位),以确保接收到的数据在传递给应用程序之前是正确的七位表示形式。
偶验位:

newtio.c_iflag =(INPCK|ISTRIP)
newtio.c_cflag|= PARENB;
newtio.c_cflag &= ~PARODD;      //清零
break;

无校验:

newtio.c cflag &= ~PARENB;
  1. 步骤五:
    设置波特率,使用函数 cfsetispeed,cfsetospeed。注意波特率前需要加 B,如 B9600

  2. 步骤六:
    设置停止位,如设置 1 位停止位

newtio.c_cflag &= ~CSTOPB;
  1. 步骤七:
    刷新输入队列:使用函数 tcflush(fd,TCIFLUSH);

fd 是使用open函数打开文件得到的串口句柄
TCIFLUSH: 刷新输入队列
TCOFLUSH: 刷新输出队列
TCIOFLUSH:刷新输入输出队列

  1. 步骤八:
    使用函数 tcsetattr(fd,TCSANOW, &newtio);设置配置

TCSANOW: 设置立刻生效
TCSADRIN:发送了所有输出以后设置才生效。

  1. 步骤九:
    使用 open 函数打开串口
open("串口节点", O_RDWR | O_NOCTTY | O_NDELAY);

O_RDWR: 读写权限
O_NOCTTY: 表示不占用终端,即这个程序不会成为这个串口的控制终端
O_NDELAY: 表示非阻塞

源Code

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>#define UART_NAME / dev / ttyS3
int set_uart(int fd, int speed, int bits, char check, int stop)
{struct termios newtio, oldtio;if (tcgetattr(fd, &oldtio) != 0){printf("tcgetattr oldtio is error\n");return -1;}bzero(&newtio, sizeof(newtio));newtio.c_cflag |= CLOCAL | CREAD;newtio.c_cflag &= ~CSIZE;switch (bits){case 7:newtio.c_cflag |= CS7;break;case 8:newtio.c_cflag |= CS8;break;}switch (check){case 'N':newtio.c_cflag &= ~PARENB; // 失能// newtio.c_iflag &= ~INPCK;  // 不使用break;case 'E':newtio.c_cflag |= PARENB;           // 使能newtio.c_cflag &= ~PARODD;          // 使用偶校验newtio.c_iflag |= (INPCK | ISTRIP); // 去除输入字符第八位break;case 'O':newtio.c_cflag |= PARENB;newtio.c_cflag |= PARODD;newtio.c_iflag |= (INPCK | ISTRIP);break;}switch (speed){case 9600:cfsetispeed(&newtio, B9600);cfsetospeed(&newtio, B9600);break;case 115200:cfsetispeed(&newtio, B115200);cfsetospeed(&newtio, B115200);break;default:break;}switch (stop){case 1:newtio.c_cflag &= ~CSTOPB;break;case 2:newtio.c_cflag |= CSTOPB;break;}tcflush(fd, TCIFLUSH);if (tcsetattr(fd, TCSANOW, &newtio) != 0){printf("tcsetattr newtio is error\n");return -2;}return 0;
}int main()
{int fd;char buf[128];int count;fd = open("/dev/ttyS3", O_RDWR | O_NOCTTY | O_NDELAY);if (fd < 0){printf("open error\n");return -1;}set_uart(fd, 115200, 8, 'N', 1);while (1){memset(buf, 0, sizeof(buf));count = read(fd, buf, sizeof(buf));// printf("Having a receive\n");buf[count] = '\0';if (count > 0){printf("read data is %s\n", buf);}}return 0;
}

使用多线程方式进行读写

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>int uartOpen(const char *device, const int baud)
{struct termios options;speed_t myBaud;int status, fd;switch (baud){case 9600:myBaud = B9600;break;case 115200:myBaud = B115200;break;default:return -2;}fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);if (fd == -1){perror("open");return -1;}fcntl(fd, F_SETFL, O_RDWR); // 设置串口阻塞办法// 获取和修改当前选项tcgetattr(fd, &options);cfmakeraw(&options); // 将终端设置为原始模式8N1无流控cfsetispeed(&options, myBaud); // 设置输入波特率cfsetospeed(&options, myBaud); // 设置输出波特率options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB;          options.c_cflag &= ~CSTOPB; // 设置1位的停止位options.c_cflag &= ~CSIZE; // 用数据位掩码清空设置options.c_cflag |= CS8;    // 设置8位的数据位tcsetattr(fd, TCSANOW, &options); // 使上面新的设置生效usleep(10000); // 10msreturn fd;
}int uartfd;/* 向串口发送数据的线程 */
void *sendDatas()
{int cnt;char *buffer = (char *)malloc(64);while (1){memset(buffer, '\0', sizeof(buffer));printf("send -> ");scanf("%s", buffer);/*向串口1对应的设备文件写入buffer的数据*/cnt = write(uartfd, buffer, strlen(buffer));if (cnt < 0)printf("Serial send datas error\n");}
}/* 读取串口数据的线程 */
void *recvDatas()
{int cnt, readSize;char *buffer = (char *)malloc(64);while (1){/* 判断串口是否有数据 */if (ioctl(uartfd, FIONREAD, &cnt) == -1){perror("ioctl");return 0;}else{readSize = read(uartfd, buffer, cnt); // 读取数据到buffer中if (readSize > 0)printf("recv -> %s", buffer); // 读取成功再打印}memset(buffer, '\0', sizeof(buffer));}
}
int main(int argc, char **argv)
{pthread_t sendThread, recvThread;uartfd = uartOpen("/dev/ttyS3", 115200);if (uartfd == -1){printf(" open error\n");return -1;}else{printf("open  succeed.\n");}/* 主函数中定义出两个线程用于接收和发送数据 */pthread_create(&sendThread, NULL, sendDatas, NULL);pthread_create(&recvThread, NULL, recvDatas, NULL);/* 主线程每10秒发送心跳包 */while (1){char alive[] = "I am alive\r\n";write(uartfd, alive, strlen(alive));sleep(10);}return 0;
}

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

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

相关文章

【react小项目】bmi-calculator

bmi-calculator 目录 bmi-calculator初始化项目01大致布局01代码 02完善样式02代码 03输入信息模块03代码 04 使用图表04代码 05详细记录信息渲染05代码 06 让数据变成响应式的06-1输入框的数据处理06-2图表&#xff0c;和记录信息的区域数据处理 07 删除功能&#xff0c;撤销功…

基于C#开发web网页管理系统模板流程-主界面统计功能完善

点击返回目录-> 基于C#开发web网页管理系统模板流程-总集篇-CSDN博客 前言 紧接上篇->基于C#开发web网页管理系统模板流程-主界面管理员入库和出库功能完善_c#web程序设计-CSDN博客 统计功能是管理系统很常见的功能&#xff0c;例如仓库管理系统要统计某时间段的出入库以…

QT信号与槽/窗口组件优化/使用QT制作QQ登录界面

使用手动连接&#xff0c;将登录框中的取消按钮使用第二中连接方式&#xff0c;右击转到槽&#xff0c;在该槽函数中&#xff0c;调用关闭函数 将登录按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断u界面上输入的账号是否为"admin"&#xff0c;…

永磁同步直线电机(PMLSM)控制与仿真3-永磁同步直线电机数学三环控制整定

文章目录 1、电流环参数整定2、速度环参数整定3、位置环参数整定 写在前面&#xff1a;原本为一篇文章写完了永磁同步直线电机数学模型介绍&#xff0c;永磁同步直线电机数学模型搭建&#xff0c;以及永磁同步直线电机三环参数整定及三环仿真模型搭建&#xff0c;但因为篇幅较长…

HTML前端

html 超文本标记语言 文本&#xff1a;文字字符 超文本&#xff1a;网页内容 标记&#xff1a;标签 标识 提供许多标签&#xff0c;不同标签功能不同&#xff0c;网页就是通过这些标签描述出来的&#xff0c;最终由浏览器解释运行我们看到的网页 <!-- html注释<!DO…

C++ 50 之 继承中的对象模型

继承中的对象模型 在C编译器的内部可以理解为结构体&#xff0c;子类是由父类成员叠加子类新成员而成&#xff1a; #include <iostream> #include <string> using namespace std;class Base03{ public:int m_a; protected:int m_b; private:int m_c; // 哪怕是…

lua对接GPT4实现对话

演示效果&#xff1a; 准备材料&#xff1a; 1、FastWeb网站开发服务&#xff1a;fwlua.com 2、一台服务器 该示例使用开源项目&#xff1a;fastweb 实现。 代码比较简单&#xff0c;主要是两部分&#xff0c;一个lua代码和一个html页面&#xff0c;用来用户发起请求和后台…

面向事件编程之观察者模式

前言 村里的老人常说&#xff1a;真男人就该懂得遵守“三不原则”——不主动、不拒绝、不负责。 一个复杂的软件系统&#xff0c;其中必然会存在各种各样的“对象”&#xff0c;如果在设计之初没有注意控制好耦合度&#xff0c;导致各个对象甚至是函数之间高度耦合&#xff0…

网络安全知识全景地图V1.0 - 20240616更新

网络安全领域的知识全景涵盖了从基础概念到高级技术的广泛内容。博主基于自身十年多的工作经验结合CISSP认证官方教材按照不同的主题和层次梳理出如下高层次的概览地图&#xff0c;可以帮助个人和组织理解网络安全领域的主题。 1.1. 基础理论 1.1.1. 网络安全概述 网络安全的…

React@16.x(29)useRef

目录 1&#xff0c;介绍2&#xff0c;和 React.createRef() 的区别3&#xff0c;计时器的问题 目前来说&#xff0c;因为函数组件每次触发更新时&#xff0c;都会重新运行。无法像类组件一样让一些内容保持不变。 所以才出现了各种 HOOK 函数&#xff1a;useState&#xff0c;u…

Camtasia Studio 2024软件最新版下载【安装详细图文教程】

​Camtasia是美国TechSmith公司出品的一款集电脑屏幕录制、视频剪辑为一体的软件套装。同时包含Camtasia 录制器、Camtasia Studio&#xff08;编辑器&#xff09;、Camtasia 菜单制作器、Camtasia 剧场、Camtasia 播放器和Screencast的内置功能。 安 装 包 获 取 地 址&#x…

AirPlay技术规范及认证资讯

AirPlay是Apple开发的一种无线技术&#xff0c;允许用户将音频、视频或图片从iOS设备、Mac电脑或其他支持AirPlay的设备无线传输到支持AirPlay的接收器设备上&#xff0c;例如智能电视或音响系统。这项技术基于Wi-Fi网络&#xff0c;提供了一种便捷的方式来共享媒体内容。AirPl…

车票信息的请求与显示

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 1 发送与分析车票信息的查询请求 得到了获取车票信息的网络请求地址&#xff0c;然后又分析出请求地址的必要参数以及车站名称转换的文件&#xff…

《软件测试52讲》——测试基础知识篇

1 你真的懂测试吗&#xff1f;从“用户登录”测试谈起 从“用户登录”测试谈起&#xff0c;“用户登录”功能作为测试对象 作为测试工程师&#xff0c;你的目标是要保证系统在各种应用场景下的功能是符合设计要求的&#xff0c;所以你需要考虑的测试用例就需要更多、更全面。 …

2078.两栋颜色不同且距离最远的房子

街上有 n 栋房子整齐地排成一列&#xff0c;每栋房子都粉刷上了漂亮的颜色。给你一个下标从 0 开始且长度为 n 的整数数组 colors &#xff0c;其中 colors[i] 表示第 i 栋房子的颜色。 返回 两栋 颜色 不同 房子之间的 最大 距离。 第 i 栋房子和第 j 栋房子之间的距离是 a…

Linux应用编程 - i2c-dev操作I2C

嵌入式Linux操作I2C设备&#xff0c;我们一般会在内核态编写I2C驱动程序。另外还能在用户空间编写I2C程序&#xff0c;下面介绍相关代码的实现。 i2c-dev框架在内核中封装了I2C通信所需要的所有通信细节&#xff0c;I2C适配器会在/dev目录下创建字符设备&#xff0c;例如&#…

kettle从入门到精通 第七十课 ETL之kettle kettle数据校验,脏数据清洗轻松拿捏

场景&#xff1a;输入在指定的错误(错误应涵盖数据类型不匹配的情况)行数内&#xff0c;trans不报错&#xff0c;但通过错误处理步骤捕捉&#xff0c;并记入文件&#xff0c;整个数据管线正常完成直至处理完最后一个输入行。 解决方案&#xff1a;使用步骤【数据检验】进行处理…

本周MoonBit新增Wasm1引用计数支持、语法即将添加错误恢复机制

MoonBit更新 【Wasm MVP】Wasm1 后端添加基于 Perceus 算法的引用计数支持 【语法】throw raise try catch 均被保留为关键字 为了即将添加的错误处理机制 【Core】List与sorted_map被移动至core/immut下 List被移动至core/immut/list包中&#xff0c;并被移除内置类型支持 …

哈希表、递归在二叉树中的应用-1372. 二叉树中的最长交错路径

题目链接及描述 1372. 二叉树中的最长交错路径 - 力扣&#xff08;LeetCode&#xff09; 题目分析 题目所述&#xff0c;计算在二叉树中交替遍历的最大深度【左->右->左】【右->左->右】&#xff0c;例如对于从当前根节点root出发&#xff0c;则此时遍历方向有两个…

今日成果2024-6-7 TrustZone TEE安全SDK开发指南

Rockchip Vendor Storage Application Note.pdf OK 开机下&#xff0c;可以实现Vendor Storage的读写。 0ms时同步RTC时间 OK Rockchip_Developer_Guide_TEE_SDK_CN.pdf 什么是TrustZone 此系统方法意味着可以保护安全内存、加密块、键盘和屏幕等外设&#xff0c;从而可确…