Cambiar la velocidad del reloj ADC no cambia la velocidad de muestreo [XMega]

0

Estoy tratando de medir qué tan rápido llegan mis muestras de ADC en un XMega128A4U. He configurado un temporizador y el ADC, y cada vez que tengo un resultado, dejo que el DMA extraiga el registro CNT del temporizador y el registro de resultados del ADC. Después de muestrear 1024 muestras, el DMA señala que finaliza y imprimo el resultado en el USB.

Mi problema es que aunque cambie el prescaler ADC, el delta entre cada muestra es el mismo.

Reloj del sistema 32MHz RC interno.

ADC Prescaler 16 (32MHz / 16 = 2MHz)

TC0: 930 - CH0=1410
TC0: 931 - CH0=1410
TC0: 932 - CH0=1378
TC0: 934 - CH0=1366
TC0: 936 - CH0=1352
TC0: 937 - CH0=1342
TC0: 939 - CH0=1330
TC0: 940 - CH0=1319
TC0: 942 - CH0=1319
TC0: 944 - CH0=1297
TC0: 945 - CH0=1290

Prescaler 512 de ADC (32Mhz / 512 = 62.5kHz)

TC0: 933 - CH0=997
TC0: 934 - CH0=994
TC0: 935 - CH0=998
TC0: 936 - CH0=986
TC0: 936 - CH0=1002
TC0: 937 - CH0=987
TC0: 938 - CH0=985
TC0: 939 - CH0=995
TC0: 940 - CH0=987
TC0: 941 - CH0=987

El temporizador se ejecuta a 4MHz (32Mhz / 8), por lo que el tiempo entre cada tick es de 250 nS. Esto simplemente no parece correcto! ¿Que me estoy perdiendo aqui? ¿Hay algo más que deba configurar que no sea el prescaler?

Código completo:

#define F_CPU 32000000UL
#include <asf.h>
#include <util\delay.h>

#define OVERSAMPLING_FACTOR 10
#define DMA_BUFFER_SIZE     (1 << OVERSAMPLING_FACTOR)
#define DMA_ADC_CH0         0
#define DMA_ADC_CH1         1
#define DMA_ADC_CH2         2

static uint16_t dma_buffer[DMA_BUFFER_SIZE];
static uint16_t tc_buffer[DMA_BUFFER_SIZE];

volatile bool dma_is_full = false;

void dma_init(void);
void adc_init(void);
void timer_init(void);

static void dma_adc_res_done(enum dma_channel_status status)
{
    if (status == DMA_CH_TRANSFER_COMPLETED)
    {
        dma_is_full = true;
    }   
}


int main (void)
{
    irq_initialize_vectors();
    cpu_irq_enable();

    sysclk_init();
    board_init();
    dma_init();
    adc_init();
    timer_init();

    stdio_usb_init();
    stdio_usb_enable();

    // Power up target board for current measurement
    ioport_set_pin_high(CURRENT_2);

    // Start ADC and timer
    tc_enable(&TCC0);
    ADCA.CTRLA |= (ADC_ENABLE_bm << ADC_ENABLE_bp);
    while (true) 
    {
        sleepmgr_enter_sleep();

        // Sleeping while DMA not finished
        if(dma_is_full == true)
        {
            _delay_ms(500); // Wait so USB has time to connect before printing
            if (udi_cdc_is_tx_ready())
            {
                char line_buffer[20];
                uint8_t line_length;

                for (uint16_t i = 0; i < DMA_BUFFER_SIZE; i++)
                {
                    line_length = sprintf(  line_buffer,
                                            "TC0: %u - CH0=%u\r\n",
                                            tc_buffer[i],
                                            dma_buffer[i]
                                            );

                    udi_cdc_write_buf(line_buffer, line_length);
                }
            }
            dma_is_full = false;
        }
        // Enabling channels again to start next transfer
//          dma_channel_enable(DMA_ADC_CH0);
//          dma_channel_enable(DMA_ADC_CH2);

    }

}


