国学芯用 发表于 2026-7-2 13:14:05

81-I2S-播放下载在FLASH中的立体声ADPCM音乐-TLV320AIC23B,AI8051U实验箱演示程序

81-I2S-播放下载在FLASH中的立体声ADPCM音乐-TLV320AIC23B,AI8051U实验箱演示程序

主程序main.C代码如下(汇编版本在附件中):

/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序            */
/*---------------------------------------------------------------------*/

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



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

#include        "TLV320AIC23.h"
#include        "W25Q128FV.h"

/*************        功能说明        **************
本程序使用AI8051U实验箱V1.2验证。用户先别修改程序,直接下载HEX文件到AI8051U实验箱V1.2来验证,下载时选择主频40.96MHz。

使用AI8051U系列MCU播放下载存储于FLASH中的ADPCM音乐,支持立体声或单声道,采样率固定为32KHz,用户可以自行修改采样率。
音乐文件由下载工具下载。
使用ADPCM是为了减少容量,16MB的FLASH可以播放8分30秒的立体声音乐,2首歌的容量。

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

/*************        IO口定义        **************/
void Send_595(u8 dat);
sbit        P_HC595_SER   = P3^4;        //pin 14        SER                data input
sbit        P_HC595_RCLK= P3^5;        //pin 12        RCLk        store (latch) clock
sbit        P_HC595_SRCLK = P3^2;        //pin 11        SRCLK        Shift data clock

/*************        变量定义        **************/
u8        xdata voice_buff;
u8        MusicType;                        //WAV类型, fmttag=0x01-->PCM, 0x02-->Windows ADPCM, 0x06-->A Law, 0x07-->Mu Law, 0x11-->IMA ADPCM
u8        MusicChannel;                //音乐声道
u16        MusicSampleRate;        //采样率
u32        FileLength;                        //文件长度(字节)
u32        PlayByteCnt;                //播放字节计数
u16        dac_L, dac_R;                //解码后输出的左后声道DAC值
bit        B_PlayEn;                        //允许播放

u16        wr_index;                //写缓冲索引
u16        rd_index;                //读缓冲索引
u32        FlashAddr;                //读FLASH地址
u8        OP_index;                //操作索引, 0:无操作, 1:读取头文件处理并启动播放, 2: 读取数据, 3:擦除FLASH, 4:写入FFLASH
u8        RcvTimeOut;
bit        B_DownLoadStart;        //开始下载

//======= ADPCM解码专用变量 ==========
u8                xdata ADPCM_Data_L;        //左声道局部缓冲
u8                xdata ADPCM_Data_R;        //右声道局部缓冲
u8                decode;                                        //编码值
long        delta;                                //偏差
long        cur_sample_L;                //左声道解码值
long        cur_sample_R;                //右声道解码值
char        index_L;                        //左声道解码索引
char        index_R;                        //右声道解码索引
u8                DecodeCnt;                        //解码计数
u16                MusicBlock;                        //BLOCK大小
bit                B_high_nibble;                //高半字节指示
//====================================

bit B_stop;                //1: 停止录音或放音
u16        second;
u8        cnt_1s;

u8        de_index;                //ADPCM索引
u16        pre_sample;                //前一个采样值
bit        B_high_nibble;        //高半字节标志
u8        adpcm_data;                //ADPCM数据

u8        cnt_1ms;                // 1ms计数,用户层不可见
u8        cnt_20ms;                //20ms计数,用户层不可见
bit        B_20ms;                        //20ms时隙,用户层使用并清除
u16        HeadPhoneVol;        //耳机音量, 0~80, 0->mute, 1->-73db, 80->+6db, 74->0db, 1db/step.

u8         LED8;                //显示缓冲
u8        display_index;        //显示位索引
u8        KeyCode;                //给用户使用的键码
u8        IO_KeyState;        //行列键盘变量



//===================================================
#define DIS_DOT                0x20
#define DIS_BLACK        0x10
#define DIS_                0x11

