1 /*
2 * Copyright (c) 2024 Renato Soma <renatoys08@gmail.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT lm35
8
9 #include <zephyr/device.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/drivers/adc.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/logging/log.h>
15
16 #define LM35_GAIN ADC_GAIN_1
17 #define LM35_REF ADC_REF_INTERNAL
18
19 LOG_MODULE_REGISTER(LM35, CONFIG_SENSOR_LOG_LEVEL);
20
21 struct lm35_data {
22 uint16_t raw;
23 };
24
25 struct lm35_config {
26 const struct device *adc;
27 uint8_t adc_channel;
28 struct adc_sequence adc_seq;
29 struct adc_channel_cfg ch_cfg;
30 };
31
lm35_sample_fetch(const struct device * dev,enum sensor_channel chan)32 static int lm35_sample_fetch(const struct device *dev, enum sensor_channel chan)
33 {
34 if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
35 return -ENOTSUP;
36 }
37
38 const struct lm35_config *cfg = dev->config;
39
40 return adc_read(cfg->adc, &cfg->adc_seq);
41 }
42
lm35_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)43 static int lm35_channel_get(const struct device *dev, enum sensor_channel chan,
44 struct sensor_value *val)
45 {
46 if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
47 return -ENOTSUP;
48 }
49
50 int err;
51 struct lm35_data *data = dev->data;
52 const struct lm35_config *cfg = dev->config;
53 int32_t mv = data->raw;
54
55 err = adc_raw_to_millivolts(adc_ref_internal(cfg->adc), cfg->ch_cfg.gain,
56 cfg->adc_seq.resolution, &mv);
57 if (err) {
58 return err;
59 }
60
61 /* LM35 computes linearly 10mV per 1 degree C */
62 val->val1 = mv / 10;
63 val->val2 = (mv % 10) * 100000;
64
65 return 0;
66 }
67
68 static DEVICE_API(sensor, lm35_driver_api) = {
69 .sample_fetch = lm35_sample_fetch,
70 .channel_get = lm35_channel_get,
71 };
72
lm35_init(const struct device * dev)73 static int lm35_init(const struct device *dev)
74 {
75 const struct lm35_config *cfg = dev->config;
76
77 if (!device_is_ready(cfg->adc)) {
78 LOG_ERR("ADC device is not ready.");
79 return -EINVAL;
80 }
81
82 adc_channel_setup(cfg->adc, &cfg->ch_cfg);
83 return 0;
84 }
85
86 #define LM35_INST(inst) \
87 static struct lm35_data lm35_data_##inst; \
88 \
89 static const struct lm35_config lm35_cfg_##inst = { \
90 .adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(inst)), \
91 .adc_channel = DT_INST_IO_CHANNELS_INPUT(inst), \
92 .adc_seq = \
93 { \
94 .channels = BIT(DT_INST_IO_CHANNELS_INPUT(inst)), \
95 .buffer = &lm35_data_##inst.raw, \
96 .buffer_size = sizeof(lm35_data_##inst.raw), \
97 .resolution = DT_INST_PROP(inst, resolution), \
98 }, \
99 .ch_cfg = { \
100 .gain = LM35_GAIN, \
101 .reference = LM35_REF, \
102 .acquisition_time = ADC_ACQ_TIME_DEFAULT, \
103 .channel_id = DT_INST_IO_CHANNELS_INPUT(inst), \
104 }}; \
105 \
106 SENSOR_DEVICE_DT_INST_DEFINE(inst, lm35_init, NULL, &lm35_data_##inst, &lm35_cfg_##inst, \
107 POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &lm35_driver_api);
108
109 DT_INST_FOREACH_STATUS_OKAY(LM35_INST)
110