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]