Hi Peter,

Thanks for the review, and sorry for the slow reply, just getting back from
holidays myself.

On Thu, 26 Aug 2021 at 17:39, Peter Maydell <peter.mayd...@linaro.org>
wrote:

>
> So if I'm reading the datasheet correctly, the LM303DLHC is
> really two completely distinct i2c devices in a single
> package with different slave addresses; this QEMU device
> implements only the magnetometer i2c device, and if we wanted
> to add the accelerometer device we'd implement that as a
> second separate QEMU i2c device ?
>

This is correct. There are two distinct dies in the chip with separate I2C
addresses, etc.,
and this should probably be modelled separately. I chose the magnetometer
since it's
a far simpler device to model than the accelrometer, but still solves the
need for a
more complex I2C sensor that can be used in testing with the I2C bus.

> +    if (value > 2047 || value < -2048) {
> > +        error_setg(errp, "value %d lsb is out of range", value);
>
> Why "lsb" ?
>
>
In my head, using LSB seemed more precise since I know exactly what value
will
be set to the registers, and exactly what I will get back when reading
versus passing
in a float that's needs to be conveted as a 'best-fit' scenario in 0.125°C
steps?

> +     * The auto-increment logic is only taken into account in this driver
> > +     * for the LSM303DLHC_MAG_REG_OUT_X_H and
> LSM303DLHC_MAG_REG_TEMP_OUT_H
> > +     * registers, which are the two common uses cases for it. Accessing
> either
> > +     * of these registers will also populate the rest of the related
> dataset.
>
> How hard would it be to implement the behaviour correctly for all cases?
> I find it's usually better to model something correctly from the start:
> usually the person writing the code knows the h/w behaviour better than
> anybody else coming along later trying to figure out why some other
> guest code doesn't work on the model...
>

I can update this to also take auto-increment into account when you don't
start at
the first record (X-axis), yes, even if that is an uncommon scenario.

> +    s->mr = 0x1;            /* Operating Mode = Single conversion. */
> > +    s->x = 0;
> > +    s->z = 0;
> > +    s->y = 0;
> > +    s->sr = 0x1;            /* DRDY = 1. */
>
> Do you understand how the DRDY and LOCK bits work ? The datasheet
> is unclear. Also, what's the difference between "single-conversion"
> and "continuous-conversion" modes ?
>

DRDY indicates that a set of XYZ samples is available in the data registers,
presumably post reset or when switching modes, and how LOCK works is that
once I read the first byte of the X register, the current sample will be
locked
until it has been fully read to prevent the values from being changed
mid-read
with a high sample rate. LOCK simply indicates this status of the registers
being
read-only until we get to the end of ou 6 byte sample.

Some details are unclear, however, such as what happens if I don't read
all six bytes, and go back to request the first X byte again? I'll need to
hook a
real sensor up to see what happens in those cases.

Single-conversion mode is documented in earlier variations of this chip
family, but
it is used for device calibration. From the LSM303DLH (not 'C' at the end):

"By placing the mode register into single-conversion mode (0x01), two data
acquisition  cycles are made on each magnetic vector. The first acquisition
is a set pulse followed shortly by measurement data of the external field.
The second acquisition has the offset strap excited in the positive bias
mode
to create about  a 0.6 gauss self-test field plus the external field. The
first
acquisition values are subtracted  from the second acquisition, and the net
measurement is placed into the data output  registers."

Given the lack of details in the LSM303DLHC datasheet, however, only
continuous mode should likely be modeled, and the way QEMU works
to model sensors makes this a moot point anyway since the output
registers are only updated when an end-user changes the values manually.
If specific values are expected by the data consumer, such as calibration
data
from single-conversion mode, those values can be set by the user regardless
of the current operating mode.


> > +    s->ira = 0x48;
> > +    s->irb = 0x34;
> > +    s->irc = 0x33;
> > +    s->temperature = 0;     /* Default to 0 degrees C (0/8 lsb = 0 C).
> */
> > +}
> > +
> > +/**
> > + * @brief Callback handler when DeviceState 'reset' is set to true.
> > + */
> > +static void lsm303dlhc_mag_reset(DeviceState *dev)
> > +{
> > +    I2CSlave *i2c = I2C_SLAVE(dev);
> > +    LSM303DLHC_Mag_State *s = LSM303DLHC_MAG(i2c);
> > +
> > +       /* Set the device into is default reset state. */
> > +       lsm303dlhc_mag_default_cfg(&s->parent_obj);
>
> Misindentation.
>
> Also, don't use the parent_obj field;
> always use the QOM cast macro when you need the pointer
> to something as a different type. In this case you already
> have the I2CSlave*, in 'i2c'. But better would be to make
> lsm303dlhc_mag_default_cfg() take a LSM303DLHC_Mag_State*
> directly rather than taking an I2CSlave* and casting it
> internally.
>

Do you have an example, just to be sure I follow? I've changed the code
as follows:

static void lsm303dlhc_mag_reset(DeviceState *dev)
{
    I2CSlave *i2c = I2C_SLAVE(dev);
    LSM303DLHCMagState *s = LSM303DLHC_MAG(i2c);

    /* Set the device into its default reset state. */
    lsm303dlhc_mag_default_cfg(s);
}

static void lsm303dlhc_mag_default_cfg(LSM303DLHCMagState *s)

Is this sufficient?

> +static void lsm303dlhc_mag_initfn(Object *obj)
> > +{
> > +    object_property_add(obj, LSM303DLHC_MSG_PROP_MAGX, "int",
> > +                lsm303dlhc_mag_get_xyz,
> > +                lsm303dlhc_mag_set_xyz, NULL, NULL);
> > +
> > +    object_property_add(obj, LSM303DLHC_MSG_PROP_MAGY, "int",
> > +                lsm303dlhc_mag_get_xyz,
> > +                lsm303dlhc_mag_set_xyz, NULL, NULL);
> > +
> > +    object_property_add(obj, LSM303DLHC_MSG_PROP_MAGZ, "int",
> > +                lsm303dlhc_mag_get_xyz,
> > +                lsm303dlhc_mag_set_xyz, NULL, NULL);
>
> What units are these in? It looks like your implementation just
> uses the property values as the raw -2048..+2048 value that the
> X/Y/Z registers read as. Would it be better for the properties to
> set the value in Gauss, and then the model to honour the
> gain settings in CRB_REG_M.GN{0,1,2} ?  That way guest code that
> adjusts the gain will get the results it is expecting.
>

I guess I found raw units that the sensor uses more intuitive personally,
with no room for unexpected translations and not having to deal with floats,
but if you feel gauss or degrees C is better, I can change these.

In that case, should I accept floating point inputs, however, or stick to
integers?
When dealing with magnetometers the values can be very small, so it's not
the
same as a temp sensor where we can provide 2300 for 23.00C.


> > +
> > +    object_property_add(obj, LSM303DLHC_MSG_PROP_TEMP, "int",
> > +                lsm303dlhc_mag_get_temperature,
> > +                lsm303dlhc_mag_set_temperature, NULL, NULL);
>
> What units is this in?
>

LSB where 1 LSB = 0.125 C, documented elsewhere, but as per the above
I can change this to degrees if you can clarify if this should be in float
or something
integere based with a specific scale factor.

Thanks for the feedback, and sorry again for the slow reply.

-- Kevin

Reply via email to