找回密码
 立即注册
查看: 348|回复: 18

用【STCAI-万能实验板】做实验 用I2S音频播放方式实现八音盒

[复制链接]
  • 打卡等级:以坛为家I
  • 打卡总天数:260
  • 最近打卡:2026-04-02 21:11:44
已绑定手机

11

主题

82

回帖

669

积分

高级会员

积分
669
发表于 2026-3-1 23:34:02 | 显示全部楼层 |阅读模式
最新成果,读纸带的电子八音盒。再也不怕弹错键了


--------------------------------------------

一直想做一个可以自定义音色,并且可以发出和弦声音的简易电子琴,看到STC32G144K246特别加大了程序存储空间,
正好赶上论坛鼓励大家学习新的芯片,组织了使用万能实验板做实验的活动,我用论坛签到积分兑换了三块实验板,申请了2个芯片,开始行动。

官方更新了脚位图,大家注意一下。

截图202603012329372975.jpg 截图202603012333276130.jpg
为了方便试验,我做了简易的标签贴纸,放在附件了,需要的朋友可以自行打印


脚位贴纸.pdf

221.18 KB, 下载次数: 7

1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:260
  • 最近打卡:2026-04-02 21:11:44
已绑定手机

11

主题

82

回帖

669

积分

高级会员

积分
669
发表于 5 天前 | 显示全部楼层
用纸带谱曲实现了八音盒模式,再也不拍弹错键了
Screenshot_2026-03-31-22-41-06-085-edit_com.bilibili.studio.jpg




回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:260
  • 最近打卡:2026-04-02 21:11:44
已绑定手机

11

主题

82

回帖

669

积分

高级会员

积分
669
发表于 2026-3-1 23:51:48 | 显示全部楼层
今天完成了单一的音乐播放,烧录完即可循环播放,借鉴了之前AI8051上例程,但是有一点不一样的地方就是STC32G144K246必须使用PLL时钟,我是个菜鸟,弄明白这些花费了一点时间。但终于出声音了。
文件见附件 电子琴.hex (450.42 KB, 下载次数: 3) ,程序如下:
  1. //=================== 头文件包含 ============================================
  2. #include "STC32G144K246.H"
  3. #include "intrins.h"
  4. #include "yinyue.h"
  5. //=================== 主时钟和采样率定义宏 =================================
  6. #define FOSC 56000000UL      // 系统主时钟 96MHz (由PLL产生)
  7. #define PLL_CLK 480000000UL  // PLL输出时钟 480MHz
  8. #define SampleRate 8000     // 定义采样率
  9. //=================== I2S配置 ============================================
  10. #define MCKOE 0 // I2S主时钟输出使能, 0:禁止I2S主时钟输出, 1:允许I2S主时钟输出
  11. #define I2SEN 0x04 // I2S使能位, 0x00:关闭, 0x04:使能
  12. #define I2S_MODE 2 // I2S模式, 0:从发送, 1:从接收, 2:主发送, 3:主接收,
  13. #define PCMSYNC 0  // PCM同步模式, 0: 长同步, 1: 短同步
  14. #define STD_MODE 0 // I2S标准模式, 0: I2S标准格式, 1: MSB对齐格式, 2:LSB对齐格式, 3:PCM模式, CS4334或CS4344选择0:I2S标准格式,PT8211选择1: MSB对齐格式
  15. #define CKPOL 0           // I2S时钟极性, 0:SCLK空闲时为低电平, 1:SCLK空闲时为高电平
  16. #define DATLEN 0   // 数据长度, 0:16位, 1:24位, 2:32位, 3:保留
  17. #define CHLEN 0           // 声道长度(在PCM模式下), 0:16位, 1: 32位
  18. // 分频系数计算
  19. #define I2S_MCLKDIV (FOSC / (8 * 16 * 2 * SampleRate))  // MCLK分频系数
  20. #define I2S_BCLKDIV (FOSC / (16 * 2 * SampleRate))      // BCLK分频系数
  21. //=================== 类型定义 ============================================
  22. typedef unsigned char u8;
  23. typedef unsigned int u16;
  24. typedef unsigned long u32;
  25. typedef signed int s16;
  26. // 扩展音频索引变量
  27. u32 yinyue_index;
  28. // 扩展音频播放状态
  29. bit yinyue;
  30. u16 buffer[2];
  31. bit flip;
  32. bit flip_local;
  33. //=================== 延时函数 ==============================================
  34. void delay_ms(u16 ms)
  35. {
  36.     u16 i;
  37.     while(ms--)
  38.     {
  39.         i = 4000;
  40.         while(i--);
  41.     }
  42. }
  43. //=================== PLL时钟初始化 ==========================================
  44. void pll_init(void)
  45. {
  46.     // STC32G的PLL初始化
  47.     HPLLCR = 0x10;           // 选择HIRC作为PLL输入
  48.     HPLLPDIV = 8;            // 预分频8, HIRC 48MHz/8 = 6MHz
  49.     HPLLCR |= 0x0e;          // 倍频80, 6MHz*80 = 480MHz
  50.     HPLLCR |= 0x80;          // 使能HPLL
  51.    
  52.     delay_ms(10);             // 等待PLL稳定
  53.    
  54.     CLKDIV = 5;              // 系统时钟分频: 480MHz/5 = 96MHz
  55.     CLKSEL = 0x04;           // 选择HPLL/2作为主时钟源
  56.    
  57.     delay_ms(10);             // 等待时钟切换完成
  58. }
  59. //=================== 系统初始化 ============================================
  60. void system_init(void)
  61. {
  62.     WTST = 0x00;
  63.     CKCON = 0x00;
  64.     EAXFR = 1;
  65.     // 初始化IO口
  66.     P0M0 = 0x00;
  67.     P0M1 = 0x00;
  68.     P1M0 = 0x00;
  69.     P1M1 = 0x00;
  70.    
  71.     P2M0 = 0x00;
  72.     P2M1 = 0x00;
  73.     P2PU = 0xff;     
  74.    
  75.     P3M0 = 0x00;
  76.     P3M1 = 0x00;
  77.     P3PU = 0xff;     
  78.     P4M0 = 0x00;
  79.     P4M1 = 0x00;
  80.     P5M0 = 0x00;
  81.     P5M1 = 0x00;
  82. }
  83. //=================== I2S初始化 ============================================
  84. void i2s_init(void)
  85. {
  86.     I2SMD = 0xff;                               // 模块使能寄存器,初始化为FFH
  87.     I2SSR = 0x00;                               // 状态寄存器清零
  88.     I2SCR = 0x80 + 0x00;                        // 使能I2S模块(0x80), +0x00:Motorola格式, +0x10:TI格式
  89.     HSCLKDIV = 1;                               // 高速时钟分频 1~255 (默认为2)
  90.     I2S_CLKDIV = 1;                             // I2S时钟分频
  91.     I2SMCKDIV = I2S_MCLKDIV;                    // I2S主时钟分频,I2SMCLK = 系统时钟/2/I2S_CLKDIV/HSCLKDIV/I2SMCKDIV, 或I2SMCLK = PLLCLK/2/I2S_CLKDIV/HSCLKDIV/I2SMCKDIV
  92.     I2SPRH = (MCKOE << 1) + (I2S_BCLKDIV & 1);  // 高字节:bit1控制I2S_BMCLK输出使能, bit0为BCLK分频系数的bit0, 同时控制是否输出MCLK
  93.     I2SPRL = I2S_BCLKDIV / 2;                    // 低字节:BCLK分频系数的bit8~bit1
  94.     I2SCFGH = I2S_MODE;                          // 配置I2S工作模式
  95.     I2SCFGL = (PCMSYNC << 7) + (STD_MODE << 4) + (CKPOL << 3) + (DATLEN << 1) + CHLEN;
  96.     P_SW3 = (P_SW3 & 0x3f) | (2 << 6);           // I2S引脚切换,
  97.                                        // 00: P3.2(BCLK) P3.3(MCLK) P3.4(SD) P3.5(LRCK),
  98.                                        // 01: P1.7(BCLK) P1.6(MCLK) P1.5(SD) P1.4(LRCK),
  99.                                        // 10: P2.3(BCLK) P2.2(MCLK) P2.1(SD) P2.0(LRCK),
  100.                                        // 11: P4.3(BCLK) P1.6(MCLK) P4.1(SD) P4.0(LRCK)
  101. }
  102. //=================== 音频初始化 ==============================================
  103. void audio_init(void)
  104. {
  105.     yinyue_index = 0;
  106.     buffer[0] = 128;
  107.     buffer[1] = 128;
  108.     flip = 0;
  109.     flip_local = 0;
  110. }
  111. //=================== I2S中断服务程序 ================================
  112. void I2S_ISR(void) interrupt 62
  113. {
  114.     if (I2SSR & 0x02)  // 发送缓冲区空
  115.     {
  116.         s16 audio_data;
  117.         
  118.         // 正确的8位转16位转换
  119.         // 减去128去除直流偏置,左移7位得到16位有符号值
  120.         audio_data = ((s16)buffer[flip] - 128) << 7;
  121.         
  122.         // 音量调节选项(取消注释其中一个):
  123.         // audio_data = ((s16)buffer[flip] - 128) << 6;  // 最小音量
  124.         // audio_data = ((s16)buffer[flip] - 128) << 7;  // 中等音量
  125.         // audio_data = ((s16)buffer[flip] - 128) << 8;  // 最大音量
  126.         
  127.         // 发送16位数据
  128.         I2SDRH = (u8)(audio_data >> 8);   // 高8位
  129.         I2SDRL = (u8)audio_data;           // 低8位
  130.         
  131.         // 左声道切换缓冲区
  132.         if (!(I2SSR & 0x04))
  133.         {
  134.             flip = !flip;
  135.         }
  136.     }
  137. }
  138. //=================== 主函数 ==============================================
  139. void main(void)
  140. {
  141.     system_init();   // 系统初始化
  142.     pll_init();      // PLL时钟初始化
  143.     i2s_init();      // I2S初始化
  144.     audio_init();    // 音频初始化
  145.    
  146.     I2SCFGH |= I2SEN;  // 使能I2S
  147.     EA = 1;              // 使能全局中断
  148.    
  149.     while (1)
  150.     {
  151.         if (flip_local != flip)
  152.         {
  153.             buffer[flip_local] = yinyue_pcm[yinyue_index];
  154.             
  155.             yinyue_index++;
  156.             if (yinyue_index >= yinyue_pcm_len)
  157.             {
  158.                 yinyue_index = 0;
  159.             }
  160.             
  161.             flip_local = flip;
  162.         }
  163.         _nop_();
  164.     }
  165. }
复制代码


截图202603012345005203.jpg



后续重新用新版本万能实验板做了,能正常播放了。下一步添加7个按键实现7个音节。

回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:208
  • 最近打卡:2026-04-03 00:02:01
已绑定手机

15

主题

285

回帖

1492

积分

金牌会员

DIY玩家

积分
1492
发表于 2026-3-2 14:56:47 | 显示全部楼层
灰太*** 发表于 2026-3-1 23:51
今天完成了单一的音乐播放,烧录完即可循环播放,借鉴了之前AI8051上例程,但是有一点不一样的地方就是STC3 ...

5V电源有无稳压滤波
真正的学徒往往怀着大师的心
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:260
  • 最近打卡:2026-04-02 21:11:44
已绑定手机

11

主题

82

回帖

669

积分

高级会员

积分
669
发表于 2026-3-3 09:40:21 来自手机 | 显示全部楼层
狂热主宰 发表于 2026-3-2 14:56
5V电源有无稳压滤波

我回来加个滤波电容试一下
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:369
  • 最近打卡:2026-04-03 10:05:28

844

主题

1万

回帖

2万

积分

管理员

积分
22789
发表于 2026-3-4 10:18:08 | 显示全部楼层

已更新设计

截图202603041018049482.jpg



截图202603041023314281.jpg
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:260
  • 最近打卡:2026-04-02 21:11:44
已绑定手机

11

主题

82

回帖

669

积分

高级会员

积分
669
发表于 2026-3-6 22:53:13 | 显示全部楼层
今天用之前AI8051U的程序修改的可以播放四个音的程序,需要注意的是这两个单片机I2S接口引脚不同,还有就是STC32G144K246必须使用PLL时钟才可以。

  1. //=================== 头文件包含 ============================================
  2. #include "STC32G144K246.H"
  3. #include "intrins.h"
  4. #include "yinxiao1.h"
  5. #include "yinxiao2.h"
  6. #include "yinxiao3.h"
  7. #include "yinxiao4.h"
  8. //=================== 主时钟和采样率定义宏 =================================
  9. #define FOSC 96000000UL      // 系统主时钟 96MHz (由PLL产生)
  10. #define PLL_CLK 480000000UL  // PLL输出时钟 480MHz
  11. #define SampleRate 16000     // 定义采样率
  12. //=================== I2S配置 ============================================
  13. #define MCKOE 0 // I2S主时钟输出使能, 0:禁止I2S主时钟输出, 1:允许I2S主时钟输出
  14. #define I2SEN 0x04 // I2S使能位, 0x00:关闭, 0x04:使能
  15. #define I2S_MODE 2 // I2S模式, 0:从发送, 1:从接收, 2:主发送, 3:主接收,
  16. #define PCMSYNC 0  // PCM同步模式, 0: 长同步, 1: 短同步
  17. #define STD_MODE 0 // I2S标准模式, 0: I2S标准格式, 1: MSB对齐格式, 2:LSB对齐格式, 3:PCM模式, CS4334或CS4344选择0:I2S标准格式,PT8211选择1: MSB对齐格式
  18. #define CKPOL 0           // I2S时钟极性, 0:SCLK空闲时为低电平, 1:SCLK空闲时为高电平
  19. #define DATLEN 0   // 数据长度, 0:16位, 1:24位, 2:32位, 3:保留
  20. #define CHLEN 0           // 声道长度(在PCM模式下), 0:16位, 1: 32位
  21. //#define I2S_MCLKDIV (FOSC / (8 * 16 * 2 * SampleRate)) // MCLK分频系数, 支持16bit.
  22. //#define I2S_BCLKDIV (FOSC / (16 * 2 * SampleRate))           // BCLK分频系数, 支持16bit.
  23. ///2#define I2S_MCLKDIV (FOSC / (8 * 16 * 2 * SampleRate)) // MCLK分频系数, 支持16bit.
  24. ///2#define I2S_BCLKDIV (FOSC / (16 * SampleRate))           // BCLK分频系数, 支持16bit.
  25. // 分频系数计算
  26. #define I2S_MCLKDIV (FOSC / (8 * 16 * 2 * SampleRate))  // MCLK分频系数
  27. #define I2S_BCLKDIV (FOSC / (16 * 2 * SampleRate))      // BCLK分频系数
  28. //=================== 类型定义 ============================================
  29. typedef unsigned char u8;
  30. typedef unsigned int u16;
  31. typedef unsigned long u32;
  32. u16 yinxiao1_index;
  33. u16 yinxiao2_index;
  34. u16 yinxiao3_index;
  35. u16 yinxiao4_index;
  36. bit yinxiao1;
  37. bit yinxiao2;
  38. bit yinxiao3;
  39. bit yinxiao4;
  40. // 时间戳变量 - 用于记录音频开始播放的时间,实现"挤掉最先播放的"逻辑
  41. u16 start_time1;
  42. u16 start_time2;
  43. u16 start_time3;
  44. u16 start_time4;
  45. u16 current_time;  // 当前时间计数器
  46. // 按键状态变量 - 用于检测按键按下事件
  47. bit key1_pressed;
  48. bit key2_pressed;
  49. bit key3_pressed;
  50. bit key4_pressed;
  51. u16 buffer[2];
  52. bit flip;
  53. bit flip_local;
  54. //=================== 延时函数 ==============================================
  55. void delay_ms(u16 ms)
  56. {
  57.     u16 i;
  58.     while(ms--)
  59.     {
  60.         i = 4000;
  61.         while(i--);
  62.     }
  63. }
  64. //=================== PLL时钟初始化 ==========================================
  65. void pll_init(void)
  66. {
  67.     // STC32G的PLL初始化
  68.     HPLLCR = 0x10;           // 选择HIRC作为PLL输入
  69.     HPLLPDIV = 8;            // 预分频8, HIRC 48MHz/8 = 6MHz
  70.     HPLLCR |= 0x0e;          // 倍频80, 6MHz*80 = 480MHz
  71.     HPLLCR |= 0x80;          // 使能HPLL
  72.    
  73.     delay_ms(10);             // 等待PLL稳定
  74.    
  75.     CLKDIV = 5;              // 系统时钟分频: 480MHz/5 = 96MHz
  76.     CLKSEL = 0x04;           // 选择HPLL/2作为主时钟源
  77.    
  78.     delay_ms(10);             // 等待时钟切换完成
  79. }
  80. //=================== 主函数 ==============================================
  81. void main(void)
  82. {
  83.         
  84.                 // 初始化系统
  85.         WTST = 0x00;
  86.         CKCON = 0x00;
  87.         EAXFR = 1;
  88.         // 初始化PLL时钟
  89.         pll_init();
  90.         // 初始化IO口
  91.         
  92.         P0M0 = 0x00;
  93.         P0M1 = 0x00;
  94.         P1M0 = 0x00;
  95.         P1M1 = 0x00;
  96.         
  97.         P2M0 = 0x00;
  98.         P2M1 = 0x00;
  99.   P2PU = 0xff;         
  100.         
  101.   P3M0 = 0x00;
  102.   P3M1 = 0x00;
  103.   P3PU = 0xff;         
  104.         P4M0 = 0x00;
  105.         P4M1 = 0x00;
  106.   P5M0 = 0x00;
  107.   P5M1 = 0x00;
  108.         I2SMD = 0xff;                                                           // 模块使能寄存器,初始化为FFH
  109.         I2SSR = 0x00;                                                           // 状态寄存器清零
  110.         I2SCR = 0x80 + 0x00;                                           // 使能I2S模块(0x80), +0x00:Motorola格式, +0x10:TI格式
  111.         HSCLKDIV = 1;                                                           // 高速时钟分频 1~255 (默认为2)
  112.         I2S_CLKDIV = 1;                                                           // I2S时钟分频
  113.         I2SMCKDIV = I2S_MCLKDIV;                                   // I2S主时钟分频,I2SMCLK = 系统时钟/2/I2S_CLKDIV/HSCLKDIV/I2SMCKDIV, 或I2SMCLK = PLLCLK/2/I2S_CLKDIV/HSCLKDIV/I2SMCKDIV
  114.         I2SPRH = (MCKOE << 1) + (I2S_BCLKDIV & 1); // 高字节:bit1控制I2S_BMCLK输出使能, bit0为BCLK分频系数的bit0, 同时控制是否输出MCLK
  115.         I2SPRL = I2S_BCLKDIV / 2;                                   // 低字节:BCLK分频系数的bit8~bit1
  116.         I2SCFGH = I2S_MODE;                                                   // 配置I2S工作模式
  117.         I2SCFGL = (PCMSYNC << 7) + (STD_MODE << 4) + (CKPOL << 3) + (DATLEN << 1) + CHLEN;
  118.         P_SW3 = (P_SW3 & 0x3f) | (2 << 6); // I2S引脚切换,
  119.                                                                           
复制代码
音效文件在头文件里,
yinxiao1.h (154.56 KB, 下载次数: 0) yinxiao2.h (152 KB, 下载次数: 0) yinxiao3.h (85.68 KB, 下载次数: 0) yinxiao4.h (121.39 KB, 下载次数: 0)
我用AI写了个将MP3转化为头文件的网页版转换程序,可以方便的进行转换
转换.html (24.39 KB, 下载次数: 1)

截图202603081952079844.jpg
截图202603081953022038.jpg
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看II
  • 打卡总天数:26
  • 最近打卡:2026-03-31 21:46:38
已绑定手机

2

主题

24

回帖

106

积分

注册会员

积分
106
发表于 2026-3-8 14:40:18 | 显示全部楼层
666,i3s和i2c有啥区别?对于本单片机来说?
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:260
  • 最近打卡:2026-04-02 21:11:44
已绑定手机

11

主题

82

回帖

669

积分

高级会员

积分
669
发表于 2026-3-8 19:42:29 | 显示全部楼层
guoju*** 发表于 2026-3-8 14:40
666,i3s和i2c有啥区别?对于本单片机来说?

简单来说,I2C是设备间用于传输数据的“控制总线”,I2S是传输数字音频的“数据总线”。
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看II
  • 打卡总天数:26
  • 最近打卡:2026-03-31 21:46:38
已绑定手机

2

主题

24

回帖

106

积分

注册会员

积分
106
发表于 2026-3-8 20:05:24 | 显示全部楼层
灰太*** 发表于 2026-3-8 19:42
简单来说,I2C是设备间用于传输数据的“控制总线”,I2S是传输数字音频的“数据总线”。 ...

收到,谢谢。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:260
  • 最近打卡:2026-04-02 21:11:44
已绑定手机

11

主题

82

回帖

669

积分

高级会员

积分
669
发表于 2026-3-11 22:45:25 来自手机 | 显示全部楼层
感谢版主,寄来了新版本的万能板。焊接好基础元件,开始继续研究。
IMG_20260311_223615.jpg
回复

使用道具 举报 送花

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

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-4-3 16:08 , Processed in 0.131996 second(s), 101 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表