Fix a bug when reading negative temperatures.
[bertos.git] / bertos / drv / pwm.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 PWM driver (implementation)
34  *
35  *
36  * \author Francesco Sacchi <batt@develer.com>
37  * \author Daniele Basile <asterix@develer.com>
38  */
39
40 #include "cfg/cfg_pwm.h"
41
42 #include <cfg/macros.h>
43 #include <cfg/module.h>
44
45 // Define logging setting (for cfg/log.h module).
46 #define LOG_LEVEL         PWM_LOG_LEVEL
47 #define LOG_VERBOSITY     PWM_LOG_FORMAT
48
49 #include <cfg/log.h>
50 #include <cfg/debug.h>
51
52 #include <drv/pwm.h>
53
54 #include CPU_HEADER(pwm)
55
56 #include <cpu/types.h>
57 #include <cpu/irq.h>
58
59 #include <string.h>
60
61 #if CFG_PWM_ENABLE_OLD_API
62
63         /**
64          * Set duty of PWM channel \p dev.
65          * The current output frequency will be maintained.
66          * \param dev PWM channel.
67          * \param duty the new duty cycle.
68          */
69         void pwm_setDuty(PwmDev dev, pwm_duty_t duty)
70         {
71                 pwm_period_t period = 0;
72                 pwm_duty_t real_duty = 0;
73
74                 duty = MIN(duty, PWM_MAX_DUTY);
75
76                 period = pwm_hw_getPeriod(dev);
77
78                 real_duty = (uint64_t)(duty * period) >> (uint64_t)PWM_MAX_PERIOD_LOG2;
79
80                 LOG_INFO("real_duty[%d] duty[%d], period[%d]\n", real_duty, duty, period);
81                 pwm_hw_setDutyUnlock(dev, real_duty);
82         }
83
84         /**
85          * Set frequency of PWM channel \p dev at \p freq.
86          * \param dev PWM channel.
87          * \param freq new frequency, in Hz.
88          * \warning This function has to be called with PWM disabled, otherwise
89          *          the output value will be undefined.
90          *          The current duty cycle value will be lost, after calling this
91          *          function the duty cycle have to be set again.
92          */
93         void pwm_setFrequency(PwmDev dev, pwm_freq_t freq)
94         {
95                 pwm_hw_setFrequency(dev, freq);
96         }
97
98         /**
99          * Enable/Disable PWM channel \p dev.
100          * \param dev PWM channel.
101          * \param state if true the PWM on \p dev is activated, if false is disabled.
102          */
103         void pwm_enable(PwmDev dev, bool state)
104         {
105                 if (state)
106                         pwm_hw_enable(dev);
107                 else
108                         pwm_hw_disable(dev);
109         }
110
111         MOD_DEFINE(pwm);
112
113         /**
114          * Initialize the PWM driver.
115          * \warning all available PWM channels are initialized.
116          */
117         void pwm_init(void)
118         {
119                 cpu_flags_t flags;
120                 PwmDev dev;
121
122                 IRQ_SAVE_DISABLE(flags);
123
124                 pwm_hw_init();
125
126                 /* set all pwm to 0 */
127                 for (dev = 0; dev < PWM_CNT; dev++)
128                         pwm_setDuty(dev, 0);
129
130                 IRQ_RESTORE(flags);
131                 MOD_INIT(pwm);
132         }
133 #endif
134
135 #if !CFG_PWM_ENABLE_OLD_API || defined(__doxygen__)
136
137         INLINE void setRealDuty(Pwm *ctx, pwm_duty_t duty)
138         {
139                 if (ctx->pol == PWM_POL_LOW_PULSE)
140                         duty = PWM_MAX_DUTY - duty;
141
142                 pwm_hwreg_t period = pwm_hw_getPeriod(ctx);
143                 pwm_hwreg_t hw_duty;
144
145                 switch (duty)
146                 {
147                 case 0:
148                         hw_duty = 0;
149                         break;
150                 case PWM_MAX_DUTY:
151                         hw_duty = period;
152                         break;
153                 default:
154                         hw_duty = (uint64_t)(duty * period) >> (uint64_t)PWM_MAX_PERIOD_LOG2;
155                 }
156
157                 pwm_hw_setDuty(ctx, hw_duty);
158         }
159
160         /**
161          * Set the duty cycle of the PWM channel linked to \p ctx.
162          * The modification will be applied to the channel immediatly.
163          * The current frequency of the channel will be maintained.
164          * \param ctx PWM channel context.
165          * \param duty the new duty cycle value.
166          * \see pwm_duty_t
167          */
168         void pwm_setDuty(Pwm *ctx, pwm_duty_t duty)
169         {
170                 ctx->duty = duty;
171
172                 if (ctx->enabled)
173                         setRealDuty(ctx, duty);
174         }
175
176         /**
177          * Set PWM frequency of channel linked to \p ctx.
178          * The modification will be applied to the channel immediatly.
179          * The duty cycle of the channel will be maintained.
180          * \param ctx PWM channel context.
181          * \param freq the new frequency of the signal, in Hz.
182          * \note Depending on the hardware implementation, this function may
183          *       generate a glitch in the output signal upon frequency changing.
184          */
185         void pwm_setFrequency(Pwm *ctx, pwm_freq_t freq)
186         {
187                 pwm_hw_setFrequency(ctx, freq);
188                 pwm_enable(ctx, ctx->enabled);
189         }
190
191         /**
192          * Set PWM polarity of pwm channel linked to \p ctx.
193          * The modification will be applied to the channel immediatly.
194          * \param ctx PWM channel context.
195          * \param pol the new polarity of the signal.
196          *
197          * \note if a channel is disabled, changing its polarity will change the
198          *       current steady output level.
199          * \see pwm_enable
200          * \see PwmPolarity
201          */
202         void pwm_setPolarity(Pwm *ctx, PwmPolarity pol)
203         {
204                 ctx->pol = pol;
205                 pwm_enable(ctx, ctx->enabled);
206         }
207
208         /**
209          * Enable/Disable the pwm channel linked to \p ctx.
210          * The modification will be applied to the channel immediatly.
211          * \param ctx PWM channel context.
212          * \param enable if true the channel will be enabled, if false will be disabled.
213          *
214          * \note When a PWM channel is disabled, the output level will be the same
215          *       as if the duty would be set to 0%.
216          *       So, if current polarity is positive, a disabled channel will be
217          *       low, if polarity is negative will be high.
218          * \see pwm_setPolarity
219          */
220         void pwm_enable(Pwm *ctx, bool enable)
221         {
222                 ctx->enabled = enable;
223
224                 if (enable)
225                         setRealDuty(ctx, ctx->duty);
226                 else
227                         setRealDuty(ctx, 0);
228         }
229
230         /**
231          * Initialize PWM driver.
232          * \param ctx pointer to a PWM context structure, used for holding PWM
233          *            driver related information.
234          * \param channel the channel you want to initialize.
235          * \note The channel will be initialized disabled and with High polarity.
236          */
237         void pwm_init(Pwm *ctx, unsigned channel)
238         {
239                 memset(ctx, 0, sizeof(*ctx));
240                 ctx->ch = channel;
241
242                 pwm_hw_init(ctx, channel);
243         }
244
245 #endif
246
247