1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/si1133/si1133.c
2022-01-17 12:04:12 +01:00

468 lines
16 KiB
C

/*
* Copyright (C) 2020 iosabi
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @ingroup drivers_si1133
* @{
*
* @file
* @brief Device driver implementation for the SI1133 UV/IR/Ambient light
* sensor with I2C interface.
*
* @author iosabi <iosabi@protonmail.com>
*
* @}
*/
#include <math.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "timex.h"
#include "ztimer.h"
#include "periph/i2c.h"
#include "si1133.h"
#include "si1133_internals.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/**
* @brief The command execution timeout in blocking mode.
*
* When forcing a measurement (SI1133_CMD_FORCE) the command is not done until
* all the measurements from all the channels are taken. This may take some time
* depending on the configuration. After the expected sampling time is over we
* wait up to this command timeout value for the command to be ready.
*/
#define SI1133_COMMAND_TIMEOUT_USEC 10000
/**
* @brief Return the expected sampling time in us for a FORCE command.
*/
static uint32_t _si1133_force_time_us(si1133_t *dev)
{
/* Sample time measured in 512 clocks of the 21 MHz, or about 24.4 us. */
uint32_t sample_time = 0;
for (uint32_t i = 0; i < dev->num_channels; i++) {
/* A forced sample consists of (1 << sw_gain) measurements where each
* one has a total 155 us (about 7 * 24.4 us) of processing time, plus
* two ADC samples. Each ADC sample requires a t_adstart time (48.8 us
* or 2 * 24.4 us) plus the time configured by the sample_time_log. */
sample_time += (2 * (2 + (1u << dev->channel[i].sample_time_log)) + 7)
<< dev->channel[i].sw_gain;
}
/* The resulting sample_time value in microseconds is sample_time * 512 / 21
* the sample_time is already a 24-bit number so we split the logic to
* fit in 32-bit arithmetic using the fact that 512 == 24 * 21 + 8. */
return sample_time * 24 + sample_time * 8 / 21;
}
/**
* @brief Run a command with no arguments.
*
* For commands CMD_PARAM_SET or CMD_PARAM_QUERY use @ref _si1133_set_param
* and @ref _si1133_get_param respectively instead.
*/
static int _si1133_run_command(si1133_t *dev, uint8_t command)
{
DEBUG("[si1133] run_command: 0x%.2x, cmd_counter=%d\n",
(unsigned)command, dev->cmd_counter);
int ret;
ret = i2c_write_reg(dev->i2c_dev, dev->address,
SI1133_REG_COMMAND, command, 0 /* flags */);
if (ret) {
DEBUG("[si1133] write command I2C error: %s\n", strerror(-ret));
return SI1133_ERR_I2C;
}
if (command == SI1133_CMD_FORCE) {
/* Wait for the expected force acquisition time. */
ztimer_sleep(ZTIMER_USEC, _si1133_force_time_us(dev));
}
if (command == SI1133_CMD_RESET_SW) {
/* Reset command sets the command counter of 0x0f. */
dev->cmd_counter = 0x0f;
/* Reset command puts the device in "Initialization Mode" which requires
* us to wait until the device is ready. */
ztimer_sleep(ZTIMER_USEC, SI1133_STARTUP_TIME_MS * US_PER_MS);
}
else if (command == SI1133_CMD_RESET_CMD_CTR) {
/* The reset cmd_counter command, well, resets it to 0. */
dev->cmd_counter = 0;
}
else {
/* Increment the expected 4-bit command counter value. */
dev->cmd_counter = (dev->cmd_counter + 1) & SI1133_RESP0_COUNTER_MASK;
}
uint8_t new_cmd_ctr;
ztimer_now_t start_time;
bool retry = false;
while (1) {
ret = i2c_read_reg(dev->i2c_dev, dev->address, SI1133_REG_RESPONSE0,
&new_cmd_ctr, 0 /* flags */);
if (ret) {
DEBUG("[si1133] read RESPONSE0 I2C error: %s\n", strerror(-ret));
return SI1133_ERR_I2C;
}
if (new_cmd_ctr & SI1133_RESP0_CMD_ERR_MASK) {
DEBUG("[si1133] Command 0x%.2x returned error %d\n",
(unsigned)command, new_cmd_ctr & SI1133_RESP0_COUNTER_MASK);
/* Error code 2 is "ADC or accumulation overflow", while error code
* 3 is output buffer overflow which can only occur in BURST mode.
* However, in FORCE mode after the first overflow we can get an
* error code 3 if we change the settings in between the overflow
* and a new FORCE command, probably due to a silicon bug when
* handling sw_gain overflows since it is not possible to write more
* than 18 bytes of output in FORCE mode while the output buffer is
* 26 bytes long. */
if ((new_cmd_ctr & SI1133_RESP0_COUNTER_MASK) >= 2) {
return SI1133_ERR_OVERFLOW;
}
return SI1133_ERR_LOGIC;
}
/* The reset command is done when the RUNNING flag is clear, the other
* commands are done when the command value is set to the expected
* one. */
if ((command == SI1133_CMD_RESET_SW)
? !(new_cmd_ctr & SI1133_RESP0_RUNNING_MASK)
: (dev->cmd_counter ==
(new_cmd_ctr & SI1133_RESP0_COUNTER_MASK))) {
break;
}
/* The command didn't yet finish in this case so it should be in running
* state and we need to retry the loop with a timeout. This avoids
* calling ztimer for commands that are immediate. */
if (retry) {
if (ztimer_now(ZTIMER_USEC) - start_time > SI1133_COMMAND_TIMEOUT_USEC) {
DEBUG("[si1133] Command 0x%.2x timeout.\n", (unsigned)command);
return SI1133_ERR_LOGIC;
}
}
else {
retry = true;
start_time = ztimer_now(ZTIMER_USEC);
}
}
if (retry) {
DEBUG("[si1133] Command overtime: %" PRIu32 " us.\n",
ztimer_now(ZTIMER_USEC) - start_time);
}
return SI1133_OK;
}
static int _si1133_set_param(si1133_t *dev, uint8_t param, uint8_t value)
{
int ret;
ret = i2c_write_reg(dev->i2c_dev, dev->address,
SI1133_REG_HOSTIN0, value, 0 /* flags */);
if (ret) {
DEBUG("[si1133] write HOSTIN0 I2C error: %s\n", strerror(-ret));
return SI1133_ERR_I2C;
}
ret = _si1133_run_command(dev, param | SI1133_CMD_PARAM_SET);
if (ret) {
return ret;
}
uint8_t resp1;
ret = i2c_read_reg(dev->i2c_dev, dev->address,
SI1133_REG_RESPONSE1, &resp1, 0);
if (ret) {
DEBUG("[si1133] read RESPONSE1 I2C error: %s\n", strerror(-ret));
return SI1133_ERR_I2C;
}
if (resp1 != value) {
DEBUG("[si1133] Expected to read back value 0x%.2" PRIu8
" when setting param 0x%.2" PRIu8 " but got 0x%.2" PRIu8 "\n",
value, param, resp1);
return SI1133_ERR_LOGIC;
}
return SI1133_OK;
}
/**
* @brief Reset the device.
*/
static int _si1133_reset(si1133_t *dev)
{
DEBUG("[si1133] reset()\n");
dev->num_channels = 0;
for (uint32_t i = 0; i < SI1133_NUM_CHANNELS; i++) {
/* Initialize the config with invalid values to force the first config
* call to update it. */
dev->channel[i].sw_gain = 0xff;
}
int ret = _si1133_run_command(dev, SI1133_CMD_RESET_SW);
if (ret) {
return ret;
}
return _si1133_run_command(dev, SI1133_CMD_RESET_CMD_CTR);
}
/**
* @brief Configure a single channel with the passed parameters.
*/
static si1133_ret_code_t _si1133_configure_channel(
si1133_t *dev, uint32_t channel_id, const si1133_channel_t *channel)
{
if (channel->sample_time_log > 14 || channel->sw_gain > 7) {
return SI1133_ERR_PARAMS;
}
if (!memcmp(channel, &dev->channel[channel_id], sizeof(si1133_channel_t))) {
/* Avoid the I2C roundtrip if the channel configuration didn't
* change. */
return SI1133_OK;
}
/* Generate the channel configuration. */
si1133_channel_params_t config;
#define SI1133_SENS_CASE(x) \
case SI1133_SENS_ ## x: \
config.adcconfig = \
SI1133_ADCMUX_ ## x << SI1133_ADCCONFIG_ADCMUX_SHIFT; \
break;
switch (channel->sensor) {
SI1133_SENS_CASE(SMALL_IR)
SI1133_SENS_CASE(MEDIUM_IR)
SI1133_SENS_CASE(LARGE_IR)
SI1133_SENS_CASE(WHITE)
SI1133_SENS_CASE(LARGE_WHITE)
SI1133_SENS_CASE(UV)
SI1133_SENS_CASE(DEEP_UV)
default:
return SI1133_ERR_PARAMS;
}
/* Use normal decimation (1) except in the extremes where we must use
* the other decimation values. */
const uint8_t hw_gain = channel->sample_time_log == 0
? 0
: (channel->sample_time_log <= 11
? channel->sample_time_log - 1
: 11);
/* Select decimation. A value of "0" in this field is the "normal"
* decimation, which corresponds to 1 in our "decimation" equation.
* The values in this hardware field are offset by 3. */
const uint8_t decimation =
(channel->sample_time_log - hw_gain + 3) & 3;
config.adcconfig |= decimation << SI1133_ADCCONFIG_DECIM_RATE_SHIFT;
/* HSIG = 0, SW_GAIN and HW_GAIN as configured. */
config.adcsens = (hw_gain << SI1133_ADCSENS_HW_GAIN_SHIFT) |
(channel->sw_gain << SI1133_ADCSENS_SW_GAIN_SHIFT);
/* 24-bit output, no output shift, no threshold. */
config.adcpost = SI1133_ADCPOST_24BIT_OUT_MASK;
/* No counter, this will only be used in Force mode. */
config.measconfig = 0;
DEBUG("[si1133] config: %.2x %.2x %.2x %.2x\n",
((uint8_t *)&config)[0], ((uint8_t *)&config)[1],
((uint8_t *)&config)[2], ((uint8_t *)&config)[3]);
for (uint8_t i = 0; i < sizeof(config); i++) {
uint8_t param = SI1133_PARAM_ADCCONFIG0 + sizeof(config) * channel_id +
i;
si1133_ret_code_t ret =
_si1133_set_param(dev, param, ((uint8_t *)&config)[i]);
if (ret) {
return ret;
}
}
memcpy(&(dev->channel[channel_id]), channel, sizeof(si1133_channel_t));
return SI1133_OK;
}
static int _si1133_read_values(si1133_t *dev, int32_t *values,
uint32_t num_channels)
{
/* We can read all registers in a single I2C burst. */
uint8_t data[3 * num_channels];
/* We only request 24-bit values from the device so they are all 3 byte
* long. */
int ret = i2c_read_regs(dev->i2c_dev, dev->address, SI1133_REG_HOSTOUTx,
data, 3 * num_channels, 0 /* flags */);
if (ret) {
return SI1133_ERR_I2C;
}
uint8_t *offset = data;
for (uint8_t i = 0; i < num_channels; i++) {
/* Sign-extend the first 8-bit value before shifting. */
int32_t value = ((int32_t)(int8_t)*(offset++)) << 16;
value |= *(offset++) << 8u;
value |= *(offset++);
values[i] = value;
}
return SI1133_OK;
}
si1133_ret_code_t si1133_init(si1133_t *dev, const si1133_params_t *params)
{
dev->i2c_dev = params->i2c_dev;
dev->address = params->address;
/* After leaving "Off Mode" the SI1133 enters an "Initialization Mode" for
* a period of time in which it can't be reached over I2C. After this time
* the device will be in Standby Mode. */
ztimer_sleep(ZTIMER_USEC, SI1133_STARTUP_TIME_MS * US_PER_MS);
i2c_acquire(params->i2c_dev);
/* check sensor ID */
uint8_t partid = 0;
int ret = i2c_read_reg(params->i2c_dev, params->address, SI1133_REG_PART_ID,
&partid, 0);
if (ret != 0) {
DEBUG("[si1133] i2c communication error: %s.\n", strerror(-ret));
i2c_release(params->i2c_dev);
return SI1133_ERR_I2C;
}
if (partid != SI1133_ID) {
DEBUG("[si1133] Invalid part id: 0x%.2u\n", (unsigned)partid);
i2c_release(params->i2c_dev);
return SI1133_ERR_NODEV;
}
#if ENABLE_DEBUG
uint8_t rev_id, hw_id;
i2c_read_reg(params->i2c_dev, params->address, SI1133_REG_REV_ID, &rev_id,
0 /* flags */);
i2c_read_reg(params->i2c_dev, params->address, SI1133_REG_HW_ID, &hw_id,
0 /* flags */);
DEBUG("[si1133] impl code: %u, silicon HW rev: %u, rev: %u.%u\n",
hw_id & 0x1f, hw_id >> 5, rev_id >> 4, rev_id & 0x0f);
#endif /* ENABLE_DEBUG */
/* We don't know the state in which the device is at this point so we need
* to perform a reset, unfortunately this requires another start-up wait. */
ret = _si1133_reset(dev);
if (ret) {
i2c_release(dev->i2c_dev);
return ret;
}
i2c_release(dev->i2c_dev);
return SI1133_OK;
}
si1133_ret_code_t si1133_configure_channels(si1133_t *dev,
const si1133_channel_t *channels,
uint32_t num_channels)
{
DEBUG("[si1133] configure_channels(num=%" PRIu32 ")\n", num_channels);
if (num_channels > SI1133_NUM_CHANNELS) {
return SI1133_ERR_PARAMS;
}
i2c_acquire(dev->i2c_dev);
si1133_ret_code_t ret;
for (uint32_t i = 0; i < num_channels; i++) {
ret = _si1133_configure_channel(dev, i, channels + i);
if (ret) {
dev->num_channels = 0;
i2c_release(dev->i2c_dev);
return ret;
}
}
/* CHAN_LIST is a bit mask of channels used. */
ret = _si1133_set_param(dev, SI1133_PARAM_CHAN_LIST,
(1u << num_channels) - 1);
if (ret) {
num_channels = 0;
}
dev->num_channels = num_channels;
i2c_release(dev->i2c_dev);
DEBUG("[si1133] Sample Time %" PRIu32 " us\n", _si1133_force_time_us(dev));
return ret;
}
si1133_ret_code_t si1133_easy_configure(si1133_t *dev,
si1133_sensor_t sensor_mask,
uint8_t sample_time_log,
uint8_t sw_gain)
{
DEBUG("[si1133] easy_configure(0x%.2x)\n", (unsigned)sensor_mask);
i2c_acquire(dev->i2c_dev);
si1133_ret_code_t ret;
uint8_t num_channels = 0;
for (uint8_t mask = sensor_mask; mask;
num_channels++, mask = mask & (mask - 1)) {
if (num_channels >= SI1133_NUM_CHANNELS) {
dev->num_channels = 0;
i2c_release(dev->i2c_dev);
return SI1133_ERR_PARAMS;
}
si1133_channel_t channel;
channel.sensor = mask ^ (mask & (mask - 1));
channel.sample_time_log = sample_time_log;
channel.sw_gain = sw_gain;
ret = _si1133_configure_channel(dev, num_channels, &channel);
if (ret) {
dev->num_channels = 0;
i2c_release(dev->i2c_dev);
return ret;
}
}
/* CHAN_LIST is a bit mask of channels used. */
ret = _si1133_set_param(dev, SI1133_PARAM_CHAN_LIST,
(1u << num_channels) - 1);
if (ret) {
num_channels = 0;
}
dev->num_channels = num_channels;
i2c_release(dev->i2c_dev);
DEBUG("[si1133] Sample Time %" PRIu32 " us\n", _si1133_force_time_us(dev));
return ret;
}
si1133_ret_code_t si1133_capture_sensors(si1133_t *dev, int32_t *values,
uint32_t num_channels)
{
if (!dev->num_channels) {
/* Must be configured before calling capture_sensors. */
return SI1133_ERR_PARAMS;
}
i2c_acquire(dev->i2c_dev);
si1133_ret_code_t force_ret;
force_ret = _si1133_run_command(dev, SI1133_CMD_FORCE);
if (force_ret != SI1133_OK && force_ret != SI1133_ERR_OVERFLOW) {
i2c_release(dev->i2c_dev);
DEBUG("[si1133] force read command error: %d\n", force_ret);
return force_ret;
}
si1133_ret_code_t ret = SI1133_OK;
if (force_ret == SI1133_ERR_OVERFLOW) {
/* We need to reset the overflow condition with a RESET_CMD_CTR */
ret = _si1133_run_command(dev, SI1133_CMD_RESET_CMD_CTR);
}
if (ret == SI1133_OK) {
ret = _si1133_read_values(dev, values, num_channels);
}
i2c_release(dev->i2c_dev);
/* If there was an error reading the I2C values then return that error,
* otherwise we want to return the CMD_FORCE return value because there
* could be an overflow non-fatal error to report. */
return ret ? ret : force_ret;
}