Forum: Mikrocontroller und Digitale Elektronik Problem mit dem STM32F205 SPI RX DMA


von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

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.

von Curby23523 N. (Gast)


Lesenswert?

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.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Angehängte Dateien:

Lesenswert?

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
Noch kein Account? Hier anmelden.