/* 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) */ #define MLX90632_REF_12 12LL /* ResCtrlRef value of Ch 1 or Ch 2 */ #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 6000 /* Autosleep delay */ #define MLX90632_EXTENDED_LIMIT 27000 /* Extended mode raw value limit */ #define MLX90632_MEAS_MAX_TIME 2000 /* Max measurement time in ms for the lowest refresh rate */
/** * struct mlx90632_data - private data for the MLX90632 device * @client: I2C client of the device * @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. * @regulator: Regulator of the device * @powerstatus: Current POWER status of the device * @interaction_ts: Timestamp of the last temperature read that is used * for power management in jiffies
*/ struct mlx90632_data { struct i2c_client *client; struct mutex lock; struct regmap *regmap;
u16 emissivity;
u8 mtyp;
u32 object_ambient_temperature; struct regulator *regulator; int powerstatus; unsignedlong interaction_ts;
};
/** * mlx90632_reset_delay() - Give the mlx90632 some time to reset properly * If this is not done, the following I2C command(s) will not be accepted.
*/ staticvoid mlx90632_reset_delay(void)
{
usleep_range(150, 200);
}
staticint mlx90632_calculate_dataset_ready_time(struct mlx90632_data *data)
{ unsignedint refresh_time; int ret;
if (data->mtyp == MLX90632_MTYP_MEDICAL) {
ret = mlx90632_get_measurement_time(data->regmap,
MLX90632_EE_MEDICAL_MEAS1); if (ret < 0) return ret;
refresh_time = ret;
ret = mlx90632_get_measurement_time(data->regmap,
MLX90632_EE_MEDICAL_MEAS2); if (ret < 0) return ret;
refresh_time += ret;
} else {
ret = mlx90632_get_measurement_time(data->regmap,
MLX90632_EE_EXTENDED_MEAS1); if (ret < 0) return ret;
refresh_time = ret;
ret = mlx90632_get_measurement_time(data->regmap,
MLX90632_EE_EXTENDED_MEAS2); if (ret < 0) return ret;
refresh_time += ret;
ret = mlx90632_get_measurement_time(data->regmap,
MLX90632_EE_EXTENDED_MEAS3); if (ret < 0) return ret;
refresh_time += ret;
}
return refresh_time;
}
/** * mlx90632_perform_measurement() - Trigger and retrieve current measurement cycle * @data: pointer to mlx90632_data object containing regmap information * * Perform a measurement and return latest measurement cycle position reported * by sensor. This is a blocking function for 500ms, as that is default sensor * refresh rate.
*/ staticint mlx90632_perform_measurement(struct mlx90632_data *data)
{ unsignedint reg_status; int ret;
ret = regmap_clear_bits(data->regmap, MLX90632_REG_STATUS,
MLX90632_STAT_DATA_RDY); if (ret < 0) return ret;
/** * mlx90632_perform_measurement_burst() - Trigger and retrieve current measurement * cycle in step sleep mode * @data: pointer to mlx90632_data object containing regmap information * * Perform a measurement and return 2 as measurement cycle position reported * by sensor. This is a blocking function for amount dependent on the sensor * refresh rate.
*/ staticint mlx90632_perform_measurement_burst(struct mlx90632_data *data)
{ unsignedint reg_status; int ret;
ret = regmap_write_bits(data->regmap, MLX90632_REG_CONTROL,
MLX90632_CFG_SOB_MASK, MLX90632_CFG_SOB_MASK); if (ret < 0) return ret;
ret = mlx90632_calculate_dataset_ready_time(data); if (ret < 0) return ret;
msleep(ret); /* Wait minimum time for dataset to be ready */
ret = regmap_read_poll_timeout(data->regmap, MLX90632_REG_STATUS,
reg_status,
(reg_status & MLX90632_STAT_BUSY) == 0,
10000, 100 * 10000); if (ret < 0) {
dev_err(&data->client->dev, "data not ready"); return -ETIMEDOUT;
}
return 2;
}
staticint mlx90632_set_meas_type(struct mlx90632_data *data, u8 type)
{ int current_powerstatus; int ret;
if (data->mtyp == type) return 0;
current_powerstatus = data->powerstatus;
ret = mlx90632_pwr_continuous(data->regmap); if (ret < 0) return ret;
ret = regmap_write(data->regmap, MLX90632_REG_I2C_CMD, MLX90632_RESET_CMD); if (ret < 0) return ret;
mlx90632_reset_delay();
ret = regmap_update_bits(data->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;
/* Iterations of calculation as described in datasheet */ for (i = 0; i < 5; ++i) {
temp = mlx90632_calc_temp_object_iteration(temp, object, TAdut, TAdut4,
Fa, Fb, Ga, Ha, Hb,
tmp_emi);
} return temp;
}
/* 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;
}
staticint mlx90632_calc_object_dsp105(struct mlx90632_data *data, int *val)
{
s16 ambient_new_raw, ambient_old_raw, object_new_raw, object_old_raw;
s32 Ea, Eb, Fa, Fb, Ga; unsignedint read_tmp;
s64 object, ambient;
s16 Ha, Hb, Gb, Ka; int ret;
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Ea, &Ea); if (ret < 0) return ret;
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Eb, &Eb); if (ret < 0) return ret;
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Fa, &Fa); if (ret < 0) return ret;
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Fb, &Fb); if (ret < 0) return ret;
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Ga, &Ga); if (ret < 0) return ret;
ret = regmap_read(data->regmap, MLX90632_EE_Ha, &read_tmp); if (ret < 0) return ret;
Ha = (s16)read_tmp;
ret = regmap_read(data->regmap, MLX90632_EE_Hb, &read_tmp); if (ret < 0) return ret;
Hb = (s16)read_tmp;
ret = regmap_read(data->regmap, MLX90632_EE_Gb, &read_tmp); if (ret < 0) return ret;
Gb = (s16)read_tmp;
ret = regmap_read(data->regmap, MLX90632_EE_Ka, &read_tmp); if (ret < 0) return ret;
Ka = (s16)read_tmp;
ret = mlx90632_read_all_channel(data,
&ambient_new_raw, &ambient_old_raw,
&object_new_raw, &object_old_raw); 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;
/** * mlx90632_pm_interraction_wakeup() - Measure time between user interactions to change powermode * @data: pointer to mlx90632_data object containing interaction_ts information * * Switch to continuous mode when interaction is faster than MLX90632_MEAS_MAX_TIME. Update the * interaction_ts for each function call with the jiffies to enable measurement between function * calls. Initial value of the interaction_ts needs to be set before this function call.
*/ staticint mlx90632_pm_interraction_wakeup(struct mlx90632_data *data)
{ unsignedlong now; int ret;
now = jiffies; if (time_in_range(now, data->interaction_ts,
data->interaction_ts +
msecs_to_jiffies(MLX90632_MEAS_MAX_TIME + 100))) { if (data->powerstatus == MLX90632_PWR_STATUS_SLEEP_STEP) {
ret = mlx90632_pwr_continuous(data->regmap); if (ret < 0) return ret;
}
}
data->interaction_ts = now;
return 0;
}
staticint mlx90632_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *channel, int *val, int *val2, long mask)
{ struct mlx90632_data *data = iio_priv(indio_dev); int ret; int cr;
pm_runtime_get_sync(&data->client->dev);
ret = mlx90632_pm_interraction_wakeup(data); if (ret < 0) goto mlx90632_read_raw_pm;
switch (mask) { case IIO_CHAN_INFO_PROCESSED: switch (channel->channel2) { case IIO_MOD_TEMP_AMBIENT:
ret = mlx90632_calc_ambient_dsp105(data, val); if (ret < 0) goto mlx90632_read_raw_pm;
ret = IIO_VAL_INT; break; case IIO_MOD_TEMP_OBJECT:
ret = mlx90632_calc_object_dsp105(data, val); if (ret < 0) goto mlx90632_read_raw_pm;
ret = IIO_VAL_INT; break; default:
ret = -EINVAL; break;
} break; case IIO_CHAN_INFO_CALIBEMISSIVITY: if (data->emissivity == 1000) {
*val = 1;
*val2 = 0;
} else {
*val = 0;
*val2 = data->emissivity * 1000;
}
ret = IIO_VAL_INT_PLUS_MICRO; break; case IIO_CHAN_INFO_CALIBAMBIENT:
*val = data->object_ambient_temperature;
ret = IIO_VAL_INT; break; case IIO_CHAN_INFO_SAMP_FREQ:
ret = mlx90632_get_refresh_rate(data, &cr); if (ret < 0) goto mlx90632_read_raw_pm;
*val = mlx90632_freqs[cr][0];
*val2 = mlx90632_freqs[cr][1];
ret = IIO_VAL_INT_PLUS_MICRO; break; default:
ret = -EINVAL; break;
}
mlx90632->regulator = devm_regulator_get(&client->dev, "vdd"); if (IS_ERR(mlx90632->regulator)) return dev_err_probe(&client->dev, PTR_ERR(mlx90632->regulator), "failed to get vdd regulator");
ret = mlx90632_enable_regulator(mlx90632); if (ret < 0) return ret;
ret = devm_add_action_or_reset(&client->dev, mlx90632_disable_regulator,
mlx90632); if (ret < 0) {
dev_err(&client->dev, "Failed to setup regulator cleanup action %d\n",
ret); return ret;
}
ret = mlx90632_wakeup(mlx90632); if (ret < 0) {
dev_err(&client->dev, "Wakeup failed: %d\n", ret); return ret;
}
ret = devm_add_action_or_reset(&client->dev, mlx90632_sleep, mlx90632); if (ret < 0) {
dev_err(&client->dev, "Failed to setup low power cleanup action %d\n",
ret); return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.