本帖最后由 aramy 于 2026-4-10 11:01 编辑
昨天用FireBeetle 2 ESP32 P4开发板驱动了SSD1306的OLED屏幕,绘制了一个油门表的图像。今天在这个项目的基础上结合FireBeetle 2 ESP32 P4开发板上的麦克风制作一个环境噪音监测器。

一、硬件
看官方说明文档可以看见,开发板上有集成一颗MEMS PDM麦克风。连接到ESP32P4板子的12、9脚,使用I2S驱动。显示部分依然是SSD1306主控的OLED屏幕。

二、驱动麦克风
PDM麦克风只需要两个数据线即可驱动。12脚为时钟线;9脚为数据线。在ESP-IDF下使用I2S进行驱动,采样率使用16000,采样深度为16位,采集到的信号为单声道信号。
- void init_microphone(void)
- {
- i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
- ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
-
- i2s_pdm_rx_config_t pdm_rx_cfg = {
- .clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
- /* The default mono slot is the left slot (whose 'select pin' of the PDM microphone is pulled down) */
- .slot_cfg = I2S_PDM_RX_SLOT_PCM_FMT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
- .gpio_cfg = {
- .clk = PDM_CLK_PIN,
- .din = PDM_DATA_PIN,
- .invert_flags = {
- .clk_inv = false,
- },
- },
- };
- ESP_ERROR_CHECK(i2s_channel_init_pdm_rx_mode(rx_handle, &pdm_rx_cfg));
- ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
- }
复制代码
三、读取麦克风信息,转换为声强值
麦克风本质上是个ADC转换器,不停地将感知到的声音强度通过AD转换,转为一个具体的数字。采样率使用16000Hz,意味着每秒钟转换16000次,每个数据都是个16位的整形。本项目是测量环境的噪音值,所以这里将收集到一段时间内麦克风的值转换成声音强度信息。
- // 计算声音强度
- float calculateVolumeRMS(int16_t *buffer, int size)
- {
- // 第一步:计算直流偏置(平均值)
- float sum = 0.0f;
- for (int i = 0; i < size; i++)
- {
- sum += buffer[i];
- }
- float dc_offset = sum / size;
-
- // 第二步:去除 DC 偏置后计算 RMS
- float ac_sum = 0.0f;
- for (int i = 0; i < size; i++)
- {
- float sample = buffer[i] - dc_offset;
- ac_sum += sample * sample;
- }
- float rms = sqrtf(ac_sum / size);
-
- // 第三步:转换为分贝(使用更合适的参考值)
- if (rms < 1.0f)
- return -100.0f;
- return 20.0f * log10f(rms / 1000.0f); // 参考值改为 1000,可根据实际调整
- }
复制代码 这里计算出的结果也是分贝(db),但是这个值和物理声学中的分贝不一样。
| 计算值 | 对应环境情况 | | -60dB | 绝对安静 | | -40dB | 安静 | | -20dB | 嘈杂 | | 0dB | 吵闹 |
四、将声强信息映射到OLED屏幕的角度。
有了噪音值,就需要展示出来。将噪音值映射到0~180度的弧线上。这里取-40dB~0dB进行映射。将映射好的角度通过一条线进行绘制,这样就能够动态地看见环境噪音值了。
- // 最小有效音量 (静音): -40 dB
- // 最大有效音量 (满载): 0 dB
-
- float map_volume_to_angle(float db) {
- float min_db = -40.0f;
- float max_db = 0.0f;
-
- // 限幅
- if (db < min_db) db = min_db;
- if (db > max_db) db = max_db;
-
- // 归一化到 0.0 ~ 1.0
- float normalized = (db - min_db) / (max_db - min_db);
-
- // 映射到角度: 180度(左) 到 360度(右) -> 范围90度
- // 或者根据你的表盘设计:180度是0%,360度是100%
- float start_angle = 180.0f;
- float end_angle = 360.0f; // 或者 450.0f 如果表盘是半圆以上
-
- return start_angle + (normalized * (end_angle - start_angle));
- }
复制代码 五、效果演示 
六、源码
SSD1306.zip
|