ESP32 I2S音频总线学习笔记(一):初识I2S通信与配置基础

文章目录

  • 简介
  • 为什么需要I2S?
  • 关于音频信号
    • 采样率
    • 分辨率
    • 音频声道
  • 怎样使用I2S传输音频?
    • 位时钟BCLK
    • 字时钟WS
    • 串行数据SD
  • I2S传输模型
  • I2S通信格式
  • I2S格式
    • 左对齐格式
    • 右对齐格式
  • i2s基本配置
  • i2s 底层API
    • 加载I2S驱动
    • 设置I2S使用的引脚
    • I2S读取数据
    • I2S发送数据
    • 卸载I2S驱动
  • 总结

简介

在音频处理领域,I2S是一种广泛使用的通信协议,它专门用于芯片之间的音频数据传输。ESP32 作为一款高性能的微控制器,不仅支持 I2S 通信,还提供了强大的硬件接口和灵活的软件库,使其成为音频项目开发的理想选择。本篇文章将介绍I2S的相关知识和使用ESP32驱动I2S音频设备时比较常用的相关底层API函数。

I2S即Inter-IC Sound, 简称I2S,意思是芯片间音频总线,它是由飞利浦开发的一种用于数字音频设备的通信协议,常用于麦克风、扬声器、音频处理器等设备之间的音频数据传输。

为什么需要I2S?

传统的音频设备,像模拟电路,传递的是电压信号,这种方式容易受到干扰,比如噪声或者信号衰减。而数字音频需要传输数据,通常是二进制的“0”和“1”,直接用模拟接口传输会很麻烦。而 I2S就是为了解决这个问题的一种数字音频接口,它让音频数据的传输变得简单、高效、而且抗干扰能力强。 使用 I2S可以很方便地把数字音频信号从一个芯片传递到另一个芯片,对于开发者来讲只需要配置好芯片的 I2S 模块,就可以实现数字音频数据的传输了。

关于音频信号

在自然界中音频信号是以模拟量的形式存在的,它是一种随时间连续变化的物理量,为了减少外界的干扰我们需要把它变成数字量,我们一般可以通过一个模数转换器把它变成数字信号(图1),数字信号在计算机或数字设备中以离散的数值形式表示和处理,比如用0和1的组合去表示,这里可以了解下PCM编码,它是一种模拟信号数字化的方法

图1
(图1)

当然我们也能通过一个数模转换器把数字信号还原成模拟信号,以便在扬声器上播放音频(图2)。

在这里插入图片描述
(图2)

这样音频信号之间的传递就可以通过数字信号来进行了(图3),可以减少外界对信号的干扰。

在这里插入图片描述
(图3)

在音频信号处理和传输中,有三个非常重要的参数决定了音频质量和设备性能:采样率、分辨率和 音频声道。

采样率

采样率就是每秒采集声音样本的频率,这个频率越快,采样的数字信号就越接近原始的声音的信号,因为采样的越快,离散数字曲线每个样本值之间的过渡就越接近,曲线就会越平滑。这个过程和录像是类似的,一个是采样光,一个采样振动。我们知道录像其实就是一帧一帧的图像快速播放,这个采样速度很快,我们肉眼分辨不了,看起来就是连续的。如果录像时采样的速度很慢,比如1秒采样3次,那我们就会丢失掉很多画面细节。采集声音的时候也是如此,大家可以想象一下,如果采集频率很慢,听到的声音会是什么样子,会明显感到声音听起来失真不连贯甚至变样。
在这里插入图片描述

在这里插入图片描述

每秒钟采集的音频样本数,常见的有8K、16K、44.1K等,采样率越高,信号还原越精细,一般使用44.1KHz采样频率就可以得到比较高保真的声音。

分辨率

对声音数据采样后,我们将得到一些离散的样本点,那我们在一些数字设备是如何存储这些样本点的呢?我们采样的数据是以二进制的形式存储的,比如对于每一个采样点我们用3位二进制来表示(图4),那么它可以表示的范围就是2^3=8 即8种量化电平信号(图5),可以简单理解为:每个采样点可以存储 8种情况的声音。如果量化位数越多,根据我们高中学过的排列组合知道,得到的样本值就会越多,那它可以表示自然界声音的细节就越多,或者说能表示的声音就越丰富。

音频数据的量化位数或量化深度,常见的有8bit、16bit、24bit、32bit等,位数越高,信号的动态范围和精细程度越好

在这里插入图片描述
(图4)

在这里插入图片描述
(图5)

音频声道

