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);