rp2/machine_pwm: Add duty_x() checks and return 0 if PWM is not started.

Changes in this commit:
- Limit duty_u16() to 65535 and duty_ns() to the period duration.
- Return 0 for pwm.freq() if the frequency has not been set yet.
- Return 0 for pwm.duty_us16() and duty_ns() unless both frequency and
  duty cycle have been set.
- Initialize the pin to PWM at the very end of the constructor, to avoid
  possible glitches on the pin when setting up the PWM.
pull/10850/head
robert-hh 2023-04-27 21:21:56 +02:00 zatwierdzone przez Damien George
rodzic 0b3b508d1d
commit 7952694a3c
1 zmienionych plików z 36 dodań i 19 usunięć

Wyświetl plik

@ -142,14 +142,14 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args
self->invert = 0; self->invert = 0;
self->duty_type = DUTY_NOT_SET; self->duty_type = DUTY_NOT_SET;
// Select PWM function for given GPIO.
gpio_set_function(gpio, GPIO_FUNC_PWM);
// Process the remaining parameters. // Process the remaining parameters.
mp_map_t kw_args; mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, all_args + n_args); mp_map_init_fixed_table(&kw_args, n_kw, all_args + n_args);
mp_machine_pwm_init_helper(self, n_args - 1, all_args + 1, &kw_args); mp_machine_pwm_init_helper(self, n_args - 1, all_args + 1, &kw_args);
// Select PWM function for given GPIO.
gpio_set_function(gpio, GPIO_FUNC_PWM);
return MP_OBJ_FROM_PTR(self); return MP_OBJ_FROM_PTR(self);
} }
@ -189,10 +189,14 @@ uint32_t get_slice_hz_ceil(uint32_t div16) {
} }
STATIC mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { STATIC mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) {
uint32_t div16 = pwm_hw->slice[self->slice].div; if (slice_freq_set[self->slice] == true) {
uint32_t top = pwm_hw->slice[self->slice].top; uint32_t div16 = pwm_hw->slice[self->slice].div;
uint32_t pwm_freq = get_slice_hz_round(div16 * (top + 1)); uint32_t top = pwm_hw->slice[self->slice].top;
return MP_OBJ_NEW_SMALL_INT(pwm_freq); uint32_t pwm_freq = get_slice_hz_round(div16 * (top + 1));
return MP_OBJ_NEW_SMALL_INT(pwm_freq);
} else {
return MP_OBJ_NEW_SMALL_INT(0);
}
} }
STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) {
@ -245,19 +249,27 @@ STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) {
} }
STATIC mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) { STATIC mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) {
uint32_t top = pwm_hw->slice[self->slice].top; if (self->duty_type != DUTY_NOT_SET && slice_freq_set[self->slice] == true) {
uint32_t cc = pwm_hw->slice[self->slice].cc; uint32_t top = pwm_hw->slice[self->slice].top;
cc = (cc >> (self->channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB)) & 0xffff; uint32_t cc = pwm_hw->slice[self->slice].cc;
cc = (cc >> (self->channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB)) & 0xffff;
// Use rounding (instead of flooring) here to give as accurate an // Use rounding (instead of flooring) here to give as accurate an
// estimate as possible. // estimate as possible.
return MP_OBJ_NEW_SMALL_INT((cc * 65535 + (top + 1) / 2) / (top + 1)); return MP_OBJ_NEW_SMALL_INT((cc * 65535 + (top + 1) / 2) / (top + 1));
} else {
return MP_OBJ_NEW_SMALL_INT(0);
}
} }
STATIC void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty_u16) { STATIC void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty_u16) {
uint32_t top = pwm_hw->slice[self->slice].top; uint32_t top = pwm_hw->slice[self->slice].top;
// Limit duty_u16 to 65535
// Use rounding here to set it as accurately as possible. // Use rounding here to set it as accurately as possible.
if (duty_u16 > 65535) {
duty_u16 = 65535;
}
uint32_t cc = (duty_u16 * (top + 1) + 65535 / 2) / 65535; uint32_t cc = (duty_u16 * (top + 1) + 65535 / 2) / 65535;
pwm_set_chan_level(self->slice, self->channel, cc); pwm_set_chan_level(self->slice, self->channel, cc);
self->duty = duty_u16; self->duty = duty_u16;
@ -266,17 +278,22 @@ STATIC void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty_u
} }
STATIC mp_obj_t mp_machine_pwm_duty_get_ns(machine_pwm_obj_t *self) { STATIC mp_obj_t mp_machine_pwm_duty_get_ns(machine_pwm_obj_t *self) {
uint32_t slice_hz = get_slice_hz_round(pwm_hw->slice[self->slice].div); if (self->duty_type != DUTY_NOT_SET && slice_freq_set[self->slice] == true) {
uint32_t cc = pwm_hw->slice[self->slice].cc; uint32_t slice_hz = get_slice_hz_round(pwm_hw->slice[self->slice].div);
cc = (cc >> (self->channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB)) & 0xffff; uint32_t cc = pwm_hw->slice[self->slice].cc;
return MP_OBJ_NEW_SMALL_INT(((uint64_t)cc * 1000000000ULL + slice_hz / 2) / slice_hz); cc = (cc >> (self->channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB)) & 0xffff;
return MP_OBJ_NEW_SMALL_INT(((uint64_t)cc * 1000000000ULL + slice_hz / 2) / slice_hz);
} else {
return MP_OBJ_NEW_SMALL_INT(0);
}
} }
STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty_ns) { STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty_ns) {
uint32_t slice_hz = get_slice_hz_round(pwm_hw->slice[self->slice].div); uint32_t slice_hz = get_slice_hz_round(pwm_hw->slice[self->slice].div);
uint32_t cc = ((uint64_t)duty_ns * slice_hz + 500000000ULL) / 1000000000ULL; uint32_t cc = ((uint64_t)duty_ns * slice_hz + 500000000ULL) / 1000000000ULL;
if (cc > 65535) { uint32_t top = pwm_hw->slice[self->slice].top;
mp_raise_ValueError(MP_ERROR_TEXT("duty larger than period")); if (cc > (top + 1)) {
cc = top + 1;
} }
pwm_set_chan_level(self->slice, self->channel, cc); pwm_set_chan_level(self->slice, self->channel, cc);
self->duty = duty_ns; self->duty = duty_ns;