Difference between revisions of "Zephyr device driver model"

From Wiki at Neela Nurseries
Jump to: navigation, search
m (adding link to section of Zephyr device drivers page, specifically about max dev name length supported by device_get_binding().)
m (Add subsection to share build time diagnostic additions to IIS2DH driver header file iis2dh.h)
 
(35 intermediate revisions by the same user not shown)
Line 1: Line 1:
  
<b>Keywords:</b>  <code>zephyr device drivers</code> <code>Zephyr device driver model</code> <code>Zephyr device drivers getting started</code> <code>LIS2DH driver study</code> <code>[[#jared_wolff_device_driver_post_at_memfault|Jared Wolff post]]</code> <code>[[#jared_wolff_device_driver_post_at_memfault|Jared Wolff device driver post]]</code> <code>[[#air_quality_wing_aqw_at_github|AQW repo]]</code> <code>Zephyr sensor_driver_api</code>
+
<b>Keywords:</b>  zephyr device drivers :: Zephyr device driver model :: Zephyr device drivers getting started :: LIS2DH driver study ::    [[#jared_wolff_device_driver_post_at_memfault|Jared Wolff post]] :: [[#jared_wolff_device_driver_post_at_memfault|Jared Wolff device dr    iver post]] :: [[#air_quality_wing_aqw_at_github|AQW repo]] :: Zephyr sensor_driver_api :: [[#nn_anchor_zephyr_device_name_max_length|de    vice_get_binding and Z_DEVICE_MAX_NAME_LEN]]
  
<b>NOTEABLE:</b> &nbsp;[[#zephyr_sensor_underscore_driver_underscore_api|page section 4 - Zephyr sensor_driver_api]]
+
<b>NOTEABLE:</b> &nbsp;[[#zephyr_sensor_underscore_driver_underscore_api|page section - Zephyr sensor_driver_api]]
  
  
<b>- OVERVIEW -</b>
+
== [[#top|^]] OVERVIEW ==
 +
 
 +
Ok roughly a year into working with Zephyr RTOS we have a better idea of Zephyr's device driver model.  A good point of reference to understand this model is [https://docs.zephyrproject.org/3.1.0/kernel/drivers/index.html Zephyr's device driver model] documentation page.
 +
 
 +
Some important features of this device driver model include Zephyr C enumerations to support notions of sensor "channels" and sensor attributes.  One such sensor attribute detailed at [https://docs.zephyrproject.org/3.1.0/hardware/peripherals/sensor.html#c.sensor_attribute.SENSOR_ATTR_CONFIGURATION Zephyr doc page on symbol SENSOR_ATTR_CONFIGURATION].  Along with Zephyr RTOS sensor attributes, channels and triggers, there is a sextet of generalized sensor or device API routines which Zephyr's driver model employs.  These are:
 +
 
 +
 
 +
<ul>
 +
<li> [https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/drivers/sensor.h#L412 sensor_attr_set()]
 +
<li> [https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/drivers/sensor.h#L444 sensor_attr_get()]
 +
<li> [https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/drivers/sensor.h#L481 sensor_trigger_set()]
 +
<li> [https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/drivers/sensor.h#L511 sensor_sample_fetch()]
 +
<li> [https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/drivers/sensor.h#L540 sensor_sample_fetch_chan()]
 +
<li> [https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/drivers/sensor.h#L573 sensor_sample_get()]
 +
</ul>
 +
 
 +
 
 +
 
 +
Original overview from 2021 August, need to review links to external references and clean up:
 +
 
 +
 
 +
<ul>
 +
<i>
 
Challenged by Zephyr's way of writing device drivers, capturing some notes here on a study of a Zephyr example project which talks to LIS2DH accelerometer on a Sparkfun Thing Plus board.  As of 2021-08-27 Friday, couple of important features Ted notes are that [https://github.com/zephyrproject-rtos/zephyr/tree/main/samples/sensor Zephyr Project's sample drivers for sensors] are like or are actually small <i>Zephyr applications (track down definition of this)</i>, which need to be studied along side [https://github.com/zephyrproject-rtos/zephyr/tree/main/drivers/sensor corresponding Zephyr driver sources].  One design pattern Ted observes from [https://github.com/circuitdojo/air-quality-wing-zephyr-drivers an out-of-tree driver for Sensirion SHTC3 sensor] is that all driver functions are declared private, making them directly callable only within the given source file.  These routines appear to be accessed via a structure whose member elements are pointers to these static functions.  Note lines 306..309 in [https://github.com/circuitdojo/air-quality-wing-zephyr-drivers/blob/main/drivers/shtc3/shtc3.c AQW driver source shtc3.c].
 
Challenged by Zephyr's way of writing device drivers, capturing some notes here on a study of a Zephyr example project which talks to LIS2DH accelerometer on a Sparkfun Thing Plus board.  As of 2021-08-27 Friday, couple of important features Ted notes are that [https://github.com/zephyrproject-rtos/zephyr/tree/main/samples/sensor Zephyr Project's sample drivers for sensors] are like or are actually small <i>Zephyr applications (track down definition of this)</i>, which need to be studied along side [https://github.com/zephyrproject-rtos/zephyr/tree/main/drivers/sensor corresponding Zephyr driver sources].  One design pattern Ted observes from [https://github.com/circuitdojo/air-quality-wing-zephyr-drivers an out-of-tree driver for Sensirion SHTC3 sensor] is that all driver functions are declared private, making them directly callable only within the given source file.  These routines appear to be accessed via a structure whose member elements are pointers to these static functions.  Note lines 306..309 in [https://github.com/circuitdojo/air-quality-wing-zephyr-drivers/blob/main/drivers/shtc3/shtc3.c AQW driver source shtc3.c].
  
 
This coding pattern of declaring all driver functions static may relate to a larger design practice in Zephyr RTOS, where all major memory uses are declared and known at compile time.
 
This coding pattern of declaring all driver functions static may relate to a larger design practice in Zephyr RTOS, where all major memory uses are declared and known at compile time.
 +
</i>
 +
</ul>
  
 +
 +
 +
<!-- comentario -->
  
 
== [[#top|^]] References To Review and Add ==
 
== [[#top|^]] References To Review and Add ==
Line 40: Line 67:
 
<!-- style="border-spacing: 2px; border: 1px solid darkgray; From https://en.wikipedia.org/wiki/Help:Table#Setting_borders -->
 
<!-- style="border-spacing: 2px; border: 1px solid darkgray; From https://en.wikipedia.org/wiki/Help:Table#Setting_borders -->
  
<!-- comment -->
 
  
 +
<!-- **** **** ****  **** **** ****  **** **** ****  **** **** ****  **** **** ****  **** **** **** -->
 +
<!-- 2021-10-12 Color annotations begin here -->
 +
<!-- **** **** ****  **** **** ****  **** **** ****  **** **** ****  **** **** ****  **** **** **** -->
 +
 +
{|- style="cellpadding=30px;"
 +
|-
 +
| style="background:#f0f0f0; valign=top; padding:10px;" | <font color="#ffffff"></font> <!-- vertical spacing row -->
 +
| style="background:#f0f0f0; width:100%; padding:10px;" |
 +
|-
 +
| style="background:#e6ffcc; valign=top; padding:10px;" | <font color="#ffffff"></font>
 +
| style="background:#f0f0f0; width:100%; padding:10px;" |
 
== [[#top|^]] Early Research and SDK Used ==
 
== [[#top|^]] Early Research and SDK Used ==
 
<span id="jared_wolff_device_driver_post_at_memfault"></span>
 
<span id="jared_wolff_device_driver_post_at_memfault"></span>
Line 62: Line 99:
 
     cmake: .
 
     cmake: .
 
     kconfig: Kconfig.nrf
 
     kconfig: Kconfig.nrf
 +
<!-- comment -->
 +
 +
|-
 +
| style="background:#f0f0f0; valign=top; padding:10px;" | <font color="#ffffff"></font> <!-- vertical spacing row -->
 +
| style="background:#f0f0f0; width:100%; padding:10px;" |
 +
|-
 +
| style="background:#ffffcc; valign=top; padding:10px;" | <font color="#ffffff"></font>
 +
| style="background:#f0f0f0; width:100%; padding:10px;" |
 +
 +
== [[#top|^]] Middle Research - Zephyr In-Tree Drivers ==
 +
There appears to be a Zephyr in-tree driver for STMicro's IIS2DH accelerometer.  Not much on-line regarding how to make use of in-tree drivers, but a search of Zephyr's source tree shows these driver files:
 +
 +
<pre>
 +
~/embedded/z3-on-var/zephyr$ find . -name '*iis2dh*.*'
 +
./drivers/sensor/iis2dh/iis2dh.c
 +
./drivers/sensor/iis2dh/iis2dh_spi.c
 +
./drivers/sensor/iis2dh/iis2dh.h
 +
./drivers/sensor/iis2dh/iis2dh_i2c.c
 +
./drivers/sensor/iis2dh/iis2dh_trigger.c
 +
./dts/bindings/sensor/st,iis2dh-spi.yaml
 +
./dts/bindings/sensor/st,iis2dh-i2c.yaml
 +
 +
~/embedded/z3-on-var/zephyr$
 +
</pre>
 +
 +
A register header file for this sensor however is located in another place:
 +
 +
<pre>
 +
~/embedded/z3-on-var$ find . -name iis2dh_reg.h
 +
./modules/hal/st/sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.h
 +
 +
~/embedded/z3-on-var/zephyr$
 +
</pre>
 +
 +
Only mid-level driver files appear to call IIS2DH initialization routines which are bus specific:
 +
<pre>
 +
~/embedded/z3-on-var/zephyr$ grep -nr iis2dh_i2c_init ./*
 +
./drivers/sensor/iis2dh/iis2dh.c:245:  iis2dh_i2c_init(dev);
 +
./drivers/sensor/iis2dh/iis2dh.h:85:int iis2dh_i2c_init(const struct device *dev);
 +
./drivers/sensor/iis2dh/iis2dh_i2c.c:44:int iis2dh_i2c_init(const struct device *dev)
 +
 +
~/embedded/z3-on-var/zephyr$
 +
</pre>
 +
 +
Source file `iis2dh.c` appears to be a middle level driver, so which sample applications call routines in this file?  Routines <code>iis2dh_set_odr()</code> and <code>iis2dh_channel_get_acc()</code> are not called by any samples in Zephyr source tree . . . and they're not called anywhere within Nordic Semi nrf SDK 1.6.1:
 +
 +
<pre>
 +
$ grep -nr iis2dh_set_odr ./*
 +
./drivers/sensor/iis2dh/iis2dh.c:88: * iis2dh_set_odr - set new sampling frequency
 +
./drivers/sensor/iis2dh/iis2dh.c:92:static int iis2dh_set_odr(const struct device *dev, uint16_t odr)
 +
./drivers/sensor/iis2dh/iis2dh.c:175:          return iis2dh_set_odr(dev, val->val1);
 +
 +
~/embedded/z3-on-var/zephyr$ grep -nr iis2dh_channel_get_acc ./*
 +
./drivers/sensor/iis2dh/iis2dh.c:116:static inline void iis2dh_channel_get_acc(const struct device *dev,
 +
./drivers/sensor/iis2dh/iis2dh.c:154:          iis2dh_channel_get_acc(dev, chan, val);
 +
 +
~/embedded/z3-on-var/nrf$ grep -nr iis2dh_channel_get_acc ./*
 +
 +
~/embedded/z3-on-var/nrf$ grep -nr iis2dh_set_odr ./*
 +
 +
~/embedded/z3-on-var/zephyr$
 +
</pre>
 +
 +
We really seem to be shielded from most of the details of in-tree drivers.  Looking at `~/embedded/z3-on-var/zephyr/samples/sensor/lis2dh/src/main.c` it looks like just a few pound includes at the top of the file are sufficient to bring in the needed driver.  Likely brings in all in-tree drivers:
 +
 +
<i>code excerpt from lis2dh main.c:</i>
 +
<pre>
 +
  1 /*
 +
  2  * Copyright (c) 2019 Nordic Semiconductor ASA
 +
  3  *
 +
  4  * SPDX-License-Identifier: Apache-2.0
 +
  5  */
 +
  6
 +
  7 #include <stdio.h>
 +
  8 #include <zephyr.h>
 +
  9 #include <device.h>
 +
10 #include <drivers/sensor.h>
 +
11
 +
12 static void fetch_and_display(const struct device *sensor)
 +
13 {
 +
14        static unsigned int count;
 +
15        struct sensor_value accel[3];
 +
16        const char *overrun = "";
 +
17        int rc = sensor_sample_fetch(sensor);
 +
18
 +
19        ++count;
 +
20        if (rc == -EBADMSG) {
 +
21                /* Sample overrun.  Ignore in polled mode. */
 +
22                if (IS_ENABLED(CONFIG_LIS2DH_TRIGGER)) {
 +
23                        overrun = "[OVERRUN] ";
 +
24                }
 +
</pre>
 +
 +
|}
 +
<!-- END OF COLOR ANNOTATIONS -->
 +
 +
<!-- comment -->
 +
 +
== * ==
 +
 +
&nbsp;
 +
 +
<!-- empty section to create horizontal white space, organizing document sections for possible later break -->
  
 
<!-- comment -->
 
<!-- comment -->
Line 578: Line 718:
 
<!-- comment -->
 
<!-- comment -->
  
== [[#top|^]] Chain Of Search edit point ==
+
=== [[#top|^]] Chain Of Search edit point ===
  
 
<pre>
 
<pre>
Line 736: Line 876:
 
491
 
491
 
</pre>
 
</pre>
 +
 +
<!-- comment -->
 +
 +
== * ==
 +
 +
&nbsp;
 +
 +
<!-- empty section to create horizontal white space, organizing document sections for possible later break -->
  
 
<!-- comment -->
 
<!-- comment -->
  
 
<span id="air_quality_wing_aqw_at_github"></span>
 
<span id="air_quality_wing_aqw_at_github"></span>
 +
 
== [[#top|^]] Jared Wolff Example Zephyr Driver repo ==
 
== [[#top|^]] Jared Wolff Example Zephyr Driver repo ==
  
Line 822: Line 971:
 
An excerpt of Jared's most telling reply to date is here:
 
An excerpt of Jared's most telling reply to date is here:
  
<pre>
+
<i>
 
     jaredwolff
 
     jaredwolff
 
     5 days ago
 
     5 days ago
 
     Edited
 
     Edited
  
Here’s the file structure from the Air Quality Wing drivers repository. The most critical files are module.yml and top level CMakeLists.txt and Kconfig.
+
Here’s the file structure from the [https://github.com/circuitdojo/air-quality-wing-zephyr-drivers Air Quality Wing drivers repository]. The most critical files are module.yml and top level CMakeLists.txt and Kconfig. . . .</i>
  
 +
<pre>
 
|____dts
 
|____dts
 
| |____bindings
 
| |____bindings
Line 941: Line 1,091:
 
</pre>
 
</pre>
 
</i>
 
</i>
 +
 +
<!-- comment -->
 +
 +
== * ==
 +
 +
&nbsp;
 +
 +
<!-- empty section to create horizontal white space, organizing document sections for possible later break -->
 +
 +
<!-- comment -->
 +
 +
== [[#top|^]] Search For Board Specific Zephyr Driver ==
 +
 +
2022-08-08 Monday - search for Zephyr WS2812 GPIO control based driver.  Search excerpt:
 +
 +
<pre>
 +
ted@localhost:~/projects-sandbox/workspace-for-rpi/zephyr$ grep -nr WS2812_STRIP_GPIO ./*
 +
./drivers/led_strip/Kconfig.ws2812:29:config WS2812_STRIP_GPIO
 +
./drivers/led_strip/CMakeLists.txt:7:zephyr_library_sources_ifdef(CONFIG_WS2812_STRIP_GPIO  ws2812_gpio.c)
 +
./samples/drivers/led_ws2812/boards/bbc_microbit.conf:2:CONFIG_WS2812_STRIP_GPIO=y
 +
./samples/drivers/led_ws2812/boards/nrf51dk_nrf51422.conf:2:CONFIG_WS2812_STRIP_GPIO=y
 +
./samples/drivers/led_ws2812/README.rst:48:  which case it will be :kconfig:option:`CONFIG_WS2812_STRIP_GPIO`.
 +
 +
ted@localhost:~/projects-sandbox/workspace-for-rpi/zephyr$ find . -name ws2812_gpio.c
 +
./drivers/led_stri</pre>p/ws2812_gpio.c
 +
 +
~/projects-sandbox/workspace-for-rpi/zephyr/drivers/led_strip$ ls -l
 +
total 60
 +
-rw-rw-r-- 1 ted ted  2397 Aug  2 11:03 apa102.c
 +
-rw-rw-r-- 1 ted ted  398 Aug  2 11:03 CMakeLists.txt
 +
-rw-rw-r-- 1 ted ted  954 Aug  2 11:03 Kconfig
 +
-rw-rw-r-- 1 ted ted  314 Aug  2 11:03 Kconfig.apa102
 +
-rw-rw-r-- 1 ted ted  556 Aug  2 11:03 Kconfig.lpd880x
 +
-rw-rw-r-- 1 ted ted  239 Aug  2 11:03 Kconfig.tlc5971
 +
-rw-rw-r-- 1 ted ted  1185 Aug  2 11:03 Kconfig.ws2812
 +
-rw-rw-r-- 1 ted ted  3508 Aug  2 11:03 lpd880x.c
 +
-rw-rw-r-- 1 ted ted 12182 Aug  2 11:03 tlc5971.c
 +
-rw-rw-r-- 1 ted ted  6924 Aug  2 11:03 ws2812_gpio.c
 +
-rw-rw-r-- 1 ted ted  6254 Aug  2 11:03 ws2812_spi.c
 +
</pre>
 +
 +
Also worth noting is the Zephyr RTOS header file `~/projects-sandbox/workspace-for-rpi/zephyr$ vi ./include/zephyr/drivers/led_strip.h`, which reminds us of the decoupling of Zephyr RTOS from out-of-tree driver details, and decoupling from specific driver implementations.
 +
 +
In file `ws2812_gpio.c` here are telling comments and calls to nrf_ API functions, functions which may be peculiar to Nordic's nRF51 family of parts.  There are also the mentioned in-line assembly instructions which are based on this source file's targeting of an ARM Cortex-M0 running at 16MHz.  We may be able to adapt this file and use it on the RPI2040 with appropriate configuration of this other processor's higher speed clock, plus adjustments to the in-line assembly.  ( That in-line assembly code is just a series of NOP instructions, three different counts of NOP series for specific WS2812 timing requirements. )
 +
 +
<!-- comment -->
 +
 +
== [[#top|^]] IIS2DH driver in Zephyr 3.2.0 ==
 +
 +
Need to find example of code which calls this following, seeming in tree Zephyr driver for STMicro IIS2DH accelerometer.  This code in source file `zephyr/drivers/sensor/iis2dh/iis2dh_i2c.c`:
 +
 +
<pre>
 +
int iis2dh_i2c_init(const struct device *dev)
 +
{
 +
        struct iis2dh_data *data = dev->data;
 +
        const struct iis2dh_device_config *config = dev->config;
 +
 +
        if (!device_is_ready(config->i2c.bus)) {
 +
                LOG_ERR("Bus device is not ready");
 +
                return -ENODEV;
 +
        }
 +
 +
        data->ctx = &iis2dh_i2c_ctx;
 +
        data->ctx->handle = (void *)dev;
 +
 +
        return 0;
 +
}
 +
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */
 +
</pre>
 +
 +
Above I2C init routine called only one place in source tree of Zephyr 3.2.0, from file `zephyr/drivers/sensor/iis2dh/iis2dh.c``:
 +
 +
<pre>
 +
static int iis2dh_init_interface(const struct device *dev)
 +
{/**
 +
* struct iis2dh_device_config - iis2dh hw configuration
 +
* @spi: SPI bus spec.
 +
* @i2c: I2C bus spec.
 +
* @pm: Power mode (lis2dh_powermode).
 +
* @int_gpio: GPIO spec for sensor pin interrupt.
 +
*/
 +
struct iis2dh_device_config {
 +
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
 +
#warning "- DEV 1028 - Zephyr build tools find SPI device instance on SPI bus with 'okay' status."
 +
        struct spi_dt_spec spi;
 +
#else
 +
#warning "- DEV 1028 - Zephyr build tools find no device instance 'okay' on SPI bus!"
 +
#endif
 +
 +
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
 +
#warning "- DEV 1028 - Zephyr build tools find I2C device instance on I2C bus with 'okay' status."
 +
        struct i2c_dt_spec i2c;
 +
#else
 +
#warning "- DEV 1028 - Zephyr build tools find no device instance 'okay' on I2C bus!"
 +
#endif
 +
        uint8_t pm;
 +
#ifdef CONFIG_IIS2DH_TRIGGER
 +
        struct gpio_dt_spec int_gpio;
 +
#endif /* CONFIG_IIS2DH_TRIGGER */
 +
};
 +
 +
 +
        int res;
 +
 +
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
 +
        res = iis2dh_spi_init(dev);
 +
        if (res) {
 +
                return res;
 +
        }
 +
#elif DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
 +
        res = iis2dh_i2c_init(dev);
 +
        if (res) {
 +
                return res;
 +
        }
 +
#else
 +
#error "BUS MACRO NOT DEFINED IN DTS"
 +
#endif
 +
 +
        return 0;
 +
}
 +
</pre>
 +
 +
In our search for how `dev` is defined, above routine is called in same source file from routine:
 +
 +
<pre>
 +
static int iis2dh_init(const struct device *dev)
 +
{
 +
        struct iis2dh_data *iis2dh = dev->data;
 +
        const struct iis2dh_device_config *cfg = dev->config;
 +
        uint8_t wai;
 +
 +
        if (iis2dh_init_interface(dev)) {
 +
                return -EINVAL;
 +
        }
 +
 +
        /* check chip ID */
 +
        if (iis2dh_device_id_get(iis2dh->ctx, &wai) < 0) {
 +
                return -EIO;
 +
        }
 +
  .
 +
  .
 +
  .
 +
</pre>
 +
 +
And in Zephyr 3.2.0 source tree routine `iis2dh_init()` is called from or referenced at the following places:
 +
 +
<pre>
 +
ted@localhost1:~/projects-sandbox/workspace-for-nexus$ grep -nr 'iis2dh_init' ./*
 +
 +
./zephyr/drivers/sensor/iis2dh/iis2dh.c:253:static int iis2dh_init(const struct device *dev)
 +
 +
./zephyr/drivers/sensor/iis2dh/iis2dh.c:321: DEVICE_DT_INST_DEFINE(inst, iis2dh_init, NULL, \
 +
</pre>
 +
 +
And near top of iis2dh.h the structure definition for 'iis2dh_device_config' appears:
 +
 +
<pre>
 +
/**
 +
* struct iis2dh_device_config - iis2dh hw configuration
 +
* @spi: SPI bus spec.
 +
* @i2c: I2C bus spec.
 +
* @pm: Power mode (lis2dh_powermode).
 +
* @int_gpio: GPIO spec for sensor pin interrupt.
 +
*/
 +
struct iis2dh_device_config {
 +
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
 +
        struct spi_dt_spec spi;
 +
#endif
 +
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
 +
        struct i2c_dt_spec i2c;
 +
#endif
 +
        uint8_t pm;
 +
#ifdef CONFIG_IIS2DH_TRIGGER
 +
        struct gpio_dt_spec int_gpio;
 +
#endif /* CONFIG_IIS2DH_TRIGGER */
 +
};
 +
</pre>
 +
 +
QUESTION:  is the macro DT_ANY_INST_ON_BUS_STATUS_OKAY not returning any ok I2C bus instances in our app build process?
 +
 +
ANSWER:  in our application build as of 2022-10-24 this macro evaluates to false, which is strange!  We have posted to Circuit Dojo forum, at
 +
 +
On Zephyr's I2C in-tree driver side, the low level routine to perform an I2C "write read" transaction is in file `/zephyr/include/zephyr/drivers/i2c.h`.  Here is an excerpt, which begins around line 991:
 +
 +
<pre>
 +
 +
/**
 +
* @brief Write then read data from an I2C device.
 +
*
 +
* This supports the common operation "this is what I want", "now give
 +
* it to me" transaction pair through a combined write-then-read bus
 +
* transaction.
 +
*
 +
* @param dev Pointer to the device structure for an I2C controller
 +
* driver configured in controller mode.
 +
* @param addr Address of the I2C device
 +
* @param write_buf Pointer to the data to be written
 +
* @param num_write Number of bytes to write
 +
* @param read_buf Pointer to storage for read data
 +
* @param num_read Number of bytes to read
 +
*
 +
* @retval 0 if successful
 +
* @retval negative on error.
 +
*/
 +
static inline int i2c_write_read(const struct device *dev, uint16_t addr,
 +
                                const void *write_buf, size_t num_write,
 +
                                void *read_buf, size_t num_read)
 +
{
 +
        struct i2c_msg msg[2];
 +
 +
        msg[0].buf = (uint8_t *)write_buf;
 +
        msg[0].len = num_write;
 +
        msg[0].flags = I2C_MSG_WRITE;
 +
 +
        msg[1].buf = (uint8_t *)read_buf;
 +
        msg[1].len = num_read;
 +
        msg[1].flags = I2C_MSG_RESTART | I2C_MSG_READ | I2C_MSG_STOP;
 +
 +
        return i2c_transfer(dev, msg, 2, addr);
 +
}
 +
</pre>
 +
 +
 +
=== [[#top|^]] IIS2DH header file diagnostic additions ===
 +
 +
Following excerpt shows build time diagnostic statements added to STMicro driver file `iis2dh.h`, as found in Zephyr RTOS 3.2.0:
 +
 +
<pre>
 +
/**
 +
* struct iis2dh_device_config - iis2dh hw configuration
 +
* @spi: SPI bus spec.
 +
* @i2c: I2C bus spec.
 +
* @pm: Power mode (lis2dh_powermode).
 +
* @int_gpio: GPIO spec for sensor pin interrupt.
 +
*/
 +
struct iis2dh_device_config {
 +
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
 +
#warning "- DEV 1028 - Zephyr build tools find SPI device instance on SPI bus with 'okay' status."
 +
        struct spi_dt_spec spi;
 +
#else
 +
#warning "- DEV 1028 - Zephyr build tools find no device instance 'okay' on SPI bus!"
 +
#endif
 +
 +
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
 +
#warning "- DEV 1028 - Zephyr build tools find I2C device instance on I2C bus with 'okay' status."
 +
        struct i2c_dt_spec i2c;
 +
#else
 +
#warning "- DEV 1028 - Zephyr build tools find no device instance 'okay' on I2C bus!"
 +
#endif
 +
        uint8_t pm;
 +
#ifdef CONFIG_IIS2DH_TRIGGER
 +
        struct gpio_dt_spec int_gpio;
 +
#endif /* CONFIG_IIS2DH_TRIGGER */
 +
};
 +
</pre>
 +
 +
<!-- comment -->
 +
 +
== * ==
 +
 +
&nbsp;
 +
 +
<!-- empty section to create horizontal white space, organizing document sections for possible later break -->
  
 
<!-- comment -->
 
<!-- comment -->

Latest revision as of 18:23, 28 October 2022

Keywords: zephyr device drivers :: Zephyr device driver model :: Zephyr device drivers getting started :: LIS2DH driver study :: Jared Wolff post :: Jared Wolff device dr iver post :: AQW repo :: Zephyr sensor_driver_api :: de vice_get_binding and Z_DEVICE_MAX_NAME_LEN

NOTEABLE:  page section - Zephyr sensor_driver_api


^ OVERVIEW

Ok roughly a year into working with Zephyr RTOS we have a better idea of Zephyr's device driver model. A good point of reference to understand this model is Zephyr's device driver model documentation page.

Some important features of this device driver model include Zephyr C enumerations to support notions of sensor "channels" and sensor attributes. One such sensor attribute detailed at Zephyr doc page on symbol SENSOR_ATTR_CONFIGURATION. Along with Zephyr RTOS sensor attributes, channels and triggers, there is a sextet of generalized sensor or device API routines which Zephyr's driver model employs. These are:



Original overview from 2021 August, need to review links to external references and clean up:


    Challenged by Zephyr's way of writing device drivers, capturing some notes here on a study of a Zephyr example project which talks to LIS2DH accelerometer on a Sparkfun Thing Plus board. As of 2021-08-27 Friday, couple of important features Ted notes are that Zephyr Project's sample drivers for sensors are like or are actually small <i>Zephyr applications (track down definition of this), which need to be studied along side corresponding Zephyr driver sources. One design pattern Ted observes from an out-of-tree driver for Sensirion SHTC3 sensor is that all driver functions are declared private, making them directly callable only within the given source file. These routines appear to be accessed via a structure whose member elements are pointers to these static functions. Note lines 306..309 in AQW driver source shtc3.c. This coding pattern of declaring all driver functions static may relate to a larger design practice in Zephyr RTOS, where all major memory uses are declared and known at compile time. </i>



^ References To Review and Add

New refs:
* https://github.com/zephyrproject-rtos/zephyr/issues/13737
* https://docs.zephyrproject.org/latest/reference/kconfig/CONFIG_GPIO.html
* https://docs.zephyrproject.org/latest/reference/kconfig/index-drivers.html
* https://docs.zephyrproject.org/latest/reference/kconfig/CONFIG_ADC.html#std-kconfig-CONFIG_ADC

./nrf/doc/nrf/ug_bootloader_adding.rst:221: west build -b nrf52840dk_nrf52840 zephyr/samples/hello_world -- -DCONFIG_BOOTLOADER_MCUBOOT=y

2021-08-24 TUE
*  https://github.com/Riphiphip/zephyr-uart-driver-example/tree/main/dts/bindings



^ Early Research and SDK Used

A couple of Zephyr driver tutorials and posts worth noting. Casting net wider for Zephyr device driver "How To" articles, find Jared Wolff's blog post:

Here is a link to a tutorial, part three of a series, which may be helpful:

What does zephyr_include_directories() perform? . . .

From Jared's blog post, he mentions in terms of driver directories and file structure the following paths and files:

 *  ncs/nrf/applications/asset_tracker      . . . example Zephyr application path
 *  ncs/nrf/drivers/sensor/CMakeLists.txt   . . . example top level Zephyr drivers path
 *  ncs/nrf/zephyr/module.yml               . . . file module.yml "in subdirectory of a dependency"

This module.yml file contains:

 build:
    cmake: .
    kconfig: Kconfig.nrf

^ Middle Research - Zephyr In-Tree Drivers

There appears to be a Zephyr in-tree driver for STMicro's IIS2DH accelerometer. Not much on-line regarding how to make use of in-tree drivers, but a search of Zephyr's source tree shows these driver files:

~/embedded/z3-on-var/zephyr$ find . -name '*iis2dh*.*'
./drivers/sensor/iis2dh/iis2dh.c
./drivers/sensor/iis2dh/iis2dh_spi.c
./drivers/sensor/iis2dh/iis2dh.h
./drivers/sensor/iis2dh/iis2dh_i2c.c
./drivers/sensor/iis2dh/iis2dh_trigger.c
./dts/bindings/sensor/st,iis2dh-spi.yaml
./dts/bindings/sensor/st,iis2dh-i2c.yaml

~/embedded/z3-on-var/zephyr$

A register header file for this sensor however is located in another place:

~/embedded/z3-on-var$ find . -name iis2dh_reg.h
./modules/hal/st/sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.h

~/embedded/z3-on-var/zephyr$

Only mid-level driver files appear to call IIS2DH initialization routines which are bus specific:

~/embedded/z3-on-var/zephyr$ grep -nr iis2dh_i2c_init ./*
./drivers/sensor/iis2dh/iis2dh.c:245:   iis2dh_i2c_init(dev);
./drivers/sensor/iis2dh/iis2dh.h:85:int iis2dh_i2c_init(const struct device *dev);
./drivers/sensor/iis2dh/iis2dh_i2c.c:44:int iis2dh_i2c_init(const struct device *dev)

~/embedded/z3-on-var/zephyr$

Source file `iis2dh.c` appears to be a middle level driver, so which sample applications call routines in this file? Routines iis2dh_set_odr() and iis2dh_channel_get_acc() are not called by any samples in Zephyr source tree . . . and they're not called anywhere within Nordic Semi nrf SDK 1.6.1:

$ grep -nr iis2dh_set_odr ./*
./drivers/sensor/iis2dh/iis2dh.c:88: * iis2dh_set_odr - set new sampling frequency
./drivers/sensor/iis2dh/iis2dh.c:92:static int iis2dh_set_odr(const struct device *dev, uint16_t odr)
./drivers/sensor/iis2dh/iis2dh.c:175:           return iis2dh_set_odr(dev, val->val1);

~/embedded/z3-on-var/zephyr$ grep -nr iis2dh_channel_get_acc ./*
./drivers/sensor/iis2dh/iis2dh.c:116:static inline void iis2dh_channel_get_acc(const struct device *dev,
./drivers/sensor/iis2dh/iis2dh.c:154:           iis2dh_channel_get_acc(dev, chan, val);

~/embedded/z3-on-var/nrf$ grep -nr iis2dh_channel_get_acc ./*

~/embedded/z3-on-var/nrf$ grep -nr iis2dh_set_odr ./*

~/embedded/z3-on-var/zephyr$

We really seem to be shielded from most of the details of in-tree drivers. Looking at `~/embedded/z3-on-var/zephyr/samples/sensor/lis2dh/src/main.c` it looks like just a few pound includes at the top of the file are sufficient to bring in the needed driver. Likely brings in all in-tree drivers:

code excerpt from lis2dh main.c:

  1 /*
  2  * Copyright (c) 2019 Nordic Semiconductor ASA
  3  *
  4  * SPDX-License-Identifier: Apache-2.0
  5  */
  6
  7 #include <stdio.h>
  8 #include <zephyr.h>
  9 #include <device.h>
 10 #include <drivers/sensor.h>
 11
 12 static void fetch_and_display(const struct device *sensor)
 13 {
 14         static unsigned int count;
 15         struct sensor_value accel[3];
 16         const char *overrun = "";
 17         int rc = sensor_sample_fetch(sensor);
 18
 19         ++count;
 20         if (rc == -EBADMSG) {
 21                 /* Sample overrun.  Ignore in polled mode. */
 22                 if (IS_ENABLED(CONFIG_LIS2DH_TRIGGER)) {
 23                         overrun = "[OVERRUN] ";
 24                 }


*

 


^ west utility basics

As of 2021-08-27 contributor Ted knows a bit more about the Python based west utility. Ted has a Zephyr based project which does not yet build but has a working west manifest file. When working with Zephyr based applications which employ west a best practice in most cases is to clone given project into a directory of its own. When running `west init` and `west update`, from these actions `west` will populate the parent directory of the `west manifest` file which it reads to download and update given Zephyr app dependencies.

This is an unusual behavior in that most firmware based project tools do not by default copy third party sources and libraries to the parent directory of the root dir of the given project.

Another behavior to note: `west` detaches third party git repos from their 'head' commit points. This means a developer making local changes to any git based Zephyr app dependency must use `git` to checkout a commit that's tracked at the remote repository. Developers may not have access to all remote code repos, but where they have access they may want to make local edits in the repo copies created by `west`, so there's a git branch check out step needed here following any `west update` invocations.


^ Zephyr sensor_driver_api

Zephyr's notion of sensor drivers has some strict, well-defined ways guiding how developers write driver code for new sensors. The lowest level driver sources for a particular sensor appear to follow a design pattern in which all driver functions are declared static. There can be many such functions, but Zephyr exposes only the following five in its header file zephyrproject-rtos/zephyr/include/drivers/sensor.h:

374 __subsystem struct sensor_driver_api {
375         sensor_attr_set_t attr_set;
376         sensor_attr_get_t attr_get;
377         sensor_trigger_set_t trigger_set;
378         sensor_sample_fetch_t sample_fetch;
379         sensor_channel_get_t channel_get;
380 };

This structure's members actually express function pointers. A code excerpt again from Jared Wolff's shtc3.c driver source file illustrates how these function pointers are assigned to functions for a specific sensor:

static const struct sensor_driver_api shtc3_api = {
    .attr_set = &shtc3_attr_set,
    .attr_get = &shtc3_attr_get,
    .sample_fetch = &shtc3_sample_fetch,
    .channel_get = &shtc3_channel_get,
};

To reiterate, Zephyr's sensor driver API is defined in the Zephyr Project sensor.h header file. In developer code where an instance of sensor_driver_api is assigned values, each of those values points to a static function in the given driver source file. In the language of C, pointers are integers such that the following type definition defines (*sensor_attr_set_t) as a pointer to a function with four parameters as they appear in this sensor.h excerpt:

325 /**
326  * @typedef sensor_attr_set_t
327  * @brief Callback API upon setting a sensor's attributes
328  *
329  * See sensor_attr_set() for argument description
330  */
331 typedef int (*sensor_attr_set_t)(const struct device *dev,
332                                  enum sensor_channel chan,
333                                  enum sensor_attribute attr,
334                                  const struct sensor_value *val);

The remaining four sensor driver API struct members are defined in similar ways in Zephyr's sensor.h header file. This makes more sense now looking at the definition of struct device. Here is an excerpt from Zephyr project drivers documentation:

    Driver Data Structures The device initialization macros populate some data structures at build time which are split into read-only and runtime-mutable parts. At a high level we have:
     struct device {
          const char *name;
          const void *config;
          const void *api;
          void * const data;
     };



TO REVIEW: Following material posted on about 2021-08-15, can be refined and some likely removed - TMH

Excerpt from file ~/ncs/zephyr/include/drivers/sensor.h:

382 /**
383  * @brief Set an attribute for a sensor
384  *
385  * @param dev Pointer to the sensor device
386  * @param chan The channel the attribute belongs to, if any.  Some
387  * attributes may only be set for all channels of a device, depending on
388  * device capabilities.
389  * @param attr The attribute to set
390  * @param val The value to set the attribute to
391  *
392  * @return 0 if successful, negative errno code if failure.
393  */
394 __syscall int sensor_attr_set(const struct device *dev,
395                               enum sensor_channel chan,
396                               enum sensor_attribute attr,
397                               const struct sensor_value *val);
398 
399 static inline int z_impl_sensor_attr_set(const struct device *dev,
400                                          enum sensor_channel chan,
401                                          enum sensor_attribute attr,
402                                          const struct sensor_value *val)
403 {
404         const struct sensor_driver_api *api =
405                 (const struct sensor_driver_api *)dev->api;
406 
407         if (api->attr_set == NULL) {
408                 return -ENOSYS;
409         }
410 
411         return api->attr_set(dev, chan, attr, val);
412 }
413 

From these two code excerpts the big question is, how is dev->api assigned in early configuring code for an I2C based sensor?


./include/drivers/sensor.h:61: SENSOR_CHAN_ACCEL_XYZ, . . . hmm, this is part of a large enumeration:

 53 enum sensor_channel {
 54         /** Acceleration on the X axis, in m/s^2. */
 55         SENSOR_CHAN_ACCEL_X,
 56         /** Acceleration on the Y axis, in m/s^2. */
 57         SENSOR_CHAN_ACCEL_Y,
 58         /** Acceleration on the Z axis, in m/s^2. */
 59         SENSOR_CHAN_ACCEL_Z,
 60         /** Acceleration on the X, Y and Z axes. */
 61         SENSOR_CHAN_ACCEL_XYZ,
 62         /** Angular velocity around the X axis, in radians/s. */
 63         SENSOR_CHAN_GYRO_X,
 64         /** Angular velocity around the Y axis, in radians/s. */
 65         SENSOR_CHAN_GYRO_Y,
 66         /** Angular velocity around the Z axis, in radians/s. */
 67         SENSOR_CHAN_GYRO_Z,
 68         /** Angular velocity around the X, Y and Z axes. */
 69         SENSOR_CHAN_GYRO_XYZ,
   .
   .
   .

^ Clues to Zephyr initialization code

NEEDS REVIEW: This section needs review as info here is incomplete, and cannot say whether it is worth keeping or not without further investigation of these files and questions about init and start up Zephyr functions - TMH

ted@localhost:~/projects/embedded/ncs/zephyr/samples/z--sandbox/blink-plus-uart/build/zephyr/boards/arm/sparkfun_thing_plus_nrf9160$ nm -s libboards__arm__sparkfun_thing_plus_nrf9160.a 

board.c.obj:
0000002c t $d
00000000 r $d
00000000 t $t
00000001 t board_sparkfun_thing_plus_nrf9160_init
00000000 r __init_sys_init_board_sparkfun_thing_plus_nrf9160_init0
         U z_impl_device_get_binding
ted@localhost:~/projects/embedded/ncs/zephyr/samples/z--sandbox/blink-plus-uart/build/zephyr/boards/arm/sparkfun_thing_plus_nrf9160$


File noted:

ted@localhost:~/projects/embedded/ncs/zephyr/samples/z--sandbox/blink-plus-uart/build/zephyr/dev_handles.c
ted@localhost:~/projects/embedded/ncs/zephyr/samples/z--sandbox/blink-plus-uart/build/zephyr$ ls *.c
dev_handles.c  isr_tables.c
ted@localhost:~/projects/embedded/ncs/zephyr/samples/z--sandbox/blink-plus-uart/build/zephyr$ ls *.h
ls: cannot access '*.h': No such file or directory

Example project build directory noted, this dir auto-gen'd:

ted@localhost:~/projects/embedded/ncs/zephyr/samples/z--sandbox/blink-plus-uart/build/zephyr$ ls include/generated/
app_data_alignment.ld  autoconf.h            driver-validation.h  otype-to-size.h     snippets-ram-sections.ld  snippets-sections.ld  version.h
app_smem_aligned.ld    device_extern.h       kobj-types-enum.h    otype-to-str.h      snippets-rodata.ld        syscall_dispatch.c
app_smem.ld            devicetree_fixups.h   ncs_version.h        pm_config.h         snippets-rom-start.ld     syscall_list.h
app_smem_unaligned.ld  devicetree_unfixed.h  offsets.h            snippets-noinit.ld  snippets-rwdata.ld        syscalls


^ Kionix KX132

Useful datasheets and documents for Kionix accelerometer KX132-1211:


^ LIS2DH driver files

Beginning with lis2dh_i2c.c, here we examine where Zephyr driver files for this accelerometer are located in Zephyr's source file project space. We'll also look at how routines and pound defines are factored across these files. Beginning with lis2dh_i2c.c file and its final twenty or so lines, we have a specific sensor API routine, a structure which entails function pointers to all the LIS2DH driver routines in this source file, and finally an initializing function for this driver:

static int lis2dh_i2c_update_reg(const struct device *dev, uint8_t reg_addr,
                                  uint8_t mask, uint8_t value)
{
        struct lis2dh_data *data = dev->data;
        const struct lis2dh_config *cfg = dev->config;

        return i2c_reg_update_byte(data->bus,
                                   cfg->bus_cfg.i2c_slv_addr,
                                   reg_addr, mask, value);
}

static const struct lis2dh_transfer_function lis2dh_i2c_transfer_fn = { 
        .read_data = lis2dh_i2c_read_data,
        .write_data = lis2dh_i2c_write_data,
        .read_reg  = lis2dh_i2c_read_reg,
        .write_reg  = lis2dh_i2c_write_reg,
        .update_reg = lis2dh_i2c_update_reg,
};

int lis2dh_i2c_init(const struct device *dev)
{
        struct lis2dh_data *data = dev->data;

        data->hw_tf = &lis2dh_i2c_transfer_fn;

        return 0;
}
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */

We also can see the closing pre-processor conditional directive, which pairs with a test at compile time which only compiles this driver API when at least one instance of the sensor is indicated in developer code:

 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)

LOG_MODULE_DECLARE(lis2dh, CONFIG_SENSOR_LOG_LEVEL);

static int lis2dh_i2c_read_data(const struct device *dev, uint8_t reg_addr,
                                 uint8_t *value, uint8_t len)
{
        struct lis2dh_data *data = dev->data;
        const struct lis2dh_config *cfg = dev->config;

   .
   .
   .
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */

Ok, so already a fair amount going on here. An important point to note and remember is that routines in lis2dh_i2c.c manifest direct calls to Zephyr RTOS's I2C driver. Most likely this is the only file where, following best factoring practices, we will see and want to make direct calls to this peripheral subsystem.

Along side the calls to an I2C driver there is also a LIS2DH initialization function, lis2dh_i2c_init(const struct device *dev). This initializer takes a pointer to a Zephyr device struct instance, and it assigns a value to *dev->data->hw_tf. We may be mistaken but hw_tf probably stands for in this case "hardware transfer function" given the name of the collection of sensor API function pointers just above this assignment.

This LIS2DH Zephyr init function begins our "chain of search", to learn how developers' or app level code interfaces with Zephyr driver code.


^ Chain Of Search

Our search begins with looking for places where lis2dh_i2c_init(const struct device *dev) is called or referenced by run-time code. This token is the name of the initializing routine called when Zephyr RTOS discovers one or more LIS2DH sensors connected (cofigured to use) an I2C bus. This routine is referenced only one place, in file lis2dh.c where it is assigned to a member of a C structure which is defined via a macro. This macro's name is IS2DH_CONFIG_I2C, and this name constitutes our next search step. Search steps are captured in the table below titled "Table x - Zephyr Driver Chain of Search".

Table x - Zephyr Driver Chain of Search

ted@localhost:~/projects/embedded/ncs/zephyr$ grep -nr lis2dh_i2c_init ./*

./drivers/sensor/lis2dh/lis2dh.h:273:int lis2dh_i2c_init(const struct device *dev);
./drivers/sensor/lis2dh/lis2dh_i2c.c:86:int lis2dh_i2c_init(const struct device *dev)
Binary file ./drivers/sensor/lis2dh/.lis2dh_i2c.c.swp matches
./drivers/sensor/lis2dh/lis2dh.c:470:		.bus_init = lis2dh_i2c_init,				\
Binary file ./samples/sensor/lis2dh/build/zephyr/drivers/sensor/lis2dh/libdrivers__sensor__lis2dh.a matches
Binary file ./samples/sensor/lis2dh/build/zephyr/drivers/sensor/lis2dh/CMakeFiles/drivers__sensor__lis2dh.dir/lis2dh.c.obj matches
Binary file ./samples/sensor/lis2dh/build/zephyr/drivers/sensor/lis2dh/CMakeFiles/drivers__sensor__lis2dh.dir/lis2dh_i2c.c.obj matches
./samples/sensor/lis2dh/build/zephyr/zephyr.lst:8026:0001375c <lis2dh_i2c_init>:
./samples/sensor/lis2dh/build/zephyr/zephyr.lst:8028:int lis2dh_i2c_init(const struct device *dev)
./samples/sensor/lis2dh/build/zephyr/zephyr.lst:8034:   1375e:	4a02      	ldr	r2, [pc, #8]	; (13768 <lis2dh_i2c_init+0xc>)
./samples/sensor/lis2dh/build/zephyr/zephyr.map:2548: .text.lis2dh_i2c_init
./samples/sensor/lis2dh/build/zephyr/zephyr.map:2550:                0x000000000001375c                lis2dh_i2c_init
./samples/sensor/lis2dh/build/zephyr/zephyr_prebuilt.map:2531: .text.lis2dh_i2c_init
./samples/sensor/lis2dh/build/zephyr/zephyr_prebuilt.map:2533:                0x000000000001375c                lis2dh_i2c_init
Binary file ./samples/sensor/lis2dh/build/zephyr/zephyr.elf matches
Binary file ./samples/sensor/lis2dh/build/zephyr/zephyr_prebuilt.elf matches
Binary file ./drivers/sensor/lis2dh/.lis2dh.c.swp matches
./drivers/sensor/lis2dh/lis2dh.c:467:#define LIS2DH_CONFIG_I2C(inst)						\
./drivers/sensor/lis2dh/lis2dh.c:480:		LIS2DH_CONFIG_I2C(inst);				\

ted@localhost:~/projects/embedded/ncs/zephyr$

lis2dh_i2c_init
LIS2DH_CONFIG_I2C
LIS2DH_DEFINE_I2C
LIS2DH_DEFINE
DT_INST_FOREACH_STATUS_OKAY

lis2dh_i2c_init --> LIS2DH_CONFIG_I2C

ted@localhost:~/projects/embedded/ncs/zephyr$ grep -nr LIS2DH_CONFIG_I2C ./*
Binary file ./drivers/sensor/lis2dh/.lis2dh.c.swp matches
./drivers/sensor/lis2dh/lis2dh.c:467:#define LIS2DH_CONFIG_I2C(inst)						\
./drivers/sensor/lis2dh/lis2dh.c:480:		LIS2DH_CONFIG_I2C(inst);				\
ted@localhost:~/projects/embedded/ncs/zephyr$

LIS2DH_CONFIG_I2C --> LIS2DH_DEFINE_I2C

ted@localhost:~/projects/embedded/ncs/zephyr$ grep -nr LIS2DH_DEFINE_I2C ./*
Binary file ./drivers/sensor/lis2dh/.lis2dh.c.swp matches
./drivers/sensor/lis2dh/lis2dh.c:358: * LIS2DH_DEFINE_I2C().
./drivers/sensor/lis2dh/lis2dh.c:477:#define LIS2DH_DEFINE_I2C(inst)						\
./drivers/sensor/lis2dh/lis2dh.c:490:		    (LIS2DH_DEFINE_I2C(inst)))

LIS2DH_DEFINE_I2C --> LIS2DH_DEFINE

ted@localhost:~/projects/embedded/ncs/zephyr$ grep -nr LIS2DH_DEFINE ./*
Binary file ./drivers/sensor/lis2dh/.lis2dh.c.swp matches
./drivers/sensor/lis2dh/lis2dh.c:357: * Device creation macro, shared by LIS2DH_DEFINE_SPI() and
./drivers/sensor/lis2dh/lis2dh.c:358: * LIS2DH_DEFINE_I2C().
./drivers/sensor/lis2dh/lis2dh.c:456:#define LIS2DH_DEFINE_SPI(inst)						\
./drivers/sensor/lis2dh/lis2dh.c:477:#define LIS2DH_DEFINE_I2C(inst)						\
./drivers/sensor/lis2dh/lis2dh.c:487:#define LIS2DH_DEFINE(inst)						\
./drivers/sensor/lis2dh/lis2dh.c:489:		    (LIS2DH_DEFINE_SPI(inst)),				\
./drivers/sensor/lis2dh/lis2dh.c:490:		    (LIS2DH_DEFINE_I2C(inst)))
./drivers/sensor/lis2dh/lis2dh.c:492:DT_INST_FOREACH_STATUS_OKAY(LIS2DH_DEFINE)
ted@localhost:~/projects/embedded/ncs/zephyr$

LIS2DH_DEFINE --> DT_INST_FOREACH_STATUS_OKAY

ted@localhost:~/projects/embedded/ncs/zephyr$ grep -nr DT_INST_FOREACH_STATUS_OKAY ./*
./doc/releases/release-notes-2.4.rst:1169:* :github:`27404` - IS_ENABLED not working with C++ (was: Is DT_INST_FOREACH_STATUS_OKAY broken on v2.3?)
./doc/guides/dts/howtos.rst:479:Finally, pass the instantiation macro to :c:func:`DT_INST_FOREACH_STATUS_OKAY`:
./doc/guides/dts/howtos.rst:484:   DT_INST_FOREACH_STATUS_OKAY(CREATE_MY_DEVICE)
./doc/guides/dts/howtos.rst:486:``DT_INST_FOREACH_STATUS_OKAY`` expands to code which calls
./doc/guides/dts/howtos.rst:567:Since this style does not use ``DT_INST_FOREACH_STATUS_OKAY()``, the driver
./doc/reference/devicetree/api.rst:91::c:func:`DT_INST_FOREACH_STATUS_OKAY`, but these require ``DT_DRV_COMPAT`` to
./doc/reference/devicetree/api.rst:192:and :c:func:`DT_INST_FOREACH_STATUS_OKAY` are special-purpose helpers without
   .
   .
   .
./drivers/sensor/lis2dh/lis2dh.c:492:DT_INST_FOREACH_STATUS_OKAY(LIS2DH_DEFINE)
   .
   .
   .
./include/devicetree.h:2183: *     DT_INST_FOREACH_STATUS_OKAY(MY_FN)
./include/devicetree.h:2210:#define DT_INST_FOREACH_STATUS_OKAY(fn) \

From ./zephyr/include/devicetree.h:

2151 /**
2152  * @brief Call "fn" on all nodes with compatible DT_DRV_COMPAT
2153  *        and status "okay"
2154  *
2155  * This macro calls "fn(inst)" on each "inst" number that refers to a
2156  * node with status "okay". Whitespace is added between invocations.
2157  *
2158  * Example devicetree fragment:
2159  *
2160  *     a {
2161  *             compatible = "vnd,device";
2162  *             status = "okay";
2163  *             label = "DEV_A";
2164  *     };
2165  *
2166  *     b {
2167  *             compatible = "vnd,device";
2168  *             status = "okay";
2169  *             label = "DEV_B";
2170  *     };
2171  *
2172  *     c {
2173  *             compatible = "vnd,device";
2174  *             status = "disabled";
2175  *             label = "DEV_C";
2176  *     };
2177  *
2178  * Example usage:
2179  *
2180  *     #define DT_DRV_COMPAT vnd_device
2181  *     #define MY_FN(inst) DT_INST_LABEL(inst),
2182  *
2183  *     DT_INST_FOREACH_STATUS_OKAY(MY_FN)
2184  *
2185  * This expands to:
2186  *
2187  *     MY_FN(0) MY_FN(1)
2188  *
2189  * and from there, to either this:
2190  *
2191  *     "DEV_A", "DEV_B",
2192  *
2193  * or this:
2194  *
2195  *     "DEV_B", "DEV_A",
2196  *
2197  * No guarantees are made about the order that a and b appear in the
2198  * expansion.
2199  *
2200  * Note that "fn" is responsible for adding commas, semicolons, or
2201  * other separators or terminators.
2202  *
2203  * Device drivers should use this macro whenever possible to
2204  * instantiate a struct device for each enabled node in the devicetree
2205  * of the driver's compatible DT_DRV_COMPAT.
2206  *
2207  * @param fn Macro to call for each enabled node. Must accept an
2208  *           instance number as its only parameter.
2209  */
2210 #define DT_INST_FOREACH_STATUS_OKAY(fn) \
2211         COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT),   \
2212                     (UTIL_CAT(DT_FOREACH_OKAY_INST_,            \
2213                               DT_DRV_COMPAT)(fn)),              \
2214                     ())

DT_INST_FOREACH_STATUS_OKAY --> DT_FOREACH_OKAY_INST_xxx . . . where 'xxx' stands for lower case


pattern present --> pattern next



pattern present --> pattern next


-->



^ Chain Of Search edit point

ted@localhost:~/projects/embedded/ncs$ grep -nr LIS2DH_DEFINE ./*
./v1.4.1/zephyr/drivers/sensor/lis2dh/lis2dh.c:356: * Device creation macro, shared by LIS2DH_DEFINE_SPI() and
./v1.4.1/zephyr/drivers/sensor/lis2dh/lis2dh.c:357: * LIS2DH_DEFINE_I2C().
./v1.4.1/zephyr/drivers/sensor/lis2dh/lis2dh.c:419:#define LIS2DH_DEFINE_SPI(inst)						\
./v1.4.1/zephyr/drivers/sensor/lis2dh/lis2dh.c:437:#define LIS2DH_DEFINE_I2C(inst)						\
./v1.4.1/zephyr/drivers/sensor/lis2dh/lis2dh.c:447:#define LIS2DH_DEFINE(inst)						\
./v1.4.1/zephyr/drivers/sensor/lis2dh/lis2dh.c:449:		    (LIS2DH_DEFINE_SPI(inst)),				\
./v1.4.1/zephyr/drivers/sensor/lis2dh/lis2dh.c:450:		    (LIS2DH_DEFINE_I2C(inst)))
./v1.4.1/zephyr/drivers/sensor/lis2dh/lis2dh.c:452:DT_INST_FOREACH_STATUS_OKAY(LIS2DH_DEFINE)
Binary file ./zephyr/drivers/sensor/lis2dh/.lis2dh.c.swp matches
./zephyr/drivers/sensor/lis2dh/lis2dh.c:357: * Device creation macro, shared by LIS2DH_DEFINE_SPI() and
./zephyr/drivers/sensor/lis2dh/lis2dh.c:358: * LIS2DH_DEFINE_I2C().
./zephyr/drivers/sensor/lis2dh/lis2dh.c:456:#define LIS2DH_DEFINE_SPI(inst)						\
./zephyr/drivers/sensor/lis2dh/lis2dh.c:477:#define LIS2DH_DEFINE_I2C(inst)						\
./zephyr/drivers/sensor/lis2dh/lis2dh.c:487:#define LIS2DH_DEFINE(inst)						\
./zephyr/drivers/sensor/lis2dh/lis2dh.c:489:		    (LIS2DH_DEFINE_SPI(inst)),				\
./zephyr/drivers/sensor/lis2dh/lis2dh.c:490:		    (LIS2DH_DEFINE_I2C(inst)))
./zephyr/drivers/sensor/lis2dh/lis2dh.c:492:DT_INST_FOREACH_STATUS_OKAY(LIS2DH_DEFINE)
ted@localhost:~/projects/embedded/ncs$

From Zephyr documentation ncs/zephyr/doc/reference/devicetree/api.rst:

185 As shown above, the ``DT_INST_*`` APIs are conveniences for addressing nodes by
186 instance number. They are almost all defined in terms of one of the
187 :ref:`devicetree-generic-apis`. The equivalent generic API can be found by
188 removing ``INST_`` from the macro name. For example, ``DT_INST_PROP(inst,
189 prop)`` is equivalent to ``DT_PROP(DT_DRV_INST(inst), prop)``. Similarly,
190 ``DT_INST_REG_ADDR(inst)`` is equivalent to ``DT_REG_ADDR(DT_DRV_INST(inst))``,
191 and so on. There are some exceptions: :c:func:`DT_ANY_INST_ON_BUS_STATUS_OKAY`
192 and :c:func:`DT_INST_FOREACH_STATUS_OKAY` are special-purpose helpers without
193 straightforward generic equivalents.
194 
195 Since ``DT_DRV_INST()`` requires ``DT_DRV_COMPAT`` to be defined, it's an error
196 to use any of these without that macro defined.


^ Tracing from LIS2DH Sample Source Code

This line of code in the LIS2DH sample project appears to instantiate the code to support a present LIS2DH sensor. The macro DEVICE_DT_GET_ANY is defined in <$NCS_ROOT>/zephyr/include/device.h.

 51 void main(void)
 52 {
 53         const struct device *sensor = DEVICE_DT_GET_ANY(st_lis2dh);

Here is the definition of the macro, which looks itself to include the one that seemed to "dead end" in our search a couple sections above:

282 /**
283  * @def DEVICE_DT_GET_ANY
284  *
285  * @brief Obtain a pointer to a device object by devicetree compatible
286  *
287  * If any enabled devicetree node has the given compatible and a
288  * device object was created from it, this returns that device.
289  *
290  * If there no such devices, this returns NULL.
291  *
292  * If there are multiple, this returns an arbitrary one.
293  *
294  * If this returns non-NULL, the device must be checked for readiness
295  * before use, e.g. with device_is_ready().
296  *
297  * @param compat lowercase-and-underscores devicetree compatible
298  * @return a pointer to a device, or NULL
299  */
300 #define DEVICE_DT_GET_ANY(compat)                                           \
301         COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(compat),                      \
302                     (DEVICE_DT_GET(DT_COMPAT_GET_ANY_STATUS_OKAY(compat))), \
303                     (NULL))

From file ncs/zephyr/doc/guides/dts/howtos.rst:

417 .. _dt-create-devices-inst:
418 
419 Option 1: create devices using instance numbers
420 ===============================================
421 
422 Use this option, which uses :ref:`devicetree-inst-apis`, if possible. However,
423 they only work when devicetree nodes for your driver's ``compatible`` are all
424 equivalent, and you do not need to be able to distinguish between them.
425 
426 To use instance-based APIs, begin by defining ``DT_DRV_COMPAT`` to the
427 lowercase-and-underscores version of the compatible that the device driver
428 supports. For example, if your driver's compatible is ``"vnd,my-device"`` in
429 devicetree, you would define ``DT_DRV_COMPAT`` to ``vnd_my_device`` in your
430 driver C file:
431 
432 .. code-block:: c
433 
434    /*
435     * Put this near the top of the file. After the includes is a good place.
436     * (Note that you can therefore run "git grep DT_DRV_COMPAT drivers" in
437     * the zephyr Git repository to look for example drivers using this style).
438     */
439    #define DT_DRV_COMPAT vnd_my_device
440 
441 .. important::
442 
443    As shown, the DT_DRV_COMPAT macro should have neither quotes nor special
444    characters. Remove quotes and convert special characters to underscores
445    when creating ``DT_DRV_COMPAT`` from the compatible property.
446 
447 Finally, define an instantiation macro, which creates each ``struct device``
448 using instance numbers. Do this after defining ``my_api_funcs``.
449 
450 .. code-block:: c
451 
452    /*
453     * This instantiation macro is named "CREATE_MY_DEVICE".
454     * Its "inst" argument is an arbitrary instance number.
455     *
456     * Put this near the end of the file, e.g. after defining "my_api_funcs".
457     */
458    #define CREATE_MY_DEVICE(inst)                                       \
459         static struct my_dev_data my_data_##inst = {                    \
460                 /* initialize RAM values as needed, e.g.: */            \
461                 .freq = DT_INST_PROP(inst, clock_frequency),            \
462         };                                                              \
463         static const struct my_dev_cfg my_cfg_##inst = {                \
464                 /* initialize ROM values as needed. */                  \
465         };                                                              \
466         DEVICE_DT_INST_DEFINE(inst,                                     \
467                               my_dev_init_function,                     \
468                               NULL,                                     \
469                               &my_data_##inst,                          \
470                               &my_cfg_##inst,                           \
471                               MY_DEV_INIT_LEVEL, MY_DEV_INIT_PRIORITY,  \
472                               &my_api_funcs);
473 
474 Notice the use of APIs like :c:func:`DT_INST_PROP` and
475 :c:func:`DEVICE_DT_INST_DEFINE` to access devicetree node data. These
476 APIs retrieve data from the devicetree for instance number ``inst`` of
477 the node with compatible determined by ``DT_DRV_COMPAT``.
478 
479 Finally, pass the instantiation macro to :c:func:`DT_INST_FOREACH_STATUS_OKAY`:
480 
481 .. code-block:: c
482 
483    /* Call the device creation macro for each instance: */
484    DT_INST_FOREACH_STATUS_OKAY(CREATE_MY_DEVICE)
485 
486 ``DT_INST_FOREACH_STATUS_OKAY`` expands to code which calls
487 ``CREATE_MY_DEVICE`` once for each enabled node with the compatible determined
488 by ``DT_DRV_COMPAT``. It does not append a semicolon to the end of the
489 expansion of ``CREATE_MY_DEVICE``, so the macro's expansion must end in a
490 semicolon or function definition to support multiple devices.
491


*

 


^ Jared Wolff Example Zephyr Driver repo

Proprietor and lead engineer of Circuitdojo, Jared Wolff has a custom Zephyr device driver underway at Github:

This driver's directory structure as of 2021-08-16 looks like:

                                                   air-quality-wing-zephyr-drivers/                         .
                                                                  |                                         .
               +------------+-----------------------+-------------+-------+-----------------+               .
               |            |                       |             |       |                 |               .
        CMakeLists.txt      |                       |             |       |                 |               .
        Kconfig             |                       |             |       |                 |               .
        README.md        drivers/                  dts/        include/  lib/             zephyr/           .
                            |                       |             |       |                 |               .
      +------------+--------+-------+              bindings/    aqw.h    CMakeLists.txt   module.yml        .
      |            |                |               |                    Kconfig                            .
CMakeLists.txt   sgp40/           shtc3/            |                    aqw.c                              .
Kconfig            |                |               |                                                       .
                 CMakeLists.txt   CMakeLists.txt   sensirion,sgp40yaml                                      .
                 Kconfig          Kconfig          sensirion,shtc3.yaml                                     .
                 sgp40.c          shtc3.c                                                                   .
                 sgp40.h          shtc3.h                                                                   .

Jared has also shared an AQW driver demo] code project on Github. The demo has a top level Kconfig file whose active stanzas number one, and point to 'Kconfig.zephyr'. This Kconfig file is found in the given Zephyr project source tree, at its top level. There is a lot of information in Kconfig.zephyr in the form of comments. But question now, following Kionix work patch file from Nordic team member Sigurd is, how to inform cmake with include paths, such that a Zephyr app main.c file can include an out-of-tree driver header file?


^ Jared Wolff AQW demo

CircuitDojo proprietor and lead engineer Jared Wolff describes writing an out-of-tree driver, and a separate Zephyr based app, the AQW demo. In 2021-08-19 community form post thread, reply 16 of 22 Jared mentions both "app separate from [out of tree] driver" and "app with driver files contained therein". Only a brief mention. Jared's AQW demo app appears to be separate. Here is the `tree` dump of dirs and files for this demo:

ted@localhost:~/projects/embedded/ncs/zephyr/samples/sandbox-de-ted/jared-wolff/demo$ tree -R
.
├── boards
│   ├── nrf52840dk_nrf52840.conf
│   └── nrf52840dk_nrf52840.overlay
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   │   └── cmake.check_cache
│   ├── Kconfig
│   │   └── Kconfig.modules
│   ├── zephyrhttps://community.jaredwolff.com/d/147-undefined-driver-enable-symbols-when-adding-local-driver-and-kconfig/17
│   │   └── include
│   │       └── generated
│   │           ├── snippets-noinit.ld
│   │           ├── snippets-ram-sections.ld
│   │           ├── snippets-rodata.ld
│   │           ├── snippets-rom-start.ld
│   │           ├── snippets-rwdata.ld
│   │           └── snippets-sections.ld
│   ├── zephyr_modules.txt
│   └── zephyr_settings.txt
├── CMakeLists.txt
├── Kconfig
├── prj.conf
├── prj.debug.conf
├── prj.release.conf
├── README.md
├── src
│   └── main.c
├── west.yml
└── z--build-messages-001--first-build.txt

8 directories, 22 files
ted@localhost:~/projects/embedded/ncs/zephyr/samples/sandbox-de-ted/jared-wolff/demo$ find . -name Kconfig
./build/Kconfig
./Kconfig

Also searched for instances of Kconfig, of which there are several in the out-of-tree driver dirs and files, but only one of interest in the demo. We can disregard './build/Kconfig' in the tail end of the above shell excerpt because cmake and or Zephyr build process creates this directory named 'build', in the file system directory where the build command is issued.

An excerpt of Jared's most telling reply to date is here:

   jaredwolff
   5 days ago
   Edited

Here’s the file structure from the Air Quality Wing drivers repository. The most critical files are module.yml and top level CMakeLists.txt and Kconfig. . . .

|____dts
| |____bindings
| | |____sensirion,shtc3.yaml
| | |____sensirion,sgp40.yaml
|____CMakeLists.txt
|____drivers
| |____CMakeLists.txt
| |____Kconfig
| |____shtc3
| | |____shtc3.c
| | |____CMakeLists.txt
| | |____Kconfig
| | |____shtc3.h
| |____sgp40
| | |____sgp40.h
| | |____CMakeLists.txt
| | |____Kconfig
| | |____sgp40.c
|____include
| |____aqw.h
|____Kconfig
|____README.md
|____lib
| |____CMakeLists.txt
| |____Kconfig
| |____aqw.c
|____zephyr
| |____module.yml

If you were, per-se, going to add an application, I would create a folder within this root and call it app (or whatever you like) and put your application Kconfig CMakeLists.txt src/main.c etc.

-rw-r--r--  1 user  staff  342 Aug 19 19:34 CMakeLists.txt
-rw-r--r--  1 user  staff  124 Aug 19 19:34 Kconfig
-rw-r--r--  1 user  staff   34 Aug 19 19:34 README.md
drwxr-xr-x  6 user  staff  192 Aug 19 19:34 drivers
drwxr-xr-x  3 user  staff   96 Aug 19 19:34 dts
drwxr-xr-x  3 user  staff   96 Aug 19 19:34 include
drwxr-xr-x  5 user  staff  160 Aug 19 19:34 lib
drwxr-xr-x  3 user  staff   96 Aug 19 19:34 zephyr
drwxr-xr-x  3 user  staff   96 Aug 19 19:34 app

It’s important to understand how the Kconfig and Cmakelists.txt trickle down and work during compilation. If you have zephyr/module.yml configured properly ./CMakeLists.txt should be processed first before your application. This is critical for loading drivers correctly.

I can’t stress enough looking at some of these repos to see how they’re formatted. It takes a bit of noodling but once you get it you’ll be on your way. 

^ Zephyr Driver versus Zephyr Module

Interesting call for help from one Thomas Oggiers and responses from one Simon and another person and collaborator tejlmand:


^ Zephyr Files To Amend For In-Tree Driver

2021-08-30 Monday note:  Several days ago Ted unsure whether an "out of tree" driver development approach would take more time or less time compared with "in Zephyr tree" driver approach. Hence this wiki page section. Ted ended up solving out-of-tree project layout issues and is actively working on a Zephyr driver out-of-tree for Kionix KX132-1211 sensor.

Starting review of Zephyr driver files ~/projects/embedded/ncs/zephyr/drivers/sensor, file Kconfig is about 215 lines long and appears to source (has stanzas beginning with 'source') for each extent driver. First few drivers sourced in this Kconfig file are:

39 comment "Device Drivers"
 40 
 41 source "drivers/sensor/adt7420/Kconfig"
 42 
 43 source "drivers/sensor/adxl345/Kconfig"
 44 
 45 source "drivers/sensor/adxl362/Kconfig"
 46 
 47 source "drivers/sensor/adxl372/Kconfig"
 48 
 49 source "drivers/sensor/ak8975/Kconfig"
 50 
 51 source "drivers/sensor/amg88xx/Kconfig"
 52 
 53 source "drivers/sensor/ams_iAQcore/Kconfig"

Similarly so for the CMakeLists.txt file here.


May be important to specify, for a Zephyr application, a device tree root. Zephyr documentation regarding development has a section on this which Ted copies an excerpt here:

Devicetree Definitions

Devicetree directory trees are found in APPLICATION_SOURCE_DIR, BOARD_DIR, and ZEPHYR_BASE, but additional trees, or DTS_ROOTs, can be added by creating this directory tree:

include/
dts/common/
dts/arm/
dts/
dts/bindings/

Where ‘arm’ is changed to the appropriate architecture. Each directory is optional. The binding directory contains bindings and the other directories contain files that can be included from DT sources.

Once the directory structure is in place, you can use it by specifying its location through the DTS_ROOT CMake Cache variable:

Using west:

west build -b <board name> -- -DDTS_ROOT=<path to dts root>

Using CMake and ninja:

cmake -B build -GNinja -DBOARD=<board name> -DDTS_ROOT=<path to dts root> .
ninja -C build

You can also define the variable in the application CMakeLists.txt file. Make sure to do so before pulling in the Zephyr boilerplate with find_package(Zephyr ...).

Note

When specifying DTS_ROOT in a CMakeLists.txt, then an absolute path must be provided, for example list(APPEND DTS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/<extra-dts-root>. When using -DDTS_ROOT=<dts-root> both absolute and relative paths can be used. Relative paths are treated relatively to the application directory.


*

 


^ Search For Board Specific Zephyr Driver

2022-08-08 Monday - search for Zephyr WS2812 GPIO control based driver. Search excerpt:

ted@localhost:~/projects-sandbox/workspace-for-rpi/zephyr$ grep -nr WS2812_STRIP_GPIO ./*
./drivers/led_strip/Kconfig.ws2812:29:config WS2812_STRIP_GPIO
./drivers/led_strip/CMakeLists.txt:7:zephyr_library_sources_ifdef(CONFIG_WS2812_STRIP_GPIO   ws2812_gpio.c)
./samples/drivers/led_ws2812/boards/bbc_microbit.conf:2:CONFIG_WS2812_STRIP_GPIO=y
./samples/drivers/led_ws2812/boards/nrf51dk_nrf51422.conf:2:CONFIG_WS2812_STRIP_GPIO=y
./samples/drivers/led_ws2812/README.rst:48:  which case it will be :kconfig:option:`CONFIG_WS2812_STRIP_GPIO`.

ted@localhost:~/projects-sandbox/workspace-for-rpi/zephyr$ find . -name ws2812_gpio.c
./drivers/led_stri
p/ws2812_gpio.c

~/projects-sandbox/workspace-for-rpi/zephyr/drivers/led_strip$ ls -l total 60 -rw-rw-r-- 1 ted ted 2397 Aug 2 11:03 apa102.c -rw-rw-r-- 1 ted ted 398 Aug 2 11:03 CMakeLists.txt -rw-rw-r-- 1 ted ted 954 Aug 2 11:03 Kconfig -rw-rw-r-- 1 ted ted 314 Aug 2 11:03 Kconfig.apa102 -rw-rw-r-- 1 ted ted 556 Aug 2 11:03 Kconfig.lpd880x -rw-rw-r-- 1 ted ted 239 Aug 2 11:03 Kconfig.tlc5971 -rw-rw-r-- 1 ted ted 1185 Aug 2 11:03 Kconfig.ws2812 -rw-rw-r-- 1 ted ted 3508 Aug 2 11:03 lpd880x.c -rw-rw-r-- 1 ted ted 12182 Aug 2 11:03 tlc5971.c -rw-rw-r-- 1 ted ted 6924 Aug 2 11:03 ws2812_gpio.c -rw-rw-r-- 1 ted ted 6254 Aug 2 11:03 ws2812_spi.c </pre>

Also worth noting is the Zephyr RTOS header file `~/projects-sandbox/workspace-for-rpi/zephyr$ vi ./include/zephyr/drivers/led_strip.h`, which reminds us of the decoupling of Zephyr RTOS from out-of-tree driver details, and decoupling from specific driver implementations.

In file `ws2812_gpio.c` here are telling comments and calls to nrf_ API functions, functions which may be peculiar to Nordic's nRF51 family of parts. There are also the mentioned in-line assembly instructions which are based on this source file's targeting of an ARM Cortex-M0 running at 16MHz. We may be able to adapt this file and use it on the RPI2040 with appropriate configuration of this other processor's higher speed clock, plus adjustments to the in-line assembly. ( That in-line assembly code is just a series of NOP instructions, three different counts of NOP series for specific WS2812 timing requirements. )


^ IIS2DH driver in Zephyr 3.2.0

Need to find example of code which calls this following, seeming in tree Zephyr driver for STMicro IIS2DH accelerometer. This code in source file `zephyr/drivers/sensor/iis2dh/iis2dh_i2c.c`:

int iis2dh_i2c_init(const struct device *dev)
{
        struct iis2dh_data *data = dev->data;
        const struct iis2dh_device_config *config = dev->config;

        if (!device_is_ready(config->i2c.bus)) {
                LOG_ERR("Bus device is not ready");
                return -ENODEV;
        }

        data->ctx = &iis2dh_i2c_ctx;
        data->ctx->handle = (void *)dev;

        return 0;
}
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */

Above I2C init routine called only one place in source tree of Zephyr 3.2.0, from file `zephyr/drivers/sensor/iis2dh/iis2dh.c``:

static int iis2dh_init_interface(const struct device *dev)
{/**
 * struct iis2dh_device_config - iis2dh hw configuration
 * @spi: SPI bus spec.
 * @i2c: I2C bus spec.
 * @pm: Power mode (lis2dh_powermode).
 * @int_gpio: GPIO spec for sensor pin interrupt.
 */
struct iis2dh_device_config {
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
#warning "- DEV 1028 - Zephyr build tools find SPI device instance on SPI bus with 'okay' status."
        struct spi_dt_spec spi;
#else
#warning "- DEV 1028 - Zephyr build tools find no device instance 'okay' on SPI bus!"
#endif

#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
#warning "- DEV 1028 - Zephyr build tools find I2C device instance on I2C bus with 'okay' status."
        struct i2c_dt_spec i2c;
#else
#warning "- DEV 1028 - Zephyr build tools find no device instance 'okay' on I2C bus!"
#endif
        uint8_t pm; 
#ifdef CONFIG_IIS2DH_TRIGGER
        struct gpio_dt_spec int_gpio;
#endif /* CONFIG_IIS2DH_TRIGGER */
};


        int res;

#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
        res = iis2dh_spi_init(dev);
        if (res) {
                return res;
        }
#elif DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
        res = iis2dh_i2c_init(dev);
        if (res) {
                return res;
        }
#else
#error "BUS MACRO NOT DEFINED IN DTS"
#endif

        return 0;
}

In our search for how `dev` is defined, above routine is called in same source file from routine:

static int iis2dh_init(const struct device *dev)
{
        struct iis2dh_data *iis2dh = dev->data;
        const struct iis2dh_device_config *cfg = dev->config;
        uint8_t wai;

        if (iis2dh_init_interface(dev)) {
                return -EINVAL;
        }

        /* check chip ID */
        if (iis2dh_device_id_get(iis2dh->ctx, &wai) < 0) {
                return -EIO;
        }
  .
  .
  .

And in Zephyr 3.2.0 source tree routine `iis2dh_init()` is called from or referenced at the following places:

ted@localhost1:~/projects-sandbox/workspace-for-nexus$ grep -nr 'iis2dh_init' ./*

./zephyr/drivers/sensor/iis2dh/iis2dh.c:253:static int iis2dh_init(const struct device *dev)

./zephyr/drivers/sensor/iis2dh/iis2dh.c:321:	DEVICE_DT_INST_DEFINE(inst, iis2dh_init, NULL,						\

And near top of iis2dh.h the structure definition for 'iis2dh_device_config' appears:

/**
 * struct iis2dh_device_config - iis2dh hw configuration
 * @spi: SPI bus spec.
 * @i2c: I2C bus spec.
 * @pm: Power mode (lis2dh_powermode).
 * @int_gpio: GPIO spec for sensor pin interrupt.
 */
struct iis2dh_device_config {
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
        struct spi_dt_spec spi;
#endif
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
        struct i2c_dt_spec i2c;
#endif
        uint8_t pm; 
#ifdef CONFIG_IIS2DH_TRIGGER
        struct gpio_dt_spec int_gpio;
#endif /* CONFIG_IIS2DH_TRIGGER */
};

QUESTION: is the macro DT_ANY_INST_ON_BUS_STATUS_OKAY not returning any ok I2C bus instances in our app build process?

ANSWER: in our application build as of 2022-10-24 this macro evaluates to false, which is strange! We have posted to Circuit Dojo forum, at

On Zephyr's I2C in-tree driver side, the low level routine to perform an I2C "write read" transaction is in file `/zephyr/include/zephyr/drivers/i2c.h`. Here is an excerpt, which begins around line 991:


/**
 * @brief Write then read data from an I2C device.
 *
 * This supports the common operation "this is what I want", "now give
 * it to me" transaction pair through a combined write-then-read bus
 * transaction.
 *
 * @param dev Pointer to the device structure for an I2C controller
 * driver configured in controller mode.
 * @param addr Address of the I2C device
 * @param write_buf Pointer to the data to be written
 * @param num_write Number of bytes to write
 * @param read_buf Pointer to storage for read data
 * @param num_read Number of bytes to read
 *
 * @retval 0 if successful
 * @retval negative on error.
 */
static inline int i2c_write_read(const struct device *dev, uint16_t addr,
                                 const void *write_buf, size_t num_write,
                                 void *read_buf, size_t num_read)
{
        struct i2c_msg msg[2];

        msg[0].buf = (uint8_t *)write_buf;
        msg[0].len = num_write;
        msg[0].flags = I2C_MSG_WRITE;

        msg[1].buf = (uint8_t *)read_buf;
        msg[1].len = num_read;
        msg[1].flags = I2C_MSG_RESTART | I2C_MSG_READ | I2C_MSG_STOP;

        return i2c_transfer(dev, msg, 2, addr);
}


^ IIS2DH header file diagnostic additions

Following excerpt shows build time diagnostic statements added to STMicro driver file `iis2dh.h`, as found in Zephyr RTOS 3.2.0:

/**
 * struct iis2dh_device_config - iis2dh hw configuration
 * @spi: SPI bus spec.
 * @i2c: I2C bus spec.
 * @pm: Power mode (lis2dh_powermode).
 * @int_gpio: GPIO spec for sensor pin interrupt.
 */
struct iis2dh_device_config {
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
#warning "- DEV 1028 - Zephyr build tools find SPI device instance on SPI bus with 'okay' status."
        struct spi_dt_spec spi;
#else
#warning "- DEV 1028 - Zephyr build tools find no device instance 'okay' on SPI bus!"
#endif

#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
#warning "- DEV 1028 - Zephyr build tools find I2C device instance on I2C bus with 'okay' status."
        struct i2c_dt_spec i2c;
#else
#warning "- DEV 1028 - Zephyr build tools find no device instance 'okay' on I2C bus!"
#endif
        uint8_t pm; 
#ifdef CONFIG_IIS2DH_TRIGGER
        struct gpio_dt_spec int_gpio;
#endif /* CONFIG_IIS2DH_TRIGGER */
};


*

 


^ Useful File Manipulations and Pattern Searches

Looking for differences between Zephyr Project instances of i2c.h:

for file in `find . -name i2c.h`; do md5sum ${file}; ls -l ${file}; done



- - - top of page - - -