当前位置:   article > 正文

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

inmp441 stm32

前言

本文在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就可以了

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

  1. uint16_t dma_buffer[512];
  2. uint32_t val24[2][64];
  3. int val32[2][64];
  4. int cb_cnt=0;
  5. extern UART_HandleTypeDef huart1;
  6. /* USER CODE END PV */
  7. /* Private function prototypes -----------------------------------------------*/
  8. void SystemClock_Config(void);
  9. /* USER CODE BEGIN PFP */
  10. /* USER CODE END PFP */
  11. /* Private user code ---------------------------------------------------------*/
  12. /* USER CODE BEGIN 0 */
  13. //一组采样数据,左通道2个u16,右通道2个u16
  14. void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
  15. {
  16. if (hi2s == &hi2s2) {
  17. cb_cnt++;
  18. // 选择当前使用的缓冲区
  19. int offset = 0;
  20. for (int i = 0; i < 64; i++) {
  21. val24[0][i] = dma_buffer[offset + 4 * i];
  22. val24[0][i] = val24[0][i] << 8;
  23. val24[0][i] = val24[0][i] + (dma_buffer[offset + 4 * i + 1] >> 8);
  24. if (val24[0][i] & 0x800000) {
  25. val32[0][i] = 0xff000000 | val24[0][i];
  26. } else {
  27. val32[0][i] = val24[0][i];
  28. }
  29. }
  30. HAL_UART_Transmit(&huart1, (uint8_t*)&val32[0][0], 256, 0xff);
  31. }
  32. }
  33. void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
  34. {
  35. if (hi2s == &hi2s2) {
  36. cb_cnt++;
  37. // 选择当前使用的缓冲区
  38. int offset = 256;
  39. for (int i = 0; i < 64; i++) {
  40. val24[1][i] = dma_buffer[offset + 4 * i];
  41. val24[1][i] = val24[1][i] << 8;
  42. val24[1][i] = val24[1][i] + (dma_buffer[offset + 4 * i + 1] >> 8);
  43. if (val24[1][i] & 0x800000) {
  44. val32[1][i] = 0xff000000 | val24[1][i];
  45. } else {
  46. val32[1][i] = val24[1][i];
  47. }
  48. }
  49. HAL_UART_Transmit(&huart1, (uint8_t*)&val32[1][0], 256, 0xff);
  50. }
  51. }

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

  1. int main(void)
  2. {
  3. /* USER CODE BEGIN 1 */
  4. /* USER CODE END 1 */
  5. /* MCU Configuration--------------------------------------------------------*/
  6. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  7. HAL_Init();
  8. /* USER CODE BEGIN Init */
  9. /* USER CODE END Init */
  10. /* Configure the system clock */
  11. SystemClock_Config();
  12. /* USER CODE BEGIN SysInit */
  13. /* USER CODE END SysInit */
  14. /* Initialize all configured peripherals */
  15. MX_GPIO_Init();
  16. MX_DMA_Init();
  17. MX_I2S2_Init();
  18. MX_USART1_UART_Init();
  19. /* USER CODE BEGIN 2 */
  20. /* USER CODE END 2 */
  21. /* Infinite loop */
  22. /* USER CODE BEGIN WHILE */
  23. HAL_I2S_Receive_DMA(&hi2s2, (uint16_t*)dma_buffer, 512); // 双缓冲区,共512个16位数据
  24. while (1)
  25. {
  26. HAL_Delay(100);
  27. /* USER CODE END WHILE */
  28. /* USER CODE BEGIN 3 */
  29. }
  30. /* USER CODE END 3 */
  31. }

上位机

注意,带上位机版本的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 

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号