142|1

12

帖子

3

TA的资源

一粒金砂(中级)

楼主
 

【Follow me第二季第4期】全部任务提交 [复制链接]

  本帖最后由 eew_9XVJps 于 2024-12-23 14:54 编辑

一、任务演示视频

【Follow me第二季第4期】全部任务演示视频-【Follow me第二季第4期】全部任务演示视频-EEWORLD大学堂

二、任务实现详情

(一)、物料介绍
此次活动的板子是Arduino Nano RP2040 Connect,主控是Raspberry Pi RP2040微控制器,板载了WiFi模块、MEMS麦克风、6轴惯性测量单元、RGB彩灯及加密协处理器,功能强大,外设丰富,具有很高的可玩性。为了配合任务我还采购了一块XIAO ESP32S3来配合完成相关的任务,利用两个板子的WIFI来实现无线数据传输功能,在这里主要是传输音频数据用于实现无线麦克风或者无线音频氛围灯的效果。除了以上两个板子之外还准备了SD卡模块,WS2812灯板以及MAX98357模块用于录音数据的存储和音乐氛围灯的展示。
 
活动购入的Arduino Nano RP2040 Connect和XIAO ESP32S3,非常的小巧精致
自备的SD卡模块
 
自备的WS2812灯板
 
自备MAX98357及喇叭
(二)、任务实现
必做任务一:搭建环境并开启第一步Blink三色LED / 串口打印Hello DigiKey & EEWorld!;
既然是Arduino官方板子,决定使用Arduino IDE进行开发,主要是环境安装简单,支持也肯定不会差。Arduino的安装较为简单,从官网上下载软件安装报后,直接在软件的开发板管理器中搜索Arduino Nano RP2040 Connect就可以看到Arduino Mbed OS Nano Boards的选项,点击安装即可。
第一个任务需要使用板载的RGB灯,从pinout资料中可以看到这个三色RGB灯由WiFi模块的三个引脚控制,因此需要下载WiFiNINA库方便对三个引脚进行单独控制。库的安装方法也较为简单,直接在Arduino IDE的库管理里面搜索WiFiNINA点击安装即可。
安装后引入相应的库头文件和SPI库头文件就可以对引脚进行操作了,这三个引脚已经在库中进行了定义,分别是LEDR、LEDG、LEDB,直接像使用其他Arduino板子上的引脚一样初始化、拉高、拉低就可以实现对RGB灯颜色和亮度的控制了。在这个控制上遇见个文件,那就是直接对灯的引脚拉高则灯全亮,拉低则灯灭,对应的使用analogWrite函数写的数值则相反,写0时灯完全亮,写255时灯几乎不亮,与digitalWrite控制相反。
程序先是对引脚和串口进行初始化,然后使用两个变量l、m分别控制rgb灯的亮度和切换,亮度l在循环过程中自加,加至最大亮度因uin8_t数据类型限制自动溢出至0,同时使灯珠控制变量自加1,如果灯珠变量超过2则重新置0,循环过程中打印相关信息输出至串口。
流程图:
代码如下:
#include <SPI.h>
#include <WiFiNINA.h>
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  pinMode(LEDB, OUTPUT);
  digitalWrite(LEDR, LOW);
  digitalWrite(LEDG, LOW);
  digitalWrite(LEDB, LOW);  
}
uint8_t l=0;//亮度控制
uint8_t m=0;//灯珠控制
void loop() {
  // put your main code here, to run repeatedly:
  if(m==0){
    analogWrite(LEDR,l);
    digitalWrite(LEDG, LOW);
    digitalWrite(LEDB, LOW);
  }
  else if(m==1){
    digitalWrite(LEDR, LOW);
    analogWrite(LEDG,l);
    digitalWrite(LEDB, LOW);
  }
  else if(m==2){
    digitalWrite(LEDR, LOW);
    digitalWrite(LEDG, LOW);
    analogWrite(LEDB,l);
  }
  l++;
  if(l==255){
    m++;
  }
  if(m>2){
    m=0;
  }
  Serial.println("Hello DigiKey & EEWorld!");
  delay(5);
}

 

效果图如下,动态效果见视频:
必做任务二:学习IMU基础知识,调试IMU传感器,通过串口打印六轴原始数据;
通过网络上查到的相关信息显示,MEMS加速度计基本原理为检测移动质量块与固定极板之间因位移而产生的电容变化和形变来检测加速度等信息变化的。
而陀螺仪则是通过外部施加周期电压产生周期性定向位移,如果有旋转发生则存在科里奥利使得移动方向发生偏移,从而从偏移中计算角速度的方法。
板载的这块IMU芯片能直接输出加速度、角速度数据,以及温度数据。为了方便获取相关数据,可下载Arduino_LSM6DSOX库来实现数据的读取,下载方法同上一个任务的库。
这个库提供了一个外部对象IMU供我们调用,使用begin函数启动后根据需要使用不同的read函数来读取加速度值、角度值和温度值。
程序流程图:
代码如下:
#include <Arduino_LSM6DSOX.h>
void setup() {
  Serial.begin(9600);
  while (!Serial);
  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");
    while (1);
  }
  delay(1000);
}
void loop() {
  float x, y, z;
  float a, b, c,t;
  if (IMU.accelerationAvailable()) {
    IMU.readAcceleration(x, y, z);
    Serial.print("Accelerometer");
    Serial.print(x);
    Serial.print('\t');
    Serial.print(y);
    Serial.print('\t');
    Serial.print(z);
    Serial.print('\t');
  }
  if (IMU.gyroscopeAvailable()) {
    IMU.readGyroscope(a, b, c);
    Serial.print("Gyroscope");
    Serial.print(a);
    Serial.print('\t');
    Serial.print(b);
    Serial.print('\t');
    Serial.print(c);
    Serial.print('\t');
  }
  if (IMU.temperatureAvailable()) {
    IMU.readTemperatureFloat(t);
    Serial.print("Temperature");
    Serial.print(t);
    Serial.println('\t');
  }
  else{
    Serial.println();
  }
}

 

效果图如下,动态效果见视频:
必做任务三:学习PDM麦克风技术知识,调试PDM麦克风,通过串口打印收音数据和音频波形。
PDM麦克风内部集成了将声波转换为电信号的 MEMS 膜和数据处理芯片,MEMS膜负责将动能转换成电信号,而数据处理芯片则将电信号进一步处理。
PDM麦克风在数据处理时将模拟信号电压转换为经过单比特脉冲密度调制的数字流,PDM 信号更接近于纵波,而不是在音频中看到的典型横波。对于波峰部分则用高密度1低密度0表示,波谷则使用高密度0低密度1表示,平稳段则使用均匀的1和0表示。PDM麦克风的优势是使用资源少,只需要2个控制引脚。
在Arduino IDE中安装了Arduino Mbed OS Nano Boards之后就直接集成了PDM库用于PDM麦克风的使用,同时也提供了演示代码,这个任务直接使用演示代码即可实现。代码基本顺序是:配置PDM回调函数(用于处理麦克风数据)->开启麦克风(设置通道数和采样率)->判断并打印麦克风数据。需要注意PDM频率不能设置过高,否则无法启动和正常工作。
程序流程图:
代码如下:
#include <PDM.h>
static const char channels = 1;
static const int frequency = 16000;
short sampleBuffer[512];
volatile int samplesRead;
void setup() {
  Serial.begin(9600);
  while (!Serial);
  PDM.onReceive(onPDMdata);
  if (!PDM.begin(channels, frequency)) {
    Serial.println("Failed to start PDM!");
    while (1);
  }
}
void loop() {
  if (samplesRead) {
    for (int i = 0; i < samplesRead; i++) {
      if(channels == 2) {
        Serial.print("L:");
        Serial.print(sampleBuffer[i]);
        Serial.print(" R:");
        i++;
      }
      Serial.println(sampleBuffer[i]);
    }
    samplesRead = 0;
  }
}
void onPDMdata() {
  int bytesAvailable = PDM.available();
  PDM.read(sampleBuffer, bytesAvailable);
  samplesRead = bytesAvailable / 2;
}

 

效果图如下,动态效果见视频:
必做任务三的进阶 任务三中使用PDM麦克风,获取到了音频数据并通过串口打印了音频数据和波形,在这里使用XIAO ESP32S3将PDM麦克风拾取的声音再次播放出来。
过程是利用Arduino Nano RP2040 Connect板载的PDM麦克风作为音乐拾取器拾取外部声音,通过UDP将数据打包后发送至XIAO ESP32S3开发板。XIAO ESP32S3支持I2S音频,将收到的声音通过I2S输出到MAX98357模块将声音放大外放。
与前面任务相比多了配置WIFI、启动WIFI和启动UDP的过程,并将收集的声音数据通过UDP打包发送。程序注意发射端和接收端的采样率要保持一致,避免声音变调。
程序流程图:
Arduino Nano RP2040 Connect的WIFI配置代码如下:
if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }
  // attempt to connect to WiFi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    WiFi.config(local_IP, gateway, subnet);
    status = WiFi.begin(ssid, pass);
    // wait 10 seconds for connection:
    delay(10000);
  }
  Serial.println("Connected to WiFi");
  printWifiStatus();
//如果收集到数据就打包发送:
if (samplesRead) {
    // send a reply, to the IP address and port that sent us the packet we received
    Udp.beginPacket(udpAddress, localPort);
    Udp.write((const uint8_t *)sampleBuffer, samplesRead*2);;
Udp.endPacket();
samplesRead=0;
  }

 

XIAO ESP32S3接收端的程序流程图:
代码如下:
#include <WiFi.h>
#include <WiFiUdp.h>
#include "driver/i2s.h"
#define I2S_SAMPLE_RATE 20000
#define I2S_DMA_BUF_LEN 256
const i2s_port_t SPK_I2S_PORT = I2S_NUM_0;
const char * udpAddress = "192,168,1,101";
const int udpPort = 2390;
//Are we currently connected?
boolean connected = false;
char *ssid = "";//网络名称
const char *password = "";//网络密码
IPAddress local_IP(192,168,1,100);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);
//The udp library class
WiFiUDP udp;
char packetBuffer[256]; //buffer to hold incoming packet
char  ReplyBuffer[] = "esp32s3";       // a string to send back
void setup(){
  // Initilize hardware serial:
  Serial.begin(115200);
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  WiFi.config(local_IP, gateway, subnet);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println();
  Serial.println(WiFi.localIP());
  // while (WiFi.softAP(ssid,password,16) != 1){// 设置网络名称和密码
  // }
  udp.begin(WiFi.localIP(),udpPort);
  I2SInit_SPK();
}
void loop(){
  int packetSize = udp.parsePacket();
  if (packetSize) {
    int len = udp.read(packetBuffer, 255);
    if (len > 0) {
      size_t sendSize;
      //Serial.println(len);
      i2s_write(SPK_I2S_PORT,(int16_t*)packetBuffer, len, &sendSize, portMAX_DELAY);
    }
  }
}
const i2s_config_t spk_i2s_config = {
      .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX ),
      .sample_rate = I2S_SAMPLE_RATE,
      .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,  //I2S输出只支持16位
      .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,                           //2-channels
      .communication_format =(i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
      .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
      .dma_buf_count = 8,
      .dma_buf_len = I2S_DMA_BUF_LEN,
};
const i2s_pin_config_t spk_pin_config = {
      .bck_io_num = 9,
      .ws_io_num = 8,
      .data_out_num = 7,
      .data_in_num = -1  //17Not used
};
void I2SInit_SPK()
{ esp_err_t err;
  err=i2s_driver_install(SPK_I2S_PORT, &spk_i2s_config, 0, NULL);
  if(err!=0){
    Serial.println("i2s driver install failed");
  }
  err=i2s_set_pin(SPK_I2S_PORT, &spk_pin_config);
  if(err!=0){
    Serial.println("i2s set pin failed");
  }
}

 

