ports/nrf: factor out routines for allocating, freeing pwm channels
This commit is contained in:
parent
7b9d26cefe
commit
2bc704fe07
|
@ -57,6 +57,12 @@ STATIC uint16_t pwm_seq[MP_ARRAY_SIZE(pwms)][CHANNELS_PER_PWM];
|
|||
|
||||
static uint8_t never_reset_pwm[MP_ARRAY_SIZE(pwms)];
|
||||
|
||||
STATIC int pwm_idx(NRF_PWM_Type *pwm) {
|
||||
for(size_t i=0; i < MP_ARRAY_SIZE(pwms); i++)
|
||||
if(pwms[i] == pwm) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void common_hal_pulseio_pwmout_never_reset(pulseio_pwmout_obj_t *self) {
|
||||
for(size_t i=0; i < MP_ARRAY_SIZE(pwms); i++) {
|
||||
NRF_PWM_Type* pwm = pwms[i];
|
||||
|
@ -133,6 +139,57 @@ bool convert_frequency(uint32_t frequency, uint16_t *countertop, nrf_pwm_clk_t *
|
|||
return false;
|
||||
}
|
||||
|
||||
NRF_PWM_Type *pwmout_allocate(uint16_t countertop, nrf_pwm_clk_t base_clock,
|
||||
bool variable_frequency, int8_t *channel_out, bool *pwm_already_in_use_out) {
|
||||
for (size_t pwm_index = 0; pwm_index < MP_ARRAY_SIZE(pwms); pwm_index++) {
|
||||
NRF_PWM_Type *pwm = pwms[pwm_index];
|
||||
bool pwm_already_in_use = pwm->ENABLE & SPIM_ENABLE_ENABLE_Msk;
|
||||
if (pwm_already_in_use) {
|
||||
if (variable_frequency) {
|
||||
// Variable frequency requires exclusive use of a PWM, so try the next one.
|
||||
continue;
|
||||
}
|
||||
|
||||
// PWM is in use, but see if it's set to the same frequency we need. If so,
|
||||
// look for a free channel.
|
||||
if (pwm->COUNTERTOP == countertop && pwm->PRESCALER == base_clock) {
|
||||
for (size_t chan = 0; chan < CHANNELS_PER_PWM; chan++) {
|
||||
if (pwm->PSEL.OUT[chan] == 0xFFFFFFFF) {
|
||||
// Channel is free.
|
||||
if(channel_out)
|
||||
*channel_out = chan;
|
||||
if(pwm_already_in_use_out)
|
||||
*pwm_already_in_use_out = pwm_already_in_use;
|
||||
return pwm;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// PWM not yet in use, so we can start to use it. Use channel 0.
|
||||
if(channel_out)
|
||||
*channel_out = 0;
|
||||
if(pwm_already_in_use_out)
|
||||
*pwm_already_in_use_out = pwm_already_in_use;
|
||||
return pwm;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void pwmout_free_channel(NRF_PWM_Type *pwm, int8_t channel) {
|
||||
// Disconnect pin from channel.
|
||||
pwm->PSEL.OUT[channel] = 0xFFFFFFFF;
|
||||
|
||||
for(int i=0; i < CHANNELS_PER_PWM; i++) {
|
||||
if (pwm->PSEL.OUT[i] != 0xFFFFFFFF) {
|
||||
// Some channel is still being used, so don't disable.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nrf_pwm_disable(pwm);
|
||||
}
|
||||
|
||||
pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self,
|
||||
const mcu_pin_obj_t* pin,
|
||||
uint16_t duty,
|
||||
|
@ -148,48 +205,16 @@ pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self,
|
|||
return PWMOUT_INVALID_FREQUENCY;
|
||||
}
|
||||
|
||||
self->pwm = NULL;
|
||||
self->channel = CHANNELS_PER_PWM; // out-of-range value.
|
||||
int8_t channel;
|
||||
bool pwm_already_in_use;
|
||||
NRF_PWM_Type* pwm;
|
||||
size_t pwm_index = 0;
|
||||
for (; pwm_index < MP_ARRAY_SIZE(pwms); pwm_index++) {
|
||||
pwm = pwms[pwm_index];
|
||||
pwm_already_in_use = pwm->ENABLE & SPIM_ENABLE_ENABLE_Msk;
|
||||
if (pwm_already_in_use) {
|
||||
if (variable_frequency) {
|
||||
// Variable frequency requires exclusive use of a PWM, so try the next one.
|
||||
continue;
|
||||
}
|
||||
|
||||
// PWM is in use, but see if it's set to the same frequency we need. If so,
|
||||
// look for a free channel.
|
||||
if (pwm->COUNTERTOP == countertop && pwm->PRESCALER == base_clock) {
|
||||
for (size_t chan = 0; chan < CHANNELS_PER_PWM; chan++) {
|
||||
if (pwm->PSEL.OUT[chan] == 0xFFFFFFFF) {
|
||||
// Channel is free.
|
||||
self->pwm = pwm;
|
||||
self->channel = chan;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Did we find a channel? If not, loop and check the next pwm.
|
||||
if (self->pwm != NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// PWM not yet in use, so we can start to use it. Use channel 0.
|
||||
self->pwm = pwm;
|
||||
self->channel = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
self->pwm = pwmout_allocate(countertop, base_clock, variable_frequency,
|
||||
&channel, &pwm_already_in_use);
|
||||
|
||||
if (self->pwm == NULL) {
|
||||
return PWMOUT_ALL_TIMERS_IN_USE;
|
||||
}
|
||||
|
||||
self->channel = channel;
|
||||
self->pin_number = pin->number;
|
||||
claim_pin(pin);
|
||||
|
||||
|
@ -200,17 +225,17 @@ pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self,
|
|||
nrf_gpio_cfg_output(self->pin_number);
|
||||
|
||||
// disable before mapping pin channel
|
||||
nrf_pwm_disable(pwm);
|
||||
nrf_pwm_disable(self->pwm);
|
||||
|
||||
if (!pwm_already_in_use) {
|
||||
reset_single_pwmout(pwm_index);
|
||||
nrf_pwm_configure(pwm, base_clock, NRF_PWM_MODE_UP, countertop);
|
||||
reset_single_pwmout(pwm_idx(self->pwm));
|
||||
nrf_pwm_configure(self->pwm, base_clock, NRF_PWM_MODE_UP, countertop);
|
||||
}
|
||||
|
||||
// Connect channel to pin, without disturbing other channels.
|
||||
pwm->PSEL.OUT[self->channel] = pin->number;
|
||||
self->pwm->PSEL.OUT[self->channel] = pin->number;
|
||||
|
||||
nrf_pwm_enable(pwm);
|
||||
nrf_pwm_enable(self->pwm);
|
||||
|
||||
common_hal_pulseio_pwmout_set_duty_cycle(self, duty);
|
||||
return PWMOUT_OK;
|
||||
|
@ -230,17 +255,7 @@ void common_hal_pulseio_pwmout_deinit(pulseio_pwmout_obj_t* self) {
|
|||
NRF_PWM_Type* pwm = self->pwm;
|
||||
self->pwm = NULL;
|
||||
|
||||
// Disconnect pin from channel.
|
||||
pwm->PSEL.OUT[self->channel] = 0xFFFFFFFF;
|
||||
|
||||
for(int i=0; i < CHANNELS_PER_PWM; i++) {
|
||||
if (self->pwm->PSEL.OUT[i] != 0xFFFFFFFF) {
|
||||
// Some channel is still being used, so don't disable.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nrf_pwm_disable(pwm);
|
||||
pwmout_free_channel(pwm, self->channel);
|
||||
}
|
||||
|
||||
void common_hal_pulseio_pwmout_set_duty_cycle(pulseio_pwmout_obj_t* self, uint16_t duty_cycle) {
|
||||
|
|
|
@ -41,5 +41,8 @@ typedef struct {
|
|||
} pulseio_pwmout_obj_t;
|
||||
|
||||
void pwmout_reset(void);
|
||||
NRF_PWM_Type *pwmout_allocate(uint16_t countertop, nrf_pwm_clk_t base_clock,
|
||||
bool variable_frequency, int8_t *channel_out, bool *pwm_already_in_use_out);
|
||||
void pwmout_free_channel(NRF_PWM_Type *pwm, int8_t channel);
|
||||
|
||||
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_PULSEIO_PWMOUT_H
|
||||
|
|
Loading…
Reference in New Issue