""

The manual defines representation clauses as follows:

A record_representation_clause specifies the storage representation of records and record extensions, that is, the order, position, and size of components (including discriminants, if any). ARM12

They are very useful when interfacing with hardware. Doing this in C quickly turns into a mess of bitwise or'ing and bitshifting certain values by certain widths which might be fast, but is hard to read. Here I will try to show how this can be done in a better way in Ada, using representation clauses.

For this post I'll be assuming we're communicating with the 8 bit hardware register called configuration register A on the HMC5883L with the following spec:

In C, it will usually be read somewhat as follows:

// taken from https://github.com/Harinadha/STM32_HMC5883Llib/blob/master/HMC5883L.c
uint8_t tmp = (HMC5883L_AVERAGING_8 << (HMC5883L_CRA_AVERAGE_BIT - HMC5883L_CRA_AVERAGE_LENGTH + 1))
            | (HMC5883L_RATE_15 << (HMC5883L_CRA_RATE_BIT - HMC5883L_CRA_RATE_LENGTH + 1))
            | (HMC5883L_BIAS_NORMAL << (HMC5883L_CRA_BIAS_BIT - HMC5883L_CRA_BIAS_LENGTH + 1));

This can be much simplified in Ada by using representation clauses to coerce the byte gotten from the hardware into a record and vice versa. This way it's much easier to see what's actually going on:

type ConfigA is
   record
      Pad : Integer range 0 .. 0;
      Averaged_Samples : Integer range 0 .. 3;
      Data_Output_Rate : Integer range 0 .. 6;
      Measurement_Mode : Integer range 0 .. 2;
   end record;
for ConfigA use
   record
      Pad at 0 range 7 .. 7;
      Averaged_Samples at 0 range 5 .. 6;
      Data_Output_Rate at 0 range 2 .. 4;
      Measurement_Mode at 0 range 0 .. 1;
   end record;
function Pack is new Ada.Unchecked_Conversion (Source => ConfigA,
                                               Target => Byte);
function Unpack is new Ada.Unchecked_Conversion (Source => Byte,
                                                 Target => ConfigA);

Using this code, it's also much easier to change a value and send it back over the wire. This is how it's done:

Read_Byte : constant Byte := I2C.Read (Address => ConfigA_Address);
Value : ConfigA := Unpack (Read_Byte);
Value.Averaged_Samples := 0;
I2C.Write (Address => ConfigA_Address, Value => Pack(Value));
[ARM12]ARM12: http://ada-auth.org/standards/12rm/html/RM-13-5-1.html

Comments