RK3568笔记二十六:音频应用

news2024/12/26 11:27:19

若该文为原创文章,转载请注明原文出处。

一、介绍

音频是我们最常用到的功能,音频也是 linux 和安卓的重点应用场合。

测试使用的是ATK-DLR3568板子,板载外挂RK809 CODEC芯片,RK官方驱动是写好的,不用在自己重新写。测试应用层录音和放音使用的是ALSA方式。

二、硬件原理图

I2S 接口一共用到了 5 根线,这个 5 根线用于 RK3568 与 RK809 之间的音频数据收
发。

三、音频驱动使能

RK 官方已经写好了 RK809 CODEC 驱动,直接配置内核使能 RK809 CODEC 驱动即可。

打开kernel/arch/arm64/configs/rockchip_linux_defconfig

修改RK817为y, 重新编译kenel.RK官方把RK809 与 RK817 CODEC 部分驱动代码做了兼容。

驱动源码路径:kernel/sound/soc/codecs/rk817_codec.c

四、设备树配置

RK809 CODEC芯片的I2S连接到RK3568的i2c0, 内核使能后,需要通过I2C配置一系列参数。

1、I2C设备树配置

设备树路径: arch/arm64/boot/dts/rockchip/rk3568-evb.dtsi

2、声卡设备树配置

配置声卡主要是为了和RK809驱动关联。

声卡驱动文件路径为 sound/soc/generic/simple-card.c

五、声卡设置与测试

alsa-utils 自带了 amixer 这个声卡设置工具,使用amixer测试录音和放音。

1、使用 amixer 设置声卡

amixer cset name='Capture MIC Path' 'Main Mic'

2、使用 arecord 录制音频

arecord -r 44100 -f S16_LE -d 10 record.wav
录制一段 10 秒音频, 采样率 44.1K,S16_LE 格式进行采样,

3、播放音频

aplay record.wav

六、开机自动配置声卡

声卡设置的保存通过 alsactl 工具来完成,此工具也是 alsa-utils 编译出来的。alsactl 默认将
声卡配置文件保存在/var/lib/alsa 目录下。
使用 amixer 设置声卡,然后输入如下命令保存声卡设置
alsactl -f /var/lib/alsa/asound.state store //保存声卡设置
如果要使用 asound.state 中的配置信息来配置声卡,执行如下命令即可:
alsactl -f /var/lib/alsa/asound.state restore

创建自启动文件

/etc/init.d/S98alactl
PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
start_restore()
{
  if [ -f "/var/lib/alsa/asound.state" ]; then
  /usr/sbin/alsactl -f /var/lib/alsa/asound.state restore &
  fi
}
stop_restore()
{
  echo -n ""
}
case "$1" in
  start)
  echo -n "ALSA: Restoring mixer setting......"
  start_restore
  echo "done."
  ;;
  stop)
  stop_restore
  echo "done."
  ;;
  restart|reload)
  echo -n "stoping restore... "
  stop_restore && sleep .3
  echo "done."
  echo -n "starting restore... "
  start_restore
  echo "done."
 ;;
 *)
 echo "Usage: $0 {start|stop|restart}"
 exit 1
 esac
 exit 0
chmod 777 /etc/init.d/S98alactl   # 赋予文件可执行权限

修改/etc/init.d/rcS,添加执行文件命令,上电后会自动配置声卡

七、使用ALSA录制音频和放音

ALSA程序网上很多,不多做介绍,直接上代码

录制音频alsa_pcm_rec.c


/*
  进行音频采集,采集pcm数据并直接保存pcm数据
  音频参数:
   	 声道数:		1
   	 采样位数:	16bitLE格式t、LE格式
   	 采样频率:	44100Hz
  运行示例:
  $ gcc linux_pcm_save.c -lasound
  $ ./a.out
*/

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <signal.h>

/*
 * 指定音频的格式,其他常用格式:
 * SND_PCM_FORMAT_U24_LE、
 * SND_PCM_FORMAT_U32_LE
 */
#define AudioFormat SND_PCM_FORMAT_S16_LE
/* 1单声道   2立体声*/
#define AUDIO_CHANNEL_SET 1
/*
 * 音频采样率,常用的采样频率:
 * 44100Hz 、16000HZ、8000HZ、48000HZ、22050HZ
 */
#define AUDIO_RATE_SET 44100

#define PCM_FILE "test.pcm"

FILE *pcm_data_file = NULL;
int run_flag = 0;

void exit_sighandler(int sig)
{
  run_flag = 1;
}

