354 lines
13 KiB
C
354 lines
13 KiB
C
/*
|
|
* This file is part of the MicroPython project, http://micropython.org/
|
|
*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2016 Damien P. George
|
|
* Copyright (c) 2023 Vekatech Ltd.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
// This file is never compiled standalone, it's included directly from
|
|
// extmod/machine_pwm.c via MICROPY_PY_MACHINE_PWM_INCLUDEFILE.
|
|
|
|
#include "py/mphal.h"
|
|
#include "py/mperrno.h"
|
|
#include "pin.h"
|
|
#include "ra/ra_gpt.h"
|
|
|
|
typedef struct _machine_pwm_obj_t {
|
|
mp_obj_base_t base;
|
|
R_GPT0_Type *pwm_inst;
|
|
uint8_t active;
|
|
uint8_t ch;
|
|
uint8_t id;
|
|
uint8_t duty;
|
|
uint32_t freq;
|
|
mp_hal_pin_obj_t pwm;
|
|
} machine_pwm_obj_t;
|
|
|
|
STATIC machine_pwm_obj_t machine_pwm_obj[] = {
|
|
#if defined(MICROPY_HW_PWM_0A)
|
|
{{&machine_pwm_type}, R_GPT0, 0, 0, 'A', 0, 0ul, MICROPY_HW_PWM_0A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_0B)
|
|
{{&machine_pwm_type}, R_GPT0, 0, 0, 'B', 0, 0ul, MICROPY_HW_PWM_0B},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_1A)
|
|
{{&machine_pwm_type}, R_GPT1, 0, 1, 'A', 0, 0ul, MICROPY_HW_PWM_1A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_1B)
|
|
{{&machine_pwm_type}, R_GPT1, 0, 1, 'B', 0, 0ul, MICROPY_HW_PWM_1B},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_2A)
|
|
{{&machine_pwm_type}, R_GPT2, 0, 2, 'A', 0, 0ul, MICROPY_HW_PWM_2A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_2B)
|
|
{{&machine_pwm_type}, R_GPT2, 0, 2, 'B', 0, 0ul, MICROPY_HW_PWM_2B},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_3A)
|
|
{{&machine_pwm_type}, R_GPT3, 0, 3, 'A', 0, 0ul, MICROPY_HW_PWM_3A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_3B)
|
|
{{&machine_pwm_type}, R_GPT3, 0, 3, 'B', 0, 0ul, MICROPY_HW_PWM_3B},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_4A)
|
|
{{&machine_pwm_type}, R_GPT4, 0, 4, 'A', 0, 0ul, MICROPY_HW_PWM_4A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_4B)
|
|
{{&machine_pwm_type}, R_GPT4, 0, 4, 'B', 0, 0ul, MICROPY_HW_PWM_4B},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_5A)
|
|
{{&machine_pwm_type}, R_GPT5, 0, 5, 'A', 0, 0ul, MICROPY_HW_PWM_5A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_5B)
|
|
{{&machine_pwm_type}, R_GPT5, 0, 5, 'B', 0, 0ul, MICROPY_HW_PWM_5B},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_6A)
|
|
{{&machine_pwm_type}, R_GPT6, 0, 6, 'A', 0, 0ul, MICROPY_HW_PWM_6A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_6B)
|
|
{{&machine_pwm_type}, R_GPT6, 0, 6, 'B', 0, 0ul, MICROPY_HW_PWM_6B},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_7A)
|
|
{{&machine_pwm_type}, R_GPT7, 0, 7, 'A', 0, 0ul, MICROPY_HW_PWM_7A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_7B)
|
|
{{&machine_pwm_type}, R_GPT7, 0, 7, 'B', 0, 0ul, MICROPY_HW_PWM_7B},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_8A)
|
|
{{&machine_pwm_type}, R_GPT8, 0, 8, 'A', 0, 0ul, MICROPY_HW_PWM_8A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_8B)
|
|
{{&machine_pwm_type}, R_GPT8, 0, 8, 'B', 0, 0ul, MICROPY_HW_PWM_8B},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_9A)
|
|
{{&machine_pwm_type}, R_GPT9, 0, 9, 'A', 0, 0ul, MICROPY_HW_PWM_9A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_9B)
|
|
{{&machine_pwm_type}, R_GPT9, 0, 9, 'B', 0, 0ul, MICROPY_HW_PWM_9B},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_10A)
|
|
{{&machine_pwm_type}, R_GPT10, 0, 10, 'A', 0, 0ul, MICROPY_HW_PWM_10A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_10B)
|
|
{{&machine_pwm_type}, R_GPT10, 0, 10, 'B', 0, 0ul, MICROPY_HW_PWM_10B},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_11A)
|
|
{{&machine_pwm_type}, R_GPT11, 0, 11, 'A', 0, 0ul, MICROPY_HW_PWM_11A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_11B)
|
|
{{&machine_pwm_type}, R_GPT11, 0, 11, 'B', 0, 0ul, MICROPY_HW_PWM_11B},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_12A)
|
|
{{&machine_pwm_type}, R_GPT12, 0, 12, 'A', 0, 0ul, MICROPY_HW_PWM_12A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_12B)
|
|
{{&machine_pwm_type}, R_GPT12, 0, 12, 'B', 0, 0ul, MICROPY_HW_PWM_12B},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_13A)
|
|
{{&machine_pwm_type}, R_GPT13, 0, 13, 'A', 0, 0ul, MICROPY_HW_PWM_13A},
|
|
#endif
|
|
#if defined(MICROPY_HW_PWM_13B)
|
|
{{&machine_pwm_type}, R_GPT13, 0, 13, 'B', 0, 0ul, MICROPY_HW_PWM_13B}
|
|
#endif
|
|
};
|
|
|
|
/******************************************************************************/
|
|
// MicroPython bindings for PWM
|
|
|
|
STATIC void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
|
machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
|
mp_printf(print, "PWM(GTIOC %d%c[#%d], runing=%u, freq=%u, duty=%u)", self->ch, self->id, self->pwm->pin, self->active, self->freq, self->duty);
|
|
}
|
|
|
|
STATIC void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
|
uint32_t D = 0ul;
|
|
|
|
enum { ARG_freq, ARG_duty };
|
|
static const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} },
|
|
{ MP_QSTR_duty, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }
|
|
};
|
|
|
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
|
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
|
|
|
if (args[ARG_freq].u_int != -1) {
|
|
if ((args[ARG_freq].u_int < 0) || (args[ARG_freq].u_int > 24000000)) {
|
|
mp_raise_ValueError(MP_ERROR_TEXT("freq should be 0-24000000"));
|
|
} else {
|
|
self->freq = args[ARG_freq].u_int;
|
|
}
|
|
}
|
|
|
|
ra_gpt_timer_init(self->pwm->pin, self->ch, self->id, 0, (float)self->freq);
|
|
|
|
if (args[ARG_duty].u_int != -1) {
|
|
if ((args[ARG_duty].u_int < 0) || (args[ARG_duty].u_int > 100)) {
|
|
mp_raise_ValueError(MP_ERROR_TEXT("duty should be 0-100"));
|
|
} else {
|
|
self->duty = args[ARG_duty].u_int;
|
|
}
|
|
}
|
|
|
|
D = self->duty * ra_gpt_timer_get_period(self->ch);
|
|
ra_gpt_timer_set_duty(self->ch, self->id, (uint32_t)(D / 100));
|
|
|
|
if (self->duty && self->freq) {
|
|
ra_gpt_timer_start(self->ch);
|
|
self->active = 1;
|
|
}
|
|
}
|
|
|
|
STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
|
mp_hal_pin_obj_t pin_id = MP_OBJ_NULL;
|
|
machine_pwm_obj_t *self = MP_OBJ_NULL;
|
|
|
|
enum { ARG_pin, ARG_freq, ARG_duty };
|
|
static const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
|
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} },
|
|
{ MP_QSTR_duty, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }
|
|
};
|
|
|
|
mp_arg_check_num(n_args, n_kw, 1, 3, true);
|
|
mp_arg_val_t init_args[MP_ARRAY_SIZE(allowed_args)];
|
|
mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, init_args);
|
|
|
|
// Get GPIO and optional device to connect to PWM.
|
|
pin_id = mp_hal_get_pin_obj(init_args[ARG_pin].u_obj);
|
|
|
|
if (pin_id) {
|
|
for (int i = 0; i < MP_ARRAY_SIZE(machine_pwm_obj); i++) {
|
|
if (pin_id->pin == machine_pwm_obj[i].pwm->pin) {
|
|
self = &machine_pwm_obj[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (self) {
|
|
if (ra_gpt_timer_is_pwm_pin(self->pwm->pin)) {
|
|
// start the PWM running for this channel
|
|
mp_map_t kw_args;
|
|
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
|
|
mp_machine_pwm_init_helper(self, n_args - 1, args + 1, &kw_args);
|
|
} else {
|
|
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Pin(%d) has no timer output"), self->pwm->pin);
|
|
}
|
|
} else {
|
|
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Pin(%d) is used with other peripheral"), pin_id->pin);
|
|
}
|
|
} else {
|
|
mp_raise_ValueError(MP_ERROR_TEXT("Pin doesn't exist"));
|
|
}
|
|
|
|
return MP_OBJ_FROM_PTR(self);
|
|
}
|
|
|
|
STATIC void mp_machine_pwm_deinit(machine_pwm_obj_t *self) {
|
|
ra_gpt_timer_deinit(self->pwm->pin, self->ch, self->id);
|
|
self->active = 0;
|
|
self->ch = 0;
|
|
self->id = ' ';
|
|
self->duty = 0;
|
|
self->freq = 0;
|
|
}
|
|
|
|
STATIC mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) {
|
|
return MP_OBJ_NEW_SMALL_INT((uint32_t)ra_gpt_timer_get_freq(self->ch));
|
|
}
|
|
|
|
STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) {
|
|
if (freq) {
|
|
ra_gpt_timer_set_freq(self->ch, (float)freq);
|
|
self->freq = (uint32_t)ra_gpt_timer_get_freq(self->ch);
|
|
if (!self->freq) {
|
|
mp_raise_ValueError(MP_ERROR_TEXT("freq should be 0-24000000"));
|
|
} else
|
|
if (!self->active) {
|
|
ra_gpt_timer_start(self->ch);
|
|
self->active = 1;
|
|
}
|
|
} else {
|
|
ra_gpt_timer_stop(self->ch);
|
|
ra_gpt_timer_set_freq(self->ch, (float)freq);
|
|
self->freq = 0;
|
|
self->active = 0;
|
|
}
|
|
}
|
|
|
|
STATIC mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) {
|
|
// give the result in %
|
|
uint64_t Dc = ra_gpt_timer_get_duty(self->ch, self->id) * 100;
|
|
return MP_OBJ_NEW_SMALL_INT(Dc / ra_gpt_timer_get_period(self->ch));
|
|
}
|
|
|
|
STATIC void mp_machine_pwm_duty_set(machine_pwm_obj_t *self, mp_int_t duty) {
|
|
// assume duty is in %
|
|
if (duty < 0 || duty > 100) {
|
|
mp_raise_ValueError(MP_ERROR_TEXT("duty should be 0-100"));
|
|
} else {
|
|
if (duty) {
|
|
uint64_t D = (uint8_t)(duty) * ra_gpt_timer_get_period(self->ch);
|
|
ra_gpt_timer_set_duty(self->ch, self->id, (uint32_t)(D / 100));
|
|
self->duty = (uint8_t)duty;
|
|
|
|
if (!self->active && self->freq) {
|
|
ra_gpt_timer_start(self->ch);
|
|
self->active = 1;
|
|
}
|
|
} else {
|
|
if (self->active) {
|
|
ra_gpt_timer_stop(self->ch);
|
|
ra_gpt_timer_set_duty(self->ch, self->id, 0);
|
|
self->duty = 0;
|
|
self->active = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
STATIC mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) {
|
|
// give the result in ratio (u16 / 65535)
|
|
uint64_t Dc = ra_gpt_timer_get_duty(self->ch, self->id) * 65535;
|
|
return MP_OBJ_NEW_SMALL_INT(Dc / ra_gpt_timer_get_period(self->ch));
|
|
}
|
|
|
|
STATIC void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty_u16) {
|
|
// assume duty is a ratio (u16 / 65535)
|
|
if (duty_u16 < 0 || duty_u16 > 65535) {
|
|
mp_raise_ValueError(MP_ERROR_TEXT("duty should be 0-65535"));
|
|
} else {
|
|
if (duty_u16) {
|
|
uint64_t D = duty_u16 * ra_gpt_timer_get_period(self->ch);
|
|
ra_gpt_timer_set_duty(self->ch, self->id, (uint32_t)(D / 65535));
|
|
self->duty = (uint8_t)((duty_u16 * 100) / 65535);
|
|
|
|
if (!self->active && self->freq) {
|
|
ra_gpt_timer_start(self->ch);
|
|
self->active = 1;
|
|
}
|
|
} else {
|
|
if (self->active) {
|
|
ra_gpt_timer_stop(self->ch);
|
|
ra_gpt_timer_set_duty(self->ch, self->id, 0);
|
|
self->duty = 0;
|
|
self->active = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
STATIC mp_obj_t mp_machine_pwm_duty_get_ns(machine_pwm_obj_t *self) {
|
|
// give the result in ns
|
|
float ns = ra_gpt_timer_tick_time(self->ch);
|
|
ns *= (float)ra_gpt_timer_get_duty(self->ch, self->id);
|
|
return MP_OBJ_NEW_SMALL_INT(ns);
|
|
}
|
|
|
|
STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty_ns) {
|
|
// assume duty is ns
|
|
uint32_t ns_min = (uint32_t)ra_gpt_timer_tick_time(self->ch);
|
|
uint32_t ns_max = ns_min * ra_gpt_timer_get_period(self->ch);
|
|
if (duty_ns) {
|
|
if (duty_ns < ns_min || duty_ns > ns_max) {
|
|
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty should be in period range %d-%d"), ns_min, ns_max);
|
|
} else {
|
|
uint32_t D = (uint32_t)duty_ns / ns_min;
|
|
ra_gpt_timer_set_duty(self->ch, self->id, D);
|
|
D *= ns_min * 100;
|
|
self->duty = D / ns_max;
|
|
|
|
if (!self->active && self->freq) {
|
|
ra_gpt_timer_start(self->ch);
|
|
self->active = 1;
|
|
}
|
|
}
|
|
} else {
|
|
if (self->active) {
|
|
ra_gpt_timer_stop(self->ch);
|
|
ra_gpt_timer_set_duty(self->ch, self->id, 0);
|
|
self->duty = 0;
|
|
self->active = 0;
|
|
}
|
|
}
|
|
}
|