#define DIS_S                0x05
#define DIS_T                0x1A
#define DIS_O                0x17
#define DIS_P                0x18
#define DIS_R                0x1D
#define DIS_E                0x0E
#define DIS_C                0x0C
#define DIS_D                0x0D
#define DIS_L                0x15
#define DIS_A                0x0A
#define DIS_Y                0x1F

#define        K0        0x01
#define        K1        0x02
#define        K2        0x04
#define        K3        0x08
#define        K4        0x10
#define        K5        0x20
#define        K6        0x40
#define        K7        0x80


u8 code t_display[]={                                                //标准字库
//       0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
        0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
//black       -   H    J       K          L           N        o   P       U   t    G    Q    r   M    y
        0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
        0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46};        //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1

u8 code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};                //位码


/*************        函数声明        **************/

void        I2S_config(void);
void        SetSampleRate(void);
void        delay_ms(u16 ms);
void        SetHeadPhoneVolume(u16 vol);
void        IO_KeyScan(void);        //50ms call
u8                CheckFile(u8 *px, u8 const *pc, u8 num);
void         CDC_StringPrint(u8 *puts);
void        PlayProcess(void);



u8 const C_DownLoad[]="DownLoad";

//==============================================================================================================
//        *******************                                                           main()                                                        *********************************
//===============================================================================================================
void main(void)
{
        u16        i;

        EAXSFR();        //SFR enable
        WTST= 0;
        CKCON = 0;

        P0M0 = 0x00;
        P0M1 = 0x00;
        P1M0 = 0x00;
        P1M1 = 0x00;
        P2M0 = 0x00;
        P2M1 = 0x00;
        P3M0 = 0x00;
        P3M1 = 0x00;
        P4M0 = 0x00;
        P4M1 = 0x00;
        P5M0 = 0x00;
        P5M1 = 0x00;

        P3n_pure_input(0x03);        //P3.0(D-)、P3.1(D+)设置为高阻
        IRC48MCR = 0x80;
        while (!(IRC48MCR & 0x01));

        uart_init();
        usb_init();
        EA = 1;
        delay_ms(1500);
//        while(DeviceState != DEVSTATE_CONFIGURED)        {        NOP(3);        }

        TFPU_CLKDIV = 1;                //TFPU分频

        SPI_Config(2, 0);        //(SPI_io, SPI_speed), 参数:         SPI_io: 切换IO(SS MOSI MISO SCLK), 0: 切换到P1.4 P1.5 P1.6 P1.7,1: 切换到P2.4 P2.5 P2.6 P2.7, 2: 切换到P4.0 P4.1 P4.2 P4.3,3: 切换到P3.5 P3.4 P3.3 P3.2,
                                                //                                                                SPI_speed: SPI的速度, 0: fosc/4,1: fosc/8,2: fosc/16,3: fosc/2

        cnt_1ms = 0;        // 1ms计数,用户层不可见
        cnt_20ms = 0;        //20ms计数,用户层不可见
        B_20ms    = 0;        //20ms时隙,用户层使用并清除
        for(i=0; i<8; i++)        LED8 = DIS_BLACK;        //上电消隐
        LED8 = DIS_S;        //显示stop
        LED8 = DIS_T;
        LED8 = DIS_O;
        LED8 = DIS_P;
        cnt_1s = 0;

        delay_ms(10);
        AIC23_Init();
        delay_ms(50);
        AIC32_InitSet();
        delay_ms(50);
        HeadPhoneVol = 80 - 30;//默认音量
        SetHeadPhoneVolume(HeadPhoneVol);        //设置音量
        LED8 = HeadPhoneVol / 10;        //显示音量
        LED8 = HeadPhoneVol % 10;

        I2S_config();
        EA = 1;

        OP_index = 0;

        B_FlashOK = 0;
        FlashCheckID();
        FlashCheckID();
        B_FlashOK = 0;
        if((FLASH_ID >= 0x12) && (FLASH_ID <= 0x19))        B_FlashOK = 1;        //检测到FLASH



        while(1)
        {

                if(OP_index != 4)        //非下载模式
                {
                        if (RxFlag)                //当RxFlag为1时, 表示已接收到CDC串口数据, 接收的数据大小保存在RxCount里面,每个包最多接收64字节, 数据保存在RxBuffer缓冲区.
                        {
                                if(RxCount == 1)        KeyCode = RxBuffer;        //PC下传键码
                                else if(RxCount == 8)
                                {
                                        if(CheckFile(RxBuffer, C_DownLoad, 8) == 0)                //(u8 *px, u8 const *pc, u8 num) 判断是不是下载请求
                                        {
                                                if(B_FlashOK)        //FLASH存在
                                                {
                                                        B_PlayEn = 0;        //停止播放
                                                        CDC_StringPrint("正\xfd在擦除\xfd FLASH,请稍后...\r\n");
                                                        FlashChipErase();        //执行片擦除命令
                                                        OP_index = 3;        //等待擦除完成
                                                        LED8 = DIS_;        //显示-EA-
                                                        LED8 = DIS_E;
                                                        LED8 = DIS_A;
                                                        LED8 = DIS_;
                                                        LED8 = DIS_BLACK;        //录放时间消隐不显示
                                                }
                                        }
                                }
                                uart_recv_done();        //对接收的数据处理完成后,一定要调用一次这个函数,以便CDC接收下一笔串口数据

                                if(KeyCode == 'r')        //返回80字节头文件
                                {
                                        FlashRead_Nbytes(0, TxBuffer, 80);        //(u32 addr, u8 *buffer, u16 size)        读取数据
                                        uart_send(80);        //触发发送. 将要发送的数据保存在TxBuffer缓冲区中, 然后调用uart_send(n)函数触发数据发送,参数为发送的字节数. 一次最多可发送64K,函数内部会自动进行USB分包.
                                }
                        }
                }

                if(OP_index != 0)        PlayProcess();


                if(B_20ms)        //1ms时隙
                {
                        B_20ms = 0;
                        if(RcvTimeOut != 0)        RcvTimeOut--;        //接收超时控制

                        if(B_PlayEn)
                        {
                                if(++cnt_1s >= 50)        //秒计时
                                {
                                        cnt_1s = 0;
                                        second++;
                                        i = second / 60;
                                        LED8 = (u8)(i/10);
                                        LED8 = (u8)(i%10 +DIS_DOT);
                                        i = second % 60;
                                        LED8 = i/10;
                                        LED8 = i%10;                //放音时间
                                }
                        }

                        if(B_stop)        //停止录音或放音
                        {
                                B_stop = 0;
                                B_PlayEn = 0;        //停止播放
                                LED8 = DIS_S;        //显示stop
                                LED8 = DIS_T;
                                LED8 = DIS_O;
                                LED8 = DIS_P;
                                LED8 = DIS_BLACK;        //录放时间消隐不显示
                        }

                        IO_KeyScan();
                        if(KeyCode != 0)        //有键按下
                        {
                                if(KeyCode == K2)        //停止播放
                                {
                                        OP_index = 0;        //操作索引, 0:无操作, 1:读取头文件处理并启动播放, 2: 读取数据, 3:擦除FLASH, 4:写入FFLASH
                                        B_PlayEn = 0;        //停止播放
                                        B_stop   = 1;
                                }
                                else if(KeyCode == K3)        //放音
                                {
                                        if(B_FlashOK)        //FLASH存在
                                        {
                                                OP_index = 1;        //操作索引, 0:无操作, 1:读取头文件处理并启动播放, 2: 读取数据, 3:擦除FLASH, 4:写入FFLASH
                                                second = 0;
                                                LED8 = DIS_P;        //显示P
                                                LED8 = 0;
                                                LED8 = 0+DIS_DOT;
                                                LED8 = 0;
                                                LED8 = 0;                //放音时间
                                        }
                                }
                                else if(KeyCode == K6)        //音量+
                                {
                                        if(++HeadPhoneVol > 80)        HeadPhoneVol = 80;        //最大音量
                                        SetHeadPhoneVolume(HeadPhoneVol);        //设置音量
                                        LED8 = HeadPhoneVol / 10;        //显示音量
                                        LED8 = HeadPhoneVol % 10;
                                }
                                else if(KeyCode == K7)        //音量-
                                {
                                        if(HeadPhoneVol != 0)        HeadPhoneVol--;        //最小音量
                                        SetHeadPhoneVolume(HeadPhoneVol);        //设置音量
                                        LED8 = HeadPhoneVol / 10;        //显示音量
                                        LED8 = HeadPhoneVol % 10;
                                }

                                KeyCode = 0;
                        }
                }
        }
}



u8 const F_RIFF[]="RIFF";                        //文件头 00H~03H
u8 const F_WAVEfmt[]="WAVEfmt ";        //文件头 08H~0FH
u8 const F_fact[]="fact";                        //文件头 28H~2BH
u8 const F_data[]="data";                        //文件头 34H~37H
u8        CheckFile(u8 *px, u8 const *pc, u8 num)
{
        u8        i;
        for(i=0; i<num; i++)
        {
                if(px != pc)        return 1;        //字符不相等错误
        }
        return 0;        //字符串正确
}

void        PlayProcess(void)
{
        u16        j;
        if(OP_index == 1)                //操作索引, 0:无操作, 1:读取头文件处理并启动播放, 2: 读取数据, 3:擦除FLASH, 4:写入FFLASH
        {
                FlashRead_Nbytes(0, voice_buff, 100);//(u32 addr, u8 *buffer, u16 size)        读取头文件
                j= 0;
                j= CheckFile(voice_buff, F_RIFF, 4);                        //检测RIFF                文件头 00H~03H
                j |= CheckFile(voice_buff+0x08, F_WAVEfmt, 8);        //检测WAVEfmt        文件头 08H~0FH
                j |= CheckFile(voice_buff+0x28, F_fact, 4);                //检测fact                文件头 28H~2BH
                j |= CheckFile(voice_buff+0x34, F_data, 4);                //检测data                文件头 34H~37H
                if((j == 0) && (voice_buff==0x11) && (voice_buff==0x00))        //文件头 14H~15H 为IMA-ADPCM的类型标识0x0011
                {
                        B_PlayEn= 0;        //停止播放
                        MusicChannel    = voice_buff;        //声道数, 小端模式,
                        MusicSampleRate = (u16)voice_buff*256 + voice_buff;        //采样率, 小端模式,
                        FileLength      = ((u32)voice_buff << 24) + ((u32)voice_buff << 16) + (u32)voice_buff*256 + voice_buff;        //数据字节长度, 小端模式,
                        MusicBlock      = (u16)voice_buff*256+voice_buff;        //数据BLOCK长度(ADPCM使用), 小端模式,
                        MusicBlock--;
                        FlashAddr = 0x003c;        //读FLASH的地址
                        FlashRead_Nbytes(FlashAddr, voice_buff, 1024);        //(u32 addr, u8 *buffer, u16 size)        读取数据
                        FlashAddr += 1024;
                        FlashRead_Nbytes(FlashAddr, voice_buff+1024, 1024);        //(u32 addr, u8 *buffer, u16 size)        读取数据
                        FlashAddr += 1024;
                        wr_index = 2048;        //写缓冲索引
                        rd_index = 0;                //读缓冲索引
                        PlayByteCnt = 0;        //播放字节计数清0
                        dac_L = 0;        //初始DAC值
                        dac_R = 0;        //初始DAC值
                        B_PlayEn = 1;        //启动播放
                        OP_index = 2;
                }
                else OP_index = 0, B_stop = 1;        //无IMA-ADPCM文件
        }

        else if(OP_index == 2)                //操作索引, 0:无操作, 1:读取头文件处理并启动播放, 2: 读取数据, 3:擦除FLASH, 4:写入FFLASH
        {
                if(!B_SPI_DMA_busy)
                {
                        j = (rd_index - wr_index) & VOICE_BUFF_MASK;        //计算空闲缓冲字节数
                        if((j > 1024) || (j == 0))        //空出了超过1024字节
                        {
                                if(FlashAddr < FileLength)        //未到文件结束, 继续读FLASH
                                {
                                //        FlashRead_Nbytes(FlashAddr, voice_buff+wr_index, 1024);        //(u32 addr, u8 *buffer, u16 size)        读取数据
                                        SPI_DMA_RxTRIG(FlashAddr, voice_buff+wr_index, 1024);//(u32 addr, u8 *buffer, u16 size);        SPI DMA读取数据
                                        FlashAddr += 1024;        //读FLASH的地址+1024
                                        wr_index+= 1024;        //写缓冲索引+1024
                                        wr_index&= VOICE_BUFF_MASK;        //溢出处理
                                }
                        }
                }
        }

        else if(OP_index == 3)                //操作索引, 0:无操作, 1:读取头文件处理并启动播放, 2: 读取数据, 3:擦除FLASH, 4:写入FFLASH
        {
                if(FlashCheckBusy() == 0)
                {
                        CDC_StringPrint("FLASH擦除\xfd完成! 请发送ADPCM音乐文件!\r\n");
                        FlashAddr= 0;
                        MusicBlock = 0;
                        RcvTimeOut = 0;
                        B_DownLoadStart = 0;
                        OP_index = 4;                //等待下传数据
                        LED8 = DIS_D;        //显示-DL-
                        LED8 = DIS_L;
                }
        }
        else if(OP_index == 4)                //操作索引, 0:无操作, 1:读取头文件处理并启动播放, 2: 读取数据, 3:擦除FLASH, 4:写入FFLASH
        {
                if (RxFlag)                        //当RxFlag为1时, 表示已接收到CDC串口数据, 接收的数据大小保存在RxCount里面,每个包最多接收64字节, 数据保存在RxBuffer缓冲区.
                {
                        B_DownLoadStart=1;
                        RcvTimeOut = 5;        // 超时100ms计数
                        if(RxCount == 64)        //完整的一块数据64字节
                        {
                                if((FlashAddr == 0) && (MusicBlock == 0))        //收到第一帧数据
                                        FileLength   = ((u32)RxBuffer << 24) + ((u32)RxBuffer << 16) + (u32)RxBuffer*256 + RxBuffer+8;        //文件总长-8(即减去RIFF和本段4字节), 小端模式,
                                for(j=0; j<64; j++)        voice_buff = RxBuffer;        //暂存数据
                                if(MusicBlock >= 256)        //满一页,则写FLASH
                                {
                                        FlashWrite_Nbytes(FlashAddr, voice_buff, 256);        //u32 addr, u8 *buffer, u16 size)
                                        FlashAddr += 256;
                                        if(FlashAddr >= FileLength)        OP_index = 0, B_stop = 1;        //下载完成
                                        MusicBlock = 0;
                                }
                        }
                        else        //不满64字节,则是最后剩余的字节
                        {
                                if((RxCount == 1) && (RxBuffer == 'c'))        OP_index = 0;        //取消下载
                                for(j=0; j<RxCount; j++)        voice_buff = RxBuffer;        //暂存数据
                                FlashWrite_Nbytes(FlashAddr, voice_buff, MusicBlock);        //u32 addr, u8 *buffer, u16 size)
                                FlashAddr += MusicBlock;
                                OP_index = 0;        //下载完成
                        }

                        uart_recv_done();               //对接收的数据处理完成后,一定要调用一次这个函数,以便CDC接收下一笔串口数据

                }
                if(B_DownLoadStart && (RcvTimeOut == 0))        B_DownLoadStart = 0, OP_index = 0, B_stop = 1;        //超时结束
                if(OP_index == 0)        CDC_StringPrint("下载结束!\r\n");
        }
}



//========================================================================
// 函数: void CDC_StringPrint(u8 *puts)
// 描述: 串口1字符串打印函数
// 参数: puts: 字符串指针.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void CDC_StringPrint(u8 *puts)
{
        u16        j;
    for (j=0; *puts != 0;        puts++)        TxBuffer = *puts;
        uart_send(j);        //触发发送. 将要发送的数据保存在TxBuffer缓冲区中, 然后调用uart_send(n)函数触发数据发送,参数为发送的字节数. 一次最多可发送64K,函数内部会自动进行USB分包.
}

/*****************************************************
        行列键扫描程序
        使用XY查找4x4键的方法,只能单键,速度快

   Y   P00      P01      P02      P03
          |      |      |      |
X         |      |      |      |
P07 ---- K00 ---- K01 ---- K02 ---- K03 ----
          |      |      |      |
P06 ---- K04 ---- K05 ---- K06 ---- K07 ----
          |      |      |      |
******************************************************/

void IO_KeyDelay(void)
{
        u8 i;
        i = 40;
        while(--i)        ;
}

void        IO_KeyScan(void)        //50ms call
{
        u8        j;

        j = IO_KeyState;        //保存上一次状态

        P06 = 0;
        IO_KeyDelay();
        IO_KeyState = P0 & 0x0f;
        P06 = 1;

        P07 = 0;
        IO_KeyDelay();
        IO_KeyState |= (P0 << 4) & 0xf0;
        IO_KeyState ^= 0xff;        //取反
        P07 = 1;

        KeyCode |= (j ^ IO_KeyState) & IO_KeyState;
}


//=================== 设置耳机音量 ==========================================
void        SetHeadPhoneVolume(u16 vol)
{
        AIC23_WriteCmd(R_HeadphoneVolume_L, (LinVol_LRS | LinVol_Mute | (vol+47)));        //耳机输出音量
        AIC23_WriteCmd(R_HeadphoneVolume_R, (RinVol_RLS | RinVol_Mute | (vol+47)));
}

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

/**************** 向HC595发送一个字节函数 ******************/
void Send_595(u8 dat)
{
        u8        i;
        for(i=0; i<8; i++)
        {
                dat <<= 1;
                P_HC595_SER   = CY;
                NOP(1);
                P_HC595_SRCLK = 1;
                NOP(1);
                P_HC595_SRCLK = 0;
        }
}

/********************** 显示扫描函数 ************************/
void DisplayScan(void)
{
        Send_595(t_display]);        //输出段码
        Send_595(~T_COM);                        //输出位码

        P_HC595_RCLK = 1;
        NOP(2);
        P_HC595_RCLK = 0;                                                        //锁存输出数据
        if(++display_index >= 8)        display_index = 0;        //8位结束回0
}


//====================== I2S初始化函数 ==================================================
#define        MCKOE                1                //I2S主时钟输出控制, 0:禁止I2S主时钟输出, 1:允许I2S主时钟输出

#define        I2SEN                0x04        //I2S模块使能, 0x00:禁止, 0x04:允许
#define        I2S_MODE        2                //I2S模式, 0:从机发送模式, 1:从机接收模式, 2:主机发送模式, 3:主机接收模式,

#define        PCMSYNC                0                //PCM帧同步, 0: 短帧同步, 1: 长帧同步
#define        STD_MODE        0                //I2S标准选择, 0: I2S飞利浦标准, 1: MSB左对齐标准, 2:LSB右对齐标准, 3:PCM标准, CS4334、CS4344使用0:I2S飞利浦标准,PT8211使用1: MSB左对齐标准。
#define        CKPOL                0                //I2S稳态时钟极性, 0:时钟稳定状态为低电平, 1:时钟稳定状态为高电平
#define        DATLEN                0                //数据长度, 0:16位, 1:24位, 2:32位, 3:保留
#define        CHLEN                0                //通道长度(每个音频通道的位数), 0:16位, 1: 32位

#define I2S_MCLKDIV                (FOSC/8/16/2/SampleRate)//(FOSC/(8*16*2*SampleRate))        //MCLK分频系数, 对于双声道16bit.
#define I2S_BCLKDIV                (FOSC/16/2/SampleRate)                //BCLK分频系数, 对于双声道16bit.

