491 lines
15 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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~10000对应0~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();
}
}