使用STM32的I2S协议读取麦克风INMP441

这篇具有很好参考价值的文章主要介绍了使用STM32的I2S协议读取麦克风INMP441。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

介绍

本文将详细介绍使用STM32的硬件I2S协议,从麦克风模块INMP441读取音频信号的步骤和避坑指南。

点这里,从Github下载工程文件

百度网盘:
链接:https://pan.baidu.com/s/1cglAGirn6bTQhIEpWDb7Ig?pwd=k9gh
提取码:k9gh

事前准备

硬件准备

  1. INMP441模块: 使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网

  2. 主控芯片:我用的是STM32F103ZET6,可以在STM的选型手册上看到哪些芯片带I2S接口,支持I2S协议的最低配STM32芯片是STM32F103R系列。

  3. 用来下载程序的Jlink-OB和用于串口通信的USB转TTL模块。

软件准备

  1. 本文采用STM32CubeMX生成初始代码 + KeilV5编程的方式,开发STM32程序。
  2. 使用Cube生成一个Keil v5 Project,并调好printf串口打印功能,方便后续开发。这里需读者具备开发STM32程序的基本能力。

麦克风模块的I2S通信原理

麦克风模块本质上是一个模数转换器(ADC),不断的把声波震动的幅值转换成数字信号,再通过I2S总线把数据发送到主控芯片。本案例中,I2S总线的主机是STM32,从机是麦克风模块。

除了电源引脚和芯片使能引脚,INMP441的引脚还包括SCK、SD、WS和L\R:
SCK:I2S时钟线,是由主机产生的高频方波,用来控制每位数据的传输时序
SD:I2S数据线,从机通过这根线把AD采样值发送给主机
WS:I2S声道选择线,I2S协议可以传输左右两个声道的数据,WS信号是由主机发送给从机的,从机根据WS的电平高低,判断当前数据帧发送左声道还是右声道数据,WS低电平时,从机发送左声道数据,高电平发送右声道。
L/R:芯片左右声道选择线,每个麦克风只能检测一处声源,因此若要进行双声道录音,就要使用两个模块,一左一右放置,然后按照下图连线:
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网
为了让麦克风知道自己是在左边还是右边,就需要将L/R置高(右声道)或置低(左声道)。以上图为例,左边麦克风的L/R接地了,因此在WS=0期间它会将自身的AD采样值发送到SD线,发送完成后它会将SD设置为高阻态(相当于悬空),让出SD线。随后WS=1时,右边的麦克风通过SD线发送右声道数据。因此主机是交替采集左右声道的数据,且不会发生信号冲突。下图是麦克风的I2S时序图:
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网
如上图,SCK是由主机生成的高频方波信号,WS的周期是SCK的64倍,因此在WS=1或WS=0的期间,SCK会经历32个周期。从WS下降沿之后的第2个SCK上升沿开始,主机会在每个SCK上升沿读取一位数据,直到第25个SCK上升沿后,主机共读取到了24bit数据。INMP441内置的ADC是24位的,这24bit数据以大端模式传输,组成了左声道的一个采样值。同理,从WS上升沿之后的第2个SCK上升沿开始,主机开始读取右声道的采样值。下图是来自STM32手册的I2S时序图:
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网
由上图可知,STM32也是从第2个SCK上升沿开始读取数据,与麦克风的协议一致。在WS=0期间CLK共出现32次上升沿,且读取动作滞后了一个SCK周期,导致WS=0期间至多读取31bit数据,因此如果采样值是32bit的,则需要在下一次读取时第一个SCK上升沿时完成最后一位的读取。这解释了上图中,为何MSB receive的左边一位是LSB receive,这是上个数据的最低位。

下图是使用示波器采集到的I2S波形实例。蓝色是SD信号,黄色是CLK,示波器触发源被设置为WS下降沿,图中波形对应一个完整的24位数值。可以从图中Ch2第二个上升沿开始读取24位数据。麦克风发出的24位采样值是带符号的,因此下面第一张图中,最高是1,代表这个数值是负数,第二张图中的采样值是正数。
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网

使用Cube设置STM32的I2S通信

以上是对I2S通信原理的解释,下面开始创建STM32程序。打开Cube,按照下图设置:
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网
上图中,8kHz频率是最低选项,这是为了方便测试,可以调通后再提高。添加DMA以提高数据存取速度,DMA Mode选择Circular,这样STM32会连续不断的进行DMA存取操作,不必频繁调用读取命令。Data With暂时选择Word,也就是32位的。

在Cube中设置完成后,点击生成代码,然后在Keil中打开Project。
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网
可以看到在Keil中,出现了i2s的库文件stm32f1xx_hal_i2s.c和初始化文件i2s.c,其中stm32f1xx_hal_i2s.c内有各个i2s的驱动函数和使用说明,i2s.c中是Cube根据上面的设置,生成的I2S通信初始化函数。

电路连接

在Cube上查看STM32的引脚分配如下:
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网
本次案例不需双声道,只接一个麦克风,按照下图接线:
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网
如上图,建议给SD线接一个10k的下拉电阻(模块本身是不下拉的)。这是因为,在每个24bit数据传输完成后,麦克风会立即将SD线设为高阻态,此时如果没有下拉,SD线会缓慢衰减到0V(如左下图所示),导致STM32会在第25bit读取到一个“1”,对开发工作造成误导。 右下图是不下拉时读取到的数据,图中数据最后的16进制“80”即二进制的1000 0000,这个1是多余的。
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网
物理连线:
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网

编写代码

打开main.c,自己添加的代码都要放在Cube注释指定的位置,否则再次用Cube生成代码时会被覆盖。先放上连续的main.c文件(省略了最后一些自动生成的代码),下文会对各个需要添加代码的地方进行说明。

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "i2s.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
//#include "stm32f1xx_hal.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
uint32_t dma[4];
uint32_t val24;
int val32;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
unsigned cb_cnt=0;
//I2S接收完成回调函数
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
{
	if(hi2s==&hi2s2){
		cb_cnt++;//回调次数计数
		//将两个32整型合并为一个
		//dat32 example: 0000fffb 00004f00
		val24=(dma[0]<<8)+(dma[1]>>8);
		//将24位有符号整型扩展到32位
		if(val24 & 0x800000){//negative
			val32=0xff000000 | val24;
		}else{//positive
			val32=val24;
		}
		//以采样频率的十分之一,串口发送采样值
		if(cb_cnt%10==0)
			printf("%d\r\n",val32);
	}
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_DMA_Init();
  MX_I2S2_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  //开启DMA传输
  HAL_I2S_Receive_DMA(&hi2s2,(uint16_t*)dma,4);	
  while (1)
  {
  	HAL_Delay(10);
	
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
//后面都是Cube自动生成的代码,省略

以下是详细介绍:
加入stdio.h文件,是为了避免printf函数出现警告。仅添加这一行代码不能实现printf串口输出功能,具体操作可以去网上搜索。

/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */

添加全局变量,数组dma[]是一个缓冲区,芯片将硬件I2S读取到的数据通过DMA传送到这个数组,无须主程序的干预。val24是用来存储24位原始采样值的。val32是将val24扩展到32位得到的结果。具体数据转换逻辑见下文。

/* USER CODE BEGIN PV */
uint32_t dma[4];
uint32_t val24;
int val32;
/* USER CODE END PV */

在main函数前,添加I2S接收回调函数。在回调函数之前定义了全局变量cb_cnt,用来计算回调函数被执行的次数。回调函数中的代码是把原始的采样数据转换为32位有符号整型,然后以十分之一的采样速率,把转换结果发送到串口,用来在上位机的串口绘图器上观察波形。

/* USER CODE BEGIN 0 */
unsigned cb_cnt=0;
//I2S接收完成回调函数
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
{
	if(hi2s==&hi2s2){
		cb_cnt++;//回调次数计数
		//将两个32整型合并为一个
		//dat32 example: 0000fffb 00004f00
		val24=(dma[0]<<8)+(dma[1]>>8);
		//将24位有符号整型扩展到32位
		if(val24 & 0x800000){//negative
			val32=0xff000000 | val24;
		}else{//positive
			val32=val24;
		}
		//以采样频率的十分之一,串口发送采样值
		if(cb_cnt%10==0)
			printf("%d\r\n",val32);
	}
}
/* USER CODE END 0 */

主函数中,仅需在while循环前加了一个函数HAL_I2S_Receive_DMA(),以开启DMA传输:

  HAL_I2S_Receive_DMA(&hi2s2,(uint16_t*)dma,4);	

由于前面将DMA Mode设置为了Circular,因此只需要调用一次该函数,就能开启DMA的连续存取。该函数的原文注释如下:

/**
  * @brief  Receive an amount of data in non-blocking mode with DMA
  * @param  hi2s pointer to a I2S_HandleTypeDef structure that contains
  *         the configuration information for I2S module
  * @param  pData a 16-bit pointer to the Receive data buffer.
  * @param  Size number of data sample to be sent:
  * @note   When a 16-bit data frame or a 16-bit data frame extended is selected during the I2S
  *         configuration phase, the Size parameter means the number of 16-bit data length
  *         in the transaction and when a 24-bit data frame or a 32-bit data frame is selected
  *         the Size parameter means the number of 24-bit or 32-bit data length.
  * @note   The I2S is kept enabled at the end of transaction to avoid the clock de-synchronization
  *         between Master and Slave(example: audio streaming).
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_I2S_Receive_DMA(I2S_HandleTypeDef *hi2s, uint16_t *pData, uint16_t Size)

根据以上说明,最后一个参数size的单位是由数据帧的长度决定的。前面在Cube中设置的数据格式为24 Bits Data on 32 Bits Frame,因此DMA读取数据的总长度为size×4字节。前面定义的DMA缓冲区是一个长度为4的uint32_t型数组,缓冲区中的数据格式为:前2个元素表示左声道数据,后2个表示右声道数据。根据前面的电路图,本次试验只接了一个左声道的麦克风,因此右声道数据始终为0.以下是在数组dma[]中,左声道数据的示例:
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网
可以观察出,dma[0]左移8位 + dma[1]右移8位,就得到了24位采样值。例如将0000fff5 00007700合并为0xfff577:

val24 = (buf32[0]<<8) + (buf32[1]>>8);

在这个DMA数据获取的阶段非常容易出错,最终的数据格式和在Cube中设置的DMA Data With、main.c中DMA缓冲数组的元素类型和DMA接收函数的参数值size都有关系,在我之前的测试中,如果把缓冲区元素转为uint8_t,就会得到不一样的结果,24bit采样值被拆分为3个字节,且未必是高位字节在前、低位字节在后的排列顺序。总之需要小心修改,通过把数据打印到串口,来寻找规律。按照本文的配置,接收到的原始数据是用2个32位无符号整型表示一个采样值,这应该不是最佳的格式,但我没有继续试验,读者可以尝试在Cube中把Data With设为Half-Word再进行测试。

麦克风发送的数值是24位二进制补码的形式,二进制补码也是单片机存储有符号整型的方式,因此可以将采样值视为24位有符号整型,为了与C语言的整型格式一致,需要将这个24位整型扩展到32位,方法为:正数保持不变,对于负数在左边加一个0xff。例如24位数0xfff577,它的最高位(符号位)是1,代表负数,因此转换为0xffff f577,代码如下:

		if(val24 & 0x800000){//negative
			val32=0xff000000 | val24;
		}else{//positive
			val32=val24;
		}

在串口绘图器中查看音频波形

使用串口绘图工具SerialPlot查看声音波形,SerialPlot相当于一个带波形绘制功能的串口助手,它的下载和使用方法可以到网上搜到。由于声音采样速度比较快,在上面回调函数中我把串口发送频率设成了采样频率的十分之一。
使用STM32的I2S协议读取麦克风INMP441,stm32,单片机,物联网
可在图中看到波形随着外界声音而波动,声音越响幅值越大,这说明采集到的数据是正常的。

至此实验完成。本文使用STM32的硬件I2S协议和DMA功能实现了对INMP441音频数据的采集,并将数据化为了有符号整型格式,以便进一步处理。

平时比较忙,不常看消息,评论区回得慢或者没回,请见谅!文章来源地址https://www.toymoban.com/news/detail-784066.html

到了这里,关于使用STM32的I2S协议读取麦克风INMP441的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ESP32设备驱动——使用I2S播放音频的物联网应用

    在物联网应用中,使用嵌入式设备进行音频播放是一个常见的需求。ESP32是一款功能强大的嵌入式开发板,它集成了Wi-Fi和蓝牙功能,适用于物联网应用。本文将介绍如何在ESP32上使用I2S(Inter-IC Sound)接口来播放音频。 I2S是一种串行音频接口,用于高质量音频数据的传输。

    2024年03月21日
    浏览(62)
  • 【音视频开发】 I2S协议及标准

    1. 介绍 I2S(Inter—IC Sound)总线,是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准,该总线专门用于音频设备之间的音频数据传输。它采用了沿独立的导线传输时钟与数据信号的设计,通过将数据和时钟信号分离,避免了因时差诱发的失真,为用户节省了

    2024年02月12日
    浏览(44)
  • 音视频的简介及其I2S协议

         将针对声音的各种要求全部叠加到 DAC 和 ADC 芯片上,那么就会得到一个专门用于音频的芯片,也就是音频编解码芯片,音频 CODEC 的本质是 ADC 和 DAC.      采样率和采样位数就是衡量一款音频CODEC 最重要的指标。比如常见音频采样率有 8K、 44.1K、 48K、 192K 甚

    2024年02月02日
    浏览(47)
  • 物联网开发笔记(51)- 使用Micropython开发ESP32开发板之通过MAX98357 I2S音频模块播放音乐

    一、目的         这一节我们学习如何使用我们的ESP32开发板来播放音乐。 二、环境         ESP32 + MAX98357 I2S音频模块 + Thonny IDE + 几根杜邦线 接线方法: 三、代码     代码1,播放wav格式文件:wav格式文件几百K,别太大ESP32存储空间有限。 音频格式转换网站: 在线转

    2024年02月16日
    浏览(50)
  • 音频I2S

    前言 基于网上资料对相关概念做整理汇总,部分内容引用自文后文章。 学习目标:简单了解相关概念、相关协议。 1 概述        数字音频接口DAI,即Digital Audio Interfaces,顾名思义,DAI表示在板级或板间传输数字音频信号的方式。相比于模拟接口,数字音频接口抗干扰能力

    2024年02月03日
    浏览(49)
  • STM32驱动INMP441麦克风实现左右通道声音采集

    注意INMP441的第4引脚,用来选择左声道还是右声道。 代码使用cubemx生成 根据以上说明,最后一个参数size的单位是由数据帧的长度决定的。前面在Cube中设置的数据格式为24 Bits Data on 32 Bits Frame,因此DMA读取数据的总长度为size×4字节。前面定义的DMA缓冲区是一个长度为4的uint3

    2024年02月14日
    浏览(63)
  • 音频——I2S 标准模式(二)

    I2S 基本概念 飞利浦(I2S)标准模式 左(MSB)对齐标准模式 右(LSB)对齐标准模式 DSP 模式 TDM 模式 飞利浦 (I2S) 标准模式 数据在跟随 LRCLK 传输的 BCLK 的第二个上升沿时传输 MSB,其他位一直到 LSB 按顺序传 传输依赖于字长、BCLK 频率和采样率(BCLK=Fs x 声道数 x 采样位数) 在每个采样的

    2024年02月10日
    浏览(45)
  • 音频——I2S TDM 模式(六)

    I2S 基本概念 飞利浦(I2S)标准模式 左(MSB)对齐标准模式 右(LSB)对齐标准模式 DSP 模式 TDM 模式 TDM 一般有三种常用操作模式:TDM A mode ,TDM B mode 和 TDM C mode, 统称为TDM mode。 基于 TDM mode,可以配置支持 TDM(Time Division Multiplexing)数据传输。可以支持传输多个声道的数据,允许多个设

    2024年02月04日
    浏览(46)
  • 一文搞懂I2S通信总线

    目录 1、物理特性 2、常见的I2S数据格式 2.1、I2S Philips标准 2.2、左对齐(MSB)标准 2.3、右对齐(LSB)标准 之前我们讲过I2C通信总线,本篇博文将讲一讲I2C的变种:I2S总线。 I2C和I2S都是由Philips公司(2006年迁移到NXP)发布的串行总线,I2S是在I2C之后发布,I2S专为传输音频数据而

    2024年01月16日
    浏览(40)
  • 瑞芯微 | I2S-音频基础 -1

    最近调试音频驱动,顺便整理学习了一下i2s、alsa相关知识,整理成了几篇文章,后续会陆续更新。 喜欢嵌入式、Li怒晓得老铁可以关注一口君账号。 名称 含义 ADC(Analog to Digit Conversion) 模拟信号转换为数字信号 AEC(Acoustic Echo Cancellor) 回声消除 AGC(Automatic Gain Control) 自

    2024年03月14日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包