[XMEGA] Read ADC via DMA

Gast #3749461
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?
Gast #3749659
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...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren