TI E2E™ 设计支持论坛将于 5 月 30 日至 6 月 1 日进行维护。如果您在此期间需要技术支持,请联系 TI 的客户支持中心寻求帮助。

This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] CCS/TM4C123GH6PM:在 TM4C123G 上使用 FreeMODBUS 实现 Modbus RTU

Guru**** 2046970 points
Other Parts Discussed in Thread: TM4C123GH6PM
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/762554/ccs-tm4c123gh6pm-modbus-rtu-implementation-using-freemodbus-on-tm4c123g

器件型号:TM4C123GH6PM
主题中讨论的其他器件: TM4C123

工具/软件:Code Composer Studio

大家好、

我正在尝试使用 FreeMODBUS 堆栈在 TM4C123G 上实现 Modbus 协议。 但问题似乎发生在硬件设置上、这里是我使用的代码

对于 port.h:

/*
FreeModbus Libary:Bare Port
* Copyright (C) 2006 Christian Walter 
*
*此库是免费软件;您可以根据

免费软件基金会发布的 GNU Lesser General Public *许可证的条款重新分发和/或*修改它;
*许可证2.1版或(您可以选择)任何更高版本。
*
*发布本库的目的是希望其有用
、*但不作任何保证;甚至没有*
适销性或特定用途适用性的暗示保证。 有关
详细信息、请参阅 GNU * Lesser General Public License。
*
*您应该已经收到 GNU Lesser General Public
*许可证以及此库的副本;如果没有,请写信至 Free Software
* Foundation、Inc.,51 Franklin St,Fifth Floor,Boston, MA 02110-1301 USA
*
*文件:$ID$
*/

#ifndef _port_H
#define _port_H

#include 
#include 
#include 
#include 
#include "inc/tm4c123gh6m.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_sysctl.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/driverlib.gpio"
#define #define


内联
#define PR_BEGIN_EXTERN extern "C"{
#definePR_END_extern _C}

define enter_critical section() IntMasterDisable();
#define EXIT_CRITICAL_SECTION () IntMasterEnable();

typedef uint8_t BOOL;

typedef unsigned char UCHAR;
typedef char char;

typedef uint16_t USHORT;
typedef Int16_t short;

tyedef Uint32_t long;
typedef int32_t long;

#ifndef true
#define true 1
#endif

#ifndef false
#define false 0
#endif

#endif

portserial.h

