CODESYS开发教程9-文件读写(CAA File库)

news2024/9/21 0:33:20

今天继续我们的小白教程,老鸟就不要在这浪费时间了😊。

前面一期我们介绍了CODESYS的定时器及触发相关的功能块。这一期主要介绍CODESYS的CAA.File库中的目录和文件读写功能块,主要包括文件路径、名称、大小的获取以及文件的创建、打开、读、写、拷贝和删除功能等。

一、文件库类型简介

文件读写有两种库:CAA File(File Access)库和SysFile库。

1.CAA File(File Access)

CAA File库包含用于访问文件目录和文件的功能块。

对于3.5.17以前的版本,通常是使用CAA File库。由于CAA File库中使用的部分类型定义在另外一个库CAA Types Extern中,因此使用时还需要包含该库。

在3.5.17及以后版本,直接使用File Access即可,如下图所示。

2.SysFile

SysFile属于CODESYS比较底层的库,函数及功能与C语言非常接近。实际上CAA File底层也是调用该库来实现的。

二、CAA.File库介绍

CAA.File库包含用于访问目录和文件的操作。

1.枚举定义

(1)文件属性定义ATTRIB

定义GetAttribute功能块获取的文件属性值。

名称

初始值

说明

ARCHIVE

0

档案文件

HIDDEN

1

隐藏文件

NORMAL

2

没有设置任何其他属性的文件

READONLY

3

只读文件

(2)文件访问模式MODE

定义file.Open功能块打开文件的访问模式。

名称

初始值

说明

MWRITE

0

写访问,文件将被覆盖或创建

MREAD

1

读取访问,文件将仅打开进行读取

MRDWR

2

读取和写入访问,文件将被覆盖或创建

MAPPD

3

文件将以WRITE模式打开,但写入的数据将附加在文件末尾

MREADPLUS

4

打开具有读/写权限的现有文件。如果文件不存在,则打开失败

MWRITEPLUS

5

创建具有读/写权限的新文件。如果文件确实存在,则丢弃内容(与file_MRDWR相同)

MAPPENDPLUS

6

使用附加(读/写)访问权限打开现有文件。如果文件不存在,“打开”将创建一个新文件

(3)错误码定义ERROR

定义在处理CAA_File.library的函数时可能会出现错误值。

名称

初始值

说明

NO_ERROR

0

无错误

FIRST_ERROR

5100

错误枚举定义的起始编号

TIME_OUT

5101

超过时间限制

ABORT

5102

xAbort信号激活导致操作终止

HANDLE_INVALID

5103

无效文件句柄

NOT_EXIST

5104

文件或目录不存在

EXIST

5105

文件或目录已经存在

NO_MORE_ENTRIES

5106

没有其他条目可用

NOT_EMPTY

5107

文件或目录不为空

READ_ONLY_CAA

5108

文件或目录写保护

WRONG_PARAMETER

5109

参数错误

ERROR_UNKNOWN

5110

未知错误

WRITE_INCOMPLETE

5111

数据写入不完整

FILE_NOT_IMPLEMENTED

5112

功能未实现

ASM_CREATEJOB_FAILED

5113

AsyncManager的作业创建失败

FILE_OPERATION_DENIED

5114

ForceFilePath/ForceIecFilePath无法访问(这个错误我也不知道是什么意思…)

FIRST_MF

5150

制造商错误定义的起始编号

LAST_ERROR

5199

错误枚举定义的编号上限

说实话,上表中定义的很多错误我用了这么久也没碰到过,常见的应该是标粗的那几个。

另外需要注意的是,以上几个枚举定义都需要通过全局变量名“FILE.xxx”来访问,比如只读文件属性要写为FILE.ATTRIB.READONLY,否则编译时会报标识未定义错误。

2.FILE_DIR_ENTRY结构

保存目录条目或文件的信息。

sEntry:CAA.FILENAME,文件或目录名。

szSize:CAA.SIZE,文件大小。

xDirectory:TRUE为目录, FALSE为文件。

xExclusive:文件访问模式,TRUE为独占访问模式,FALSE为多个实例可以同时访问。

dtLastModification:上次修改的日期和时间,日期时间格式为2023-01-17-11:13:00

注意:使用本结构需要通过FILE.FILE_DIR_ENTRY实现。

3.目录操作功能块

目录操作功能块:

功能块名称

功能

