31-HSPWMA-仿USB声卡播放立体声音乐-CDC实时传输音频流-180MHz

#include    "stc.h"
#include    "usb.h"
#include    "uart.h"
#include     <math.h>


/*************    功能说明    **************

程序编写: STC技术支持 梁工
用户请先别修改程序, 直接下载"Objects"里的"audio-play.hex"测试. 下载时选择主频30MHZ。
本例程使用STC32G12K128验证,MCU内部ROSC工作于30MHz,PLL到180MHz给PWM时钟,同时6分频得到30MHz给CPU做系统时钟。
使用2路HSPWM输出播放声音,由上位机CDC串口连续发送音频流。
两路HSPWM工作时钟为180MHz,PWM周期为4097,输出PWM频率大约为44KHz(与CD采样率44.1KHz相近),占空比为12位。
PWMA1P--P1.0    左声道
PWMA3P--P1.4    右声道

支持PCM、ADPCM、A-Law播放程序,上位机CDC串口下传音频流。
支持16位PCM格式,   单声道或双声道采样率最高支持44.1KHz。
支持IMA-ADPCM格式, 单通道或双通道采样率最高支持44.1KHz。
支持A-law(A率)格式,单通道或双通道采样率最高支持44.1KHz。

串口助手“每个数据包的大小”设置为1024,选择“自动连续发送”,“数据包之间的延时(ms)”根据播放的文件类型和采样率设置,或者均设置为0。
PCM  采样率    包间延时时间     数据率
     44.1KHz       0ms        176.4KB/S
      32KHz     1ms或2ms        128KB/S
      16KHz     4ms或5ms         64KB/S

ADPCM 采样率    包间延时时间     数据率
     44.1KHz       5ms          44.1KB/S
      32KHz       10ms            32KB/S
      16KHz       10ms            16KB/S

A law 采样率    包间延时时间     数据率
     44.1KHz    2ms或3ms        88.2KB/S
      32KHz     4ms或5ms          64KB/S
      16KHz       10ms            32KB/S


输出PWM要经过低通滤波器获得光滑的模拟信号输出,请参考附图的电路:3阶巴特沃斯低通滤波器。

语音解码算法可能看似跟网上的有差异,但算法实际是一样的,我只是将其简化操作而已。

******************************************/


/*************    本地常量声明    **************/

/*************    IO口定义    **************/

/*************    本地变量声明    **************/
#define    VOICE_BUFF_LENTH    4096    //播放缓冲长度

u16    rd_index;    //读缓冲索引
u16    wr_index;    //写缓冲索引
u8    xdata voice_buff[VOICE_BUFF_LENTH];    //播放缓冲

u8    MusicType;            //WAV类型, fmttag=0x01-->PCM, 0x02-->Windows ADPCM, 0x06-->A Law, 0x07-->Mu Law, 0x11-->IMA ADPCM
u8    MusicChannel;        //音乐声道
u8    MusicBitNum;        //采样长度
u32    MusicSampleRate;    //采样率
u32    ByteLength;            //数据长度(字节)
u32    ByteCnt;            //播放字节计数
bit    B_PlayEn;            //允许播放
u16    left_pwm;            //左声道PWM值
u16    right_pwm;            //右声道PWM值

u8    law_data;            //Alaw加码专用变量

//======= ADPCM解码专用变量 ==========
u8        edata ADPCM_Data_L[4];    //左声道局部缓冲
u8        edata ADPCM_Data_R[4];    //右声道局部缓冲
u8        decode;                    //编码值
long    delta;                //偏差
long    cur_sample_L;        //左声道解码值
long    cur_sample_R;        //右声道解码值
char    index_L;            //左声道解码索引
char    index_R;            //右声道解码索引
u16        dac_L, dac_R;        //解码后输出的左后声道DAC值
u8        DecodeCnt;            //解码计数
u16        MusicBlock;            //BLOCK大小
bit        B_high_nibble;        //高半字节指示
//====================================


u8    PWMA_ISR_En;    //每个通道可以单独允许中断处理, bit4:通道4, bit3:通道3, bit2:通道2, bit1:通道1.

u16    TX1_Limit;    //接收数据限时


/*************    本地函数声明    **************/
u8        Timer0_Config(u8 t, u32 reload);    //t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us)
void      delay_ms(u16 ms);
void     PWMA_config(void);
void    delay(void);
u8        ReadPWMA(u8 addr);
void    WritePWMA(u8 addr, u8 dat);

/****************  外部函数声明和外部变量声明 *****************/

/*********************** 主函数 ******************************/

void main(void)
{
    u16    j;

    WTST  = 0;
    CKCON = 0;
    EAXFR = 1;    //SFR enable, 允许访问扩展寄存器

    P2M1 = 0x00;
    P2M0 = 0x00;
    P2 = 0xff;    //一些IO用于测试指示

    P5n_standard(0x10);        //P5.4设置为准双向口, 输出MCU时钟分频, 用于监测MCU系统时钟.
    MCLKOCR = 0x00 + 100;    //主时钟输出控制寄存器, 0x00:从P5.4输出,  0x80:从P1.6输出. +0:不输出时钟, +1~127:输出1~127分频.

    P3M0 &= ~0x03;    //P3.0 P3.1设置为高阻输入
    P3M1 |= 0x03;
    IRC48MCR = 0x80;            //开启USB时钟
    while (!(IRC48MCR & 0x01));    //等待USB时钟稳定

    uart_init();    //初始化USB-CDC串口
    usb_init();        //初始化USB
    EA = 1;            //允许全局中断

    delay_ms(1500);        //延时一下
    while(DeviceState != DEVSTATE_CONFIGURED)    //等待设置完成
    {
        NOP(3);
    }


    left_pwm  = 2048;
    right_pwm = 2048;
    PWMA_config();    // 初始化PWM
    Timer0_Config(0, MAIN_Fosc / 16000);        //设置采样率(中断频率)
    B_PlayEn = 1;

    while (1)
    {
        if (RxFlag)                    //当RxFlag为1时,表示已接收到CDC串口数据, 接收的数据大小保存在RxCount里面,每个包最多接收64字节, 数据保存在RxBuffer缓冲区.
        {
            if(RxCount == 14)        //PC下传命令, 文件相关信息
            {
                B_PlayEn = 0;    //禁止播放
                MusicType       = RxBuffer[1];    //WAV类型, fmttag=0x01-->PCM, 0x02-->Windows ADPCM, 0x06-->A Law, 0x07-->Mu Law, 0x11-->IMA ADPCM
                MusicChannel    = RxBuffer[2];    //声道数
                MusicBitNum     = RxBuffer[3];    //采样位数
                MusicSampleRate = ((u32 *)&RxBuffer)[1];    //采样率, Buffer)[4]~Buffer)[7], 大端模式
                ByteLength      = ((u32 *)&RxBuffer)[2];    //数据字节长度, Buffer)[8]~Buffer)[11], 大端模式
                MusicBlock      = ((u16 *)&RxBuffer)[6];    //数据BLOCK长度(ADPCM使用), Buffer)[12]~Buffer)[13], 大端模式
                MusicBlock--;

                Timer0_Config(0, MAIN_Fosc / MusicSampleRate);        //设置采样率(中断频率)
                rd_index = 0;        //读索引清0
                wr_index = 0;        //写索引清0
                ByteCnt  = 0;        //播放字节计数清0
                dac_L = 0;    //初始DAC值
                dac_R = 0;    //初始DAC值
                B_PlayEn = 1;    //禁止播放
                uart_recv_done();        //对接收的数据处理完成后,一定要调用一次这个函数,以便CDC接收下一笔串口数据
                P21 = 0;    //接收指示,用于测试
            }

            else if(RxCount == 64)        //PC下传数据, 最多64字节一包
            {
                j = rd_index;
                if(j != rd_index)    j = rd_index;    //避免读rd_index时,刚好中断改变
                j = j - wr_index;    //计算空闲缓冲字节数
                j &= 0x0fff;        //缓冲位4096字节
                if((j > 64) || (j == 0))    //空出了超过64字节, 则开始接收一帧64字节数据
                {
                    wr_index &= 0x0fc0;    // 64字节,低6位为0
                    for(j=0; j<64; j++)    voice_buff[wr_index++] = RxBuffer[j];    //接收到的64字节送播放缓冲
                    wr_index = wr_index & 0x0fff;    //避免溢出

                    P20 = 0;    //播放指示灯, 低亮
                    TX1_Limit = 4096;
                    P21 = ~P21;    //接收指示灯取反,用于测试
                    uart_recv_done();        //对接收的数据处理完成后,一定要调用一次这个函数,以便CDC接收下一笔串口数据
                }
            }
            else uart_recv_done();        //对接收的数据处理完成后,一定要调用一次这个函数,以便CDC接收下一笔串口数据
        }
    }
}
/**********************************************/

//========================================================================
// 函数: void  delay_ms(u16 ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数, 1~65535ms. 自动适应主时钟.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-1
// 备注:
//========================================================================
void  delay_ms(u16 ms)
{
     u16 i;
     do
     {
         i = MAIN_Fosc / 6000;
        while(--i)    ;
     }while(--ms);
}