/*
* FreeModbus Libary:裸端口
* Copyright (C) 2006 Christian Walter
*
*此库是免费软件;您可以重新分发和/或
*根据 GNU Lesser General Public 的条款对其进行修改
*由免费软件基金会发布的许可证;或者
*许可证2.1版,或(您可以选择)任何更高版本。
*
*发布此库的目的是希望它有用、
*但不提供任何保证;甚至没有的暗示保证
*适销性或特定用途的适用性。 请参阅 GNU
*较宽松通用公共许可证,了解更多详细信息。
*
*您应该已经收到 GNU Lesser General Public 的副本
*随此库一起提供许可证;如果没有、请写入免费软件
* Foundation、Inc.、51 Franklin St、Fifth Floor、Boston、 MA 02110-1301美国
*
*文件:$ID$
*

#include
#include
#include "inc/tm4c123gh6m.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/gpio.h"
#include "driverlib/timer.h"
#include "driverlib/pin_map.h"
#include "driverlib/uart.h"

#include "port.h"

/*---------------- Modbus 包括------------------ *
#include "mB.h"
#include "mbport.h"

/*---------------- 静态函数------------------------------------------------------- *
static void prvUARTTxReadyISR( void );
static void prvUARTRxISR( void );

/*---------------- 开始执行--- *
无效
vMBPortSerialEnable( BOOL xRxEnable,BOOL xTxEnable )

/*如果 xRXEnable 启用串行接收中断。 如果启用了 xTxENable
*发送器为空中断。
*
if (xRxEnable)

UARTIntEnable (UART0_BASE、UART_INT_RX|UART_INT_RT);
UARTIntRegister (UART0_BASE、&prvUARTRxISR);

其他
UARTIntDisable (UART0_BASE、UART_INT_RX);

if (xTxEnable)

UARTIntEnable (UART0_BASE、UART_INT_TX);
UARTIntRegister (UART0_BASE、&prvUARTTxReadyISR);

其他
UARTIntDisable (UART0_BASE、UART_INT_TX);

布尔
xMBPortSerialInit( UCHAR ucPORT、Ulong ulBaudRate、UCHAR ucDataBits、eMBParity eParity )

(空) ucPORT;
uint32_t 奇偶校验、位、秒位;
//
//启用 UART 使用的 GPIO 外设。
//
SysCtlPeripheralEnable (SYSCTL_Periph_GPIOA);
//
//启用 UART0
//
SysCtlPeripheralEnable (SYSCTL_Periph_UART0);

//
//为 UART 模式配置 GPIO 引脚。
//
GPIOPinConfigure (GPIO_PA0_U0RX);
GPIOPinConfigure (GPIO_PA1_U0TX);
GPIOPinTypeUART (GPIO_Porta_base、GPIO_PIN_0 | GPIO_PIN_1);

//
//选择 UART 的长度和奇偶校验
//

switch (eParity)

案例 MB_PAR_LEVen:
奇偶校验= UART_CONFIG_PAR_LEVen;
stopbits = UART_CONFIG_STOP_ONE;
中断;
案例 MB_PAR_ODD:
奇偶校验= UART_CONFIG_PAR_ODD;
stopbits = UART_CONFIG_STOP_ONE;
中断;
案例 MB_PAR_NONE:
奇偶校验= UART_CONFIG_PAR_NONE;
stopbits = UART_CONFIG_STOP_Two;//确保11位 RTU 字符格式

中断;

Switch( ucDataBits )

案例8:
位= UART_CONFIG_WLEN_8;
中断;
案例7:
位= UART_CONFIG_WLEN_7;
中断;

//
//初始化控制台 I/O 的 UART
//
UARTConfigSetExpClk (UART0_BASE、SysCtlClockGet ()、ulBaudRate、(位|秒位|奇偶校验));
//禁用 FIFO,以确保中断仅用于 RX 的更改
UARTFIFODisable (UART0_BASE);
//
//启用 UART 和中断
//
UARTIntEnable (UART0_BASE、UART_INT_RX|UART_INT_RT);
UARTIntRegister (UART0_BASE、&prvUARTRxISR);
UARTEnable (UART0_BASE);


返回 false;

布尔
xMBPortSerialPutByte (char ucByte)

/*在 UART 发送缓冲区中放入一个字节。 此函数被调用
*如果 pxMBFrameCBTransmitterEmpty()已经存在,则按协议栈
*被调用。 *
UARTCharPut (UART0_BASE、ucByte);
if (UARTCharsAvail (UART0_BASE))

UARTCharPut (UART0_BASE、UARTCharGet (UART0_BASE));

返回 true;

布尔
xMBPortSerialGetByte (char * pucByte)

/*返回 UART 接收缓冲区中的字节。 此函数被调用
*在调用 pxMBFrameCBByteRecepto()后由协议栈提供。
*
UARTIntClear (UART0_BASE、UARTIntStatus (UART0_BASE、TRUE));
while (UARTCharsAvail (UART0_BASE))

* pucByte = UARTCharGetNonBlocking (UART0_BASE);

返回 true;

/*为发送缓冲区空中断创建中断处理程序
*(或等效项)。 此函数应如此
*调用 pxMBFrameCBTransmitterEmpty(),它告诉协议栈
*可以发送新字符。 然后、协议栈将调用
* xMBPortSerialPutByte()发送字符。
*
静态空 prvUARTTxReadyISR(空)

pxMBFrameCBTransmitterEmpty();

/*为目标的接收中断创建中断处理程序
*处理器。 然后,此函数应调用 pxMBFrameCBByteRecepthat()。 。
然后,协议栈将调用 xMBPortSerialGetByte()来检索
*字符。
*
静态 void prvUARTRxISR( void )

pxMBFrameCBByteRecept接收 器();

porttimer.h

/*
* FreeModbus Libary:裸端口
* Copyright (C) 2006 Christian Walter
*
*此库是免费软件;您可以重新分发和/或
*根据 GNU Lesser General Public 的条款对其进行修改
*由免费软件基金会发布的许可证;或者
*许可证2.1版,或(您可以选择)任何更高版本。
*
*发布此库的目的是希望它有用、
*但不提供任何保证;甚至没有的暗示保证
*适销性或特定用途的适用性。 请参阅 GNU
*较宽松通用公共许可证,了解更多详细信息。
*
*您应该已经收到 GNU Lesser General Public 的副本
*随此库一起提供许可证;如果没有、请写入免费软件
* Foundation、Inc.、51 Franklin St、Fifth Floor、Boston、 MA 02110-1301美国
*
*文件:$ID$
*
#include
#include
#include "inc/tm4c123gh6m.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_sysctl.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/gpio.h"
#include "driverlib/timer.h"

/*---------------- 平台包括: *
#include "port.h"

/*---------------- Modbus 包括------------------ *
#include "mB.h"
#include "mbport.h"

/*---------------- 静态函数------------------------------------------------------- *
static void prvTIMERExpiredISR( void );

/*---------------- 开始执行--- *
布尔
xMBPortTimersInit( USHORT usTim1Timerout50us )

uint32_t a;
SysCtlPeripheralEnable (SYSCTL_Periph_TIMER0);
TimerConfigure (TIMER0_BASE、TIMER_CFG_ONE_SHOT);
a = usTim1Timerout50us*SysCtlClockGet ()/2000-1;
TimerLoadSet (TIMER0_BASE、TIMER_A、A);
TimerIntRegister (TIMER0_BASE、TIMER_A、&prvTIMERExpiredISR);
IntEnable (INT_TIMER0A);
TimerIntEnable (TIMER0_BASE、TIMER_TINA_TIMEOUT);
IntMasterEnable();
GPIOPinWrite (GPIO_PORTF_BASE、GPIO_PIN_2、4);

返回 false;


内联空
vMBPortTimersEnable()

/*启用超时传递给 xMBPortTimersInit()*/的计时器