备注

DirOpen

打开目录

DirClose

关闭目录

DirCreate

创建目录

DirList

读取目录条目

DirCopy

拷贝目录

DirRemove

删除目录

依赖于操作系统和文件系统

DirRename

重命名目录

4.文件操作功能块

文件操作功能块如下表所示:

功能块名称

功能

备注

Open

打开文件

Read

读取文件内容

Write

内容写入文件

Flush

将缓冲写入文件

Close

关闭文件

Copy

复制文件

Rename

重命名文件

Delete

删除文件

已打开的文件也可删除,依赖于操作系统和文件系统

EOF

检查是否到达文件结尾

GetAttribute

获取文件属性

GetPos

返回文件访问的当前偏移位置

文件必须通过file.Open打开

SetPos

设置文件访问的当前偏移量

文件必须通过file.Open打开

GetSize

返回文件大小

GetTime

返回上次修改的日期和时间

5.功能块主要参数

由于各个功能块的参数和操作模式基本类似,各个功能块的大部分参数都是类似的,这里就不针对每个功能块的参数一一说明。

xExecute:输入,上升沿开始执行,下降沿复位输出。如果在功能块完成其动作之前出现下降沿,则输出以通常的方式操作,并且仅在动作完成或发生错误时复位。在这种情况下,对应的输出值(xDone,xError)在输出端只持续一个周期。

xAbort:输入,TRUE则立即停止操作,并将所有输出置为初始值。

sDirName:输入,待操作目录名称。

sFileName:输入,待操作的文件名称。

eFileMode:输入,文件操作模式,由FILE.MODE定义。

udiTimeOut:输入,定义功能块因超时而中止操作并输出错误消息的时间,单位µs。

hDir:待操作的目录句柄。

hFile:待操作的文件句柄。

pBuffer:读取或写入数据缓冲区的首地址,通过ADR获取。

szBuffer:要读取的字节数。

xOverWrite:输入,TRUE为覆盖已存在的文件或目录,FALSE为报错。

xDone:输出,TRUE为操作成功。

xAborted:输出,TRUE为操作被用户中止。

xEOF:输出,TRUE为达到文件结尾。

xBusy:输出,TRUE为功能块正在执行中。

xError:输出,TRUE为发生错误,功能块终止运行;FALSE为无错误。

eError:输出,错误ID,由ERROR定义。

eFileAttrib:输出,文件属性,由FILE.ATTRIB定义。

uidPos:输出,文件指针偏移位置(相对于文件开头的字节数)。

szSize:输出,文件实际大小,单位为字节。

dtLastModification:上次修改的日期和时间,格式为2023-02-03-16:23:00

三、使用示例

这里需要注意的是,在早期版本的CODESYS官方示例中,CAA.HANDLE、CAA.FILENAME、CAA.SIZE等变量是以CAA_HANDLE、CAA_FILENAME、CAA_SIZE的形式出现的,具体从哪个库版本开始改的,我也记不得了,总之改过来以后使用新版本的库就不会报错了~~。

1.目录操作使用示例

以下为目录操作的示例,其功能是在控制器指定目录下建立新目录,然后对目录进行打开、获取目录属性列表、关闭、拷贝、重命名和删除操作。需要注意的是这些操作需要在实际的控制器上才能执行,仿真模式下会报5113号错误。本次测试使用的控制器是禾川的Q0,使用其它控制器时需要正确指定可进行读写操作的目录位置。

程序变量定义如下:

PROGRAM testDir

VAR

       xDirInit:       BOOL := FALSE;

    uiDirState:     UINT := 0;

    sDirNewName:    CAA.FILENAME:='$$flashfiles$$\TestDirectory';

    sDirNextName:   CAA.FILENAME:='$$flashfiles$$\NewDirectory';

    hDir:         CAA.HANDLE;

    deNewDirectory: FILE.FILE_DIR_ENTRY;

       eError:                  FILE.ERROR;

    fDirCreate:     FILE.DirCreate;

    fDirOpen:       FILE.DirOpen;

    fDirClose:      FILE.DirClose;

    fDirList:       FILE.DirList;

       fDirCopy:       FILE.DirCopy;

    fDirRename:     FILE.DirRename;

    fDirRm:         FILE.DirRemove;

END_VAR

程序如下:

