[应用方案] 我的DIY设计-触摸电子琴

[复制链接]
 楼主| chenqiguang1998 发表于 2023-6-30 18:31 | 显示全部楼层 |阅读模式
#申请开发板# #申请原创# 我的DIY设计-触控电子琴
制作触控电子琴的方案如下:
一、项目方案的构想
首先,我们需要了解RD8T37P48RJ芯片的特点和功能,包括其1T 8051内核、工业级集成触控按键功能、Flash存储器,48pin等。基于这些特点,我们可以提出以下方案:利用该芯片的触控功能实现电子琴的按键控制,利用其Flash存储器存储音符数据和谱子,使用无源蜂鸣器产生声音,通过LCD1602显示电子琴的状态和播放的音符。
296f82d48155995f350035635bcbcebe
二、设计、搭建
设计过程需要基于RD8T37P48RJ芯片进行,包括电路设计、程序设计和调试等。首先,我们需要设计电子琴的按键电路,将芯片的触控输入口连接到按键电路中,并根据需要设计LCD1602的接口电路和蜂鸣器的驱动电路。然后,我们需要编写程序,实现电子琴的各种功能,包括音符的播放、谱子的存储和显示等。最后,我们需要将程序下载到芯片中,并进行调试,确保电子琴的各种功能正常工作。
以下是各部分的功能代码与原理:
按键电路
按键电路需要基于RD8T37P48RJ芯片的触控输入口实现。我们可以使用按键开关和电阻器等元件构成按键电路,并将触控输入口的引脚连接到按键电路中。在程序中,我们需要使用触控输入口的API函数来检测按键的状态,从而实现电子琴的按键控制。有趣的是,我们可以使用官方提供的无代码快速开发工具EasyCodeCube进行快速生产,类似于stm32cubemax(当然我还是会更喜欢自己写)。
音符播放
音符播放需要使用无源蜂鸣器实现。我们可以将蜂鸣器的引脚连接到RD8T37P48RJ芯片的具有定时器功能的GPIO口,并在程序中使用pwm产生方波,调整占空比,从而驱动蜂鸣器发出不同的声音。
在音谱中,每一个音符都有对应的频率:
6a14f013b4135bbc21290e43ce25ae74
每个音符满足12平分率(前面一个音符的频率)*2^(1/12)=(后面一个音符的频率)
或者说(后面一个音符的频率)/{2^(1/12)}=(前面一个音符的频率);
音符频率计算: T = 65536 - 1/Fr/2/MC
T:  要算得的定时器初值
Fr:  各音阶对应的频率
MC: 一个机器周期所需的时间 ,比如11.0592MHz的晶振对应的机器周期为1.085μs
EX:低音Do对应的频率为262,则T = 65536 – 1/2/MC/262*(10^6),假设机器周期为1.085μs,则T=63777,对应十六进制数为0xF921,分别写进TH0,和TL0;
定时器的高低占空比与所驱动的无源蜂鸣器发出的声音频率的对应公式为:
频率(Hz)= 定时器时钟频率 / ((定时器计数值高字节)* 256 + 定时器计数值低字节)
而节拍的实现和原理如下:

曲调
1/4拍的延迟时间
1/8拍的延迟时间
4/4
125ms
62ms
3/4
187ms
94ms
2/4
250ms
125ms
参考手册:
08c03d6cd7a3d0ec070d3da5e1e25039
07a7b0f6db8d7e956b46c717658da5ef
因此,此部分驱动代码,可如下:

  1. void play_sound(unsigned char key_code) {

  2. check_key();

  3. if (key_count == 1 && key_state == key_code) {

  4. set_frequency(frequency_array[key_code - 1]);

  5. while (KEY_PIN & key_code);

  6. set_frequency(0); // 清空频率,停止PWM波

  7. }

  8. }

  9. void init_timer() {

  10. TMOD |= 0x01; // 设置定时器0为模式1

  11. TH0 = 0xFF; // 设置高电平时钟周期为255us

  12. TL0 = 0xFF; // 设置低电平时钟周期为255us

  13. ET0 = 1; // 启用定时器0中断

  14. TR0 = 1; // 启动定时器0

  15. }

  16. void timer_isr() interrupt 1 {

  17. static unsigned char count = 0;

  18. count++;

  19. if (count == frequency) {

  20. BUZZER_PIN = 1; // 输出高电平

  21. count = 0;

  22. } else {

  23. BUZZER_PIN = 0; // 输出低电平

  24. }

  25. }


