Files

500 lines
14 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*================================================================
Copyright (c) 2022, Quectel Wireless Solutions Co., Ltd. All rights reserved.
Quectel Wireless Solutions Proprietary and Confidential.
=================================================================*/
#include <stdio.h>
#include "ql_rtos.h"
#include "ql_application.h"
#include "ql_audio.h"
#include "ql_tts.h"
#include "ql_iic.h"
#include "ql_gpio.h"
/***************参数定义***************/
#define STATEconfirm 0xFC //状态机确认 回读STATEconfirm的寄存值确认IC正常工作状态
#define NORMAL_I2S 0x00
#define NORMAL_LJ 0x01
#define NORMAL_DSPA 0x03
#define NORMAL_DSPB 0x23
#define Format_Len24 0x00
#define Format_Len20 0x01
#define Format_Len18 0x02
#define Format_Len16 0x03
#define Format_Len32 0x04
#define VDDA_3V3 0x00
#define VDDA_1V8 0x01
#define MCLK_PIN 0x00
#define SCLK_PIN 0x01
/***************参数定义***************/
/***************参数选择***************/
#define MSMode_MasterSelOn 0 //产品主从模式选择:默认选择0为SlaveMode,打开为1选择MasterMode
#define Ratio PCM_FS_64 //实际Ratio=MCLK/LRCK比率,需要和实际时钟比例匹配
#define Format NORMAL_DSPA //数据格式选择,需要和实际时序匹配
#define Format_Len Format_Len16 //数据长度选择,需要和实际时序匹配
#define SCLK_DIV 4 //SCLK分频选择:(选择范围1~20),SCLK=MCLK/SCLK_DIV,超过后非等比增加具体对应关系见相应DS说明
#define SCLK_INV 0 //默认对齐方式为下降沿,1为上升沿对齐,需要和实际时序匹配
#define MCLK_SOURCE MCLK_PIN //是否硬件没接MCLK需要用SCLK当作MCLK
#define ADCChannelSel 1 //单声道ADC输入通道选择是CH1(MIC1P/1N)还是CH2(MIC2P/2N),
#define DACChannelSel 0 //单声道DAC输出通道选择:默认选择0:L声道,1:R声道
#define VDDA_VOLTAGE VDDA_1V8 //模拟电压选择为3V3还是1V8,需要和实际硬件匹配
#define ADC_PGA_GAIN 8 //ADC模拟增益:(选择范围0~10),具体对应关系见相应DS说明
#define ADC_Volume 191 //ADC数字增益:(选择范围0~255),191:0DB,±0.5dB/Step
#define DAC_Volume 191 //DAC数字增益:(选择范围0~255),191:0DB,±0.5dB/Step
#define Dmic_Selon 0 //DMIC选择:默认选择关闭0,打开为1
#define ADC2DAC_Sel 0 //LOOP选择:内部ADC数据给到DAC自回环输出:默认选择关闭0,打开为1
#define DACHPModeOn 0 //输出负载开启HP驱动:默认选择关闭0,打开为1
#define ES8312_MONONOUT 0
#define ES8312_FM 0
/***************参数选择***************/
// #define ES8311_DEBUG_REGS
static int is_compatible_sc2601 = 0;//Is the ES8311 driver compatible with the SC2601
static int rt5616_init_reg[] = {
0x08080002, //0002 is the addr, 0808 is the value
0x08080003,
0x80000005,
0x04C0000D,
0x0808000F,
0xAFAF0019,
0x5C5C001C,
0x0000001E,
0x38200027,
0x80800029,
0x1250002A,
0x0000003B,
0x004F003C,
0x2000003D,
0x004F003E,
0x40000045,
0x0000004D,
0x0000004E,
0x0278004F,
0x00000050,
0x00000051,
0x02780052,
0xC8000053,
0x98060061,
0x88000062,
0xF8FE0063,
0x0A000064,
0xCC000065,
0x3F000066,
0x003D006A,
0x3600006C,
0x80820070,
0x00000073,
0x0C000074,
0x50000080,
0x0E1C0081,
0x08000082,
0x0019008E,
0x3100008F,
0x2C000093,
0x02000094,
0x208000B0,
0x000000B1,
0x001100FA,
};
static uint16 es8311_init_reg[] = {
0x0045,//45 is the addr, 0x00 is the value
0x3001,
0x1002,
0x0002,
0x1003,
0x2416,
0x2004,
0x0005,
0x2006,
0x0007,
0xff08,
0x0f09,
0x0f0a,
0x000b,
0x000c,
0x1310,
0x7f11,
0x3f01,
0x8000,
0x010d,
0x1014,
0x2812,
0x1013,
0x020e,
0x440f,
0x0015,
0x0a1b,
0x6a1c,
0x0837,
0x0044,
0xbf17,
0xbf32,
};
#define RT5616_INIT_REG_LEN sizeof(rt5616_init_reg)/sizeof(rt5616_init_reg[0])
#define ES8311_INIT_REG_LEN sizeof(es8311_init_reg)/sizeof(es8311_init_reg[0])//xu
#define CODEC_IIC_NO 0 //EC600M iic_no 1/EC800M iic_no 0
#define I2C_SLAVE_ADDR 0x1B //codec 5616
#define ES8311_REG_I2C_DEVICEID 0xFD
#define SC2601_REG_I2C_DEVICEID 0x56
#define ES8311_I2C_SLAVE_ADDR 0x18
static short ql_rt5616_read_reg(unsigned char RegAddr, unsigned short *p_value)
{
unsigned short status = 1;
unsigned char temp_buf[2];
unsigned char retry_count = 5;
do
{
status = ql_i2c_read(CODEC_IIC_NO, I2C_SLAVE_ADDR, RegAddr, temp_buf, 2);
if (status != 0)
{
printf("Error:rt5616 [%dth] device[0x%x] addr[0x%x] failed\n", retry_count, I2C_SLAVE_ADDR, RegAddr);
}
else
{
*p_value = (((unsigned short)temp_buf[0]) << 8) | temp_buf[1];
break;
}
} while (--retry_count);
return status;
}
static short ql_rt5616_write_reg(unsigned char RegAddr, unsigned short RegData)
{
unsigned short status = 1;
unsigned char param_data[3] = {0x00};
unsigned char retry_count = 5;
param_data[0] = (unsigned char)((RegData >> 8) & 0xFF);
param_data[1] = (unsigned char)(RegData & 0xff);
do
{
status = ql_i2c_write(CODEC_IIC_NO, I2C_SLAVE_ADDR, RegAddr, param_data, 2);
if (status != 0)
{
printf("Error:[%dth] device[0x%x] addr[0x%x] failed\n", retry_count, I2C_SLAVE_ADDR, RegAddr);
}
else
{
break;
}
} while (--retry_count);
return status;
}
static void ql_rt5616_init(void)
{
char i = 0;
for (i = 0; i < RT5616_INIT_REG_LEN; i++)
{
ql_rt5616_write_reg(rt5616_init_reg[i]&0xFFFF, rt5616_init_reg[i]>>16);
}
}
static void ql_rt5616_open(void) //初始化5616型号codec
{
unsigned short status = 0;
unsigned short val = 0;
ql_i2c_init(CODEC_IIC_NO, 0); //不同项目的使用的IIC_NO有区别,硬件上也不相同,请根据具体项目更改
status = ql_rt5616_read_reg(0xFE, &val);
if ((status == 0) && (0x10EC == val))
{
ql_rt5616_init();
printf("quec_rt5616_init!!!\n");
}
}
static void ql_rt5616_close(void) //给5616款codec下电
{
static unsigned int reg_5616_power[] = {0x00000061, 0x00000062, 0x00000063, 0x00000064, 0x00000065, 0x00000066};
char i = 0;
for (i = 0; i < sizeof(reg_5616_power)/sizeof(reg_5616_power[0]); i++)
{
ql_rt5616_write_reg(reg_5616_power[i]&0xFFFF, reg_5616_power[i]>>16);
}
}
static short ql_es8311_read_reg(unsigned char RegAddr, unsigned char *p_value)
{
unsigned short status = 1;
unsigned char temp_buf = 0;
unsigned char retry_count = 5;
do
{
status = ql_i2c_read(CODEC_IIC_NO, ES8311_I2C_SLAVE_ADDR, RegAddr, &temp_buf, 1);
if (status != 0)
{
printf("Error:es8311 [%dth] device[0x%x] addr[0x%x] failed\n", retry_count, ES8311_I2C_SLAVE_ADDR, RegAddr);
}
else
{
*p_value = temp_buf;
break;
}
} while (--retry_count);
return status;
}
static short ql_es8311_write_reg(unsigned char RegAddr, unsigned char RegData)
{
unsigned short status = 1;
unsigned char retry_count = 5;
unsigned char temp_buf = RegData;
do
{
status = ql_i2c_write(CODEC_IIC_NO, ES8311_I2C_SLAVE_ADDR, RegAddr, &temp_buf, 1);
if (status != 0)
{
printf("Error:[%dth] device[0x%x] addr[0x%x] failed\n", retry_count, ES8311_I2C_SLAVE_ADDR, RegAddr);
}
else
{
break;
}
} while (--retry_count);
return status;
}
static void delay_us(UINT32 us)
{
UINT32 i;
while(us--)
{
i=50;
while(i--);
}
}
static void ql_es8311_init(void)
{
ql_es8311_write_reg(0x45,0x00);
ql_es8311_write_reg(0x01,0x30);
ql_es8311_write_reg(0x02,0x10);
if(Ratio == PCM_FS_256)//Ratio=MCLK/LRCK=256:12M288-48K;4M096-16K; 2M048-8K
{
ql_es8311_write_reg(0x02,0x00);//MCLK DIV=1
ql_es8311_write_reg(0x03,0x10);
ql_es8311_write_reg(0x16,0x24);
ql_es8311_write_reg(0x04,0x20);
ql_es8311_write_reg(0x05,0x00);
ql_es8311_write_reg(0x06,(SCLK_INV<<5) + SCLK_DIV -1);
ql_es8311_write_reg(0x07,0x00);
ql_es8311_write_reg(0x08,0xFF);
}
if(Ratio == PCM_FS_128)// Ratio=MCLK/LRCK=128:6M144-48K;2M048-16K; 1M024-8K
{
ql_es8311_write_reg(0x02,0x08);
ql_es8311_write_reg(0x03,0x10);
ql_es8311_write_reg(0x16,0x24);
ql_es8311_write_reg(0x04,0x20);
ql_es8311_write_reg(0x05,0x00);
ql_es8311_write_reg(0x06,(SCLK_INV<<5) + SCLK_DIV -1);
ql_es8311_write_reg(0x07,0x00);
ql_es8311_write_reg(0x08,0x7F);
}
if(Ratio == PCM_FS_64)// Ratio=MCLK/LRCK=64:3M072-48K;1M024-16K; 512k-8K
{
/*当512K/8K时,DVDD必须接1V8,否则无法正常工作*/
ql_es8311_write_reg(0x02,0x18);
ql_es8311_write_reg(0x03,0x20);
ql_es8311_write_reg(0x16,0x20);
ql_es8311_write_reg(0x04,0x20);
ql_es8311_write_reg(0x05,0x00);
ql_es8311_write_reg(0x06,(SCLK_INV<<5) + SCLK_DIV -1);
ql_es8311_write_reg(0x07,0x00);
ql_es8311_write_reg(0x08,0x3F);
}
if(is_compatible_sc2601)
{
ql_es8311_write_reg(0x52, 0x01);//1.6新增,APLL TEST
ql_es8311_write_reg(0x05, 0x11);//1.6新增,APLL ADC/DAC DIV
ql_es8311_write_reg(0x4d,0x1c);//SPEED vref set
ql_es8311_write_reg(0x50, 0x01);//1.6新增,APLL ICP=1
ql_es8311_write_reg(0x7a,0x20);//NG
ql_es8311_write_reg(0x67,0x01);//DAC DEM EN
}
ql_es8311_write_reg(0x09,(DACChannelSel<<7) + Format + (Format_Len<<2));
ql_es8311_write_reg(0x0A,Format + (Format_Len<<2));
ql_es8311_write_reg(0x0B,0x00);
ql_es8311_write_reg(0x0C,0x00);
ql_es8311_write_reg(0x10,(0x1C*DACHPModeOn) + (0x60*VDDA_VOLTAGE) + 0x03);
ql_es8311_write_reg(0x11,0x7F);
ql_es8311_write_reg(0x00,0x80 + (MSMode_MasterSelOn<<6));//Slave Mode
ql_es8311_write_reg(0x0D,0x01);
ql_es8311_write_reg(0x01,0x3F + (MCLK_SOURCE<<7));//做主情况下BCLK只能输出,不能选择引MCLK
ql_es8311_write_reg(0x14,(Dmic_Selon<<6) + (ADCChannelSel<<4) + ADC_PGA_GAIN);//选择CH1输入+30DB GAIN
ql_es8311_write_reg(0x12,0x28);
ql_es8311_write_reg(0x13,0x00 + (DACHPModeOn<<4));
/*****************************************/
/*****************************************/
ql_es8311_write_reg(0x0E,0x02);
ql_es8311_write_reg(0x0F,0x44);
ql_es8311_write_reg(0x15,0x00);
ql_es8311_write_reg(0x1B,0x0A);
ql_es8311_write_reg(0x1C,0x6A);
ql_es8311_write_reg(0x37,0x08);
ql_es8311_write_reg(0x44,(ADC2DAC_Sel <<7));
ql_es8311_write_reg(0x17,ADC_Volume);
ql_es8311_write_reg(0x32,DAC_Volume);
//sc2601 require additional configuration
if(is_compatible_sc2601)
{
uint8_t dac_rst,dac_iface = 0;
ql_es8311_read_reg(0x09,&dac_iface);
ql_es8311_write_reg(0x31,0x20);//MUTE ON
dac_iface |= 0x40;
ql_es8311_write_reg(0x09,dac_iface);
ql_es8311_write_reg(0x6e, 0x14);//启用16bit位宽,若是24bit请把这0x16注释
ql_es8311_write_reg(0x71,0x00);
delay_us(15000);;///若LRCK为16K用5ms,若为8K则此delay加倍
ql_es8311_read_reg(0x00,&dac_rst);
dac_rst |= 0x01;
ql_es8311_write_reg(0x00,dac_rst);//rst = 1
dac_rst &= 0xfe;
ql_es8311_write_reg(0x00,dac_rst);//rst = 0
delay_us(200);
dac_iface &= 0xbf;
ql_es8311_write_reg(0x09,dac_iface);
ql_es8311_write_reg(0x71,0x80);
ql_es8311_write_reg(0x31,0x00);//mute off
}
#ifdef ES8311_DEBUG_REGS
//debug,print the values of all registers
uint8_t reg = 0;
for (int i = 0; i < 0xFF; i++) {
ql_es8311_read_reg(i,&reg);
printf("0x%x,REG:0x%x\n", i,reg);
}
#endif
}
static void ql_es8311_open(void) //初始化8311型号codec
{
unsigned short status = 0;
unsigned char val = 0;
ql_i2c_init(CODEC_IIC_NO, 0); //不同项目的使用的IIC_NO有区别,硬件上也不相同,请根据具体项目更改
status = ql_es8311_read_reg(ES8311_REG_I2C_DEVICEID, &val);
if ((status == 0) && (0x83 == val))
{
ql_es8311_init();
printf("quec_es8311_init!!!\n");
}
else
{
//Not ES8311, try SC2601
status = ql_es8311_read_reg(SC2601_REG_I2C_DEVICEID, &val);
if ((status == 0) && (0x01 == val))
{
is_compatible_sc2601 = 1;
ql_es8311_init();
printf("quec_sc2601_init!!!\n");
}
}
}
static ql_cb_on_speakerpa play_audio_pa_cb(unsigned int on){
if(on){
printf("PA on\r\n");
ql_gpio_init(GPIO_PIN_NO_86, PIN_DIRECTION_OUT, PIN_PULL_PU, PIN_LEVEL_HIGH);
}else{
printf("PA off\r\n");
ql_gpio_init(GPIO_PIN_NO_86, PIN_DIRECTION_OUT, PIN_PULL_PD, PIN_LEVEL_LOW);
}
}
static char *tmp_buf = NULL;
static audio_track_handle track_hd;
static audio_record_handle record_hd;
static void ql_audio_track_callback(acm_audio_track_handle handle, acm_audio_track_event_t event)
{
//printf("audio_track_callback event:%d\r\n", event);
}
static void ql_audio_record_callback(acm_audio_track_handle handle, acm_audio_record_event_t event)
{
//printf("audio_record_callback event:%d\r\n", event);
}
static void quec_codec_test(void * argv)
{
unsigned int record_len;
unsigned int rate = 8000;
unsigned int channels = 1;
int read_ret = -1, write_ret = -1;
while(!ql_get_audio_state()){
ql_rtos_task_sleep_ms(50);
}
printf("quec_codec_test start ...\r\n");
int i = 0;
ql_codecpcm_config pcm_config[] = {Ratio,PCM_CODEC_SAMPLE_8000,0};
ql_set_volume(2);
ql_bind_speakerpa_cb(play_audio_pa_cb);
ql_codec_choose(AUD_EXTERNAL_CODEC, &pcm_config); //模块内部配置后向外置codec输出pcm数据
ql_es8311_open(); //初始化外置codec
record_len = 320;
tmp_buf = malloc(record_len);
printf("choose external codec play audio ...\r\n");
record_hd = ql_audio_record_open(rate, channels, 0x00, ql_audio_record_callback);
track_hd = ql_audio_track_open(rate, channels, 0x00, ql_audio_track_callback);
while (1)
{
//printf("start record \n");
read_ret = ql_audio_record_read(record_hd, tmp_buf, &record_len);
printf("ql_pcm_read, pcm read:%d %d\r\n", read_ret,record_len);
//ql_audio_record_close(record_hd, 0);
//printf("start play\n");
if(read_ret==0){
write_ret = ql_audio_track_write(track_hd, tmp_buf, record_len);
}
ql_rtos_task_sleep_ms(20);
}
}
// application_init(quec_codec_test, "quec_codec_test", 3, 0);