总结: Android Audio不简单呀,一个人摸索入门不容易的,研究了一段时间,感觉还不是很懂,但以下的知识对入门还是有帮助的。

Audio架构中的名词

FE(Front End)

提供pcm的设备信息,将数据从用户空间传输到音频后端,以及从音频后端捕获录制等
vendor/qcom/opensource/audio-hal/primary-hal/msm8974/platform.c
platform_init中根据声卡的名称"bengal-qrd-snd-card"获取得知配置platform_info对应的文件
PLATFORM_INFO_XML_PATH_QRD_NAME,audio_platform_info_qrd.xml,那这个文件是用来干嘛的呢?

其一就是包含pcm_ids,pcm就是我们这里提到的音频前端FE,访问pcm设备时需要知道设备的id(/dev/snd/pcmC0D3P /dev/snd/pcmC0D17c\ C0: card0 D3/D17: device id c:capture p:playbac),不同的平台上可能出现差异,所以Framework就通过usecase来指定pcm设备,pcm_device_table用于管理usecase与pcm_ids的map关系,最终通过配置文件来修改以实现map关系的管理

pcm_device_table[USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE,        //PCM_PLAYBACKLOWLATENCY_PCM_DEVICE},    //PCM_CAPTURE<pcm_ids><usecase name="USECASE_AUDIO_PLAYBACK_LOW_LATENCY" type="out" id="9"/>

vendor/qcom/opensource/audio-hal/primary-hal/hal/audio_hw.c中包含use_case_table,
常见的就是low_latency(按键音等低延时)、deep_buffer(音乐等)、compress_offload(mp3、flac、acc等不需要软件解码,送入ADSP进行解码)、record(录音)、voice_call(语音通话)、voip_call(网络通话)等等

BE(Back End)

执行音频路由任务,连接Audio Device(Mic Speaker Codec等),ADSP 处于音频前端和音频后端之间
audio_platform_info_qrd.xml中包含的其二backend_names,audio device与interface的对应关系,使用QCAT Tool显示RX Devices,需要做如下修改,因为并非使用高通的WSA

- <device name="SND_DEVICE_OUT_SPEAKER" interface="WSA_CDC_DMA_RX_0"/>
+ <device name="SND_DEVICE_OUT_SPEAKER" interface="SEC_MI2S_RX"/>

ACDB(Audio Calibration Database)

audio_platform_info_qrd.xml中包含的其三acdb_ids,用于给每个音频设备在DSP 路径上的配置,或者可以说是DSP的配置文件

APR(Asynchronous Packet Router)

APR是基于SMD(Share memory Driver,提供通道让不同设备数据交互)和SMMU(System Memory Management Unit, 如果外部设备有一段可以访问的内存,那么可以直接通过smmu把这段内存映射给cpu,cpu可以直接去访问这段内存)的一种IPC 通信方式,用于Ap与QDSP通信
ADSP.VT.5.4.1/adsp_proc/apr - vendor/qcom/opensource/audio-kernel/ipc/apr.c

DAI(Digital Audio Interface)

一个machine当然要包含自己内部的音频控制接口单元–cpu_dai和外部音频设备通信协议转换接口单元–codec_dai

I2S(Inter-IC Sound)

SCLK串行时钟、WS声道选择线、Tx用于传输音频数据、Rx用于接收音频数据

PCM(Pulse Code Modulation)

BCLK位时钟,依据采样率变化、SYNC同步信号、Tx传输音频数据、Rx接受音频数据

SWR(SoundWire)

SWR由MIPI联盟推出的音频接口,有主从的两线制(clock,data)总线,Mipi SoundWire Spec 学习笔记

SLIM (Serial Low Power Interchip Media Bus)

SLIM由MIPI联盟开发的音频接口,同SWR类似,但支持多个Data线,可用于与aux(不适用dai) codec通信

ALSA(Advanced Linux Sound Architecture)


图片来自于ALSA 架构,推荐博客高通音频架构(三),ASOC(ALSA Systen on chip)主要提供一套独立的嵌入式系统设备管理框架。
(1) ALSA Library : 应用通过ALSA Library于内核空间进行交互,Android中使用tinyalsa,支持PCM和Control接口
(2) ALSA Layer: 处于内核空间,向上提供接口,如PCM、Control、Mixer,向下驱动硬件设备 I2S、Codec、Machine
(3) ASOC Machine:作为Codec和Platform的载体,将硬件设备关联起来,连接codec_dai与cpu_dai,构成硬件通路
(4) ASOC Platform: 可以简单理解为就是Soc平台,负责Soc侧的音频传输,音频接口配置与控制等
(5) ASOC Codec: 控制音频解码,完成音频采集和播放过程中模拟与数字间的转换,aux代表未使用dai的codec,
(6) DAPM: Dynamic Audio Power Management 在任何时候都能工作在最小功耗下,控制内部widget通路上下点

ADSP的加载过程

高通子系统subsystem基础知识

kernel/msm-4.19/drivers/soc/qcom/subsys-pil-tz.c (generic peripheral image loader (PIL) driver)
在devictree中可以发现有 qcom,firmware-name = adsp cdsp venus modem ipa_fws slpi
其实在PIL驱动并没有发生image加载过程,真正的加载有对应的子模块处理中,adsp就是在adsp-loader.c中

vendor/qcom/opensource/audio-kernel/dsp/adsp-loader.c
通过init.qcom.rc启动时 write /sys/kernel/boot_adsp/boot 1 ,之后就会subsystem_get(“adsp”)获取并加载;
其他例如slpi, 要看高通不同的平台子模块的组成,有些PIL是没有load slpi模块,sensor也是adsp中,
所以启动的节点会出现/sys/kernel/boot_adsp/ 以及 /sys/kernle/boot_slpi/两种情况
kernel/msm-4.19/drivers/sensors/sensors_ssc.c subsystem_get_with_fwname(“slpi”)获取并加载,如果有slpi的话

子系统的通信方式有三种(1)smen (2)spss(Secure-Processor-SubSystem) (3)spi,之前分析过adsp与glink以及ssc的通信都是基于smem
之前分析的博客  Android Qcom USB Driver学习(九)        Android Qcom Sensor架构学习

AMSS contents.xml和build.py可以得知各子系统的镜像 common/build/ufs/bin/asic/pil_split_bins中打包成NON-HLOS.bin或者是SDA.bin
PILDxe会去解析uefipil.cfg,其中就包含modem镜像的组成adsp slpi cdsp modem spss等几个子系统

Android推动GKI,能有更多的可动态加载的内核模块 (DLKM),老点的Android版本audio也一直在执行DLKM的,以adsp-loader为例
vendor/qcom/opensource/audio-kernel/dsp/Kbuild Android.mk 将adsp-loader.c 模块化为audio_adsp_loader.ko
在out目录下有/dlkm/lib/modules/audio_adsp_loader.ko 和 /vendor/lib/modules/audio_adsp_loader.ko (Android Kernel ko)

vendor/lib/modules # modinfo audio_adsp_loader.ko
filename:       audio_adsp_loader.ko
license:        GPL v2
description:    ADSP Loader module
alias:          of:N*T*Cqcom,adsp-loader
alias:          of:N*T*Cqcom,adsp-loaderC*
depends:
name:           adsp_loader_dlkm
vermagic:       4.19.157+ SMP preempt mod_unload modversions aarch64uart boot log:
subsys-pil-tz ab00000.qcom,lpass: Falling back to syfs fallback for: adsp.mdt
subsys-pil-tz ab00000.qcom,lpass: Falling back to syfs fallback for: adsp.b02
subsys-pil-tz ab00000.qcom,lpass: Subsystem error monitoring/handling services are up
adsprpc: fastrpc_restart_notifier_cb: adsp subsystem is up
adsprpc: fastrpc_rpmsg_probe: opened rpmsg channel for adsp

之前遇到过Google服务器导致ADSP中的SSC编译有问题,因为SSC用到了谷歌的Protocol,ADSP子系统启动有问题会开不了机,
看编译出的镜像size变小也可以得知有问题,暂时还没遇到过不加载导致Audio功能有问题的情况。
那高通的ADSP当作为audio究竟是如何工作?高通msm8996平台的ASOC音频路径分析

高通平台上fe dai link是链接cpu和adsp的,并非使用的i2s等硬件接口,而是使用共享内存的方式,在数据传输过程中没有实际的操作,platform直接将音频数据最终写入adsp,它描述的并非一个物理设备,这段是从博主那边看来的,自己还需看代码体会一下,挖坑。

Audio设备树配置

[Linux Audio Driver] 高通平台MI2S总线配置
可见devicetree中的asoc-cpu asoc-platform包含了所有的dai接口,这里使用的MI2S1对应Secondary
硬件接口:LPI_MI2S1_CLK LPI_MI2S1_WS LPI_MI2S1_DATA0 LPI_MI2S1_DATA1
注意这里的gpio与lpg gpio的对应关系,硬件上的gpio102-gpio105对应lpg goui10-13

qcom,mi2s-audio-intf = <1>;
qcom,sec-mi2s-gpios = <&cdc_sec_mi2s_gpios>; &q6core {cdc_sec_mi2s_gpios: msm_cdc_pinctrl_sec {compatible = "qcom,msm-cdc-pinctrl";pinctrl-names = "aud_active", "aud_sleep";pinctrl-0 = <&lpi_i2s2_sck_active &lpi_i2s2_ws_active&lpi_i2s2_sd0_active &lpi_i2s2_sd1_active>;pinctrl-1 = <&lpi_i2s2_sck_sleep &lpi_i2s2_ws_sleep&lpi_i2s2_sd0_sleep &lpi_i2s2_sd1_sleep>;qcom,lpi-gpios;};
};

耳机检测相关 0:NC(normally-closed) 1:NO(normally-open),高通音频MBHC耳机系统软件相关配置归纳
根据硬件原理图,确认HS-DET和HPG-L引脚的连接状态,未插入时断开的则时NO,未插入时连接的则是NC

qcom,msm-mbhc-hphl-swh = <1>;
qcom,msm-mbhc-gnd-swh = <1>;

DMIC0 DCMI1数字Mic输入接口对应硬件DMIC1_CLK DMIC1_DATA DMIC2_CLK DMIC2_DATA, micbias麦克风工作电压

qcom,cdc-dmic01-gpios = <&cdc_dmic01_gpios>;
qcom,cdc-dmic23-gpios = <&cdc_dmic23_gpios>;qcom,cdc-micbias1-mv = <1800>
qcom,cdc-micbias2-mv = <1800>
qcom,cdc-micbias3-mv = <1800>

如果是dai的codec则添加在这里asoc-codec和asoc-codec-names,vendor/qcom/opensource/audio-kernel/asoc/bengal.c,populate_snd_card_dailinks将所有的snd_soc_dai_link保持在snd_soc_card中,在msm_populate_dai_link_component_of_node, 通过上面记录的num_links,逐个通过platform_name去找platform_of_node,对应devicetree中的asoc-platform-names和asoc-platform,通过cpu_dai_name去找cpu_of_node对应devicetree中的asoc-cpu-names和asoc-cpu,codec同理对应asoc-codec和asoc-codec-names,上述的内容都包含在sns_soc_dai_link中,硬件上我们得知使用的Secondary MI2S接的PA(PA也当作codec来看),所以需要配置dai_link,在max98390 snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98390, max98390_dai, ARRAY_SIZE(max98390_dai));
分别传入snd_soc_codec_driver以及snd_soc_dai_driver,devm_snd_soc_register_card中调用soc_probe_link_dais来probe codec,当然这里并非作为i2c器件的i2c_driver的max98390_i2c_probe,而是作为alsa component的snd_soc_component_driver或者snd_soc_codec_driver(这两种其实都是抽象component示例最终挂在到全局链表component_list)的max98390_probe

asoc-codec  = <&stub_codec>, <&bolero>;
asoc-codec-names = "msm-stub-codec.1", "bolero_codec";static struct snd_soc_dai_link msm_mi2s_be_dai_links[MI2S_DAI_LINK_MAX] = {{.name = LPASS_BE_SEC_MI2S_RX,.stream_name = "Secondary MI2S Playback",.cpu_dai_name = "msm-dai-q6-mi2s.1",.platform_name = "msm-pcm-routing",
-       .codec_name = "msm-stub-codec.1",
-       .codec_dai_name = "msm-stub-rx",
+      .codec_name     = "max98390.0-0038",
+      .codec_dai_name = "max98390-aif1",   .no_pcm = 1,.dpcm_playback = 1,.id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX,.be_hw_params_fixup = msm_be_hw_params_fixup,.ops = &msm_mi2s_be_ops,.ignore_suspend = 1,.ignore_pmdown_time = 1,},static struct snd_soc_dai_driver max98390_dai[] = {{.name = "max98390-aif1",.playback = {.stream_name = "HiFi Playback",.channels_min = 1,.channels_max = 2,.rates = MAX98390_RATES,.formats = MAX98390_FORMATS,},.capture = {.stream_name = "HiFi Capture",.channels_min = 1,.channels_max = 2,.rates = MAX98390_RATES,.formats = MAX98390_FORMATS,},.ops = &max98390_dai_ops,}
};

高通各平台之间也是差异的,snd_soc_dai_link结构体中仅codecs为snd_soc_dai_link_component,有些是cpu codecs platforms都是,所以在snd_soc_dai_link中使用了SND_SOC_DAILINK_REG,对应到SND_SOC_DAILINK_DEF/SND_SOC_DAILINK_DEFS定义的dai links kernel/msm-5.4/techpack/audio/asoc/msm-dailink.h

SND_SOC_DAILINK_DEFS(sec_mi2s_rx,DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.2")),
-   DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-rx")),
+   DAILINK_COMP_ARRAY(COMP_CODEC("max98390.1-0038", "max98390-aif1")),DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing")));static struct snd_soc_dai_link msm_mi2s_be_dai_links[]
{.name = LPASS_BE_SEC_MI2S_RX,.stream_name = "Secondary MI2S Playback",.no_pcm = 1,.dpcm_playback = 1,.id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX,.be_hw_params_fixup = msm_be_hw_params_fixup,.ops = &msm_mi2s_be_ops,.ignore_suspend = 1,.ignore_pmdown_time = 1,SND_SOC_DAILINK_REG(sec_mi2s_rx),
},

高通的PA相关的属性,0:没使用WSA PA, 1: 单声道 2:双声道,项目上没有用到置为0就不研究了

qcom,wsa-max-devs = <0>;
qcom,wsa-devs = <0>;
qcom,wsa-aux-dev-prefix = "SpkrMono";

硬件接口 SWR_TX_CLK SWR_TX_DATA0 SWR_TX_DATA1 SWR_RX_CLK SWR_RX_DATA0 SWR_TX_DATA1 对应LPI_GPIO 0-5

qcom,codec-max-aux-devs = <1>;
qcom,codec-aux-devs = <&wcd937x_codec>;

snd_soc_of_parse_audio_routing解析存入of_dapm_routes:snd_soc_dapm_route,上述devicetree对应routes.sink routes.source(kcontrol为空是dirct直连),通过snd_soc_dapm_add_routes完成音频路径的注册,作用是将系统中注册的各种wideget:snd_soc_dapm_widget连接在一起

qcom,audio-routing =                  "TX DMIC0", "MIC BIAS1",          "MIC BIAS1", "Digital Mic0",

widget是通过snd_soc_dapm_new_controls注册的,四种widget的定义在include/sound/soc-dapm.h
(1)platform domain:SND_SOC_DAPM_MIC        (2)path domain:SND_SOC_DAPM_MICBIAS_E
(3)Codec domain:SND_SOC_DAPM_VMID        (4)stream domain:SND_SOC_DAPM_ADC_E
项目中使用wcd937x没有DMIC输入,提供MICBIAS,DMIC来自bolero

bolero_codec
/hon4290-android12-vendor/vendor/qcom/opensource/audio-kernel/asoc/bengal.c
static const struct snd_soc_dapm_widget msm_int_dapm_widgets[] = {SND_SOC_DAPM_MIC("Digital Mic0", msm_dmic_event),SND_SOC_DAPM_MIC("Digital Mic1", msm_dmic_event),
}; vendor/qcom/opensource/audio-kernel/asoc/codecs/bolero/tx-macro.c
static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_common[]SND_SOC_DAPM_ADC_E("TX DMIC0", NULL, SND_SOC_NOPM, 0, 0,tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), wcd937x_codec
vendor/qcom/opensource/audio-kernel/asoc/codecs/wcd937x/wcd937x.c
static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] =/* mixer widgets adc1_switch kcontrol SOC_DAPM_SINGLE*/SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0, adc1_switch, ARRAY_SIZE(adc1_switch),wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),/* micbias widgets*/SND_SOC_DAPM_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0, wcd937x_codec_enable_micbias,SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),

我们知道widget也是分起点终点的,不同的kernel版本会带来变化,SND_SOC_DAPM_MICBIAS_E(不能定义在route的起点和重点)变为SND_SOC_DAPM_SUPPLY录音时mic bias 无法自动打开问题,所以audio-routing的配置需要修改为

qcom,audio-routing ="TX DMIC0", "Digital Mic0","TX DMIC0", "MIC BIAS1",SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, 0, 0, wcd938x_codec_enable_micbias,SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),

高通hal还有route相关的配置configs/bengal/mixer_paths_qrd.xml,在 bring up的时候,先使用tinymix/tinycap/tinyplay验证通路,再进行修改

tinymix "SEC_MI2S_RX Audio Mixer MultiMedia1" "1"
tinymix "SEC_MI2S_RX SampleRate" "KHZ_48"
tinymix "SEC_MI2S_RX Format" "S16_LE"
tinymix "SEC_MI2S_RX Channels" "Two"
tinyplay /data/test.wavtinymix "MultiMedia1 Mixer TX_CDC_DMA_TX_3" "1"
tinymix "TX_CDC_DMA_TX_3 Channels" "One"
tinymix "TX_AIF1_CAP Mixer DEC0" "1"
tinymix "TX DMIC MUX0" "DMIC0"
tinycap /data/test.wav
<path name="deep-buffer-playback">
-   <ctl name="RX_CDC_DMA_RX_1 Audio Mixer MultiMedia1" value="1" />
+   <ctl name="SEC_MI2S_RX Audio Mixer MultiMedia1" value="1" />
</path><path name="speaker">
-   <ctl name="RX_CDC_DMA_RX_1 Channels" value="One" />
-   <ctl name="RX_MACRO RX2 MUX" value="AIF2_PB" />
-   <ctl name="RX INT2_1 MIX1 INP0" value="RX2" />
-   <ctl name="AUX_RDAC Switch" value="1" />
-   <ctl name="SpkrMono WSA_RDAC" value="Switch" />
+   <ctl name="SEC_MI2S_RX SampleRate" value="KHZ_48"/>
+   <ctl name="SEC_MI2S_RX Format" value="S16_LE" />
+   <ctl name="SEC_MI2S_RX Channels" value="Two" />
</path><path name="dmic1"><ctl name="TX_CDC_DMA_TX_3 Channels" value="One" /><ctl name="TX_AIF1_CAP Mixer DEC0" value="1" /><ctl name="TX DMIC MUX0" value="DMIC0" />
</path>static const struct snd_kcontrol_new msm_mi2s_snd_controls[] = {SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate,mi2s_rx_sample_rate_get, mi2s_rx_sample_rate_put),SOC_ENUM_EXT("SEC_MI2S_RX Format", mi2s_rx_format,msm_mi2s_rx_format_get, msm_mi2s_rx_format_put),SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs,msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),static struct dev_config mi2s_rx_cfg[] = {[SEC_MI2S]  = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
};

最后再通过 tinymix “SEC_MI2S_RX Audio Mixer MultiMedia1” “1”理解一下playback的route

PCM->MultiMedia1 Playback->MM_DL1 -> MultiMedia1 ⊕ SEC_MI2S_RX Audio Mixer->SEC_MI2S_RX->Secondary MI2S Playback

MM_DL1:SND_SOC_DAPM_AIF_IN对应一个数字音频输入接口, SEC_MI2S_RX:SND_SOC_DAPM_AIF_OUT对应一个数字音频输出接口;dai widget和stream widget是通过stream name来匹配的,通过aif_name来指定,MultiMedia1 Playback(stream_name)连接FE DAI, Secondary MI2S Playback(stream_name)连接BE DAI;SEC_MI2S_RX Audio Mixer:SND_SOC_DAPM_MIXER是个mixer(可用于多个MultiMediax混合),MultiMedia1作为kcontrol,连接Source和Sink

Android Qcom Audio架构学习相关推荐

  1. Android Qcom Sensor架构学习

    Android Sensor Brief Flow Android Sensor Specific Flow ADSP SSC ADSP.VT.5.4.1/adsp_proc/ssc_api/pb/ ...

  2. Android Qcom USB Driver学习(一)

    该系列文章总目录链接与各部分简介: Android Qcom USB Driver学习(零) USB接口类型 Android终端上常用的USB接口:TypeC(现在的主流),MicroB(以前的设备) ...

  3. Android Qcom USB Driver学习(二)

    该系列文章总目录链接与各部分简介: Android Qcom USB Driver学习(零) BC v1.2充电规范 Battery Charging Specification USB port 如 ...

  4. Android Qcom USB Driver学习(八)

    该系列文章总目录链接与各部分简介: Android Qcom USB Driver学习(零) 因为要看usb charging的问题,所以需要补充一下battery的相关知识,算是入门吧 BAT SC ...

  5. Android Qcom USB Driver学习(六)

    该系列文章总目录链接与各部分简介: Android Qcom USB Driver学习(零) 眼图基础知识与详解 10分钟教会你看眼图 USB2.0 HUB眼图调试经验总结 一篇文章教你如何全面了解眼 ...

  6. Android Qcom USB Driver学习(四)

    该系列文章总目录链接与各部分简介: Android Qcom USB Driver学习(零) VID/PID识别USB设备 CDC-ACM驱动介绍   CDC-ACM(Communication De ...

  7. Android Qcom USB Driver学习(九)

    该系列文章总目录链接与各部分简介: Android Qcom USB Driver学习(零) 高通的某些平台将电源管理移植到了ADSP Subsystem, 分析一下其中比较关心的部分 Archite ...

  8. Android 音频(Audio)架构

    一.概述 Android 的音频硬件抽象层 (HAL) 可将 android.media 中特定于音频的较高级别的框架 API 连接到底层音频驱动程序和硬件.本部分介绍了有关提升性能的实现说明和提示. ...

  9. Android Qcom lcd display 学习(3)

    Android Display整体架构 Android 图形组件 Android Graphic analyze display:高通display overview MDP(mobile displ ...

最新文章

  1. 未转变者rust服务器推荐,如果steam只留一个游戏,你会选择吃鸡还是CSGO
  2. redis便捷启动,shell启动redis
  3. 大数据治理工程师_大数据治理关键技术解析(转自EAWorld)
  4. scala创建并使用Enumerations
  5. 揭秘自动驾驶纯视觉算法,探索自动驾驶的未来
  6. Struts2中 radio标签的详细使用方法
  7. 1、CSS 盒子模型,2、边框样式,3、CSS 轮廓(outline),
  8. 拓端tecdat|R语言markov switching model马尔可夫转换分析研究水资源
  9. 【JavaWeb】【笔记】《JavaWeb入门经典》 第15章 Struts框架
  10. oracle数据库下载地址
  11. mysql load data 更新_mysql 用load data 导入数据时,数据被截断问题 | 学步园
  12. 计算机毕业设计ssm龙腾集团员工信息管理系统39r5l系统+程序+源码+lw+远程部署
  13. tkinter自定义下拉多选框
  14. 异构网络中基于元图的推荐——FMG
  15. Docker常用命令-全流程
  16. 离线计算系统之HDFS Java API
  17. 美国商会呼吁对ICO进行澄清
  18. C#读取Excel数据的几种方式(包含大量数据读取)
  19. Python语言的应用领域主要有哪些?
  20. WS-Discover详解

热门文章

  1. ORB 特征提取算法(理论篇)
  2. android status_bar_height动态调整,Android沉浸状态栏(StatusBar)兼容方案
  3. Qt TextEdit 使用
  4. android设置移动联通电信wap接入点
  5. Linux之分区【详细总结】
  6. 电脑关闭打印机与无线服务器,电脑怎么设置wifi连接打印机
  7. 如何用一简单的CSS制作响应式HTML网页
  8. XShell简介与安装
  9. Hive开发人员如何提升?
  10. 云客网解析网站内容采集的注意要点