国学芯用 发表于 2026-7-2 13:15:09

82-I2S接口-数字录放音-存储在FLASH中-TLV320AIC23B,AI8051U实验箱演示程序

82-I2S接口-数字录放音-存储在FLASH中-TLV320AIC23B,AI8051U实验箱演示程序

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

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

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



#include "../comm/AI8051U.h"
#include        "intrins.h"
#include        "TLV320AIC23.h"
#include        "W25Q128FV.h"
#include "Uart1.h"
/*************        功能说明        **************
本程序使用AI8051U实验箱V1.2验证。用户先别修改程序,直接下载HEX文件到AI8051U实验箱V1.2来验证,下载时选择主频36.864MHz。

使用AI8051U系列MCU做数字录音测试,语音存储于FLASH中. 话筒放大电路低通滤波转折频率为3400Hz左右,合适8~16K采样。
为了能少用点空间,语音使用A率压缩/解压缩,每秒字节数等于采样率。
8KHz采样,码率为 8KB/S,16MB FLASH可以录音34分钟。
16KHz采样,码率为16KB/S,16MB FLASH可以录音17分钟。

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



//对于I2S的时钟必须是 256*SampleRate的整数倍。对于立体声16位,左右声道时钟WS=SampleRate,数据时钟BCLK=32*SampleRate,主时钟MCLK=8*BCLK=256*SampleRate。

       
//        #define SampleRate                48000                        //定义采样率
//        #define SampleRate                36000                        //定义采样率
//        #define SampleRate                24000                        //定义采样率
//        #define SampleRate                16000                        //定义采样率
//        #define SampleRate                12000                        //定义采样率
       
//        #define SampleRate                6000                        //定义采样率

//        #define FOSC                        40960000UL                //定义主时钟
//        #define FOSC                        32768000UL                //定义主时钟
//        #define SampleRate                32000                        //定义采样率
//        #define SampleRate                16000                        //定义采样率
//        #define SampleRate               8000                        //定义采样率

//        #define FOSC                        33868800UL                //定义主时钟
//        #define SampleRate                44100                        //定义采样率
//        #define SampleRate                22050                        //定义采样率
//        #define SampleRate                11025                        //定义采样率

#defineCHANNEL2                          //设置ADC通道(咪头通道),   取值为0~7, 对应P1.0~P1.7, 使用别的ADC输入口则要修改ADC初始化函数.

#define        VOICE_BUFF_LENGTH        16384                //必须是4096、8192、16384这3个数之一
#define        VOICE_BUFF_MASK                (VOICE_BUFF_LENGTH-1)
#define        FLASH_CAP                        (16380*1024)        // FLASH容量

#define SampleRate                8000UL                        //定义采样率
//36864000/8000 == 4608
//(16*2) * (2*72+0) *8 ==36864
//I2S_MCLKDIV 18 I2S_BCLKDIV 72
//
#define I2S_MCLKDIV                (FOSC/(8*16*2*SampleRate))        //MCLK分频系数, 对于双声道16bit. 18
#define I2S_BCLKDIV                (FOSC/(16*2*SampleRate))                //BCLK分频系数, 对于双声道16bit. 144

/*************        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

/*************        变量定义        **************/
u32        FileLength;                //文件长度(字节)
u32        PlayByteCnt;        //播放字节计数
u16        dac;                        //解码后输出的左后声道DAC值
bit        B_PlayEn;                //允许播放
bit        B_record;                //允许录音
bit B_stop;                        //1: 停止录音或放音

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

u16        second;
u16        cnt_1s;

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;                //行列键盘变量
u32 isrCnt = 0;

//===================================================
#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                CheckString(u8 *px, u8 const *pc, u8 num);
void         CDC_StringPrint(u8 *puts);
void        PlayProcess(void);
void        ADC_config(void);


u8 const F_ALAW[]="ALAW";                        //文件头 00H~03H


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

        EAXSFR();        //SFR enable
        WTST= 0;
        CKCON = 0;
       
        RSTFLAG |= 0x04;   //软复位标志写1清零,设置硬件复位后需要检测P3.2的状态选择运行区域,硬件复位后进入系统ISP检测到P3.2为低电平,才会进入USB下载模式
       
        P0M0 = 0x00;
        P0M1 = 0x00;
        P1M0 = 0x00;
        P1M1 = 0x00;
        P2M0 = 0x00;
        P2M1 = 0x00;
        P3M0 = 0x00;
        P3M1 = 0x00;
        P4M0 = 0x00;
        P4M1 = 0x00;
        P5M0 = 0x00;
        P5M1 = 0x00;
       
        UART1_config(2);
        //usb_init();
// IE2 |= EUSB;       //IE2相关的中断位操作使能后,需要重新设置EUSB
        EA = 1;

        //delay_ms(2000);
        printf("start");
        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
       
        ADC_config();
        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;

        OP_index = 0;

        B_FlashOK = 0;
        FlashCheckID();
        FlashCheckID();
        B_FlashOK = 0;
        if((FLASH_ID >= 0x12) && (FLASH_ID <= 0x19))        B_FlashOK = 1;        //检测到FLASH
        if(!B_FlashOK){
                printf("!B_FlashOK");
                while(1);
        }
        printf("I2S_config\n");
        EA = 0;
        I2S_config();
        EA = 1;
        printf("CLKSEL:%bu,CLKDIV:%bu,\n",(u8)CLKSEL,(u8)CLKDIV);
        printf("I2S_MCLKDIV:%bu,I2S_BCLKDIV:%bu,\n",(u8)I2S_MCLKDIV,(u8)I2S_BCLKDIV);
        printf("I2SMCKDIV:%bu,I2SPRL:%bu,\n",(u8)I2SMCKDIV,(u8)I2SPRL);
        //while(1);
        //printf("while");
        while(1)
        {
                if(OP_index != 0)        PlayProcess();

                if(B_20ms)        //20ms时隙
                {
                        //printf("isrCnt:%lu\n",isrCnt);
                        isrCnt = 0;
                        B_20ms = 0;

                        if(B_PlayEn || B_record)
                        {
                                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_SPI_DMA_busy)        //停止录音或放音
                        {
                                B_stop = 0;
                                OP_index = 0;
                                LED8 = DIS_S;        //显示stop
                                LED8 = DIS_T;
                                LED8 = DIS_O;
                                LED8 = DIS_P;
                                LED8 = DIS_BLACK;

                                if(B_record)        //录音结束, 保存ALAW标识和数据长度
                                {
                                        B_record = 0;
                                        for(i=0; i<4; i++)        voice_buff = F_ALAW;        //A LAW标识
                                        voice_buff = (u8)(FlashAddr >> 24);        //录音FLASH长度,大端模式
                                        voice_buff = (u8)(FlashAddr >> 16);
                                        voice_buff = (u8)(FlashAddr >> 8);
                                        voice_buff = (u8)FlashAddr;
                                        FlashWrite_Nbytes(0, voice_buff, 8);        //(u32 addr, u8 *buffer, u16 size)        写入数据
                                }
                        }

                        IO_KeyScan();

                       
                        if(KeyCode != 0)        //有键按下
                        {
                                printf("KeyCode:%bu\n",KeyCode);
                                if(KeyCode == K1)        //停止播放或录音
                                {
                                        printf("stop\n");
                                        OP_index = 0;        //操作索引, 0:无操作, 1:读取头文件处理并启动播放, 2: 读取数据, 3:启动录音,4:保存录音数据
                                        B_PlayEn = 0;        //停止播放
                                        B_stop   = 1;
                                }

                                else if(KeyCode == K2)        //录音
                                {
                                        printf("record\n");
                                        if(B_record){
                                                printf("record1\n");
                                                OP_index = 0, B_stop = 1;        //正在录音则停止录音
                                        }
                                        else if(B_FlashOK)        //FLASH存在, 则启动录音
                                        {
                                                printf("record2\n");
                                                if(!B_PlayEn)        //未放音才可以录音
                                                {
                                                        printf("record3\n");
                                                        OP_index = 3;        //操作索引, 0:无操作, 1:读取头文件处理并启动播放, 2: 读取数据, 3:启动录音,4:保存录音数据
                                                        cnt_1s = 0;
                                                        second = 0;
                                                        LED8 = DIS_R;        //显示R
                                                        LED8 = 0;
                                                        LED8 = 0+DIS_DOT;
                                                        LED8 = 0;
                                                        LED8 = 0;                //录音时间
                                                }
                                        }
                                }

                                else if(KeyCode == K3)        //放音
                                {
                                        printf("play\n");
                                        if(B_PlayEn){
                                                printf("play1\n");
                                                B_PlayEn = 0, B_stop = 1;        //正在播放停止播放
                                        }
                                        else if(B_FlashOK)        //FLASH存在, 则启动放音
                                        {
                                                printf("play2\n");
                                                if(!B_record)        //未录音则可以播放
                                                {
                                                        printf("play3\n");
                                                        OP_index = 1;        //操作索引, 0:无操作, 1:读取头文件处理并启动播放, 2: 读取数据, 3:启动录音,4:保存录音数据
                                                        cnt_1s = 0;
                                                        second = 0;
                                                        LED8 = DIS_P;        //显示P
                                                        LED8 = 0;
                                                        LED8 = 0+DIS_DOT;
                                                        LED8 = 0;
                                                        LED8 = 0;                //播放时间
                                                }
                                        }
                                }

                                else if(KeyCode == K6)        //音量+
                                {
                                        printf("+\n");
                                        if(++HeadPhoneVol > 80)        HeadPhoneVol = 80;        //最大音量
                                        SetHeadPhoneVolume(HeadPhoneVol);        //设置音量
                                        LED8 = HeadPhoneVol / 10;        //显示音量
                                        LED8 = HeadPhoneVol % 10;
                                }
                                else if(KeyCode == K7)        //音量-
                                {
                                        printf("-\n");
                                        if(HeadPhoneVol != 0)        HeadPhoneVol--;        //最小音量
                                        SetHeadPhoneVolume(HeadPhoneVol);        //设置音量
                                        LED8 = HeadPhoneVol / 10;        //显示音量
                                        LED8 = HeadPhoneVol % 10;
                                }

                                KeyCode = 0;
                        }
               
                }
        }
}


