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

FreeModbus RTU 移植指南

FreeModbus 简介

FreeModbus 是一个免费的软件协议栈,实现了 Modbus 从机功能:

  • 纯 C 语言
  • 支持 Modbus RTU/ASCII
  • 支持 Modbus TCP

本文介绍 Modbus RTU 移植。

移植环境:

  • 裸机
  • Keil MDK 编译器
  • Cortex-M3 内核芯片(LPC1778/88)

移植概述

1.体系架构相关

项目描述
INLINE宏,编译器相关,内联指令或关键字
PR_BEGIN_EXTERN_C
PR_END_EXTERN_C
宏,按照 C 代码编译
ENTER_CRITICAL_SECTION( )
EXIT_CRITICAL_SECTION( )
宏,进入临界区和退出临界区
BOOL
UCHAR
CHAR
USHORT
SHORT
ULONG
LONG
数据类型
TRUE
FALSE
宏,BOOL 类型变量的值

2.定时器
需要移植的定时器函数

定时器函数描述
BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )初始化,由协议栈回调, usTim1Timerout50us 的单位是 50us
void vMBPortTimersEnable( )使能定时器,协议栈回调
定时器计数器清零,然后开始计数
void vMBPortTimersDisable( )禁止定时器,由协议栈回调
定时器计数器清零,停止计数
void prvvTIMERExpiredISR( void )通知协议栈定时器中断发生,需手动安装到定时器中断服务函数中

3.串口
需要移植的函数

定时器函数描述
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )初始化串口硬件,由协议栈回调
void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )使能/禁止串口发送和接收,由协议栈回调
BOOL xMBPortSerialPutByte( CHAR ucByte )通过串口发送一字节数据
BOOL xMBPortSerialGetByte( CHAR * pucByte )从串口接收一字节数据
void prvvUARTRxISR( void )通知协议栈串口接收中断发生,协议栈会进行数据接收处理。需手动安装到串口接收中断服务函数中
void prvvUARTTxReadyISR( void )通知协议栈串口发送中断发生,协议栈会进行数据发送。需手动安装到串口发送中断服务函数中

4.事件
事件相关回调函数需要移植:

事件回调函数描述
BOOL xMBPortEventInit( void )初始化
BOOL xMBPortEventPost( eMBEventType eEvent )事件投递
可以在这个函数中解析事件,并执行自己的事件函数。
BOOL xMBPortEventGet( eMBEventType * eEvent )获取事件

mb_config.h 文件属于协议栈的一部分,直接修改不合理
assert,直接调用 C 标准库函数, 但这个依赖硬件

移植细节

并不是所有函数都需要重头编写,协议栈 \freemodbus\demo\BARE\port\ 文件夹下给出了移植框架:

port
|---- port.h :体系架构相关
|---- porttimer.c :定时器相关
|---- portserial.c :串口相关
|---- portevent.c :事件相关

1.体系架构
port.h 文件:

#include <assert.h>
#include <stdint.h>
#include "cmsis_compiler.h"#define	INLINE                      __INLINE
#define PR_BEGIN_EXTERN_C           extern "C" {
#define	PR_END_EXTERN_C             }#ifndef assert
#define assert(ignore) ((void)0)
#endif#define ENTER_CRITICAL_SECTION( )   EnterCriticalSection()
#define EXIT_CRITICAL_SECTION( )    ExitCriticalSection()typedef uint8_t BOOL;typedef unsigned char UCHAR;
typedef char CHAR;typedef uint16_t USHORT;
typedef int16_t SHORT;typedef uint32_t ULONG;
typedef int32_t LONG;#ifndef TRUE
#define TRUE            1
#endif#ifndef FALSE
#define FALSE           0
#endifvoid EnterCriticalSection(void);
void ExitCriticalSection(void);

进入和退出临界区函数,实际上是开关中断,这部分点击这里可以获取详细的信息。我们新建一个 port.c 文件,在这个文件中实现一个可以嵌套使用的进入和退出临界区代码:

#include "cmsis_compiler.h"static uint32_t nesting_count = 0;
static uint32_t old_state;void EnterCriticalSection(void)
{uint32_t cur_state;cur_state = __get_PRIMASK();__disable_irq();if(nesting_count == 0)old_state = cur_state;nesting_count ++;
}void ExitCriticalSection(void)
{nesting_count --;if(0 == nesting_count)__set_PRIMASK(old_state);
}

2.定时器
Modbus RTU 使用超时机制判断数据帧结束:串口超过 3.5 个字符传输时间没有收到数据,则认为一帧结束。
这需要一个硬件定时器。
协议栈会根据传入的波特率自动计算 3.5 个字符传输时间是多少,单位是 50us,简化后的代码如下所示:

/* If baudrate > 19200 then we should use the fixed timer values t35 = 1750us. * Otherwise t35 must be 3.5 times the character time.*/
if( ulBaudRate > 19200 )
{usTimerT35_50us = 35;       /* 1800us. */
}
else
{/* The timer reload value for a character is given by:** ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )*             = 11 * Ticks_per_1s / Baudrate*             = 220000 / Baudrate* The reload for t3.5 is 1.5 times this value and similary* for t3.5.*/usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
}
xMBPortTimersInit( ( USHORT ) usTimerT35_50us )

所以就可以根据传入的 3.5 个字符传输时间 usTimerT35_50us 来初始化硬件定时器。我的系统刚好有个 50us 中断一次的定时器,所以我直接使用这个定时器来移植,移植代码在 porttime.c 文件中:

#include <stdbool.h>
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"static bool IsTimerEnable = false;
static USHORT Timerout50usCount = 0;
static USHORT Timerout50usCountCur = 0;/* ----------------------- static functions ---------------------------------*/
static void prvvTIMERExpiredISR( void );/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{Timerout50usCount = usTim1Timerout50us;IsTimerEnable = false;return TRUE;
}inline void
vMBPortTimersEnable(  )
{/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */IsTimerEnable = true;Timerout50usCountCur = 0;
}inline void
vMBPortTimersDisable(  )
{/* Disable any pending timers. */IsTimerEnable = false;Timerout50usCountCur = 0;
}/*需手动安装到定时器中断服务函数*/
void
vMBPortTimersISR(  )
{if(IsTimerEnable){Timerout50usCountCur ++;if(Timerout50usCountCur >= Timerout50usCount)prvvTIMERExpiredISR();}
}/* Create an ISR which is called whenever the timer has expired. This function* must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that* the timer has expired.*/
static void prvvTIMERExpiredISR( void )
{( void )pxMBPortCBTimerExpired(  );
}

有一点我很好奇, 3.5 个字符传输时间 usTimerT35_50us 为什么要格式化成 50us 的倍数?
我注意到代码 xMBPortTimersInit( ( USHORT ) usTimerT35_50us ) 在传递参数时进行了一次数据强制转换,也就是协议栈使用的 USHORT 数据类型,一般这个数据类型最大值是 65536,如果不转换成 50us 的倍数,低波特率(比如 1200bps )必然会出现数据溢出现象。
那协议栈为什么又非要使用 USHORT 数据类型呢?
不清楚,大概是当时主流 MCU 还不是 32 位的,USHORT 数据类型可以更快更节省 RAM 。

何时使能定时器?

  1. 启动协议栈(eMBRTUStart
  2. 接收到 1 字节数据(xMBRTUReceiveFSM):复位计数器,重新开始计时

何时关闭定时器?

  1. 停止协议栈(eMBRTUStop
  2. 超时发生(3.5 个字符传输时间):收到新的数据帧,停止计时

定时器与接收关系密切,参与接收状态机的状态迁移:
在这里插入图片描述
3.串口
串口用于收发数据。移植代码在 portserial.c 中:

#include "port.h"/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR( void );
static void prvvUARTRxISR( void );void down3_set_to_recv(void);
void down3_set_to_send(void);
void down3_put_byte( CHAR data);
void down3_get_byte(CHAR *pucByte);
void init_down3_uart2(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity);/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{/* If xRXEnable enable serial receive interrupts. If xTxENable enable* transmitter empty interrupts.*/if(xRxEnable){down3_set_to_recv();}if(xTxEnable){down3_set_to_send();prvvUARTTxReadyISR();}
}BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{init_down3_uart2(ucPORT, ulBaudRate, ucDataBits, eParity);return TRUE;
}BOOL
xMBPortSerialPutByte( CHAR ucByte )
{/* Put a byte in the UARTs transmit buffer. This function is called* by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been* called. */down3_put_byte(ucByte);return TRUE;
}BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{/* Return the byte in the UARTs receive buffer. This function is called* by the protocol stack after pxMBFrameCBByteReceived( ) has been called.*/down3_get_byte(pucByte);return TRUE;
}/*需手动安装到串口接收中断服务函数*/
void
vMBPortSerialRecvISR(void)
{prvvUARTRxISR();
}/*需手动安装到串口发送中断服务函数*/
void
vMBProtSerialSendISR(void)
{prvvUARTTxReadyISR();
}/* Create an interrupt handler for the transmit buffer empty interrupt* (or an equivalent) for your target processor. This function should then* call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that* a new character can be sent. The protocol stack will then call * xMBPortSerialPutByte( ) to send the character.*/
static void prvvUARTTxReadyISR( void )
{pxMBFrameCBTransmitterEmpty(  );
}/* Create an interrupt handler for the receive interrupt for your target* processor. This function should then call pxMBFrameCBByteReceived( ). The* protocol stack will then call xMBPortSerialGetByte( ) to retrieve the* character.*/
static void prvvUARTRxISR( void )
{pxMBFrameCBByteReceived(  );
}

4.事件
协议栈使用前后台架构,中断产生 事件 ,主循环处理 事件

事件生产者消费者描述
EV_READY定时器中断 (porttimer.c)
prvvTIMERExpiredISR
主循环 (mb.c)
eMBPoll
协议栈初始化完毕
EV_FRAME_RECEIVED定时器中断 (porttimer.c)
prvvTIMERExpiredISR
主循环 (mb.c)
eMBPoll
接收到一帧数据
如果数据帧校验正确,则产生 EV_EXECUTE 事件
EV_EXECUTE主循环 (mb.c)
eMBPoll
主循环 (mb.c)
eMBPoll
解析命令,生成应答数据,添加 CRC ,启动数据发送,数据将由串口发送中断发送
EV_FRAME_SENT串口发送中断 (portserial.c)
prvvUARTTxReadyISR
主循环 (mb.c)
eMBPoll
应答数据全部发送完成

事件一般用队列实现,以便消费者来不及处理事件时,暂时保存事件。对于简单应用,如果满足消费者消费事件的速度 大于等于 生产者生产事件的速度,则可以使用协议栈 \freemodbus\demo\BARE\port\portevent.c 文件中的源码,直接使用,不用修改:

#include "mb.h"
#include "mbport.h"/* ----------------------- Variables ----------------------------------------*/
static eMBEventType eQueuedEvent;
static BOOL     xEventInQueue;/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortEventInit( void )
{xEventInQueue = FALSE;return TRUE;
}BOOL
xMBPortEventPost( eMBEventType eEvent )
{xEventInQueue = TRUE;eQueuedEvent = eEvent;return TRUE;
}BOOL
xMBPortEventGet( eMBEventType * eEvent )
{BOOL            xEventHappened = FALSE;if( xEventInQueue ){*eEvent = eQueuedEvent;xEventInQueue = FALSE;xEventHappened = TRUE;}return xEventHappened;
}

在发送事件处就可以完成的功能,为什么要绕一圈非得用事件来完成呢?
方便解耦。
对于裸机环境,使用事件将处理过程从中断转移到主循环,从而使中断服务函数简单。
对于有操作系统的应用,事件可以方便的实现操作系统移植层,实现协议栈进程与中断之间的通讯。协议栈进程会因为等待事件而进入阻塞状态。

相关文章:

FreeModbus RTU 移植指南

FreeModbus 简介 FreeModbus 是一个免费的软件协议栈&#xff0c;实现了 Modbus 从机功能&#xff1a; 纯 C 语言支持 Modbus RTU/ASCII支持 Modbus TCP 本文介绍 Modbus RTU 移植。 移植环境&#xff1a; 裸机Keil MDK 编译器Cortex-M3 内核芯片&#xff08;LPC1778/88&…...

《唐诗三百首》数据源网络下载

2023年的 元宵之夜&#xff0c;这场以“长安”为主题的音乐会火了&#xff01;在抖音&#xff0c;超过2300万人次观看了直播&#xff0c;在线同赏唐诗与交响乐的融合。许多网友惊呼&#xff0c;上学时那些害怕背诵的诗句&#xff0c;原来还可以有这么美的表达这场近80分钟的音乐…...

(深度学习快速入门)第五章第一节2:GAN经典案例之MNIST手写数字生成

获取pdf&#xff1a;密码7281 文章目录一&#xff1a;数据集介绍二&#xff1a;GAN简介&#xff08;1&#xff09;简介&#xff08;2&#xff09;损失函数三&#xff1a;代码编写&#xff08;1&#xff09;参数及数据预处理&#xff08;2&#xff09;生成器与判别器模型&#x…...

雁过留痕,竟是病毒的痕迹?

凌恩生物全新升级宏病毒组分析流程&#xff1b;聚焦DNA&#xff0c;RNA病毒组研究热点&#xff1b;高灵敏度检测vOTUs&#xff1b;多软件整合&#xff0c;精准鉴定病毒序列&#xff1b;直击地化循环关键环节&#xff0c;助力宏病毒组科研成功&#xff01;期刊&#xff1a;Micro…...

Linux基本功系列之sort命令实战

文章目录前言一. sort命令介绍二. 语法格式及常用选项三. 参考案例3.1 按照文本默认排序3.2 忽略相同的行3.3 按数字大小进行排序3.4 检查文件是否已经按照顺序排序3.5 将第3列按照数字大小进行排序3.6 将排序结果输出到文件四. 探讨 -k的高级用法总结前言 大家好&#xff0c;…...

【笔记】移动端自动化:adb调试工具+appium+UIAutomatorViewer

学习源&#xff1a; https://www.bilibili.com/video/BV11p4y197HQ https://blog.csdn.net/weixin_47498728/category_11818905.html 一、移动端测试环境搭建 学习目标 1.能够搭建java 环境 2.能够搭建android 环境 &#xff08;一&#xff09;整体思路 我们的目标是Andr…...

面试复习题--性能检测原理

1、布局性能检测 Systrace&#xff0c;内存优化工具中也用到了 Systrace,这里关注 Systrace 中的 Frames 页面&#xff0c;正常情况下圆点为绿色&#xff0c;当出现黄色或者红色的圆点时&#xff0c;表现出现了丢帧。 Layout Inspector&#xff0c;是 AndroidStudio 自带工具…...

@LoadBalanced 和 @RefreshScope 同时使用,负载均衡失效分析

背景 最近引入了 Nacos Config 配置管理能力&#xff0c;说起来用法很简单&#xff0c;还是踩了三个坑。 Nacos Config 的 nacos 的帐号密码加密配置后&#xff0c;怎么解密而且在 NacosConfigBootstrapConfiguration 真正注入 Nacos Config 注入之前&#xff0c;而且不能触发…...

2023年个人计划

2023年个人计划 可能是最近太清闲&#xff0c;感觉生活很无聊&#xff0c;就胡乱做下新年的规划吧&#xff0c;扰乱下烦闷的心 1 二宝健健康康&#xff0c;活泼可爱 目前老婆已经怀孕5周左右了&#xff0c;二宝将在进行年中降生&#xff0c;希望老婆少受点罪&#xff0c;二宝…...

加拿大访问学者家属如何办理探亲签证?

由于大多数访问学者的访学期限都为一年&#xff0c;家人来访不仅可以缓解访学的寂寞生活&#xff0c;而且也是家人到加拿大体验国外风情的好机会。家属在国内申请赴加签证时&#xff0c;如果材料齐全&#xff0c;一般上午递交了申请&#xff0c;下午就可以拿到签证。以下是家人…...

操作系统基础---多线程

文章目录操作系统基础---多线程1.为何引入线程程序并发的时空开销线程的设计思路线程的状态和线程控制块TCB2.线程与进程的比较3.线程的实现⭐1.内核支持线程KST2.用户级线程3.组合方式操作系统基础—多线程 1.为何引入线程 利用传统的进程概念和设计方法已经难以设计出适合于…...

2022-12-10青少年软件编程(C语言)等级考试试卷(六级)解析

2022-12-10青少年软件编程&#xff08;C语言&#xff09;等级考试试卷&#xff08;六级&#xff09;解析T1、区间合并 给定 n 个闭区间 [ai; bi]&#xff0c;其中i1,2,...,n。任意两个相邻或相交的闭区间可以合并为一个闭区间。例如&#xff0c;[1;2] 和 [2;3] 可以合并为 [1;3…...

太酷了,用Python实现一个动态条形图!

大家好&#xff0c;我是小F&#xff5e;说起动态条形图&#xff0c;小F之前推荐过两个Python库&#xff0c;比如「Bar Chart Race」、「Pandas_Alive」&#xff0c;都可以实现。今天就给大家再介绍一个新的Python库「pynimate」&#xff0c;一样可以制作动态条形图&#xff0c;…...

单元测试junit+mock

单元测试 是什么&#xff1f; 单元测试&#xff08;unit testing&#xff09;&#xff0c;是指对软件中的最小可测试单元进行检查和验证。至于“单元”的大小或范围&#xff0c;并没有一个明确的标准&#xff0c;“单元”可以是一个方法、类、功能模块或者子系统。 单元测试通…...

2022Q4手机银行新版本聚焦提升客群专属、财富开放平台、智能化能力,活跃用户规模6.91亿人

易观&#xff1a;2022年第4季度&#xff0c;手机银行APP迭代升级加快&#xff0c;手机银行作为零售银行服务及经营的主阵地&#xff0c;与零售银行业务发展的联系日益紧密。迭代升级一方面可以顺应零售银行发展战略及方向&#xff0c;对手机银行业务布局进行针对性调整优化&…...

YOLO-V1~V3经典物体检测算法介绍

大名鼎鼎的YOLO物体检测算法如今已经出现了V8版本&#xff0c;我们先来了解一下它前几代版本都做了什么吧。本篇文章介绍v1-v3&#xff0c;后续会继续更新。一、节深度学习经典检测方法概述1.1 检测任务中阶段的意义我们所学的深度学习经典检测方法 &#xff0c;有些是单阶段的…...

SparkSQL 核心编程

文章目录SparkSQL 核心编程1、新的起点2、SQL 语法1) 读取 json 文件创建 DataFrame2) 对 DataFrame 创建一个临时表3) 通过SQL语句实现查询全表3、DSL 语法1) 创建一个DataFrame2) 查看DataFrame的Schema信息3) 只查看"username"列数据4) 查看"username"列…...