/*
======================================================= WAV数据格式 ==========================================================================
================ 单声道 =======================================
16位单声道PCM(小端模式,低字节在前):
          双字0(4字节)             |           双字1(4字节)             | .......
     采样1      |      采样2       |      采样3      |      采样4       | .......
字节0   字节1   |  字节2   字节3   | 字节0   字节1   |  字节2   字节3   | .......
低字节  高字节  |  低字节  高字节  | 低字节  高字节  |  低字节  高字节  | .......

================ 双声道 =======================================
16位双声道PCM(小端模式,低字节在前):
        双字0(4字节)            |         双字1(4字节)            | .......
             采样1              |              采样2              | .......
    左声道     |     右声道     |     左声道     |     右声道     | .......
字节0   字节1  | 字节2   字节3  | 字节4   字节5  | 字节6   字节7  | .......
低字节  高字节 | 低字节  高字节 | 低字节  高字节 | 低字节  高字节 | .......
=================================================================================================================================================

======================================================= A-law数据格式 ==========================================================================
================ 单声道 =======================================
单声道A-law数据(小端模式,低字节在前):
      双字0(4字节)      |       双字1(4字节)      | .......
字节0 字节1 字节2 字节3 | 字节4 字节5 字节6 字节7 | .......
采样0 采样1 采样2 采样3 | 采样4 采样5 采样6 采样7 | .......

================ 双声道 =======================================
双声道A-law数据(小端模式,低字节在前):
        双字0(4字节)       |       双字1(4字节)         | .......
   采样0        采样1      |    采样2         采样3     | .......
字节0  字节1  字节2  字节3 | 字节4  字节5  字节6  字节7 | .......
左声道 右声道 左声道 右声道| 左声道 右声道 左声道 右声道| .......
=================================================================================================================================================

======================================================= ADPCM数据格式 ==========================================================================
IMA-ADPCM(Adaptive Differential Pulse Code Modulation)
================ 单声道 =======================================
单声道ADPCM一个BLOCK(256或512字节)数据格式(小端模式,低字节在前):
                     双字0(4字节)                           | .......
       字节0      字节1  |          字节2           | 字节3 |        字节4          |        字节5          |...|          字节511            |
采样0未压缩低字节 高字节 | 上一个BLOCK最后一个index | 保留  | 低4位采样1 高4位采样2 | 低4位采样3 高4位采样4 |...| 低4位采样1015 高4位采样1016 |

================ 双声道 =======================================
双声道ADPCM一个BLOCK(512或1024字节)数据格式(小端模式,低字节在前):
                     双字0(4字节)左声道                     |                     双字1(4字节)左声道                       |
       字节0      字节1  |          字节2           | 字节3 |        字节4      字节5  |          字节6           | 字节7  |
采样0未压缩低字节 高字节 | 上一个BLOCK最后一个index | 保留  | 采样0未压缩低字节 高字节 | 上一个BLOCK最后一个index | 保留   |

|                    双字2(4字节)左声道                      |                    双字3(4字节)右声道                      |...| 双字254 | 双字255 |
| B0~B3 B4~B7 B8~B11 B12~B15 B16~B19 B20~B23 B24~B27 B28~B31 | B0~B3 B4~B7 B8~B11 B12~B15 B16~B19 B20~B23 B24~B27 B28~B31
| 采样1 采样2 采样3  采样4    采样5   采样6   采样7   采样8  | 采样1 采样2 采样3  采样4    采样5   采样6   采样7   采样8  |...|  左声道 |  右声道 |
=================================================================================================================================================
*/

//**************** A率解压缩 *******************
//  将7bit A-law数据解压缩成11bit数据
//**************************************************
u16    const T_Alaw_decode[128]={
0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000A,0x000B,0x000C,0x000D,0x000E,0x000F,
0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001A,0x001B,0x001C,0x001D,0x001E,0x001F,
0x0020,0x0022,0x0024,0x0026,0x0028,0x002A,0x002C,0x002E,0x0030,0x0032,0x0034,0x0036,0x0038,0x003A,0x003C,0x003E,
0x0040,0x0044,0x0048,0x004C,0x0050,0x0054,0x0058,0x005C,0x0060,0x0064,0x0068,0x006C,0x0070,0x0074,0x0078,0x007C,
0x0080,0x0088,0x0090,0x0098,0x00A0,0x00A8,0x00B0,0x00B8,0x00C0,0x00C8,0x00D0,0x00D8,0x00E0,0x00E8,0x00F0,0x00F8,
0x0100,0x0110,0x0120,0x0130,0x0140,0x0150,0x0160,0x0170,0x0180,0x0190,0x01A0,0x01B0,0x01C0,0x01D0,0x01E0,0x01F0,
0x0200,0x0220,0x0240,0x0260,0x0280,0x02A0,0x02C0,0x02E0,0x0300,0x0320,0x0340,0x0360,0x0380,0x03A0,0x03C0,0x03E0,
0x0400,0x0440,0x0480,0x04C0,0x0500,0x0540,0x0580,0x05C0,0x0600,0x0640,0x0680,0x06C0,0x0700,0x0740,0x0780,0x07C0};

