【掌控板-arduino】5.1 读取声音数据及采样率

1 前言

之前用python实现过,参见【掌控板-mpython】3、向txt文件写入字符串、声音数据获取

尝试基于ardiuno抓取并计算采样率

2 硬件

在这里插入图片描述
采用的是IO36,对应P10。

在这里插入图片描述

3 安装库AnalogPin

查看源码路径:
mind+的arduino:

D:\mind+\Arduino\libraries

arduino的安装lib:

C:\Users\XXXX\Documents\Arduino\libraries

查看mind+中sound信息:D:\mind+\Arduino\libraries\MPython\MPython.cpp
查找到使用的是AnalogPin,io口为A0,这个A0支持自己配置,查看下light对应的正好是IO39,故sound的A0应该为36。

//MPython.cpp
AnalogPin light(39), sound(A0);

查看下AnalogPin的定义
提供了一个read函数,初始化是输入io信息。
analogRead就不展开了,参见文章arduino 的analogRead() 和analogWrite()

模拟输入analogRead()函数的返回值范围是0 到1023;???

//MPython.h
class AnalogPin
{
public:
    AnalogPin(uint8_t _io);
    uint16_t read();
private:
    uint8_t io;
};

//MPython.cpp
AnalogPin::AnalogPin(uint8_t _io)
    : io(_io)
{
}

uint16_t AnalogPin::read()
{
    return analogRead(io);
}

可以看到返回值类型为uint16_t,即2字节

在这里插入图片描述

4 读取串口sound数据

参考代码:Arduino例程解读与实验1.AnalogReadSerial(用串口读取模拟口数据)

/************************
  func 1 analogRead sound
*************************/
# define A_SOUND 36
# define A_LIGHT 39
//sound is IO36
//light is IO39

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}

void loop() {
  // read the input on analog pin 0:
  //读取A0脚输入值大小(0-5V对应0~1023)
  int sensorValue = analogRead(A_SOUND);
  
  // print out the value you read:
  Serial.println(sensorValue);
  delay(1); // delay 1 ms in between reads for stability

}

5 点亮led

实现定时点亮

/************************
  func 2 light led
*************************/
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel pixels(3,17, NEO_GRB + NEO_KHZ800);

bool status_led = false;

void led_on(){
  pixels.setPixelColor(0, pixels.Color(10, 0, 0));
  pixels.show();
  Serial.println("led_on");
}

void led_off(){
  pixels.setPixelColor(0, pixels.Color(0, 0, 0));
  pixels.show();
  Serial.println("led_off");
}

void change_led_status(){
  if (status_led == false){
    led_on();
    status_led = true;
  }
  else if (status_led == true){
    led_off();
    status_led = false;
  }
  else
    Serial.println("status_led error.");
}

void setup() {
  Serial.begin(9600);
  pixels.begin();

  // set time
  
}
void loop() {
  // judge time IRQ
  change_led_status();
  delay(500);
}

6 定时中断1s

增加定时中断,时间为1s,查看抓取到多少此sound数据

设置一个定时器,每1s点亮或者熄灭led灯。

6.1 参考文档

参考文章:Arduino 定时器中断 外部中断
arduino定时器

6.2 增加定时器——安装MsTimer2库

定时中断有两种,一种是有外接的硬件定时器,当时间到了硬件定时器会发送中断,另一种是软件定时器,根据程序运行时间产生中断。
目前掌控板上未找到硬件定时器。
在这里插入图片描述
可以查看下提供的示例。
在这里插入图片描述
报错只支持AVR

安装avrprog,依旧无效

示例也无效
在这里插入图片描述

6.3 增加定时器——使用hw_timer_t

///************************
//  func 3 time irq -- hw_timer_t
//  error: reboot
//*************************/

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel pixels(3,17, NEO_GRB + NEO_KHZ800);

volatile bool status_led = false;

void led_on(){
  pixels.setPixelColor(0, pixels.Color(10, 0, 0));
  pixels.show();
  Serial.println("led_on");
}

void led_off(){
  pixels.setPixelColor(0, pixels.Color(0, 0, 0));
  pixels.show();
  Serial.println("led_off");
}

void change_led_status(){
  if (status_led == false){
    led_on();
    status_led = true;
  }
  else if (status_led == true){
    led_off();
    status_led = false;
  }
  else
    Serial.println("status_led error.");
}

 
hw_timer_t * timer = NULL;            //声明一个定时器

//void IRAM_ATTR onTimer() {            //中断函数
void IRAM_ATTR onTimer() {            //中断函数
//  change_led_status();
  Serial.println('2');
}
 
 
void setup() {
  Serial.begin(115200);                        
  timer = timerBegin(0, 80, true);                //初始化
  timerAttachInterrupt(timer, &onTimer, true);    //调用中断函数
  timerAlarmWrite(timer, 1000000, true);        //timerBegin的参数二 80位80MHZ,这里为1000000  意思为1秒
  timerAlarmEnable(timer);                //定时器使能
 
//timerDetachInterrupt(timer);            //关闭定时器
}
 
 
void loop() {
 
}

若不执行change_led_status()函数,则可以正常运行,但增加该函数,则会不断重启
在这里插入图片描述

参考资料:Arduino定时器中断attachInterrupt()详解

6.4 小结

两种方法创建定时器中断均失败,不确定是什么原因导致的,如果有遇到过相同问题的朋友可以一起讨论下,谢谢~

7 结合定时和声音获取

7.1 代码

尝试直接在定时中断中打印log,然后循环中读取sound并计数。

/************************
  func 4 analogRead sound + time irq
*************************/
# include <Arduino.h>
# define A_SOUND 36
# define A_LIGHT 39
//sound is IO36
//light is IO39

int count_sound = 0;
volatile int status_time_irq = 0;

hw_timer_t * timer = NULL;            //声明一个定时器

//void IRAM_ATTR onTimer() {            //中断函数
void IRAM_ATTR onTimer() {            //中断函数
//  change_led_status();
//  status_time_irq = 1;
  Serial.println("xxxxxx");
}

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(115200);                        
  timer = timerBegin(0, 80, true);                //初始化
  timerAttachInterrupt(timer, &onTimer, true);    //调用中断函数
  timerAlarmWrite(timer, 1000000, true);        //timerBegin的参数二 80位80MHZ,这里为1000000  意思为1秒
  timerAlarmEnable(timer);                //定时器使能
 
}

void loop() {
  
  if (status_time_irq == 1)
//    return;
    Serial.println("irq");
  else{
    // read the input on analog pin 0:
    //读取A0脚输入值大小(0-5V对应0~1023)
    int sensorValue = analogRead(A_SOUND);
    
    // print out the value you read:
    Serial.println(count_sound);
//    Serial.print('\t');
//    Serial.println(sensorValue);
    count_sound = count_sound +1;
  }
  
//  delay(1); // delay 1 ms in between reads for stability

}

7.2 结果

在这里插入图片描述
如图,计数到到2125时,出现异常并重启
关机log为:Guru Meditation Error: Core 1 panic’ed (Interrupt wdt timeout on CPU1)

7.3 分析中断异常

根据提示的log
参考文章:[esp32] Guru Meditation 错误解析及解决方案
ESP32 官方文档(五)严重错误

Interrupt wdt timeout on CPU0 / CPU1(看门狗超时)
表示发生了中断看门狗超时. 有关详细信息,请参阅看门狗.
重点可能要看下watchdog

参考论坛:Interrupt wdt timeout on CPU0
看起来使用双核来避免这个问题,不过现在报错的是cpu1.

7 问题分析——使用hw_timer_t

参考文章:
[ESP32系列教程]ESP32 Arduino教程:定时器中断
[ESP32系列教程]ESP32 MicroPython教程:定时器中断

根据参考文章得到两点

1 为使编译器将代码分配到IRAM内,中断处理程序应该具有 IRAM_ATTR属性。而且,根据IDF文档的说明(参见此处),中断处理程序只能调用同样位于IRAM内的函数。
2

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在我之前的项目中,我展示了如何使用Arduino开发和BitVoicer服务器控制几个LED 。在这个项目中,我将使事情变得更加复杂。我还将使用Arduino DUE数模转换器(DAC)合成语音。如果您没有Arduino DUE,则可以使用其他Arduino,但是您将需要一个外部DAC和一些其他代码来操作DAC(BVSSpeaker库将无法帮助您)。 在下面的视频中,您可以看到我还让Arduino播放了一首歌曲,并使LED闪烁,就像它们是钢琴键一样。对不起,我的钢琴技巧,但这是我能做到的最好的:)。LED实际上以与真实C,D和E键相同的顺序和时序闪烁,因此,如果您周围有钢琴,则可以跟随LED并播放同一首歌曲。这是一个不再存在的老零售商(Mappin)的叮当声。 将执行以下过程将语音命令转换为LED活动和合成语音: 1. Sparkfun Electret Breakout将捕获并放大音频波; 2.放大后的信号将通过Arduino的模数转换器(ADC)进行数字化和缓冲; 3.音频样本将使用Arduino串行端口传输到BitVoicer服务器; 4. BitVoicer服务器将处理音频流并识别其包含的语音; 5.识别的语音将映射到预定义的命令,这些命令将发送回Arduino。如果其中一个命令用于合成语音,则BitVoicer Server将准备音频流并将其发送到Arduino; 6. Arduino将识别命令并执行适当的操作。如果接收到音频流,它将被排队到BVSSpeaker类中,并使用DUE DAC和DMA播放。 7. SparkFun单声道音频放大器会放大DAC信号,因此可以驱动8欧姆扬声器。 第一步是将Arduino和面包与组件连接,如下图所示。我必须在扬声器下方放置一个小的橡胶垫,因为它会振动很多,而没有橡胶垫的话,音频质量会受到很大影响。 在这里,与我以前的项目相比,有一个小但重要的区别。大多数Arduino均以5V运行,但DUE以3.3V运行。因为在3.3V下运行Sparkfun驻极体突破效果更好,所以如果您使用5V Arduino,建议您在3.3V引脚和AREF引脚之间添加一个跳线。DUE已经使用了3.3V模拟基准,因此您不需要AREF引脚的跳线。实际上,DUE上的AREF引脚通过电阻桥连接到微控制器。要使用AREF引脚,必须从PCB上拆下电阻器BR1。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值