Skip to content

Commit f4a7e71

Browse files
Ayush1325dpgeorge
authored andcommitted
zephyr/machine_pwm: Implement PWM support.
Implement PWM support using standard zephyr APIs, exposed as the standard MicroPython `machine.PWM` class. Signed-off-by: Ayush Singh <[email protected]>
1 parent 3c8d1b1 commit f4a7e71

File tree

3 files changed

+208
-0
lines changed

3 files changed

+208
-0
lines changed

ports/zephyr/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Features supported at this time:
1919
* `machine.Pin` class for GPIO control, with IRQ support.
2020
* `machine.I2C` class for I2C control.
2121
* `machine.SPI` class for SPI control.
22+
* `machine.PWM` class for PWM control
2223
* `socket` module for networking (IPv4/IPv6).
2324
* "Frozen modules" support to allow to bundle Python modules together
2425
with firmware. Including complete applications, including with

ports/zephyr/machine_pwm.c

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2024 Ayush Singh <[email protected]>
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 "extmod/modmachine.h"
28+
#include "py/runtime.h"
29+
#include "stdint.h"
30+
#include "zephyr/drivers/pwm.h"
31+
#include "zephyr_device.h"
32+
33+
#define VALUE_NOT_SET (-1)
34+
35+
typedef struct _machine_pwm_obj_t {
36+
mp_obj_base_t base;
37+
const struct device *pwm;
38+
uint32_t channel;
39+
int32_t freq;
40+
int32_t duty_u16;
41+
int32_t duty_ns;
42+
pwm_flags_t flags;
43+
} machine_pwm_obj_t;
44+
45+
static void configure_pwm(machine_pwm_obj_t *self) {
46+
const uint32_t period = NSEC_PER_SEC / self->freq;
47+
48+
if ((self->duty_u16 == VALUE_NOT_SET && self->duty_ns == VALUE_NOT_SET) ||
49+
self->freq == VALUE_NOT_SET) {
50+
mp_raise_ValueError(MP_ERROR_TEXT("Frequency and duty values must be set"));
51+
}
52+
53+
if (self->duty_ns == VALUE_NOT_SET) {
54+
self->duty_ns = (self->duty_u16 * period) / UINT16_MAX;
55+
}
56+
57+
if (self->duty_ns < 0) {
58+
self->duty_ns = 0;
59+
} else if (self->duty_ns > period) {
60+
self->duty_ns = period;
61+
}
62+
63+
pwm_set(self->pwm, self->channel, period, self->duty_ns, self->flags);
64+
}
65+
66+
/******************************************************************************/
67+
// MicroPython bindings for PWM
68+
69+
static void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in,
70+
mp_print_kind_t kind) {
71+
machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
72+
mp_printf(print, "<PWM Controller %p channel=%d ", self->pwm, self->channel);
73+
74+
if (self->duty_ns != VALUE_NOT_SET) {
75+
mp_printf(print, " duty_ns=%d", self->duty_ns);
76+
} else {
77+
mp_printf(print, " duty_u16=%d", self->duty_u16);
78+
}
79+
80+
mp_printf(print, " freq=%d, flags=%u>", self->freq, self->flags);
81+
}
82+
83+
// This called from pwm.init() method
84+
static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args,
85+
const mp_obj_t *pos_args,
86+
mp_map_t *kw_args) {
87+
88+
enum {
89+
ARG_freq,
90+
ARG_duty_u16,
91+
ARG_duty_ns,
92+
ARG_invert,
93+
};
94+
static const mp_arg_t allowed_args[] = {
95+
{MP_QSTR_freq, MP_ARG_INT, {.u_int = VALUE_NOT_SET}},
96+
{MP_QSTR_duty_u16, MP_ARG_INT, {.u_int = VALUE_NOT_SET}},
97+
{MP_QSTR_duty_ns, MP_ARG_INT, {.u_int = VALUE_NOT_SET}},
98+
{MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1}},
99+
};
100+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
101+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args),
102+
allowed_args, args);
103+
104+
// Maybe change PWM timer
105+
if (args[ARG_freq].u_int > 0) {
106+
self->freq = args[ARG_freq].u_int;
107+
}
108+
109+
// Set duty_u16 cycle?
110+
int32_t duty = args[ARG_duty_u16].u_int;
111+
if (duty >= 0) {
112+
self->duty_u16 = duty;
113+
self->duty_ns = VALUE_NOT_SET;
114+
}
115+
// Set duty_ns value?
116+
duty = args[ARG_duty_ns].u_int;
117+
if (duty >= 0) {
118+
self->duty_ns = duty;
119+
self->duty_u16 = VALUE_NOT_SET;
120+
}
121+
122+
self->flags = 0;
123+
if (args[ARG_invert].u_int >= 0) {
124+
self->flags |= PWM_POLARITY_INVERTED;
125+
}
126+
127+
configure_pwm(self);
128+
}
129+
130+
// PWM(pin-tuple, freq, [args])
131+
static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type,
132+
size_t n_args, size_t n_kw,
133+
const mp_obj_t *args) {
134+
mp_obj_t *items;
135+
uint32_t wanted_chan;
136+
const struct device *wanted_pwm;
137+
138+
// Check number of arguments
139+
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
140+
141+
// Get referred Pin object(s)
142+
if (!mp_obj_is_type(args[0], &mp_type_tuple)) {
143+
mp_raise_ValueError(
144+
MP_ERROR_TEXT("Pin id must be tuple of (\"pwm_x\", channel#)"));
145+
}
146+
147+
mp_obj_get_array_fixed_n(args[0], 2, &items);
148+
wanted_pwm = zephyr_device_find(items[0]);
149+
wanted_chan = mp_obj_get_int(items[1]);
150+
151+
machine_pwm_obj_t *pwm = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type);
152+
pwm->pwm = wanted_pwm;
153+
pwm->channel = wanted_chan;
154+
pwm->duty_ns = VALUE_NOT_SET;
155+
pwm->duty_u16 = VALUE_NOT_SET;
156+
pwm->freq = VALUE_NOT_SET;
157+
pwm->flags = 0;
158+
159+
if (n_args > 1 || n_kw > 0) {
160+
// pin mode given, so configure this GPIO
161+
mp_map_t kw_args;
162+
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
163+
mp_machine_pwm_init_helper(pwm, n_args - 1, args + 1, &kw_args);
164+
}
165+
166+
return (mp_obj_t)pwm;
167+
}
168+
169+
// This called from pwm.deinit() method
170+
static void mp_machine_pwm_deinit(machine_pwm_obj_t *self) {
171+
self->duty_ns = 0;
172+
self->duty_u16 = VALUE_NOT_SET;
173+
configure_pwm(self);
174+
}
175+
176+
static mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) {
177+
return MP_OBJ_NEW_SMALL_INT(self->freq);
178+
}
179+
180+
static void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) {
181+
self->freq = freq;
182+
configure_pwm(self);
183+
}
184+
185+
static void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self,
186+
mp_int_t duty_ns) {
187+
self->duty_ns = duty_ns;
188+
self->duty_u16 = VALUE_NOT_SET;
189+
configure_pwm(self);
190+
}
191+
192+
static mp_obj_t mp_machine_pwm_duty_get_ns(machine_pwm_obj_t *self) {
193+
return MP_OBJ_NEW_SMALL_INT(self->duty_ns);
194+
}
195+
196+
static mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) {
197+
return MP_OBJ_NEW_SMALL_INT(self->duty_u16);
198+
}
199+
200+
static void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self,
201+
mp_int_t duty_u16) {
202+
self->duty_ns = VALUE_NOT_SET;
203+
self->duty_u16 = duty_u16;
204+
configure_pwm(self);
205+
}

ports/zephyr/mpconfigport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@
7272
#define MICROPY_PY_MACHINE_WDT (1)
7373
#define MICROPY_PY_MACHINE_WDT_INCLUDEFILE "ports/zephyr/machine_wdt.c"
7474
#endif
75+
#define MICROPY_PY_MACHINE_PWM (1)
76+
#define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/zephyr/machine_pwm.c"
7577
#define MICROPY_PY_STRUCT (0)
7678
#ifdef CONFIG_NETWORKING
7779
// If we have networking, we likely want errno comfort

0 commit comments

Comments
 (0)