文章目录

      • 概述
      • 1)codec对象-WM8960
      • 2)ALSA下的kcontrol的构造与使用
      • 3)ASOC-ALSA下的kcontrol构造与使用
        • 1、通用寄存器对象 - kcontrol
        • 2、DAPM下的寄存器对象-widget
        • 3、如何构造widget?
        • 4、抽象对象widget、route与path
          • 1)route与path的关系?
          • 2)widget与path的关系
          • 3)一条完整的path - complete path
          • 3)complete path是如何生成的?
      • 4)DAPM下的数据结构
      • 5)DAPM场景分析
        • 1、widget什么时候会被打开?
        • 2、tinymix/tinyplay/tinymix调用DAPM

概述

ALSA声卡驱动中的DAPM详解之一 :https://blog.csdn.net/DroidPhone/article/details/12793293基本概念
1、DAPM : Dynamic Audio Power Management
>>可以当做ASoc-ALSA系统下的一个子系统,构造也是相当复杂,但声卡驱动需要熟悉此框架,使用其框架的宏和API来构造(电源)寄存器对象;产生的背景:
1)ASOC框架中动态音频电源管理子系统 - 设计的目的是省电
2)同时也达到减少暴露给应用的操作(寄存器操作)效果 - 比如封装打开某个input通道、MUX、mixer操作等等;
3)
DAPM是Dynamic Audio Power Management的缩写,直译过来就是动态音频电源管理的意思,DAPM是为了使基于linux的移动设备上的音频子系统,在任何时候都工作在最小功耗状态下。DAPM对用户空间的应用程序来说是透明的,所有与电源相关的开关都在ASoc core中完成。用户空间的应用程序无需对代码做出修改,也无需重新编译,DAPM根据当前激活的音频流(playback/capture)和声卡中的mixer等的配置来决定那些音频控件的电源开关被打开或关闭。
4)
在嵌入式领域中,由于数字功放内部的寄存器很多,没有DAPM之前,我们可以在init里面逐一将其设置好,即完全打开数字功放的所有功能,虽然简单,但比较费电,因此设计DAPM来控制按需打开(这套框架也相当复杂);
5)
此前在ASOC-ALSA分析中,看到很多DAPM的影子,现在来看看DAPM子系统如何嵌入到ASOC框架control中的,注意数字功放里面很多寄存器,DAPM主要控制电源相关的寄存器(Mixer、MUX、switch),音量volume则不需要。2、DAPM Widget用于表示音频路径中的物理连接点或控制点,根据一定条件(自动路由管理)触发关闭/打开 控制点以达到节能状态3、kcontrol : 代表一个控制寄存器(可能是寄存器中某几个bit),即功能控制4、coalesced : 合并

DAPM框架有较多的抽象概念,理解起来比较费劲和枯燥,我们先逐个拆解"对象",并梳理这些对象之间的关系,最后从宏观上看应用的效果,接下来使用WM8960 (有丰富的寄存器) 功放作为硬件声卡展开说明

1)codec对象-WM8960

Input Signal Path
在这里插入图片描述

1、PGA 是 “可编程增益放大器”(Programmable Gain Amplifier);

2、以上是Left Input/Left Output示意图,三路输入对应三条音频路径,输入到输出需要经过内部多个器件(寄存器);

1)比如:LINPUT1 -> LMN1 -> Left Input PGA -> LMICBOOST -> LMIC2B -> Left Boose Mixer -> Left ADC

2)可以看到有些部件是功能部件,有些是开关部件;

3、假如用户只适用LINPUT1,其它两路不适用,全部寄存器都设置打开,而有些模拟器件即使没有输入源信号也会耗电,而DAPM的任务是按需打开;

4、哪些寄存器对象是需要DAPM控制?

1)不是所有器件都需要DAPM控制,跟电源相关且可以通过寄存器关闭的器件,则需要DAPM参与把控,比如LIN1BOOST/LIN2BOOST/LIN3BOOST/Left Boost Mixer,而LINVOL则不需要DAPM参与把控;

