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

news2024/10/5 20:12:46

一.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_priv
                                GFP_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;   // 设置cpmponent

    snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,      // 向寄存器写入值,RT5651_PWR_ANLG1的值为0x63,寄存器地址0x63用于电源控制寄存器3
            RT5651_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=0

    rt5651_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用于电源控制寄存器3
                        snd_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);
        else
                rt5651_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连线如下:

                    *              *                         * <------ * <---- MIC
 RAM <--------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.coloradmin.cn/o/1849913.html

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

相关文章

C++ 编程技巧分享

侯捷 C 学习路径&#xff1a;面向对象的高级编程 -> STL库 -> C11新特性 -> cmake 1.1. C 与 C的区别 在C语言中&#xff0c;主要存在两大类内容&#xff0c;数据和处理数据的函数&#xff0c;二者彼此分离&#xff0c;是多对多的关系。不同的函数可以调用同一个数据…

Docker开机自动重启及自动启动容器

Docker开机自动重启及自动启动容器 Windows开机自动重启设置容器自动启动 Windows开机自动重启 勾选 Start Docker Desktop when you sign in to your computer 设置容器自动启动 1.docker update 命令 Usage: docker update [OPTIONS] CONTAINER [CONTAINER...]Update co…

32.基于分隔符解决黏包和半包

LineBasedFrameDecoder 基于换行/n (linux)或回车换行/r/n(windows)进行分割。 使用LIneBasedFrameDecoder构造方法,需要设定一个最大长度。 如果超过了最大长度,还是没有找到换行符,就这位这个数据段太长了,抛出ToolLongFrameException DelimiterBasedFrameDecoder …

IF膨胀时代,“水刊”当赢?2023热门“水刊”影响因子详解!

【欧亚科睿学术】 1 “四大水刊”详情 图片来源&#xff1a;欧亚科睿学术整理 “四大水刊”的影响因子均有所下跌&#xff0c;其中&#xff0c;曾经被列入中科院预警名单的期刊MEDICINE&#xff0c;其影响因子已是连续三年持续下降。从JCR分区来看&#xff0c;四本期刊分区均…

新手(初学者)学R语言第一课,从学正确导入数据开始

初看题目好像我在教你怎么导入数据&#xff0c;不不不&#xff0c;我是在教你正确的导入数据&#xff0c;不是说数据导入R就叫正确导入数据了。本章为新手教程&#xff0c;老手可以跳过。 这个内容早就想写了&#xff0c;今天有点空和大家聊一下。为什么R语言对于新手而言不太友…

threeJS 基础 03---动画

1.动画效果渲染循环 注&#xff1a; 使用循环渲染时&#xff0c;不用手动渲染到画布且再次调用监听更新事件&#xff0c;两者只用使用其中之一即可 周期循环&#xff0c;默认理想状态下每秒循环60次 requestAnimationFrame 渲染帧率&#xff08;详情见threeJs的文档&#xff…

区块链会议投稿资讯CCF A--WINE 2024 截止7.15 附录用率 附录用的区块链文章

Conference&#xff1a;The Conference on Web and Internet Economics (WINE) CCF level&#xff1a;CCF A Categories&#xff1a;Cross-cutting/comprehensive/emerging Year&#xff1a;2024 Conference time&#xff1a; December 2-5, 2024 录用率&#xff1a; sele…

Pyqt5 + Qt Creator实现QML开发环境配置

先安装Qt Creator, 该软件主要是为了编辑QML文件 在pycharm中配置外部插件&#xff0c;实现Qt Creator的调用 配置完成后&#xff0c;右击qml文件选择Qt Creator就可以直接进行编辑了

高效22KW双向DCDC储能、充电电源模块项目设计开发

22kW 双向CLL谐振变换器的目标是输出电压范围宽、高效率和高功率密度的双向应用&#xff0c;如电动汽车车载充电器和储能系统。研究了一种新的灵活的 CLLC 双向谐振变换器增益控制方案&#xff0c;以便在充放电模式下实现高效率和宽电压增益范围。得益于 Wolfspeed C3MTM 1200V…

Python+Selenium自动化入门