// IMA-ADPCM的表格数据
char idata index_adjust[16] = {-1,-1,-1,-1,2,4,6,8,-1,-1,-1,-1,2,4,6,8};

u16 const step_table[89] =
{
    7,8,9,10,11,12,13,14,16,17,19,21,23,25,28,31,34,37,41,45,
    50,55,60,66,73,80,88,97,107,118,130,143,157,173,190,209,230,253,279,307,337,371,
    408,449,494,544,598,658,724,796,876,963,1060,1166,1282,1411,1552,1707,1878,2066,
    2272,2499,2749,3024,3327,3660,4026,4428,4871,5358,5894,6484,7132,7845,8630,9493,
    10442,11487,12635,13899,15289,16818,18500,20350,22385,24623,27086,29794,32767
};


//========================================================================
// 函数: void timer0_ISR (void) interrupt TIMER0_VECTOR
// 描述:  timer0中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2018-12-20
//========================================================================
void timer0_ISR (void) interrupt TMR0_VECTOR
{
P27 = 1;    //解码时间指示
    if(B_PlayEn)    //正在播放
    {
        if(ByteCnt >= ByteLength)
        {
            B_PlayEn = 0;    //播放完成了
            P20 = 1;    //播放指示灯, 低亮
        }

        if(TX1_Limit != 0)
        {
            if(--TX1_Limit == 0)    //超时无数据,重新请求发送
            {
                P20 = 1;    //播放指示灯, 低亮
            }
        }

        if(rd_index != wr_index)    //数据流未溢出
        {
            WritePWMA((u8)&PWMA_CCR1H, (u8)(left_pwm/256));        // 比较值, 控制占空比(高电平时钟数)
            WritePWMA((u8)&PWMA_CCR1L, (u8)(left_pwm%256));
            WritePWMA((u8)&PWMA_CCR3H, (u8)(right_pwm/256));    // 比较值, 控制占空比(高电平时钟数)
            WritePWMA((u8)&PWMA_CCR3L, (u8)(right_pwm%256));

            if(MusicType == 1)    //PCM,  WAV类型, fmttag=0x01-->PCM, 0x02-->Windows ADPCM, 0x06-->A Law, 0x07-->Mu Law, 0x11-->IMA ADPCM
            {
                if(MusicChannel == 1)    //单声道    5.5us
                {
                    left_pwm = (u16)voice_buff[rd_index] + ((u16)voice_buff[rd_index+1]<<8);    //左声道
                    rd_index += 2;
                    rd_index &= 0x0fff;
                    ByteCnt += 2;        //每个采样2个字节
                    left_pwm += 32768;    //有符号转位无符号
                    left_pwm /= 16;        //转位12位PWM值
                    right_pwm = left_pwm;    //单声道
                }
                else    //双声道    6.5us
                {
                    left_pwm = (u16)voice_buff[rd_index] + ((u16)voice_buff[rd_index+1]<<8);    //左声道
                    rd_index += 2;
                    right_pwm = (u16)voice_buff[rd_index] + ((u16)voice_buff[rd_index+1]<<8);    //左声道
                    rd_index += 2;
                    rd_index &= 0x0fff;
                    ByteCnt += 4;        //每个采样4个字节
                    left_pwm += 32768;    //有符号转位无符号
                    left_pwm /= 16;        //转位12位PWM值
                    right_pwm += 32768;    //有符号转位无符号
                    right_pwm /= 16;    //转位12位PWM值
                }
            }

            else if(MusicType == 6)    //A-law, WAV类型, fmttag=0x01-->PCM, 0x02-->Windows ADPCM, 0x06-->A Law, 0x07-->Mu Law, 0x11-->IMA ADPCM
            {
                if(MusicChannel == 1)    //单声道    5.5us
                {
                    law_data = voice_buff[rd_index++] ^ 0x55;        //Get Complement 偶数位取反, 从缓冲读数据字节,偶数位取反,循环队列,指向下一个数据
                    left_pwm = T_Alaw_decode[law_data & 0x7f];        // 解压缩码
                    if(law_data & 0x80)    left_pwm = 2048 - left_pwm;    //如果是负的
                    else                left_pwm = 2048 + left_pwm;    //如果是正的
                    right_pwm = left_pwm;    //单声道
                    rd_index &= 0x0fff;
                    ByteCnt++;        //一个采样1个字节
                }
                else    //双声道    6.5us
                {
                    law_data = voice_buff[rd_index++] ^ 0x55;        //Get Complement 偶数位取反, 从缓冲读数据字节,偶数位取反,循环队列,指向下一个数据
                    left_pwm = T_Alaw_decode[law_data & 0x7f];        // 解压缩码
                    if(law_data & 0x80)    left_pwm = 2048 - left_pwm;    //如果是负的
                    else                left_pwm = 2048 + left_pwm;    //如果是正的
                    law_data = voice_buff[rd_index++] ^ 0x55;        //Get Complement 偶数位取反, 从缓冲读数据字节,偶数位取反,循环队列,指向下一个数据
                    right_pwm = T_Alaw_decode[law_data & 0x7f];        // 解压缩码
                    if(law_data & 0x80)    right_pwm = 2048 - right_pwm;    //如果是负的
                    else                right_pwm = 2048 + right_pwm;    //如果是正的

                    rd_index &= 0x0fff;
                    ByteCnt += 2;    //一个采样2个字节
                }
            }

            else if(MusicType == 17)    //ADPCM,  WAV类型, fmttag=0x01-->PCM, 0x02-->Windows ADPCM, 0x06-->A Law, 0x07-->Mu Law, 0x11-->IMA ADPCM
            {
                if(MusicChannel == 1)    //单声道    平均9us
                {
                    if((rd_index & MusicBlock) == 0)    //BLOCK开始, 256或512字节一个BLOAK
                    {
                        dac_L = (u16)voice_buff[rd_index] + ((u16)voice_buff[rd_index+1] << 8);    // 未压缩的采样值
                        rd_index += 2;
                        index_L = voice_buff[rd_index++];    //index值
                        rd_index++;            //空字节
                        ByteCnt += 4;    //4个字节
                        B_high_nibble = 0;
                        dac_L += 32768;        //转成线性
                        cur_sample_L = dac_L;
                        cur_sample_L -= 32768;    //转回有符号
                    }
                    else
                    {
                        decode = voice_buff[rd_index];    //从缓冲读数据字节
                        if(B_high_nibble)        //处理高半字节
                        {
                            ByteCnt++;
                            decode >>= 4;
                            rd_index++;        //指向下一个数据
                            rd_index &= 0x0fff;
                        }
                        decode &= 0x0f;
                        delta = ((u32)step_table[index_L] * ((decode & 0x07)*2 +1)) / 8;    // 计算delta
                        if(decode & 8 )        delta = -delta;    //负的delta
                        cur_sample_L += delta;    //计算出当前的波形数据
                             if(cur_sample_L >= 32768)    dac_L = 65535;
                        else if(cur_sample_L < -32768)    dac_L = 0;
                        else                             dac_L = (u16)(cur_sample_L + 32768);

                        index_L += index_adjust[decode];
                             if (index_L < 0)    index_L = 0;
                        else if (index_L > 88)    index_L = 88;

                        B_high_nibble = ~B_high_nibble;
                    }
                        dac_R = dac_L;        //单声道
                }
                else    //双声道    平均13us, 峰值15us
                {
                    if((rd_index & MusicBlock) == 0)    //BLOCK开始, 512或1024字节一个BLOAK
                    {
                        dac_L = (u16)voice_buff[rd_index] + ((u16)voice_buff[rd_index+1] << 8);    // 未压缩的采样值
                        rd_index += 2;
                        index_L = voice_buff[rd_index];    //index值
                        rd_index += 2;

                        dac_R = (u16)voice_buff[rd_index] + ((u16)voice_buff[rd_index+1] << 8);    // 未压缩的采样值
                        rd_index += 2;
                        index_R = voice_buff[rd_index];    //index值
                        rd_index += 2;

                        ByteCnt += 8;    //8个字节
                        B_high_nibble = 0;
                        dac_L += 32768;        //转成线性
                        cur_sample_L = dac_L;
                        cur_sample_L -= 32768;    //转回有符号
                        dac_R += 32768;        //转成线性
                        cur_sample_R = dac_R;
                        cur_sample_R -= 32768;    //转回有符号
                    }
                    else
                    {
                        if((rd_index & 0x0007) == 0)    //2个DWORD 8个字节
                        {
                            ADPCM_Data_L[0] = voice_buff[rd_index++];    //不用循坏,时间尽量短
                            ADPCM_Data_L[1] = voice_buff[rd_index++];
                            ADPCM_Data_L[2] = voice_buff[rd_index++];
                            ADPCM_Data_L[3] = voice_buff[rd_index++];
                            ADPCM_Data_R[0] = voice_buff[rd_index++];    //不用循坏,时间尽量短
                            ADPCM_Data_R[1] = voice_buff[rd_index++];
                            ADPCM_Data_R[2] = voice_buff[rd_index++];
                            ADPCM_Data_R[3] = voice_buff[rd_index];
                            B_high_nibble = 0;
                            DecodeCnt = 0;
                        }

                        //================ 左声道解压缩 ============================
                        decode = ADPCM_Data_L[DecodeCnt];    //从缓冲读数据字节
                        if(B_high_nibble)    decode >>= 4;    //处理高半字节
                        decode &= 0x0f;

                        delta = ((u32)step_table[index_L] * ((decode & 0x07)*2 +1)) / 8;    // 计算delta
                        if(decode & 8 )        delta = -delta;    //负的delta
                        cur_sample_L += delta;    //计算出当前的波形数据
                             if(cur_sample_L >= 32768)    dac_L = 65535;
                        else if(cur_sample_L < -32768)    dac_L = 0;
                        else                             dac_L = (u16)(cur_sample_L + 32768);

                        index_L += index_adjust[decode];
                             if (index_L < 0)    index_L = 0;
                        else if (index_L > 88)    index_L = 88;
                        //======================== 左声道解压缩完毕 ==================================

                        //======================== 右声道解压缩 ============================
                        decode = ADPCM_Data_R[DecodeCnt];    //从缓冲读数据字节
                        if(B_high_nibble)            //处理高半字节
                        {
                            if(DecodeCnt >= 3)    //4个字节都处理完成
                            {
                                ByteCnt += 8;    //4个字节
                                rd_index++;        //指向下一个数据
                                rd_index &= 0x0fff;
                            }
                            DecodeCnt++;
                            decode >>= 4;
                        }
                        decode &= 0x0f;

                        delta = ((u32)step_table[index_R] * ((decode & 0x07)*2 +1)) / 8;    // 计算delta
                        if(decode & 8 )        delta = -delta;    //负的delta
                        cur_sample_R += delta;    //计算出当前的波形数据
                             if(cur_sample_R >= 32768)    dac_R = 65535;
                        else if(cur_sample_R < -32768)    dac_R = 0;
                        else                             dac_R = (u16)(cur_sample_R + 32768);

                        index_R += index_adjust[decode];
                             if (index_R < 0)    index_R = 0;
                        else if (index_R > 88)    index_R = 88;
                        //======================== 右声道解压缩完毕 ==================================

                        B_high_nibble = ~B_high_nibble;    //高半字节指示
                    }
                }
                left_pwm  = dac_L/16;    //转位12位PWM值
                right_pwm = dac_R/16;    //转位12位PWM值
            }
        }
    }
P27 = 0;
}

//========================================================================
// 函数:u8    Timer0_Config(u8 t, u32 reload)
// 描述: timer0初始化函数.
// 参数:      t: 重装值类型, 0表示重装的是系统时钟数, 其余值表示重装的是时间(us).
//       reload: 重装值.
// 返回: 0: 初始化正确, 1: 重装值过大, 初始化错误.
// 版本: V1.0, 2018-12-20
//========================================================================
u8    Timer0_Config(u8 t, u32 reload)    //t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us)
{
    TR0 = 0;    //停止计数

    if(t != 0)    reload = (u32)(((float)MAIN_Fosc * (float)reload)/1000000UL);    //重装的是时间(us), 计算所需要的系统时钟数.
    if(reload >= (65536UL * 12))    return 1;    //值过大, 返回错误
    if(reload < 65536UL)    AUXR |= 0x80;        //1T mode
    else
    {
        AUXR &= ~0x80;    //12T mode
        reload = reload / 12;
    }
    reload = 65536UL - reload;
    TH0 = (u8)(reload >> 8);
    TL0 = (u8)(reload);

    ET0 = 1;    //允许中断
    PT0 = 1;    //高优先级中断
    TMOD = (TMOD & ~0x03) | 0;    //工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
    TR0 = 1;    //开始运行
    return 0;
}

