发作品签到
专业版

基于CH32V305 的桌面音箱

工程标签

561
0
0
2

简介

这是一个基于 WCH 出品的 Ch32V305 制作的桌面音箱,它能够从 USB接口播放你系统的声音,同时还提供了 USB端口扩展功能。

简介:这是一个基于 WCH 出品的 Ch32V305 制作的桌面音箱,它能够从 USB接口播放你系统的声音,同时还提供了 USB端口扩展功能。
星火计划2024
复刻成本:300

开源协议

GPL 3.0

创建时间:2024-10-16 12:04:26更新时间:2024-11-19 10:50:10

描述

前一段时间笔者入手了一台显示器,但是将它连接到主机之后,一直无法发出声音。经过一段时间的研究终于发现原因:显示器的 HDMI 接口向主机汇报了一个音频设备,但是显示器并没有扬声器,因此虽然主机设定了HDMI 音源,但是无法发出声音。为了解决这个问题,我决心制造一个桌面音箱,使用USB接口连接主机,此外还增加了一个 USB Hub可以对USB进行扩展。最终这个作品不仅仅是一个音箱还是一个桌搭,可以扩展出多个 USB接口方便用户使用。

硬件设计上比较简单,使用到的芯片有三种:

首先核心芯片使用的是WCH(南京沁恒微电子股份有限公司)出品的CH32V305芯片:

它是WCH互联型青稞RISC-V MCU CH32V307系列单片机中的一员,这个系列单片机带有USB High Speed 控制器,能够方便的实现 USB 主机和设备功能。其中CH32V305采用TSSOP20封装,适用于对体积要求较高的场合,同时方便焊接对于DIY用户非常友好。这里通过编程,CH32V305将自身模拟为USB音频设备,插入电脑后系统就会出现一个声音设备,(图片2, 插入后 Windows 中的音频设备,需要修改文字)操作系统会将音频数据通过USB接口送至该芯片,然后芯片将收到的音频信号发送到IIS总线上。IIS 是“Inter-IC Sound bus”的缩写,还可以写作I2S,这是一种广泛应用于数字音频传输的串行接口标准。它最初由飞利浦(Philips)公司开发,用于解决在集成电路之间传输音频数据的问题。I2S协议定义了音频数据的传输格式、时序和控制信号。这部分电路设计比较简单,可以看作是一个CH32V305 的最小系统,外部使用一颗16Mhz晶振为它提供信号:

CH32V305 负责将USB信号转化为 IIS 信号,之后HT513 数字功放芯片负责接收IIS信号并且转化为音频信号通过扬声器播放出来。HT513是嘉兴禾润电子科技有限公司出品的一款低成本单声道D类音频功放,其在5V供电时可提供最大2.8W的输出功率; 在6.5V供电时可提供最大4. 7W的输出功率。它内部集成了DA转换器,其I2S输入最大支持32-bit字节,最高支持192K的采样率, 更特别的在于这款芯片可以通过I2C接口进行控制输出增益,这样可以配合多种规格的扬声器,彻底避免因为供电不足导致的破音问题。例如:当前使用USB接口供电,理论上最大功率为 0.5Ax5V=2.5W,如果你使用了2个3W的扬声器,很可能会遇到音源输出较大时设备自动重启或者声音畸变的问题,如果你可以直接控制音量,那么可以保证设备仍然能够正常工作。

这个芯片是单声道功放,因此,我们需要使用2颗分别处理左声道和右声道,在HT513上,引脚5用于I2C 地址设定,通过该引脚设置为高和低,可以让芯片的地址选择为0x6E和0x6C(7-bits 地址)之间切换。在上电的时候,两个HT513地址不同,可以分别进行编程,通过设定寄存器可以让选择将左声道或者右声道数据送至扬声器。需要注意的一点是:CH32V305 的引脚3和引脚4分别是 I2C的SCL和SDA信号,同时也提供了 UART3 的 TX 和 RX复用功能,我们通过这个 UART端口输出一些log信息用于调试。实践中发现这两种信号不会发生相互干扰,因此,直接将这两个引脚连接到HT513芯片和对外的调试引脚上即可,中间无需特别隔离。

如前所述,除了音频功能,还要对 USB 输入进行扩展,选择了同样是沁恒微电子出品的USB HUB芯片:CH334 ,外围只需要一颗晶振和电容即可实现高速USB 的扩展,方便用户使用。

CH32V305 只能使用串行2 线调试接口(2-wire SDI Serial Debug Interface)进行代码烧写,为此,板子上预留如下接口。使用时,需要将SWD 和 SWC 以及RST引脚连接到WCH-LinkE沁恒仿真器上(这个仿真器核心也是CH32V305)。然后使用WCH-LinkUtility.exe进行下载。下载引脚在设计上有可能被复用为其他功能,因此在每次下载时需要先通过菜单选择通过RST引脚进行一次复位。

CH32V305和 HT513 都使用 3.3V供电,为此,使用TLV1117LV33DCYR 将5V转化为3.3V。此外因为板子上提供了 USB Hub 功能,考虑到可能会遇到功耗较高的设备(例如,USB转机械硬盘),因此还预留了一个外部供电接口,同时为了简化设计,直接使用了一个12V转5V的小板。有需要时,将这个小板焊接在右上方,接入DC座子提供的电源,然后将降压后的输出接入到5V中。

 

最终 PCB 设计如下:

硬件完成后,即可着手开始设计软件。编程直接使用 CH32V307系列配套的MounRiver Studio IDE来完成。MounRiver Studio是一套集成开发环境,名称中的 “山河” 取自“天圆地方,山河相依”,它是一款面向RISC-V、Arm等内核MCU的国产专业嵌入式项目开发、调试环境,提供烧录工具及完善的项目管理功能。编译后,生成 HEX文件,通过WCH-LinkE下载到板子上即可工作。

为了实现USB Audio ,这里使用了 CherryUSB框架,CherryUSB是一个简单、高效的USB设备驱动框架。它是专为资源受限的微控制器(MCU)设计,致力于简化USB设备的开发过程,让嵌入式系统与PC或其他支持USB的设备之间的通信变得更加容易。引入这个框架之后,很容易的开发出USB Audio 设备,我们只需要专注于收到后的数据处理即可。

关键的代码如下:

  1. HT513 的初始化,位于HT513_Init() 函数中:

其中的HT513_ADDR_L 和 HT513_ADDR_R分别是2个 HT513的地址,需要特别注意的是代码中使用的是 8-Bit

    //  PC8 = 1

    GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET);

 

    //  初始化 IIC

    IIC_Init(100000, 0xA0);

 

    //  测试

    HT513_ReadOneByte(HT513_ADDR_L, 0x19);

    HT513_ReadOneByte(HT513_ADDR_R, 0x19);

 

    //  取消静音

    HT513_WriteOneByte(HT513_ADDR_L, 0x12, 0xF4);

    HT513_WriteOneByte(HT513_ADDR_R, 0x12, 0xF4);

 

    //  设置模式  左对齐16bit

    HT513_WriteOneByte(HT513_ADDR_L, 0x13, 0x70);

    HT513_WriteOneByte(HT513_ADDR_R, 0x13, 0x70);

 

    //  设置声道

    HT513_WriteOneByte(HT513_ADDR_L, 0x17, 0xB0);

 

    //  设置音量

    HT513_WriteOneByte(HT513_ADDR_L, 0x16, 0xB0);

    HT513_WriteOneByte(HT513_ADDR_R, 0x16, 0xB0);

 

    //  读取数据

    HT513_L_17 = HT513_ReadOneByte(HT513_ADDR_L, 0x17);

    HT513_R_17 = HT513_ReadOneByte(HT513_ADDR_R, 0x17);

 

    //  设置好了取消

    I2C_DeInit(I2C2);

  1. 上电播放预定义音频的代码。
  2. 首先要取得音频数据。使用诸如格式工厂或者Golden Wave 这样的工具对音频进行剪辑,然后保存为 16K,16Bits,单声道的 WAVE 格式。WAVE(*.WAV)是‌微软公司开发的一种声音文件格式,符合‌PIFF(Resource Interchange File Format)文件规范,用于保存‌Windows平台的音频信息资源,被Windows平台及其应用程序所支持。WAV格式支持多种音频位数、‌采样频率和‌声道,是‌计算机和工业界支持最广泛,最便于使用的音频格式。为了读取到其中的音频信息,我们还需要对这种格式有简单的了解。一个 WAVE 可以看作是 RIFF数据块+ FORMAT区块+ DATA区块。

RIFF区块用于标识WAVE格式以及提供整个文件的大小信息

名称

字节数

内容

ChunkID

4

"RIFF" 标识符

ChunkSize

4

表示从下个地址开始到文件尾的总字节数

Format

4

"WAVE" 标识符

 

FORMAT区块 用于描述声音数据的格式

名称

字节数

内容

 Subchunk1ID

