|
|
|
@ -26,6 +26,9 @@
|
|
|
|
|
/* FIXME: A reasonable clock for PWM resolution, could be determined dynamically. */
|
|
|
|
|
#define PWM_IMPL_REASONABLE_CLK (10000000UL) /* 10MHz */
|
|
|
|
|
|
|
|
|
|
#define PWM_IMPL_MR_RL_MASK \
|
|
|
|
|
(CTIMER_MCR_MR0RL_MASK | CTIMER_MCR_MR1RL_MASK | CTIMER_MCR_MR2RL_MASK | CTIMER_MCR_MR3RL_MASK)
|
|
|
|
|
|
|
|
|
|
/* Timer channel ID fields */
|
|
|
|
|
|
|
|
|
|
#define PWM_IMPL_CT_OFFSET_Pos 0
|
|
|
|
@ -34,7 +37,7 @@
|
|
|
|
|
#define PWM_IMPL_CT_CH_Pos 1
|
|
|
|
|
#define PWM_IMPL_CT_CH_Msk (3U << PWM_IMPL_CT_CH_Pos)
|
|
|
|
|
|
|
|
|
|
#define PWM_IMPL_CT_INST_Pos 4
|
|
|
|
|
#define PWM_IMPL_CT_INST_Pos 3
|
|
|
|
|
#define PWM_IMPL_CT_INST_Msk (0x1FU << PWM_IMPL_CT_INST_Pos)
|
|
|
|
|
|
|
|
|
|
#define PWM_IMPL_CT_OFFSET(x) ((x & PWM_IMPL_CT_OFFSET_Msk) >> PWM_IMPL_CT_OFFSET_Pos)
|
|
|
|
@ -64,7 +67,7 @@
|
|
|
|
|
(((port << PWM_IMPL_PIN_PORT_Pos) & PWM_IMPL_PIN_PORT_Msk) | ((id << PWM_IMPL_PIN_ID_Pos) & PWM_IMPL_PIN_ID_Msk) | \
|
|
|
|
|
((func << PWM_IMPL_PIN_FUNC_Pos) & PWM_IMPL_PIN_FUNC_Msk) | PWM_IMPL_PIN_VALID_Msk)
|
|
|
|
|
|
|
|
|
|
static uint32_t const s_pwm_channel_map[PWM_IMPL_INST_COUNT][PWM_IMPL_INST_COUNT * 2] = {
|
|
|
|
|
static uint32_t const s_pwm_channel_map[PWM_IMPL_INST_COUNT][PWM_IMPL_INST_CH * 2] = {
|
|
|
|
|
{
|
|
|
|
|
PWM_IMPL_PIN(0, 0, 3), PWM_IMPL_PIN(0, 30, 3), // CT0MAT0, Channel ID: 0, 1
|
|
|
|
|
PWM_IMPL_PIN(0, 3, 2), PWM_IMPL_PIN(0, 31, 3), // CT0MAT1, Channel ID: 2, 3
|
|
|
|
@ -105,6 +108,13 @@ static CTIMER_Type *const s_pwm_ct_list[] = {
|
|
|
|
|
CTIMER0, CTIMER1, CTIMER2, CTIMER3, CTIMER4,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static IRQn_Type const s_pwm_irq_list[] = {
|
|
|
|
|
CTIMER0_IRQn,
|
|
|
|
|
CTIMER1_IRQn,
|
|
|
|
|
CTIMER2_IRQn,
|
|
|
|
|
CTIMER3_IRQn,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static bool mrb_machine_pwm_ctimer_in_use(uint8_t ctimer_id) {
|
|
|
|
|
/* Determine whether CTIMERx is in use:
|
|
|
|
|
* 1: Check module power down status
|
|
|
|
@ -150,7 +160,7 @@ int mrb_machine_pwm_impl_init(uint32_t channel, machine_pwm_config_t *config) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t ctimer_pin = s_pwm_channel_map[ctimer_id][ctimer_off];
|
|
|
|
|
uint32_t ctimer_pin = s_pwm_channel_map[ctimer_id][ctimer_mat * 2 + ctimer_off];
|
|
|
|
|
CTIMER_Type *ctimer_inst = s_pwm_ct_list[ctimer_id];
|
|
|
|
|
|
|
|
|
|
/* Check valid bit to see if there really is a pin for this channel */
|
|
|
|
@ -161,6 +171,9 @@ int mrb_machine_pwm_impl_init(uint32_t channel, machine_pwm_config_t *config) {
|
|
|
|
|
if (!mrb_machine_pwm_ctimer_in_use(ctimer_id)) {
|
|
|
|
|
/* CTimer is not in use, initialize timer */
|
|
|
|
|
|
|
|
|
|
/* Disable interrupt for CTx */
|
|
|
|
|
DisableIRQ(s_pwm_irq_list[ctimer_id]);
|
|
|
|
|
|
|
|
|
|
CLOCK_AttachClk(s_pwm_clk_attach_list[ctimer_id]);
|
|
|
|
|
|
|
|
|
|
ctimer_config_t ct_cfg;
|
|
|
|
@ -170,25 +183,44 @@ int mrb_machine_pwm_impl_init(uint32_t channel, machine_pwm_config_t *config) {
|
|
|
|
|
|
|
|
|
|
CTIMER_Init(ctimer_inst, &ct_cfg);
|
|
|
|
|
|
|
|
|
|
ctimer_inst->MCR = CTIMER_MCR_MR3R_MASK;
|
|
|
|
|
} else {
|
|
|
|
|
ctimer_inst->MCR |= CTIMER_MCR_MR3R_MASK;
|
|
|
|
|
CTIMER_EnableResetMatchChannel(ctimer_inst, kCTIMER_Match_3, true);
|
|
|
|
|
|
|
|
|
|
ctimer_inst->MR[3] = 0x00000000UL;
|
|
|
|
|
ctimer_inst->MCR |= (CTIMER_MCR_MR3RL_MASK);
|
|
|
|
|
|
|
|
|
|
CTIMER_StartTimer(ctimer_inst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Calculate new reload value */
|
|
|
|
|
uint32_t rl_match_old = ctimer_inst->MR[3];
|
|
|
|
|
uint32_t rl_match_new = (PWM_IMPL_REASONABLE_CLK / config->freq);
|
|
|
|
|
|
|
|
|
|
uint32_t ch_enabled = ctimer_inst->MCR & PWM_IMPL_MR_RL_MASK;
|
|
|
|
|
|
|
|
|
|
/* Do not sync channels while we are fiddling with shadow registers */
|
|
|
|
|
ctimer_inst->MCR &= ~(PWM_IMPL_MR_RL_MASK);
|
|
|
|
|
|
|
|
|
|
/* Update period channel */
|
|
|
|
|
ctimer_inst->MSR[3] = rl_match_new;
|
|
|
|
|
|
|
|
|
|
/* To disable PWM output: Write MRx to 0xFFFFFFFF, to enable, enable reload from shadow. */
|
|
|
|
|
|
|
|
|
|
for (uint8_t i = 0; i < 3; i++) {
|
|
|
|
|
if (ctimer_inst->PWMC & (1 << i)) {
|
|
|
|
|
uint32_t new_duty = (uint32_t)((uint64_t)ctimer_inst->MSR[i] * rl_match_new / rl_match_old);
|
|
|
|
|
ctimer_inst->MSR[i] = new_duty;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* New channel */
|
|
|
|
|
ctimer_inst->MSR[ctimer_mat] = rl_match_new - ((config->duty * rl_match_new) / 65536);
|
|
|
|
|
|
|
|
|
|
/* Start sync MRx values with shadow registers */
|
|
|
|
|
ctimer_inst->MCR |= ch_enabled | (1U << (CTIMER_MCR_MR0RL_SHIFT + ctimer_mat));
|
|
|
|
|
|
|
|
|
|
/* From now on, the pin will be controlled by MATx PWM */
|
|
|
|
|
ctimer_inst->PWMC |= (1 << ctimer_mat);
|
|
|
|
|
|
|
|
|
|
/* Calculate new */
|
|
|
|
|
uint32_t rl_match = (PWM_IMPL_REASONABLE_CLK / config->freq);
|
|
|
|
|
ctimer_inst->MSR[3] = rl_match;
|
|
|
|
|
|
|
|
|
|
/* TODO: Calculate all enabled match channel values due to possible frequency change !! */
|
|
|
|
|
for(uint8_t i = 0; i < 3; i++) {
|
|
|
|
|
if(ctimer_inst->PWMC & (1 << i)) {
|
|
|
|
|
/* PWM channel i is enabled, write new value to shadow register */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Function is retrieved from const array. */
|
|
|
|
|
uint32_t iocon_mode = IOCON_PIO_SLEW(0) | IOCON_DIGITAL_EN | IOCON_PIO_FUNC(PWM_IMPL_PIN_FUNC(ctimer_pin));
|
|
|
|
|
|
|
|
|
@ -198,9 +230,100 @@ int mrb_machine_pwm_impl_init(uint32_t channel, machine_pwm_config_t *config) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int mrb_machine_pwm_impl_config_get(uint32_t channel, machine_pwm_config_t *config) {
|
|
|
|
|
config->freq = 1000;
|
|
|
|
|
config->duty = 32768;
|
|
|
|
|
config->enabled = true;
|
|
|
|
|
int mrb_machine_pwm_impl_output_set(uint32_t channel, bool enable) {
|
|
|
|
|
uint8_t ctimer_id = PWM_IMPL_CT_INST(channel);
|
|
|
|
|
uint8_t ctimer_mat = PWM_IMPL_CT_CH(channel);
|
|
|
|
|
uint8_t ctimer_off = PWM_IMPL_CT_OFFSET(channel);
|
|
|
|
|
|
|
|
|
|
/* Sanity checks */
|
|
|
|
|
if ((ctimer_id >= PWM_IMPL_INST_COUNT) || (ctimer_mat >= PWM_IMPL_INST_CH)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t ctimer_pin = s_pwm_channel_map[ctimer_id][ctimer_mat * 2 + ctimer_off];
|
|
|
|
|
CTIMER_Type *ctimer_inst = s_pwm_ct_list[ctimer_id];
|
|
|
|
|
|
|
|
|
|
/* Check valid bit to see if there really is a pin for this channel */
|
|
|
|
|
if (PWM_IMPL_PIN_VALID(ctimer_pin) == 0) {
|
|
|
|
|
return -2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (enable) {
|
|
|
|
|
ctimer_inst->MCR |= (1 << (CTIMER_MCR_MR0RL_SHIFT + ctimer_mat));
|
|
|
|
|
} else {
|
|
|
|
|
ctimer_inst->MCR &= ~(1 << (CTIMER_MCR_MR0RL_SHIFT + ctimer_mat));
|
|
|
|
|
ctimer_inst->MR[ctimer_mat] = 0xFFFFFFFFUL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool mrb_machine_pwm_impl_output_get(uint32_t channel) {
|
|
|
|
|
uint8_t ctimer_id = PWM_IMPL_CT_INST(channel);
|
|
|
|
|
uint8_t ctimer_mat = PWM_IMPL_CT_CH(channel);
|
|
|
|
|
uint8_t ctimer_off = PWM_IMPL_CT_OFFSET(channel);
|
|
|
|
|
|
|
|
|
|
/* Sanity checks */
|
|
|
|
|
if ((ctimer_id >= PWM_IMPL_INST_COUNT) || (ctimer_mat >= PWM_IMPL_INST_CH)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t ctimer_pin = s_pwm_channel_map[ctimer_id][ctimer_off];
|
|
|
|
|
CTIMER_Type *ctimer_inst = s_pwm_ct_list[ctimer_id];
|
|
|
|
|
|
|
|
|
|
/* Check valid bit to see if there really is a pin for this channel */
|
|
|
|
|
if (PWM_IMPL_PIN_VALID(ctimer_pin) == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ctimer_inst->MCR & (1 << (CTIMER_MCR_MR0RL_SHIFT + ctimer_mat))) {
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int mrb_machine_pwm_impl_duty_set(uint32_t channel, uint16_t duty) {
|
|
|
|
|
uint8_t ctimer_id = PWM_IMPL_CT_INST(channel);
|
|
|
|
|
uint8_t ctimer_mat = PWM_IMPL_CT_CH(channel);
|
|
|
|
|
uint8_t ctimer_off = PWM_IMPL_CT_OFFSET(channel);
|
|
|
|
|
|
|
|
|
|
/* Sanity checks */
|
|
|
|
|
if ((ctimer_id >= PWM_IMPL_INST_COUNT) || (ctimer_mat >= PWM_IMPL_INST_CH)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t ctimer_pin = s_pwm_channel_map[ctimer_id][ctimer_off];
|
|
|
|
|
CTIMER_Type *ctimer_inst = s_pwm_ct_list[ctimer_id];
|
|
|
|
|
|
|
|
|
|
/* Check valid bit to see if there really is a pin for this channel */
|
|
|
|
|
if (PWM_IMPL_PIN_VALID(ctimer_pin) == 0) {
|
|
|
|
|
return -2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctimer_inst->MSR[ctimer_mat] = ctimer_inst->MSR[3] - ((uint32_t)duty * ctimer_inst->MSR[3] / 65536);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
uint16_t mrb_machine_pwm_impl_duty_get(uint32_t channel) {
|
|
|
|
|
uint8_t ctimer_id = PWM_IMPL_CT_INST(channel);
|
|
|
|
|
uint8_t ctimer_mat = PWM_IMPL_CT_CH(channel);
|
|
|
|
|
uint8_t ctimer_off = PWM_IMPL_CT_OFFSET(channel);
|
|
|
|
|
|
|
|
|
|
/* Sanity checks */
|
|
|
|
|
if ((ctimer_id >= PWM_IMPL_INST_COUNT) || (ctimer_mat >= PWM_IMPL_INST_CH)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t ctimer_pin = s_pwm_channel_map[ctimer_id][ctimer_off];
|
|
|
|
|
CTIMER_Type *ctimer_inst = s_pwm_ct_list[ctimer_id];
|
|
|
|
|
|
|
|
|
|
/* Check valid bit to see if there really is a pin for this channel */
|
|
|
|
|
if (PWM_IMPL_PIN_VALID(ctimer_pin) == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t duty = ctimer_inst->MSR[ctimer_mat] * 65536 / ctimer_inst->MSR[3];
|
|
|
|
|
|
|
|
|
|
return duty;
|
|
|
|
|
}
|