// 校验一个字符串,正确返回0,错误返回非0
u8        CheckString(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:启动录音,4:保存录音数据
        {
                FlashRead_Nbytes(0, voice_buff, 1024);//(u32 addr, u8 *buffer, u16 size)        读取文件
                if(CheckString(voice_buff, F_ALAW, 4) == 0)                        //检测ALAW                文件头 00H~03H
                {
                        B_PlayEn = 0;        //停止播放
                        B_record = 0;        //停止录音
                        FileLength = ((u32)voice_buff << 24) + ((u32)voice_buff << 16) + (u32)voice_buff*256 + voice_buff;        //数据字节长度, 大端模式,
                        if(FileLength < FLASH_CAP)        //小于falsh容量
                        {
                                FlashAddr = 1024;
                                wr_index= 1024;        //写缓冲索引
                                rd_index= 16;                //读缓冲索引, 从16开始存放语音数据
                                PlayByteCnt = 16;        //播放字节计数
                                dac = 0;
                                B_PlayEn = 1;        //启动播放
                                printf("start play\n");
                                OP_index = 2;
                        }
                }
                else{
                        printf("PlayProcess nodata\n");
                       OP_index = 0, B_stop = 1;        //无语音数据
                }
        }

        else if(OP_index == 2)                //操作索引, 0:无操作, 1:读取头文件处理并启动播放, 2: 读取数据, 3:启动录音,4:保存录音数据
        {
                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:启动录音,4:保存录音数据
        {
                B_PlayEn = 0;        //停止播放
                B_record = 0;        //停止录音
                FlashAddr= 0;
                wr_index = 16;        //写缓冲索引, 语音数据从16开始
                rd_index = 0;                //读缓冲索引, 从16开始存放语音数据
                for(j=0; j<16; j++)        voice_buff = 0xff;        //预留16字节
                B_record = 1;        //开始录音
                OP_index = 4;
        }
        else if(OP_index == 4)                //操作索引, 0:无操作, 1:读取头文件处理并启动播放, 2: 读取数据, 3:启动录音,4:保存录音数据
        {
                if(!B_SPI_DMA_busy)        //SPI DMA空闲
                {
                        j = (wr_index - rd_index) & VOICE_BUFF_MASK;        //已有数据字节数
                        if(j > 256)        //空出了超过256字节
                        {
                                //printf("j:%u,wr_index:%u,rd_index:%u\n",j,wr_index,rd_index);
                                if((FlashAddr & 0x00ffff) == 0)
                                {
                                        FlashSectorErase(FlashAddr, 64);        //(u32 addr, u8 sec)        擦除一个扇区64K
                                        while(FlashCheckBusy() != 0);                //Flash忙检测
                                }
                                //FlashWrite_Nbytes(FlashAddr, voice_buff+rd_index, 256);        //(u32 addr, u8 *buffer, u16 size)        写入数据
                                SPI_DMA_TxTRIG(FlashAddr, voice_buff+rd_index, 256);//(u32 addr, u8 *buffer, u16 size);        SPI DMA写入数据
                                rd_index+= 256;        //读缓冲索引+256
                                rd_index&= VOICE_BUFF_MASK;        //溢出处理
                                FlashAddr += 256;        //写FLASH的地址+256
                                if(FlashAddr >= FLASH_CAP){
                                        OP_index = 0, B_record = 1, B_stop = 1;        //录音完成了
                                        printf("OP_index4 record finish\n");
                                }
                               
                        }
                }
        }
}


/*****************************************************
        行列键扫描程序
        使用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
}

/********************** ADC初始化函数 ************************/
#define D_ADC_POWER        (1<<7)        /* ADC电源,1开启,0关闭 */
#define D_ADC_START        (1<<6)        /* 启动转换,自动清0 */
#define D_ADC_FLAG        (1<<5)        /* 完成标志,软件清0 */
#define D_ADC_EPWMT        (1<<4)        /* 允许PWMA触发ADC */

#define        D_ADC_SPEED        5                /* 0~15, ADC时钟 = SYSclk/2/(n+1) */
#define        D_RES_FMT        (1<<5)        /* ADC结果格式 0: 左对齐, ADC_RES: D9 D8 D7 D6 D5 D4 D3 D2, ADC_RESL: D1 D0 000000 */
                                                        /*             1: 右对齐, ADC_RES: 000000D9 D8, ADC_RESL: D7 D6 D5 D4 D3 D2 D1 D0 */
#define CSSETUP                (1<<7)        /* 0~1,ADC通道选择时间      0: 1个ADC时钟, 1: 2个ADC时钟,默认0(默认1个ADC时钟)        */
#define CSHOLD                (1<<5)        /* 0~3,ADC通道选择保持时间(n+1)个ADC时钟, 默认1(默认2个ADC时钟)                                        */
#define SMPDUTY                10                /* 10~31, ADC模拟信号采样时间(n+1)个ADC时钟, 默认10(默认11个ADC时钟)                                */
                                                        /* ADC转换时间: 10位ADC固定为10个ADC时钟, 12位ADC固定为12个ADC时钟.                                 */
void        ADC_config(void)
{
//        EAXSFR();        //SFR enable
        P1n_pure_input(1<<CHANNEL);        //设置要做ADC的IO做高阻输入
        ADC_CONTR = D_ADC_POWER + CHANNEL;        //ADC on + channel
        ADCCFG = D_RES_FMT + D_ADC_SPEED;
        ADCTIM = CSSETUP + CSHOLD + SMPDUTY;
//          ADC_CONTR |= ADC_START;        //启动ADC转换,完成后自动清零
//          ADC_CONTR &= ~ADC_FLAG;        //清除ADC完成(中断)标志
//          ADC_CONTR |= ADC_EPWMT;        //允许PWM触发ADC
//        EADC= 1;        //允许ADC中断
//        IPH |= PADCH;        //ADC 中断优先级高位
//        PADC= 1;        //ADC 中断优先级
}


/*********** A率压缩/解压缩算法 ******************************

压缩: 根据国际标准,A率是以12位ADC,A=87.6为基准的,13折线法逼近。
解压缩: 本程序是I2S声音输出,是16位的DAC,所以计算时,所有数据都*16。

        law_data    b7    b6 b5 b4    b3 b2 b1 b0
                   minusExponent      Mantissa
***************************************************************/

const u8        T_Alaw_encode={
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,
48,48,48,48,49,49,49,49,50,50,50,50,51,51,51,51,52,52,52,52,53,53,53,53,54,54,54,54,55,55,55,55,
56,56,56,56,57,57,57,57,58,58,58,58,59,59,59,59,60,60,60,60,61,61,61,61,62,62,62,62,63,63,63,63,
64,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65,66,66,66,66,66,66,66,66,67,67,67,67,67,67,67,67,
68,68,68,68,68,68,68,68,69,69,69,69,69,69,69,69,70,70,70,70,70,70,70,70,71,71,71,71,71,71,71,71,
72,72,72,72,72,72,72,72,73,73,73,73,73,73,73,73,74,74,74,74,74,74,74,74,75,75,75,75,75,75,75,75,
76,76,76,76,76,76,76,76,77,77,77,77,77,77,77,77,78,78,78,78,78,78,78,78,79,79,79,79,79,79,79,79};

const        u8        T_Alaw_encodeH[]={                /* 113个数据, 避免溢出 */
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 96, 97, 97, 98, 98, 99, 99,100,100,101,101,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,
112,112,112,112,113,113,113,113,114,114,114,114,115,115,115,115,116,116,116,116,117,117,117,117,118,118,118,118,119,119,119,119,
120,120,120,120,121,121,121,121,122,122,122,122,123,123,123,123,124,124,124,124,125,125,125,125,126,126,126,126,127,127,127,127,127};


const        u16        T_Alaw_decode={
0x0000,0x0010,0x0020,0x0030,0x0040,0x0050,0x0060,0x0070,0x0080,0x0090,0x00A0,0x00B0,0x00C0,0x00D0,0x00E0,0x00F0,
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,
0x0800,0x0880,0x0900,0x0980,0x0A00,0x0A80,0x0B00,0x0B80,0x0C00,0x0C80,0x0D00,0x0D80,0x0E00,0x0E80,0x0F00,0x0F80,
0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700,0x1800,0x1900,0x1A00,0x1B00,0x1C00,0x1D00,0x1E00,0x1F00,
0x2000,0x2200,0x2400,0x2600,0x2800,0x2A00,0x2C00,0x2E00,0x3000,0x3200,0x3400,0x3600,0x3800,0x3A00,0x3C00,0x3E00,
0x4000,0x4400,0x4800,0x4C00,0x5000,0x5400,0x5800,0x5C00,0x6000,0x6400,0x6800,0x6C00,0x7000,0x7400,0x7800,0x7C00};

/**************** A率压缩 *******************
将12bit ADC线性二进制值数据压缩成8bit A-law数据并存入缓冲
        Convert 12 Bit Liner Data to 8bit ALAW data
**************************************************/
void Alaw_encode(u16 adc)        //输入12bit无符号的ADC值
{
        u8        law_data;
        u8        minus;

        if(adc & 0x0800)        minus = 0x00,        adc = adc - 2048;        // >= 2048为正, 0~2047, 以中间值2048为0点.
        else                                minus = 0x80,        adc = 2048 - adc;        //< 2048为负. 1~2048

        if((adc & 0xff00) == 0)        law_data = T_Alaw_encode[(u8)adc];        //0~255 --> 0~79
        else                                        law_data = T_Alaw_encodeH[(u8)((adc - 256)/16)];        //256~2047 --> 80~127

        voice_buff = (law_data + minus) ^ 0x55;        //数据写入缓冲,偶数位取反        循环队列,指向下一个数据
        wr_index++;
        wr_index &= VOICE_BUFF_MASK;        //溢出处理
}

//**************** A率解压缩 *******************
//将8bit A-law数据解压缩成16bit数据并输出到DAC
//        Convert 8bit Alaw data to 16 Bit Liner Data
//**************************************************
void Alaw_decode(void)        //解压缩后得到16位有符号DAC
{
        u8        law_data;
        law_data = voice_buff ^ 0x55;                //Get Complement 偶数位取反, 从缓冲读数据字节,偶数位取反,循环队列,指向下一个数据
        dac = T_Alaw_decode;        // 解压缩码
        if(law_data & 0x80)        dac = 0-dac;                //如果是负的, 转成有符号
        rd_index++;
        rd_index &= VOICE_BUFF_MASK;        //溢出处理
}


//====================== 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位

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模块

}


//====================== I2S中断函数 ==================================================
void I2S_ISR(void) interrupt INT4_VECTOR//             16      //0083H//I2S_VECTOR 62
{
        u16        j;
        isrCnt++;
        if (I2SSR & 0x02)                                //发送缓冲区空
        {
                I2SDRH = (u8)(dac /256);        //更新音频数据, 单声道不分左右
                I2SDRL = (u8)(dac %256);
               
                if((I2SSR & 0x04) == 0)                //左声道,解码ADPCM,启动ADC
                {
                        if(B_record)        //正在录音, 则启动ADC
                        {
                                ADC_RES = 0;        ADC_RESL = 0;
                                  ADC_CONTR |= ADC_START;        //启动ADC转换,完成后自动清零
                        }
                        if(++cnt_1ms >= (u8)(SampleRate/1000))
                        {
                                //printf("DisplayScan dac:%u I2SDRH %bu I2SDRL %bu\n",dac,I2SDRH,I2SDRL);
                                DisplayScan();        //1ms扫描显示一位
                                cnt_1ms = 0;
                                if(++cnt_20ms == 20)        cnt_20ms = 0, B_20ms = 1;                //20ms时隙
                        }
                }
                else        //右声道
                {
                        if(B_PlayEn)        //正在播放
                        {
                                //printf("B_PlayEn dac:%u I2SDRH %bu I2SDRL %bu\n",dac,I2SDRH,I2SDRL);
                                Alaw_decode();        //解压缩后得到16位有符号DAC
                                if(++PlayByteCnt >= FileLength){
                                        //printf("I2S_ISR playEnd\n");
                                        B_PlayEn = 0, B_stop = 1;        //播放完成了
                                }
                        }
                        else        dac = 0;
                        if(B_record)        //正在录音, 读取ADC,A-LAW压缩
                        {
                                j = (u16)ADC_RES * 256 + (u16)ADC_RESL;
                                ADC_CONTR &= ~ADC_FLAG;        //清除ADC完成(中断)标志
                                Alaw_encode(j);        //输入12bit无符号的ADC值
                        }
                }
        }
//        I2SSR &= ~0x5B;                //已自动清除中断标志
}


页: [1]
查看完整版本: 82-I2S接口-数字录放音-存储在FLASH中-TLV320AIC23B,AI8051U实验箱演示程序