int main(int argc, char *argv[])
{
  int  i;
  int  err;
  char *buffer;
  int  buffer_frames = 1024;
  unsigned int rate = AUDIO_RATE_SET;
  snd_pcm_t *capture_handle;       // 一个指向PCM设备的	句柄
  snd_pcm_hw_params_t *hw_params;  //此结构包含有关硬件的信息,可用于指定PCM流的配置

  /* 注册信号捕获退出接口 */
  signal(2, exit_sighandler);

  /* PCM的采样格式在pcm.h文件里有定义 */
  snd_pcm_format_t format = AudioFormat;  // 采样位数:16bit、LE格式

  /*打开音频采集卡硬件,并判断硬件是否打开成功,若打开失败则打印出错误提示
	  * SND_PCM_STREAM_PLAYBACK 输出流
   * SND_PCM_STREAM_CAPTURE  输入流
   */
  if((err = snd_pcm_open(&capture_handle, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0)
  {
    printf("无法打开音频设备.\n");
    exit(1);
  }
  printf("音频接口打开成功.\n");

  /* 创建一个保存PCM数据的文件 */
  if((pcm_data_file = fopen(PCM_FILE, "wb")) == NULL)
  {
    printf("无法创建音频文件.\n");
    exit(1);
  }
  printf(" 用于录制的音频文件已打开.\n");

  /* 分配硬件参数结构对象,并判断是否分配成功 */
  if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
  {
    printf("无法分配硬件参数结构.\n");
    exit(1);
  }
  printf("硬件参数结构分配成功.\n");

  /* 按照默认设置对硬件对象进行设置,并判断是否成功 */
  if((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0)
  {
    printf("无法初始化硬件参数结构.\n");
    exit(1);
  }
  printf("硬件参数结构初始化成功.\n");


  /*
  		设置数据为交叉模式,并判断是否设置成功
  		interleaved/non interleaved:交叉/非交叉模式。
    表示在多声道数据传输的过程中是采样交叉的模式还是非交叉的模式。
  		对多声道数据,如果采样交叉模式,使用一块buffer即可,其中各声道的数据交叉传输;
    如果使用非交叉模式,需要为各声道分别分配一个buffer,各声道数据分别传输。
 	*/
        if((err = snd_pcm_hw_params_set_access (capture_handle,hw_params,SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
 	{
 	  printf("无法设置访问类型(%s)\n",snd_strerror(err));
 	  exit(1);
 	}
 	printf("访问类型设置成功.\n");

 	/* 设置数据编码格式,并判断是否设置成功 */
 	if((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, format)) < 0)
 	{
 	  printf("无法设置格式.\n");
 	  exit(1);
 	}
 	printf("PCM数据格式设置成功.\n");

 	/* 设置采样频率,并判断是否设置成功 */
 	if((err = snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &rate, 0)) < 0)
 	{
 	  printf("无法设置采样率.\n");
 	  exit(1);
 	}
 	printf("采样率设置成功.\n");

 	/* 设置声道,并判断是否设置成功 */
 	if((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, AUDIO_CHANNEL_SET)) < 0)
 	{
 	  printf("无法设置声道参数.\n");
 	  exit(1);
 	}
 	printf("声道参数设置成功.\n");

 	/* 将配置写入驱动程序中,并判断是否配置成功 */
 	if((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0)
 	{
 	  printf("无法向驱动程序设置参数.\n");
 	  exit(1);
 	}
 	printf("参数设置成功.\n");

 	/* 使采集卡处于空闲状态 */
 	snd_pcm_hw_params_free(hw_params);

 	/* 准备音频接口,并判断是否准备好 */
 	if((err = snd_pcm_prepare(capture_handle)) < 0)
 	{
 	  printf("无法使用音频接口.\n");
 	  exit(1);
 	}
 	printf("音频接口准备好.\n");

 	/* 配置一个数据缓冲区用来缓冲数据 */
 	/* snd_pcm_format_width(format) 获取样本格式对应的大小(单位是:bit) */
 	int frame_byte = snd_pcm_format_width(format)/8;
 	buffer = malloc(buffer_frames*frame_byte*AUDIO_CHANNEL_SET);
 	printf("缓冲区分配成功.\n");

 	/* 开始采集音频PCM数据 */
 	printf("开始采集数据...\n");

 	while(1)
 	{
 	  /* 从声卡设备读取一帧音频数据 2048字节 */
 	  if((err=snd_pcm_readi(capture_handle, buffer, buffer_frames)) != buffer_frames)
 	  {
 	    printf("从音频接口读取失败.\n");
 	    exit(1);
 	  }

 	  /* 写数据到文件: 音频的每帧数据样本大小是16位=2个字节 */
 	  fwrite(buffer, (buffer_frames*AUDIO_CHANNEL_SET), frame_byte, pcm_data_file);
 	  if(run_flag)
 	  {
 	    printf("停止采集.\n");
 	    break;
 	  }
 	}

 	/* 释放数据缓冲区 */
 	free(buffer);

 	/* 关闭音频采集卡硬件 */
 	snd_pcm_close(capture_handle);

 	/* 关闭文件流 */
 	fclose(pcm_data_file);

 	return 0;
}

播放音频alsa_pcm_play.c


/*
 * 进行音频采集,读取存放pcm数据文件通过声卡进行播放'
 * 音频参数:
 *     声道数:      1
 *     采样位叔:   16bit、LE格式
 *     采样频率:    44100Hz
 * 运行救命:
 * $ gcc alsa_pcm_play.c -lasound
 * $ ./a.out test.pcm
 */

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <signal.h>

/* 指定音频的格式,其他常用格式:
 *
 * SND_PCM_FORMAT_U24_LE、
 * SND_PCM_FORMAT_U32_LE
 */
#define AudioFormat  SND_PCM_FORMAT_S16_LE
/* 1单声道   2立体声 */
#define AUDIO_CHANNEL_SET 1
/* 音频采样率,常用的采样频率:
 * 44100Hz 、16000HZ、8000HZ、48000HZ、22050HZ
 */
#define AUDIO_RATE_SET     44100

FILE *pcm_data_file = NULL;

int run_flag = 0;

void exit_sighandler(int sig)
{
  run_flag = 1;
}

int main(int argc, char *argv[])
{
  int  i;
  int  err;
  char *buffer;
  int  buffer_frames = 1024;
  unsigned int rate = AUDIO_RATE_SET;
  snd_pcm_t *capture_handle;   // 一个指向PCM设备的句柄
  snd_pcm_hw_params_t *hw_params; //此结构包含有关硬件的信息,可用于指定PCM流的配置

  /* 注册信号捕获退出接口 */
  signal(2, exit_sighandler);

  /* PCM的采样格式在pcm.h文件里有定义 */
  snd_pcm_format_t format = AudioFormat;

  /*打开音频采集卡硬件,并判断硬件是否打开成功,若打开失败则打印出错误提示
	  * SND_PCM_STREAM_PLAYBACK 输出流
   * SND_PCM_STREAM_CAPTURE  输入流
   */
  if((err = snd_pcm_open(&capture_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
  {
    printf("无法打开音频设备.\n");
    exit(1);
  }
  printf("音频接口打开.\n");

  /* 打开存放PCM数据的文件 */
  if((pcm_data_file = fopen(argv[1], "rb")) == NULL)
  {

    printf("无法打开音频文件.\n");
    exit(1);
  }
  printf("用于播放的音频文件已打开.\n");

  /* 分配硬件参数结构对象, 并判断是否分配成功 */
  if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
  {
    printf("无法分配参数结构.\n");
    exit(1);
  }
  printf("硬件参数结构已分配成功.\n");

  /* 按照默认设置对硬件对象进行设置,并判断是否设置成功 */
  if((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0)
  {
    printf("无法初始化硬件参数结构.\n");
    exit(1);
  }
  printf("硬件参数结构初始化成功.\n");

  /*
  		设置数据为交叉模式,并判断是否设置成功
  		interleaved/non interleaved:交叉/非交叉模式。
    表示在多声道数据传输的过程中是采样交叉的模式还是非交叉的模式。
  		对多声道数据,如果采样交叉模式,使用一块buffer即可,其中各声道的数据交叉传输;
    如果使用非交叉模式,需要为各声道分别分配一个buffer,各声道数据分别传输。
 	*/
	 if((err = snd_pcm_hw_params_set_access (capture_handle,hw_params,SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
  {
    printf("无法设置访问类型.\n");
    exit(1);
  }
  printf("访问类型访问.\n");

  /* 设置数据编码格式,并判断是否设置成功 */
  if((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, format)) < 0)
  {
    printf("无法设置格式.\n");
    exit(1);
  }
  printf("PCM数据格式设置成功.\n");

  /* 设置采样频率,并判断是否设置成功 */
  if((err = snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &rate, 0)) < 0)
  {
    printf("无法设置采样率.\n");
    exit(1);
  }
  printf("采样率设置成功.\n");

  /* 设置声道,并判断是否设置成功 */
  if((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, AUDIO_CHANNEL_SET)) < 0)
  {
    printf("无法设置声道数.\n");
    exit(1);
  }
  printf("声道数设置成功.\n");

  /* 将配置写入驱动程序中,并判断是否配置成功 */
  if((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0)
  {
    printf("无法向驱动程序设置参数.\n");
    exit(1);
  }
  printf("参数设置成功.\n");

  /* 使采集卡处理空闲状态 */
  snd_pcm_hw_params_free(hw_params);

  /* 准备音频接口, 并判断是否准备好  */
  if((err = snd_pcm_prepare(capture_handle)) < 0)
  {
    printf("无法使用音频接口.\n");
    exit(1);
  }
  printf("音频接口准备好.\n");

 	/* 配置一个数据缓冲区用来缓冲数据 */
 	/* snd_pcm_format_width(format) 获取样本格式对应的大小(单位是:bit) */
 	int frame_byte = snd_pcm_format_width(format)/8;
 	buffer = malloc(buffer_frames*frame_byte*AUDIO_CHANNEL_SET);
 	printf("缓冲区分配成功.\n");

  /* 开始播放音频数据...*/
  printf("开始播放音频数据...\n");

  int read_cnt;

  while(1)
  {
    read_cnt = fread(buffer, 1, frame_byte*(buffer_frames*AUDIO_CHANNEL_SET), pcm_data_file);
    if(read_cnt <= 0)
      break;

    /* 向声卡设备写一帧音频数据: 2048字节 */
    if((err = snd_pcm_writei(capture_handle, buffer, buffer_frames)) != buffer_frames)
    {
      printf("向音频接口写数据失败.\n");
      exit(1);
    }

    if(run_flag)
    {
      printf("停止播放.\n");
      break;
    }
  }

  printf("播放完成.\n");
  /* 释放数据缓冲区 */
  free(buffer);

  /* 关闭音频采集卡硬件 */
  snd_pcm_close(capture_handle);

  /* 关闭文件流 */
  fclose(pcm_data_file);

  return 0;
}

编译:

/opt/atk-dlrk356x-toolchain/usr/bin/aarch64-buildroot-linux-gnu-g++ alsa_pcm_rec.c -o alsa_pcm_play -lasound
/opt/atk-dlrk356x-toolchain/usr/bin/aarch64-buildroot-linux-gnu-g++ alsa_pcm_play.c -o alsa_pcm_play -lasound

生成的可执行文件,通过ADB或TFTP等方式上传到板子运行。

测试录音和放音正常。

如有侵权,或需要完整代码,请及时联系博主。

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

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

相关文章

C语言 | Leetcode C语言题解之第113题路径总和II

题目&#xff1a; 题解&#xff1a; int** ret; int retSize; int* retColSize;int* path; int pathSize;typedef struct {struct TreeNode* key;struct TreeNode* val;UT_hash_handle hh; } hashTable;hashTable* parent;void insertHashTable(struct TreeNode* x, struct Tr…

第八篇【传奇开心果系列】Python微项目技术点案例示例:以微项目开发为案例,深度解读Dearpygui 编写图形化界面桌面程序的优势

传奇开心果博文系列 系列博文目录Python微项目技术点案例示例系列 博文目录前言一、开发图形化界面桌面程序的优势介绍二、跨平台特性示例代码和解析三、高性能特性示例代码和解析四、简单易用特性示例代码和解析五、扩展性强示例代码和解析六、现代化设计示例代码和解析七、知…

【PB案例学习笔记】-09滚动条使用

写在前面 这是PB案例学习笔记系列文章的第8篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gitee…

如何使用KNN

导入文件和库 加载数据集、拆分数据集 训练模型 预测 打印结果

用C#调用SAP 的WebServices接口

文章目录 用C#调用SAP 的WebServices接口创建C#的项目添加窗体添加引用在表单的装载事件里编写代码运行结果SAP的RFC函数 用C#调用SAP 的WebServices接口 创建C#的项目 添加窗体 添加引用 在表单的装载事件里编写代码 using System; using System.Collections.Generic; using …

MicroLED:苹果对知识产权的影响

Yole的洞察揭示&#xff0c;MicroLED IP在经历了七年的爆炸式增长后&#xff0c;已然屹立于行业之巅。苹果公司&#xff0c;作为微LED领域的先行者&#xff0c;早在2014年便敏锐地捕捉到Luxvue这家初创公司的潜力&#xff0c;将其纳入麾下&#xff0c;引发了业界的广泛关注。然…

204页 | MES项目需求案例方案:效率+精细化+品质+数据互联(免费下载)

【1】关注本公众号&#xff0c;转发当前文章到微信朋友圈 【2】私信发送 MES项目需求案例方案 【3】获取本方案PDF下载链接&#xff0c;直接下载即可。 如需下载本方案PPT/WORD原格式&#xff0c;请加入微信扫描以下方案驿站知识星球&#xff0c;获取上万份PPT/WORD解决方案&…

上位机图像处理和嵌入式模块部署(f103 mcu运行freertos)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 mcu一般有两个用途,一个是控制,一个是非控制。控制类的应用一般要求实时性比较高,什么时候做什么,都是有严格的时间要求的。而非控制类的应用,则要求实现尽可能多的功能,比如…

如何调用通义千问大模型API

目录 登录阿里云 大模型服务平台百炼 登录控制台 QWen Long QWen 通义千问开源系列 大语言模型 OpenAI接口兼容 登录阿里云 阿里云-计算&#xff0c;为了无法计算的价值 大模型服务平台百炼 降价信息&#xff1a; 登录控制台 右上角取得API key 创建Key QWen Long qw…

03.tomcat环境搭建

上传软件包 JDK #man bash #PATH 存放命令的路径 ## ls #加入环境变量&#xff0c;注意&#xff1a;EOF的单引号的意思就是追加到文件中的内容带有变量的不做解析&#xff0c;否则会被解析 cat >>/etc/profile <<EOF export JAVA_HOME/application/jdk export PAT…

修改元组元素

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 场景模拟&#xff1a;伊米咖啡馆&#xff0c;由于麝香猫咖啡需求量较大&#xff0c;库存不足&#xff0c;店长想把它换成拿铁咖啡。 实例08 将麝香猫…

Python 开心消消乐

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

硬盘录像机DVR如何连接到外网的视频监控接入网关(国标网关)

目录 一、要求 二、工作准备 三、接入 1、查看SIP服务器ID号和相关国标对接参数&#xff0c;如下&#xff1a; 2、DVR国标参数配置 3、进行连接 &#xff08;1&#xff09;设备接入 &#xff08;2&#xff09;配置通道 &#xff08;3&#xff09;通道接入 &#xff0…

蓝桥杯算法心得——李白打酒(加强版)

大家好&#xff0c;我是晴天学长&#xff0c;记忆化搜索&#xff0c;找到技巧非常重要&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 2) .算法思路 1.memo三维表示记录的结果 3&#xff09;.算法步骤 1…

[ FreeRTOS 基础知识] RTOS 背景介绍

文章目录 RTOS简介IOTOSFreertos简介RTOS划分 RTOS简介 &#xff08;1&#xff09;实时操作系统&#xff0c;本用于追求实时性的嵌入式系统。 典型&#xff1a;ucos、uclinux、vxworks &#xff08;实时性的指当事件产生的时候&#xff0c;需要花多久的时间做出响应。&#xf…

俄罗斯半导体领域迈出坚实步伐:首台光刻机诞生,目标直指7纳米工艺

近日&#xff0c;国外媒体纷纷报道&#xff0c;俄罗斯在半导体技术领域取得了重要突破&#xff0c;首台光刻机已经制造完成并正在进行严格的测试阶段。这一里程碑式的事件标志着俄罗斯在自主发展半导体技术的道路上迈出了坚实的一步。 据俄罗斯联邦工业和贸易部副部长瓦西里-什…

【电源专题】功率电感器啸叫原因及典型案例

啸叫产生的原因 声波是在空气中传播的弹性波,人的可听到的频率范围大约20~20kHz。在DC-DC转换器的功率电感器中,当流过人耳可听范围频率的交流电流以及脉冲波时,电感器主体会发生振动,该现象称为"线圈噪音",有时也称为啸叫。 啸叫一般是由电感器产生,…

cmake使用交叉编译工具链并验证

目录 一、内容 二、配置 1. 准备cmake文件 2. 使用交叉编译 三、验证 1. 构建阶段验证 2. 编译阶段验证 一、内容 目的&#xff1a;在X86环境下编译ARM平台软件 编写交叉编译配置文件&#xff1a;xx.cmake 执行cmake命令时指定&#xff1a;cmake \ -DCMAKE_TOOLCHAIN_F…

什么是容器:从基础到进阶的全面介绍

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

【JavaEE初阶】HTTP协议|HTTP请求|URL基本格式|URLencode

&#x1f4a1;推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击跳转到网站】 HTTP请求(Request) 认识URL URL基本格式 平时我们俗称的"⽹址"其实就是说的URL(Uniform Resource Loc…