hardware/peripherals/sensor/fetch_and_get.html · hardware/peripherals/sensor/fetch_and_get.rst 공식 원문 ↗

Zephyr 3.7.0 · Hardware · Peripherals · Sensor · Fetch and Get

Fetch and Get

Blocking fetch와 channel get, work queue·dedicated trigger thread의 차이를 설명합니다.

Official pathhardware/peripherals/sensor/fetch_and_get.html
Source filehardware/peripherals/sensor/fetch_and_get.rst
Source versionZephyr 3.7.0
TranslationFull · reviewed

본문 기준: Zephyr 3.7.0 LTS3 · 이후 유지보수 패치는 별도입니다.

Part 1

요약·해설

Lock 없는 multi-context 호출의 undefined behavior와 callback latency를 포함합니다.

Data

blocking fetch · value get

Triggers

work queue · dedicated thread

Part 2

접을 수 있는 영어 원문 전체

영어 원문 전체 펼치기
원문 SHA-256 F06C81EF4A257B84345CAA70120485B05B9254B8FB203ADF5BC0802F3B4CDB8A
.. _sensor-fetch-and-get:

Fetch and Get
#############

The stable and long existing APIs for reading sensor data and handling triggers
are:

* :c:func:`sensor_sample_fetch`
* :c:func:`sensor_sample_fetch_chan`
* :c:func:`sensor_channel_get`
* :c:func:`sensor_trigger_set`

These functions work together. The fetch APIs block the calling context which
must be a thread until the requested :c:enum:`sensor_channel` (or all channels)
has been obtained and stored into the driver instance's private data.

The channel data most recently fetched can then be obtained as a
:c:struct:`sensor_value` by calling :c:func:`sensor_channel_get` for each channel
type.

.. warning::
   It should be noted that calling fetch and get from multiple contexts without
   a locking mechanism is undefined and most sensor drivers do not attempt to
   internally provide exclusive access to the device during or between these
   calls.

Polling
*******

Using fetch and get sensor can be read in a polling manner from software threads.


.. literalinclude:: ../../../../../../samples/sensor/magn_polling/src/main.c
   :language: c

Triggers
********

Triggers in the stable API require enabling triggers with a device
specific Kconfig. The device specific Kconfig typically allows selecting the
context the trigger runs. The application then needs to register a callback with
a function signature matching :c:type:`sensor_trigger_handler_t` using
:c:func:`sensor_trigger_set` for the specific triggers (events) to listen for.

.. note::
   Triggers may not be set from user mode threads, and the callback is not
   run in a user mode context.

There are typically two options provided for each driver where to run trigger
handlers. Either the trigger handler is run using the system work queue thread
(:ref:`workqueues_v2`) or a dedicated thread. A great example can be found in
the BMI160 driver which has Kconfig options for selecting a trigger mode.
See :kconfig:option:`CONFIG_BMI160_TRIGGER_NONE`,
:kconfig:option:`CONFIG_BMI160_TRIGGER_GLOBAL_THREAD` (work queue),
:kconfig:option:`CONFIG_BMI160_TRIGGER_OWN_THREAD` (dedicated thread).

Some notable attributes of using a driver dedicated thread vs the system work
queue.

* Driver dedicated threads have dedicated stack (RAM) which only gets used for
  that single trigger handler function.
* Driver dedicated threads *do* get their own priority typically which lets you
  prioritize trigger handling among other threads.
* Driver dedicated threads will not have head of line blocking if the driver
  needs time to handle the trigger.

.. note::
   In all cases it's very likely there will be variable delays from the actual
   interrupt to your callback function being run. In the work queue
   (GLOBAL_THREAD) case the work queue itself can be the source of variable
   latency!

.. literalinclude:: ../../../../../hardware/peripherals/sensor/tap_count.c
   :language: c

Part 3

한국어 전문 번역

Sensor data를 읽고 trigger를 처리하는 오랫동안 존재한 stable API는 다음과 같습니다.

  • sensor_sample_fetch()
  • sensor_sample_fetch_chan()
  • sensor_channel_get()
  • sensor_trigger_set()

이 함수들은 함께 동작합니다. Fetch API는 요청한 sensor_channel 또는 모든 channel을 얻어 driver instance의 private data에 저장할 때까지 호출 context를 block합니다. 따라서 호출 context는 thread여야 합니다.

그 뒤 channel type마다 sensor_channel_get()을 호출하면 가장 최근에 fetch한 channel data를 sensor_value로 얻을 수 있습니다.

Polling

Fetch와 get을 사용하면 software thread에서 polling 방식으로 sensor를 읽을 수 있습니다.

/*
 * Copyright (c) 2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

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

int main(void)
{
	const struct device *const dev = DEVICE_DT_GET(DT_ALIAS(magn0));
	struct sensor_value value_x, value_y, value_z;
	int ret;

	if (!device_is_ready(dev)) {
		printk("sensor: device not ready.\n");
		return 0;
	}

	printk("Polling magnetometer data from %s.\n", dev->name);

	while (1) {
		ret = sensor_sample_fetch(dev);
		if (ret) {
			printk("sensor_sample_fetch failed ret %d\n", ret);
			return 0;
		}

		ret = sensor_channel_get(dev, SENSOR_CHAN_MAGN_X, &value_x);
		ret = sensor_channel_get(dev, SENSOR_CHAN_MAGN_Y, &value_y);
		ret = sensor_channel_get(dev, SENSOR_CHAN_MAGN_Z, &value_z);
		printf("( x y z ) = ( %f  %f  %f )\n",
		       sensor_value_to_double(&value_x),
		       sensor_value_to_double(&value_y),
		       sensor_value_to_double(&value_z));

		k_sleep(K_MSEC(500));
	}
	return 0;
}

Triggers

Stable API의 trigger를 사용하려면 device-specific Kconfig로 trigger를 enable해야 합니다. 이 Kconfig는 보통 trigger가 실행될 context를 선택하게 합니다. Application은 들을 특정 trigger(event)마다 sensor_trigger_handler_t와 일치하는 signature의 callback을 sensor_trigger_set()으로 등록해야 합니다.

각 driver는 trigger handler를 실행할 위치로 보통 두 가지 option을 제공합니다. System work queue thread에서 실행하거나 dedicated thread에서 실행할 수 있습니다. BMI160 driver는 trigger mode 선택을 위한 다음 Kconfig option을 제공하는 좋은 예입니다.

  • CONFIG_BMI160_TRIGGER_NONE
  • CONFIG_BMI160_TRIGGER_GLOBAL_THREAD: work queue
  • CONFIG_BMI160_TRIGGER_OWN_THREAD: dedicated thread

System work queue와 비교한 driver-dedicated thread의 주요 특성은 다음과 같습니다.

  • 단일 trigger handler function만 사용하는 dedicated stack(RAM)을 갖습니다.
  • 보통 자체 priority를 가지므로 다른 thread 사이에서 trigger handling priority를 정할 수 있습니다.
  • Driver가 trigger 처리에 시간이 걸려도 head-of-line blocking이 발생하지 않습니다.
/*
 * Copyright (c) 2024 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/drivers/sensor.h>


const struct device *const accel0 = DEVICE_DT_GET(DT_ALIAS(accel0));

static struct tap_count_state {
	struct sensor_trigger trig;
	uint32_t count;
} tap_count_state = {
	.trig = {
		.chan = SENSOR_CHAN_ACCEL_XYZ,
		.type = SENSOR_TRIG_TAP,
	},
	.count = 0,
};

void tap_handler(const struct device *dev, const struct sensor_trigger *trig)
{
	struct tap_count_state *state = CONTAINER_OF(trig, struct tap_count_state, trig);

	state->count++;

	printk("Tap! Total Taps: %u\n", state->count);
}

int main(void)
{
	int rc;

	printk("Tap Counter Example (%s)\n", CONFIG_ARCH);

	rc = sensor_trigger_set(accel0, &tap_count_state.trig, tap_handler);

	if (rc != 0) {
		printk("Failed to set trigger handler for taps, error %d\n", rc);
		return rc;
	}

	return rc;
}

Source

출처

영어 원문에는 문단, 문서 지시문, 표, 코드, 코드 심벌, 경로를 그대로 보존했습니다.