RK3568平台(音频篇)RT5651解码芯片Codec驱动分析

一.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: rt5651@1a {#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,即音频编解码器的设备节点,其名称为 rt5651,I2C从设备7位地址为0x1a;

compatible:指定设备驱动程序的兼容性,即告诉内核该设备可以被哪些驱动程序所使用;

reg:指定了rt5651设备在I2C控制器上的设备地址;

clock-names:指定时钟名称,"mclk"表示MCLK时钟;

clocks:mclk时钟来自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_table:id列表,用于和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,   // 注册component&soc_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 Playback(snd_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]写入10'b// 即配置LDO output电压为1.2V    snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);   // Set the COMPONENT DAPM bias level,即dapm->bias_level=0rt5651_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:正常模式  1:ASRC模式// 位[12]为I2S2模式选择控制  0:正常模式  1:ASRC模式 // 位[9] Select Control for ASRC Mode in DMIC1 Function 0:正常模式  1:ASRC模式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]写入11'b,芯片手册中并没有描述这两位有什么作用}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 Control:0下电、1上电// RT5651_PWR_MB位[13],MBIAS Power Control:0下电、1上电// RT5651_PWR_BG位[11],MBIAS Bandgap Power Control:0下电、1上电// RT5651_PWR_VREF2位[4],VREF2 Power Control:0下电、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 Control:0 Fast VREF、1  Slow VREF, (For good analog performance)// RT5651_PWR_FV2位[0],VREF2 Fast Mode Control:0 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 output;RK3568与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,      // 0,dai的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,   // 1,dai的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,},
};

其中:

name:codec dai的名称标识,dai_link通过配置codec dai_name来找到对应的codec dai;

capture:描述capture的能力;如回放设备所支持的声道数、采样率、音频格式;非常重要的字段;

playback:描述playback的能力;如录制设备所支持声道数、采样率、音频格式;非常重要的字段;

ops:codec 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_CFM:ALC5651作为主机,BCLK 和 LRCLK由ALC5651提供;

SND_SOC_DAIFMT_CBS_CFS:ALC5651作为从机,BCLK和LRCLK由SoC/CPU提供;

.......

hw_params:codec dai硬件参数设置,根据上层设定的声道数、采样率、数据格式,来配置codec dai相关寄存器;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/861500.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【学习】如何利用Python技术进行软件测试相关工作

Python是一种广泛使用的高级编程语言&#xff0c;它因其简洁的语法、强大的库支持和跨平台特性而受到开发者的喜爱。在软件测试领域&#xff0c;Python同样发挥着重要作用&#xff0c;它可以帮助测试人员编写自动化测试脚本、进行接口测试、性能测试、以及处理测试数据等。以下…

【Linux】进程信号_3

文章目录 八、进程信号2. 信号的保存3. 信号的处理 未完待续 八、进程信号 2. 信号的保存 实际执行信号的处理动作称为信号递达(Delivery) 信号从产生到递达之间的状态,称为信号未决(Pending)。 进程可以选择阻塞 (Block )某个信号。 被阻塞的信号产生时将保持在未决状态,直到…

华为开发者调试工具使用介绍(MDC)

MDC的开发过程的三大工具&#xff1a;MMC、MDS、Mind Studio&#xff0c;这三个工具完成了开发过程中的配置文件编写、代码编写以及AI模型的开发三个任务。除了开发&#xff0c;MDC还准备了两个调试工具&#xff0c;用于使用过程中数据的查看等。这一些调试工具分别对映射MDC中…

单目标应用:基于鳗鱼和石斑鱼优化器(Eel and grouper optimizer,EGO)的微电网优化(MATLAB代码)

一、微电网模型介绍 微电网多目标优化调度模型简介_vmgpqv-CSDN博客 参考文献&#xff1a; [1]李兴莘,张靖,何宇,等.基于改进粒子群算法的微电网多目标优化调度[J].电力科学与工程, 2021, 37(3):7 二、鳗鱼和石斑鱼优化器求解微电网 2.1算法简介 鳗鱼和石斑鱼优化器&…

MySQL简介:开源数据库的基石(一)

目录 引言&#xff1a;数据库领域的革新者 一、MySQL的发展历程&#xff1a;从开源先锋到行业领袖 二、MySQL的核心特性&#xff1a;性能、安全与灵活性并重 三、MySQL的应用场景&#xff1a;从Web开发到企业级应用的全面覆盖 四、MySQL在开源数据库中的地位&#xff1a;开…

Excel直接打开csv文件后,数据不能正常显示的解决方法

很多处理公司数据的同事偶尔也会遇到使用Microsoft的Excel或者金山的WPS打开csv文件后&#xff0c;发现数据不能正常显示&#xff0c;其实csv属于一种数据库文件&#xff0c;遵循数据库存储的规则&#xff0c;建议使用导入的方式进行打开。 1.打开一张空白的Excel表&#xff0…

Spring Boot 集成 H2 数据库

