drivers: sensor: Support Hamamatsu Photonics S11059 Color Sensor
DataSheet: https://datasheetspdf.com/pdf/1323325/Hamamatsu/S11059-02DT/1 Testing Environment: esp32 Signed-off-by: Hiroki Tada <tada.hiroki@fujitsu.com>
This commit is contained in:
parent
36cc74e7e8
commit
943158326c
|
@ -130,6 +130,7 @@ add_subdirectory_ifdef(CONFIG_RPI_PICO_TEMP rpi_pico_temp)
|
|||
add_subdirectory_ifdef(CONFIG_XMC4XXX_TEMP xmc4xxx_temp)
|
||||
add_subdirectory_ifdef(CONFIG_TMD2620 tmd2620)
|
||||
add_subdirectory_ifdef(CONFIG_ZEPHYR_NTC_THERMISTOR zephyr_thermistor)
|
||||
add_subdirectory_ifdef(CONFIG_S11059 s11059)
|
||||
|
||||
if(CONFIG_USERSPACE OR CONFIG_SENSOR_SHELL OR CONFIG_SENSOR_SHELL_BATTERY)
|
||||
# The above if() is needed or else CMake would complain about
|
||||
|
|
|
@ -299,4 +299,6 @@ source "drivers/sensor/tmd2620/Kconfig"
|
|||
|
||||
source "drivers/sensor/zephyr_thermistor/Kconfig"
|
||||
|
||||
source "drivers/sensor/s11059/Kconfig"
|
||||
|
||||
endif # SENSOR
|
||||
|
|
6
drivers/sensor/s11059/CMakeLists.txt
Normal file
6
drivers/sensor/s11059/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Copyright (c) 2022 Hiroki Tada
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(s11059.c)
|
12
drivers/sensor/s11059/Kconfig
Normal file
12
drivers/sensor/s11059/Kconfig
Normal file
|
@ -0,0 +1,12 @@
|
|||
# S11059 color sensor
|
||||
|
||||
# Copyright (c) 2022 Hiroki Tada
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config S11059
|
||||
bool "S11059 color sensor"
|
||||
default y
|
||||
depends on DT_HAS_HAMAMATSU_S11059_ENABLED
|
||||
select I2C
|
||||
help
|
||||
Enable driver for S11059 color sensor
|
308
drivers/sensor/s11059/s11059.c
Normal file
308
drivers/sensor/s11059/s11059.c
Normal file
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Hiroki Tada
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Datasheet:
|
||||
* https://datasheetspdf.com/pdf/1323325/Hamamatsu/S11059-02DT/1
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT hamamatsu_s11059
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
LOG_MODULE_REGISTER(S11059, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
/* register address */
|
||||
#define S11059_REG_ADDR_CONTROL 0x00
|
||||
#define S11059_REG_ADDR_MANUAL_TIMING 0x01
|
||||
#define S11059_REG_ADDR_DATA 0x03
|
||||
|
||||
/* control bit */
|
||||
#define S11059_CONTROL_GAIN 3
|
||||
#define S11059_CONTROL_STANDBY_MONITOR 5
|
||||
#define S11059_CONTROL_STADBY 6
|
||||
#define S11059_CONTROL_ADC_RESET 7
|
||||
|
||||
/* bit mask for control */
|
||||
#define S11059_BIT_MASK_INTEGRATION_TIME 0x03
|
||||
#define S11059_BIT_MASK_CONTROL_STANDBY_MONITOR 0x20
|
||||
|
||||
/* factors for converting sensor samples to Lux */
|
||||
#define S11059_CONVERT_FACTOR_LOW_RED (112)
|
||||
#define S11059_CONVERT_FACTOR_LOW_GREEN (83)
|
||||
#define S11059_CONVERT_FACTOR_LOW_BLUE (44)
|
||||
#define S11059_CONVERT_FACTOR_LOW_IR (3 * 10)
|
||||
#define S11059_CONVERT_FACTOR_HIGH_RED (117 * 10)
|
||||
#define S11059_CONVERT_FACTOR_HIGH_GREEN (85 * 10)
|
||||
#define S11059_CONVERT_FACTOR_HIGH_BLUE (448)
|
||||
#define S11059_CONVERT_FACTOR_HIGH_IR (30 * 10)
|
||||
|
||||
#define S11059_INTEGRATION_TIME_MODE_00 175
|
||||
#define S11059_INTEGRATION_TIME_MODE_01 2800
|
||||
#define S11059_INTEGRATION_TIME_MODE_10 44800
|
||||
#define S11059_INTEGRATION_TIME_MODE_11 358400
|
||||
|
||||
#define S11059_WAIT_PER_LOOP 400
|
||||
#define S11059_INITIAL_CONTROL 0x04
|
||||
#define S11059_MAX_MANUAL_TIMING UINT16_MAX
|
||||
#define S11059_CARRY_UP 10000
|
||||
|
||||
#define S11059_NUM_GAIN_MODE 2
|
||||
|
||||
enum s11059_channel {
|
||||
RED,
|
||||
GREEN,
|
||||
BLUE,
|
||||
IR,
|
||||
NUM_OF_COLOR_CHANNELS
|
||||
};
|
||||
|
||||
struct s11059_dev_config {
|
||||
struct i2c_dt_spec bus;
|
||||
uint8_t gain;
|
||||
int64_t integration_time; /* integration period (unit: us) */
|
||||
};
|
||||
|
||||
struct s11059_data {
|
||||
uint16_t samples[NUM_OF_COLOR_CHANNELS];
|
||||
};
|
||||
|
||||
static const uint16_t convert_factors[S11059_NUM_GAIN_MODE][NUM_OF_COLOR_CHANNELS] = {
|
||||
{S11059_CONVERT_FACTOR_LOW_RED, S11059_CONVERT_FACTOR_LOW_GREEN,
|
||||
S11059_CONVERT_FACTOR_LOW_BLUE, S11059_CONVERT_FACTOR_LOW_IR},
|
||||
{S11059_CONVERT_FACTOR_HIGH_RED, S11059_CONVERT_FACTOR_HIGH_GREEN,
|
||||
S11059_CONVERT_FACTOR_HIGH_BLUE, S11059_CONVERT_FACTOR_HIGH_IR}};
|
||||
|
||||
/* Integration timing in Manual integration mode */
|
||||
static const uint32_t integ_time_factor[] = {
|
||||
S11059_INTEGRATION_TIME_MODE_00, S11059_INTEGRATION_TIME_MODE_01,
|
||||
S11059_INTEGRATION_TIME_MODE_10, S11059_INTEGRATION_TIME_MODE_11};
|
||||
|
||||
static int s11059_convert_channel_to_index(enum sensor_channel chan)
|
||||
{
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_RED:
|
||||
return RED;
|
||||
case SENSOR_CHAN_GREEN:
|
||||
return GREEN;
|
||||
case SENSOR_CHAN_BLUE:
|
||||
return BLUE;
|
||||
default:
|
||||
return IR;
|
||||
}
|
||||
}
|
||||
|
||||
static int s11059_samples_read(const struct device *dev, uint8_t addr, uint16_t *val, uint32_t size)
|
||||
{
|
||||
const struct s11059_dev_config *cfg = dev->config;
|
||||
int rc;
|
||||
|
||||
if (size < NUM_OF_COLOR_CHANNELS * 2) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = i2c_burst_read_dt(&cfg->bus, addr, (uint8_t *)val, size);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < NUM_OF_COLOR_CHANNELS; i++) {
|
||||
val[i] = sys_be16_to_cpu(val[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s11059_control_write(const struct device *dev, uint8_t control)
|
||||
{
|
||||
const struct s11059_dev_config *cfg = dev->config;
|
||||
const uint8_t opcode[] = {S11059_REG_ADDR_CONTROL, control};
|
||||
|
||||
return i2c_write_dt(&cfg->bus, opcode, sizeof(opcode));
|
||||
}
|
||||
|
||||
static int s11059_manual_timing_write(const struct device *dev, uint16_t manual_time)
|
||||
{
|
||||
const struct s11059_dev_config *cfg = dev->config;
|
||||
const uint8_t opcode[] = {S11059_REG_ADDR_MANUAL_TIMING, manual_time >> 8,
|
||||
manual_time & 0xFF};
|
||||
|
||||
return i2c_write_dt(&cfg->bus, opcode, sizeof(opcode));
|
||||
}
|
||||
|
||||
static int s11059_start_measurement(const struct device *dev)
|
||||
{
|
||||
const struct s11059_dev_config *cfg = dev->config;
|
||||
uint8_t control;
|
||||
int rc;
|
||||
|
||||
/* read current control */
|
||||
rc = i2c_reg_read_byte_dt(&cfg->bus, S11059_REG_ADDR_CONTROL, &control);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("%s, Failed to read current control.", dev->name);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* reset adc block */
|
||||
WRITE_BIT(control, S11059_CONTROL_ADC_RESET, 1);
|
||||
WRITE_BIT(control, S11059_CONTROL_STADBY, 0);
|
||||
rc = s11059_control_write(dev, control);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("%s, Failed to reset adc.", dev->name);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* start device */
|
||||
WRITE_BIT(control, S11059_CONTROL_ADC_RESET, 0);
|
||||
rc = s11059_control_write(dev, control);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("%s, Failed to start device.", dev->name);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s11059_integ_time_calculate(const struct device *dev, uint16_t *manual_time,
|
||||
uint8_t *mode)
|
||||
{
|
||||
const struct s11059_dev_config *cfg = dev->config;
|
||||
int64_t tmp;
|
||||
|
||||
if (cfg->integration_time < integ_time_factor[0]) {
|
||||
*mode = 0;
|
||||
*manual_time = 1;
|
||||
} else {
|
||||
*manual_time = S11059_MAX_MANUAL_TIMING;
|
||||
for (uint8_t i = 0; i < ARRAY_SIZE(integ_time_factor); i++) {
|
||||
*mode = i;
|
||||
tmp = cfg->integration_time / integ_time_factor[i];
|
||||
if (tmp < S11059_MAX_MANUAL_TIMING) {
|
||||
*manual_time = (uint16_t)tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s11059_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
||||
{
|
||||
const struct s11059_dev_config *cfg = dev->config;
|
||||
struct s11059_data *drv_data = dev->data;
|
||||
uint16_t values[NUM_OF_COLOR_CHANNELS];
|
||||
uint8_t control;
|
||||
int rc;
|
||||
|
||||
if (chan != SENSOR_CHAN_ALL) {
|
||||
LOG_ERR("%s, Unsupported sensor channel", dev->name);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
rc = s11059_start_measurement(dev);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("%s, Failed to start measurement.", dev->name);
|
||||
return rc;
|
||||
}
|
||||
|
||||
do {
|
||||
rc = i2c_reg_read_byte_dt(&cfg->bus, S11059_REG_ADDR_CONTROL, &control);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("%s, Failed to read control.", dev->name);
|
||||
return rc;
|
||||
}
|
||||
k_usleep(S11059_WAIT_PER_LOOP);
|
||||
|
||||
} while (!(control & S11059_BIT_MASK_CONTROL_STANDBY_MONITOR));
|
||||
|
||||
rc = s11059_samples_read(dev, S11059_REG_ADDR_DATA, values, sizeof(values));
|
||||
if (rc < 0) {
|
||||
LOG_ERR("%s, Failed to get sample.", dev->name);
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < NUM_OF_COLOR_CHANNELS; i++) {
|
||||
drv_data->samples[i] = values[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s11059_channel_get(const struct device *dev, enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
const struct s11059_dev_config *cfg = dev->config;
|
||||
struct s11059_data *drv_data = dev->data;
|
||||
const uint8_t index = s11059_convert_channel_to_index(chan);
|
||||
const uint16_t factor = convert_factors[cfg->gain][index];
|
||||
uint32_t meas_value;
|
||||
|
||||
meas_value = drv_data->samples[index] * S11059_CARRY_UP / factor;
|
||||
val->val1 = meas_value / (S11059_CARRY_UP / 10);
|
||||
val->val2 = meas_value % (S11059_CARRY_UP / 10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s11059_init(const struct device *dev)
|
||||
{
|
||||
const struct s11059_dev_config *cfg = dev->config;
|
||||
uint8_t control = S11059_INITIAL_CONTROL;
|
||||
uint16_t manual_time;
|
||||
uint8_t timing_mode;
|
||||
int rc;
|
||||
|
||||
/* device set */
|
||||
if (!i2c_is_ready_dt(&cfg->bus)) {
|
||||
LOG_ERR("%s, device is not ready.", dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = s11059_integ_time_calculate(dev, &manual_time, &timing_mode);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("%s, Failed to calculate manual timing.", dev->name);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = s11059_manual_timing_write(dev, manual_time);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("%s, Failed to set manual timing.", dev->name);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* set integration time mode and gain*/
|
||||
control |= timing_mode & S11059_BIT_MASK_INTEGRATION_TIME;
|
||||
WRITE_BIT(control, S11059_CONTROL_GAIN, cfg->gain);
|
||||
rc = s11059_control_write(dev, control);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("%s, Failed to set gain and integration time.", dev->name);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sensor_driver_api s11059_driver_api = {
|
||||
.sample_fetch = s11059_sample_fetch,
|
||||
.channel_get = s11059_channel_get,
|
||||
};
|
||||
|
||||
#define S11059_INST(inst) \
|
||||
static struct s11059_data s11059_data_##inst; \
|
||||
static const struct s11059_dev_config s11059_config_##inst = { \
|
||||
.bus = I2C_DT_SPEC_INST_GET(inst), \
|
||||
.gain = DT_INST_PROP(inst, high_gain), \
|
||||
.integration_time = DT_INST_PROP(inst, integration_time)}; \
|
||||
SENSOR_DEVICE_DT_INST_DEFINE(inst, s11059_init, NULL, &s11059_data_##inst, \
|
||||
&s11059_config_##inst, POST_KERNEL, \
|
||||
CONFIG_SENSOR_INIT_PRIORITY, &s11059_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(S11059_INST)
|
28
dts/bindings/sensor/hamamatsu,s11059.yaml
Normal file
28
dts/bindings/sensor/hamamatsu,s11059.yaml
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Copyright (c) 2022, Hiroki Tada
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
Hamamatsu Photonics S11059 Color Sensor. See datasheet at
|
||||
https://datasheetspdf.com/pdf/1323325/Hamamatsu/S11059-02DT/1
|
||||
|
||||
compatible: "hamamatsu,s11059"
|
||||
|
||||
include: [sensor-device.yaml, i2c-device.yaml]
|
||||
|
||||
properties:
|
||||
high-gain:
|
||||
type: boolean
|
||||
description: |
|
||||
When present, the high gain is enabled.
|
||||
The gain ratio is 10 times from low to high.
|
||||
|
||||
integration-time:
|
||||
type: int
|
||||
default: 546000
|
||||
description: |
|
||||
Integration time (unit is us).
|
||||
By setting this value to the desired integration time,
|
||||
the Integration time setting and Manual timing register
|
||||
values are set automatically.
|
||||
|
||||
The default value is 546ms. See datasheet, bottom of page 3.
|
|
@ -694,3 +694,9 @@ test_i2c_wsen_pads: wsen_pads@6A {
|
|||
drdy-gpios = <&test_gpio 0 0>;
|
||||
odr = <1>;
|
||||
};
|
||||
|
||||
test_i2c_s11059: s11059@6b {
|
||||
compatible = "hamamatsu,s11059";
|
||||
reg = <0x6b>;
|
||||
integration-time = <546000>;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue