491 lines
15 KiB
C
Raw Normal View History

#include "asm/mcpwm.h"
#include "asm/clock.h"
#include "asm/gpio.h"
#define MCPWM_DEBUG_ENABLE 0
#if MCPWM_DEBUG_ENABLE
#define mcpwm_debug(fmt, ...) printf("[MCPWM] "fmt, ##__VA_ARGS__)
#else
#define mcpwm_debug(...)
#endif
#define MCPWM_CLK clk_get("mcpwm")
PWM_TIMER_REG *get_pwm_timer_reg(pwm_ch_num_type index)
{
PWM_TIMER_REG *reg = NULL;
switch (index) {
case pwm_ch0:
reg = (PWM_TIMER_REG *)(&(JL_MCPWM->TMR0_CON));
break;
case pwm_ch1:
reg = (PWM_TIMER_REG *)(&(JL_MCPWM->TMR1_CON));
break;
case pwm_ch2:
reg = (PWM_TIMER_REG *)(&(JL_MCPWM->TMR2_CON));
break;
case pwm_ch3:
reg = (PWM_TIMER_REG *)(&(JL_MCPWM->TMR3_CON));
break;
default:
break;
}
return reg;
}
PWM_CH_REG *get_pwm_ch_reg(pwm_ch_num_type index)
{
PWM_CH_REG *reg = NULL;
switch (index) {
case pwm_ch0:
reg = (PWM_CH_REG *)(&(JL_MCPWM->CH0_CON0));
break;
case pwm_ch1:
reg = (PWM_CH_REG *)(&(JL_MCPWM->CH1_CON0));
break;
case pwm_ch2:
reg = (PWM_CH_REG *)(&(JL_MCPWM->CH2_CON0));
break;
case pwm_ch3:
reg = (PWM_CH_REG *)(&(JL_MCPWM->CH3_CON0));
break;
default:
break;
}
return reg;
}
/*
* @brief MCPWM的频率
* @parm frequency
*/
void mcpwm_set_frequency(pwm_ch_num_type ch, pwm_aligned_mode_type align, u32 frequency)
{
PWM_TIMER_REG *reg = get_pwm_timer_reg(ch);
if (reg == NULL) {
return;
}
reg->tmr_con = 0;
reg->tmr_cnt = 0;
reg->tmr_pr = 0;
u32 i = 0;
u32 mcpwm_div_clk = 0;
u32 mcpwm_tmr_pr = 0;
u32 mcpwm_fre_min = 0;
u32 clk = MCPWM_CLK;
for (i = 0; i < 16; i++) {
mcpwm_fre_min = clk / (65536 * (1 << i));
if ((frequency >= mcpwm_fre_min) || (i == 15)) {
break;
}
}
reg->tmr_con |= (i << 3); //div 2^i
mcpwm_div_clk = clk / (1 << i);
if (frequency == 0) {
mcpwm_tmr_pr = 0;
} else {
if (align == pwm_center_aligned) { //中心对齐
mcpwm_tmr_pr = mcpwm_div_clk / (frequency * 2) - 1;
} else {
mcpwm_tmr_pr = mcpwm_div_clk / frequency - 1;
}
}
reg->tmr_pr = mcpwm_tmr_pr;
//timer mode
if (align == pwm_center_aligned) { //中心对齐
reg->tmr_con |= 0b10;
} else {
reg->tmr_con |= 0b01;
}
}
static u32 old_mcpwm_clk = 0;
static void clock_critical_enter(void)
{
old_mcpwm_clk = clk_get("mcpwm");
}
static void clock_critical_exit(void)
{
u32 new_mcpwm_clk = clk_get("mcpwm");
if (new_mcpwm_clk == old_mcpwm_clk) {
return;
}
PWM_CH_REG *pwm_reg = NULL;
PWM_TIMER_REG *timer_reg = NULL;
for (u8 ch = 0; ch < 4; ch++) {
if (JL_MCPWM->MCPWM_CON0 & BIT(ch + 8)) {
pwm_reg = get_pwm_ch_reg(ch);
timer_reg = get_pwm_timer_reg(ch);
if (new_mcpwm_clk > old_mcpwm_clk) {
timer_reg->tmr_pr = timer_reg->tmr_pr * (new_mcpwm_clk / old_mcpwm_clk);
pwm_reg->ch_cmpl = pwm_reg->ch_cmpl * (new_mcpwm_clk / old_mcpwm_clk);
} else {
timer_reg->tmr_pr = timer_reg->tmr_pr / (old_mcpwm_clk / new_mcpwm_clk);
pwm_reg->ch_cmpl = pwm_reg->ch_cmpl / (old_mcpwm_clk / new_mcpwm_clk);
}
pwm_reg->ch_cmph = pwm_reg->ch_cmpl;
}
}
}
CLOCK_CRITICAL_HANDLE_REG(mcpwm, clock_critical_enter, clock_critical_exit)
/*
* @brief
* @parm pwm_ch_num pwm_ch0pwm_ch1pwm_ch2
* @parm duty 0 ~ 10000 0% ~ 100%
*/
void mcpwm_set_duty(pwm_ch_num_type pwm_ch, u16 duty)
{
PWM_TIMER_REG *timer_reg = get_pwm_timer_reg(pwm_ch);
PWM_CH_REG *pwm_reg = get_pwm_ch_reg(pwm_ch);
if (pwm_reg && timer_reg) {
pwm_reg->ch_cmpl = timer_reg->tmr_pr * duty / 10000;
pwm_reg->ch_cmph = pwm_reg->ch_cmpl;
timer_reg->tmr_cnt = 0;
timer_reg->tmr_con |= 0b01;
if (duty == 10000) {
timer_reg->tmr_cnt = 0;
timer_reg->tmr_con &= ~(0b11);
} else if (duty == 0) {
timer_reg->tmr_cnt = pwm_reg->ch_cmpl;
timer_reg->tmr_con &= ~(0b11);
}
}
}
/*
* @brief
* @parm pwm_ch_num pwm_ch0pwm_ch1pwm_ch2
* @parm enable 1 0
*/
void mctimer_ch_open_or_close(pwm_ch_num_type pwm_ch, u8 enable)
{
if (pwm_ch > pwm_ch_max) {
return;
}
if (enable) {
JL_MCPWM->MCPWM_CON0 |= BIT(pwm_ch + 8); //TnEN
} else {
JL_MCPWM->MCPWM_CON0 &= (~BIT(pwm_ch + 8)); //TnDIS
}
}
/*
* @brief
* @parm pwm_ch_num pwm_ch0pwm_ch1pwm_ch2
* @parm enable 1 0
*/
void mcpwm_ch_open_or_close(pwm_ch_num_type pwm_ch, u8 enable)
{
if (pwm_ch >= pwm_ch_max) {
return;
}
if (enable) {
JL_MCPWM->MCPWM_CON0 |= BIT(pwm_ch); //PWMnEN
} else {
JL_MCPWM->MCPWM_CON0 &= (~BIT(pwm_ch)); //PWMnDIS
}
}
/*
* @brief MCPWM模块
*/
void mcpwm_open(pwm_ch_num_type pwm_ch)
{
if (pwm_ch >= pwm_ch_max) {
return;
}
PWM_CH_REG *pwm_reg = get_pwm_ch_reg(pwm_ch);
pwm_reg->ch_con1 &= ~(0b111 << 8);
pwm_reg->ch_con1 |= (pwm_ch << 8); //sel mctmr
mcpwm_ch_open_or_close(pwm_ch, 1);
mctimer_ch_open_or_close(pwm_ch, 1);
}
/*
* @brief MCPWM模块
*/
void mcpwm_close(pwm_ch_num_type pwm_ch)
{
mctimer_ch_open_or_close(pwm_ch, 0);
mcpwm_ch_open_or_close(pwm_ch, 0);
}
void log_pwm_info(pwm_ch_num_type pwm_ch)
{
PWM_CH_REG *pwm_reg = get_pwm_ch_reg(pwm_ch);
PWM_TIMER_REG *timer_reg = get_pwm_timer_reg(pwm_ch);
mcpwm_debug("tmr%d con0 = 0x%x", pwm_ch, timer_reg->tmr_con);
mcpwm_debug("tmr%d pr = 0x%x", pwm_ch, timer_reg->tmr_pr);
mcpwm_debug("pwm ch%d_con0 = 0x%x", pwm_ch, pwm_reg->ch_con0);
mcpwm_debug("pwm ch%d_con1 = 0x%x", pwm_ch, pwm_reg->ch_con1);
mcpwm_debug("pwm ch%d_cmph = 0x%x, pwm ch%d_cmpl = 0x%x", pwm_ch, pwm_reg->ch_cmph, pwm_ch, pwm_reg->ch_cmpl);
mcpwm_debug("MCPWM_CON0 = 0x%x", JL_MCPWM->MCPWM_CON0);
mcpwm_debug("mcpwm clk = %d", MCPWM_CLK);
}
void mcpwm_init(struct pwm_platform_data *arg)
{
//set output IO
PWM_CH_REG *pwm_reg = get_pwm_ch_reg(arg->pwm_ch_num);
if (pwm_reg == NULL) {
return;
}
//set mctimer frequency
mcpwm_set_frequency(arg->pwm_ch_num, arg->pwm_aligned_mode, arg->frequency);
pwm_reg->ch_con0 = 0;
if (arg->complementary_en) { //是否互补
pwm_reg->ch_con0 &= ~(BIT(5) | BIT(4));
pwm_reg->ch_con0 |= BIT(5); //L_INV
} else {
pwm_reg->ch_con0 &= ~(BIT(5) | BIT(4));
}
mcpwm_open(arg->pwm_ch_num); //mcpwm enable
//set duty
mcpwm_set_duty(arg->pwm_ch_num, arg->duty);
//H:
if (arg->h_pin < IO_MAX_NUM) { //任意引脚
pwm_reg->ch_con0 |= BIT(2); //H_EN
gpio_set_fun_output_port(arg->h_pin, FO_MCPWM_CH0H + 2 * arg->pwm_ch_num, 0, 1);
gpio_set_direction(arg->h_pin, 0); //DIR output
}
//L:
if (arg->l_pin < IO_MAX_NUM) { //任意引脚
pwm_reg->ch_con0 |= BIT(3); //L_EN
gpio_set_fun_output_port(arg->l_pin, FO_MCPWM_CH0L + 2 * arg->pwm_ch_num, 0, 1);
gpio_set_direction(arg->l_pin, 0); //DIR output
}
log_pwm_info(arg->pwm_ch_num);
}
///////////// for test code //////////////////
void mcpwm_test(void)
{
#define PWM_CH0_ENABLE 1
#define PWM_CH1_ENABLE 1
#define PWM_CH2_ENABLE 1
#define PWM_CH3_ENABLE 1
struct pwm_platform_data pwm_p_data;
#if PWM_CH0_ENABLE
pwm_p_data.pwm_aligned_mode = pwm_edge_aligned; //边沿对齐
pwm_p_data.pwm_ch_num = pwm_ch0; //通道号
pwm_p_data.frequency = 1000; //1KHz
pwm_p_data.duty = 5000; //占空比50%
pwm_p_data.h_pin = IO_PORTA_06; //任意引脚
pwm_p_data.l_pin = -1; //任意引脚,不需要就填-1
pwm_p_data.complementary_en = 0; //两个引脚的波形, 0: 同步, 1: 互补互补波形的占空比体现在H引脚上
mcpwm_init(&pwm_p_data);
#endif
#if PWM_CH1_ENABLE
pwm_p_data.pwm_aligned_mode = pwm_edge_aligned; //边沿对齐
pwm_p_data.pwm_ch_num = pwm_ch1; //通道号
pwm_p_data.frequency = 1000; //1KHz
pwm_p_data.duty = 2500; //占空比25%
pwm_p_data.h_pin = IO_PORTA_01; //任意引脚
pwm_p_data.l_pin = IO_PORTA_02; //任意引脚,不需要就填-1
pwm_p_data.complementary_en = 1; //两个引脚的波形, 0: 同步, 1: 互补互补波形的占空比体现在H引脚上
mcpwm_init(&pwm_p_data);
#endif
#if PWM_CH2_ENABLE
pwm_p_data.pwm_aligned_mode = pwm_edge_aligned; //边沿对齐
pwm_p_data.pwm_ch_num = pwm_ch2; //通道号
pwm_p_data.frequency = 10000; //10KHz
pwm_p_data.duty = 5000; //占空比50%
pwm_p_data.h_pin = IO_PORTA_03; //任意引脚
pwm_p_data.l_pin = -1; //任意引脚,不需要就填-1
mcpwm_init(&pwm_p_data);
#endif
#if PWM_CH3_ENABLE
pwm_p_data.pwm_aligned_mode = pwm_edge_aligned; //边沿对齐
pwm_p_data.pwm_ch_num = pwm_ch3; //通道号
pwm_p_data.frequency = 10000; //10KHz
pwm_p_data.duty = 7500; //占空比75%
pwm_p_data.h_pin = IO_PORTA_04; //任意引脚
pwm_p_data.l_pin = IO_PORTA_05; //任意引脚,不需要就填-1
pwm_p_data.complementary_en = 0; //两个引脚的波形, 0: 同步, 1: 互补互补波形的占空比体现在H引脚上
mcpwm_init(&pwm_p_data);
#endif
extern void clk_out(u8 gpio, enum CLK_OUT_SOURCE clk);
clk_out(IO_PORTA_07, LSB_CLK_OUT);
extern void wdt_clear();
while (1) {
wdt_clear();
}
}
/******************************* 外部引脚中断参考代码 ***************************/
void (*io_isr_cbfun)(u8 index) = NULL;
void set_io_ext_interrupt_cbfun(void (*cbfun)(u8 index))
{
io_isr_cbfun = cbfun;
}
___interrupt
void io_interrupt(void)
{
u32 io_index = -1;
if (JL_MCPWM->CH0_CON1 & BIT(15)) {
JL_MCPWM->CH0_CON1 |= BIT(14);
io_index = 0;
} else if (JL_MCPWM->CH1_CON1 & BIT(15)) {
JL_MCPWM->CH1_CON1 |= BIT(14);
io_index = 1;
} else if (JL_MCPWM->CH2_CON1 & BIT(15)) {
JL_MCPWM->CH2_CON1 |= BIT(14);
io_index = 2;
} else if (JL_MCPWM->CH3_CON1 & BIT(15)) {
JL_MCPWM->CH3_CON1 |= BIT(14);
io_index = 3;
} else {
return;
}
if (io_isr_cbfun) {
io_isr_cbfun(io_index);
}
}
void io_ext_interrupt_init(u8 index, u8 port, u8 trigger_mode)
{
if (port > IO_PORT_MAX) {
return;
}
gpio_set_die(port, 1);
gpio_set_direction(port, 1);
if (trigger_mode) {
gpio_set_pull_up(port, 1);
gpio_set_pull_down(port, 0);
JL_MCPWM->FPINCON &= ~BIT(16 + index);//下降沿触发
} else {
gpio_set_pull_up(port, 0);
gpio_set_pull_down(port, 1);
JL_MCPWM->FPINCON |= BIT(16 + index);//上升沿触发
}
JL_MCPWM->FPINCON |= BIT(8 + index);//开启滤波
JL_MCPWM->FPINCON |= (0b111111 << 0); //滤波时间 = 16 * 64 / hsb_clk (单位s)
gpio_set_fun_input_port(port, PFI_MCPWM_FPIN_A + index * 4);
request_irq(IRQ_CHX_PWM_IDX, 3, io_interrupt, 0); //注册中断函数
PWM_CH_REG *pwm_reg = get_pwm_ch_reg(index);
pwm_reg->ch_con1 = BIT(14) | BIT(11) | BIT(4) | (index << 0);
JL_MCPWM->MCPWM_CON0 |= BIT(index);
printf("JL_MCPWM->CH%d_CON1 = 0x%x\n", index, pwm_reg->ch_con1);
printf("JL_MCPWM->FPINCON = 0x%x\n", JL_MCPWM->FPINCON);
printf("JL_MCPWM->MCPWM_CON0 = 0x%x\n", JL_MCPWM->MCPWM_CON0);
}
void io_ext_interrupt_close(u8 index, u8 port)
{
if (port > IO_PORT_MAX) {
return;
}
gpio_set_die(port, 0);
gpio_set_direction(port, 1);
gpio_set_pull_up(port, 0);
gpio_set_pull_down(port, 0);
gpio_disable_fun_input_port(PFI_MCPWM_FPIN_A + index * 4);
PWM_CH_REG *pwm_reg = get_pwm_ch_reg(index);
pwm_reg->ch_con1 = BIT(14);
}
///////////// 使用举例如下 //////////////////
void my_io_isr_cbfun(u32 index)
{
printf("io index --> %d Hello world !\n", index);
}
void io_ext_interrupt_test(void)
{
set_io_ext_interrupt_cbfun(my_io_isr_cbfun);
io_ext_interrupt_init(0, IO_PORTA_01, 1);
io_ext_interrupt_init(1, IO_PORTA_02, 1);
io_ext_interrupt_init(2, IO_PORTA_03, 1);
io_ext_interrupt_init(3, IO_PORTA_04, 1);
extern void wdt_clear();
while (1) {
wdt_clear();
}
}
/******************************* 用timer做pwm的参考代码 ***************************/
/**
* @param JL_TIMERx : JL_TIMER0/1/2/3
* @param pwm_io : JL_PORTA_01, JL_PORTB_02,,,IO
* @param fre : Hz
* @param duty : 0~100000~100%
*/
void timer_pwm_init(JL_TIMER_TypeDef *JL_TIMERx, u32 pwm_io, u32 fre, u32 duty)
{
switch ((u32)JL_TIMERx) {
case (u32)JL_TIMER0 :
gpio_set_fun_output_port(pwm_io, FO_TMR0_PWM, 0, 1);
break;
case (u32)JL_TIMER1 :
gpio_set_fun_output_port(pwm_io, FO_TMR1_PWM, 0, 1);
break;
case (u32)JL_TIMER2 :
gpio_set_fun_output_port(pwm_io, FO_TMR2_PWM, 0, 1);
break;
case (u32)JL_TIMER3 :
bit_clr_ie(IRQ_TIME3_IDX);
gpio_set_fun_output_port(pwm_io, FO_TMR3_PWM, 0, 1);
break;
default:
return;
}
u32 u_clk = 24000000;
//初始化timer
JL_TIMERx->CON = 0;
JL_TIMERx->CON |= (0b110 << 10); //时钟源选择STD_24M时钟源
JL_TIMERx->CON |= (0b0001 << 4); //时钟源再4分频
JL_TIMERx->CNT = 0; //清计数值
JL_TIMERx->PRD = u_clk / (4 * fre); //设置周期
JL_TIMERx->PWM = (JL_TIMERx->PRD * duty) / 10000; //设置初始占空比0~10000对应0~100%
JL_TIMERx->CON |= (0b01 << 0); //计数模式
JL_TIMERx->CON |= BIT(8); //PWM使能
//设置引脚状态
gpio_set_die(pwm_io, 1);
gpio_set_pull_up(pwm_io, 0);
gpio_set_pull_down(pwm_io, 0);
gpio_set_direction(pwm_io, 0);
printf("JL_TIMERx->PRD = 0x%x\n", JL_TIMERx->PRD);
printf("JL_TIMERx->CON = 0x%x\n", JL_TIMERx->CON);
}
void set_timer_pwm_duty(JL_TIMER_TypeDef *JL_TIMERx, u32 duty)
{
JL_TIMERx->PWM = (JL_TIMERx->PRD * duty) / 10000; //0~10000对应0~100%
}
void timer_pwm_test(void)
{
timer_pwm_init(JL_TIMER2, IO_PORTA_01, 10000, 2000);
timer_pwm_init(JL_TIMER3, IO_PORTA_02, 10000, 5000);
extern void wdt_clear();
while (1) {
wdt_clear();
}
}