Update benchmark projects.
[bertos.git] / bertos / drv / thermo.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2005 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief Thermo-control driver.
34  *
35  * The Thermo controll can works both with kernel or without it. In the case
36  * we use kernel, the thermo controll is done by one process that poll every
37  * CONFIG_THERMO_INTERVAL_MS the temperature sensor and make all operation to
38  * follow the target temperature. While we not use the kernel the module works
39  * with one timer interrupt in the same way of the kenel case.
40  *
41  * \author Giovanni Bajo <rasky@develer.com>
42  * \author Francesco Sacchi <batt@develer.com>
43  * \author Daniele Basile <asterix@develer.com>
44  *
45  */
46
47 #include "hw/thermo_map.h"
48 #include "hw/hw_thermo.h"
49
50 #include "cfg/cfg_thermo.h"
51
52 #include <cfg/module.h>
53 #include <cfg/macros.h>
54 #include <cfg/debug.h>
55 // Define logging setting (for cfg/log.h module).
56 #define LOG_LEVEL         CONFIG_THERMO_LOG_LEVEL
57 #define LOG_VERBOSITY     CONFIG_THERMO_LOG_FORMAT
58 #include <cfg/log.h>
59
60 #include <drv/thermo.h>
61 #include <drv/timer.h>
62 #include <drv/ntc.h>
63
64 #include <kern/proc.h>
65
66 #define THERMO_OFF          0
67 #define THERMO_HEATING      BV(0)
68 #define THERMO_FREEZING     BV(1)
69 #define THERMO_TGT_REACH    BV(2)
70 #define THERMOERRF_NTCSHORT BV(3)
71 #define THERMOERRF_NTCOPEN  BV(4)
72 #define THERMOERRF_TIMEOUT  BV(5)
73 #define THERMO_ACTIVE       BV(6)
74 #define THERMO_TIMER        BV(7)
75
76 #define THERMO_ERRMASK      (THERMOERRF_NTCSHORT | THERMOERRF_NTCOPEN | THERMOERRF_TIMEOUT)
77
78
79 #if CONFIG_KERN
80         /** Stack process for Thermo process. */
81         static PROC_DEFINE_STACK(thermo_poll_stack, 400);
82 #else
83         /** Timer for thermo-regulation. */
84         static Timer thermo_timer;
85 #endif
86
87 typedef struct ThermoControlDev
88 {
89         deg_t          hifi_samples[CONFIG_THERMO_HIFI_NUM_SAMPLES];
90         deg_t          cur_hifi_sample;
91         deg_t          target;
92         thermostatus_t status;
93         ticks_t        expire;
94         ticks_t        on_time;
95 } ThermoControlDev;
96
97 /** Array of thermo-devices. */
98 ThermoControlDev devs[THERMO_CNT];
99
100 /**
101  * Return the status of the specific \a dev thermo-device.
102  */
103 thermostatus_t thermo_status(ThermoDev dev)
104 {
105         ASSERT(dev < THERMO_CNT);
106         return devs[dev].status;
107 }
108
109
110 /**
111  * Do a single thermo control for device \a dev.
112  */
113 static void thermo_do(ThermoDev index)
114 {
115         ThermoControlDev* dev = &devs[index];
116         deg_t cur_temp;
117         deg_t tolerance = thermo_hw_tolerance(index);
118
119         cur_temp = thermo_hw_read(index);
120
121         // Store the sample into the hifi FIFO buffer for later interpolation
122         dev->hifi_samples[dev->cur_hifi_sample] = cur_temp;
123         if (++dev->cur_hifi_sample == CONFIG_THERMO_HIFI_NUM_SAMPLES)
124                 dev->cur_hifi_sample = 0;
125
126         cur_temp = thermo_readTemperature(index);
127
128         if (cur_temp == NTC_SHORT_CIRCUIT || cur_temp == NTC_OPEN_CIRCUIT)
129         {
130                 if (cur_temp == NTC_SHORT_CIRCUIT)
131                 {
132                         LOG_INFOB(if (!(dev->status & THERMOERRF_NTCSHORT))
133                                 LOG_INFO("dev[%d], thermo_do: NTC_SHORT\n",index););
134
135                         dev->status |= THERMOERRF_NTCSHORT;
136                 }
137                 else
138                 {
139
140                         LOG_INFOB(if (!(dev->status & THERMOERRF_NTCOPEN))
141                                 LOG_INFO("dev[%d], thermo_do: NTC_OPEN\n", index););
142
143                         dev->status |= THERMOERRF_NTCOPEN;
144                 }
145
146                 /* Reset timeout when there is an ntc error */
147                 dev->expire = thermo_hw_timeout(index) + timer_clock();
148                 thermo_hw_off(index);
149                 return;
150         }
151         dev->status &= ~(THERMOERRF_NTCOPEN | THERMOERRF_NTCSHORT);
152
153         if ((cur_temp < dev->target - tolerance) || (cur_temp > dev->target + tolerance))
154         {
155                 dev->status &= ~THERMO_TGT_REACH;
156
157                 /* Check for timeout */
158                 if (timer_clock() - dev->expire > 0)
159                 {
160                         dev->status |= THERMOERRF_TIMEOUT;
161                         LOG_INFO("dev[%d], thermo_do: TIMEOUT\n", index);
162                 }
163         }
164         else /* In target */
165         {
166                 /* Clear errors */
167                 dev->status &= ~THERMO_ERRMASK;
168                 dev->status |= THERMO_TGT_REACH;
169
170                 /* Reset timeout in case we go out of target in the future */
171                 dev->expire = thermo_hw_timeout(index) + timer_clock();
172         }
173
174         if (cur_temp < dev->target)
175                 dev->status = (dev->status | THERMO_HEATING) & ~THERMO_FREEZING;
176         else
177                 dev->status = (dev->status & ~THERMO_HEATING) | THERMO_FREEZING;
178
179         thermo_hw_set(index, dev->target, cur_temp);
180
181 }
182
183 static void poll(void)
184 {
185         for (int i = 0; i < THERMO_CNT; ++i)
186                 if (devs[i].status & THERMO_ACTIVE)
187                 {
188                         LOG_INFO("THERMO [%d] on_time[%ld],\n", i, ticks_to_ms(devs[i].on_time));
189                         if ((devs[i].status & THERMO_TIMER) && (devs[i].on_time - timer_clock() < 0))
190                         {
191                                 thermo_stop(i);
192                                 continue;
193                         }
194
195                         thermo_do((ThermoDev)i);
196                 }
197 }
198
199 #if CONFIG_KERN
200         static void NORETURN thermo_poll(void)
201         {
202                 for (;;)
203                 {
204                         poll();
205                         timer_delay(CONFIG_THERMO_INTERVAL_MS);
206                 }
207         }
208 #else
209         /**
210          * Thermo soft interrupt.
211          */
212         static void thermo_softint(void)
213         {
214                 poll();
215                 timer_add(&thermo_timer);
216         }
217 #endif
218
219 /**
220  * Starts a thermo-regulation for channel \a dev, and turn off timer
221  * when \a on_time was elapsed.
222  */
223 void thermo_timer(ThermoDev dev, mtime_t on_time)
224 {
225         ASSERT(dev < THERMO_CNT);
226         devs[dev].on_time = timer_clock() + ms_to_ticks(on_time);
227         devs[dev].status |= THERMO_TIMER;
228         thermo_start(dev);
229 }
230
231
232 /**
233  * Set the target temperature \a temperature for a specific \a dev thermo-device.
234  */
235 void thermo_setTarget(ThermoDev dev, deg_t temperature)
236 {
237         ASSERT(dev < THERMO_CNT);
238         devs[dev].target = temperature;
239         devs[dev].expire = timer_clock() + thermo_hw_timeout(dev);
240
241         LOG_INFO("THERMO Set Target dev[%d], T[%d.%d]\n", dev, temperature / 10, temperature % 10);
242 }
243
244 /**
245  * Starts a thermo-regulation for channel \a dev.
246  */
247 void thermo_start(ThermoDev dev)
248 {
249         int i;
250         deg_t temp;
251
252         ASSERT(dev < THERMO_CNT);
253
254         devs[dev].status |= THERMO_ACTIVE;
255         LOG_INFO("THERMO Start dev[%d], status[%04x]\n", dev, devs[dev].status);
256
257         /* Initialize the hifi FIFO with a constant value (the current temperature) */
258         temp = thermo_hw_read(dev);
259         for (i = 0; i < CONFIG_THERMO_HIFI_NUM_SAMPLES; ++i)
260                 devs[dev].hifi_samples[i] = temp;
261         devs[dev].cur_hifi_sample = 0;
262
263         /* Reset timeout */
264         devs[dev].expire = timer_clock() + thermo_hw_timeout(dev);
265 }
266
267 /**
268  * Stops a thermo-regulation for channel \a dev.
269  */
270 void thermo_stop(ThermoDev dev)
271 {
272         ASSERT(dev < THERMO_CNT);
273
274         devs[dev].status &= ~THERMO_ACTIVE;
275         thermo_hw_off(dev);
276 }
277
278
279 /**
280  * Clear errors for channel \a dev.
281  */
282 void thermo_clearErrors(ThermoDev dev)
283 {
284         ASSERT(dev < THERMO_CNT);
285         devs[dev].status &= ~(THERMO_ERRMASK);
286 }
287
288
289 /**
290  * Read the temperature of the thermo-device \a dev using mobile mean.
291  */
292 deg_t thermo_readTemperature(ThermoDev dev)
293 {
294         int i;
295         long accum = 0;
296
297         MOD_CHECK(thermo);
298
299         for (i = 0; i < CONFIG_THERMO_HIFI_NUM_SAMPLES; i++)
300                 accum += devs[dev].hifi_samples[i];
301
302         return (deg_t)(accum / CONFIG_THERMO_HIFI_NUM_SAMPLES);
303 }
304
305 MOD_DEFINE(thermo)
306
307 /**
308  * Init thermo-control and associated hw.
309  */
310 void thermo_init(void)
311 {
312         THERMO_HW_INIT;
313
314         /* Set all status to off */
315         for (int i = 0; i < THERMO_CNT; i++)
316                 devs[i].status = THERMO_OFF;
317
318         MOD_INIT(thermo);
319
320         #if CONFIG_KERN
321                 proc_new_with_name("Thermo", thermo_poll, NULL, sizeof(thermo_poll_stack), thermo_poll_stack);
322         #else
323                 timer_setDelay(&thermo_timer, ms_to_ticks(CONFIG_THERMO_INTERVAL_MS));
324                 timer_setSoftint(&thermo_timer, (Hook)thermo_softint, 0);
325                 timer_add(&thermo_timer);
326         #endif
327 }