IF NOT xDirInit THEN

    fDirCreate(xExecute:=FALSE);

    fDirClose(xExecute:=FALSE);

    fDirList(xExecute:=FALSE);

    fDirRm(xExecute:=FALSE);

    xDirInit:=TRUE;

    uiDirState:=0;

ELSE

    CASE uiDirState OF

    0: (* 创建新目录 *)

        fDirCreate.sDirName:=sDirNewName;

        fDirCreate.xParent:=FALSE;

        fDirCreate(xExecute:=TRUE);

        IF fDirCreate.xDone THEN

            uiDirState:=1;

        END_IF

        IF fDirCreate.xError THEN (* 错误处理*)

                     eError:=fDirCreate.eError;

            ;

        END_IF

    1: (* 打开目录 *)

        fDirOpen.sDirName:=sDirNewName;

        fDirOpen(xExecute:=TRUE);

        IF fDirOpen.xDone THEN

            hDir := fDirOpen.hDir;

            uiDirState:=2;

        END_IF

        IF fDirOpen.xError THEN (* 错误处理 *)

                     eError:=fDirOpen.eError;

            ;

        END_IF

    2: (* 获取目录属性列表 *)

        fDirList.hDir:=hDir;

        fDirList(xExecute:=TRUE);

        IF fDirList.xDone THEN

            deNewDirectory.sEntry :=fDirList.deDirEntry.sEntry;

            deNewDirectory.szSize :=fDirList.deDirEntry.szSize;

            deNewDirectory.xDirectory :=fDirList.deDirEntry.xDirectory;

            deNewDirectory.xExclusive :=fDirList.deDirEntry.xExclusive;

            deNewDirectory.dtLastModification :=fDirList.deDirEntry.dtLastModification;

            uiDirState:=3;

        END_IF

        IF fDirOpen.xError THEN (* 错误处理 *)

                     eError:=fDirList.eError;

            ;

        END_IF

    3: (* 关闭目录 *)

        fDirClose.hDir:=hDir;

        fDirClose(xExecute:=TRUE);

        IF fDirClose.xDone THEN

            uiDirState:=4;

        END_IF

        IF fDirClose.xError THEN (* 错误处理 *)

                     eError:=fDirClose.eError;

            ;

        END_IF

       4: (* 目录拷贝 *)

        fDirCopy.sDirNameSource:=sDirNewName;

        fDirCopy.sDirNameDest:='$$flashfiles$$\TestDirectory1';

        fDirCopy(xExecute:=TRUE);

        IF fDirCopy.xDone THEN

            uiDirState:=5;

        END_IF

        IF fDirCopy.xError THEN (* 错误处理 *)

                     eError:=fDirCopy.eError;

            ;

        END_IF

    5: (* 目录重命名 *)

        fDirRename.sDirNameOld:=sDirNewName;

        fDirRename.sDirNameNew:=sDirNextName;

        fDirRename(xExecute:=TRUE);

        IF fDirRename.xDone THEN

            uiDirState:=6;

        END_IF

        IF fDirRename.xError THEN (* 错误处理 *)

                     eError:=fDirRename.eError;

            ;

        END_IF

    6: (* 删除目录 *)

        fDirRm.sDirName:=sDirNextName;

        fDirRm.udiTimeOut:=100000;      (* 超时时间 100ms *)

        fDirRm.xRecursive:=FALSE;

        fDirRm(xExecute:=TRUE);

        IF fDirRm.xDone THEN

            uiDirState:=7;

        END_IF

        IF fDirRm.xError THEN (* 错误处理 *)

                     eError:=fDirRm.eError;

            ;

        END_IF

    7: (* 示例结束 *)

        ;

    END_CASE

END_IF

2.文件操作使用示例

以下为文件操作的示例,其功能是在控制器指定目录下建立新文件并将指定文本内容写入文件,然后进行读取、关闭、拷贝、重命名和删除文件操作。需要注意的是这些操作需要在实际的控制器上才能执行,仿真模式下会报错。本次测试使用的控制器是禾川的Q0,使用其它控制器时需要正确指定可进行读写操作的目录位置。

程序变量定义如下:

PROGRAM testFile

