Arduino 即插即用项目:节拍器

Arduino 即插即用是一款面向创客的套件,包含最新的 Arduino UNO R4 WiFi 以及一系列 Modulino 板。本文档描述了如何组装即插即用组件,并对其进行编程,使其作为节拍器运行,通过音调和灯光指示音乐节拍。它还利用 UNO R4 WiFi 的矩阵显示屏来显示每分钟节拍数 (BPM)。组装项目的图像包含在图 1 中。操作过程在视频 1 中展示。我们可以听到节拍器,重点突出第 1 拍。我们还可以看到灯光,第 1 拍为蓝色,其他拍为红色。

在此下载代码:Metronome.zip (3.0 KB)

1 :Arduino 即插即用套件组件组装为节拍器项目的图像。Modulino模块通过 Qwiic 连接器的菊花链进行电气连接。

视频 1 :4 拍音乐节拍(4/4 拍)的节拍器操作。第 1 拍为高音调,蓝色 LED,第 2、3 和 4 拍为低音调,红色 LED。

项目中使用的组件

该项目使用 Arduino 即插即用套件中包含的组件构建。唯一的附加组件是来自红色半透明 Hammond 1591STRD 外壳的盖子。这个红色透镜使得更容易看到和拍摄 UNO R4 WiFi 的 LED 显示矩阵。

如果您想了解更多关于 Arduino 12 x 8 LED 矩阵的信息,请参阅这篇文章,其中描述了 Charlieplexing 技术。

Modulino 教程:

您可能还对以下 DigiKey 教程感兴趣,这些教程探讨了 Modulino 板的操作:

物理连接

Arduino 即插即用硬件及其 Qwiic 连接大大简化了组装过程。图 1 展示了一种组件定位方式。菊花链的放置顺序和电气连接并不关键,因为每个Modulino板都是单独寻址的。因此,您可以自由选择任何方向,仅受连接线长度的限制。

软件描述

对于节拍器来说,可预测的定时至关重要。当我们同时结合光和声音时,定时尤其具有挑战性。程序员必须仔细协调独立Modulino模块的操作,特别注意阻塞和非阻塞方法。

什么是阻塞代码?

阻塞代码的常见示例是Arduino的delay()函数。回想一下,delay()会暂停您的loop()代码的操作,直到延迟时间过去。在delay()执行期间,微控制器实际上是无能为力的,因为它无法响应用户输入。

技术提示 :delay()函数会暂停您的loop()程序。它不会影响使用中断服务例程(ISRs)在后台运行的进程。例如,与millis()相关的定时器机制会继续在后台运行。这是一个受欢迎的操作,因为主节拍器完全依赖于这些作为后台中断服务例程(ISRs)发生的定时机制。

跟踪每分钟节拍数

每分钟节拍数(BPM)变量由Modulino旋钮(编码器)模块维护。旋钮模块由一个旋转编码器和一个微控制器组成,用于监控位置并在Qwiic总线上通信。该微控制器跟踪用户输入的“旋转点击”的整数,每次点击相当于BPM值的单个变化。此操作通过get()方法实现。set()方法用于限制BPM值。

维护BPM的函数如下所示。在此示例中,set()方法用于将BPM值限制在1到300 BPM之间。

int get_BPM( ){
  int BPM = encoder.get( );
  if (BPM < 1) {
    encoder.set(1);
    BPM = 1;                          // Don't divide by zero
  }
  if (BPM > MAX_BPM) {
    encoder.set(MAX_BPM);
    BPM = MAX_BPM;
  }
  return BPM;
}

显示 BPM

Arduino UNO R4 WiFi的LED矩阵用于显示BPM,如视频1所示。当BPM值发生变化时,特别是当(BPM != last_BPM)时,执行此代码。注意使用 snprintf() 函数将 BPM 值转换为适合在 LED 矩阵上显示的人类可读字符串。

技术提示 :sprintf() 和 snprintf() 函数位于 C 标准 I/O (stdio.h) 库中,执行类似的操作。然而,应避免使用 sprintf(),因为它不检查相关缓冲区的大小。如果没有这种保护,很容易覆盖缓冲区,导致难以排查的异常操作。

  if (BPM != last_BPM) {
    i = 0;                            // Always start a new sequence with beat 1
    delay(1000);                      // Allow the user time to set the new BPM (blocking metronome is off)
    BPM = get_BPM();                  // User should be done, get the new value
    last_BPM = BPM;
    matrix.beginDraw();               // Standard Arduino code to operate the UNO R4 WiFi’s LED matrix
    matrix.stroke(0xFFFFFFFF);
    matrix.textScrollSpeed(50);

    snprintf(buffer, sizeof(buffer), "   %d BPM   ", BPM);  // See tech tip
    matrix.textFont(Font_5x7);
    matrix.beginText(0, 1, 0xFFFFFF);
    matrix.println(buffer);
    matrix.endText(SCROLL_LEFT);
    matrix.endDraw();

    delay_val = 60000 / BPM;          // 60 second per minute and 1000 ms per second
  }

