#include "system/includes.h" #include "app_config.h" #include "app_action.h" #include "app_main.h" #include "gpio.h" #include "asm/power/p11.h" // 可配参数 #define AUDIO_PWM_SOURCE (1) // 0:timer 1:mcpwm #define AUDIO_PWM_MODE (3) // 0:single mode 1:diff mode 0 2:diff mode 1 3:diff mode 2 #define FADE_STEP (100) // PWM开始和结尾的淡入淡出步进,每 FADE_STEP 个点更新一次占空比单位 #if (AUDIO_PWM_SOURCE == 0) #define PWM_DEAD_AREA (50) // PWM死区时钟数 因为占空比在中断更新,这个时间要大于中断的整个处理时间,但要小于PWM周期 #elif (AUDIO_PWM_SOURCE == 1) #define PWM_DEAD_AREA (0) // PWM死区时钟数 MCPWM 模块有占空比更新时机控制机制,不需要死区 #endif #define BUF_SIZE (1024 * 4) // AUDIO PWM 缓冲buf大小 u32 pwm_io_P = IO_PORTB_08; // PWM 输出 P 端 IO 选择,单端只关注 P 端即可 u32 pwm_io_N = IO_PORTB_09; // PWM 输出 N 端 IO 选择,差分需要配置 N 端 // PWM cbuf 缓冲区相关参数 volatile s16 audio_pwm_cbuf[BUF_SIZE] = {0}; volatile u32 rptr = 0; volatile u32 wptr = 0; static u8 pwm_is_closed = 1; // PWM 状态及占空比控制参数 #define STATE_STOP (0) #define STATE_FADE_IN (1) #define STATE_START (2) #define STATE_FADE_OUT (3) volatile u8 audio_pwm_state = 0; volatile u32 audio_pwm_target = 0; volatile u32 audio_pwm_cur = 0; volatile u32 audio_pwm_prd = 0; // 周期时钟数,也是占空比的分辨率 u16 pcm_to_pwm_scale = 0; // 16bit pcm数据 转成 占空比分辨率范围 的比例值 u32 audio_pwm_zero = 0; // PCM 数据 0 的时候对应的 PWM 值 u32 half_wave_limit = 0; volatile u8 pwm_need_resume = 0; static OS_SEM pwm_need_resume_sem ; static void (*pwm_resume_handler)(void *) = NULL; static u8 drop_flag = 0; void state_printf(void) { printf("state :%d, pwm_h:%d, pwm_l:%d", audio_pwm_state, JL_MCPWM->CH3_CMPH, JL_MCPWM->CH3_CMPL); } void set_state(u8 state) { if (state == STATE_STOP) { //要设置pwm 停止需要等cbuffer 里面的数据播完 u8 err_cnt = 0; while (abs(wptr - rptr) > 1) { delay(3000); err_cnt++; if (err_cnt > 500) { printf("wptr:%x,rptr:%x!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", wptr, rptr); break; } } audio_pwm_state = state; pwm_is_closed = 1; } else { audio_pwm_state = state; pwm_is_closed = 0; } } #if (AUDIO_PWM_SOURCE == 0) // timer ___interrupt void usr_pwm_timer_isr(void) __attribute__((section(".audio_pwm_code"))) { s32 pcm = 0; s32 pcm1 = 0; JL_TIMER3->CON |= BIT(14); /* JL_PORTA->DIR &= ~BIT(5); */ /* JL_PORTA->OUT &= ~BIT(5); */ if (drop_flag == 0) { drop_flag = 1; return; } else { drop_flag = 0; } if (audio_pwm_state == STATE_START) { if (rptr != wptr) { pcm = (s32)(audio_pwm_cbuf[rptr]); #if (AUDIO_PWM_MODE == 0) if (pcm >= 0) { pcm += pcm_to_pwm_scale / 2; } else { pcm -= pcm_to_pwm_scale / 2; } pcm /= pcm_to_pwm_scale; if (pcm > half_wave_limit) { pcm = half_wave_limit; } else if (pcm < -half_wave_limit) { pcm = -half_wave_limit; } JL_TIMER3->PWM = (u32)(pcm + audio_pwm_zero); // P #elif (AUDIO_PWM_MODE == 1) if (pcm >= 0) { pcm += pcm_to_pwm_scale / 2; } else { pcm -= pcm_to_pwm_scale / 2; } pcm /= pcm_to_pwm_scale; if (pcm > half_wave_limit) { pcm = half_wave_limit; } else if (pcm < -half_wave_limit) { pcm = -half_wave_limit; } JL_TIMER3->PWM = (u32)(pcm + audio_pwm_zero); // P JL_TIMER2->PWM = (u32)(pcm + audio_pwm_zero); // N #elif (AUDIO_PWM_MODE == 2) if (pcm >= 0) { pcm += pcm_to_pwm_scale / 2; } else { pcm -= pcm_to_pwm_scale / 2; } pcm /= pcm_to_pwm_scale; if (pcm > half_wave_limit) { pcm = half_wave_limit; } else if (pcm < -half_wave_limit) { pcm = -half_wave_limit; } JL_TIMER3->PWM = (u32)(pcm + audio_pwm_zero); // P JL_TIMER2->PWM = (u32)(-pcm + audio_pwm_zero); // N #elif (AUDIO_PWM_MODE == 3) if (pcm >= 0) { pcm += pcm_to_pwm_scale / 2; pcm /= pcm_to_pwm_scale; if (pcm > half_wave_limit) { pcm = half_wave_limit; } JL_TIMER3->PWM = (u32)(PWM_DEAD_AREA + pcm); // P JL_TIMER2->PWM = (u32)(PWM_DEAD_AREA); // N } else { pcm = -pcm; pcm -= pcm_to_pwm_scale / 2; pcm /= pcm_to_pwm_scale; if (pcm > half_wave_limit) { pcm = half_wave_limit; } JL_TIMER3->PWM = (u32)(PWM_DEAD_AREA); // P JL_TIMER2->PWM = (u32)(PWM_DEAD_AREA + pcm); // N } #endif rptr++; if (rptr >= BUF_SIZE) { rptr = 0; } } else { /* putchar('e'); */ } } else if ((audio_pwm_state == STATE_FADE_IN) || (audio_pwm_state == STATE_FADE_OUT)) { if (audio_pwm_cur > audio_pwm_target) { audio_pwm_cur--; /* printf("{%d", audio_pwm_cur); */ } else if (audio_pwm_cur < audio_pwm_target) { audio_pwm_cur++; /* printf("}%d", audio_pwm_cur); */ } else { if (audio_pwm_state == STATE_FADE_IN) { audio_pwm_state = STATE_START; } else { audio_pwm_state = STATE_STOP; } } #if (AUDIO_PWM_MODE == 0) JL_TIMER3->PWM = audio_pwm_cur / FADE_STEP; #elif ((AUDIO_PWM_MODE == 1) || (AUDIO_PWM_MODE == 2)) JL_TIMER3->PWM = audio_pwm_cur / FADE_STEP; JL_TIMER2->PWM = audio_pwm_cur / FADE_STEP; #elif (AUDIO_PWM_MODE == 3) // 这种调制方式不需要淡入淡出 if (audio_pwm_state == STATE_FADE_IN) { audio_pwm_state = STATE_START; } else { audio_pwm_state = STATE_STOP; } #endif } else { // do no thing } /* JL_PORTA->DIR &= ~BIT(5); */ /* JL_PORTA->OUT |= BIT(5); */ } void usr_pwm_timer_init(void) { printf("############ usr_pwm_timer_init ########## \n"); bit_clr_ie(IRQ_TIME3_IDX); request_irq(IRQ_TIME3_IDX, 3, usr_pwm_timer_isr, 0); irq_unmask_set(IRQ_TIME3_IDX, 0); JL_TIMER3->CON = BIT(14); JL_TIMER3->CON |= (6 << 10); //时钟源选择稳定的STD_24M JL_TIMER3->CON |= (0b0000 << 4); //时钟源再1分频 JL_TIMER3->CON &= ~BIT(9); // PWM_INV 信号反相 JL_TIMER3->CON |= BIT(8); // PWM IO OUT JL_TIMER3->CNT = 0; JL_TIMER3->PRD = 750; // 频率 32kHz JL_TIMER3->PWM = 0; JL_TIMER3->CON |= BIT(14) | BIT(0); audio_pwm_prd = JL_TIMER3->PRD; #if (AUDIO_PWM_MODE == 3) pcm_to_pwm_scale = 32767 / audio_pwm_prd; audio_pwm_zero = 0; half_wave_limit = audio_pwm_prd; #else pcm_to_pwm_scale = 65536 / (audio_pwm_prd - PWM_DEAD_AREA); audio_pwm_zero = PWM_DEAD_AREA + (audio_pwm_prd - PWM_DEAD_AREA) / 2; half_wave_limit = (audio_pwm_prd - PWM_DEAD_AREA) / 2; #endif gpio_set_fun_output_port(pwm_io_P, FO_TMR3_PWM, 0, 1); //设置引脚状态 gpio_set_die(pwm_io_P, 1); gpio_set_pull_up(pwm_io_P, 0); gpio_set_pull_down(pwm_io_P, 0); gpio_set_direction(pwm_io_P, 0); #if ((AUDIO_PWM_MODE == 1) || (AUDIO_PWM_MODE == 2) || (AUDIO_PWM_MODE == 3)) JL_TIMER2->CON = 0; JL_TIMER2->CON |= (6 << 10); //时钟源选择稳定的STD_24M JL_TIMER2->CON |= (0b0000 << 4); //时钟源再1分频 #if (AUDIO_PWM_MODE == 1) JL_TIMER2->CON |= BIT(9); // PWM_INV 信号反相 #elif ((AUDIO_PWM_MODE == 2) || (AUDIO_PWM_MODE == 3)) JL_TIMER2->CON &= ~BIT(9); // PWM_INV 信号反相 #endif JL_TIMER2->CON |= BIT(8); // PWM IO OUT JL_TIMER2->PRD = 750; // 频率 32kHz JL_TIMER2->PWM = 0; JL_TIMER2->CON |= BIT(0); JL_TIMER2->CNT = 0; JL_TIMER3->CNT = 0; // sync gpio_set_fun_output_port(pwm_io_N, FO_TMR2_PWM, 0, 1); //设置引脚状态 gpio_set_die(pwm_io_N, 1); gpio_set_pull_up(pwm_io_N, 0); gpio_set_pull_down(pwm_io_N, 0); gpio_set_direction(pwm_io_N, 0); /* gpio_set_hd(pwm_io_N, 1); */ /* gpio_set_hd0(pwm_io_N, 1); */ #endif audio_pwm_state = STATE_START; } void usr_pwm_timer_uninit(void) { JL_TIMER3->CON = 0; #if ((AUDIO_PWM_MODE == 1) || (AUDIO_PWM_MODE == 2) || (AUDIO_PWM_MODE == 3)) JL_TIMER2->CON = 0; #endif } #elif (AUDIO_PWM_SOURCE == 1) // mcpwm ___interrupt void usr_mcpwm_isr(void) __attribute__((section(".audio_pwm_code"))) { s32 pcm = 0; s32 pcm1 = 0; if (JL_MCPWM->TMR3_CON & BIT(12)) { JL_MCPWM->TMR3_CON |= BIT(10);//清中断标志 } if (drop_flag == 0) { drop_flag = 1; return; } else { drop_flag = 0; } /* JL_PORTA->DIR &= ~BIT(5); */ /* JL_PORTA->OUT &= ~BIT(5); */ if (audio_pwm_state == STATE_START) { if (rptr != wptr) { pcm = (s32)(audio_pwm_cbuf[rptr]); #if (AUDIO_PWM_MODE == 0) if (pcm >= 0) { pcm += pcm_to_pwm_scale / 2; } else { pcm -= pcm_to_pwm_scale / 2; } pcm /= pcm_to_pwm_scale; if (pcm > half_wave_limit) { pcm = half_wave_limit; } else if (pcm < -half_wave_limit) { pcm = -half_wave_limit; } JL_MCPWM->CH3_CMPH = (u32)(pcm + audio_pwm_zero); // P #elif (AUDIO_PWM_MODE == 1) if (pcm >= 0) { pcm += pcm_to_pwm_scale / 2; } else { pcm -= pcm_to_pwm_scale / 2; } pcm /= pcm_to_pwm_scale; if (pcm > half_wave_limit) { pcm = half_wave_limit; } else if (pcm < -half_wave_limit) { pcm = -half_wave_limit; } JL_MCPWM->CH3_CMPH = (u32)(pcm + audio_pwm_zero); // P JL_MCPWM->CH3_CMPL = (u32)(pcm + audio_pwm_zero); // N #elif (AUDIO_PWM_MODE == 2) if (pcm >= 0) { pcm += pcm_to_pwm_scale / 2; } else { pcm -= pcm_to_pwm_scale / 2; } pcm /= pcm_to_pwm_scale; if (pcm > half_wave_limit) { pcm = half_wave_limit; } else if (pcm < -half_wave_limit) { pcm = -half_wave_limit; } JL_MCPWM->CH3_CMPH = (u32)(pcm + audio_pwm_zero); // P pcm1 = pcm; JL_MCPWM->CH3_CMPL = (u32)(-pcm + audio_pwm_zero); // N #elif (AUDIO_PWM_MODE == 3) if (pcm >= 0) { pcm += pcm_to_pwm_scale / 2; pcm /= pcm_to_pwm_scale; if (pcm > half_wave_limit) { pcm = half_wave_limit; } JL_MCPWM->CH3_CMPH = (u32)(PWM_DEAD_AREA + pcm); // P JL_MCPWM->CH3_CMPL = PWM_DEAD_AREA; // N } else { pcm = -pcm; pcm -= pcm_to_pwm_scale / 2; pcm /= pcm_to_pwm_scale; if (pcm > half_wave_limit) { pcm = half_wave_limit; } JL_MCPWM->CH3_CMPH = PWM_DEAD_AREA; // P JL_MCPWM->CH3_CMPL = (u32)(PWM_DEAD_AREA + pcm); // N } #endif rptr++; if (rptr >= BUF_SIZE) { rptr = 0; } } else { /* putchar('e'); */ } } else if ((audio_pwm_state == STATE_FADE_IN) || (audio_pwm_state == STATE_FADE_OUT)) { if (audio_pwm_cur > audio_pwm_target) { audio_pwm_cur--; /* printf("{%d", audio_pwm_cur); */ } else if (audio_pwm_cur < audio_pwm_target) { audio_pwm_cur++; /* printf("}%d", audio_pwm_cur); */ } else { if (audio_pwm_state == STATE_FADE_IN) { audio_pwm_state = STATE_START; /* putchar('s'); */ } else { audio_pwm_state = STATE_STOP; /* putchar('p'); */ } } JL_MCPWM->CH3_CMPH = audio_pwm_cur / FADE_STEP; #if ((AUDIO_PWM_MODE == 1) || (AUDIO_PWM_MODE == 2)) JL_MCPWM->CH3_CMPL = audio_pwm_cur / FADE_STEP; #elif (AUDIO_PWM_MODE == 3) // 这种调制方式不需要淡入淡出 if (audio_pwm_state == STATE_FADE_IN) { audio_pwm_state = STATE_START; } else if (audio_pwm_state == STATE_FADE_OUT) { audio_pwm_state = STATE_STOP; } #endif } else { JL_MCPWM->CH3_CMPH = PWM_DEAD_AREA; // P JL_MCPWM->CH3_CMPL = PWM_DEAD_AREA; // N // do no thing } /* JL_PORTA->DIR &= ~BIT(5); */ /* JL_PORTA->OUT |= BIT(5); */ } void usr_mcpwm_init(void) { printf("############ usr_mcpwm_init ########## \n"); request_irq(IRQ_MCPWM_TIMER, 3, usr_mcpwm_isr, 0); irq_unmask_set(IRQ_MCPWM_TIMER, 0); JL_MCPWM->MCPWM_CON0 &= ~BIT(8 + 3); JL_MCPWM->TMR3_CNT = 0; JL_MCPWM->TMR3_PR = clk_get("lsb") / 32000; audio_pwm_prd = JL_MCPWM->TMR3_PR; #if (AUDIO_PWM_MODE == 3) pcm_to_pwm_scale = 32767 / audio_pwm_prd; audio_pwm_zero = 0; half_wave_limit = audio_pwm_prd; #else pcm_to_pwm_scale = 65536 / (audio_pwm_prd - PWM_DEAD_AREA); audio_pwm_zero = PWM_DEAD_AREA + (audio_pwm_prd - PWM_DEAD_AREA) / 2; half_wave_limit = (audio_pwm_prd - PWM_DEAD_AREA) / 2; #endif JL_MCPWM->TMR3_CON |= BIT(8); // IE JL_MCPWM->TMR3_CON |= BIT(10) | BIT(0); JL_MCPWM->MCPWM_CON0 |= BIT(8 + 3); // MCTIMER3 enable gpio_set_fun_output_port(pwm_io_P, FO_MCPWM_CH3H, 0, 1); gpio_set_die(pwm_io_P, 1); gpio_set_direction(pwm_io_P, 0); gpio_set_pull_up(pwm_io_P, 0); gpio_set_pull_down(pwm_io_P, 0); #if ((AUDIO_PWM_MODE == 1) || (AUDIO_PWM_MODE == 2) || (AUDIO_PWM_MODE == 3)) gpio_set_fun_output_port(pwm_io_N, FO_MCPWM_CH3L, 0, 1); gpio_set_die(pwm_io_N, 1); gpio_set_direction(pwm_io_N, 0); gpio_set_pull_up(pwm_io_N, 0); gpio_set_pull_down(pwm_io_N, 0); #endif JL_MCPWM->CH3_CON0 = 0; JL_MCPWM->CH3_CON0 |= BIT(2); // PWM H enable JL_MCPWM->CH3_CON0 |= BIT(3); // PWM L enable JL_MCPWM->CH3_CON0 &= ~BIT(4); // PWM H 信号反相使能 #if (AUDIO_PWM_MODE == 1) JL_MCPWM->CH3_CON0 |= BIT(5); // PWM L 信号反相使能 #elif ((AUDIO_PWM_MODE == 2) || (AUDIO_PWM_MODE == 3)) JL_MCPWM->CH3_CON0 &= ~BIT(5); // PWM L 信号反相使能 #endif SFR(JL_MCPWM->CH3_CON0, 0, 2, 2); // duty update type select SFR(JL_MCPWM->CH3_CON1, 8, 3, 3); // MCPWM CH3 select MCTIMER3 JL_MCPWM->MCPWM_CON0 |= BIT(0 + 3); // MCPWM CH3 enable audio_pwm_state = STATE_START; } void usr_mcpwm_uninit(void) { pwm_is_closed = 1; } #endif static void audio_pwm_task(void *param) { printf(">>>> audio_pwm_task\n"); u32 buf_points = 0; while (1) { if (pwm_need_resume) { if (rptr < wptr) { buf_points = wptr - rptr; } else if (rptr == wptr) { buf_points = 0; } else { buf_points = BUF_SIZE - rptr + wptr; } if (buf_points < 1024) { //32 ms if (pwm_resume_handler != NULL) { pwm_resume_handler(NULL); } /* putchar('R'); */ } else { /* putchar('N'); */ } } else { /* printf("enter audio_pwm.c %d\n",__LINE__); */ os_sem_pend(&pwm_need_resume_sem, 0); //在不需要跑任务的时候pend住,避免任务频繁起来 /* printf("enter audio_pwm.c %d\n",__LINE__); */ } os_time_dly(1); } } void audio_pwm_set_resume(void (*resume)(void *)) { pwm_resume_handler = resume; } void audio_pwm_open(void) { printf("audio_pwm_open\n"); clk_set("sys", 96 * 1000000L); os_sem_create(&pwm_need_resume_sem, 0); os_task_create(audio_pwm_task, NULL, 2, 1024, 128, "audio_pwm_task"); #if (AUDIO_PWM_SOURCE == 0) // timer usr_pwm_timer_init(); #elif (AUDIO_PWM_SOURCE == 1) // mcpwm usr_mcpwm_init(); #endif // timer of mcpwm //开机进低功耗要把输出清0,不然有杂声 JL_MCPWM->CH3_CMPH = PWM_DEAD_AREA; // P JL_MCPWM->CH3_CMPL = PWM_DEAD_AREA; // N /* pwm_is_closed = 0; */ } void audio_pwm_close(void) { #if (AUDIO_PWM_SOURCE == 0) // timer usr_pwm_timer_uninit(); #elif (AUDIO_PWM_SOURCE == 1) // mcpwm usr_mcpwm_uninit(); #endif // timer of mcpwm } void audio_pwm_start(void) { audio_pwm_state = STATE_FADE_IN; #if (AUDIO_PWM_SOURCE == 0) // timer audio_pwm_cur = JL_TIMER3->PWM * FADE_STEP; #elif (AUDIO_PWM_SOURCE == 1) // mcpwm audio_pwm_cur = JL_MCPWM->CH3_CMPH; #endif // timer of mcpwm audio_pwm_target = audio_pwm_zero * FADE_STEP; } void audio_pwm_stop(void) { audio_pwm_state = STATE_FADE_OUT; #if (AUDIO_PWM_SOURCE == 0) // timer audio_pwm_cur = JL_TIMER3->PWM * FADE_STEP; #elif (AUDIO_PWM_SOURCE == 1) // mcpwm audio_pwm_cur = JL_MCPWM->CH3_CMPH; #endif // timer of mcpwm audio_pwm_target = 0; } static int __audio_pwm_write(s16 *data, u32 len) { u32 free_points = 0; u32 wpoints = len / 2; local_irq_disable(); if (rptr <= wptr) { if (rptr == 0) { // 不能让 wptr 和 rptr 碰到一起 free_points = BUF_SIZE - wptr - 1; } else { free_points = BUF_SIZE - wptr; } } else { free_points = rptr - wptr - 1; } if (free_points == 0) { local_irq_enable(); return 0; } if (wpoints > free_points) { wpoints = free_points; } memcpy(&(audio_pwm_cbuf[wptr]), data, wpoints * 2); wptr += wpoints; if (wptr >= BUF_SIZE) { wptr = 0; } local_irq_enable(); return wpoints * 2; } int audio_pwm_write(s16 *data, u32 len) { u32 wlen = 0; wlen = __audio_pwm_write(data, len); if (len != wlen) { data += wlen / 2; wlen += __audio_pwm_write(data, len - wlen); } if (wlen != len) { /* putchar('S'); */ pwm_need_resume = 1; os_sem_post(&pwm_need_resume_sem); } else { /* putchar('W'); */ pwm_need_resume = 0; os_sem_set(&pwm_need_resume_sem, 0); } return wlen; } static u8 pwm_demo_idle_query() { return pwm_is_closed; } REGISTER_LP_TARGET(pwm_demo_lp_target) = { .name = "pwm_demo", .is_idle = pwm_demo_idle_query, };