Hallöchen, hier zickt der SPI RX DMA rum. Ich lese über den SPI einen externen 24Bit ADC mit 6 Kanälen aus. Das sind dann 18 Bytes. Den ADC betreibe ich im "Loopmode", der ADC bekam einmal das Steuerbyte und dann kann man den CS auf low lassen. Bei jedem anlegen von CLK bekommt man dann alle 18 Bytes zugesendet. IRQ getrieben und per Polling kommen auch immer die passenden 18 Bytes im Speicher an. Dann das Ganze auf DMA umgebaut. Nur ab und zu ließt dieser falsche Bytes vom SPI. Also die Bytes sind nicht verschoben, sondern völlig anders, aber auch nicht alle Bytes sind blödsinn. Das falsch Auslesen kommt auch unregelmäßig. Jetzt könnt das ja nen Timingproblem sein und der ADC sendet murks. Also eine Erkennung in die SW gebaut welche mein Oszi triggert bei diesem Fehler. Und siehe da, am SPI RX Pin liegen valide Daten an! Aber nicht im Speicher! Der DMA wirft keine Errorflags und der DMA funktioniert wunderbar beim auslesen des internen ADC. Weiterhin läuft der DMA beim UART TX bestens. Also hab ichs wohl nicht komplett vergeigt den DMA zu initialisieren. Kann mir einer erklären was hier abgeht? Code kann ich gerne nachliefern, habs nur erstmal nicht angehangen, weil alles selbergeschrieben ist ohne CMSIS/HAL.
Mw E. schrieb: > Code kann ich gerne nachliefern, habs nur erstmal nicht angehangen, weil > alles selbergeschrieben ist ohne CMSIS/HAL. Dann zeig doch mal, wie du DMA, SPI initialisierst und wie du sonst mit dem DMA / SPI umgehst. Also die relevanten Codestellen.
Hier, kannste haben ;) Sämtliche DMA Transfers stellen den DMA über die dma.c ein. "enum dma_periph peri" enthält dabei codiert DMA1 o. 2, den kanal und den Stream. Per Timer wird der Auslesevorgang 1000mal/s angestoßen:
1 | static void callb_tim4_cc4_triggered(unsigned int statreg, void *callb_userdata){ |
2 | |
3 | (void)callb_userdata; |
4 | |
5 | if (statreg & SR_CC4IF){ |
6 | //weiteres Auslesen circulär ohne weiteres Controlbyte
|
7 | if (spi_is_transfering){ |
8 | return; |
9 | }
|
10 | spi_is_transfering = 1; |
11 | int dma_res = spi_dmamode( |
12 | SPI1, //Welcher SPI |
13 | (uint8_t*)&extadc_tx[1], //SPI Sendebuffer |
14 | (uint8_t*)&extadc_rx[1], //SPI Empfangsbuffer |
15 | DMA_SPI1TX_V1, //DMA Sendestream |
16 | DMA_SPI1RX_V2, //DMA Empfangsstream |
17 | 18, //Anzahl der zu übertragenden Daten |
18 | callb_dma_adcext, //IRQ Handler DMA |
19 | NULL, //IRQ Handler DMA Daten |
20 | callb_adc_ext_irq, //IRQ Handler SPI Err |
21 | NULL); //IRQ Handler SPI Err Daten |
22 | if (dma_res){ |
23 | dprintf("SPI DMA REERR: %i\n", dma_res); |
24 | asm("udf"); |
25 | }
|
26 | }
|
27 | }
|
Im SPI Treiber wird das dann durchgereicht und noch mir Prioritäten versehen. Um ein SPI CLK zu erzeugen müssen ja Nullbytes gesendet werden.
1 | int spi_dmamode( |
2 | unsigned int spi_base, //Welcher SPI |
3 | uint8_t *tx, //SPI Sendebuffer |
4 | uint8_t *rx, //SPI Empfangsbuffer |
5 | enum dma_periph stream_tx, //DMA Sendestream |
6 | enum dma_periph stream_rx, //DMA Empfangsstream |
7 | uint16_t buflen, //Anzahl der zu übertragenden Daten |
8 | void (*callb_dma)(unsigned int, void*), //IRQ Handler DMA |
9 | void *callb_data_dma, //IRQ Handler DMA Daten |
10 | void (*callb)(unsigned int, void*), //IRQ Handler SPI Err |
11 | void *callb_data){ //IRQ Handler SPI Err Daten |
12 | |
13 | volatile struct spi * const spi = (struct spi *)spi_bases[spi_base]; |
14 | |
15 | spi->CR2 &= ~(CR2_TXDMAEN | CR2_RXDMAEN); |
16 | asm("DSB"); |
17 | |
18 | int dma_arr; |
19 | |
20 | //RX DMA zuerst einstellen, falls DMA Bits noch gesetzt sind
|
21 | dma_arr = dma_periph( |
22 | stream_rx, //Angabe der Peripherie |
23 | (unsigned int)rx, |
24 | (unsigned int)&(spi->DR), |
25 | buflen, //Anzahl der zu übertragenden Elemente |
26 | DMA_SIZE_BYTE, //Größe der zu Übertragenden Daten |
27 | 0, //nicht circular |
28 | DMA_PERIPH_TO_MEM, //in welche Richtung geht der DMA (mem to mem ist verboten) |
29 | DMA_PRIO_HIGH, //Priorität des DMA |
30 | callb_dma, //Callback bei auftretendem IRQ |
31 | callb_data_dma //Daten für den Callback |
32 | );
|
33 | if (dma_arr){ |
34 | return -1; |
35 | }
|
36 | |
37 | //TX DMA
|
38 | dma_arr = dma_periph( |
39 | stream_tx, //Angabe der Peripherie |
40 | (unsigned int)tx, |
41 | (unsigned int)&(spi->DR), |
42 | buflen, //Anzahl der zu übertragenden Elemente |
43 | DMA_SIZE_BYTE, //Größe der zu Übertragenden Daten |
44 | 0, //nicht circular |
45 | DMA_MEM_TO_PERIPH, //in welche Richtung geht der DMA (mem to mem ist verboten) |
46 | DMA_PRIO_LOW, //Priorität des DMA |
47 | callb_dma, //Callback bei auftretendem IRQ |
48 | callb_data_dma //Daten für den Callback |
49 | );
|
50 | if (dma_arr){ |
51 | return -1; |
52 | }
|
53 | |
54 | callb_irq[spi_base] = callb; |
55 | callb_irq_data[spi_base] = callb_data; |
56 | |
57 | spi->CR2 |= CR2_ERRIE | CR2_TXDMAEN | CR2_RXDMAEN; |
58 | |
59 | //IRQ aktivieren
|
60 | nvic_enable_irq(spi_irq_nbrs[spi_base], 0xF, 0xF); |
61 | |
62 | return 0; |
63 | }
|
Der interne ADC wird so angestoßen:
1 | dma_res = adc_int_dmamode( |
2 | ADC1, //Welcher ADC |
3 | (uint16_t*)adc_werte, //ADC Daten wohin |
4 | callb_dma, //IRQ Handler DMA |
5 | NULL, //IRQ Handler DMA Daten |
6 | callb_adc_int_irq, //IRQ Handler ADc Err |
7 | NULL, //IRQ Handler ADc Err Daten |
8 | ADCM_SCANCONT); |
9 | if (dma_res){ |
10 | dprintf("ADC DMA ERR: %i\n", dma_res); |
11 | asm("udf"); |
12 | }
|
Der Treiber des internen ADC:
1 | int adc_int_dmamode( |
2 | unsigned int adc_base, //Welcher ADC |
3 | uint16_t *mem_buf, //ADC Daten wohin |
4 | void (*callb_dma)(unsigned int, void*), //IRQ Handler DMA |
5 | void *callb_data_dma, //IRQ Handler DMA Daten |
6 | void (*callb)(unsigned int, uint16_t, void*), //IRQ Handler ADc Err |
7 | void *callb_data, //IRQ Handler ADc Err Daten |
8 | enum adc_mode amode){ //ADC Mode |
9 | |
10 | volatile struct adc * const adc = (struct adc *)adc_addrs[adc_base]; |
11 | |
12 | //Modus setzen
|
13 | adc_int_xmode(adc, amode); |
14 | unsigned int circular = 0; |
15 | if (ADCM_SCANCONT == amode){ |
16 | circular = 1; |
17 | }
|
18 | unsigned int sequ_len = adc->SQR1 >> SQR1_SLEN_SHIFT; |
19 | sequ_len &= 0b1111; |
20 | sequ_len += 1; |
21 | //dprintf("DMA_ADC scanlen: %u\n", sequ_len);
|
22 | int i, dma_arr; |
23 | |
24 | //es gibt 2 Versuche, denn jede Periph ist in 2 DMA Vetreten
|
25 | //beide belegt -> dammnit
|
26 | for (i = 0; i < 2; i++){ |
27 | dma_arr = dma_periph( |
28 | dma_streamchan[adc_base][i], //Angabe der Peripherie |
29 | (unsigned int)mem_buf, |
30 | (unsigned int)&(adc->DR), |
31 | sequ_len, //Anzahl der zu übertragenden Elemente |
32 | DMA_SIZE_HWORD, //Größe der zu Übertragenden Daten |
33 | circular, //mem wird circulär befüllt = DMA geht nie zuende |
34 | DMA_PERIPH_TO_MEM, //in welche Richtung geht der DMA (mem to mem ist verboten) |
35 | DMA_PRIO_MED, //Priorität des DMA |
36 | callb_dma, //Callback be auftretendem IRQ |
37 | callb_data_dma //Daten für den Callback |
38 | );
|
39 | if (0 == dma_arr){ |
40 | break; |
41 | }
|
42 | }
|
43 | if (dma_arr){ |
44 | return -1; |
45 | }
|
46 | |
47 | callb_irq[adc_base] = callb; |
48 | callb_irq_data[adc_base] = callb_data; |
49 | |
50 | //IRQ aktivieren
|
51 | nvic_enable_irq(IRQ_NBR_ADC, 0xF, 0xF); |
52 | adc->CR1 |= CR1_OVRIE; |
53 | |
54 | //Leinen los!
|
55 | adc->CR1 &= ~CR1_EOCIE; |
56 | adc->CR2 &= ~CR2_EOCS; |
57 | adc->CR2 |= CR2_DMA | CR2_DDS; //Damit bei SCANCONT nach einem SCAN der DMAREQ weiterhin ausgelöst wird |
58 | |
59 | return 0; |
60 | }
|
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.