void dma_init(void)
{
    // =================================================
    // Config DMA Channel 0 for ADC samples
    // =================================================    

    struct dma_channel_config dmach_adc_res1_conf;
    memset(&dmach_adc_res1_conf, 0, sizeof(dmach_adc_res1_conf));

    dma_channel_set_burst_length(&dmach_adc_res1_conf, DMA_CH_BURSTLEN_2BYTE_gc);   
    dma_channel_set_transfer_count(&dmach_adc_res1_conf, DMA_BUFFER_SIZE*2);

    dma_channel_set_src_reload_mode(&dmach_adc_res1_conf, DMA_CH_SRCRELOAD_BURST_gc);
    dma_channel_set_dest_reload_mode(&dmach_adc_res1_conf, DMA_CH_DESTRELOAD_BLOCK_gc);

    dma_channel_set_src_dir_mode(&dmach_adc_res1_conf, DMA_CH_SRCDIR_INC_gc);
    dma_channel_set_dest_dir_mode(&dmach_adc_res1_conf, DMA_CH_DESTDIR_INC_gc);

    dma_channel_set_single_shot(&dmach_adc_res1_conf);
    dma_channel_set_source_address(&dmach_adc_res1_conf, (uint16_t)(uintptr_t)&ADCA.CH0RES);
    dma_channel_set_destination_address(&dmach_adc_res1_conf, (uint16_t)(uintptr_t)dma_buffer);

    // Set DMA trigger source to CH2 complete interrupt flag
    dma_channel_set_trigger_source(&dmach_adc_res1_conf, DMA_CH_TRIGSRC_ADCA_CH0_gc);   

    // =================================================
    // Config DMA Channel 2 to pull timestamps from TC0
    // =================================================
    struct dma_channel_config dmach_tc_conf;
    memset(&dmach_tc_conf, 0, sizeof(dmach_tc_conf));

    dma_channel_set_burst_length(&dmach_tc_conf, DMA_CH_BURSTLEN_2BYTE_gc);
    dma_channel_set_transfer_count(&dmach_tc_conf, DMA_BUFFER_SIZE*2);

    dma_channel_set_src_reload_mode(&dmach_tc_conf, DMA_CH_SRCRELOAD_BURST_gc);
    dma_channel_set_dest_reload_mode(&dmach_tc_conf, DMA_CH_DESTRELOAD_BLOCK_gc);

    dma_channel_set_src_dir_mode(&dmach_tc_conf, DMA_CH_SRCDIR_INC_gc);
    dma_channel_set_dest_dir_mode(&dmach_tc_conf, DMA_CH_DESTDIR_INC_gc);

    dma_channel_set_single_shot(&dmach_adc_res1_conf);
    dma_channel_set_source_address(&dmach_tc_conf, (uint16_t)(uintptr_t)&TCC0.CNT);
    dma_channel_set_destination_address(&dmach_tc_conf, (uint16_t)(uintptr_t)tc_buffer);
    dma_channel_set_trigger_source(&dmach_tc_conf, DMA_CH_TRIGSRC_ADCA_CH0_gc);


    // Enable DMA and configure channels
    dma_enable();
    dma_set_callback(DMA_ADC_CH0, dma_adc_res_done);
    dma_channel_set_interrupt_level(&dmach_adc_res1_conf, DMA_INT_LVL_MED);
    dma_channel_write_config(DMA_ADC_CH0, &dmach_adc_res1_conf);

    dma_channel_set_interrupt_level(&dmach_tc_conf, DMA_INT_LVL_MED);
    dma_channel_write_config(DMA_ADC_CH2, &dmach_tc_conf);

    dma_channel_enable(DMA_ADC_CH0);
    dma_channel_enable(DMA_ADC_CH2);


}


void adc_init(void)
{
    PR.PRPA &= ~PR_ADC_bm;      // Power up
    ADCA.CTRLA = ADC_FLUSH_bm;  // Cancel pending conversions (in case it was already powered)
    ADCA.CTRLA = 0;             // Disable ADC
    ADCA.EVCTRL = 0;                

    /*  Set up ADCA

        Full speed, no limit (2 MSPMS)
        12 bit resolution
        Unsigned conversion
        ADC Reference AREFB (2.5V)
        Freerunning mode
    */
    ADCA.CTRLB  |= ADC_CURRLIMIT_NO_gc 
                | ADC_RESOLUTION_12BIT_gc 
                | ADC_SIGN_OFF 
                | ADC_FREERUN_bm
                ;   

    ADCA.CTRLB &= ~ADC_CONMODE_bm;  
    ADCA.REFCTRL |= ADC_REF_AREFB;
    ADCA.PRESCALER = ADC_PRESCALER_DIV512_gc; // 32MHz/16 = 2MHz = ADC max clock speed


    /*  Set up CH0 on ADCA

        Single-ended measurement on VOUT3
        1x gain
        Interrupt flag set on conversion complete (used by DMA)
        Interrupt OFF (DMA polls interrupt flag, doesn't need ADC ISR)
    */
    //! Note: sys_clk = 32000000
    ADCA.CH0.CTRL |= ADC_CH_INPUTMODE_SINGLEENDED_gc | ADC_CH_GAIN_1X_gc;
    ADCA.CH0.MUXCTRL = VOUT_3;
    ADCA.CH0.INTFLAGS = 0xFF;       // Clear
    ADCA.CH0.INTCTRL |= ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_OFF_gc;  
}

void timer_init(void)
{
    tc_enable(&TCC0);
    tc_set_wgm(&TCC0, TC_WG_NORMAL);
    tc_write_clock_source(&TCC0, TC_CLKSEL_DIV8_gc); // 4MHz
    tc_disable(&TCC0);
}
    
pregunta chwi

1 respuesta

1

Hubo un error ... Estaba configurando el modo de disparo único para el ADC DMA dos veces en lugar de para el canal TC DMA.

dma_channel_set_single_shot(&dmach_adc_res1_conf);
    
respondido por el chwi

Lea otras preguntas en las etiquetas