void delay(void)
{
    u16    i;
    for    (i=0; i<100; i++);
}

u8    ReadPWMA(u8 addr)
{
    u8    dat;

    while (HSPWMA_ADR &    0x80); //等待前一个异步读写完成
    HSPWMA_ADR = addr |    0x80;  //设置间接访问地址,只需要设置原XFR地址的低7位
                                //HSPWMA_ADDR寄存器的最高位写1,表示读数据
    while (HSPWMA_ADR &    0x80); //等待当前异步读取完成
    dat    = HSPWMA_DAT;           //读取异步数据

    return dat;
}

void WritePWMA(u8 addr, u8 dat)
{
    while (HSPWMA_ADR &    0x80); //等待前一个异步读写完成
    HSPWMA_DAT = dat;           //准备需要写入的数据
    HSPWMA_ADR = addr &    0x7f;  //设置间接访问地址,只需要设置原XFR地址的低7位
                                //HSPWMA_ADDR寄存器的最高位写0,表示写数据
}

//========================================================================
// 函数: void PWMA_config(void)
// 描述: PWM配置函数。
// 参数: noe.
// 返回: none.
// 版本: V1.0, 2022-3-15
// 备注:
//========================================================================
void PWMA_config(void)
{
    u8    ccer1;
    u8    ccer2;
    u8    ps;
    u8    eno;

    P_SW2 |= 0x80;        //SFR enable

    CLKSEL |= 0x80;                        //内部PLL输出时钟选择, |=0x80: 选择PLL 144MHz, &=~0x80: 选择96MHz(默认)
    USBCLK  = (USBCLK &~0x60) | (1<<5);    //选择PLL输入时钟分频,保证输入时钟为12M, 0: 1分频(对应12MHz), 1: 2分频(对应24MHz), 2: 4分频(对应48MHz), 3: 8分频(对应96MHz)
    USBCLK |= 0x80;                        //PLL倍频控制, |=0x80: 使能PLL倍频. &= ~0x80: 禁止PLL倍频
    delay();                            //等待PLL锁频
    CLKSEL |= 0x40;                        //高速IO时钟源选择, |=0x40: 选择PLLCLK,  &=~0x40: 选择MCLK(默认)
    HSCLKDIV = 0;                        //高速时钟分频器 0~255 (默认2)
    CLKDIV  = 6;                         //主时钟分频系数, 1~255,  144/5=28.8MHz
    CLKSEL = (CLKSEL &~0x0c) | (1<<2);    //主时钟源选择, 0: MCKSEL选择的时钟源(默认), 1: 内部PLL输出, 2: 内部PLL输出/2, 3: 内部48MHz高速IRC

    HSPWMA_CFG = 0x04 + 0x02 + 0x01;    // +0x04:使能异步模式下的PWM中断, +0x00:禁止中断.  +0x02:使能异步控制模式, +0x00:禁止异步控制模式.   +0x01:固定.
    WritePWMA((u8)&PWMA_ENO, 0x00);        // IO输出禁止
    WritePWMA((u8)&PWMA_IER, 0x00);        // 禁止中断
    WritePWMA((u8)&PWMA_SR1, 0x00);        // 清除状态
    WritePWMA((u8)&PWMA_SR2, 0x00);        // 清除状态
    ccer1 = 0;
    ccer2 = 0;
    ps    = 0;
    eno   = 0;
    PWMA_ISR_En = 0;

    //通过异步方式设置PWMA的相关寄存器
    WritePWMA((u8)&PWMA_PSCRH, 0x00);    // 预分频寄存器, 分频 Fck_cnt = Fck_psc/(PSCR[15:0}+1), 边沿对齐PWM频率 = SYSclk/((PSCR+1)*(AAR+1)), 中央对齐PWM频率 = SYSclk/((PSCR+1)*(AAR+1)*2).
    WritePWMA((u8)&PWMA_PSCRL, 0x00);
    WritePWMA((u8)&PWMA_DTR, 24);        // 死区时间配置, n=0~127: DTR= n T,   0x80 ~(0x80+n), n=0~63: DTR=(64+n)*2T,
                                        //                0xc0 ~(0xc0+n), n=0~31: DTR=(32+n)*8T,   0xE0 ~(0xE0+n), n=0~31: DTR=(32+n)*16T,
    WritePWMA((u8)&PWMA_ARRH, 4096/256);    // 自动重装载寄存器,  控制PWM周期
    WritePWMA((u8)&PWMA_ARRL, 4096%256);

    WritePWMA((u8)&PWMA_CCMR1, 0x68);        // 通道模式配置, PWM模式1, 预装载允许
    WritePWMA((u8)&PWMA_CCR1H, 2048/256);    // 比较值, 控制占空比(高电平时钟数)
    WritePWMA((u8)&PWMA_CCR1L, 2048%256);
    ccer1 |= 0x05;            // 开启比较输出, 高电平有效
    ps    |= 0;                // 选择IO, 0:选择P1.0 P1.1, 1:选择P2.0 P2.1, 2:选择P6.0 P6.1,
    eno   |= 0x03;            // IO输出允许,  bit7: ENO4N, bit6: ENO4P, bit5: ENO3N, bit4: ENO3P,  bit3: ENO2N,  bit2: ENO2P,  bit1: ENO1N,  bit0: ENO1P
//    PWMA_ISR_En |= 0x02;    // 使能中断
/*
    WritePWMA((u8)&PWMA_CCMR2, 0x68);        // 通道模式配置, PWM模式1, 预装载允许
    WritePWMA((u8)&PWMA_CCR2H, 800/256);    // 比较值, 控制占空比(高电平时钟数)
    WritePWMA((u8)&PWMA_CCR2L, 800%256);
    ccer1 |= 0x50;            // 开启比较输出, 高电平有效
    ps    |= (1<<2);        // 选择IO, 0:选择P1.2 P1.3, 1:选择P2.2 P2.3, 2:选择P6.2 P6.3,
    eno   |= 0x0C;            // IO输出允许,  bit7: ENO4N, bit6: ENO4P, bit5: ENO3N, bit4: ENO3P,  bit3: ENO2N,  bit2: ENO2P,  bit1: ENO1N,  bit0: ENO1P
//    PWMA_ISR_En |= 0x04;    // 使能中断
*/
    WritePWMA((u8)&PWMA_CCMR3, 0x68);        // 通道模式配置, PWM模式1, 预装载允许
    WritePWMA((u8)&PWMA_CCR3H, 2048/256);    // 比较值, 控制占空比(高电平时钟数)
    WritePWMA((u8)&PWMA_CCR3L, 2048%256);
    ccer2 |= 0x05;            // 开启比较输出, 高电平有效
    ps    |= (0<<4);        // 选择IO, 0:选择P1.4 P1.5, 1:选择P2.4 P2.5, 2:选择P6.4 P6.5,
    eno   |= 0x30;            // IO输出允许,  bit7: ENO4N, bit6: ENO4P, bit5: ENO3N, bit4: ENO3P,  bit3: ENO2N,  bit2: ENO2P,  bit1: ENO1N,  bit0: ENO1P
//    PWMA_ISR_En |= 0x08;    // 使能中断
/*
    WritePWMA((u8)&PWMA_CCMR4, 0x68);        // 通道模式配置, PWM模式1, 预装载允许
    WritePWMA((u8)&PWMA_CCR4H, 1600/256);    // 比较值, 控制占空比(高电平时钟数)
    WritePWMA((u8)&PWMA_CCR4L, 1600%256);
    ccer2 |= 0x50;            // 开启比较输出, 高电平有效
    ps    |= (1<<6);        // 选择IO, 0:选择P1.6 P1.7, 1:选择P2.6 P2.7, 2:选择P6.6 P6.7, 3:选择P3.3 P3.4
    eno   |= 0xc0;            // IO输出允许,  bit7: ENO4N, bit6: ENO4P, bit5: ENO3N, bit4: ENO3P,  bit3: ENO2N,  bit2: ENO2P,  bit1: ENO1N,  bit0: ENO1P
//    PWMA_ISR_En |= 0x10;    // 使能中断
*/
//    PWMA_ISR_En = 0x01;    // 使能更新中断

    WritePWMA((u8)&PWMA_CCER1, ccer1);        // 捕获/比较使能寄存器1
    WritePWMA((u8)&PWMA_CCER2, ccer2);        // 捕获/比较使能寄存器2
    PWMA_PS = ps;                            // 选择IO
    WritePWMA((u8)&PWMA_IER, PWMA_ISR_En);    // 设置允许通道1~4中断处理

    WritePWMA((u8)&PWMA_BKR, 0x80);        // 主输出使能 相当于总开关
    WritePWMA((u8)&PWMA_CR1, 0x81);        // 使能计数器, 允许自动重装载寄存器缓冲, 边沿对齐模式, 向上计数,  bit7=1:写自动重装载寄存器缓冲(本周期不会被打扰), =0:直接写自动重装载寄存器本(周期可能会乱掉)
    WritePWMA((u8)&PWMA_EGR, 0x01);        // 产生一次更新事件, 清除计数器和预分频计数器, 装载预分频寄存器的值
    WritePWMA((u8)&PWMA_ENO,  eno);        // 允许IO输出
}

//    PWMA_PS   = (0<<6)+(0<<4)+(0<<2)+0;    //选择IO, 4项从高到低(从左到右)对应PWM1 PWM2 PWM3 PWM4, 0:选择P1.x, 1:选择P2.x, 2:选择P6.x,
//  PWMA_PS    PWM4N PWM4P    PWM3N PWM3P    PWM2N PWM2P    PWM1N PWM1P
//    00       P1.7  P1.6     P1.5  P1.4     P1.3  P5.4     P1.1  P1.0
//    01       P2.7  P2.6     P2.5  P2.4     P2.3  P2.2     P2.1  P2.0
//    02       P6.7  P6.6     P6.5  P6.4     P6.3  P6.2     P6.1  P6.0
//    03       P3.3  P3.4      --    --       --    --       --    --


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值