2)ALSA下的kcontrol的构造与使用

1、kcontrol对应的结构体 - snd_kcontrol_new 与 snd_kcontrol
struct snd_kcontrol_new {snd_ctl_elem_iface_t iface;	/* interface identifier */const unsigned char *name;	/* ASCII name of item */snd_kcontrol_info_t *info;snd_kcontrol_get_t *get;snd_kcontrol_put_t *put;unsigned long private_value;
};以snd_kcontrol_new为模版,构造实际的寄存器对象-snd_kcontrol
struct snd_kcontrol {struct list_head list;		/* list of controls */struct snd_ctl_elem_id id;unsigned int count;		/* count of same elements */ 相同元素-比如左右声道,在链表里面会占用两个idsnd_kcontrol_info_t *info;snd_kcontrol_get_t *get;snd_kcontrol_put_t *put;union {snd_kcontrol_tlv_rw_t *c;const unsigned int *p;} tlv;unsigned long private_value;void *private_data;void (*private_free)(struct snd_kcontrol *kcontrol);struct snd_kcontrol_volatile vd[0];	/* volatile data */
};info/get/put这些函数怎么使用,直接看用户层怎么使用即可一目了然2、tinymix的使用
1)查看tinymix可控的寄存器列表
root@# tinymix
Mixer name: 'audiocodec'  //const unsigned char *name;
Number of controls: 16
ctl type num name          value
1   INT  1  "digital volume" 0  //snd_kcontrol_info_t *info;
2   INT  1  "LINEIN to output mixer gain control" 3
3   BOOL 1  "LINEOUT Switch" On
...2)设置音量
1、根据name来操作
tinymix "LINEOUT volume" "2" //snd_kcontrol_put_t *put;
2、根据ctl id来操作  - 使用成员struct snd_ctl_elem_id id表示,数值代表加入card list里面的顺序。
tinymix 1 "2" //snd_kcontrol_put_t *put; //

3、private_value字段

可以用来定义该控件所对应的寄存器的地址以及对应的控制位在寄存器中的位置信息,供给put/get/info函数所使用!

以Bass Boost 寄存器为例:
在这里插入图片描述

1)一个寄存器会有多个功能,一个kcontrol代表某个功能,shift代表从第几个bit开始,max表示占多少bit;

2)snd_kcontrol_put_t *put; 会将用户传下来的值 经过转换 最终写到对应寄存器中去;

3)ASOC-ALSA下的kcontrol构造与使用

ASOC-ALSA相对ALSA,进一步封装,但最终还是会调用ALSA那套kcontrol逻辑

