0

I'm developing a modbus RTU slave device with a 32-bit MCU. The operation of the device depends on many parameters. So, the device should support modbus function codes for parameter read and write. Which specific function codes to support is up to my design.

A modbus master device reads or writes the slave device's registers at certain addresses. So, I need to map parameters to registers with addresses. I've come up with this approach: Use one or more holding registers to represent a parameter. (A holding register is a 16-bit variable with read and write access.) Use the following union to make the connection between parameters and registers: struct for parameters, and uint16_t array for registers.

typedef union{
    uint16_t holding_register[PARAMETER_STRUCT_SIZE];
    struct the_parameters{
        float parameter1,parameter2,parameter3;
        uint8_t parameter4,parameter5;
    };
}modbus_union;

When a modbus master device wants to access a register at an address, it can access the element in the array at the index. The meaning of a certain register can be maintained in documentation.

Equivalently, a uint16_t pointer can be used to point at struct the_parameters. And in modbus protocol, it can just be dealt with as an array.

The problem is, my code should be MISRAC-2004 compliant to pass a safety test. The related MISRAC rules are:

Rule 18.4 (required): Unions shall not be used.
Rule 17.1 (required): Pointer arithmetic shall only be applied to pointers that address an array or array element.

How to implement modbus RTU in MISRAC-2004-complaint C?

Jackoo
  • 117
  • 1
  • 1
  • 12
  • My current solution is to use switch case to jump to different functions to read/write certain number of registers at certain address (e.g. read registers 0~5 is supported by a certain function, but read 0~4 is not supported). Invalid float value is not the modbus protocol should be concerned about. – Jackoo Jun 01 '21 at 05:51
  • There is no pointer arithmetic in the code you posted, so we can't answer that part. – Lundin Jun 01 '21 at 06:24
  • Apart from that, the main issues with that union aren't related to MISRA-C. You need to consider alignment/padding as well as endianess. Why using a union with 16 bit chunks seems rather cumbersome to work with for a network structure. As for the `union` usage rule, you should update to MISRA-C:2012. The 2004 version is quite dated at this point. The union usage rule has been relaxed and is advisory in 2012. The MISRA-C:2012 rule about unions raise the following concerns too: padding, alignment, endianess, bit-order. – Lundin Jun 01 '21 at 06:30
  • 1
    @KamilCuk - you shouldn't be posting links to (illegally) hosted copyright material... :( – Andrew Jun 03 '21 at 20:33
  • First things first... do you understand why your existing code is flagged as non-compliant? Do you understand the Rules that are flagged, and the problems they seek to address? – Andrew Jun 03 '21 at 20:35

1 Answers1

1

You should do the serialization of your parameters to the bytes that are send manually using bit shifts and masks.

This could be done by creating functions that convert between your types and arrays of bytes.

In your current example, the result is dependent on the endianness of your microcontroller, as well as how your compiler chooses to pack the values in your struct.

You might especially have a problem in this case as many microcontrollers are little-endian, however the bytes that make up a modbus word are big endian (though some implementations still do/expect otherwise), and if your parameter spans multiple words, then you should properly document which mix of endianness you have chosen.

For instance, the minimalmodbus python package allows you to choose between

Using unions for packing data is one of the exceptions as described in later versions of the misra standard Is it legal write to a byte array in a union and read from an int to convert values in MISRA C? However, you should properly document why you deviate from the standard and ideally put in place some static asserts that check endianness and packing among other things at compile time.

Lanting
  • 3,060
  • 12
  • 28