1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2018-05-07     aozima       the first version
9  * 2018-11-16     Ernest Chen  add finsh command and update adc function
10  * 2022-05-11     Stanley Lwin add finsh voltage conversion command
11  */
12 
13 #include <rtthread.h>
14 #include <rtdevice.h>
15 
16 #include <string.h>
17 #include <stdlib.h>
18 
19 #define DBG_TAG "adc"
20 #define DBG_LVL DBG_INFO
21 #include <rtdbg.h>
22 
_adc_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)23 static rt_ssize_t _adc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
24 {
25     rt_err_t              result;
26     rt_size_t             i;
27     struct rt_adc_device *adc   = (struct rt_adc_device *)dev;
28     rt_uint32_t          *value = (rt_uint32_t *)buffer;
29 
30     for (i = 0; i < size; i++)
31     {
32         result = adc->ops->convert(adc, pos, value);
33         if (result != RT_EOK)
34         {
35             return 0;
36         }
37         value++;
38     }
39 
40     return i;
41 }
42 
_adc_control(rt_device_t dev,int cmd,void * args)43 static rt_err_t _adc_control(rt_device_t dev, int cmd, void *args)
44 {
45     rt_adc_device_t adc = (struct rt_adc_device *)dev;
46     rt_err_t        result = RT_ERROR;
47 
48     if (cmd == RT_ADC_CMD_ENABLE && adc->ops->enabled)
49     {
50         result = adc->ops->enabled(adc, (rt_int8_t)(rt_base_t)args, RT_TRUE);
51     }
52     else if (cmd == RT_ADC_CMD_DISABLE && adc->ops->enabled)
53     {
54         result = adc->ops->enabled(adc, (rt_int8_t)(rt_base_t)args, RT_FALSE);
55     }
56     else if (cmd == RT_ADC_CMD_GET_RESOLUTION && adc->ops->get_resolution && args)
57     {
58         rt_uint8_t resolution = adc->ops->get_resolution(adc);
59         if (resolution != 0)
60         {
61             *((rt_uint8_t *)args) = resolution;
62             LOG_D("resolution: %d bits", resolution);
63             result = RT_EOK;
64         }
65     }
66     else if (cmd == RT_ADC_CMD_GET_VREF && adc->ops->get_vref && args)
67     {
68         rt_int16_t value = adc->ops->get_vref(adc);
69         if (value != 0)
70         {
71             *((rt_int16_t *)args) = value;
72             result                = RT_EOK;
73         }
74     }
75 
76     return result;
77 }
78 
79 #ifdef RT_USING_DEVICE_OPS
80 const static struct rt_device_ops adc_ops =
81     {
82         RT_NULL,
83         RT_NULL,
84         RT_NULL,
85         _adc_read,
86         RT_NULL,
87         _adc_control,
88 };
89 #endif
90 
rt_hw_adc_register(rt_adc_device_t device,const char * name,const struct rt_adc_ops * ops,const void * user_data)91 rt_err_t rt_hw_adc_register(rt_adc_device_t device, const char *name, const struct rt_adc_ops *ops, const void *user_data)
92 {
93     RT_ASSERT(ops != RT_NULL && ops->convert != RT_NULL);
94     rt_err_t result;
95 
96     device->parent.type        = RT_Device_Class_ADC;
97     device->parent.rx_indicate = RT_NULL;
98     device->parent.tx_complete = RT_NULL;
99 
100 #ifdef RT_USING_DEVICE_OPS
101     device->parent.ops = &adc_ops;
102 #else
103     device->parent.init    = RT_NULL;
104     device->parent.open    = RT_NULL;
105     device->parent.close   = RT_NULL;
106     device->parent.read    = _adc_read;
107     device->parent.write   = RT_NULL;
108     device->parent.control = _adc_control;
109 #endif
110     device->ops              = ops;
111     device->parent.user_data = (void *)user_data;
112 
113     result = rt_device_register(&device->parent, name, RT_DEVICE_FLAG_RDWR);
114 
115     return result;
116 }
117 
rt_adc_read(rt_adc_device_t dev,rt_int8_t channel)118 rt_uint32_t rt_adc_read(rt_adc_device_t dev, rt_int8_t channel)
119 {
120     RT_ASSERT(dev);
121     rt_uint32_t value;
122     rt_err_t    result;
123 
124     result = dev->ops->convert(dev, channel, &value);
125     if (result != RT_EOK)
126         return 0;
127 
128     return value;
129 }
130 
rt_adc_enable(rt_adc_device_t dev,rt_int8_t channel)131 rt_err_t rt_adc_enable(rt_adc_device_t dev, rt_int8_t channel)
132 {
133     RT_ASSERT(dev);
134     rt_err_t result;
135 
136     if (dev->ops->enabled != RT_NULL)
137     {
138         result = dev->ops->enabled(dev, channel, RT_TRUE);
139     }
140     else
141     {
142         result = -RT_ENOSYS;
143     }
144 
145     return result;
146 }
147 
rt_adc_disable(rt_adc_device_t dev,rt_int8_t channel)148 rt_err_t rt_adc_disable(rt_adc_device_t dev, rt_int8_t channel)
149 {
150     RT_ASSERT(dev);
151     rt_err_t result;
152 
153     if (dev->ops->enabled != RT_NULL)
154     {
155         result = dev->ops->enabled(dev, channel, RT_FALSE);
156     }
157     else
158     {
159         result = -RT_ENOSYS;
160     }
161 
162     return result;
163 }
164 
rt_adc_voltage(rt_adc_device_t dev,rt_int8_t channel)165 rt_int16_t rt_adc_voltage(rt_adc_device_t dev, rt_int8_t channel)
166 {
167     RT_ASSERT(dev);
168 
169     rt_uint32_t value;
170     rt_int16_t  vref, voltage;
171     rt_uint8_t  resolution;
172     rt_err_t    result;
173 
174     /*get the resolution in bits*/
175     resolution = dev->ops->get_resolution(dev);
176     /*get the reference voltage*/
177     vref = dev->ops->get_vref(dev);
178     if (vref == 0)
179         goto _voltage_exit;
180 
181     /*read the value and convert to voltage*/
182     result = dev->ops->enabled(dev, channel, RT_TRUE);
183     if (result != RT_EOK)
184         goto _voltage_exit;
185     result = dev->ops->convert(dev, channel, &value);
186     if (result != RT_EOK)
187         goto _voltage_exit;
188     result = dev->ops->enabled(dev, channel, RT_FALSE);
189     if (result != RT_EOK)
190         goto _voltage_exit;
191     voltage = value * vref / ((1 << resolution) - 1);
192 
193 _voltage_exit:
194     return voltage;
195 }
196 
197 #ifdef RT_USING_FINSH
198 
adc(int argc,char ** argv)199 static int adc(int argc, char **argv)
200 {
201     int                    value      = 0;
202     rt_int16_t             voltage    = 0;
203     rt_err_t               result     = -RT_ERROR;
204     static rt_adc_device_t adc_device = RT_NULL;
205     char                  *result_str;
206 
207     if (argc > 1)
208     {
209         if (!strcmp(argv[1], "probe"))
210         {
211             if (argc == 3)
212             {
213                 adc_device = (rt_adc_device_t)rt_device_find(argv[2]);
214                 result_str = (adc_device == RT_NULL) ? "failure" : "success";
215                 rt_kprintf("probe %s %s \n", argv[2], result_str);
216             }
217             else
218             {
219                 rt_kprintf("adc probe <device name>   - probe adc by name\n");
220             }
221         }
222         else
223         {
224             if (adc_device == RT_NULL)
225             {
226                 rt_kprintf("Please using 'adc probe <device name>' first\n");
227                 return -RT_ERROR;
228             }
229             if (!strcmp(argv[1], "enable"))
230             {
231                 if (argc == 3)
232                 {
233                     result     = rt_adc_enable(adc_device, atoi(argv[2]));
234                     result_str = (result == RT_EOK) ? "success" : "failure";
235                     rt_kprintf("%s channel %d enables %s \n", adc_device->parent.parent.name, (rt_base_t)atoi(argv[2]), result_str);
236                 }
237                 else
238                 {
239                     rt_kprintf("adc enable <channel>   - enable adc channel\n");
240                 }
241             }
242             else if (!strcmp(argv[1], "read"))
243             {
244                 if (argc == 3)
245                 {
246                     value = rt_adc_read(adc_device, atoi(argv[2]));
247                     rt_kprintf("%s channel %d  read value is 0x%08X \n", adc_device->parent.parent.name, (rt_base_t)atoi(argv[2]), value);
248                 }
249                 else
250                 {
251                     rt_kprintf("adc read <channel>     - read adc value on the channel\n");
252                 }
253             }
254             else if (!strcmp(argv[1], "disable"))
255             {
256                 if (argc == 3)
257                 {
258                     result     = rt_adc_disable(adc_device, atoi(argv[2]));
259                     result_str = (result == RT_EOK) ? "success" : "failure";
260                     rt_kprintf("%s channel %d disable %s \n", adc_device->parent.parent.name, (rt_base_t)atoi(argv[2]), result_str);
261                 }
262                 else
263                 {
264                     rt_kprintf("adc disable <channel>  - disable adc channel\n");
265                 }
266             }
267             else if (!strcmp(argv[1], "voltage"))
268             {
269                 if (argc == 3)
270                 {
271                     voltage = rt_adc_voltage(adc_device, atoi(argv[2]));
272                     rt_kprintf("%s channel %d voltage is %d.%03dV \n", adc_device->parent.parent.name, (rt_base_t)atoi(argv[2]), voltage / 1000, voltage % 1000);
273                 }
274                 else
275                 {
276                     rt_kprintf("adc convert voltage <channel> \n");
277                 }
278             }
279             else
280             {
281                 rt_kprintf("Unknown command. Please enter 'adc' for help\n");
282             }
283         }
284     }
285     else
286     {
287         rt_kprintf("Usage: \n");
288         rt_kprintf("adc probe <device name> - probe adc by name\n");
289         rt_kprintf("adc read <channel>      - read adc value on the channel\n");
290         rt_kprintf("adc disable <channel>   - disable adc channel\n");
291         rt_kprintf("adc enable <channel>    - enable adc channel\n");
292         rt_kprintf("adc voltage <channel>   - read voltage on the channel\n");
293 
294         result = -RT_ERROR;
295     }
296     return RT_EOK;
297 }
298 MSH_CMD_EXPORT(adc, adc [option]);
299 
300 #endif /* RT_USING_FINSH */
301