1、通用寄存器对象 - kcontrol
内核提供一堆宏供开发者定义kcontrol(用来定义所有寄存器对象)
1)android\kernel\fusion\4.19\include\sound\soc.h
/** Convenience kcontrol builders*/
#define SOC_SINGLE(xname, reg, shift, max, invert) \
{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\.put = snd_soc_put_volsw, \.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }2)android\kernel\fusion\4.19\sound\soc\soc-ops.c
通用info/put/get对应的操作函数3)开发者需要根据寄存器属于哪一种分类,每种不同分类的寄存器所对应的put/get不一样,然后使用对应的内核宏来进行构造
常用的寄存器对象 - 基本可以覆盖所有功放寄存器类型
1、SOC_SINGLE 寄存器对象 - 操作一个寄存器
2、SOC_SINGLE_TLV 寄存器对象 - 用于音量,增益寄存器
3、SOC_DOUBLE 寄存器对象 - 操作两个寄存器
4、Mixer 寄存器对象 - 多合一寄存器
5、Mux 寄存器对象 - 多选一寄存器6、如果需要自定义put/get函数,可以使用EXT后缀的内核宏
SOC_SINGLE_EXT
SOC_DOUBLE_EXT4)构造例子
static const struct snd_kcontrol_new wm8960_snd_controls[] = {
SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,6, 1, 0),
SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT1 Volume",WM8960_LINPATH, 4, 3, 0, micboost_tlv),
SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0),
SOC_ENUM("ADC Polarity", wm8960_enum[0]),
...
}细节可以展开代码进行研究
2、DAPM下的寄存器对象-widget
1、DAMP子系统下 基本操作单元不是kcontrol,而是widget,一个widget可以包含一个kcontrol,也可以包含多个kcontrol;
2、widget是对kcontrol的进一个封装,使其具备更多的能力(连接、状态),使其与电源管理关联起来;
3、DAPM框架下 定义kcontrol(主要是电源相关的寄存器)的宏,分类很多!android\kernel\fusion\4.19\include\sound\soc-dapm.h
/** SoC dynamic audio power management** We can have up to 4 power domains*  1. Codec domain - VREF, VMID*     Usually controlled at codec probe/remove, although can be set*     at stream time if power is not needed for sidetone, etc.*  2. Platform/Machine domain - physically connected inputs and outputs*     Is platform/machine and user action specific, is set in the machine*     driver and by userspace e.g when HP are inserted*  3. Path domain - Internal codec path mixers*     Are automatically set when mixer and mux settings are*     changed by the user.*  4. Stream domain - DAC's and ADC's.*     Enabled when stream playback/capture is started.*//* codec domain */
#define SND_SOC_DAPM_VMID(wname) \
{	.id = snd_soc_dapm_vmid, .name = wname, .kcontrol_news = NULL, \.num_kcontrols = 0}/* platform domain */
#define SND_SOC_DAPM_INPUT(wname) \
{	.id = snd_soc_dapm_input, .name = wname, .kcontrol_news = NULL, \.num_kcontrols = 0, .reg = SND_SOC_NOPM }/* path domain */
#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \wcontrols, wncontrols)\
{	.id = snd_soc_dapm_mixer, .name = wname, \SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \.kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
3、如何构造widget?

widget是DAPM下的基本操作单元,widget实际上是一个抽象感念,

与kcontrol的关系,一个widget可以包含一个kcontrol,也可以包含多个kcontrol,也可不包含kcontrol
在这里插入图片描述

构造例子
widget的构造分为两个阶段,
第一阶段是使用内核宏进行静态构造(name、reg等赋值);
第二阶段则使用内核api snd_soc_dapm_new_controls进行动态构造1、内核宏(codc驱动)
static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("LINPUT1"),  //虚拟端点,不包含kcontrolSND_SOC_DAPM_SUPPLY("MICB", WM8960_POWER1, 1, 0, NULL, 0), //包含一个kcontrolSND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0,  //包含多个kcontrolwm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)),SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0,wm8960_lin, ARRAY_SIZE(wm8960_lin)),SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER1, 3, 0),SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0),SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0,&wm8960_loutput_mixer[0],ARRAY_SIZE(wm8960_loutput_mixer)),SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0),SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0),SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0),SND_SOC_DAPM_OUTPUT("SPK_LP"), //虚拟端点,不包含kcontrol
};static const struct snd_kcontrol_new wm8960_lin_boost[] = {
SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0),
SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0),
SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),
};注意与kcontrol不同的get/put函数
.get = snd_soc_dapm_get_volsw,
.put = snd_soc_dapm_put_volsw,  //调用很深!简单看看snd_soc_dapm_put_volsw的调用
--dapm_kcontrol_set_value(kcontrol, val | (rval << width));
--soc_dapm_mixer_update_power(card, kcontrol, connect, rconnect);
----soc_dapm_connect_path(path, rconnect, "mixer update");
----dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
------dapm_seq_run(card, &down_list, event, false);
------dapm_widget_update(card);
------dapm_seq_run(card, &up_list, event, true);2、内核api snd_soc_dapm_new_controls (machine驱动)
wm8960_probe
--wm8960_add_widgets
----snd_soc_dapm_new_controls
------snd_soc_dapm_new_control_unlocked(dapm, widget);//涉及很多变量设置,需要花费一定精力去梳理
//根据dapm type,挂上对应check_power方法,用于判断该widget是否需要上电
//上电条件:1、widget位于complete path上;2、有APP在使用声卡;
--------w->is_ep = SND_SOC_DAPM_EP_SOURCE; //当type=snd_soc_dapm_mic,会设置endpoint
--------w->power_check = dapm_generic_check_power; 
--------w->connected = 1;
----snd_soc_dapm_link_dai_widgets(card);
----snd_soc_dapm_connect_dai_link_widgets(card);
----snd_soc_dapm_add_routes()
----snd_soc_dapm_new_widgets(card);
------dapm_new_mixer(w);
--------dapm_create_or_share_kcontrol(w, i);
----------snd_soc_cnew()
----------snd_ctl_add(card, kcontrol); //添加kcontrol到card链表里面去,kcontrol的name=(widget name + kcontrol name)
------dapm_new_mux(w);3、实际开发时,操作相近的例子进行构造
4、几点疑问:
1)通用寄存器对象与DAPM下的寄存器对象会重复吗? 不会重复
2)DAPM的kcontrol也需要给用户使用? 需要,在machine probe中会挂到card下的kcontrol链表中,在哪里触发?在machine代码probe一路下来,最终在snd_soc_dapm_new_widgets()
4、抽象对象widget、route与path
1)route与path的关系?
1、内核设计为了方便path的表示,使用route用来表示path的链接,然后在初始化代码中自动构造出path。2、比如:
struct snd_soc_dapm_route {const char *sink;  //终点widget的nameconst char *control; //连接两个widget的kcontrolconst char *source; //起始widget的name
};static const struct snd_soc_dapm_route audio_paths[] = {{ "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
}
3、可以看出一个path只能连接相邻的两个widget4、route如何转化为path?
wm8960_probe
--wm8960_add_widgets
----snd_soc_dapm_add_routes()
------snd_soc_dapm_add_route(dapm, route);
//最终添加到snd_soc_dapm_context下path链表中
--------snd_soc_dapm_add_path(dapm, wsource, wsink, route->control,route->connected); snd_soc_dapm_add_path()的工作
1)找到source/sink,构造path
2)设置path中的connect,当path中的kcontrol为空 (即path中间没有switch),则connect恒为1;不为空,则根据kcontrol 寄存器的值设置connect;
3)将path放进链表里面去,构造出complete path
2)widget与path的关系

在这里插入图片描述

1、一个path相当于一个跳线,但只能连上 物理硬件上相邻的widget;

2、complete path : 多个path连起来,完整实现一条音频流通路 即为一个complete path;

3)一条完整的path - complete path
1、多个path组成完整的一条通路,叫做完全路径,比如一个录音功能需要一条完全路径来实现
2、举个例子
static const struct snd_soc_dapm_route audio_paths[] = {{ "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },  //path1{ "Left Input Mixer", "Boost Switch", "Left Boost Mixer" }, //path2{ "Left ADC", NULL, "Left Input Mixer" }, //path3
}
path1->path2->path3 组成了一条完整的complete path

链表结构图
在这里插入图片描述

3)complete path是如何生成的?
1、complete path其实是抽象出来的概念,不是静态定义的,而是利用链表算法动态生成,通过递归遍历算法 动态探索出一条complete path。2、怎么从一个widget,遍历所有跟此widget在同一条complete path上的widget?在构造path时,每个widget都有记录此path的变量,称为edges,区分是in还是out
snd_soc_dapm_add_path(){list_add(&path->list, &dapm->card->paths);snd_soc_dapm_for_each_direction(dir)list_add(&path->list_node[dir], &widgets[dir]->edges[dir]);
}#define snd_soc_dapm_for_each_direction(dir) \for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \(dir)++)

1、举个例子

widget1的拓扑关系
在这里插入图片描述

完全展开widget的拓扑关系,可以包含此widget在同一条complete path上的所有widget;

最后利用递归算法(利用edges[in] 实现向后遍历,利用edges[out] 实现向前遍历)

4)DAPM下的数据结构

在这里插入图片描述

1、DAPM数据结构里存在大量链表;

2、DAPM结构体指向混乱,但有个总的源头 - snd_soc_card;

5)DAPM场景分析

1、widget什么时候会被打开?

上层发出请求后(指tinymix / tinyplay),会触发一次事件,判断widget的成员connect为1时,打开此widget

那connect在哪里被设置?

1)注册route,path时会设置一次;

2)后续上层发出请求后 会调用check_power进行动态更新;

1、check power的实现
android\kernel\fusion\4.19\sound\soc\soc-dapm.c
dapm_generic_check_power() //DAPM实现的关键,也是重难点
--is_connected_input_ep() //ep endpoint,如 input1/input2
--is_connected_output_ep() //ep endpoint,如 speaker/headphone
----is_connected_ep()
------snd_soc_dapm_widget_for_each_path(widget, rdir, path) //涉及内核算法,遍历每一个widget,确认该widget位于complete path,位于的话就需要被打开
2、tinymix/tinyplay/tinymix调用DAPM
1)DAPM中重点的概念和算法都已经清楚,接下来从应用层的角度看看如何使用DAMP;
2)关键的实现都集中的soc-dapm.c文件中,这里使用树状图来进行记录说明1)tinyplay,tinycap
播放/录音前都会调用 soc_pcm_prepare
android\kernel\fusion\4.19\sound\soc\soc-pcm.c
soc_pcm_prepare// stream's name = Playback or Capturesnd_soc_dapm_stream_event(rtd, stream's name, SND_SOC_DAPM_STREAM_START)soc_dapm_stream_event()// 找出每一个widget// 如果strstr(w->sname, stream) // w->sname中含有Playback or Capture// w->active = 1		dapm_power_widgets()2)tinymix调用过程:
设置寄存器,会调用其put函数
android\kernel\fusion\4.19\sound\soc\soc-dapm.c
snd_soc_dapm_put_volsw()
--dapm_kcontrol_set_value(kcontrol, val | (rval << width));
--soc_dapm_mixer_update_power(card, kcontrol, connect, rconnect);
----soc_dapm_connect_path(path, rconnect, "mixer update");
----dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);3)tinyplay,tinycap,tinymix最终都会调用dapm_power_widgets
dapm_power_widgets()// 对于每一个widget// power = w->power_check(w);  // 确定是否要上电list_for_each_entry(w, &card->dapm_dirty, dirty) {dapm_power_one_widget(w, &up_list, &down_list);--dapm_widget_power_check(w);----w->power_check(w); //调用power check设置power字段--dapm_widget_set_power(w, power, up_list, down_list);----if (power) // 放入不同的链表, 以后统一上是或关闭dapm_seq_insert(w, &up_list, true); //上电widget listelsedapm_seq_insert(w, &down_list, false); //掉电widget list}		//给down_list上的所有widget掉电dapm_seq_run(dapm, &down_list, event, false);//根据dapm->update设置kcontrol, // update来自tinymix的调用dapm_widget_update(dapm);//给up_list上的所有widget上电dapm_seq_run(dapm, &up_list, event, true);4)给链表中的widget上电或关闭
dapm_seq_rundapm_seq_run_coalescedsnd_soc_update_bits

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

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

相关文章

如何修改anaconda 创建新虚拟环境的路径(默认是C:\.conda\envs)

参考文章&#xff1a; 如何修改anaconda 创建新虚拟环境的路径(默认是C&#xff1a;\.conda\envs)_anaconda创建环境怎么改路径-CSDN博客

前缀和计算

前缀和 输入一个长度为n的整数序列。接下来再输入m个询问&#xff0c;每个询问输入一对l, r。对于每个询问&#xff0c;输出原序列中从第l个数到第r个数的和。 所用方法和基本原理 前缀和数组的构建&#xff1a; 首先定义了一个方法getPrefixSum来构建前缀和数组。前缀和数组…

BP神经网络支持向量机实现风机故障诊断

BP神经网络&#xff0c;支持向量机等用于风机故障诊断 BP神经网络&#xff0c;支持向量机等用于风机故障诊断/成功算法/bp20111202_FDD.m , 1580 BP神经网络&#xff0c;支持向量机等用于风机故障诊断/成功算法/BP_FDD.m , 6044 BP神经网络&#xff0c;支持向量机等用于风机故…

c++ std::initializer_list

测试代码&#xff1a; int sum(std::initializer_list<int> params) { // 传递若干同类型参数int total 0;for (auto num : params) {total num;}return total; }void testInitializer_list() {// 自定义类支持列表初始化class Demo {public:Demo(std::initializer_li…

Python 数据分析与机器学习入门 (五):Matplotlib 数据可视化基础

引言&#xff1a;为何可视化至关重要&#xff1f; 俗话说&#xff0c;“一图胜千言”。在数据分析领域&#xff0c;这句话尤其正确。原始的数据表格和统计摘要虽然精确&#xff0c;但往往难以揭示数据中隐藏的模式、趋势、异常值和关系。数据可视化通过将数据转换成图形&#…

AI基础1--线性代数(TODO)

1 前言 关于矩阵的运算&#xff0c;其实之前写过一篇&#xff1a;算法矩阵提速原理_矩阵分块计算速度会更快嘛-CSDN博客 还是那句话&#xff0c;计算机懂个毛的高等数学。只是矩阵运算的并行性和结构化特点与 SIMD/GPU 的执行模型非常一致。在实际硬件实现中&#xff0c;许多矩…

如何让宿主机完全看不到Wi-Fi?虚拟机独立联网隐匿上网实战!

“如何让宿主机完全看不到Wi-Fi&#xff1f;虚拟机独立联网隐匿上网实战&#xff01;” 一、前言 在某些特定环境&#xff08;如企业办公或信息安全测试&#xff09;中&#xff0c;我们可能有这样的需求&#xff1a; 让宿主机无法识别或使用某个USB网络设备&#xff0c;但虚拟…

Excel基础操作知识笔记

​ 学习视频链接&#xff1a; ​​​​​​【公开课】Excel基础大全&#xff08;1-66集&#xff09;【超高清版】_哔哩哔哩_bilibili 深圳则秀教育官方账号的个人空间-深圳则秀教育官方账号个人主页-哔哩哔哩视频 Excel技巧零基础入门公开课小白&#xff08;Excel表格制作|Exc…

【2025/06/30】GitHub 今日热门项目

GitHub 今日热门项目 &#x1f680; 每日精选优质开源项目 | 发现优质开源项目&#xff0c;跟上技术发展趋势 &#x1f4cb; 报告概览 &#x1f4ca; 统计项&#x1f4c8; 数值&#x1f4dd; 说明&#x1f4c5; 报告日期2025-06-30 (周一)GitHub Trending 每日快照&#x1f55…

Oracle 进阶语法实战:从多维分析到数据清洗的深度应用​(第四课)

在《Oracle 树形统计再进阶》(第三课)基础上&#xff0c;我们跳出传统 SQL 聚合框架&#xff0c;探索Oracle 特有的高级语法特性&#xff0c;包括多维分析神器MODEL子句、数据清洗利器正则表达式、PL/SQL 存储过程优化&#xff0c;以及基于执行计划的查询调优技巧。这些技术能解…

SpringBoot -- 自动配置原理

SpringBoot 自动配置原理 基础知识 Bean扫描 我们在学习 Spring 的时候&#xff0c;如果要把标注一下注解的类扫描进 IOC 容器 Controller&#xff0c;Service&#xff0c;Mapper&#xff0c;是需要通过一下两种方式实现的&#xff0c;但是我们在 SpringBoot 工程中并没有编写…

Kubernetes从入门到精通-服务发现Service

一、为什么需要 Service&#xff1f; Pod 的动态性&#xff1a; Pod 是 Kubernetes 调度的基本单位。它们可能因为故障、滚动更新、扩缩容等原因随时被创建或销毁。 Pod IP 的不稳定性&#xff1a; 每个 Pod 都有自己的 IP 地址&#xff0c;但当 Pod 重建时&#xff0c;IP 地址…

Milvus 资源调度系统的核心部分:「查询节点」「资源组」「数据库」

Milvus 的资源管理分为三层&#xff1a;查询节点、资源组和 数据库。 查询节点&#xff1a;处理查询任务的组件。它在物理机或容器&#xff08;如 Kubernetes 中的 pod&#xff09;上运行。 资源组&#xff1a;查询节点的集合&#xff0c;充当逻辑组件&#xff08;数据库和 C…

我的第一个开源项目:用Python搭建轻量级静态网页服务器—— 零基础也能实现的Web开发初体验

一、为什么选择静态服务器&#xff1f; 极简高效&#xff1a;无需数据库或复杂后端逻辑&#xff0c;适合展示简历、作品集等静态内容 学习曲线平缓&#xff1a;是理解HTTP协议和Web服务原理的最佳入门方式 资源消耗低&#xff1a;单文件Python脚本即可运行&#xff0c;内存占…

github 图床使用免费CDN加速(jsdelivr)

github做图床大部分人都知道&#xff0c;但是国内访问速度不稳定&#xff0c;所以使用jsdelivr加速。 jsdelivr是什么呢&#xff1f;它是一个免费、快速和可信赖的CDN加速服务&#xff0c;直接集成在github中的&#xff0c;无需额外操作即可使用。 本文分两部份&#xff0c;最…

lte高阶调制和AMC

文章目录 LTE高阶调制AMC LTE高阶调制 首先什么是调制?调制是把通信系统中的基带信号&#xff08;低频&#xff09;转化成适合信道传输的高频信号的过程。 波长&#xff08;λ&#xff09;与频率&#xff08;f&#xff09; 基本关系&#xff1a; λc/f&#xff0c;λc/f&…

shardingsphere5.2.1与SpringBoot3.X的版本冲突问题

1.先说一下我的版本配置与遇到的问题 问题产生的依赖和版本&#xff1a; 主要依赖依赖版本jdk17SpringBoot 3.3.13shardingsphere-jdbc 5.2.1 问题产生的原因&#xff1a; 主要就是shardingsphere-jdbc 与SpringBoot版本冲突&#xff0c;因为Spring Boot 需要 SnakeYAML 库来解…

FPGA控制88E1512 PHY芯片完成网络通信

一、88E1512分析 本文不对88E1512进行详细解析&#xff0c;仅对调试过程中重点使用的几个寄存器进行说明。 1.1 MDIO时序分析 根据手册&#xff0c;MDIO时序中&#xff0c;mdc时钟最高为12Mhz。占空比和建立保持时间要求可以观察上述表格。 MDIO的读数据时序图如下&#xff1a…

Ai大模型 - ocr图像识别形成结构化数据(pp-ocr+nlp结合) 以及训练微调实现方案(初稿)

全局目录,一步到位 功能流程第一阶段 基于现有条件进行 调研,测试与评估1.1 ocr深度学习模型 pp-ocr1.2 nlp结构化模型1.3 硬件要求: 第二阶段 模型训练微调2.1 更换ocr-GPU模型, 下载相关环境2.2 nlp模型 语义训练2.3 最低硬件要求:2.4 样本数据: (重点)2.5 进一步增强模型能力…

【Linux】软硬链接,动静态库

目录 一、认识一下常用指令 1、建立一个软链接 2、建立一个硬链接 3、删除文件的第二种方式&#xff1a;删除链接unlink指令 二、什么是硬链接&#xff1f; 三、软硬链接的原理&#xff1a; 四、应用场景 1、建立一个软链接可以快速在一个比较深的路径中找到目标文件进行…