RK3588 Android12音频驱动分析全网最全

news2024/11/16 17:41:25

最近没有搞音频相关的了,在搞BMS, 把之前的经验总结一下。

一、先看一下Android 12音频总架构

从这张图可以看到音频数据流一共经过了3个用户空间层的进程,然后才流到kernel驱动层。Android版本越高,通用性越高,耦合性越低,但是带来的资源开销也越大,延时也越大。本文主要介绍驱动层相关的知识。详细流程可以参考:Android12 AudioFlinger 读写PCM数据-CSDN博客; Android12 Native C++ 层AudioRecord录音AudioTrack播放_android c++ 录制音频-CSDN博客

Android 12 AudioFlinger 分析(RK3588)-CSDN博客

二、ASoC音频驱动框架

2.1 ASoC简介:

ASoC的由来
     ASoC全程ALSA System on Chip ,是基于标准ALSA驱动层封装而来,可以更好地支持嵌入式处理器和移动设备中音频驱动功能。简单的理解就是ASoC把alsa根据硬件抽象成三个独立的驱动模块。通过snd_soc_dai_link 结构体来连接到一起组成一个声卡card暴露给用户空间。
ASoc音频系统可以被划分为:板载硬件(Machine)、Soc(Platform)、Codec三大部分,如下图所示:

经典常用DAI(digital audio interface)接口有I2S,PDM,TDM, SPDIF,UART,USB。 控制接口大部分是I2C,SPI。
Platform的作用:
Platform端包含I2S等数字接口驱动,和dma pcm驱动这个是直接调用linux dma功能api,具体dma驱动是另外一套驱动框架。主要作用是按软件设定的格式收发pcm数据。soc传输pcm数据软件层流程(更详细可参考tinyalsa播放录音调用流程-CSDN博客):

在platform驱动初始化时会申请dma通道配置相关参数如,源地址,目的地址,数据位宽,burst size,传输方向等。I2S IP一般会有32个sample的FIFO,I2S驱动会设置一个空闲阀值如15,当I2S IP空闲值大于等于15就会通过Req发信号给DMA IP,DMA收到信号后从指定memory地址(软件层的Ring_Buffer)搬运数据到I2S的FIFO,然后返回Ack信号给I2S IP。DMA搬运数据达到一定值就会产生一个中断给CPU,从而运行DMA中断处理函数更新读写指针等相关参数。录音流程则是返过来。
从上面两个图可以看出,对于满负荷运行场景出现断续卡顿问题除了CPU软件层发生XRUN外,芯片各个模块都高负荷运行内部AXI APB AHB总线带宽可能不够,导致DMA不能及时搬运数据,也就是I2S和DMA硬件也会发生XRUN。这种情况从CPU软件层很难有好的解决方法。
Codec的作用:
数模转换,对于playback是将接收到的数字信号转换为模拟信号,对于capture是将采集到的模拟信号转换为数字信号发送出去。对于一些高级的codec还提供了多路音源混音,EQ,DRC,AGC,NR降噪等功能。
Machine的作用:
一款PCBA产品可以有很多个codec的音频接口,cpu端的音频接口,Machine驱动的作用就是通过snd_soc_dai_link 结构体把具体的platform,codec链接在一起组成音频通路,注册成一个声卡card暴露给用户空间。

2.2 具体驱动例子:

下面基于RK3588, kernel-5.10平台举个例子:
Platform端(RK3588),这部分驱动是soc芯片原厂应该做好的:

