【Follow me第二季第4期】任务四 RGB LED亮度显示PDM麦克风收到的声音大小
[复制链接]
本帖最后由 Netseye 于 2024-12-6 16:11 编辑
要在 Arduino Nano RP2040 Connect 上通过 RGB LED 显示 PDM 麦克风 收到的声音大小,可以通过读取麦克风的音量数据,并根据音量调整 RGB LED 的颜色和亮度。
主要步骤:
-
读取 PDM 麦克风音频数据。
-
计算音频信号的强度(音量)。
-
调整 RGB LED 的颜色和亮度,根据音量大小显示不同的效果。
实现
计算声音的大小(音量或振幅)通常需要对采样数据进行处理。在数字音频处理中,声音的大小可以通过计算振幅的平均值或最大值来近似表示。以下是几种常见的计算方法:
1. 峰值振幅法(Peak Amplitude)
计算采样数据中的最大或最小振幅,取绝对值的最大值。
int16_t getMaxAmplitude(short *buffer, int length) {
int16_t maxAmplitude = 0;
for (int i = 0; i < length; i++) {
int16_t amplitude = abs(buffer[i]);
if (amplitude > maxAmplitude) {
maxAmplitude = amplitude;
}
}
return maxAmplitude; // 返回最大振幅
}
-
优点:简单、直观。
-
缺点:容易受短时间内的尖锐噪声影响。
2. 均方根法(RMS,Root Mean Square)
RMS 是衡量声音能量的常用方法,它计算采样值的平方平均再开方,能更稳定地反映声音的大小。
float getRMSAmplitude(short *buffer, int length) {
long sum = 0;
for (int i = 0; i < length; i++) {
sum += (long)buffer[i] * buffer[i]; // 计算平方和
}
float rms = sqrt(sum / (float)length); // 计算平方平均值并开方
return rms;
}
-
优点:更稳定,能更准确地反映整体音量。
-
缺点:计算稍微复杂一些。
3. 平均绝对振幅法(Mean Absolute Amplitude)
计算所有采样值的绝对值平均。
float getAverageAmplitude(short *buffer, int length) {
long sum = 0;
for (int i = 0; i < length; i++) {
sum += abs(buffer[i]); // 计算绝对值和
}
return sum / (float)length; // 返回平均值
}
-
优点:简单且能消除正负波形的抵消影响。
-
缺点:没有 RMS 稳定,但比峰值法好。
能够更灵敏一些我们采用峰值振幅法(Peak Amplitude) 来计算音量
您可以根据 音量 值的大小划分不同的等级。例如,设置 4 个等级:
-
将 音量 值映射到 LED 亮度或颜色:
根据音量等级,设置 LED 的颜色或亮度。例如:
-
低音量:红色 LED 亮起
-
中低音量:绿色 LED 亮起
-
中高音量:蓝色 LED 亮起
-
高音量:RGB 全亮
代码示例:
以下代码实现了通过 RGB LED 显示声音的强度,根据声音的大小变化调整 LED 的亮度和颜色。
#include <PDM.h>
#include <WiFiNINA.h>
// 默认输出通道数
static const char channels = 1;
// 默认 PCM 输出频率
static const int frequency = 20000;
// 用于读取样本的缓冲区,每个样本是 16 位
short sampleBuffer[512];
// 已读取的音频样本数量
volatile int samplesRead;
void setup() {
Serial.begin(9600); // 初始化串口通信
while (!Serial); // 等待串口连接
pinMode(LEDR, OUTPUT); // 设置红色 LED 引脚为输出
pinMode(LEDG, OUTPUT); // 设置绿色 LED 引脚为输出
pinMode(LEDB, OUTPUT); // 设置蓝色 LED 引脚为输出
// 配置数据接收回调函数
PDM.onReceive(onPDMdata);
// 初始化 PDM 麦克风:
// - 一个通道(单声道模式)
// - 20 kHz 采样率
if (!PDM.begin(channels, frequency)) {
Serial.println("无法启动 PDM!");
while (1); // 启动失败时停止程序
}
}
void loop() {
// 等待读取样本
if (samplesRead) {
float rms = getMaxAmplitude(sampleBuffer, samplesRead);
controlLEDs(rms);
Serial.println(rms);
// 清空已读取的样本数量
samplesRead = 0;
}
}
void onPDMdata() {
// 查询可用字节数
int bytesAvailable = PDM.available();
// 从 PDM 麦克风读取数据到样本缓冲区
PDM.read(sampleBuffer, bytesAvailable);
// 16 位样本,每个样本占 2 字节
samplesRead = bytesAvailable / 2;
}
int16_t getMaxAmplitude(short *buffer, int length) {
int16_t maxAmplitude = 0;
for (int i = 0; i < length; i++) {
int16_t amplitude = abs(buffer[i]);
if (amplitude > maxAmplitude) {
maxAmplitude = amplitude;
}
}
return maxAmplitude; // 返回最大振幅
}
void controlLEDs(float rms) {
// 按 RMS 值进行分级
if (rms < 300) {
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, LOW);
}
else if (rms < 500) {
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, LOW);
}
else if (rms < 3000) {
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, HIGH);
}
else {
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, HIGH);
}
}
代码说明:
-
初始化 PDM 麦克风:
-
PDM.begin(1, 16000) 启动单声道,采样率为 16kHz。
-
设置回调函数 PDM.onReceive(onPDMdata) 处理采样数据。
-
计算最大振幅:
-
getMaxAmplitude() 函数计算采样数据中的最大振幅,反映音频信号的强度。
-
RGB LED 控制:
-
setLEDColor() 函数将音量值映射到 LED 的亮度。map() 函数将音量值(0 到 255)映射到 RGB LED 的亮度值。
-
使用红色和绿色 LED 显示不同的颜色,蓝色 LED 不随音量变化。
-
音量和颜色映射:
-
随着音量的增大,红色和绿色 LED 的亮度增亮,从而实现声音大小与 RGB LED 亮度和颜色的动态显示。
调试:
-
上传代码:将代码上传到 Arduino Nano RP2040 Connect 板子。
-
串口监视器:打开串口监视器查看音量数据。
-
LED 反馈:观察 RGB LED 的颜色和亮度变化,根据麦克风拾取到的音频信号显示不同的颜色和亮度。
|