Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

272 lines
6.3 KiB

/* ds3231.c - Driver for Maxim DS3231 temperature sensor */
/*
* Copyright (c) 2024 Gergo Vari <work@gergovari.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/init.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/rtio/work.h>
#include <zephyr/drivers/mfd/ds3231.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/__assert.h>
#include <math.h>
#include "ds3231.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(SENSOR_DS3231, CONFIG_SENSOR_LOG_LEVEL);
#include <inttypes.h>
#define DT_DRV_COMPAT maxim_ds3231_sensor
struct sensor_ds3231_data {
const struct device *dev;
uint16_t raw_temp;
};
struct sensor_ds3231_conf {
const struct device *mfd;
};
int sensor_ds3231_read_temp(const struct device *dev, uint16_t *raw_temp)
{
const struct sensor_ds3231_conf *config = dev->config;
uint8_t buf[2];
int err = mfd_ds3231_i2c_get_registers(config->mfd, DS3231_REG_TEMP_MSB, buf, 2);
*raw_temp = ((uint16_t)((buf[0]) << 2) | (buf[1] >> 6));
if (err != 0) {
return err;
}
return 0;
}
/* Fetch and Get (will be deprecated) */
int sensor_ds3231_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
struct sensor_ds3231_data *data = dev->data;
int err = sensor_ds3231_read_temp(dev, &(data->raw_temp));
if (err != 0) {
LOG_ERR("ds3231 sample fetch failed %d", err);
return err;
}
return 0;
}
static int sensor_ds3231_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
struct sensor_ds3231_data *data = dev->data;
switch (chan) {
case SENSOR_CHAN_AMBIENT_TEMP:
const uint16_t raw_temp = data->raw_temp;
val->val1 = (int8_t)(raw_temp & GENMASK(8, 2)) >> 2;
uint8_t frac = raw_temp & 3;
val->val2 = (frac * 25) * pow(10, 4);
break;
default:
return -ENOTSUP;
}
return 0;
}
/* Read and Decode */
struct sensor_ds3231_header {
uint64_t timestamp;
} __attribute__((__packed__));
struct sensor_ds3231_edata {
struct sensor_ds3231_header header;
uint16_t raw_temp;
};
void sensor_ds3231_submit_sync(struct rtio_iodev_sqe *iodev_sqe)
{
uint32_t min_buf_len = sizeof(struct sensor_ds3231_edata);
int rc;
uint8_t *buf;
uint32_t buf_len;
const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
const struct device *dev = cfg->sensor;
const struct sensor_chan_spec *const channels = cfg->channels;
rc = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len);
if (rc != 0) {
LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len);
rtio_iodev_sqe_err(iodev_sqe, rc);
return;
}
struct sensor_ds3231_edata *edata;
edata = (struct sensor_ds3231_edata *)buf;
if (channels[0].chan_type != SENSOR_CHAN_AMBIENT_TEMP) {
return;
}
uint16_t raw_temp;
rc = sensor_ds3231_read_temp(dev, &raw_temp);
if (rc != 0) {
LOG_ERR("Failed to fetch samples");
rtio_iodev_sqe_err(iodev_sqe, rc);
return;
}
edata->header.timestamp = k_ticks_to_ns_floor64(k_uptime_ticks());
edata->raw_temp = raw_temp;
rtio_iodev_sqe_ok(iodev_sqe, 0);
}
void sensor_ds3231_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
{
struct rtio_work_req *req = rtio_work_req_alloc();
if (req == NULL) {
LOG_ERR("RTIO work item allocation failed."
"Consider to increase CONFIG_RTIO_WORKQ_POOL_ITEMS.");
rtio_iodev_sqe_err(iodev_sqe, -ENOMEM);
return;
}
/* TODO: optimize with new bus shims
* to avoid swapping execution contexts
* for a small register read
*/
rtio_work_req_submit(req, iodev_sqe, sensor_ds3231_submit_sync);
}
static int sensor_ds3231_decoder_get_frame_count(const uint8_t *buffer,
struct sensor_chan_spec chan_spec,
uint16_t *frame_count)
{
int err = -ENOTSUP;
if (chan_spec.chan_idx != 0) {
return err;
}
switch (chan_spec.chan_type) {
case SENSOR_CHAN_AMBIENT_TEMP:
*frame_count = 1;
break;
default:
return err;
}
if (*frame_count > 0) {
err = 0;
}
return err;
}
static int sensor_ds3231_decoder_get_size_info(struct sensor_chan_spec chan_spec, size_t *base_size,
size_t *frame_size)
{
switch (chan_spec.chan_type) {
case SENSOR_CHAN_AMBIENT_TEMP:
*base_size = sizeof(struct sensor_q31_sample_data);
*frame_size = sizeof(struct sensor_q31_sample_data);
return 0;
default:
return -ENOTSUP;
}
}
static int sensor_ds3231_decoder_decode(const uint8_t *buffer, struct sensor_chan_spec chan_spec,
uint32_t *fit, uint16_t max_count, void *data_out)
{
if (*fit != 0) {
return 0;
}
struct sensor_q31_data *out = data_out;
out->header.reading_count = 1;
const struct sensor_ds3231_edata *edata = (const struct sensor_ds3231_edata *)buffer;
switch (chan_spec.chan_type) {
case SENSOR_CHAN_AMBIENT_TEMP:
out->header.base_timestamp_ns = edata->header.timestamp;
const uint16_t raw_temp = edata->raw_temp;
out->shift = 8 - 1;
out->readings[0].temperature = (q31_t)raw_temp << (32 - 10);
break;
default:
return -EINVAL;
}
*fit = 1;
return 1;
}
SENSOR_DECODER_API_DT_DEFINE() = {
.get_frame_count = sensor_ds3231_decoder_get_frame_count,
.get_size_info = sensor_ds3231_decoder_get_size_info,
.decode = sensor_ds3231_decoder_decode,
};
int sensor_ds3231_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder)
{
ARG_UNUSED(dev);
*decoder = &SENSOR_DECODER_NAME();
return 0;
}
static int sensor_ds3231_init(const struct device *dev)
{
const struct sensor_ds3231_conf *config = dev->config;
if (!device_is_ready(config->mfd)) {
return -ENODEV;
}
return 0;
}
static DEVICE_API(sensor, driver_api) = {
.sample_fetch = sensor_ds3231_sample_fetch,
.channel_get = sensor_ds3231_channel_get,
#ifdef CONFIG_SENSOR_ASYNC_API
.submit = sensor_ds3231_submit,
.get_decoder = sensor_ds3231_get_decoder,
#endif
};
#define SENSOR_DS3231_DEFINE(inst) \
static struct sensor_ds3231_data sensor_ds3231_data_##inst; \
static const struct sensor_ds3231_conf sensor_ds3231_conf_##inst = { \
.mfd = DEVICE_DT_GET(DT_INST_PARENT(inst))}; \
SENSOR_DEVICE_DT_INST_DEFINE(inst, &sensor_ds3231_init, NULL, &sensor_ds3231_data_##inst, \
&sensor_ds3231_conf_##inst, POST_KERNEL, \
CONFIG_SENSOR_DS3231_INIT_PRIORITY, &driver_api);
DT_INST_FOREACH_STATUS_OKAY(SENSOR_DS3231_DEFINE)