Forum: Mikrocontroller und Digitale Elektronik [XMEGA] Read ADC via DMA


von Jiri D. (Gast)


Lesenswert?

Hallo!

Ich moechte mehrere samples vom ADCA eines ATxmega32A4U mit DMA 
auslesen, aber ich bekomme immer nur 0x0000 in den buffer geschrieben. 
Das Vorgehen ist so:
1. ADCA, differenziell zu PADGND, in FREERUNning mode setzen
1. In einer Timer ISR alle 100ms:
   - Mittelwert der 16 samples von zuvor bilden
   - DMA Transaktion starten, 2B je burst, 16 bursts gesamt

ADCA setup:
1
#include <string.h>     /* memset() */
2
#include <stdint.h>
3
4
#define NUM_SAMPLES ((sizeof(samples) / sizeof(samples[0])))
5
static uint16_t samples[16];
6
7
void
8
init(void)
9
{
10
    memset(samples, 0, sizeof(samples));
11
12
    /* configure ADCA */
13
    ADCA.CTRLA = ADC_DMASEL_OFF_gc | ADC_ENABLE_bm;
14
    ADCA.CTRLB = ADC_CURRLIMIT_NO_gc | ADC_CONMODE_bm
15
               | ADC_RESOLUTION_12BIT_gc;
16
    ADCA.REFCTRL = ADC_REFSEL_AREFB_gc;
17
    ADCA.PRESCALER = ADC_PRESCALER_DIV512_gc;
18
    ADCA.EVCTRL = 0;
19
20
    ADCA.CH0.CTRL = ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_DIFF_gc;
21
    /* "ADC_CH_MUXNEG_PADGND" undefined, corresponds to 0x05 */
22
    ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc | 0x05;
23
    ADCA.CH0.INTCTRL = 0;
24
    ADCA.CH0.SCAN = 0;
25
26
    ADCA.CTRLA |= ADC_FLUSH_bm;
27
    ADCA.CTRLB |= ADC_FREERUN_bm;
28
    ADCA.CH0.CTRL |= ADC_CH_START_bm;
29
}

ISR & helper functions:
1
static void
2
start_dma(void)
3
{
4
    /* configure DMA.CH1 */
5
    DMA.CH1.CTRLA &= ~DMA_CH_ENABLE_bm;
6
    while (DMA.CH1.CTRLA & DMA_CH_ENABLE_bm)
7
        nop(1);
8
    DMA.CH1.CTRLA |= DMA_CH_RESET_bm;
9
    while (DMA.CH1.CTRLA & DMA_CH_RESET_bm)
10
        nop(1);
11
    DMA.CH1.ADDRCTRL =
12
        DMA_CH_SRCRELOAD_BURST_gc | DMA_CH_SRCDIR_INC_gc |
13
        DMA_CH_DESTRELOAD_NONE_gc | DMA_CH_DESTDIR_INC_gc;
14
    uint16_t adc_addr = (uint16_t) &ADCA.CH1.RESL;
15
    DMA.CH1.SRCADDR0 = adc_addr & 0xff;
16
    DMA.CH1.SRCADDR1 = (adc_addr >> 8) & 0xff;
17
    DMA.CH1.SRCADDR2 = 0;
18
    DMA.CH1.DESTADDR0 = ((uint16_t) samples) & 0xff;
19
    DMA.CH1.DESTADDR1 = (((uint16_t) samples) >> 8) & 0xff;
20
    DMA.CH1.DESTADDR2 = 0;
21
    DMA.CH1.TRIGSRC = DMA_CH_TRIGSRC_ADCA_CH0_gc;
22
    DMA.CH1.TRFCNT = 2;
23
    DMA.CH1.REPCNT = NUM_SAMPLES;
24
    DMA.CH1.CTRLA =
25
        DMA_CH_REPEAT_bm | DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_2BYTE_gc;
26
27
    DMA.CH1.CTRLA |= DMA_CH_ENABLE_bm;
28
}
29
30
static void
31
avg_flt_samples(void)
32
{
33
    uint32_t sum = 0;
34
    for (uint8_t i = 0; i < NUM_SAMPLES; ++i)
35
        sum += samples[i];
36
    sum += NUM_SAMPLES / 2; /* for rounding */
37
38
    /* extern: measurement_buffer
39
     * struct { uint8_t pending_data : 1; uint16_t measurement; }; */
40
    measurement_buffer.pending_data = 1;
41
    measurement_buffer.data = (uint16_t) (sum / NUM_SAMPLES);
42
}
43
44
45
ISR(TCC0_OVF_vect)
46
{
47
    static enum {
48
        INITIAL_RUN,
49
        LATER_RUN
50
    } state = INITIAL_RUN;
51
52
    if (INITIAL_RUN == state) {
53
        /* it's the first time that we're here, so there's
54
         * no data to precess yet */
55
        start_dma();
56
        state = LATER_RUN;
57
    } else {
58
        /* we're at a later run, so process the data and go for a new
59
         * round */
60
        if (!(DMA.CH1.CTRLB & (DMA_CH_CHBUSY_bm | DMA_CH_CHPEND_bm))
61
            && (DMA.INTFLAGS & DMA_CH1TRNIF_bm)) {
62
            /* DMA transaction has completed */
63
            avg_flt_samples();
64
            start_dma();
65
        }
66
    }
67
}

Das Problem zeigt sich nun so:
- In samples[.] stehen immer nur 0x0000en
- Der ADC laeuft (manuelles Auslesen von ADCA.CH0.RES liefert 
realistische
  Werte)
- Der DMA laeuft, macht aber nur einen burst

Ich habe auch avr-gdb+avarice drangehaengt; dabei scheinen alle Register 
usw. korrekt gesetzt zu werden, DMA wartet auf trigger, wird getriggert 
(macht einen burst, REPCNT geht um 1 runter), und ist dann disabled.

Und das erscheint mir irgendwie seltsam...
Der obige Code funktioniert analog (BURSTLEN=TRFCNT=1) fuer einen UART 
aber ohne Probleme.

Hat jemand eine Idee?

von Jiri D. (Gast)


Lesenswert?

Fehler gefunden:
1
-uint16_t adc_addr = (uint16_t) &ADCA.CH1.RESL;
2
+uint16_t adc_addr = (uint16_t) &ADCA.CH0.RESL;

ist natuerlich bloed, wenn man ADC.CH0 initialisiert, aber die Messwerte 
bei ADC.CH1 erwartet...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.