首页 > Python资料 博客日记

STM32 使用IIS实现INMP441模块全速音频采样(附python上位机)

2024-07-07 17:00:05Python资料围观95

文章STM32 使用IIS实现INMP441模块全速音频采样(附python上位机)分享给大家,欢迎收藏Python资料网,专注分享技术知识

前言

本文在stm32平台上,利用IIS协议和INMP441模块,实现了对于音频的采样,并通过串口将数据实时返回电脑

本文只是一个初学者的经验分享,希望为同样刚接触相关协议的朋友提供一些帮助,有错误的地方还请包涵

本文是在另一位大佬工作基础上的改进版,建议先阅读该文章

使用STM32的I2S协议读取麦克风INMP441-CSDN博客

在其中已经将IIS协议、工程基本配置讲的非常详细,一些基础的内容本文不再赘述

最后,本文附上了笔者使用的python上位机,里面实现了一些基础功能

需要强调的基本概念

需要提前说明的是,UART速率过慢,不适合用于音频到上位机的实时传输,本文仅处于初级开发阶段,因此未作深入。如果要做相关开发,可以采用IIS采集 - IIS发送,等等方法。

此外,不要使用性能过差的平台做音频相关的深度开发,内存、处理速度、接口资源都是问题。

1.IIS周期

IIS个三根通信相关的引脚,时钟SCK、帧选择WS、数据SD

帧选择信号WD控制左右声道,当WD=0时,设置为左声道的模块输出;WD=1时,右声道输出;帧的大小受IIS帧格式影响,如果是24bit data on 32 frame的格式,帧大小就是32bit,如果是16bit data on 16 bit frame格式,帧大小就是16bit;

在CUBEMX中的IIS配置界面,有一个参数Audio frequency,这个参数等于采样频率

那么有:

采样频率Fs = audio frequency

帧选择信号WS = Fs

时钟信号SCK = Fs * 2 * 帧大小,即一个采样周期里,左右声道各采集一帧

2.实时上传的信号速率

在IIS的采样频率为Fs = 8khz情况下,要实现音频数据的实时上传,需要满足两个条件:

① 同上位机的通讯速率大于采样数据的产生速率:

假设使用USART上传,并且只采集单声道数据,对于INMP441的24位ADC数据,我们需要使用uint32型发送数据,那么数据产生速率为:

32 * 8000 = 256000 bit/s

UART发送一字节数据,最少需要1个开始位和1个停止位,那么UART串口波特率必须满足:

256000 / 8 * 10 = 320000 bit/s,

对于其他通讯协议,同理

②中断处理时间小于采样数据产生时间:

HAL库处理中断需要绕很大一个圈子:

触发中断 - 进入DMA通道IRQ - 进入具体DMA连接IRQ - 各种判断 - 进入半完成/完成中断

这个过程非常耗时,再加上UART传输比较慢,很可能无法及时处理中断,如下图

这里就是处理速度过慢,即使用了双缓冲,也不得不抛弃一半的数据,只能达到4khz的数据回报率

解决方法

对于问题1,我们提高串口波特率即可,对于本文所使用的STM32f103,可以在CUBEMX中观察到最大波特率范围,我们这里设置为360000bit/s

对于问题2,我们可以通过增大缓冲区的方式,将中断处理耗时平摊到每一次采集上。但如果还想给程序加上RTOS,或者临时去处理些其他任务,还是用更好的芯片吧

将缓冲区大小增加到512后,可以从图中看到,不再出现无法及时响应中断的问题,跑满了8khz

程序实现

请结合之前贴出的文章阅读,这里只指出修改了的地方

首先,DMA传输方式用half word就可以了

程序中用增加半完成中断的方式做了双缓冲

uint16_t dma_buffer[512];
uint32_t val24[2][64];
int val32[2][64];
int cb_cnt=0;
extern UART_HandleTypeDef huart1;
/* 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 */
//一组采样数据,左通道2个u16,右通道2个u16
void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
  if (hi2s == &hi2s2) {
    cb_cnt++;
    // 选择当前使用的缓冲区
    int offset = 0;

    for (int i = 0; i < 64; i++) {
      val24[0][i] = dma_buffer[offset + 4 * i];
      val24[0][i] = val24[0][i] << 8;
      val24[0][i] = val24[0][i] + (dma_buffer[offset + 4 * i + 1] >> 8);

      if (val24[0][i] & 0x800000) {
        val32[0][i] = 0xff000000 | val24[0][i];
      } else {
        val32[0][i] = val24[0][i];
      }
    }

    HAL_UART_Transmit(&huart1, (uint8_t*)&val32[0][0], 256, 0xff);
  }
}

void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
{
  if (hi2s == &hi2s2) {
    cb_cnt++;
    // 选择当前使用的缓冲区
    int offset = 256;

    for (int i = 0; i < 64; i++) {
      val24[1][i] = dma_buffer[offset + 4 * i];
      val24[1][i] = val24[1][i] << 8;
      val24[1][i] = val24[1][i] + (dma_buffer[offset + 4 * i + 1] >> 8);

      if (val24[1][i] & 0x800000) {
        val32[1][i] = 0xff000000 | val24[1][i];
      } else {
        val32[1][i] = val24[1][i];
      }
    }

    HAL_UART_Transmit(&huart1, (uint8_t*)&val32[1][0], 256, 0xff);
  }
}

最后,主函数中仅对DMA开启函数的调用做了一些修改

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_DMA_Init();
  MX_I2S2_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	HAL_I2S_Receive_DMA(&hi2s2, (uint16_t*)dma_buffer, 512); // 双缓冲区,共512个16位数据
	
  while (1)
  {
		HAL_Delay(100);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

上位机

注意,带上位机版本的stm32程序与前文中不同,请和上位机一同下载

上位机所需库:pyserial、wave、numpy、scipy(如果要做fft)

工程文件及功能:

server.py - 主程序,包括了菜单支持

SerialRec.py - 包含串口通讯类,在这里修改串口通讯参数

txt2wave.py - 将txt文件转换成音频,做了一个简单的高通滤波,不过没什么用

txt_fft_viewer.py / wav_fft_viewer - 两个用来观察频域的文件,不是很有用,真要做相关操作还是自己写吧

使用:

运行server.py,键盘根据提示输入,直到打开串口

串口参数可以在SerialRec.py里面修改全局变量,默认用的360000波特率

先使用功能1接收采样数据,接收数据量由 单片机完成中断中的停止条件 决定

最后的判断就是终止条件,这里设置调用半完成+完成中断共1000次,每次发送64次采样,即发送64000个采样数据,在8000hz采样率下,共发送8s

接收完毕后使用功能2将原始数据转换成整型

最后使用功能3将数据保存到txt文件,默认保存到txtEaxample文件夹下

txt2wave.py中,修改文件名,并按情况修改采样率,音量调节值,和高通滤波器截至频率

运行该文件,转换后的音频保存到waveExample下

8000hz采样率下的音质还是不错的

最后,贴出下载链接,偷懒就放百度网盘了

删除链接中的中文即可

链接:https://pan.baidu.co防m/s/1EY5Y_uJyCt吞V2BngGNeJH3A?pwd=ais2 
提取码:ais2 


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

标签:

相关文章

本站推荐