分为单声道,双声道,单声道是一种只有一个音频信号通道,所有声音都合并到一个通道中输出,无论是通过一个扬声器还是两个扬声器,听到的声音是完全相同的。双声道分为左声道和右声道,具有两个独立音频通道,左声道和右声道可以传递不同的声音信号,具有空间感和方向感,也就是我们平常说的立体声。

怎样使用I2S传输音频?

使用I2S传输音频的时候,需要用到时钟信号、控制信号以及数据信号(图6),它们之间是分开传输的。对于标准通信模式下的 I2S 总线主要包含以下几个信号:位时钟BCLK,字时钟WS,串行数据SD。有的时候还需要MCLK:主时钟线,该信号线可选,具体取决于从机,主要用于向 I2S 从机提供参考时钟。
(注意这几种信号有几种其它叫法,这里结合英文选择了这几个名称,大家只要在使用能区分就行)~

在这里插入图片描述
图(6)

位时钟BCLK

BCLK(Bit Clock,位时钟)
也叫BCK, SCLK(Serial Clock),对应数字音频的每一位数据,是模块内的同步信号

BCLK 定义了数据传输的速率,用来控制数据的传输节奏。它的频率通常是采样率乘以每个采样的位数再乘以声道数量。例如,对于 44.1kHz 的采样率、16 位双声道音频,BCLK 的频率为 44.1kHz × 16 × 2= 1.4112 MHz。所以对于双声道来说,BCLK的频率=2×采样频率×采样位数。

字时钟WS

WS(Word Select , 字选择时钟)
也叫 LRCLK (Left Right Clock)即左右声道时钟, 用于标识当前正在传输的是左声道数据还是右声道数据。对于飞利浦公司定义的I2S标准,当 WS 为低电平时表示左声道,高电平时表示右声道。一个完整的 WS 信号周期包含两个声道的数据(左声道和右声道)

WS 信号的频率等于音频的采样率。
例如,如果音频采样率是 44.1kHz,那么 WS 的频率也是 44.1kHz。

串行数据SD

SD(Serial Data,串行数据)
i2s传输时的音频数据,是用二进制补码表示的,具体数据传输的格式主要包括:I2S格式,左对齐格式,右对齐格式,

每个 BCK 周期会传输一位数据。数据发送端和接收端会根据 BCK 信号的跳变(上升沿或下降沿)同步数据的发送和接收。当 WS 为低电平时,SD 传输左声道数据;当 WS 为高电平时,SD 传输右声道数据。

比如我们传输16 位双声道音频,SD数据为1 0 1 0 1 1 0 1 1 0 0 0 1 0 0 1 1 1 0 0 1 0 0 1 1 0 0 0 1 0 0 1

它表示

  • 每个 BCK 周期传输一位音频数据。
  • 当 WS 为低电平时,SD 按位传输左声道的 16 位数据1 0 1 0 1 1 0 1 1 0 0 0 1 0 0 1
  • 当 WS 为高电平时,SD 按位传输右声道的 16 位数据 1 1 0 0 1 0 0 1 1 0 0 0 1 0 0 1,

个人总结

字时钟:高低电平翻转,总线在传输双声道音频 0为左, 1为右,字时钟的频率=采样率
位时钟:串行数据线的信号会在位时钟上升沿被采样,位时钟的频率=2×采样率×采样位数。
串行数据:用二进制补码表示的音频数据,先传输高位,再传输低位,
Tips:字时钟和位时钟都是由主机发送

了解了I2S是如何传输后,我们再来看看他的传输模型和通信格式。

I2S传输模型

I2S通信支持全双工和半双工通信,支持主/从模式。主设备就是发送时钟的,从设备在时钟的控制下发送或者接收数据。

连接到I2S总线的设备可以分为两类:

控制器——控制 SCK 和 WS 信号。
目标设备——接收 SCK 和 WS 信号

总线上只能有一个控制器,但是总线可以有多个目标设备。

音频设备,可以分为三类:

发射器——发送音频信号。
接收器——接收音频信号。
控制器——控制音频信号

这里我们至少需要一个发射器和接收器,控制器是可选的,主要用于向 I2S 从机提供参考时钟

根据I2S总线谁作为控制器,谁作为目标设备,我们可以有三种I2S传输模型:

  1. 发射器作为控制器, 接收器作为目标设备

在这里插入图片描述

  1. 发射器作为目标设备,接收器作为控制器

在这里插入图片描述

  1. 发射器和接收器都作为目标设备,其他I2S控制器作为控制器

在这里插入图片描述

总结:三种经典I2S传输模型
在这里插入图片描述

I2S通信格式

PCM510xA 支持行业标准的音频数据格式,包括标准 I2S 和 左对齐(Left-justified) 格式等。
在这里插入图片描述

I2S格式

I2S格式:又称飞利浦格式(图7),数据最高位总是出现在字时钟变化后的第二个位时钟脉冲处(滞后一个位时钟),这种格式下数据MSB的位置是确定的,LSB的位置取决于字长。
在这里插入图片描述

图(7)

左对齐格式

左对齐格式(图8):数据最高位出现在字时钟变化后的第一个位时钟脉冲处(无滞后位时钟)

在这里插入图片描述
图(8)

右对齐格式

右对齐格式:又称日本格式,这种格式和左对齐差不多,只不过整体是靠右对齐的,即数据LSB与WCLK跳变沿对齐。

在这里插入图片描述
图(9)

i2s基本配置

①,i2s的时钟使能和GPIO口配置 ②,配置为i2s模式
③,i2s标准,无论有多少位有效数据,即数据的最高位总是出现在WS变化(也就是一帧开始)后的第2个CK脉冲处。
④,i2s数据长度,包括16位,16位扩展(16位数据以32位包发送),24位,32位。 ⑤,设置i2s时钟
⑥,设置i2s空闲状态下时钟电平 ⑦,i2s使能

i2s 底层API

这里我们以ESP32 I2S通信为例,开发环境是Arduino IDE,介绍它的相关底层API,在我们调库的时候下面这些函数会被调用,我们看一下它的实现过程。

加载I2S驱动

函数原型:

esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void *i2s_queue)

参数说明::

i2s_port_t i2s_num:指定使用的 I2S 外设端口。i2s_port_t 是 I2S 外设端口的枚举类型,有两个端口可用:I2S_NUM_0 和 I2S_NUM_1,对应 ESP32 的第一个和第二个 I2S 外设。

typedef enum {I2S_NUM_0 = 0,                 /*!< I2S port 0 */
#if SOC_I2S_NUM > 1I2S_NUM_1 = 1,                 /*!< I2S port 1 */
#endifI2S_NUM_MAX,                   /*!< I2S port max */
} i2s_port_t;

const i2s_config_t * i2s_config : 设置I2S 外设的配置参数。其中i2s_config_t 是一个结构体,定义了 I2S 外设的配置选项,这里我们只看常用的配置选项就可以了,主要包括mode、 sample_rate、bits_per_sample等,如下:

typedef struct {i2s_mode_t              mode;                       /*< 设置 I2S 的工作模式 */uint32_t                sample_rate;                /*!< 设置音频采样率 */i2s_bits_per_sample_t   bits_per_sample;            /*!< 设置采样位数 */i2s_channel_fmt_t       channel_format;             /*!< 设置数据通道格式.*/i2s_comm_format_t       communication_format;       /*!< 设置I2C数据传输格式 */int                     intr_alloc_flags;           /*!< 设置中断相关标志位*/int                     dma_buf_count;  dma缓存个数,            int                     dma_buf_len;                
} i2s_driver_config_t;typedef i2s_driver_config_t i2s_config_t;

int queue_size: 数据传输的队列的大小
void * i2s_queue:存放和管理 I2S 传输的数据。

这个函数有一个esp_err_t 的返回值,如果返回ESP_OK表示加载I2S驱动成功。

设置I2S使用的引脚

函数原型:

esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin)

参数说明:

i2s_port_t i2s_num:指定使用的 I2S 外设端口,I2S_NUM_0 或I2S_NUM_1。

i2s_pin_config_t * pin:配置I2S接口的各个引脚,i2s_pin_config_t 是一个结构体,里面是关于I2S引脚,如时钟引脚,左右声道选择引脚,数据输入引脚等引脚的配置。如下:

typedef struct {int mck_io_num;     /*!< MCK in out pin. Note that ESP32 supports setting MCK on GPIO0/GPIO1/GPIO3 only*/int bck_io_num;     /*!< BCK in out pin*/int ws_io_num;      /*!< WS in out pin*/int data_out_num;   /*!< DATA out pin*/int data_in_num;    /*!< DATA in pin*/
} i2s_pin_config_t

I2S读取数据

函数原型:

esp_err_t i2s_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_read, TickType_t ticks_to_wait);*/用于从 I2S接口读取音频数据

参数说明:
i2s_port_t i2s_num:I2S_NUM_0 或I2S_NUM_1。

void * dest:读取目标数据的缓存区

size_t size:要读取的数据大小,单位是字节

size_t * bytes_read:实际读取到的字节数

TickType_t ticks_to_wait:超时等待时间,因为I2S是按一定频率读取数据的,如果一次传输的数据很多就需要等待,这个参数一般写入portMAX_DELAY 表示无限等待。

如果返回ESP_OK表示I2S读取数据成功。

I2S发送数据

函数原型:

esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *bytes_written, TickType_t ticks_to_wait);*/用于向 I2S 接口写入音频数据

参数说明:
i2s_port_t i2s_num:I2S_NUM_0 或I2S_NUM_1。

const void *src:写入源数据的缓存区

size_t size:要写入的数据大小,单位是字节

size_t *bytes_written:实际写入的字节数

TickType_t ticks_to_wait:超时等待时间,因为I2S是按一定频率发送数据的,如果一次传输的数据很多就需要等待,这个参数一般写入portMAX_DELAY 表示无限等待。

卸载I2S驱动

函数原型:

esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num);

参数说明:
卸载I2S驱动的话我们只需要传入I2S端口就行了。

i2s_port_t i2s_num:指定使用的 I2S 外设端口。i2s_port_t 是 I2S 外设端口的枚举类型,有两个端口可用:I2S_NUM_0 和 I2S_NUM_1,对应 ESP32 的第一个和第二个 I2S 外设。

总结

以上我们介绍了I2S的相关知识和配置的相关函数,下篇文章我们来看一下一个使用ESP32驱动I2S设备的小案例!这系列的文章主要是分享一下本人学习过程的相关知识,如果有错误可以交流学习下!

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

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

相关文章

Eclipse中引入NS3项目

参考资料&#xff1a; 博主&#xff1a;深度不睡觉 NS3的3.36版本将Eclipse作IDE_ns3使用eclipse-CSDN博客 从1.2安装eclipse开始 其中参考教程中省略的几点&#xff1a; 1.下载解压tar包 mkdir /Tools/Eclipse/EclipseTool # 新建目录 tar -zxvf /path/to/eclipse-cpp-20…

机器学习周报-TCN文献阅读

文章目录 摘要Abstract 1 TCN通用架构1.1 序列建模任务描述1.2 因果卷积&#xff08;Causal Convolutions&#xff09;1.3 扩张卷积&#xff08;Dilated Convolutions&#xff09;1.4 残差连接&#xff08;Residual Connections&#xff09; 2 TCN vs RNN3 TCN缺点4 代码4.1 TC…

Quartz任务调度框架实现任务动态执行

说明&#xff1a;之前使用Quartz&#xff0c;都是写好Job&#xff0c;指定一个时间点&#xff0c;到点执行。最近有个需求&#xff0c;需要根据前端用户设置的时间点去执行&#xff0c;也就是说任务执行的时间点是动态变化的。本文介绍如何用Quartz任务调度框架实现任务动态执行…

Spring-kafka快速Demo示例

使用Spring-Kafka快速发送/接受Kafka消息示例代码&#xff0c;项目结构是最基础的SpringBoot结构&#xff0c;提前安装好Kafka&#xff0c;确保Kafka已经正确启动 pom.xml&#xff0c;根据个人情况更换springboot、java版本等 <?xml version"1.0" encoding&qu…

【C++】B2079 求出 e 的值

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目介绍输入格式输出格式输入输出样例说明/提示 &#x1f4af;实现方法一&#xff1a;单层 for 循环计算代码实现运行逻辑解析优点不足 &#x1f4af;实现方法二&#xff…

STM32配合可编程加密芯片SMEC88ST的防抄板加密方案设计

SMEC88ST SDK卡发包下载 目前市场上很多嵌入式产品方案都是可以破解复制的&#xff0c;主要是因为方案主芯片不具备防破解的功能&#xff0c;这就导致开发者投入大量精力、财力开发的新产品一上市就被别人复制&#xff0c;到市场上的只能以价格竞争&#xff0c;最后工厂复制的产…

精准识别花生豆:基于EfficientNetB0的深度学习检测与分类项目

精准检测花生豆&#xff1a;基于EfficientNet的深度学习分类项目 在现代农业生产中&#xff0c;作物的质量检测和分类是确保产品质量的重要环节。针对花生豆的检测与分类需求&#xff0c;我们开发了一套基于深度学习的解决方案&#xff0c;利用EfficientNetB0模型实现高效、准…

MarkItDown的使用(将Word、Excel、PDF等转换为Markdown格式)

MarkItDown的使用&#xff08;将Word、Excel、PDF等转换为Markdown格式&#xff09; 本文目录&#xff1a; 零、时光宝盒&#x1f33b; 一、简介 二、安装 三、使用方法 3.1、使用命令行形式 3.2、用 Python 调用 四、总结 五、参考资料 零、时光宝盒&#x1f33b; &a…

Qanything 2.0源码解析系列6 PDF解析逻辑

