Part 1
요약·해설
Lock 없는 multi-context 호출의 undefined behavior와 callback latency를 포함합니다.
blocking fetch · value get
work queue · dedicated thread
Part 2
접을 수 있는 영어 원문 전체
영어 원문 전체 펼치기
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_NONECONFIG_BMI160_TRIGGER_GLOBAL_THREAD: work queueCONFIG_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
출처
영어 원문에는 문단, 문서 지시문, 표, 코드, 코드 심벌, 경로를 그대로 보존했습니다.