谱子存储和读取
谱子存储和读取可以通过spi连接外部tf-card,根据手册,芯片支持spi的操作,当然也可以为了减少工作量,使用相关软件将乐谱转化为频率存储数组之类的进行存储,例如Music Encode。
fd60e0bd22b2dd7fe65e3b7811a2fa55
将以上框选区数据表  (音符,音高,节拍),放入解构函数进行处理,进行音符、音高、节拍的实现;

  1. play_song(unsigned char* notes, unsigned char* pitches, unsigned char* durations, unsigned int clock_frequency)

  2. {

  3. unsigned char i;

  4. unsigned int note_freq;

  5. unsigned char note_duration;

  6. unsigned int pwm_period;

  7. unsigned char pwm_duty_cycle;

  8. note_freq = note[notes[i] - 1] * clock_frequency / 440; // 根据音符计算频率

  9. note_duration = durations[i]; // 根据节拍计算持续时间

  10. pwm_period = (clock_frequency / 2) * note_duration; // 根据频率和持续时间计算 PWM 周期(假设 PWM 频率为半时钟频率)

  11. pwm_duty_cycle = (unsigned char)(pwm_period / (clock_frequency / 256)); // 根据 PWM 周期计算占空比

  12. while (pwm_duty_cycle < 256 - pwm_duty_cycle) { // 通过延时控制音符的持续时间

  13. unsigned int x;

  14. for (x = 0; x < 1000; x++); // 通过空循环来延时,根据实际需求调整延时时间的长短

  15. }

  16. set_frequency(note_freq);

  17. }


LCD1602显示
LCD1602显示需要使用LCD1602液晶显示屏实现。我们可以将LCD1602的引脚连接到RD8T37P48RJ芯片的GPIO口,并在程序中使用LCD1602的API函数进行显示。在程序中,我们需要使用LCD1602的API函数进行显示控制,包括清屏、光标定位、字符输出等,来实现在按键按下时候显示播放的音符名。
代码如下:

  1. #define LCD_RS P3_0 // RS引脚连接到P3_0

  2. #define LCD_RW P3_1 // RW引脚连接到P3_1

  3. #define LCD_E P3_2 // E引脚连接到P3_2

  4. #define LCD_DATA P3_3 // 数据引脚连接到P3_3

  5. #define LCD_D0 P3_4 // D0引脚连接到P3_4

  6. #define LCD_D1 P3_5 // D1引脚连接到P3_5

  7. #define LCD_D2 P3_6 // D2引脚连接到P3_6

  8. #define LCD_D3 P3_7 // D3引脚连接到P3_7

  9. #define LCD_D4 P4_0 // D4引脚连接到P4_0

  10. #define LCD_D5 P4_1 // D5引脚连接到P4_1

  11. #define LCD_D6 P4_2// D6引脚连接到P4_2

  12. #define LCD_D7 P4_3// D7引脚连接到P4_3

  13. // 定义音符名字数组和当前播放音符的下标

  14. char note_names[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};

  15. int current_note = 0;

  16. // 初始化LCD1602显示器

  17. void init_lcd() {

  18. lcd_init(); // 初始化LCD1602库

  19. lcd_clear(); // 清屏

  20. lcd_setCursor(0, 0); // 设置光标位置为第一行第一列

  21. }

  22. // 播放音符时更新LCD16显示内容

  23. void play_note() {

  24. lcd_write(LCD_DATA, note_names[current_note]); // 在第一行第一列写入当前播放的音符名字

  25. }

  26. // 停止播放音符时清除LCD显示内容

  27. void stop_note() {

  28. lcd_clear(); // 清屏

  29. }

  30. void initLcd() {

  31. lcdInit();

  32. lcdClear();

  33. void playNote() {

  34. lcdWrite(LCD_DATA, noteNames[currentNote]);

  35. }

  36. void stopNote() {

  37. lcdClear();

  38. }


三、调试流程
调试过程需要分为几个步骤进行。首先,我们需要确保电子琴的电路连接正确,并检查程序的编译和下载是否正常。然后,我们需要进行基本的测试,例如按键的测试、LCD1602的测试和蜂鸣器的测试等。如果基本测试通过,我们可以进行更复杂的测试,例如播放谱子、存储和读取音符等。最后,我们需要对电子琴进行全面的测试,确保其功能正常、音质良好和显示清晰等。在进行pcb的绘制后再进行更完善的制作;
d0dc9a7a5b9d881ec6eae67d8c69052d
8a275c840a1591ff45c6e46951b670e3
4e68e408963a9c599c3eaf8843733a8e

评论

楼主的编程能力非常了得,学习一下。  发表于 2023-6-30 22:26
田舍郎 发表于 2023-6-30 23:08 来自手机 | 显示全部楼层
音调准不准
mwxpk 发表于 2023-7-1 08:43 | 显示全部楼层
琴键需有压力传感变化,不是按下就可以,先搞明白乐器。玩具怎么搞有要的就行。
yangxiaor520 发表于 2023-7-1 11:31 来自手机 | 显示全部楼层
搞个自动演奏,自动识别乐谱。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

10

主题

59

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部