| .. warning:: this list is incomplete. See drivers/sensors for ar full list of |
| supported sensors |
| |
| ============== |
| Sensor Drivers |
| ============== |
| |
| ADXL345 |
| ======= |
| |
| Contributed by Alan Carvalho de Assis |
| |
| The ADXL345 accelerometer can operate in I2C or SPI mode. To operate in I2C |
| mode just connect the CS pin to Vddi/o. |
| |
| In order to operate in SPI mode CS need to use connected to microcontroller, |
| it cannot leave unconnected. |
| |
| In SPI mode it works with clock polarity (CPOL) = 1 and clock phase (CPHA) |
| = 1. |
| |
| ADXL372 |
| ======= |
| |
| Contributed by Bob Feretich |
| |
| The ADXL372 is a 200g tri-axis accelerometer that is capable of detecting |
| and recording shock impact impact events. Recording trigger |
| characteristics are programmed into the sensor via multiple threshold and |
| duration registers. The ADXL372 is a SPI only device that can transfer |
| data at 10 MHz. The data transfer performance of this part permits the |
| sensor to be sampled "on demand" rather than periodically sampled by a |
| worker task. |
| |
| See the description of the "Common Sensor Register Interface" below for more |
| details. It also implements the "Sensor Cluster Driver Interface". |
| |
| LSM330_SPI |
| ========== |
| |
| Contributed by Bob Feretich |
| |
| The LSM330 consists of a multi-range tri-axis accelerometer and a |
| multi-range tri-axis gyroscope. The tri-axis accelerometer features two |
| state machines that can be firmware programmed for event detection. The |
| tri-axis gyroscope features threshold and duration registers for event |
| detection. |
| |
| This driver supports the LSM330 in SPI mode. In this mode, the LSM330 |
| that can transfer data at 10 MHz. The data transfer performance of |
| this part permits the sensor to be sampled "on demand" rather than |
| periodically sampled by a worker task. See the description of the "Common |
| Sensor Register Interface" below for more details. It also implements the |
| "Sensor Cluster Driver Interface". |
| |
| MPL115A |
| ======= |
| |
| Contributed by Alan Carvalho de Assis |
| |
| This driver has support only for MPL115A1 (SPI), but support to MPL115A2 |
| (I2C) can be added easily. |
| |
| 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. |
| |
| ADT7320 |
| ======= |
| |
| Contributed by Augusto Fraga Giachero |
| |
| The ADT7320 is a SPI temperature sensor with a temperature range of |
| −40°C to +150°C. |