VAR

       xFileStdInit:   BOOL:=FALSE;

    uiFileStdState: UINT:=0;

    sFileName:        CAA.FILENAME:= 'TestFile.txt';

    hFile:          CAA.HANDLE;

    sFileTestString:STRING:='Hello 2023!';

    sFileString:    STRING:='';

    szFileSize1:    CAA.SIZE := 0;

    szFileSize2:    CAA.SIZE := 0;

    sFileNewName:   CAA.FILENAME:= 'NewFile.txt';

    szCopiedFileSize:   CAA_SIZE := 0;

       eError:                  FILE.ERROR;

    fOpen:          FILE.Open;

    fWrite:         FILE.Write;

    fRead:          FILE.Read;

    fClose:         FILE.Close;

       fCopy:          FILE.Copy;

    fRename:        FILE.Rename;

    fDel:           FILE.Delete;

END_VAR

程序如下:

IF NOT xFileStdInit THEN

    fOpen(xExecute:=FALSE);

    fClose(xExecute:=FALSE);

    fWrite(xExecute:=FALSE);

    fRead(xExecute:=FALSE);

    fDel(xExecute:=FALSE);

    fRename(xExecute:=FALSE);

    fCopy(xExecute:=FALSE);

    xFileStdInit:=TRUE;

    uiFileStdState:=0;

ELSE

    CASE uiFileStdState OF

    0: (* 创建新文件 *)

        fOpen.sFileName:=sFileName;

        fOpen.eFileMode:=FILE.MODE.MRDWR;

        fOpen.xExclusive:=TRUE;

        fOpen(xExecute:=TRUE);

        IF fOpen.xDone THEN

            hFile:=fOpen.hFile;

            uiFileStdState:=1;

        END_IF

        IF fOpen.xError THEN (* 错误处理 *)

                     eError:=fOpen.eError; //错误号

            ;

        END_IF

    1:(* 文本内容写入文件 *)

        fWrite.hFile:=hFile;

        fWrite.pBuffer:=ADR(sFileTestString);

        szFileSize1:=SIZEOF(sFileTestString);

        fWrite.szSize:=szFileSize1;

        fWrite.udiTimeOut:=100000;       (* 100ms Timeout *)

        fWrite(xExecute:=TRUE);

        IF fWrite.xDone THEN

            uiFileStdState:=2;

        END_IF

        IF fWrite.xError THEN (* 错误处理 *)

                     eError:=fWrite.eError;

            ;

        END_IF

    2:(* 读取文件 - TestFile.txt*)

        fRead.hFile:=hFile;

        fRead.udiTimeOut:=100000;       (* 超时时间 100ms *)

        fRead.pBuffer:=ADR(sFileString);

        fRead.szBuffer:=255;

        fRead(xExecute:=TRUE);

        IF fRead.xDone THEN

            szFileSize2:=fRead.szSize;

            IF szFileSize2 = szFileSize1 THEN

                uiFileStdState:=3;

            ELSE (* 错误处理 *)

                            eError:=fRead.eError;

                ;

            END_IF

        END_IF

        IF fRead.xError THEN (* 错误处理 *)

                     eError:=fRead.eError;

            ;

        END_IF

    3:  (* 关闭文件  - TestFile.txt *)

        fClose.hFile:=hFile;

        fClose(xExecute:=TRUE);

        IF fClose.xDone THEN

            uiFileStdState:=4;

        END_IF

        IF fClose.xError THEN (* 错误处理 *)

                     eError:=fClose.eError;

            ;

        END_IF

    4:(* 拷贝 *)

        fCopy.sFileNameSource:=sFileName;

        fCopy.sFileNameDest:='DestFile.txt';

        fCopy.udiTimeOut:=100000;       (* 超时时间 100ms     *)

        fCopy.xOverWrite:=TRUE;         (* 覆盖已有文件 *)

        fCopy( xExecute:=TRUE);

        IF fCopy.xDone THEN

            szCopiedFileSize := fCopy.szSize;

            uiFileStdState:=5;

        END_IF

        IF fCopy.xError THEN (* 错误处理 *)

                     eError:=fCopy.eError;

            ;

        END_IF

    5: (* 文件重命名 *)

        fRename.sFileNameOld:='DestFile.txt';

        fRename.sFileNameNew:=sFileNewName;

        fRename( xExecute:=TRUE);

        IF fRename.xDone THEN

            uiFileStdState:=6;

        END_IF

        IF fRename.xError THEN (* 错误处理 *)

                     eError:=fRename.eError;

            ;

        END_IF

    6:(* 删除文件 *)

        fDel.sFileName:=sFileNewName;

        fDel( xExecute:=TRUE);

        IF fDel.xDone THEN

            uiFileStdState:=7;

        END_IF

        IF fDel.xError THEN (* 错误处理 *)

                     eError:=fDel.eError;

            ;

        END_IF

    7:  (* end of example *)

            ;

    END_CASE