TimerEnable (TIMER0_BASE、TIMER_A);

内联空
vMBPortTimersDisable()

/*禁用任何挂起的计时器。 *
TimerIntClear (TIMER0_BASE、TIMER_TINA_TIMEOUT);
GPIOPinWrite (GPIO_PORTF_BASE、GPIO_PIN_2、0);

/*创建一个 ISR,该 ISR 在计时器过期时调用。 此函数
*然后必须调用 pxMBPortCBTimerExpired ()通知协议栈
*计时器已过期。
*
静态 void prvTIMERExpiredISR( void )

( void )pxMBPortCBTimerExpired ();

用于 main.c  

/*
FreeModbus Libary:Bare Demo Application
*版权所有(C) 2006 Christian Walter 
*
*此程序是免费软件;您可以根据

*免费软件基金会发布的 GNU 通用公共许可证条款重新分发和/或修改*它;许可证的第2版或
*(您可以选择)任何后续版本。
*
**本计划的发布目的是希望其有用
、*但没有任何保证;甚至没有*
适销性或特定用途适用性的暗示保证。 有关
更多详细信息、请参阅* GNU 通用公共许可证。
*
*您应该已经收到 GNU 通用公共许可证*的副本
以及此程序;如果没有,请写信至 Free Software
* Foundation、Inc.,51 Franklin St,Fifth Floor,Boston, MA 02110-1301 USA
*
*文件:$ID$
*/

/*--- Modbus 包括------------------ //
#include "mb.h"
#include "mbport.h"
#include 
#include 
#include "inc/tm4c123gh6m.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/gpio.h"
#include "driverlib/timer.h"
--- 定义了------------------------------------------------------- */
#define REG_INPUT_START 3000
#define REG_INPUT_NREGS 4

/*-------------- 静态变量------------------------------------------------------- */
static USHORT usRegInputStart = REG_INPUT_START;
static USHORT usRegInputBuf[REG_INPUT_NREGS];

/*------------ 开始执行--- */
void Configuration()
{
SysCtlClockSet (SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHz);// 16MHz 时钟
SysCtlPeripheralEnable (SYSCTL_Periph_GPIOF);
GPIOPinTypeGPIOOutput (GPIO_PORTF_BASE、GPIO_PIN_3 | GPIO_PIN_2);
SysCtlDelay (100000);
GPIOPinWrite (GPIO_PORTF_BASE、GPIO_PIN_3 | GPIO_PIN_2、0);
}

