| 参数分类 | 项目 | 规格详情 |
|---|---|---|
| 基础信息 | 产品型号 | A025 |
| 基础信息 | 产品名称 | 16 位电容式触摸钢琴键盘 (A025) |
| 基础信息 | 产品重量 | 17.5克 |
| 基础参数 | PCB 外形尺寸 | 100.00mm × 50.50mm |
| 基础参数 | 触摸按键数量 | 16 键(KEY1~KEY16),钢琴标准键位布局 |
| 基础参数 | 通信接口 | I2C 总线(SCL/SDA),兼容 Arduino、STM32 等单片机 |
| 基础参数 | 供电电压 | 3.3V~5V DC |
| 基础参数 | 板载功能 | 电源指示灯 (PWR)、I2C 地址可配置、防误触算法 |
| 接口定义 (插座) | VCC | 电源正极,输入 3.3V~5V |
| 接口定义 (插座) | GND | 电源负极,需与主控单片机共地 |
| 接口定义 (插座) | SCL | I2C 时钟信号线 |
| 接口定义 (插座) | SDA | I2C 数据信号线 |
| 关键特性 | 感应方式 | 电容触摸感应,无机械触点、零磨损、轻触触发 |
| 关键特性 | 键位布局 | 16 键覆盖两个八度音域,包含白键黑键,可直接映射标准音符 |
| 关键特性 | 通信优势 | I2C 仅占用 2 根 IO,节省主控引脚,支持多模块级联 |
| 关键特性 | 功耗特性 | 低功耗电路设计,静态电流小,适配电池便携设备 |
| 关键特性 | 安装结构 | 四角预留固定孔,可直接锁装在设备面板 |
| 包装方案 | 可选包装 | ①防静电袋软包 ②白卡挂孔盒 ③牛皮纸盒 |