END_IF

从上面的示例可以看出,文件操作实际上是通过状态机的方式进行的,即打开、读取或写入、属性获取、关闭等操作都是每次执行一步,一个操作执行完成后转到下一状态。这个流程在CODESYS中读写文件是比较推荐的,可以避免因某一步骤操作时间过长导致的任务超时。

四、结论

CAA File库的使用其实并不复杂,新手只要弄清楚Open、Read、Write、Close等几个主要功能块的用法,照着上面的示例改一下基本上能够解决大部分的问题。这篇文章本来打算连SysFile库一锅烩的,写着写着发现实在是太长了,还是留着下次再写吧^-^~~

另外说明一下,本文的示例是在CODESYS 3.5.17版本上测试的,如果是比较老的版本上(比如3.5.10)上可能无法正常运行。实际上CODESYS的库也是在不停的改来改去的,新手建议用最新的版本。考古的同学需要自己去翻文档了(这里要吐槽一下,CODESYS在线帮助里面有很多示例代码使用的类型定义也是在远古版本的库里面,放到现在的新版本里面会直接报错……)。

------------------

原创不易,感兴趣的多支持!

 

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

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

相关文章

软测(概念) · 软件测试的基本概念 · 什么是需求 · 测试用例的概念 · 软件错误(bug)的概念

一、什么是软件测试软件测试和开发的区别测试和调试的区别一个优秀的软件测试人员具备的素质二、什么是需求从测试人员角度看待需求三、测试用例的概念四、软件错误(bug)的概念一、什么是软件测试 最常见的解释是:软件测试就是找 BUG&#x…

个人博客美化

总体参考: Butterfly 文档:https://butterfly.js.organzhiyu :https://anzhiy.cn张洪 Heo :https://blog.zhheo.comLeonus :https://blog.leonus.cn 注:博客所有美化大部分(全部)都参…

React项目实战之租房app项目(九)登录模块基础布局和功能实现

前言 目录前言一、房屋详情模块二、登录模块2.1 登录模块效果图2.2 基础布局2.3 调用接口实现登录2.4 实现表单验证功能2.4.1 formik介绍2.4.2 formik基本使用2.4.3 添加表单验证2.5 代码优化总结一、房屋详情模块 房屋详情模块主要是展示之前获取到的房源信息,由于…

为防护加码,飞凌嵌入式i.MX93系列开发板让通信安全又稳定

来源:飞凌嵌入式官网www.forlinx.com随着新基建的加快推进,智能制造迎来了更好的发展时机,嵌入式板卡等智能设备也在更多的应用场景中大放异彩。但随着现场的设备数量的剧增,环境中的各种干扰信号也随之增加,这就对设备…

windows下GitHub的SSH key配置

SSH Key 是一种方法来确定受信任的计算机,从而实现免密码登录。 Git是分布式的代码管理工具,远程的代码管理是基于SSH的,所以要使用远程的Git则需要SSH的配置。 下面的步骤将完成 生成SSH密钥 并 添加公共密钥到GitHub上的帐户 先设置GitHub…

Apifox接口测试工具详细解析

最近发现一款接口测试工具--apifox,我我们很难将它描述为一款接口管理工具 或 接口自测试工具。 官方给了一个简单的公式,更能说明apifox可以做什么。 Apifox Postman Swagger Mock JMeter Apifox的特点: 接口文档定义: Apif…

接口测试学习第二天

1、全局变量 概念:在postman全局生效的变量,全局唯一。设置: 代码设置:pm.globals.set("glb_age",100)//示例: pm.globals.set("glb_age",100) 获取: 代码获取:var 接收值…

Java的内部类详解(成员内部类、静态内部类、局部内部类、匿名内部类)

Java知识点总结:想看的可以从这里进入 目录2.2.4、 内部类1、成员内部类2、静态内部类3、局部内部类4、匿名内部类2.2.4、 内部类 一个类定义在另一个类内,那么这个类就是一个内部类,比如:在类A中定义一个类B,B就是内…

