Zephyr driver demo
Zephyr Driver and Separate Demo
LOCAL PAGE RETIRED 2023-06-13
- OVERVIEW - Here list and begin to describe key, salient features of a Zephyr RTOS based driver and separately build-able demo to exercise given driver. Driver for first version 0p0 is "out of tree", in other words outside of Zephyr project's source tree.
Ted has the beginning of an out-of-tree Zephyr device driver at the github hosted project kionix-driver-demo. This project relies on `west` meta-tool that's used by the Zephyr RTOS community and folks using Zephyr in their projects. `West` in turn when invoked to initialize and then update this tiny project will pull in the Kionix driver code itself, which is only to the point of reading a sensor ID string and part number of Kionix KX132-1211 accelerometer.
Contents
- 1 ^ Key Zephyr App Files
- 2 ^ Supporting Linux Commands
- 3 ^ Configuration and Set Up Steps
- 4 ^ Zephyr Overlay Files and Device Node Label Strings
- 5 ^ Zephyr Project Uniform Sensor API
- 6 ^ Chain From Main To Print Not Applicable
- 7 ^ AQW Example Sensor Structs and Data Fetch Functions
- 8 ^ Memfault Article Review and CheckList of Macros
- 9 ^ References
^ Key Zephyr App Files
Move to refs:
* https://dev.w3.org/html5/html-author/charref å * https://meta.wikimedia.org/wiki/Table_background_colors * https://community.jaredwolff.com/d/4-nrf-cloud-agps-sample-no-gps-data/6
Key files in the two parts of Zephyr out-of-tree driver and demonstrating app:
Zephyr app | Driver out-of-tree |
---|---|
demo (app) ¦ CMakeLists.txt <- set toolchain variant and toolchain path Kconfig <- refers to Zephyr top level Kconfig west.yml <- chooses SDK, driver projects (1) |
driver ¦ drivers/ dts/bindings/ include/ zephyr/ ¦ +--module.yml CMakeLists.txt <- · add subdirs when given drivers enabled, ▸ add include dirs holding header files Kconfig <- · refers to Kconfig in child 'drivers' directory README.md |
Note: the out-of-tree driver has a zephyr/module.yml dir and file. The zephyr app does not.
All the more that is in Jared Wolff's AQW demo:
ted@localhost:~/projects/embedded/ncs/zephyr/samples/sandbox-de-ted/jared-wolff/demo$ tree -R . ├── boards │ ├── nrf52840dk_nrf52840.conf │ └── nrf52840dk_nrf52840.overlay ├── 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 2 directories, 11 files
^ Supporting Linux Commands
^ Useful Commands To Manage Zephyr Project Details
Command available on Ubuntu hosts to effectively locate Zephyr RTOS project installations:
ted@localhost:~/projects/embedded$ locate VERSION | grep 'ION$'
Committing changes to Zephyr app dependencies which are secondary git repositories linked and locally updated via `west` and the given project's west.yml manifest file:
Command line excerpt - switch from git project detached head to 'main' branch of development, and merge:
1053 git status 1054 git checkout main 1055 git merge c601129
In the above excerpt the argument to `git merge` is the start of the commit hash of the detached head commit history which, for some reason `west` meta-tool creates whenever developer issues `west update` in a local Zephyr application project. The commit hash here is arbitrary. It will be different pretty much every time a developer updates local code from remote repositories. Important to make a note of most recent hash before checking out a named branch!
2021-10-01 Friday
Message from `west update` when dependency third party repo changed:
=== updating zephyr-driver-work-v2 (kionix-drivers): --- zephyr-driver-work-v2: fetching, need revision main From https://github.com/tedhavelka/zephyr-driver-work-v2 * branch main -> FETCH_HEAD HEAD is now at 6466b94 Adding code to fetch and provide copy of KX132-1211 numeric part ID. WARNING: left behind zephyr-driver-work-v2 branch "sensor-configuration-routines-work-001"; to switch back to it (fast forward): git -C ../kionix-drivers checkout sensor-configuration-routines-work-001
^ Configuration and Set Up Steps
^ New Zephyr app `west init` and `west update`
Upon, after first invocation of `west init && west update` in a newly cloned Zephyr app project, files pulled down include:
ted@localhost:~/projects/sandbox-2/kionix-driver-demo$ ls bootloader modules tools west.yml zephyr ted@localhost:~/projects/sandbox-2/kionix-driver-demo$ git status . On branch main Your branch is up to date with 'origin/main'. Untracked files: (use "git add <file>..." to include in what will be committed) .west/ bootloader/ modules/ tools/ zephyr/ nothing added to commit but untracked files present (use "git add" to track)
The west.yml file specifies an ncs software development kit, which include Zephyr and bootloader. All the directories and files above not tracked by git were downloaded by the `west` meta-tool. So this non-tracking is consistent with the way `west` works, and brings in files to a `west manifest` project that is itself under git control.
^ Zephyr Overlay Files and Device Node Label Strings
Given a meta-compiled symbol assignment which takes a DTS node 'label' string, best to avoid use of minus character in these labels:
ted@ubuntu-vm-0p2:~/embedded/z1-sandbox-2021-08-26/kionix-driver-demo$ grep -nr KX132-1211 ./* Binary file ./boards/.sparkfun_thing_plus_nrf9160.overlay.swp matches ./boards/nrf9160dk_nrf9160ns.overlay:14: label = "KX132-1211"; ./boards/sparkfun_thing_plus_nrf9160.overlay:15: label = "KX132-1211"; ./boards/nrf9160dk_nrf9160.overlay:14: label = "KX132-1211"; ./build/mcuboot/zephyr/.config:421:# KX132-1211 driver ./build/mcuboot/zephyr/.config:423:# end of KX132-1211 driver ./build/zephyr/include/generated/autoconf.h:61:#define CONFIG_KX132_1211_DRV_NAME "KX132-1211" ./build/zephyr/include/generated/devicetree_unfixed.h:6118:#define DT_N_S_soc_S_peripheral_50000000_S_i2c_9000_S_kx132_1211_1f_P_label "KX132-1211" ./build/zephyr/sparkfun_thing_plus_nrf9160.dts.pre.tmp:645: label = "KX132-1211"; ./build/zephyr/zephyr.dts:253: label = "KX132-1211"; ./build/zephyr/.config:346:# KX132-1211 driver ./build/zephyr/.config:349:CONFIG_KX132_1211_DRV_NAME="KX132-1211" ./build/zephyr/.config:350:# end of KX132-1211 driver Binary file ./build/zephyr/edt.pickle matches ted@ubuntu-vm-0p2:~/embedded/z1-sandbox-2021-08-26/kionix-driver-demo$
^ Zephyr Project Uniform Sensor API
Zephyr Project's notion of sensor channels for reading back data in given units is important. Here is a starting point documentation on this topic:
Many of the most important programmatic elements of Zephyr's sensor API are defined in sensors.h
, which includes C structs, an enum of Zephyr supported "sensor channels", function prototypes and some other programmatic API features. As of 2021-09-13 Zephyr's v2.6 release tag holds this header file at the URL:
https://github.com/zephyrproject-rtos/zephyr/blob/v2.6-branch/include/drivers/sensor.h
One thing that's becoming more clear, Zephyr's macros and build process create some level of indirection between a developer's custom code calls to sensor API functions like sample_fetch()
and channel_get()
and the functions themselves. The Zephyr source tree, for example, we can search for patterns matching these API routine names. We'll find prototypes for them, but no definitions even though their names appear generic, e.g. their names do not indicate that they're written for a given sensor.
Through the "macros magic" of Zephyr RTOS build process a developer's specific sensor driver code is connected to, and or ultimately populates the API functions fetch, get, trigger, etc which developer project code calls to configure and obtain readings from sensors. For this reason the follow section of this wiki page, "Chain of Calls..." cannot show a more traditional series of C language calls from a program's nominal starting point at int main()
up to the deepest stack frame which holds a sensor's value ready to store or write to UART or file.
^ Chain From Main To Print Not Applicable
The following section was an early effort of Ted's to follow C code in a traditional, "few or no macros" way and to show the sequence of routine calls from a Zephyr sample app's int main()
routine through sensor reading and further to the printing or storing of a reading. Further work with Zephyr apps and out-of-tree driver development reveal that only after compiling a specific Zephyr project can this chain of calls be traced out by reviewing source code statically. This so due to Zephyr's complicated framework of macros which take into account not only traditional C language header and source files, but also Device Tree Source (DTS) and . . . (interrupted, need to review and pick up edit here - TMH)
^ Programmatic Chain From main Function To Print Sensor Readings
This section aims to capture a clear sequence of C-based routines and routine calls, from int main()
to an effective printf() type of function in a Zephyr RTOS application. In part this wiki page section is motivated by Zephyr's multiple ways of obtaining programmatic handles or pointers to devices, and what appear to be at least two distinct but strictly guided ways of connecting app code to a sensor driver API.
Though we cannot build and test it for lack of specific development board, Jared Wolff's Air Quality Wing driver demo we assume compiles and runs correctly given Jared's sharing of the project, and addition of two sensors in the past two weeks. We'll use this as a starting point looking at 'main.c' and 'inc main()' there . . .
In project "air-quality-wing-zephyr-demo" . . . | |
In main.c |
err = aqw_init(sensors, ARRAY_SIZE(sensors), sensor_cb); |
In project "air-quality-wing-zephyr-drivers" . . . | |
In ./lib/aqw.c |
int aqw_init(struct aqw_sensor **_sensors, size_t _sensor_count, aqw_sensor_data_ready_t _cb) |
|
aqw_sensors[i]->dev = device_get_binding(aqw_sensors[i]->dev_name); |
Hmm we haven't searched very far and already find that 'device_get_binding()' is not directly defined seemingly anywhere in Zephyr 2.6.0 sources. In zephyr/kernel/device.c there are defs for const struct device *z_impl_device_get_binding()
and static inline const struct device *z_vrfy_device_get_binding()
.
./kernel/device.c:104:const struct device *z_impl_device_get_binding(const char *name) ./kernel/device.c:136:static inline const struct device *z_vrfy_device_get_binding(const char *name)
Some related information and explanation of the two ways to obtain handles/pointers to devices in Zephyr app in this document:
- https://docs.zephyrproject.org/latest/guides/dts/howtos.html#get-devicetree-outputs
- https://docs.zephyrproject.org/latest/guides/dts/howtos.html#get-a-struct-device-from-a-devicetree-node
Research and review also zephyr/include/device.h
header file section with comment block:
. . . 122 /** 123 * @def DEVICE_DEFINE 124 * 125 * @brief Create device object and set it up for boot time initialization, 126 * with the option to pm_control. In case of Device Idle Power 127 * Management is enabled, make sure the device is in suspended state after 128 * initialization. 129 * 130 * @details This macro defines a device object that is automatically 131 * configured by the kernel during system initialization. Note that 132 * devices set up with this macro will not be accessible from user mode 133 * since the API is not specified; 134 * 135 * @param dev_name Device name. This must be less than Z_DEVICE_MAX_NAME_LEN 136 * characters (including terminating NUL) in order to be looked up from user 137 * mode with device_get_binding(). 138 * . . .
Tracing the "sensor fetch" command in Jared Wolff's Air Quality Wing Demo, eventually get to 'sensor_channel_get()' function which does not appear to be defined anywhere in Zephyr sources:
cpguest@ubuntu-vm-0p2:~/embedded/z1-sandbox-2021-08-26/zephyr/drivers$ grep -nr sensor_channel_get ./* ./clock_control/nrf_clock_calibration.c:182: rc = sensor_channel_get(temp_sensor, SENSOR_CHAN_DIE_TEMP, ./sensor/sensor_shell.c:106: err = sensor_channel_get(dev, i, value); ./sensor/shell_battery.c:39: err = sensor_channel_get(dev, chan, val); ./sensor/sensor_handlers.c:50:static inline int z_vrfy_sensor_channel_get(const struct device *dev, ./sensor/sensor_handlers.c:56: return z_impl_sensor_channel_get((const struct device *)dev, chan, ./sensor/sensor_handlers.c:59:#include <syscalls/sensor_channel_get_mrsh.c> cpguest@ubuntu-vm-0p2:~/embedded/z1-sandbox-2021-08-26/zephyr/drivers$
^ AQW Example Sensor Structs and Data Fetch Functions
Air Quality Wing code by Jared Wolff of CircuitDojo. AQW header file showing sensor types enumeration and two key structure:
Air Quality Wing 'lib' source file, good reference for sensor structure syntax and use:
^ Memfault Article Review and CheckList of Macros
Keyword and key phrases: memfault blog post de Jared Wolff
[ Interrupt dot memfault dot com ]
Jared Wolff's post about Zephyr drivers mentions the macro DEVICE_AND_API_INIT
among other key Zephyr driver code features. But we don't have this macro present Kionix driver work. Yet the driver is working. Are we employing an older or different way to initialize our Kionix driver? Has DEVICE_AND_API_INIT
been superceded by something newer? . . .
To come: list of important Zephyr driver API macros and features
^ References
Linux driver for KX022A, close relative of KX132 and appears to be authored by Rohm developers: