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