rk3588-evb1-lp4.dtsi:
i2s0_8ch: i2s@fe470000 {
  compatible = "rockchip,rk3588-i2s-tdm";
  reg = <0x0 0xfe470000 0x0 0x1000>;
  interrupts = <GIC_SPI 180 IRQ_TYPE_LEVEL_HIGH>;
  ......................................
};
rockchip_i2s_tdm.c:
static const struct snd_soc_component_driver rockchip_i2s_tdm_component = {
  .name = DRV_NAME,
};
//这些函数和具体soc有关,操作soc相关寄存器配置i2s,tdm格式
static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = {
  .hw_params = rockchip_i2s_tdm_hw_params,//设置bclk,lrclk,采样位宽通道数
  .set_sysclk = rockchip_i2s_tdm_set_sysclk,// 设置mclk
  .set_fmt = rockchip_i2s_tdm_set_fmt,//设置i2s格式,
  .set_tdm_slot = rockchip_dai_tdm_slot,// 设置tdm通道数
  .trigger = rockchip_i2s_tdm_trigger,// 启动/停止i2s收发数据
};
static int rockchip_i2s_tdm_dai_prepare(struct platform_device *pdev,
           struct snd_soc_dai_driver **soc_dai)
{
   struct snd_soc_dai_driver rockchip_i2s_tdm_dai = {
    .probe = rockchip_i2s_tdm_dai_probe,
    .playback = {
      .stream_name = "Playback",
      .channels_min = 2,
      .channels_max = 16,
      .rates = SNDRV_PCM_RATE_8000_192000,
      .formats = (SNDRV_PCM_FMTBIT_S8 |....),
    },
    .capture = {
    ...........................
    },
    .ops = &rockchip_i2s_tdm_dai_ops,
  };
  *soc_dai = devm_kmemdup(&pdev->dev, &rockchip_i2s_tdm_dai,
         sizeof(rockchip_i2s_tdm_dai), GFP_KERNEL);
   return 0;
}
// 解析rk3588-evb1-lp4.dtsi文件 i2s0_8ch节点填充各个参数然后注册一个snd_soc_dai_driver 
static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
{
   struct device_node *node = pdev->dev.of_node;
   struct device_node *cru_node;
   const struct of_device_id *of_id;
   struct rk_i2s_tdm_dev *i2s_tdm;
   struct snd_soc_dai_driver *soc_dai;
   //playback m2d, 地址为i2s的tx fifo
   i2s_tdm->playback_dma_data.addr = res->start + I2S_TXDR;
   i2s_tdm->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
   i2s_tdm->playback_dma_data.maxburst = 8;
   //capture d2m, 地址为i2s的rx fifo
   i2s_tdm->capture_dma_data.addr = res->start + I2S_RXDR;
   i2s_tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
   i2s_tdm->capture_dma_data.maxburst = 8;
   dev_set_drvdata(&pdev->dev, i2s_tdm);
   // 填充soc_dai各个字段
  ret = rockchip_i2s_tdm_dai_prepare(pdev, &soc_dai);
   // 注册dai接口
  ret = devm_snd_soc_register_component(&pdev->dev,
             &rockchip_i2s_tdm_component,
              soc_dai, 1);
   // 注册dma pcm,会调用到linux dma通用api。
  ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
}

Codec端驱动是芯片原厂提供,产品开发公司适配调试:

rk3588-evb1-lp4.dtsi:
&i2c7 {
  status = "okay";
  es8388: es8388@11 {
    status = "okay";
    compatible = "everest,es8388", "everest,es8323";
    ........................................
  };
};
es8328.c:
// 这些函数操作codec寄存器,设置格式等。i2c接口
static const struct snd_soc_dai_ops es8328_dai_ops = {
  .startup  = es8328_startup,
  .hw_params = es8328_hw_params,
  .mute_stream  = es8328_mute,
  .set_sysclk = es8328_set_sysclk,
  .set_fmt  = es8328_set_dai_fmt,
  .no_capture_mute = 1,
};
// dai接口参数
static struct snd_soc_dai_driver es8328_dai = {
  .name = "es8328-hifi-analog",
  .playback = {
    .stream_name = "Playback",
    .channels_min = 2,
    .channels_max = 2,
    .rates = ES8328_RATES,
    .formats = ES8328_FORMATS,
  },
  .capture = {
  ........................
  },
  .ops = &es8328_dai_ops,
  .symmetric_rates = 1,
};
// 设置各种控制接口
static const struct snd_soc_component_driver es8328_component_driver = {
};
// 解析rk3588-evb1-lp4.dtsi文件 es8388节点填充各个参数然后注册一个snd_soc_dai_driver 
int es8328_probe(struct device *dev, struct regmap *regmap)
{
   return devm_snd_soc_register_component(dev,
      &es8328_component_driver, &es8328_dai, 1);
}

Machine端驱动是产品开发公司实现/调试:

rk3588-evb1-lp4.dtsi:
es8388_sound: es8388-sound {
status = "okay";
compatible = "rockchip,multicodecs-card";
rockchip,card-name = "rockchip-es8388";
rockchip,format = "i2s";
rockchip,mclk-fs = <256>;
//指定platform端那个dai接口
rockchip,cpu = <&i2s0_8ch>;
//指定codec端那个dai接口
rockchip,codec = <&es8388>;
rockchip,audio-routing =
"Headphone", "LOUT1",
..............................
"RINPUT2", "Headset Mic";
};
rockchip_multicodecs.c:
static int rk_multicodecs_probe(struct platform_device *pdev)
{
   struct snd_soc_card *card;
   struct device_node *np = pdev->dev.of_node;
   struct snd_soc_dai_link *link;//该结构体用于绑定platform,codec。
  /*一个声卡包含下面三个comment。
   */
   struct snd_soc_dai_link_component *cpus;
   struct snd_soc_dai_link_component *platforms;
   struct snd_soc_dai_link_component *codecs;
   link = &mc_data->dai_link;
   link->name = "dailink-multicodecs";
   link->stream_name = link->name;
   link->init = rk_dailink_init;
   link->ops = &rk_ops; // snd_soc_ops
   link->cpus = cpus;
   link->platforms = platforms;
   link->num_cpus  = 1;
   link->num_platforms = 1;
   link->ignore_pmdown_time = 1;
   card->dai_link = link; //填充
   card->num_links = 1;
  codecs = devm_kcalloc(&pdev->dev, idx,
          sizeof(*codecs), GFP_KERNEL);
   link->codecs = codecs;
   link->num_codecs = idx;
  idx = 0;
   for (i = 0; i < count; i++) {
     // dts : rockchip,codec = <&es8388>;
    node = of_parse_phandle(np, "rockchip,codec", i);
     if (!node)
       return -ENODEV;
     if (!of_device_is_available(node))
       continue;
    ret = of_parse_phandle_with_fixed_args(np, "rockchip,codec",
                0, i, &args);
     if (ret)
       return ret;
     codecs[idx].of_node = node;
    ret = snd_soc_get_dai_name(&args, &codecs[idx].dai_name);
     if (ret)
       return ret;
    idx++;
  }
   // dts: rockchip,cpu = <&i2s0_8ch>;
   link->cpus->of_node = of_parse_phandle(np, "rockchip,cpu", 0);
   link->platforms->of_node = link->cpus->of_node;
  ret = snd_soc_of_parse_audio_routing(card, "rockchip,audio-routing");
   //注册声卡
  ret = devm_snd_soc_register_card(&pdev->dev, card);
}

到此一个声卡就注册成功了,在用户空间可以看到:

console:/storage/emulated/0 # cat /proc/asound/cards
 0 [rockchipes8388 ]: rockchip-es8388 - rockchip-es8388
                      rockchip-es8388
2.3 详细声卡驱动注册UML流程图:

2.4 RK 额外提供的Combo DAI :

RK 平台支持任意 DAI 的组合使用,重组 DAI 生成 Combo DAI,如下图所示:

示例:将 i2s_8ch_2 和 pdm_8ch 组合成 Combo DAI ,这样用户层看到的是一个声卡节点可以同时读写多个i2s,pdm的数据,避免驱动和进程调度导致的动态延时差。

&i2s_8ch_2 { 
     status = "okay"; 
     rockchip,no-dmaengine; 
 }; 
 &pdm_8ch { 
     status = "okay"; 
     rockchip,no-dmaengine; 
 }; 
     &multi-dais { 
     dais = <&i2s_8ch_2>, <&pdm_8ch>; 
 }; 

三、音频常用协议

3.1 PDM协议(切记虽然叫数字麦克风但是tx发送0x55,rx接收到的并不是0x55)

PDM(Pulse Density Modulation)是一种用数字信号表示模拟信号的调制方法,脉冲的相对密度对应于模拟信号的振幅。同为将模拟量转换为数字量的方法,PCM使用等间隔采样方法,将每次采样的模拟分量幅度表示为N位的数字分量(N = 量化深度),因此PCM方式每次采样的结果都是N bit字长的数据。PDM则使用远高于PCM采样率的时钟采样调制模拟分量,只有1位输出,要么为0,要么为1。因此通过PDM方式表示的数字音频也被称为Oversampled 1-bit Audio。相比PDM一连串的0和1,PCM的量化结果更为直观简单。
在以PDM方式作为模数转换方法的应用接收端,需要用到抽取滤波器(Decimation Filter)将密密麻麻的0和1代表的密度分量转换为幅值分量,而PCM方式得到的就已经是幅值相关的数字分量。如下图所示意为通过PDM方式数字化的正弦波。

红色是模拟信号的正玄波,蓝色柱条代表1,白色代表0。可以看到正玄波的peaks蓝色密度最大也就是1最多代表模拟信号幅度最大。正玄波的troughs蓝色最少表示幅度最小。
PCM方式的逻辑更加简单,但需要用到数据时钟,采样时钟和数据信号三根信号线;PDM方式的逻辑相对复杂,但它只需要两根信号线,即时钟和数据。通过PDM接口方式,传输双声道数据只要用到两根信号线。如下图示为Maxim的Class-D类型功放MAX98358对PDM接口时序的要求,可以看到它在PDM_CLK的上升沿采样左声道数据,在PDM_CLK下降沿采样右声道数据。

PDM MIC sph0690lm4h-1 时序:

3.2 TDM协议

时分复用技术(time-division multiplexing, TDM)是将不同的信号相互交织在不同的时间段内,沿着同一个信道传输;在接收端再用某种方法,将各个时间段内的信号提取出来还原成原始信号的通信技术。TDM是在时间上将信道划分在不同的时隙,在不同的时隙上间插不同的脉冲信号,依次来实现时域上多路信号的复用。假设每个输入的数据比特率是9. 6kbit / s ,线路的最大比特率为76. 8 kbit / s ,则可传输8 路信号。这种技术可以在同一个信道上传输多路信号。在音频领域常用于传输多通道pcm数据。

典型TDM格式时序图:


一般SOC端每个IP有4跟线,可以配置成I2S,PCM,TDM格式,具体时序查看datasheet为准。
时钟(BCLK)频率的计算:
FSYNC/LRCK的频率等于音频的采样率(例如44.1 kHz,48 kHz等)。Frame每次传输包括所有声道的数据。PCM采样音频数据量化深度一般在16-32bit(最常见为16/24bit)。那么对于8声道,每个声道32bit音频数据,采样率48kHz的系统,TDM的系统时钟速率为:8 × 32 × 48kHz = 12.288 MHz,在一些Datasheet中可以见到TDM128/TDM256/TDM384/TDM512等,数字的含义为单个TDM数据帧包含数据的比特数(即帧长)。如上例8声道(Channels)32bit的音频数据,亦称为TDM256(=8*32)。TDM系统时钟速率就可以简单地用采样率乘以TDM帧长计算得出。相同的例子,TDM系统时钟速率:48kHz × 256 = 12.288 MHz。
 

3.3 PCM协议

PCM (Pulse Code Modulation) 脉冲编码调制是将模拟信号数字化的方法。原理是用一个固定的频率对模拟信号进行采样,采样后的信号在波形上看就像一串连续的幅值不一的脉冲(脉搏似的短暂起伏的电冲击),把这些脉冲的幅值按一定精度进行量化,这些量化后的数值被连续的输出、传输、处理或记录到存储介质中,所有这些组成了数字音频的产生过程(抽样、量化、编码三个过程)。下图为4 bit 采样深度的PCM数据量化示意图。

典型PCM格式时序图:

PCM格式分为长短帧,MODE A, MODE B格式,具体情况看相应datasheet手册。
长帧同步,FSYNC脉冲宽度等于1个Slot的长度。
短帧同步,FSYNC脉冲宽度等于1个BCLK周期长度。
Mode A: 数据在FSYNC有效后,BCLK的第2个上升沿有效(one clock delay)。
Mode B: 数据在FSYNC有效后,BCLK的第1个上升沿有效(no delay)。
3.3 I2S协议

I2S格式是pcm的特例,平时用的最多,用于传输两声道场景。
典型I2S格式时序图:

3.3 总结:

PCM音频接口传输单声道数据常用于通话场景其接口名称为PCM,如蓝牙模块通话时一般都是pcm接口,A2DP音频编码数据则是走串口。
常规产品一般只需要双声道则使用I2S接口。
对于需要多声道的产品则使用TDM传输多声道的数据。
PDM常用于数字麦(DM),也有用于播放的。
很多芯片带的"i2s"数字音频接口可以支持配置成pcm,i2s,tdm三种格式中的一种用的是同样的物理引脚。
对于一些高级的codec/dsp 一般都会有配套上位机和调试板,上位机有ui图形化配置很方便,调试的时候在soc端注册声卡成功后,把codec的i2c接口连接到调试板上,然后在上位机进行调试,这样可以避免频繁修改驱动代码编译烧录带来的时间消耗,也不用太过关注datasheet。对于没有上位机的codec,可以在shell命令用i2c-tool工具直接读写修改codec寄存器进行调试。

四、常见问题及调试手段

4.1 播放、录音声音不正常

确定SOC和Codec驱动设置的音频参数如,位宽,采样率,声道数,左右对齐,长短帧,AB格式等是否设置的一致,查看Soc和Codec的datasheet时序图。
使用逻辑分析仪抓数据:
对于播放:应用程序可以固定写0x1234,0x4567等,用逻辑分析仪抓数据设置对应格式进行解析看看数据是否一致。
对于录音:逻辑分析仪抓数据后设置对应格式解析数据后导出数据到电脑转成bin文件在电脑播放,
同时应用程序录音文件拷贝到电脑进行播放,对比。排除是codec还是soc端问题。


4.2 播放、录音断续卡顿

查看cpu loading,应用程序是否及时收发数据(如APP有其它耗时阻塞操作),驱动是否发生XRUN,等异常。优化应用程序,调整进程/线程优先级,调整period_size,period_count值。
示波器抓波形是否有干扰毛刺。
查看I2S,和DMA相关寄存器,Req,Ack是否配对一样,确定芯片内部相关模块是否发生异常XRUN。


4.3 播放、录音有杂音

引起杂音的问题很多,如下列举常用定位方法:
使用逻辑分析仪和示波器,先确定是硬件问题(模拟电路)还是软件问题(数字电路)。
确认时钟信号是否准确,检查 jitter 是否过大,比如,对于 HDA 音频, jitter 小于 0.5 ns。
确认时钟上是否有毛刺,特别是在边沿有效值判定范围电压内,如果出现毛刺,会被芯片识别为时 钟,导致时序出现问题。
确认 CODEC 电源和地 情况,CODEC 对电源噪声敏感,任何耦合进电源或地的噪声都将导致 CODEC性能下降,底噪增大,出现杂音。
硬件采用差分电路 抑制共模噪声。 检查硬件 PCB 布局,排查噪声来源。


4.4 播放、录音无声

逻辑分析仪抓波形是否正常,确定是否有数据。
检查功放,mic是否损坏。示波器检查codec是否有模拟信号输出。用信号发生器代替mic,或更换mic测试。
通过在软件各层dump数据确定问题在哪一层,如Android12就有权限管理导致应用程序录音无声问题。
直接使用tinyplay,tinycap,tinymix等调试工具验证。排除hal,framework,app问题。
查看驱动是否有出错日志,DMA是否正常产生中断,查看DAI(I2S PDM)相关状态寄存器是否异常。


4.5 播放POP音

检查音频链路上电时序。
Mute unmute机制是否合理。
pcm数据是否有突变,软件是否做了fade in,fade out,codec/dsp是否有硬件相关功能参数可以设置。


4.6 Codec常见初始化问题

i2c初始化失败,检查原理图,地址是否设置对了,datasheet上电时序要求,尝试降低i2c速率。
某些功能设置后不生效,可以dump寄存器看看是否和代码写入的一致,配置每个寄存器后加入一定延时。

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

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

相关文章

【Portswigger 学院】CORS

教程和靶场来源于 Burpsuite 的官网 Portswigger&#xff1a;Cross-origin resource sharing (CORS) - PortSwigger 跨域资源共享&#xff08;Cross-origin resource sharing&#xff0c;CORS&#xff09;是一种浏览器机制&#xff0c;允许浏览器访问不同源的资源。同源策略的作…

【Python】已解决Python错误:ImportError: cannot import name get_column_letter的报错解决办法

【Python】已解决Python错误&#xff1a;ImportError: cannot import name get_column_letter的报错解决办法 &#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家…

Chat-TTS chat-tts-ui 实机部署上手测试!Ubuntu服务器实机 2070Super*2 8GB部署全流程

项目介绍 开源的项目&#xff0c;感谢各位大佬的贡献&#xff01; 官方介绍&#xff1a;一个简单的本地网页界面&#xff0c;使用ChatTTS将文字合成为语音&#xff0c;同时支持对外提供API接口。A simple native web interface that uses ChatTTS to synthesize text into spe…

没有名为 keras.preprocessing 的模块

估计是因为版本原因 我安装的是 3.3.3版本 >>> import keras >>> print(keras.__version__) 3.3.3 keras.preprocessing.image 将 keras.preprocessing.image 改为 from keras_preprocessing.image 之后报image_type啥的错误&#xff0c;后面查找之后…

C++ 84 之 文件读写

#include <iostream> #include <cstring> #include <string> using namespace std; #include <fstream> // 文件流的头文件int main() {// 写入: 文件内容// (文件位置&#xff0c; 如果这个不存在&#xff0c; 就新建一个)// 写法1&#xff1a; of…

JAVA实现利用phantomjs对URL页面(网页)进行转图片保存

一、前期准备 1、下载phantomjs工具 地址&#xff1a;https://phantomjs.org/download.html 解压到指定文件夹&#xff0c;后续代码要调用该工具&#xff0c;记住路径 2、准备好模板NetToPicMoban.js 用于给phantomjs提供需要执行的js&#xff0c;具体放在那看自己的需求&…

运算放大器(运放)缓冲器(跟随器)电路

运算放大器(Operational Amplifier) 运算放大器(Operational Amplifier)是一种差分放大器&#xff0c;具有高输入电阻、低输出电阻、高开放增益&#xff08;开环增益&#xff09;&#xff0c;并具有可放大输入引脚与-输入引脚间的电压差的功能。 设计目标 输入输入输出输出频…

“Photoshop AI插件:StartAI的全面使用攻略

随着人工智能技术的飞速发展&#xff0c;Photoshop作为设计师们不可或缺的工具&#xff0c;也在不断地融入AI技术&#xff0c;以提升设计效率和效果。在2024年&#xff0c;PSAI插件StartAI因其强大的功能和易用性&#xff0c;成为了Photoshop用户的得力帮手。下面来给大家详细介…

银行数仓项目实战(五)--搭建数仓和数据标准化

文章目录 搭建数仓数据采集 标准层 搭建数仓 数据采集 业务系统源 -》ODS贴源层&#xff0c;添加标签&#xff0c;添加系统来源&#xff0c;添加时间戳 问甲方要建表语句&#xff0c;自己添加etldt字段和来源字段&#xff0c;通过之前文章教的Kettle把数据抽到自己数据库中。…

远程桌面另一台服务器连接不上,局域网IP如何访问另一台服务器

在IT运维工作中&#xff0c;远程桌面连接是日常工作中不可或缺的一部分。然而&#xff0c;当尝试远程桌面连接至另一台服务器时&#xff0c;如果连接不上&#xff0c;可能会引发一系列问题&#xff0c;影响到工作效率和信息安全。特别是在局域网环境中&#xff0c;确保能够正确…

MySQL学习——创建MySQL Workbench中的Connections

在MySQL Workbench中&#xff0c;Connections&#xff08;连接&#xff09;是用户与MySQL数据库进行交互的桥梁。 本文将添加一个新连接&#xff0c;该连接可以是初始连接&#xff0c;也可以是附加连接。在开始之前&#xff0c;必须安装、启动MySQL服务器的实例&#xff0c;并…

今年618各云厂商的香港服务器优惠活动汇总

又到了一年618年中钜惠活动时间&#xff0c;2024年各大云服务器厂商都有哪些活动呢&#xff1f;有哪些活动包括香港服务器呢&#xff1f;带着这些问题&#xff0c;小编给大家一一讲解各大知名厂商的618活动有哪些值得关注的地方&#xff0c;如果对你有帮助&#xff0c;欢迎点赞…

SK投屏助手:电脑控制手机,游戏与App轻松畅玩

SK投屏助手让您将手机画面完美投射至电脑屏幕&#xff0c;而且不止于此&#xff01;现在&#xff0c;您可以利用电脑反向控制手机&#xff0c;轻松操作各种游戏和应用程序。无论是在家中放松还是在工作中提高效率&#xff0c;SK投屏助手都能成为您的得力助手。立即体验无缝连接…

51单片机STC89C52RC——3.1 数码管静态展示

目的 让数码管在指定位置显示指定数字 一&#xff0c;STC单片机模块 二&#xff0c;数码管 2.1 数码管位置 2.2 生活中用到的数目管 红绿灯 LED数码管在生活中随处可见&#xff0c;洗衣机、电饭煲、热水器、微波炉、冰箱、这些最基本的家用电器上基本都用到了这种7段LED数…

windows cmd bat 批处理脚本找到监听端口并并杀掉进程 nestat -ano

echo off rem 终止正在监听在端口8888上的进程 rem tokens5 表示第五个token for /f tokens5 %%p in (netstat -ano ^| findstr 8888) do ( set pid%%p ) echo xxxx %pid% rem xxxx TCP [::]:8888 [::]:0 LISTENING 2720 taskkill /pid %pid% /f if %errorlevel% equ 0 ( echo …

从零开始认识思科,并学会认识思科1.认识思科

hello大家好&#xff0c;我是风屿&#xff0c;今天我将从零开始带领大家认识思科设备中的各种技术以及配置&#xff0c;方便以后配置思科的设备&#xff0c;以及考取相应的证书。 在当今的数字化时代&#xff0c;网络扮演着至关重要的角色。而在网络技术领域&#xff0c;思科无…

java语言his系统医保接口 云HIS系统首页功能实现springboot框架+Saas模式 his系统项目源码

java语言his系统医保接口 云HIS系统首页功能实现springboot框架Saas模式 his系统项目源码 HIS系统的实施旨在整个医院建设企业级的计算机网络系统&#xff0c;并在其基础上构建企业级的应用系统&#xff0c;实现整个医院的人、财、物等各种信息的顺畅流通和高度共享&#xff0c…

英伟达市值飙升,超越苹果微软并超过英国股市总市值

原标题&#xff1a;英伟达超越苹果微软市值&#xff0c;成为全球市值最高的企业 易采游戏网6月19日消息&#xff1a;近日&#xff0c;美国科技巨头英伟达市值的迅速增长引起了市场广泛关注。据最新数据显示&#xff0c;截至本周二收盘&#xff0c;英伟达的市场资本化已达到3.34…

数据结构之探索“队列”的奥秘

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;数据结构&#xff08;Java版&#xff09; 目录 队列有关概念 队列的使用 队列模拟实现 循环队列的模拟实现 622. 设计循环队列 双端队…

【C++初阶路】--- 类和对象(中)

目录 一、this指针1.1 this指针的引出1.2 this指针的特性1.3. C语言和C实现Stack的对比 二、类的6个默认成员函数三、构造函数3.1 概念3.2 特性 一、this指针 1.1 this指针的引出 如下定义一个日期类Date class Date { public://void InitDate(Date* const this, int year …