高级教程
虽然可以通过仅设置定义(参见配置和选项)来使用lib_xua编程USB音频设备,但某些开发人员可能希望使用lib_xua提供的构建模块从头开始编写USB音频设备。
这可能是出于多种原因,例如添加复杂的DSP,与其他功能合并等。本节描述了这些构建模块及其用途。
强烈建议此时查阅应用笔记AN00246。
核心硬件资源
用户必须声明和初始化相关硬件资源(全局),并将它们传递给lib_xua的相关函数。至少需要以下资源:
- 一个用于音频主时钟输入的1位端口
- 一个时钟块,将从主时钟输入端口进行时钟同步
在使用默认的异步操作模式时,还需要一个额外的端口:
- 一个用于内部反馈计算的n位端口(通常使用空闲的未使用端口,例如XS1_PORT_16B)
这些资源的示例声明可能如下所示:
in port p_mclk_in = PORT_MCLK_IN;
in port p_for_mclk_count = PORT_MCLK_COUNT; /* 用于计数主时钟脉冲的额外端口 */
clock clk_audio_mclk = on tile[0] : XS1_CLKBLK_5; /* 主时钟 */
PORT_MCLK_IN和PORT_MCLK_COUNT的定义源自项目里的XN文件。XUA_AudioHub()函数需要音频主时钟输入来同步物理音频I/O。较不明显的是,当以异步模式运行时,XUA_Buffer()任务也具有相同的要求 - 需要音频主时钟用于USB反馈系统和数据包大小。
由于上述原因,如果XUD_AudioHub()和XUA_Buffer()核心必须驻留在不同的tile上,则必须为每个提供单独的主时钟输入端口,例如:
/* 用于音频IO tile的主时钟 */
in port p_mclk_in = PORT_MCLK_IN;
/* 用于USB反馈的资源 */
in port p_mclk_in_usb = PORT_MCLK_IN_USB; /* USB tile的额外主时钟输入 */
虽然本节中描述的硬件资源满足了lib_xua操作(或构建)的基本要求,但项目通常还需要一些其他的音频I/O,例如I2S或S/PDIF。这些应根据需要传递给各个核心 - 参见 API。
运行核心组件
核心组件的最基本形式如下所示:
par
{
/* lib_xua的Endpoint 0核心 */
XUA_Endpoint0(c_ep_out[0], c_ep_in[0], c_aud_ctl, ...);
/* Buffering核心 - 处理与EP之间的音频数据,并给予/获取与音频I/O核心之间的数据 */
/* 注意,这会生成两个核心 */
XUA_Buffer(c_ep_out[1], c_ep_in[1], c_sof, c_aud_ctl, p_for_mclk_count, c_aud);
/* AudioHub / IO核心负责大部分音频I/O,例如I2S(也作为所有音频的中心) */
XUA_AudioHub(c_aud, ...);
}
XUA_Buffer()期望其p_for_mclk_count参数在接收之前从音频主时钟进行同步。以下代码满 足此要求:
{
/* 将主时钟时钟块连接到时钟块引脚 */
/* 从mclk引脚时钟时钟块 */
set_clock_src(clk_audio_mclk_usb, p_mclk_in_usb);
/* 从时钟块时钟"count"端口 */
set_port_clock(p_for_mclk_count, clk_audio_mclk_usb);
/* 启动时钟 */
start_clock(clk_audio_mclk_usb);
XUA_Buffer(c_ep_out[1], c_ep_in[1], c_sof, c_aud_ctl, p_for_mclk_count, c_aud);
}
将此配置保持在XUA_Buffer()之外意味着不排除与其他组件共享p_mclk_in_usb端口的可能性。对于USB连接,还必须调用XUD_Main()(来自lib_xud):
/* 低级USB设备层核心 */
on tile[1] : XUD_Main(c_ep_out, 2, c_ep_in, 2, c_sof, epTypeTableOut, epTypeTableIn, null, null, -1, XUD_SPEED_HS, XUD_PWR_SELF);
此外,还必须声明所需的通信通道:
/* 用于lib_xud的通道数组 */
chan c_ep_out[2];
chan c_ep_in[2];
/* 用于将SOF通知从XUD传递给Buffering核心的通道 */
chan c_sof;
/* 用于在EP0和设备的其余部分之间传递控制消息的通道(通过缓冲核心) */
chan c_aud_ctl;
本节提供了足够的信息来实现USB音频设备的框架程序。运行时,xCORE设备将在总线上呈现为USB音频类设备。由于尚未实例化任何物理音频接口,音频流传输将受到影响。
I2S/TDM
I2S/TDM通常是大多数产品的基础,并内置在XUA_AudioHub()核心中。
为了启用I2S,必须声明一个数据线(每个方向一个)的端口数组:
/* 端口声明。注意,这些定义来自XN文件 */
buffered out port :32 p_i2s_dac[] = {PORT_I2S_DAC0}; /* I2S数据线(s) */
buffered in port :32 p_i2s_adc[] = {PORT_I2S_ADC0}; /* I2S数据线(s) */
还需要样本时钟和位时钟的端口:
buffered out port :32 p_lrclk = PORT_I2S_LRCLK; /* I2S位时钟 */
buffered out port :32 p_bclk = PORT_I2S_BCLK; /* I2S L/R时钟 */
所有这些端口都必须是1位端口,32位带缓冲的。根据xCORE是总线从属/主控,端口必须分别声明为输入/输出。
然后,必须适当地将这些端口传递给XUA_AudioHub()任务。
I2S功能还需要两个时钟块,一个用于位时钟和样本时钟,例如:
/* 时钟块声明 */
clock clk_audio_bclk = on tile [0]: XS1_CLKBLK_4; /* 位时钟 */
clock clk_audio_mclk = on tile [0]: XS1_CLKBLK_5; /* 主时钟 */
这些硬件资源必须传递给调用XUA_AudioHub()的部分:
/* AudioHub/IO 核心负责大部分音频I/O,例如I2S(也作为所有音频的中心) */
on tile [0]: XUA_AudioHub(c_aud, clk_audio_mclk, clk_audio_bclk, p_mclk_in, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc);
混音器
由于混音器没有I/O,因此实例化非常简单。在通信方面,混音器核心被插入在AudioHub和Buffering核心之间。
它需要三个通道端口作为参数,一个用于与Buffering核心之间的音频收发,一个用于与AudioHub核心之间的音频收发,另一个用于来自Endpoint0核心的控制请求。
混音器任务将根据当前采样频率(通过从缓冲任务的数据通道传递)自动处理混音计数的变化。
下面是调用混音器任务的示例(某些参数列表已经简略):
chan c_aud_0, c_aud_1, c_mix_ctl;
par
{
XUA_Buffer(..., c_aud_0, ...);
mixer(c_aud0, c_aud_1, c_mix_ctl);
XUA_AudioHub(c_aud_1, ...);
XUA_Endpoint0(..., c_mix_ctl, ...);
}