Forum: Mikrocontroller und Digitale Elektronik STM32 CubeMx Lib mit SPI sehr langsam?


von Stampede (Gast)


Lesenswert?

Hallo,

ich nutze einen STM32F103, der mit 64MHz läuft (HSI mit PLL).
Dazu die SPI Lib von Cubemx:

Der Code in der Main ist simpel, und testweise das einzige das 
ausgeführt wird:
1
      if(ADS1262GetDRDY(Channel1)
2
      {
3
        if(ADS1262ReadDataCRC(Channel1, &ADC1Result, 6) == 0)
4
        {
5
[..] // Bisschen Daten kopieren
6
        }
7
      }

ADS1262GetDRDY() prüft dabei nur, ob ein GPIO gesetzt ist oder nicht. 
Die eigentliche Leseroutine sieht so aus:
1
uint8_t ADS1262ReadDataCRC(uint8_t ADCIndex, ADCRESULT *Result, uint8_t NumBytes)
2
{
3
  uint8_t ADCBytes[6], CRCValue;
4
  SPI_HandleTypeDef *pSPIHandle;
5
6
  ADCBytes[0] = 0;
7
  ADCBytes[1] = 0;
8
  ADCBytes[2] = 0;
9
  ADCBytes[3] = 0;
10
  ADCBytes[4] = 0;
11
  ADCBytes[5] = 0;
12
13
  if(ADCIndex == Channel1)
14
  {
15
    pSPIHandle = &hspi1_adc;
16
  }
17
  else if(ADCIndex == Channel2)
18
  {
19
    pSPIHandle = &hspi2_adc;
20
  }
21
  else
22
    return 1;
23
  // check for errors when calling the function
24
  if(NumBytes != 6 || Result == NULL)
25
    return 1;
26
  else
27
  {
28
    // Get the data
29
    //HAL_SPI_Init(&hspi1_adc);
30
    mADC_CS_ASSERT(ADCIndex);
31
    while(HAL_SPI_STATE_BUSY == HAL_SPI_GetState(pSPIHandle));    // wait for SPI to become free
32
    HAL_SPI_Receive(pSPIHandle, ADCBytes, 6, 2);          // receive data from ADC
33
    mADC_CS_DEASSERT(ADCIndex);
34
    CRCValue = 0xFF & (ADCBytes[1]+ADCBytes[2]+ADCBytes[3]+ADCBytes[4]+0x9B);
35
    // check for transmission errors
36
    if(CRCValue == ADCBytes[5])
37
    {
38
      Result->Status = ADCBytes[0];
39
      Result->ADCValue = (ADCBytes[1]<<24) |  (ADCBytes[2]<<16) | (ADCBytes[3]<<8) | (ADCBytes[4]<<0);
40
      Result->Checksum = ADCBytes[5];
41
      return(0);
42
    }
43
    else
44
    {
45
      return(2);
46
    }
47
  }
48
}

Die Leseroutine braucht für die 6 gammeligen Bytes geschlagene 36µs (von 
CS low bis wieder high), der SPI läuft dabei mit 8MHz.
Allein von CS low bis zum start der CLK vergehen fast 8µs, nach jedem 
Byte ca. 3µs.
Mir schein als ob diese aufgeblasene CubeMX Lib nur abartig langsam ist. 
Der STM32F103 hat Prefetch & Cache eingeschaltet, 2 Waitstates für den 
Flash wie gefordert.

Hat jemand mal Messergebnisse um vergleichen zu können? Bringt SPI mit 
DMA da was?


Danke und Grüße
Stampede

von Wolf-Stefan (Gast)


Lesenswert?

Stampede schrieb:
> Die Leseroutine braucht für die 6 gammeligen Bytes geschlagene 36µs (von
> CS low bis wieder high), der SPI läuft dabei mit 8MHz.
> Allein von CS low bis zum start der CLK vergehen fast 8µs, nach jedem
> Byte ca. 3µs.
...

> Mir schein als ob diese aufgeblasene CubeMX Lib nur abartig langsam ist.
Das habe ich auch festgestellt.

Das ist kein Wunder, wenn man sich mal den HAL-Code anschaut.
In HAL_SPI_Transmit werden:
- irgendwelche Parameter gecheckt
- die SPI-Resource gelockt
- ein Timeout initialisiert
- geguckt, ob überhaupt Daten zum Verschicken da sind
- diverse Daten im Handle aktualisiert
- ggf. nochmal im Konfigurationsregister geschrieben
- ggf. CRC initialisiert
- ggf. das SPI enabeld
- nach der Breite des Datentransfer geschaut (8 Bit/16 Bit)
- und yeah Daten ins Datenregister des SPI geschrieben
- auf das TXE-Flag gewartet
- das Overrun-Flag zurückgesetzt
- Timeout und andere Fehlerfälle behandelt
- ggf. CRC aktualisiert
- und die SPI-Resource wieder freigegeben


> Der STM32F103 hat Prefetch & Cache eingeschaltet, 2 Waitstates für den
> Flash wie gefordert.
Ich glaube das hat da gar nicht so viel Einfluss.
Ich hatte hier testweise die Taktfrequenz von 8 MHz auf 16 MHz erhöht 
(CM4).
Die Übertragung auf der Leitung war immer noch wesentlich kürzer, als 
das Ganze drumherum.

> Hat jemand mal Messergebnisse um vergleichen zu können? Bringt SPI mit
> DMA da was?
Die Zeit zwischen zwei Bytes mit
1
  HAL_SPI_Transmit( &TFT_SPI_Handle, &data, 1, 1);
betrug irgendwas um die 50 µs.

Ich habe den ganzen Overhead aus dem HAL weggelassen und jetzt nur noch 
den relevanten Teil dastehen:
1
  // warten bis frei
2
  while((( TFT_SPI_Handle.Instance->SR) & SPI_FLAG_TXE) != SPI_FLAG_TXE);
3
  // Datum rausballern
4
  TFT_SPI_Handle.Instance->DR = data;
5
  // Overflowbit zurücksetzen, ohne dem gings irgendwie nicht
6
  TFT_SPI_Handle.Instance->SR &= ~( SPI_FLAG_OVR);
Damit wurde der Abstand zwischen den Datenbytes auf ca. 2 µs reduziert.

Die Initialisierung mit HAL_SPI_Init und HAL_SPI_MspInit habe ich 
gelassen.
Der Einsatz von DMA könnte natürlich auch noch was bringen.

Mir reicht das oben erreichte.

Wolf

von dasrotemopped (Gast)


Lesenswert?

CubeMX bietet für jede Peripherie (SPI, I2C, ADC, DAC .. usw.) immer 3 
Modi an: polling, IRQ, DMA. Polling ist das langsamste, DMA das 
schnellste. Du verwendest Polling. Verwende DMA und du wirst sehen, das 
die selbe SPI Hardware mit CubeMX ca 8x so schnell wird.

Gruß,

dasrotemopped.

von Wühlhase (Gast)


Lesenswert?

Ja...die HAL rührt reichlich Mehraufwand ein. Der Vorteil der HAL soll 
eine bessere Portierbarkeit zwischen verschiedenen Mikrocontrollern 
ermöglichen-behauptet zumindest ST.

Ich persönlich finde die CubeMX zwar an sich eine wirklich gute Idee, 
bin von der HAL aber auch nicht allzusehr begeistert. Die meisten 
Funktionen, die eigentlich mit ein oder zwei Whileschleifen und einem 
einfachen Kopierbefehl erschlagen werden könnten arten in der HAL zu 
riesigen Code-Romanen aus.

von Wolf-Stefan (Gast)


Lesenswert?

dasrotemopped schrieb:
> Du verwendest Polling. Verwende DMA und du wirst sehen, das
> die selbe SPI Hardware mit CubeMX ca 8x so schnell wird.

So das habe ich jetzt mal gemacht. Und damit ich nicht mit dem Scope 
stoppen muss, lass ich den STM auch gleich ausrechnen, wie schnell der 
Transfer erledigt wird.
Randbedingungen: STM32F4xx, 16 MHz Systemtakt, 8 MHz SPI-Takt
theoretisches Maximum: 1 MByte/s
Test: 4 Rechtecke auf einem ILI9341-Display (320x240, 16-Bit-Farbe) 
einfarbig einfärben
Dabei werden reichlich 150 KByte zum Display geschickt.

Hier die Ergebnisse:
1
HAL, polling mode            50 kByte/s
2
HAL, interrupt mode         100 kByte/s
3
zu Fuss (siehe oben)        315 kByte/s
4
HAL, DMA mode               991 kByte/s
5
HAL, DMA + Rest zu Fuss     997 kByte/s
Dabei fragt man sich, warum STM beim DMA-Counter zu geizig war und nur 
16 Bit (statt z.B. 24) verbaut hat. So musste ich den DMA-Transfer 
unnötigerweise splitten.

Danke nochmal für den Anschubser, sonst hätte ich das sicher nicht 
weiter optimiert.

Wolf

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.