469 lines
15 KiB
C
Raw Permalink 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")
/*mcpwm硬件引脚上下为一对---CH0--- ---CH1--- ---CH2--- ---CH3--- */
static u8 pwm_hw_h_pin[4] = {IO_PORTA_00, IO_PORTA_07, IO_PORTB_02, IO_PORTB_04};
static u8 pwm_hw_l_pin[4] = {IO_PORTA_03, IO_PORTA_08, IO_PORTB_03, IO_PORTB_05};
//fpin
static u8 pwm_fpin[4] = {IO_PORTA_02, IO_PORTA_04, IO_PORTB_07, IO_PORTA_01};
//mctmr extern clk in pin
static u8 mctmr_clkin_pin[4] = {IO_PORTA_05, IO_PORTA_06, IO_PORTB_00, IO_PORTB_06};
//output_channle
static u8 CHx_CHx_PWM_H[3][3] = {CH0_CH0_PWM_H, CH0_CH1_PWM_H, CH0_CH2_PWM_H, CH1_CH0_PWM_H, CH1_CH1_PWM_H, CH1_CH2_PWM_H, CH2_CH0_PWM_H, CH2_CH1_PWM_H, CH2_CH2_PWM_H};
static u8 CHx_CHx_PWM_L[3][3] = {CH0_CH0_PWM_L, CH0_CH1_PWM_L, CH0_CH2_PWM_L, CH1_CH0_PWM_L, CH1_CH1_PWM_L, CH1_CH2_PWM_L, CH2_CH0_PWM_L, CH2_CH1_PWM_L, CH2_CH2_PWM_L};
PWM_TIMER_REG *get_pwm_timer_reg(pwm_timer_num_type index)
{
PWM_TIMER_REG *reg = NULL;
switch (index) {
case pwm_timer0:
reg = (PWM_TIMER_REG *)(&(JL_MCPWM->TMR0_CON));
break;
case pwm_timer1:
reg = (PWM_TIMER_REG *)(&(JL_MCPWM->TMR1_CON));
break;
case pwm_timer2:
reg = (PWM_TIMER_REG *)(&(JL_MCPWM->TMR2_CON));
break;
case pwm_timer3:
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;
}
static u32 _pow(u32 num, int n)
{
u32 powint = 1;
int i;
for (i = 1; i <= n; i++) {
powint *= num;
}
return powint;
}
/*
* @brief MCPWM的频率
* @parm frequency
*/
void mcpwm_set_frequency(pwm_timer_num_type ch, pwm_aligned_mode_type align, u32 frequency)
{
PWM_TIMER_REG *reg = get_pwm_timer_reg(ch);
if (reg == NULL) {
return;
}
u32 i = 0;
u32 mcpwm_div_clk = 0;
u32 mcpwm_tmr_pr = 0;
u32 mcpwm_fre_min = 0;
reg->tmr_con = 0;
reg->tmr_cnt = 0;
reg->tmr_pr = 0;
u32 clk = MCPWM_CLK;
for (i = 0; i < 16; i++) {
mcpwm_fre_min = clk / (65536 * _pow(2, i));
if ((frequency >= mcpwm_fre_min) || (i == 15)) {
break;
}
}
reg->tmr_con |= (i << 3); //div 2^i
//mcpwm_div_clk = MCPWM_CLK / _pow(2, i);
mcpwm_div_clk = clk / _pow(2, 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;
}
}
/*
* @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, pwm_timer_num_type timer_ch, u16 duty)
{
PWM_TIMER_REG *timer_reg = get_pwm_timer_reg(timer_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;
//pwm_reg->ch_cmph = timer_reg->tmr_pr * 8000 / 10000; //%80 for test 互补
//printf("cmpl = %d, cmph = %d, tmr_pr = %d", pwm_reg->ch_cmpl, pwm_reg->ch_cmph, timer_reg->tmr_pr);
timer_reg->tmr_cnt = 0;
}
}
/*
* @brief
* @parm pwm_ch_num pwm_ch0pwm_ch1pwm_ch2
* @parm enable 1 0
*/
void mctimer_ch_open_or_close(pwm_timer_num_type timer_ch, u8 enable)
{
if (timer_ch > pwm_timer_max) {
return;
}
if (enable) {
JL_MCPWM->MCPWM_CON0 |= BIT(timer_ch + 8); //TnEN
} else {
JL_MCPWM->MCPWM_CON0 &= (~BIT(timer_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, pwm_timer_num_type timer_ch)
{
if ((pwm_ch >= pwm_ch_max) || (timer_ch >= pwm_timer_max)) {
return;
}
PWM_CH_REG *pwm_reg = get_pwm_ch_reg(pwm_ch);
pwm_reg->ch_con1 &= ~(0b111 << 8);
pwm_reg->ch_con1 |= (timer_ch << 8); //sel mctmr
mctimer_ch_open_or_close(timer_ch, 1);
mcpwm_ch_open_or_close(pwm_ch, 1);
}
/*
* @brief MCPWM模块
*/
void mcpwm_close(pwm_ch_num_type pwm_ch, pwm_timer_num_type timer_ch)
{
mctimer_ch_open_or_close(timer_ch, 0);
mcpwm_ch_open_or_close(pwm_ch, 0);
}
void log_pwm_info(pwm_ch_num_type pwm_ch, pwm_timer_num_type timer_ch)
{
PWM_CH_REG *pwm_reg = get_pwm_ch_reg(pwm_ch);
PWM_TIMER_REG *timer_reg = get_pwm_timer_reg(timer_ch);
mcpwm_debug("pwm_ch %d @ 0x%x, timer_ch %d @ 0x%x", pwm_ch, pwm_reg, timer_ch, timer_reg);
mcpwm_debug("tmr%d con0 = 0x%x", timer_ch, timer_reg->tmr_con);
mcpwm_debug("tmr%d pr = 0x%x", timer_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("pwm clk = %d\n\n", MCPWM_CLK);
}
void mcpwm_init(struct pwm_platform_data *arg)
{
u8 use_output_ch_flag = 0;
//set mctimer frequency
mcpwm_set_frequency(arg->pwm_timer_num, arg->pwm_aligned_mode, arg->frequency);
//set duty
mcpwm_set_duty(arg->pwm_ch_num, arg->pwm_timer_num, arg->duty);
//set output IO
PWM_CH_REG *pwm_reg = get_pwm_ch_reg(arg->pwm_ch_num);
if (pwm_reg == NULL) {
return;
}
pwm_reg->ch_con0 = 0;
//H:
if (arg->h_pin == pwm_hw_h_pin[arg->pwm_ch_num]) { //硬件引脚
pwm_reg->ch_con0 |= BIT(2); //H_EN
gpio_set_direction(arg->h_pin, 0); //DIR output
} else {
pwm_reg->ch_con0 &= ~BIT(2); //H_DISABLE
if (arg->h_pin < IO_MAX_NUM) { //任意引脚
//TODO: output_channle
if (arg->pwm_ch_num >= pwm_ch3) {
printf("error: mcpwm ch %d not support output_channel", arg->pwm_ch_num);
goto _CH_L_SET;
}
gpio_output_channle(arg->h_pin, CHx_CHx_PWM_H[arg->h_pin_output_ch_num][arg->pwm_ch_num]);
use_output_ch_flag = 1;
}
}
_CH_L_SET:
//L:
if (arg->l_pin == pwm_hw_l_pin[arg->pwm_ch_num]) { //硬件引脚
pwm_reg->ch_con0 |= BIT(3); //L_EN
gpio_set_direction(arg->l_pin, 0); //DIR output
} else {
pwm_reg->ch_con0 &= ~BIT(3); //L_DISABLE
if (arg->l_pin < IO_MAX_NUM) { //任意引脚
//TODO:
if (arg->pwm_ch_num >= pwm_ch3) {
printf("error: mcpwm ch %d not support output_channel", arg->pwm_ch_num);
goto _PWM_OPEN;
}
if ((use_output_ch_flag == 1) && (arg->h_pin_output_ch_num == arg->l_pin_output_ch_num)) {
arg->l_pin_output_ch_num ++;
if (arg->l_pin_output_ch_num > 2) {
arg->l_pin_output_ch_num = 0;
}
}
gpio_output_channle(arg->l_pin, CHx_CHx_PWM_L[arg->l_pin_output_ch_num][arg->pwm_ch_num]);
}
}
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));
}
_PWM_OPEN:
mcpwm_open(arg->pwm_ch_num, arg->pwm_timer_num); //mcpwm enable
log_pwm_info(arg->pwm_ch_num, arg->pwm_timer_num);
}
void mcpwm_test(void)
{
#define PWM_CH0_ENABLE 1
#define PWM_CH1_ENABLE 1
#define PWM_CH2_ENABLE 0 //一般不使用
#define PWM_CH3_ENABLE 1
struct pwm_platform_data pwm_p_data;
//io_test();
#if PWM_CH0_ENABLE
//CH0
pwm_p_data.pwm_aligned_mode = pwm_edge_aligned; //边沿对齐
pwm_p_data.frequency = 1000; //1KHz
pwm_p_data.pwm_ch_num = pwm_ch0; //通道0
pwm_p_data.pwm_timer_num = pwm_timer0; //时基选择通道0
pwm_p_data.duty = 5000; //占空比50%
//hw
pwm_p_data.h_pin = IO_PORTA_00; //没有则填 -1。h_pin_output_ch_num无效可不配置
pwm_p_data.l_pin = IO_PORTA_03; //硬件引脚l_pin_output_ch_num无效可不配置
//output_channel
/* pwm_p_data.h_pin = IO_PORTB_00; //没有则填 -1。h_pin_output_ch_num无效可不配置 */
/* pwm_p_data.l_pin = IO_PORTB_01; //硬件引脚l_pin_output_ch_num无效可不配置 */
/* pwm_p_data.h_pin_output_ch_num = 0; //output channel0 */
/* pwm_p_data.l_pin_output_ch_num = 1; //output channel1 */
pwm_p_data.complementary_en = 1; //两个引脚的波形, 1: 互补, 0: 同步;
mcpwm_init(&pwm_p_data);
#endif
#if PWM_CH1_ENABLE
//CH1
pwm_p_data.pwm_aligned_mode = pwm_edge_aligned; //边沿对齐
pwm_p_data.frequency = 1000; //1KHz
pwm_p_data.pwm_ch_num = pwm_ch1; //通道0
pwm_p_data.pwm_timer_num = pwm_timer0; //时基选择通道0
pwm_p_data.duty = 5000; //占空比50%
//hw
pwm_p_data.h_pin = IO_PORTA_07; //没有则填 -1。h_pin_output_ch_num无效可不配置
pwm_p_data.l_pin = IO_PORTA_08; //硬件引脚l_pin_output_ch_num无效可不配置
//output_channel
/* pwm_p_data.h_pin = IO_PORTA_00; //没有则填 -1。h_pin_output_ch_num无效可不配置 */
/* pwm_p_data.l_pin = IO_PORTA_04; //硬件引脚l_pin_output_ch_num无效可不配置 */
/* pwm_p_data.h_pin_output_ch_num = 0; //output channel0 */
/* pwm_p_data.l_pin_output_ch_num = 2; //output channel1 */
pwm_p_data.complementary_en = 1; //两个引脚的波形同步
mcpwm_init(&pwm_p_data);
#endif
#if PWM_CH2_ENABLE
//CH2, BD29使用CH2需要注意,H硬件是PB2,mask设置为短按复位(2ms),需要关闭才能使用
//关闭PB2长按复位方法: p33_and_1byte(P3_PR_PWR, ~BIT(3));
pwm_p_data.pwm_aligned_mode = pwm_edge_aligned; //边沿对齐
pwm_p_data.frequency = 1000; //1KHz
pwm_p_data.pwm_ch_num = pwm_ch2; //通道0
pwm_p_data.pwm_timer_num = pwm_timer0; //时基选择通道0
pwm_p_data.duty = 5000; //占空比50%
//hw
pwm_p_data.h_pin = IO_PORTB_02; //没有则填 -1。h_pin_output_ch_num无效可不配置
pwm_p_data.l_pin = IO_PORTB_03; //硬件引脚l_pin_output_ch_num无效可不配置
//output_channel
/* pwm_p_data.h_pin = IO_PORTB_00; //没有则填 -1。h_pin_output_ch_num无效可不配置 */
/* pwm_p_data.l_pin = IO_PORTB_04; //没有则填 -1。h_pin_output_ch_num无效可不配置 */
/* pwm_p_data.h_pin_output_ch_num = 0; //output channel0 */
/* pwm_p_data.l_pin_output_ch_num = 1; //output channel0 */
pwm_p_data.complementary_en = 1; //两个引脚的波形同步
mcpwm_init(&pwm_p_data);
#endif
//注意: CH3, CH4, CH5不支持通过output channel输出
#if PWM_CH3_ENABLE
//CH3
pwm_p_data.pwm_aligned_mode = pwm_edge_aligned; //边沿对齐
pwm_p_data.frequency = 1000; //1KHz
pwm_p_data.pwm_ch_num = pwm_ch3; //通道0
pwm_p_data.pwm_timer_num = pwm_timer0; //时基选择通道0
pwm_p_data.duty = 5000; //占空比50%
//only support hw
pwm_p_data.h_pin = IO_PORTB_04; //没有则填 -1。h_pin_output_ch_num无效可不配置
pwm_p_data.l_pin = IO_PORTB_05; //硬件引脚l_pin_output_ch_num无效可不配置
pwm_p_data.complementary_en = 1; //两个引脚的波形同步
mcpwm_init(&pwm_p_data);
#endif
while (1);
}
/************************************** 外部引脚中断参考代码 **************************/
static IO_ISR_FUNC io_isr_cbfun;
___interrupt
void io_interrupt(void)
{
JL_MCPWM->CH0_CON1 |= BIT(14);
io_isr_cbfun();
}
void io_ext_interrupt_init(u8 port, trigger_mode_type trigger_mode, IO_ISR_FUNC cbfun)
{
if (port > IO_PORT_DM) {
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->FPIN_CON = 0;
} else {
gpio_set_pull_up(port, 0);
gpio_set_pull_down(port, 1);
JL_MCPWM->FPIN_CON = BIT(16);
}
if (port == IO_PORTA_02) {
JL_IOMAP->CON3 &= ~BIT(12); //使用硬件引脚
} else {
JL_IOMAP->CON3 |= BIT(12); //使用input_channel 2
JL_IOMAP->CON2 &= ~(0b111111 << 16);
JL_IOMAP->CON2 |= (port << 16);
}
io_isr_cbfun = cbfun;
request_irq(IRQ_MCPWMX_IDX, 3, io_interrupt, 0); //注册中断函数
JL_MCPWM->CH0_CON1 = BIT(14) | BIT(11) | BIT(4);
JL_MCPWM->MCPWM_CON0 |= BIT(0);
}
void io_ext_interrupt_close(u8 port)
{
if (port > IO_PORT_DM) {
return;
}
gpio_set_die(port, 0);
gpio_set_direction(port, 1);
gpio_set_pull_up(port, 0);
gpio_set_pull_down(port, 0);
JL_MCPWM->CH0_CON1 = BIT(14);
}
///////////// 使用举例如下 //////////////////
void my_io_isr_cbfun(void)
{
printf("Hello world !\n");
}
void io_ext_interrupt_test(void)
{
io_ext_interrupt_init(IO_PORTA_09, rising_edge_trigger, my_io_isr_cbfun);
while (1);
}