Mikrocontroller und Digitale Elektronik
›
Thread
Problem mit dem STM32F205 SPI RX DMA
ME
Mw E.
(fritzler)
OP
(Firma: fritzler-avr.de)
25.02.2018 09:40
#5328759
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.
CN
Curby23523 N.
Gast
25.02.2018 09:44
#5328760
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.
ME
Mw E.
(fritzler)
OP
(Firma: fritzler-avr.de)
25.02.2018 10:13
#5328799
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 &= 0 b1111 ;
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 }
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.
Noch kein Account?
Die Registrierung ist kostenlos und dauert nur eine Minute.
Jetzt registrieren