1 /*
2  * Copyright (C) 2015-2021 Alibaba Group Holding Limited
3  */
4 
5 #include <stdio.h>
6 
7 #include "aos_hal_pwm.h"
8 #include "modmachine.h"
9 #include "mphalport.h"
10 #include "py/mperrno.h"
11 #include "py/nlr.h"
12 #include "py/runtime.h"
13 #include "ulog/ulog.h"
14 
15 #define LOG_TAG "machine_pwm"
16 
17 enum {
18     PWN_CHANNEL_0,
19     PWN_CHANNEL_1,
20     PWN_CHANNEL_2,
21     PWN_CHANNEL_3,
22     PWN_CHANNEL_MAX
23 };
24 
25 // Forward dec'l
26 extern const mp_obj_type_t machine_pwm_type;
27 
28 // Params for PW operation
29 #define PWFREQ (5000)
30 #define PWDUTY (50)
31 
32 typedef struct _machine_pwm_obj_t {
33     mp_obj_base_t base;
34     uint8_t id;
35     pwm_dev_t dev;
36     mp_uint_t duty_cycle;
37     mp_uint_t freq;
38 } machine_pwm_obj_t;
39 
update_param(machine_pwm_obj_t * self,mp_uint_t newfreq,mp_uint_t newduty)40 STATIC int update_param(machine_pwm_obj_t *self, mp_uint_t newfreq, mp_uint_t newduty)
41 {
42     int status = -1;
43     pwm_dev_t *dev = &self->dev;
44 
45     mp_uint_t oduty = self->duty_cycle;
46     mp_uint_t ofreq = self->freq;
47 
48     pwm_config_t cfg = {
49         .duty_cycle = newduty / 100.f,
50         .freq = newfreq,
51     };
52 
53     status = aos_hal_pwm_para_chg(dev, cfg);
54     if (status != 0) {
55         LOGE(LOG_TAG, "aos_hal_pwm_para_chg failed, status=%d\n", status);
56         return status;
57     }
58 
59     self->freq = newfreq;
60     self->duty_cycle = newduty / 100.f;
61     self->dev.config.freq = self->freq;
62     self->dev.config.duty_cycle = self->duty_cycle;
63 
64     return status;
65 }
66 
67 /******************************************************************************/
68 
69 // MicroPython bindings for PWM
70 
machine_pwm_print(const mp_print_t * print,mp_obj_t self_in,mp_print_kind_t kind)71 STATIC void machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind)
72 {
73     machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
74     mp_printf(print, "PWM(%u", self->id);
75     mp_printf(print, ", freq=%u, duty=%u)", self->freq, self->duty_cycle);
76 }
77 
machine_pwm_init_helper(machine_pwm_obj_t * self,size_t n_args,const mp_obj_t * pos_args,mp_map_t * kw_args)78 STATIC void machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
79 {
80     enum {
81         ARG_freq,
82         ARG_duty
83     };
84     static const mp_arg_t allowed_args[] = {
85         { MP_QSTR_freq, MP_ARG_INT, { .u_int = -1 } },
86         { MP_QSTR_duty, MP_ARG_INT, { .u_int = PWDUTY } },
87     };
88     mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
89     mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
90 
91     pwm_dev_t *dev = &self->dev;
92 
93     // Set freq and duty cycle?
94     mp_int_t freq = args[ARG_freq].u_int;
95     mp_int_t duty = args[ARG_duty].u_int;
96 
97     /* if freq inited */
98     if (freq != -1) {
99         dev->config.duty_cycle = duty / 100.f;
100         dev->config.freq = freq;
101 
102         int status = aos_hal_pwm_init(dev);
103         if (status != 0) {
104             LOGE(LOG_TAG, "aos_hal_pwm_init failed, status=%d\n", status);
105             return;
106         }
107 
108         status = aos_hal_pwm_start(dev);
109         if (status != 0) {
110             LOGE(LOG_TAG, "aos_hal_pwm_start failed, status=%d\n", status);
111         }
112     }
113 }
114 
machine_pwm_make_new(const mp_obj_type_t * type,size_t n_args,size_t n_kw,const mp_obj_t * args)115 STATIC mp_obj_t machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args)
116 {
117     mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
118 
119     // get PWM id
120     mp_int_t pwm_id = mp_obj_get_int(args[0]);
121     if (pwm_id < 0 || pwm_id >= PWN_CHANNEL_MAX) {
122         mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("PWM(%d) does not exist"), pwm_id);
123     }
124 
125     // create PWM object from the given pin
126     machine_pwm_obj_t *self = m_new_obj(machine_pwm_obj_t);
127     self->base.type = &machine_pwm_type;
128     self->id = pwm_id;
129     self->duty_cycle = PWDUTY / 100.f;
130     self->freq = PWFREQ;
131     self->dev.port = pwm_id;
132     self->dev.config.duty_cycle = self->duty_cycle;
133     self->dev.config.freq = self->freq;
134 
135     // start the PWM running for this channel
136     mp_map_t kw_args;
137     mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
138     machine_pwm_init_helper(self, n_args - 1, args + 1, &kw_args);
139 
140     return MP_OBJ_FROM_PTR(self);
141 }
142 
machine_pwm_init(size_t n_args,const mp_obj_t * args,mp_map_t * kw_args)143 STATIC mp_obj_t machine_pwm_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args)
144 {
145     machine_pwm_init_helper(args[0], n_args - 1, args + 1, kw_args);
146     return mp_const_none;
147 }
148 MP_DEFINE_CONST_FUN_OBJ_KW(machine_pwm_init_obj, 1, machine_pwm_init);
149 
machine_pwm_deinit(mp_obj_t self_in)150 STATIC mp_obj_t machine_pwm_deinit(mp_obj_t self_in)
151 {
152     machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
153     aos_hal_pwm_stop(&self->dev);
154     aos_hal_pwm_finalize(&self->dev);
155     return mp_const_none;
156 }
157 STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pwm_deinit_obj, machine_pwm_deinit);
158 
machine_pwm_freq(size_t n_args,const mp_obj_t * args)159 STATIC mp_obj_t machine_pwm_freq(size_t n_args, const mp_obj_t *args)
160 {
161     machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
162     if (n_args == 1) {
163         // get
164         return MP_OBJ_NEW_SMALL_INT(self->freq);
165     }
166 
167     // set
168     int freq = mp_obj_get_int(args[1]);
169     int status = update_param(self, freq, self->duty_cycle);
170     return MP_OBJ_NEW_SMALL_INT(status);
171 }
172 
173 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_freq_obj, 1, 2, machine_pwm_freq);
174 
machine_pwm_duty(size_t n_args,const mp_obj_t * args)175 STATIC mp_obj_t machine_pwm_duty(size_t n_args, const mp_obj_t *args)
176 {
177     machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
178 
179     if (n_args == 1) {
180         // get
181         return mp_obj_new_int(self->duty_cycle);
182     }
183 
184     // set
185     mp_int_t duty = mp_obj_get_int(args[1]);
186     mp_int_t status = update_param(self, self->freq, duty);
187     return MP_OBJ_NEW_SMALL_INT(status);
188 }
189 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_obj, 1, 2, machine_pwm_duty);
190 
machine_pwm_freqduty(size_t n_args,const mp_obj_t * args)191 STATIC mp_obj_t machine_pwm_freqduty(size_t n_args, const mp_obj_t *args)
192 {
193     machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
194 
195     mp_int_t freq = mp_obj_get_int(args[1]);
196     mp_int_t duty = mp_obj_get_int(args[2]);
197 
198     int status = update_param(self, freq, duty);
199     return MP_OBJ_NEW_SMALL_INT(status);
200 }
201 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_freqduty_obj, 1, 2, machine_pwm_freqduty);
202 
203 STATIC const mp_rom_map_elem_t machine_pwm_locals_dict_table[] = {
204     { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pwm_init_obj) },
205     { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_pwm_deinit_obj) },
206     { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_pwm_freq_obj) },
207     { MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&machine_pwm_duty_obj) },
208     { MP_ROM_QSTR(MP_QSTR_freqduty), MP_ROM_PTR(&machine_pwm_freqduty_obj) },
209 };
210 
211 STATIC MP_DEFINE_CONST_DICT(machine_pwm_locals_dict, machine_pwm_locals_dict_table);
212 
213 const mp_obj_type_t machine_pwm_type = {
214     { &mp_type_type },
215     .name = MP_QSTR_PWM,
216     .print = machine_pwm_print,
217     .make_new = machine_pwm_make_new,
218     .locals_dict = (mp_obj_dict_t *)&machine_pwm_locals_dict,
219 };
220