技术提示 :Arduino LED 矩阵显示 API 是阻塞的。因此,当节拍器运行时,BPM 显示不可用。

如何维护时间

在 Arduino 微控制器中,有许多不同的方法来跟踪时间,包括阻塞的 delay()、使用 millis(),以及对于高级用户,专用的硬件定时器。在这个项目中,我选择使用本文中描述的非阻塞定时器代码。这段代码是一个折衷方案,将 millis() 函数嵌入到一个紧凑的基于类的例程中。请注意,参考文章包括一个教程,描述了传统的 Arduino 非阻塞技术以及基于类的实现。

当你阅读定时文章时,你会注意到对可编程逻辑控制器 (PLC) 的强调。事实上,定时器代码的功能就像使用 PLC 的结构化文本 (ST) 语言编程的定时器模块。以下是定时器类的使用示例。在这个例子中,TON_1 每 5 秒产生一个脉冲。TOF_1 定时器将这个脉冲延长 300 毫秒。结果是产生一个每 5000 毫秒一次的 300 毫秒脉冲的 blink 变量。

  // Produce a pulse that is one program scan long that occurs once every 5 seconds.
  // Use that pulse to blink an LED with a 300 ms on period.

  TON_1.update(!TON_1.Q, 5000);
  TOF_1.update(TON_1.Q, 300);
  int blink = TOF_1.Q;

技术提示 :这段定时器代码是作为 PLC 实验开发的。回想一下,PLC 是为机器或过程的实时控制而设计的;在基本层面上,PLC 代码是非阻塞的。

这段定时器代码也是 AI 实验的一部分。我让 ChatGPT 4.0 构建一个基于类的 Arduino 定时器模块,其操作类似于 PLC 的结构化文本 (ST) 实现。经过几个小时的改进,代码在 Arduino 环境中按预期运行,具有类似 ST 的(非阻塞)语句。这是一个通过将技术从一个环境(PLC ST)适应到另一个环境(Arduino C++)来提高编码技能的好例子。

保持音乐节拍

现在我们有了心跳,节拍器被硬编码为保持 4/4 拍号,如这段代码片段所示。第一个节拍通过更高的880 Hz音调和蓝色LED的闪烁来强调,如视频1所示。所有其他节拍通过440 Hz音调和红色LED的闪烁来提示。

代码清单中混合了阻塞和非阻塞例程。蜂鸣器是非阻塞的,因为诸如buzzer.tone(880, 50)的语句会播放880 Hz音调50毫秒。LED闪烁需要更复杂的阻塞机制。请注意,在用于设置的show()和用于清除LED的第二个show()方法之间必须经过一段时间。由于LED非常亮,5毫秒的周期就足够了。

if (TON_1.Q) {                // This is a pulse (high for one loop( ) cycle) coincident with the BPM.
    if (i >= 4) {             // TODO: Allow user to adjust the musical meter.
      i = 0;
    }

    if (i == 0) {
      buzzer.tone(880, 50);	  // hard code musical note A3
      flash_LED(BLUE, 50, 5);

    } else {
      buzzer.tone(440, 50);   // hard code musical note A4
      flash_LED(RED, 50, 5);
    }
    i++;
  }

未来的改进

这个项目是一个快速演示,探索如何使用Arduino即插即用。设备仍有很大的改进空间。这里有一些思路供你参考。

  • 使用Modulino按钮模块来控制节拍器。目前,它是硬编码为4/4拍。包含用户可选择的切分拍、3/4拍、普通拍甚至5/4拍会很有用——这样你就可以演奏《碟中谍》的主题曲了。

  • 加入一个按钮来播放恒定的调音音符。

  • 提高用户界面的响应速度。例如,实现粗调和微调控制,让用户快速定位所需的BPM。

  • 添加一个脚踏板来滑动(动态调整时间),为独奏者提供更自然的乐句。

  • 测量并校准设备以获得精确的BPM。

  • 对于有雄心的实验者,可以转向使用先进组件和技术的面包板,例如直接数字合成(DDS)来生成正弦调音音符,并能够改变音量。你还可以添加一个任意波形发生器,将节拍的声音从机械的哔哔声改为木块甚至牛铃——我需要更多的牛铃!

  • 修改12 x 8显示器,使其成为非阻塞的。这将允许显示器不断滚动BPM。

最后的思考

Arduino即插即用套件提供了一个方便的平台,学生可以通过它探索各种传感器和执行器。无面包板的设计让学生可以专注于软件。

本文描述的节拍器非常容易复制。同时,还有许多需要改进的地方。你的第一个挑战是使用Modulino Buttons模块来促进节拍的变化,从普通拍子切换到切分拍、华尔兹、5/4拍,甚至是Pink Floyd的《Money》中那种爵士风格的7/4拍。

本着开源社区的精神,请复制、修改并分发你自己的节拍器。

相关信息

请点击以下链接获取相关和有用的信息: