好的公司网站制作直播网站开发框架
news/
2025/10/7 18:10:37/
文章来源:
好的公司网站制作,直播网站开发框架,视频优化网站怎么做,网站开发职务一.Audio Codec的必要性
在理想状况下#xff0c;对于录音过程#xff0c;只需要将麦克风获取到的analog信号通过ADC转换为digital信号并存储即可#xff0c;对于播放音过程#xff0c;只需要将digital信号通过DAC转换为analog并输出到speaker播放即可。
但在实际的过程中…一.Audio Codec的必要性
在理想状况下对于录音过程只需要将麦克风获取到的analog信号通过ADC转换为digital信号并存储即可对于播放音过程只需要将digital信号通过DAC转换为analog并输出到speaker播放即可。
但在实际的过程中对于录音过程而言会受到外界声源的干扰麦克风自身对信号的衰减以及物理链路接口上引入的杂音等因素的影响对于放音可能会受digital数据本身的问题等因素的影响。
举个简单的例子拿着手机或者固定电话和别人讲话的时候虽然一边自己说话一边听电话另外一端的人讲话但是从听筒中并没有非常明显的听到自己的讲话声音。这中间就是一些Audio Codec在起作用它们可以实现回音消除噪音抵消以及ALC/Limiter等当然它也实现了最重要的AD和DA功能。
二.RT5651设备树配置
i2c1 {status okay;i2c-scl-rising-time-ns 300;i2c-scl-falling-time-ns 15;rt5651: rt56511a {#sound-dai-cells 0;compatible rockchip,rt5651;reg 0x1a;clocks cru SCLK_I2S_8CH_OUT;clock-names mclk;status okay;};
};
其中
status 指定设备状态为“正常”表示该设备状态为正常运行
i2c-scl-rising-time-ns定义了SCL信号上升时间的最小值单位是纳秒
i2c-scl-falling-time-ns定义了SCL信号下降时间的最小值单位是纳秒
接着定义I2C从设备节点rt5651即音频编解码器的设备节点其名称为 rt5651I2C从设备7位地址为0x1a
compatible指定设备驱动程序的兼容性即告诉内核该设备可以被哪些驱动程序所使用
reg指定了rt5651设备在I2C控制器上的设备地址
clock-names指定时钟名称mclk表示MCLK时钟
clocksmclk时钟来自SCLK_I2S_8CH_OUT
status 指定设备状态为“正常”表示该设备状态为正常运行
三.Codec驱动分析
rt5651驱动路径
kernel-5.10\sound\soc\codecs\rt5651.c
rt5651_i2c_driver
这里我们需要关注一下i2c_driver结构体变量rt5651_i2c_driver
static struct i2c_driver rt5651_i2c_driver {.driver {.name rt5651,.acpi_match_table ACPI_PTR(rt5651_acpi_match),.of_match_table of_match_ptr(rt5651_of_match),},.probe rt5651_i2c_probe,.id_table rt5651_i2c_id,
};
其成员
driver.of_match_table用于设备树匹配
probe当I2C驱动和I2C从设备信息匹配成功之后就会调用probe函数
id_tableid列表用于和I2C从设备名称进行匹配
I2C从设备驱动中的rt5651_i2c_id匹配成功会执行probe探测函数
static int rt5651_i2c_probe(struct i2c_client *i2c) // 参数为I2C从设备
{struct rt5651_priv *rt5651;int ret;int err;rt5651 devm_kzalloc(i2c-dev, sizeof(*rt5651), // 动态申请内存数据结构类型为struct rt5651_privGFP_KERNEL);if (NULL rt5651)return -ENOMEM;......ret devm_snd_soc_register_component(i2c-dev, // 注册componentsoc_component_dev_rt5651,rt5651_dai, ARRAY_SIZE(rt5651_dai));return ret;
}
执行probe函数后这里重点看devm_snd_soc_register_component函数
调用devm_snd_soc_register_component注册的component该函数会动态申请一个component并将其添加到全局链表component_list中同时会建立dai_driver与component的关系。
codec驱动注册流程主要包含一下几个步骤
(1) 构造一个struct snd_soc_component_driver实例比如这里的soc_component_dev_rt5651用于描述codec driver需要初始化成员name、controls、dapm_widgets、dapm_routes等
(2) 构造一个struct snd_soc_dai_driver比如这里的rt5651_dai数组用于描述dai和 pcm的能力和操作需要初始化成员name、probe、playback、capture、ops等
(3) 调用devm_snd_soc_register_component注册component
devm_snd_soc_register_component函数第二个参数为soc_component_dev_rt5651
soc_component_dev_rt5651:
static const struct snd_soc_component_driver soc_component_dev_rt5651 {.probe rt5651_probe,.suspend rt5651_suspend,.resume rt5651_resume,.set_bias_level rt5651_set_bias_level,.set_jack rt5651_set_jack,.controls rt5651_snd_controls, // kcontrol定义.num_controls ARRAY_SIZE(rt5651_snd_controls), .dapm_widgets rt5651_dapm_widgets, // widget定义 .num_dapm_widgets ARRAY_SIZE(rt5651_dapm_widgets),.dapm_routes rt5651_dapm_routes, // route定义 .num_dapm_routes ARRAY_SIZE(rt5651_dapm_routes),.use_pmdown_time 1,.endianness 1,
};
其中controls、dapm_widgets、dapm_routes是与dapm相关的可以用来表述codec内部的音频路径。
DAPM简介
DAPM是Dynamic Audio Power Management的缩写直译过来就是动态音频电源管理的意思DAPM是为了使基于linux的移动设备上的音频子系统在任何时候都工作在最小功耗状态下。DAPM对用户空间的应用程序来说是透明的所有与电源相关的开关都在ASoc core中完成。DAPM根据当前激活的音频流playback/capture和声卡中的mixer等的配置来决定那些音频控件的电源开关被打开或关闭。
在datasheel里面描述了音频数据流具体路径细节如下图 上图中我们使用箭头标识了一条用于多媒体音频播放右声道的路径音频通路是
AIF1 Playbacksnd_soc_dapm_dai_in类型的playback dai widget -- AIF1RX AIF表示音频数字接口
AIF1RX -- IF1 DAC
IF1 DAC -- IF1 DAC1 R;
IF1 DAC1 R -- DAC MIXR通过rt5651_dac_r_mix名称为INF1 Switch控制通断由MX29寄存器位14来实现静音控制0非静音1静音
DAC MIXR -- Audio DSP
Audio DSP -- Stereo DAC MIXR通过rt5651_sto_dac_r_mix名称DAC R1 Switch控制通断由MX2A寄存器位6来实现静音控制0非静音1静音
Stereo DAC MIXR -- DAC R1
DAC R1 -- OUT MIXR 通过rt5651_out_r_mix名称为DAC R1 Switch控制通断由MX52寄存器位0来实现静音控制0非静音1静音
OUT MIXR -- HPOVOL R通过hpovol_r_control名称为Switch控制通断由MX02寄存器位6来实现静音控制0非静音1静音
HPOVOL R -- HPOR MIX通过rt5651_hpo_mix名称为HPO MIX HPVOL Switch控制通断由MX45寄存器位13来实现静音控制0非静音1静音
HPOR MIX -- HP Amp
HP Amp - HPO R Playback通过hpo_r_mute_control名称为Switch控制通断由MX02寄存器位7来实现静音控制0非静音1静音
HPO R Playback -- HPOR
HPOR -- Headphones最后一个path定义在Machine驱动中
其中红色部分表示有相应的kcontrol即需要switch打开在该路径中 HPOL 为SND_SOC_DAPM_EP_SINK类型端点但是路径中并没有SND_SOC_DAPM_EP_SOURCE类型端点。
kcontrol
在sound/soc/codecs/rt5651.c定义了大量的kcontrol包括普通kcontrol和dapm kcontrol
通常一个kcontrol代表着一个mixer混音器或者是一个mux多路开关又或者是一个音量控制器等等。
kcontrol的定义例如
static const struct snd_kcontrol_new rt5651_sto1_adc_l_mix[] {SOC_DAPM_SINGLE(ADC1 Switch, RT5651_STO1_ADC_MIXER,RT5651_M_STO1_ADC_L1_SFT, 1, 1),SOC_DAPM_SINGLE(ADC2 Switch, RT5651_STO1_ADC_MIXER,RT5651_M_STO1_ADC_L2_SFT, 1, 1),
};
widget
在sound/soc/codecs/rt5651.c定义了大量的widget
widget把kcontrol和动态电源管理进行了有机的结合同时还具备音频路径的连接功能一个widget可以与他相邻的widge有某种动态的连接关系。
widget的定义例如
SND_SOC_DAPM_MIXER(Stereo1 ADC MIXL, SND_SOC_NOPM, 0, 0,rt5651_sto1_adc_l_mix,ARRAY_SIZE(rt5651_sto1_adc_l_mix)),
route
在sound/soc/codecs/rt5651.c定义了widget的链接路径
系统中注册的各种widget需要互相连接在一起才能协调工作连接关系通过snd_soc_dapm_route结构来定义.
在rt5651_dapm_widgets中我们可以找到位于多媒体音频播放右声道路径上的route:
{IF1 DAC, NULL, AIF1RX},
{IF1 DAC1 R, NULL, IF1 DAC},
{DAC MIXR, INF1 Switch, IF1 DAC1 R},
{Audio DSP, NULL, DAC MIXR},
{Stereo DAC MIXR, DAC R1 Switch, Audio DSP},
{DAC R1, NULL, Stereo DAC MIXR},
{OUT MIXR, DAC R1 Switch, DAC R1}
{HPOVOL R, Switch, OUT MIXR},
{HPOR MIX, HPO MIX HPVOL Switch, HPOVOL R},
{HP Amp, NULL, HPOR MIX},
{HPO R Playback, Switch, HP Amp},
{HPOR, NULL, HPO R Playback},
rt5651_probe:
static int rt5651_probe(struct snd_soc_component *component)
{struct rt5651_priv *rt5651 snd_soc_component_get_drvdata(component); // 取出component-dev设备的driver_data就是上面介绍的rt5651结构变量rt5651-component component; // 设置cpmponentsnd_soc_component_update_bits(component, RT5651_PWR_ANLG1, // 向寄存器写入值RT5651_PWR_ANLG1的值为0x63寄存器地址0x63用于电源控制寄存器3RT5651_PWR_LDO_DVO_MASK, RT5651_PWR_LDO_DVO_1_2V); // RT5651_PWR_LDO_DVO_MASK值为0x03 RT5651_PWR_LDO_DVO_1_2V值为2 因此这里向位[1:0]写入10b// 即配置LDO output电压为1.2V snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); // Set the COMPONENT DAPM bias level即dapm-bias_level0rt5651_apply_properties(component);return 0;
}
rt5651_set_bias_level:
set_bias_level用于设置codec域的偏置电压那什么是偏置电压呢在电容式麦克风中为了使麦克风的工作点稳定需要加一个直流电压这个直流电压就是偏置电压偏置电压的作用主要有以下几个方面
稳定麦克风的工作点在没有偏置电压的情况下麦克风的输出信号会受到温度、湿度等环境因素的影响导致输出信号的偏移而偏置电压可以保持麦克风的工作不变保证输出信号的稳定性
提高麦克风的灵敏度偏置电压可以使麦克风的灵敏度增加从而提高声音的捕获能力
降低麦克风的噪声偏移电压可以降低麦克风的噪声水平使得麦克风的输出信号更清晰
对于ALC5651芯片MICBIAS1引脚会输出一个偏置电压提供给外置麦克风: static int rt5651_set_bias_level(struct snd_soc_component *component,enum snd_soc_bias_level level)
{switch (level) {case SND_SOC_BIAS_PREPARE: // 准备状态// 获取dapm-bias_level待机-准备if (SND_SOC_BIAS_STANDBY snd_soc_component_get_bias_level(component)) {// RT5651_PLL_MODE_1的值为0x83寄存器0x83为ASRC控制寄存器这里是判断位[15]、[12]、[9]是否为1// 其中位[15]为I2S1模式选择控制 0正常模式 1ASRC模式// 位[12]为I2S2模式选择控制 0正常模式 1ASRC模式 // 位[9] Select Control for ASRC Mode in DMIC1 Function 0正常模式 1ASRC模式if (snd_soc_component_read(component, RT5651_PLL_MODE_1) 0x9200) // RT5651_D_MISC的值为0xFA寄存器地址0xFA为基本控制寄存器snd_soc_component_update_bits(component, RT5651_D_MISC, 0xc00, 0xc00); // 这里向位[11:10]写入11b芯片手册中并没有描述这两位有什么作用}break;case SND_SOC_BIAS_STANDBY: // 待机状态// 获取dapm-bias_level关闭状态-待机状态if (SND_SOC_BIAS_OFF snd_soc_component_get_bias_level(component)) {// RT5651_PWR_ANLG1的值为0x63寄存器地址0x63用于电源控制寄存器3snd_soc_component_update_bits(component, RT5651_PWR_ANLG1, // RT5651_PWR_VREF1位[15]VREF1 Power Control0下电、1上电// RT5651_PWR_MB位[13]MBIAS Power Control0下电、1上电// RT5651_PWR_BG位[11]MBIAS Bandgap Power Control0下电、1上电// RT5651_PWR_VREF2位[4]VREF2 Power Control0下电、1上电RT5651_PWR_VREF1 | RT5651_PWR_MB | RT5651_PWR_BG | RT5651_PWR_VREF2, RT5651_PWR_VREF1 | RT5651_PWR_MB |RT5651_PWR_BG | RT5651_PWR_VREF2);usleep_range(10000, 15000);// RT5651_PWR_FV1位[14]VREF1 Fast Mode Control0 Fast VREF、1 Slow VREF, (For good analog performance)// RT5651_PWR_FV2位[0]VREF2 Fast Mode Control0 Fast VREF、1 Slow VREF, (For good analog performance)snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,RT5651_PWR_FV1 | RT5651_PWR_FV2,RT5651_PWR_FV1 | RT5651_PWR_FV2);// RT5651_D_MISC的值为0xFA寄存器地址0xFA为基本控制寄存器,芯片手册中并没有描述位[1]有什么作用snd_soc_component_update_bits(component, RT5651_D_MISC, 0x1, 0x1);}break;case SND_SOC_BIAS_OFF: // 关闭状态snd_soc_component_write(component, RT5651_D_MISC, 0x0010);snd_soc_component_write(component, RT5651_PWR_DIG1, 0x0000);snd_soc_component_write(component, RT5651_PWR_DIG2, 0x0000);snd_soc_component_write(component, RT5651_PWR_VOL, 0x0000);snd_soc_component_write(component, RT5651_PWR_MIXER, 0x0000);/* Do not touch the LDO voltage select bits on bias-off */snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,~RT5651_PWR_LDO_DVO_MASK, 0);/* Leave PLL1 and jack-detect power as is, all others off */snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,~(RT5651_PWR_PLL | RT5651_PWR_JD_M), 0);break;default:break;}return 0;
}
rt5651_set_jack:
static int rt5651_set_jack(struct snd_soc_component *component,struct snd_soc_jack *jack, void *data)
{if (jack)rt5651_enable_jack_detect(component, jack, data);elsert5651_disable_jack_detect(component);return 0;
}
在ASoC中使用struct snd_soc_jack来描述jack并提供了对其状态、引脚等进行管理和通知的功能。这里如何定义了jack将会调用rt5651_enable_jack_detect用于实现麦克风插入/拔出的检测。
rt5651_dai:
数字音频接口DAI即Digital Audio Interfaces顾名思义DAI表示在板级或板间传输数字音频信号的方式。
由于ALC5651有两组I2S接口可以同时用于耳机输出以及Line outputRK3568与ALC5651连线如下 * * * ------ * ---- MICRAM --------PCM- * ---------- * -I2S------------I2S1- * ** * | * ------ * ---- Line output**************** | * *RK3568 | * *-----I2S2- * ------ * ---- HEADPHONE***********ALC5651
codec dai和pcm配置信息通过结构体snd_soc_dai_driver描述包括了dai的能力描述和操作接口devm_snd_soc_register_component函数第三个参数为rt5651_dai数组中长度为2分别与ALC5651的两组I2S接口一一对应
static struct snd_soc_dai_driver rt5651_dai[] {{.name rt5651-aif1, // dai的名称会赋值给与其关联的snd_soc_dai的name成员 // 音频数据链路是通过dai_name到ALC5651的component的dai_list链表中来查找dai的 .id RT5651_AIF1, // 0dai的id.playback { // 声卡注册的时候会为其创建一个类型为snd_soc_dapm_dai_in的playback dai widget其name以及sname均设置为AIF1 Playback.stream_name AIF1 Playback, .channels_min 1, // 最小通道数.channels_max 2, // 最大通道数.rates RT5651_STEREO_RATES,.formats RT5651_FORMATS,},.capture { // 声卡注册的时候会为其创建一个类型为snd_soc_dapm_dai_out的capture dai widget其name以及sname均设置为AIF1 Capture.stream_name AIF1 Capture,.channels_min 1,.channels_max 2,.rates RT5651_STEREO_RATES, // 支持的采样率 SNDRV_PCM_RATE_8000_96000 999~96000之间.formats RT5651_FORMATS, // 支持的位深度 (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)},.ops rt5651_aif_dai_ops,},{.name rt5651-aif2,.id RT5651_AIF2, // 1dai的id.playback {.stream_name AIF2 Playback,.channels_min 1,.channels_max 2,.rates RT5651_STEREO_RATES,.formats RT5651_FORMATS,},.capture {.stream_name AIF2 Capture,.channels_min 1,.channels_max 2,.rates RT5651_STEREO_RATES,.formats RT5651_FORMATS,},.ops rt5651_aif_dai_ops,},
};
其中
namecodec dai的名称标识dai_link通过配置codec dai_name来找到对应的codec dai
capture描述capture的能力如回放设备所支持的声道数、采样率、音频格式非常重要的字段
playback描述playback的能力如录制设备所支持声道数、采样率、音频格式非常重要的字段
opscodec dai的操作函数集这些函数集非常重要用于dai的时钟配置、格式配置、硬件参数配置。
rt5651_aif_dai_ops:
static const struct snd_soc_dai_ops rt5651_aif_dai_ops {.hw_params rt5651_hw_params,.set_fmt rt5651_set_dai_fmt,.set_sysclk rt5651_set_dai_sysclk,.set_pll rt5651_set_dai_pll,
};
其中 set_sysclk用于设置系统时钟对于codec dai来说系统时钟指的是ALC5651 i2s1接口的MCLK信号线输入的时钟同时也是RK3399 i2s0接口的MCLK信号线输出的时钟对应的时钟名称为clk_i2sout当上层打开pcm设备时需要回调该接口设置ALC5651的系统时钟ALC5651才能正常工作
set_pll用于设置ALC5651的PLL的分频系数ALC5651一般接了一个MCLK作为ALC5651的PLL输入时钟源回调该函数基于MCLK来产生ALC5651 PLL时钟
set_fmt设置数字音频接口格式具体见 include/sound/soc-dai.h
SND_SOC_DAIFMT_I2S数字音频接口是I2S格式常用于多媒体音频
SND_SOC_DAIFMT_RIGHT_J数字音频接口是I2S右对齐格式
SND_SOC_DAIFMT_LEFT_J数字音频接口是I2S左对齐格式
SND_SOC_DAIFMT_DSP_A数字音频接口是PCM格式常用于语音通话
SND_SOC_DAIFMT_DSP_B数字音频接口是PCM格式常用于语音通话
SND_SOC_DAIFMT_CBM_CFMALC5651作为主机BCLK 和 LRCLK由ALC5651提供
SND_SOC_DAIFMT_CBS_CFSALC5651作为从机BCLK和LRCLK由SoC/CPU提供
.......
hw_paramscodec dai硬件参数设置根据上层设定的声道数、采样率、数据格式来配置codec dai相关寄存器
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/930695.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!