AnsweredAssumed Answered

DMA Data Transfer From AD7606 Using EPPI

Question asked by spflanze on Feb 11, 2013
Latest reply on Mar 21, 2013 by SachinV

I have interfaced an AD7606 A to D converter to EPPI1 on a BF547. I am receiving continuous data from it. I find the first four values in the buffer I give DMA are not changed from the default values I filled it with when I allocated it. The fifth on onward seem to have values I expect. There is another problem explained below.

 

These are the EPPI connections:

 

AD7606 Pin
AD7606 Name
BF547 Name
12

RD/SCLK

PPI1_CLK
14BUSYPPI1_FS1
15FIRST DATAPPI2_FS2

DB[0-15]PPI1_D[0-15]
6PAR/SER/BYTE/SELGround
13CSGround
9, 10CONVST A, BTMR6

 

TMR 6 outputs a clock at 1kHz. My goal is to have 8 clock pulses come out of PPI1_CLK inside every period of the AD7606's BUSY output so that 8 conversion values are clocked out. I have set the PPI1_CLK to 18kHz, so I expect these 8 pulses to be output in a little less than half the time of one TMR6 period.

 

On the oscilloscope I see the expected 1kHz clock signal is coming out of AD7606's BUSY output. But what I get out of PPI1_CLK is a continuous 18kHz square wave, rather than the needed groups of 8 pulses I thought would be set by the ADI_EPPI_CMD_SET_SAMPLES_PER_LINE command. I need help to get the desired groups of eight pulses for proper framing.

 

The DMA alternately fills two buffers in Chained Loopback Mode.

 

Here are the relevant code fragments:

 

#define N_CHAN_ADC 8

#define ADC_EPPI_HORIZONTAL_DELAY 1

 

ADI_DEV_DMA_INFO adc_dma_info;

 

ADI_DEV_CMD_VALUE_PAIR EPPI_ADC_SETUP[] = {

    { ADI_DEV_CMD_SET_DATAFLOW, (void *)FALSE }, //  Enables/disables dataflow through the device

    { ADI_DEV_CMD_SET_SYNCHRONOUS, (void *)FALSE }, // The DMA channel's FIFO might have data between descriptors.

    { ADI_DEV_CMD_OPEN_PERIPHERAL_DMA, &adc_dma_info }, // Device manager opens a DMA channel for the peripheral

    { ADI_DEV_CMD_SET_INBOUND_DMA_CHANNEL_ID, (void *)ADI_DMA_DMA13 }, // The DMA Channel for BF547's EPP1

    { ADI_DEV_CMD_GET_INBOUND_DMA_INFO, &adc_dma_info }, // Gets Inbound DMA channel Information

    { ADI_DEV_CMD_SET_DATAFLOW_METHOD, (void *)ADI_DEV_MODE_CHAINED_LOOPBACK  }, // Data will rotate around one buffer.

    { ADI_EPPI_CMD_SET_SAMPLES_PER_LINE, (void *)N_CHAN_ADC }, // Sets EPPI Samples Per Line Register

    { ADI_EPPI_CMD_SET_PORT_DIRECTION, (void *)0 }, // 0 - EPPI in Receive mode (input) 1 - EPPI in Transmit mode

    { ADI_EPPI_CMD_SET_TRANSFER_TYPE, (void *)0x3 }, // 0b11 - Non-ITU-R 656, GP Only

    { ADI_EPPI_CMD_SET_FRAME_SYNC_CONFIG, (void *)0x1 }, // 0b01 - GP 1 FS Mode

    { ADI_EPPI_CMD_ENABLE_BLANKGEN, (void *)0 }, // 0 - Disabled

    { ADI_EPPI_CMD_ENABLE_INTERNAL_CLOCK_GEN, (void *)TRUE }, // 1 - Enabled (generated internally)

    { ADI_EPPI_CMD_ENABLE_INTERNAL_FS_GEN, (void *)FALSE }, // 0 - Disabled (FS supplied externally) (From AD7606 BUSY output)

    { ADI_EPPI_CMD_SET_CLOCK_POLARITY, (void *)0x2 }, // 0b10 - Sample data on rising edge and sample/drive syncs on falling edge

    { ADI_EPPI_CMD_SET_FRAME_SYNC_POLARITY, (void *)0x0 }, // FS2 = FIRST DATA FS1 = Busy  Both start low and are active high

    { ADI_EPPI_CMD_SET_DATA_LENGTH, (void *)0x4 }, // DLEN (Data Length) 0b100 - 16 bits

    { ADI_EPPI_CMD_SET_SKIP_ENABLE, (void *)FALSE }, // For Receive mode Only, 0 - Skipping disabled

    { ADI_EPPI_CMD_SET_PACK_UNPACK_ENABLE, (void *)TRUE }, // Enable EPPI DMA packing/unpacking

    { ADI_EPPI_CMD_SET_SWAP_ENABLE, (void *)FALSE }, //  Enable Data swapping

    { ADI_EPPI_CMD_SET_SPLIT_EVEN_ODD, (void *)0 }, // Enable sub-split odd samples

    { ADI_EPPI_CMD_SET_DMA_CHANNEL_MODE, (void *)0 }, // 0 - One Channel mode

    { ADI_EPPI_CMD_SET_FIFO_REGULAR_WATERMARK, (void *)0x2 }, // 10 - FIFO 50% Full

    { ADI_EPPI_CMD_SET_FIFO_URGENT_WATERMARK, (void *)0x1}, // 01 - FIFO 75% Full

    { ADI_EPPI_CMD_SET_HORIZONTAL_DELAY, (void *)ADC_EPPI_HORIZONTAL_DELAY }, // Delay to allow data lines stabilize. Reference t10 spec at page 7 & 10 @ http://www.analog.com/static/imported-files/data_sheets/AD7606_7606-6_7606-4.pdf

    { ADI_DEV_CMD_END, NULL }

};

 

