mirror of
https://github.com/apache/nuttx.git
synced 2026-08-09 06:25:09 +00:00
Documentation: update sensors documentation
update sensors documentation: - add info about different sensor frameworks in one place - fix headers style for sensors_uorb.rst: headers - fix long lines for sensors_uorb.rst so it's possible to read this file in terminal IDE - add code sections for sensors_uorb.rst Signed-off-by: raiden00pl <raiden00@railab.me>
This commit is contained in:
parent
4558db18e4
commit
0652cbf304
6 changed files with 775 additions and 574 deletions
|
|
@ -38,7 +38,6 @@ following section.
|
|||
rptun.rst
|
||||
rwbuffer.rst
|
||||
sensors.rst
|
||||
sensors_uorb.rst
|
||||
segger.rst
|
||||
spi.rst
|
||||
syslog.rst
|
||||
|
|
|
|||
|
|
@ -1,440 +1,32 @@
|
|||
.. warning:: This list is incomplete. See drivers/sensors for a full list of
|
||||
supported sensors
|
||||
|
||||
==============
|
||||
Sensor Drivers
|
||||
==============
|
||||
|
||||
Common Sensor Register Interface
|
||||
================================
|
||||
|
||||
Contributed by Bob Feretich
|
||||
|
||||
Background and problem statement:
|
||||
|
||||
The capabilities and performance of modern sensors have grown tremendously.
|
||||
Most sensors are now capable of some degree of autonomous behavior and
|
||||
several permit the user to load firmware into them and perform as
|
||||
nanocontrollers. Other sensors have very sophisticated built-in digital
|
||||
filters that can be programmed with hundreds of parameters.
|
||||
|
||||
Currently most sensor drivers in the NuttX drivers/sensors
|
||||
directory implement file_ops open(), close(), and read() functions.
|
||||
The open() function initializes the sensor and places it in a mode where
|
||||
it can transfer live data in a default configuration. The close() function
|
||||
places the sensor in a low power shutdown mode. The read() function
|
||||
returns the most recent data sample from the sensor's most used data
|
||||
output registers. The write() function is rarely implemented and when it
|
||||
is there is no consistency in its use. The lseek() and poll() functions
|
||||
seem to be completely ignored. This results in the sensors being operated
|
||||
in only their most primitive modes using a fixed "default configuration".
|
||||
|
||||
To work around this problem sensor drivers have implemented ioctl()
|
||||
functions to perform configuration, program the sensor, and manage
|
||||
autonomous activity. Ioctls provide a method where user programs can
|
||||
tunnel through a high level driver to access and control device specific
|
||||
features. The problem with using ioctls is that before the ioctl interface
|
||||
can be used, the sensor driver must be opened; and the open() function
|
||||
causes the driver to start performing these primitive actions, so before
|
||||
ioctls can manage the drivers as desired, ioctls must first be used to
|
||||
undo the generic actions caused by the open() function. Another major
|
||||
issue is that there is no consistency from sensor to sensor on ioctl
|
||||
definitions, not even for the most common sensor actions like writing a
|
||||
sensor control register or reading a sensor status register.
|
||||
|
||||
Purpose:
|
||||
|
||||
The purpose of the "Common Sensor Register Interface" is to implement a
|
||||
consistent and more useful definition of file_ops interface and to make the
|
||||
file_ops open() function more flexible in establishing the initial
|
||||
operational state of the sensor. Compatibility for user applications that
|
||||
implement the current open(), close(), read() interface will be
|
||||
maintained; and the much greater capabilities of modern sensors will
|
||||
become accessible through this interface.
|
||||
|
||||
Scope:
|
||||
|
||||
Applicable to I2C and SPI attached sensors, and some serial port attached
|
||||
sensors.
|
||||
|
||||
The file_ops interface definition:
|
||||
|
||||
open(): This function performs the below actions...
|
||||
|
||||
1) Reads the sensors ID register. If the sensor responds with an
|
||||
unexpected value, then...
|
||||
|
||||
a) The driver's write() function is disabled.
|
||||
b) The open function initializes the driver instance, so
|
||||
that read() and lseek() operations may be performed to enable
|
||||
problem diagnoses, but the sensor hardware is not initialized.
|
||||
(No write operations are performed to the sensor.)
|
||||
c) The errno global variable is set to positive ENODEV
|
||||
("No such device").
|
||||
d) The open() function returns successfully with a file_handle.
|
||||
Note that the calling routine should clear errno before
|
||||
calling open(). (The file_ops rules prevent drivers from
|
||||
setting errno to zero.)
|
||||
|
||||
2) The other file_ops functions are enabled.
|
||||
3) The driver's "current reg address" state variable is set to the
|
||||
sensor's first sensor data output register. (This will make
|
||||
calls to read() return live sensor data and maintain compatibility
|
||||
with existing user programs.)
|
||||
4) If the driver supports a default worker task and an interrupt
|
||||
handler is specified by in the sensor configuration structure, then
|
||||
the default worker task is bound to the default worker task.
|
||||
5) The sensor configuration structure (that was provided to the driver
|
||||
registration function) is examined to determine whether a custom
|
||||
sensor configuration is specified. (The custom configuration is
|
||||
basically an array of (device_reg_address, value) pairs that are
|
||||
written to the sensor via "single register write" operations.
|
||||
If a custom sensor configuration was specified, then that
|
||||
configuration is written to the sensor, otherwise the "default
|
||||
sensor configuration" is written to the sensor.
|
||||
(A side effect of writing this data may result in interrupts
|
||||
occurring and data being transferred to/from the worker task.)
|
||||
6) The open() function returns successfully with a file_handle.
|
||||
|
||||
``close()``: This function stops sensor activity and places it in a low
|
||||
power mode. The file_ops interface functions are disabled for this
|
||||
instance of the sensor driver. (Except for open())
|
||||
|
||||
``read()``: The action of this function is dependent on whether a "default
|
||||
worker task" is running and the value of the driver's "current reg
|
||||
address" state variable.
|
||||
|
||||
If a "default worker task" is running,
|
||||
|
||||
AND the driver's "current reg address" is equal to the value of
|
||||
the first sensor data output register,
|
||||
AND the number of bytes to be read is less than or equal to the
|
||||
number of bytes in a "default worker task" sample,
|
||||
|
||||
Then data is copied from the "default worker task's" sample memory to
|
||||
the caller's provided buffer.
|
||||
|
||||
Otherwise, this function transfers data from sensor registers to the
|
||||
data buffer provided by the caller. The first byte read is from the
|
||||
sensor register address specified by the sensor's "current reg
|
||||
address". The addresses of subsequent bytes to be read are context
|
||||
sensitive. If more than bus transfer is needed to complete the read,
|
||||
then a "multi-byte" (sometimes called "burst mode") data transfer
|
||||
will be used to fill the buffer.
|
||||
See the sensor's datasheet to determine the auto-increment
|
||||
behavior of a "multi-byte" data transfers.
|
||||
|
||||
Note: That most sensors collect only a few bytes of data per sample.
|
||||
Small data transfers occurring over a high speed bus (like SPI and some
|
||||
high speed i2c and serial interfaces) are much more efficient when
|
||||
collected directly from the sensor hardware than by using a worker task
|
||||
as an intermediary.
|
||||
|
||||
``write()``: This function transfers data from the data buffer provided by
|
||||
the caller to sensor registers. The first byte written is to the
|
||||
sensor register address specified by the sensor's "current reg
|
||||
address". The addresses of subsequent bytes to be read are context
|
||||
sensitive. If more than bus transfer is needed to complete the write,
|
||||
then a "multi-byte" (sometimes called "burst mode") data
|
||||
transfer will be used to transfer data from the buffer.
|
||||
|
||||
See the sensor's datasheet to determine the auto-increment
|
||||
behavior of a "multi-byte" data transfers.
|
||||
|
||||
Note: If write() function was disabled, then no writes will be performed
|
||||
and the function will return 0 (characters transferred) and errno
|
||||
is set to -EROFS ("read-only file system").
|
||||
|
||||
``lseek()``: This function sets the value of the sensor's "current reg address"
|
||||
(seek_address). The open() function initializes the "current reg address"
|
||||
to the first sensor data output register, so unless the user needs
|
||||
to change the sensor configuration, lseek() does not need to be
|
||||
called. Neither read() nor write() change the sensor's "current reg
|
||||
address".
|
||||
|
||||
The definition of lseek is...::
|
||||
|
||||
off_t lseek(int fd, off_t offset, int whence);
|
||||
|
||||
For whence == SEEK_SET, the sensor's "current reg address" will be set
|
||||
to offset.
|
||||
|
||||
For whence == SEEK_CUR, offset will be added to the sensor's "current
|
||||
reg address".
|
||||
|
||||
For whence == SEEK_END, offset is ignored and the sensor's "current
|
||||
reg address" is set to the first sensor data output register.
|
||||
|
||||
lseek() will return an error if the resulting "current reg address"
|
||||
is invalid for the sensor.
|
||||
|
||||
``ioctl()``: Ioctls() may still be used and this interface make no attempt to
|
||||
regulate them. But, it is expected that far fewer ioctls will be needed.
|
||||
|
||||
The above interface can be used to fully configure a sensor to the needs
|
||||
of an application, including the ability to load firmware into sensor
|
||||
state machines
|
||||
|
||||
Sensor Cluster Driver Interface
|
||||
===============================
|
||||
|
||||
Contributed by Bob Feretich
|
||||
|
||||
Background and problem statement:
|
||||
|
||||
Most microcontrollers can support SPI bus transfers at 8 MHz or greater.
|
||||
Most SPI attached sensors can support a 10 MHz SPI bus. Most tri-axis
|
||||
accelerometers, tri-axis gyroscopes, or tri-axis magnetometers use only 6
|
||||
bytes per sample. Many sensors use less than 6 bytes per sample. On an 8
|
||||
MHz SPI bus it takes about 8 microseconds to transfer a 6 byte sample.
|
||||
(This time includes a command byte, 6 data bytes, and chip select select
|
||||
setup and hold.) So, for the below discussion keep in mind that the sensor
|
||||
sample collection work we want to perform should ideally take 8 microseconds
|
||||
per sample.
|
||||
|
||||
The drivers in the drivers/sensors directory support only the user space
|
||||
file_ops interface (accessing drivers through the POSIX open/read/close
|
||||
functions using a file descriptor). Also these drivers typically start
|
||||
their own worker task to perform sensor data collection, even when their
|
||||
sensors only transfer a few bytes of data per sample and those transfers
|
||||
are being made over a high performance bus.
|
||||
|
||||
Using the current implementation...
|
||||
|
||||
1) A sensor "data ready" or timer interrupt occurs.
|
||||
2) Context is saved and and the driver's interrupt handler is scheduled
|
||||
to run.
|
||||
3) The NuttX scheduler dispatches the driver's interrupt handler task.
|
||||
4) The driver's interrupt handler task posts to a semaphore that the
|
||||
driver's worker task is waiting on.
|
||||
5) NuttX restores the context for the driver's worker task and starts it
|
||||
running.
|
||||
6) The driver's worker task starts the i/o to collect the sample.) (This is
|
||||
where the 8 microseconds of real work gets performed.) And waits on a
|
||||
SPI data transfer complete semaphore.
|
||||
7) The NuttX saves the context of the driver's worker task, and the
|
||||
scheduler dispatches some other task to run while we are waiting.
|
||||
Note that this is a good thing. This task is probably performing some
|
||||
other real work. We want this to happen during the data transfer.
|
||||
8) The completion of the data transfer causes an interrupt. NuttX saves the
|
||||
current context and restores the driver's worker task's context.
|
||||
9) The driver's worker task goes to sleep waiting on the semaphore for the
|
||||
next sensor "data ready" or timer interrupt.
|
||||
10) The NuttX saves the context of the driver's worker task, and the
|
||||
scheduler dispatches some other task to run while we are waiting.
|
||||
|
||||
Independently with the above...
|
||||
|
||||
a) The sensor application program performs a file_ops read() to collect a
|
||||
sample.
|
||||
b) The NuttX high level driver receives control, performs a thin layer of
|
||||
housekeeping and calls the sensor driver's read function.
|
||||
c) The sensor driver's read function copies the most recent sample from the
|
||||
worker task's data area to the application's buffer and returns.
|
||||
d) The NuttX high level driver receives control, performs a thin layer of
|
||||
housekeeping and returns.
|
||||
e) The application processes the sample.
|
||||
|
||||
Using a 216 MHz STM32F7 with no other activity occurring, we have timed the
|
||||
above the elapsed time for the above to be on average 45 microseconds.
|
||||
|
||||
Most sensor applications process data from multiple sensors. (An 9-DoF IMU
|
||||
is typically represented as three sensors (accelerometer, gyroscope, and
|
||||
magnetometer). In this case there are three copies of 1-10 occurring in
|
||||
parallel.
|
||||
|
||||
In applications where live data is being used, the context switch
|
||||
thrashing and cache pollution of this approach cripples system
|
||||
performance. In applications where sensor FIFO data is being used and
|
||||
therefore a large amount of data is collected per iteration, the non "zero
|
||||
copy" nature of the data collection becomes a performance issue.
|
||||
|
||||
Purpose:
|
||||
|
||||
The "Sensor Cluster Driver Interface" provides a standard mechanism for
|
||||
an application to collect data from multiple sensor drivers in a much more
|
||||
efficient manner. It significantly reduces the number of running tasks and
|
||||
the context thrashing and cache pollution caused by them. It also permits
|
||||
"zero copy" collection of sensor data.
|
||||
|
||||
The Sensor Cluster Driver Interface uses a single "worker task" to be shared
|
||||
by an arbitrary number of drivers. This shared worker task is a kernel
|
||||
task that is registered like a driver, supports a driver interface to
|
||||
application programs, and collects data from multiple sensors (a cluster of
|
||||
sensors), we refer to it a "Sensor Cluster Driver".
|
||||
|
||||
Its goal is to change the sequence of events detailed above to...
|
||||
|
||||
1) A sensor "data ready" or timer interrupt occurs.
|
||||
2) Context is saved and and the cluster driver's interrupt handler is
|
||||
scheduled to run.
|
||||
3) The NuttX scheduler dispatches the cluster driver's interrupt handler
|
||||
task.
|
||||
4) The cluster driver's interrupt handler task posts to a semaphore that
|
||||
the cluster driver's worker task is waiting on.
|
||||
5) NuttX restores the context for the driver's worker task and starts it
|
||||
running.
|
||||
6) The cluster driver's worker task starts the i/o to collect the sample.
|
||||
There are two choices here. Programmed I/O (PIO) or DMA. If PIO is
|
||||
fastest for a small sample size, but it will lock up the processor for
|
||||
the full duration of the transfer; it can only transfer from one
|
||||
sensor at a time; and the worker task should manually yield control
|
||||
occasionally to permit other tasks to run. DMA has higher start and
|
||||
completion overhead, but it is much faster for long transfers, can
|
||||
perform simultaneous transfers from sensors on different buses, and
|
||||
automatically releases the processor while the transfer is occurring.
|
||||
For this reason our drivers allows the worker task to choose between
|
||||
PIO (driver_read()) and DMA (driver_exchange()), a common extension to
|
||||
the sensor_cluster_operations_s structure. So either way after one or
|
||||
more transfers we yield control and move to the next step. Note that
|
||||
the data is being transferred directly into the buffer provided by the
|
||||
application program; so no copy needs to be performed.
|
||||
7) The NuttX saves the context of the cluster driver's worker task, and the
|
||||
scheduler dispatches some other task to run while we are waiting.
|
||||
Again note that this is a good thing. This task is probably performing
|
||||
some other real work. We want this to happen during the data transfer.
|
||||
8) The completion of the last of the previous data transfers causes an
|
||||
interrupt. NuttX saves the current context and restores the cluster
|
||||
driver's worker task's context. If there is more sensor data to
|
||||
collect, then goto Step 6. Otherwise it posts to a semaphore that
|
||||
will wake the application.
|
||||
9) The driver's worker task goes to sleep waiting on the semaphore for the
|
||||
next sensor "data ready" or timer interrupt.
|
||||
10) The NuttX saves the context of the driver's worker task, and the
|
||||
scheduler dispatches some other task to run while we are waiting.
|
||||
|
||||
Independently with the above...
|
||||
|
||||
a) The sensor application program performs a file_ops read() to collect a
|
||||
sample.
|
||||
b) The NuttX high level driver receives control, performs a thin layer of
|
||||
housekeeping and calls the sensor driver's read function.
|
||||
c) The sensor driver's read function copies the most recent sample from the
|
||||
worker task's data area to the application's buffer and returns.
|
||||
d) The NuttX high level driver receives control, performs a thin layer of
|
||||
housekeeping and returns.
|
||||
e) The application processes the sample.
|
||||
|
||||
So when collecting data from three sensors, this mechanism saved...
|
||||
|
||||
* the handling of 2 sensor "data ready" or timer interrupts (Steps 1 - 4).
|
||||
* 2 occurrences of waking and scheduling of a worker task (Step 5).
|
||||
* 2 context switches to other tasks (Step 9 & 10)
|
||||
* if the three sensors were on separate buses, then 2 occurrences of
|
||||
|
||||
Steps 6 - 8 could have also been saved.
|
||||
|
||||
* An extra copy operation of the collected sensor data.
|
||||
* The cache pollution caused by 2 competing worker tasks.
|
||||
|
||||
Definitions:
|
||||
|
||||
"Leaf Driver" - a kernel driver that implements the "Sensor Cluster Driver
|
||||
Interface" so that it can be called by Cluster drivers.
|
||||
|
||||
"Cluster Driver" - a kernel driver that uses the "Sensor Cluster Driver
|
||||
Interface" to call leaf drivers.
|
||||
|
||||
"Entry-Point Vector" - an array of function addresses to which a leaf driver
|
||||
will permit calls by a Cluster Driver.
|
||||
|
||||
"Leaf Driver Instance Handle" - a pointer to an opaque Leaf Driver structure
|
||||
that identifies an instance of the leaf driver. Leaf Drivers store this
|
||||
handle in its configuration structure during registration.
|
||||
|
||||
Sensor Cluster Interface description:
|
||||
|
||||
* The definition of an entry-point vector. This is similar to the
|
||||
entry-point vector that is provided to the file-ops high level driver.
|
||||
This entry-point vector must include the sensor_cluster_operations_s
|
||||
structure as its first member.
|
||||
* The the definition of an driver entry-point vector member in the leaf
|
||||
driver's configuration structure. The leaf driver registration function
|
||||
must store the address of its entry-point vector in this field.
|
||||
* The the definition of an instance handle member in the leaf drivers
|
||||
configuration structure. The leaf driver registration function must store
|
||||
a handle (opaque pointer) to the instance of the leaf driver being
|
||||
registered in this field. Note that this should be the same handle that
|
||||
the leaf driver supplies to NuttX to register itself. The cluster driver
|
||||
will include this handle as a parameter in calls made to the leaf driver.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
struct sensor_cluster_operations_s
|
||||
{
|
||||
CODE int (*driver_open)(FAR void *instance_handle, int32_t arg);
|
||||
CODE int (*driver_close)(FAR void *instance_handle, int32_t arg);
|
||||
CODE ssize_t (*driver_read)(FAR void *instance_handle, FAR char *buffer,
|
||||
size_t buflen);
|
||||
CODE ssize_t (*driver_write)(FAR void *instance_handle,
|
||||
FAR const char *buffer, size_t buflen);
|
||||
CODE off_t (*driver_seek)(FAR void *instance_handle, off_t offset,
|
||||
int whence);
|
||||
CODE int (*driver_ioctl)(FAR void *instance_handle, int cmd,
|
||||
unsigned long arg);
|
||||
CODE int (*driver_suspend)(FAR void *instance_handle, int32_t arg);
|
||||
CODE int (*driver_resume)(FAR void *instance_handle, int32_t arg);
|
||||
};
|
||||
|
||||
Note that the sensor_cluster_operations_s strongly resembles the NuttX fs.h
|
||||
file_operations structures. This permits the current file_operations
|
||||
functions to become thin wrappers around these functions.
|
||||
|
||||
``driver_open()`` Same as the fs.h open() except that arg can be specify
|
||||
permitting more flexibility in sensor configuration and initial operation.
|
||||
when arg = 0 the function of driver_open() must be identical to open().
|
||||
|
||||
``driver_close()`` Same as the fs.h close() except that arg can be specify
|
||||
permitting more flexibility in selecting a sensor low power state.
|
||||
when arg = 0 the function of driver_close() must be identical to close().
|
||||
|
||||
``driver_read()`` Same as the fs.h read().
|
||||
|
||||
``driver_write()`` Same as the fs.h write(). Optional. Set to NULL if not
|
||||
supported.
|
||||
|
||||
``driver_seek()`` Same as the fs.h seek(). Optional. Set to NULL if not
|
||||
supported.
|
||||
|
||||
``driver_ioctl()`` Same as the fs.h ioctl(). Optional. Set to NULL if not
|
||||
supported.
|
||||
|
||||
``driver_suspend()`` and ``driver_resume()`` Optional. Set to NULL if not
|
||||
supported. It is common for sensor applications to conserve power and
|
||||
send their microcontroller into a low power sleep state. It seems
|
||||
appropriate to reserve these spots for future use. These driver entry
|
||||
points exist in Linux and Windows. Since microcontrollers and sensors
|
||||
get more capable every year, there should soon be a requirement for
|
||||
these entry points. Discussion on how to standardize their use and
|
||||
implementation should
|
||||
be taken up independently from this driver document.
|
||||
|
||||
Note that all drivers are encouraged to extend their entry-point vectors
|
||||
beyond this common segment. For example it may be beneficial for the
|
||||
worker task to select between programmed i/o and DMA data transfer
|
||||
routines. Unregulated extensions to the Entry-Point Vector should be
|
||||
encouraged to maximize the benefits of a sensor's features.
|
||||
|
||||
Operation:
|
||||
|
||||
Board logic (configs directory) will register the cluster driver. The
|
||||
cluster driver will register the leaf drivers that it will call.
|
||||
This means that the cluster driver has access to the leaf driver's
|
||||
configuration structures and can pass the Leaf Driver Instance Handle to
|
||||
the leaf driver as a parameter in calls made via the Entry-Point Vector.
|
||||
|
||||
Either board logic or an application program may open() the cluster
|
||||
driver. The cluster driver open() calls the open() function of the leaf
|
||||
drivers. The cluster driver open() or read() function can launch the
|
||||
shared worker task that collects the data.
|
||||
|
||||
The cluster driver close() function calls the close functions of the leaf
|
||||
drivers.
|
||||
|
||||
Implemented Drivers
|
||||
===================
|
||||
Currently in NuttX we have 3 different approaches to sensor interfaces:
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
:maxdepth: 1
|
||||
|
||||
sensors/*
|
||||
sensors/sensors_uorb.rst
|
||||
sensors/sensors_legacy.rst
|
||||
sensors/sensors_cluster.rst
|
||||
|
||||
The preferred way for implementing new sensors is
|
||||
the :ref:`New sensor framework <new_sensor_framework>`, which provides the most
|
||||
general interafce.
|
||||
|
||||
.. attach files to avoid warinigs, but don't show them here !
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
sensors/adt7320.rst
|
||||
sensors/adxl345.rst
|
||||
sensors/adxl362.rst
|
||||
sensors/adxl372.rst
|
||||
sensors/aht10.rst
|
||||
sensors/ak09912.rst
|
||||
sensors/lsm330.rst
|
||||
sensors/mcp9600.rst
|
||||
sensors/mpl115a.rst
|
||||
sensors/sht4x.rst
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
|
@ -0,0 +1,439 @@
|
|||
.. warning:: This interface is not commonly used in NuttX and is not
|
||||
recomended for implementing new sensors. It should be removed
|
||||
in the future.
|
||||
|
||||
=========================
|
||||
Sensor Cluster (Obsolote)
|
||||
=========================
|
||||
|
||||
Common Sensor Register Interface
|
||||
================================
|
||||
|
||||
Contributed by Bob Feretich
|
||||
|
||||
Background and problem statement:
|
||||
|
||||
The capabilities and performance of modern sensors have grown tremendously.
|
||||
Most sensors are now capable of some degree of autonomous behavior and
|
||||
several permit the user to load firmware into them and perform as
|
||||
nanocontrollers. Other sensors have very sophisticated built-in digital
|
||||
filters that can be programmed with hundreds of parameters.
|
||||
|
||||
Currently most sensor drivers in the NuttX drivers/sensors
|
||||
directory implement file_ops open(), close(), and read() functions.
|
||||
The open() function initializes the sensor and places it in a mode where
|
||||
it can transfer live data in a default configuration. The close() function
|
||||
places the sensor in a low power shutdown mode. The read() function
|
||||
returns the most recent data sample from the sensor's most used data
|
||||
output registers. The write() function is rarely implemented and when it
|
||||
is there is no consistency in its use. The lseek() and poll() functions
|
||||
seem to be completely ignored. This results in the sensors being operated
|
||||
in only their most primitive modes using a fixed "default configuration".
|
||||
|
||||
To work around this problem sensor drivers have implemented ioctl()
|
||||
functions to perform configuration, program the sensor, and manage
|
||||
autonomous activity. Ioctls provide a method where user programs can
|
||||
tunnel through a high level driver to access and control device specific
|
||||
features. The problem with using ioctls is that before the ioctl interface
|
||||
can be used, the sensor driver must be opened; and the open() function
|
||||
causes the driver to start performing these primitive actions, so before
|
||||
ioctls can manage the drivers as desired, ioctls must first be used to
|
||||
undo the generic actions caused by the open() function. Another major
|
||||
issue is that there is no consistency from sensor to sensor on ioctl
|
||||
definitions, not even for the most common sensor actions like writing a
|
||||
sensor control register or reading a sensor status register.
|
||||
|
||||
Purpose:
|
||||
|
||||
The purpose of the "Common Sensor Register Interface" is to implement a
|
||||
consistent and more useful definition of file_ops interface and to make the
|
||||
file_ops open() function more flexible in establishing the initial
|
||||
operational state of the sensor. Compatibility for user applications that
|
||||
implement the current open(), close(), read() interface will be
|
||||
maintained; and the much greater capabilities of modern sensors will
|
||||
become accessible through this interface.
|
||||
|
||||
Scope:
|
||||
|
||||
Applicable to I2C and SPI attached sensors, and some serial port attached
|
||||
sensors.
|
||||
|
||||
The file_ops interface definition:
|
||||
|
||||
open(): This function performs the below actions...
|
||||
|
||||
1) Reads the sensors ID register. If the sensor responds with an
|
||||
unexpected value, then...
|
||||
|
||||
a) The driver's write() function is disabled.
|
||||
b) The open function initializes the driver instance, so
|
||||
that read() and lseek() operations may be performed to enable
|
||||
problem diagnoses, but the sensor hardware is not initialized.
|
||||
(No write operations are performed to the sensor.)
|
||||
c) The errno global variable is set to positive ENODEV
|
||||
("No such device").
|
||||
d) The open() function returns successfully with a file_handle.
|
||||
Note that the calling routine should clear errno before
|
||||
calling open(). (The file_ops rules prevent drivers from
|
||||
setting errno to zero.)
|
||||
|
||||
2) The other file_ops functions are enabled.
|
||||
3) The driver's "current reg address" state variable is set to the
|
||||
sensor's first sensor data output register. (This will make
|
||||
calls to read() return live sensor data and maintain compatibility
|
||||
with existing user programs.)
|
||||
4) If the driver supports a default worker task and an interrupt
|
||||
handler is specified by in the sensor configuration structure, then
|
||||
the default worker task is bound to the default worker task.
|
||||
5) The sensor configuration structure (that was provided to the driver
|
||||
registration function) is examined to determine whether a custom
|
||||
sensor configuration is specified. (The custom configuration is
|
||||
basically an array of (device_reg_address, value) pairs that are
|
||||
written to the sensor via "single register write" operations.
|
||||
If a custom sensor configuration was specified, then that
|
||||
configuration is written to the sensor, otherwise the "default
|
||||
sensor configuration" is written to the sensor.
|
||||
(A side effect of writing this data may result in interrupts
|
||||
occurring and data being transferred to/from the worker task.)
|
||||
6) The open() function returns successfully with a file_handle.
|
||||
|
||||
``close()``: This function stops sensor activity and places it in a low
|
||||
power mode. The file_ops interface functions are disabled for this
|
||||
instance of the sensor driver. (Except for open())
|
||||
|
||||
``read()``: The action of this function is dependent on whether a "default
|
||||
worker task" is running and the value of the driver's "current reg
|
||||
address" state variable.
|
||||
|
||||
If a "default worker task" is running,
|
||||
|
||||
AND the driver's "current reg address" is equal to the value of
|
||||
the first sensor data output register,
|
||||
AND the number of bytes to be read is less than or equal to the
|
||||
number of bytes in a "default worker task" sample,
|
||||
|
||||
Then data is copied from the "default worker task's" sample memory to
|
||||
the caller's provided buffer.
|
||||
|
||||
Otherwise, this function transfers data from sensor registers to the
|
||||
data buffer provided by the caller. The first byte read is from the
|
||||
sensor register address specified by the sensor's "current reg
|
||||
address". The addresses of subsequent bytes to be read are context
|
||||
sensitive. If more than bus transfer is needed to complete the read,
|
||||
then a "multi-byte" (sometimes called "burst mode") data transfer
|
||||
will be used to fill the buffer.
|
||||
See the sensor's datasheet to determine the auto-increment
|
||||
behavior of a "multi-byte" data transfers.
|
||||
|
||||
Note: That most sensors collect only a few bytes of data per sample.
|
||||
Small data transfers occurring over a high speed bus (like SPI and some
|
||||
high speed i2c and serial interfaces) are much more efficient when
|
||||
collected directly from the sensor hardware than by using a worker task
|
||||
as an intermediary.
|
||||
|
||||
``write()``: This function transfers data from the data buffer provided by
|
||||
the caller to sensor registers. The first byte written is to the
|
||||
sensor register address specified by the sensor's "current reg
|
||||
address". The addresses of subsequent bytes to be read are context
|
||||
sensitive. If more than bus transfer is needed to complete the write,
|
||||
then a "multi-byte" (sometimes called "burst mode") data
|
||||
transfer will be used to transfer data from the buffer.
|
||||
|
||||
See the sensor's datasheet to determine the auto-increment
|
||||
behavior of a "multi-byte" data transfers.
|
||||
|
||||
Note: If write() function was disabled, then no writes will be performed
|
||||
and the function will return 0 (characters transferred) and errno
|
||||
is set to -EROFS ("read-only file system").
|
||||
|
||||
``lseek()``: This function sets the value of the sensor's "current reg address"
|
||||
(seek_address). The open() function initializes the "current reg address"
|
||||
to the first sensor data output register, so unless the user needs
|
||||
to change the sensor configuration, lseek() does not need to be
|
||||
called. Neither read() nor write() change the sensor's "current reg
|
||||
address".
|
||||
|
||||
The definition of lseek is...::
|
||||
|
||||
off_t lseek(int fd, off_t offset, int whence);
|
||||
|
||||
For whence == SEEK_SET, the sensor's "current reg address" will be set
|
||||
to offset.
|
||||
|
||||
For whence == SEEK_CUR, offset will be added to the sensor's "current
|
||||
reg address".
|
||||
|
||||
For whence == SEEK_END, offset is ignored and the sensor's "current
|
||||
reg address" is set to the first sensor data output register.
|
||||
|
||||
lseek() will return an error if the resulting "current reg address"
|
||||
is invalid for the sensor.
|
||||
|
||||
``ioctl()``: Ioctls() may still be used and this interface make no attempt to
|
||||
regulate them. But, it is expected that far fewer ioctls will be needed.
|
||||
|
||||
The above interface can be used to fully configure a sensor to the needs
|
||||
of an application, including the ability to load firmware into sensor
|
||||
state machines
|
||||
|
||||
Sensor Cluster Driver Interface
|
||||
===============================
|
||||
|
||||
Contributed by Bob Feretich
|
||||
|
||||
Background and problem statement:
|
||||
|
||||
Most microcontrollers can support SPI bus transfers at 8 MHz or greater.
|
||||
Most SPI attached sensors can support a 10 MHz SPI bus. Most tri-axis
|
||||
accelerometers, tri-axis gyroscopes, or tri-axis magnetometers use only 6
|
||||
bytes per sample. Many sensors use less than 6 bytes per sample. On an 8
|
||||
MHz SPI bus it takes about 8 microseconds to transfer a 6 byte sample.
|
||||
(This time includes a command byte, 6 data bytes, and chip select select
|
||||
setup and hold.) So, for the below discussion keep in mind that the sensor
|
||||
sample collection work we want to perform should ideally take 8 microseconds
|
||||
per sample.
|
||||
|
||||
The drivers in the drivers/sensors directory support only the user space
|
||||
file_ops interface (accessing drivers through the POSIX open/read/close
|
||||
functions using a file descriptor). Also these drivers typically start
|
||||
their own worker task to perform sensor data collection, even when their
|
||||
sensors only transfer a few bytes of data per sample and those transfers
|
||||
are being made over a high performance bus.
|
||||
|
||||
Using the current implementation...
|
||||
|
||||
1) A sensor "data ready" or timer interrupt occurs.
|
||||
2) Context is saved and and the driver's interrupt handler is scheduled
|
||||
to run.
|
||||
3) The NuttX scheduler dispatches the driver's interrupt handler task.
|
||||
4) The driver's interrupt handler task posts to a semaphore that the
|
||||
driver's worker task is waiting on.
|
||||
5) NuttX restores the context for the driver's worker task and starts it
|
||||
running.
|
||||
6) The driver's worker task starts the i/o to collect the sample.) (This is
|
||||
where the 8 microseconds of real work gets performed.) And waits on a
|
||||
SPI data transfer complete semaphore.
|
||||
7) The NuttX saves the context of the driver's worker task, and the
|
||||
scheduler dispatches some other task to run while we are waiting.
|
||||
Note that this is a good thing. This task is probably performing some
|
||||
other real work. We want this to happen during the data transfer.
|
||||
8) The completion of the data transfer causes an interrupt. NuttX saves the
|
||||
current context and restores the driver's worker task's context.
|
||||
9) The driver's worker task goes to sleep waiting on the semaphore for the
|
||||
next sensor "data ready" or timer interrupt.
|
||||
10) The NuttX saves the context of the driver's worker task, and the
|
||||
scheduler dispatches some other task to run while we are waiting.
|
||||
|
||||
Independently with the above...
|
||||
|
||||
a) The sensor application program performs a file_ops read() to collect a
|
||||
sample.
|
||||
b) The NuttX high level driver receives control, performs a thin layer of
|
||||
housekeeping and calls the sensor driver's read function.
|
||||
c) The sensor driver's read function copies the most recent sample from the
|
||||
worker task's data area to the application's buffer and returns.
|
||||
d) The NuttX high level driver receives control, performs a thin layer of
|
||||
housekeeping and returns.
|
||||
e) The application processes the sample.
|
||||
|
||||
Using a 216 MHz STM32F7 with no other activity occurring, we have timed the
|
||||
above the elapsed time for the above to be on average 45 microseconds.
|
||||
|
||||
Most sensor applications process data from multiple sensors. (An 9-DoF IMU
|
||||
is typically represented as three sensors (accelerometer, gyroscope, and
|
||||
magnetometer). In this case there are three copies of 1-10 occurring in
|
||||
parallel.
|
||||
|
||||
In applications where live data is being used, the context switch
|
||||
thrashing and cache pollution of this approach cripples system
|
||||
performance. In applications where sensor FIFO data is being used and
|
||||
therefore a large amount of data is collected per iteration, the non "zero
|
||||
copy" nature of the data collection becomes a performance issue.
|
||||
|
||||
Purpose:
|
||||
|
||||
The "Sensor Cluster Driver Interface" provides a standard mechanism for
|
||||
an application to collect data from multiple sensor drivers in a much more
|
||||
efficient manner. It significantly reduces the number of running tasks and
|
||||
the context thrashing and cache pollution caused by them. It also permits
|
||||
"zero copy" collection of sensor data.
|
||||
|
||||
The Sensor Cluster Driver Interface uses a single "worker task" to be shared
|
||||
by an arbitrary number of drivers. This shared worker task is a kernel
|
||||
task that is registered like a driver, supports a driver interface to
|
||||
application programs, and collects data from multiple sensors (a cluster of
|
||||
sensors), we refer to it a "Sensor Cluster Driver".
|
||||
|
||||
Its goal is to change the sequence of events detailed above to...
|
||||
|
||||
1) A sensor "data ready" or timer interrupt occurs.
|
||||
2) Context is saved and and the cluster driver's interrupt handler is
|
||||
scheduled to run.
|
||||
3) The NuttX scheduler dispatches the cluster driver's interrupt handler
|
||||
task.
|
||||
4) The cluster driver's interrupt handler task posts to a semaphore that
|
||||
the cluster driver's worker task is waiting on.
|
||||
5) NuttX restores the context for the driver's worker task and starts it
|
||||
running.
|
||||
6) The cluster driver's worker task starts the i/o to collect the sample.
|
||||
There are two choices here. Programmed I/O (PIO) or DMA. If PIO is
|
||||
fastest for a small sample size, but it will lock up the processor for
|
||||
the full duration of the transfer; it can only transfer from one
|
||||
sensor at a time; and the worker task should manually yield control
|
||||
occasionally to permit other tasks to run. DMA has higher start and
|
||||
completion overhead, but it is much faster for long transfers, can
|
||||
perform simultaneous transfers from sensors on different buses, and
|
||||
automatically releases the processor while the transfer is occurring.
|
||||
For this reason our drivers allows the worker task to choose between
|
||||
PIO (driver_read()) and DMA (driver_exchange()), a common extension to
|
||||
the sensor_cluster_operations_s structure. So either way after one or
|
||||
more transfers we yield control and move to the next step. Note that
|
||||
the data is being transferred directly into the buffer provided by the
|
||||
application program; so no copy needs to be performed.
|
||||
7) The NuttX saves the context of the cluster driver's worker task, and the
|
||||
scheduler dispatches some other task to run while we are waiting.
|
||||
Again note that this is a good thing. This task is probably performing
|
||||
some other real work. We want this to happen during the data transfer.
|
||||
8) The completion of the last of the previous data transfers causes an
|
||||
interrupt. NuttX saves the current context and restores the cluster
|
||||
driver's worker task's context. If there is more sensor data to
|
||||
collect, then goto Step 6. Otherwise it posts to a semaphore that
|
||||
will wake the application.
|
||||
9) The driver's worker task goes to sleep waiting on the semaphore for the
|
||||
next sensor "data ready" or timer interrupt.
|
||||
10) The NuttX saves the context of the driver's worker task, and the
|
||||
scheduler dispatches some other task to run while we are waiting.
|
||||
|
||||
Independently with the above...
|
||||
|
||||
a) The sensor application program performs a file_ops read() to collect a
|
||||
sample.
|
||||
b) The NuttX high level driver receives control, performs a thin layer of
|
||||
housekeeping and calls the sensor driver's read function.
|
||||
c) The sensor driver's read function copies the most recent sample from the
|
||||
worker task's data area to the application's buffer and returns.
|
||||
d) The NuttX high level driver receives control, performs a thin layer of
|
||||
housekeeping and returns.
|
||||
e) The application processes the sample.
|
||||
|
||||
So when collecting data from three sensors, this mechanism saved...
|
||||
|
||||
* the handling of 2 sensor "data ready" or timer interrupts (Steps 1 - 4).
|
||||
* 2 occurrences of waking and scheduling of a worker task (Step 5).
|
||||
* 2 context switches to other tasks (Step 9 & 10)
|
||||
* if the three sensors were on separate buses, then 2 occurrences of
|
||||
|
||||
Steps 6 - 8 could have also been saved.
|
||||
|
||||
* An extra copy operation of the collected sensor data.
|
||||
* The cache pollution caused by 2 competing worker tasks.
|
||||
|
||||
Definitions:
|
||||
|
||||
"Leaf Driver" - a kernel driver that implements the "Sensor Cluster Driver
|
||||
Interface" so that it can be called by Cluster drivers.
|
||||
|
||||
"Cluster Driver" - a kernel driver that uses the "Sensor Cluster Driver
|
||||
Interface" to call leaf drivers.
|
||||
|
||||
"Entry-Point Vector" - an array of function addresses to which a leaf driver
|
||||
will permit calls by a Cluster Driver.
|
||||
|
||||
"Leaf Driver Instance Handle" - a pointer to an opaque Leaf Driver structure
|
||||
that identifies an instance of the leaf driver. Leaf Drivers store this
|
||||
handle in its configuration structure during registration.
|
||||
|
||||
Sensor Cluster Interface description:
|
||||
|
||||
* The definition of an entry-point vector. This is similar to the
|
||||
entry-point vector that is provided to the file-ops high level driver.
|
||||
This entry-point vector must include the sensor_cluster_operations_s
|
||||
structure as its first member.
|
||||
* The the definition of an driver entry-point vector member in the leaf
|
||||
driver's configuration structure. The leaf driver registration function
|
||||
must store the address of its entry-point vector in this field.
|
||||
* The the definition of an instance handle member in the leaf drivers
|
||||
configuration structure. The leaf driver registration function must store
|
||||
a handle (opaque pointer) to the instance of the leaf driver being
|
||||
registered in this field. Note that this should be the same handle that
|
||||
the leaf driver supplies to NuttX to register itself. The cluster driver
|
||||
will include this handle as a parameter in calls made to the leaf driver.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
struct sensor_cluster_operations_s
|
||||
{
|
||||
CODE int (*driver_open)(FAR void *instance_handle, int32_t arg);
|
||||
CODE int (*driver_close)(FAR void *instance_handle, int32_t arg);
|
||||
CODE ssize_t (*driver_read)(FAR void *instance_handle, FAR char *buffer,
|
||||
size_t buflen);
|
||||
CODE ssize_t (*driver_write)(FAR void *instance_handle,
|
||||
FAR const char *buffer, size_t buflen);
|
||||
CODE off_t (*driver_seek)(FAR void *instance_handle, off_t offset,
|
||||
int whence);
|
||||
CODE int (*driver_ioctl)(FAR void *instance_handle, int cmd,
|
||||
unsigned long arg);
|
||||
CODE int (*driver_suspend)(FAR void *instance_handle, int32_t arg);
|
||||
CODE int (*driver_resume)(FAR void *instance_handle, int32_t arg);
|
||||
};
|
||||
|
||||
Note that the sensor_cluster_operations_s strongly resembles the NuttX fs.h
|
||||
file_operations structures. This permits the current file_operations
|
||||
functions to become thin wrappers around these functions.
|
||||
|
||||
``driver_open()`` Same as the fs.h open() except that arg can be specify
|
||||
permitting more flexibility in sensor configuration and initial operation.
|
||||
when arg = 0 the function of driver_open() must be identical to open().
|
||||
|
||||
``driver_close()`` Same as the fs.h close() except that arg can be specify
|
||||
permitting more flexibility in selecting a sensor low power state.
|
||||
when arg = 0 the function of driver_close() must be identical to close().
|
||||
|
||||
``driver_read()`` Same as the fs.h read().
|
||||
|
||||
``driver_write()`` Same as the fs.h write(). Optional. Set to NULL if not
|
||||
supported.
|
||||
|
||||
``driver_seek()`` Same as the fs.h seek(). Optional. Set to NULL if not
|
||||
supported.
|
||||
|
||||
``driver_ioctl()`` Same as the fs.h ioctl(). Optional. Set to NULL if not
|
||||
supported.
|
||||
|
||||
``driver_suspend()`` and ``driver_resume()`` Optional. Set to NULL if not
|
||||
supported. It is common for sensor applications to conserve power and
|
||||
send their microcontroller into a low power sleep state. It seems
|
||||
appropriate to reserve these spots for future use. These driver entry
|
||||
points exist in Linux and Windows. Since microcontrollers and sensors
|
||||
get more capable every year, there should soon be a requirement for
|
||||
these entry points. Discussion on how to standardize their use and
|
||||
implementation should
|
||||
be taken up independently from this driver document.
|
||||
|
||||
Note that all drivers are encouraged to extend their entry-point vectors
|
||||
beyond this common segment. For example it may be beneficial for the
|
||||
worker task to select between programmed i/o and DMA data transfer
|
||||
routines. Unregulated extensions to the Entry-Point Vector should be
|
||||
encouraged to maximize the benefits of a sensor's features.
|
||||
|
||||
Operation:
|
||||
|
||||
Board logic (configs directory) will register the cluster driver. The
|
||||
cluster driver will register the leaf drivers that it will call.
|
||||
This means that the cluster driver has access to the leaf driver's
|
||||
configuration structures and can pass the Leaf Driver Instance Handle to
|
||||
the leaf driver as a parameter in calls made via the Entry-Point Vector.
|
||||
|
||||
Either board logic or an application program may open() the cluster
|
||||
driver. The cluster driver open() calls the open() function of the leaf
|
||||
drivers. The cluster driver open() or read() function can launch the
|
||||
shared worker task that collects the data.
|
||||
|
||||
The cluster driver close() function calls the close functions of the leaf
|
||||
drivers.
|
||||
|
||||
Implemented Drivers
|
||||
===================
|
||||
|
||||
- adxl372 :doc:`adxl372`
|
||||
- lsm330 :doc:`lsm330`
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
=====================
|
||||
Sensor Legacy Drivers
|
||||
=====================
|
||||
|
||||
The old sensors implementation where the character device interface is not
|
||||
standardized in any way.
|
||||
|
||||
This approach is not recommended for new drivers, because without a
|
||||
standarized interface, creating portable application is imposible.
|
||||
|
||||
Implemented Drivers
|
||||
===================
|
||||
|
||||
Drivers that are available also with the new sensor framework are marked with ``[*]``.
|
||||
|
||||
- :doc:`adt7320`
|
||||
- adxl345
|
||||
- :doc:`aht10`
|
||||
- :doc:`ak09912`
|
||||
- amg88xx
|
||||
- apds9922
|
||||
- apds9960
|
||||
- as5048a
|
||||
- as5048b
|
||||
- as726x
|
||||
- bh1749nuc [*]
|
||||
- bh1750fvi
|
||||
- bmg160
|
||||
- bmi088 [*]
|
||||
- bmi160 [*]
|
||||
- bmi270 [*]
|
||||
- bmp180 [*]
|
||||
- dhtxx
|
||||
- fxos8700cq
|
||||
- hall3ph
|
||||
- hc_sr04
|
||||
- hdc1008
|
||||
- hts221
|
||||
- ina219
|
||||
- ina226
|
||||
- ina3221
|
||||
- isl29023
|
||||
- kxtj9
|
||||
- lis2dh
|
||||
- lis331dl
|
||||
- lis3dh
|
||||
- lis3dsh
|
||||
- lis3mdl
|
||||
- lm75
|
||||
- lm92
|
||||
- lps25h
|
||||
- lsm303agr
|
||||
- lsm6dsl
|
||||
- lsm9ds1
|
||||
- ltc4151
|
||||
- max31855
|
||||
- max31865
|
||||
- max44009
|
||||
- max6675
|
||||
- mb7040
|
||||
- :doc:`mcp9600`
|
||||
- mcp9844
|
||||
- mlx90393
|
||||
- mlx90614
|
||||
- :doc:`mpl115a`
|
||||
- mpu60x0
|
||||
- ms58xx
|
||||
- msa301
|
||||
- qencoder
|
||||
- scd30
|
||||
- scd41
|
||||
- sgp30
|
||||
- sht21
|
||||
- sht3x
|
||||
- :doc:`sht4x`
|
||||
- sps30
|
||||
- t67xx
|
||||
- veml6070
|
||||
- vl53l1x
|
||||
- xen1210
|
||||
- zerocross
|
||||
|
|
@ -1,38 +1,55 @@
|
|||
============================
|
||||
Sensor Driver Model For uORB
|
||||
============================
|
||||
.. _new_sensor_framework:
|
||||
|
||||
=====================
|
||||
Sensor "uORB" Drivers
|
||||
=====================
|
||||
|
||||
NuttX, in order to uniformly manage all sensors, reuse common code, and
|
||||
reduce space occupation, extracts the common parts of all sensor drivers
|
||||
into an **upper half layer** that provides general functionalities.
|
||||
The **lower half layer** is responsible for the actual interaction with sensor registers.
|
||||
The **lower half layer** is responsible for the actual interaction with sensor
|
||||
registers.
|
||||
|
||||
NuttX sensor drivers focus more on physical sensors. For virtually fused
|
||||
sensors generated through integration, they are automatically created through
|
||||
application advertisements or subscriptions. For devices like IMUs, which integrate
|
||||
multiple sensors into one unit, multiple lower halves need to be instantiated within the
|
||||
driver, and device nodes are registered separately through the API (sensor_register)
|
||||
provided by the upper half.
|
||||
multiple sensors into one unit, multiple lower halves need to be instantiated
|
||||
within the driver, and device nodes are registered separately through the API
|
||||
(sensor_register) provided by the upper half.
|
||||
|
||||
Naming
|
||||
======
|
||||
|
||||
The name used for this component in NuttX may be misleading because this sensor
|
||||
framework is not dependent on "uORB" in any way. Sensors implemented in this way
|
||||
can be used as general character drivers with a standardized interface.
|
||||
|
||||
**Driver Model**
|
||||
^^^^^^^^^^^^^^^^
|
||||
================
|
||||
|
||||
The NuttX Sensor Upperhalf Driver is primarily responsible for registering device nodes,
|
||||
implementing the struct file_operations, multi-user access, ring buffer management,
|
||||
low power consumption, and downsampling logic. The Lowerhalf Driver is divided into rpmsg
|
||||
half and a general lower half. The rpmsg half is responsible for cross-core subscription
|
||||
and publication with remote CPUs, while the general lower half is responsible for interacting
|
||||
with sensor hardware. The main actions performed by the general lower half include a set of
|
||||
sensor operations such as activate, set_interval, batch, selftest, set_calibvalue, calibrate,
|
||||
and control. Under interrupt or polling mechanisms, sensor events are sent to the ring buffer
|
||||
in the upper layer.
|
||||
The NuttX Sensor Upperhalf Driver is primarily responsible for registering device
|
||||
nodes, implementing the struct file_operations, multi-user access, ring buffer
|
||||
management, low power consumption, and downsampling logic. The Lowerhalf Driver
|
||||
is divided into rpmsg half and a general lower half. The rpmsg half is responsible
|
||||
for cross-core subscription and publication with remote CPUs, while the general
|
||||
lower half is responsible for interacting with sensor hardware. The main actions
|
||||
performed by the general lower half include a set of sensor operations such as
|
||||
``activate``, ``set_interval``, ``batch``, ``selftest``, ``set_calibvalue``,
|
||||
``calibrate``, and ``control``. Under interrupt or polling mechanisms,
|
||||
sensor events are sent to the ring buffer in the upper layer.
|
||||
|
||||
.. image:: sensor_driver_model.png
|
||||
:width: 800px
|
||||
:align: center
|
||||
|
||||
Problems to solve
|
||||
=================
|
||||
|
||||
The current implementation uses the ``float`` type which may make it difficult
|
||||
to use on platforms without FPU support.
|
||||
|
||||
**Code**
|
||||
^^^^^^^^
|
||||
========
|
||||
|
||||
::
|
||||
|
||||
|
|
@ -48,66 +65,75 @@ in the upper layer.
|
|||
|
||||
|
||||
**Data Structures**
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
===================
|
||||
|
||||
**Sensor Types**
|
||||
----------------
|
||||
|
||||
NuttX defines 50+ types of sensors, covering most physical sensors. All type definitions
|
||||
are located in include/nuttx/uorb.h. If a new type needs to be added, a comment must be
|
||||
provided for the new type, explaining the purpose and units of the sensor.
|
||||
NuttX defines 50+ types of sensors, covering most physical sensors. All type
|
||||
definitions are located in include/nuttx/uorb.h. If a new type needs to be added,
|
||||
a comment must be provided for the new type, explaining the purpose and units of
|
||||
the sensor.
|
||||
|
||||
**SENSOR_TYPE_CUSTOM**
|
||||
|
||||
This is a custom type used for irregular sensor devices where the event structure changes or is dynamically altered. It is registered using sensor_custom_register.
|
||||
This is a custom type used for irregular sensor devices where the event structure
|
||||
changes or is dynamically altered. It is registered using ``sensor_custom_register``.
|
||||
|
||||
**SENSOR_TYPE_ACCELEROMETER**
|
||||
|
||||
Accelerometer, used to measure the acceleration vector of the device. Units: m/s^2.
|
||||
Event data structure: (This indicates that there is a specific data structure for accelerometer events, but the actual structure is not provided in the text you gave.)
|
||||
Accelerometer, used to measure the acceleration vector of the device. Units: m/s=2.
|
||||
Event data structure: (This indicates that there is a specific data structure for
|
||||
accelerometer events, but the actual structure is not provided in the text you gave.)
|
||||
|
||||
(won't introduce them one by one since there are many)
|
||||
|
||||
**Sensor Topic Definition**
|
||||
---------------------------
|
||||
|
||||
The data structure for sensors, which is also the topic structure for uORB, is defined
|
||||
in include/nuttx/uorb.h.
|
||||
The data structure for sensors, which is also the topic structure for uORB, is
|
||||
defined in ``include/nuttx/uorb.h``.
|
||||
|
||||
**Lower Half Structure**
|
||||
------------------------
|
||||
|
||||
This structure serves as a bridge between the sensor driver's upper half and lower half.
|
||||
Both the upper half and lower half populate this structure, with the lower half responsible
|
||||
for synchronizing configuration information and the upper half for exposing data reporting interfaces.
|
||||
This structure serves as a bridge between the sensor driver's upper half and
|
||||
lower half. Both the upper half and lower half populate this structure, with
|
||||
the lower half responsible for synchronizing configuration information and
|
||||
the upper half for exposing data reporting interfaces.
|
||||
|
||||
The lower part highlighted in red is filled by the lower half driver, while the rest is
|
||||
filled by the upper half.
|
||||
The lower part highlighted in red is filled by the lower half driver, while
|
||||
the rest is filled by the upper half.
|
||||
|
||||
``type`` indicates the sensor type: SENSOR_TYPE_XXX;
|
||||
``type`` indicates the sensor type: ``SENSOR_TYPE_XXX``
|
||||
|
||||
``nbuffer`` specifies the length of the ring buffer in the upper half driver;
|
||||
|
||||
``uncalibrated`` indicates whether the data reported by the lower half driver is uncalibrated.
|
||||
If true, it means uncalibrated data is reported, and the registered device node will have a suffix "_uncal".
|
||||
``uncalibrated`` indicates whether the data reported by the lower half driver is
|
||||
uncalibrated. If true, it means uncalibrated data is reported, and the registered
|
||||
device node will have a suffix ``_uncal``.
|
||||
|
||||
``ops`` represents the set of sensor operations implemented by the lower half driver.
|
||||
|
||||
``push_event`` and ``notify_event`` are not used simultaneously and are filled by the upper half.
|
||||
``push_event`` and ``notify_event`` are not used simultaneously and are filled
|
||||
by the upper half.
|
||||
|
||||
``push_event`` works in conjunction with the ring buffer for the lower half to report data to the ring buffer;
|
||||
``push_event`` works in conjunction with the ring buffer for the lower half to
|
||||
report data to the ring buffer;
|
||||
|
||||
``notify_event`` is used in conjunction with fetch to notify the upper half that data is ready when actively
|
||||
``notify_event`` is used in conjunction with fetch to notify the upper half that
|
||||
data is ready when actively
|
||||
pulling data in a blocking operation.
|
||||
|
||||
``sensor_lock`` and ``sensor_unlock`` are filled by the upper half and exported to the lower half to avoid
|
||||
recursive deadlock issues. Currently, they are only used for sensor_rpmsg.
|
||||
``sensor_lock`` and ``sensor_unlock`` are filled by the upper half and exported
|
||||
to the lower half to avoid recursive deadlock issues. Currently, they are only
|
||||
used for sensor_rpmsg.
|
||||
|
||||
``priv`` is filled by the upper half and represents the upper context.
|
||||
|
||||
``persist`` indicates whether the topic is a notification-type topic.
|
||||
|
||||
::
|
||||
.. code:: C
|
||||
|
||||
struct sensor_lowerhalf_s
|
||||
{
|
||||
|
|
@ -141,16 +167,17 @@ including registration and timestamp acquisition.
|
|||
For the 50+ types of sensors, the sensor_register function can be used to register
|
||||
a character device. The parameter dev represents the handle of the lower half,
|
||||
and devno is the index of the device name. If the registration is successful,
|
||||
a node will be created under /dev/topic, for example: /dev/topic/sensor_accel0.
|
||||
a node will be created under ``/dev/{topic}``, for example: ``/dev/topic/sensor_accel0``.
|
||||
If it fails, an error code will be returned.
|
||||
|
||||
For custom special-type drivers, the ``sensor_custom_register`` function needs to be
|
||||
used to register a character device. The parameter dev is the handle of the lower half,
|
||||
path is the path of the character device, and esize is the element size of the data reported
|
||||
by the sensor. If the registration is successful, a character device node will be created
|
||||
at the specified path. If it fails, an error code will be returned.
|
||||
For custom special-type drivers, the ``sensor_custom_register`` function needs
|
||||
to be used to register a character device. The parameter dev is the handle of
|
||||
the lower half, path is the path of the character device, and esize is
|
||||
the element size of the data reported by the sensor. If the registration is
|
||||
successful, a character device node will be created at the specified path.
|
||||
If it fails, an error code will be returned.
|
||||
|
||||
::
|
||||
.. code:: C
|
||||
|
||||
int sensor_register(FAR struct sensor_lowerhalf_s *dev, int devno);
|
||||
void sensor_unregister(FAR struct sensor_lowerhalf_s *dev, int devno);
|
||||
|
|
@ -165,30 +192,32 @@ at the specified path. If it fails, an error code will be returned.
|
|||
|
||||
The function returns a timestamp with microsecond precision.
|
||||
|
||||
::
|
||||
|
||||
static inline uint64_t sensor_get_timestamp(void);
|
||||
.. code:: C
|
||||
|
||||
static inline uint64_t sensor_get_timestamp(void);
|
||||
|
||||
**Sensor Driver Operation Set**
|
||||
-------------------------------
|
||||
|
||||
Sensor driver frameworks for different systems and platforms always revolve around
|
||||
sensor characteristics, and the NuttX Sensor is no exception. For sensors, common
|
||||
operations include: opening/closing, initializing range/resolution/filtering,
|
||||
Sensor driver frameworks for different systems and platforms always revolve
|
||||
around sensor characteristics, and the NuttX Sensor is no exception. For sensors,
|
||||
common operations include: opening/closing, initializing range/resolution/filtering,
|
||||
setting the sampling rate (ODR)/hardware FIFO/operating mode, and interrupt control.
|
||||
Based on practical applications and references from other systems, several key points
|
||||
have been selected to form the sensor operation set. For those without the need for
|
||||
dynamic changes, they can simply be passed as parameters to the initialization function.
|
||||
Based on practical applications and references from other systems, several key
|
||||
points have been selected to form the sensor operation set. For those without
|
||||
the need for dynamic changes, they can simply be passed as parameters to the
|
||||
initialization function.
|
||||
|
||||
**Opening/Closing**
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When the caller invokes open and close, the corresponding open and close
|
||||
in the lower half will be called, with parameters being lower and filep respectively.
|
||||
filep contains user information, so the driver can differentiate between different users.
|
||||
Currently, this interface is only used by the sensor_rpmsg lower half.
|
||||
in the lower half will be called, with parameters being lower and filep
|
||||
respectively. filep contains user information, so the driver can differentiate
|
||||
between different users. Currently, this interface is only used by the sensor_rpmsg
|
||||
lower half.
|
||||
|
||||
::
|
||||
.. code:: C
|
||||
|
||||
CODE int (*open)(FAR struct sensor_lowerhalf_s *lower,
|
||||
FAR struct file *filep);
|
||||
|
|
@ -199,10 +228,11 @@ Currently, this interface is only used by the sensor_rpmsg lower half.
|
|||
**Activating/Deactivating the Sensor**
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When the caller invokes open, if it is a subscriber, it will call activate in the lower half
|
||||
to activate the sensor. When close is called, deactivate is invoked to turn off the sensor.
|
||||
When the caller invokes open, if it is a subscriber, it will call activate in
|
||||
the lower half to activate the sensor. When close is called, deactivate is
|
||||
invoked to turn off the sensor.
|
||||
|
||||
::
|
||||
.. code:: C
|
||||
|
||||
CODE int (*activate)(FAR struct sensor_lowerhalf_s *lower,
|
||||
FAR struct file *filep, bool enable);
|
||||
|
|
@ -210,16 +240,22 @@ to activate the sensor. When close is called, deactivate is invoked to turn off
|
|||
**Setting the Sampling Rate**
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Applications (including the Sensor service) set the sampling rate of the sensor through the
|
||||
system call ioctl.
|
||||
Applications (including the Sensor service) set the sampling rate of the sensor
|
||||
through the system call ioctl.
|
||||
|
||||
Call flow: ioctl(fd, SNIOC_SET_INTERVAL, &interval) -> vfs -> sensor_ioctl -> set_interval().
|
||||
Call flow:
|
||||
|
||||
The sampling interval between consecutive samples of the sensor is set in microseconds.
|
||||
If period_us exceeds the range of min_delay and max_delay, it will be adjusted. When modifying
|
||||
the sampling rate, it should be ensured that the data that has already been prepared is not lost.
|
||||
#. ``ioctl(fd, SNIOC_SET_INTERVAL, &interval)``
|
||||
#. vfs
|
||||
#. ``sensor_ioctl``
|
||||
#. ``set_interval()``.
|
||||
|
||||
::
|
||||
The sampling interval between consecutive samples of the sensor is set in
|
||||
microseconds. If period_us exceeds the range of min_delay and max_delay, it
|
||||
will be adjusted. When modifying the sampling rate, it should be ensured that
|
||||
the data that has already been prepared is not lost.
|
||||
|
||||
.. code:: C
|
||||
|
||||
CODE int (*batch)(FAR struct sensor_lowerhalf_s *lower,
|
||||
FAR struct file *filep,
|
||||
|
|
@ -231,7 +267,7 @@ the sampling rate, it should be ensured that the data that has already been prep
|
|||
|
||||
To proactively obtain sensor data, set to NULL if using interrupt or polling methods.
|
||||
|
||||
::
|
||||
.. code:: C
|
||||
|
||||
CODE int (*fetch)(FAR struct sensor_lowerhalf_s *lower,
|
||||
FAR struct file *filep,
|
||||
|
|
@ -242,7 +278,7 @@ To proactively obtain sensor data, set to NULL if using interrupt or polling met
|
|||
|
||||
The sensor self-test is mainly used for factory testing and aging purposes.
|
||||
|
||||
::
|
||||
.. code:: C
|
||||
|
||||
CODE int (*selftest)(FAR struct sensor_lowerhalf_s *lower,
|
||||
FAR struct file *filep,
|
||||
|
|
@ -254,7 +290,7 @@ The sensor self-test is mainly used for factory testing and aging purposes.
|
|||
Trigger calibration with calibrate and return calibration data to arg.
|
||||
Use set_calibvalue to set calibration data to the underlying sensor.
|
||||
|
||||
::
|
||||
.. code:: C
|
||||
|
||||
CODE int (*calibrate)(FAR struct sensor_lowerhalf_s *lower,
|
||||
FAR struct file *filep,
|
||||
|
|
@ -268,9 +304,10 @@ Use set_calibvalue to set calibration data to the underlying sensor.
|
|||
**Sensor Information**
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Use get_info to proactively obtain sensor information data, with the return value being sensor_device_info_s.
|
||||
Use get_info to proactively obtain sensor information data, with the return value
|
||||
being ``sensor_device_info_s``.
|
||||
|
||||
::
|
||||
.. code:: C
|
||||
|
||||
struct sensor_device_info_s
|
||||
{
|
||||
|
|
@ -284,7 +321,7 @@ Use get_info to proactively obtain sensor information data, with the return valu
|
|||
uint32_t fifo_max_event_count;
|
||||
char name[SENSOR_INFO_NAME_SIZE];
|
||||
char vendor[SENSOR_INFO_NAME_SIZE];
|
||||
};
|
||||
};
|
||||
|
||||
CODE int (*get_info)(FAR struct sensor_lowerhalf_s *lower,
|
||||
FAR struct file *filep,
|
||||
|
|
@ -293,114 +330,167 @@ Use get_info to proactively obtain sensor information data, with the return valu
|
|||
**Custom Control**
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In addition to the above controls, if certain sensor control requirements are still not met, the control
|
||||
command with custom controls can be used. Call flow: ioctl(fd, custom macro cmd, custom parameters) -> vfs ->
|
||||
sensor_ioctl -> control().
|
||||
In addition to the above controls, if certain sensor control requirements are
|
||||
still not met, the control command with custom controls can be used.
|
||||
|
||||
::
|
||||
Call flow:
|
||||
|
||||
CODE int (*control)(FAR struct file *filep,
|
||||
FAR struct sensor_lowerhalf_s *lower,
|
||||
int cmd, unsigned long arg);
|
||||
#. ``ioctl(fd, custom macro cmd, custom parameters)``
|
||||
#. vfs
|
||||
#. ``sensor_ioctl``
|
||||
#. ``control()``.
|
||||
|
||||
.. code:: C
|
||||
|
||||
CODE int (*control)(FAR struct file *filep,
|
||||
FAR struct sensor_lowerhalf_s *lower,
|
||||
int cmd, unsigned long arg);
|
||||
|
||||
**Downsampling**
|
||||
----------------
|
||||
|
||||
The downsampling capability of Vela Sensor is provided by the sensor upper half at the driver layer,
|
||||
supporting both aligned and unaligned downsampling mechanisms. When the publisher pushes the main line
|
||||
index each time, the subscriber retrieves data from its own index. If the difference between the two
|
||||
indexes exceeds the length of the internal queue, data will be lost. Otherwise, the next theoretical
|
||||
data point is calculated based on the subscription frequency, the publication frequency factor, and the current index.
|
||||
The downsampling capability of Vela Sensor is provided by the sensor upper half
|
||||
at the driver layer, supporting both aligned and unaligned downsampling mechanisms.
|
||||
When the publisher pushes the main line index each time, the subscriber retrieves
|
||||
data from its own index. If the difference between the two indexes exceeds the
|
||||
length of the internal queue, data will be lost. Otherwise, the next theoretical
|
||||
data point is calculated based on the subscription frequency, the publication
|
||||
frequency factor, and the current index.
|
||||
|
||||
**Multi-Core Mechanism**
|
||||
------------------------
|
||||
|
||||
The cross-core capability of Vela Sensor is provided by the sensor rpmsg lower half at the driver layer,
|
||||
which is primarily responsible for sending or receiving subscription and broadcast messages from other cores.
|
||||
The cross-core capability of Vela Sensor is provided by the sensor rpmsg lower
|
||||
half at the driver layer, which is primarily responsible for sending or receiving
|
||||
subscription and broadcast messages from other cores.
|
||||
|
||||
**Publishing Topics**
|
||||
---------------------
|
||||
|
||||
When a local application publishes a topic for the first time, it broadcasts the message to all cores.
|
||||
If there are subscriptions on other cores, they bind with each other. A stub is created locally to represent
|
||||
the subscription on a remote core, and a proxy is created on the remote core to represent the local publisher.
|
||||
All subsequent communication between them is determined by the context of the stub and proxy.
|
||||
When a local application publishes a topic for the first time, it broadcasts
|
||||
the message to all cores. If there are subscriptions on other cores, they bind
|
||||
with each other. A stub is created locally to represent the subscription on a
|
||||
remote core, and a proxy is created on the remote core to represent the local
|
||||
publisher. All subsequent communication between them is determined by the
|
||||
context of the stub and proxy.
|
||||
|
||||
**Subscribing to Topics**
|
||||
-------------------------
|
||||
|
||||
When a local application subscribes to a topic for the first time, if the message is broadcast to all cores and there
|
||||
are publishers on other cores, they bind with each other and communicate through stubs and proxies.
|
||||
When a local application subscribes to a topic for the first time, if the message
|
||||
is broadcast to all cores and there are publishers on other cores, they bind with
|
||||
each other and communicate through stubs and proxies.
|
||||
|
||||
**Remote Control**
|
||||
------------------
|
||||
|
||||
When a local subscriber modifies the sampling rate and the publisher of that topic is remote, the local proxy will
|
||||
publish this sampling rate to the remote stub. Upon receiving this control information, the stub sets it for the
|
||||
actual physical hardware. The same applies to other controls.
|
||||
When a local subscriber modifies the sampling rate and the publisher of that
|
||||
topic is remote, the local proxy will publish this sampling rate to the remote
|
||||
stub. Upon receiving this control information, the stub sets it for the actual
|
||||
physical hardware. The same applies to other controls.
|
||||
|
||||
**Remote Message Publishing**
|
||||
-----------------------------
|
||||
|
||||
When local data is published, the sensor rpmsg lower half collects all messages within a sampling interval that
|
||||
does not exceed half of the fastest topic's interval and sends them to other cores together, reducing IPC
|
||||
occurrences and saving power consumption.
|
||||
When local data is published, the sensor rpmsg lower half collects all messages
|
||||
within a sampling interval that does not exceed half of the fastest topic's
|
||||
interval and sends them to other cores together, reducing IPC occurrences and
|
||||
saving power consumption.
|
||||
|
||||
**Subscription and Publication Order**
|
||||
--------------------------------------
|
||||
|
||||
There is no order restriction for advertising and subscribing to topics. For notification-type topics, even
|
||||
if the advertisement is canceled immediately after data publication, other cores can still successfully obtain
|
||||
the latest data. For general-purpose topics, subscribing after publication will only allow reading of data published
|
||||
after the subscription.
|
||||
There is no order restriction for advertising and subscribing to topics.
|
||||
For notification-type topics, even if the advertisement is canceled immediately
|
||||
after data publication, other cores can still successfully obtain the latest data.
|
||||
For general-purpose topics, subscribing after publication will only allow reading
|
||||
of data published after the subscription.
|
||||
|
||||
**Programming Modes**
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
=====================
|
||||
|
||||
NuttX Sensor drivers support three data retrieval methods: proactive, interrupt-driven, and polling. The proactive
|
||||
method allows filling sensor events using a buffer passed in by the app, reducing memory copy operations.
|
||||
The interrupt-driven and polling methods open an internal circular buffer, where each event is automatically pushed
|
||||
upon generation. The size of the buffer is set by sensor_lowerhalf_s::buffer_size. For sensors with high sampling rates,
|
||||
it is recommended to set the buffer size for 2-3 events, while for sensors with low sampling rates, setting it for 1 event
|
||||
is sufficient.
|
||||
NuttX Sensor drivers support three data retrieval methods: proactive,
|
||||
interrupt-driven, and polling. The proactive method allows filling sensor events
|
||||
using a buffer passed in by the app, reducing memory copy operations.
|
||||
The interrupt-driven and polling methods open an internal circular buffer, where
|
||||
each event is automatically pushed upon generation. The size of the buffer is set
|
||||
by sensor_lowerhalf_s::buffer_size. For sensors with high sampling rates, it is
|
||||
recommended to set the buffer size for 2-3 events, while for sensors with low
|
||||
sampling rates, setting it for 1 event is sufficient.
|
||||
|
||||
**Proactive Retrieval**
|
||||
-----------------------
|
||||
|
||||
This method is recommended for sensors with low sampling rates and small data volumes. The sensor_ops_s::fetch function must be implemented.
|
||||
The call flow is: read(fd, buf, len) -> vfs -> sensor_read -> fetch().
|
||||
It is not advisable to use the fetch method to retrieve sensor data. When the caller invokes read, accessing the bus to obtain data has two
|
||||
disadvantages: the bus speed is low, which may block the upper layer; and the retrieved data may be old and not representative of the current state.
|
||||
This method is recommended for sensors with low sampling rates and small data
|
||||
volumes. The ``sensor_ops_s::fetch`` function must be implemented.
|
||||
|
||||
When using the fetch function, the upper layer will automatically disable the circular buffer and can directly use the user-space buffer to store
|
||||
register data, reducing memory copy operations. When the character device node is opened in non-blocking mode, the fetch operation will directly
|
||||
read the registers via the I2C/SPI bus, and the poll operation will always succeed. When opened in blocking mode, if there is no ready data when
|
||||
read is called, the poll function can be used to monitor it. If a POLLIN event occurs, the read function should be called immediately to retrieve
|
||||
the data.
|
||||
The call flow is:
|
||||
|
||||
#. ``read(fd, buf, len)``
|
||||
#. vfs
|
||||
#. ``sensor_read``
|
||||
#. ``fetch()``
|
||||
|
||||
It is not advisable to use the fetch method to retrieve sensor data. When the
|
||||
caller invokes read, accessing the bus to obtain data has two disadvantages:
|
||||
the bus speed is low, which may block the upper layer; and the retrieved data
|
||||
may be old and not representative of the current state.
|
||||
|
||||
When using the fetch function, the upper layer will automatically disable
|
||||
the circular buffer and can directly use the user-space buffer to store register
|
||||
data, reducing memory copy operations. When the character device node is opened
|
||||
in non-blocking mode, the fetch operation will directly read the registers via
|
||||
the I2C/SPI bus, and the poll operation will always succeed. When opened in
|
||||
blocking mode, if there is no ready data when read is called, the poll function
|
||||
can be used to monitor it. If a POLLIN event occurs, the read function should
|
||||
be called immediately to retrieve the data.
|
||||
|
||||
**Interrupt-Driven Retrieval**
|
||||
------------------------------
|
||||
|
||||
For sensors with hardware interrupts, sensor data can be read via the bus in the interrupt's bottom half, and the event can be pushed to the upper
|
||||
layer's circular buffer using sensor_lowerhalf_s::push_event. When using the internal circular buffer, data generated in each interrupt's bottom
|
||||
half is pushed to the upper layer's circular buffer. Upper-layer applications read data directly from the buffer. When the buffer has no data,
|
||||
it will depend on the blocking flag in f_oflags to determine whether to wait. Common sensors operate in interrupt mode. When an interrupt occurs,
|
||||
a worker is scheduled to start the bottom half, which then retrieves sensor data via buses such as I2C or SPI and calls the push_event interface
|
||||
to push the data to the upper half's buffer. It is recommended to configure an interrupt pin for sensors with a sampling rate higher than 25Hz.
|
||||
For sensors with hardware interrupts, sensor data can be read via the bus in
|
||||
the interrupt's bottom half, and the event can be pushed to the upper layer's
|
||||
circular buffer using ``sensor_lowerhalf_s::push_event``. When using the internal
|
||||
circular buffer, data generated in each interrupt's bottom half is pushed to
|
||||
the upper layer's circular buffer. Upper-layer applications read data directly
|
||||
from the buffer. When the buffer has no data, it will depend on the blocking
|
||||
flag in f_oflags to determine whether to wait. Common sensors operate in
|
||||
interrupt mode. When an interrupt occurs, a worker is scheduled to start
|
||||
the bottom half, which then retrieves sensor data via buses such as
|
||||
I2C or SPI and calls the push_event interface to push the data to the upper
|
||||
half's buffer. It is recommended to configure an interrupt pin for sensors
|
||||
with a sampling rate higher than 25Hz.
|
||||
|
||||
**Polling Retrieval**
|
||||
---------------------
|
||||
|
||||
For sensors without hardware interrupts, data generated by the sensor can be collected through periodic polling, with the polling period typically
|
||||
varying based on the sampling rate.
|
||||
For sensors without hardware interrupts, data generated by the sensor can be
|
||||
collected through periodic polling, with the polling period typically varying
|
||||
based on the sampling rate.
|
||||
|
||||
**Example Driver**
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
drivers/sensors/
|
||||
adxl362_uorb.c bh1749nuc_uorb.c bmi088_uorb.c bmi270_uorb.c bmp180_uorb.c ds18b20_uorb.c fs3000_uorb.c goldfish_gnss_uorb.c hyt271_uorb.c lsm9ds1_uorb.c mpu9250_uorb.c wtgahrs2_uorb.c
|
||||
adxl372_uorb.c bme680_uorb.c bmi160_uorb.c bmm150_uorb.c bmp280_uorb.c fakesensor_uorb.c gnss_uorb.c goldfish_sensor_uorb.c l3gd20_uorb.c ltr308_uorb.c ms56xx_uorb.c
|
||||
|
||||
Implemented Drivers
|
||||
===================
|
||||
|
||||
- :doc:`adxl362`
|
||||
- :doc:`adxl372`
|
||||
- bh1749nuc
|
||||
- bme680
|
||||
- bmi088
|
||||
- bmi160
|
||||
- bmi270
|
||||
- bmm150
|
||||
- bmp180
|
||||
- bmp280
|
||||
- ds18b20
|
||||
- fakesensor
|
||||
- fs3000
|
||||
- gnss
|
||||
- goldfish_gnss
|
||||
- goldfish_sensor
|
||||
- hyt271
|
||||
- l3gd20
|
||||
- lsm9ds1
|
||||
- ltr308
|
||||
- mpu9250
|
||||
- ms56xx
|
||||
- wtgahrs2
|
||||
Loading…
Add table
Add a link
Reference in a new issue