int
main (void)
{
eMBErrorCode EStatus;

配置();

EStatus = eMBInit( MB_RTU、0x0A、0、9600、MB_PAR_EVLETE );

/*启用 Modbus 协议栈。 *
EStatus = eMBEnable();
// eMBClose ();

for (;;)
{
( void )eMBPoll();

/*在这里、我们只计算轮询周期数。 *
usRegInputBuf[0]++;
usRegInputBuf[1]= 5;
usRegInputBuf[2]= 6;
usRegInputBuf[3]= 33;

}


}

eMBErrorCode
eMBRegInputCB( UCHAR * puRegBuffer、USHORT usAddress、USHORT usRegs )
{
eMBErrorCode EStatus = MB_ENOERR;
内部 iRegIndex;

if ((usAddress >= REG_INPUT_START)
&&(usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS)
{
iRegIndex =( int )( usAddress - usRegInputStart );
while (usNRegs >0)
{
*puRegBuffer++=
(unsigned char)(usRegInputBuf[iRegIndex]>> 8);
*puRegBuffer++=
(unsigned char)(usRegInputBuf[iRegIndex]& 0xFF);
iRegIndex++;
usNRegs--;
}
}
其他
{
EStatus = MB_ENOREG;
}

返回 EStatus;
}

eMBErrorCode
eMBRegHoldingCB (UCHAR * puRegBuffer、USHORT usAddress、USHORT usNRegs、
eMBRegisterMode eMode)
{
返回 MB_ENOREG;
}


eMBErrorCode
eMBRegCoilsCB (UCHAR * puRegBuffer、USHORT usAddress、USHORT usNils、
eMBRegisterMode eMode)
{
返回 MB_ENOREG;
}

eMBErrorCode
eMBRegDisconteCB( UCHAR * puRegBuffer、USHORT usAddress、USHORT us离散)
{
返回 MB_ENOREG;
} 

测试后、我发现微控制器仍然接收数据、但不会处理。 这可能是由于计时器设置、但我不确定、因为没有要观察的输出。 我尝试使用 GPIO LED 来跟踪数据过程、但当涉及到计时器时、中断条件可能会阻止它。

请帮我解决这个问题。 我确实在 TM4C 上查找了 Modbus 实现、但没有任何解决方案可以提供帮助、因此我不知道下一步要做什么。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好!
    您使用的是我们无法支持的第三方代码。 我将建议您在计时器中断 ISR 中放置一个断点、并调试发生的情况。 您是否看到计时器中断发生?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    感谢你的答复。   

    我将断点放入中断 ISR 中、但看不到发生计时器中断。 起初、我以为可能是因为我没有正确重新启动计时器。 我尝试使用它来实现它  

    bool
    xMBPortTimersInit( USHORT usTim1Timerout50us )
    {
    SysCtlPeripheralEnable (SYSCTL_Periph_TIMER0);
    TimerConfigure (TIMER0_BASE、TIMER_CFG_ONE_SHOT);
    A =(((usTim1Timerout50us*SysCtlClockGet ())/20000)-1;
    TimerLoadSet (TIMER0_BASE、TIMER_A、A);
    TimerIntRegister (TIMER0_BASE、TIMER_A、&prvTIMERExpiredISR);
    IntEnable (INT_TIMER0A);
    TimerIntEnable (TIMER0_BASE、TIMER_TINA_TIMEOUT);
    TimerEnable (TIMER0_BASE、TIMER_A);
    
    返回 false;
    }
    
    
    内联 void
    vMBPortTimersEnable()
    {
    /*启用超时传递给 xMBPortTimersInit()*/的计时器
    TimerLoadSet (TIMER0_BASE、TIMER_A、A);
    TimerIntEnable (TIMER0_BASE、TIMER_TINA_TIMEOUT);
    TimerEnable (TIMER0_BASE、TIMER_A);
    }
    
    内联 void
    vMBPortTimersDisable()
    {
    /*禁用任何挂起的计时器。 *
    TimerDisable (TIMER0_BASE、TIMER_A);
    TimerIntDisable (TIMER0_BASE、TIMER_TINA_TIMEOUT);
    TimerIntClear (TIMER0_BASE、TIMER_TIMA_TIMEOUT);
    }
    
    //创建一个 ISR,该 ISR 在计时器过期时调用。
    然后,此函数*必须调用 pxMBPortCBTimerExpired (),以通知协议栈
    *计时器已过期。
    */
    static void prvTIMERExpiredISR( void )
    {
    TimerIntClear (TIMER0_BASE、TIMER_TINA_TIMEOUT);
    ( void )pxMBPortCBTimerExpired ();
    } 

    但它仍然不起作用。 我不知道为什么在我运行一个单独的程序时中断仍然会发生(我是指一个用于定时器中断的程序、而不是上面的程序)。 请帮我解决这个问题。 谢谢!

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    尝试 TimerIntRegister (TIMER0_BASE、TIMER_A、prvTIMERExpiredISR)、而不是 TimerIntRegister (TIMER0_BASE、TIMER_A、 prvTIMERExpiredISR)。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    我尝试过它,但中断点仅在我将它放在 vMBPortTimersEnable()中时才起作用,当我在 ISR 中放置中断点时它不起作用(我一次只放置1个中断点以避免冲突)。

    关于代码、您能告诉我 prvTIMERExpiredISR 和&prvTIMERExpiredISR 之间的区别吗? 因为我认为"&a"是"a"的引用、它适用于函数 extern void TimerIntRegister (uint32_t ui32Base、uint32_t ui32Timer、void (* pfnHandler)(void))-在 pfnHandler 中有一个指针。

    此致、
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    我不清楚你的意思。 您是否说过、如果在 vMBPortTimersEnable 中设置了断点、那么您将看到计时器发生中断? 当中断没有出现时、检查定时器中断状态寄存器、即 GPTMRIS 和 GPTMMIS 寄存器、并查看是否有任何标志置位。 此外、检查 GPTMIMR 寄存器以查看是否启用了中断。

    prvTIMERExpiredISR 已是函数指针。 您不需要&。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    关于断点、当我将断点放在 vMBPortTimersEnable 中时、该过程在 TimerEnable 函数处停止。 但是、当我在计时器中断函数中放置断点时、该过程并未停止。 因此、我的假设是该过程中未调用该函数。  

    关于检查状态寄存器、我以前没有做过类似的事情。 因此、我将尽快尝试并回复您。  

    感谢您的建议。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Charles:

    我发现、由于某些不适用的函数、未调用中断。 所以我决定自己重写所有内容、该计划按预期工作(感谢您的建议)。

    但目前、我在线圈、寄存器等的地址方面遇到了一些问题 Modbus 协议要求读取或读取/写入某些寄存器。

    例如:

    • 线圈(R/W):0x00000
    • 离散输入 (RO):0x10000
    • 输入寄存器(RO):0x30000
    • 保持寄存器(R/W):0x40000

    我发现数据的地址可以使用 HWREG 或直接使用指针来分配。 但我不确定如果我这么做、程序是否会发生冲突。 我已经读出该范围中的数据是针对闪存 ROM 的、但我找不到更具体的存储器映射。 我担心 TM4C123GH6PM 是否使用相同类型的存储器映射;因为首先、当我调试程序时、我发现分立式输入(只读、由于标准 Modbus 指南、在0x10000处)全部为1。 (下图)

    我尝试查找其他 MCU 的存储器设置、发现对于 STM32和 Arduino、它们有寄存器组可将数据放入所需的地址、但我在 TM4C123GH6PM 中找不到任何类似的功能。 可以帮我解决这个问题吗? 谢谢你。

    此致、

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好!
    我想您会混淆 Modbus 地址与内部闪存地址。 Modbus 从地址用于 Modbus 器件、而不是用于内部闪存。 如果要基于 RS232实现 Modbus、则需要使用 UART 模块。 请参阅数据表中的 UART 模块以了解这些寄存器。

    我认为以下帖子可能对您有所帮助。
    e2e.ti.com/.../1466567
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    很抱歉、我一开始就没有明确说明、我试图暗示 TM4C123GH6PM 上的 Modbus 协议是从设备、并将我的笔记本电脑上的 C#程序用作主设备。 我在 RS485上执行它、我使用了 RS232和 UART 模块的转换器。
    现在我想将 TM4C123GH6PM 编程为从器件、存储器(而不是地址)是否会保存在内部闪存中? 或者、如果不是、它将存储在何处?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    为什么要将 MCU 用作 Modbus 从设备? TM4C123 MCU 本身不支持 MODBUS。 您将需要以某种方式将其建模为 MODBUS 器件。 您需要将 Modbus 地址映射到 RAM 区域。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    那么、您是说我需要通过调用主程序中的数组将 Modbus 数据放入 RAM 区域、对吧? 我读出 RAM 地址是0x20000000、所以当我进行调试时、内存浏览器看起来是这样的(下图)、我对吗?

    此致、

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好!
    是的、您的理解是正确的。 如果您能使其正常工作、请您与社区分享您的项目、因为我认为寻找相同解决方案的许多人都将从中受益。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    我想分享我的项目、但现在我只为 Modbus 协议编写几个函数(不完全)、因为我的应用和代码有点复杂。 但是、我考虑在完成当前研究后立即重写完整的计划。
    感谢您的帮助、祝您一切顺利!
    此致、