Re: [PATCH v5 4/5] iio:temperature:mlx90632: Adding extended calibration option

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Aug 13, 2020 at 10:53 AM Crt Mori <cmo@xxxxxxxxxxx> wrote:
>
> For some time the market wants medical grade accuracy in medical range,
> while still retaining the declared accuracy outside of the medical range
> within the same sensor. That is why we created extended calibration
> which is automatically switched to when object temperature is too high.
>
> This patch also introduces the object_ambient_temperature variable which
> is needed for more accurate calculation of the object infra-red
> footprint as sensor's ambient temperature might be totally different
> than what the ambient temperature is at object and that is why we can
> have some more errors which can be eliminated. Currently this temperature
> is fixed at 25, but the interface to adjust it by user (with external
> sensor or just IR measurement of the other object which acts as ambient),
> will be introduced in another commit.

FWIW,
Reviewed-by: Andy Shevchenko <andy.shevchenko@xxxxxxxxx>

> Signed-off-by: Crt Mori <cmo@xxxxxxxxxxx>
> ---
>  drivers/iio/temperature/mlx90632.c | 224 ++++++++++++++++++++++++++++-
>  1 file changed, 222 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iio/temperature/mlx90632.c b/drivers/iio/temperature/mlx90632.c
> index d87c792b7e72..9fa8c078c037 100644
> --- a/drivers/iio/temperature/mlx90632.c
> +++ b/drivers/iio/temperature/mlx90632.c
> @@ -11,6 +11,7 @@
>  #include <linux/gpio/consumer.h>
>  #include <linux/i2c.h>
>  #include <linux/kernel.h>
> +#include <linux/limits.h>
>  #include <linux/module.h>
>  #include <linux/math64.h>
>  #include <linux/of.h>
> @@ -58,6 +59,8 @@
>  /* Control register address - volatile */
>  #define MLX90632_REG_CONTROL   0x3001 /* Control Register address */
>  #define   MLX90632_CFG_PWR_MASK                GENMASK(2, 1) /* PowerMode Mask */
> +#define   MLX90632_CFG_MTYP_MASK               GENMASK(8, 4) /* Meas select Mask */
> +
>  /* PowerModes statuses */
>  #define MLX90632_PWR_STATUS(ctrl_val) (ctrl_val << 1)
>  #define MLX90632_PWR_STATUS_HALT MLX90632_PWR_STATUS(0) /* hold */
> @@ -65,6 +68,18 @@
>  #define MLX90632_PWR_STATUS_STEP MLX90632_PWR_STATUS(2) /* step */
>  #define MLX90632_PWR_STATUS_CONTINUOUS MLX90632_PWR_STATUS(3) /* continuous*/
>
> +/* Measurement types */
> +#define MLX90632_MTYP_MEDICAL 0
> +#define MLX90632_MTYP_EXTENDED 17
> +
> +/* Measurement type select*/
> +#define MLX90632_MTYP_STATUS(ctrl_val) (ctrl_val << 4)
> +#define MLX90632_MTYP_STATUS_MEDICAL MLX90632_MTYP_STATUS(MLX90632_MTYP_MEDICAL)
> +#define MLX90632_MTYP_STATUS_EXTENDED MLX90632_MTYP_STATUS(MLX90632_MTYP_EXTENDED)
> +
> +/* I2C command register - volatile */
> +#define MLX90632_REG_I2C_CMD    0x3005 /* I2C command Register address */
> +
>  /* Device status register - volatile */
>  #define MLX90632_REG_STATUS    0x3fff /* Device status register */
>  #define   MLX90632_STAT_BUSY           BIT(10) /* Device busy indicator */
> @@ -78,9 +93,21 @@
>  #define MLX90632_RAM_2(meas_num)       (MLX90632_ADDR_RAM + 3 * meas_num + 1)
>  #define MLX90632_RAM_3(meas_num)       (MLX90632_ADDR_RAM + 3 * meas_num + 2)
>
> +/* Name important RAM_MEAS channels */
> +#define MLX90632_RAM_DSP5_EXTENDED_AMBIENT_1 MLX90632_RAM_3(17)
> +#define MLX90632_RAM_DSP5_EXTENDED_AMBIENT_2 MLX90632_RAM_3(18)
> +#define MLX90632_RAM_DSP5_EXTENDED_OBJECT_1 MLX90632_RAM_1(17)
> +#define MLX90632_RAM_DSP5_EXTENDED_OBJECT_2 MLX90632_RAM_2(17)
> +#define MLX90632_RAM_DSP5_EXTENDED_OBJECT_3 MLX90632_RAM_1(18)
> +#define MLX90632_RAM_DSP5_EXTENDED_OBJECT_4 MLX90632_RAM_2(18)
> +#define MLX90632_RAM_DSP5_EXTENDED_OBJECT_5 MLX90632_RAM_1(19)
> +#define MLX90632_RAM_DSP5_EXTENDED_OBJECT_6 MLX90632_RAM_2(19)
> +
>  /* Magic constants */
>  #define MLX90632_ID_MEDICAL    0x0105 /* EEPROM DSPv5 Medical device id */
>  #define MLX90632_ID_CONSUMER   0x0205 /* EEPROM DSPv5 Consumer device id */
> +#define MLX90632_ID_EXTENDED   0x0505 /* EEPROM DSPv5 Extended range device id */
> +#define MLX90632_ID_MASK       GENMASK(14, 0) /* DSP version and device ID in EE_VERSION */
>  #define MLX90632_DSP_VERSION   5 /* DSP version */
>  #define MLX90632_DSP_MASK      GENMASK(7, 0) /* DSP version in EE_VERSION */
>  #define MLX90632_RESET_CMD     0x0006 /* Reset sensor (address or global) */
> @@ -88,6 +115,7 @@
>  #define MLX90632_REF_3         12LL /**< ResCtrlRef value of Channel 3 */
>  #define MLX90632_MAX_MEAS_NUM  31 /**< Maximum measurements in list */
>  #define MLX90632_SLEEP_DELAY_MS 3000 /**< Autosleep delay */
> +#define MLX90632_EXTENDED_LIMIT 27000 /* Extended mode raw value limit */
>
>  /**
>   * struct mlx90632_data - private data for the MLX90632 device
> @@ -95,16 +123,23 @@
>   * @lock: Internal mutex for multiple reads for single measurement
>   * @regmap: Regmap of the device
>   * @emissivity: Object emissivity from 0 to 1000 where 1000 = 1.
> + * @mtyp: Measurement type physical sensor configuration for extended range
> + *        calculations
> + * @object_ambient_temperature: Ambient temperature at object (might differ of
> + *                              the ambient temperature of sensor.
>   */
>  struct mlx90632_data {
>         struct i2c_client *client;
>         struct mutex lock;
>         struct regmap *regmap;
>         u16 emissivity;
> +       u8 mtyp;
> +       u32 object_ambient_temperature;
>  };
>
>  static const struct regmap_range mlx90632_volatile_reg_range[] = {
>         regmap_reg_range(MLX90632_REG_I2C_ADDR, MLX90632_REG_CONTROL),
> +       regmap_reg_range(MLX90632_REG_I2C_CMD, MLX90632_REG_I2C_CMD),
>         regmap_reg_range(MLX90632_REG_STATUS, MLX90632_REG_STATUS),
>         regmap_reg_range(MLX90632_RAM_1(0),
>                          MLX90632_RAM_3(MLX90632_MAX_MEAS_NUM)),
> @@ -120,6 +155,7 @@ static const struct regmap_range mlx90632_read_reg_range[] = {
>         regmap_reg_range(MLX90632_EE_CTRL, MLX90632_EE_I2C_ADDR),
>         regmap_reg_range(MLX90632_EE_Ha, MLX90632_EE_Hb),
>         regmap_reg_range(MLX90632_REG_I2C_ADDR, MLX90632_REG_CONTROL),
> +       regmap_reg_range(MLX90632_REG_I2C_CMD, MLX90632_REG_I2C_CMD),
>         regmap_reg_range(MLX90632_REG_STATUS, MLX90632_REG_STATUS),
>         regmap_reg_range(MLX90632_RAM_1(0),
>                          MLX90632_RAM_3(MLX90632_MAX_MEAS_NUM)),
> @@ -204,6 +240,26 @@ static int mlx90632_perform_measurement(struct mlx90632_data *data)
>         return (reg_status & MLX90632_STAT_CYCLE_POS) >> 2;
>  }
>
> +static int mlx90632_set_meas_type(struct regmap *regmap, u8 type)
> +{
> +       int ret;
> +
> +       if ((type != MLX90632_MTYP_MEDICAL) && (type != MLX90632_MTYP_EXTENDED))
> +               return -EINVAL;
> +
> +       ret = regmap_write(regmap, MLX90632_REG_I2C_CMD, MLX90632_RESET_CMD);
> +       if (ret < 0)
> +               return ret;
> +
> +       ret = regmap_write_bits(regmap, MLX90632_REG_CONTROL,
> +                                (MLX90632_CFG_MTYP_MASK | MLX90632_CFG_PWR_MASK),
> +                                (MLX90632_MTYP_STATUS(type) | MLX90632_PWR_STATUS_HALT));
> +       if (ret < 0)
> +               return ret;
> +
> +       return mlx90632_pwr_continuous(regmap);
> +}
> +
>  static int mlx90632_channel_new_select(int perform_ret, uint8_t *channel_new,
>                                        uint8_t *channel_old)
>  {
> @@ -305,6 +361,104 @@ static int mlx90632_read_all_channel(struct mlx90632_data *data,
>         return ret;
>  }
>
> +static int mlx90632_read_ambient_raw_extended(struct regmap *regmap,
> +                                             s16 *ambient_new_raw, s16 *ambient_old_raw)
> +{
> +       unsigned int read_tmp;
> +       int ret;
> +
> +       ret = regmap_read(regmap, MLX90632_RAM_DSP5_EXTENDED_AMBIENT_1, &read_tmp);
> +       if (ret < 0)
> +               return ret;
> +       *ambient_new_raw = (s16)read_tmp;
> +
> +       ret = regmap_read(regmap, MLX90632_RAM_DSP5_EXTENDED_AMBIENT_2, &read_tmp);
> +       if (ret < 0)
> +               return ret;
> +       *ambient_old_raw = (s16)read_tmp;
> +
> +       return 0;
> +}
> +
> +static int mlx90632_read_object_raw_extended(struct regmap *regmap, s16 *object_new_raw)
> +{
> +       unsigned int read_tmp;
> +       s32 read;
> +       int ret;
> +
> +       ret = regmap_read(regmap, MLX90632_RAM_DSP5_EXTENDED_OBJECT_1, &read_tmp);
> +       if (ret < 0)
> +               return ret;
> +       read = (s16)read_tmp;
> +
> +       ret = regmap_read(regmap, MLX90632_RAM_DSP5_EXTENDED_OBJECT_2, &read_tmp);
> +       if (ret < 0)
> +               return ret;
> +       read = read - (s16)read_tmp;
> +
> +       ret = regmap_read(regmap, MLX90632_RAM_DSP5_EXTENDED_OBJECT_3, &read_tmp);
> +       if (ret < 0)
> +               return ret;
> +       read = read - (s16)read_tmp;
> +
> +       ret = regmap_read(regmap, MLX90632_RAM_DSP5_EXTENDED_OBJECT_4, &read_tmp);
> +       if (ret < 0)
> +               return ret;
> +       read = (read + (s16)read_tmp) / 2;
> +
> +       ret = regmap_read(regmap, MLX90632_RAM_DSP5_EXTENDED_OBJECT_5, &read_tmp);
> +       if (ret < 0)
> +               return ret;
> +       read = read + (s16)read_tmp;
> +
> +       ret = regmap_read(regmap, MLX90632_RAM_DSP5_EXTENDED_OBJECT_6, &read_tmp);
> +       if (ret < 0)
> +               return ret;
> +       read = read + (s16)read_tmp;
> +
> +       if (read > S16_MAX || read < S16_MIN)
> +               return -ERANGE;
> +
> +       *object_new_raw = read;
> +
> +       return 0;
> +}
> +
> +static int mlx90632_read_all_channel_extended(struct mlx90632_data *data, s16 *object_new_raw,
> +                                             s16 *ambient_new_raw, s16 *ambient_old_raw)
> +{
> +       int tries = 4;
> +       int ret;
> +
> +       mutex_lock(&data->lock);
> +       ret = mlx90632_set_meas_type(data->regmap, MLX90632_MTYP_EXTENDED);
> +       if (ret < 0)
> +               goto read_unlock;
> +
> +       do {
> +               ret = mlx90632_perform_measurement(data);
> +               if (ret < 0)
> +                       goto read_unlock;
> +       } while ((ret != 19) && tries--);
> +
> +       if (tries < 0) {
> +               ret = -ETIMEDOUT;
> +               goto read_unlock;
> +       }
> +
> +       ret = mlx90632_read_object_raw_extended(data->regmap, object_new_raw);
> +       if (ret < 0)
> +               goto read_unlock;
> +
> +       ret = mlx90632_read_ambient_raw_extended(data->regmap, ambient_new_raw, ambient_old_raw);
> +
> +read_unlock:
> +       (void) mlx90632_set_meas_type(data->regmap, MLX90632_MTYP_MEDICAL);
> +
> +       mutex_unlock(&data->lock);
> +       return ret;
> +}
> +
>  static int mlx90632_read_ee_register(struct regmap *regmap, u16 reg_lsb,
>                                      s32 *reg_value)
>  {
> @@ -359,9 +513,23 @@ static s64 mlx90632_preprocess_temp_obj(s16 object_new_raw, s16 object_old_raw,
>         return div64_s64((tmp << 19ULL), 1000LL);
>  }
>
> +static s64 mlx90632_preprocess_temp_obj_extended(s16 object_new_raw, s16 ambient_new_raw,
> +                                                s16 ambient_old_raw, s16 Ka)
> +{
> +       s64 VR_IR, kKa, tmp;
> +
> +       kKa = ((s64)Ka * 1000LL) >> 10ULL;
> +       VR_IR = (s64)ambient_old_raw * 1000000LL +
> +               kKa * div64_s64((s64)ambient_new_raw * 1000LL,
> +                               MLX90632_REF_3);
> +       tmp = div64_s64(
> +                       div64_s64((s64) object_new_raw * 1000000000000LL, MLX90632_REF_12),
> +                       VR_IR);
> +       return div64_s64(tmp << 19ULL, 1000LL);
> +}
> +
>  static s32 mlx90632_calc_temp_ambient(s16 ambient_new_raw, s16 ambient_old_raw,
> -                                     s32 P_T, s32 P_R, s32 P_G, s32 P_O,
> -                                     s16 Gb)
> +                                     s32 P_T, s32 P_R, s32 P_G, s32 P_O, s16 Gb)
>  {
>         s64 Asub, Bsub, Ablock, Bblock, Cblock, AMB, sum;
>
> @@ -433,6 +601,31 @@ static s32 mlx90632_calc_temp_object(s64 object, s64 ambient, s32 Ea, s32 Eb,
>         return temp;
>  }
>
> +static s32 mlx90632_calc_temp_object_extended(s64 object, s64 ambient, s64 reflected,
> +                                             s32 Ea, s32 Eb, s32 Fa, s32 Fb, s32 Ga,
> +                                             s16 Ha, s16 Hb, u16 tmp_emi)
> +{
> +       s64 kTA, kTA0, TAdut, TAdut4, Tr4, TaTr4;
> +       s64 temp = 25000;
> +       s8 i;
> +
> +       kTA = (Ea * 1000LL) >> 16LL;
> +       kTA0 = (Eb * 1000LL) >> 8LL;
> +       TAdut = div64_s64((ambient - kTA0) * 1000000LL, kTA) + 25 * 1000000LL;
> +       Tr4 = mlx90632_calc_ta4(reflected, 10);
> +       TAdut4 = mlx90632_calc_ta4(TAdut, 10000LL);
> +       TaTr4 = Tr4 - div64_s64(Tr4 - TAdut4, tmp_emi) * 1000;
> +
> +       /* Iterations of calculation as described in datasheet */
> +       for (i = 0; i < 5; ++i) {
> +               temp = mlx90632_calc_temp_object_iteration(temp, object, TAdut, TaTr4,
> +                                                          Fa / 2, Fb, Ga, Ha, Hb,
> +                                                          tmp_emi);
> +       }
> +
> +       return temp;
> +}
> +
>  static int mlx90632_calc_object_dsp105(struct mlx90632_data *data, int *val)
>  {
>         s32 ret;
> @@ -480,6 +673,26 @@ static int mlx90632_calc_object_dsp105(struct mlx90632_data *data, int *val)
>         if (ret < 0)
>                 return ret;
>
> +       if (object_new_raw > MLX90632_EXTENDED_LIMIT &&
> +           data->mtyp == MLX90632_MTYP_EXTENDED) {
> +               ret = mlx90632_read_all_channel_extended(data, &object_new_raw,
> +                                                        &ambient_new_raw, &ambient_old_raw);
> +               if (ret < 0)
> +                       return ret;
> +
> +               /* Use extended mode calculations */
> +               ambient = mlx90632_preprocess_temp_amb(ambient_new_raw,
> +                                                      ambient_old_raw, Gb);
> +               object = mlx90632_preprocess_temp_obj_extended(object_new_raw,
> +                                                              ambient_new_raw,
> +                                                              ambient_old_raw, Ka);
> +               *val = mlx90632_calc_temp_object_extended(object, ambient,
> +                                                         data->object_ambient_temperature,
> +                                                         Ea, Eb, Fa, Fb, Ga,
> +                                                         Ha, Hb, data->emissivity);
> +               return 0;
> +       }
> +
>         ambient = mlx90632_preprocess_temp_amb(ambient_new_raw,
>                                                ambient_old_raw, Gb);
>         object = mlx90632_preprocess_temp_obj(object_new_raw,
> @@ -653,6 +866,7 @@ static int mlx90632_probe(struct i2c_client *client,
>         i2c_set_clientdata(client, indio_dev);
>         mlx90632->client = client;
>         mlx90632->regmap = regmap;
> +       mlx90632->mtyp = MLX90632_MTYP_MEDICAL;
>
>         mutex_init(&mlx90632->lock);
>         indio_dev->name = id->name;
> @@ -672,12 +886,17 @@ static int mlx90632_probe(struct i2c_client *client,
>                 dev_err(&client->dev, "read of version failed: %d\n", ret);
>                 return ret;
>         }
> +       read = read & MLX90632_ID_MASK;
>         if (read == MLX90632_ID_MEDICAL) {
>                 dev_dbg(&client->dev,
>                         "Detected Medical EEPROM calibration %x\n", read);
>         } else if (read == MLX90632_ID_CONSUMER) {
>                 dev_dbg(&client->dev,
>                         "Detected Consumer EEPROM calibration %x\n", read);
> +       } else if (read == MLX90632_ID_EXTENDED) {
> +               dev_dbg(&client->dev,
> +                       "Detected Extended range EEPROM calibration %x\n", read);
> +               mlx90632->mtyp = MLX90632_MTYP_EXTENDED;
>         } else if ((read & MLX90632_DSP_MASK) == MLX90632_DSP_VERSION) {
>                 dev_dbg(&client->dev,
>                         "Detected Unknown EEPROM calibration %x\n", read);
> @@ -689,6 +908,7 @@ static int mlx90632_probe(struct i2c_client *client,
>         }
>
>         mlx90632->emissivity = 1000;
> +       mlx90632->object_ambient_temperature = 25000; /* 25 degrees milliCelsius */
>
>         pm_runtime_disable(&client->dev);
>         ret = pm_runtime_set_active(&client->dev);
> --
> 2.25.1
>


--
With Best Regards,
Andy Shevchenko



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux