448|1

57

帖子

5

TA的资源

一粒金砂(高级)

楼主
 

【Follow me第二季第1期】创意任务三: 触控&MIDI钢琴 [复制链接]

  本帖最后由 MioChan 于 2024-7-31 00:24 编辑

水果钢琴原理就是当手触摸水果时,检测其对应引脚的电容发生的变化,当其大于某个阈值时,执行if内语句,可以说是把水果当成一个大号“按钮”。除了A0其余引脚都支持这个触摸检测的功能,用CircuitPlayground.readCap(Pin)就可以读取引脚的电容值。

 

 

手边没有完整的水果,所以就实现一个最简单的,直接用手触摸A1-A7焊盘就能发出do ra mi .. 对应的音,用水果的话应该稍微改一下判断阈值即可,原理大同小异。

下面是演示:

水果钢琴原理演示

 

代码:

#include <Adafruit_CircuitPlayground.h>

// 触控引脚及其音符频率
int touchPins[] = {A1, A2, A3, A4, A5, A6, A7};
int touchFrequencies[] = {262, 294, 330, 349, 392, 440, 494}; 

void setup() {
  CircuitPlayground.begin();
  Serial.begin(115200); 

void loop() {
  checkTouch();
  delay(5);
}

void checkTouch() {
  for (int i = 0; i < 7; i++) {
    int pin = touchPins[i];
    int touchValue = CircuitPlayground.readCap(pin);
    if (touchValue > 1000) {
      Serial.print("Touched pad A");
      Serial.println(pin - A0);
      playTouchNoteAndLight(i);
      return; 
    }
  }
}

void playTouchNoteAndLight(int pad) {
  int frequency = touchFrequencies[pad];
 
  uint32_t randomColor = CircuitPlayground.colorWheel(random(0, 255));
  tone(A0, frequency);//播放
  for (int i = 0; i < 10; i++) { //亮灯
    CircuitPlayground.setPixelColor(i, randomColor);
  }
  delay(600); // 播放600毫秒
  noTone(A0); //停止播放
   for (int i = 0; i < 10; i++) { //关灯
  CircuitPlayground.setPixelColor(i, 0); 
  }
}

 

 

下面再实现一个进阶版,可以根据MIDI音符信号,使用板载蜂鸣器进行播放。程序会自动识别传入MIDI音符的音调,时值以及力度,会像创意任务一中的一样用彩色LED给出力度的电平指示,可以作为外部MIDI乐器用DAW或MIDI键盘直接控制。此外,还手动实现了一个PWM发生器,可以通过板子上的两个按钮实时的调整蜂鸣器音量大小。

下面是视频演示:

创意任务3

 

对应代码:

#include <Adafruit_CircuitPlayground.h>
#include <MIDIUSB.h>

// MIDI音符到频率的映射,A0到C8
const int noteFrequencies[] = {
  27, 29, 31, 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, 65, 69, 73, 78, 82, 87, 93, 98, 104, 110, 117, 123, 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 4186
};

// 定义渐变颜色数组
uint32_t gradientColors[10] = {
  CircuitPlayground.colorWheel(90), 
  CircuitPlayground.colorWheel(80),
  CircuitPlayground.colorWheel(70),
  CircuitPlayground.colorWheel(60),
  CircuitPlayground.colorWheel(50),
  CircuitPlayground.colorWheel(40),  
  CircuitPlayground.colorWheel(30),
  CircuitPlayground.colorWheel(20),
  CircuitPlayground.colorWheel(10),
  CircuitPlayground.colorWheel(0)
};


int volume = 100; // 初始音量


int currentNote = -1;
unsigned long noteStartTime = 0;
unsigned long noteDuration = 0;
int currentFrequency = 0;

void setup() {
  CircuitPlayground.begin();
  Serial.begin(115200); 
}

void loop() {
  checkButtons(); // 检查按钮状态
  midiEventPacket_t rx;
  do {
    rx = MidiUSB.read();
    if (rx.header != 0) {
      Serial.print("Received: ");
      Serial.print(rx.header);
      Serial.print("-");
      Serial.print(rx.byte1);
      Serial.print("-");
      Serial.print(rx.byte2);
      Serial.print("-");
      Serial.println(rx.byte3);
      
      // 检查NOTE ON 消息
      if ((rx.byte1 & 0xF0) == 0x90 && rx.byte3 != 0) {
        int note = rx.byte2; // 获取音符音高
        int velocity = rx.byte3; // 获取音符力度
        playNoteAndLight(note, velocity);
      } else if ((rx.byte1 & 0xF0) == 0x80 || ((rx.byte1 & 0xF0) == 0x90 && rx.byte3 == 0)) {
        stopNoteAndLight();
      }
    }
  } while (rx.header != 0);
  
  if (currentNote != -1 && millis() - noteStartTime >= noteDuration) {
    stopNoteAndLight();
  }
  
  delay(5);
}

void playNoteAndLight(int note, int velocity) {
  if (note >= 21 && note <= 108) { // 确保音符在有效范围内
    int frequency = noteFrequencies[note - 21];
    currentFrequency = frequency;
    currentNote = note;
    noteStartTime = millis();
    noteDuration = 1000; 

    // 调用PWM函数来播放音符
    startPWM(A0, frequency, volume);

    // 计算亮灯的数量
    int ledCountToLight = map(velocity, 0, 127, 0, 10);
    

    Serial.print("Velocity: ");
    Serial.print(velocity);
    Serial.print(", LED Count: ");
    Serial.println(ledCountToLight);
    
    // 更新 LED 灯的亮度
    for (int i = 0; i < 10; i++) {
      if (i < ledCountToLight) {
        // 点亮 LED
        CircuitPlayground.setPixelColor(i, gradientColors[i]);
      } else {
        // 关闭 LED
        CircuitPlayground.setPixelColor(i, 0);
      }
    }
  }
}

void stopNoteAndLight() {
  stopPWM(A0); // 停止自PWM
  turnOffAllLEDs(); // 关闭所有 LED 灯

  currentNote = -1;
}

void turnOffAllLEDs() {
  for (int i = 0; i < 10; i++) {
    CircuitPlayground.setPixelColor(i, 0); // 关闭 LED
  }
}

void checkButtons() {
  if (CircuitPlayground.leftButton()) {
    volume = max(volume - 5, 0); // 减小音量
    Serial.print("Volume Down: ");
    Serial.println(volume);
    delay(200); 
  }
  if (CircuitPlayground.rightButton()) {
    volume = min(volume + 5, 127); // 增大音量
    Serial.print("Volume Up: ");
    Serial.println(volume);
    delay(200); 
  }
}

void startPWM(int pin, int frequency, int volume) {
  int dutyCycle = map(volume, 0, 127, 0, 255);
  analogWrite(pin, dutyCycle); 
  tone(pin, frequency);
}

void stopPWM(int pin) {
  noTone(pin);
}

 

到此所有任务就都做完了,板子的所有功能也都玩了一遍~

最新回复

这设计还是有点意思的!看来楼主还是一个音乐家~   详情 回复 发表于 2024-7-31 18:16
点赞(1) 关注
 
 

回复
举报

7244

帖子

2

TA的资源

版主

沙发
 

这设计还是有点意思的!看来楼主还是一个音乐家~

 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

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

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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

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

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

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