当前位置: 首页 > news >正文

CSerialPort教程4.3.x (2) - CSerialPort源码简介

CSerialPort教程4.3.x (2) - CSerialPort源码简介

前言

CSerialPort项目是一个基于C/C++的轻量级开源跨平台串口类库,可以轻松实现跨平台多操作系统的串口读写,同时还支持C#, Java, Python, Node.js等。

CSerialPort项目的开源协议自 V3.0.0.171216 版本后采用GNU Lesser General Public License v3.0

为了让开发者更好的使用CSerialPort进行开发,特编写基于4.3.x版本的CSerialPort教程系列。

CSerialPort项目地址:

  • https://github.com/itas109/CSerialPort
  • https://gitee.com/itas109/CSerialPort

本文对CSerialPort 4.3.0版本源码进行简介。

1. CSerialPort源码目录结构

CSerialPort # root
+--- .clang-format # code format 代码规范
├── bindings # 第三方语言接口
│   ├── c # c interface c接口
│   ├── csharp # csharp interface c#接口
│   ├── java # java interface java接口
│   ├── javascript # javascript interface javascript接口
│   └── python # python interface python接口
├── CHANGELOG.md # change log 修改日志
├── cmake # cross compile 交叉编译
├── CMakeLists.txt
├── doc # document 文档
├── examples # example 示例程序
│   ├── CommMFC # CSerialPort MFC Demo MFC程序示例
│   ├── CommNoGui # CSerialPort No Gui Demo 无界面程序示例
│   ├── CommQT # CSerialPort QT Demo QT程序示例
│   ├── CommTui # CSerialPort tui Demo 文本界面程序示例
│   ├── CommWXWidgets # CSerialPort wxwidgets Demo wxwidgets界面程序示例
├── include # headers 头文件
│   └── CSerialPort
│       ├── ibuffer.hpp # lightweight cross-platform buffer library 轻量级跨平台缓冲区类
│       ├── ithread.hpp # lightweight cross-platform thread library 轻量级跨平台线程类
│       ├── itimer.hpp # lightweight cross-platform timer library 轻量级跨平台定时器类
│       ├── iutils.hpp # lightweight cross-platform utils library 轻量级跨平台工具类
│       ├── SerialPortBase.h # CSerialPort Base class 串口基类
│       ├── SerialPort_global.h # Global difine of CSerialPort 串口全局定义 
│       ├── SerialPort.h # lightweight cross-platform serial port library 轻量级跨平台串口类库
│       ├── SerialPortInfoBase.h # CSerialPortInfo Base class 串口信息辅助类基类
│       ├── SerialPortInfo.h # CSerialPortInfo class 串口信息辅助类
│       ├── SerialPortInfoUnixBase.h # CSerialPortInfo unix class unix串口信息辅助类基类
│       ├── SerialPortInfoWinBase.h # CSerialPortInfo windows class windows串口信息辅助类基类
│       ├── SerialPortListener.h # CSerialPortListener interface class 串口事件监听接口类
│       ├── SerialPortUnixBase.h # CSerialPort unix Base class unix串口基类
│       ├── SerialPort_version.h # CSerialPort version 版本定义
│       └── SerialPortWinBase.h # CSerialPort Windows Base class windows串口基类
├── lib # lib 库目录
├── LICENSE # LGPL3.0 license
├── pic # picture 图片
├── README.md
├── README_zh_CN.md
├── src # source 源代码
├── test # unit test 单元测试
└── VERSION # version 版本号

2. CSerialPort常用函数

2.1 串口信息相关函数

2.1.1 获取串口信息列表函数 availablePortInfos

该函数获取串口信息列表,主要包括:

  • 串口名称 (如COM2)
  • 描述信息 (如USB-SERIAL CH340)
  • 硬件id (如USB\VID_1A86&PID_7523&REV_0264)
static vector<SerialPortInfo> itas109::CSerialPortInfo::availablePortInfos()

2.2 串口操作相关函数

2.2.1 串口初始化函数 init

该函数用于串口初始化。

void itas109::CSerialPort::init(const char *portName,                                 // 串口名称 Windows:COM1 Linux:/dev/ttyS0int baudRate = itas109::BaudRate9600,                 // 波特率itas109::Parity parity = itas109::ParityNone,         // 校验位itas109::DataBits dataBits = itas109::DataBits8,      // 数据位itas109::StopBits stopbits = itas109::StopOne,        // 停止位itas109::FlowControl flowControl = itas109::FlowNone, // 流控制unsigned int readBufferSize = 4096                    // 读取缓冲区大小
)

注意:

  • 5位数据位不能使用2位停止位
  • 1.5位停止位不能使用5位数据位
  • POSIX系统8位数据位不能使用0校验
  • windows数据位范围为4 - 8
  • 1.5位停止位仅对windows有效
  • 停止位数字1表示StopOneAndHalf,即1.5位停止位,并非1位停止位

2.2.2 打开串口函数 open

该函数用于打开串口。

bool itas109::CSerialPort::open()

true表示打开成功,false表示打开失败。

打开失败可用itas109::CSerialPort::getLastError()获取错误码

2.2.3 关闭串口函数 close

该函数用于关闭串口。

void itas109::CSerialPort::close()

2.2.4 是否打开串口成功函数 isOpen

该函数用于获取是否打开串口成功。

bool itas109::CSerialPort::isOpen()

true表示串口打开成功,false表示串口打开失败。

2.2.5 向串口写入数据函数 writeData

该函数用于向串口写入数据。

int itas109::CSerialPort::writeData(const void *data, // 待写入数据int maxSize       // 写入长度
)

成功则返回写入字节数,失败则返回-1。

写入失败可用itas109::CSerialPort::getLastError()获取错误码。

2.2.6 从串口读取指定长度数据函数 readData

该函数用于从串口读取指定长度数据。

int readData(void *data,  // 读取结果int size     // 读取长度
)

正常则返回读取字节数,失败则返回-1。

读取失败可用itas109::CSerialPort::getLastError()获取错误码。

注意:
CSerialPort异步操作模式下,需要配合connectReadEvent函数使用。详见第三节的代码示例。

2.2.7 从串口读取所有数据函数 readAllData

该函数用于从串口读取所有数据。

int itas109::CSerialPort::readAllData(void *data // 读取结果
)

正常则返回读取字节数,失败则返回-1。

读取失败可用itas109::CSerialPort::getLastError()获取错误码。

注意:
CSerialPort异步操作模式下,需要配合connectReadEvent函数使用。详见第三节的代码示例。

2.2.8 绑定读取事件函数 connectReadEvent

该函数用于异步模式(默认)下,绑定读取响应的结果。

需要继承CSerialPortListener,并实现onReadEvent虚函数。详见第三节的代码示例。

class MyListener : public CSerialPortListener
{
public:void onReadEvent(const char *portName, unsigned int readBufferLen){}
}

2.2.9 获取CSerialPort版本信息函数 getVersion

该函数用于获取CSerialPort版本信息。

std::string itas109::CSerialPort::getVersion()

返回CSerialPort版本信息,如https://github.com/itas109/CSerialPort - V4.3.0.230215

2.2.10 错误码 SerialPortError

错误码数值错误码宏定义错误码说明
-1ErrorUnknownunknown error 未知错误
0ErrorOKok 成功
1ErrorFailgeneral error一般性错误
2ErrorNotImplementednot implemented 未实现
3ErrorInnerinner error 内部错误(如内存访问异常等)
4ErrorNullPointernull pointer error 空指针错误
5ErrorInvalidParaminvalid parameter error 无效的参数
6ErrorAccessDeniedaccess denied error 权限错误
7ErrorOutOfMemoryout of memory 内存不足
8ErrorTimeouttime out error 超时
9ErrorNotInitnot init 未初始化
10ErrorInitFailedinit failed 初始化失败
11ErrorAlreadyExistalready exist 已经存在
12ErrorNotExistnot exist 不存在
13ErrorAlreadyOpenalready open 已经打开
14ErrorNotOpennot open 未打开
15ErrorOpenFailedopen failed 打开失败
16ErrorCloseFailedclose failed 关闭失败
17ErrorWriteFailedwrite failed 写入失败
18ErrorReadFailedread failed 读取失败

3. CSerialPort简单代码示例

  • 操作步骤
$ mkdir CSerialPortDemo
$ cd CSerialPortDemo
$ git clone https://github.com/itas109/CSerialPort
$ touch CSerialPortDemo.cpp
$ touch CMakeLists.txt
$ mkdir bin 
$ cd bin
$ cmake ..
$ cmake --build .
  • 目录结构
