美文网首页
20151127:研究I2S中时钟的生成

20151127:研究I2S中时钟的生成

作者: skylaugher | 来源:发表于2018-11-11 13:41 被阅读0次

    一: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_0samplesIn_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
    

    相关文章

      网友评论

          本文标题:20151127:研究I2S中时钟的生成

          本文链接:https://www.haomeiwen.com/subject/yfmxfqtx.html