英特尔锐炫秒杀RTX 3060,XeSS现已支持超过35款游戏!

一款显卡的性能可以达到什么程度?除了架构、规格等硬件因素,驱动的优化程度同样至关重要。Intel携带Arc锐炫回归独立显卡市场,作为“后起之秀”,驱动的优劣更是关键中的关键。Intel也正是这么做的。2022年6月,Intel正式…

2023 NFT防骗指南:六大骗局,3招带你远离…

网上流传着一句:币圈一天,人间一年。在刚刚过去的农历新年,一直低迷的加密领域迎来了“短暂性复苏”,加密市场总市值重回万亿美元。 同时复苏的还有NFT市场,据欧科云链OKLink链上数据显示,2023年1月份的NFT…

计算机网络-http协议版本对比

概述 HTTP 是基于 TCP/IP 协议的一个应用层协议,是现代互联网的一个基础协议。规定了客户端与服务端之间的通信格式以及所占用的服务端口80(HTTPS是443)。 超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响…

【Flutter】Flutter Developer 101 入门小册 专栏指引

你好,我是小雨青年,一名程序员。 在2023年,我决定做这个Flutter专栏,从基础到部署,一站式解决大家对于Fulltter的学习需求。 目前本专栏的大概目录为本文最后所示,后续随着内容的不断更新,会逐…

2023年“华数杯”国际大学生数学建模B题赛题发布

ICM 问题B:社会稳定早期预警研究 背景 人类和所有的动物一样,都有寻求利益和避免伤害的本能。人类成为创造之主 的关键在于,他们比其他动物更善于避免伤害。危机总是潜伏着未来。人类发展的 历史是一部不断尝试超越危机的历史 (严耀军&#x…

鸿蒙开发学习|基础环境和开发工具

系列文章目录 第一章 HarmonyOS是什么 第二章 基础环境和开发工具 文章目录系列文章目录前言一、DevEco Studio工具简介二、DevEco Studio搭建开发流程1.运行环境要求2.下载和安装DevEco Studio三、安装HarmonyOS开发插件总结前言 HUAWEI DevEco Studio是基于IntelliJ IDEA C…

三阶魔方七步还原法公式备忘录

魔方公式备忘 转动符号图解 魔方七步公式: 1.底面十字还原 2.底角还原 3.中间层还原 上棱到左棱 U’L’U’LUFUF’ 上棱到右棱 URUR’U’F’U’F 4.顶面十字 循环做FRUR’U’F’直到出现十字 5.顶面还原(小鱼公式) 左手 L’U’LU’L’U’2L …

2、Maven——IDEA与eclipse(MyEclipse)创建工程的区别、Maven创建基本java工程

目录 一、IDEA与eclipse创建工程的区别 二、IDEA创建多个工程 1、 创建空工程:Empty Project 2、创建Module 三、Maven创建基本java工程 1、pom.xml 2、依赖坐标的使用 3、远程Maven仓库 4、Maven项目框架 (1) main目录 &#xff…

MySQL之主从复制集群搭建

简述 这篇文章主要记录使用docker compose搭建MySQL主从复制集群搭建,方便后续进行本地测试开发。 这篇文章主要介绍一主一从的搭建过程。 主从架构,可以缓解MySQL的数据存储以及访问的压力。 一. 主从复制原理 原理图如下: 步骤&#xf…

AVL平衡树(Java实现)

概念 AVL树可以定义为高度平衡二叉搜索树,其中每个节点与平衡因子相关联,该平衡因子通过从其左子树的子树中减去其右子树的高度来计算。AVL树是由GM Adelson - Velsky和EM Landis于1962年发明的。为了纪念其发明者,这树结构被命名为AVL。 定…

Android集成Unity

前言 随着前两年元宇宙的提出,虚拟现实开始在各大平台大展身手。各个平台都开始搭上了元宇宙的列车,Unity作为虚拟引擎中的热门,渲染效果和开发效率极其出色;Android作为移动开发的巨头之一也搭上了元宇宙这趟列车。今天&#xf…

图像基础概念

加解串器图像相关的概念:相关的概念:一个像素时钟可以产生两个像素可以通过调大frame freelance或vts来增大 V blinking sensor的基本配置曝光:一行一行进行曝光:每一行曝光时间 非常短 从第一行到最后一行为有效时间同步信号:同步信号的处理同步曝光的需求:曝光的…