4

"fmt " 标识符,最后一位是空格

Subchunk1Size

4

该区块数据的长度(不包含该区块ID和Size的长度)

AudioFormat

2

音频格式,PCM音频数据的值为1

NumChannels

2

通道数

SampleRate

4

采样率

ByteRate

4

每秒数据字节数 = SampleRate * NumChannels * BitsPerSample / 8

BlockAlign

2

每个采样点所需的字节数 = NumChannels * BitsPerSample / 8

BitsPerSample

2

量化位数(bit)

 

DATA区块 包含数据的大小和实际声音

名称

字节数

内容

Subchunk2ID

4

"data" 标识符

Subchunk2Size

4

该区块数据的长度,(不包含该区块ID和Size的长度),也就是PCM字节数

Data

*

音频数据

 

打开一个 16K 采样率,16Bits ,单声道的WAVE文件,可以看到下面的数据。根据上面的文件格式定义,红色方框表示了RIFF区块起始位置;绿色方框是FORMAT区块的起始位置,我们可以对照上述格式数据检查音频文件是否满足要求;蓝色方框表示数据的位置。最终,我们需要的第一笔数据就在0x1E 0x00 0x15 0x01。从这里一直拷贝到文件结尾保存为另外的文件即可。

  1. 将上面的数据转化为 C 的 Header文件定义,放在 raw[] 数组中。HT513初始化完成后,可以将预定义的音频数据发送到HT513经由扬声器播放出来。这时候,首先要对CH32V305 的 IIS 进行初始化,初始化波特率为 16000Hz, 16Bits 采样率,双声道。这是通过 调用i2s_init((u32)16000);函数来实现的。CH32V305带有128KB的闪存,除去代码占用,还可以使用 至少96KB 的空间,因此可以存储96K/16K=6秒的单声道数据。这部分数据存储在h 文件中,数据长度为 raw_size, 具体数据在 char raw[] 数组中。

播放时,取出上述数组,将一个声道的数据复制到另外一个声道中,放在缓冲,然后通过 DMA 播放出去。特别注意的是 DMA 可以看作是“并行”运行,因此,我们增加了一个延时确保声音播放完成后才会继续下面的播放:

    for (u16 j=0;j

        for (u16 i=0;i

            Audio_Databuf[i*4  ]=raw[j*(ISO_PKT_SIZE/2)+i*2];

            Audio_Databuf[i*4+1]=raw[j*(ISO_PKT_SIZE/2)+i*2+1];

            Audio_Databuf[i*4+2]=raw[j*(ISO_PKT_SIZE/2)+i*2];

            Audio_Databuf[i*4+3]=raw[j*(ISO_PKT_SIZE/2)+i*2+1];

        }

        DMA_Tx_Init( DMA1_Channel5, (u32)&SPI2->DATAR, (u32)&Audio_Databuf[0], ISO_PKT_SIZE /2);

        DMA_Cmd( DMA1_Channel5, ENABLE );

        // 根据音频数据长度计算出来播放时间

        // 播放时间=总长度/2 / 16000 * 1000000

        Delay_Us(ISO_PKT_SIZE/2/2/16*1000);

}

  1. 为了实现USB Audio ,这次还使用了 CherryUSB框架,CherryUSB是一个简单、高效的USB设备驱动框架。它是专为资源受限的微控制器(MCU)设计,致力于简化USB设备的开发过程,让嵌入式系统与PC或其他支持USB的设备之间的通信变得更加容易。引入这个框架之后,很容易的开发出USB Audio 设备,我们只需要专注于收到后的数据处理即可。
  2. USB 描述符的设定。这里决定了Windows 如何识别出这个是声卡。代码在 c 文件中,如果想更改设备名称可以在这个文件中进行设定.
  3. 设备会将自身报告为一个采样率 48000Hz,16Bits,双声道的音频设备,具体在h通过如下代码设定。

/* AUDIO Class Config */

#define AUDIO_SPEAKER_FREQ            48000U

#define AUDIO_SPEAKER_FRAME_SIZE_BYTE 2u

#define AUDIO_SPEAKER_RESOLUTION_BIT  16u

  1. 之前播放开机音之后,我们需要重新设定IIS ,使用i2s_init((u32)AUDIO_SPEAKER_FREQ);重新设定

