编程指导 - USB Audio
以下的章节提供如何在USB音频软件平台上编写代码的指导,其中包括创建和运行程序的指示,以及如何创建你自己的客制化USB音频应用。
xTIME composer Studio 的编辑器未必支持简体中文,因此以下代码的指导部分仅作参考,具体操作请查看英文文档。并且xTIME composer Studio编辑器一般仅适用于xu208, xu216芯片及以下版本芯片的代码编译,对于xu316芯片应使用XTC编译器。
5.1 开始
5.1.1 创建和运行
要编译,请在项目资源管理器中选择相关项目(例如app_usb_aud_l1),然后单击编译图标。
要安装软件,打开xTIME composer Studio,按照以下步骤:
-
选择“文件→导入”。
-
选择“常规”→“现有项目进入工作区”,然后单击“下一步”。
-
单击“浏览”选择存档文件并选择文件固件ZIP文件。
-
请确保要导入的项目已在“项目”列表中进行了勾选。导入所有组件和您感兴趣的应用程序。
-
单击“完成”。
要编译,请在项目资源管理器中选择相关的项目(例如app_usb_aud_l1),然后单击编译图标。
从命令行中,您可以执行以下步骤:
-
要安装,请解压缩软件包。
-
要编译,请更改相关的应用程序目录(例如
app_usb_aud_l1)并执行以下命令:xmake all
该项目的主创建文件在app目录(例如app_usb_aud_l1)中。此文件指定了编译选项和已使用的模块。Makefile使用了放置于module_xmos_common中的基本结构。该系统包括来自相关模块的源文件,并在module_xmos_common中进行了记录。
5.1.2 将应用程序安装到flash上
要升级固件,你必须首先:
-
将USB音频板插入您的电脑。
-
将xTAG-2连接到USB音频板上,并将xTAG-2插入到您的PC或Mac电脑上。
要从 xTIME composer Studio升级闪存,请执行以下步骤:
-
启动 xTIME composer Studio,并打开一个工作区。
-
选择“文件 →导入→C/XC→C/XC可执行文件”。
-
单击“浏览”并选择新的固件(XE)文件。
-
依次单击“下一步”和“完成”。
-
此时将显示一个“调试配置”窗口。单击“关闭”。
-
选择“运行→Flash配置”。
-
双击xCORE应用程序以创建一个新的Flash配置。
-
浏览项目方框和C/XC应用程序方框中的XE文件。
-
请确保xTAG-2设备出现在目标列表中。
-
单击“Flash”。
从命令行开始:
-
打开XMOS命令行工具(桌面工具提示符),并执行以下命令:
xflash < binary >. xe
5.2 程序结构
5.2.1 应用和模块
代码被分成几个模块目录。这些模块的代码可以通过添加到应用程序Makefile中定义的USED_MODULES中的模块名称来包含:
| name | module |
|---|---|
| module_xud | Low level USB device library |
| module_usb_shared | Low level USB device library |
| module_usb_device | Common code for USB device applications |
| module_usb_audio | Common code for USB audio applications |
| module_spdif_tx | S/PDIF transmit code |
| module_spdif_rx | S/PDIF receive code |
| module_adat_rx | ADAT receive code |
| module_usb_midi | MIDI I/O code |
| module_dfu | Device Firmware Upgrade code |
图29:USB音频所使用的模块
这里提供了一些应用的库,他们包括一些可以被编译执行的Makefiles文件:
| name | module |
|---|---|
| app_usb_aud_l1 | USB Audio 2.0 Reference Design application |
| app_usb_aud_l2 | USB Audio 2.0 Multichannel Reference Design application |
| app_usb_aud_skc_u16 | U16 SliceKit with Audio Slice application |
| app_usb_aud_xk_u8_2c | Multi-function Audio board application |
| app_usb_aud_skc_su1 | DJ kit application |
图30:USB音频参考应用程序
5.3 编译配置
由于框架的灵活性,因此有许多不同的编译选项。例如,输入和输出通道计数,音频类版本,接口类型等。“编译配置”是一组编译选项,它们组合起来生成一个特定的特性集。编译配置在应用程序制作文件中列出及其相关的选项,它们可以在xTIME Composer GUI中编译,也可以通过如下命令来编译:
xmake CONFIG =< config name >
当使用“buil dall”(命令行上的xmake all)编译引用设计应用程序时,所有配置都会自动编译。每个应用程序都使用命名方案将特性集链接到编译配置/二进制。 使用了相同的基本方案的不同变体。下一节将描述此方案。
5.4 经过验证的编译配置
不可能对所有的编译配置排列都进行详尽的测试。因此,XMOS测试编译配置的子集以获得正确的测试,这些基于通常的设备配置。
应用程序中存在编译配置表示它是已验证的编译配置,应该被认为受支持。
5.5配置命名方案
本节描述了为每个生成配置生成的编译配置(以及相应二进制文件)的命名方案。每个相关的编译选项在配置名称中分配一个位置,用一个字符表示选项值(通常用“x”表示“关闭”或“禁用”)。例如,图31列出了单片L系列参考设计的编译选项。
| **Build Option ** | Name Options | Denoted by |
|---|---|---|
| Audio Class Version | 1 or 2 | 1 or 2 |
| Audio Input | on or off | i or x |
| Audio Output | on or off | o or x |
| MIDI | on or off | m or x |
| S/PDIF Output | on or off | s or x |
图31 单片L系列编译选项
5.6 一个USB音频应用程序
本节提供了单片USB音频参考设计(l系列)示例的介绍,可以在app_usb_aud_l1目录中找到。
在每个应用程序目录中,src目录被排列为两个文件夹:
-
一个核心(
core)目录,其中包含必须对USB音频框架 -
一个扩展(
extensions)目录,其中包括对框架的扩展,如编解码器配置等
每个应用程序的核心文件夹包含:
-
一个
.xn文件来描述硬件平台的应用程序将运行在 -
一个自定义的头文件:
customdefines.h,它用来配置框架
5.6.1 用户自定义
customdefines.h 文件已经包含为了特定应用程序而定制的USB音频框架所需的所有#defines定义。通常,这些重载的的默认值在在module_usb_audio下的devicedefifines.h中。
首先,这里提供一些定义来确定整体使能。下段代码的功能为,启用和关闭S/PDIF输出和DFU端口。请注意,ifndef用于检查这个命名选项还没有在编译规则文件中被定义(防止重复定义)。
/* Enable / Disable MIDI - Default is MIDI off */
# ifndef MIDI
# define MIDI (0)
# endif
/* Enable / Disable SPDIF - Default is SPDIF on */
# ifndef SPDIF_TX
# define SPDIF_TX (1)
# endif
接下来,下端代码定义了应用程序的音频属性。此应用程序具 有立体声输入和立体声输出,且具有S/PDIF输出,可重复模拟通道1和2(注释通道从0开始建立索引):
/* Number of USB streaming channels - Default is 2 in 2 out */
# ifndef NUM_USB_CHAN_IN
# define NUM_USB_CHAN_IN (2) /* Device to Host */
# endif
# ifndef NUM_USB_CHAN_OUT
# define NUM_USB_CHAN_OUT (2) /* Host to Device */
# endif
/* Number of IS2 chans to DAC ..*/
# ifndef I2S_CHANS_DAC
# define I2S_CHANS_DAC (2)
# endif
/* Number of I2S chans from ADC */
# ifndef I2S_CHANS_ADC
# define I2S_CHANS_ADC (2)
# endif
/* Index of SPDIF TX channel ( duplicated DAC channels 1/2) */
# define SPDIF_TX_INDEX (0)
然后,下段代码为硬件上的主时钟和设备的最大采样率设置了一些定义。
/* Master clock defines ( in Hz ) */
# define MCLK_441 (256*44100) /* 44.1 , 88.2 etc */
# define MCLK_48 (512*48000) /* 48 , 96 etc */
/* Maximum frequency device runs at */
# ifndef MAX_FREQ
# define MAX_FREQ (192000)
# endif
最后,还需要设置一些一般的USB识别定义。这些是为XMOS参考设计而设置的,但根据制造商而有所不同:
# define VENDOR_ID (0 x20B1 ) /* XMOS VID */
# define PID_AUDIO_2 (0 x0002 ) /* L1 USB Audio Reference Design PID */
# define PID_AUDIO_1 (0 x0003 ) /* L1 USB Audio Reference Design PID */
所有完整的定义描述可以在customdefines.h中了解。请参见7.1
5.6.2 配置功能
除了自定义定义文件之外,该应用程序还需要提供特定于该应用程序的用户功能的实现。
对于app_usb_aud_l1,其实现可以在audiohw.xc中找到。
首先,需要使用代码来初始化外部音频硬件。在L1参考设计板上的 CODEC(多媒体数字信号编解码器)里,没有需要的额外操作,所以功能模块是空的:
void AudioHwInit ( chanend ? c_codec )
{
return ;
}
每改变采样率,都需要调用函数AudioHwConfifig()。在L1参考设计板上的CODEC编解码器中,编解码器必须重置,并设置来自电路板上的两个振荡器的相关时钟输入。
编解码器复位线和时钟选择线都连接到32位端口32A。这可以通过port32A_peek和port32A_out函数来访问:
# define PORT32A_PEEK(X) {asm ("peek %0, res[%1]":" =r"(X):"r"(XS1_PORT_32A));}
# define PORT32A_OUT(X) {asm ("out res[%0], %1"::"r"(XS1_PORT_32A),"r"(X));}
/* Configures the CODEC for the required sample frequency .
* CODEC reset and frequency select are connected to port 32 A
*
* Port 32 A is shared with other functionality ( LEDs etc ) so we
* access via inline assembly . We also take care to retain the
* state of the other bits .
*/
void AudioHwConfig (unsigned samFreq, unsigned mClk, chanend ? c_codec, unsigned dsdMode, unsigned samRes_DAC, unsigned samRes_ADC)
{
timer t;
unsigned time ;
unsigned tmp ;
/* Put codec in reset and set master clock select appropriately */
/* Read current port output */
PORT32A_PEEK ( tmp );
/* Put CODEC reset line low */
tmp &= (~ P32A_COD_RST );
if (( samFreq % 22050) == 0)
{
/* Frequency select low for 441000 etc */
tmp &= (~ P32A_CLK_SEL );
}
else // if (( samFreq % 24000) == 0)
{
/* Frequency select high for 48000 etc */
tmp |= P32A_CLK_SEL ;
}
PORT32A_OUT ( tmp ) ;
/* Hold in reset for 2 ms */
t :> time ;
time += 200000;
t when timerafter ( time ) : > int _ ;
/* Codec out of reset */
PORT32A_PEEK ( tmp );
tmp |= P32A_COD_RST ;
PORT32A_OUT ( tmp ) ;
}
最后,该应用程序具有音频流启动/停止功能,可启用/禁用电路板上的LED(也可在端口32A上):
# include <xs1.h >
# include "port32A.h"
/* Functions that handle functions that must occur on stream
* start / stop e .g. DAC mute /un - mute . These need implementing
* for a specific design .
*
* Implementations for the L1 USB Audio Reference Design
*/
/* Any actions required for stream start e .g. DAC un - mute - run every
* stream start .
*
* For L1 USB Audio Reference Design we illuminate LED B ( connected
* to port 32 A )
*
* Since this port is shared with other functionality inline assembly
* is used to access the port resource .
*/
void UserAudioStreamStart(void)
{
int x;
/* Peek at current port value using port 32 A resource ID */
asm ("peek %0 , res[%1]":" =r"(x):"r"(XS1_PORT_32A));
x |= P32A_LED_B ;
/* Output to port */
asm ("out res [%0], %1"::"r"( XS1_PORT_32A ),"r"(x));
}
/* Any actions required on stream stop e.g . DAC mute - run every
* stream stop
* For L1 USB Audio Reference Design we extinguish LED B ( connected
* to port 32 A )
*/
void UserAudioStreamStop(void)
{
int x;
asm ("peek %0 , res[%1]":" =r"(x):"r"(XS1_PORT_32A));
x &= (~ P32A_LED_B ) ;
asm ("out res [%0], %1"::"r"( XS1_PORT_32A ),"r"(x));
}
5.6.3 主程序
因此,main()函数在所有应用程序之间共享,这是框架的一部分。它位于sc_usb_audio中,包含:·
-
对框架中使用的所有端口的声明。这些取决于应用程序运行的PCB。·
-
一个
main()函数,它声明了一些通道,然后有一个并行运行所需内核的par语句。
该框架支持具有多个tile的设备,因此它使用on tile[n]:语法。
第一个核心运行的是XUD库:
# if (AUDIO_CLASS ==2)
XUD_Manager ( c_xud_out, ENDPOINT_COUNT_OUT, c_xud_in, ENDPOINT_COUNT_IN , c_sof, epTypeTableOut, epTypeTableIn, p_usb_rst, clk, 1, XUD_SPEED_HS , XUD_PWR_CFG);
# else
XUD_Manager (c_xud_out, ENDPOINT_COUNT_OUT, c_xud_in, ENDPOINT_COUNT_IN, c_sof, epTypeTableOut, epTypeTableIn, p_usb_rst, clk, 1, XUD_SPEED_FS, XUD_PWR_CFG);
# endif
连接到此驱动程序的通道阵列在7.3中描述了它们的组成。连接到XUD驱动程序的通道被输入缓冲核和解耦核:
buffer ( c_xud_out [ ENDPOINT_NUMBER_OUT_AUDIO ], /* Audio Out */
c_xud_in [ ENDPOINT_NUMBER_IN_AUDIO ], /* Audio In */
# if ( NUM_USB_CHAN_IN == 0) || defined ( UAC_FORCE_FEEDBACK_EP )
c_xud_in [ ENDPOINT_NUMBER_IN_FEEDBACK ], /* Audio FB */
# endif
# ifdef MIDI
c_xud_out [ ENDPOINT_NUMBER_OUT_MIDI ], /* MIDI Out */ // 2
c_xud_in [ ENDPOINT_NUMBER_IN_MIDI ], /* MIDI In */ // 4
c_midi ,
# endif
# ifdef IAP
c_xud_out [ ENDPOINT_NUMBER_OUT_IAP ], /* iAP Out */
c_xud_in [ ENDPOINT_NUMBER_IN_IAP ], /* iAP In */
# ifdef IAP_INT_EP
c_xud_in [ ENDPOINT_NUMBER_IN_IAP_INT ], /* iAP Interrupt In */
# endif
c_iap ,
# ifdef IAP_EA_NATIVE_TRANS
c_xud_out [ ENDPOINT_NUMBER_OUT_IAP_EA_NATIVE_TRANS ],
c_xud_in [ ENDPOINT_NUMBER_IN_IAP_EA_NATIVE_TRANS ],
c_EANativeTransport_ctrl ,
c_ea_data ,
# endif
# endif
# if defined ( SPDIF_RX ) || defined ( ADAT_RX )
/* Audio Interrupt - only used for interrupts on external clock change*/
c_xud_in [ ENDPOINT_NUMBER_IN_INTERRUPT ],
c_clk_int ,
# endif
c_sof , c_aud_ctl , p_for_mclk_count
# ifdef HID_CONTROLS
, c_xud_in [ ENDPOINT_NUMBER_IN_HID ]
# endif
# ifdef CHAN_BUFF_CTRL
, c_buff_ctrl
# endif
);
{
thread_speed();
decouple(c_mix_out
# ifdef CHAN_BUFF_CTRL
, c_buff_ctrl
# endif
);
}
然后,这些设备连接到控制I2S输出和S/PDIF输出的音频驱动程序(如果启用)。如果启用了S/PDIF输出,该组件将生成两个核,而不是一个核。
{
thread_speed () ;
# ifdef MIXER
# define AUDIO_CHANNEL c_mix_out
# else
# define AUDIO_CHANNEL c_aud_in
# endif
audio ( AUDIO_CHANNEL ,
# if defined ( SPDIF_TX ) && ( SPDIF_TX_TILE != AUDIO_IO_TILE )
c_spdif_tx ,
# endif
# if defined ( SPDIF_RX ) || defined ( ADAT_RX )
c_dig_rx ,
# endif
c_aud_cfg , c_adc
# if XUD_TILE != 0
, dfuInterface
# endif
# if ( NUM_PDM_MICS > 0)
, c_pdm_pcm
# endif
# ifdef RUN_DSP_TASK
, i_dsp
# endif
);
}
# if defined ( SPDIF_RX ) || defined ( ADAT_RX )
{
thread_speed () ;
clockGen ( c_spdif_rx , c_adat_rx , p_pll_clk , c_dig_rx , c_clk_ctl, c_clk_int );
}
# endif
最后,如果启用了MIDI,那么您需要一个核心来驱动MIDI的输入和输出。MIDI核心还可以选择处理与苹果设备的身份验证。由于许可问题, 此代码仅适用于苹果MFI的许可方。详情请联系XMOS公司。
on tile [ MIDI_TILE ]:
{
thread_speed () ;
usb_midi ( p_midi_rx , p_midi_tx , clk_midi , c_midi , 0 , null , null, null, null);
}
5.7 添加自定义代码
USB音频解决方案的灵活性意味着,您可以修改参考应用程序,以更改功能集或添加额外的功能。除了XUD库之外,软件的任何部分都可以进行修改。
参考设计已经在不同的主机操作系统类型中进行了验证。但是,对代码的修改可能会使此验证的结果无效,因此强烈鼓励您完全重新测试生成的软件。
一般步骤为:
-
将您希望基于代码的eclipse项目或应用程序目录(例如
app_usb_aud_l1或app_usb_aud_l2复制到一个具有不同名称的单独目录中。 -
复制任何您希望更改的模块(大多数情况下,您可能并不想这样做)。更新新应用程序的Makefile以使用这些新的自定义模块。
-
对代码进行适当的更改,重新编译和刷新设备以进行测试。
一旦你复制了一份,你就需要:
-
为电路板提供一个
.xn文件(适当地更新Makefile中的目标变量)。 -
使用要设置的特定定义更新
depive_depens.h。 -
更新
main.xc。 -
在其他您需要的文件中添加任何自定义代码。
下面的部分展示了一些示例更改示例,并概述了如何更改代码。
5.7.1 示例:更改输出格式
您可能希望自定义数字输出格式,例如对于一个需要编解码器的编解码器。要做到这一点,您需要改变音频控制系统中的主音频驱动程序循环。在更改后,您需要重新测试该功能。XMOS定时分析器可以帮助保证您的更改不会破坏此核心的定时要求。
5.7.2 示例:将DSP添加到输出流中
要添加一些DSP需要额外的计算核心,因此需要删除一些现有的功能(例如S/PDIF)。按照以下步骤更新代码:
-
使用7.1中的定义来删除一些功能,以释放一个核心。
-
添加另一个核心来执行DSP。这个核心可能有三个XC通道:一个通道从解耦器核心接收样本,另一个通道输出到音频驱动程序——这样核心在到达音频驱动程序时“拦截”音频数据;第三个通道可以接收来自端点0的控制命令。
-
在这个核心上实现DSP。这需要是同步的(即,对于从解耦器接收到的每个样本,一个样本需要被输出到音频驱动程序)。