436 lines
14 KiB
C
436 lines
14 KiB
C
/*!
|
|
* \file xc_drv_pwm.c
|
|
*
|
|
* \brief Target xc pwm driver implementation
|
|
*
|
|
* \copyright Revised BSD License, see section \ref LICENSE.
|
|
*
|
|
* \code
|
|
*
|
|
* _ __ _ ________ _
|
|
* | |/ /(_)___ / ____/ /_ (_)___
|
|
* | // / __ \/ / / __ \/ / __ \
|
|
* / |/ / / / / /___/ / / / / /_/ /
|
|
* /_/|_/_/_/ /_/\____/_/ /_/_/ .___/
|
|
* /_/
|
|
* (C) 2022-2025 XinChip
|
|
*
|
|
* \endcode
|
|
*
|
|
* \author ( XinChip ) Alex-J
|
|
*
|
|
* \author ( XinChip )
|
|
*/
|
|
/*------------------------------------------------------------------------------------
|
|
INCLUDE HEADE FILES
|
|
-------------------------------------------------------------------------------------*/
|
|
#include "xc_drv_pwm.h"
|
|
#include "xc_drv_gpio.h"
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
Macros
|
|
-------------------------------------------------------------------------------------*/
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
Global Variables
|
|
-------------------------------------------------------------------------------------*/
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
TypeDef
|
|
-------------------------------------------------------------------------------------*/
|
|
typedef enum
|
|
{
|
|
STATE_UNINITIALIZED,
|
|
STATE_INITIALIZED,
|
|
} xincx_drv_state_t;
|
|
|
|
pwm_handler_callback pwm_n_callback[3] = {
|
|
pwm_capture_ch0_callback,
|
|
pwm_capture_ch1_callback,
|
|
pwm_capture_ch2_callback,
|
|
};
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
Local Variables
|
|
-------------------------------------------------------------------------------------*/
|
|
static uint32_t pwm_clk_init_state = STATE_UNINITIALIZED;
|
|
|
|
static uint16_t pwm_freq_to_period(PWM_InitCfg_t *pwm_cfg, PWM_ClkDiv_TypeDef div_clk);
|
|
|
|
double period;
|
|
/*------------------------------------------------------------------------------------
|
|
Func Prototype
|
|
-------------------------------------------------------------------------------------*/
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
Functions
|
|
-------------------------------------------------------------------------------------*/
|
|
|
|
void xc_pwm_init(uint8_t reg_idx, PWM_InitCfg_t *init_cfg)
|
|
{
|
|
if (pwm_clk_init_state == STATE_UNINITIALIZED) {
|
|
|
|
cpr_ctlapbclken_grctl__pwm_pclk_en__setf(ENABLE);
|
|
cpr_rstctl_ctlapb_sw__pwm_rstn__setf(RSTCTL_ENABLE);
|
|
cpr_rstctl_ctlapb_sw__pwm_rstn__setf(RSTCTL_DISABLE);
|
|
|
|
cpr_pwm_clk_ctl__pwm_clk_en__setf(ENABLE);
|
|
cpr_pwm_clk_ctl__pwm_clksel__setf(init_cfg->SrcClk);
|
|
cpr_pwm_clk_ctl__pwm_clk0_div__setf(init_cfg->DivClk);
|
|
cpr_pwm_clk_ctl__pwm_clk1_div__setf(init_cfg->DivClk);
|
|
|
|
pwm_clk_init_state = STATE_INITIALIZED;
|
|
}
|
|
|
|
pwm_pin_set(reg_idx, init_cfg);
|
|
|
|
pwm_en__en__setf(reg_idx, DISABLE);
|
|
|
|
pwm_en__en_sel__setf(reg_idx, init_cfg->SrcEnable);
|
|
|
|
xc_pwm_dutycycle_set(reg_idx, init_cfg->DutyCycleAcc, init_cfg->DutyCycle);
|
|
|
|
pwm_period__period__setf(reg_idx, pwm_freq_to_period(init_cfg, PWM_CLK_DIV0));
|
|
|
|
pwm_compen__pwmcompen__setf(reg_idx, init_cfg->InvertEnable);
|
|
|
|
pwm_comptime__comptime__setf(reg_idx, init_cfg->InvertDelay);
|
|
}
|
|
|
|
void xc_pwm_start(uint8_t reg_idx) { pwm_en__en__setf(reg_idx, PWM_EN_ENABLE); }
|
|
|
|
void xc_pwm_stop(uint8_t reg_idx) { pwm_en__en__setf(reg_idx, PWM_EN_DISABLE); }
|
|
|
|
void pwm_src_enable_set(uint8_t reg_idx, uint8_t src_enable) { pwm_en__en_sel__setf(reg_idx, src_enable); }
|
|
|
|
void xc_pwm_start_all(void) { pwm_en__pwm_en_all__setf(PWM0_IDX, PWM_EN_ALL_ENABLE); }
|
|
|
|
void xc_pwm_stop_all(void) { pwm_en__pwm_en_all__setf(PWM0_IDX, PWM_EN_ALL_DISABLE); }
|
|
|
|
void pwm_pin_set(uint8_t reg_idx, PWM_InitCfg_t *pwm_cfg)
|
|
{
|
|
GPIO_InitCfg_t gpio_cfg;
|
|
if(pwm_cfg->OutputPin==GPIO_11||pwm_cfg->OutputPin==GPIO_12||pwm_cfg->OutputPin==GPIO_13){
|
|
gpio_cfg.Mux = GPIO_Mux1;
|
|
}else
|
|
{
|
|
gpio_cfg.Mux = GPIO_Mux0;
|
|
}
|
|
gpio_cfg.Dir = GPIO_DIR_OUTPUT;
|
|
gpio_cfg.Pull = GPIO_PULLDOWN;
|
|
gpio_cfg.Int = NOT_INT;
|
|
gpio_cfg.Pin = pwm_cfg->OutputPin;
|
|
|
|
if (reg_idx == PWM0_IDX || reg_idx == PWM1_IDX) {
|
|
gpio_cfg.FunSel = (reg_idx == PWM0_IDX) ? PWM0 : PWM1;
|
|
xc_gpio_init(&gpio_cfg);
|
|
|
|
if (pwm_cfg->InvertEnable) {
|
|
gpio_cfg.FunSel = (reg_idx == PWM0_IDX) ? PWM0_INV : PWM1_INV;
|
|
|
|
gpio_cfg.Pin = pwm_cfg->OutputInvertPin;
|
|
xc_gpio_init(&gpio_cfg);
|
|
}
|
|
} else if (reg_idx == PWM2_IDX) {
|
|
gpio_cfg.Mux = GPIO_Mux3;
|
|
if (pwm_cfg->OutputPin == GPIO_12) {
|
|
xc_gpio_init(&gpio_cfg);
|
|
}
|
|
} else if (reg_idx == PWM3_IDX) {
|
|
gpio_cfg.Mux = GPIO_Mux3;
|
|
if (pwm_cfg->OutputPin == GPIO_13) {
|
|
xc_gpio_init(&gpio_cfg);
|
|
}
|
|
} else if (reg_idx == PWM4_IDX) {
|
|
gpio_cfg.Mux = GPIO_Mux2;
|
|
if (pwm_cfg->OutputPin == GPIO_0) {
|
|
xc_gpio_init(&gpio_cfg);
|
|
}
|
|
} else if (reg_idx == PWM5_IDX) {
|
|
gpio_cfg.Mux = GPIO_Mux2;
|
|
if (pwm_cfg->OutputPin == GPIO_1) {
|
|
xc_gpio_init(&gpio_cfg);
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint16_t pwm_freq_to_period(PWM_InitCfg_t *pwm_cfg, PWM_ClkDiv_TypeDef div_clk)
|
|
{
|
|
uint32_t pwm_clk;
|
|
|
|
switch (pwm_cfg->SrcClk) {
|
|
case PWM_CLK_SRC_32M_DIV: {
|
|
pwm_clk = xc_clock_hfclk_in_get() / PWM_CLK_DIV_CAL(div_clk);
|
|
} break;
|
|
|
|
case PWM_CLK_SRC_32K_DIV: {
|
|
pwm_clk = xc_clock_lfclk_in_get() / PWM_CLK_DIV_CAL(div_clk);
|
|
} break;
|
|
|
|
case PWM_CLK_SRC_32K: {
|
|
pwm_clk = CLOCK_LFCLK_IN_32K;
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uint32_t freq_max = PWM_FREQ_MAX_CAL(pwm_clk, pwm_cfg->DutyCycleAcc);
|
|
|
|
uint32_t freq = PWM_FREQ_CAL(pwm_clk, pwm_cfg->DutyCycleAcc, pwm_cfg->Period);
|
|
|
|
if (freq > freq_max) {
|
|
DEBUG("%s,line=%d,set freq=%d,The maximum configurable frequency %d hz "
|
|
"has been exceeded.\n",
|
|
__func__, __LINE__, freq, freq_max);
|
|
} else {
|
|
DEBUG("%s,line=%d,set freq=%d,maximum configurable frequency %d hz\n", __func__, __LINE__, freq, freq_max);
|
|
}
|
|
|
|
return pwm_cfg->Period;
|
|
}
|
|
|
|
void xc_pwm_dutycycle_set(uint8_t reg_idx, uint32_t dutycycle_acc, uint32_t dutycycle)
|
|
{
|
|
pwm_en__mode__setf(reg_idx, PWM_EN_MODE_EDGE_ALIGNED);
|
|
|
|
pwm_ocpy__ocpy_ratio_config__setf(reg_idx, (dutycycle_acc - 1));
|
|
|
|
pwm_ocpy__ocpy_ratio__setf(reg_idx, dutycycle_acc - dutycycle);
|
|
|
|
pwm_up__update__setf(reg_idx, PWM_UP_UPDATE_ENABLE);
|
|
}
|
|
|
|
void xc_pwm_lowpower_enable(uint8_t reg_idx) { pwm_en__sleep_en__setf(reg_idx, PWM_EN_SLEEP_EN_ENABLE); }
|
|
|
|
void xc_pwm_lowpower_disable(uint8_t reg_idx) { pwm_en__sleep_en__setf(reg_idx, PWM_EN_SLEEP_EN_DISABLE); }
|
|
|
|
void xc_pwm_lowpower_countdirection_set(uint8_t reg_idx, uint32_t direction)
|
|
{
|
|
pwm_en__sleep_incr__setf(reg_idx, direction);
|
|
}
|
|
|
|
void xc_pwm_brake_enable(uint8_t reg_idx)
|
|
{
|
|
uint32_t val;
|
|
|
|
val = pwm_break_ctl__pwm_brk_enable__getf(PWM0_IDX);
|
|
|
|
if (reg_idx == PWM0_IDX || reg_idx == PWM1_IDX) {
|
|
val &= ~(PWM_BRK0_ENABLE_ENABLE);
|
|
val |= (PWM_BRK0_ENABLE_ENABLE);
|
|
} else if (reg_idx == PWM2_IDX || reg_idx == PWM3_IDX) {
|
|
val &= ~(PWM_BRK1_ENABLE_ENABLE);
|
|
val |= (PWM_BRK1_ENABLE_ENABLE);
|
|
} else if (reg_idx == PWM4_IDX || reg_idx == PWM5_IDX) {
|
|
val &= ~(PWM_BRK2_ENABLE_ENABLE);
|
|
val |= (PWM_BRK2_ENABLE_ENABLE);
|
|
}
|
|
|
|
pwm_break_ctl__pwm_brk_enable__setf(PWM0_IDX, val);
|
|
}
|
|
|
|
void xc_pwm_brake_disable(uint8_t reg_idx)
|
|
{
|
|
uint32_t val;
|
|
|
|
val = pwm_break_ctl__pwm_brk_enable__getf(PWM0_IDX);
|
|
|
|
if (reg_idx == PWM0_IDX || reg_idx == PWM1_IDX) {
|
|
val &= ~(PWM_BRK0_ENABLE_ENABLE);
|
|
} else if (reg_idx == PWM2_IDX || reg_idx == PWM3_IDX) {
|
|
val &= ~(PWM_BRK1_ENABLE_ENABLE);
|
|
} else if (reg_idx == PWM4_IDX || reg_idx == PWM5_IDX) {
|
|
val &= ~(PWM_BRK2_ENABLE_ENABLE);
|
|
}
|
|
|
|
pwm_break_ctl__pwm_brk_enable__setf(PWM0_IDX, val);
|
|
}
|
|
|
|
uint8_t xc_pwm_brake_signal_valid_get(void) { return pwm_break_ctl__pwm_brk_sync__getf(PWM0_IDX); }
|
|
|
|
void xc_pwm_brake_signal_mask_set(uint32_t mask) { pwm_break_ctl__pwm_brk_mask__setf(PWM0_IDX, mask); }
|
|
|
|
void xc_pwm_brake_signal_trigger_level_set(uint32_t level) { pwm_break_ctl__pwm_brk_inv__setf(PWM0_IDX, level); }
|
|
|
|
void xc_pwm_brake_recovery_mode_set(uint32_t mode) { pwm_break_ctl__brk_mode__setf(PWM0_IDX, mode); }
|
|
|
|
uint8_t xc_pwm_brake_recovery_mode_get(void) { return (pwm_break_ctl__brk_mode__getf(PWM0_IDX)); }
|
|
|
|
void xc_pwm_brake_debounce_set(uint32_t debounce, uint32_t step)
|
|
{
|
|
pwm_break_ctl__brk_dbc_en__setf(PWM0_IDX, debounce);
|
|
pwm_break_ctl__brk_dbc_step__setf(PWM0_IDX, step);
|
|
}
|
|
|
|
void xc_pwm_brake_clear() { pwm_break_ctl__clear__setf(PWM0_IDX, PWM_BRK_BRK_CLEAR_EXIT); }
|
|
|
|
void xc_pwm_capture_enable(uint8_t ch) { cap_tim_ctl__capture_enable__setf(ch, PWM_CAPTURE_ENABLE); }
|
|
|
|
void xc_pwm_capture_disable(uint8_t ch) { cap_tim_ctl__capture_enable__setf(ch, PWM_CAPTURE_DISABLE); }
|
|
|
|
void xc_pwm_capture_enable_it(uint8_t ch)
|
|
{
|
|
if (ch == 0) {
|
|
pwm_cap_tim_int_en__capture_upd_0_en__setf(ENABLE);
|
|
} else if (ch == 1) {
|
|
pwm_cap_tim_int_en__capture_upd_1_en__setf(ENABLE);
|
|
} else if (ch == 2) {
|
|
pwm_cap_tim_int_en__capture_upd_2_en__setf(ENABLE);
|
|
}
|
|
}
|
|
|
|
void xc_pwm_capture_disable_it(uint8_t ch)
|
|
{
|
|
if (ch == PWM_IC_CH0) {
|
|
pwm_cap_tim_int_en__capture_upd_0_en__setf(DISABLE);
|
|
} else if (ch == PWM_IC_CH1) {
|
|
pwm_cap_tim_int_en__capture_upd_1_en__setf(DISABLE);
|
|
} else if (ch == PWM_IC_CH2) {
|
|
pwm_cap_tim_int_en__capture_upd_2_en__setf(DISABLE);
|
|
}
|
|
}
|
|
|
|
void xc_pwm_capture_signal_set(uint8_t ch, uint32_t signal) { cap_tim_ctl__capture_sig_sel__setf(ch, signal); }
|
|
|
|
void xc_pwm_capture_debounce_enable(uint8_t ch, uint32_t step)
|
|
{
|
|
cap_tim_ctl__dbc_en__setf(ch, PWM_CAPTURE_DBC_ENABLE);
|
|
|
|
cap_tim_ctl__dbc_step__setf(ch, step);
|
|
}
|
|
|
|
void xc_pwm_capture_debounce_disable(uint8_t ch, uint32_t step)
|
|
{
|
|
cap_tim_ctl__dbc_en__setf(ch, PWM_CAPTURE_DBC_DISABLE);
|
|
|
|
cap_tim_ctl__dbc_step__setf(ch, step);
|
|
}
|
|
|
|
void xc_pwm_capture_edge_set(uint8_t ch, uint32_t edge) { cap_tim_ctl__capture_mode__setf(ch, edge); }
|
|
|
|
void xc_pwm_capture_psc_set(uint8_t ch, uint32_t psc) { cap_tim_ctl__capture_psc__setf(ch, psc); }
|
|
|
|
void xc_pwm_capture_counter_enable(uint8_t ch)
|
|
{
|
|
cap_tim_ctl__common_cnt_enable__setf(ch, PWM_CAPTURE_COMMON_CNT_ENABLE);
|
|
}
|
|
|
|
void xc_pwm_capture_counter_disable(uint8_t ch)
|
|
{
|
|
cap_tim_ctl__common_cnt_enable__setf(ch, PWM_CAPTURE_COMMON_CNT_DISABLE);
|
|
}
|
|
|
|
uint16_t xc_pwm_capture_val_get(uint8_t ch) { return cap_tim_val__capture_value__getf(ch); }
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------------------------
|
|
* PWM Timer
|
|
*-----------------------------------------------------------------------------------------------
|
|
*/
|
|
/**
|
|
****************************************************************************************
|
|
* @brief
|
|
*
|
|
* @param[in]
|
|
* @param[in]
|
|
****************************************************************************************
|
|
*/
|
|
void xc_pwm_timer_init(uint8_t reg_idx, PWM_Timer_InitCfg_t *init_cfg)
|
|
{
|
|
if (reg_idx > PWM_TIMER5_IDX)
|
|
return;
|
|
|
|
if (pwm_clk_init_state == STATE_UNINITIALIZED) {
|
|
cpr_ctlapbclken_grctl__pwm_pclk_en__setf(ENABLE);
|
|
cpr_rstctl_ctlapb_sw__pwm_rstn__setf(RSTCTL_ENABLE);
|
|
cpr_rstctl_ctlapb_sw__pwm_rstn__setf(RSTCTL_DISABLE);
|
|
|
|
cpr_pwm_clk_ctl__pwm_clk_en__setf(ENABLE);
|
|
cpr_pwm_clk_ctl__pwm_clksel__setf(init_cfg->src_clk);
|
|
cpr_pwm_clk_ctl__pwm_clk0_div__setf(init_cfg->clk_div);
|
|
cpr_pwm_clk_ctl__pwm_clk1_div__setf(init_cfg->clk_div);
|
|
|
|
pwm_clk_init_state = STATE_INITIALIZED;
|
|
}
|
|
|
|
uint32_t ctl_reg = (init_cfg->timer_mode << TIMER_MODE_POS) | (init_cfg->timer_int_mask_en << TIMER_INT_MASK_POS) |
|
|
(init_cfg->timer_pwm_en << TIMER_PWM_POS) |
|
|
(init_cfg->timer_0to100_pwm_en << TIMER_ON10OPWM_EN_POS);
|
|
|
|
pwm_timer_controlreg_set(reg_idx, ctl_reg);
|
|
}
|
|
|
|
void xc_pwm_timer_deinit(uint8_t reg_idx)
|
|
{
|
|
pwm_clk_init_state = STATE_UNINITIALIZED;
|
|
cpr_ctlapbclken_grctl__pwm_pclk_en__setf(DISABLE);
|
|
}
|
|
|
|
/**
|
|
****************************************************************************************
|
|
* @brief
|
|
*
|
|
* @param[in]
|
|
* @param[in]
|
|
****************************************************************************************
|
|
*/
|
|
void xc_pwm_timer_set_freq(uint8_t reg_idx, uint32_t freq) { period = xc_clock_hfclk_in_get() / 2 / freq; }
|
|
|
|
void xc_pwm_timer_set_dutycycle(uint8_t reg_idx, double dutycycle)
|
|
{
|
|
uint16_t high_cnt = ((uint32_t)(period * dutycycle) / 100);
|
|
uint16_t low_cnt = (uint32_t)(period - high_cnt);
|
|
|
|
pwm_timer_loadcount__timer_lc__setf(reg_idx, low_cnt);
|
|
pwm_timer_loadcount2__timer_lc2__setf(reg_idx, high_cnt);
|
|
}
|
|
|
|
void xc_pwm_timer_set_high_cnt(uint8_t reg_idx, uint16_t high_cnt)
|
|
{
|
|
pwm_timer_loadcount2__timer_lc2__setf(reg_idx, high_cnt);
|
|
}
|
|
|
|
void xc_pwm_timer_set_low_cnt(uint8_t reg_idx, uint16_t low_cnt)
|
|
{
|
|
pwm_timer_loadcount__timer_lc__setf(reg_idx, low_cnt);
|
|
}
|
|
/**
|
|
****************************************************************************************
|
|
* @brief
|
|
*
|
|
* @param[in]
|
|
* @param[in]
|
|
****************************************************************************************
|
|
*/
|
|
void xc_pwm_timer_start(uint8_t reg_idx) { pwm_timer_controlreg__timer_en__setf(reg_idx, ENABLE); }
|
|
|
|
void xc_pwm_timer_stop(uint8_t reg_idx) { pwm_timer_controlreg__timer_en__setf(reg_idx, DISABLE); }
|
|
|
|
/*-----------------------------------------------------------------------------------------------*/
|
|
|
|
void PWM_Handler(void)
|
|
{
|
|
uint32_t cap_tim_int;
|
|
|
|
cap_tim_int = pwm_cap_tim_int_get();
|
|
|
|
pwm_cap_tim_int_set(cap_tim_int); // clear inter
|
|
|
|
if ((cap_tim_int & PWM_CAPTURE_UPD0_EN_ENABLE) == PWM_CAPTURE_UPD0_EN_ENABLE) {
|
|
pwm_capture_ch0_callback(NULL);
|
|
}
|
|
if ((cap_tim_int & PWM_CAPTURE_UPD1_EN_ENABLE) == PWM_CAPTURE_UPD1_EN_ENABLE) {
|
|
pwm_capture_ch1_callback(NULL);
|
|
}
|
|
if ((cap_tim_int & PWM_CAPTURE_UPD2_EN_ENABLE) == PWM_CAPTURE_UPD2_EN_ENABLE) {
|
|
pwm_capture_ch2_callback(NULL);
|
|
}
|
|
}
|
|
|
|
__WEAK uint8_t pwm_capture_ch0_callback(void *context) { return 0; }
|
|
|
|
__WEAK uint8_t pwm_capture_ch1_callback(void *context) { return 0; }
|
|
|
|
__WEAK uint8_t pwm_capture_ch2_callback(void *context) { return 0; }
|