STM32F407ZGT6マイコンでI2Sインターフェースを使用したINMP441マイクモジュールの実装方法

INMP441マイクモジュールの概要

INMP441は高性能、低消費電力、デジタル出力、ボトムポート搭載の全方向性MEMSマイクロフォンです。この完全なINMP441ソリューションは、MEMSセンサー、信号調節回路、アナログ-デジタルコンバーター、アンチエイリアシングフィルター、電源管理、および業界標準の24ビットI²Sインターフェースで構成されています。I²Sインターフェースにより、INMP441はDSPやマイクロコントローラなどのデジタルプロセッサに直接接続でき、オーディオコーデックを必要としません。INMP441は高いSN比(信号対雑音比)を持ち、近距離アプリケーションに最適です。また、平坦な広帯域周波数応答を持つため、自然で高精細な音声を実現します。

ピン配置と機能

SCK: I²Sインターフェースのシリアルデータクロック
WS: I²Sインターフェースのシリアルデータワード選択
L/R: 左/右チャンネル選択。Lowレベル時にI²Sフレームの左チャンネルで信号を出力します。Highレベル時に右チャンネルで信号を出力します。
SD: I²Sインターフェースのシリアルデータ出力
VCC: 入力電源(1.8V~3.3V)
GND: 電源グラウンド

SerialPlotソフトウェアによる波形表示

取得した音声データはSerialPlotソフトウェアを使用して可視化できます。

CUBEMX設定手順

UARTの有効化

I2Sの有効化

実装コード

/* ユーザーコードヘッダー終了 */
/* インクルードファイル --------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "i2s.h"
#include "usart.h"
#include "gpio.h"

/* プライベートインクルード ----------------------------------------------------------*/
/* ユーザーコードインクルード開始 */
#include "stdio.h"
#include "control.h"
/* ユーザーコードインクルード終了 */

/* プライベート型定義 -----------------------------------------------------------*/
/* ユーザーPTD開始 */
uint8_t status, buffer, signal_flag = 0;

/* ユーザーPTD終了 */

/* プライベート定義 ------------------------------------------------------------*/
/* ユーザーPD開始 */

/* ユーザーPD終了 */

/* プライベートマクロ -------------------------------------------------------------*/
/* ユーザーPM開始 */

int fputc(int c, FILE* stream)
{
    HAL_UART_Transmit(&huart1, (const uint8_t*)&c, 1, 0xFFFF);
    return c;
}

/* ユーザーPM終了 */

/* プライベート変数 ---------------------------------------------------------*/

/* ユーザーPV開始 */

uint32_t audio_buffer[4];
uint32_t raw_value;

int processed_value;
/* ユーザーPV終了 */

/* プライベート関数プロトタイプ -----------------------------------------------*/
void SystemClock_Config(void);
/* ユーザーPFP開始 */

/* ユーザーPFP終了 */

/* プライベートユーザーコード ---------------------------------------------------------*/
/* ユーザーコード0開始 */
unsigned callback_counter = 0;

// I2S受信完了コールバック関数
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) {
    if(hi2s == &hi2s2) {
        callback_counter++;
        raw_value = (audio_buffer[0] << 8) + (audio_buffer[1] >> 8);
        
        if(raw_value & 0x800000) {
            processed_value = 0xff000000 | raw_value;
        } else {
            processed_value = raw_value;
        }
        
        // サンプリング周波数の1/10でシリアル送信
        if(callback_counter % 10 == 0) {
            printf("%d\r\n", processed_value / 32);
        }
        
        HAL_I2S_Receive(&hi2s2, (uint16_t*)audio_buffer, 4, 100);
    }
}
/* ユーザーコード0終了 */

/**
  * @brief  アプリケーションエントリーポイント
  * @retval int
  */
int main(void)
{
    /* ユーザーコード1開始 */
    /* ユーザーコード1終了 */

    /* MCU設定--------------------------------------------------------*/

    /* 全ペリフェラルのリセット、FlashインターフェースとSysTickの初期化 */
    HAL_Init();

    /* ユーザー初期化開始 */
    /* ユーザー初期化終了 */

    /* システムクロック設定 */
    SystemClock_Config();

    /* ユーザーSysInit開始 */
    /* ユーザーSysInit終了 */

    /* 設定済み全ペリフェラルの初期化 */
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_USART1_UART_Init();
    MX_I2S2_Init();
    MX_USART2_UART_Init();
    /* ユーザーコード2開始 */
    HAL_UART_Receive_IT(&huart2, &status, 1); // 割り込み有効化
    HAL_I2S_Receive_DMA(&hi2s2, (uint16_t*)audio_buffer, 4);
    /* ユーザーコード2終了 */

    /* 無限ループ */
    /* ユーザーWHILE開始 */
    while (1) {
        HAL_Delay(10);
    }
    /* ユーザーWHILE終了 */
    /* ユーザーコード3開始 */
}
/* ユーザーコード3終了 */

/**
  * @brief システムクロック設定
  * @retval None
  */
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /** メイン内部レギュレーター出力電圧設定
    */
    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

    /** RCC_OscInitTypeDef構造体で指定されたパラメータに従ってRCC発振器を初期化
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 4;
    RCC_OscInitStruct.PLL.PLLN = 168;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
    RCC_OscInitStruct.PLL.PLLQ = 4;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        Error_Handler();
    }

    /** CPU、AHB、APBバスクロックを初期化
    */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
    {
        Error_Handler();
    }
}

/* ユーザーコード4開始 */

/* ユーザーコード4終了 */

/**
  * @brief エラー発生時に実行される関数
  * @retval None
  */
void Error_Handler(void)
{
    /* ユーザーエラーハンドラーデバッグ開始 */
    /* HALエラー状態を報告するユーザー実装を追加できます */
    __disable_irq();
    while (1)
    {
    }
    /* ユーザーエラーハンドラーデバッグ終了 */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  assert_paramエラーが発生したソースファイル名とソース行番号を報告
  * @param  file: ソースファイル名へのポインタ
  * @param  line: assert_paramエラー行のソース行番号
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
    /* ユーザーコード6開始 */
    /* ファイル名と行番号を報告するユーザー実装を追加できます
       例: printf("パラメータ値エラー: ファイル %s 行 %d\r\n", file, line) */
    /* ユーザーコード6終了 */
}
#endif /* USE_FULL_ASSERT */

SDカード保存時の問題と解決策

2024年9月20日追記
SDカードにデータを保存する際に問題が発見されました。INMP441が継続的にデータを収集している状態で、単一の.txtファイルを作成する場合は問題ありませんが、先にフォルダを作成し、その中に.txtファイルを書き込む場合に問題が発生します。
解決策:フォルダ作成時にINMP441を一時停止します。

// DMA転送を中止
HAL_DMA_Abort(hi2s2.hdmarx);
// DMA転送を再開 I2Sを再起動
MX_DMA_Init();
MX_I2S2_Init();
HAL_I2S_Receive_DMA(&hi2s2, (uint16_t*)audio_buffer, 4);

タグ: STM32 I2S INMP441 MEMSマイク DMA

6月8日 17:58 投稿