From bccc75b19a00dd97147c8f31daf86142cad66b36 Mon Sep 17 00:00:00 2001 From: Yuval Peress Date: Wed, 20 Nov 2024 13:11:38 -0700 Subject: [PATCH] sensors: generate attributes/channels/triggers Add code generation for the sensor attribute, channel, and trigger enums. Included in this is the ability to query the sensor's information statically using the compatible string similar to how devicetree does it. Signed-off-by: Yuval Peress --- doc/CMakeLists.txt | 18 +- doc/_extensions/zephyr/sensor/__init__.py | 22 ++ doc/conf.py | 7 +- doc/hardware/peripherals/sensor/index.rst | 67 ++++ doc/zephyr.doxyfile.in | 1 + drivers/sensor/CMakeLists.txt | 2 + .../sensor/asahi_kasei/akm09918c/sensor.yaml | 24 ++ drivers/sensor/attributes.yaml | 68 ++++ drivers/sensor/bosch/bma4xx/sensor.yaml | 34 ++ drivers/sensor/bosch/bmi160/sensor.yaml | 50 +++ drivers/sensor/channels.yaml | 244 ++++++++++++++ drivers/sensor/codegen.cmake | 33 ++ drivers/sensor/triggers.yaml | 52 +++ drivers/sensor/units.yaml | 88 +++++ include/zephyr/drivers/sensor.h | 313 +----------------- include/zephyr/drivers/sensor_spec.h | 100 ++++++ scripts/sensors/gen_defines.py | 112 +++++++ scripts/sensors/sensor_constants.h.j2 | 157 +++++++++ tests/drivers/sensor/generator/CMakeLists.txt | 17 + tests/drivers/sensor/generator/prj.conf | 17 + tests/drivers/sensor/generator/test_deps.yaml | 28 ++ .../drivers/sensor/generator/test_macros.cpp | 70 ++++ .../drivers/sensor/generator/test_sensor.yaml | 26 ++ tests/drivers/sensor/generator/testcase.yaml | 11 + 24 files changed, 1245 insertions(+), 316 deletions(-) create mode 100644 doc/_extensions/zephyr/sensor/__init__.py create mode 100644 drivers/sensor/asahi_kasei/akm09918c/sensor.yaml create mode 100644 drivers/sensor/attributes.yaml create mode 100644 drivers/sensor/bosch/bma4xx/sensor.yaml create mode 100644 drivers/sensor/bosch/bmi160/sensor.yaml create mode 100644 drivers/sensor/channels.yaml create mode 100644 drivers/sensor/codegen.cmake create mode 100644 drivers/sensor/triggers.yaml create mode 100644 drivers/sensor/units.yaml create mode 100644 include/zephyr/drivers/sensor_spec.h create mode 100644 scripts/sensors/gen_defines.py create mode 100644 scripts/sensors/sensor_constants.h.j2 create mode 100644 tests/drivers/sensor/generator/CMakeLists.txt create mode 100644 tests/drivers/sensor/generator/prj.conf create mode 100644 tests/drivers/sensor/generator/test_deps.yaml create mode 100644 tests/drivers/sensor/generator/test_macros.cpp create mode 100644 tests/drivers/sensor/generator/test_sensor.yaml create mode 100644 tests/drivers/sensor/generator/testcase.yaml diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index bcabb520a79a3fe..468dee8d43d7d78 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -96,6 +96,12 @@ endfunction() #------------------------------------------------------------------------------- # Doxygen (standalone) +set(CMAKE_C_ARCHIVE_CREATE " cr ") +set(CMAKE_C_ARCHIVE_FINISH " s ") +add_library(zephyr_interface INTERFACE) +include(${ZEPHYR_BASE}/drivers/sensor/codegen.cmake) +set_target_properties(zephyr_sensor.constants PROPERTIES LINKER_LANGUAGE C) + set(DOXY_OUT ${CMAKE_CURRENT_BINARY_DIR}/doxygen) set(DOXYFILE_IN ${CMAKE_CURRENT_LIST_DIR}/zephyr.doxyfile.in) set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/zephyr.doxyfile) @@ -116,6 +122,8 @@ set_target_properties( ADDITIONAL_CLEAN_FILES "${DOXY_OUT}" ) +add_dependencies(doxygen zephyr_sensor.constants.__generate_constants) + #------------------------------------------------------------------------------- # devicetree @@ -149,6 +157,8 @@ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${GEN_DEVICETREE_ #------------------------------------------------------------------------------- # html +set(SPHINXOPTS_EXTRA ${SPHINXOPTS_EXTRA} -D sensor_yaml_files="${sensor_yaml_files}") + add_doc_target( html COMMAND ${CMAKE_COMMAND} -E env ${SPHINX_ENV} OUTPUT_DIR=${DOCS_HTML_DIR} @@ -172,7 +182,7 @@ set_target_properties( ADDITIONAL_CLEAN_FILES "${DOCS_SRC_DIR};${DOCS_HTML_DIR};${DOCS_DOCTREE_DIR}" ) -add_dependencies(html devicetree) +add_dependencies(html devicetree zephyr_sensor.constants.__generate_constants) #------------------------------------------------------------------------------- # html-live @@ -202,7 +212,7 @@ set_target_properties( ADDITIONAL_CLEAN_FILES "${DOCS_SRC_DIR};${DOCS_HTML_DIR};${DOCS_DOCTREE_DIR}" ) -add_dependencies(html-live devicetree) +add_dependencies(html-live devicetree zephyr_sensor.constants.__generate_constants) #------------------------------------------------------------------------------- # pdf @@ -230,7 +240,7 @@ set_target_properties( ADDITIONAL_CLEAN_FILES "${DOCS_SRC_DIR};${DOCS_LATEX_DIR};${DOCS_DOCTREE_DIR}" ) -add_dependencies(latex devicetree) +add_dependencies(latex devicetree zephyr_sensor.constants.__generate_constants) if(LATEX_PDFLATEX_FOUND AND LATEXMK) if(WIN32) @@ -252,7 +262,7 @@ if(LATEX_PDFLATEX_FOUND AND LATEXMK) USES_TERMINAL ) - add_dependencies(pdf latex) + add_dependencies(pdf latex zephyr_sensor.constants.__generate_constants) endif() #------------------------------------------------------------------------------- diff --git a/doc/_extensions/zephyr/sensor/__init__.py b/doc/_extensions/zephyr/sensor/__init__.py new file mode 100644 index 000000000000000..b87d355527bf205 --- /dev/null +++ b/doc/_extensions/zephyr/sensor/__init__.py @@ -0,0 +1,22 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: Apache-2.0 + +from typing import Any + +from sphinx.application import Sphinx + +__version__ = "0.0.1" + +def setup(app: Sphinx) -> dict[str, Any]: + """Introduce the sensor utilities""" + + # Add the sensor_yaml_files configuration value + app.add_config_value( + name="sensor_yaml_files", default="", rebuild="html", types=str + ) + + return { + "version": __version__, + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/doc/conf.py b/doc/conf.py index fd1ab005388d169..29359ea8239985f 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -94,6 +94,7 @@ "zephyr.external_content", "zephyr.domain", "zephyr.api_overview", + "zephyr.sensor", ] # Only use image conversion when it is really needed, e.g. LaTeX build. @@ -249,7 +250,11 @@ doxyrunner_doxyfile = ZEPHYR_BASE / "doc" / "zephyr.doxyfile.in" doxyrunner_outdir = ZEPHYR_BUILD / "doxygen" doxyrunner_fmt = True -doxyrunner_fmt_vars = {"ZEPHYR_BASE": str(ZEPHYR_BASE), "ZEPHYR_VERSION": version} +doxyrunner_fmt_vars = { + "ZEPHYR_BASE": str(ZEPHYR_BASE), + "CMAKE_BINARY_DIR": str(ZEPHYR_BUILD.parent), + "ZEPHYR_VERSION": version, +} doxyrunner_outdir_var = "DOXY_OUT" # -- Options for zephyr.doxybridge plugin --------------------------------- diff --git a/doc/hardware/peripherals/sensor/index.rst b/doc/hardware/peripherals/sensor/index.rst index e9c4979cd0768c7..8d9fe560f433098 100644 --- a/doc/hardware/peripherals/sensor/index.rst +++ b/doc/hardware/peripherals/sensor/index.rst @@ -97,6 +97,71 @@ Implementing Read and Decode * MUST implement :c:type:`sensor_get_decoder_t` returning the :c:struct:`sensor_decoder_api` for that device type. +Adding your ``sensor.yaml`` +=========================== + +Sensor YAML files can be added to enhance your driver writing experience. Each +sensor can introduce a ``sensor.yaml`` file which follows the schema described +in `pw_sensor`_. In tree, the file will automatically be picked up by the build +system and entries will be created for your driver. See the example at +``tests/drivers/sensor/generator/`` for the macro usage, you can do things +like: + +* Get the number of attributes, channels, and triggers supported by a specific + compatible string. +* Get the number of instances of a specific channel of a sensor. +* Check if a driver supports setting a specific attribute/channel pair. +* Automatically index your drivers into the Zephyr documentation + +The primary use case for these is to verify that a DT entry's compatible value +supports specific requirements. For example: + +* Assume we're building a lid angle calculation which requires 2 + accelerometers. These accelerometers will be specified in the devicetree + using the chosen nodes so we can access them via ``DT_CHOSEN(lid_accel)`` + and ``DT_CHOSEN(base_accel)``. +* We can now run a static assert like + ``BUILD_ASSERT(SENSOR_SPEC_CHAN_INST_COUNT(DT_PROP(DT_CHOSEN(lid_accel), compatible), accel_xyz) > 0);`` + +Additional features are in development and will allow drivers to: + +* Generate boilerplate code for the sensor API such as attribute set/get, + submit, etc. +* Generate common config/data struct definitions. +* Abstract the bus API so you can read/write registers without worrying about + i2c/spi/etc. +* Optimize memory allocation based to YAML metadata. Using the static data + available in the ``sensor.yaml`` file, we will be able to allocate the right + size buffer for streaming and one-shot reading APIs as well as client side + buffers. + +Adding sensors out of tree +-------------------------- + +Zephyr supports adding sensors from modules. There are 3 CMake variables that +should be updated to make that happen: + +* ``ZEPHYR_EXTRA_SENSOR_YAML_FILES``: this is a cmake list containing full + paths to sensor YAML files that are not in the Zephyr tree. +* ``ZEPHYR_EXTRA_SENSOR_INCLUDE_PATHS``: this is a cmake list containing full + paths to directories which contains custom attributes, channels, triggers, or + units. These paths are directories which the ``zephyr_sensor_library`` cmake + function will use to find definition files which are included in your sensor + YAML file. It's similar to header include paths. +* ``ZEPHYR_EXTRA_SENSOR_DEFINITION_FILES``: this is a cmake list containing + full paths to files containing custom attributes, channels, triggers, or + units. The file list is used to determine when the sensor library needs to be + rebuilt. + +Why ``sensor.yaml`` files? +-------------------------- + +* We already use YAML files to describe boards. +* They allow us to add metadata per driver type. See below for a comparison to + devicetree. +* They're portable. Downstream users may implement additional generators to do + new things. + .. _sensor-api-reference: API Reference @@ -104,3 +169,5 @@ API Reference .. doxygengroup:: sensor_interface .. doxygengroup:: sensor_emulator_backend + +.. _`pw_sensor`: https://pigweed.dev/pw_sensor/py/ diff --git a/doc/zephyr.doxyfile.in b/doc/zephyr.doxyfile.in index 036bef82c66245f..b1ef733589adcfb 100644 --- a/doc/zephyr.doxyfile.in +++ b/doc/zephyr.doxyfile.in @@ -972,6 +972,7 @@ WARN_LOGFILE = INPUT = @ZEPHYR_BASE@/doc/_doxygen/mainpage.md \ @ZEPHYR_BASE@/doc/_doxygen/groups.dox \ + @CMAKE_BINARY_DIR@/zephyr_sensor.constants/ \ @ZEPHYR_BASE@/kernel/include/kernel_arch_interface.h \ @ZEPHYR_BASE@/include/zephyr/arch/cache.h \ @ZEPHYR_BASE@/include/zephyr/sys/atomic.h \ diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index ca65a4128003c55..eebfeda58b2e8d7 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -64,6 +64,8 @@ add_subdirectory_ifdef(CONFIG_VEAA_X_3 veaa_x_3) add_subdirectory_ifdef(CONFIG_VOLTAGE_DIVIDER voltage_divider) add_subdirectory_ifdef(CONFIG_TACH_ENE_KB1200 ene_tach_kb1200) +include(codegen.cmake) + zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/sensor.h) zephyr_library() diff --git a/drivers/sensor/asahi_kasei/akm09918c/sensor.yaml b/drivers/sensor/asahi_kasei/akm09918c/sensor.yaml new file mode 100644 index 000000000000000..e6a9cc1367bc8c4 --- /dev/null +++ b/drivers/sensor/asahi_kasei/akm09918c/sensor.yaml @@ -0,0 +1,24 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: BDS-3-Clause + +deps: + - "drivers/sensor/attributes.yaml" + - "drivers/sensor/channels.yaml" + - "drivers/sensor/triggers.yaml" + - "drivers/sensor/units.yaml" +compatible: + org: "asahi-kasei" + part: "akm09918c" +supported-buses: + - i2c +channels: + magn_xyz: [] + magn_x: [] + magn_y: [] + magn_z: [] +attributes: + - attribute: "sampling_frequency" + channel: "magn_xyz" + units: "frequency" +extras: + doc-link: "asahi_kasei_akm09918c_driver" diff --git a/drivers/sensor/attributes.yaml b/drivers/sensor/attributes.yaml new file mode 100644 index 000000000000000..fa65afcd9371b86 --- /dev/null +++ b/drivers/sensor/attributes.yaml @@ -0,0 +1,68 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: BDS-3-Clause + +attributes: + sampling_frequency: + name: "sampling rate" + description: > + Sensor sampling frequency, i.e. how many times a second the sensor takes a measurement. + lower_thresh: + name: "lower threshold" + description: "Lower threshold for trigger." + upper_thresh: + name: "upper threshold" + description: "Upper threshold for trigger." + slope_th: + name: "any motion threshold" + description: "Threshold for any-motion (slope) trigger." + slope_dur: + name: "slope value threshold duration" + description: > + Duration for which the slope values needs to be outside the threshold for the trigger to + fire. + hysteresis: + name: "hysteresis" + description: "Hysteresis for trigger thresholds." + oversampling: + name: "oversampling factor" + description: "Oversampling factor" + full_scale: + name: "range" + description: "Sensor range, in SI units." + offset: + name: "value offset" + description: > + The sensor value returned will be altered by the amount indicated by offset: final_value = + sensor_value + offset. + calib_target: + name: "calibration target" + description: > + Calibration target. This will be used by the internal chip's algorithms to calibrate itself + on a certain axis, or all of them. + configuration: + name: "operating mode" + description: "Configure the operating modes of a sensor." + calibration: + name: "calibration value" + description: "Set a calibration value needed by a sensor." + feature_mask: + name: "enabled/disabled feature" + description: "Enable/disable sensor features" + alert: + name: "alert threshold" + description: "Alert threshold or alert enable/disable" + ff_dur: + name: "free-fall duration" + description: > + Free-fall duration represented in milliseconds. If the sampling frequency is changed during + runtime, this attribute should be set to adjust freefall duration to the new sampling + frequency. + batch_duration: + name: "batch duration" + description: "Hardware batch duration in ticks" + gain: + name: "gain" + description: "Configure the gain of a sensor." + resolution: + name: "resolution" + description: "Configure the resolution of a sensor." diff --git a/drivers/sensor/bosch/bma4xx/sensor.yaml b/drivers/sensor/bosch/bma4xx/sensor.yaml new file mode 100644 index 000000000000000..5a2d36d0af316f9 --- /dev/null +++ b/drivers/sensor/bosch/bma4xx/sensor.yaml @@ -0,0 +1,34 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: BDS-3-Clause + +deps: + - "drivers/sensor/attributes.yaml" + - "drivers/sensor/channels.yaml" + - "drivers/sensor/triggers.yaml" + - "drivers/sensor/units.yaml" +compatible: + org: "bosch" + part: "bma4xx" +supported-buses: + - i2c + - spi +description: | + Sensor driver implementation for bma422 and bma423. +channels: + accel_x: [] + accel_y: [] + accel_z: [] + accel_xyz: [] + die_temp: [] +attributes: + - attribute: "sampling_frequency" + channel: "accel_xyz" + units: "frequency" + - attribute: "full_scale" + channel: "accel_xyz" + units: "acceleration" + - attribute: "offset" + channel: "accel_xyz" + units: "acceleration" +extras: + doc-link: "bosch_bma4xx_driver" diff --git a/drivers/sensor/bosch/bmi160/sensor.yaml b/drivers/sensor/bosch/bmi160/sensor.yaml new file mode 100644 index 000000000000000..5b7ff72808fb3c4 --- /dev/null +++ b/drivers/sensor/bosch/bmi160/sensor.yaml @@ -0,0 +1,50 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: BDS-3-Clause + +deps: + - "drivers/sensor/attributes.yaml" + - "drivers/sensor/channels.yaml" + - "drivers/sensor/triggers.yaml" + - "drivers/sensor/units.yaml" +compatible: + org: "bosch" + part: "bmi160" +supported-buses: + - i2c + - spi +description: | + Sensor driver implementation for bmi160. +channels: + accel_x: [] + accel_y: [] + accel_z: [] + accel_xyz: [] + gyro_x: [] + gyro_y: [] + gyro_z: [] + gyro_xyz: [] + die_temp: [] +attributes: + - attribute: "sampling_frequency" + channel: "accel_xyz" + units: "frequency" + - attribute: "sampling_frequency" + channel: "gyro_xyz" + units: "frequency" + - attribute: "full_scale" + channel: "accel_xyz" + units: "acceleration" + - attribute: "full_scale" + channel: "gyro_xyz" + units: "angular_velocity" + - attribute: "offset" + channel: "accel_xyz" + units: "acceleration" + - attribute: "offset" + channel: "gyro_xyz" + units: "angular_velocity" +triggers: + - data_ready + - delta +extras: + doc-link: "bosch_bmi160_driver" diff --git a/drivers/sensor/channels.yaml b/drivers/sensor/channels.yaml new file mode 100644 index 000000000000000..6225fda3e1439c4 --- /dev/null +++ b/drivers/sensor/channels.yaml @@ -0,0 +1,244 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: BDS-3-Clause + +channels: + accel_x: + name: "Acceleration X axis" + description: "Acceleration on the X axis, in m/s^2." + units: "acceleration" + accel_y: + name: "Acceleration Y axis" + description: "Acceleration on the Y axis, in m/s^2." + units: "acceleration" + accel_z: + name: "Acceleration Z axis" + description: "Acceleration on the Z axis, in m/s^2." + units: "acceleration" + accel_xyz: + name: "Acceleration X, Y, & Z axes" + description: "Acceleration on the X, Y and Z axes." + units: "acceleration" + gyro_x: + name: "Angular velocity X axis" + description: "Angular velocity around the X axis, in radians/s." + units: "angular_velocity" + gyro_y: + name: "Angular velocity Y axis" + description: "Angular velocity around the Y axis, in radians/s." + units: "angular_velocity" + gyro_z: + name: "Angular velocity Z axis" + description: "Angular velocity around the Z axis, in radians/s." + units: "angular_velocity" + gyro_xyz: + name: "Angular velocity X, Y, & Z axes" + description: "Angular velocity around the X, Y and Z axes." + units: "angular_velocity" + magn_x: + name: "Magnetic field X axis" + description: "Magnetic field on the X axis, in Gauss." + units: "magnetic_field" + magn_y: + name: "Magnetic field Y axis" + description: "Magnetic field on the Y axis, in Gauss." + units: "magnetic_field" + magn_z: + name: "Magnetic field Z axis" + description: "Magnetic field on the Z axis, in Gauss." + units: "magnetic_field" + magn_xyz: + name: "Magnetic field X, Y, & Z axes" + description: "Magnetic field on the X, Y and Z axes." + units: "magnetic_field" + die_temp: + name: "Internal die teperature" + description: "Device die temperature in degrees Celsius." + units: "temperature" + ambient_temp: + name: "Ambient temperature" + description: "Ambient temperature in degrees Celsius." + units: "temperature" + press: + name: "Pressure" + description: "Pressure in kilopascal." + units: "pressure" + prox: + name: "Proximity" + description: "Proximity. Adimensional. A value of 1 indicates that an object is close." + units: "near" + humidity: + name: "HUMIDITY" + description: "Humidity, in percent." + units: "percent" + light: + name: "LIGHT" + description: "Illuminance in visible spectrum, in lux." + units: "illuminance" + ir: + name: "IR" + description: "Illuminance in infra-red spectrum, in lux." + units: "illuminance" + red: + name: "RED" + description: "Illuminance in red spectrum, in lux." + units: "illuminance" + green: + name: "GREEN" + description: "Illuminance in green spectrum, in lux." + units: "illuminance" + blue: + name: "BLUE" + description: "Illuminance in blue spectrum, in lux." + units: "illuminance" + altitude: + name: "ALTITUDE" + description: "Altitude, in meters" + units: "distance" + pm_1_0: + name: "PM_1_0" + description: "1.0 micro-meters Particulate Matter, in ug/m^3" + units: "density" + pm_2_5: + name: "PM_2_5" + description: "2.5 micro-meters Particulate Matter, in ug/m^3" + units: "density" + pm_10: + name: "PM_10" + description: "10 micro-meters Particulate Matter, in ug/m^3" + units: "density" + distance: + name: "DISTANCE" + description: "Distance. From sensor to target, in meters" + units: "distance" + co2: + name: "CO2" + description: "CO2 level, in parts per million (ppm)" + units: "concentration" + o2: + name: "CO2" + description: "O2 level, in parts per million (ppm)" + units: "concentration" + voc: + name: "VOC" + description: "VOC level, in parts per billion (ppb)" + units: "concentration" + gas_res: + name: "GAS_RES" + description: "Gas sensor resistance in ohms." + units: "resistance" + voltage: + name: "VOLTAGE" + description: "Voltage, in volts" + units: "volts" + vshunt: + name: "VSHUNT" + description: "Current Shunt Voltage in milli-volts" + units: "volts" + current: + name: "CURRENT" + description: "Current, in amps" + units: "current" + power: + name: "POWER" + description: "Power in watts" + units: "power" + resistance: + name: "RESISTANCE" + description: "Resistance , in Ohm" + units: "resistance" + rotation: + name: "ROTATION" + description: "Angular rotation, in degrees" + units: "angle" + pos_dx: + name: "Position change X axis" + description: "Position change on the X axis, in points." + units: "points" + pos_dy: + name: "Position change Y axis" + description: "Position change on the Y axis, in points." + units: "points" + pos_dz: + name: "Position change Z axis" + description: "Position change on the Z axis, in points." + units: "points" + pos_dxyz: + name: "Position change X, Y, & Z axes" + description: "Position change in 3D, in points." + units: "points" + rpm: + name: "RPM" + description: "Revolutions per minute, in RPM." + units: "angular_velocity" + gauge_voltage: + name: "GAUGE_VOLTAGE" + description: "Voltage, in volts" + units: "volts" + gauge_avg_current: + name: "GAUGE_AVG_CURRENT" + description: "Average current, in amps" + units: "current" + gauge_stdby_current: + name: "GAUGE_STDBY_CURRENT" + description: "Standby current, in amps" + units: "current" + gauge_max_load_current: + name: "GAUGE_MAX_LOAD_CURRENT" + description: "Max load current, in amps" + units: "current" + gauge_temp: + name: "GAUGE_TEMP" + description: "Gauge temperature" + units: "temperature" + gauge_state_of_charge: + name: "GAUGE_STATE_OF_CHARGE" + description: "State of charge measurement in %" + units: "percent" + gauge_full_charge_capacity: + name: "GAUGE_FULL_CHARGE_CAPACITY" + description: "Full Charge Capacity in mAh" + units: "electrical_charge" + gauge_remaining_charge_capacity: + name: "GAUGE_REMAINING_CHARGE_CAPACITY" + description: "Remaining Charge Capacity in mAh" + units: "electrical_charge" + gauge_nom_avail_capacity: + name: "GAUGE_NOM_AVAIL_CAPACITY" + description: "Nominal Available Capacity in mAh" + units: "electrical_charge" + gauge_full_avail_capacity: + name: "GAUGE_FULL_AVAIL_CAPACITY" + description: "Full Available Capacity in mAh" + units: "electrical_charge" + gauge_avg_power: + name: "GAUGE_AVG_POWER" + description: "Average power in mW" + units: "power" + gauge_state_of_health: + name: "GAUGE_STATE_OF_HEALTH" + description: "State of health measurement in %" + units: "percent" + gauge_time_to_empty: + name: "GAUGE_TIME_TO_EMPTY" + description: "Time to empty in minutes" + units: "time" + gauge_time_to_full: + name: "GAUGE_TIME_TO_FULL" + description: "Time to full in minutes" + units: "time" + gauge_cycle_count: + name: "Cycle count" + description: "Cycle count (total number of charge/discharge cycles)" + units: "battery_cycles" + gauge_design_voltage: + name: "GAUGE_DESIGN_VOLTAGE" + description: "Design voltage of cell in V (max voltage)" + units: "volts" + gauge_desired_voltage: + name: "GAUGE_DESIRED_VOLTAGE" + description: "Desired voltage of cell in V (nominal voltage)" + units: "volts" + gauge_desired_charging_current: + name: "GAUGE_DESIRED_CHARGING_CURRENT" + description: "Desired charging current in mA" + units: "current" diff --git a/drivers/sensor/codegen.cmake b/drivers/sensor/codegen.cmake new file mode 100644 index 000000000000000..8c56dcfdb347b10 --- /dev/null +++ b/drivers/sensor/codegen.cmake @@ -0,0 +1,33 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: Apache-2.0 + +# Search for all sensor.yaml files in Zephyr +set(sensor_yaml_search_path "${ZEPHYR_BASE}/drivers/sensor") +file(GLOB_RECURSE sensor_yaml_files RELATIVE ${sensor_yaml_search_path} + "${sensor_yaml_search_path}/**/sensor.yaml" +) +list(TRANSFORM sensor_yaml_files PREPEND "${sensor_yaml_search_path}/") + +# Create a library with the generated sensor_constants.h +# This library uses the sensor.yaml files in Zephyr plus any new yaml files +# added by the application via ZEPHYR_EXTRA_SENSOR_YAML_FILES +zephyr_sensor_library(zephyr_sensor.constants + OUT_HEADER + ${CMAKE_BINARY_DIR}/zephyr_sensor.constants/zephyr/generated/sensor_constants.h + OUT_INCLUDES + ${CMAKE_BINARY_DIR}/zephyr_sensor.constants/ + GENERATOR + "${ZEPHYR_BASE}/scripts/sensors/gen_defines.py" + GENERATOR_INCLUDES + ${ZEPHYR_BASE} + ${ZEPHYR_EXTRA_SENSOR_INCLUDE_PATHS} + INPUTS + ${CMAKE_CURRENT_LIST_DIR}/attributes.yaml + ${CMAKE_CURRENT_LIST_DIR}/channels.yaml + ${CMAKE_CURRENT_LIST_DIR}/triggers.yaml + ${CMAKE_CURRENT_LIST_DIR}/units.yaml + ${ZEPHYR_EXTRA_SENSOR_DEFINITION_FILES} + SOURCES + ${sensor_yaml_files} + ${ZEPHYR_EXTRA_SENSOR_YAML_FILES} +) diff --git a/drivers/sensor/triggers.yaml b/drivers/sensor/triggers.yaml new file mode 100644 index 000000000000000..478203ba0ff5485 --- /dev/null +++ b/drivers/sensor/triggers.yaml @@ -0,0 +1,52 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: BDS-3-Clause + +triggers: + timer: + name: "timer" + description: > + Timer-based trigger, useful when the sensor does not have an interrupt + line. + data_ready: + name: " data ready" + description: "Trigger fires whenever new data is ready." + delta: + name: "significant change" + description: > + Trigger fires when the selected channel varies significantly. + This includes any-motion detection when the channel is + acceleration or gyro. If detection is based on slope between + successive channel readings, the slope threshold is configured + via the @ref SENSOR_ATTR_SLOPE_TH and @ref SENSOR_ATTR_SLOPE_DUR + attributes. + near_far: + name: "near/far" + description: "Trigger fires when a near/far event is detected." + threshold: + name: "threshold reached" + description: > + Trigger fires when channel reading transitions configured + thresholds. The thresholds are configured via the @ref + SENSOR_ATTR_LOWER_THRESH, @ref SENSOR_ATTR_UPPER_THRESH, and + @ref SENSOR_ATTR_HYSTERESIS attributes. + tap: + name: "tap" + description: "Trigger fires when a single tap is detected." + double_tap: + name: "double tap" + description: "Trigger fires when a double tap is detected." + freefall: + name: "free fall" + description: "Trigger fires when a free fall is detected." + motion: + name: "motion" + description: "Trigger fires when motion is detected." + stationary: + name: "stationary" + description: "Trigger fires when no motion has been detected for a while." + fifo_watermark: + name: "FIFO watermark" + description: "Trigger fires when the FIFO watermark has been reached." + fifo_full: + name: "FIFO full" + description: "Trigger fires when the FIFO becomes full." diff --git a/drivers/sensor/units.yaml b/drivers/sensor/units.yaml new file mode 100644 index 000000000000000..a91f537f4a8e6ff --- /dev/null +++ b/drivers/sensor/units.yaml @@ -0,0 +1,88 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: BDS-3-Clause + +units: + acceleration: + name: "acceleration" + description: "change in velocity" + symbol: "m/s^2" + angular_velocity: + name: "angular velocity" + description: "rate of rotation" + symbol: "rad/s" + frequency: + name: "frequency" + description: "events per second" + symbol: "Hz" + magnetic_field: + name: "magnetic field" + description: "strength of magnetic field" + symbol: "Gs" + temperature: + name: "temperature" + description: "temperature" + symbol: "C" + pressure: + name: "pressure" + description: "pressure" + symbol: "kPa" + points: + name: "points" + description: "points" + symbol: "pts" + near: + name: "near" + description: "Boolean describing if an object is near or not" + symbol: "near" + battery_cycles: + name: "battery cycles" + description: "battery charge/discharge cycle count" + symbol: "cycles" + percent: + name: "percent" + description: "A relative count based on 100" + symbol: "%" + illuminance: + name: "illuminance" + description: "The brightness of a light source" + symbol: "lux" + distance: + name: "distance" + description: "The distance measured between 2 points." + symbol: "m" + density: + name: "density" + description: "Mass per unit of volume" + symbol: "ug/m^3" + concentration: + name: "concentration" + description: "Particle count per unit of volume" + symbol: "ppb" + resistance: + name: "electrical resistance" + description: "Electrical resistance" + symbol: "Ohm" + volts: + name: "volts" + description: "Electrical potential" + symbol: "V" + current: + name: "current" + description: "Electrical current" + symbol: "A" + power: + name: "power" + description: "Power (either electrical or neutonian)" + symbol: "W" + angle: + name: "angle" + description: "The angle between 2 vectors" + symbol: "rad" + electrical_charge: + name: "electrical charge" + description: "Electrical charge" + symbol: "Ah" + time: + name: "time" + description: "Time" + symbol: "s" diff --git a/include/zephyr/drivers/sensor.h b/include/zephyr/drivers/sensor.h index 6cd8cc708279780..325ac78fd6f383b 100644 --- a/include/zephyr/drivers/sensor.h +++ b/include/zephyr/drivers/sensor.h @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -55,230 +56,7 @@ struct sensor_value { int32_t val2; }; -/** - * @brief Sensor channels. - */ -enum sensor_channel { - /** Acceleration on the X axis, in m/s^2. */ - SENSOR_CHAN_ACCEL_X, - /** Acceleration on the Y axis, in m/s^2. */ - SENSOR_CHAN_ACCEL_Y, - /** Acceleration on the Z axis, in m/s^2. */ - SENSOR_CHAN_ACCEL_Z, - /** Acceleration on the X, Y and Z axes. */ - SENSOR_CHAN_ACCEL_XYZ, - /** Angular velocity around the X axis, in radians/s. */ - SENSOR_CHAN_GYRO_X, - /** Angular velocity around the Y axis, in radians/s. */ - SENSOR_CHAN_GYRO_Y, - /** Angular velocity around the Z axis, in radians/s. */ - SENSOR_CHAN_GYRO_Z, - /** Angular velocity around the X, Y and Z axes. */ - SENSOR_CHAN_GYRO_XYZ, - /** Magnetic field on the X axis, in Gauss. */ - SENSOR_CHAN_MAGN_X, - /** Magnetic field on the Y axis, in Gauss. */ - SENSOR_CHAN_MAGN_Y, - /** Magnetic field on the Z axis, in Gauss. */ - SENSOR_CHAN_MAGN_Z, - /** Magnetic field on the X, Y and Z axes. */ - SENSOR_CHAN_MAGN_XYZ, - /** Device die temperature in degrees Celsius. */ - SENSOR_CHAN_DIE_TEMP, - /** Ambient temperature in degrees Celsius. */ - SENSOR_CHAN_AMBIENT_TEMP, - /** Pressure in kilopascal. */ - SENSOR_CHAN_PRESS, - /** - * Proximity. Adimensional. A value of 1 indicates that an - * object is close. - */ - SENSOR_CHAN_PROX, - /** Humidity, in percent. */ - SENSOR_CHAN_HUMIDITY, - /** Illuminance in visible spectrum, in lux. */ - SENSOR_CHAN_LIGHT, - /** Illuminance in infra-red spectrum, in lux. */ - SENSOR_CHAN_IR, - /** Illuminance in red spectrum, in lux. */ - SENSOR_CHAN_RED, - /** Illuminance in green spectrum, in lux. */ - SENSOR_CHAN_GREEN, - /** Illuminance in blue spectrum, in lux. */ - SENSOR_CHAN_BLUE, - /** Altitude, in meters */ - SENSOR_CHAN_ALTITUDE, - - /** 1.0 micro-meters Particulate Matter, in ug/m^3 */ - SENSOR_CHAN_PM_1_0, - /** 2.5 micro-meters Particulate Matter, in ug/m^3 */ - SENSOR_CHAN_PM_2_5, - /** 10 micro-meters Particulate Matter, in ug/m^3 */ - SENSOR_CHAN_PM_10, - /** Distance. From sensor to target, in meters */ - SENSOR_CHAN_DISTANCE, - - /** CO2 level, in parts per million (ppm) **/ - SENSOR_CHAN_CO2, - /** O2 level, in parts per million (ppm) **/ - SENSOR_CHAN_O2, - /** VOC level, in parts per billion (ppb) **/ - SENSOR_CHAN_VOC, - /** Gas sensor resistance in ohms. */ - SENSOR_CHAN_GAS_RES, - - /** Voltage, in volts **/ - SENSOR_CHAN_VOLTAGE, - - /** Current Shunt Voltage in milli-volts **/ - SENSOR_CHAN_VSHUNT, - - /** Current, in amps **/ - SENSOR_CHAN_CURRENT, - /** Power in watts **/ - SENSOR_CHAN_POWER, - - /** Resistance , in Ohm **/ - SENSOR_CHAN_RESISTANCE, - - /** Angular rotation, in degrees */ - SENSOR_CHAN_ROTATION, - - /** Position change on the X axis, in points. */ - SENSOR_CHAN_POS_DX, - /** Position change on the Y axis, in points. */ - SENSOR_CHAN_POS_DY, - /** Position change on the Z axis, in points. */ - SENSOR_CHAN_POS_DZ, - /** Position change on the X, Y and Z axis, in points. */ - SENSOR_CHAN_POS_DXYZ, - - /** Revolutions per minute, in RPM. */ - SENSOR_CHAN_RPM, - - /** Voltage, in volts **/ - SENSOR_CHAN_GAUGE_VOLTAGE, - /** Average current, in amps **/ - SENSOR_CHAN_GAUGE_AVG_CURRENT, - /** Standby current, in amps **/ - SENSOR_CHAN_GAUGE_STDBY_CURRENT, - /** Max load current, in amps **/ - SENSOR_CHAN_GAUGE_MAX_LOAD_CURRENT, - /** Gauge temperature **/ - SENSOR_CHAN_GAUGE_TEMP, - /** State of charge measurement in % **/ - SENSOR_CHAN_GAUGE_STATE_OF_CHARGE, - /** Full Charge Capacity in mAh **/ - SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY, - /** Remaining Charge Capacity in mAh **/ - SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY, - /** Nominal Available Capacity in mAh **/ - SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY, - /** Full Available Capacity in mAh **/ - SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY, - /** Average power in mW **/ - SENSOR_CHAN_GAUGE_AVG_POWER, - /** State of health measurement in % **/ - SENSOR_CHAN_GAUGE_STATE_OF_HEALTH, - /** Time to empty in minutes **/ - SENSOR_CHAN_GAUGE_TIME_TO_EMPTY, - /** Time to full in minutes **/ - SENSOR_CHAN_GAUGE_TIME_TO_FULL, - /** Cycle count (total number of charge/discharge cycles) **/ - SENSOR_CHAN_GAUGE_CYCLE_COUNT, - /** Design voltage of cell in V (max voltage)*/ - SENSOR_CHAN_GAUGE_DESIGN_VOLTAGE, - /** Desired voltage of cell in V (nominal voltage) */ - SENSOR_CHAN_GAUGE_DESIRED_VOLTAGE, - /** Desired charging current in mA */ - SENSOR_CHAN_GAUGE_DESIRED_CHARGING_CURRENT, - - /** All channels. */ - SENSOR_CHAN_ALL, - - /** - * Number of all common sensor channels. - */ - SENSOR_CHAN_COMMON_COUNT, - - /** - * This and higher values are sensor specific. - * Refer to the sensor header file. - */ - SENSOR_CHAN_PRIV_START = SENSOR_CHAN_COMMON_COUNT, - - /** - * Maximum value describing a sensor channel type. - */ - SENSOR_CHAN_MAX = INT16_MAX, -}; - -/** - * @brief Sensor trigger types. - */ -enum sensor_trigger_type { - /** - * Timer-based trigger, useful when the sensor does not have an - * interrupt line. - */ - SENSOR_TRIG_TIMER, - /** Trigger fires whenever new data is ready. */ - SENSOR_TRIG_DATA_READY, - /** - * Trigger fires when the selected channel varies significantly. - * This includes any-motion detection when the channel is - * acceleration or gyro. If detection is based on slope between - * successive channel readings, the slope threshold is configured - * via the @ref SENSOR_ATTR_SLOPE_TH and @ref SENSOR_ATTR_SLOPE_DUR - * attributes. - */ - SENSOR_TRIG_DELTA, - /** Trigger fires when a near/far event is detected. */ - SENSOR_TRIG_NEAR_FAR, - /** - * Trigger fires when channel reading transitions configured - * thresholds. The thresholds are configured via the @ref - * SENSOR_ATTR_LOWER_THRESH, @ref SENSOR_ATTR_UPPER_THRESH, and - * @ref SENSOR_ATTR_HYSTERESIS attributes. - */ - SENSOR_TRIG_THRESHOLD, - - /** Trigger fires when a single tap is detected. */ - SENSOR_TRIG_TAP, - - /** Trigger fires when a double tap is detected. */ - SENSOR_TRIG_DOUBLE_TAP, - - /** Trigger fires when a free fall is detected. */ - SENSOR_TRIG_FREEFALL, - - /** Trigger fires when motion is detected. */ - SENSOR_TRIG_MOTION, - - /** Trigger fires when no motion has been detected for a while. */ - SENSOR_TRIG_STATIONARY, - - /** Trigger fires when the FIFO watermark has been reached. */ - SENSOR_TRIG_FIFO_WATERMARK, - - /** Trigger fires when the FIFO becomes full. */ - SENSOR_TRIG_FIFO_FULL, - /** - * Number of all common sensor triggers. - */ - SENSOR_TRIG_COMMON_COUNT, - - /** - * This and higher values are sensor specific. - * Refer to the sensor header file. - */ - SENSOR_TRIG_PRIV_START = SENSOR_TRIG_COMMON_COUNT, - - /** - * Maximum value describing a sensor trigger type. - */ - SENSOR_TRIG_MAX = INT16_MAX, -}; +#include /** * @brief Sensor trigger spec. @@ -290,80 +68,6 @@ struct sensor_trigger { enum sensor_channel chan; }; -/** - * @brief Sensor attribute types. - */ -enum sensor_attribute { - /** - * Sensor sampling frequency, i.e. how many times a second the - * sensor takes a measurement. - */ - SENSOR_ATTR_SAMPLING_FREQUENCY, - /** Lower threshold for trigger. */ - SENSOR_ATTR_LOWER_THRESH, - /** Upper threshold for trigger. */ - SENSOR_ATTR_UPPER_THRESH, - /** Threshold for any-motion (slope) trigger. */ - SENSOR_ATTR_SLOPE_TH, - /** - * Duration for which the slope values needs to be - * outside the threshold for the trigger to fire. - */ - SENSOR_ATTR_SLOPE_DUR, - /* Hysteresis for trigger thresholds. */ - SENSOR_ATTR_HYSTERESIS, - /** Oversampling factor */ - SENSOR_ATTR_OVERSAMPLING, - /** Sensor range, in SI units. */ - SENSOR_ATTR_FULL_SCALE, - /** - * The sensor value returned will be altered by the amount indicated by - * offset: final_value = sensor_value + offset. - */ - SENSOR_ATTR_OFFSET, - /** - * Calibration target. This will be used by the internal chip's - * algorithms to calibrate itself on a certain axis, or all of them. - */ - SENSOR_ATTR_CALIB_TARGET, - /** Configure the operating modes of a sensor. */ - SENSOR_ATTR_CONFIGURATION, - /** Set a calibration value needed by a sensor. */ - SENSOR_ATTR_CALIBRATION, - /** Enable/disable sensor features */ - SENSOR_ATTR_FEATURE_MASK, - /** Alert threshold or alert enable/disable */ - SENSOR_ATTR_ALERT, - /** Free-fall duration represented in milliseconds. - * If the sampling frequency is changed during runtime, - * this attribute should be set to adjust freefall duration - * to the new sampling frequency. - */ - SENSOR_ATTR_FF_DUR, - - /** Hardware batch duration in ticks */ - SENSOR_ATTR_BATCH_DURATION, - /* Configure the gain of a sensor. */ - SENSOR_ATTR_GAIN, - /* Configure the resolution of a sensor. */ - SENSOR_ATTR_RESOLUTION, - /** - * Number of all common sensor attributes. - */ - SENSOR_ATTR_COMMON_COUNT, - - /** - * This and higher values are sensor specific. - * Refer to the sensor header file. - */ - SENSOR_ATTR_PRIV_START = SENSOR_ATTR_COMMON_COUNT, - - /** - * Maximum value describing a sensor attribute type. - */ - SENSOR_ATTR_MAX = INT16_MAX, -}; - /** * @typedef sensor_trigger_handler_t * @brief Callback API upon firing of a trigger @@ -423,19 +127,6 @@ typedef int (*sensor_channel_get_t)(const struct device *dev, enum sensor_channel chan, struct sensor_value *val); -/** - * @brief Sensor Channel Specification - * - * A sensor channel specification is a unique identifier per sensor device describing - * a measurement channel. - * - * @note Typically passed by value as the size of a sensor_chan_spec is a single word. - */ -struct sensor_chan_spec { - uint16_t chan_type; /**< A sensor channel type */ - uint16_t chan_idx; /**< A sensor channel index */ -}; - /** @cond INTERNAL_HIDDEN */ /* Ensure sensor_chan_spec is sensibly sized to pass by value */ BUILD_ASSERT(sizeof(struct sensor_chan_spec) <= sizeof(uintptr_t), diff --git a/include/zephyr/drivers/sensor_spec.h b/include/zephyr/drivers/sensor_spec.h new file mode 100644 index 000000000000000..9981766b79cb818 --- /dev/null +++ b/include/zephyr/drivers/sensor_spec.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_SPEC_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_SPEC_H_ + +#include + +/** + * @addtogroup sensor_interface + * @{ + */ + +/** + * @def SENSOR_SPEC_COMPAT(compat) + * Generates the prefix for any sensor spec for a given compatible value + */ +#define SENSOR_SPEC_COMPAT(compat) UTIL_CAT(SENSOR_SPEC, DT_DASH(compat)) + +/** + * @def SENSOR_SPEC_COMPAT_EXISTS(compat) + * 1 if the given compat value is a sensor, 0 otherwise + */ +#define SENSOR_SPEC_COMPAT_EXISTS(compat) IS_ENABLED(UTIL_CAT(SENSOR_SPEC_COMPAT(compat), _EXISTS)) + +/** + * @def SENSOR_SPEC_CHAN_COUNT(compat) + * The number of channels supported by the sensor with the given compatible value + */ +#define SENSOR_SPEC_CHAN_COUNT(compat) UTIL_CAT(SENSOR_SPEC_COMPAT(compat), _CHAN_COUNT) + +/** + * @def SENSOR_SPEC_ATTR_COUNT(compat) + * The number of attributes supported by the sensor with the given compatible value + */ +#define SENSOR_SPEC_ATTR_COUNT(compat) UTIL_CAT(SENSOR_SPEC_COMPAT(compat), _ATTR_COUNT) + +/** + * @def SENSOR_SPEC_TRIG_COUNT(compat) + * The number of triggers supported by the sensor with the given compatible value + */ +#define SENSOR_SPEC_TRIG_COUNT(compat) UTIL_CAT(SENSOR_SPEC_COMPAT(compat), _TRIG_COUNT) + +/** + * @def SENSOR_SPEC_CHAN_INST_COUNT(compat, chan) + * The number of channel instances for the given compatible/channel pair. + */ +#define SENSOR_SPEC_CHAN_INST_COUNT(compat, chan) \ + UTIL_CAT(SENSOR_SPEC_COMPAT(compat), DT_DASH(CH, chan, COUNT)) + +/** + * @def SENSOR_SPEC_CHAN_INST(compat, chan, inst) + * The prefix used for a given compatible/channel/instance tuple. + */ +#define SENSOR_SPEC_CHAN_INST(compat, chan, inst) \ + UTIL_CAT(SENSOR_SPEC_COMPAT(compat), DT_DASH(CH, chan, inst)) + +/** + * @def SENSOR_SPEC_CHAN_INST_EXISTS(compat, chan, inst) + * 1 if the given compatible/channel/instance tuple exists, 0 otherwise + */ +#define SENSOR_SPEC_CHAN_INST_EXISTS(compat, chan, inst) \ + IS_ENABLED(UTIL_CAT(SENSOR_SPEC_CHAN_INST(compat, chan, inst), _EXISTS)) + +/** + * @def SENSOR_SPEC_CHAN_INST_NAME(compat, chan, inst) + * The C string name of the given channel instance of a sensor + */ +#define SENSOR_SPEC_CHAN_INST_NAME(compat, chan, inst) \ + UTIL_CAT(SENSOR_SPEC_CHAN_INST(compat, chan, inst), _NAME) + +/** + * @def SENSOR_SPEC_CHAN_INST_DESC(compat, chan, inst) + * The C string description of the given channel instance of a sensor + */ +#define SENSOR_SPEC_CHAN_INST_DESC(compat, chan, inst) \ + UTIL_CAT(SENSOR_SPEC_CHAN_INST(compat, chan, inst), _DESC) + +/** + * @def SENSOR_SPEC_CHAN_INST_SPEC(compat, chan, inst) + * The sensor_chan_spec values of the given channel instance of a sensor + */ +#define SENSOR_SPEC_CHAN_INST_SPEC(compat, chan, inst) \ + UTIL_CAT(SENSOR_SPEC_CHAN_INST(compat, chan, inst), _SPEC) + +/** + * @def SENSOR_SPEC_ATTR_EXISTS(compat, attr, chan) + * 1 if the given attribute/channel combination is configurable for the given sensor, 0 otherwise. + */ +#define SENSOR_SPEC_ATTR_EXISTS(compat, attr, chan) \ + IS_ENABLED(UTIL_CAT(SENSOR_SPEC_COMPAT(compat), DT_DASH(AT, attr, CH, chan, EXISTS))) + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_SPEC_H_ */ diff --git a/scripts/sensors/gen_defines.py b/scripts/sensors/gen_defines.py new file mode 100644 index 000000000000000..ce1ae6e81212e35 --- /dev/null +++ b/scripts/sensors/gen_defines.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: Apache-2.0 +import os +import re +import sys + +import pw_sensor.constants_generator as pw_sensor +import yaml +from jinja2 import Environment, FileSystemLoader + + +def getSensorCompatibleString(sensor: pw_sensor.SensorSpec) -> str: + """ + Concatenates the compatible org and part, replacing ',' and '-' with '_'. + + Args: + sensor: The sensor object with compatible.org and compatible.part attributes. + + Returns: + A string with the formatted compatible string. + """ + return re.sub(r"[\-\,]", "_", f"{sensor.compatible.org}_{sensor.compatible.part}") + + +def formatStringForC(string: None | str) -> str: + """ + Format a string (or None) to work inside a C string literal. + + Args: + string: The string to format (may be None) + + Returns: + A string that can be placed inside a C string. + """ + if string: + return string.strip().replace("\n", "\\n") + return "" + + +def enumerate_filter(iterable): + """ + Jinja filter used to enumerate an iterable + + Args: + iterable: Any iterable object + + Returns: + An enumeration of the iterable + """ + return enumerate(iterable) + + +def _sort_channel_keys(s: str) -> tuple[str, int]: + """ + Sort channel keys alphabetically except: + Items ending in _XYZ or _DXYZ should come after [_X, _Y, _Z] and + [_DX, _DY, _DZ] respectively. + Args: + s: String to sort + Returns: + A tuple with the substring to use for further sorting and a sorting + priority. + """ + text = s.upper() + if text.endswith("_XYZ"): + return (s[:-3], 1) # Sort XYZ after X, Y, Z + elif text.endswith("_DXYZ"): + return (s[:-4], 1) # Sort DXYZ after DX, DY, DZ + elif text.endswith(("_X", "_Y", "_Z")): + return (s[:-1], 0) # Sort single/double letter suffixes before XYZ + elif text.endswith(("_DX", "_DY", "_DZ")): + return (s[:-2], 0) # Sort single/double letter suffixes before DXYZ + else: + return (s, 2) # Keep other strings unchanged + + +def sort_sensor_channels(chan_list): + """ + Jinja sort function for sensor channel IDs with a preference for _x, _y, _z, and _xyz suffixes. + + Args: + chan_list: List of channel ID keys + + Returns: + Sorted list of the channel ID keys + """ + new_list = list(chan_list) + new_list.sort(key=_sort_channel_keys) + return new_list + + +def main() -> None: + definition = yaml.safe_load(sys.stdin) + spec: pw_sensor.InputSpec = pw_sensor.create_dataclass_from_dict( + cls=pw_sensor.InputSpec, + data=definition, + ) + script_dir = os.path.dirname(os.path.abspath(__file__)) + env = Environment(loader=FileSystemLoader(script_dir)) + env.globals["getSensorCompatibleString"] = getSensorCompatibleString + env.globals["formatStringForC"] = formatStringForC + env.globals["sort_sensor_channels"] = sort_sensor_channels + env.filters["enumerate"] = enumerate_filter + + template = env.get_template("sensor_constants.h.j2") + print(template.render({"spec": spec})) + + +if __name__ == "__main__": + main() diff --git a/scripts/sensors/sensor_constants.h.j2 b/scripts/sensors/sensor_constants.h.j2 new file mode 100644 index 000000000000000..cceb73a8de78016 --- /dev/null +++ b/scripts/sensors/sensor_constants.h.j2 @@ -0,0 +1,157 @@ +{# Copyright (c) 2024 Google Inc #} +{# SPDX-License-Identifier: BDS-3-Clause #} +/* Auto-generated file, do not edit */ +#ifndef _INCLUDE_ZEPHYR_GENERATED_SENSOR_CONSTANTS_H_ +#define _INCLUDE_ZEPHYR_GENERATED_SENSOR_CONSTANTS_H_ + +/** + * @addtogroup sensor_interface + * @{ + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Sensor channels. + */ +enum sensor_channel { +{% for chan_id in sort_sensor_channels(spec.channels.keys()) %} + /** + * {{spec.channels[chan_id].description.strip()}} + * + * Units: {{spec.units[spec.channels[chan_id].units].symbol}} + */ + SENSOR_CHAN_{{chan_id.upper().replace('-', '_')}}, +{% endfor %} + + /** All channels */ + SENSOR_CHAN_ALL, + + /** Number of all common sensor channels. */ + SENSOR_CHAN_COMMON_COUNT = (SENSOR_CHAN_ALL + 1), + + /** + * This and higher values are sensor specific. + * Refer to the sensor header file. + */ + SENSOR_CHAN_PRIV_START = SENSOR_CHAN_COMMON_COUNT, + + /** + * Maximum value describing a sensor channel type. + */ + SENSOR_CHAN_MAX = INT16_MAX, +}; + +/** + * @brief Sensor attribute types. + */ +enum sensor_attribute { +{% for attr_id, attr in spec.attributes.items() %} + /** + * {{attr.description.strip()}} + */ + SENSOR_ATTR_{{attr_id.upper().replace('-', '_')}}, +{% endfor %} + + /** Number of all common sensor attributes. */ + SENSOR_ATTR_COMMON_COUNT, + + /** + * This and higher values are sensor specific. + * Refer to the sensor header file. + */ + SENSOR_ATTR_PRIV_START = SENSOR_ATTR_COMMON_COUNT, + + /** + * Maximum value describing a sensor attribute type. + */ + SENSOR_ATTR_MAX = INT16_MAX, +}; + +/** + * @brief Sensor trigger types. + */ +enum sensor_trigger_type { +{% for trig_id, trig in spec.triggers.items() %} + /** + * {{trig.description.strip()}} + */ + SENSOR_TRIG_{{trig_id.upper().replace('-', '_')}}, +{% endfor %} + + /** Number of all common sensor triggers. */ + SENSOR_TRIG_COMMON_COUNT, + + /** + * This and higher values are sensor specific. + * Refer to the sensor header file. + */ + SENSOR_TRIG_PRIV_START = SENSOR_TRIG_COMMON_COUNT, + + /** + * Maximum value describing a sensor trigger type. + */ + SENSOR_TRIG_MAX = INT16_MAX, +}; + +/** + * @brief Sensor Channel Specification + * + * A sensor channel specification is a unique identifier per sensor device describing + * a measurement channel. + * + * @note Typically passed by value as the size of a sensor_chan_spec is a single word. + */ +struct sensor_chan_spec { + uint16_t chan_type; /**< A sensor channel type */ + uint16_t chan_idx; /**< A sensor channel index */ +}; + +#define SENSOR_SPEC_FOREACH_COMPAT(fn) \ +{%- for sensor in spec.sensors.values() %} + fn({{ getSensorCompatibleString(sensor) }}) {% if not loop.last %}\ + {%- endif %} +{%- endfor %} + +{% for sensor in spec.sensors.values() %} +/* + * Definitions for {{sensor.compatible.org}},{{sensor.compatible.part}} + */ +#define SENSOR_SPEC_{{ getSensorCompatibleString(sensor) }}_EXISTS 1 +#define SENSOR_SPEC_{{ getSensorCompatibleString(sensor) }}_CHAN_COUNT {{ sensor.channels | length }} +#define SENSOR_SPEC_{{ getSensorCompatibleString(sensor) }}_ATTR_COUNT {{ sensor.attributes | length }} +#define SENSOR_SPEC_{{ getSensorCompatibleString(sensor) }}_TRIG_COUNT {{ sensor.triggers | length }} +{% for chan_id, channels in sensor.channels.items() -%} +#define SENSOR_SPEC_{{ getSensorCompatibleString(sensor) }}_CH_{{chan_id}}_COUNT {{ channels | length }} +{% for chan_index, chan in channels | enumerate -%} +#define SENSOR_SPEC_{{ getSensorCompatibleString(sensor) }}_CH_{{chan_id}}_{{chan_index}}_EXISTS 1 +#define SENSOR_SPEC_{{ getSensorCompatibleString(sensor) }}_CH_{{chan_id}}_{{chan_index}}_NAME "{{ chan.name }}" +#define SENSOR_SPEC_{{ getSensorCompatibleString(sensor) }}_CH_{{chan_id}}_{{chan_index}}_DESC "{{ formatStringForC(chan.description) }}" +#define SENSOR_SPEC_{{ getSensorCompatibleString(sensor) }}_CH_{{chan_id}}_{{chan_index}}_SPEC { \ + .chan_type = SENSOR_CHAN_{{chan_id.upper().replace('-', '_')}}, \ + .chan_idx = {{chan_index}}, \ +} +{% endfor -%} {# sensor channel instance loop #} +{%- endfor -%} {# sensor channel loop #} +{%- for attribute in sensor.attributes -%} +#define SENSOR_SPEC_{{ getSensorCompatibleString(sensor) }}_AT_{{ attribute.attribute }}_CH_{{ attribute.channel }}_EXISTS 1 +{% endfor %} {# sensor attribute loop #} +{% for trigger in sensor.triggers -%} +#define SENSOR_SPEC_{{ getSensorCompatibleString(sensor) }}_TR_{{ trigger }}_EXISTS 1 +#define SENSOR_SPEC_{{ getSensorCompatibleString(sensor) }}_TR_{{ trigger }}_NAME "{{ spec.triggers[trigger].name }}" +#define SENSOR_SPEC_{{ getSensorCompatibleString(sensor) }}_TR_{{ trigger }}_DESC "{{ formatStringForC(spec.triggers[trigger].description) }}" +{% endfor -%} {# sensor trigger loop #} +{%- endfor %} {# sensor loop #} +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* _INCLUDE_ZEPHYR_GENERATED_SENSOR_CONSTANTS_H_ */ \ No newline at end of file diff --git a/tests/drivers/sensor/generator/CMakeLists.txt b/tests/drivers/sensor/generator/CMakeLists.txt new file mode 100644 index 000000000000000..415bd10e12402a9 --- /dev/null +++ b/tests/drivers/sensor/generator/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: BDS-3-Clause + +cmake_minimum_required(VERSION 3.20.0) + +# These must be set BEFORE calling find_package() +set(ZEPHYR_EXTRA_SENSOR_YAML_FILES ${CMAKE_CURRENT_LIST_DIR}/test_sensor.yaml) +set(ZEPHYR_EXTRA_SENSOR_DEFINITION_FILES ${CMAKE_CURRENT_LIST_DIR}/test_deps.yaml) +set(ZEPHYR_EXTRA_SENSOR_INCLUDE_PATHS ${CMAKE_CURRENT_LIST_DIR}) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(sensor.generator C CXX) + +target_sources(app + PRIVATE + test_macros.cpp +) diff --git a/tests/drivers/sensor/generator/prj.conf b/tests/drivers/sensor/generator/prj.conf new file mode 100644 index 000000000000000..9bd091a767bfe82 --- /dev/null +++ b/tests/drivers/sensor/generator/prj.conf @@ -0,0 +1,17 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: BDS-3-Clause + +# Sensor configs: +CONFIG_SENSOR=y + +# Test configs: +CONFIG_TEST=y +CONFIG_ZTEST=y + +# C++ configs: +CONFIG_CPP=y +CONFIG_STD_CPP20=y +CONFIG_REQUIRES_FULL_LIBCPP=y + +# Enable logging +CONFIG_LOG=y diff --git a/tests/drivers/sensor/generator/test_deps.yaml b/tests/drivers/sensor/generator/test_deps.yaml new file mode 100644 index 000000000000000..57efd4073d383d8 --- /dev/null +++ b/tests/drivers/sensor/generator/test_deps.yaml @@ -0,0 +1,28 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: BDS-3-Clause + +attributes: + attr0: + name: "attribute 0" + description: "The first new attribute" + attr1: + name: "attribute 1" + description: "The second new attribute" +channels: + foo: + name: "foo" + description: "A measurement of 'foo'" + units: "foos" + bar: + name: "bar" + description: "A measurement of 'bar'" + units: "bars" +units: + foos: + name: "foos" + description: "The number of foos" + symbol: "foo" + bars: + name: "bars" + description: "The number of bars" + symbol: "bar" diff --git a/tests/drivers/sensor/generator/test_macros.cpp b/tests/drivers/sensor/generator/test_macros.cpp new file mode 100644 index 000000000000000..321cf16fd1f2b1f --- /dev/null +++ b/tests/drivers/sensor/generator/test_macros.cpp @@ -0,0 +1,70 @@ +/* Copyright (c) 2024 Google Inc + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +ZTEST_SUITE(SensorGenerator, nullptr, nullptr, nullptr, nullptr, nullptr); + +#define ASSERT_COMPAT_EXISTS(compat) \ + ZTEST(SensorGenerator, test_##compat##_Exists) \ + { \ + zassert_true(SENSOR_SPEC_COMPAT_EXISTS(compat)); \ + } + +/* + * Verify that every compatible value provided by the generated + * SENSOR_SPEC_FOREACH_COMPAT exists. + */ +SENSOR_SPEC_FOREACH_COMPAT(ASSERT_COMPAT_EXISTS) + +ZTEST(SensorGenerator, test_static_counts) +{ + zexpect_equal(2, SENSOR_SPEC_CHAN_COUNT(zephyr_test_sensor)); + zexpect_equal(3, SENSOR_SPEC_ATTR_COUNT(zephyr_test_sensor)); + zexpect_equal(0, SENSOR_SPEC_TRIG_COUNT(zephyr_test_sensor)); +} + +ZTEST(SensorGenerator, test_channel_bar_static_info) +{ + zassert_equal(2, SENSOR_SPEC_CHAN_INST_COUNT(zephyr_test_sensor, bar)); + zexpect_true(SENSOR_SPEC_CHAN_INST_EXISTS(zephyr_test_sensor, bar, 0)); + zexpect_true(SENSOR_SPEC_CHAN_INST_EXISTS(zephyr_test_sensor, bar, 1)); + zexpect_str_equal("left", SENSOR_SPEC_CHAN_INST_NAME(zephyr_test_sensor, bar, 0)); + zexpect_str_equal("right", SENSOR_SPEC_CHAN_INST_NAME(zephyr_test_sensor, bar, 1)); + zexpect_str_equal("Left side of the bar", + SENSOR_SPEC_CHAN_INST_DESC(zephyr_test_sensor, bar, 0)); + zexpect_str_equal("Right side of the bar", + SENSOR_SPEC_CHAN_INST_DESC(zephyr_test_sensor, bar, 1)); + + const struct sensor_chan_spec bar0_spec = + SENSOR_SPEC_CHAN_INST_SPEC(zephyr_test_sensor, bar, 0); + const struct sensor_chan_spec bar1_spec = + SENSOR_SPEC_CHAN_INST_SPEC(zephyr_test_sensor, bar, 1); + + zexpect_equal(SENSOR_CHAN_BAR, bar0_spec.chan_type); + zexpect_equal(SENSOR_CHAN_BAR, bar1_spec.chan_type); + zexpect_equal(0, bar0_spec.chan_idx); + zexpect_equal(1, bar1_spec.chan_idx); +} + +ZTEST(SensorGenerator, test_channel_foo_static_info) +{ + zassert_equal(1, SENSOR_SPEC_CHAN_INST_COUNT(zephyr_test_sensor, foo)); + zexpect_true(SENSOR_SPEC_CHAN_INST_EXISTS(zephyr_test_sensor, foo, 0)); + zexpect_str_equal("foo", SENSOR_SPEC_CHAN_INST_NAME(zephyr_test_sensor, foo, 0)); + zexpect_str_equal("A measurement of 'foo'", + SENSOR_SPEC_CHAN_INST_DESC(zephyr_test_sensor, foo, 0)); + + const struct sensor_chan_spec foo0_spec = + SENSOR_SPEC_CHAN_INST_SPEC(zephyr_test_sensor, foo, 0); + zexpect_equal(SENSOR_CHAN_FOO, foo0_spec.chan_type); + zexpect_equal(0, foo0_spec.chan_idx); +} + +ZTEST(SensorGenerator, test_attribute_static_info) +{ + zexpect_true(SENSOR_SPEC_ATTR_EXISTS(zephyr_test_sensor, attr0, foo)); + zexpect_true(SENSOR_SPEC_ATTR_EXISTS(zephyr_test_sensor, attr1, foo)); + zexpect_true(SENSOR_SPEC_ATTR_EXISTS(zephyr_test_sensor, attr0, bar)); +} diff --git a/tests/drivers/sensor/generator/test_sensor.yaml b/tests/drivers/sensor/generator/test_sensor.yaml new file mode 100644 index 000000000000000..c998d893f94b606 --- /dev/null +++ b/tests/drivers/sensor/generator/test_sensor.yaml @@ -0,0 +1,26 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: BDS-3-Clause + +deps: + - "test_deps.yaml" +compatible: + org: "zephyr" + part: "test-sensor" +supported-buses: ["i2c"] +attributes: + - attribute: "attr0" + channel: "foo" + units: "bars" + - attribute: "attr1" + channel: "foo" + units: "foos" + - attribute: "attr0" + channel: "bar" + units: "bars" +channels: + foo: [] + bar: + - name: "left" + description: "Left side of the bar" + - name: "right" + description: "Right side of the bar" diff --git a/tests/drivers/sensor/generator/testcase.yaml b/tests/drivers/sensor/generator/testcase.yaml new file mode 100644 index 000000000000000..24f30cc06e58dc5 --- /dev/null +++ b/tests/drivers/sensor/generator/testcase.yaml @@ -0,0 +1,11 @@ +# Copyright (c) 2024 Google Inc +# SPDX-License-Identifier: BDS-3-Clause + +tests: + drivers.sensor.generator: + tags: + - drivers + - sensor + - subsys + platform_allow: + - native_sim