This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 504f838577 sensors/nau7802: Add support for the NAU7802
504f838577 is described below

commit 504f838577fef42069d1f10f62eca58f15183b4f
Author: Daniel Byshkin <onl...@bskdany.com>
AuthorDate: Thu Mar 27 14:51:06 2025 -0400

    sensors/nau7802: Add support for the NAU7802
    
    This patch adds support for the NAU7802 24 bit ADC from Nuvoton.
    Please read the documentation for more details.
    
    Signed-off-by: Daniel Byshkin <onl...@bskdany.com>
---
 .../components/drivers/special/sensors.rst         |   1 +
 .../components/drivers/special/sensors/nau7802.rst | 211 +++++
 .../drivers/special/sensors/sensors_uorb.rst       |   1 +
 drivers/sensors/Kconfig                            |   7 +
 drivers/sensors/Make.defs                          |   5 +
 drivers/sensors/nau7802.c                          | 937 +++++++++++++++++++++
 include/nuttx/sensors/ioctl.h                      |  18 +
 include/nuttx/sensors/nau7802.h                    | 108 +++
 8 files changed, 1288 insertions(+)

diff --git a/Documentation/components/drivers/special/sensors.rst 
b/Documentation/components/drivers/special/sensors.rst
index 5c9efb16cc..2b4bd4455a 100644
--- a/Documentation/components/drivers/special/sensors.rst
+++ b/Documentation/components/drivers/special/sensors.rst
@@ -29,6 +29,7 @@ general interafce.
     sensors/lsm330.rst
     sensors/mcp9600.rst
     sensors/mpl115a.rst
+    sensors/nau7802.rst
     sensors/sht4x.rst
     sensors/lsm6dso32.rst
     sensors/lis2mdl.rst
