大家好,又见面了,我是你们的朋友全栈君。
1.硬件
代码语言:javascript复制Hi3516支持内置AudioCodec/外置音频解码芯片; 由于这里硬件上外接FM1288,故走的是外置,通过I2S接入, 通过MIC单端/差分输入音频.
由于之前一直调试内置Codec,这里任然保留内置相关代码;
2.重要概念
2.1. I2S相关概念
代码语言:javascript复制(1)LRCLK (帧时钟,也称为WS)
当LRCLK为低电平时表示传输左声道,高电平时表示传输右声道,
LRCLK的频率 = 采样频率
(2)SCLK(串行时钟) ,也叫BCLK(位时钟);方波形式存在
对应数字音频的每一位数据,SCLK都有一个脉冲.
SCLK的频率 = 2 * 采样频率 * 采样位数;
(3) SDATA 串行数据,就是二进制补码表示的音频数据.
(4) MCLK(主时钟,也叫系统时钟)
一般采样频率的256倍或384倍,它并不是必须的,可有可无,具体看要求.
(5)
I2S是一种比较简单的数字接口协议,没有地址或设备选择机制;
在I2S总线上,只能同时存在一个主设备和发送设备;
主设备可以是发送设备,也可以是接收设备,或是协调发送设备和接收设备的其他控制设备;
2.2 关键点
代码语言:javascript复制(1) 底层时序不需要我们自己实现,如i2s如何控制FM1288等.
(2) i2c用的连在主芯片(hi3516dv300)上,就可以直接用底层的驱动.
(3) FM1288的i2s,应该已经封装到mpp里面,只需通过i2c配置好codec,然后通过mpp库操作ai/ao接口即可.
(4) 通过i2c配置下寄存器,一般codec都是配置i2s的模式(主/从,采样率等参数),i2s的bclk,ws等;
(5) 主时钟(MCLK)要看看是有海思做主(master)还是由codec()做主 (slave);
(6) 一般都是由主设备提供MCLK,帧时钟和位时钟都是通过这个MLCK计算出来的.
3.音频上行(ai->aenc)
代码语言:javascript复制软件设计上参考: 对应SDK/mpp/sample/audio/sample_audio.c
先配置i2c.通过引脚图及hdmi命令配置:
如:system("himm 0x114F004C 0x103");
据手册: 在 AI/AO 设备主模式下,建议用户先配置好AI/AO,在配置codec;
3.1. 输入初始化
代码语言:javascript复制 HI_S32 rc ,i;
AIO_ATTR_S stAioAttr;
AUDIO_DEV AiDev = 0;
stAioAttr.enSamplerate = AUDIO_SAMPLE_RATE_8000;
stAioAttr.enBitwidth = AUDIO_BIT_WIDTH_16;
stAioAttr.enWorkmode = AIO_MODE_I2S_MASTER;
stAioAttr.enSoundmode = AUDIO_SOUND_MODE_MONO;
stAioAttr.u32EXFlag = 1;
stAioAttr.u32FrmNum = 30;
stAioAttr.u32PtNumPerFrm = 320;
stAioAttr.u32ChnCnt = 2; //最大只能支持2个
stAioAttr.u32ClkSel = 1;
//stAioAttr.enI2sType = AIO_I2STYPE_INNERCODEC; 选择内置Codec解码
stAioAttr.enI2sType = AIO_I2STYPE_EXTERN;//若外部单独有解码芯片,需要选择外部,另需要确认是否支持;
rc = HI_MPI_AI_SetPubAttr(AiDev, &stAioAttr);
//rc错误处理,以下均省略...
rc = HI_MPI_AI_Enable(AiDev);
AI_CHN_PARAM_S pstChnParam;
for(int j = 0; j < stAioAttr.u32ChnCnt; j )
{
rc = HI_MPI_AI_GetChnParam(AiDev, j, &pstChnParam);
pstChnParam.u32UsrFrmDepth = 30;
rc = HI_MPI_AI_SetChnParam(AiDev, j,&pstChnParam);
rc = HI_MPI_AI_EnableChn(AiDev, j);
}
#if 1 //这部分据实际情况慢慢调
//3516dv300输入不支持 HI_MPI_AI_SetVqeAttr,可用HI_MPI_AI_SetTalkVqeAttr
//声音质量增强功能(Talk)相关属性
AUDIO_DEV AiDevId = 0;
AI_CHN AiChn = 0;
AUDIO_DEV AoDevId = 0;
AO_CHN AoChn = 0;
AI_TALKVQE_CONFIG_S pstVqeConfig;
memset(&pstVqeConfig, 0, sizeof(AI_TALKVQE_CONFIG_S));
pstVqeConfig.u32OpenMask = AI_TALKVQE_MASK_HPF | AI_TALKVQE_MASK_ANR ;//| AI_TALKVQE_MASK_AGC ;
pstVqeConfig.s32WorkSampleRate = AUDIO_SAMPLE_RATE_8000;
pstVqeConfig.s32FrameSample = 320;
pstVqeConfig.enWorkstate = VQE_WORKSTATE_MUSIC ;
//去低频,表现为轰轰不舒适声音
pstVqeConfig.stHpfCfg.bUsrMode = HI_TRUE;
pstVqeConfig.stHpfCfg.enHpfFreq = AUDIO_HPF_FREQ_150;//建议120或150
//回声抵消
pstVqeConfig.stAecCfg.bUsrMode = HI_FALSE;
// pstVqeConfig.stAecCfg.s8CngMode = 1;//舒适噪音模式
// pstVqeConfig.stAecCfg.s8NearAllPassEnergy = 1;//判断是否无光泽传输的远端能量阈值:
// pstVqeConfig.stAecCfg.s8NearCleanSupEnergy = 2;//近端信号强制复位的能量门限:
// pstVqeConfig.stAecCfg.s16DTHnlSortQTh = 16384;
// pstVqeConfig.stAecCfg.s16EchoBandLow = 10;//语音处理band1,低频参数,
// pstVqeConfig.stAecCfg.s16EchoBandHigh = 25;//41;//语音处理band1,高频参数,
// pstVqeConfig.stAecCfg.s16EchoBandLow2 = 28;//47;
// pstVqeConfig.stAecCfg.s16EchoBandHigh2 = 35;//63;
// HI_S16 s16ERLBand[6] = {4, 6, 36, 49, 50, 51};
// HI_S16 s16ERL[7] = {7, 10, 16, 10, 18, 18, 18};
// memcpy(pstVqeConfig.stAecCfg.s16ERLBand,s16ERLBand,sizeof(s16ERLBand));
// memcpy(pstVqeConfig.stAecCfg.s16ERL,s16ERL,sizeof(s16ERL));
// pstVqeConfig.stAecCfg.s16VioceProtectFreqL = 3;
// pstVqeConfig.stAecCfg.s16VioceProtectFreqL1 = 6;
//去除外界噪音
pstVqeConfig.stAnrCfg.bUsrMode = HI_TRUE;
pstVqeConfig.stAnrCfg.s16NrIntensity = 15;//[0~25]越大降噪力度越高,损伤越高.
pstVqeConfig.stAnrCfg.s16NoiseDbThr = 60;//[30~60]越大,检测力度越弱,声音更平滑
pstVqeConfig.stAnrCfg.s8SpProSwitch = 1;//[0/1]是否开启对音乐细节检测,喧闹场景不建议开
//AGC 更多是放大输入源的声音
pstVqeConfig.stAgcCfg.bUsrMode = HI_FALSE;
#if 0
pstVqeConfig.stAgcCfg.s8TargetLevel = -2;
pstVqeConfig.stAgcCfg.s8NoiseFloor = -40;
pstVqeConfig.stAgcCfg.s8MaxGain = 30;
pstVqeConfig.stAgcCfg.s8AdjustSpeed = 10;
pstVqeConfig.stAgcCfg.s8ImproveSNR = 2;//上限6db
pstVqeConfig.stAgcCfg.s8UseHighPassFilt = 3;
pstVqeConfig.stAgcCfg.s8OutputMode = 0;
pstVqeConfig.stAgcCfg.s16NoiseSupSwitch = 1;//开启噪声抑制
#endif
//遇到库找不到请看下面
rc = HI_MPI_AI_SetTalkVqeAttr(AiDevId, AiChn, AoDevId, AoChn,&pstVqeConfig);
rc = HI_MPI_AI_EnableVqe(AiDevId, AiChn);
#endif
3.2. 输出初始化
代码语言:javascript复制 HI_S32 rc ,i;
HI_S32 s32AoChnCnt;
AIO_ATTR_S stAioAttr;
AUDIO_DEV AoDev = 0;
rc = HI_MPI_AO_Disable(AoDev );//先禁用AO设备
stAioAttr.enSamplerate = AUDIO_SAMPLE_RATE_8000;
stAioAttr.enBitwidth = AUDIO_BIT_WIDTH_16;
stAioAttr.enWorkmode = AIO_MODE_I2S_MASTER;
stAioAttr.enSoundmode = AUDIO_SOUND_MODE_MONO;
stAioAttr.u32EXFlag = 1;
stAioAttr.u32FrmNum = 30;
stAioAttr.u32PtNumPerFrm = 320;
stAioAttr.u32ChnCnt = 2;
stAioAttr.u32ClkSel = 1;
//stAioAttr.enI2sType = AIO_I2STYPE_INNERCODEC;
stAioAttr.enI2sType = AIO_I2STYPE_EXTERN;//若外部单独有解码芯片,需要选择外部,另需要确认是否支持;
rc = HI_MPI_AO_SetPubAttr(AoDev , &stAioAttr);
rc = HI_MPI_AO_Enable(AoDev );
/* enable AO channle */
s32AoChnCnt = stAioAttr.u32ChnCnt;
for (i = 0; i < s32AoChnCnt >> stAioAttr.enSoundmode; i )
{
rc = HI_MPI_AO_EnableChn(AoDev , i);
}
#if 0 //没用到
AUDIO_DEV AoDevId = 0;
AO_CHN AoChn = 0;
AO_VQE_CONFIG_S pstVqeConfig;
memset(&pstVqeConfig, 0, sizeof(AO_VQE_CONFIG_S));
pstVqeConfig.u32OpenMask = AO_VQE_MASK_HPF | AO_VQE_MASK_ANR | AO_VQE_MASK_AGC;
pstVqeConfig.s32WorkSampleRate = AUDIO_SAMPLE_RATE_8000;
pstVqeConfig.s32FrameSample = 320;
pstVqeConfig.enWorkstate = VQE_WORKSTATE_COMMON ;
//高通滤波功能相关配置
pstVqeConfig.stHpfCfg.bUsrMode = HI_TRUE;
pstVqeConfig.stHpfCfg.enHpfFreq = AUDIO_HPF_FREQ_150;
//去噪
pstVqeConfig.stAnrCfg.bUsrMode = HI_TRUE;//用户模式
pstVqeConfig.stAnrCfg.s16NrIntensity = 15;//[0~25]越大降噪力度越高,损伤越高.
pstVqeConfig.stAnrCfg.s16NoiseDbThr = 60;//[30~60]越大,检测力度越弱,声音更平滑
pstVqeConfig.stAnrCfg.s8SpProSwitch = 1;//[0/1]是否开启对音乐细节检测,喧闹场景不建议开
//自动增益控制
pstVqeConfig.stAgcCfg.bUsrMode = HI_TRUE;
pstVqeConfig.stAgcCfg.s8TargetLevel = -2;
pstVqeConfig.stAgcCfg.s8NoiseFloor = -20;
pstVqeConfig.stAgcCfg.s8MaxGain = 30;
pstVqeConfig.stAgcCfg.s8AdjustSpeed = 10;
pstVqeConfig.stAgcCfg.s8ImproveSNR = 0;//上限6db
pstVqeConfig.stAgcCfg.s8UseHighPassFilt = 0;
pstVqeConfig.stAgcCfg.s8OutputMode = 0;
pstVqeConfig.stAgcCfg.s16NoiseSupSwitch = 1;//开启噪声抑制
rc = HI_MPI_AO_SetVqeAttr(AoDevId, AoChn, &pstVqeConfig);
rc = HI_MPI_AO_EnableVqe(AoDevId, AoChn);
printf("HI_MPI_AO_EnableVqe AoDevId[%d] rc[%d] n",AoDevId,rc);
#endif
3.3. codec配置
代码语言:javascript复制如果是外置解码芯片,这里不需要配置内部Codec; 外置解码芯片就相当于codec;
应该不需要配置这么多,视情况而定!
代码语言:javascript复制 //func(enSamplerate) enSamplerate传进来
HI_S32 fdAcodec= -1;
HI_S32 ret = HI_SUCCESS;
ACODEC_FS_E i2s_fs_sel;
switch (enSamplerate)
{
case AUDIO_SAMPLE_RATE_8000:
i2s_fs_sel = ACODEC_FS_8000;
break;
case AUDIO_SAMPLE_RATE_16000:
i2s_fs_sel = ACODEC_FS_16000;
break;
case AUDIO_SAMPLE_RATE_32000:
i2s_fs_sel = ACODEC_FS_32000;
break;
case AUDIO_SAMPLE_RATE_48000:
i2s_fs_sel = ACODEC_FS_48000;
break;
default:
i2s_fs_sel = ACODEC_FS_8000;
break;
}
fdAcodec = open("/dev/acodec",O_RDWR);
if(fdAcodec < 0)
{
printf("[%s %d] open dev/acodec failed!n",__FUNCTION__,__LINE__);
return HI_FALSE;
}
//将内置Codec恢复为默认设置
if(ioctl(fdAcodec, ACODEC_SOFT_RESET_CTRL))
{
printf("reset the audio code to the default config errorn");
}
//设置I2s1采样率
if(ioctl(fdAcodec, ACODEC_SET_I2S1_FS, &i2s_fs_sel))
{
printf("[%s %d] set i2s1 fs failed!n",__FUNCTION__,__LINE__);
return HI_FALSE;
}
//输入方式选择
ACODEC_MIXER_E input_mode = ACODEC_MIXER_IN1;//单端输入 ACODEC_MIXER_IN_D;//:差分
if (ioctl(fdAcodec, ACODEC_SET_MIXER_MIC, &input_mode))
{
printf("%s: select acodec input_mode failedn", __FUNCTION__);
ret = HI_FAILURE;
}
int iVol = 50;//[-78-80] 建议[19-50]
#if 1
//输入音量之PGA模块
if (ioctl(fdAcodec, ACODEC_SET_INPUT_VOL, &iVol))
{
printf("%s: ioctl ACODEC_SET_INPUT_VOL failedn", __FUNCTION__);
return HI_FALSE;
}
unsigned int gain_mac;
gain_mac = 15;//15为最大增益30db 16:-1.5db
if (ioctl(fdAcodec, ACODEC_SET_GAIN_MICL, &gain_mac))
{
printf("%s: ioctl ACODEC_SET_GAIN_MICL failedn", __FUNCTION__);
ret = HI_FAILURE;
}
if (ioctl(fdAcodec, ACODEC_SET_GAIN_MICR, &gain_mac))
{
printf("%s: ioctl ACODEC_SET_GAIN_MICR failedn", __FUNCTION__);
ret = HI_FAILURE;
}
//输入音量之BOOST模块
if (ioctl(fdAcodec, ACODEC_SET_INPUT_VOL, &iVol))
{
printf("%s: ioctl ACODEC_SET_INPUT_VOL failedn", __FUNCTION__);
return HI_FALSE;
}
unsigned int enable_boostl;
enable_boostl = 0x1;//取1时模拟增益增加20db
if (ioctl(fdAcodec, ACODEC_ENABLE_BOOSTL, &enable_boostl))
{
printf("%s: ioctl ACODEC_ENABLE_BOOSTL failedn", __FUNCTION__);
return HI_FALSE;
}
unsigned int enable_boostr;
enable_boostr = 0x1;//取1时模拟增益增加20db
if (ioctl(fdAcodec, ACODEC_ENABLE_BOOSTR, &enable_boostr))
{
printf("%s: ioctl ACODEC_ENABLE_BOOSTR failedn", __FUNCTION__);
return HI_FALSE;
}
//#else
//输入音量之ADC模块
if (ioctl(fdAcodec, ACODEC_SET_INPUT_VOL, &iVol))
{
printf("%s: ioctl ACODEC_SET_INPUT_VOL failedn", __FUNCTION__);
return HI_FALSE;
}
ACODEC_VOL_CTRL vol_ctrl;
vol_ctrl.vol_ctrl_mute = 0;//不静音
vol_ctrl.vol_ctrl = 0;//[0~127] 0音量最大
//左声道输入音量控制
if(ioctl(fdAcodec, ACODEC_SET_ADCL_VOL, &vol_ctrl))
{
printf("%s: acodec set adcl vol failedn", __FUNCTION__);
ret = HI_FAILURE;
}
if(ioctl(fdAcodec, ACODEC_SET_ADCR_VOL, &vol_ctrl))
{
printf("%s: acodec set adcr vol failedn", __FUNCTION__);
ret = HI_FAILURE;
}
#endif
//输出总音量控制
iVol = 4; //[-126~6]
if (ioctl(fdAcodec, ACODEC_SET_OUTPUT_VOL, &iVol))
{
printf("[%s %d] set output vol fs failed!n",__FUNCTION__,__LINE__);
}
//把MICIN静音(MUTE)功能关闭
#if 1
HI_U32 u32MuteCtrl = 0;
if(ioctl(fdAcodec, ACODEC_SET_MICL_MUTE, &u32MuteCtrl))
{
printf("%s: select acodec mute_l failedn", __FUNCTION__);
ret = HI_FAILURE;
}
if(ioctl(fdAcodec, ACODEC_SET_MICR_MUTE, &u32MuteCtrl))
{
printf("%s: select acodec mute_r failedn", __FUNCTION__);
ret = HI_FAILURE;
}
#endif
close(fdAcodec);
4.音频下行 (adec->ao)
一般情况上行没问题,下行只是方向相反而已! 伪代码:
代码语言:javascript复制 HI_MPI_ADEC_CreateChn(chn,&解码参数);
HI_MPI_SYS_Bind(HI_ID_ADEC,..HI_ID_AO);
while(获取流)
{
HI_MPI_ADEC_SendStream(chn,&码流结构体,阻塞否?);
}
5.遇到的问题
代码语言:javascript复制问题1:
HI3516DV300 HI_MPI_AI_EnableVqe调节音频使能Vqe时出现.
dlopen libhive_common.so/libsecurec.so or libhive_AEC.so failed
解决方案:
在sdk中找到对应的.so文件,放到 usr/lib 下并记得改权限(重要).
问题2:
下行过程遇到的问题:HI_MPI_ADEC_SendStream 0xA0188040(码流错误);
排查过程:
1> 初步怀疑解码参数问题,检查后排除;可以在send_stream前面将下行码流保存成对应格式,如.g711a格式,在pc上播放;首先确保码流到这里能播放;
2> 第一步能通过,那么试试自编自解是否可行(首先确保上行ok,音频功放ok),在上行前先创建解码通道并绑定ao,在HI_MPI_AENC_GetStream获取流后立马又发给ao;
3> 第二步基本都没问题,记录下发送成功的字节长度;最终将问题定位到海思头(一定确保上行码流长度和下行码流长度一致)
如上行324字节(320 4字节海思头),下行也要保证一样,多了少了可能就有问题!!!
6.fm1288芯片
- 首先,向厂家要一份linux的驱动参考代码,方便参考配置;
- fm1288芯片使能的标志是MIC_BIAS引脚输出高电平;
- PWD# 引脚需要上电, RST#引脚需要复位在拉高,且需要在其后的90ms的软件初始化;
- I2S类型需选择AIO_I2STYPE_EXTERN才能通过I2C配置成功,不然一直显示超时;
- 参考 https://blog.csdn.net/qq_37565330/article/details/79460692
7.总结
代码语言:javascript复制多看手册,一般手册上都能找到答案.
结合sdk示例.
若软件上没啥问题,可适当排查硬件.硬件时钟,与示波器结合.分析时序等等一步步排查;
参考:https://baike.baidu.com/item/I2S/3443390?fr=aladdin
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/190250.html原文链接:https://javaforall.cn