cpointersembeddedstm32modbus

Passing a pointer of a locally created array in C to a function different behave


I'm facing a super strange issue regarding passing a pointer of an array which is created locally inside a function to another function. This second function have to edit the same array.

So basically I've implemented on an STM microcontroller (uC) the MODBUS protocol. The uC has only to respond to reading holding resiter and writing register functions (0x3 and 0x10 function).

Clearly, it have to send back a response to the master.

When the function requested is reading the holding register, all works fine. The function is processed as (frame_buffer is the RX baffer of the serial port)

uint8_t function_code = frame_buffer[1];
uint16_t start_address = (frame_buffer[2] << 8) | frame_buffer[3];
uint16_t quantity = (frame_buffer[4] << 8) | frame_buffer[5];
HandleReadHoldingRegisters(start_address, quantity);

void HandleReadHoldingRegisters(uint16_t start_addr, uint16_t quantity)
{
    // Validate address and quantity
    if ((start_addr + quantity) > MODBUS_HOLDING_REGISTERS_SIZE || quantity == 0 || quantity > 125) {
        SendModbusException(0x03, 0x02); // Illegal Data Address
        return;
    }

    uint8_t response_buffer[256];
    uint8_t byte_count = quantity * 2;
    
    response_buffer[0] = MODBUS_SLAVE_ADDRESS;
    response_buffer[1] = 0x03;
    response_buffer[2] = byte_count;

    // Copy register data into the response buffer
    for (int i = 0; i < quantity; i++) {
        response_buffer[3 + (i * 2)] = (modbus_holding_registers[start_addr + i] >> 8) & 0xFF; // High byte
        response_buffer[4 + (i * 2)] = modbus_holding_registers[start_addr + i] & 0xFF;      // Low byte
    }

    uint16_t response_length = 3 + byte_count;
    SendModbusResponse(response_buffer, response_length);
}

void SendModbusResponse(uint8_t* data, uint16_t length)
{
    uint16_t crc = crc16_test((char*)data, length);
    data[length] = crc & 0xFF;         // CRC Low
    data[length + 1] = (crc >> 8) & 0xFF; // CRC High
    
    // Assumes RS485 direction control pin is handled, if necessary, before transmitting
    HAL_UART_Transmit_IT(&huart4, data, length + 2);
}

everything works correctly.

When it has to respond to a write request the code is more or less similar

HandleWriteMultipleRegisters(start_address, quantity, &frame_buffer[7]);
void HandleWriteMultipleRegisters(uint16_t start_addr, uint16_t quantity, uint8_t* data)
{
    // Validate address and quantity
    if ((start_addr + quantity) > MODBUS_HOLDING_REGISTERS_SIZE || quantity == 0 || quantity > 123) {
        SendModbusException(0x10, 0x02); // Illegal Data Address
        return;
    }

    // Write the data from the request into our holding registers
    for (int i = 0; i < quantity; i++) {
        uint16_t reg_value = (data[i * 2] << 8) | data[(i * 2) + 1];
        modbus_holding_registers[start_addr + i] = reg_value;
    }

    // Send the acknowledgment response
    uint8_t response_buffer[8];
    response_buffer[0] = MODBUS_SLAVE_ADDRESS;
    response_buffer[1] = 0x10;
    response_buffer[2] = (start_addr >> 8) & 0xFF;
    response_buffer[3] = start_addr & 0xFF;
    response_buffer[4] = (quantity >> 8) & 0xFF;
    response_buffer[5] = quantity & 0xFF;

    SendModbusResponse(response_buffer, 6);
}

but, in this case, the data send back to the master ar wrong. Only response_buffer[0] and response_buffer[1] are correct, all the other values are wrong in response_buffer

The strange thing is that if I change the SendModbusResponse(response_buffer, 6); with

uint16_t crc = crc16_test((char*)response_buffer, 6);
response_buffer[6] = crc & 0xFF;         // CRC Low
response_buffer[7] = (crc >> 8) & 0xFF;  // CRC High

// Transmit complete response with CRC
HAL_UART_Transmit_IT(&huart4, response_buffer, 8);

Everything works correctly.

I think there's an issue on how the array pointer is sent and processed in SendModbusResponse or HandleWriteMultipleRegisters but I can't understand what's going on.

I have also removed all the optimization of the compiler but still, same issue. In debug mode response_buffer seems to be generated corrected before being sent to the serial.


Solution

  • HAL_UART_Transmit_IT is a non-blocking function according to its sources. Therefore the function SendModbusResponse (and it's callers) will return before the whole buffer is transmitted. But when it return, all locally allocated arrays are ceasing to exist (that is the memory is being reused for something else) and the asynchronous transmission is picking up the already corrupted data.