效果图如下,动态效果见视频:
选做任务一(非必做):通过RGB LED不同颜色、亮度显示PDM麦克风收到的声音大小;
从上一个任务程序可以看到,PDM可以直接输出声音的大小,大小范围为正负两位数值,在获取声音大小时只需要取数值的绝对值即可。另外可以根据实际环境音量的大小范围将其划分为3段后分别指定给R,G和B三个颜色,因为需要输出给LED等控制灯的亮度,可以使用map函数将划分后的音量大小转换为analogWrite可接受的范围,循环过程中需要记得将灯光重置为0,并在点亮后进行适当的延时。
程序流程图:
代码如下:
#include <PDM.h>
#include <SPI.h>
#include <WiFiNINA.h>
static const char channels = 1;
static const int frequency = 16000;
short sampleBuffer[512];
volatile int samplesRead;
void setup() {
  Serial.begin(9600);
  while (!Serial);
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  pinMode(LEDB, OUTPUT);
  digitalWrite(LEDR, LOW);
  digitalWrite(LEDG, LOW);
  digitalWrite(LEDB, LOW);
  PDM.onReceive(onPDMdata);
  if (!PDM.begin(channels, frequency)) {
    Serial.println("Failed to start PDM!");
    while (1);
  }
}
void loop() {
  if (samplesRead) {
    for (int i = 0; i < 6; i++) {
      uint16_t temp=0;
      for(int j= 0; j < 10;j++){
        temp+=abs(sampleBuffer[j+i*10]);
      }
      temp = temp/10;
      //Serial.println(temp);
      uint8_t lum;
      analogWrite(LEDG,255);
      analogWrite(LEDB,255);
      analogWrite(LEDR,255);
      if(1089>temp){
        lum=map(temp,0,10892,255,0);//蓝色
        analogWrite(LEDB,lum);
      }
      else if(2178>temp && temp>=1089){
        lum=map(temp,10892,21784,255,0);//绿色
        analogWrite(LEDG,lum);
      }
      else if(3267>=temp && temp>=2178){
        lum=map(temp,21784,32676,255,0);//红色
        analogWrite(LEDR,lum);
      }
      delay(20);  
    }
    samplesRead = 0;
  }
}
void onPDMdata() {
  int bytesAvailable = PDM.available();
  PDM.read(sampleBuffer, bytesAvailable);
  samplesRead = bytesAvailable / 2;
}

 

效果图如下,动态效果见视频:
上面这个任务使用板载的一个LED灯来显示声音,效果不够明显,于是想到了对声音数据进行FFT之后利用WS2812灯板进行显示,于是决定这么做。实际测试后发现PDM库和FASTELD库冲突,两个启动后板子就不能工作了,没有深入研究是为什么,改为利用XIAO ESP32S3开发板通过UDP接收数据后进行FFT并推送至WS2812灯板进行显示。
大体流程如下:
 
代码方面相较于前面的UDP发送音频和播放音频Arduino Nano RP2040 Connect可以直接使用原代码,XIAO ESP32S3开发板的接收端比前面多了FASTLED初始化及FF变换相关内容。代码如下:
#include <arduinoFFT.h>
#include <FastLED.h>
#include <FastLED_NeoMatrix.h>
#include <WiFi.h>
#include <WiFiUdp.h>
const int udpPort = 2390;
boolean connected = false;
char *ssid = "CMCC-sdtm";
const char *password = "mvkrxs76";
IPAddress local_IP(192,168,1,100);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);
WiFiUDP udp;
#define SAMPLES 64        // Must be a power of 2
#define LED_PIN     4     // Data pin to LEDS  D2
#define NUM_LEDS    256    // 灯珠数
#define BRIGHTNESS  150    // LED 亮度
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB
#define xres 16            // Total number of  columns in the display
#define yres 16            // Total number of  rows in the display
#define SAMPLING_FREQ 15000
double vReal[SAMPLES];
double vImag[SAMPLES];
//幅值算法*************************************************************
//从group取出特征角标取出初始幅值→加上shift偏移→乘gain倍率→除以gain2倍率
//暂时只用了freq_gain2
int freq_group[8] = {2, 5, 12, 20, 30, 38, 50, 75};
int freq_shift[8] = {-50, -10, -10, -10, -5, -5, -5, -5};
int freq_gain[8] = {1, 1, 1, 1, 2, 2, 4, 4};
int freq_gain2[xres] = {40, 42, 45, 47, 51, 55, 57, 59, 62, 65, 69, 71, 73, 76, 80, 82};
//算出来的长度值
unsigned char freq_block[xres] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int Intensity[xres] = { }; // initialize Frequency Intensity to zero
int Intensity_last[xres] = { }; // initialize Frequency Intensity to zero
int Displacement = 1;
CRGB leds[NUM_LEDS];            // Create LED Object
ArduinoFFT<double> FFT = ArduinoFFT<double>(vReal, vImag, SAMPLES, SAMPLING_FREQ);  // Create FFT object
int color = 0; //颜色变化
int color_rate = 1;  //单次变换间隔倍数
int color_interval = 1;  //颜色变换间隔
FastLED_NeoMatrix matrix = FastLED_NeoMatrix(leds, 8, 8,2,2,
  NEO_MATRIX_TOP  + NEO_MATRIX_RIGHT +
  NEO_MATRIX_COLUMNS       + NEO_MATRIX_PROGRESSIVE +
  NEO_TILE_TOP + NEO_TILE_RIGHT + NEO_TILE_ROWS + NEO_TILE_PROGRESSIVE);