Qanything 2.0源码解析系列6: PDF解析逻辑 type: Post status: Published date: 2024/12/04 summary: 深入剖析Qanything是如何拆解PDF的,核心是pdf转markdown category: 技术分享 原文:www.feifeixu.top 😀 前言: 在前面的文章中探究了图片是怎么进行解析的,这篇文章对…

【Agent】Chatbot、Copilot与Agent如何帮助我们的提升效率?

人工智能&#xff08;AI&#xff09;技术的迅猛发展正在深刻改变我们的生活和工作方式。你是否曾想过&#xff0c;未来的工作场景会是什么样子&#xff1f;AI的崛起不仅仅是科技的进步&#xff0c;更是我们生活方式的革命。今天&#xff0c;我们将深入探讨三种主要的AI能力&…

如何使用python读写游戏内存以及使用特征码匹配基址

一.读写内存所需的基本参数 接下来我将使用GTA5游戏举例 1.通过进程名称获取进程pid from psutil import process_iterdef get_process_id_by_name(process_name):for process in process_iter(["pid", "name"]):if process.info["name"] pr…

简述css中z-index的作用?如何用定位使用?

z-index是一个css属性&#xff0c;用于控制元素的堆叠顺序&#xff0c; 如何使用定位用index 1、position&#xff1a;relative&#xff1b; z-index&#xff1b; 相对于自己来定位的&#xff0c;可以根据top&#xff0c;bottom&#xff0c;right&#xff0c;left&#xff…

CCNP_SEC_ASA 第六天作业

实验需求&#xff1a; 为保障内部用户能够访问Internet&#xff0c;请把10.1.1.0/24网络动态转换到外部地址池202.100.1.100-202.100.1.200&#xff0c;如果地址池耗尽后&#xff0c;PAT到Outside接口 提示&#xff1a;需要看到如下输出信息 Inside#telnet 202.100.1.1 Trying …

计算机网络 (13)信道复用技术

前言 计算机网络中的信道复用技术是一种提高网络资源利用率的关键技术。它允许在一条物理信道上同时传输多个用户的信号&#xff0c;从而提高了信道的传输效率和带宽利用率。 一、信道复用技术的定义 信道复用&#xff08;Multiplexing&#xff09;就是在一条传输媒体上同时传输…

敏捷开发Scrum的深入理解和实践

敏捷开发&#xff0c;特别是Scrum方法&#xff0c;已经逐渐成为软件开发领域的主流方法。Scrum不仅适用于软件开发&#xff0c;还适用于其他需要快速响应变化和灵活交付的领域。本文将深入探讨Scrum的核心概念、流程、优势、挑战及其在实践中的应用。 一、Scrum的核心概念 Scru…

计算机视觉目标检测-2

文章目录 摘要abstract1.Fast R-CNN1.1 RoI pooling1.2 End-to -End model1.3 多任务损失-Multi-task loss1.4 R-CNN、SPPNet、Fast R-CNN效果比对 2.Faster R-CNN2.1 RPN原理2.2 效果对比2.3 Faster R-CNN总结 3.总结4.参考文献 摘要 本周学习了Fast R-CNN和Faster R-CNN算法…

JavaScript网页设计案例:动态交互式任务列表

在现代网页开发中&#xff0c;JavaScript被广泛应用于实现动态交互效果。看完这一篇你就可以设计一个动态任务列表&#xff0c;全面展示HTML、CSS和JavaScript在前端开发中的实际应用。通过本案例&#xff0c;你将深入了解事件监听、DOM操作以及用户交互设计的实现过程。 案例需…

【MySQL】索引 面试题

文章目录 适合创建索引的情况创建索引的注意事项MySQL中不适合创建索引的情况索引失效的常见情况 索引定义与作用 索引是帮助MySQL高效获取数据的有序数据结构&#xff0c;通过维护特定查找算法的数据结构&#xff08;如B树&#xff09;&#xff0c;以某种方式引用数据&#xf…

使用Excel制作通达信自定义“序列数据“

序列数据的视频教程演示 Excel制作通达信自定义序列数据 1.序列数据的制作方法&#xff1a;删掉没有用的数据&#xff08;行与列&#xff09;和股代码格式处理&#xff0c;是和外部数据的制作方法是相同&#xff0c;自己上面看历史博文。只需要判断一下&#xff0c;股代码跟随的…

【ELK】ES单节点升级为集群模式--太细了!

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言准备工作1. 查看现状【单节点】2. 原节点改集群模式3. 改es配置文件&#xff0c;增加集群相关配置项4. *改docker映射的端口* 启动新节点5. docker-compose起一…