diff --git a/Documentation/components/drivers/special/sensors/nau7802.rst 
b/Documentation/components/drivers/special/sensors/nau7802.rst
new file mode 100644
index 0000000000..fe2e2b6288
--- /dev/null
+++ b/Documentation/components/drivers/special/sensors/nau7802.rst
@@ -0,0 +1,211 @@
+=======
+NAU7802
+=======
+
+Contributed by Daniel Byshkin.
+
+The Adafruit NAU7802 is a high-resolution 24-bit ADC with an integrated load 
cell amplifier. It is designed for use with load cells and other sensors that 
require high precision and low noise measurements. The NAU7802 features a 
built-in programmable gain amplifier (PGA) that allows for easy calibration and 
adjustment of the sensor's output.
+
+The driver uses the :doc:`uorb
+</components/drivers/special/sensors/sensors_uorb>` interface.
+
+Application Programming Interface
+=================================
+
+.. code-block:: c
+
+   #include <nuttx/sensors/nau7802.h>
+
+The NAU7802 registration function allows the driver to be registered as a UORB
+driver. Registering this driver will cause the ``/dev/uorb/sensor_force<n>`` 
topic
+to appear, where ``n`` is the value of ``devno``.
+
+Registering the device in polling mode will create a kernel thread to poll the 
sensor
+
+.. code-block:: c
+
+   int err = nau7802_register(i2c_master, 0, 0x2a);
+   if(err < 0){
+     printf("Failed to register NAU7802: %d\n", err);
+   }
+
+The following are available commands for the NAU7802 driver:
+
+``SNIOC_RESET``
+----------------
+
+Performs a reset of all registers on the NAU7802.
+
+.. code-block:: c
+
+   orb_ioctl(sensor, SNIOC_RESET);
+
+``SNIOC_SET_GAIN``
+------------------------
+
+This command sets the gain of the NAU7802. The possible values are dictated by 
the
+``nau7802_gain_e`` enum. The default value is 128x.
+
+.. code-block:: c
+
+   orb_ioctl(sensor, SNIOC_SET_GAIN, NAU7802_GAIN_128);
+
+``SNIOC_SET_INTERVAL``
+------------------------
+
+This commands sets the polling interval of the NAU7802. The possible values 
are dictated by the
+``nau7802_odr_e`` enum. The default value is 10HZ.
+
+.. code-block:: c
+
+   orb_ioctl(sensor, SNIOC_SET_INTERVAL, NAU7802_ODR_10HZ);
+
+``SNIOC_SET_LDO``
+------------------------
+
+This command sets the LDO voltage of the NAU7802. The possible values are 
dictated by the
+``nau7802_ldo_e`` enum. The default value is 3.0V.
+
+.. code-block:: c
+
+    orb_ioctl(sensor, SNIOC_SET_LDO, NAU7802_LDO_3V0);
+
+``SNIOC_CALIBRATE``
+------------------------
+
+This commands performs one of the calibration procedures of the NAU7802. The 
possible calibration modes are:
+
+ - NAU7802_CALMOD_INTERNAL: Removes internal PGA gain and offset errors.
+ - NAU7802_CALMOD_OFFSET: Calibrates the zero point of the sensor. 
+ - NAU7802_CALMOD_GAIN: Calibrates the max value of the sensor. 
+
+.. code-block:: c
+
+   orb_ioctl(sensor, SNIOC_CALIBRATE, NAU7802_CALMOD_INTERNAL);
+
+For the gain calibration mode the user must place a known weight on the 
sensor. Unfortunately the NAU7802 records it as the maximum value, thus if your 
loadcell supports up to 100kg you shall put a 100kg weight on. 
+
+A workaround would be to do a manual calibration by placing a smaller known 
weight and polling the sensor to get an average point, then using such point to 
offset the recorded values. An example is provided below.
+
+.. code-block:: c
+
+  #include "stdio.h"
+  #include <errno.h>
+  #include <fcntl.h>
+  #include <nuttx/sensors/nau7802.h>
+  #include <signal.h>
+  #include <stdbool.h>
+  #include <stdio.h>
+  #include <stdlib.h>
+  #include <sys/ioctl.h>
+  #include <uORB/uORB.h>
+  #include <unistd.h>
+
+  int get_data(const struct orb_metadata *imu_meta, int imu, struct 
sensor_force *data) {
+    int err = 0;
+    bool update = false;
+    err = orb_check(imu, &update);
+    if (err < 0) {
+        return err;
+    }
+
+    err = orb_copy(imu_meta, imu, data);
+    if (err < 0) {
+        return err;
+    }
+    return err;
+  }
+
+  int main(int argc, char **argv) {
+    int err;
+    int imu;
+    char *name = "sensor_force0";
+
+    const struct orb_metadata *imu_meta = orb_get_meta(name);
+    if (imu_meta == NULL) {
+        fprintf(stderr, "Failed to get metadata for %s\n", name);
+        return EXIT_FAILURE;
+    }
+
+    imu = orb_subscribe(imu_meta);
+    if (imu < 0) {
+        fprintf(stderr, "Could not subsribe to %s: %d\n", name, errno);
+        return EXIT_FAILURE;
+    }
+
+    struct sensor_force data;
+
+    // flush 10 readings
+    for (int i = 0; i < 10; i++) {
+        err = get_data(imu_meta, imu, &data);
+        if (err < 0) {
+            printf("Error reading data\n");
+        }
+        usleep(100000); 
+    }
+
+    long zero_point = 0;
+    for (int i = 0; i < 10; i++) {
+        err = get_data(imu_meta, imu, &data);
+        if (err < 0) {
+            printf("Error reading data\n");
+        } else {
+            zero_point += data.force / 10;
+        }
+        usleep(100000); 
+    }
+    printf("Zero point: %ld\n", zero_point);
+
+    printf("Place weigth on the sensor... you have 5 seconds from when you see 
this message\n");
+    usleep(5000000);
+    printf("Starting gain calibration\n");
+
+    long weight_point = 0;
+    for (int i = 0; i < 10; i++) {
+        err = get_data(imu_meta, imu, &data);
+        if (err < 0) {
+            printf("Error reading data\n");
+        } else {
+            weight_point += data.force / 10;
+        }
+        usleep(100000);
+    }
+    printf("Weight value: %ld\n", weight_point);
+    float known_weight_val = 15000; // 1.5kg
+
+    while (true) {
+        err = get_data(imu_meta, imu, &data);
+        if (err < 0) {
+            printf("Error reading data\n");
+        } else {
+            printf("Force: %.3f\n", known_weight_val * (data.force - 
zero_point) / (weight_point - zero_point));
+        }
+        usleep(50000);
+    }
+
+    orb_unsubscribe(imu);
+    return EXIT_SUCCESS;
+  }  
+
+
+
+``SNIOC_GET_CALIBVALUE:``
+----------------------------
+
+This commands gets the gain calibration value when set by the 
``SNIOC_CALIBRATE`` command. 
+
+.. code-block:: c
+
+    uint32_t cal_value;
+    orb_ioctl(sensor, SNIOC_GET_CALIBVALUE, (unsigned long)&cal_value);
+
+
+``SNIOC_SET_CALIBVALUE:``
+---------------------------
+
+This commands sets the gain calibration value, useful when you calibrated the 
sensor with the gain calibration mode once and want to reuse it later on.
+
+.. code-block:: c
+
+    uint32_t cal_value;
+    orb_ioctl(sensor, SNIOC_SET_CALIBVALUE, cal_value);
diff --git a/Documentation/components/drivers/special/sensors/sensors_uorb.rst 
b/Documentation/components/drivers/special/sensors/sensors_uorb.rst
index b51a9cc3b9..d327d1f199 100644
--- a/Documentation/components/drivers/special/sensors/sensors_uorb.rst
+++ b/Documentation/components/drivers/special/sensors/sensors_uorb.rst
@@ -489,6 +489,7 @@ Implemented Drivers
 - ltr308
 - mpu9250
 - ms56xx
+- :doc:`nau7802`
 - :doc:`sht4x`
 - :doc:`lsm6dso32`
 - wtgahrs2
diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig
index 6d6675f3d9..b293887a5d 100644
--- a/drivers/sensors/Kconfig
+++ b/drivers/sensors/Kconfig
@@ -194,6 +194,13 @@ config SENSORS_BH1750FVI
        ---help---
                Enable driver support for the Rohm BH1750FVI light sensor.
 
+config SENSORS_NAU7802
+       bool "Adafruit NAU7802 ADC sensor support"
+       default n
+       select I2C 
+       ---help---
+               Enable driver support for the Adafruit NAU7802 sensor.
+
 config BH1750FVI_I2C_FREQUENCY
        int "BH1750FVI I2C frequency"
        default 400000
diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs
index 7a43756f66..a501071171 100644
--- a/drivers/sensors/Make.defs
+++ b/drivers/sensors/Make.defs
@@ -34,6 +34,11 @@ ifeq ($(CONFIG_SENSORS_RPMSG),y)
   CSRCS += sensor_rpmsg.c
 endif
 
+
+ifeq ($(CONFIG_SENSORS_NAU7802),y)
+  CSRCS += nau7802.c
+endif
+
 ifeq ($(CONFIG_SENSORS_GNSS),y)
   CSRCS += gnss_uorb.c
 endif
diff --git a/drivers/sensors/nau7802.c b/drivers/sensors/nau7802.c
new file mode 100644
index 0000000000..ea176a075c
--- /dev/null
+++ b/drivers/sensors/nau7802.c
@@ -0,0 +1,937 @@
+/****************************************************************************
+ * drivers/sensors/nau7802.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/nuttx.h>
+
+#include <debug.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <nuttx/clock.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/kthread.h>
+#include <nuttx/mutex.h>
+#include <nuttx/random.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/sensors/nau7802.h>
+#include <nuttx/sensors/sensor.h>
+#include <nuttx/signal.h>
+#include <nuttx/wqueue.h>
+
+#ifndef CONFIG_SENSORS_NAU7802_I2C_FREQUENCY
+#define CONFIG_SENSORS_NAU7802_I2C_FREQUENCY 100000
+#endif
+
+#ifndef CONFIG_SENSORS_NAU7802_THREAD_STACKSIZE
+#define CONFIG_SENSORS_NAU7802_THREAD_STACKSIZE 10000
+#endif
+
+/* Registers */
+
+#define REG_PU_CTRL 0x00 // power up control
+#define REG_CTRL_1 0x01  // control/config reg 1
+#define REG_CTRL_2 0x02  // control/config reg 2
+
+#define REG_GCAL1_B3 0x6 // gain calibration registers
+#define REG_GCAL1_B2 0x7
+#define REG_GCAL1_B1 0x8
+#define REG_GCAL1_B0 0x9
+
+#define REG_ADCO_B2 0x12 // data bit 23 to 16
+#define REG_ADCO_B1 0x13 // data bit 15 to 8
+#define REG_ADCO_B0 0x14 // data bit 7 to 0
+#define REG_ADC 0x15     // ADC / chopper control
+#define REG_PGA 0x1B     // PGA control
+#define REG_POWER 0x1C   // power control
+
+/* Bits for the PU_CTRL register */
+
+#define BIT_RR 0x0    // register reset
+#define BIT_PUD 0x1   // power up digital
+#define BIT_PUA 0x2   // power up analog
+#define BIT_PUR 0x3   // power up ready
+#define BIT_CS 0x4    // cycle start
+#define BIT_CR 0x5    // cycle ready
+#define BIT_AVVDS 0x7 // AVDDS source select
+
+/* Bits for the CTRL_2 register */
+
+#define CAL_START 0x2
+#define CAL_ERR 0x3
+
+/* ODR to Interval */
+
+static const uint32_t ODR_TO_INTERVAL[] =
+{
+    [NAU7802_ODR_10HZ] = 100000,
+    [NAU7802_ODR_20HZ] = 50000,
+    [NAU7802_ODR_40HZ] = 25000,
+    [NAU7802_ODR_80HZ] = 12500,
+    [NAU7802_ODR_320HZ] = 3125
+  };
+
+typedef struct
+{
+  struct sensor_lowerhalf_s lower;
+  FAR struct i2c_master_s *i2c;
+  uint8_t addr;
+  sem_t run;
+  mutex_t devlock;
+  bool enabled;
+  nau7802_odr_e odr;
+} nau7802_dev_s;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nau7802_read_reg
+ *
+ * Description:
+ *   Read `nbytes` from the register at `addr` into `buf`.
+ ****************************************************************************/
+
+static int nau7802_read_reg(FAR nau7802_dev_s *dev, uint8_t addr, void *buf,
+                            uint8_t nbytes)
+{
+  struct i2c_msg_s readcmd[2] = {
+      {
+          .frequency = CONFIG_SENSORS_NAU7802_I2C_FREQUENCY,
+          .addr = dev->addr,
+          .flags = I2C_M_NOSTOP,
+          .buffer = &addr,
+          .length = sizeof(addr),
+      },
+      {
+          .frequency = CONFIG_SENSORS_NAU7802_I2C_FREQUENCY,
+          .addr = dev->addr,
+          .flags = I2C_M_READ,
+          .buffer = buf,
+          .length = nbytes,
+      },
+  };
+
+  return I2C_TRANSFER(dev->i2c, readcmd, 2);
+}
+
+/****************************************************************************
+ * Name: nau7802_write_reg
+ *
+ * Description:
+ *   Write `nbytes` from `buf` to the registers starting at `addr`.
+ ****************************************************************************/
+
+static int nau7802_write_reg(FAR nau7802_dev_s *dev, uint8_t addr, void *buf,
+                             uint8_t nbytes)
+{
+  struct i2c_msg_s writecmd[2] = {
+      {
+          .frequency = CONFIG_SENSORS_NAU7802_I2C_FREQUENCY,
+          .addr = dev->addr,
+          .flags = I2C_M_NOSTOP,
+          .buffer = &addr,
+          .length = sizeof(addr),
+      },
+      {
+          .frequency = CONFIG_SENSORS_NAU7802_I2C_FREQUENCY,
+          .addr = dev->addr,
+          .flags = I2C_M_NOSTART,
+          .buffer = buf,
+          .length = nbytes,
+      },
+  };
+
+  return I2C_TRANSFER(dev->i2c, writecmd, 2);
+}
+
+/****************************************************************************
+ * Name: nau7802_set_bits
+ * Description:
+ *   Helper function to set bits in a register.
+ * Arguments:
+ *   addr - The address of the register to modify
+ *   n_bits - The number of bits to set
+ *   n_bit_shifts - The number of bits to shift
+ *   value - The value to set
+ ****************************************************************************/
+
+static int nau7802_set_bits(FAR nau7802_dev_s *dev, uint8_t addr,
+                            uint8_t n_bits, uint8_t n_bit_shifts,
+                            uint8_t value)
+{
+  int err = 0;
+  uint8_t reg_val;
+
+  err = nau7802_read_reg(dev, addr, &reg_val, sizeof(reg_val));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  uint8_t mask = ((1 << n_bits) - 1);
+
+  reg_val &= ~(mask << n_bit_shifts);
+
+  reg_val |= ((value & mask) << n_bit_shifts);
+
+  return nau7802_write_reg(dev, addr, &reg_val, sizeof(reg_val));
+}
+
+/****************************************************************************
+ * Name: nau7802_read_bit
+ ****************************************************************************/
+
+static int nau7802_read_bit(FAR nau7802_dev_s *dev, uint8_t addr,
+                                          uint8_t bit, bool *val)
+{
+  int err = 0;
+  uint8_t reg_val;
+
+  err = nau7802_read_reg(dev, addr, &reg_val, sizeof(reg_val));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  *val = (reg_val >> bit) & 1;
+  return err;
+}
+
+/****************************************************************************
+ * Name: nau7802_reset
+ ****************************************************************************/
+
+static int nau7802_reset(FAR nau7802_dev_s *dev)
+{
+  int err = 0;
+  err = nau7802_set_bits(dev, REG_PU_CTRL, 1, BIT_RR, 1);
+  if (err < 0)
+    {
+      return err;
+    }
+
+  err = nau7802_set_bits(dev, REG_PU_CTRL, 1, BIT_RR, 0);
+  if (err < 0)
+    {
+      return err;
+    }
+
+  err = nau7802_set_bits(dev, REG_PU_CTRL, 1, BIT_PUD, 1);
+  if (err < 0)
+    {
+      return err;
+    }
+
+  /* waiting 200 micoroseconds for the power up */
+
+  usleep(200);
+
+  uint8_t reg_val;
+  err = nau7802_read_reg(dev, REG_PU_CTRL, &reg_val, sizeof(reg_val));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  /* check if power up is successful */
+
+  if (((reg_val >> BIT_PUR) & 1) == 1)
+    {
+      return 0;
+    }
+  else
+    {
+      snerr("Power up failed\n");
+      return -1;
+    }
+}
+
+/****************************************************************************
+ * Name: nau7802_enable
+ * Description:
+ *  Enable or disable the NAU7802.
+ ****************************************************************************/
+
+static int nau7802_enable(FAR nau7802_dev_s *dev, bool enable)
+{
+  int err = 0;
+  if (!enable)
+    {
+      err = nau7802_set_bits(dev, REG_PU_CTRL, 1, BIT_PUA, 0);
+      if (err < 0)
+        {
+          return err;
+        }
+
+      err = nau7802_set_bits(dev, REG_PU_CTRL, 1, BIT_PUD, 0);
+      if (err < 0)
+        {
+          return err;
+        }
+
+      return err;
+    }
+
+  err = nau7802_set_bits(dev, REG_PU_CTRL, 1, BIT_PUD, 1);
+  if (err < 0)
+    {
+      return err;
+    }
+
+  err = nau7802_set_bits(dev, REG_PU_CTRL, 1, BIT_PUA, 1);
+  if (err < 0)
+    {
+      return err;
+    }
+
+  usleep(600000);
+
+  err = nau7802_set_bits(dev, REG_PU_CTRL, 1, BIT_CS, 1);
+  if (err < 0)
+    {
+      return err;
+    }
+
+  bool reg_val;
+  err = nau7802_read_bit(dev, REG_PU_CTRL, BIT_PUR, &reg_val);
+  if (err < 0 || !reg_val)
+    {
+      return err;
+    }
+
+  return err;
+}
+
+/****************************************************************************
+ * Name: nau7802_data_available
+ * Description:
+ *  Check if data is available over I2C.
+ ****************************************************************************/
+
+static int nau7802_data_available(FAR nau7802_dev_s *dev, bool *val)
+{
+  return nau7802_read_bit(dev, REG_PU_CTRL, BIT_CR, val);
+}
+
+/****************************************************************************
+ * Name: nau7802_read_data
+ *
+ * Description:
+ *  Read the ADC data from the NAU7802 into the sensor_force structure.
+ ****************************************************************************/
+
+static int nau7802_read_data(FAR nau7802_dev_s *dev,
+                             FAR struct sensor_force *data)
+{
+  uint8_t msb;
+  uint8_t mid;
+  uint8_t lsb;
+  int32_t value;
+  int err = 0;
+
+  err = nau7802_read_reg(dev, REG_ADCO_B2, &msb, sizeof(msb));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  nau7802_read_reg(dev, REG_ADCO_B1, &mid, sizeof(mid));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  nau7802_read_reg(dev, REG_ADCO_B0, &lsb, sizeof(lsb));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  /* Combine into 24-bit value and sign extend to 32-bit */
+
+  value = (int32_t)((uint32_t)msb << 16 | (uint32_t)mid << 8 | lsb);
+
+  /* Sign extend if negative (MSB is set) */
+
+  if (value & 0x800000)
+    {
+      value |= 0xff000000;
+    }
+
+  data->timestamp = sensor_get_timestamp();
+  data->event = 0;
+  data->force = value;
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nau7802_set_ldo
+ * Description:
+ *  Set the LDO voltage.
+ ****************************************************************************/
+
+static int nau7802_set_ldo(FAR nau7802_dev_s *dev, nau7802_ldo_e voltage)
+{
+  if (voltage == NAU7802_LDO_V_EXTERNAL)
+    {
+      return nau7802_set_bits(dev, REG_PU_CTRL, 1, BIT_AVVDS, 0);
+    }
+
+  int err = 0;
+  err = nau7802_set_bits(dev, REG_PU_CTRL, 1, BIT_AVVDS, 1);
+  if (err < 0)
+    {
+      return err;
+    }
+
+  return nau7802_set_bits(dev, REG_CTRL_1, 3, 3, voltage);
+}
+
+/****************************************************************************
+ * Name: nau7802_set_gain
+ ****************************************************************************/
+
+static int nau7802_set_gain(FAR nau7802_dev_s *dev, nau7802_gain_e gain)
+{
+  return nau7802_set_bits(dev, REG_CTRL_1, 3, 0, gain);
+}
+
+/****************************************************************************
+ * Name: nau7802_set_interval
+ ****************************************************************************/
+
+static int nau7802_set_interval(FAR nau7802_dev_s *dev,
+                                      nau7802_odr_e rate)
+{
+  dev->odr = rate;
+  return nau7802_set_bits(dev, REG_CTRL_2, 3, 4, rate);
+}
+
+/****************************************************************************
+ * Name: nau7802_push_data
+ *
+ * Description:
+ *    Reads some data with exclusive device access and pushed it to the UORB
+ *    upper half.
+ ****************************************************************************/
+
+static int nau7802_push_data(FAR nau7802_dev_s *dev)
+{
+  int err = 0;
+  struct sensor_force data;
+
+  err = nxmutex_lock(&dev->devlock);
+  if (err < 0)
+    {
+      return err;
+    }
+
+  if (!dev->enabled)
+    {
+      err = -EAGAIN;
+      goto unlock_ret;
+    }
+
+  bool data_ready;
+  err = nau7802_data_available(dev, &data_ready);
+  if (err < 0 || !data_ready)
+    {
+      goto unlock_ret;
+    }
+
+  err = nau7802_read_data(dev, &data);
+  if (err < 0)
+    {
+      goto unlock_ret;
+    }
+
+  dev->lower.push_event(dev->lower.priv, &data, sizeof(data));
+
+unlock_ret:
+  nxmutex_unlock(&dev->devlock);
+
+  return err;
+}
+
+/****************************************************************************
+ * Name: nau7802_get_calibvalue
+ * Description:
+ *  Get the gain calibration value.
+ ****************************************************************************/
+
+static int nau7802_get_calibvalue(FAR nau7802_dev_s *dev, unsigned long arg)
+{
+  uint32_t *calibvalue = (uint32_t *)arg;
+  uint8_t reg_b3;
+  uint8_t reg_b2;
+  uint8_t reg_b1;
+  uint8_t reg_b0;
+
+  int err = 0;
+
+  err = nau7802_read_reg(dev, REG_GCAL1_B3, &reg_b3, sizeof(reg_b3));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  err = nau7802_read_reg(dev, REG_GCAL1_B2, &reg_b2, sizeof(reg_b2));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  err = nau7802_read_reg(dev, REG_GCAL1_B1, &reg_b1, sizeof(reg_b1));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  err = nau7802_read_reg(dev, REG_GCAL1_B0, &reg_b0, sizeof(reg_b0));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  *calibvalue = (reg_b3 << 24) | (reg_b2 << 16) | (reg_b1 << 8) | reg_b0;
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nau7802_set_calibvalue
+ * Description:
+ *  Set the gain calibration value.
+ ****************************************************************************/
+
+static int nau7802_set_calibvalue(FAR struct sensor_lowerhalf_s *lower,
+                                  FAR struct file *filep, unsigned long arg)
+{
+  FAR nau7802_dev_s *dev = container_of(lower, FAR nau7802_dev_s, lower);
+  uint32_t calibvalue = (uint32_t)arg;
+  uint8_t reg_b3 = (calibvalue >> 24) & 0xff;
+  uint8_t reg_b2 = (calibvalue >> 16) & 0xff;
+  uint8_t reg_b1 = (calibvalue >> 8) & 0xff;
+  uint8_t reg_b0 = calibvalue & 0xff;
+
+  int err = 0;
+  err = nau7802_write_reg(dev, REG_GCAL1_B3, &reg_b3, sizeof(reg_b3));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  err = nau7802_write_reg(dev, REG_GCAL1_B2, &reg_b2, sizeof(reg_b2));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  err = nau7802_write_reg(dev, REG_GCAL1_B1, &reg_b1, sizeof(reg_b1));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  err = nau7802_write_reg(dev, REG_GCAL1_B0, &reg_b0, sizeof(reg_b0));
+  if (err < 0)
+    {
+      return err;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nau7802_calibrate
+ * Description:
+ *   Perform either an INTERNAL, OFFSET or GAIN calibration.
+ *   The gain calibration value is saved and can be retrieved via the
+ *   SNIOC_GET_GAIN_CALIBVALUE command
+ ****************************************************************************/
+
+static int nau7802_calibrate(FAR struct sensor_lowerhalf_s *lower,
+                             FAR struct file *filep, unsigned long int arg)
+{
+  FAR nau7802_dev_s *dev = container_of(lower, FAR nau7802_dev_s, lower);
+  nau7802_calibmode_e mode = (nau7802_calibmode_e)arg;
+  int err = 0;
+
+  /* Choosing calibration mode */
+
+  err = nau7802_set_bits(dev, REG_CTRL_2, 2, 0, mode);
+  if (err < 0)
+    {
+      return err;
+    }
+
+  /* Start calibration */
+
+  err = nau7802_set_bits(dev, REG_CTRL_2, 1, CAL_START, 1);
+  if (err < 0)
+    {
+      return err;
+    }
+
+  /* Wait for calibration to complete */
+
+  bool reg_val;
+  do
+    {
+      err = nau7802_read_bit(dev, REG_CTRL_2, CAL_START, &reg_val);
+      if (err < 0)
+        {
+          return err;
+        }
+
+      usleep(10000);
+    }
+  while (reg_val);
+
+  /* Check calibration error bit */
+
+  err = nau7802_read_bit(dev, REG_CTRL_2, CAL_ERR, &reg_val);
+  if (err < 0)
+    {
+      return err;
+    }
+
+  if (reg_val)
+    {
+      snerr("Calibration failed\n");
+      return -1;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nau7802_control
+ ****************************************************************************/
+
+static int nau7802_control(FAR struct sensor_lowerhalf_s *lower,
+                           FAR struct file *filep, int cmd,
+                           unsigned long arg)
+{
+  FAR nau7802_dev_s *dev = container_of(lower, FAR nau7802_dev_s, lower);
+  int err = 0;
+
+  err = nxmutex_lock(&dev->devlock);
+  if (err)
+    {
+      return err;
+    }
+
+  switch (cmd)
+    {
+    case SNIOC_RESET:
+      err = nau7802_reset(dev);
+      break;
+
+    case SNIOC_SET_GAIN:
+      err = nau7802_set_gain(dev, arg);
+      break;
+
+    case SNIOC_SET_LDO:
+      err = nau7802_set_ldo(dev, arg);
+      break;
+
+    case SNIOC_GET_CALIBVALUE:
+      err = nau7802_get_calibvalue(dev, arg);
+      break;
+
+    default:
+      err = -EINVAL;
+      snerr("Unknown command for NAU7802: lu\n", cmd);
+      break;
+    }
+
+  nxmutex_unlock(&dev->devlock);
+  return err;
+}
+
+/****************************************************************************
+ * Name: nau7802_activate
+ ****************************************************************************/
+
+static int nau7802_activate(FAR struct sensor_lowerhalf_s *lower,
+                            FAR struct file *filep, bool enable)
+{
+  FAR nau7802_dev_s *dev = container_of(lower, FAR nau7802_dev_s, lower);
+  bool start_thread = false;
+  int err = 0;
+
+  /* Start the collection thread if not already enabled */
+
+  if (enable && !dev->enabled)
+    {
+      start_thread = true;
+
+      err = nau7802_reset(dev);
+      if (err < 0)
+        {
+          return err;
+        }
+
+      err = nau7802_enable(dev, true);
+      if (err < 0)
+        {
+          return err;
+        }
+
+      uint8_t reg_val;
+      err = nau7802_read_reg(dev, 0x1f, &reg_val, sizeof(reg_val));
+      if (err < 0 || (reg_val & 0xf) != 0xf)
+        {
+          snerr("Could not read the revision register\n");
+          return err;
+        }
+
+      err = nau7802_set_ldo(dev, NAU7802_LDO_V_3V0);
+      if (err < 0)
+        {
+          return err;
+        }
+
+      err = nau7802_set_gain(dev, NAU7802_GAIN_128);
+      if (err < 0)
+        {
+          return err;
+        }
+
+      err = nau7802_set_interval(dev, NAU7802_ODR_10HZ);
+      if (err < 0)
+        {
+          return err;
+        }
+
+      /* Disable ADC chopper */
+
+      err = nau7802_set_bits(dev, REG_ADC, 2, 4, 0x3);
+      if (err < 0)
+        {
+          return err;
+        }
+
+      /* Use low ESR caps */
+
+      err = nau7802_set_bits(dev, REG_PGA, 1, 6, 0);
+      if (err < 0)
+        {
+          return err;
+        }
+
+      /* PGA stabilizer cap on output */
+
+      err = nau7802_set_bits(dev, REG_POWER, 1, 7, 1);
+      if (err < 0)
+        {
+          return err;
+        }
+    }
+
+  dev->enabled = enable;
+
+  if (start_thread)
+    {
+      return nxsem_post(&dev->run);
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nau7802_get_info
+ ****************************************************************************/
+
+static int nau7802_get_info(FAR struct sensor_lowerhalf_s *lower,
+                            FAR struct file *filep,
+                            FAR struct sensor_device_info_s *info)
+{
+  info->version = 0;
+  info->power = 2.1f;
+  memcpy(info->name, "NAU7802", sizeof("NAU7802"));
+  memcpy(info->vendor, "Nuvoton", sizeof("Nuvoton"));
+
+  info->min_delay = 3125;   /* For 320 ODR (fastest rate) */
+  info->max_delay = 100000; /* For 10 ODR (slowest rate) */
+  info->fifo_reserved_event_count = 0;
+  info->fifo_max_event_count = 0;
+  info->max_range = 1.0f;
+  info->resolution = 0;
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: nau7802_thread
+ *
+ * Description:
+ *   Kernel thread to poll the NAU7802
+ ****************************************************************************/
+
+static int nau7802_thread(int argc, FAR char *argv[])
+{
+  FAR nau7802_dev_s *dev =
+      (FAR nau7802_dev_s *)((uintptr_t)strtoul(argv[1], NULL, 16));
+  int err = 0;
+
+  while (true)
+    {
+      /* If the sensor is disabled we wait indefinitely */
+
+      if (!dev->enabled)
+        {
+          err = nxsem_wait(&dev->run);
+          if (err < 0)
+            {
+              continue;
+            }
+        }
+
+      /* If the sensor is enabled, grab some data */
+
+      err = nau7802_push_data(dev);
+      if (err < 0)
+        {
+          return err;
+        }
+
+      /* Wait for next measurement cycle */
+
+      nxsig_usleep(ODR_TO_INTERVAL[dev->odr]);
+    }
+
+  return err;
+}
+
+static const struct sensor_ops_s g_sensor_ops =
+{
+  .activate = nau7802_activate,
+  .get_info = nau7802_get_info,
+  .control = nau7802_control,
+  .calibrate = nau7802_calibrate,
+  .set_calibvalue = nau7802_set_calibvalue
+};
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nau7802_register
+ *
+ * Description:
+ *   Register the NAU7802 device as a UORB sensor.
+ *
+ * Input Parameters:
+ *   i2c     - An instance of the I2C interface to use to communicate with
+ *             the NAU7802.
+ *   addr    - The I2C address of the NAU7802. Should always be 0x2a.
+ *   devno   - The device number to use for the topic (i.e. /dev/mag0)
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ ****************************************************************************/
+
+int nau7802_register(FAR struct i2c_master_s *i2c, int devno, uint8_t addr)
+{
+  int err;
+  FAR nau7802_dev_s *priv = kmm_zalloc(sizeof(nau7802_dev_s));
+  DEBUGASSERT(i2c != NULL);
+  DEBUGASSERT(addr == 0x2a);
+
+  if (priv == NULL)
+    {
+      snerr("Failed to allocate nau7802 instance\n");
+      return -ENOMEM;
+    }
+
+  err = nxmutex_init(&priv->devlock);
+  if (err < 0)
+    {
+      snerr("Failed to register nau7802 driver: %d\n", err);
+      goto del_mem;
+    }
+
+  err = nxsem_init(&priv->run, 0, 0);
+  if (err < 0)
+    {
+      snerr("Failed to register nau7802 driver: %d\n", err);
+      goto del_mutex;
+    }
+
+  priv->i2c = i2c;
+  priv->addr = addr;
+  priv->lower.ops = &g_sensor_ops;
+  priv->lower.type = SENSOR_TYPE_FORCE;
+  priv->enabled = false;
+  priv->odr = NAU7802_ODR_10HZ; /* 10Hz (0.1s) default ODR */
+
+  err = sensor_register(&priv->lower, devno);
+  if (err < 0)
+    {
+      snerr("Failed to register nau7802 driver: %d\n", err);
+      goto del_sem;
+    }
+
+  FAR char *argv[2];
+  char arg1[32];
+  snprintf(arg1, 16, "%p", priv);
+  argv[0] = arg1;
+  argv[1] = NULL;
+
+  err = kthread_create("nau7802_thread", SCHED_PRIORITY_DEFAULT,
+                       CONFIG_SENSORS_NAU7802_THREAD_STACKSIZE,
+                       nau7802_thread, argv);
+  if (err < 0)
+    {
+      snerr("Failed to create the nau7802 notification kthread\n");
+      goto sensor_unreg;
+    }
+
+  sninfo("Registered nau7802 driver with kernel polling thread\n");
+
+  if (err < 0)
+    {
+    sensor_unreg:
+      sensor_unregister(&priv->lower, devno);
+    del_sem:
+      nxsem_destroy(&priv->run);
+    del_mutex:
+      nxmutex_destroy(&priv->devlock);
+    del_mem:
+      kmm_free(priv);
+      return err;
+    }
+
+  sninfo("Registered NAU7802 driver\n");
+  return err;
+}
diff --git a/include/nuttx/sensors/ioctl.h b/include/nuttx/sensors/ioctl.h
index 7fdda71c08..6b10c41d4f 100644
--- a/include/nuttx/sensors/ioctl.h
+++ b/include/nuttx/sensors/ioctl.h
@@ -480,6 +480,24 @@
 
 #define SNIOC_LPF                     _SNIOC(0x00A0)
 
+/* Command:      SNIOC_SET_GAIN
+ * Description:  Sets the gain of the sensor.
+ */
+
+#define SNIOC_SET_GAIN                _SNIOC(0x00A1)
+
+/* Command:      SNIOC_SET_LDO
+ * Description:  Sets the LDO voltage of the sensor.
+ */
+
+#define SNIOC_SET_LDO                 _SNIOC(0x00A2)
+
+/* Command:      SNIOC_GET_CALIBVALUE
+ * Description:  Get the gain calibration value of the sensor.
+ */
+
+#define SNIOC_GET_CALIBVALUE          _SNIOC(0x00A3)
+
 /****************************************************************************
  * Public types
  ****************************************************************************/
diff --git a/include/nuttx/sensors/nau7802.h b/include/nuttx/sensors/nau7802.h
new file mode 100644
index 0000000000..23150aaacf
--- /dev/null
+++ b/include/nuttx/sensors/nau7802.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+ * include/nuttx/sensors/nau7802.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/irq.h>
+#include <nuttx/sensors/ioctl.h>
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct i2c_master_s; /* Forward reference */
+
+typedef int (*nau7802_attach)(xcpt_t, FAR void *arg);
+
+/* Valid calibration modes */
+
+typedef enum
+{
+  NAU7802_CALMOD_INTERNAL = 0,
+  NAU7802_CALMOD_OFFSET = 2,
+  NAU7802_CALMOD_GAIN = 3
+} nau7802_calibmode_e;
+
+/* Valid LDO voltage settings */
+
+typedef enum
+{
+  NAU7802_LDO_V_4V5,
+  NAU7802_LDO_V_4V2,
+  NAU7802_LDO_V_3V9,
+  NAU7802_LDO_V_3V6,
+  NAU7802_LDO_V_3V3,
+  NAU7802_LDO_V_3V0,
+  NAU7802_LDO_V_2V7,
+  NAU7802_LDO_V_2V4,
+  NAU7802_LDO_V_EXTERNAL
+} nau7802_ldo_e;
+
+/* Valid gain settings */
+
+typedef enum
+{
+  NAU7802_GAIN_1,
+  NAU7802_GAIN_2,
+  NAU7802_GAIN_4,
+  NAU7802_GAIN_8,
+  NAU7802_GAIN_16,
+  NAU7802_GAIN_32,
+  NAU7802_GAIN_64,
+  NAU7802_GAIN_128
+} nau7802_gain_e;
+
+/* Valid odr values */
+
+typedef enum
+{
+  NAU7802_ODR_10HZ = 0,
+  NAU7802_ODR_20HZ = 1,
+  NAU7802_ODR_40HZ = 2,
+  NAU7802_ODR_80HZ = 3,
+  NAU7802_ODR_320HZ = 7
+} nau7802_odr_e;
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nau7802_register
+ *
+ * Description:
+ *   Register the nau7802 device as a UORB sensor.
+ *
+ * Input Parameters:
+ *   i2c     - An instance of the I2C interface to use to communicate with
+ *             the nau7802.
+ *   addr    - The I2C address of the nau7802. Should always be 0x2a.
+ *   devno   - The device number to use for the topic (i.e. /dev/mag0)
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int nau7802_register(FAR struct i2c_master_s *i2c, int devno, uint8_t addr);


Reply via email to