本帖最后由 我是超超我最棒 于 2025-1-13 15:42 编辑
任务物料清单如下:
一根Micro USB 线
一块Arduino Nano RP2040 Connect开发板
一根自制跳线
本次活动的心得体会如下:
从技术学习角度看,一方面深入了解了传感器与外设交互的细节。像代码中对 IMU(惯性测量单元)的操作,不仅熟悉了其初始化流程,还掌握如何获取加速度计和陀螺仪的采样率数据,以及实时读取运动数据,这为开发涉及运动监测、姿态感知类项目,如可穿戴设备、智能运动器材等奠定基础。对于 PDM 麦克风的运用同样如此,知晓了从配置数据接收回调,到依据采样规则处理音频样本的全过程,在音频相关开发,像是简易录音设备、环境声音监测有了技术支撑。
编程思维上有很大提升,整个代码结构严谨,遵循 Arduino 典型的 setup () 与 loop () 框架。在 setup () 里有条不紊地完成初始化工作,确保后续程序稳定运行,这教会我们前期准备工作的全面性和重要性。loop () 函数内,通过巧妙的条件判断来处理不同传感器数据就绪情况,以及对音频样本阈值判断控制 LED,体现了事件驱动编程思维,让程序高效响应外部变化,而非盲目执行指令。
调试过程更是积累宝贵经验,遇到 IMU 初始化失败、PDM 启动异常等问题时,学会借助串口输出信息排查。比如 IMU 初始化不成功时在串口打印错误提示并阻塞程序,方便定位硬件连接、驱动配置错误;处理音频样本时,通过串口输出观察样本数据,辅助判断数据处理逻辑正误,明白了调试工具在开发中的 “眼睛” 作用,让看不见的代码流程和数据流转可视化。
项目整合视野得到拓展,这段代码融合多种功能,集传感器数据采集、音频处理、LED 控制于一体。意识到在实际物联网项目中,往往需要整合不同模块,各模块协同运作才能实现复杂系统功能,单一技术难以满足需求,为未来设计综合性更强的嵌入式项目积累信心与思路。
总之,编写此代码如同经历一场知识与技能的小型探险,从技术细节到宏观项目构建,全方位提升了 Arduino 开发能力。
任务一:搭建环境并Blink三色LED,以及踩坑
虽然踩了坑,回想起来还是很有收获的
blink
https://content.arduino.cc/assets/Blink.ino.elf.uf2
最后还是用最简单的方法让板子闪了起来。
任务二:学习IMU基础知识,调试IMU传感器,串口打印数据
有了上面的经验Arduino不是驾轻就熟的事。别忘了安装Arduino_LSM6DSOX的库
#include <Arduino_LSM6DSOX.h>
float Ax, Ay, Az;
float Gx, Gy, Gz;
void setup() {
Serial.begin(9600);
while(!Serial);
if (!IMU.begin()) {
Serial.println("初始化 IMU 失败!");
while (1);
}
Serial.print("加速度计采样率 = ");
Serial.print(IMU.accelerationSampleRate());
Serial.println("Hz");
Serial.println();
Serial.print("陀螺仪采样率 = ");
Serial.print(IMU.gyroscopeSampleRate());
Serial.println("Hz");
Serial.println();
}
void loop() {
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(Ax, Ay, Az);
Serial.println("加速度计数据: ");
Serial.print(Ax);
Serial.print('\t');
Serial.print(Ay);
Serial.print('\t');
Serial.println(Az);
Serial.println();
}
if (IMU.gyroscopeAvailable()) {
IMU.readGyroscope(Gx, Gy, Gz);
Serial.println("陀螺仪数据: ");
Serial.print(Gx);
Serial.print('\t');
Serial.print(Gy);
Serial.print('\t');
Serial.println(Gz);
Serial.println();
}
delay(500);
}
软件流程图如下:
开始
|-- 初始化串口通信,设置波特率为 9600
|-- 等待串口准备就绪
|-- 尝试初始化 IMU(惯性测量单元)
|-- 判断 IMU 初始化是否成功
|-- 若失败,串口输出 “初始化 IMU 失败!” 并进入死循环
|-- 若成功,执行以下操作:
|---- 串口输出加速度计采样率信息
|---- 串口输出陀螺仪采样率信息
进入循环(loop)
|-- 判断加速度计数据是否可用
|-- 若可用
|---- 读取加速度计数据(Ax、Ay、Az)
|---- 串口输出 “加速度计数据:” 以及 Ax、Ay、Az 的值
|-- 判断陀螺仪数据是否可用
|-- 若可用
|---- 读取陀螺仪数据(Gx、Gy、Gz)
|---- 串口输出 “陀螺仪数据: ” 以及 Gx、Gy、Gz 的值
|-- 程序延迟 500 毫秒
|-- 循环回到开头,重复上述判断与操作
任务三:学习PDM麦克风技术知识,调试PDM麦克风,通过串打印
麦克风这还是很特别的
Arduino代码如下,别忘了安装WIFININA的库
#include <WiFiNINA.h>
#include <PDM.h>
bool LED_SWITCH = false;
// default number of output channels
static const char channels = 1;
// default PCM output frequency
static const int frequency = 20000;
// Buffer to read samples into, each sample is 16-bits
short sampleBuffer[512];
// Number of audio samples read
volatile int samplesRead;
void setup() {
Serial.begin(9600);
pinMode(LEDB, OUTPUT);
while (!Serial);
// Configure the data receive callback
PDM.onReceive(onPDMdata);
// Optionally set the gain
// Defaults to 20 on the BLE Sense and -10 on the Portenta Vision Shields
// PDM.setGain(30);
// Initialize PDM with:
// - one channel (mono mode)
// - a 16 kHz sample rate for the Arduino Nano 33 BLE Sense
// - a 32 kHz or 64 kHz sample rate for the Arduino Portenta Vision Shields
if (!PDM.begin(channels, frequency)) {
Serial.println("启动PDM失败!");
while (1);
}
}
void loop() {
// Wait for samples to be read
if (samplesRead) {
// Print samples to the serial monitor or plotter
for (int i = 0; i < samplesRead; i++) {
if (channels == 2) {
Serial.print("左:");
Serial.print(sampleBuffer[i]);
Serial.print(" 右:");
i++;
}
Serial.println(sampleBuffer[i]);
if (sampleBuffer[i] > 10000 || sampleBuffer[i] <= -10000) {
LED_SWITCH = !LED_SWITCH;
if (LED_SWITCH) {
Serial.println();
digitalWrite(LEDB, HIGH);
Serial.println("开启!");
Serial.println();
delay(1000);
}
else {
Serial.println();
digitalWrite(LEDB, LOW);
Serial.println("关闭!");
Serial.println();
delay(1000);
}
}
}
// Clear the read count
samplesRead = 0;
}
}
/**
Callback function to process the data from the PDM microphone.
NOTE: This callback is executed as part of an ISR.
Therefore using `Serial` to print messages inside this function isn't supported.
* */
void onPDMdata() {
// Query the number of available bytes
int bytesAvailable = PDM.available();
// Read into the sample buffer
PDM.read(sampleBuffer, bytesAvailable);
// 16-bit, 2 bytes per sample
samplesRead = bytesAvailable / 2;
}
任务三的软件流程如下:
开始
|-- 初始化串口通信,设置波特率为 9600
|-- 将引脚(对应 LED)设置为输出模式
|-- 等待串口准备就绪
|-- 配置 PDM(脉冲密度调制)数据接收回调函数(关联onPDMdata函数)
|-- (可选操作,此处代码中注释掉了)设置 PDM 增益
|-- 初始化 PDM,传入通道数和频率参数
|-- 判断 PDM 初始化是否成功
|-- 若失败,串口输出 “启动 PDM 失败!” 并进入死循环
进入循环(loop)
|-- 判断是否有音频样本被读取(即samplesRead是否大于 0)
|-- 若有样本被读取:
|---- 遍历已读取的样本(循环变量 i 从 0 到samplesRead - 1)
|------ 判断通道数是否为 2
|-------- 若是,输出 “左:” 及对应样本值,再输出 “右:” 及下一个样本值,并让 i 自增 1
|------ 输出当前样本值(单通道情况或双通道中的一个通道情况)
|------ 判断样本值是否大于 10000 或者小于等于 -10000
|-------- 若是,切换 LED_SWITCH 的状态
|-------- 根据 LED_SWITCH 状态:
|---------- 若为 true(开启):
|------------ 向串口输出开启提示信息
|------------ 将 LED 引脚置为高电平(点亮 LED)
|------------ 程序延迟 1000 毫秒
|---------- 若为 false(关闭):
|------------ 向串口输出关闭提示信息
|------------ 将 LED 引脚置为低电平(熄灭 LED)
|------------ 程序延迟 1000 毫秒
|---- 将samplesRead置为 0(清除已读样本计数)
|-- 循环回到开头,重复上述判断与样本处理操作
回调函数 onPDMdata(由 PDM 数据接收触发执行)
|-- 查询可用字节数(通过PDM.available获取)
|-- 将数据读取到样本缓冲区(sampleBuffer)
|-- 根据 16 位、每个样本 2 字节的规则,计算已读取的样本数量
总结视频如下
|