void        I2S_config(void)
{
        I2SMD = 0xff;                                        //内部保留字节,需设置为FFH
        I2SSR = 0x00;                                        //状态寄存器清0
        I2SCR = 0x80+0x00;                                //使能发送缓冲区空中断(0x80), +0x00:Motorola格式, +0x10:TI格式
        HSCLKDIV    = 1;                                //高速时钟分频器 1~255 (默认2)
        I2S_CLKDIV = 1;                                        //I2S主时钟分频
        I2SMCKDIV= I2S_MCLKDIV;                                        //I2S时钟分频,I2SMCLK = 主频/2/I2S_CLKDIV/HSCLKDIV/I2SMCKDIV,或I2SMCLK = PLLCLK/2/I2S_CLKDIV/HSCLKDIV/I2SMCKDIV
        I2SPRH = (MCKOE << 1) + (I2S_BCLKDIV & 1);        //设置I2S_BMCLK分频系数的bit0, 并允许或禁止输出MCLK。
        I2SPRL = I2S_BCLKDIV/2;                                                //设置I2S_BMCLK分频系数的bit8~bit1
        I2SCFGH = I2S_MODE;                                //设置I2S模式为主机发送模式
        I2SCFGL = (PCMSYNC << 7) + (STD_MODE << 4) + (CKPOL << 3) + (DATLEN << 1) + CHLEN;
        P_SW3 = (P_SW3 & 0x3f) | (1<<6);        //I2S端口切换, 0: P3.2(BCLK) P3.3(MCLK) P3.4(SD) P3.5(WS),        2024-7-21
                                                                                //             1: P1.7(BCLK) P1.6(MCLK) P1.5(SD) P1.4(WS),
                                                                                //             2: P2.3(BCLK) P2.2(MCLK) P2.1(SD) P2.0(WS),
                                                                                //             3: P4.3(BCLK) P1.6(MCLK) P4.1(SD) P4.0(WS),
        I2SCFGH |= I2SEN;                //使能I2S模块
}

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

