-2

I have a Nordic nRF52840DK board that needs to be connected to an LSM6DSL accelerometer. I am using the Zephyr RTOS and the sample code from Zephyr is shown below:

/*
 * Copyright (c) 2018 STMicroelectronics
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr.h>
#include <device.h>
#include <drivers/sensor.h>
#include <stdio.h>
#include <sys/util.h>

static inline float out_ev(struct sensor_value *val)
{
    return (val->val1 + (float)val->val2 / 1000000);
}

static int print_samples;
static int lsm6dsl_trig_cnt;

static struct sensor_value accel_x_out, accel_y_out, accel_z_out;
static struct sensor_value gyro_x_out, gyro_y_out, gyro_z_out;
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
static struct sensor_value magn_x_out, magn_y_out, magn_z_out;
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
static struct sensor_value press_out, temp_out;
#endif

#ifdef CONFIG_LSM6DSL_TRIGGER
static void lsm6dsl_trigger_handler(const struct device *dev,
                    struct sensor_trigger *trig)
{
    static struct sensor_value accel_x, accel_y, accel_z;
    static struct sensor_value gyro_x, gyro_y, gyro_z;
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
    static struct sensor_value magn_x, magn_y, magn_z;
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
    static struct sensor_value press, temp;
#endif
    lsm6dsl_trig_cnt++;

    sensor_sample_fetch_chan(dev, SENSOR_CHAN_ACCEL_XYZ);
    sensor_channel_get(dev, SENSOR_CHAN_ACCEL_X, &accel_x);
    sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Y, &accel_y);
    sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Z, &accel_z);

    /* lsm6dsl gyro */
    sensor_sample_fetch_chan(dev, SENSOR_CHAN_GYRO_XYZ);
    sensor_channel_get(dev, SENSOR_CHAN_GYRO_X, &gyro_x);
    sensor_channel_get(dev, SENSOR_CHAN_GYRO_Y, &gyro_y);
    sensor_channel_get(dev, SENSOR_CHAN_GYRO_Z, &gyro_z);

#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
    /* lsm6dsl external magn */
    sensor_sample_fetch_chan(dev, SENSOR_CHAN_MAGN_XYZ);
    sensor_channel_get(dev, SENSOR_CHAN_MAGN_X, &magn_x);
    sensor_channel_get(dev, SENSOR_CHAN_MAGN_Y, &magn_y);
    sensor_channel_get(dev, SENSOR_CHAN_MAGN_Z, &magn_z);
#endif

#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
    /* lsm6dsl external press/temp */
    sensor_sample_fetch_chan(dev, SENSOR_CHAN_PRESS);
    sensor_channel_get(dev, SENSOR_CHAN_PRESS, &press);

    sensor_sample_fetch_chan(dev, SENSOR_CHAN_AMBIENT_TEMP);
    sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
#endif

    if (print_samples) {
        print_samples = 0;

        accel_x_out = accel_x;
        accel_y_out = accel_y;
        accel_z_out = accel_z;

        gyro_x_out = gyro_x;
        gyro_y_out = gyro_y;
        gyro_z_out = gyro_z;

#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
        magn_x_out = magn_x;
        magn_y_out = magn_y;
        magn_z_out = magn_z;
#endif

#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
        press_out = press;
        temp_out = temp;
#endif
    }

}
#endif