Android核心开发【UI绘制流程解析+原理】

一、UI如何进行具体绘制 UI从数据加载到具体展现的过程&#xff1a; 进程间的启动协作&#xff1a; 二、如何加载到数据 应用从启动到onCreate的过程&#xff1a; Activity生产过程详解&#xff1a; 核心对象 绘制流程源码路径 1、Activity加载ViewRootImpl ActivityThread…...

计算机组成原理第七章笔记记录

仅仅作为笔记记录,B站视频链接&#xff0c;若有错误请指出&#xff0c;谢谢 基本概念 演变过程 I/O系统基本组成 I/O软件 包括驱动程序、用户程序、管理程序、升级补丁等 下面的两种方式是用来实现CPU和I/O设备的信息交换的 I/O指令 CPU指令的一部分,由操作码,命令码,设备…...

ORB-SLAM2编译、安装等问题汇总大全(Ubuntu20.04、eigen3、pangolin0.5、opencv3.4.10)

ORB-SLAM2编译、安装等问题汇总大全&#xff08;Ubuntu20.04、eigen3、pangolin0.5、opencv3.4.10&#xff09; 1&#xff1a;环境说明: 使用的Linux发行版本为Ubuntu 20.04 SLAM2下载地址为&#xff1a;git clone https://github.com/raulmur/ORB_SLAM2.git ORB_SLAM2 2&a…...

终极指南:使用Legacy-iOS-Kit轻松降级、越狱和修复旧款iOS设备

终极指南&#xff1a;使用Legacy-iOS-Kit轻松降级、越狱和修复旧款iOS设备 【免费下载链接】Legacy-iOS-Kit An all-in-one tool to downgrade/restore, save SHSH blobs, and jailbreak legacy iOS devices 项目地址: https://gitcode.com/gh_mirrors/le/Legacy-iOS-Kit …...

C12832 LCD嵌入式驱动库详解:mbed平台128×32点阵显示开发指南

1. C12832 LCD驱动库概述C12832_lcd 是专为 mbed 应用开发板&#xff08;Application Board&#xff09;板载液晶显示屏设计的嵌入式驱动库。该显示屏型号为 C12832&#xff0c;是一款 12832 点阵、单色、COG&#xff08;Chip-on-Glass&#xff09;结构的 STN 液晶模块&#xf…...

电赛小车避坑指南:从2011到2024,那些年我们踩过的传感器和通信模块的‘坑’

电赛小车避坑指南&#xff1a;从2011到2024&#xff0c;那些年我们踩过的传感器和通信模块的"坑" 参加全国大学生电子设计竞赛的同学们都知道&#xff0c;小车控制类赛题一直是热门选项。从2011年的双车自主超车到2024年的自动行驶小车&#xff0c;这些题目看似简单&…...