u16 const step_table =
{
        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 I2S_ISR(void) interrupt I2C_VECTOR//I2S_VECTOR 62                //输出2个正弦波
{
        if (I2SSR & 0x02)                                //发送缓冲区空
        {
                if((I2SSR & 0x04) == 0)                //左声道
                {
                        I2SDRH = (u8)(dac_L /256);                //发送下一帧音频数据
                        I2SDRL = (u8)(dac_L %256);

                        if(++cnt_1ms >= (u8)(SampleRate/1000))
                        {
                                DisplayScan();        //1ms扫描显示一位
                                cnt_1ms = 0;
                                if(++cnt_20ms == 20)        cnt_20ms = 0, B_20ms = 1;                //20ms时隙
                        }
                }
                else        //右声道6~8us @40.96MHz
                {
                        I2SDRH = (u8)(dac_R /256);                //发送下一帧音频数据
                        I2SDRL = (u8)(dac_R %256);

                        if(B_PlayEn)        //正在播放
                        {
                                if(PlayByteCnt >= FileLength)        B_PlayEn = 0, B_stop = 1;        //播放完成了
                                if(MusicChannel == 1)        //ADPCM 单声道
                                {
                                        if((rd_index & MusicBlock) == 0)        //BLOCK开始, 一般是256或512或1024字节一个BLOAK
                                        {
                                                dac_L = (u16)voice_buff + ((u16)voice_buff << 8);        // 未压缩的采样值, 小端模式
                                                rd_index += 2;        //指向index
                                                index_L = voice_buff;        //index值
                                                rd_index   +=2;
                                                PlayByteCnt+= 4;        //已播放字节+4个字节
                                                B_high_nibble = 0;
                                                dac_L += 32768;                //转成无符号线性
                                                cur_sample_L= dac_L;
                                                cur_sample_L -= 32768;        //转回有符号
                                        }
                                        else        //不是BLOCK开始
                                        {
                                                decode = voice_buff;        //从缓冲读数据字节
                                                if(B_high_nibble)                //处理高半字节
                                                {
                                                        decode >>= 4;
                                                        PlayByteCnt++;        //已播放字节+1字节
                                                        rd_index++;                //指向下一个数据
                                                        rd_index &= VOICE_BUFF_MASK;        //溢出处理
                                                }
                                                decode &= 0x0f;
                                                delta = ((u32)step_table * ((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;
                                                       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        //ADPCM 双声道
                                {
                                        if((rd_index & MusicBlock) == 0)        //BLOCK开始, 512或1024字节一个BLOAK
                                        {
                                                dac_L = (u16)voice_buff + ((u16)voice_buff << 8);        // 未压缩的采样值
                                                rd_index += 2;        //指向index
                                                index_L = voice_buff;        //index值
                                                rd_index += 2;

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

                                                PlayByteCnt += 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        //不是BLOCK开始
                                        {
                                                if((rd_index & 0x0007) == 0)        //2个DWORD 8个字节
                                                {
                                                        ADPCM_Data_L = voice_buff;        //不用循坏,时间尽量短
                                                        ADPCM_Data_L = voice_buff;
                                                        ADPCM_Data_L = voice_buff;
                                                        ADPCM_Data_L = voice_buff;
                                                        ADPCM_Data_R = voice_buff;        //不用循坏,时间尽量短
                                                        ADPCM_Data_R = voice_buff;
                                                        ADPCM_Data_R = voice_buff;
                                                        ADPCM_Data_R = voice_buff;                //最后一个读索引不+1, 处理完这些数据才+1,避免写入缓冲误判
                                                        B_high_nibble = 0;
                                                        DecodeCnt   = 0;
                                                }

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

                                                delta = ((u32)step_table * ((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;
                                                       if (index_L < 0)        index_L = 0;
                                                else if (index_L > 88)        index_L = 88;
                                                //======================== 左声道解压缩完毕 ==================================

                                                //======================== 右声道解压缩 ============================
                                                decode = ADPCM_Data_R;        //从缓冲读数据字节
                                                if(B_high_nibble)                        //处理高半字节
                                                {
                                                        decode >>= 4;
                                                        DecodeCnt++;
                                                        if(DecodeCnt >= 4)        //4个字节都处理完成
                                                        {
                                                                PlayByteCnt += 8;        //已播放字节数+8个字节
                                                                rd_index++;                        //指向下一个数据
                                                                rd_index &= VOICE_BUFF_MASK;        //溢出处理
                                                        }
                                                }
                                                decode &= 0x0f;

                                                delta = ((u32)step_table * ((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;
                                                       if (index_R < 0)        index_R = 0;
                                                else if (index_R > 88)        index_R = 88;
                                                //======================== 右声道解压缩完毕 ==================================

                                                B_high_nibble = ~B_high_nibble;        //高半字节指示
                                        }
                                }
                                dac_L -= 32768;                //转成有符号
                                dac_R -= 32768;                //转成有符号
                        }
                        else
                        {
                                dac_L = 0;                //无播放静音
                                dac_R = 0;                //无播放静音
                        }
                }
        }
//        I2SSR &= ~0x5B;                //已自动清楚中断标志
}


页: [1]
查看完整版本: 81-I2S-播放下载在FLASH中的立体声ADPCM音乐-TLV320AIC23B,AI8051U实验箱演示程序