本章内容需有一定Python基础&#xff0c;如何不懂的&#xff0c;请先学习Python。 什么&#xff1f;&#xff1f;没有好的学习资料&#xff0c;给你准备好了&#xff01;&#xff01; Web自动化环境搭建 1、软件准备 python64位安装包chrome64位浏览器&驱动浏览器驱动下…

基于SpringBoot+协同过滤算法的家政服务平台设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;…

Java 超详细实现导入导出 (包含时间转换问题和样式)

序言 工作中遇到了导入导出问题&#xff0c;并且出现了导入或导出Excel时间格式变为数字的问题。通过学习解决实现了这些功能&#xff0c;记录总结分享给大家。本文将详细介绍如何使用 Java 编程语言和 Apache POI 库来实现这些功能。我们将通过一个示例项目演示如何从数据库中…

STM32学习笔记(十)--I2C、IIC总线协议详解

概述&#xff1a;Inter Integrated Circuit&#xff0c;一组多从 多组多从 有应答 是一种同步&#xff08;具有时钟线需要同步时钟SCL&#xff09;、串行&#xff08;一位一位的往一个方向发送&#xff09;、半双工&#xff08;发送接收存在一种&#xff09;通信总线。 &…

代码随想录算法训练营第六十七天 | 字符串接龙、有向图的完全可达性、岛屿的周长

字符串接龙 文字讲解&#xff1a;110. 字符串接龙 | 代码随想录 解题思路 本题只需要求出最短路径的长度就可以了&#xff08;想到广搜&#xff09;&#xff0c;不用找出具体路径。 所以这道题要解决两个问题&#xff1a; 图中的线是如何连在一起的起点和终点的最短路径长…

Stable Diffusion AI绘画助力建筑设计艺术创新——城市建筑设计大模型分享

大家好&#xff0c;我是向阳 今天我将针对建筑设计方面的AI大模型进行简单介绍&#xff0c;我们将通过富有想象力的关键词或结合Stable Diffusion 的ControlNet 给原本只有黑白线条的线稿变成彩色的效果图&#xff0c;可能你只需要短短几分钟就可以让黑白线稿变成几种甚至十几种…

临时关闭Windows安全中心

在使用WindowsOS是&#xff0c;微软安全中心是我们必不可少的安全防护&#xff0c;但有时我们也会产生想要将其关闭的需求&#xff0c;下面将要介绍如何临时关闭Windows的安全中心 一、打开安全中心、选择“病毒与威胁防护”&#xff0c;点击“管理设置” 之后将其实时保护关闭…

材料科学SCI期刊,中科院3区,收稿范围广,易录用

一、期刊名称 International Journal of Material Forming 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;材料科学 影响因子&#xff1a;2.4 中科院分区&#xff1a;3区 三、期刊征稿范围 该杂志发表和传播材料成型领域的原创研究。该研究应构成对材料…

multiprocessing多进程计算及与rabbitmq消息通讯实践

1. 需求与设计 我所设计的计算服务旨在满足多个客户对复杂计算任务的需求。由于这些计算任务通常耗时较长且资源消耗较大&#xff0c;为了优化客户体验并减少等待时间&#xff0c;我采取了并行计算的策略来显著提升计算效率。 为实现这一目标&#xff0c;我计划利用Python的m…

展讯-源码编译

1.硬件要求 编译主机要求&#xff0c;注意尽量不要使用虚拟机 CPU&#xff1a;Intel(R) Core(TM) i7-4790 CPU 3.60 GHz 内存&#xff1a;8G 硬盘&#xff1a;500G SSD Ubuntu 版本&#xff1a;Ubuntu 64-bit 14.04.5 LTS 这是最低版本要求&#xff0c;实际性能越高越好 2…

尽管担任社长 宫崎英高95%时间还是在做游戏

宫崎英高表示&#xff0c;虽然他是 FromSoftware 工作室的社长&#xff0c;但他日常工作的绝大部分时间都是在指导游戏开发。因魂系列大获成功而成为 FromSoftware 社长的宫崎英高在接受《卫报》采访时表示&#xff0c;企业高管生活并不是他的兴趣所在。 “我喜欢帮助年轻的监督…