void setup() {
  Serial.begin(9600);         //Initialize Serial
  delay(3000);                  // power-up safety delay
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); //Initialize LED strips
  FastLED.setBrightness(BRIGHTNESS);
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  WiFi.config(local_IP, gateway, subnet);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println();
  Serial.println(WiFi.localIP());
  udp.begin(WiFi.localIP(),udpPort);
}
void loop() {
  for(int i = 0; i < 4; i++){
    Visualizer();
    delay(1);
    flow(i);
  }
}
void flow(int rate){
  if(rate == 3){
    for(int i = 0; i < xres; i++){
      if(freq_block[i]>0){
        freq_block[i] = freq_block[i]-1;
       }
    }
  }
}
void Visualizer(){
  //Collect Samples
  getSamples();
  FastLED.clear();
  //Update Display
  displayUpdate();
  FastLED.show();
}
char packetBuffer[256]; //buffer to hold incoming packet
void getSamples(){
  int packetSize = udp.parsePacket();
  if (packetSize) {
    int len = udp.read(packetBuffer, 255);
    if (len > 0) {
      for(int i = 0; i < SAMPLES; i++){
        vReal[i] = packetBuffer[i]/2;
        //Serial.println(vReal[i]);
        vImag[i] = 0;
      }
      //FFT
      FFT.windowing(vReal, 1, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
      FFT.compute(vReal, vImag, SAMPLES, FFT_FORWARD);
      FFT.complexToMagnitude(vReal, vImag, SAMPLES);
      //Update Intensity Array
      for(int i = 2; i < (xres*Displacement)+2; i+=Displacement){
        vReal[i] = constrain(vReal[i],freq_gain2[i-2] ,1800);            // set max value for input data
        vReal[i] = map(vReal[i], freq_gain2[i-2], 1800, 0, yres);        // map data to fit our display
        Intensity[(i/Displacement)-2] --;                      // Decrease displayed value
        if (vReal[i] > Intensity[(i/Displacement)-2])          // Match displayed value to measured value
          Intensity[(i/Displacement)-2] = vReal[i];
      }
    }
  }
}
void displayUpdate(){
  int color = 0; // 初始化颜色变量
  int Intensity_ava[xres] = { };
  for(int x=0; x< xres; x++){
    Intensity_ava[x]=(Intensity_last[x]+Intensity[x])/2;  
  }
  for(int i = 0; i < xres; i++){ // 遍历矩阵的每一行
    for(int j = 0; j < yres; j++){ // 遍历每一行的每一个像素
      if(j <= Intensity_ava[i]){ // Light everything within the intensity range
        if(j > freq_block[i]){
          matrix.drawPixel(i, j, CHSV(color, 255, BRIGHTNESS)); // 绘制亮像素
        }
      }
    }
    color += 255 / xres; // Increment the Hue to get the Rainbow effect
    if(color > 255){
      color = 0; // Wrap around the Hue value if it exceeds 255
    }
  }
  for (int i = 0; i < xres; i++) {
    Intensity_last[i] = Intensity_ava[i];
  }
  delay(5);
}

 

效果图如下,动态效果见视频:
 
自定任务:Arduino Nano RP2040 Connect板载的PDM麦克风实现录音功能
看到板载有麦克风为什么不试一下录制声音呢,因此想通过板载的PDM麦克风实现声音拾取,并将收集到的数据以WAV格式形式存储在SD卡中。SD卡通过SPI接口实现与主控板通讯,SPI引脚直接使用了默认引脚。
我使用的SD卡外置模块为5V供电,需要将主控板的VBUS焊盘连接上来进行供电,否则3V3无法启动。
流程顺序大体如下:
 
在刚开始是按照这个程序进行测试发现播放录制的声音明显速度变快了,在群里咨询了各位大佬,提出了各种意见,后来测试发现这个PDM只有在固定频率下才能输出正常速度的声音,速率高了就快,速率低了就慢,只有14000左右时声音即清楚又与实际音频速度一致。按说这个时采样率应该不影响程序速度什么的,不知道底层是怎么写的,程序如下:
#include <PDM.h>
#include "wave.h"
#include <SPI.h>
#include "SdFat.h"
const int chipSelect = 10;
// default number of output channels
static const char channels = 1;
// default PCM output frequency
static const int frequency = 14000;
// Buffer to read samples into, each sample is 16-bits
short sampleBuffer[128];
const int record_time = 30;  // second
const char filename[] = "/record.wav";
const int waveDataSize = record_time * 14000;
char partWavData[1024];
// Number of audio samples read
volatile int samplesRead;
FsFile file;  // 录音文件
SdFs sd;
void setup() {
  Serial.begin(9600);
  delay(2000);
  if(!sd.begin(SdSpiConfig(chipSelect, DEDICATED_SPI, SD_SCK_MHZ(16))))//初始化SD
  {
    Serial.println(F("sd init error"));
    return;
  }
  sd.remove(filename);
  file = sd.open(filename, O_WRITE|O_CREAT);
  if(!file)
  {
    Serial.println("crate file error");
    return;
  }
  // Configure the data receive callback
  auto header = CreateWaveHeader(1, 14000, 16);
  header.riffSize = waveDataSize + 44 - 8;
  header.dataSize = waveDataSize;
  file.write(&header,44);
  PDM.onReceive(onPDMdata);
  // Optionally set the gain
  // Defaults to 20 on the BLE Sense and 24 on the Portenta Vision Shield
  PDM.setGain(10);
  // 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 Shield
  if (!PDM.begin(channels, frequency)) {
    Serial.println("Failed to start PDM!");
    while (1);
  }
  else{
    Serial.println("start");
  }
}
int count = waveDataSize/128;
void loop() {
  // Wait for samples to be read
  if(count==0){
    samplesRead=0;
    file.close();
    Serial.println("finish");
  }
  if (samplesRead) {
    file.write((const byte*)sampleBuffer, 128);
    count--;
    // Clear the read count
    samplesRead = 0;
  }
  // if (samplesRead) {
  //   Serial.println(samplesRead);
  //   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);//每次64样本
  // 16-bit, 2 bytes per sample
  samplesRead = bytesAvailable / 2;
}

 

效果图如下,动态效果见视频:
选做任务二(非必做):通过IMU数据结合机器学习算法,识别运动状态,并通过串口打印。
这个任务不是很懂,根据官方资料应该是可以将芯片训练数据发送给IMU,IMU能根据收集数据和训练数据进行对比做出响应?这是烧录后的实际效果。
根据官网页面介绍完成开发吧安装,STM32duino X-NUCLEO-IKS01A3 库安装,然后打开示例并修改引脚信息,就可以进行烧录了。
烧录后可以看到在不同的运动状态下开发板可以识别出不同的信息并通过串口打印出来。
代码信息:
#include "LSM6DSOXSensor.h"
#include "lsm6dsox_activity_recognition_for_mobile.h"
#ifdef ARDUINO_SAM_DUE
#define DEV_I2C Wire1
#elif defined(ARDUINO_ARCH_STM32)
#define DEV_I2C Wire
#elif defined(ARDUINO_ARCH_AVR)
#define DEV_I2C Wire
#else
#define DEV_I2C Wire
#endif
#define SerialPort Serial
#define INT_1 INT_IMU
//Interrupts.
volatile int mems_event = 0;
// Components
LSM6DSOXSensor AccGyr(&DEV_I2C, LSM6DSOX_I2C_ADD_L);
// MLC
ucf_line_t *ProgramPointer;
int32_t LineCounter;
int32_t TotalNumberOfLine;
void INT1Event_cb();
void printMLCStatus(uint8_t status);
void setup() {
  uint8_t mlc_out[8];
  // Led.
  pinMode(LED_BUILTIN, OUTPUT);
  // Force INT1 of LSM6DSOX low in order to enable I2C
  pinMode(INT_1, OUTPUT);
  digitalWrite(INT_1, LOW);
  delay(200);
  // Initialize serial for output.
  SerialPort.begin(115200);
  // Initialize I2C bus.
  DEV_I2C.begin();
  AccGyr.begin();
  AccGyr.Enable_X();
  AccGyr.Enable_G();
  /* Feed the program to Machine Learning Core */
  /* Activity Recognition Default program */  
  ProgramPointer = (ucf_line_t *)lsm6dsox_activity_recognition_for_mobile;
  TotalNumberOfLine = sizeof(lsm6dsox_activity_recognition_for_mobile) / sizeof(ucf_line_t);
  SerialPort.println("Activity Recognition for LSM6DSOX MLC");
  SerialPort.print("UCF Number Line=");
  SerialPort.println(TotalNumberOfLine);
  for (LineCounter=0; LineCounter<TotalNumberOfLine; LineCounter++) {
    if(AccGyr.Write_Reg(ProgramPointer[LineCounter].address, ProgramPointer[LineCounter].data)) {
      SerialPort.print("Error loading the Program to LSM6DSOX at line: ");
      SerialPort.println(LineCounter);
      while(1) {
        // Led blinking.
        digitalWrite(LED_BUILTIN, HIGH);
        delay(250);
        digitalWrite(LED_BUILTIN, LOW);
        delay(250);
      }
    }
  }
  SerialPort.println("Program loaded inside the LSM6DSOX MLC");
  //Interrupts.
  pinMode(INT_1, INPUT);
  attachInterrupt(INT_1, INT1Event_cb, RISING);
  /* We need to wait for a time window before having the first MLC status */
  delay(3000);
  AccGyr.Get_MLC_Output(mlc_out);
  printMLCStatus(mlc_out[0]);
}
void loop() {
  if (mems_event) {
    mems_event=0;
    LSM6DSOX_MLC_Status_t status;
    AccGyr.Get_MLC_Status(&status);
    if (status.is_mlc1) {
      uint8_t mlc_out[8];
      AccGyr.Get_MLC_Output(mlc_out);
      printMLCStatus(mlc_out[0]);
    }
  }
}
void INT1Event_cb() {
  mems_event = 1;
}
void printMLCStatus(uint8_t status) {
  switch(status) {
    case 0:
      SerialPort.println("Activity: Stationary");
      break;
    case 1:
      SerialPort.println("Activity: Walking");
      break;
    case 4:
      SerialPort.println("Activity: Jogging");
      break;
    case 8:
      SerialPort.println("Activity: Biking");
      break;
    case 12:
      SerialPort.println("Activity: Driving");
      break;
    default:
      SerialPort.println("Activity: Unknown");
      break;
  }  
}

 

效果图如下,动态效果见视频:
(三)、总结
非常有幸能够参与此次活动,通过此次活动使我对IMU、UDP及PDM麦克风的相关知识有了更深入和具体的了解,通过直播活动及自己查找资料提高了自己的知识面和能力。此次活动的主控板选择非常合适,可以学习和研究的地方很多,比如除了上面任务要求的和自选的任务之外的还有语音识别功能,蓝牙功能以及RP2040的PIO等功能,活动后我也将继续去进一步学习。这种学习模式也非常有助于问题的解决和个人能力的提高,通过活动将大量不同地方的人聚集起来交流技术和知识。最后建议以后多组织这种活动,同时能否增加一些专门解答的老师之类的人员以提高活动效果。

 

三、可编译下载的代码

download.eeworld.com.cn/detail/eew_9XVJps/635331

最新回复

这灯板真是羡慕啊   详情 回复 发表于 4 天前
点赞 关注
 
 

回复
举报

651

帖子

0

TA的资源

纯净的硅(高级)

沙发
 

这灯板真是羡慕啊

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/3 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表