/*********************************** DEMO1 **********************************\
串口打印按键值
\******************************************************************************/
#include <Wire.h>
#define PIANO_ADDR 0x50
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("16键触摸钢琴就绪");
}
void loop() {
Wire.beginTransmission(PIANO_ADDR);
Wire.write(0x00);
Wire.endTransmission();
delay(10);
Wire.requestFrom(PIANO_ADDR, 2);
if (Wire.available() >= 2) {
byte high = Wire.read();
byte low = Wire.read();
uint16_t keys = (high << 8) | low;
if (keys != 0) {
Serial.print("触发按键:");
for (int i = 0; i < 16; i++) {
if (keys & (1 << i)) {
Serial.print(i + 1);
Serial.print(" ");
}
}
Serial.println();
}
}
delay(50);
}
/*********************************** DEMO2 **********************************\
按键值映射为声音频率
\******************************************************************************/
#include <TTP229.h>
const int SCL_PIN = 4; // The pin number of the clock pin.
const int SDO_PIN = 3; // The pin number of the data pin.
TTP229 ttp229(SCL_PIN, SDO_PIN); // TTP229(sclPin, sdoPin)
void setup()
{
Serial.begin(115200);
Serial.println("Start Touching One Key At a Time!");
pinMode(2,OUTPUT);
}
void loop()
{
uint8_t key = ttp229.ReadKey16(); // Blocking
if (key) Serial.println(key);
if (key==1){
tone(2,523);//
}else if (key==2){
tone(2,587);
}else if (key==3){
tone(2,659);
}else if (key==4){
tone(2,698);
}else if (key==5){
tone(2,784);
}else if (key==6){
tone(2,880);
}else if (key==7){
tone(2,988);
}else if (key==8){
tone(2,1046);
}else if (key==9){
tone(2,1175);
}else if (key==10){
tone(2,1318);
}else if (key==11){
tone(2,1397);
}else if (key==12){
tone(2,1568);
}else if (key==13){
tone(2,1760);
}else if (key==14){
tone(2,1976);
}else if (key==15){
tone(2,494);
}else if (key==16){
tone(2,440);
}else{
noTone(2);
}
// uint8_t key = ttp229.GetKey16(); // Non Blocking
// Serial.println(key);
}
// 两组TTP229按键组合使用 + 上电自动播放开机音乐(≤20音符)
const int module1_SCL = 2;
const int module1_SDA = 3;
const int module2_SCL = 4;
const int module2_SDA = 5;
const int BUZZER_PIN = 9;
const int module1_base = 1;
const int module2_base = 17;
const bool KEY_PRESSED_LEVEL = LOW;
// 电子琴频率表(32键)
const int noteFrequencies[] = {
131,147,165,175,196,220,247,262,
294,330,349,392,440,494,523,587,
659,698,784,880,988,1047,1175,1319,
1397,1568,1760,1976,2093,2349,2637,2794
};
uint32_t lastReadTime = 0;
// ==================== 开机音乐 ====================
struct BootNote {
uint16_t freq;
uint16_t dur;
};
// 18个音符(含停顿),<=20
const BootNote bootMelody[] = {
{523,120},{659,120},{784,150},{0,60},
{784,120},{880,120},{988,150},{0,60},
{988,120},{1047,200},{0,80},
{784,120},{659,120},{523,200},{0,100},
{659,120},{784,120},{1047,220}
};
const uint8_t BOOT_LEN = sizeof(bootMelody)/sizeof(bootMelody[0]);
bool bootPlaying = true;
uint8_t bootIndex = 0;
uint32_t bootNextTime = 0;
// 非阻塞播放函数
void playBootMelody() {
if (!bootPlaying) return;
uint32_t now = millis();
if (now < bootNextTime) return;
if (bootIndex >= BOOT_LEN) {
noTone(BUZZER_PIN);
bootPlaying = false;
return;
}
uint16_t f = bootMelody[bootIndex].freq;
uint16_t d = bootMelody[bootIndex].dur;
if (f == 0) noTone(BUZZER_PIN);
else tone(BUZZER_PIN, f);
bootNextTime = now + d;
bootIndex++;
}
// ==================== TTP229读取 ====================
uint16_t readTTP229Stable(int sclPin, int sdaPin) {
uint16_t data = 0;
digitalWrite(sclPin, LOW);
delayMicroseconds(5);
for (int i = 0; i < 16; i++) {
digitalWrite(sclPin, HIGH);
delayMicroseconds(3);
if (digitalRead(sdaPin) == KEY_PRESSED_LEVEL) {
data |= (1 << i);
}
digitalWrite(sclPin, LOW);
delayMicroseconds(3);
}
digitalWrite(sclPin, HIGH);
delayMicroseconds(5);
return data;
}
int getRawKey() {
uint16_t keys1 = readTTP229Stable(module1_SCL, module1_SDA);
for (int i = 0; i < 16; i++) {
if (keys1 & (1 << i)) return module1_base + i;
}
uint16_t keys2 = readTTP229Stable(module2_SCL, module2_SDA);
for (int i = 0; i < 16; i++) {
if (keys2 & (1 << i)) return module2_base + i;
}
return 0;
}
int getDebouncedKey() {
static int candidate = 0;
static int stable = 0;
static int count = 0;
const int PRESS_TH = 2;
const int RELEASE_TH = 12;
int raw = getRawKey();
if (raw == candidate) {
if (count < 255) count++;
} else {
candidate = raw;
count = 1;
}
if (stable == 0) {
if (candidate > 0 && count >= PRESS_TH) {
stable = candidate;
return stable;
}
return 0;
}
if (candidate == 0) {
if (count >= RELEASE_TH) {
stable = 0;
return 0;
}
return stable;
}
if (candidate != stable && count >= PRESS_TH) {
stable = candidate;
return stable;
}
return stable;
}
// ==================== SETUP ====================
void setup() {
Serial.begin(9600);
pinMode(module1_SCL, OUTPUT);
pinMode(module1_SDA, INPUT_PULLUP);
pinMode(module2_SCL, OUTPUT);
pinMode(module2_SDA, INPUT_PULLUP);
digitalWrite(module1_SCL, HIGH);
digitalWrite(module2_SCL, HIGH);
pinMode(BUZZER_PIN, OUTPUT);
noTone(BUZZER_PIN);
// 启动开机音乐
bootPlaying = true;
bootIndex = 0;
bootNextTime = millis();
}
// ==================== LOOP ====================
void loop() {
// 优先播放开机音乐
if (bootPlaying) {
playBootMelody();
return; // 播放期间不响应按键
}
if (millis() - lastReadTime >= 10) {
int key = getDebouncedKey();
static int playingKey = 0;
if (key != playingKey) {
if (key > 0) {
tone(BUZZER_PIN, noteFrequencies[key - 1]);
Serial.println(key);
} else {
noTone(BUZZER_PIN);
}
playingKey = key;
}
lastReadTime = millis();
}
}
// 三组TTP229(48键)+ 严格十二平均律(C3~B6) + 按住持续发声 + 双阈值消抖
// + 开机音乐(<=20音符) + Key48 自动演奏超级马里奥曲子
const int module1_SCL = 2;
const int module1_SDA = 3;
const int module2_SCL = 4;
const int module2_SDA = 5;
const int module3_SCL = 6;
const int module3_SDA = 7;
const int BUZZER_PIN = 9;
const int module1_base = 1; // 1~16
const int module2_base = 17; // 17~32
const int module3_base = 33; // 33~48
const bool KEY_PRESSED_LEVEL = LOW;
// ==================== 严格十二平均律频率表(48键) ====================
// 48 notes from C3 to B6 (4 octaves), rounded to integer Hz.
const int noteFrequencies[48] = {
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
};
uint32_t lastReadTime = 0;
// ==================== 通用“乐谱事件”结构 ====================
struct NoteEvent {
uint16_t freq; // 0=rest
uint16_t durMs; // duration in ms
};
// ==================== 开机音乐(<=20音符) ====================
const NoteEvent bootMelody[] = {
{523,120},{659,120},{784,150},{0,60},
{784,120},{880,120},{988,150},{0,60},
{988,120},{1047,200},{0,80},
{784,120},{659,120},{523,200},{0,100},
{659,120},{784,120},{1047,220}
};
const uint8_t BOOT_LEN = sizeof(bootMelody)/sizeof(bootMelody[0]);
bool bootPlaying = true;
uint8_t bootIndex = 0;
uint32_t bootNextTime = 0;
void playBootMelody() {
if (!bootPlaying) return;
uint32_t now = millis();
if (now < bootNextTime) return;
if (bootIndex >= BOOT_LEN) {
noTone(BUZZER_PIN);
bootPlaying = false;
return;
}
uint16_t f = bootMelody[bootIndex].freq;
uint16_t d = bootMelody[bootIndex].durMs;
if (f == 0) noTone(BUZZER_PIN);
else tone(BUZZER_PIN, f);
bootNextTime = now + d;
bootIndex++;
}
// ==================== Key48 自动演奏(约30秒科技感) ====================
// 风格:短促跳跃 + 分层琶音 + 扫频感(用音阶步进模拟)
// 总时长约 30 秒(取决于实际节拍误差)
//const NoteEvent autoSong[] = {
// // A段:启动脉冲 + 上行
// {988,90},{0,30},{1319,90},{0,30},{988,90},{0,30},{1568,120},{0,60},
// {784,90},{0,30},{1047,120},{0,60},{1319,90},{0,30},{1760,150},{0,90},
//
// // 琶音循环1(低->高,偏电子)
// {262,120},{330,120},{392,120},{523,160},{0,40},
// {294,120},{370,120},{440,120},{587,160},{0,40},
// {330,120},{415,120},{494,120},{659,160},{0,40},
// {349,120},{440,120},{523,120},{698,180},{0,60},
//
// // B段:高音“提示音”+ 回落
// {1976,120},{0,60},{1568,90},{0,30},{1865,120},{0,60},{1480,90},{0,30},
// {1760,150},{0,90},{1319,120},{0,60},{1568,150},{0,120},
//
// // 琶音循环2(更密一点)
// {262,100},{392,100},{523,100},{784,140},{0,40},
// {277,100},{415,100},{554,100},{831,140},{0,40},
// {294,100},{440,100},{587,100},{880,140},{0,40},
// {311,100},{466,100},{622,100},{932,160},{0,60},
//
// // C段:上行“扫频感”(用半音快速步进模拟)
// {988,70},{1047,70},{1109,70},{1175,70},{1245,70},{1319,90},{0,50},
// {1319,70},{1397,70},{1480,70},{1568,70},{1661,70},{1760,120},{0,80},
//
// // 过门:短促闪烁
// {659,80},{0,20},{784,80},{0,20},{659,80},{0,20},{988,120},{0,60},
// {523,80},{0,20},{659,80},{0,20},{523,80},{0,20},{784,140},{0,80},
//
// // D段:收束 + 结束确认音
// {1047,120},{988,120},{932,120},{880,120},{831,120},{784,160},{0,80},
// {1319,100},{0,40},{1568,120},{0,60},{1976,220},{0,120},
// {1047,120},{0,60},{1319,150},{0,80},{1976,260}
//};
//
//const uint16_t AUTO_LEN = sizeof(autoSong)/sizeof(autoSong[0]);
// ==================== Super Mario Enhanced (≈40s) ====================
const NoteEvent autoSong[] = {
// Intro
{659,120},{0,30},{659,120},{0,150},{659,120},{0,150},
{523,120},{0,30},{659,120},{0,30},{784,240},{0,200},
{392,240},{0,180},
// A theme
{523,160},{0,40},{392,120},{0,40},{330,160},{0,40},
{440,160},{0,40},{494,160},{0,40},{466,160},{440,160},
{392,120},{0,30},
// Bass groove (fake)
{196,90},{0,30},{196,90},{0,30},{247,90},{0,30},{196,90},{0,60},
{220,90},{0,30},{220,90},{0,30},{262,90},{0,30},{220,90},{0,60},
// Lead run
{659,120},{0,30},{784,120},{0,30},{880,180},{0,60},
{698,120},{0,30},{784,120},{0,30},{659,120},{0,30},
{523,120},{0,30},{587,120},{0,30},{494,220},{0,180},
// B-like phrase (short)
{392,160},{0,40},{330,160},{0,40},{262,160},{0,40},
{440,160},{0,40},{494,160},{0,40},{466,160},{440,160},
{392,120},{0,30},
// Signature again
{659,120},{0,30},{659,120},{0,150},{659,120},{0,150},
{523,120},{0,30},{659,120},{0,30},{784,240},{0,200},
{392,260},{0,200},
// Ending
{523,120},{0,30},{659,120},{0,30},{784,120},{0,30},
{1047,240},{0,80},{784,120},{0,30},{659,160},{0,60},
{523,320}
};
const uint16_t AUTO_LEN = sizeof(autoSong)/sizeof(autoSong[0]);
bool autoPlaying = false;
uint16_t autoIndex = 0;
uint32_t autoNextTime = 0;
void startAutoSong() {
autoPlaying = true;
autoIndex = 0;
autoNextTime = millis();
}
void stopAutoSong() {
autoPlaying = false;
noTone(BUZZER_PIN);
}
void playAutoSong() {
if (!autoPlaying) return;
uint32_t now = millis();
if (now < autoNextTime) return;
if (autoIndex >= AUTO_LEN) {
stopAutoSong();
return;
}
uint16_t f = autoSong[autoIndex].freq;
uint16_t d = autoSong[autoIndex].durMs;
if (f == 0) noTone(BUZZER_PIN);
else tone(BUZZER_PIN, f);
autoNextTime = now + d;
autoIndex++;
}
// ==================== TTP229读取 ====================
uint16_t readTTP229Stable(int sclPin, int sdaPin) {
uint16_t data = 0;
digitalWrite(sclPin, LOW);
delayMicroseconds(5);
for (int i = 0; i < 16; i++) {
digitalWrite(sclPin, HIGH);
delayMicroseconds(3);
if (digitalRead(sdaPin) == KEY_PRESSED_LEVEL) data |= (1 << i);
digitalWrite(sclPin, LOW);
delayMicroseconds(3);
}
digitalWrite(sclPin, HIGH);
delayMicroseconds(5);
return data;
}
// 单音:优先级 module1 -> module2 -> module3
int getRawKey() {
uint16_t keys1 = readTTP229Stable(module1_SCL, module1_SDA);
for (int i = 0; i < 16; i++) if (keys1 & (1 << i)) return module1_base + i;
uint16_t keys2 = readTTP229Stable(module2_SCL, module2_SDA);
for (int i = 0; i < 16; i++) if (keys2 & (1 << i)) return module2_base + i;
uint16_t keys3 = readTTP229Stable(module3_SCL, module3_SDA);
for (int i = 0; i < 16; i++) if (keys3 & (1 << i)) return module3_base + i;
return 0;
}
// 双阈值消抖
int getDebouncedKey() {
static int candidate = 0;
static int stable = 0;
static int count = 0;
const int PRESS_TH = 2; // 10ms*2=20ms
const int RELEASE_TH = 12; // 10ms*12=120ms
int raw = getRawKey();
if (raw == candidate) {
if (count < 255) count++;
} else {
candidate = raw;
count = 1;
}
if (stable == 0) {
if (candidate > 0 && count >= PRESS_TH) {
stable = candidate;
return stable;
}
return 0;
}
if (candidate == 0) {
if (count >= RELEASE_TH) {
stable = 0;
return 0;
}
return stable;
}
if (candidate != stable && count >= PRESS_TH) {
stable = candidate;
return stable;
}
return stable;
}
// ==================== SETUP ====================
void setup() {
Serial.begin(9600);
pinMode(module1_SCL, OUTPUT);
pinMode(module1_SDA, INPUT_PULLUP);
digitalWrite(module1_SCL, HIGH);
pinMode(module2_SCL, OUTPUT);
pinMode(module2_SDA, INPUT_PULLUP);
digitalWrite(module2_SCL, HIGH);
pinMode(module3_SCL, OUTPUT);
pinMode(module3_SDA, INPUT_PULLUP);
digitalWrite(module3_SCL, HIGH);
pinMode(BUZZER_PIN, OUTPUT);
noTone(BUZZER_PIN);
// 开机音乐启动
bootPlaying = true;
bootIndex = 0;
bootNextTime = millis();
}
// ==================== LOOP ====================
void loop() {
// 1) 开机音乐优先(期间不响应按键)
if (bootPlaying) {
playBootMelody();
return;
}
// 2) 自动演奏模式优先(允许用Key48再次按下停止)
if (autoPlaying) {
playAutoSong();
// 仍然扫描按键,用于“再按一次48停止”
if (millis() - lastReadTime >= 10) {
int key = getDebouncedKey();
static int lastKey = 0;
if (key != lastKey) {
if (key == 48) stopAutoSong();
lastKey = key;
}
lastReadTime = millis();
}
return;
}
// 3) 正常弹奏模式
if (millis() - lastReadTime >= 10) {
int key = getDebouncedKey();
static int playingKey = 0;
static int lastKeyEdge = 0;
// Key48:触发自动演奏(只在“按下边沿”触发)
if (key == 48 && lastKeyEdge != 48) {
startAutoSong();
playingKey = 0;
noTone(BUZZER_PIN);
lastKeyEdge = 48;
lastReadTime = millis();
return;
}
lastKeyEdge = key;
// 普通键:1~47 正常发声
int keyForPlay = (key == 48) ? 0 : key;
if (keyForPlay != playingKey) {
if (keyForPlay > 0) {
int idx = keyForPlay - 1; // 0..46
if (idx >= 0 && idx < 48) {
tone(BUZZER_PIN, noteFrequencies[idx]);
Serial.println(keyForPlay);
} else {
noTone(BUZZER_PIN);
}
} else {
noTone(BUZZER_PIN);
}
playingKey = keyForPlay;
}
lastReadTime = millis();
}
}
/*
* 代码实现:多个按键值同时实现,并在串口实时打印。
*/
#include <TTP229.h>
// 初始化TTP229对象,SCL引脚接A5,SDO引脚接A4
TTP229 ttp229(A5, A4);
void setup() {
Serial.begin(9600);
Serial.println("TTP229 多键检测模式启动");
Serial.println("请同时按下多个按键...");
}
void loop() {
// 读取所有按键状态,返回一个16位整数
uint16_t keys = ttp229.ReadKeys16();
if (keys != 0) {
Serial.print("按键状态 (二进制): ");
for (int i = 15; i >= 0; i--) {
Serial.print((keys >> i) & 1);
}
Serial.print(" 物理按键编号: ");
// 直接映射:位0 -> 按键1,位1 -> 按键2,……,位15 -> 按键16
for (int i = 0; i < 16; i++) {
if (keys & (1 << i)) {
Serial.print(i + 1);
Serial.print(" ");
}
}
Serial.println();
delay(200); // 简单防抖
}
}