Android14音頻子系統-ASoC-ALSA之DAPM電源管理子系統

文章目錄

      • 概述
      • 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、建立一個軟鏈接可以快速在一個比較深的路徑中找到目標文件進行…