blob: f1d7060dfe1bfe6d853713e3a1f6ef05fbded308 [file] [log] [blame]
Damien George632d8ef2016-03-02 13:37:27 +00001/******************************************************************************
2 * Copyright 2013-2014 Espressif Systems (Wuxi)
3 *
4 * FileName: pwm.c
5 *
6 * Description: pwm driver
7 *
8 * Modification history:
9 * 2014/5/1, v1.0 create this file.
10 * 2016/3/2: Modifications by dpgeorge to suit MicroPython
11*******************************************************************************/
12#include <stdio.h>
13#include <string.h>
14
15#include "etshal.h"
16#include "os_type.h"
17#include "gpio.h"
18
19#include "esppwm.h"
20
21#include "py/mpprint.h"
22#define PWM_DBG(...)
23//#define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__)
24
25#define ICACHE_RAM_ATTR // __attribute__((section(".text")))
26
27#define PWM_CHANNEL 8
28#define PWM_DEPTH 1023
29#define PWM_FREQ_MAX 1000
30#define PWM_1S 1000000
31
32struct pwm_single_param {
33 uint16_t gpio_set;
34 uint16_t gpio_clear;
35 uint32_t h_time;
36};
37
38struct pwm_param {
39 uint32_t period;
40 uint16_t freq;
41 uint16_t duty[PWM_CHANNEL];
42};
43
44STATIC const uint8_t pin_num[PWM_CHANNEL] = {0, 2, 4, 5, 12, 13, 14, 15};
45
46STATIC struct pwm_single_param pwm_single_toggle[2][PWM_CHANNEL + 1];
47STATIC struct pwm_single_param *pwm_single;
48
49STATIC struct pwm_param pwm;
50
51STATIC int8_t pwm_out_io_num[PWM_CHANNEL] = {-1, -1, -1, -1, -1, -1, -1, -1};
52
53STATIC uint8_t pwm_channel_toggle[2];
54STATIC uint8_t *pwm_channel;
55STATIC uint8_t pwm_toggle = 1;
56STATIC uint8_t pwm_timer_down = 1;
57STATIC uint8_t pwm_current_channel = 0;
58STATIC uint16_t pwm_gpio = 0;
59STATIC uint8_t pwm_channel_num = 0;
60
61//XXX: 0xffffffff/(80000000/16)=35A
62#define US_TO_RTC_TIMER_TICKS(t) \
63 ((t) ? \
64 (((t) > 0x35A) ? \
65 (((t)>>2) * ((APB_CLK_FREQ>>4)/250000) + ((t)&0x3) * ((APB_CLK_FREQ>>4)/1000000)) : \
66 (((t) *(APB_CLK_FREQ>>4)) / 1000000)) : \
67 0)
68
69//FRC1
70#define FRC1_ENABLE_TIMER BIT7
71
72typedef enum {
73 DIVDED_BY_1 = 0,
74 DIVDED_BY_16 = 4,
75 DIVDED_BY_256 = 8,
76} TIMER_PREDIVED_MODE;
77
78typedef enum {
79 TM_LEVEL_INT = 1,
80 TM_EDGE_INT = 0,
81} TIMER_INT_MODE;
82
83STATIC void ICACHE_FLASH_ATTR
84pwm_insert_sort(struct pwm_single_param pwm[], uint8 n)
85{
86 uint8 i;
87
88 for (i = 1; i < n; i++) {
89 if (pwm[i].h_time < pwm[i - 1].h_time) {
90 int8 j = i - 1;
91 struct pwm_single_param tmp;
92
93 memcpy(&tmp, &pwm[i], sizeof(struct pwm_single_param));
94 memcpy(&pwm[i], &pwm[i - 1], sizeof(struct pwm_single_param));
95
96 while (tmp.h_time < pwm[j].h_time) {
97 memcpy(&pwm[j + 1], &pwm[j], sizeof(struct pwm_single_param));
98 j--;
99 if (j < 0) {
100 break;
101 }
102 }
103
104 memcpy(&pwm[j + 1], &tmp, sizeof(struct pwm_single_param));
105 }
106 }
107}
108
109STATIC volatile uint8 critical = 0;
110
111#define LOCK_PWM(c) do { \
112 while( (c)==1 ); \
113 (c) = 1; \
114} while (0)
115
116#define UNLOCK_PWM(c) do { \
117 (c) = 0; \
118} while (0)
119
120void ICACHE_FLASH_ATTR
121pwm_start(void)
122{
123 uint8 i, j;
124 PWM_DBG("--Function pwm_start() is called\n");
125 PWM_DBG("pwm_gpio:%x,pwm_channel_num:%d\n",pwm_gpio,pwm_channel_num);
126 PWM_DBG("pwm_out_io_num[0]:%d,[1]:%d,[2]:%d\n",pwm_out_io_num[0],pwm_out_io_num[1],pwm_out_io_num[2]);
127 PWM_DBG("pwm.period:%d,pwm.duty[0]:%d,[1]:%d,[2]:%d\n",pwm.period,pwm.duty[0],pwm.duty[1],pwm.duty[2]);
128
129 LOCK_PWM(critical); // enter critical
130
131 struct pwm_single_param *local_single = pwm_single_toggle[pwm_toggle ^ 0x01];
132 uint8 *local_channel = &pwm_channel_toggle[pwm_toggle ^ 0x01];
133
134 // step 1: init PWM_CHANNEL+1 channels param
135 for (i = 0; i < pwm_channel_num; i++) {
136 uint32 us = pwm.period * pwm.duty[i] / PWM_DEPTH;
137 local_single[i].h_time = US_TO_RTC_TIMER_TICKS(us);
138 PWM_DBG("i:%d us:%d ht:%d\n",i,us,local_single[i].h_time);
139 local_single[i].gpio_set = 0;
140 local_single[i].gpio_clear = 1 << pin_num[pwm_out_io_num[i]];
141 }
142
143 local_single[pwm_channel_num].h_time = US_TO_RTC_TIMER_TICKS(pwm.period);
144 local_single[pwm_channel_num].gpio_set = pwm_gpio;
145 local_single[pwm_channel_num].gpio_clear = 0;
146 PWM_DBG("i:%d period:%d ht:%d\n",pwm_channel_num,pwm.period,local_single[pwm_channel_num].h_time);
147 // step 2: sort, small to big
148 pwm_insert_sort(local_single, pwm_channel_num + 1);
149
150 *local_channel = pwm_channel_num + 1;
151 PWM_DBG("1channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time);
152 // step 3: combine same duty channels
153 for (i = pwm_channel_num; i > 0; i--) {
154 if (local_single[i].h_time == local_single[i - 1].h_time) {
155 local_single[i - 1].gpio_set |= local_single[i].gpio_set;
156 local_single[i - 1].gpio_clear |= local_single[i].gpio_clear;
157
158 for (j = i + 1; j < *local_channel; j++) {
159 memcpy(&local_single[j - 1], &local_single[j], sizeof(struct pwm_single_param));
160 }
161
162 (*local_channel)--;
163 }
164 }
165 PWM_DBG("2channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time);
166 // step 4: cacl delt time
167 for (i = *local_channel - 1; i > 0; i--) {
168 local_single[i].h_time -= local_single[i - 1].h_time;
169 }
170
171 // step 5: last channel needs to clean
172 local_single[*local_channel-1].gpio_clear = 0;
173
174 // step 6: if first channel duty is 0, remove it
175 if (local_single[0].h_time == 0) {
176 local_single[*local_channel - 1].gpio_set &= ~local_single[0].gpio_clear;
177 local_single[*local_channel - 1].gpio_clear |= local_single[0].gpio_clear;
178
179 for (i = 1; i < *local_channel; i++) {
180 memcpy(&local_single[i - 1], &local_single[i], sizeof(struct pwm_single_param));
181 }
182
183 (*local_channel)--;
184 }
185
186 // if timer is down, need to set gpio and start timer
187 if (pwm_timer_down == 1) {
188 pwm_channel = local_channel;
189 pwm_single = local_single;
190 // start
191 gpio_output_set(local_single[0].gpio_set, local_single[0].gpio_clear, pwm_gpio, 0);
192
193 // yeah, if all channels' duty is 0 or 255, don't need to start timer, otherwise start...
194 if (*local_channel != 1) {
195 pwm_timer_down = 0;
196 RTC_REG_WRITE(FRC1_LOAD_ADDRESS, local_single[0].h_time);
197 }
198 }
199
200 if (pwm_toggle == 1) {
201 pwm_toggle = 0;
202 } else {
203 pwm_toggle = 1;
204 }
205
206 UNLOCK_PWM(critical); // leave critical
207 PWM_DBG("3channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time);
208}
209
210/******************************************************************************
211 * FunctionName : pwm_set_duty
212 * Description : set each channel's duty params
213 * Parameters : uint8 duty : 0 ~ PWM_DEPTH
214 * uint8 channel : channel index
215 * Returns : NONE
216*******************************************************************************/
217void ICACHE_FLASH_ATTR
218pwm_set_duty(uint16 duty, uint8 channel)
219{
220 uint8 i;
221 for(i=0;i<pwm_channel_num;i++){
222 if(pwm_out_io_num[i] == channel){
223 channel = i;
224 break;
225 }
226 }
227 if(i==pwm_channel_num) // non found
228 return;
229
230 LOCK_PWM(critical); // enter critical
231 if (duty < 1) {
232 pwm.duty[channel] = 0;
233 } else if (duty >= PWM_DEPTH) {
234 pwm.duty[channel] = PWM_DEPTH;
235 } else {
236 pwm.duty[channel] = duty;
237 }
238 UNLOCK_PWM(critical); // leave critical
239}
240
241/******************************************************************************
242 * FunctionName : pwm_set_freq
243 * Description : set pwm frequency
244 * Parameters : uint16 freq : 100hz typically
245 * Returns : NONE
246*******************************************************************************/
247void ICACHE_FLASH_ATTR
248pwm_set_freq(uint16 freq, uint8 channel)
249{
250 LOCK_PWM(critical); // enter critical
251 if (freq > PWM_FREQ_MAX) {
252 pwm.freq = PWM_FREQ_MAX;
253 } else if (freq < 1) {
254 pwm.freq = 1;
255 } else {
256 pwm.freq = freq;
257 }
258
259 pwm.period = PWM_1S / pwm.freq;
260 UNLOCK_PWM(critical); // leave critical
261}
262
263/******************************************************************************
264 * FunctionName : pwm_get_duty
265 * Description : get duty of each channel
266 * Parameters : uint8 channel : channel index
267 * Returns : NONE
268*******************************************************************************/
269uint16 ICACHE_FLASH_ATTR
270pwm_get_duty(uint8 channel)
271{
272 uint8 i;
273 for(i=0;i<pwm_channel_num;i++){
274 if(pwm_out_io_num[i] == channel){
275 channel = i;
276 break;
277 }
278 }
279 if(i==pwm_channel_num) // non found
280 return 0;
281
282 return pwm.duty[channel];
283}
284
285/******************************************************************************
286 * FunctionName : pwm_get_freq
287 * Description : get pwm frequency
288 * Parameters : NONE
289 * Returns : uint16 : pwm frequency
290*******************************************************************************/
291uint16 ICACHE_FLASH_ATTR
292pwm_get_freq(uint8 channel)
293{
294 return pwm.freq;
295}
296
297/******************************************************************************
298 * FunctionName : pwm_period_timer
299 * Description : pwm period timer function, output high level,
300 * start each channel's high level timer
301 * Parameters : NONE
302 * Returns : NONE
303*******************************************************************************/
304STATIC void ICACHE_RAM_ATTR
Paul Sokolovskyba640bd2016-03-26 00:34:28 +0200305pwm_tim1_intr_handler(void *dummy)
Damien George632d8ef2016-03-02 13:37:27 +0000306{
Paul Sokolovskyba640bd2016-03-26 00:34:28 +0200307 (void)dummy;
Damien George632d8ef2016-03-02 13:37:27 +0000308 uint8 local_toggle = pwm_toggle; // pwm_toggle may change outside
309 RTC_CLR_REG_MASK(FRC1_INT_ADDRESS, FRC1_INT_CLR_MASK);
310
311 if (pwm_current_channel >= (*pwm_channel - 1)) { // *pwm_channel may change outside
312 pwm_single = pwm_single_toggle[local_toggle];
313 pwm_channel = &pwm_channel_toggle[local_toggle];
314
315 gpio_output_set(pwm_single[*pwm_channel - 1].gpio_set,
316 pwm_single[*pwm_channel - 1].gpio_clear,
317 pwm_gpio,
318 0);
319
320 pwm_current_channel = 0;
321
322 if (*pwm_channel != 1) {
323 RTC_REG_WRITE(FRC1_LOAD_ADDRESS, pwm_single[pwm_current_channel].h_time);
324 } else {
325 pwm_timer_down = 1;
326 }
327 } else {
328 gpio_output_set(pwm_single[pwm_current_channel].gpio_set,
329 pwm_single[pwm_current_channel].gpio_clear,
330 pwm_gpio, 0);
331
332 pwm_current_channel++;
333 RTC_REG_WRITE(FRC1_LOAD_ADDRESS, pwm_single[pwm_current_channel].h_time);
334 }
335}
336
337/******************************************************************************
338 * FunctionName : pwm_init
339 * Description : pwm gpio, params and timer initialization
340 * Parameters : uint16 freq : pwm freq param
341 * uint16 *duty : each channel's duty
342 * Returns : NONE
343*******************************************************************************/
344void ICACHE_FLASH_ATTR
345pwm_init(void)
346{
347 uint8 i;
348
349 RTC_REG_WRITE(FRC1_CTRL_ADDRESS, //FRC2_AUTO_RELOAD|
350 DIVDED_BY_16
351 | FRC1_ENABLE_TIMER
352 | TM_EDGE_INT);
353 RTC_REG_WRITE(FRC1_LOAD_ADDRESS, 0);
354
355 for (i = 0; i < PWM_CHANNEL; i++) {
356 pwm_gpio = 0;
357 pwm.duty[i] = 0;
358 }
359
360 pwm_set_freq(500, 0);
361 pwm_start();
362
363 ETS_FRC_TIMER1_INTR_ATTACH(pwm_tim1_intr_handler, NULL);
364 TM1_EDGE_INT_ENABLE();
365 ETS_FRC1_INTR_ENABLE();
366}
367
368int ICACHE_FLASH_ATTR
369pwm_add(uint8_t pin_id, uint32_t pin_mux, uint32_t pin_func){
370 PWM_DBG("--Function pwm_add() is called. channel:%d\n", channel);
371 PWM_DBG("pwm_gpio:%x,pwm_channel_num:%d\n",pwm_gpio,pwm_channel_num);
372 PWM_DBG("pwm_out_io_num[0]:%d,[1]:%d,[2]:%d\n",pwm_out_io_num[0],pwm_out_io_num[1],pwm_out_io_num[2]);
373 PWM_DBG("pwm.duty[0]:%d,[1]:%d,[2]:%d\n",pwm.duty[0],pwm.duty[1],pwm.duty[2]);
374 int channel = -1;
375 for (int i = 0; i < PWM_CHANNEL; ++i) {
376 if (pin_num[i] == pin_id) {
377 channel = i;
378 break;
379 }
380 }
381 if (channel == -1) {
382 return -1;
383 }
384 uint8 i;
385 for(i=0;i<PWM_CHANNEL;i++){
386 if(pwm_out_io_num[i]==channel) // already exist
387 return channel;
388 if(pwm_out_io_num[i] == -1){ // empty exist
389 LOCK_PWM(critical); // enter critical
390 pwm_out_io_num[i] = channel;
391 pwm.duty[i] = 0;
392 pwm_gpio |= (1 << pin_num[channel]);
393 PIN_FUNC_SELECT(pin_mux, pin_func);
394 GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[channel])), GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[channel]))) & (~ GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE))); //disable open drain;
395 pwm_channel_num++;
396 UNLOCK_PWM(critical); // leave critical
397 return channel;
398 }
399 }
400 return -1;
401}
402
403bool ICACHE_FLASH_ATTR
404pwm_delete(uint8 channel){
405 PWM_DBG("--Function pwm_delete() is called. channel:%d\n", channel);
406 PWM_DBG("pwm_gpio:%x,pwm_channel_num:%d\n",pwm_gpio,pwm_channel_num);
407 PWM_DBG("pwm_out_io_num[0]:%d,[1]:%d,[2]:%d\n",pwm_out_io_num[0],pwm_out_io_num[1],pwm_out_io_num[2]);
408 PWM_DBG("pwm.duty[0]:%d,[1]:%d,[2]:%d\n",pwm.duty[0],pwm.duty[1],pwm.duty[2]);
409 uint8 i,j;
410 for(i=0;i<pwm_channel_num;i++){
411 if(pwm_out_io_num[i]==channel){ // exist
412 LOCK_PWM(critical); // enter critical
413 pwm_out_io_num[i] = -1;
414 pwm_gpio &= ~(1 << pin_num[channel]); //clear the bit
415 for(j=i;j<pwm_channel_num-1;j++){
416 pwm_out_io_num[j] = pwm_out_io_num[j+1];
417 pwm.duty[j] = pwm.duty[j+1];
418 }
419 pwm_out_io_num[pwm_channel_num-1] = -1;
420 pwm.duty[pwm_channel_num-1] = 0;
421 pwm_channel_num--;
422 UNLOCK_PWM(critical); // leave critical
423 return true;
424 }
425 }
426 // non found
427 return true;
428}