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); // 10ms
    return 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;
}


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

(游戏:三个数的加法)编写程序,随机产生三个一位整数,并提示用户输入这三个整数的和,判断用户输入的和是否正确。

(游戏:三个数的加法)编写程序&#xff0c;随机产生三个一位整数&#xff0c;并提示用户输入这三个整 数的和&#xff0c;判断用户输入的和是否正确。 package myjava; import java.math.*; import java.util.Scanner; public class cy {public static void main(String[]args)…

自动化数据驱动?最全接口自动化测试yaml数据驱动实战

前言 我们在做自动化测试的时候&#xff0c;通常会把配置信息和测试数据存储到特定的文件中&#xff0c;以实现数据和脚本的分离&#xff0c;从而提高代码的易读性和可维护性&#xff0c;便于后期优化。 而配置文件的形式更是多种多样&#xff0c;比如&#xff1a;ini、yaml、…

微型操作系统内核源码详解系列五(1):arm cortex m3架构

系列一&#xff1a;微型操作系统内核源码详解系列一&#xff1a;rtos内核源码概论篇&#xff08;以freertos为例&#xff09;-CSDN博客 系列二&#xff1a;微型操作系统内核源码详解系列二&#xff1a;数据结构和对象篇&#xff08;以freertos为例&#xff09;-CSDN博客 系列…

MySQL 中 Varchar(50) 和 varchar(500) 区别是什么?

一. 问题描述 我们在设计表结构的时候&#xff0c;设计规范里面有一条如下规则: 对于可变长度的字段&#xff0c;在满足条件的前提下&#xff0c;尽可能使用较短的变长字段长度。 为什么这么规定&#xff1f;我在网上查了一下&#xff0c;主要基于两个方面 基于存储空间的考…

洗地机哪款好?洗地机十大名牌排行榜

随着科技的发展&#xff0c;各种家居清洁工具层出不穷&#xff0c;为我们的生活带来了诸多便利。在众多清洁工具中&#xff0c;洗地机的清洁效果更受大家喜爱&#xff0c;它能够完美解决了扫地机无法做到的干湿垃圾“一遍清洁”效果&#xff0c;而且几乎能解决日常生活中所有的…

2024下半年教师资格证报名全流程来啦!!

2024下半年教师资格证报名全流程来啦&#xff01;&#xff01; 一、重要时间节点 ✅2024下半年教师资格证考试 ⏰笔试报名&#xff1a;7月5日-8日 ⏰笔试时间&#xff1a;9月15日 ⏰笔试成绩查询&#xff1a;11月8日 ⏰面试报名&#xff1a;11月8日起 ⏰面试时间&#xff1a;12…

网络基础

文章目录 网络发展协议网络分层结构局域网通信IP地址和MAC地址 网络发展 独立模式: 计算机之间相互独立 ; 网络互联: 多台计算机连接在一起, 完成数据共享因为人和人之间数据要互相协作&#xff0c;所以网络的出现是必然的&#xff0c;同时随着网络发展&#xff0c;必然会产生…

c++20 规范, vs2019 , 头文件 <mutex> ,注释以及几个探讨

&#xff08;1 探讨一&#xff09; mutex 这个名称的来源是 mutual exclusion &#xff1a;互相排斥。 mutex 与 recursive_mutex 的数据成员的定义如下&#xff1a; 测试如下&#xff1a; 运行以下&#xff1a; 以及&#xff1a; &#xff08;2 探讨二&#xff09; recursive_…

聚焦赛宁网安竞赛平台+赛事服务,引领网络安全竞赛新潮流

第八届XCTF总决赛将在2024年6月22日于中国成都震撼开启&#xff0c;本届总决赛分为个人Live Solo和团队KOH巅峰对决两个赛道&#xff0c;从个人和团队多角度全方位考察参赛人员的竞技水平。 巅峰对决 智慧的火花在此碰撞 个人Live Solo赛制 Live Solo赛分为晋级赛和Solo赛。…

长亭网络通信基础

长亭笔试之前就已经学过一遍了 这算温故而知新吧 TCP/IP 首先我在这里默写一下之前的7层和4层 应用层 应 【表示层 数据格式转换 传 【会话层 …

智能驾驶新高度:比亚迪无图城市领航夜闯城中村

在各种创新科技日新月异的今天&#xff0c;智能驾驶技术也给我们带来了越来越多的惊喜。 近日&#xff0c;比亚迪旗下的高端品牌腾势&#xff0c;凭借其全新车型腾势N7&#xff0c;在智能驾驶领域展现出了令人瞩目的实力。 在一场别开生面的“无图城市领航”实测中&#xff0c;…

大型企业IT基础架构和应用运维体系

大型企业IT基础架构和应用运维体系 在数字化转型的浪潮中&#xff0c;大型企业面临着日益复杂的IT环境。高效的IT基础架构和应用运维体系&#xff0c;是确保企业业务连续性和竞争力的关键。本文将探讨大型企业如何构建强健的IT基础架构&#xff0c;并建立高效的应用运维体系&a…

上网行为管理产品有哪些?好用的四款上网行为管理产品

上网行为管理产品是现代企业网络安全架构中的重要组成部分&#xff0c;它们旨在帮助企业有效监控、管理和控制员工的网络使用行为&#xff0c;确保网络资源的合理利用&#xff0c;保障信息安全&#xff0c;提升工作效率。 以安企神为例&#xff0c;我们将详细介绍它的主要功能…

vulnhub靶场-xxe打靶教程

目录 靶机导入 信息收集 发现IP 端口扫描 目录扫描 漏洞利用 靶机下载地址&#xff1a;XXE Lab: 1 ~ VulnHub 靶机导入 导入虚拟机 开启虚拟机 信息收集 发现IP arp-scan -l 发现靶机IP是192.168.202.150 端口扫描 使用nmap进行扫描 nmap -sS -A 192.168.202.150 …

python文件操作(Windows路径,内置os库)+ 正则表达式(语法与re库)

文章目录 前言1 Windows路径(1)绝对路径 (Absolute Path)(2)相对路径 (Relative Path)一、python文件的读写操作1 读写(1)打开文件file = open(test1.txt,r,encoding=utf-8) --- 打开文件(2)读取文件内容content = file.read() --- 读取全部内容,返回字符串lines = re…

HTML静态网页成品作业(HTML+CSS)—— 明星吴磊介绍网页(5个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有5个页面。 二、作品演示 三、代…

uni-app前端,社区团购系统搭建部署

目录 前言&#xff1a; 一、社区团购模式&#xff1a; 二、社区团购系统功能&#xff1a; 三、总结&#xff1a; 前言&#xff1a; 区团购系统是一种利用互联网技术和组织力量&#xff0c;通过线上线下结合的方式&#xff0c;为社区居民提供方便快捷的商品和服务采购平台。…

Qwen2的各模型性能、占用显存和推理速度比较(摘自官方文档)

Qwen2的各模型性能、占用显存和推理速度比较&#xff08;摘自官方文档&#xff09; 性能 推理速度&#xff08;从大到小&#xff09; 72B 57B-A14B 7B 1.5B 0.5B

邮件钓鱼--有无SPF演示--Swaks

目录 临时邮箱网址: Swaks 简单使用说明&#xff1a;(kali自带) 操作流程: 无SPF:(直接伪造发信人) 演示1 演示2 演示3 ​编辑 有SPF:--演示 临时邮箱网址: http://24mail.chacuo.net/ https://www.linshi-email.com/ Swaks 简单使用说明&#xff1a;(kali自带) -t –t…

【PPT教程】一键重置幻灯片背景的方法,新建幻灯片带默认背景

目的是替换18届的研电赛ppt背景为19届 这里写目录标题 1.设计->设置背景格式2.图片或纹理填充->插入3.选择需要替换为背景的照片4.点击下方的应用到全部 1.设计->设置背景格式 2.图片或纹理填充->插入 3.选择需要替换为背景的照片 4.点击下方的应用到全部 此时全部…