Markdown Viewer浏览器扩展完全指南:从安装到高级配置

Markdown Viewer浏览器扩展完全指南&#xff1a;从安装到高级配置 【免费下载链接】markdown-viewer Markdown Viewer / Browser Extension 项目地址: https://gitcode.com/gh_mirrors/ma/markdown-viewer Markdown Viewer是一款功能丰富的浏览器扩展&#xff0c;专为提…...

js06----流程控制

目录 2.4.1、顺序流程控制 2.4.2、分支流程控制 &#xff08;1&#xff09;if分支语句&#xff08;条件判断语句&#xff09; &#xff08;2&#xff09;if....else...语句 需求1&#xff1a; 需求2&#xff1a; &#xff08;3&#xff09;if...else if...else语句&…...

【GNSS定位原理及算法杂记2】GNSS观测量:从捕获到解算,揭秘接收机内部信号处理链路

1. GNSS观测量&#xff1a;定位技术的三大支柱 当你打开手机地图查看自己的位置时&#xff0c;背后是GNSS接收机在默默工作。它通过处理来自太空卫星的信号&#xff0c;最终计算出你所在的位置。这个过程中最关键的就是三种观测量&#xff1a;伪距、载波相位和多普勒频移。这三…...

团队用ai写代码越来越猛但为什么改个功能像在拆炸弹背后是流程断了

最近不少团队反馈&#xff0c;AI Coding 跑得飞快&#xff0c;两周就能堆出新功能&#xff0c;可一旦要改个按钮颜色&#xff0c;整个系统却像在拆炸弹。这种“改功能崩塌”的怪圈&#xff0c;正让许多管理者头疼&#xff1a;明明用了最先进的工具&#xff0c;交付反而更慢了。…...

Translumo完整指南:高效实时屏幕翻译工具解决你的多语言障碍难题

Translumo完整指南&#xff1a;高效实时屏幕翻译工具解决你的多语言障碍难题 【免费下载链接】Translumo Advanced real-time screen translator for games, hardcoded subtitles in videos, static text and etc. 项目地址: https://gitcode.com/gh_mirrors/tr/Translumo …...

League-Toolkit英雄联盟辅助工具完全指南:从配置到精通的高效使用手册

League-Toolkit英雄联盟辅助工具完全指南&#xff1a;从配置到精通的高效使用手册 【免费下载链接】League-Toolkit 兴趣使然的、简单易用的英雄联盟工具集。支持战绩查询、自动秒选等功能。基于 LCU API。 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit …...

4个步骤让普通用户实现黑苹果EFI自动生成:OpCore Simplify智能工具全解析

4个步骤让普通用户实现黑苹果EFI自动生成&#xff1a;OpCore Simplify智能工具全解析 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 如何用智能工具解…...