void main(void)
{
    int cnt = 0;
    char out_str[64];
    struct sensor_value odr_attr;
    const struct device *lsm6dsl_dev = device_get_binding(DT_LABEL(DT_INST(0, st_lsm6dsl)));

    if (lsm6dsl_dev == NULL) {
        printk("Could not get LSM6DSL device\n");
        return;
    }

    /* set accel/gyro sampling frequency to 104 Hz */
    odr_attr.val1 = 104;
    odr_attr.val2 = 0;

    if (sensor_attr_set(lsm6dsl_dev, SENSOR_CHAN_ACCEL_XYZ,
                SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr) < 0) {
        printk("Cannot set sampling frequency for accelerometer.\n");
        return;
    }

    if (sensor_attr_set(lsm6dsl_dev, SENSOR_CHAN_GYRO_XYZ,
                SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr) < 0) {
        printk("Cannot set sampling frequency for gyro.\n");
        return;
    }

#ifdef CONFIG_LSM6DSL_TRIGGER
    struct sensor_trigger trig;

    trig.type = SENSOR_TRIG_DATA_READY;
    trig.chan = SENSOR_CHAN_ACCEL_XYZ;

    if (sensor_trigger_set(lsm6dsl_dev, &trig, lsm6dsl_trigger_handler) != 0) {
        printk("Could not set sensor type and channel\n");
        return;
    }
#endif

    if (sensor_sample_fetch(lsm6dsl_dev) < 0) {
        printk("Sensor sample update error\n");
        return;
    }

    while (1) {
        /* Erase previous */
        printk("\0033\014");
        printf("LSM6DSL sensor samples:\n\n");

        /* lsm6dsl accel */
        sprintf(out_str, "accel x:%f ms/2 y:%f ms/2 z:%f ms/2",
                              out_ev(&accel_x_out),
                              out_ev(&accel_y_out),
                              out_ev(&accel_z_out));
        printk("%s\n", out_str);

        /* lsm6dsl gyro */
        sprintf(out_str, "gyro x:%f dps y:%f dps z:%f dps",
                               out_ev(&gyro_x_out),
                               out_ev(&gyro_y_out),
                               out_ev(&gyro_z_out));
        printk("%s\n", out_str);

#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
        /* lsm6dsl external magn */
        sprintf(out_str, "magn x:%f gauss y:%f gauss z:%f gauss",
                               out_ev(&magn_x_out),
                               out_ev(&magn_y_out),
                               out_ev(&magn_z_out));
        printk("%s\n", out_str);
#endif

#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
        /* lsm6dsl external press/temp */
        sprintf(out_str, "press: %f kPa - temp: %f deg",
            out_ev(&press_out), out_ev(&temp_out));
        printk("%s\n", out_str);
#endif

        printk("loop:%d trig_cnt:%d\n\n", ++cnt, lsm6dsl_trig_cnt);

        print_samples = 1;
        k_sleep(K_MSEC(2000));
    }
}

From my previous experience working with Arduino, you need to define the pins before the main function, but there isn't any definition on pins in this code example. So my question is: How does the board know which pins to use when connected to the sensor?

Thanks in advance!

  • 2
    The board does not know anything. You, as the developer, should know what is connected to what and write the firmware accordingly. Arduino is a toy with a confusing concept which does not reflect any practices accepted in the "real" embedded development. – Eugene Sh. Jul 26 '21 at 16:37
  • Read the friendly manual for the specific MCU, the relevant section such as GPIO, SPI, I2C or whatever you need. Then use a pre-made register map if available. – Lundin Jul 26 '21 at 16:38
  • you look at a schematic and the data sheet for the part. Looks like that part talks over i2c or spi or both. I'd look at the definition of `device_get_binding` and `sensor_channel_get` to see what it's doing. – yhyrcanus Jul 26 '21 at 17:04
  • 1
    Maybe the pins are fixed if a specific interface (I2C) is used. Or there might be some configuration in a (sample) header file. Hardware abstraction libraries often provide functions to separate hardware specific code from your application. Symbols like `SENSOR_CHAN_ACCEL_XYZ` may imply a relation to specific pins. The included header files might be starting points for finding out the details. – Bodo Jul 26 '21 at 17:10
  • Surely there is a lot of magic happening in that `device_get_binding()` function. – kkrambo Jul 26 '21 at 17:59
  • But thus is not Arduino. The method of initialising and accessing I/O is specific to the platform and nothing to do with the programming language. Zephyr is much more complex than Arduino, and in this case that initialisation is performed in the runtime initialisation generated by the [configuration](https://docs.zephyrproject.org/latest/reference/kconfig/index.html#configuration-options) or Device Tree as in the one answer - seems to be in a state of flux in that regard. The generated code will be executed before `main()` is invoked, so that the driver API is ready to go. – Clifford Jul 27 '21 at 13:25

1 Answers1

3

In embedded C (bare metal and some RTOS), the developer is expected to know what is connected where and which registers to use to access the connected hardware by driving the right pins.

In the specific case of Zephyr (as well as Das U-Boot and Linux on some architectures), the system configuration (among other things pins, but not only) is described by a device-tree. It is a "a data structure for describing hardware", which allows the firmware code to become independent of the actual hardware.

The device-tree will describe many things, the most important being:

  • the device's memory map
  • the list of CPUs and their properties (frequency, caches, ...)
  • the peripherals, as well as their configuration

Zephyr does have a very good introduction to device-tree.


In the code you posted, Zephyr does retrieve the device information from the device-tree when executing the following line:

const struct device *lsm6dsl_dev = device_get_binding(DT_LABEL(DT_INST(0, st_lsm6dsl)));
Simon Doppler
  • 1,918
  • 8
  • 26