很早之前使用 Arduino Pro Micro 实现过USB耳机转接器,这次尝试使用 WCH 的 Ch554 来实现(实际上可以使用 更见偏移的 Ch552 来实现,但是因为 Ch552 有烧写次数限制,所以最终是在 Ch554上进行开发)。
无需过多了解 USB Audio的相关知识,所作的工作基本上只有:通过描述符报告自己是一个USB Audio 设备。之后 Windows 就会发送 48Khz 16位双声道的采样数据给设备(如果想了解更多,推荐去USB中文网阅读相关内容)。我们在设备响应的 OUTPUT 端点上即可收到数据。
需要特别注意的是,代码中有一个向HOST 汇报当前支持采样率的描述符。这里申明了2个采样率:22,050Hz和48000Hz。
0x0E, //Size of the descriptor, in bytes
0x24, //CS_INTERFACE Descriptor Type
0x02, //FORMAT_TYPE descriptor subtype
0x01, //FORMAT_TYPE_I
0x02, //Indicates the number of physical channels in the audio data stream.
0x02, //The number of bytes occupied by one audio subframe. Can be 1, 2, 3 or 4.
0x10, //The number of effectively used bits from the available bits in an audio subframe.
0x02, //Indicates how the sampling frequency can be programmed:
0x22,0x56,0x00, // Sampling frequency 1 in Hz for this isochronous data endpoint.
0x80,0xBB,0x00, // Sampling frequency 2 in Hz for this isochronous data endpoint.
在工作过程中,Windows会通知当前使用的采样率。
需要注意的是:
- 如果你只报告支持 48000的采样率,那么Windows就不会发送 SET_CUR
- Windows不支持所有的采样率,我这边实验只支持48000Hz的采样率,换句话,你申明支持 24000或者22050,实际上并不会使用。
具体的数据是下面这样的,可以看到这种同步传输/等时传输的数据和通常的最大区别在于不会有 ACK 信号,相当于HOST 直接丢出来不管对错。
从上面可以看到每个数据之间间隔是1ms,每笔数据 192字节。
对应在代码中会在USBAudioSpeaker.c文件中的Mass_Storage_Out函数进行处理:
void Mass_Storage_Out (void) {
PWM_CTRL |= bPWM2_OUT_EN;
for (uint8_t i = 0; i < BOT_EP_Rx_Length; i=i+4){
PWM_DATA2 = BOT_Rx_Buf[i+1];
// Delay for 20833ns
for (uint16_t j=0;j<51;j++) {
__asm__ ("nop\n");
}
}
PWM_CTRL &= (~bPWM2_OUT_EN);
//Serial0_println("Ending");
BOT_EP_Tx_ISO_Valid();
}
经过前面的工作,现在能够拿到PC输出的音频数据,接下来的问题就是如何将收到的数据通过喇叭播放出去。这个过程相当于一个 DAC (数字到模拟)的过程。这次选择的方法是:通过 PWM 进行模拟。这是使用的是CH554 芯片,它支持PWM:2 组 PWM 输出,PWM1/PWM2 为 2 路 8 位 。在下图可以看到 P1.5/P3.1/P3.0/P3.4都是可以选择的引脚。代码使用了 P3.4这个引脚。
PWM初始化代码如下,特别注意使用了1分频产生 PWM 信号,我们使用的主频为 24Mhz 5V,因此频率是 24000000/256=93750Hz
// 打开 PWM2 功能
PIN_FUNC &= ~(bPWM2_PIN_X);
// PWM 分频设置
// 1 分频,这样 PWM 频率为 Fsys/256
PWM_CK_SE=1;
上述设置之后,直接在 PWM_DATA2 寄存器中填写你要生成的高电平比例即可产生对应的 PWM 信号。对应的代码就在前面提到的void Mass_Storage_Out (void) {} 函数中。此外,使用NOP 指令制作了一个简单的延时,延时 1/48000=20833ns:
// Delay for 20833ns
for (uint16_t j=0;j<51;j++) {
__asm__ ("nop\n");
}
在编译时,还需要对项目进行如下设置:
- 选择 Ch552 Board,前面提到了Ch552和 Ch554 在代码方面是完全兼容的;
- 设置USB 为“U148B USB Ram”
- Clock Source 为 “24Mhz(internal),5V”
硬件方面非常简单,Ch554最小系统,喇叭接到对应引脚即可:
这是我设计的用于测试 Ch554 和 Ch559 最小开发板。Ch554和Ch559 最小系统外围只需要2个电容即可,两颗芯片相互独立:
完整代码:
电路图:
从上面可以看到,Ch55xduino提供的 USB 框架扩展性不错。Ch554 可以方便的通过 Ch55xduino 实现一个USB Speaker 的功能。目前美中不足的只是音频质量较差(所有看到的人都怀疑这个是一个收音机),后续会持续进行改进。