Result = adi_dev_Open(

  adi_dev_ManagerHandle, // Handle to the device manager that controls the device.

  &ADIEPPIEntryPoint, // Address of the physical driver’s entry point.

  1, // EPPI Device Index of the EPPI to open.

  NULL, // The device manager passes this value back to the client as an argument in the callback function.

  &ADC_EPPI_handle, // Pointer to an application-provided location where the device  manager stores an identifier defined by the device manager.

  ADI_DEV_DIRECTION_INBOUND, // Data direction for the device: inbound, outbound or bidirectional.

  adi_dma_ManagerHandle, // Handle to the DMA manager service that is used for this device (can be NULL if DMA is not used

  NULL, // Handle to the deferred callback service that is used for this device. If NULL, all callbacks will be live and not deferred.

  adc_cb // Address of the client’s callback function.

);

  // Make adc_Buff settings that do not change:   

  adc_Buff[0].ElementWidth = 2; // Data element width (in bytes)

  adc_Buff[0].CallbackParameter = (void *)&adc_Buff[0]; // Callback pArg Used to identify which buffer.

  adc_Buff[0].pAdditionalInfo = (void *)0; // Callback Arg value

  adc_Buff[0].ElementCount = 0; // The element count is set in adc_rate_duration_Set().

  adc_Buff[0].Data = NULL; // Null the data pointer until memory is allocated to it in adc_rate_duration_Set().

  adc_Buff[0].pNext = &adc_Buff[1]; // Next Buffer in Chain.

  adc_Buff[1].ElementWidth = adc_Buff[0].ElementWidth; // Data element width (in bytes)

  adc_Buff[1].CallbackParameter = (void *)&adc_Buff[1]; // Used to identify which buffer.

  adc_Buff[1].pAdditionalInfo = (void *)1; // Callback pArg value

  adc_Buff[1].ElementCount = 0;

  adc_Buff[1].Data = NULL;

  adc_Buff[1].pNext = NULL; // Terminate Buffer.

 

 

/** ***** adc_rate_duration_Set() *****

  Set the sample rate of the A to D converter and the duration of the DFT. The duration

  is the segment of time the DFT is done over. The sample rate and the duration determine

  how many samples are stored and input to the DFT.

 

  Inputs:

      frate: The the sample frequency, or rate. If zero the current setting is unchanged.

      duration: The length of the time segment the DFT is done over. If zero the current setting is unchanged.

 

    Global Outputs:

        DFT_N: The number A to D conversions per channel within duration.

*/

u32 adc_rate_duration_Set( double frate, double duration ){

   

    u32 Result = 0;

    bool enabled;

    int i, n;

    u32 fcclk, fsclk, fvco;

    double fclock_div;

    u16 dclock_div;

   

    enabled = ADC.enabled;

    if( Result = adc_enable( false ) ) return Result;

   

    if( frate == 0 ){ if( Result = adc_rate_Get( &frate ) ) return Result; }

    else{

         if( Result = adc_rate_Set( frate ) ) return Result;

         if( Result = adc_rate_Get( &frate ) ) return Result;

    }

    if( duration == 0 ){ if( Result = adc_duration_Get( &duration ) ) return Result; }

   

    VDK_AcquireMutex( mUsing_ADC_Data );

   

        do{

        // Enforce 100ms duration multiples for maximum rejection of 50Hz and 60Hz

        duration = floor( duration * 10 + 0.5 ) / 10; // Find the nearest 100ms multiple.

        if( duration < .1 ) duration = .1;

        DFT_N = floor( duration * frate + .5);

        adc_Buff[0].ElementCount = N_CHAN_ADC * DFT_N;

        adc_Buff[1].ElementCount = adc_Buff[0].ElementCount;

 

        // Enforce that, as nearly as possible, there be an integer number of samples in the duration

        // by adjusting the sample rate if necessary:

        frate = (adc_Buff[0].ElementCount/N_CHAN_ADC)/duration;

        if( Result = adc_rate_Set( frate ) ) break;

 

        adc_Buff[0].Data = realloc( adc_Buff[0].Data, sizeof(u16)*(adc_Buff[0].ElementCount + adc_Buff[1].ElementCount) );

        if( adc_Buff[0].Data == NULL ){ Result = ERR_IMI_MEMORY; break; }

   

        adc_Buff[1].Data = &((u16*)adc_Buff[0].Data)[adc_Buff[0].ElementCount];

 

        for( i=0; i<N_CHAN_ADC; i++ ){

            if( Result = fft_wisdom_set( fft_wisdom[i].H, DFT_N, &fft_wisdom[i] )) break;

        }

       

        if( Result = adi_dev_Read( ADC_EPPI_handle, ADI_DEV_1D, (ADI_DEV_BUFFER *)adc_Buff ) ) break;

 

        // Fill the buffer with default A to D values so it can be known for sure it is getting filled

        // with A to D data.

        for( n=0; n<2; n++){

            for( i=0; i<adc_Buff[n].ElementCount; i++ ){

                ((u16*)adc_Buff[n].Data)[i] = 0x0fff;

            }

        }

       

        // Set the EPPIx_CLK Frequency. For reliability set to a lowest possible value necessary to

        // read all 8 conversions in half the time between conversions at the conversion rate.

        // The generated clock frequency is given by following formula:

    // EPPIx_CLK = (SCLK) / (2 * (EPPIx_CLKDIV[15:0] + 1))

 

        if( Result = adi_pwr_GetFreq( &fcclk, &fsclk, &fvco ) ){ break; }

        fclock_div = floor( (fsclk/(frate*(4*(N_CHAN_ADC + ADC_EPPI_HORIZONTAL_DELAY)))) - .5 );

        if( fclock_div > 0xFFFE ){ Result = ERR_IMI_ARG_RANGE; break; }

        dclock_div = fclock_div;

        if( dclock_div > 0xFFFE || dclock_div < 1 ){ Result = ERR_IMI_ARG_RANGE; break; }

 

        if( Result = adi_dev_Control( ADC_EPPI_handle, ADI_EPPI_CMD_SET_CLOCK_DIV, (void *)dclock_div ) ){

            break;

        }

 

       

        if( enabled ){

            Result = adc_enable( true );

        }

    }while(false);

 

    VDK_ReleaseMutex( mUsing_ADC_Data );

   

    return Result;

}

 

/** ***** adc_enable() *****

    Enables or disables A to D conversion.

    The CONVSTx triggers to the A to D converter are turned off.

    The DMA is disabled.

   

    Inputs:

        enable: True if A to D is to be enabled. False if disabled. 

   

    Returns:

        Error code if there is an error, 0 otherwise.   

 

    Global Inputs:

        TMR_CONVST_ADC: Enumerator value that uniquely identifies the timer used to trigger the A to D.

        ADC_EPPI_handle: Handle used to identify the EPPI interfaced to the A to D converter.

 

    Global Outputs:

        ADC.enabled: Set true if enablement succeded, false if disablement succeeded.   

*/

 

u32 adc_enable( bool enable ){

    u32 Result;

    if( enable ){

        if( adc_Buff[0].Data && adc_Buff[1].Data ){

            if( Result = adi_tmr_GPControl( TMR_CONVST_ADC, ADI_TMR_GP_CMD_ENABLE_TIMER, (void *)TRUE ) ) return Result;

                if( Result = adi_dev_Control( ADC_EPPI_handle, ADI_DEV_CMD_SET_DATAFLOW, (void *)TRUE ) ) return Result;

        }else return ERR_IMI_ADC_RATE_DUR_REQ;

    }else{

        if( Result = adi_dev_Control( ADC_EPPI_handle, ADI_DEV_CMD_SET_DATAFLOW, (void *)FALSE ) ) return Result;

        if( Result = adi_tmr_GPControl( TMR_CONVST_ADC, ADI_TMR_GP_CMD_ENABLE_TIMER, (void *)FALSE ) ) return Result;

        }       

    }

    ADC.enabled = enable;

    return 0;

}

Outcomes