一:makefile中用到的模组:
USED_MODULES =
module_usb_shared :
module_usb_device :
module_usb_audio :很重要的应用程序都在这儿,包括main()函数,以及EP0。
module_xud :很重要的底层核心库,不能改动
module_spdif_tx :spdif发送功能本项目不用
module_spdif_rx :如果spdif接收连接到DAC上,则此功能也不用。而且xMOS的工程师建议不要用xMOS上的SPDIF接收功能,原因:(1)效果不如直连DAC的好;(2)节省SPDIF RX占用的两个core,省电,降功耗;
module_usb_midi :本项目不用midi功能
module_dfu :升级功能
module_i2c_shared :已分析
module_i2c_single_port :已分析
module_adat_txmodule_adat_rx :本项目不用adat功能
二:标准库的帮助文档位置
在Program Files\XMOS\xTIMEcomposer\Community_14.1.1\doc\libs\html下有各种库的帮助网页
三:Main函数的大致结构
->Main() //module_usb_audio:main.xc
->usb_audio_core //CoreUSB Audio functions
->XUD_Manager //This performs the low-level USB I/O operations
->buffer //Endpointbuffer:Buffers data from audio endpoints
->Endpoint0 //Handles all requests to the device
->decouple //Manage the data transfer between theUSB audio buffer
//and the Audio I/O driver.
->usb_audio_io
->mixer //Digitalsample mixer
//The thread mixes audio streams between the decouple()
//thread and the audio() thread
->audio //Audio driver thread
//This function drivers I2S ports and handles samples
//to/from other digital I/O threads.
->configure_clock_src(clk_audio_mclk, p_mclk_in);
->start_clock(clk_audio_mclk);
->AudioHwInit(c_config); //Thisfunction is called when the audio core starts
//after the device boots up and should initialize
//the external audio harware e.g. clocking, DAC, ADC etc
->while(1)
->ConfigAudioPortsWrapper
->ConfigAudioPorts
->AudioHwConfig //This function is called when the audiocore starts
//or changes sample rate. It should configure the
//extenal audio hardware to run at the specified
//sample rate given the supplied master clock frequency.
->deliver
->DoSampleTransfer
->outuint(c_out, samplesIn_0[i]);
->InitPorts(divide);
->while(1)
->p_lrclk <: 0x80000000,0x7FFFFFFF
->doI2SClocks
->p_bclk <: 0xF0F0F0F0 etc.
->DoSampleTransfer
->clockGen //for SPDIF RX or ADAT RX
->iAP
20151204添加:decouple和audio之间的channel关系
Decouple | Audio |
---|---|
Decouple只有一个chanend,与mixer或audio交换信息和数据 ;Decouple与buffer通过全局变量aud_to_host_flag等和全局数组outAudioBuffer、audioBuffIn交换数据和信息 | audio只有一个chanend,要么decouble交换信息和样本,要么跟mixer交换信息和样本 |
初始化各种全局变量或从全局变量中获得各种信息 | 配置i2s的时钟源 |
非常重要的中断处理函数: set_interrupt_handler(handle_audio_request, 1, c_mix_out, 0); | 配置硬件ADC/DAC/PLL等AudioHwInit |
While(1) | While(1) |
如果g_freqChange_flag==SET_SAMPLE_FREQ | 根据采样率\样本分辨率等信息计算bclk用的divide值; |
则 inuint(c_mix_out); | |
outct(c_mix_out, SET_SAMPLE_FREQ); | |
outuint(c_mix_out, sampFreq); | |
全局变量 | |
如果g_freqChange_flag==SET_SAMPLE_FORMAT_IN | 根据divide及输入方式dsdmode等信息配置bclk; |
则 全局变量 | |
如果g_freqChange_flag==SET_SAMPLE_FORMAT_OUT 则 全局变量 | 配置ADC/DAC/PLL,AudioHWConfig |
inuint(c_mix_out); | |
outct(c_mix_out, SET_STREAM_FORMAT_OUT); | |
outuint(c_mix_out, dsdMode); | |
outuint(c_mix_out, sampRes); | |
后面语句全部是各种全局变量、指针计算 | 调用command=deliver() |
返回while | 如果command==SET_SAMPLE_FREQ; |
则从c中读入curSamFreq(inuint) | |
如果command==SET_STREAM_FORMAT_OUT | |
则从c中读入dsdmode和curSamRes_DAC (inuint) |
deliver
调用DosampleTransfer(),如果是命令,则返回
如果不是命令,则
初始化I2S:InitPorts(divide)
While(1)
ADC:从I2S总线上读入样本值,存入数组samplesIn_1或samplesIn_0
DAC:将samplesOut中输出到I2S总线
调用DosampleTransder(),如果是命令则返回,否则重新while
handle_audio_request | DoSampleTransfer |
---|---|
underflowSample=inuint(c) | Outintc(c,underflowword) |
outuint(c,0) | Testct(c)检查是否是控制令牌 |
如果是,则读出控制命令intc(c),返回命令 | |
如果不是: | |
Inuint; 没有定义MIXER时有; | |
ADC输入: 从c中获取各通道样本:inuint(c),放到全局数组中 | ADC:则将样本从数组samplesIn_0或samplesIn_1输出到decouple(outuint) |
DAC输出: 从全局数组中读出样本输出到c中,outuint | DAC:则从decouple读入样本到数组samplesOut中(inuint) |
之后全是调整全局变量、指针等运算。 |
四:I2S时钟的设置路径
1、在audio()函数的开始将inport : PORT_MCLK_IN设置为audio clock block的源
configure_clock_src(clk_audio_mclk,p_mclk_in);
start_clock(clk_audio_mclk); //Puts a clock into a running state.
2、在while(1)循环中,首先计算
a) 如果当前采样率能整除512*44100,则mCLK=512*44100
b) 如果当前采样率能整除512*48000,则mCLK=512*48000
c) I2S中一个样本32bit,一个通道两个声道,因此左右声道两个样本的总比特数为numbits=64
d) divide = mCLK/(当前采样率*numbits)
e) 调用函数ConfigAudioPortsWrapper()设置I2S的位时钟、帧始终
i. 当divide=1时,直接设置位时钟=audio clock block(clk_audio_mclk)
1.configure_port_clock_output(p_bclk,clk_audio_mclk);位时钟=audio clock block
2.configure_clock_src(clk_audio_bclk,p_mclk_in); clk_audio_bclk=p_mclk_in
ii. 当divide为其它值时,
1. configure_out_port_no_ready(p_bclk,clk_audio_mclk, 0)将位时钟port配置给一个clockedoutput,注意本函数不是赋值,而是将一个port与时钟化的port进行相关
2. configure_clock_src(clk_audio_bclk,p_bclk); clk_audio_bclk=p_bclk
iii.将I2S的帧时钟与clk_audio_bclk相关
1. configure_out_port_no_ready(p_lrclk,clk_audio_bclk, 0);
iv. 将I2S的各个数据线与clk_audio_bclk相关
for(int i = 0; i < numPortsDac; i++)
{ //DAC
configure_out_port_no_ready(p_i2s_dac[i], clk_audio_bclk, 0);
}
for(int i = 0; i < numPortsAdc; i++)
{ //ADC
configure_in_port_no_ready(p_i2s_adc[i], clk_audio_bclk);
}
v. 启动clk_audio_bclk时钟,也就意味着启动了位时钟、帧时钟以及各信号线。
start_clock(clk_audio_bclk);
f) 调用函数AudioHwConfig(),根据采样率和mCLK两个数据对PLL芯片和ADC/DAC进行设置
i. 根据mCLK的值,设置MCLK_FSL=0,输出22.5792MHz;MCLK_FSL=1,24.576MHz
ii.设置ADC/DAC的寄存器;
g) 调用函数deliver(),该函数返回值获取的命令command
i. 调用函数command=DoSampleTransfer(c_out, readBuffNo, underflowWord);
1.Outuint(c_out,underflowWord);将underflowWord输出到c_out;
2.Testct(c_out);检测c_out的下一个字节是否是控制token
a)如果是,则将控制token赋给command;
b)拉低帧时钟和位时钟信号线;
c)返回command;
3.如果NUM_USB_CHAN_OUT>0,从c_out中读入NUM_USB_CHAN_OUT个字节到samplesOut[];
4.如果NUM_USB_CHAN_IN>0,将I2S_CHAN_ADC个字节samplesIn[]输出到c_out;
ii.InitPorts(divide);
1.如果divide不等于1,则输出0x80000000,b_clk开始必须为高;然后同步p_bclk
2.清除帧时钟、数据线使用的缓冲buffer
3.如果divide==1,预充填0到数据线和帧时钟
a)调用doI2SClock(divide),根据divide,输出不同的值到p_bclk生成位时钟
4.如果divide!=1,预充填0到数据线和帧时钟
a)调用doI2SClock(divide),根据divide,输出不同的值到p_bclk生成位时钟
iii.while(1)
1.如果是ADC,那么从I2S中读数据到数组samplesIn_1[]或samplesIn_2[],输出帧时钟0x800000000 (LEFT channel)
2.如果是DAC,将数组samplesOut[]输出到数据线;然后调用doI2SClock(divide)
3.如果是ADC,那么从I2S中读数据到数组samplesIn_1[]或samplesIn_2[],输出帧时钟0x7FFFFFFF (RIGHT channel)
4.如果是DAC,将数组samplesOut[]输出到数据线;然后调用doI2SClock(divide)
h)如果command==SET_SAMPLE_FREQ,则从c_mix_out中读入当前采样频率;
i)如果command==SET_STREAM_FORMAT_OUT,则从c_mix_out中读入dsdmode和curSamRes_DAC