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]