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?