需要注意的是,采样率并不能随意选择, Windows 只支持几个特定的采样率,如果你使用自定义的采样率很可能会遇到无法工作的问题。此外,这次选择的 HT513 芯片需要MCLK ,因此我们还要特别考虑产生整数的 MCLK 以便使用。CH32V305 的时钟树如下,其中最左侧的 OSC_IN 和 OSC_OUT 是我们选择的晶振,可以选范围是 3-25Mhz ,最终的目标是提供给右上角的I2S Interface使用:

 

I2S 用到的引脚如下:

  • SD:串行数据,这里用来发送音频信号的数据;
  • WS:字选,这里作为数据控制信号输出,即使用高低电平通知接收端当前数据为左声道还是右声道;
  • CK:时钟信号,这里用作为IIS时钟信号, 这里我们需要产生 Fs=48000 x 2 (双通道)x 16bit= 1,536,000Hz
  • MCK:主时钟,这个信号输出CK时钟信号的频率预先设置为256×Fs,其中Fs 是音频信号的采样频率。

 

  1. CherryUSB收到音频数据后会使用回调位于c 中的 usbd_audio_out_callback() 函数,在这个函数中使用下面的代码将USB 缓冲区中的数据拷贝到Audio_Databuf[] 中:

usbd_ep_start_read(busid, AUDIO_OUT_EP, &Audio_Databuf[ISO_PKT_SIZE * ((++buffer_recv) & (MAX_PKT_CNT-1))], AUDIO_OUT_PACKET);

  1. 当 Windows 设置音频输出到 USB 声卡后,CherryUSB 会调用 usbd_audio_open() ,在其中我们会调用 i2s_start() ,设置 DMA 开始工作,DMA 负责将Audio_Databuf缓冲中的数据不停的搬移发送到IIS 总线上,配合前面的负责填充Audio_Databuf[] 的usbd_audio_out_callback()函数就能实现不停的播放了。

//  I2S 传输完成的中断

void DMA1_Channel5_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

void DMA1_Channel5_IRQHandler( void )

{

    //  传输完成,继续下一个传输

    if(DMA_GetITStatus(DMA1_IT_TC5)==SET )

    {

                             //  清中断标志

                             DMA_ClearITPendingBit(DMA1_IT_GL5);

 

        //  继续播放

        i2s_play();

    }

}

  1. 人耳对于声音有着很高的敏感性,因此音频对于实时性要求很高,有时候会遇到主机端发送数据来不及发送到 IIS总线的情况,为此我们设计了一个机制:当发现当前缓冲区数据达到阈值的时候会主动丢弃部分数据。例如,主机发送过来的数据包为 96 Bytes, 我们只播放其中的94 Bytes, 这样来实现节省时间的目的。这种方法非常简单使用。为了评估这种情况,在main() 函数中调用i2s_loop() 函数,这个函数的用途就是通过串口输出当前的缓冲区以及放弃数据的情况,用于评估播放质量。

    //  定时输出数据,间隔是256ms

    if (I2S_DMA_STAT && sp_cnt == 255)

    {

        printf("send=%d recv=%d cnt=%d equ=%d +=%d -=%d\n", sp_send, sp_recv, sp_size, sp_equ, sp_low, sp_hi);

        sp_rst = 1;

}

 

可以看到,一个简单的USB 扬声器包含了诸多细节,从晶振的选择,IIS总线频率的计算到代码的编写,特殊情况的处理,如果没有亲自动手实现,永远不会碰到如此之多的问题。制作的过程就是“知其然”,更是“知其所以然”的过程,获得的经验使得我们能够将他们应用到其他的项目之中。我想这也许这就是“自主研发”很重要的原因吧。在这次项目制作过程中,当我们遇到问题时,直接联系了南京沁恒微电子和嘉兴禾润电子科技的FAE,他们都及时给与了帮助,同时我们也将遇到的问题给与反馈。这也是在每一次产品的设计中我们尽量使用国产芯片的原因,不但能够取得及时的技术支持,还能让国产芯片更多的得到反馈。最终的成品也让中国芯片有机会走到聚光灯下,让中国芯片成为中国制造密不可分的一部分。

 

完整的功能介绍和演示可以在 https://www.bilibili.com/video/BV1JbUEY2EQA/ 看到

设计图

未生成预览图,请在编辑器重新保存一次

BOM

暂无BOM

附件

序号文件名称下载次数
1
上.mp4
0
2
下.mp4
0
3
外壳设计.zip
4
4
305USBAudio.7z
7
克隆工程
添加到专辑
0
0
分享
侵权投诉

评论

全部评论(1
按时间排序|按热度排序
粉丝0|获赞0
相关工程
暂无相关工程

底部导航