灰太狼8888 发表于 2026-3-1 23:34:02

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

最新成果,读纸带的电子八音盒。再也不怕弹错键了{:4_174:}


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

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

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

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


灰太狼8888 发表于 6 天前

用纸带谱曲实现了八音盒模式,再也不拍弹错键了





灰太狼8888 发表于 2026-3-1 23:51:48

今天完成了单一的音乐播放,烧录完即可循环播放,借鉴了之前AI8051上例程,但是有一点不一样的地方就是STC32G144K246必须使用PLL时钟,我是个菜鸟,弄明白这些花费了一点时间。但终于出声音了。
文件见附件,程序如下:
//=================== 头文件包含 ============================================
#include "STC32G144K246.H"
#include "intrins.h"
#include "yinyue.h"

//=================== 主时钟和采样率定义宏 =================================
#define FOSC 56000000UL      // 系统主时钟 96MHz (由PLL产生)
#define PLL_CLK 480000000UL// PLL输出时钟 480MHz
#define SampleRate 8000   // 定义采样率

//=================== I2S配置 ============================================
#define MCKOE 0 // I2S主时钟输出使能, 0:禁止I2S主时钟输出, 1:允许I2S主时钟输出

#define I2SEN 0x04 // I2S使能位, 0x00:关闭, 0x04:使能
#define I2S_MODE 2 // I2S模式, 0:从发送, 1:从接收, 2:主发送, 3:主接收,

#define PCMSYNC 0// PCM同步模式, 0: 长同步, 1: 短同步
#define STD_MODE 0 // I2S标准模式, 0: I2S标准格式, 1: MSB对齐格式, 2:LSB对齐格式, 3:PCM模式, CS4334或CS4344选择0:I2S标准格式,PT8211选择1: MSB对齐格式
#define CKPOL 0           // I2S时钟极性, 0:SCLK空闲时为低电平, 1:SCLK空闲时为高电平
#define DATLEN 0   // 数据长度, 0:16位, 1:24位, 2:32位, 3:保留
#define CHLEN 0           // 声道长度(在PCM模式下), 0:16位, 1: 32位

// 分频系数计算
#define I2S_MCLKDIV (FOSC / (8 * 16 * 2 * SampleRate))// MCLK分频系数
#define I2S_BCLKDIV (FOSC / (16 * 2 * SampleRate))      // BCLK分频系数

//=================== 类型定义 ============================================
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
typedef signed int s16;

// 扩展音频索引变量
u32 yinyue_index;

// 扩展音频播放状态
bit yinyue;

u16 buffer;
bit flip;
bit flip_local;

//=================== 延时函数 ==============================================
void delay_ms(u16 ms)
{
    u16 i;
    while(ms--)
    {
      i = 4000;
      while(i--);
    }
}

//=================== PLL时钟初始化 ==========================================
void pll_init(void)
{
    // STC32G的PLL初始化
    HPLLCR = 0x10;         // 选择HIRC作为PLL输入
    HPLLPDIV = 8;            // 预分频8, HIRC 48MHz/8 = 6MHz
    HPLLCR |= 0x0e;          // 倍频80, 6MHz*80 = 480MHz
    HPLLCR |= 0x80;          // 使能HPLL
   
    delay_ms(10);             // 等待PLL稳定
   
    CLKDIV = 5;            // 系统时钟分频: 480MHz/5 = 96MHz
    CLKSEL = 0x04;         // 选择HPLL/2作为主时钟源
   
    delay_ms(10);             // 等待时钟切换完成
}

//=================== 系统初始化 ============================================
void system_init(void)
{
    WTST = 0x00;
    CKCON = 0x00;
    EAXFR = 1;

    // 初始化IO口
    P0M0 = 0x00;
    P0M1 = 0x00;
    P1M0 = 0x00;
    P1M1 = 0x00;
   
    P2M0 = 0x00;
    P2M1 = 0x00;
    P2PU = 0xff;   
   
    P3M0 = 0x00;
    P3M1 = 0x00;
    P3PU = 0xff;   

    P4M0 = 0x00;
    P4M1 = 0x00;
    P5M0 = 0x00;
    P5M1 = 0x00;
}

//=================== I2S初始化 ============================================
void i2s_init(void)
{
    I2SMD = 0xff;                               // 模块使能寄存器,初始化为FFH
    I2SSR = 0x00;                               // 状态寄存器清零
    I2SCR = 0x80 + 0x00;                        // 使能I2S模块(0x80), +0x00:Motorola格式, +0x10:TI格式
    HSCLKDIV = 1;                               // 高速时钟分频 1~255 (默认为2)
    I2S_CLKDIV = 1;                           // I2S时钟分频
    I2SMCKDIV = I2S_MCLKDIV;                  // I2S主时钟分频,I2SMCLK = 系统时钟/2/I2S_CLKDIV/HSCLKDIV/I2SMCKDIV, 或I2SMCLK = PLLCLK/2/I2S_CLKDIV/HSCLKDIV/I2SMCKDIV
    I2SPRH = (MCKOE << 1) + (I2S_BCLKDIV & 1);// 高字节:bit1控制I2S_BMCLK输出使能, bit0为BCLK分频系数的bit0, 同时控制是否输出MCLK
    I2SPRL = I2S_BCLKDIV / 2;                  // 低字节:BCLK分频系数的bit8~bit1
    I2SCFGH = I2S_MODE;                        // 配置I2S工作模式
    I2SCFGL = (PCMSYNC << 7) + (STD_MODE << 4) + (CKPOL << 3) + (DATLEN << 1) + CHLEN;
    P_SW3 = (P_SW3 & 0x3f) | (2 << 6);         // I2S引脚切换,
                                       // 00: P3.2(BCLK) P3.3(MCLK) P3.4(SD) P3.5(LRCK),
                                       // 01: P1.7(BCLK) P1.6(MCLK) P1.5(SD) P1.4(LRCK),
                                       // 10: P2.3(BCLK) P2.2(MCLK) P2.1(SD) P2.0(LRCK),
                                       // 11: P4.3(BCLK) P1.6(MCLK) P4.1(SD) P4.0(LRCK)
}

//=================== 音频初始化 ==============================================
void audio_init(void)
{
    yinyue_index = 0;
    buffer = 128;
    buffer = 128;
    flip = 0;
    flip_local = 0;
}

//=================== I2S中断服务程序 ================================
void I2S_ISR(void) interrupt 62
{
    if (I2SSR & 0x02)// 发送缓冲区空
    {
      s16 audio_data;
      
      // 正确的8位转16位转换
      // 减去128去除直流偏置,左移7位得到16位有符号值
      audio_data = ((s16)buffer - 128) << 7;
      
      // 音量调节选项(取消注释其中一个):
      // audio_data = ((s16)buffer - 128) << 6;// 最小音量
      // audio_data = ((s16)buffer - 128) << 7;// 中等音量
      // audio_data = ((s16)buffer - 128) << 8;// 最大音量
      
      // 发送16位数据
      I2SDRH = (u8)(audio_data >> 8);   // 高8位
      I2SDRL = (u8)audio_data;         // 低8位
      
      // 左声道切换缓冲区
      if (!(I2SSR & 0x04))
      {
            flip = !flip;
      }
    }
}

//=================== 主函数 ==============================================
void main(void)
{
    system_init();   // 系统初始化
    pll_init();      // PLL时钟初始化
    i2s_init();      // I2S初始化
    audio_init();    // 音频初始化
   
    I2SCFGH |= I2SEN;// 使能I2S
    EA = 1;            // 使能全局中断
   
    while (1)
    {
      if (flip_local != flip)
      {
            buffer = yinyue_pcm;
            
            yinyue_index++;
            if (yinyue_index >= yinyue_pcm_len)
            {
                yinyue_index = 0;
            }
            
            flip_local = flip;
      }
      _nop_();
    }
}



attach://132743.mp4

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

狂热主宰 发表于 2026-3-2 14:56:47

灰太狼8888 发表于 2026-3-1 23:51
今天完成了单一的音乐播放,烧录完即可循环播放,借鉴了之前AI8051上例程,但是有一点不一样的地方就是STC3 ...

5V电源有无稳压滤波

灰太狼8888 发表于 2026-3-3 09:40:21

狂热主宰 发表于 2026-3-2 14:56
5V电源有无稳压滤波

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

神农鼎 发表于 2026-3-4 10:18:08


已更新设计






灰太狼8888 发表于 2026-3-6 22:53:13

今天用之前AI8051U的程序修改的可以播放四个音的程序,需要注意的是这两个单片机I2S接口引脚不同,还有就是STC32G144K246必须使用PLL时钟才可以。


//=================== 头文件包含 ============================================
#include "STC32G144K246.H"
#include "intrins.h"

#include "yinxiao1.h"
#include "yinxiao2.h"
#include "yinxiao3.h"
#include "yinxiao4.h"


//=================== 主时钟和采样率定义宏 =================================
#define FOSC 96000000UL      // 系统主时钟 96MHz (由PLL产生)
#define PLL_CLK 480000000UL// PLL输出时钟 480MHz
#define SampleRate 16000   // 定义采样率

//=================== I2S配置 ============================================
#define MCKOE 0 // I2S主时钟输出使能, 0:禁止I2S主时钟输出, 1:允许I2S主时钟输出

#define I2SEN 0x04 // I2S使能位, 0x00:关闭, 0x04:使能
#define I2S_MODE 2 // I2S模式, 0:从发送, 1:从接收, 2:主发送, 3:主接收,

#define PCMSYNC 0// PCM同步模式, 0: 长同步, 1: 短同步
#define STD_MODE 0 // I2S标准模式, 0: I2S标准格式, 1: MSB对齐格式, 2:LSB对齐格式, 3:PCM模式, CS4334或CS4344选择0:I2S标准格式,PT8211选择1: MSB对齐格式
#define CKPOL 0         // I2S时钟极性, 0:SCLK空闲时为低电平, 1:SCLK空闲时为高电平
#define DATLEN 0   // 数据长度, 0:16位, 1:24位, 2:32位, 3:保留
#define CHLEN 0         // 声道长度(在PCM模式下), 0:16位, 1: 32位

//#define I2S_MCLKDIV (FOSC / (8 * 16 * 2 * SampleRate)) // MCLK分频系数, 支持16bit.
//#define I2S_BCLKDIV (FOSC / (16 * 2 * SampleRate))         // BCLK分频系数, 支持16bit.
///2#define I2S_MCLKDIV (FOSC / (8 * 16 * 2 * SampleRate)) // MCLK分频系数, 支持16bit.
///2#define I2S_BCLKDIV (FOSC / (16 * SampleRate))         // BCLK分频系数, 支持16bit.

// 分频系数计算
#define I2S_MCLKDIV (FOSC / (8 * 16 * 2 * SampleRate))// MCLK分频系数
#define I2S_BCLKDIV (FOSC / (16 * 2 * SampleRate))      // BCLK分频系数






//=================== 类型定义 ============================================
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

u16 yinxiao1_index;
u16 yinxiao2_index;
u16 yinxiao3_index;
u16 yinxiao4_index;

bit yinxiao1;
bit yinxiao2;
bit yinxiao3;
bit yinxiao4;

// 时间戳变量 - 用于记录音频开始播放的时间,实现"挤掉最先播放的"逻辑
u16 start_time1;
u16 start_time2;
u16 start_time3;
u16 start_time4;
u16 current_time;// 当前时间计数器

// 按键状态变量 - 用于检测按键按下事件
bit key1_pressed;
bit key2_pressed;
bit key3_pressed;
bit key4_pressed;

u16 buffer;
bit flip;
bit flip_local;



//=================== 延时函数 ==============================================
void delay_ms(u16 ms)
{
    u16 i;
    while(ms--)
    {
      i = 4000;
      while(i--);
    }
}

