/*================================================================ Copyright (c) 2022, Quectel Wireless Solutions Co., Ltd. All rights reserved. Quectel Wireless Solutions Proprietary and Confidential. =================================================================*/ /*================================================================= EDIT HISTORY FOR MODULE This section contains comments describing changes made to the module. Notice that changes are listed in reverse chronological order. WHEN WHO WHAT, WHERE, WHY ------------ ------- ------------------------------------------------------------------------------- 19/04/2022 Fias.fan Added PWM interface =================================================================*/ #include "quec_pwm.h" #include "utlTimer.h" //#include "quec_atcmd_network.h" #include "quec_rtos.h" #include "quec_gpio.h" #include "gpio.h" #include "bsp.h" #if defined QUEC_PWM_ENBALE #ifndef MIN_SYS #if defined QUEC_PWM_LOG_ENABLE #define pwm_log(msg, ...) custom_log("PWM", msg, ##__VA_ARGS__) #else #define pwm_log(msg, ...) #endif #define PWM_PSCLK (13000000) // 13 MHz #define PWM_PSCLK_32K (32000) //32KHz #define PWM_MAX_FREQ (PWM_PSCLK) #define PWM_SD_POS (6) #define PWM_FD_POS (10) #define PWM_FREQ_MASK (0x3f) #define PWM_DUTY_CYCLE_MASK (0x3ff) #define PWM_PERIOD_MASK (0x3ff) #define PWM_0_START_ADDRESS (0xD401A000) #define PWM_1_START_ADDRESS (0xD401A400) #define PWM_2_START_ADDRESS (0xD401A800) #define PWM_3_START_ADDRESS (0xD401AC00) #define APBC_PWM0_CLK_RST (0xD401500C) #define APBC_PWM1_CLK_RST (0xD4015010) #define APBC_PWM2_CLK_RST (0xD4015014) #define APBC_PWM3_CLK_RST (0xD4015018) #define PWM_RST_BIT 0x4 #define PWM_FUNC_CLK_EN_BIT 0x2 #define PWM_APB_CLK_EN_BIT 0x1 #define pwm_reg_read(reg) *(volatile unsigned int *)(reg) #define pwm_reg_write(reg, val) *(volatile unsigned int *)(reg) = (val); #define pwm_reg_bit_set(reg, val) *(volatile unsigned int *)(reg) |= (val); #define pwm_reg_bit_clr(reg, val) *(volatile unsigned int *)(reg) &= ~(val); static volatile QUEC_PWM_REGISTERED_T *pwm_register[QUEC_PWM_MAX_DEVICE] = { (volatile QUEC_PWM_REGISTERED_T *)(PWM_0_START_ADDRESS), (volatile QUEC_PWM_REGISTERED_T *)(PWM_1_START_ADDRESS), (volatile QUEC_PWM_REGISTERED_T *)(PWM_2_START_ADDRESS), (volatile QUEC_PWM_REGISTERED_T *)(PWM_3_START_ADDRESS) }; static unsigned long pwm_apb_register[QUEC_PWM_MAX_DEVICE] = { (APBC_PWM0_CLK_RST), (APBC_PWM1_CLK_RST), (APBC_PWM2_CLK_RST), (APBC_PWM3_CLK_RST) }; struct QUEC_PWM_MAP_GPIO_S gpio_map_pwm_table[] = { {117, 0, 2}, {32, 1, 2}, {10, 2, 3}, {11, 3, 3}, #ifdef __QUECTEL_PROJECT_EG800WCN_11LG__ {6, 2, 5}, #else {6, 2, 3}, #endif #ifdef __QUECTEL_PROJECT_EG800PCN_LA__ {8, 2, 1}, #else {8, 0, 1}, #endif #ifdef __QUECTEL_PROJECT_EG800WCN_11LG__ {7, 3, 5}, {9, 3, 3}, #else {7, 3, 3}, #endif {69, 0, 1}, {70, 1, 1}, {89, 2, 2}, {29, 0, 1}, {56, 1, 2}, {58, 3, 2}, }; extern void Delay(unsigned long uSec); void quec_pwm_apb_bus_clk_enable(QUEC_PWM_DEVICE_NUM_E pwm_num) { if(IsChipCraneLRH() || IsChipCraneLG()) { pwm_reg_bit_clr(pwm_apb_register[pwm_num],PWM_RST_BIT); pwm_reg_bit_set(pwm_apb_register[pwm_num],PWM_APB_CLK_EN_BIT); //CPUartLogPrintf("%s,addr=%x,,reg=%x",__func__,pwm_apb_register[pwm_num],pwm_reg_read(pwm_apb_register[pwm_num])); } else { if(pwm_num <= QUEC_PWM_DEVICE_1) { pwm_reg_bit_clr(pwm_apb_register[QUEC_PWM_DEVICE_0],PWM_RST_BIT); pwm_reg_bit_set(pwm_apb_register[QUEC_PWM_DEVICE_0],PWM_APB_CLK_EN_BIT); //CPUartLogPrintf("%s,addr=%x,,reg=%x",__func__,pwm_apb_register[QUEC_PWM_DEVICE_0],pwm_reg_read(pwm_apb_register[QUEC_PWM_DEVICE_0])); } else { pwm_reg_bit_clr(pwm_apb_register[QUEC_PWM_DEVICE_2],PWM_RST_BIT); pwm_reg_bit_set(pwm_apb_register[QUEC_PWM_DEVICE_2],PWM_APB_CLK_EN_BIT); //CPUartLogPrintf("%s,addr=%x,,reg=%x",__func__,pwm_apb_register[QUEC_PWM_DEVICE_2],pwm_reg_read(pwm_apb_register[QUEC_PWM_DEVICE_2])); } } } //获取GPIO复用为pwm功能的pwm号 int quec_gpio_mapto_pwm_device_num(unsigned int gpio_num) { unsigned int i; for(i = 0; i < (sizeof(gpio_map_pwm_table)/sizeof(gpio_map_pwm_table[0])); i++) { if(gpio_map_pwm_table[i].gpio_num == gpio_num) { return gpio_map_pwm_table[i].pwm_num; } } return QUEC_PWM_MAX_DEVICE; } //获取GPIO复用为pwm功能的fun号 int quec_pwm_get_gpio_function(unsigned int gpio_num) { unsigned int i; for(i = 0; i < (sizeof(gpio_map_pwm_table)/sizeof(gpio_map_pwm_table[0])); i++) { if(gpio_map_pwm_table[i].gpio_num == gpio_num) { return gpio_map_pwm_table[i].gpio_fun; } } return -1; } void quec_pwm_apb_bus_clk_disable(QUEC_PWM_DEVICE_NUM_E pwm_num) { if(IsChipCraneLRH() || IsChipCraneLG()) { pwm_reg_bit_clr(pwm_apb_register[pwm_num],PWM_APB_CLK_EN_BIT); //CPUartLogPrintf("%s,addr=%x,,reg=%x",__func__,pwm_apb_register[pwm_num],pwm_reg_read(pwm_apb_register[pwm_num])); } else { if(pwm_num <= QUEC_PWM_DEVICE_1) { pwm_reg_bit_clr(pwm_apb_register[QUEC_PWM_DEVICE_0],PWM_APB_CLK_EN_BIT); //CPUartLogPrintf("%s,addr=%x,,reg=%x",__func__,pwm_apb_register[QUEC_PWM_DEVICE_0],pwm_reg_read(pwm_apb_register[QUEC_PWM_DEVICE_0])); } else { pwm_reg_bit_clr(pwm_apb_register[QUEC_PWM_DEVICE_2],PWM_APB_CLK_EN_BIT); //CPUartLogPrintf("%s,addr=%x,,reg=%x",__func__,pwm_apb_register[QUEC_PWM_DEVICE_2],pwm_reg_read(pwm_apb_register[QUEC_PWM_DEVICE_2])); } } } int quec_pwm_enable(unsigned int gpio_num) { int pwm_num = 0; pwm_num = (QUEC_PWM_DEVICE_NUM_E)quec_gpio_mapto_pwm_device_num(gpio_num); if( pwm_num >= QUEC_PWM_MAX_DEVICE ) { return -1; } //barry 解决宝蓝低功耗pwm无法关闭 pwm_reg_bit_clr(pwm_apb_register[pwm_num],PWM_RST_BIT); quec_pwm_apb_bus_clk_enable((QUEC_PWM_DEVICE_NUM_E)pwm_num); Delay(1*1000); pwm_reg_bit_set(pwm_apb_register[pwm_num],PWM_FUNC_CLK_EN_BIT); //pwm_log("%s, addr=%x,,reg=%x",__func__,pwm_apb_register[pwm_num],pwm_reg_read(pwm_apb_register[pwm_num])); return 0; } int quec_pwm_disable(unsigned int gpio_num) { int pwm_num = 0; pwm_num = quec_gpio_mapto_pwm_device_num(gpio_num); if ( pwm_num >= QUEC_PWM_MAX_DEVICE ) return -1; /* Warning !!! Using of a Graceful shutdown should be done with a care */ /* Writing 0 to the SD bit, needs a little delay before turn the clock to off */ pwm_reg_bit_clr(pwm_apb_register[pwm_num],PWM_FUNC_CLK_EN_BIT); Delay(100*1000); quec_pwm_apb_bus_clk_disable((QUEC_PWM_DEVICE_NUM_E)pwm_num); //pwm_log("%s, addr=%x,reg=%x",__func__,pwm_apb_register[pwm_num],pwm_reg_read(pwm_apb_register[pwm_num])); return 0; } int quec_pwm_pinmux_configure(unsigned int gpio_num) { int fun = quec_pwm_get_gpio_function(gpio_num); if(fun != -1) { (*(volatile unsigned long *)(GPIO_MFPR_ADDR(gpio_num))) = (0x10c0 | fun); return 0; } return -1; } QUEC_PWM_RETURN_CODE_E quec_pwm_configure( QUEC_PWM_DEVICE_NUM_E pwm_num , QUEC_PWM_CONFIG_T *configuration ) { unsigned short total_cycle; unsigned char i; if ( pwm_num >= QUEC_PWM_MAX_DEVICE ) return QUEC_PWM_RC_ILLEGALL_DEVICE_NUMBER; if( (PWM_PSCLK/configuration->clk_freq ) > PWM_MAX_FREQ ) return QUEC_PWM_RC_ILLEGALL_FREQUENCY; if ( configuration->high_one_cycle_duration > configuration->total_one_cycle_duration ) return QUEC_PWM_RC_ILLEGALL_DUTY_CYCLE; quec_pwm_apb_bus_clk_enable(pwm_num); pwm_reg_bit_clr(pwm_apb_register[pwm_num],PWM_RST_BIT); //clear rst bit pwm_reg_bit_clr(pwm_apb_register[pwm_num], 1<< 4); //clr clk src pwm_reg_bit_set(pwm_apb_register[pwm_num],configuration->clk_src << 4); //set clk src 32K or 13M pwm_register[pwm_num]->control = ( ( configuration->shutdown << PWM_SD_POS) | (( configuration->clk_freq - 1 ) & PWM_FREQ_MASK )); pwm_register[pwm_num]->duty_cycle = (( configuration->pwm_out << PWM_FD_POS) | (configuration->high_one_cycle_duration & PWM_DUTY_CYCLE_MASK)); pwm_register[pwm_num]->period_val = ( configuration->total_one_cycle_duration & PWM_PERIOD_MASK); //pwm_log("%s,control=%x,,duty_cycle=%x,, period_val=%x",__func__,pwm_register[pwm_num]->control,pwm_register[pwm_num]->duty_cycle,pwm_register[pwm_num]->period_val); return QUEC_PWM_RC_OK; } int quec_pwm_config_plus_ex(unsigned int gpio_num,QL_PWM_CONFIG_EX *pwm_parameter) { int ret = 0; int pwm_num = 0; QUEC_PWM_CONFIG_T pwm_config ; memset(&pwm_config,0,sizeof(QUEC_PWM_CONFIG_T)); if(CHIP_IS_CRANEL) { if(gpio_num != 117 && gpio_num != 32 && gpio_num != 11 && gpio_num != 10 && gpio_num != 6 && gpio_num != 8 && gpio_num != 7){ return -1; } } else if(CHIP_IS_CRANELR) { if(gpio_num != 69 && gpio_num != 70 && gpio_num != 89){ return -1; } } else if(CHIP_IS_CRANELS) { if(gpio_num != 29 && gpio_num != 56 && gpio_num != 58){ return -1; } } else if(CHIP_IS_CRANELG) { if(gpio_num != 6 && gpio_num != 7 && gpio_num != 9 && gpio_num != 69 && gpio_num != 70 && gpio_num != 89){ return -1; } } //Although the register has a full duty cycle output of 1, it will show a little duty cycle from the oscilloscope, so it is replaced by a gpio output high and low level if(pwm_parameter->high_one_cycle_duration==0 || pwm_parameter->total_one_cycle_duration==0) { quec_gpio_init( (GPIO_PIN_NUMBER_E)gpio_num,PIN_DIRECTION_OUT, PIN_PULL_PD,PIN_LEVEL_LOW); return 0; } if(pwm_parameter->high_one_cycle_duration==pwm_parameter->total_one_cycle_duration) { quec_gpio_init( (GPIO_PIN_NUMBER_E)gpio_num,PIN_DIRECTION_OUT, PIN_PULL_PU,PIN_LEVEL_HIGH ); return 0; } /**/ pwm_num = quec_gpio_mapto_pwm_device_num(gpio_num); ret = quec_pwm_pinmux_configure(gpio_num); if(ret != 0) { //pwm_log("gpio number =%d is invalid.\n",gpio_num); return -1; } pwm_config.shutdown = QUEC_PWM_SHUTDOWN_ABRUPTLY; pwm_config.pwm_out = QUEC_PWM_OUT_SET_BY_DCYCLE; pwm_config.clk_src = (QUEC_PWM_CLK_SRC_E)pwm_parameter->clk_src; pwm_config.clk_freq =pwm_parameter->pwm_psc; pwm_config.high_one_cycle_duration = pwm_parameter->high_one_cycle_duration; pwm_config.total_one_cycle_duration = pwm_parameter->total_one_cycle_duration ; // printf("PWM=%d\n %d\n %ld\n %d\n %d\n",gpio_num,pwm_parameter->clk_src,pwm_parameter->pwm_psc,pwm_parameter->high_one_cycle_duration,pwm_parameter->total_one_cycle_duration); if(pwm_config.total_one_cycle_duration>1024) { return -1; } ret = (int)quec_pwm_configure((QUEC_PWM_DEVICE_NUM_E)pwm_num,&pwm_config); if((QUEC_PWM_RETURN_CODE_E)ret != QUEC_PWM_RC_OK) { return -1; } return 0; } #endif /*MIN_SYS*/ #endif