$ tree
.
+--- CMakeLists.txt
+--- CSerialPort
|   +--- include
|   +--- src
|   +--- ...
+--- CSerialPortDemo.cpp
  • CSerialPortDemo.cpp

注意:接收函数需要继承CSerialPortListener

#include <iostream>#include "CSerialPort/SerialPort.h"
#include "CSerialPort/SerialPortInfo.h"#include <vector>
using namespace itas109;
using namespace std;class MyListener : public CSerialPortListener
{
public:MyListener(CSerialPort *sp): p_sp(sp){};void onReadEvent(const char *portName, unsigned int readBufferLen){if (readBufferLen > 0){char *data = new char[readBufferLen + 1]; // '\0'if (data){// readint recLen = p_sp->readData(data, readBufferLen);if (recLen > 0){data[recLen] = '\0';std::cout << portName << ", Length: " << recLen << ", Str: " << data << std::endl;}delete[] data;data = NULL;}}};private:CSerialPort *p_sp;
};int main()
{CSerialPort sp;MyListener listener(&sp);std::cout << "Version : " << sp.getVersion() << std::endl << std::endl;vector<SerialPortInfo> m_availablePortsList = CSerialPortInfo::availablePortInfos();std::cout << "availableFriendlyPorts: " << std::endl;for (size_t i = 1; i <= m_availablePortsList.size(); ++i){SerialPortInfo serialPortInfo = m_availablePortsList[i - 1];std::cout << i << " - " << serialPortInfo.portName << " " << serialPortInfo.description << " " << serialPortInfo.hardwareId << std::endl;}if (m_availablePortsList.size() == 0){std::cout << "No valid port" << std::endl;}else{std::cout << std::endl;int input = -1;do{std::cout << "Please Input The Index Of Port(1 - " << m_availablePortsList.size() << ")" << std::endl;std::cin >> input;if (input >= 1 && input <= m_availablePortsList.size()){break;}} while (true);const char *portName = m_availablePortsList[input - 1].portName;std::cout << "Port Name: " << portName << std::endl;sp.init(portName,              // windows:COM1 Linux:/dev/ttyS0itas109::BaudRate9600, // baudrateitas109::ParityNone,   // parityitas109::DataBits8,    // data bititas109::StopOne,      // stop bititas109::FlowNone,     // flow4096                   // read buffer size);sp.setReadIntervalTimeout(0); // read interval timeout 0ms// sp.setOperateMode(itas109::SynchronousOperate);sp.open();std::cout << "Open " << portName << (sp.isOpen() ? " Success. " : " Failed. ");std::cout << "Code: " << sp.getLastError() << ", Message: " << sp.getLastErrorMsg() << std::endl;// 绑定读取函数sp.connectReadEvent(&listener);// 写入数据sp.writeData("itas109", 7);for (;;){}}return 0;
}
  • CMakeLists.txt
cmake_minimum_required(VERSION 2.8.12)project(CSerialPortDemo)if(APPLE)find_library(IOKIT_LIBRARY IOKit)find_library(FOUNDATION_LIBRARY Foundation)
endif()include_directories(CSerialPort/include)
file(GLOB_RECURSE COMMON_SOURCES CSerialPort/src/SerialPort.cpp CSerialPort/src/SerialPortBase.cpp CSerialPort/src/SerialPortInfo.cpp CSerialPort/src/SerialPortInfoBase.cpp)
if (CMAKE_HOST_WIN32)file(GLOB_RECURSE OS_ABOUT_SOURCES CSerialPort/src/SerialPortInfoWinBase.cpp CSerialPort/src/SerialPortWinBase.cpp)
elseif (CMAKE_HOST_UNIX)file(GLOB_RECURSE OS_ABOUT_SOURCES CSerialPort/src/SerialPortInfoUnixBase.cpp CSerialPort/src/SerialPortUnixBase.cpp)
endif ()	add_executable( ${PROJECT_NAME} CSerialPortDemo.cpp ${COMMON_SOURCES} ${OS_ABOUT_SOURCES})if (WIN32)target_link_libraries( ${PROJECT_NAME} setupapi )
elseif (APPLE)target_link_libraries( ${PROJECT_NAME} ${FOUNDATION_LIBRARY} ${IOKIT_LIBRARY})
elseif (UNIX)target_link_libraries( ${PROJECT_NAME} pthread )
endif ()
  • 运行结果(windows下环回测试)
Version : https://github.com/itas109/CSerialPort - V4.3.0.230215availableFriendlyPorts:
1 - COM1 USB-SERIAL CH340  USB\VID_1A86&PID_7523&REV_0264Please Input The Index Of Port(1 - 1)
1
Port Name: COM1
Open COM1 Success. Code: 0, Message: success
COM1, Length: 7, Str: itas109

License

License under CC BY-NC-ND 4.0: 署名-非商业使用-禁止演绎


Reference:

  1. https://github.com/itas109/CSerialPort
  2. https://gitee.com/itas109/CSerialPort
  3. https://blog.csdn.net/itas109

相关文章:

CSerialPort教程4.3.x (2) - CSerialPort源码简介

CSerialPort教程4.3.x (2) - CSerialPort源码简介 前言 CSerialPort项目是一个基于C/C的轻量级开源跨平台串口类库&#xff0c;可以轻松实现跨平台多操作系统的串口读写&#xff0c;同时还支持C#, Java, Python, Node.js等。 CSerialPort项目的开源协议自 V3.0.0.171216 版本…...

【数据结构OJ题】有效的括号

原题链接&#xff1a;https://leetcode.cn/problems/valid-parentheses/ 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 这道题目主要考查了栈的特性&#xff1a; 题目的意思主要是要做到3点匹配&#xff1a;类型、顺序、数量。 题目给的例子是比较…...

Java性能分析中常用命令和工具

当涉及到 Java 性能分析时&#xff0c;有一系列强大的命令和工具可以帮助开发人员分析应用程序的性能瓶颈、内存使用情况和线程问题。以下是一些常用的 Java 性能分析命令和工具&#xff0c;以及它们的详细说明和示例。 以下是一些常用的性能分析命令和工具汇总&#xff1a; …...

JVM性能分析-jstat工具观察gc频率

jstat jstat是java自带的工具&#xff0c;在bin目录下 用法 语法&#xff1a;jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]] [kqkyyj-2 bin]$ jstat -help Usage: jstat -help|-optionsjstat -<option> [-t] [-h&l…...

mysql 查询报错 1267 - Illegal mix of collations

mysql 查询报错 1267 - Illegal mix of collations 详细报错: 1267 - Illegal mix of collations (utf8mb4_0900_ai_ci,IMPLICIT) and (utf8mb4_unicode_ci,IMPLICIT) for 主要的原因其实就是两张表的字符集不一样改一下就行了。 注: 改了表还是报错的话,那就是表内的字段没有…...

【ARM】Day6

cotex-A7核UART总线实验 1. 键盘输入一个字符‘a’&#xff0c;串口工具显示‘b’ 2. 键盘输入一个字符串"nihao"&#xff0c;串口工具显示“nihao” uart.h #ifndef __UART4_H__ #define __UART4_H__#include "stm32mp1xx_rcc.h" #include "stm3…...

深入理解Flink Mailbox线程模型

文章目录 整体设计processMail1.Checkpoint Tigger2.ProcessingTime Timer Trigger processInput兼容SourceStreamTask 整体设计 Mailbox线程模型通过引入阻塞队列配合一个Mailbox线程的方式&#xff0c;可以轻松修改StreamTask内部状态的修改。Checkpoint、ProcessingTime Ti…...

Docker搭建LNMP运行Wordpress平台

一、项目1.1 项目环境1.2 服务器环境1.3 任务需求 二、Linux 系统基础镜像三、Nginx1、建立工作目录2、编写 Dockerfile 脚本3、准备 nginx.conf 配置文件4、生成镜像5、创建自定义网络6、启动镜像容器7、验证 nginx 四、Mysql1、建立工作目录2、编写 Dockerfile3、准备 my.cnf…...

10个常见渐变交互效果

1、透明度渐变背景交互 <div class"fade-background"></div> Copy .fade-background {width: 200px;height: 200px;background: linear-gradient(to bottom, rgba(255, 0, 0, 0), rgba(255, 0, 0, 1));transition: background 0.5s ease; }.fade-backgro…...

[线程/C]基础

文章目录 1. 线程介绍2. 创建线程2.1 线程函数2.2 创建线程 3. 线程退出4. 线程回收4.1 线程函数4.2 回收子线程数据4.2.1 使用子线程栈4.2.2 使用全局变量4.2.3 使用主线程栈 5. 线程分离6. 其他线程函数6.1 线程取消6.2 线程ID的比较 1. 线程介绍 线程是轻量级的进程&#x…...

Spring Clould 负载均衡 - Ribbon

视频地址&#xff1a;微服务&#xff08;SpringCloudRabbitMQDockerRedis搜索分布式&#xff09; Ribbon-负载均衡原理&#xff08;P14&#xff09; 具体实现时通过LoaBalanced注解实现&#xff0c;表示RestTemplate要被Ribbon拦截处理 orderservice调用user时候&#xff0c…...

活用DNS技术实现相同IP的不同端口映射不同域名

WindowsDNS基本配置 在内网的 Windows 服务器环境中&#xff0c;你可以通过配置 DNS 服务和 Web 服务器来实现所需的域名解析和端口转发。如下是一些基本的步骤来实现配置&#xff1a; 1&#xff0c;配置 Windows DNS 服务 在你的 Windows 服务器上配置 DNS 服务&#xff0c…...

AutoHotkey:定时删除目录下指定分钟以前的文件,带UI界面

删除指定目录下&#xff0c;所有在某个指定分钟以前的文件&#xff0c;可以用来清理经常生成很多文件的目录&#xff0c;但又需要保留最新的一部分文件 支持拖放目录到界面 能够记忆设置&#xff0c;下次启动后不用重新设置&#xff0c;可以直接开始 应用场景比如&#xff1a…...

一文学会sklearn中的交叉验证的方法

前言 在机器学习中&#xff0c;我们经常需要评估模型的性能。而为了准确评估模型的性能&#xff0c;我们需要使用一种有效的评估方法。五折交叉验证&#xff08;5-fold cross-validation&#xff09;就是其中一种常用的模型评估方法&#xff0c;用于评估机器学习模型的性能和泛…...

【MySQL面试题(66道)】

文章目录 MySQL面试题(66道)基础1.什么是内连接、外连接、交叉连接、笛卡尔积呢&#xff1f;2.那 MySQL 的内连接、左连接、右连接有有什么区别&#xff1f;3.说一下数据库的三大范式&#xff1f;4.varchar 与 char 的区别&#xff1f;5.blob 和 text 有什么区别&#xff1f;6.…...

CSSCI、北核期刊投稿指南(2023年更新)

该数据为经管类的期刊投稿指南&#xff0c;包含发表难度&#xff0c;文章数量&#xff0c;影响因子&#xff0c;用户评价等指标。共5份文件&#xff0c;分别为国内所有期刊信息库、投稿指南&#xff08;CSSCI版本、CSSCI扩展版本、北大核刊版本、建议期刊版本&#xff09; 一、…...

构建 NodeJS 影院微服务并使用 docker 部署它(02/4)

一、说明 构建一个微服务的电影网站&#xff0c;需要Docker、NodeJS、MongoDB&#xff0c;这样的案例您见过吗&#xff1f;如果对此有兴趣&#xff0c;您就继续往下看吧。 图片取自网络 — 封面由我制作 这是✌️“构建 NodeJS 影院微服务”系列的第二篇文章。 二、对第一部分的…...

HTML <style> 标签

实例 <html> <head> <style type="text/css"> h1 {color:red} p {color:blue} </style> </head><body> <h1>Header 1</h1> <p>A paragraph.</p> </body> </html>定义和用法 <style>…...

设计模式——迪米特法则

文章目录 基本介绍应用实例应用实例改进迪米特法则注意事项和细节 基本介绍 一个对象应该对其他对象保持最少的了解类与类关系越密切&#xff0c;耦合度越大迪米特法则(Demeter Principle)又叫最少知道原则&#xff0c;即一个类对自己依赖的类知道的越少越好。也就是说&#x…...

区块链基本概念与当前生态简介

区块链是一种去中心化的分布式账本技术&#xff0c;它通过将数据按照时间顺序链接成区块&#xff0c;并使用密码学算法确保数据的安全性和完整性。每个区块包含一定数量的交易记录&#xff0c;而且每个区块都包含了前一个区块的哈希值&#xff0c;这样形成了一个不可篡改的链式…...

MambaAD实战:5分钟搞定工业缺陷检测的SoTA模型部署(附代码)

MambaAD工业缺陷检测实战&#xff1a;从模型原理到产线部署全指南 引言&#xff1a;当状态空间模型遇见工业质检 在液晶面板生产线上&#xff0c;一个0.1mm的亮点缺陷可能导致整批产品报废&#xff1b;在汽车零部件铸造车间&#xff0c;细微的表面裂纹可能引发严重的安全隐患。…...

Ludusavi完整指南:如何专业备份和管理PC游戏存档

Ludusavi完整指南&#xff1a;如何专业备份和管理PC游戏存档 【免费下载链接】ludusavi Backup tool for PC game saves 项目地址: https://gitcode.com/gh_mirrors/lu/ludusavi Ludusavi是一款基于Rust语言开发的跨平台PC游戏存档备份工具&#xff0c;专为保护玩家游戏…...

【高通Camera_Tuning】优化树荫下及背景绿植时白平衡偏色问题(一)

参考案例&#xff1a;在室外拍摄时白平衡正常&#xff0c;但遇到树荫下或背景有绿植时出现偏色&#xff08;偏蓝&#xff09;问题。可通过修改绿区解决偏色问题。解决方法&#xff1a;1.开启Green zone在3A文件 -- /* Green */ -- /* Green Projection Enable */将/* Green Pr…...

C++的std--ranges算法自定义比较器与等价关系在集合操作中的运用

C20引入的std::ranges库为算法操作带来了革命性改进&#xff0c;其中自定义比较器与等价关系的灵活运用&#xff0c;显著提升了集合操作的表达能力。通过精确控制元素间的比较逻辑&#xff0c;开发者能够实现更复杂的业务需求&#xff0c;例如处理自定义对象集合或实现非标准排…...

软件测试生命周期全解析:用考试答题逻辑,零基础吃透测试核心

之前我们用考场答题的类比&#xff0c;轻松搞懂了软件开发生命周期&#xff0c;很多初学者恍然大悟&#xff1a;原来编程就是一场有章法的“考试”。但一场考试能不能拿到高分、能不能符合出题人&#xff08;客户&#xff09;的要求&#xff0c;光靠埋头答题&#xff08;开发编…...

HunyuanVideo-Foley实战案例:为纪录片自动匹配环境音效的完整工作流

HunyuanVideo-Foley实战案例&#xff1a;为纪录片自动匹配环境音效的完整工作流 1. 项目背景与需求 在纪录片制作过程中&#xff0c;环境音效的采集和匹配往往需要耗费大量时间和人力成本。传统方式需要音效师实地录制或从音效库中手动挑选&#xff0c;整个过程耗时且难以保证…...

MCP项目笔记六(PluginsLoader)

C 插件加载器&#xff1a;从目录扫描、动态库加载、实例创建&#xff0c;到安全卸载的设计思路与实现细节。一、整体架构概览 这段代码实现了一个完整的运行时插件系统&#xff08;Runtime Plugin System&#xff09;。所谓插件系统&#xff0c;就是让主程序在编译完成后&#…...

GStreamer性能优化指南:在Jetson TX2上实现4K视频低延迟处理(基于NVMM内存)

GStreamer性能优化指南&#xff1a;在Jetson TX2上实现4K视频低延迟处理&#xff08;基于NVMM内存&#xff09; 在嵌入式视觉和实时视频处理领域&#xff0c;NVIDIA Jetson TX2凭借其强大的GPU和专用硬件加速单元&#xff0c;成为工业级应用的理想选择。但要将这块开发板的性能…...

如何用Marker实现PDF到Markdown的精准转换?三个技巧提升文档处理效率

如何用Marker实现PDF到Markdown的精准转换&#xff1f;三个技巧提升文档处理效率 【免费下载链接】marker 一个高效、准确的工具&#xff0c;能够将 PDF 和图像快速转换为 Markdown、JSON 和 HTML 格式&#xff0c;支持多语言和复杂布局处理&#xff0c;可选集成 LLM 提升精度&…...

效率提升秘籍:用快马AI自动生成六花直装更新页面,节省开发时间

作为一名经常需要维护应用更新页面的开发者&#xff0c;我深刻体会到手动编写更新日志的繁琐。每次版本迭代&#xff0c;从整理更新内容到排版发布&#xff0c;往往要耗费大量时间。最近尝试用InsCode(快马)平台的AI功能自动生成更新页面&#xff0c;效率提升非常明显。 传统更…...