Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 
 

16 KiB

Modbus Retention and Private Functions Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Replace the communication-history extension with 0x42 extended-register write and 0x43 odd-count register read, while retaining RTC persistence and documenting the TouchWin functions.

Architecture: modbus.c remains the protocol-only layer: it validates request frames, reads or writes the configured 0x00010000 storage, and builds RTU responses. main.c owns persistence policy, adding 0x42 to successful write operations so the existing RTC backup service saves it. The host protocol harness executes the parser against explicit backing storage; the TouchWin document contains the matching HMI C functions and PSW bindings.

Tech Stack: STM32F407 C firmware, uC/OS-II, IAR project, Modbus RTU, RTC backup registers, GNU GCC host test executable with assert.

Global Constraints

  • Treat 0x41, 0x42, and 0x43 as this training project’s private protocol, not standard Modbus function codes.
  • Keep standard data address range 0x0000..0x270F; only private 0x41 and 0x42 access 0x00010000.
  • 0x42 writes one 16-bit value to 0x00010000, has a fixed 10-byte RTU ADU, and echoes its complete request PDU on success.
  • 0x43 reads one to 123 contiguous holding registers and rejects even quantities and ranges beyond 0x270F.
  • Remove all communication-history state and API, including RAM records, 0x43 history parsing, HMI PSW 500..509, and history display text.
  • Keep TouchWin clear local: it writes only PSW350 = 0 and does not transmit or modify PLC data.
  • Follow the supplied Xinje C conventions: four-space indentation, braces on separate lines, explicit validation, named constants, and concise Doxygen-style API comments.
  • Preserve all unrelated uncommitted user changes. Do not alter the pre-existing .git/index.lock.

File Structure

  • Core/Modbus/modbus.h: private function-code constants and the minimal MODBUS_SLAVE data model.
  • Core/Modbus/modbus.c: private 0x42 write builder, 0x43 odd-count read builder, request dispatch, and removal of history code.
  • Core/Modbus/modbus_test.c: host-only parser regression harness using caller-owned holding-register and coil storage.
  • Core/Src/main.c: persistence classification for 0x42 and removal of history collection from the application task.
  • document/TouchWin自组Modbus功能码函数.md: TouchWin C definitions and callable functions for read, write, clear, and odd-count read.
  • document/TouchWin_Modbus_附加功能测试界面方案.svg: revised training screen reference without communication history, using PSW350, PSW351, and PSW370..PSW494.

Task 1: Add Failing Protocol Tests for the Remapped Extensions

Files:

  • Create: Core/Modbus/modbus_test.c
  • Modify: none
  • Test: Core/Modbus/modbus_test.c

Interfaces:

  • Consumes: ModbusSlaveInit(), ModbusConfigureExtendedHoldingRegister(), ModbusProcessFrame(), ModbusCrc16() from modbus.h.

  • Produces: modbus_protocol_test.exe, a host parser test binary that returns 0 only when every assert succeeds.

Create Core/Modbus/modbus_test.c with project-compatible initialization. The test must not call the obsolete two-argument ModbusSlaveInit() from HEAD; it must supply real storage:

static uint16_t TestHoldingRegisters[MODBUS_DATA_POINT_COUNT];
static uint8_t TestCoilStorage[MODBUS_COIL_STORAGE_SIZE];
static uint16_t TestExtendedRegister;

static void TestInitSlave(MODBUS_SLAVE *slave)
{
    assert(ModbusSlaveInit(slave, TEST_SLAVE_ADDRESS,
                           TestHoldingRegisters, TestCoilStorage)
           == MODBUS_STATUS_OK);
    assert(ModbusConfigureExtendedHoldingRegister(slave,
                                                  &TestExtendedRegister)
           == MODBUS_STATUS_OK);
}

Add TestWriteExtendedRegister() using a 10-byte request. It must send 01 42 00 01 00 00 BE EF CRC_L CRC_H, expect MODBUS_STATUS_OK, a 10-byte response that echoes bytes 0..7, a valid CRC, and TestExtendedRegister == 0xBEEFU.

static void TestWriteExtendedRegister(void)
{
    uint8_t request[10U] = {1U, 0x42U, 0U, 1U, 0U, 0U,
                            0xBEU, 0xEFU, 0U, 0U};
    uint8_t response[MODBUS_RTU_ADU_MAX_LENGTH];
    uint16_t responseLength;
    MODBUS_SLAVE slave;

    TestInitSlave(&slave);
    TestAppendCrc(request, 8U);
    assert(ModbusProcessFrame(&slave, request, sizeof(request), response,
                              sizeof(response), &responseLength)
           == MODBUS_STATUS_OK);
    assert(responseLength == sizeof(request));
    assert(memcmp(response, request, 8U) == 0);
    assert(TestExtendedRegister == 0xBEEFU);
    TestAssertFrameCrc(response, responseLength);
}

Add <string.h> for memcmp. Add a sibling test whose address bytes encode 0x00010001; it must expect MODBUS_STATUS_EXCEPTION, function byte 0xC2, and exception code MODBUS_EXCEPTION_ILLEGAL_DATA_ADDR.

Seed TestHoldingRegisters[0], [1], and [2] with 0x1234, 0xABCD, and 0x0001. Send 01 43 00 00 00 03 CRC_L CRC_H; assert status OK, response payload 01 43 06 12 34 AB CD 00 01, and a valid CRC. Add a separate request with count 2 and assert exception function 0xC3 with code MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE.

Run:

gcc -std=c99 -Wall -Wextra -Werror -ICore/Modbus Core/Modbus/modbus.c Core/Modbus/modbus_test.c -o modbus_protocol_test.exe
.\modbus_protocol_test.exe

Expected: the executable reaches an assertion because current 0x42 is an odd-count read and current 0x43 is communication history. The source must compile cleanly; a test compile error is not an acceptable RED result.

git add -- Core/Modbus/modbus_test.c
git commit -m "test: cover remapped private modbus functions"

Do not run these commands while .git/index.lock exists.

Task 2: Remap the Parser and Remove Communication History

Files:

  • Modify: Core/Modbus/modbus.h:45-132
  • Modify: Core/Modbus/modbus.c:56-85, Core/Modbus/modbus.c:530-755, Core/Modbus/modbus.c:785-890, Core/Modbus/modbus.c:990-1100
  • Test: Core/Modbus/modbus_test.c

Interfaces:

  • Consumes: the constants and extendedHoldingRegister supplied by MODBUS_SLAVE.

  • Produces: MODBUS_FC_WRITE_EXTENDED_HOLDING_REGS (0x42U), MODBUS_FC_READ_ODD_COUNT_REGS (0x43U), and successful protocol dispatch for both requests.

In modbus.h, replace the current private constants with:

#define MODBUS_FC_READ_EXTENDED_HOLDING_REGS  (0x41U)
#define MODBUS_FC_WRITE_EXTENDED_HOLDING_REGS (0x42U)
#define MODBUS_FC_READ_ODD_COUNT_REGS        (0x43U)
#define MODBUS_EXTENDED_HOLDING_ADDRESS       (0x00010000UL)

Delete MODBUS_FC_READ_COMMUNICATION_HISTORY, every MODBUS_HISTORY_* macro, MODBUS_HISTORY_RECORD, all history members of MODBUS_SLAVE, and the ModbusRecordHistory() declaration. Keep the extended-register pointer and its configuration API.

Replace the old ModbusBuildReadOddCountRegistersResponse() at the 0x42 position with a builder accepting a 32-bit address and one 16-bit value. It must validate ModbusIsExtendedRegisterAddressValid(), write through slave->extendedHoldingRegister, and build the fixed response:

txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_WRITE_EXTENDED_HOLDING_REGS;
ModbusWriteU32(&txBuffer[2U], startAddress);
ModbusWriteU16(&txBuffer[6U], registerValue);
*txLength = 8U;

Add ModbusWriteU32() beside ModbusReadU32() and then append CRC. Require txCapacity >= 10U. Return illegal address for any address except 0x00010000.

Retain the existing odd-count validation and response data loop, but rename its comment to 0x43 and assign MODBUS_FC_READ_ODD_COUNT_REGS to the response function byte. Its PDU remains 16-bit start address plus 16-bit count; it must not retain any offset/count history interpretation.

In ModbusProcessFrame(), use exact request lengths and payload parsing:

case MODBUS_FC_WRITE_EXTENDED_HOLDING_REGS:
    if (rxLength != 10U) {
        requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
        break;
    }
    requestResult = ModbusBuildWriteExtendedRegisterResponse(
        slave, ModbusReadU32(&rxBuffer[2U]), ModbusReadU16(&rxBuffer[6U]),
        txBuffer, txCapacity, txLength);
    break;

case MODBUS_FC_READ_ODD_COUNT_REGS:
    if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
        requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
        break;
    }
    requestResult = ModbusBuildReadOddCountRegistersResponse(
        slave, ModbusReadU16(&rxBuffer[2U]), ModbusReadU16(&rxBuffer[4U]),
        txBuffer, txCapacity, txLength);
    break;

Delete ModbusBuildHistoryResponse(), the history initialization loop in ModbusSlaveInit(), and the full ModbusRecordHistory() definition. Remove variables that become unused.

Run the same two commands from Task 1. Expected: modbus_protocol_test.exe exits 0, with no GCC warnings. Add a 0x41 regression assertion in the test harness before accepting the result, proving its 10-byte request still returns the configured 16-bit value.

git add -- Core/Modbus/modbus.h Core/Modbus/modbus.c Core/Modbus/modbus_test.c
git commit -m "feat: remap private modbus extensions"

Task 3: Persist 0x42 Writes and Remove Application History Calls

Files:

  • Modify: Core/Src/main.c:107-109, Core/Src/main.c:488-536, Core/Src/main.c:739-840
  • Test: Core/Modbus/modbus_test.c