//=================== PLL时钟初始化 ==========================================
void pll_init(void)
{
    // STC32G的PLL初始化
    HPLLCR = 0x10;         // 选择HIRC作为PLL输入
    HPLLPDIV = 8;            // 预分频8, HIRC 48MHz/8 = 6MHz
    HPLLCR |= 0x0e;          // 倍频80, 6MHz*80 = 480MHz
    HPLLCR |= 0x80;          // 使能HPLL
   
    delay_ms(10);             // 等待PLL稳定
   
    CLKDIV = 5;            // 系统时钟分频: 480MHz/5 = 96MHz
    CLKSEL = 0x04;         // 选择HPLL/2作为主时钟源
   
    delay_ms(10);             // 等待时钟切换完成
}






//=================== 主函数 ==============================================
void main(void)
{
      
                // 初始化系统
      WTST = 0x00;
      CKCON = 0x00;
      EAXFR = 1;

      // 初始化PLL时钟
      pll_init();

      // 初始化IO口
      

      P0M0 = 0x00;
      P0M1 = 0x00;
      P1M0 = 0x00;
      P1M1 = 0x00;
      
      P2M0 = 0x00;
      P2M1 = 0x00;
P2PU = 0xff;         
      
P3M0 = 0x00;
P3M1 = 0x00;
P3PU = 0xff;         

      P4M0 = 0x00;
      P4M1 = 0x00;
P5M0 = 0x00;
P5M1 = 0x00;

      I2SMD = 0xff;                                                         // 模块使能寄存器,初始化为FFH
      I2SSR = 0x00;                                                         // 状态寄存器清零
      I2SCR = 0x80 + 0x00;                                           // 使能I2S模块(0x80), +0x00:Motorola格式, +0x10:TI格式
      HSCLKDIV = 1;                                                         // 高速时钟分频 1~255 (默认为2)
      I2S_CLKDIV = 1;                                                         // I2S时钟分频
      I2SMCKDIV = I2S_MCLKDIV;                                 // I2S主时钟分频,I2SMCLK = 系统时钟/2/I2S_CLKDIV/HSCLKDIV/I2SMCKDIV, 或I2SMCLK = PLLCLK/2/I2S_CLKDIV/HSCLKDIV/I2SMCKDIV
      I2SPRH = (MCKOE << 1) + (I2S_BCLKDIV & 1); // 高字节:bit1控制I2S_BMCLK输出使能, bit0为BCLK分频系数的bit0, 同时控制是否输出MCLK
      I2SPRL = I2S_BCLKDIV / 2;                                 // 低字节:BCLK分频系数的bit8~bit1
      I2SCFGH = I2S_MODE;                                                   // 配置I2S工作模式
      I2SCFGL = (PCMSYNC << 7) + (STD_MODE << 4) + (CKPOL << 3) + (DATLEN << 1) + CHLEN;
      P_SW3 = (P_SW3 & 0x3f) | (2 << 6); // I2S引脚切换,
                                                                        音效文件在头文件里,

我用AI写了个将MP3转化为头文件的网页版转换程序,可以方便的进行转换




guojun0718 发表于 2026-3-8 14:40:18

666,i3s和i2c有啥区别?对于本单片机来说?

灰太狼8888 发表于 2026-3-8 19:42:29

guojun0718 发表于 2026-3-8 14:40
666,i3s和i2c有啥区别?对于本单片机来说?

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

guojun0718 发表于 2026-3-8 20:05:24

灰太狼8888 发表于 2026-3-8 19:42
简单来说,I2C是设备间用于传输数据的“控制总线”,I2S是传输数字音频的“数据总线”。 ...

收到,谢谢。

灰太狼8888 发表于 2026-3-11 22:45:25

感谢版主{:4_196:},寄来了新版本的万能板。焊接好基础元件,开始继续研究。
页: [1] 2
查看完整版本: 用【STCAI-万能实验板】做实验 用I2S音频播放方式实现八音盒