1 /*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2016 Damien P. George
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
27 #include <stdio.h>
28
29 #include "py/nlr.h"
30 #include "py/runtime.h"
31 #include "py/mperrno.h"
32 #include "modmachine.h"
33 #include "mphalport.h"
34
35 #include "ulog/ulog.h"
36 #include "aos_hal_pwm.h"
37
38 #define LOG_TAG "machine_pwm"
39
40 enum {
41 PWN_CHANNEL_0,
42 PWN_CHANNEL_1,
43 PWN_CHANNEL_2,
44 PWN_CHANNEL_3,
45 PWN_CHANNEL_MAX
46 };
47
48 // Forward dec'l
49 extern const mp_obj_type_t machine_pwm_type;
50
51 // Params for PW operation
52 #define PWFREQ (5000)
53 #define PWDUTY (50)
54
55 typedef struct _machine_pwm_obj_t {
56 mp_obj_base_t base;
57 uint8_t id;
58 pwm_dev_t dev;
59 mp_uint_t duty_cycle;
60 mp_uint_t freq;
61 } machine_pwm_obj_t;
62
update_param(machine_pwm_obj_t * self,mp_uint_t newfreq,mp_uint_t newduty)63 STATIC int update_param(machine_pwm_obj_t* self, mp_uint_t newfreq, mp_uint_t newduty) {
64 int status = -1;
65 pwm_dev_t *dev = &self->dev;
66
67 mp_uint_t oduty = self->duty_cycle;
68 mp_uint_t ofreq = self->freq;
69
70 pwm_config_t cfg = {
71 .duty_cycle = newduty / 100.f,
72 .freq = newfreq,
73 };
74
75 status = aos_hal_pwm_para_chg(dev, cfg);
76 if (status != 0) {
77 LOGE(LOG_TAG, "aos_hal_pwm_para_chg failed, status=%d\n", status);
78 return status;
79 }
80
81 self->freq = newfreq;
82 self->duty_cycle = newduty / 100.f;
83 self->dev.config.freq = self->freq;
84 self->dev.config.duty_cycle = self->duty_cycle;
85
86 return status;
87 }
88
89 /******************************************************************************/
90
91 // MicroPython bindings for PWM
92
machine_pwm_print(const mp_print_t * print,mp_obj_t self_in,mp_print_kind_t kind)93 STATIC void machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
94 machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
95 mp_printf(print, "PWM(%u", self->id);
96 mp_printf(print, ", freq=%u, duty=%u)", self->freq, self->duty_cycle);
97 }
98
machine_pwm_init_helper(machine_pwm_obj_t * self,size_t n_args,const mp_obj_t * pos_args,mp_map_t * kw_args)99 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) {
100 enum { ARG_freq, ARG_duty };
101 static const mp_arg_t allowed_args[] = {
102 { MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} },
103 { MP_QSTR_duty, MP_ARG_INT, {.u_int = PWDUTY} },
104 };
105 mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
106 mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
107
108 pwm_dev_t *dev = &self->dev;
109
110 // Set freq and duty cycle?
111 mp_int_t freq = args[ARG_freq].u_int;
112 mp_int_t duty = args[ARG_duty].u_int;
113
114 /* if freq inited */
115 if(freq != -1) {
116 dev->config.duty_cycle = duty / 100.f;
117 dev->config.freq = freq;
118
119 int status = aos_hal_pwm_init(dev);
120 if (status != 0) {
121 LOGE(LOG_TAG, "aos_hal_pwm_init failed, status=%d\n", status);
122 return;
123 }
124
125 status = aos_hal_pwm_start(dev);
126 if (status != 0) {
127 LOGE(LOG_TAG, "aos_hal_pwm_start failed, status=%d\n", status);
128 }
129 }
130 }
131
machine_pwm_make_new(const mp_obj_type_t * type,size_t n_args,size_t n_kw,const mp_obj_t * args)132 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) {
133 mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
134
135 // get PWM id
136 mp_int_t pwm_id = mp_obj_get_int(args[0]);
137 if (pwm_id < 0 || pwm_id >= PWN_CHANNEL_MAX) {
138 mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("PWM(%d) does not exist"), pwm_id);
139 }
140
141 // create PWM object from the given pin
142 machine_pwm_obj_t *self = m_new_obj(machine_pwm_obj_t);
143 self->base.type = &machine_pwm_type;
144 self->id = pwm_id;
145 self->duty_cycle = PWDUTY / 100.f;
146 self->freq = PWFREQ;
147 self->dev.port = pwm_id;
148 self->dev.config.duty_cycle = self->duty_cycle;
149 self->dev.config.freq = self->freq;
150
151 // start the PWM running for this channel
152 mp_map_t kw_args;
153 mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
154 machine_pwm_init_helper(self, n_args - 1, args + 1, &kw_args);
155
156 return MP_OBJ_FROM_PTR(self);
157 }
158
machine_pwm_init(size_t n_args,const mp_obj_t * args,mp_map_t * kw_args)159 STATIC mp_obj_t machine_pwm_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
160 machine_pwm_init_helper(args[0], n_args - 1, args + 1, kw_args);
161 return mp_const_none;
162 }
163 MP_DEFINE_CONST_FUN_OBJ_KW(machine_pwm_init_obj, 1, machine_pwm_init);
164
machine_pwm_deinit(mp_obj_t self_in)165 STATIC mp_obj_t machine_pwm_deinit(mp_obj_t self_in) {
166 machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
167 aos_hal_pwm_stop(&self->dev);
168 aos_hal_pwm_finalize(&self->dev);
169 return mp_const_none;
170 }
171 STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pwm_deinit_obj, machine_pwm_deinit);
172
machine_pwm_freq(size_t n_args,const mp_obj_t * args)173 STATIC mp_obj_t machine_pwm_freq(size_t n_args, const mp_obj_t *args) {
174 machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
175 if (n_args == 1) {
176 // get
177 return MP_OBJ_NEW_SMALL_INT(self->freq);
178 }
179
180 // set
181 int freq = mp_obj_get_int(args[1]);
182 int status = update_param(self, freq, self->duty_cycle);
183 return MP_OBJ_NEW_SMALL_INT(status);
184 }
185
186 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_freq_obj, 1, 2, machine_pwm_freq);
187
machine_pwm_duty(size_t n_args,const mp_obj_t * args)188 STATIC mp_obj_t machine_pwm_duty(size_t n_args, const mp_obj_t *args) {
189 machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
190
191 if (n_args == 1) {
192 // get
193 return mp_obj_new_int(self->duty_cycle);
194 }
195
196 // set
197 mp_int_t duty = mp_obj_get_int(args[1]);
198 mp_int_t status = update_param(self, self->freq, duty);
199 return MP_OBJ_NEW_SMALL_INT(status);
200 }
201 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_obj, 1, 2, machine_pwm_duty);
202
machine_pwm_freqduty(size_t n_args,const mp_obj_t * args)203 STATIC mp_obj_t machine_pwm_freqduty(size_t n_args, const mp_obj_t *args) {
204 machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
205
206 mp_int_t freq = mp_obj_get_int(args[1]);
207 mp_int_t duty = mp_obj_get_int(args[2]);
208
209 int status = update_param(self, freq, duty);
210 return MP_OBJ_NEW_SMALL_INT(status);
211 }
212 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_freqduty_obj, 1, 2, machine_pwm_freqduty);
213
214 STATIC const mp_rom_map_elem_t machine_pwm_locals_dict_table[] = {
215 { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pwm_init_obj) },
216 { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_pwm_deinit_obj) },
217 { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_pwm_freq_obj) },
218 { MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&machine_pwm_duty_obj) },
219 { MP_ROM_QSTR(MP_QSTR_freqduty), MP_ROM_PTR(&machine_pwm_freqduty_obj) },
220 };
221
222 STATIC MP_DEFINE_CONST_DICT(machine_pwm_locals_dict, machine_pwm_locals_dict_table);
223
224 const mp_obj_type_t machine_pwm_type = {
225 { &mp_type_type },
226 .name = MP_QSTR_PWM,
227 .print = machine_pwm_print,
228 .make_new = machine_pwm_make_new,
229 .locals_dict = (mp_obj_dict_t *)&machine_pwm_locals_dict,
230 };
231