Interfaces:

  • Consumes: MODBUS_FC_WRITE_EXTENDED_HOLDING_REGS and existing ModbusBackupSave().

  • Produces: an application-level successful 0x42 transaction that is classified as a write and therefore persisted by the existing RTC path.

Delete the AppGetHistoryResultCode() prototype and definition, the historyResult local variable, and each ModbusRecordHistory() call. Preserve existing request validation, link-state accounting, debug snapshots, response transmission, and protocol-error counting.

Update AppIsModbusWriteFunction() with the private write code:

case MODBUS_FC_WRITE_EXTENDED_HOLDING_REGS:
    return 1U;

It belongs in the same switch as 0x05, 0x06, 0x0F, and 0x10, so the existing success-only block invokes ModbusBackupSave(&ModbusSlave) for a valid 0x42 write. Do not call the backup service from a failed or exception response.

Open EWARM/Modbus.ewp in IAR Embedded Workbench and build the Modbus target. Expected: zero C errors from removed history symbols and the new 0x42 constant. The current environment does not provide iccarm, so record an unavailable IAR build rather than substituting a host build for firmware validation.

With machine outputs isolated and VBAT fitted, write a nonzero value through 0x42, verify the 0x42 echo, power-cycle the test PLC, then use 0x41 to read back the same value. Clear only PSW350 before the power cycle; do not send a PLC write as part of clearing. Record the write and read frames with the observed value.

git add -- Core/Src/main.c
git commit -m "feat: persist extended register writes"

Task 4: Publish the TouchWin Functions and Remove History from the Screen Reference

Files:

  • Modify: document/TouchWin自组Modbus功能码函数.md
  • Modify: document/TouchWin_Modbus_附加功能测试界面方案.svg
  • Test: document/TouchWin自组Modbus功能码函数.md

Interfaces:

  • Consumes: the final 0x41/0x42/0x43 wire contracts and the shared TouchWin helpers ModbusAppendCrc(), ModbusSendReceive(), and ModbusCheckResponse().

  • Produces: copyable TouchWin C functions plus a reference screen that has no communication-history controls.

Document only these mappings:

#define MODBUS_FUNCTION_READ_EXTENDED_REGISTER  (0x41)
#define MODBUS_FUNCTION_WRITE_EXTENDED_REGISTER (0x42)
#define MODBUS_FUNCTION_READ_ODD_COUNT_REGS     (0x43)

#define PSW_EXTENDED_RESULT                     (350)
#define PSW_EXTENDED_WRITE_VALUE                (351)
#define PSW_ODD_START_ADDRESS                   (370)
#define PSW_ODD_COUNT                           (371)
#define PSW_ODD_RESULTS                         (372)

Delete all history constants, PSW500..PSW509, and the history section. State that PSW372..PSW494 contains 123 WORD results, requiring 123 display components to show all values; a training screen may show the first eight or 16 only.

Add a complete function that reads PSW351, sends the fixed frame, verifies the echoed eight-byte PDU, and always updates PSW319 and PSW320:

writeValue = PSW[PSW_EXTENDED_WRITE_VALUE];
request[0] = MODBUS_SLAVE_ADDRESS;
request[1] = MODBUS_FUNCTION_WRITE_EXTENDED_REGISTER;
request[2] = 0x00;
request[3] = 0x01;
request[4] = 0x00;
request[5] = 0x00;
request[6] = (BYTE)(writeValue >> 8);
request[7] = (BYTE)writeValue;
ModbusAppendCrc(request, 8);

The response capacity and required response length are both 10 bytes. When status is OK, compare response[2]..response[7] with request[2]..request[7]; on mismatch set MODBUS_STATUS_RESPONSE_ERROR.

Document the clear function exactly as:

void ModbusClearExtendedResult(void)
{
    PSW[PSW_EXTENDED_RESULT] = 0;
}

It must not call ModbusSendReceive(). Change the odd-count example’s function constant to 0x43 and retain its existing PSW370, PSW371, PSW372..PSW494, count, range, and byte-count validation.

Replace the history panel with the 0x42 write value (PSW351), Write button, PSW350 read result, and Clear button. Change the right-hand read panel heading from 42 to 43; keep its eight visible sample result boxes and state that the full backing range has 123 WORDs. Change the RTC trigger text to 05 / 06 / 0F / 10 / 42 成功后; remove all history and PSW500-series text.

Run:

Select-String -Path 'document\TouchWin自组Modbus功能码函数.md','document\TouchWin_Modbus_附加功能测试界面方案.svg' -Pattern '通信历史|PSW500|PSW501|PSW502|MODBUS_FUNCTION_READ_HISTORY'

Expected: no matches. Then inspect the SVG at normal size to verify the 0x42 write and Clear controls do not overlap text.

git add -- document/TouchWin自组Modbus功能码函数.md document/TouchWin_Modbus_附加功能测试界面方案.svg
git commit -m "docs: document retained modbus extensions"

Final Verification