你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】基于 STM32 I2S 的音频应用开发介绍

[复制链接]
STMCU小助手 发布时间:2022-2-15 20:36
前言
在音频开发中,I2S(Inter-IC Sound)接口被广泛采用。大部分 STM32 集成了 I2S 接口。本文主要为了让 STM32 使用者了解 I2S 音频接口,及快速实现 I2S 接口的音频应用开发。 首先,对 STM32 的 I2S 接口进行简单介绍,然后描述了几种常见 I2S 音频应用架构及每种架构音频部分的电路图,最后围绕每种架构给出实现例,以便读者进行参考理解。其中,实现例会围绕 STM32CubeMX 展开,以便开发者能够参考并快速、简便地实现软件开发。除此之外,在 Cube 软件包中有 I2S 外设应用例程,提供了更完善的实现参考。

一 STM32 I2S 接口简介
I2S(Inter-IC Sound)是飞利浦公司针对数字音频设备之间的音频数据传输,制定的一种总线标准。
STM32 I2S 接口信号线构成如下表:

(L%2~T~P1I_HGBS`$L$RYIT.png

其中,SD 和 SD_Ext 信号线可分别配置为发送或者接收。在 Cube 驱动库中已对其进行封装,例如当配置 SD 信号线为发送端时,SD_Ext 自动被配置为接收端;配置 SD 为接收端时,SD_Ext 自动被配置为发送端。
全双工 I2S 是由两个 I2S 外设组成,如下图所示。

9KV_9[UQE[9J)~_~PYXT`LJ.png

对于构成全双工 I2S 的每个 I2S 外设,都具有单独的寄存器组,如下表所示(以 STM32F413xG/H 为例)。在 Cube 驱动库中,全双工下的两个 I2S 外设操作已经被封装,用户只需像配置一个全双工 SPI 一样,对一个全双工 I2S 的 API 进行调用即可。后续会以实例形式进行描述。
外设 寄存器地址空间

KAV_X0BBWMREBYIPGMXZA.png

STM32 I2S 支持四种接口标准和数据格式,如下表。更多内容请参考对应型号 STM32 的参考手册。

D1_17)@QA7X1`0ZM7G3AI]X.png

由表可看出,STM32 I2S 支持音频分辨率可为 16,24 和 32 位。I2S 时钟配置及数据格式选择决定了音频采样率,时钟产生架构如下图所示。不同系列 STM32 I2S 接口能够支持的最大音频采样率有差异,更多采样率支持情况请参考对应型号STM32 的参考手册。

_7VGC1Z4XANBS6T%NWT3{RC.png

图中 MCK、CK 分别对应 I2S 总线上的主时钟和总线时钟。其中 I2SxCLK 获取路径如下图所示(对应于右侧的 I2Sclocks)。红色线路或者绿色线路可选,本文中以红色线路为例,利用 PLL 时钟源获取 I2SxCLK 时钟。
注:下图是 STM32F429 时钟配置图的部分。不同型号 STM32 的时钟树存在差异,具体以实际采用型号的时钟树为准。

HZCWS58__TCKR_{C{$P18NM.png

在遵循 I2S 标准的实现方案中,采样率公式如下 (注:Fs 为采样率,得益于 Cube 驱动库中的良好 API 实现,可以直接设置采样率,使用者不需要按照下述公式进行 I2SDIV 和 IDD 的计算及配置。):

S2OR_K%}CVWUV{RM9%QSZZC.png

上述采样率公式不能直接用于 PDM 输出的 MEMS 麦克风,通过后一节中介绍可知,PDM 麦克风访问只是利用了 I2S 的数据和时钟线,并且在采集到麦克风位流数据后,需要经过降频操作(PDM 转 PCM,ST 提供了 PDM 转 PCM 库支持,更多介绍可参考 AN3998),从而获得 PCM 数据。所以,在这种情况下,主时钟配置为失能,数据位宽需要与帧位宽相同。折算后的采样率为:


}T)}(L0IU90Q_EY4_F(~_P2.png

其中,DIV 为 PDM 转 PCM 的降频因子,由调用的 API 决定。



二 常见 I2S 接口音频应用实现
I2S 接口应用相对固定,整理两种音频支持结构如下。

BI7N{}IU2Q%6SR}JXO2JY}G.png

其中,麦克风与播放器功能的实现相互独立。可根据实现需要决定采用的实现架构。
实现 1 参考电路如下图。原理图摘自 STM32F413H-DISCO 板,可在 ST 官网获取完整的原理图及 BOM 表等资源。其中CODEC_MCK、CODEC_CK、CODEC_WS、CODEC_SD、CODEC_ext_SD 分别对应 I2S 的 MCK、CLK、WS、SD 和ext_SD 信号线。


@_[K7G~{S[S91U{LY39RBVT.png

实现 2 参考电路如下图。其中单麦电路和双麦电路同时存在仅为读者参考理解,实际开发时可根据应用需要选择单麦或者双麦实现。原理图摘自 STM32F411E-DISCO 和 STM32429I-EVAL 板,可在 ST 官网获取完整的原理图及 BOM 表等资源。


XPI_FD34R~7~N$F@8W5CQPP.png

在实现 2 中,直接采集麦克风数据。市面上 MEMS 麦克风有 PCM 输出和 PDM 输出之分,其中 PDM 麦克风由于内部结构相对简单,成本更低,被大量采用。图中 MP45DT02 和 MP34DT01TR 都为 PDM 输出的 MEMS 麦克风。PDM 数据不能直接使用,需要经过滤波,降频等操作获得 PCM 数据。
另外,I2S 对双麦克风的支持需要结合定时器及 2 个 IO 复用引脚。实现框架如下图。


B1G`6@XMB18QTPTTX{V[2WQ.png


通过定时器对 I2S_CLK 信号进行两分频输出,然后将获得的信号提供给 MEMS 麦克风的数据线。实现时序图如下所示。依据 I2S 标准(Pilips 标准、左对齐标准和右对齐标准)时序, I2S_CLK 的上升沿获取数据。而对于文中提及的两种 MEMS麦克风,输入时钟(TIM_CLK_OUT)的下降沿使得左通道麦克风(LR 引脚下拉)输出有效数据,右通道麦克风(LR 引脚上拉)数据线进入高阻态;输入时钟的上升沿,左通道麦克风数据线进入高阻态,右通道麦克风输出有效数据。从而实现双麦克风采集。


J8ZKKK@ORV(81%%LEOWOPLI.png

三 应用实现例
本节围绕上述介绍的两种典型实现架构,结合 ST 评估板,介绍 I2S 接口应用在 STM32CubeMX 工具上配置实现,以及在生成工程后的 API 调用,最终实现基于 I2S 接口的音频数据传输。利用 STM32CubeMX,能够更快的实现针对自定义STM32 平台的开发。实现流程如下。


LF{9A$]BAIZBEISV5P6{72A.png

4.1 前期准备

602N]$HW3FY`KB%OJ4I[X{X.png

4.2 应用实现
4.2.1 实现 1
结合 STM32CubeMX 的软件开发流程如下图。


1L`JHN[PVKJB52NEA`7%$UR.png

接下来一步一步呈现实现过程。
步骤 1:在 STM32CubeMX 中根据硬件选择 STM32F413ZHTx、外部时钟、调试接口、I2C 通道和 I2S 通道(利用 I2C接口配置和控制编解码器),如下截图。硬件电路原理图可以在上节的链接网址中获取。其中,I2S 工作于主模式


Z${A5R}R74K1@QP_PO0W)98.png

选择各外设后,由于外设功能可关联到不同的引脚,自动分配的引脚可能与硬件连接的引脚不一致。此时,可以在需要重新关联的引脚上按住 Ctrl 键+鼠标左键按下,出现支持相同功能的全部复用引脚,将其拖动到与硬件设计一致的引脚上。如上图步骤 5 所示。
步骤 2:时钟配置。时钟配置涉及环节较多,STM32CubeMX 提供了便捷的时钟配置实现,如下图,只需简单的几步,即可获得最高主频运行的时钟配置。需要注意“ Input Frequency ”值,应保持与外部高速时钟一致。
尽管在上述 I2S 接口介绍中, I2S 采样率与时钟配置有关联,但在 HAL 库实现中会根据 I2S 中的采样率参数,自动完成时钟参数配置。


MBT5S%T5DZP660P_U{Q88S3.png

步骤 3:I2S 配置。点击切换到 Configuration 标签页,按照如下步骤进入 I2S 配置界面

BQJ%{MFGE]@I]A47@$AON%6.png

I2S 参数配置。配置后截图如下。

6TQOZFPM4ZQ@0`BR0A90RYF.png
Transmission Mode: 传输模式。决定 SD 数据线传输方向(SD_Ext 方向相反)。根据硬件设计, I2Sx_SD 向编解码器输出数据,所以选择发送模式。
Communication Standard: 传输标准。本文中采用 I2S Philips 标准(需要利用 I2C 发送命令配置 WM8994 工作于相同标准)。
Data and Frame Format: 数据位宽和帧位宽。如同“传输标准”的配置,保持与编解码器配置一致。
Selected Audio Frequency: 音频频率。可选频率 8KHz、11KHz、16KHz、22KHz、32KHz、44KHz、48KHz、96KHz、192KHz,这里选择 48KHz。如同“传输标准”的配置,保持与编解码器配置一致。
Clock polarity:时钟极性(非激活态时)。
I2S DMA 设置。切换到 DMA Settings 标签页,按照下图步骤设置。(图为设置完成后截图。)


)~5SAZYUB1YG)6_N~]G0UX9.png

步骤 4:I2C 配置。点击 FMPI2C1 图标进入 FMPI2C 配置界面,参数配置如下图。参数介绍请参考对应型号的参考手册。
)N1O6[`BPB7H7H_JS~{{QTT.png

步骤 5:生成 IAR 工程文件。在菜单栏 \ Project \ Settings 打开工程设置界面,设置工程名、工程存放位置以及对应的IDE 工具(本文中采用 IAR EWARM)。其他保持默认,更多参数介绍请参考 UM1718。点击菜单栏 \ Project \ Generate code 生成工程。STM32CubeMX 生成工程中包含了时钟、外设等初始化。开发者可以在此基础上增加函数调用实现应用开发。
步骤 6:利用 IAR EWARM 打开工程,添加 API 调用,实现音频数据传输,具体步骤如下。
1. 添加编解码器 wm8994 的驱动函数。为简化操作,直接采用 Cube 软件包中提供的 wm8994 驱动文件 wm8994.c,wm8994.h 和 audio.h(文件位于 STM32Cube_FW_F4_V1.16.0\Drivers\BSP\Components\wm8994 和STM32Cube_FW_F4_V1.16.0\Drivers\BSP\Components\Common)。其中 wm8994.c 复制到 src 文件夹中,wm8994.h 和 audio.h 复制到 inc 文件夹中,并添加到工程中,如下图。


)E3XG6NS~6(Z9N{32P}X9ZJ.png

2. 按照下表增添应用代码。实现如下音频数据流向。为简便起见,直接将 I2S 接收和发送关联到同一个存储空间,并同时执行,在实际应用中,应加以优化完善,避免读写位置交错引起的错误。另外,由于硬件上仅有一路麦克风通道输入,尽管采用双麦克风通道配置,但有一路麦克风通道无有效数据,体现在耳机输出上,有一路无有效输出。


T`OVH8GKH0JV0YED0$_Z218.png

3. 编译生成执行文件,下载运行。
注:下表中省略号表示,之间有其他未列出的代码部分


main.h
  1. /* 增加如下代码 */
  2. #include <stdint.h>
  3. /**
  4. * @brief Audio I2C Slave address
  5. */
  6. #define AUDIO_I2C_ADDRESS ((uint16_t)0x34)
  7. /* Codec output DEVICE */
  8. #define OUTPUT_DEVICE_SPEAKER ((uint16_t)0x0001)
  9. #define OUTPUT_DEVICE_HEADPHONE ((uint16_t)0x0002)
  10. #define OUTPUT_DEVICE_BOTH ((uint16_t)0x0003)
  11. #define OUTPUT_DEVICE_AUTO ((uint16_t)0x0004)
  12. #define INPUT_DEVICE_DIGITAL_MICROPHONE_1 ((uint16_t)0x0100)
  13. #define INPUT_DEVICE_DIGITAL_MICROPHONE_2 ((uint16_t)0x0200)
  14. #define INPUT_DEVICE_INPUT_LINE_1 ((uint16_t)0x0300)
  15. #define INPUT_DEVICE_INPUT_LINE_2 ((uint16_t)0x0400)
  16. #define INPUT_DEVICE_DIGITAL_MIC1_MIC2 ((uint16_t)0x0800)
  17. #define AUDIO_VOLUME 80
  18. void AUDIO_IO_Write(uint8_t Addr, uint16_t Reg, uint16_t Value);
  19. uint16_t AUDIO_IO_Read(uint8_t Addr, uint16_t Reg);
  20. uint8_t WM8994_Config(void);
复制代码

main.c

  1. /* 添加 wm8994 驱动头文件 */
  2. /* USER CODE BEGIN Includes */
  3. #include "wm8994.h"
  4. ……
  5. /* 增加音频数据流暂存空间 */
  6. /* USER CODE BEGIN PV */
  7. /* Private variables -------------------------------------------
  8. --------------*/
  9. uint8_t AudioBuf[2*2*48] = {0}; //2 CH*16bit*48KHz
  10. /* USER CODE BEGIN 4 */
  11. /**
  12. * @brief Writes a single data.
  13. * @param Addr: I2C address
  14. * @param Reg: Reg address
  15. * @param Value: Data to be written
  16. * @retval None
  17. */
  18. void AUDIO_IO_Write(uint8_t Addr, uint16_t Reg, uint16_t Value)
  19. {
  20. uint16_t tmp = Value;
  21. Value = ((uint16_t)(tmp >> 8) & 0x00FF);
  22. Value |= ((uint16_t)(tmp << 8)& 0xFF00);
  23. /* Check the communication status */
  24. if(HAL_FMPI2C_Mem_Write(&hfmpi2c1, Addr, (uint16_t)Reg,
  25. FMPI2C_MEMADD_SIZE_16BIT, (uint8_t*)&Value, 2, 1000) != HAL_OK)
  26. {
  27. /* De-initialize the I2C communication bus */
  28. HAL_FMPI2C_DeInit(&hfmpi2c1);
  29. /* Re-Initialize the I2C communication bus */
  30. MX_FMPI2C1_Init();
  31. }
  32. }
  33. /**
  34. * @brief Reads a single data.
  35. * @param Addr: I2C address
  36. * @param Reg: Reg address
  37. * @retval Data to be read
  38. */
  39. uint16_t AUDIO_IO_Read(uint8_t Addr, uint16_t Reg)
  40. {
  41. uint16_t read_value = 0, tmp = 0;
  42. if(HAL_FMPI2C_Mem_Read(&hfmpi2c1, Addr, (uint16_t)Reg,
  43. FMPI2C_MEMADD_SIZE_16BIT, (uint8_t*)&read_value, 2,
  44. 1000) !=HAL_OK)
  45. {
  46. /* De-initialize the I2C communication bus */
  47. HAL_FMPI2C_DeInit(&hfmpi2c1);
  48. /* Re-Initialize the I2C communication bus */
  49. MX_FMPI2C1_Init();
  50. }
  51. tmp = ((uint16_t)(read_value >> 8) & 0x00FF);
  52. tmp |= ((uint16_t)(read_value << 8)& 0xFF00);
  53. read_value = tmp;
  54. return read_value;
  55. }
  56. /**
  57. * @brief Initializes Audio low level.
  58. */
  59. void AUDIO_IO_Init(void)
  60. {
  61. //Already defined in MX_FMPI2C1_Init().
  62. }
  63. void AUDIO_IO_Delay(uint32_t Delay)
  64. {
  65. HAL_Delay(Delay);
  66. }
  67. uint8_t WM8994_Config(void)
  68. {
  69. uint8_t ret = HAL_ERROR;
  70. uint32_t deviceid = 0x00;
  71. /* wm8994 codec initialization */
  72. deviceid = wm8994_ReadID(AUDIO_I2C_ADDRESS);
  73. if((deviceid) == WM8994_ID)
  74. {
  75. /* Reset the Codec Registers */
  76. wm8994_Reset(AUDIO_I2C_ADDRESS);
  77. ret = HAL_OK;
  78. }
  79. else
  80. {
  81. ret = HAL_ERROR;
  82. }
  83. if(ret == HAL_OK)
  84. {
  85. /* Initialize the codec internal registers */
  86. wm8994_Init(AUDIO_I2C_ADDRESS,
  87. INPUT_DEVICE_INPUT_LINE_1|OUTPUT_DEVICE_AUTO, AUDIO_VOLUME,
  88. I2S_AUDIOFREQ_48K);
  89. }
  90. if(wm8994_SetMute(AUDIO_I2C_ADDRESS, AUDIO_MUTE_OFF) != 0)
  91. {
  92. ret = HAL_ERROR;
  93. }
  94. return ret;
  95. }
  96. /* USER CODE END 4 */
  97. HAL_I2SEx_TransmitReceive_DMA(&hi2s2,(uint16_t
  98. *)AudioBuf,(uint16_t *)AudioBuf,(sizeof(AudioBuf)/2));

  99. if( WM8994_Config() != HAL_OK)
  100. {
  101. Error_Handler();
  102. }
复制代码

wm8994.h
  1. /* 修改头文件路径 */
  2. //#include "../Common/audio.h"
  3. #include "audio.h"
  4. #include "main.h"
  5. ……
  6. /* 修改函数声明 */
  7. //uint8_t AUDIO_IO_Read(uint8_t Addr, uint16_t Reg);
  8. uint16_t AUDIO_IO_Read(uint8_t Addr, uint16_t Reg);
复制代码


wm8994.c w    c wm8994_Init

  1. /* 修改参数,降低放大倍数*/
  2. case INPUT_DEVICE_INPUT_LINE_1 :
  3. ……
  4. /* Disable mute on IN1L_TO_MIXINL and +30dB on IN1L PGA
  5. output */
  6. //counter += CODEC_IO_Write(DeviceAddr, 0x29, 0x0035);
  7. counter += CODEC_IO_Write(DeviceAddr, 0x29, 0x0033);
  8. /* Disable mute on IN1R_TO_MIXINL, Gain = +30dB */
  9. //counter += CODEC_IO_Write(DeviceAddr, 0x2A, 0x0035);
  10. counter += CODEC_IO_Write(DeviceAddr, 0x2A, 0x0033);
  11. ……
  12. /* 修改参数,避免后续操作将麦克风偏置 1 关闭*/
  13. /* Enable bias generator, Enable VMID, Enable HPOUT1 (Left)
  14. and Enable HPOUT1 (Right) input stages */
  15. /* idem for Speaker */
  16. //power_mgnt_reg_1 |= 0x0303 | 0x3003;
  17. if(input_device > 0)
  18. power_mgnt_reg_1 |= 0x0303 | 0x3003 | 0x0010;
  19. else
  20. power_mgnt_reg_1 |= 0x0303 | 0x3003;
复制代码


由上述添加及修改,可知在利用 STM32CubeMX 配置、生成工程后,I2S 数据接收和发送实现方便,只需要调用 HAL 库提供的 API 即可。工作较多集中在 STM32 的音频接口了解和编解码器功能配置方面。编解码器方面,一般编解码器厂商会有文档、配套工具或者配置例程提供。

4.2.2 实现 2
这种架构实现例,可参考 Cube 软件包中提供的例程,不再做展开介绍。例程路径如下:
1.STM32Cube_FW_F4_V1.16.0\Projects\STM32F401-Discovery\Applications\Audio\Audio_playback_and_record
2.STM32Cube_FW_F4_V1.16.0\Projects\STM32F411E-Discovery\Applications\Audio\Audio_playback_and_record


四 低功耗设计
不同功耗模式下,I2S 工作情况如下表。


8O{X7XO9IE9GC5MBS(98MDL.png

注1. 不同系列 STM32,低功耗模式有差异,具体以参考手册为准。
注2. 批处理模式(BAM)并非所有 STM32 产品都支持,支持情况请以对应型号的 STM32 参考手册中描述为准。BAM能够实现在睡眠模式下,批量获取数据,然后再退出睡眠模式进行运算处理等操作。能够进一步实现功耗的降低。更多关于 BAM 介绍可参考 RM0430。


五 小结
在 STM32 音频开发中,结合 STM32CubeMX 和 Cube 软件包中提供的例程,容易完成基于 STM32 的音频平台搭建。
I2S 各协议时序清晰,开发过程中,出现异常时,开发者可以先利用示波器,判断波形对应时序是否正确,以此缩小问题范围,加快问题定位及解决。






收藏 评论0 发布时间:2022-2-15 20:36

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版