diff --git a/Documentation/components/drivers/special/index.rst b/Documentation/components/drivers/special/index.rst index e50ab363c2b..f2df595c826 100644 --- a/Documentation/components/drivers/special/index.rst +++ b/Documentation/components/drivers/special/index.rst @@ -38,7 +38,6 @@ following section. rptun.rst rwbuffer.rst sensors.rst - sensors_uorb.rst segger.rst spi.rst syslog.rst diff --git a/Documentation/components/drivers/special/sensors.rst b/Documentation/components/drivers/special/sensors.rst index 53dff63e272..6891bebec21 100644 --- a/Documentation/components/drivers/special/sensors.rst +++ b/Documentation/components/drivers/special/sensors.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 `, 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 diff --git a/Documentation/components/drivers/special/sensor_driver_model.png b/Documentation/components/drivers/special/sensors/sensor_driver_model.png similarity index 100% rename from Documentation/components/drivers/special/sensor_driver_model.png rename to Documentation/components/drivers/special/sensors/sensor_driver_model.png diff --git a/Documentation/components/drivers/special/sensors/sensors_cluster.rst b/Documentation/components/drivers/special/sensors/sensors_cluster.rst new file mode 100644 index 00000000000..4eb50d77688 --- /dev/null +++ b/Documentation/components/drivers/special/sensors/sensors_cluster.rst @@ -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` diff --git a/Documentation/components/drivers/special/sensors/sensors_legacy.rst b/Documentation/components/drivers/special/sensors/sensors_legacy.rst new file mode 100644 index 00000000000..f578fbe11a5 --- /dev/null +++ b/Documentation/components/drivers/special/sensors/sensors_legacy.rst @@ -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 diff --git a/Documentation/components/drivers/special/sensors_uorb.rst b/Documentation/components/drivers/special/sensors/sensors_uorb.rst similarity index 50% rename from Documentation/components/drivers/special/sensors_uorb.rst rename to Documentation/components/drivers/special/sensors/sensors_uorb.rst index 582ce025472..4e6f829ba2d 100644 --- a/Documentation/components/drivers/special/sensors_uorb.rst +++ b/Documentation/components/drivers/special/sensors/sensors_uorb.rst @@ -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