Linux--串口屏显示控制实验

一、 实验简介

实验目标:在Linux下通过串口屏显示并控制功能模块的状态和参数
操作系统:Ubuntu 20.04.6 LTS
串口屏:迪文串口屏 DMG48270C043_03W
在这里插入图片描述

二、实现代码-- C语言

   代码功能就是在Linux下使用串口和TCP,重点在于如何处理好串口和网口接收的数据。 通过本实验可以基本掌握如何在Linux下使用串口和TCP。网上很多教程都会介绍Liunx下如何实现串口和TCP这两种通信方式,很少有结合实际应用进行介绍的文章,本文就将结合一个实际应用例子进行分析记录,也是对自己学习这些东西的一个简单总结记录。

2.1 通信协议

  这里的通信协议不是说解释串口或是TCP这样的通信协议,本文指的是在传输数据过程自己定义的数据帧格式,不同的数据需要设置功能模块的不同功能通信协议的有助于我们管理不同的控制信号。发送数据时按以下格式设置好数据后再通过到其他模块。

主控模块<----->功能模块 网口通信TCP

字段指令头指令码数据长度数据
长度(字节)114N
0xAA不同指令N具体数据

主控模块<----->串口屏 串口通信

2.2 串口通信

   Linux的串口表现为设备文件。实验使用的串口屏是USB扩展的,实验前需要安装CH341驱动串口设备文件命名为dev/ttyCH341USB*,不同的硬件平台对串口设备文件的命名有所区别。

2.2.1 初始化串口

   在Linux下,不管是设备、文档、可执行程序,对于内核来说都是读写文件,会涉及到open、read、write的操作,对于文件操作就有了“阻塞”和“非阻塞”的概念。不同模式对文件的处理方式会有不同,使用的场景也不一样。

“阻塞”的定义
对于 read,当串口的接收缓冲区没有数据的时候,read函数会阻塞在那里,不返回,程序也无法下一步执行,一直到串口的接收缓冲区中有数据可读时,read读到了想要长度的字节数后才会返回,返回值为读到的字节数。
对于write,当串口发送缓冲区满时,或者剩下的空间小鱼将要写入的字节数时,则write阻塞,一直到串口的发送缓冲区中剩下的空间大于等于将要写入的字节数,再执行写操作,返回写入的字节数。

“非阻塞”的定义
对于read,当串口的接收缓冲区中没有数据时,read操作立即返回,返回值为0.
对于write,当串口发送缓冲区满,或者剩下的空间小鱼将要写入的字节数时,write仍然会被执行,写入当前串口发送缓冲区剩下的空间字节数,然后返回写入的字节数。

serial init

    serial = serial_new();
    if (serial_open(serial,"/dev/ttyCH341USB1",115200) <0) //打开并设置设备文件
    {
        serial_free(serial);
        printf("serial open failed!\n");
    }
    else
    {   
        printf("serial opened! \n");
    }

serial_open 函数
   网上很多都是直接用的open函数,实验使用的函数同样基于open函数通过该函数设置好串口,默认使用的阻塞模式。

int serial_open(serial_t *serial, const char *path, uint32_t baudrate) {
    return serial_open_advanced(serial, path, baudrate, 8, PARITY_NONE, 1, false, false);//参数设置
}

