Difference between revisions of "Zephyr drivers"
m (Add section "Zephyr Kernel Level Device Support") |
m (→^ Zephyr device tree macros: - to obtain value of a given device tree node property) |
||
(57 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | drivers | + | <i><b>Keywords:</b> sensor interrupts : sensor triggers : Zephyr triggers : sensor interrupt support</i> |
+ | |||
+ | <!-- | ||
+ | == [[#top|^]] OVERVIEW == | ||
+ | --> | ||
+ | |||
+ | OVERVIEW | ||
+ | |||
+ | 2022 Q4 notes on Zephyr RTOS drivers, driver frameworks and driver design. Current projects include work to extend KX132-1211 Zephyr out-of-tree driver, plus work to craft an IIS2DH driver which supports in Zephyr RTOS context both I2C and SPI bus connections for this sensor. | ||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | == [[#top|^]] Example Zephyr drivers == | ||
+ | |||
+ | Example apps here offer varying amounts of insight into Zephyr RTOS' framework(s) for device driver development. Of particular interest 2022 Q4 are the ways in which developers use Zephyr device tree macros to obtain pointers to devices, in their mostly C based code, at compile time. Though not fully explained in Zephyr's extensive documentation it appears that device pointers which can be correctly passed to and used with API function [https://docs.zephyrproject.org/latest/kernel/drivers/index.html#c.device_is_ready device_is_ready()] and macro [https://docs.zephyrproject.org/latest/kernel/drivers/index.html#c.DEVICE_DT_GET DEVICE_DT_GET()] involve pairings of macros, which must be expanded prior to these calls in order to compile correctly. | ||
* https://github.com/circuitdojo/pcf85063a | * https://github.com/circuitdojo/pcf85063a | ||
Line 5: | Line 19: | ||
* https://docs.zephyrproject.org/latest/services/smf/index.html | * https://docs.zephyrproject.org/latest/services/smf/index.html | ||
+ | * https://github.com/zephyrproject-rtos/zephyr/tree/main/samples/drivers/spi_bitbang | ||
+ | |||
+ | * https://github.com/zephyrproject-rtos/zephyr/tree/main/samples/drivers/uart/stm32/single_wire | ||
+ | <pre> | ||
+ | ted@localhost1:~/projects-sandbox/workspace-out-of-tree/zephyr$ grep -nr init_res ./* | grep -v reset | grep init_res | ||
+ | ./include/zephyr/device.h:416: unsigned int init_res : 8; | ||
+ | ./include/zephyr/net/dns_resolve.h:459:void dns_init_resolver(void); | ||
+ | ./include/zephyr/net/dns_resolve.h:462:#define dns_init_resolver(...) | ||
+ | ./kernel/device.c:84: dev->state->init_res = rc; | ||
+ | ./kernel/device.c:161: return dev->state->initialized && (dev->state->init_res == 0U); | ||
+ | ./subsys/net/ip/net_core.c:476: dns_init_resolver(); | ||
+ | ./subsys/net/lib/dns/resolve.c:1428:void dns_init_resolver(void) | ||
+ | </pre> | ||
+ | |||
+ | What's going on in LM77 temperature sensor Kconfig file, on line 10? Interesting Kconfig stanza contains $(dt_compat_enabled,lm77): | ||
+ | |||
+ | * https://github.com/zephyrproject-rtos/zephyr/blob/main/drivers/sensor/lm77/Kconfig#L10 | ||
+ | |||
+ | |||
+ | Zephyr sensor API routine `device_is_ready()` is prototyped in [https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/device.h#L710 zephyr/include/zephyr/device.h]. A search for `device_is_ready()` implementation gives: | ||
+ | |||
+ | <pre> | ||
+ | ted@localhost1:~/projects-sandbox/workspace-out-of-tree/zephyr$ grep -nr z_device_is_ready ./* | ||
+ | ./include/zephyr/device.h:782:bool z_device_is_ready(const struct device *dev); | ||
+ | ./include/zephyr/device.h:803: return z_device_is_ready(dev); | ||
+ | ./kernel/device.c:108: if (z_device_is_ready(dev) && (dev->name == name)) { | ||
+ | ./kernel/device.c:114: if (z_device_is_ready(dev) && (strcmp(name, dev->name) == 0)) { | ||
+ | ./kernel/device.c:151:bool z_device_is_ready(const struct device *dev) | ||
+ | </pre> | ||
+ | |||
+ | The body of this API routine is: | ||
+ | <pre> | ||
+ | bool z_device_is_ready(const struct device *dev) | ||
+ | { | ||
+ | /* | ||
+ | * if an invalid device pointer is passed as argument, this call | ||
+ | * reports the `device` as not ready for usage. | ||
+ | */ | ||
+ | if (dev == NULL) { | ||
+ | return false; | ||
+ | } | ||
+ | |||
+ | return dev->state->initialized && (dev->state->init_res == 0U); | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | Having some trouble, however, finding places where `init_res` and `initialized` data members of device state structure get initialized. Looked recursively in both modules and zephyr source tree. Looks like there may be one place where this assignment takes place, in `zephyr/kernel/device.c`: | ||
+ | |||
+ | <pre> | ||
+ | ted@localhost1:~/projects-sandbox/workspace-out-of-tree/zephyr$ grep -nr init_res ./* | grep -v reset | grep init_res | ||
+ | ./include/zephyr/device.h:416: unsigned int init_res : 8; | ||
+ | ./include/zephyr/net/dns_resolve.h:459:void dns_init_resolver(void); | ||
+ | ./include/zephyr/net/dns_resolve.h:462:#define dns_init_resolver(...) | ||
+ | ./kernel/device.c:84: dev->state->init_res = rc; | ||
+ | ./kernel/device.c:161: return dev->state->initialized && (dev->state->init_res == 0U); | ||
+ | ./subsys/net/ip/net_core.c:476: dns_init_resolver(); | ||
+ | ./subsys/net/lib/dns/resolve.c:1428:void dns_init_resolver(void) | ||
+ | </pre> | ||
+ | |||
+ | Routine which iterates over and initializes all devices needing initialization prior to app code starting: | ||
+ | |||
+ | <pre> | ||
+ | 54 void z_sys_init_run_level(int32_t level) | ||
+ | 55 { | ||
+ | 56 static const struct init_entry *levels[] = { | ||
+ | 57 __init_PRE_KERNEL_1_start, | ||
+ | 58 __init_PRE_KERNEL_2_start, | ||
+ | 59 __init_POST_KERNEL_start, | ||
+ | 60 __init_APPLICATION_start, | ||
+ | 61 #ifdef CONFIG_SMP | ||
+ | 62 __init_SMP_start, | ||
+ | 63 #endif | ||
+ | 64 /* End marker */ | ||
+ | 65 __init_end, | ||
+ | 66 }; | ||
+ | 67 const struct init_entry *entry; | ||
+ | 68 | ||
+ | 69 for (entry = levels[level]; entry < levels[level+1]; entry++) { | ||
+ | 70 const struct device *dev = entry->dev; | ||
+ | 71 int rc = entry->init(dev); | ||
+ | 72 | ||
+ | 73 if (dev != NULL) { | ||
+ | 74 /* Mark device initialized. If initialization | ||
+ | 75 * failed, record the error condition. | ||
+ | 76 */ | ||
+ | 77 if (rc != 0) { | ||
+ | 78 if (rc < 0) { | ||
+ | 79 rc = -rc; | ||
+ | 80 } | ||
+ | 81 if (rc > UINT8_MAX) { | ||
+ | 82 rc = UINT8_MAX; | ||
+ | 83 } | ||
+ | 84 dev->state->init_res = rc; | ||
+ | 85 } | ||
+ | 86 dev->state->initialized = true; | ||
+ | 87 } | ||
+ | 88 } | ||
+ | 89 } | ||
+ | </pre> | ||
+ | |||
+ | Sensor init priority: | ||
+ | |||
+ | <pre> | ||
+ | ted@localhost1:~/projects-sandbox/workspace-for-nexus/zephyr$ grep -nr SENSOR_INIT_PRIORITY ./* | grep -v CONFIG_SENSOR | grep SENSOR_INIT_PRIORITY | ||
+ | ./boards/arm/thingy52_nrf52832/board.c:33:#error BOARD_CCS_VDD_PWR_CTRL_INIT_PRIORITY must be lower than SENSOR_INIT_PRIORITY | ||
+ | ./boards/arm/thingy52_nrf52832/Kconfig:15: BOARD_VDD_PWR_CTRL_INIT_PRIORITY, but smaller than SENSOR_INIT_PRIORITY. | ||
+ | ./drivers/sensor/Kconfig:17:config SENSOR_INIT_PRIORITY | ||
+ | </pre> | ||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | === [[#top|^]] device_get_binding API routine === | ||
+ | |||
+ | 2022-1129 New section on Zephyr `device_get_binding()` API, which Ted hopes can serve as a run time, dynamic assigner of device pointers for reintializing hung hardware or late-to-start, indeterminate start time hardware. | ||
+ | |||
+ | As of past four days 2022 Nov 26 through 29 Ted finds that Zephyr has a strong leaning to set up devices at boot time, and have device structures and pointers declared and assigned largely at compile time. The "DT_INST" family macros all seem designed to work only as static declarations, not as part of code which can refer to and assign a pointer to a device at arbitrary firmware run times. Build time errors seem to indicate that these macros expand correctly, as intended, when they appear at a global or file scoped level. That is, a macro such as DEVICE_DT_INST_DEFINE compiles correctly -- if in largely hidden ways -- when it appears outside the scope of any C function. In contrast Zephyr build tools through errors when this and related DT_ macros appear in function bodies. It looks like these macros may not be expanding inside the function bodies. There must be some basic C rules here we don't yet fully grasp . . . | ||
+ | |||
+ | In searching Zephyr 3.2.0 source tree for instances of calls to `device_get_binding()`, Ted looking for way or ways that code builds the [https://docs.zephyrproject.org/latest/kernel/drivers/index.html#c.device_get_binding device name] parameter this API routine expects. There are many use instances of this API routine in Zephyr's `tests` directory. There is one test whose CMakeLists.txt file includes interesting notes: | ||
+ | |||
+ | <pre> | ||
+ | "./tests/kernel/device/CMakeLists.txt" 16L, 660C | ||
+ | </pre> | ||
+ | |||
+ | Content of this file: | ||
+ | |||
+ | <pre> | ||
+ | 1 # SPDX-License-Identifier: Apache-2.0 | ||
+ | 2 | ||
+ | 3 cmake_minimum_required(VERSION 3.20.0) | ||
+ | 4 find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) | ||
+ | 5 project(device) | ||
+ | 6 | ||
+ | 7 FILE(GLOB app_sources src/*.c) | ||
+ | 8 target_sources(app PRIVATE ${app_sources}) | ||
+ | 9 | ||
+ | 10 # device_get_binding() compares pointers first before doing strcmp(). | ||
+ | 11 # However, enabling coverage forces -O0 to disable any compiler | ||
+ | 12 # optimizations. There would be multiple copies of the same string, | ||
+ | 13 # and the code pathing doing pointer comparison would not be tested | ||
+ | 14 # at all. So add this flag to merge string constants such that | ||
+ | 15 # the pointer comparison would be exercised. | ||
+ | 16 zephyr_cc_option_ifdef(CONFIG_COVERAGE -fmerge-constants) | ||
+ | </pre> | ||
+ | |||
+ | Further, there are some Zephyr code samples which also call `device_get_binding()` with a given device name. One of the I2S samples looks promising, it's at [https://github.com/zephyrproject-rtos/zephyr/tree/main/samples/drivers/i2s/litex zephyr/samples/drivers/i2s/litex] where in main.c there's a call to `[https://github.com/zephyrproject-rtos/zephyr/blob/main/samples/drivers/i2s/litex/src/main.c#L37 device_get_binding("i2s_rx")]`. The parameter here is double quoted, so its a hard-coded string. But what does this match? A search for this pattern from the top level Zephyr 3.2.0 source tree dir reveals: | ||
+ | |||
+ | <i> | ||
+ | Grep search results for `i2s_rx`, used in double quotes in call to `device_get_binding()`:<br /> | ||
+ | ( note, blank lines added for clarity about significant search results ) | ||
+ | </i> | ||
+ | |||
+ | <pre> | ||
+ | ted@localhost1:~/projects-sandbox/workspace-out-of-tree/zephyr$ grep -nr i2s_rx ./* | ||
+ | ./boards/riscv/litex_vexriscv/litex_vexriscv.dts:67:&i2s_rx { | ||
+ | ./drivers/interrupt_controller/intc_vexriscv_litex.c:24:#define I2S_RX_IRQ DT_IRQN(DT_NODELABEL(i2s_rx)) | ||
+ | ./drivers/i2s/i2s_litex.h:35:#define I2S_RX_FIFO_ADDR DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), fifo) | ||
+ | ./drivers/i2s/i2s_litex.h:36:#define I2S_RX_FIFO_DEPTH DT_PROP(DT_NODELABEL(i2s_rx), fifo_depth) | ||
+ | |||
+ | ./drivers/i2s/i2s_litex.h:47:#define I2S_BASE_ADDR DT_REG_ADDR(DT_NODELABEL(i2s_rx)) . . . use of DT_REG_ADDR macro - TMH | ||
+ | |||
+ | ./drivers/i2s/i2s_litex.h:48:#define I2S_EV_STATUS_OFFSET (DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), ev_status) \ | ||
+ | ./drivers/i2s/i2s_litex.h:50:#define I2S_EV_PENDING_OFFSET (DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), ev_pending) \ | ||
+ | ./drivers/i2s/i2s_litex.h:52:#define I2S_EV_ENABLE_OFFSET (DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), ev_enable) \ | ||
+ | ./drivers/i2s/i2s_litex.h:54:#define I2S_CONTROL_OFFSET (DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), rx_ctl) \ | ||
+ | ./drivers/i2s/i2s_litex.h:56:#define I2S_STATUS_OFFSET (DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), rx_stat) \ | ||
+ | ./drivers/i2s/i2s_litex.h:58:#define I2S_CONFIG_OFFSET (DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), rx_conf) \ | ||
+ | ./drivers/i2s/i2s_litex.c:635:#if DT_NODE_HAS_STATUS(DT_NODELABEL(i2s_rx), okay) | ||
+ | ./drivers/i2s/i2s_mcux_sai.c:118:static void i2s_rx_stream_disable(const struct device *, | ||
+ | ./drivers/i2s/i2s_mcux_sai.c:179:static void i2s_rx_stream_disable(const struct device *dev, | ||
+ | ./drivers/i2s/i2s_mcux_sai.c:378: i2s_rx_stream_disable(dev, false, false); | ||
+ | ./drivers/i2s/i2s_mcux_sai.c:389: i2s_rx_stream_disable(dev, false, false); | ||
+ | ./drivers/i2s/i2s_mcux_sai.c:402: i2s_rx_stream_disable(dev, | ||
+ | ./drivers/i2s/i2s_mcux_sai.c:421: i2s_rx_stream_disable(dev, true, false); | ||
+ | ./drivers/i2s/i2s_mcux_sai.c:427: i2s_rx_stream_disable(dev, true, true); | ||
+ | ./drivers/i2s/i2s_mcux_sai.c:814:static int i2s_rx_stream_start(const struct device *dev) | ||
+ | ./drivers/i2s/i2s_mcux_sai.c:944: ret = i2s_rx_stream_start(dev); | ||
+ | ./drivers/i2s/i2s_mcux_sai.c:969: i2s_rx_stream_disable(dev, true, true); | ||
+ | ./drivers/i2s/i2s_mcux_sai.c:1006: i2s_rx_stream_disable(dev, true, true); | ||
+ | |||
+ | ./dts/riscv/riscv32-litex-vexriscv.dtsi:211: i2s_rx: i2s_rx@e000a800 { <-- This result looks promising, a dts nodelabel - TMH | ||
+ | |||
+ | ./samples/drivers/i2s/echo/sample.yaml:6: filter: dt_nodelabel_enabled("i2s_rxtx") or | ||
+ | ./samples/drivers/i2s/echo/sample.yaml:7: (dt_nodelabel_enabled("i2s_rx") and dt_nodelabel_enabled("i2s_tx")) | ||
+ | ./samples/drivers/i2s/echo/src/main.c:15:#if DT_NODE_EXISTS(DT_NODELABEL(i2s_rxtx)) | ||
+ | ./samples/drivers/i2s/echo/src/main.c:16:#define I2S_RX_NODE DT_NODELABEL(i2s_rxtx) | ||
+ | ./samples/drivers/i2s/echo/src/main.c:19:#define I2S_RX_NODE DT_NODELABEL(i2s_rx) | ||
+ | . | ||
+ | . | ||
+ | . | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | <!-- comentario --> | ||
== [[#top|^]] Zephyr Kernel Level Device Support == | == [[#top|^]] Zephyr Kernel Level Device Support == | ||
+ | |||
+ | Interesting code excerpt from `zephy/kernel/device.c`, this following routine is the implementation behind a wrapper like function `device_is_ready()`. But this routine, and sensibly so, doesn't say anything about how a given device is initialized. It only looks at two data members of a device instance data structure: | ||
<pre> | <pre> | ||
Line 24: | Line 233: | ||
163 | 163 | ||
</pre> | </pre> | ||
+ | |||
+ | An additional note of import, a majority of Zephyr RTOS `z_` implementation functions are just wrappers to third party, often Hardware Abstraction Layer function definitions. This wrapping of a Zephyr API around specific microcontroller families decouples Zephyr from knowing or needing to know about hardware details of a given target hardware. This decoupling is very important! | ||
+ | |||
+ | On a somewhat unrelated note, a couple of important Zephyr documents, including Zephyr device tree macros used to obtain node identifiers: | ||
+ | |||
+ | * https://docs.zephyrproject.org/latest/build/dts/api-usage.html#node-identifiers | ||
+ | |||
+ | * https://docs.zephyrproject.org/latest/build/dts/dt-vs-kconfig.html Device Tree versus Kconfig, a fairly short read | ||
+ | |||
+ | * https://docs.zephyrproject.org/1.14.0/guides/kconfig/index.html | ||
+ | |||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | == [[#top|^]] Understanding Zephyr SPI API == | ||
+ | |||
+ | To make use of Zephyr API for SPI bus, we must in part understand how each of the three parameters passed to [https://docs.zephyrproject.org/latest/hardware/peripherals/spi.html#c.spi_read spi_read()] and [https://docs.zephyrproject.org/latest/hardware/peripherals/spi.html#c.spi_write spi_write()] are defined. Further, when we have a sensor with which we communicate in our Zephyr based app we need to understand how parts of this sensor's 'config' structure (1) in the firmware relate to the SPI bus to which the sensor is connected. Our sensor driver's code which talks with the sensor via SPI must pass certain SPI bus related pointers to Zephyr's SPI Application Programmers' Interface. | ||
+ | |||
+ | Another important SPI defining resource in Zephyr RTOS is the header file [https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/drivers/spi.h#L379 spi.h#L379]. This file defines the small data structure named `spi_dt_spec`. It gives us the data member names for a SPI bus peripheral, as Zephyr sees them. | ||
+ | |||
+ | 2022-11-25 | ||
+ | |||
+ | The best Zephyr SPI instructions we find so far are in the sample app `spi_bitbang`. The source file main.c there, sets up RX and TX buffers, and some Zephyr driver structures about these. There is an interesting design pattern in Zephyr's SPI API: receive and transmit buffer pairs can be made multiple, in two arrays of these same buffers. The SPI interface function `spi_tranceive()`, about which spi_write() and spi_read() wrap, steps through the count of write buffers and receive buffers it is passed from calling code. In this way, a careful choosing of sets of buffers permits app developers to express more complex SPI communications by defining the buffers with needed lengths, and then call the SPI API of Zephyr RTOS just once. | ||
+ | |||
+ | <i>Section footnotes:</i><br /> | ||
+ | <i> | ||
+ | (1) in Zephyr 2.6.0 some device drivers such as IIS2DH placed bus device pointers in sensor's structure 'data', for mutable sensor data. Newer Zephyr releases, at latest by 3.2.0, IIS2DH driver authors have moved bus device pointers to this sensor's 'config' structure, designed to hold unchanging, compile-time assigned data. | ||
+ | </i> | ||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | == [[#top|^]] Zephyr logging == | ||
+ | |||
+ | Note, in a Zephyr project or an out-of-tree driver, what we have learned here is that LOG_MODULE_REGISTER(project_name) must be called in one -- possibly the first -- of the project's source files. Any other dot c files from which developers need logging capability must include the function like macro LOG_MODULE_DECLARE(). | ||
+ | |||
+ | * https://docs.zephyrproject.org/3.0.0/reference/logging/index.html#logging-in-a-module | ||
+ | * https://docs.zephyrproject.org/3.0.0/reference/logging/index.html#c.LOG_MODULE_DECLARE | ||
+ | |||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | == [[#top|^]] LPC55S69 flexcomm2 and hs_lspi pins conflict == | ||
+ | |||
+ | zephyr/boards/arm/lpcxpresso55s69/lpcxpresso55s69-pinctrl.dtsi lines 21 - 28 | ||
+ | |||
+ | Note that in spite of the comment above in the dot dtsi file from NXP, SPI communications with an IIS2DH sensor work while UART2 is also in use and working - TMH | ||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | == [[#top|^]] STMicro device context data structure == | ||
+ | |||
+ | Symbol `stmdev_ctx_t` is defined in file <b>modules/hal/st/sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.c</b>. On github this file as of 2022-11-18 located at https://github.com/zephyrproject-rtos/hal_st/tree/master/sensor/stmemsc/iis2dh_STdC/driver' | ||
+ | |||
+ | Data structure `stmdev_ctx_t` defined as follows: | ||
+ | |||
+ | <pre> | ||
+ | 104 /** @addtogroup Interfaces_Functions | ||
+ | 105 * @brief This section provide a set of functions used to read and | ||
+ | 106 * write a generic register of the device. | ||
+ | 107 * MANDATORY: return 0 -> no Error. | ||
+ | 108 * @{ | ||
+ | 109 * | ||
+ | 110 */ | ||
+ | 111 | ||
+ | 112 typedef int32_t (*stmdev_write_ptr)(void *, uint8_t, const uint8_t *, uint16_t); | ||
+ | 113 typedef int32_t (*stmdev_read_ptr)(void *, uint8_t, uint8_t *, uint16_t); | ||
+ | 114 | ||
+ | 115 typedef struct | ||
+ | 116 { | ||
+ | 117 /** Component mandatory fields **/ | ||
+ | 118 stmdev_write_ptr write_reg; | ||
+ | 119 stmdev_read_ptr read_reg; | ||
+ | 120 /** Customizable optional pointer **/ | ||
+ | 121 void *handle; | ||
+ | 122 } stmdev_ctx_t; | ||
+ | </pre> | ||
+ | |||
+ | Parameter lists for the register write and register read functions are nearly the same, with exception of the third parameter. In the register write function this parameter is qualified with C `const`, as it is only expected to be read by the function using it. | ||
+ | |||
+ | |||
+ | |||
+ | With the above definition, an instance of stmdev_cts_t is created for each device with device tree compatible property of 'st_iis2dh', and is assigned in `zephyr/drivers/sensor/iis2dh/iis2dh-i2c.c` as follows: | ||
+ | |||
+ | <pre> | ||
+ | 38 stmdev_ctx_t iis2dh_i2c_ctx = { | ||
+ | 39 .read_reg = (stmdev_read_ptr) iis2dh_i2c_read, | ||
+ | 40 .write_reg = (stmdev_write_ptr) iis2dh_i2c_write, | ||
+ | 41 }; | ||
+ | 42 | ||
+ | 43 int iis2dh_i2c_init(const struct device *dev) | ||
+ | 44 { | ||
+ | 45 struct iis2dh_data *data = dev->data; | ||
+ | 46 const struct iis2dh_device_config *config = dev->config; | ||
+ | 47 | ||
+ | 48 if (!device_is_ready(config->i2c.bus)) { | ||
+ | 49 LOG_ERR("Bus device is not ready"); | ||
+ | 50 return -ENODEV; | ||
+ | 51 } | ||
+ | 52 | ||
+ | 53 data->ctx = &iis2dh_i2c_ctx; | ||
+ | 54 data->ctx->handle = (void *)dev; | ||
+ | 55 | ||
+ | 56 return 0; | ||
+ | 57 } | ||
+ | 58 #endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ | ||
+ | </pre> | ||
+ | |||
+ | The above init function is called from iis2dh.c: | ||
+ | |||
+ | <pre> | ||
+ | 232 static int iis2dh_init_interface(const struct device *dev) | ||
+ | 233 { | ||
+ | 234 int res; | ||
+ | 235 | ||
+ | 236 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) | ||
+ | 237 res = iis2dh_spi_init(dev); | ||
+ | 238 if (res) { | ||
+ | 239 return res; | ||
+ | 240 } | ||
+ | 241 #elif DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) | ||
+ | 242 res = iis2dh_i2c_init(dev); | ||
+ | 243 if (res) { | ||
+ | 244 return res; | ||
+ | 245 } | ||
+ | 246 #else | ||
+ | 247 #error "BUS MACRO NOT DEFINED IN DTS" | ||
+ | 248 #endif | ||
+ | 249 | ||
+ | 250 return 0; | ||
+ | 251 } | ||
+ | 252 | ||
+ | 253 static int iis2dh_init(const struct device *dev) | ||
+ | 254 { | ||
+ | 255 struct iis2dh_data *iis2dh = dev->data; | ||
+ | 256 const struct iis2dh_device_config *cfg = dev->config; | ||
+ | 257 uint8_t wai; | ||
+ | 258 | ||
+ | 259 if (iis2dh_init_interface(dev)) { | ||
+ | 260 return -EINVAL; | ||
+ | 261 } | ||
+ | |||
+ | </pre> | ||
+ | |||
+ | |||
+ | |||
+ | Pointer *dev has a member named 'data', which in turn has a member named 'ctx', which in turn gets assigned the address of a not named instance of `iis2dh_i2c_ctx`. This means that we have `dev->data->ctx->read_reg()` and `dev->data->ctx->write_reg` on successful completion of routine iis2dh_i2c_init(). This is reflected in <b>modules/hal/st/sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.c</b>, in the two generalized register read and write functions: | ||
+ | |||
+ | <pre> | ||
+ | 39 /** | ||
+ | 40 * @brief Read generic device register | ||
+ | 41 * | ||
+ | 42 * @param ctx read / write interface definitions(ptr) | ||
+ | 43 * @param reg register to read | ||
+ | 44 * @param data pointer to buffer that store the data read(ptr) | ||
+ | 45 * @param len number of consecutive register to read | ||
+ | 46 * @retval interface status (MANDATORY: return 0 -> no Error) | ||
+ | 47 * | ||
+ | 48 */ | ||
+ | 49 int32_t iis2dh_read_reg(stmdev_ctx_t *ctx, uint8_t reg, uint8_t *data, | ||
+ | 50 uint16_t len) | ||
+ | 51 { | ||
+ | 52 int32_t ret; | ||
+ | 53 | ||
+ | 54 ret = ctx->read_reg(ctx->handle, reg, data, len); | ||
+ | 55 | ||
+ | 56 return ret; | ||
+ | 57 } | ||
+ | 58 | ||
+ | 59 /** | ||
+ | 60 * @brief Write generic device register | ||
+ | 61 * | ||
+ | 62 * @param ctx read / write interface definitions(ptr) | ||
+ | 63 * @param reg register to write | ||
+ | 64 * @param data pointer to data to write in register reg(ptr) | ||
+ | 65 * @param len number of consecutive register to write | ||
+ | 66 * @retval interface status (MANDATORY: return 0 -> no Error) | ||
+ | 67 * | ||
+ | 68 */ | ||
+ | 69 int32_t iis2dh_write_reg(stmdev_ctx_t *ctx, uint8_t reg, | ||
+ | 70 uint8_t *data, | ||
+ | 71 uint16_t len) | ||
+ | 72 { | ||
+ | 73 int32_t ret; | ||
+ | 74 | ||
+ | 75 ret = ctx->write_reg(ctx->handle, reg, data, len); | ||
+ | 76 | ||
+ | 77 return ret; | ||
+ | 78 } | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | . . . | ||
+ | |||
+ | |||
+ | |||
+ | https://docs.zephyrproject.org/latest/hardware/peripherals/i2c.html#c.i2c_burst_read_dt | ||
+ | |||
+ | |||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | == [[#top|^]] Zephyr interrupts == | ||
+ | |||
+ | This sections aims to cover Zephyr interrupts, and some example Zephyr interrupt set up code fragments. | ||
+ | |||
+ | In Zephyr RTOS context, triggers are interrupts which are generated by sensors. Zephyr constructs a notion of "trigger types" and "trigger channels". Some pages from Zephyr 3.2.0 documentation: | ||
+ | <!-- comentario --> | ||
+ | * https://docs.zephyrproject.org/latest/hardware/peripherals/sensor.html#triggers | ||
+ | * https://docs.zephyrproject.org/latest/hardware/peripherals/sensor.html#c.sensor_trigger_type | ||
+ | |||
+ | |||
+ | This section created to help address a challenge we have, setting up a sensor interrupt or Zephyr "trigger" in KX132 driver. While unsure, there is suspicion that the `int_gpio` member of the sensor's `config` data structure may not be getting assigned a GPIO port at all, in an early Zephyr init level at boot time. In a diagnostic, `cfg->int_gpio.port->name` shows as "=&", which does not look like a valid port name. | ||
+ | |||
+ | KX132's config data structure include set up codes this snippet of code: | ||
+ | |||
+ | <pre> | ||
+ | 58 uint8_t pm; | ||
+ | 59 #ifdef CONFIG_KX132_TRIGGER | ||
+ | 60 #warning "KX132 1211 driver - compiling gpio_dt_spec instance in struct 'kx132_device_config'" | ||
+ | 61 // # REF https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/drivers/gpio.h#L271 | ||
+ | 62 struct gpio_dt_spec int_gpio; | ||
+ | 63 #endif /* CONFIG_KX132_TRIGGER */ | ||
+ | 64 }; | ||
+ | </pre> | ||
+ | |||
+ | In a different sample app, iis2dh-driver-demo, we were able to set up a trigger per the code from Zephyr's `samples/basic/button` app. The early declarative elements to prepare to use a GPIO pin on the MCU, as an interrupt input line connected to a sensor, are in this sample: | ||
+ | |||
+ | <pre> | ||
+ | 127 #include <zephyr/drivers/gpio.h> // needed to provide GPIO_DT_SPEC_GET_OR Zephyr device tree macro | ||
+ | 128 | ||
+ | 129 #define SENSOR_INT1_NODE DT_ALIAS(sensorinterrupt1) | ||
+ | 130 #if !DT_NODE_HAS_STATUS(SENSOR_INT1_NODE, okay) | ||
+ | 131 #error "- DEV 1108 - Could not find in device tree source any sensor interrupt type node!" | ||
+ | 132 #endif | ||
+ | 133 //static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0}); | ||
+ | 134 static const struct gpio_dt_spec interrupt_line_1 = GPIO_DT_SPEC_GET_OR(SENSOR_INT1_NODE, gpios, {0}); | ||
+ | 135 | ||
+ | 136 static struct gpio_callback sensor_interrupt_1_cb_data; | ||
+ | </pre> | ||
+ | |||
+ | Device tree overlay expresses the above referenced DTS node alias: | ||
+ | |||
+ | <pre> | ||
+ | 5 / { | ||
+ | 6 aliases { | ||
+ | 9 sensorinterrupt1 = &iis2dhint1; | ||
+ | 11 }; | ||
+ | 12 }; | ||
+ | |||
+ | 65 / { | ||
+ | 66 sensor_interrupts { | ||
+ | 67 compatible = "gpi set up codeo-keys"; | ||
+ | 68 iis2dhint1: iis2dh_int1 { | ||
+ | 69 gpios = < &gpio1 9 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; | ||
+ | 70 }; | ||
+ | 71 /* ... other buttons ... */ | ||
+ | 72 }; | ||
+ | 73 }; | ||
+ | </pre> | ||
+ | set up code | ||
+ | In a driver use case, the interrupt line(s) or pins themselves likely won't require a DTS node alias. These GPIO pins will be expressed as values in the sensor's device tree node. The property will be one of `irq-gpios`, `drdy-gpios` and similar, depending on the given sensor's device tree bindings file. Of course, the port controlling the given pins must somewhere in device tree or DTS overlay files be enabled. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | Zephyr interrupt set up code from button sample app . . . | ||
+ | |||
+ | <pre> | ||
+ | MODULE_ID__THREAD_IIS2DH, sensor->state->init_res); | ||
+ | |||
+ | |||
+ | |||
+ | // 'button' from samples/basic/button becomes 'interrupt_line_1': | ||
+ | |||
+ | // STEP config step | ||
+ | if (!device_is_ready(interrupt_line_1.port)) { | ||
+ | printk("Error: sensor interrupt 1 port '%s' detected as not ready\n", | ||
+ | interrupt_line_1.port->name); | ||
+ | // return; | ||
+ | } | ||
+ | |||
+ | // STEP config step | ||
+ | rstatus = gpio_pin_configure_dt(&interrupt_line_1, GPIO_INPUT); | ||
+ | if (rstatus != 0) { | ||
+ | printk("Error %d: failed to configure %s pin %d\n", | ||
+ | rstatus, interrupt_line_1.port->name, interrupt_line_1.pin); | ||
+ | // return; | ||
+ | } | ||
+ | |||
+ | // STEP config step | ||
+ | rstatus = gpio_pin_interrupt_configure_dt(&interrupt_line_1, | ||
+ | GPIO_INT_EDGE_TO_ACTIVE); | ||
+ | if (rstatus != 0) { | ||
+ | printk("Error %d: failed to configure interrupt on %s pin %d\n", | ||
+ | rstatus, interrupt_line_1.port->name, interrupt_line_1.pin); | ||
+ | // return; | ||
+ | } | ||
+ | |||
+ | // STEP config step | ||
+ | // gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin)); | ||
+ | // gpio_add_callback(button.port, &button_cb_data); | ||
+ | // printk("Set up button at %s pin %d\n", button.port->name, button.pin); | ||
+ | |||
+ | gpio_init_callback(&sensor_interrupt_1_cb_data, sensor_interrupt_1_cb, BIT(interrupt_line_1.pin)); | ||
+ | gpio_add_callback(interrupt_line_1.port, &sensor_interrupt_1_cb_data); | ||
+ | printk("Set up button at %s pin %d\n", interrupt_line_1.port->name, interrupt_line_1.pin); | ||
+ | |||
+ | </pre> | ||
+ | |||
+ | Interestingly this demo code gives the following port name after the GPIO based interrupt is successfully configured: | ||
+ | |||
+ | <pre> | ||
+ | *** Booting Zephyr OS build zephyr-v3.2.0 *** | ||
+ | - scoreboard - in setter function test val holds 0, | ||
+ | - scoreboard - setting test val to 5, | ||
+ | Success in init of KX132! | ||
+ | - DEV 1028 - symbol ST_IIS2DH got assigned 'DT_N_S_soc_S_peripheral_50000000_S_spi_9f000_S_iis2dh_0' | ||
+ | - kd_thread_iis2dh - Success finding iis2dh device, | ||
+ | - kd_thread_iis2dh - sensor device 'iis2dh@0' is ready, | ||
+ | - kd_thread_iis2dh - INFO: device set up code set up codename (Zephyr compile time setting) found to be 'iis2dh@0' | ||
+ | - kd_thread_iis2dh - INFO: boot time Zephyr init result value holds 0, | ||
+ | INFO: sensor interrupt 1 port 'gpio@1' detected as ready! | ||
+ | Set up button at gpio@1 pin 9 | ||
+ | </pre> | ||
+ | |||
+ | Name `gpio@1` is definitely not equivalent or like `=&`. So QUESTION: where in the run time process, and in the source code of a given out-of-tree driver, is a valid port controller assigned to `const struct device *dev->cfg->int_gpio.port`? | ||
+ | |||
+ | In Jared Wolff's sgp40 driver there's explicit code to assign values to sensor data structure members which support an enable line. Line of interest is [https://github.com/circuitdojo/air-quality-wing-zephyr-drivers/blob/main/drivers/sgp40/sgp40.c#L153 sgp40.c line 153]. In this case the line communication is an enable line, not an interrupt, but the needed set up should be equivalent. | ||
+ | |||
+ | QUESTION: where and how is STMicro IIS2DH driver achieving the same set up for its "data ready", drdy gpio? | ||
+ | |||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | <ul> | ||
+ | === [[#top|^]] QUESTION sensor interrupts configured in drivers at boot time? === | ||
+ | |||
+ | From 2022 Q4, it looks like Zephyr sensor drivers, a couple at least, may not handle or set up SPI chip select lines. This so in spite of there being a way to express chip select lines in device tree overlays. From other work in 2022 Q4 it looks further like certain Zephyr out-of-tree drivers may also not provide for setting up sensor interrupts at boot time. In particular, contributor Ted has so far found few to no example code or developer forum posts on how to configure STMicro sensor "data ready" interrupts in or using the driver provided by STMicro. | ||
+ | |||
+ | It is possible to manually set up a GPIO port and pin to serve as a physical interrupt line coming into the MCU. This can be done by borrowing code from [https://github.com/zephyrproject-rtos/zephyr/tree/main/samples/basic/buttonZephyr 3.2.0 button sample app]. But is this always necessary when enabling a hardware interrupt line support from a sensor, in a Zephyr based app? | ||
+ | |||
+ | So far still in the research and debugging stage on this question. Some references to Zephyr triggers include: | ||
+ | |||
+ | <ul> | ||
+ | * [[#ref--zephyr-interrupts--zephyr-s-issues-s-30133|local ref #1 - zephyrproject-rtos/zephyr/issues/30133]] | ||
+ | |||
+ | Zephyr sensor trigger structure defined here: | ||
+ | |||
+ | * https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/drivers/sensor.h#L264 | ||
+ | </ul> | ||
+ | |||
+ | |||
+ | <!-- comentario --> | ||
+ | </ul> | ||
+ | |||
+ | == [[#top|^]] C Preprocessor macros == | ||
+ | |||
+ | * https://stackoverflow.com/questions/1489932/how-can-i-concatenate-twice-with-the-c-preprocessor-and-expand-a-macro-as-in-ar | ||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | == [[#top|^]] Zephyr device tree macros == | ||
+ | |||
+ | Section on Zephyr device tree macros, many defined in devicetree.h. Also worth noting is [https://docs.zephyrproject.org/3.2.0/build/dts/howtos.html#device-drivers-that-depend-on-other-devices Zephyr 3.2.0 notes on device drivers that depend on other devices]. | ||
+ | |||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | === [[#top|^]] device drivers two ways === | ||
+ | |||
+ | Zephyr documentation states that, using [https://docs.zephyrproject.org/3.2.0/build/dts/howtos.html#write-device-drivers-using-devicetree-apis devicetree API] there are two way to write device drivers. These ways are (1) by using instance numbers and (2) by using nodelabels. Links to both these device driver topics: | ||
+ | |||
+ | * https://docs.zephyrproject.org/3.2.0/build/dts/howtos.html#option-1-create-devices-using-instance-numbers | ||
+ | |||
+ | * https://docs.zephyrproject.org/3.2.0/build/dts/howtos.html#option-2-create-devices-using-node-labels | ||
+ | |||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | === [[#top|^]] Devicetree guide links === | ||
+ | |||
+ | Some important Zephyr device tree macro and use documentation links: | ||
+ | |||
+ | * https://docs.zephyrproject.org/latest/build/dts/api-usage.html#property-access | ||
+ | |||
+ | * https://docs.zephyrproject.org/latest/build/dts/api-usage.html#interrupts-properties | ||
+ | |||
+ | * https://docs.zephyrproject.org/latest/build/dts/api-usage.html#phandle-properties | ||
+ | |||
+ | 2022-11-30 | ||
+ | |||
+ | Macros DT_COMPAT_GET_ANY_STATUS_OK and DT_NODE_PATH | ||
+ | |||
+ | * https://docs.zephyrproject.org/latest/build/dts/api/api.html#c.DT_COMPAT_GET_ANY_STATUS_OKAY | ||
+ | |||
+ | * https://docs.zephyrproject.org/latest/build/dts/api/api.html#c.DT_NODE_PATH | ||
+ | |||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | === [[#top|^]] some macro definitions === | ||
+ | |||
+ | <i>Definition of DT_DRV_INST</i> | ||
+ | |||
+ | <pre> | ||
+ | 2903 /** | ||
+ | 2904 * @defgroup devicetree-inst Instance-based devicetree APIs | ||
+ | 2905 * @ingroup devicetree | ||
+ | 2906 * @{ | ||
+ | 2907 */ | ||
+ | 2908 | ||
+ | 2909 /** | ||
+ | 2910 * @brief Node identifier for an instance of a `DT_DRV_COMPAT` compatible | ||
+ | 2911 * @param inst instance number | ||
+ | 2912 * @return a node identifier for the node with `DT_DRV_COMPAT` compatible and | ||
+ | 2913 * instance number @p inst | ||
+ | 2914 */ | ||
+ | 2915 #define DT_DRV_INST(inst) DT_INST(inst, DT_DRV_COMPAT) | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | <i>Definition of DT_INST</i> | ||
+ | |||
+ | <pre> | ||
+ | 232 /** | ||
+ | 233 * @brief Get a node identifier for an instance of a compatible | ||
+ | 234 * | ||
+ | 235 * All nodes with a particular compatible property value are assigned | ||
+ | 236 * instance numbers, which are zero-based indexes specific to that | ||
+ | 237 * compatible. You can get a node identifier for these nodes by | ||
+ | 238 * passing DT_INST() an instance number, @p inst, along with the | ||
+ | 239 * lowercase-and-underscores version of the compatible, @p compat. | ||
+ | 240 * | ||
+ | 241 * Instance numbers have the following properties: | ||
+ | 242 * | ||
+ | 243 * - for each compatible, instance numbers start at 0 and are contiguous | ||
+ | 244 * - exactly one instance number is assigned for each node with a compatible, | ||
+ | 245 * **including disabled nodes** | ||
+ | 246 * - enabled nodes (status property is `okay` or missing) are assigned the | ||
+ | 247 * instance numbers starting from 0, and disabled nodes have instance | ||
+ | 248 * numbers which are greater than those of any enabled node | ||
+ | 249 * | ||
+ | 250 * No other guarantees are made. In particular: | ||
+ | 251 * | ||
+ | 252 * - instance numbers **in no way reflect** any numbering scheme that | ||
+ | 253 * might exist in SoC documentation, node labels or unit addresses, | ||
+ | 254 * or properties of the /aliases node (use DT_NODELABEL() or DT_ALIAS() | ||
+ | 255 * for those) | ||
+ | 256 * - there **is no general guarantee** that the same node will have | ||
+ | 257 * the same instance number between builds, even if you are building | ||
+ | 258 * the same application again in the same build directory | ||
+ | 259 * | ||
+ | 260 * Example devicetree fragment: | ||
+ | 261 * | ||
+ | 262 * @code{.dts} | ||
+ | 263 * serial1: serial@40001000 { | ||
+ | 264 * compatible = "vnd,soc-serial"; | ||
+ | 265 * status = "disabled"; | ||
+ | 266 * current-speed = <9600>; | ||
+ | 267 * ... | ||
+ | 268 * }; | ||
+ | 269 * | ||
+ | 270 * serial2: serial@40002000 { | ||
+ | 271 * compatible = "vnd,soc-serial"; | ||
+ | 272 * status = "okay"; | ||
+ | 273 * current-speed = <57600>; | ||
+ | 274 * ... | ||
+ | 275 * }; | ||
+ | 276 * | ||
+ | 277 * serial3: serial@40003000 { | ||
+ | 278 * compatible = "vnd,soc-serial"; | ||
+ | 279 * current-speed = <115200>; | ||
+ | 280 * ... | ||
+ | 281 * }; | ||
+ | |||
+ | 282 * @endcode | ||
+ | 283 * | ||
+ | 284 * Assuming no other nodes in the devicetree have compatible | ||
+ | 285 * `"vnd,soc-serial"`, that compatible has nodes with instance numbers | ||
+ | 286 * 0, 1, and 2. | ||
+ | 287 * | ||
+ | 288 * The nodes `serial@40002000` and `serial@40003000` are both enabled, so | ||
+ | 289 * their instance numbers are 0 and 1, but no guarantees are made | ||
+ | 290 * regarding which node has which instance number. | ||
+ | 291 * | ||
+ | 292 * Since `serial@40001000` is the only disabled node, it has instance | ||
+ | 293 * number 2, since disabled nodes are assigned the largest instance | ||
+ | 294 * numbers. Therefore: | ||
+ | 295 * | ||
+ | 296 * @code{.c} | ||
+ | 297 * // Could be 57600 or 115200. There is no way to be sure: | ||
+ | 298 * // either serial@40002000 or serial@40003000 could | ||
+ | 299 * // have instance number 0, so this could be the current-speed | ||
+ | 300 * // property of either of those nodes. | ||
+ | 301 * DT_PROP(DT_INST(0, vnd_soc_serial), current_speed) | ||
+ | 302 * | ||
+ | 303 * // Could be 57600 or 115200, for the same reason. | ||
+ | 304 * // If the above expression expands to 57600, then | ||
+ | 305 * // this expands to 115200, and vice-versa. | ||
+ | 306 * DT_PROP(DT_INST(1, vnd_soc_serial), current_speed) | ||
+ | 307 * | ||
+ | 308 * // 9600, because there is only one disabled node, and | ||
+ | 309 * // disabled nodes are "at the the end" of the instance | ||
+ | 310 * // number "list". | ||
+ | 311 * DT_PROP(DT_INST(2, vnd_soc_serial), current_speed) | ||
+ | 312 * @endcode | ||
+ | 313 * | ||
+ | 314 * Notice how `"vnd,soc-serial"` in the devicetree becomes `vnd_soc_serial` | ||
+ | 315 * (without quotes) in the DT_INST() arguments. (As usual, `current-speed` | ||
+ | 316 * in the devicetree becomes `current_speed` as well.) | ||
+ | 317 * | ||
+ | 318 * Nodes whose `compatible` property has multiple values are assigned | ||
+ | 319 * independent instance numbers for each compatible. | ||
+ | 320 * | ||
+ | 321 * @param inst instance number for compatible @p compat | ||
+ | 322 * @param compat lowercase-and-underscores compatible, without quotes | ||
+ | 323 * @return node identifier for the node with that instance number and | ||
+ | 324 * compatible | ||
+ | 325 */ | ||
+ | 326 #define DT_INST(inst, compat) UTIL_CAT(DT_N_INST, DT_DASH(inst, compat)) | ||
+ | </pre> | ||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | From Zephyr 3.2.0 `zephyr/devicetree/spi.h`: | ||
+ | |||
+ | <pre> | ||
+ | 202 * spi1: spi@... { | ||
+ | 203 * compatible = "vnd,spi"; | ||
+ | 204 * cs-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>, | ||
+ | 205 * <&gpio2 20 GPIO_ACTIVE_LOW>; | ||
+ | 206 * | ||
+ | 207 * a: spi-dev-a@0 { | ||
+ | 208 * reg = <0>; | ||
+ | 209 * }; | ||
+ | 210 * | ||
+ | 211 * b: spi-dev-b@1 { | ||
+ | 212 * reg = <1>; | ||
+ | 213 * }; | ||
+ | 214 * }; | ||
+ | 215 * | ||
+ | 216 * Example usage: | ||
+ | 217 * | ||
+ | 218 * DT_SPI_DEV_CS_GPIOS_PIN(DT_NODELABEL(a)) // 10 | ||
+ | 219 * DT_SPI_DEV_CS_GPIOS_PIN(DT_NODELABEL(b)) // 20 | ||
+ | </pre> | ||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | === [[#top|^]] list of all DT macros === | ||
+ | |||
+ | <pre> | ||
+ | ted@localhost1:~/projects-sandbox/workspace-out-of-tree/zephyr/include/zephyr$ grep -n DT_ devicetree.h | grep define | ||
+ | 77:#define DT_INVALID_NODE _ | ||
+ | 82:#define DT_ROOT DT_N | ||
+ | 134:#define DT_PATH(...) DT_PATH_INTERNAL(__VA_ARGS__) | ||
+ | 190:#define DT_NODELABEL(label) DT_CAT(DT_N_NODELABEL_, label) | ||
+ | 230:#define DT_ALIAS(alias) DT_CAT(DT_N_ALIAS_, alias) | ||
+ | 326:#define DT_INST(inst, compat) UTIL_CAT(DT_N_INST, DT_DASH(inst, compat)) | ||
+ | 351:#define DT_PARENT(node_id) UTIL_CAT(node_id, _PARENT) | ||
+ | 358:#define DT_INST_PARENT(inst) DT_PARENT(DT_DRV_INST(inst)) | ||
+ | 383:#define DT_GPARENT(node_id) DT_PARENT(DT_PARENT(node_id)) | ||
+ | 406: * #define SOC_NODE DT_NODELABEL(soc_label) | ||
+ | 420:#define DT_CHILD(node_id, child) UTIL_CAT(node_id, DT_S_PREFIX(child)) | ||
+ | 463:#define DT_COMPAT_GET_ANY_STATUS_OKAY(compat) \ | ||
+ | 495:#define DT_NODE_PATH(node_id) DT_CAT(node_id, _PATH) | ||
+ | 521:#define DT_NODE_FULL_NAME(node_id) DT_CAT(node_id, _FULL_NAME) | ||
+ | 549:#define DT_NODE_CHILD_IDX(node_id) DT_CAT(node_id, _CHILD_IDX) | ||
+ | 571:#define DT_SAME_NODE(node_id1, node_id2) \ | ||
+ | 614:#define DT_PROP(node_id, prop) DT_CAT3(node_id, _P_, prop) | ||
+ | 644:#define DT_PROP_LEN(node_id, prop) DT_CAT4(node_id, _P_, prop, _LEN) | ||
+ | 649: * If the property is defined (as determined by DT_NODE_HAS_PROP()), | ||
+ | 660:#define DT_PROP_LEN_OR(node_id, prop, default_value) \ | ||
+ | 684:#define DT_PROP_HAS_IDX(node_id, prop, idx) \ | ||
+ | 719:#define DT_PROP_HAS_NAME(node_id, prop, name) \ | ||
+ | 744:#define DT_PROP_BY_IDX(node_id, prop, idx) \ | ||
+ | 760:#define DT_PROP_OR(node_id, prop, default_value) \ | ||
+ | 773:#define DT_LABEL(node_id) DT_PROP(node_id, label) __DEPRECATED_MACRO | ||
+ | 815:#define DT_ENUM_IDX(node_id, prop) DT_CAT4(node_id, _P_, prop, _ENUM_IDX) | ||
+ | 831:#define DT_ENUM_IDX_OR(node_id, prop, default_idx_value) \ | ||
+ | 894:#define DT_STRING_TOKEN(node_id, prop) \ | ||
+ | 910:#define DT_STRING_TOKEN_OR(node_id, prop, default_value) \ | ||
+ | 971:#define DT_STRING_UPPER_TOKEN(node_id, prop) \ | ||
+ | 988:#define DT_STRING_UPPER_TOKEN_OR(node_id, prop, default_value) \ | ||
+ | 1039:#define DT_STRING_TOKEN_BY_IDX(node_id, prop, idx) \ | ||
+ | 1089:#define DT_STRING_UPPER_TOKEN_BY_IDX(node_id, prop, idx) \ | ||
+ | 1131: * #define N1 DT_NODELABEL(n1) | ||
+ | 1145:#define DT_PROP_BY_PHANDLE_IDX(node_id, phs, idx, prop) \ | ||
+ | 1167:#define DT_PROP_BY_PHANDLE_IDX_OR(node_id, phs, idx, prop, default_value) \ | ||
+ | 1181:#define DT_PROP_BY_PHANDLE(node_id, ph, prop) \ | ||
+ | 1225: * #define LED DT_NODELABEL(led) | ||
+ | 1238:#define DT_PHA_BY_IDX(node_id, pha, idx, cell) \ | ||
+ | 1252: * defined, so it's safe to use DT_PROP_OR() here, because that uses an | ||
+ | 1264:#define DT_PHA_BY_IDX_OR(node_id, pha, idx, cell, default_value) \ | ||
+ | 1274:#define DT_PHA(node_id, pha, cell) DT_PHA_BY_IDX(node_id, pha, 0, cell) | ||
+ | 1290:#define DT_PHA_OR(node_id, pha, cell, default_value) \ | ||
+ | 1333:#define DT_PHA_BY_NAME(node_id, pha, name, cell) \ | ||
+ | 1346: * defined, so it's safe to use DT_PROP_OR() here, because that uses an | ||
+ | 1357:#define DT_PHA_BY_NAME_OR(node_id, pha, name, cell, default_value) \ | ||
+ | 1393: * #define NODE DT_NODELABEL(n) | ||
+ | 1407:#define DT_PHANDLE_BY_NAME(node_id, pha, name) \ | ||
+ | 1439: * #define N1 DT_NODELABEL(n1) | ||
+ | 1459:#define DT_PHANDLE_BY_IDX(node_id, prop, idx) \ | ||
+ | 1473:#define DT_PHANDLE(node_id, prop) DT_PHANDLE_BY_IDX(node_id, prop, 0) | ||
+ | 1521:#define DT_NUM_RANGES(node_id) DT_CAT(node_id, _RANGES_NUM) | ||
+ | 1575:#define DT_RANGES_HAS_IDX(node_id, idx) \ | ||
+ | 1630:#define DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(node_id, idx) \ | ||
+ | 1670:#define DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(node_id, idx) \ | ||
+ | 1719:#define DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(node_id, idx) \ | ||
+ | 1768:#define DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(node_id, idx) \ | ||
+ | 1817:#define DT_RANGES_LENGTH_BY_IDX(node_id, idx) \ | ||
+ | 1841: * #define RANGE_LENGTH(node_id, idx) DT_RANGES_LENGTH_BY_IDX(node_id, idx), | ||
+ | 1859:#define DT_FOREACH_RANGE(node_id, fn) \ | ||
+ | 1907:#define DT_NODE_VENDOR_BY_IDX(node_id, idx) \ | ||
+ | 1922:#define DT_NODE_VENDOR_HAS_IDX(node_id, idx) \ | ||
+ | 1939:#define DT_NODE_VENDOR_BY_IDX_OR(node_id, idx, default_value) \ | ||
+ | 1951:#define DT_NODE_VENDOR_OR(node_id, default_value) \ | ||
+ | 1971:#define DT_NUM_REGS(node_id) DT_CAT(node_id, _REG_NUM) | ||
+ | 1984:#define DT_REG_HAS_IDX(node_id, idx) \ | ||
+ | 1993:#define DT_REG_ADDR_BY_IDX(node_id, idx) \ | ||
+ | 2007:#define DT_REG_SIZE_BY_IDX(node_id, idx) \ | ||
+ | 2017:#define DT_REG_ADDR(node_id) DT_REG_ADDR_BY_IDX(node_id, 0) | ||
+ | 2026:#define DT_REG_SIZE(node_id) DT_REG_SIZE_BY_IDX(node_id, 0) | ||
+ | 2034:#define DT_REG_ADDR_BY_NAME(node_id, name) \ | ||
+ | 2043:#define DT_REG_SIZE_BY_NAME(node_id, name) \ | ||
+ | 2064:#define DT_NUM_IRQS(node_id) DT_CAT(node_id, _IRQ_NUM) | ||
+ | 2076:#define DT_IRQ_HAS_IDX(node_id, idx) \ | ||
+ | 2089:#define DT_IRQ_HAS_CELL_AT_IDX(node_id, idx, cell) \ | ||
+ | 2099:#define DT_IRQ_HAS_CELL(node_id, cell) DT_IRQ_HAS_CELL_AT_IDX(node_id, 0, cell) | ||
+ | 2110:#define DT_IRQ_HAS_NAME(node_id, name) \ | ||
+ | 2134: * #define SERIAL DT_NODELABEL(my_serial) | ||
+ | 2148:#define DT_IRQ_BY_IDX(node_id, idx, cell) \ | ||
+ | 2166:#define DT_IRQ_BY_NAME(node_id, name, cell) \ | ||
+ | 2176:#define DT_IRQ(node_id, cell) DT_IRQ_BY_IDX(node_id, 0, cell) | ||
+ | 2188:#define DT_IRQN(node_id) DT_IRQ(node_id, irq) | ||
+ | 2208:#define DT_CHOSEN(prop) DT_CAT(DT_CHOSEN_, prop) | ||
+ | 2216:#define DT_HAS_CHOSEN(prop) IS_ENABLED(DT_CAT3(DT_CHOSEN_, prop, _EXISTS)) | ||
+ | 2237:#define DT_FOREACH_NODE(fn) DT_FOREACH_HELPER(fn) | ||
+ | 2250:#define DT_FOREACH_STATUS_OKAY_NODE(fn) DT_FOREACH_OKAY_HELPER(fn) | ||
+ | 2277: * #define FOOBAR_AND_COMMA(node_id) DT_PROP(node_id, foobar), | ||
+ | 2295:#define DT_FOREACH_CHILD(node_id, fn) \ | ||
+ | 2338:#define DT_FOREACH_CHILD_SEP(node_id, fn, sep) \ | ||
+ | 2356:#define DT_FOREACH_CHILD_VARGS(node_id, fn, ...) \ | ||
+ | 2374:#define DT_FOREACH_CHILD_SEP_VARGS(node_id, fn, sep, ...) \ | ||
+ | 2392:#define DT_FOREACH_CHILD_STATUS_OKAY(node_id, fn) \ | ||
+ | 2411:#define DT_FOREACH_CHILD_STATUS_OKAY_SEP(node_id, fn, sep) \ | ||
+ | 2433:#define DT_FOREACH_CHILD_STATUS_OKAY_VARGS(node_id, fn, ...) \ | ||
+ | 2454:#define DT_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS(node_id, fn, sep, ...) \ | ||
+ | 2507:#define DT_FOREACH_PROP_ELEM(node_id, prop, fn) \ | ||
+ | 2527:#define DT_FOREACH_PROP_ELEM_VARGS(node_id, prop, fn, ...) \ | ||
+ | 2583:#define DT_FOREACH_STATUS_OKAY(compat, fn) \ | ||
+ | 2613: * #define MY_FN(node_id, operator) DT_PROP(node_id, val) operator | ||
+ | 2632:#define DT_FOREACH_STATUS_OKAY_VARGS(compat, fn, ...) \ | ||
+ | 2662:#define DT_NODE_EXISTS(node_id) IS_ENABLED(DT_CAT(node_id, _EXISTS)) | ||
+ | 2685:#define DT_NODE_HAS_STATUS(node_id, status) \ | ||
+ | 2707:#define DT_HAS_COMPAT_STATUS_OKAY(compat) \ | ||
+ | 2716:#define DT_NUM_INST_STATUS_OKAY(compat) \ | ||
+ | 2747:#define DT_NODE_HAS_COMPAT(node_id, compat) \ | ||
+ | 2764:#define DT_NODE_HAS_COMPAT_STATUS(node_id, compat, status) \ | ||
+ | 2780:#define DT_NODE_HAS_PROP(node_id, prop) \ | ||
+ | 2800:#define DT_PHA_HAS_CELL_AT_IDX(node_id, pha, idx, cell) \ | ||
+ | 2813:#define DT_PHA_HAS_CELL(node_id, pha, cell) \ | ||
+ | 2857:#define DT_BUS(node_id) DT_CAT(node_id, _BUS) | ||
+ | 2867:#define DT_BUS_LABEL(node_id) DT_PROP(DT_BUS(node_id), label) __DEPRECATED_MACRO | ||
+ | 2897:#define DT_ON_BUS(node_id, bus) IS_ENABLED(DT_CAT3(node_id, _BUS_, bus)) | ||
+ | 2915:#define DT_DRV_INST(inst) DT_INST(inst, DT_DRV_COMPAT) | ||
+ | 2926:#define DT_INST_CHILD(inst, child) \ | ||
+ | 2943:#define DT_INST_FOREACH_CHILD(inst, fn) \ | ||
+ | 2959:#define DT_INST_FOREACH_CHILD_SEP(inst, fn, sep) \ | ||
+ | 2977:#define DT_INST_FOREACH_CHILD_VARGS(inst, fn, ...) \ | ||
+ | 2994:#define DT_INST_FOREACH_CHILD_SEP_VARGS(inst, fn, sep, ...) \ | ||
+ | 3008:#define DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, fn) \ | ||
+ | 3025:#define DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(inst, fn, sep) \ | ||
+ | 3041:#define DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(inst, fn, ...) \ | ||
+ | 3059:#define DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS(inst, fn, sep, ...) \ | ||
+ | 3068:#define DT_INST_ENUM_IDX(inst, prop) \ | ||
+ | 3079:#define DT_INST_ENUM_IDX_OR(inst, prop, default_idx_value) \ | ||
+ | 3088:#define DT_INST_PROP(inst, prop) DT_PROP(DT_DRV_INST(inst), prop) | ||
+ | 3096:#define DT_INST_PROP_LEN(inst, prop) DT_PROP_LEN(DT_DRV_INST(inst), prop) | ||
+ | 3107:#define DT_INST_PROP_HAS_IDX(inst, prop, idx) \ | ||
+ | 3118:#define DT_INST_PROP_HAS_NAME(inst, prop, name) \ | ||
+ | 3128:#define DT_INST_PROP_BY_IDX(inst, prop, idx) \ | ||
+ | 3138:#define DT_INST_PROP_OR(inst, prop, default_value) \ | ||
+ | 3147:#define DT_INST_LABEL(inst) DT_INST_PROP(inst, label) __DEPRECATED_MACRO | ||
+ | 3158:#define DT_INST_STRING_TOKEN(inst, prop) \ | ||
+ | 3168:#define DT_INST_STRING_UPPER_TOKEN(inst, prop) \ | ||
+ | 3178:#define DT_INST_STRING_TOKEN_BY_IDX(inst, prop, idx) \ | ||
+ | 3188:#define DT_INST_STRING_UPPER_TOKEN_BY_IDX(inst, prop, idx) \ | ||
+ | 3199:#define DT_INST_PROP_BY_PHANDLE(inst, ph, prop) \ | ||
+ | 3213:#define DT_INST_PROP_BY_PHANDLE_IDX(inst, phs, idx, prop) \ | ||
+ | 3224:#define DT_INST_PHA_BY_IDX(inst, pha, idx, cell) \ | ||
+ | 3236:#define DT_INST_PHA_BY_IDX_OR(inst, pha, idx, cell, default_value) \ | ||
+ | 3247:#define DT_INST_PHA(inst, pha, cell) DT_INST_PHA_BY_IDX(inst, pha, 0, cell) | ||
+ | 3257:#define DT_INST_PHA_OR(inst, pha, cell, default_value) \ | ||
+ | 3269:#define DT_INST_PHA_BY_NAME(inst, pha, name, cell) \ | ||
+ | 3281:#define DT_INST_PHA_BY_NAME_OR(inst, pha, name, cell, default_value) \ | ||
+ | 3292:#define DT_INST_PHANDLE_BY_NAME(inst, pha, name) \ | ||
+ | 3304:#define DT_INST_PHANDLE_BY_IDX(inst, prop, idx) \ | ||
+ | 3315:#define DT_INST_PHANDLE(inst, prop) DT_INST_PHANDLE_BY_IDX(inst, prop, 0) | ||
+ | 3324:#define DT_INST_REG_HAS_IDX(inst, idx) DT_REG_HAS_IDX(DT_DRV_INST(inst), idx) | ||
+ | 3332:#define DT_INST_REG_ADDR_BY_IDX(inst, idx) DT_REG_ADDR_BY_IDX(DT_DRV_INST(inst), idx) | ||
+ | 3340:#define DT_INST_REG_SIZE_BY_IDX(inst, idx) \ | ||
+ | 3349:#define DT_INST_REG_ADDR_BY_NAME(inst, name) \ | ||
+ | 3358:#define DT_INST_REG_SIZE_BY_NAME(inst, name) \ | ||
+ | 3366:#define DT_INST_REG_ADDR(inst) DT_INST_REG_ADDR_BY_IDX(inst, 0) | ||
+ | 3373:#define DT_INST_REG_SIZE(inst) DT_INST_REG_SIZE_BY_IDX(inst, 0) | ||
+ | 3382:#define DT_INST_IRQ_BY_IDX(inst, idx, cell) \ | ||
+ | 3392:#define DT_INST_IRQ_BY_NAME(inst, name, cell) \ | ||
+ | 3401:#define DT_INST_IRQ(inst, cell) DT_INST_IRQ_BY_IDX(inst, 0, cell) | ||
+ | 3408:#define DT_INST_IRQN(inst) DT_INST_IRQ(inst, irq) | ||
+ | 3415:#define DT_INST_BUS(inst) DT_BUS(DT_DRV_INST(inst)) | ||
+ | 3425:#define DT_INST_BUS_LABEL(inst) DT_BUS_LABEL(DT_DRV_INST(inst)) __DEPRECATED_MACRO | ||
+ | 3434:#define DT_INST_ON_BUS(inst, bus) DT_ON_BUS(DT_DRV_INST(inst), bus) | ||
+ | 3445:#define DT_INST_STRING_TOKEN_OR(inst, name, default_value) \ | ||
+ | 3456:#define DT_INST_STRING_UPPER_TOKEN_OR(inst, name, default_value) \ | ||
+ | 3482: * #define DT_DRV_COMPAT vnd_some_sensor | ||
+ | 3491:#define DT_ANY_INST_ON_BUS_STATUS_OKAY(bus) \ | ||
+ | 3526: * #define DT_DRV_COMPAT vnd_device | ||
+ | 3527: * #define MY_FN(inst) DT_INST_PROP(inst, foobar), | ||
+ | 3559:#define DT_INST_FOREACH_STATUS_OKAY(fn) \ | ||
+ | 3576:#define DT_INST_FOREACH_STATUS_OKAY_VARGS(fn, ...) \ | ||
+ | 3592:#define DT_INST_FOREACH_PROP_ELEM(inst, prop, fn) \ | ||
+ | 3609:#define DT_INST_FOREACH_PROP_ELEM_VARGS(inst, prop, fn, ...) \ | ||
+ | 3618:#define DT_INST_NODE_HAS_PROP(inst, prop) \ | ||
+ | 3631:#define DT_INST_PHA_HAS_CELL_AT_IDX(inst, pha, idx, cell) \ | ||
+ | 3643:#define DT_INST_PHA_HAS_CELL(inst, pha, cell) \ | ||
+ | 3653:#define DT_INST_IRQ_HAS_IDX(inst, idx) DT_IRQ_HAS_IDX(DT_DRV_INST(inst), idx) | ||
+ | 3663:#define DT_INST_IRQ_HAS_CELL_AT_IDX(inst, idx, cell) \ | ||
+ | 3673:#define DT_INST_IRQ_HAS_CELL(inst, cell) \ | ||
+ | 3682:#define DT_INST_IRQ_HAS_NAME(inst, name) \ | ||
+ | 3691:#define DT_PATH_INTERNAL(...) \ | ||
+ | 3698:#define DT_S_PREFIX(name) _S_##name | ||
+ | 3714:#define DT_CAT(a1, a2) a1 ## a2 | ||
+ | 3716:#define DT_CAT3(a1, a2, a3) a1 ## a2 ## a3 | ||
+ | 3718:#define DT_CAT4(a1, a2, a3, a4) a1 ## a2 ## a3 ## a4 | ||
+ | 3720:#define DT_CAT5(a1, a2, a3, a4, a5) a1 ## a2 ## a3 ## a4 ## a5 | ||
+ | 3722:#define DT_CAT6(a1, a2, a3, a4, a5, a6) a1 ## a2 ## a3 ## a4 ## a5 ## a6 | ||
+ | 3724:#define DT_CAT7(a1, a2, a3, a4, a5, a6, a7) \ | ||
+ | 3727:#define DT_CAT8(a1, a2, a3, a4, a5, a6, a7, a8) \ | ||
+ | 3730: * If you need to define a bigger DT_CATN(), do so here. Don't leave | ||
+ | 3735:#define DT_DASH(...) MACRO_MAP_CAT(DT_DASH_PREFIX, __VA_ARGS__) | ||
+ | 3737:#define DT_DASH_PREFIX(name) _##name | ||
+ | 3739:#define DT_NODE_HAS_STATUS_INTERNAL(node_id, status) \ | ||
+ | 3742:#define DT_COMPAT_ON_BUS_INTERNAL(compat, bus) \ | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | 2023-01-10 Tuesday - to obtain value of a given device tree node property: | ||
+ | |||
+ | <pre> | ||
+ | 7166 /* Generic property macros: */ | ||
+ | 7167 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_IDX_0_EXISTS 1 | ||
+ | 7168 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_IDX_0_PH DT_N_S_soc_S_peripheral_40000000_S_gpio_1 | ||
+ | 7169 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_IDX_0_VAL_pin 14 | ||
+ | 7170 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_IDX_0_VAL_pin_EXISTS 1 | ||
+ | 7171 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_IDX_0_VAL_flags 0 | ||
+ | 7172 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_IDX_0_VAL_flags_EXISTS 1 | ||
+ | 7173 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_FOREACH_PROP_ELEM(fn) fn(DT_N_S_soc_S_peripheral_40000 000_S_flexcomm_89000_S_kx132_1, drdy_gpios, 0) | ||
+ | 7174 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_FOREACH_PROP_ELEM_VARGS(fn, ...) fn(DT_N_S_soc_S_perip heral_40000000_S_flexcomm_89000_S_kx132_1, drdy_gpios, 0, __VA_ARGS__) | ||
+ | 7175 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_LEN 1 | ||
+ | 7176 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_EXISTS 1 | ||
+ | 7177 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_reg {1 /* 0x1 */} | ||
+ | 7178 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_reg_IDX_0 1 | ||
+ | 7179 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_reg_IDX_0_EXISTS 1 | ||
+ | </pre> | ||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | == [[#top|^]] HAL level driver code == | ||
+ | |||
+ | This section intended for findings about Hardware Abstraction Layer device driver code, normally provided by MCU or part manufacturer engineering teams. A starting search point is NXP's GPIO controller HAL code base: | ||
+ | |||
+ | zephyr/drivers/gpio$ vi gpio_mcux_lpc.c | ||
+ | |||
+ | 2022-12-11 Sunday | ||
+ | |||
+ | In some SPI debugging efforts on a board bring-up task, we're also interested to review NXP's HAL level SPI driver sources. Excerpts from a successful build, which appear to identify by name the NXP SPI source file of interest: | ||
+ | |||
+ | <pre> | ||
+ | [77/184] Building C object zephyr/drivers/spi/CMa keFiles/drivers__spi.dir/spi_mcux_flexcomm.c.obj^[[K^M | ||
+ | [78/184] Linking C static library zephyr/soc/arm/common/cortex_m/libsoc__arm__common__cortex_m.a | ||
+ | </pre> | ||
+ | |||
+ | Source file `spi_mcux_flexcomm.c` is found in Zephyr 3.2.0's source tree path `zephyr/drivers/spi/spi_mcux_flexcomm.c`. | ||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | == [[#top|^]] Supporting and Tangential Topics == | ||
+ | |||
+ | Following sections hold notes and links to topics, which are themselves supporting but not specifically RTOS driver in their content: | ||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | == [[#top|^]] Sensors to seek for Zephyr demo exercising == | ||
+ | |||
+ | <i><b>Keywords:</b> sensor interrupts : sensor triggers : Zephyr triggers : sensor interrupt support</i> | ||
+ | |||
+ | [ ] [https://www.adafruit.com/product/1782 MCP9808] I2C based temperature sensor, [https://www.mouser.com/datasheet/2/268/25095A-15487.pdf datasheet], long lead times at Mouser | ||
+ | |||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | == [[#top|^]] References == | ||
+ | |||
+ | <span id="ref--zephyr-interrupts--zephyr-s-issues-s-30133"></span> | ||
+ | 1. https://github.com/zephyrproject-rtos/zephyr/issues/30133 | ||
+ | |||
+ | |||
+ | <!-- comentario --> | ||
+ | |||
+ | |||
+ | <center> | ||
+ | [[#top|- - - top of page - - -]] | ||
+ | </center> | ||
+ | |||
+ | |||
+ | <!-- comentario --> |
Latest revision as of 01:28, 11 January 2023
Keywords: sensor interrupts : sensor triggers : Zephyr triggers : sensor interrupt support
OVERVIEW
2022 Q4 notes on Zephyr RTOS drivers, driver frameworks and driver design. Current projects include work to extend KX132-1211 Zephyr out-of-tree driver, plus work to craft an IIS2DH driver which supports in Zephyr RTOS context both I2C and SPI bus connections for this sensor.
Contents
- 1 ^ Example Zephyr drivers
- 2 ^ Zephyr Kernel Level Device Support
- 3 ^ Understanding Zephyr SPI API
- 4 ^ Zephyr logging
- 5 ^ LPC55S69 flexcomm2 and hs_lspi pins conflict
- 6 ^ STMicro device context data structure
- 7 ^ Zephyr interrupts
- 8 ^ C Preprocessor macros
- 9 ^ Zephyr device tree macros
- 10 ^ HAL level driver code
- 11 ^ Supporting and Tangential Topics
- 12 ^ Sensors to seek for Zephyr demo exercising
- 13 ^ References
^ Example Zephyr drivers
Example apps here offer varying amounts of insight into Zephyr RTOS' framework(s) for device driver development. Of particular interest 2022 Q4 are the ways in which developers use Zephyr device tree macros to obtain pointers to devices, in their mostly C based code, at compile time. Though not fully explained in Zephyr's extensive documentation it appears that device pointers which can be correctly passed to and used with API function device_is_ready() and macro DEVICE_DT_GET() involve pairings of macros, which must be expanded prior to these calls in order to compile correctly.
ted@localhost1:~/projects-sandbox/workspace-out-of-tree/zephyr$ grep -nr init_res ./* | grep -v reset | grep init_res ./include/zephyr/device.h:416: unsigned int init_res : 8; ./include/zephyr/net/dns_resolve.h:459:void dns_init_resolver(void); ./include/zephyr/net/dns_resolve.h:462:#define dns_init_resolver(...) ./kernel/device.c:84: dev->state->init_res = rc; ./kernel/device.c:161: return dev->state->initialized && (dev->state->init_res == 0U); ./subsys/net/ip/net_core.c:476: dns_init_resolver(); ./subsys/net/lib/dns/resolve.c:1428:void dns_init_resolver(void)
What's going on in LM77 temperature sensor Kconfig file, on line 10? Interesting Kconfig stanza contains $(dt_compat_enabled,lm77):
Zephyr sensor API routine `device_is_ready()` is prototyped in zephyr/include/zephyr/device.h. A search for `device_is_ready()` implementation gives:
ted@localhost1:~/projects-sandbox/workspace-out-of-tree/zephyr$ grep -nr z_device_is_ready ./* ./include/zephyr/device.h:782:bool z_device_is_ready(const struct device *dev); ./include/zephyr/device.h:803: return z_device_is_ready(dev); ./kernel/device.c:108: if (z_device_is_ready(dev) && (dev->name == name)) { ./kernel/device.c:114: if (z_device_is_ready(dev) && (strcmp(name, dev->name) == 0)) { ./kernel/device.c:151:bool z_device_is_ready(const struct device *dev)
The body of this API routine is:
bool z_device_is_ready(const struct device *dev) { /* * if an invalid device pointer is passed as argument, this call * reports the `device` as not ready for usage. */ if (dev == NULL) { return false; } return dev->state->initialized && (dev->state->init_res == 0U); }
Having some trouble, however, finding places where `init_res` and `initialized` data members of device state structure get initialized. Looked recursively in both modules and zephyr source tree. Looks like there may be one place where this assignment takes place, in `zephyr/kernel/device.c`:
ted@localhost1:~/projects-sandbox/workspace-out-of-tree/zephyr$ grep -nr init_res ./* | grep -v reset | grep init_res ./include/zephyr/device.h:416: unsigned int init_res : 8; ./include/zephyr/net/dns_resolve.h:459:void dns_init_resolver(void); ./include/zephyr/net/dns_resolve.h:462:#define dns_init_resolver(...) ./kernel/device.c:84: dev->state->init_res = rc; ./kernel/device.c:161: return dev->state->initialized && (dev->state->init_res == 0U); ./subsys/net/ip/net_core.c:476: dns_init_resolver(); ./subsys/net/lib/dns/resolve.c:1428:void dns_init_resolver(void)
Routine which iterates over and initializes all devices needing initialization prior to app code starting:
54 void z_sys_init_run_level(int32_t level) 55 { 56 static const struct init_entry *levels[] = { 57 __init_PRE_KERNEL_1_start, 58 __init_PRE_KERNEL_2_start, 59 __init_POST_KERNEL_start, 60 __init_APPLICATION_start, 61 #ifdef CONFIG_SMP 62 __init_SMP_start, 63 #endif 64 /* End marker */ 65 __init_end, 66 }; 67 const struct init_entry *entry; 68 69 for (entry = levels[level]; entry < levels[level+1]; entry++) { 70 const struct device *dev = entry->dev; 71 int rc = entry->init(dev); 72 73 if (dev != NULL) { 74 /* Mark device initialized. If initialization 75 * failed, record the error condition. 76 */ 77 if (rc != 0) { 78 if (rc < 0) { 79 rc = -rc; 80 } 81 if (rc > UINT8_MAX) { 82 rc = UINT8_MAX; 83 } 84 dev->state->init_res = rc; 85 } 86 dev->state->initialized = true; 87 } 88 } 89 }
Sensor init priority:
ted@localhost1:~/projects-sandbox/workspace-for-nexus/zephyr$ grep -nr SENSOR_INIT_PRIORITY ./* | grep -v CONFIG_SENSOR | grep SENSOR_INIT_PRIORITY ./boards/arm/thingy52_nrf52832/board.c:33:#error BOARD_CCS_VDD_PWR_CTRL_INIT_PRIORITY must be lower than SENSOR_INIT_PRIORITY ./boards/arm/thingy52_nrf52832/Kconfig:15: BOARD_VDD_PWR_CTRL_INIT_PRIORITY, but smaller than SENSOR_INIT_PRIORITY. ./drivers/sensor/Kconfig:17:config SENSOR_INIT_PRIORITY
^ device_get_binding API routine
2022-1129 New section on Zephyr `device_get_binding()` API, which Ted hopes can serve as a run time, dynamic assigner of device pointers for reintializing hung hardware or late-to-start, indeterminate start time hardware.
As of past four days 2022 Nov 26 through 29 Ted finds that Zephyr has a strong leaning to set up devices at boot time, and have device structures and pointers declared and assigned largely at compile time. The "DT_INST" family macros all seem designed to work only as static declarations, not as part of code which can refer to and assign a pointer to a device at arbitrary firmware run times. Build time errors seem to indicate that these macros expand correctly, as intended, when they appear at a global or file scoped level. That is, a macro such as DEVICE_DT_INST_DEFINE compiles correctly -- if in largely hidden ways -- when it appears outside the scope of any C function. In contrast Zephyr build tools through errors when this and related DT_ macros appear in function bodies. It looks like these macros may not be expanding inside the function bodies. There must be some basic C rules here we don't yet fully grasp . . .
In searching Zephyr 3.2.0 source tree for instances of calls to `device_get_binding()`, Ted looking for way or ways that code builds the device name parameter this API routine expects. There are many use instances of this API routine in Zephyr's `tests` directory. There is one test whose CMakeLists.txt file includes interesting notes:
"./tests/kernel/device/CMakeLists.txt" 16L, 660C
Content of this file:
1 # SPDX-License-Identifier: Apache-2.0 2 3 cmake_minimum_required(VERSION 3.20.0) 4 find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) 5 project(device) 6 7 FILE(GLOB app_sources src/*.c) 8 target_sources(app PRIVATE ${app_sources}) 9 10 # device_get_binding() compares pointers first before doing strcmp(). 11 # However, enabling coverage forces -O0 to disable any compiler 12 # optimizations. There would be multiple copies of the same string, 13 # and the code pathing doing pointer comparison would not be tested 14 # at all. So add this flag to merge string constants such that 15 # the pointer comparison would be exercised. 16 zephyr_cc_option_ifdef(CONFIG_COVERAGE -fmerge-constants)
Further, there are some Zephyr code samples which also call `device_get_binding()` with a given device name. One of the I2S samples looks promising, it's at zephyr/samples/drivers/i2s/litex where in main.c there's a call to `device_get_binding("i2s_rx")`. The parameter here is double quoted, so its a hard-coded string. But what does this match? A search for this pattern from the top level Zephyr 3.2.0 source tree dir reveals:
Grep search results for `i2s_rx`, used in double quotes in call to `device_get_binding()`:
( note, blank lines added for clarity about significant search results )
ted@localhost1:~/projects-sandbox/workspace-out-of-tree/zephyr$ grep -nr i2s_rx ./* ./boards/riscv/litex_vexriscv/litex_vexriscv.dts:67:&i2s_rx { ./drivers/interrupt_controller/intc_vexriscv_litex.c:24:#define I2S_RX_IRQ DT_IRQN(DT_NODELABEL(i2s_rx)) ./drivers/i2s/i2s_litex.h:35:#define I2S_RX_FIFO_ADDR DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), fifo) ./drivers/i2s/i2s_litex.h:36:#define I2S_RX_FIFO_DEPTH DT_PROP(DT_NODELABEL(i2s_rx), fifo_depth) ./drivers/i2s/i2s_litex.h:47:#define I2S_BASE_ADDR DT_REG_ADDR(DT_NODELABEL(i2s_rx)) . . . use of DT_REG_ADDR macro - TMH ./drivers/i2s/i2s_litex.h:48:#define I2S_EV_STATUS_OFFSET (DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), ev_status) \ ./drivers/i2s/i2s_litex.h:50:#define I2S_EV_PENDING_OFFSET (DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), ev_pending) \ ./drivers/i2s/i2s_litex.h:52:#define I2S_EV_ENABLE_OFFSET (DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), ev_enable) \ ./drivers/i2s/i2s_litex.h:54:#define I2S_CONTROL_OFFSET (DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), rx_ctl) \ ./drivers/i2s/i2s_litex.h:56:#define I2S_STATUS_OFFSET (DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), rx_stat) \ ./drivers/i2s/i2s_litex.h:58:#define I2S_CONFIG_OFFSET (DT_REG_ADDR_BY_NAME(DT_NODELABEL(i2s_rx), rx_conf) \ ./drivers/i2s/i2s_litex.c:635:#if DT_NODE_HAS_STATUS(DT_NODELABEL(i2s_rx), okay) ./drivers/i2s/i2s_mcux_sai.c:118:static void i2s_rx_stream_disable(const struct device *, ./drivers/i2s/i2s_mcux_sai.c:179:static void i2s_rx_stream_disable(const struct device *dev, ./drivers/i2s/i2s_mcux_sai.c:378: i2s_rx_stream_disable(dev, false, false); ./drivers/i2s/i2s_mcux_sai.c:389: i2s_rx_stream_disable(dev, false, false); ./drivers/i2s/i2s_mcux_sai.c:402: i2s_rx_stream_disable(dev, ./drivers/i2s/i2s_mcux_sai.c:421: i2s_rx_stream_disable(dev, true, false); ./drivers/i2s/i2s_mcux_sai.c:427: i2s_rx_stream_disable(dev, true, true); ./drivers/i2s/i2s_mcux_sai.c:814:static int i2s_rx_stream_start(const struct device *dev) ./drivers/i2s/i2s_mcux_sai.c:944: ret = i2s_rx_stream_start(dev); ./drivers/i2s/i2s_mcux_sai.c:969: i2s_rx_stream_disable(dev, true, true); ./drivers/i2s/i2s_mcux_sai.c:1006: i2s_rx_stream_disable(dev, true, true); ./dts/riscv/riscv32-litex-vexriscv.dtsi:211: i2s_rx: i2s_rx@e000a800 { <-- This result looks promising, a dts nodelabel - TMH ./samples/drivers/i2s/echo/sample.yaml:6: filter: dt_nodelabel_enabled("i2s_rxtx") or ./samples/drivers/i2s/echo/sample.yaml:7: (dt_nodelabel_enabled("i2s_rx") and dt_nodelabel_enabled("i2s_tx")) ./samples/drivers/i2s/echo/src/main.c:15:#if DT_NODE_EXISTS(DT_NODELABEL(i2s_rxtx)) ./samples/drivers/i2s/echo/src/main.c:16:#define I2S_RX_NODE DT_NODELABEL(i2s_rxtx) ./samples/drivers/i2s/echo/src/main.c:19:#define I2S_RX_NODE DT_NODELABEL(i2s_rx) . . .
^ Zephyr Kernel Level Device Support
Interesting code excerpt from `zephy/kernel/device.c`, this following routine is the implementation behind a wrapper like function `device_is_ready()`. But this routine, and sensibly so, doesn't say anything about how a given device is initialized. It only looks at two data members of a device instance data structure:
150 151 bool z_device_is_ready(const struct device *dev) 152 { 153 /* 154 * if an invalid device pointer is passed as argument, this call 155 * reports the `device` as not ready for usage. 156 */ 157 if (dev == NULL) { 158 return false; 159 } 160 161 return dev->state->initialized && (dev->state->init_res == 0U); 162 } 163
An additional note of import, a majority of Zephyr RTOS `z_` implementation functions are just wrappers to third party, often Hardware Abstraction Layer function definitions. This wrapping of a Zephyr API around specific microcontroller families decouples Zephyr from knowing or needing to know about hardware details of a given target hardware. This decoupling is very important!
On a somewhat unrelated note, a couple of important Zephyr documents, including Zephyr device tree macros used to obtain node identifiers:
- https://docs.zephyrproject.org/latest/build/dts/dt-vs-kconfig.html Device Tree versus Kconfig, a fairly short read
^ Understanding Zephyr SPI API
To make use of Zephyr API for SPI bus, we must in part understand how each of the three parameters passed to spi_read() and spi_write() are defined. Further, when we have a sensor with which we communicate in our Zephyr based app we need to understand how parts of this sensor's 'config' structure (1) in the firmware relate to the SPI bus to which the sensor is connected. Our sensor driver's code which talks with the sensor via SPI must pass certain SPI bus related pointers to Zephyr's SPI Application Programmers' Interface.
Another important SPI defining resource in Zephyr RTOS is the header file spi.h#L379. This file defines the small data structure named `spi_dt_spec`. It gives us the data member names for a SPI bus peripheral, as Zephyr sees them.
2022-11-25
The best Zephyr SPI instructions we find so far are in the sample app `spi_bitbang`. The source file main.c there, sets up RX and TX buffers, and some Zephyr driver structures about these. There is an interesting design pattern in Zephyr's SPI API: receive and transmit buffer pairs can be made multiple, in two arrays of these same buffers. The SPI interface function `spi_tranceive()`, about which spi_write() and spi_read() wrap, steps through the count of write buffers and receive buffers it is passed from calling code. In this way, a careful choosing of sets of buffers permits app developers to express more complex SPI communications by defining the buffers with needed lengths, and then call the SPI API of Zephyr RTOS just once.
Section footnotes:
(1) in Zephyr 2.6.0 some device drivers such as IIS2DH placed bus device pointers in sensor's structure 'data', for mutable sensor data. Newer Zephyr releases, at latest by 3.2.0, IIS2DH driver authors have moved bus device pointers to this sensor's 'config' structure, designed to hold unchanging, compile-time assigned data.
^ Zephyr logging
Note, in a Zephyr project or an out-of-tree driver, what we have learned here is that LOG_MODULE_REGISTER(project_name) must be called in one -- possibly the first -- of the project's source files. Any other dot c files from which developers need logging capability must include the function like macro LOG_MODULE_DECLARE().
- https://docs.zephyrproject.org/3.0.0/reference/logging/index.html#logging-in-a-module
- https://docs.zephyrproject.org/3.0.0/reference/logging/index.html#c.LOG_MODULE_DECLARE
^ LPC55S69 flexcomm2 and hs_lspi pins conflict
zephyr/boards/arm/lpcxpresso55s69/lpcxpresso55s69-pinctrl.dtsi lines 21 - 28
Note that in spite of the comment above in the dot dtsi file from NXP, SPI communications with an IIS2DH sensor work while UART2 is also in use and working - TMH
^ STMicro device context data structure
Symbol `stmdev_ctx_t` is defined in file modules/hal/st/sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.c. On github this file as of 2022-11-18 located at https://github.com/zephyrproject-rtos/hal_st/tree/master/sensor/stmemsc/iis2dh_STdC/driver'
Data structure `stmdev_ctx_t` defined as follows:
104 /** @addtogroup Interfaces_Functions 105 * @brief This section provide a set of functions used to read and 106 * write a generic register of the device. 107 * MANDATORY: return 0 -> no Error. 108 * @{ 109 * 110 */ 111 112 typedef int32_t (*stmdev_write_ptr)(void *, uint8_t, const uint8_t *, uint16_t); 113 typedef int32_t (*stmdev_read_ptr)(void *, uint8_t, uint8_t *, uint16_t); 114 115 typedef struct 116 { 117 /** Component mandatory fields **/ 118 stmdev_write_ptr write_reg; 119 stmdev_read_ptr read_reg; 120 /** Customizable optional pointer **/ 121 void *handle; 122 } stmdev_ctx_t;
Parameter lists for the register write and register read functions are nearly the same, with exception of the third parameter. In the register write function this parameter is qualified with C `const`, as it is only expected to be read by the function using it.
With the above definition, an instance of stmdev_cts_t is created for each device with device tree compatible property of 'st_iis2dh', and is assigned in `zephyr/drivers/sensor/iis2dh/iis2dh-i2c.c` as follows:
38 stmdev_ctx_t iis2dh_i2c_ctx = { 39 .read_reg = (stmdev_read_ptr) iis2dh_i2c_read, 40 .write_reg = (stmdev_write_ptr) iis2dh_i2c_write, 41 }; 42 43 int iis2dh_i2c_init(const struct device *dev) 44 { 45 struct iis2dh_data *data = dev->data; 46 const struct iis2dh_device_config *config = dev->config; 47 48 if (!device_is_ready(config->i2c.bus)) { 49 LOG_ERR("Bus device is not ready"); 50 return -ENODEV; 51 } 52 53 data->ctx = &iis2dh_i2c_ctx; 54 data->ctx->handle = (void *)dev; 55 56 return 0; 57 } 58 #endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */
The above init function is called from iis2dh.c:
232 static int iis2dh_init_interface(const struct device *dev) 233 { 234 int res; 235 236 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) 237 res = iis2dh_spi_init(dev); 238 if (res) { 239 return res; 240 } 241 #elif DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) 242 res = iis2dh_i2c_init(dev); 243 if (res) { 244 return res; 245 } 246 #else 247 #error "BUS MACRO NOT DEFINED IN DTS" 248 #endif 249 250 return 0; 251 } 252 253 static int iis2dh_init(const struct device *dev) 254 { 255 struct iis2dh_data *iis2dh = dev->data; 256 const struct iis2dh_device_config *cfg = dev->config; 257 uint8_t wai; 258 259 if (iis2dh_init_interface(dev)) { 260 return -EINVAL; 261 }
Pointer *dev has a member named 'data', which in turn has a member named 'ctx', which in turn gets assigned the address of a not named instance of `iis2dh_i2c_ctx`. This means that we have `dev->data->ctx->read_reg()` and `dev->data->ctx->write_reg` on successful completion of routine iis2dh_i2c_init(). This is reflected in modules/hal/st/sensor/stmemsc/iis2dh_STdC/driver/iis2dh_reg.c, in the two generalized register read and write functions:
39 /** 40 * @brief Read generic device register 41 * 42 * @param ctx read / write interface definitions(ptr) 43 * @param reg register to read 44 * @param data pointer to buffer that store the data read(ptr) 45 * @param len number of consecutive register to read 46 * @retval interface status (MANDATORY: return 0 -> no Error) 47 * 48 */ 49 int32_t iis2dh_read_reg(stmdev_ctx_t *ctx, uint8_t reg, uint8_t *data, 50 uint16_t len) 51 { 52 int32_t ret; 53 54 ret = ctx->read_reg(ctx->handle, reg, data, len); 55 56 return ret; 57 } 58 59 /** 60 * @brief Write generic device register 61 * 62 * @param ctx read / write interface definitions(ptr) 63 * @param reg register to write 64 * @param data pointer to data to write in register reg(ptr) 65 * @param len number of consecutive register to write 66 * @retval interface status (MANDATORY: return 0 -> no Error) 67 * 68 */ 69 int32_t iis2dh_write_reg(stmdev_ctx_t *ctx, uint8_t reg, 70 uint8_t *data, 71 uint16_t len) 72 { 73 int32_t ret; 74 75 ret = ctx->write_reg(ctx->handle, reg, data, len); 76 77 return ret; 78 }
. . .
https://docs.zephyrproject.org/latest/hardware/peripherals/i2c.html#c.i2c_burst_read_dt
^ Zephyr interrupts
This sections aims to cover Zephyr interrupts, and some example Zephyr interrupt set up code fragments.
In Zephyr RTOS context, triggers are interrupts which are generated by sensors. Zephyr constructs a notion of "trigger types" and "trigger channels". Some pages from Zephyr 3.2.0 documentation:
- https://docs.zephyrproject.org/latest/hardware/peripherals/sensor.html#triggers
- https://docs.zephyrproject.org/latest/hardware/peripherals/sensor.html#c.sensor_trigger_type
This section created to help address a challenge we have, setting up a sensor interrupt or Zephyr "trigger" in KX132 driver. While unsure, there is suspicion that the `int_gpio` member of the sensor's `config` data structure may not be getting assigned a GPIO port at all, in an early Zephyr init level at boot time. In a diagnostic, `cfg->int_gpio.port->name` shows as "=&", which does not look like a valid port name.
KX132's config data structure include set up codes this snippet of code:
58 uint8_t pm; 59 #ifdef CONFIG_KX132_TRIGGER 60 #warning "KX132 1211 driver - compiling gpio_dt_spec instance in struct 'kx132_device_config'" 61 // # REF https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/drivers/gpio.h#L271 62 struct gpio_dt_spec int_gpio; 63 #endif /* CONFIG_KX132_TRIGGER */ 64 };
In a different sample app, iis2dh-driver-demo, we were able to set up a trigger per the code from Zephyr's `samples/basic/button` app. The early declarative elements to prepare to use a GPIO pin on the MCU, as an interrupt input line connected to a sensor, are in this sample:
127 #include <zephyr/drivers/gpio.h> // needed to provide GPIO_DT_SPEC_GET_OR Zephyr device tree macro 128 129 #define SENSOR_INT1_NODE DT_ALIAS(sensorinterrupt1) 130 #if !DT_NODE_HAS_STATUS(SENSOR_INT1_NODE, okay) 131 #error "- DEV 1108 - Could not find in device tree source any sensor interrupt type node!" 132 #endif 133 //static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0}); 134 static const struct gpio_dt_spec interrupt_line_1 = GPIO_DT_SPEC_GET_OR(SENSOR_INT1_NODE, gpios, {0}); 135 136 static struct gpio_callback sensor_interrupt_1_cb_data;
Device tree overlay expresses the above referenced DTS node alias:
5 / { 6 aliases { 9 sensorinterrupt1 = &iis2dhint1; 11 }; 12 }; 65 / { 66 sensor_interrupts { 67 compatible = "gpi set up codeo-keys"; 68 iis2dhint1: iis2dh_int1 { 69 gpios = < &gpio1 9 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; 70 }; 71 /* ... other buttons ... */ 72 }; 73 };
set up code
In a driver use case, the interrupt line(s) or pins themselves likely won't require a DTS node alias. These GPIO pins will be expressed as values in the sensor's device tree node. The property will be one of `irq-gpios`, `drdy-gpios` and similar, depending on the given sensor's device tree bindings file. Of course, the port controlling the given pins must somewhere in device tree or DTS overlay files be enabled.
Zephyr interrupt set up code from button sample app . . .
MODULE_ID__THREAD_IIS2DH, sensor->state->init_res); // 'button' from samples/basic/button becomes 'interrupt_line_1': // STEP config step if (!device_is_ready(interrupt_line_1.port)) { printk("Error: sensor interrupt 1 port '%s' detected as not ready\n", interrupt_line_1.port->name); // return; } // STEP config step rstatus = gpio_pin_configure_dt(&interrupt_line_1, GPIO_INPUT); if (rstatus != 0) { printk("Error %d: failed to configure %s pin %d\n", rstatus, interrupt_line_1.port->name, interrupt_line_1.pin); // return; } // STEP config step rstatus = gpio_pin_interrupt_configure_dt(&interrupt_line_1, GPIO_INT_EDGE_TO_ACTIVE); if (rstatus != 0) { printk("Error %d: failed to configure interrupt on %s pin %d\n", rstatus, interrupt_line_1.port->name, interrupt_line_1.pin); // return; } // STEP config step // gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin)); // gpio_add_callback(button.port, &button_cb_data); // printk("Set up button at %s pin %d\n", button.port->name, button.pin); gpio_init_callback(&sensor_interrupt_1_cb_data, sensor_interrupt_1_cb, BIT(interrupt_line_1.pin)); gpio_add_callback(interrupt_line_1.port, &sensor_interrupt_1_cb_data); printk("Set up button at %s pin %d\n", interrupt_line_1.port->name, interrupt_line_1.pin);
Interestingly this demo code gives the following port name after the GPIO based interrupt is successfully configured:
*** Booting Zephyr OS build zephyr-v3.2.0 *** - scoreboard - in setter function test val holds 0, - scoreboard - setting test val to 5, Success in init of KX132! - DEV 1028 - symbol ST_IIS2DH got assigned 'DT_N_S_soc_S_peripheral_50000000_S_spi_9f000_S_iis2dh_0' - kd_thread_iis2dh - Success finding iis2dh device, - kd_thread_iis2dh - sensor device 'iis2dh@0' is ready, - kd_thread_iis2dh - INFO: device set up code set up codename (Zephyr compile time setting) found to be 'iis2dh@0' - kd_thread_iis2dh - INFO: boot time Zephyr init result value holds 0, INFO: sensor interrupt 1 port 'gpio@1' detected as ready! Set up button at gpio@1 pin 9
Name `gpio@1` is definitely not equivalent or like `=&`. So QUESTION: where in the run time process, and in the source code of a given out-of-tree driver, is a valid port controller assigned to `const struct device *dev->cfg->int_gpio.port`?
In Jared Wolff's sgp40 driver there's explicit code to assign values to sensor data structure members which support an enable line. Line of interest is sgp40.c line 153. In this case the line communication is an enable line, not an interrupt, but the needed set up should be equivalent.
QUESTION: where and how is STMicro IIS2DH driver achieving the same set up for its "data ready", drdy gpio?
^ QUESTION sensor interrupts configured in drivers at boot time?
From 2022 Q4, it looks like Zephyr sensor drivers, a couple at least, may not handle or set up SPI chip select lines. This so in spite of there being a way to express chip select lines in device tree overlays. From other work in 2022 Q4 it looks further like certain Zephyr out-of-tree drivers may also not provide for setting up sensor interrupts at boot time. In particular, contributor Ted has so far found few to no example code or developer forum posts on how to configure STMicro sensor "data ready" interrupts in or using the driver provided by STMicro.
It is possible to manually set up a GPIO port and pin to serve as a physical interrupt line coming into the MCU. This can be done by borrowing code from 3.2.0 button sample app. But is this always necessary when enabling a hardware interrupt line support from a sensor, in a Zephyr based app?
So far still in the research and debugging stage on this question. Some references to Zephyr triggers include:
-
Zephyr sensor trigger structure defined here:
^ C Preprocessor macros
^ Zephyr device tree macros
Section on Zephyr device tree macros, many defined in devicetree.h. Also worth noting is Zephyr 3.2.0 notes on device drivers that depend on other devices.
^ device drivers two ways
Zephyr documentation states that, using devicetree API there are two way to write device drivers. These ways are (1) by using instance numbers and (2) by using nodelabels. Links to both these device driver topics:
- https://docs.zephyrproject.org/3.2.0/build/dts/howtos.html#option-2-create-devices-using-node-labels
^ Devicetree guide links
Some important Zephyr device tree macro and use documentation links:
2022-11-30
Macros DT_COMPAT_GET_ANY_STATUS_OK and DT_NODE_PATH
^ some macro definitions
Definition of DT_DRV_INST
2903 /** 2904 * @defgroup devicetree-inst Instance-based devicetree APIs 2905 * @ingroup devicetree 2906 * @{ 2907 */ 2908 2909 /** 2910 * @brief Node identifier for an instance of a `DT_DRV_COMPAT` compatible 2911 * @param inst instance number 2912 * @return a node identifier for the node with `DT_DRV_COMPAT` compatible and 2913 * instance number @p inst 2914 */ 2915 #define DT_DRV_INST(inst) DT_INST(inst, DT_DRV_COMPAT)
Definition of DT_INST
232 /** 233 * @brief Get a node identifier for an instance of a compatible 234 * 235 * All nodes with a particular compatible property value are assigned 236 * instance numbers, which are zero-based indexes specific to that 237 * compatible. You can get a node identifier for these nodes by 238 * passing DT_INST() an instance number, @p inst, along with the 239 * lowercase-and-underscores version of the compatible, @p compat. 240 * 241 * Instance numbers have the following properties: 242 * 243 * - for each compatible, instance numbers start at 0 and are contiguous 244 * - exactly one instance number is assigned for each node with a compatible, 245 * **including disabled nodes** 246 * - enabled nodes (status property is `okay` or missing) are assigned the 247 * instance numbers starting from 0, and disabled nodes have instance 248 * numbers which are greater than those of any enabled node 249 * 250 * No other guarantees are made. In particular: 251 * 252 * - instance numbers **in no way reflect** any numbering scheme that 253 * might exist in SoC documentation, node labels or unit addresses, 254 * or properties of the /aliases node (use DT_NODELABEL() or DT_ALIAS() 255 * for those) 256 * - there **is no general guarantee** that the same node will have 257 * the same instance number between builds, even if you are building 258 * the same application again in the same build directory 259 * 260 * Example devicetree fragment: 261 * 262 * @code{.dts} 263 * serial1: serial@40001000 { 264 * compatible = "vnd,soc-serial"; 265 * status = "disabled"; 266 * current-speed = <9600>; 267 * ... 268 * }; 269 * 270 * serial2: serial@40002000 { 271 * compatible = "vnd,soc-serial"; 272 * status = "okay"; 273 * current-speed = <57600>; 274 * ... 275 * }; 276 * 277 * serial3: serial@40003000 { 278 * compatible = "vnd,soc-serial"; 279 * current-speed = <115200>; 280 * ... 281 * }; 282 * @endcode 283 * 284 * Assuming no other nodes in the devicetree have compatible 285 * `"vnd,soc-serial"`, that compatible has nodes with instance numbers 286 * 0, 1, and 2. 287 * 288 * The nodes `serial@40002000` and `serial@40003000` are both enabled, so 289 * their instance numbers are 0 and 1, but no guarantees are made 290 * regarding which node has which instance number. 291 * 292 * Since `serial@40001000` is the only disabled node, it has instance 293 * number 2, since disabled nodes are assigned the largest instance 294 * numbers. Therefore: 295 * 296 * @code{.c} 297 * // Could be 57600 or 115200. There is no way to be sure: 298 * // either serial@40002000 or serial@40003000 could 299 * // have instance number 0, so this could be the current-speed 300 * // property of either of those nodes. 301 * DT_PROP(DT_INST(0, vnd_soc_serial), current_speed) 302 * 303 * // Could be 57600 or 115200, for the same reason. 304 * // If the above expression expands to 57600, then 305 * // this expands to 115200, and vice-versa. 306 * DT_PROP(DT_INST(1, vnd_soc_serial), current_speed) 307 * 308 * // 9600, because there is only one disabled node, and 309 * // disabled nodes are "at the the end" of the instance 310 * // number "list". 311 * DT_PROP(DT_INST(2, vnd_soc_serial), current_speed) 312 * @endcode 313 * 314 * Notice how `"vnd,soc-serial"` in the devicetree becomes `vnd_soc_serial` 315 * (without quotes) in the DT_INST() arguments. (As usual, `current-speed` 316 * in the devicetree becomes `current_speed` as well.) 317 * 318 * Nodes whose `compatible` property has multiple values are assigned 319 * independent instance numbers for each compatible. 320 * 321 * @param inst instance number for compatible @p compat 322 * @param compat lowercase-and-underscores compatible, without quotes 323 * @return node identifier for the node with that instance number and 324 * compatible 325 */ 326 #define DT_INST(inst, compat) UTIL_CAT(DT_N_INST, DT_DASH(inst, compat))
From Zephyr 3.2.0 `zephyr/devicetree/spi.h`:
202 * spi1: spi@... { 203 * compatible = "vnd,spi"; 204 * cs-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>, 205 * <&gpio2 20 GPIO_ACTIVE_LOW>; 206 * 207 * a: spi-dev-a@0 { 208 * reg = <0>; 209 * }; 210 * 211 * b: spi-dev-b@1 { 212 * reg = <1>; 213 * }; 214 * }; 215 * 216 * Example usage: 217 * 218 * DT_SPI_DEV_CS_GPIOS_PIN(DT_NODELABEL(a)) // 10 219 * DT_SPI_DEV_CS_GPIOS_PIN(DT_NODELABEL(b)) // 20
^ list of all DT macros
ted@localhost1:~/projects-sandbox/workspace-out-of-tree/zephyr/include/zephyr$ grep -n DT_ devicetree.h | grep define 77:#define DT_INVALID_NODE _ 82:#define DT_ROOT DT_N 134:#define DT_PATH(...) DT_PATH_INTERNAL(__VA_ARGS__) 190:#define DT_NODELABEL(label) DT_CAT(DT_N_NODELABEL_, label) 230:#define DT_ALIAS(alias) DT_CAT(DT_N_ALIAS_, alias) 326:#define DT_INST(inst, compat) UTIL_CAT(DT_N_INST, DT_DASH(inst, compat)) 351:#define DT_PARENT(node_id) UTIL_CAT(node_id, _PARENT) 358:#define DT_INST_PARENT(inst) DT_PARENT(DT_DRV_INST(inst)) 383:#define DT_GPARENT(node_id) DT_PARENT(DT_PARENT(node_id)) 406: * #define SOC_NODE DT_NODELABEL(soc_label) 420:#define DT_CHILD(node_id, child) UTIL_CAT(node_id, DT_S_PREFIX(child)) 463:#define DT_COMPAT_GET_ANY_STATUS_OKAY(compat) \ 495:#define DT_NODE_PATH(node_id) DT_CAT(node_id, _PATH) 521:#define DT_NODE_FULL_NAME(node_id) DT_CAT(node_id, _FULL_NAME) 549:#define DT_NODE_CHILD_IDX(node_id) DT_CAT(node_id, _CHILD_IDX) 571:#define DT_SAME_NODE(node_id1, node_id2) \ 614:#define DT_PROP(node_id, prop) DT_CAT3(node_id, _P_, prop) 644:#define DT_PROP_LEN(node_id, prop) DT_CAT4(node_id, _P_, prop, _LEN) 649: * If the property is defined (as determined by DT_NODE_HAS_PROP()), 660:#define DT_PROP_LEN_OR(node_id, prop, default_value) \ 684:#define DT_PROP_HAS_IDX(node_id, prop, idx) \ 719:#define DT_PROP_HAS_NAME(node_id, prop, name) \ 744:#define DT_PROP_BY_IDX(node_id, prop, idx) \ 760:#define DT_PROP_OR(node_id, prop, default_value) \ 773:#define DT_LABEL(node_id) DT_PROP(node_id, label) __DEPRECATED_MACRO 815:#define DT_ENUM_IDX(node_id, prop) DT_CAT4(node_id, _P_, prop, _ENUM_IDX) 831:#define DT_ENUM_IDX_OR(node_id, prop, default_idx_value) \ 894:#define DT_STRING_TOKEN(node_id, prop) \ 910:#define DT_STRING_TOKEN_OR(node_id, prop, default_value) \ 971:#define DT_STRING_UPPER_TOKEN(node_id, prop) \ 988:#define DT_STRING_UPPER_TOKEN_OR(node_id, prop, default_value) \ 1039:#define DT_STRING_TOKEN_BY_IDX(node_id, prop, idx) \ 1089:#define DT_STRING_UPPER_TOKEN_BY_IDX(node_id, prop, idx) \ 1131: * #define N1 DT_NODELABEL(n1) 1145:#define DT_PROP_BY_PHANDLE_IDX(node_id, phs, idx, prop) \ 1167:#define DT_PROP_BY_PHANDLE_IDX_OR(node_id, phs, idx, prop, default_value) \ 1181:#define DT_PROP_BY_PHANDLE(node_id, ph, prop) \ 1225: * #define LED DT_NODELABEL(led) 1238:#define DT_PHA_BY_IDX(node_id, pha, idx, cell) \ 1252: * defined, so it's safe to use DT_PROP_OR() here, because that uses an 1264:#define DT_PHA_BY_IDX_OR(node_id, pha, idx, cell, default_value) \ 1274:#define DT_PHA(node_id, pha, cell) DT_PHA_BY_IDX(node_id, pha, 0, cell) 1290:#define DT_PHA_OR(node_id, pha, cell, default_value) \ 1333:#define DT_PHA_BY_NAME(node_id, pha, name, cell) \ 1346: * defined, so it's safe to use DT_PROP_OR() here, because that uses an 1357:#define DT_PHA_BY_NAME_OR(node_id, pha, name, cell, default_value) \ 1393: * #define NODE DT_NODELABEL(n) 1407:#define DT_PHANDLE_BY_NAME(node_id, pha, name) \ 1439: * #define N1 DT_NODELABEL(n1) 1459:#define DT_PHANDLE_BY_IDX(node_id, prop, idx) \ 1473:#define DT_PHANDLE(node_id, prop) DT_PHANDLE_BY_IDX(node_id, prop, 0) 1521:#define DT_NUM_RANGES(node_id) DT_CAT(node_id, _RANGES_NUM) 1575:#define DT_RANGES_HAS_IDX(node_id, idx) \ 1630:#define DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(node_id, idx) \ 1670:#define DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(node_id, idx) \ 1719:#define DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(node_id, idx) \ 1768:#define DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(node_id, idx) \ 1817:#define DT_RANGES_LENGTH_BY_IDX(node_id, idx) \ 1841: * #define RANGE_LENGTH(node_id, idx) DT_RANGES_LENGTH_BY_IDX(node_id, idx), 1859:#define DT_FOREACH_RANGE(node_id, fn) \ 1907:#define DT_NODE_VENDOR_BY_IDX(node_id, idx) \ 1922:#define DT_NODE_VENDOR_HAS_IDX(node_id, idx) \ 1939:#define DT_NODE_VENDOR_BY_IDX_OR(node_id, idx, default_value) \ 1951:#define DT_NODE_VENDOR_OR(node_id, default_value) \ 1971:#define DT_NUM_REGS(node_id) DT_CAT(node_id, _REG_NUM) 1984:#define DT_REG_HAS_IDX(node_id, idx) \ 1993:#define DT_REG_ADDR_BY_IDX(node_id, idx) \ 2007:#define DT_REG_SIZE_BY_IDX(node_id, idx) \ 2017:#define DT_REG_ADDR(node_id) DT_REG_ADDR_BY_IDX(node_id, 0) 2026:#define DT_REG_SIZE(node_id) DT_REG_SIZE_BY_IDX(node_id, 0) 2034:#define DT_REG_ADDR_BY_NAME(node_id, name) \ 2043:#define DT_REG_SIZE_BY_NAME(node_id, name) \ 2064:#define DT_NUM_IRQS(node_id) DT_CAT(node_id, _IRQ_NUM) 2076:#define DT_IRQ_HAS_IDX(node_id, idx) \ 2089:#define DT_IRQ_HAS_CELL_AT_IDX(node_id, idx, cell) \ 2099:#define DT_IRQ_HAS_CELL(node_id, cell) DT_IRQ_HAS_CELL_AT_IDX(node_id, 0, cell) 2110:#define DT_IRQ_HAS_NAME(node_id, name) \ 2134: * #define SERIAL DT_NODELABEL(my_serial) 2148:#define DT_IRQ_BY_IDX(node_id, idx, cell) \ 2166:#define DT_IRQ_BY_NAME(node_id, name, cell) \ 2176:#define DT_IRQ(node_id, cell) DT_IRQ_BY_IDX(node_id, 0, cell) 2188:#define DT_IRQN(node_id) DT_IRQ(node_id, irq) 2208:#define DT_CHOSEN(prop) DT_CAT(DT_CHOSEN_, prop) 2216:#define DT_HAS_CHOSEN(prop) IS_ENABLED(DT_CAT3(DT_CHOSEN_, prop, _EXISTS)) 2237:#define DT_FOREACH_NODE(fn) DT_FOREACH_HELPER(fn) 2250:#define DT_FOREACH_STATUS_OKAY_NODE(fn) DT_FOREACH_OKAY_HELPER(fn) 2277: * #define FOOBAR_AND_COMMA(node_id) DT_PROP(node_id, foobar), 2295:#define DT_FOREACH_CHILD(node_id, fn) \ 2338:#define DT_FOREACH_CHILD_SEP(node_id, fn, sep) \ 2356:#define DT_FOREACH_CHILD_VARGS(node_id, fn, ...) \ 2374:#define DT_FOREACH_CHILD_SEP_VARGS(node_id, fn, sep, ...) \ 2392:#define DT_FOREACH_CHILD_STATUS_OKAY(node_id, fn) \ 2411:#define DT_FOREACH_CHILD_STATUS_OKAY_SEP(node_id, fn, sep) \ 2433:#define DT_FOREACH_CHILD_STATUS_OKAY_VARGS(node_id, fn, ...) \ 2454:#define DT_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS(node_id, fn, sep, ...) \ 2507:#define DT_FOREACH_PROP_ELEM(node_id, prop, fn) \ 2527:#define DT_FOREACH_PROP_ELEM_VARGS(node_id, prop, fn, ...) \ 2583:#define DT_FOREACH_STATUS_OKAY(compat, fn) \ 2613: * #define MY_FN(node_id, operator) DT_PROP(node_id, val) operator 2632:#define DT_FOREACH_STATUS_OKAY_VARGS(compat, fn, ...) \ 2662:#define DT_NODE_EXISTS(node_id) IS_ENABLED(DT_CAT(node_id, _EXISTS)) 2685:#define DT_NODE_HAS_STATUS(node_id, status) \ 2707:#define DT_HAS_COMPAT_STATUS_OKAY(compat) \ 2716:#define DT_NUM_INST_STATUS_OKAY(compat) \ 2747:#define DT_NODE_HAS_COMPAT(node_id, compat) \ 2764:#define DT_NODE_HAS_COMPAT_STATUS(node_id, compat, status) \ 2780:#define DT_NODE_HAS_PROP(node_id, prop) \ 2800:#define DT_PHA_HAS_CELL_AT_IDX(node_id, pha, idx, cell) \ 2813:#define DT_PHA_HAS_CELL(node_id, pha, cell) \ 2857:#define DT_BUS(node_id) DT_CAT(node_id, _BUS) 2867:#define DT_BUS_LABEL(node_id) DT_PROP(DT_BUS(node_id), label) __DEPRECATED_MACRO 2897:#define DT_ON_BUS(node_id, bus) IS_ENABLED(DT_CAT3(node_id, _BUS_, bus)) 2915:#define DT_DRV_INST(inst) DT_INST(inst, DT_DRV_COMPAT) 2926:#define DT_INST_CHILD(inst, child) \ 2943:#define DT_INST_FOREACH_CHILD(inst, fn) \ 2959:#define DT_INST_FOREACH_CHILD_SEP(inst, fn, sep) \ 2977:#define DT_INST_FOREACH_CHILD_VARGS(inst, fn, ...) \ 2994:#define DT_INST_FOREACH_CHILD_SEP_VARGS(inst, fn, sep, ...) \ 3008:#define DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, fn) \ 3025:#define DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(inst, fn, sep) \ 3041:#define DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(inst, fn, ...) \ 3059:#define DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS(inst, fn, sep, ...) \ 3068:#define DT_INST_ENUM_IDX(inst, prop) \ 3079:#define DT_INST_ENUM_IDX_OR(inst, prop, default_idx_value) \ 3088:#define DT_INST_PROP(inst, prop) DT_PROP(DT_DRV_INST(inst), prop) 3096:#define DT_INST_PROP_LEN(inst, prop) DT_PROP_LEN(DT_DRV_INST(inst), prop) 3107:#define DT_INST_PROP_HAS_IDX(inst, prop, idx) \ 3118:#define DT_INST_PROP_HAS_NAME(inst, prop, name) \ 3128:#define DT_INST_PROP_BY_IDX(inst, prop, idx) \ 3138:#define DT_INST_PROP_OR(inst, prop, default_value) \ 3147:#define DT_INST_LABEL(inst) DT_INST_PROP(inst, label) __DEPRECATED_MACRO 3158:#define DT_INST_STRING_TOKEN(inst, prop) \ 3168:#define DT_INST_STRING_UPPER_TOKEN(inst, prop) \ 3178:#define DT_INST_STRING_TOKEN_BY_IDX(inst, prop, idx) \ 3188:#define DT_INST_STRING_UPPER_TOKEN_BY_IDX(inst, prop, idx) \ 3199:#define DT_INST_PROP_BY_PHANDLE(inst, ph, prop) \ 3213:#define DT_INST_PROP_BY_PHANDLE_IDX(inst, phs, idx, prop) \ 3224:#define DT_INST_PHA_BY_IDX(inst, pha, idx, cell) \ 3236:#define DT_INST_PHA_BY_IDX_OR(inst, pha, idx, cell, default_value) \ 3247:#define DT_INST_PHA(inst, pha, cell) DT_INST_PHA_BY_IDX(inst, pha, 0, cell) 3257:#define DT_INST_PHA_OR(inst, pha, cell, default_value) \ 3269:#define DT_INST_PHA_BY_NAME(inst, pha, name, cell) \ 3281:#define DT_INST_PHA_BY_NAME_OR(inst, pha, name, cell, default_value) \ 3292:#define DT_INST_PHANDLE_BY_NAME(inst, pha, name) \ 3304:#define DT_INST_PHANDLE_BY_IDX(inst, prop, idx) \ 3315:#define DT_INST_PHANDLE(inst, prop) DT_INST_PHANDLE_BY_IDX(inst, prop, 0) 3324:#define DT_INST_REG_HAS_IDX(inst, idx) DT_REG_HAS_IDX(DT_DRV_INST(inst), idx) 3332:#define DT_INST_REG_ADDR_BY_IDX(inst, idx) DT_REG_ADDR_BY_IDX(DT_DRV_INST(inst), idx) 3340:#define DT_INST_REG_SIZE_BY_IDX(inst, idx) \ 3349:#define DT_INST_REG_ADDR_BY_NAME(inst, name) \ 3358:#define DT_INST_REG_SIZE_BY_NAME(inst, name) \ 3366:#define DT_INST_REG_ADDR(inst) DT_INST_REG_ADDR_BY_IDX(inst, 0) 3373:#define DT_INST_REG_SIZE(inst) DT_INST_REG_SIZE_BY_IDX(inst, 0) 3382:#define DT_INST_IRQ_BY_IDX(inst, idx, cell) \ 3392:#define DT_INST_IRQ_BY_NAME(inst, name, cell) \ 3401:#define DT_INST_IRQ(inst, cell) DT_INST_IRQ_BY_IDX(inst, 0, cell) 3408:#define DT_INST_IRQN(inst) DT_INST_IRQ(inst, irq) 3415:#define DT_INST_BUS(inst) DT_BUS(DT_DRV_INST(inst)) 3425:#define DT_INST_BUS_LABEL(inst) DT_BUS_LABEL(DT_DRV_INST(inst)) __DEPRECATED_MACRO 3434:#define DT_INST_ON_BUS(inst, bus) DT_ON_BUS(DT_DRV_INST(inst), bus) 3445:#define DT_INST_STRING_TOKEN_OR(inst, name, default_value) \ 3456:#define DT_INST_STRING_UPPER_TOKEN_OR(inst, name, default_value) \ 3482: * #define DT_DRV_COMPAT vnd_some_sensor 3491:#define DT_ANY_INST_ON_BUS_STATUS_OKAY(bus) \ 3526: * #define DT_DRV_COMPAT vnd_device 3527: * #define MY_FN(inst) DT_INST_PROP(inst, foobar), 3559:#define DT_INST_FOREACH_STATUS_OKAY(fn) \ 3576:#define DT_INST_FOREACH_STATUS_OKAY_VARGS(fn, ...) \ 3592:#define DT_INST_FOREACH_PROP_ELEM(inst, prop, fn) \ 3609:#define DT_INST_FOREACH_PROP_ELEM_VARGS(inst, prop, fn, ...) \ 3618:#define DT_INST_NODE_HAS_PROP(inst, prop) \ 3631:#define DT_INST_PHA_HAS_CELL_AT_IDX(inst, pha, idx, cell) \ 3643:#define DT_INST_PHA_HAS_CELL(inst, pha, cell) \ 3653:#define DT_INST_IRQ_HAS_IDX(inst, idx) DT_IRQ_HAS_IDX(DT_DRV_INST(inst), idx) 3663:#define DT_INST_IRQ_HAS_CELL_AT_IDX(inst, idx, cell) \ 3673:#define DT_INST_IRQ_HAS_CELL(inst, cell) \ 3682:#define DT_INST_IRQ_HAS_NAME(inst, name) \ 3691:#define DT_PATH_INTERNAL(...) \ 3698:#define DT_S_PREFIX(name) _S_##name 3714:#define DT_CAT(a1, a2) a1 ## a2 3716:#define DT_CAT3(a1, a2, a3) a1 ## a2 ## a3 3718:#define DT_CAT4(a1, a2, a3, a4) a1 ## a2 ## a3 ## a4 3720:#define DT_CAT5(a1, a2, a3, a4, a5) a1 ## a2 ## a3 ## a4 ## a5 3722:#define DT_CAT6(a1, a2, a3, a4, a5, a6) a1 ## a2 ## a3 ## a4 ## a5 ## a6 3724:#define DT_CAT7(a1, a2, a3, a4, a5, a6, a7) \ 3727:#define DT_CAT8(a1, a2, a3, a4, a5, a6, a7, a8) \ 3730: * If you need to define a bigger DT_CATN(), do so here. Don't leave 3735:#define DT_DASH(...) MACRO_MAP_CAT(DT_DASH_PREFIX, __VA_ARGS__) 3737:#define DT_DASH_PREFIX(name) _##name 3739:#define DT_NODE_HAS_STATUS_INTERNAL(node_id, status) \ 3742:#define DT_COMPAT_ON_BUS_INTERNAL(compat, bus) \
2023-01-10 Tuesday - to obtain value of a given device tree node property:
7166 /* Generic property macros: */ 7167 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_IDX_0_EXISTS 1 7168 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_IDX_0_PH DT_N_S_soc_S_peripheral_40000000_S_gpio_1 7169 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_IDX_0_VAL_pin 14 7170 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_IDX_0_VAL_pin_EXISTS 1 7171 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_IDX_0_VAL_flags 0 7172 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_IDX_0_VAL_flags_EXISTS 1 7173 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_FOREACH_PROP_ELEM(fn) fn(DT_N_S_soc_S_peripheral_40000 000_S_flexcomm_89000_S_kx132_1, drdy_gpios, 0) 7174 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_FOREACH_PROP_ELEM_VARGS(fn, ...) fn(DT_N_S_soc_S_perip heral_40000000_S_flexcomm_89000_S_kx132_1, drdy_gpios, 0, __VA_ARGS__) 7175 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_LEN 1 7176 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_drdy_gpios_EXISTS 1 7177 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_reg {1 /* 0x1 */} 7178 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_reg_IDX_0 1 7179 #define DT_N_S_soc_S_peripheral_40000000_S_flexcomm_89000_S_kx132_1_P_reg_IDX_0_EXISTS 1
^ HAL level driver code
This section intended for findings about Hardware Abstraction Layer device driver code, normally provided by MCU or part manufacturer engineering teams. A starting search point is NXP's GPIO controller HAL code base:
zephyr/drivers/gpio$ vi gpio_mcux_lpc.c
2022-12-11 Sunday
In some SPI debugging efforts on a board bring-up task, we're also interested to review NXP's HAL level SPI driver sources. Excerpts from a successful build, which appear to identify by name the NXP SPI source file of interest:
[77/184] Building C object zephyr/drivers/spi/CMa keFiles/drivers__spi.dir/spi_mcux_flexcomm.c.obj^[[K^M [78/184] Linking C static library zephyr/soc/arm/common/cortex_m/libsoc__arm__common__cortex_m.a
Source file `spi_mcux_flexcomm.c` is found in Zephyr 3.2.0's source tree path `zephyr/drivers/spi/spi_mcux_flexcomm.c`.
^ Supporting and Tangential Topics
Following sections hold notes and links to topics, which are themselves supporting but not specifically RTOS driver in their content:
^ Sensors to seek for Zephyr demo exercising
Keywords: sensor interrupts : sensor triggers : Zephyr triggers : sensor interrupt support
[ ] MCP9808 I2C based temperature sensor, datasheet, long lead times at Mouser
^ References
1. https://github.com/zephyrproject-rtos/zephyr/issues/30133