mirror of
https://github.com/apache/nuttx.git
synced 2026-08-18 11:41:51 +08: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:
@@ -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
|
||||
+230
-140
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user