int serial_open_advanced(serial_t *serial, const char *path, uint32_t baudrate, unsigned int databits, serial_parity_t parity, unsigned int stopbits, bool xonxoff, bool rtscts) {
    struct termios termios_settings;

    /* Validate args */
    if (databits != 5 && databits != 6 && databits != 7 && databits != 8)
        return _serial_error(serial, SERIAL_ERROR_ARG, 0, "Invalid data bits (can be 5,6,7,8)");
    if (parity != PARITY_NONE && parity != PARITY_ODD && parity != PARITY_EVEN)
        return _serial_error(serial, SERIAL_ERROR_ARG, 0, "Invalid parity (can be PARITY_NONE,PARITY_ODD,PARITY_EVEN)");
    if (stopbits != 1 && stopbits != 2)
        return _serial_error(serial, SERIAL_ERROR_ARG, 0, "Invalid stop bits (can be 1,2)");

    memset(serial, 0, sizeof(serial_t));

    /* Open serial port */
    if ((serial->fd = open(path, O_RDWR | O_NOCTTY)) < 0)
        return _serial_error(serial, SERIAL_ERROR_OPEN, errno, "Opening serial port \"%s\"", path);
    fcntl(serial->fd,F_SETFL,0);
    memset(&termios_settings, 0, sizeof(termios_settings));

    /* c_iflag */

    /* Ignore break characters */
    termios_settings.c_iflag = IGNBRK;
    if (parity != PARITY_NONE)
        termios_settings.c_iflag |= INPCK;
    /* Only use ISTRIP when less than 8 bits as it strips the 8th bit */
    if (parity != PARITY_NONE && databits != 8)
        termios_settings.c_iflag |= ISTRIP;
    if (xonxoff)
        termios_settings.c_iflag |= (IXON | IXOFF);

    /* c_oflag */
    termios_settings.c_oflag = 0;

    /* c_lflag */
    termios_settings.c_lflag = 0;

    /* c_cflag */
    /* Enable receiver, ignore modem control lines */
    termios_settings.c_cflag = CREAD | CLOCAL;

    /* Databits */
    if (databits == 5)
        termios_settings.c_cflag |= CS5;
    else if (databits == 6)
        termios_settings.c_cflag |= CS6;
    else if (databits == 7)
        termios_settings.c_cflag |= CS7;
    else if (databits == 8)
        termios_settings.c_cflag |= CS8;

    /* Parity */
    if (parity == PARITY_EVEN)
        termios_settings.c_cflag |= PARENB;
    else if (parity == PARITY_ODD)
        termios_settings.c_cflag |= (PARENB | PARODD);

    /* Stopbits */
    if (stopbits == 2)
        termios_settings.c_cflag |= CSTOPB;

    /* RTS/CTS */
    if (rtscts)
        termios_settings.c_cflag |= CRTSCTS;

    /* Baudrate */
    cfsetispeed(&termios_settings, _serial_baudrate_to_bits(baudrate));
    cfsetospeed(&termios_settings, _serial_baudrate_to_bits(baudrate));

    /* Set termios attributes */
    if (tcsetattr(serial->fd, TCSANOW, &termios_settings) < 0) {
        int errsv = errno;
        close(serial->fd);
        serial->fd = -1;
        return _serial_error(serial, SERIAL_ERROR_CONFIGURE, errsv, "Setting serial port attributes");
    }
    serial->use_termios_timeout = false;
    return 0;
}

2.2.2 读写串口

fd为文件识别号,buf为收发数组
接收数据

int len;unsigned char buf[11];
len = read(fd, buf, 11);
if (len < 0){    
printf("reading data faile \n");
}

发送数据

int len;
char buf[] = "hello world!";
len = write(fd, buf, sizeof(buf));
if (len< 0) {    
printf("write data to serial failed! \n");
}

2.3 TCP通信

   Socket编程是一种网络编程的方式,用于实现不同计算机之间的数据通信。在网络应用中,Socket是端到端通信的一个抽象概念,它建立在网络模型的传输层之上,提供了一种方式来允许程序跨网络发送和接收数据。最常见的是TCP(传输控制协议)和UDP(用户数据报协议)。

2.4.1 TCP初始化

Socket编程通常涉及以下几个基本步骤:

  1. 创建Socket:首先,需要在客户端和服务器端分别创建Socket。在服务器端,Socket用于监听来自客户端的连接请求;在客户端,Socket用于与服务器建立连接。
  2. 绑定(Bind):服务器端的Socket需要绑定到一个网络地址(IP地址)和端口上,以便客户端能够找到并连接到它。
  3. 监听(Listen):服务器端的Socket开始监听绑定的地址和端口,等待客户端的连接请求。
  4. 接受连接(Accept):当服务器Socket监听到客户端的连接请求时,它会接受这个连接,从而在服务器和客户端之间建立一个新的通信链路。
  5. 数据传输:一旦连接建立,客户端和服务器端就可以通过读写操作在这个连接上发送和接收数据。数据的发送和接收可以是阻塞的也可以是非阻塞的,这取决于Socket的配置。
  6. 关闭连接:数据传输完成后,双方可以关闭连接。关闭连接的一方会向对方发送一个连接释放信号,以结束会话。
#include"TCPserver.h"
 
int TCPserverinit(void)
{
	//服务器监听套接字和连接套接字
	listen_fd=-1;
	connect_fd=-1;
	struct sockaddr_in servaddr;//定义服务器对应的套接字地址
	//服务器接收和发送缓冲区
	uint8_t sendbuf[MAXLINE], recbuf[MAXLINE];
 
	//初始化套接字地址结构体
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;//IPv4
	servaddr.sin_port = htons(PORT);//设置监听端口
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//表示接收任意IP的连接请求
 
	//创建套接字
	if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
		//如果创建套接字失败,返回错误信息
		//strerror(int errnum)获取错误的描述字符串
		printf("create socket error: %s(error: %d)\n", strerror(errno), errno);
		exit(0);
	}
	//绑定套接字和本地IP地址和端口
	if(bind(listen_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
		//绑定出现错误
		printf("bind socket error: %s(error: %d)\n", strerror(errno), errno);
		exit(0);
	}
 
	//使得listen_fd变成监听描述符
	if(listen(listen_fd, 10) == -1){
		printf("listen socket error: %s(error: %d)\n", strerror(errno), errno);
		exit(0);
	}
	//accept阻塞等待客户端请求
	printf("等待客户端发起连接\n");
	if((connect_fd = accept(listen_fd, (struct sockaddr*)NULL, NULL)) == -1){
		printf("accept socket error: %s(error: %d)\n", strerror(errno), errno);
	    }
}

2.4.1 TCP收发数据

   在Linux下,都是按读写文件的方式去管理设备。
接收数据

int len;unsigned char buf[11];
len = read(fd, buf, 11);
if (len < 0){    
printf("reading data faile \n");
}

发送数据

int len;
char buf[] = "hello world!";
len = write(fd, buf, sizeof(buf));
if (len< 0) {    
printf("write data to serial failed! \n");
}

2.4 数据收发处理

2.4.1 串口屏

void* ScreenTranslateMessage(void* arg)
{
    uint8_t i,j=0;
    uint8_t hedebyte[3];
    uint8_t onebyte;
    while (1)
    {
        serial_read(serial,&onebyte, 1);
        usleep(5000);
        if(onebyte==0X5A) //识别帧头标识
            {
            hedebyte[0]=onebyte;
            onebyte=0;
            serial_read(serial, hedebyte+1, 2);
            usleep(5000);
            }
        if (hedebyte[0]==0X5A && hedebyte[1]==0XA5)
            {
            sdatalen=hedebyte[2];
            serial_read(serial,sdata,sdatalen);//接受串口屏反馈数据
            usleep(2500);
                for (i = 0; i<3; i++)
                {
                    printf("%02x",hedebyte[i]);
                }
                for (j = 0; j<sdatalen; j++)//数据
                {
                    printf("%02x",sdata[j]);
                }
                printf("\n");
            serialPolling(sdata,sdatalen); //按照通信协议依次处理数据,通过TCP反馈到功能模块
            memset(sdata,0,sizeof(sdata));
            sdatalen=0;
            }
        usleep(5000);
        memset(hedebyte,0,sizeof(hedebyte));
    }  
    return NULL;
}
uint8_t serialPolling(uint8_t *data,uint8_t datalen)
{
    int i=0,j=0;
    double power=2;
    uint8_t chartonum=0;

    if(datalen==0) 
    {   return 0;}
    else
    {
       LCDaddr=((uint16_t)data[1]<<8)|data[2];//识别串口屏部件
       switch (LCDaddr)
       {
        case State_check:
            sendbuf[0]=0xAA;
            sendbuf[1]=0x01;
            sendbuf[2]=0x00;
            sendbuf[3]=0x00;
            sendbuf[4]=0x00;
            sendbuf[5]=0x00; //设置数据帧
            write(connect_fd, sendbuf, 6);//通过TCP反馈到功能模块
            break;
        case IP_address : //获得网络网址,将bits数据转化为网址
            for(i=4;i < 19;i ++)
            {
                if(data[i]!= 0xFF)
                {
                    IPaddress[i] = data[i];
                    if(data[i]!=0x2e)
                    {
                    chartonum=(IPaddress[i]-0x30)*pow(10,power);
                    IP_buffer[j]=IP_buffer[j]+chartonum;
                    power--;
                    }
                    else
                    {
                        j++;
                        power=2;
                    }
		        }
	        }
            memset(IPaddress,0,15);
            break;
        default:
        break;
       }
    }

}

2.4.2 功能模块

void* TCPtranslateMessage(void* arg)
{
    while (1)
    {   
        //读取客户端发来的信息
        ssize_t tdatalen = read(connect_fd,tdata, sizeof(tdata));
        if(tdatalen <= 0){
                return 0;
        }
        for (uint8_t i = 0; i < tdatalen; i++)
        {
            printf("%02x",tdata[i]);
        }
        printf("\n");
        TCPPolling(tdata,tdatalen);//处理TCP接收数据,和串口类似先找帧头然后针对不同数据进行处理
        memset(tdata,0,sizeof(tdata));
    }
    return NULL;
}

根据自己的需求去设计处理数据的函数

uint8_t TCPPolling(uint8_t *data,uint8_t datalen)
{
    uint8_t position=6;
    if(datalen==0) 
    {   return 0;}
    else
    {
       command=data[1];
       switch (command)
       {
       case 01:
        /* code */
        //状态显示,设置屏幕显示数据
        DW_SetValue(Running_state,data[position]);
        DW_SetValue(Input_datastate,data[position+1]);
        break;
       case 02:
        DW_SetValue(Output_power,data[position]);
        DW_SetValue(PAR_coefficient,data[position+1]);
        break;
       case 03:
        /* code */
        break;
       default:
        break;
       }
    }

三、参考资料

《T5L DGUSII应用开发指南》
串口通信协议和Linux下的串口编程
TCP的socket详解
迪文串口屏教程
迪文串口屏(T5L2 & DGUS II)开发 – 入门

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

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

相关文章

linux 文本编辑命令【重点】

目录 vi&vim介绍 vim安装 vim使用 查找命令 find grep 文本编辑的命令&#xff0c;主要包含两个: vi 和 vim vi&vim介绍 作用: vi命令是Linux系统提供的一个文本编辑工具&#xff0c;可以对文件内容进行编辑&#xff0c;类似于Windows中的记事本 语法: vi file…

MySQL锁三部曲:临键、间隙与记录的奇妙旅程

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 MySQL锁三部曲&#xff1a;临键、间隙与记录的奇妙旅程 前言临键锁的奥秘间隙锁记录锁 前言 在数据库世界中&#xff0c;锁是维护数据完整性的一种关键机制。而MySQL中的临键锁、间隙锁和记录锁则是锁…

Git笔记——4

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、操作标签 二、推送标签 三、多人协作一 完成准备工作 协作开发 将内容合并进master 四、多人协作二 协作开发 将内容合并进master 五、解决 git branch -a…

37、IO进程线程/使用消息队列完成进程间通信20240225

一、使用消息队列完成两个进程间相互通信。 代码&#xff1a; 进程1代码&#xff1a; #include<myhead.h> struct msgbuf {long mtype;//消息类型char mtext[1024];//消息正文 }; //宏定义结构体消息正文大小 #define MSGSIZE (sizeof(struct msgbuf)-sizeof(long)) i…

大学生多媒体课程学习网站thinkphp+vue

开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp 前端框架&#xff1a;vue.js 服务器&#xff1a;apache 数据库&#xff1a;mysql 运行环境:phpstudy/wamp/xammp等开发背景 &#xff08;一&#xff09; 研究课程的提出 &#xff08;二&#xff09;学习网站的分类与界定…

Three.js 基础属性

三维坐标系 辅助观察坐标系 THREE.AxesHelper()的参数表示坐标系坐标轴线段尺寸大小&#xff0c;你可以根据需要改变尺寸。 // AxesHelper&#xff1a;辅助观察的坐标系 const axesHelper new THREE.AxesHelper(150); scene.add(axesHelper);材质半透明设置 设置材质半透明…

Vulnhub靶机:Hacker_Kid

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;Hacker_Kid&#xff08;10.0.2.42&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://download.vulnhub.com/hac…

Python和Jupyter简介

在本notebook中&#xff0c;你将&#xff1a; 1、学习如何使用一个Jupyter notebook 2、快速学习Python语法和科学库 3、学习一些IPython特性&#xff0c;我们将在之后教程中使用。 这是什么&#xff1f; 这是只为你运行在一个个人"容器"中的一个Jupyter noteboo…

蓝桥杯备战刷题(自用)

1.被污染的支票 #include <iostream> #include <vector> #include <map> #include <algorithm> using namespace std; int main() {int n;cin>>n;vector<int>L;map<int,int>mp;bool ok0;int num;for(int i1;i<n;i){cin>>nu…

【前端素材】推荐优质后台管理系统Start Admin平台模板(附源码)

一、需求分析 后台管理系统是一种用于管理网站、应用程序或系统的工具&#xff0c;它通常作为一个独立的后台界面存在&#xff0c;供管理员或特定用户使用。下面详细分析后台管理系统的定义和功能&#xff1a; 1. 定义 后台管理系统是一个用于管理和控制网站、应用程序或系统…

Github开源贡献者的狂欢——教你如何免费领取价值$200的Starknet空投

前言&#xff1a; 2024 又迎来了四年一度的 BTC 减半时刻&#xff0c;币圈仿佛一下又热闹了起来&#xff0c;这几天有一个新的基于 ETH 的项目诞生了&#xff1a;StarkNet&#xff0c;代号 STRK&#xff0c;凡是在前 5000 个开源项目贡献过至少 3 个 commit 的程序猿都会被空投…

C#,数组数据波形排序(Sort in Wave Form)的朴素算法与源代码

1 波形排序 所谓“波形排序”就是一大一小。 将n个身高互不相同的人排成一行 ,对于每个人 ,要求他要么比相邻的人均高 ,要么比相邻的人均矮 ,问共有多少种排法 ,这一问题称为波形排列问题。 2 源程序 using System; using System.Collections; using System.Collections.Gen…

Makefile静态库动态库的构建和链接之工程实用篇

静态库和动态库的构建和链接 现有C工程目录结构如下&#xff1a; add.h int add(int a, int b);add.cpp #include "add.h"int add(int a, int b) {return ab; }main.cpp #include <iostream> #include "add.h"int main() {std::cout << a…

Spring Boot 项目集成camunda流程引擎

使用camunda开源工作流引擎有&#xff1a;通过docker运行、使用springboot集成、部署camunda发行包、基于源代码编译运行等多种方式。 其中&#xff0c;通过源代码编译运行的方式最为复杂&#xff0c;具体参考&#xff1a;https://lowcode.blog.csdn.net/article/details/1362…

个人博客系列-前端部署-创建框架(4)

项目环境介绍 Vue3 Vite TypeScript 服务器&#xff1a;阿里云contos node版本&#xff1a;v18.18.2 npm版本&#xff1a;v10.2.4 执行下面一行命令&#xff0c;创建vue3框架 npm create vuelatest修改端口&#xff1a;9528&#xff0c; 此步骤可以忽略&#xff08;使用默…

云呐矿井智能化运维工是什么?智能机器人运维岗位

煤矿智能运维是指利用先进的信息技术和自动控制&#xff0c;在煤矿生产过程中对煤矿设备进行监测、维护和管理。其职责和工作任务主要包括: 工作环境:  面对复杂的地质条件和极端的气候环境&#xff0c;煤矿智能运维工程师往往需要在地下煤矿、监测中心等环境中工作。因此&a…

MCU多核异构通信原理

摘要&#xff1a; 本文结合瑞萨RZ/G2L 多核处理器&#xff0c;给大家讲述一下多核异构设计及通信的原理。 随着电子技术的不断发展&#xff0c;以及市场需求的日益增长&#xff0c;嵌入式系统不仅要求执行复杂的控制任务&#xff0c;还需要实时地采集和处理数据。 为了满足这…

游戏配置内存“瘦身”策略

背景 游戏配置数据绝对是游戏服务器进程的内存大头,有些游戏服务器单纯数据配置的容量就超过一个G。因此,这部分内存优化也就放在首要位置了。 优化策略 在《服务器进程如何降低内存》一文中,我们讲述了可以通过“优化游戏配置缓存”来降低游戏服务器进程的内存使用量。本…

基于协同过滤算法的体育商品推荐系统

摘要 本文深入探讨了基于协同过滤算法的体育商品推荐系统的构建方法及其在电子商务中的重要性。首先&#xff0c;介绍了协同过滤算法的基本原理&#xff0c;包括用户-商品矩阵、相似度度量和推荐生成。其次&#xff0c;探讨了协同过滤算法在体育商品推荐中的两种主要应用方式&a…

sql-labs第46关(order by盲注脚本)

一、环境 网上有自己找 二、解释 order by 注入我们看他的true和false来进行注入出来 二、实操 让我们用sort 看看源码 最终我们的id是放到order by后面了 如果我们直接用列去排序 ?sortusername/password username&#xff1a; password&#xff1a; 可以看到顺序是不…