1. 引言 Spring Boot 以其简洁的配置和快速开发能力&#xff0c;成为现代微服务架构的首选框架之一。而H2数据库作为一个轻量级的内存数据库&#xff0c;非常适合开发阶段作为嵌入式数据库进行单元测试和功能验证。本文将手把手教你如何在Spring Boot项目中集成H2数据库&#…

Open3D 显示带有强度的点云数据

目录 一、概述 1.1强度信息的意义 1.2应用场景 二、代码实现 三、实现效果 一、概述 在点云数据中&#xff0c;强度&#xff08;Intensity&#xff09;指的是激光雷达传感器在扫描环境时&#xff0c;每个点返回的反射强度值。这些强度值代表了激光脉冲返回的能量&#xff…

centos 破解密码

重启您的CentOS系统。 在GRUB引导加载器启动过程中&#xff0c;当看到启动画面时&#xff0c;按下e键进入编辑模式。 找到以 linux16 或 linux 开头的启动行。 在该行的末尾添加 rd.break 或者ro&#xff08;只读&#xff09;修改为 rw 加init/sysroot/bin/sh参数&#xff0…

Nacos详解

nacos官方文档&#xff1a;https://nacos.io/ 一、什么是nacos&#xff1f; Nacos是 Dynamic Naming and Configuration Service的首字母简称&#xff0c;一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 nacos官方文档&#xff1a;https://nacos.io/ nac…

数据结构--栈(图文)

栈是一种基本的抽象数据类型&#xff0c;具有后进先出的特点。在栈这种数据结构中&#xff0c;元素只能在一端进行插入和删除操作&#xff0c;这一端被称为栈顶&#xff08;Top&#xff09;&#xff0c;而另一端则称为栈底&#xff08;Bottom&#xff09;。 栈的概念及特点 栈…

springboot社区维修平台

设计技术&#xff1a; springboot、mysql、maven、前端vue 主要功能&#xff1a; 住户管理、社区公告管理、维修工管理、维修订单管理、接单信息管理、订单信息管理、在线沟通管理、举报信息管理、留言板管理、系统管理等功能模块。 管理员功能模块 管理员通过后台登录页面…

基于单片机和 Arduino 平台的六自由度可控机械手臂

摘 要 : 为了降低机械手臂的设计开发难度 &#xff0c; 并使之尽早地投入应用 &#xff0c; 设计一种基于单片机和 Arduino 平台的六自由度可控机械手臂 。提出六自由度可控机械手臂的控制方案&#xff0c; 给出机械手臂控制系统的结构框图 。 详细设计六自由度可控机械手臂…

Websocket在Java中的实践——自动注册端点

在《Websocket在Java中的实践——握手拦截器》中我们使用握手拦截器实现了路径解析的工作。这个过程略显复杂&#xff0c;因为路径解析这样比较底层的工作应该由框架来解决&#xff0c;而不应该交由开发者来做。本文介绍的自动注册端点的功能就可以很优雅的解决这个问题。 依赖…

[C++][设计模式][原型模式]详细讲解

1.动机 在软件系统中&#xff0c;经常面临这“某些结构复杂的对象”的创建工作&#xff1b;由于需求的变化&#xff0c;这些对象经常面临着剧烈的变化&#xff0c;但是它们却拥有比较稳定一致的接口如何应对这种变化&#xff1f;如何向“客户程序(使用这些对象的程序)”隔离出…

STM32HAL库--IIC实验(速记版)

STM32 的普通 IO 口模拟 IIC 时序&#xff0c;可实现与 EEPROM 外设双向通信。 IIC 简介 IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线&#xff0c;用于连接微控制器以及其外围设备。 IIC 是由数据线 SDA 和时钟线 SCL 构成的串行总线。 IIC …

在数据库领域是如何实现“多租户”的呢?

数据库多租技术介绍 随着云计算时代的到来&#xff0c;多租户的概念也逐渐广为人知。“多租户”使得租户之间可以共享物理资源&#xff0c;能够帮助用户节约硬件成本和运维成本&#xff0c;提高资源利用效率。同时&#xff0c;在实现的过程中&#xff0c;考虑到共享带来的安全…

three.js - matcap材质(MeshMatcapMaterial)

说一下matcap纹理 先总结&#xff1a;MeshMatcapMaterial材质&#xff0c;通过采样含有光照信息的贴图来模拟光照效果。这种材质特别适用于模拟静态光源下的光照&#xff0c;并且&#xff0c;因其简单性和快速性而被广泛应用于各种场景。但是&#xff0c;由于其性能考虑&#x…

系统思考—啤酒游戏经营决策沙盘

在日常的教学中&#xff0c;我们通过系统思考仿真演练深入探索决策背后的动因。例如&#xff0c;我经常教授的麻省理工学院研发的“啤酒游戏”和“人民航空策略模拟”&#xff0c;这些都是麻省理工MBA学生的必修课。此外&#xff0c;还有更简洁的“红黑游戏”“收获季节”等模拟…

ElasticSearch索引架构与存储

关于ES官网的介绍: Elasticsearch provides near real-time search and analytics for all types of data. Whether you have structured or unstructured text, numerical data, or geospatial data, Elasticsearch can efficiently store and index it in a way that support…