Forum: Mikrocontroller und Digitale Elektronik STM32F4 - SPI TX Interrupt verbieten


von Johannes N. (strangeman)


Lesenswert?

Hallo!

Ich habe hier das STM32F429-DISCOVERY board und möchte gerne mit dem SPI 
Datenübertragungen realisieren. Allerings treten Interrupts auf, die ich 
eigentlich deaktiviert habe.

So initialisiere ich das ganze:
1
    // Init SPI1 ////////////////////////////////////////////////////
2
    SPI_InitTypeDef SPI_InitStruct;
3
    // enable peripheral clock
4
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
5
6
    /* configure SPI1 */
7
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; 
8
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // transmit in master mode, NSS pin has to be always high
9
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; 
10
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_High; // clock is low when idle
11
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; 
12
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set; // set the NSS management to internal and pull internal NSS high
13
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; 
14
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;// data is transmitted MSB first
15
    SPI_Init(SPI1, &SPI_InitStruct);
16
    SPI_Cmd(SPI1, ENABLE); // enable SPI1
17
18
    // configure interrupts
19
    SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_ERR, ENABLE);
20
    SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE, DISABLE);
21
    SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, DISABLE);
22
23
    NVIC_InitTypeDef NVIC_InitStructure;
24
    NVIC_InitStructure.NVIC_IRQChannel = SPI1_IRQn;
25
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
26
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
27
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
28
    NVIC_Init(&NVIC_InitStructure);

Später starte ich dann in einer Initialisierungsroutine eine 
Übertragung, bei der ich einfach per Polling byte für byte an die 
Peripherie rausschiebe.
Im regulären Betrieb kommen dann Übertragungen mit dem DMA.

Ich brauche also niemals die Receive/Transmit interrupts. Daher habe ich 
explizit die Interrupts für TXE (Transfer register empty) und RXNE 
(receive register not empty) abgeschaltet. Ich möchte mit der ISR 
nämlich nur Fehlerfälle abfangen. Dazu sieht meine ISR momentan so aus:
1
  // called on error conditions
2
  extern "C" void SPI1_IRQHandler()
3
  {
4
    if (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE))
5
      while (1) {}
6
    else if (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE))
7
      while (1) {}
8
    else if (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_OVR))
9
      while (1) {}
10
    else if (SPI_I2S_GetFlagStatus(SPI1, SPI_FLAG_MODF))
11
      while (1) {}
12
    else if (SPI_I2S_GetFlagStatus(SPI1, SPI_FLAG_CRCERR))
13
      while (1) {}
14
    else if (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TIFRFE))
15
      while (1) {}
16
    else if (SPI_I2S_GetFlagStatus(SPI1, I2S_FLAG_UDR))
17
      while (1) {}
18
    else if (SPI_I2S_GetFlagStatus(SPI1, I2S_FLAG_CHSIDE))
19
      while (1) {}
20
    else
21
      while (1) {}
22
  }

Das Ziel ist es, während der Entwicklung des Programms die auftretenden 
Fehlerfälle mitzubekommen. Nun pasiert aber etwas interessantes:
Der TXE Interrupt wird ausgelöst, obwohl ich ihn explizit deaktiviert 
habe - was läuft hier falsch?!

Danke an alle, die mir einen Tip geben können!
LG,
StrangeMan

PS: Ich habe es eben nochmal mit dem Debugger überprüft: 
SPI_I2S_ITConfig() wird genau dreimal aufgerufen, das sind die drei oben 
aus dem Quellcode. Es ist also sichergestellt, dass die Einstellungen 
nicht überschrieben werden.

: Bearbeitet durch User
von struct (Gast)


Lesenswert?

SPI_Init(SPI1, &SPI_InitStruct);
sitzt an der falschen Stelle. Erst beschreibst Du alle Struct-Variablen 
und dann machst Du das SPI_Init(...)
Damit setzt Du die Struct auf default.
SPI_Init(...) immer am Anfang.

von Peter F. (peter_da_steht_er)


Lesenswert?

Hallo könntest du bitte mal dein SPI read Funktion posten. Ich wäre sehr 
interessiert

von Johannes N. (strangeman)


Lesenswert?

@struct:
Ich glaube, du verwechselst das mit SPI_StructInit(). SPI_Init() 
initialisiert tatsächlich das SPI mit den entsprechenden Parametern aus 
dem struct.

@peter_da_steht_er:
Im Zuge der Initialisierung mache ich das per Polling - ich lese aber 
auch nix sondern schreibe nur.
1
    GPIOA->BSRRH = CS_PIN; // low
2
    for (int i = 0; i < 25; i++)
3
    {
4
      /* Fill output buffer with data */
5
      SPI1->DR = transmitbuffer[i];
6
      /* Wait for SPI to end everything */
7
      while (((SPI1)->SR & (SPI_SR_TXE | SPI_SR_RXNE)) == 0 || ((SPI1)->SR & SPI_SR_BSY));
8
      /* Read data register */
9
      SPI1->DR;
10
    }
11
    GPIOA->BSRRL = CS_PIN; // high

Später lasse ich dann zwei DMA laufen, einer versorgt das 
SPI-Datenregister mit Sendedaten, der andere liest das Empfangene byte 
ein.
1
    // Init DMA Interrupt for transmitting
2
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
3
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);
4
5
    // enable DAM2_Stream2 (RX) interrupt for button processing and error checking
6
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream2_IRQn;
7
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 14;
8
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 6;
9
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
10
    NVIC_Init(&NVIC_InitStructure);
11
    // enable DAM2_Stream5 (TX) interrupt for error checking
12
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream5_IRQn;
13
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 14;
14
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 6;
15
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
16
    NVIC_Init(&NVIC_InitStructure);
17
18
    // enable interrupt for failure modes
19
    DMA_ITConfig(DMA2_Stream2, DMA_IT_FE, ENABLE);
20
    DMA_ITConfig(DMA2_Stream2, DMA_IT_TE, ENABLE);
21
    DMA_ITConfig(DMA2_Stream5, DMA_IT_FE, ENABLE);
22
    DMA_ITConfig(DMA2_Stream5, DMA_IT_TE, ENABLE);
23
24
    // enable DMA2 clock
25
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

Das Empfangen und Senden starte ich dann so:
1
      // start new transmission
2
      GPIOA->BSRRH = CS_PIN; // low
3
4
      // start shifting out data with the dma
5
      DMA_ClearITPendingBit(DMA2_Stream5, DMA_IT_TCIF5); // leaving this line out, breaks everything! Why?!
6
7
      // DMA Stream for writing led data
8
      DMA_InitTypeDef DMA_InitStruct;
9
      DMA_StructInit(&DMA_InitStruct);
10
      DMA_InitStruct.DMA_Channel = DMA_Channel_3;
11
      DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->DR);
12
      DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)(transmitbuffer);
13
      DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;
14
      DMA_InitStruct.DMA_BufferSize = 24*2 + 1;
15
      DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
16
      DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
17
      DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
18
      DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
19
      DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
20
      DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;
21
      DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
22
      DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
23
      DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
24
      DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
25
      DMA_Init(DMA2_Stream5, &DMA_InitStruct);
26
27
      // DMA Stream for reading button data
28
      DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);
29
      DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);
30
      DMA_ITConfig(DMA2_Stream2, DMA_IT_TC, ENABLE);
31
32
      DMA_StructInit(&DMA_InitStruct);
33
      DMA_InitStruct.DMA_Channel = DMA_Channel_3;
34
      DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->DR);
35
      DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)(&inputBuffer[transferStage]);
36
      DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;
37
      DMA_InitStruct.DMA_BufferSize = 4;
38
      DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
39
      DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
40
      DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
41
      DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
42
      DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
43
      DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;
44
      DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
45
      DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
46
      DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
47
      DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
48
      DMA_Init(DMA2_Stream2, &DMA_InitStruct);
49
50
      // enable the DMAs and start transmitting data
51
      DMA_Cmd(DMA2_Stream2, ENABLE);
52
      DMA_Cmd(DMA2_Stream5, ENABLE);

Der TX DMA schreibt 49 Bytes auf den SPI, der RX DMA liest die ersten 4 
Bytes von SPI, und hört dann auf, da es nicht mehr Daten zu lesen gibt.
Hier sind die dazugehörigen Interrupts:
1
  // irq handler for incoming button data and error handling
2
  extern "C" void DMA2_Stream2_IRQHandler()
3
  {
4
    if (DMA_GetFlagStatus(DMA2_Stream2, DMA_FLAG_TCIF2))
5
    {
6
      DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);
7
      DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);
8
9
      // Processing here ...
10
    }
11
    /*  DMA_FLAG_TEIFx:  Streamx transfer error flag
12
     *  DMA_FLAG_DMEIFx: Streamx direct mode error flag
13
     *  DMA_FLAG_FEIFx:  Streamx FIFO error flag
14
     */
15
    else if (DMA_GetFlagStatus(DMA2_Stream2, DMA_FLAG_TEIF2))
16
    {
17
      while (1) {};
18
    }
19
    else if (DMA_GetFlagStatus(DMA2_Stream2, DMA_FLAG_DMEIF2))
20
    {
21
      while (1) {};
22
    }
23
    else if (DMA_GetFlagStatus(DMA2_Stream2, DMA_FLAG_FEIF2))
24
    {
25
      while (1) {};
26
    }
27
  }
28
29
  // irq handler for TX-channel error handling
30
  extern "C" void DMA2_Stream5_IRQHandler()
31
  {
32
    /*  DMA_FLAG_TEIFx:  Streamx transfer error flag
33
     *  DMA_FLAG_DMEIFx: Streamx direct mode error flag
34
     *  DMA_FLAG_FEIFx:  Streamx FIFO error flag
35
     */
36
    if (DMA_GetFlagStatus(DMA2_Stream5, DMA_FLAG_TEIF5))
37
    {
38
      while (1) {};
39
    }
40
    else if (DMA_GetFlagStatus(DMA2_Stream5, DMA_FLAG_DMEIF5))
41
    {
42
      while (1) {};
43
    }
44
    else if (DMA_GetFlagStatus(DMA2_Stream5, DMA_FLAG_FEIF5))
45
    {
46
      while (1) {};
47
    }
48
  }

Das funktioniert aber eigentlich ganz gut. Ich poste den Code jetzt nur, 
weil du gefragt hast und es sicher auch noch andere interessiert.

Mein Problem hat aber mit dem DMA nichts zu tun - es geht ja nur darum, 
warum der TXE Interrupt vom SPI triggert, obwohl ich ihn abgeschaltet 
habe.

Vielen Dank für Hilfe,
Johannes

von Johannes N. (strangeman)


Lesenswert?

So ihr lieben, ich hab meinen Fehler. Hier kommt die Lösung:

Dass das TXE Bit gesetzt war, verleitete mich zu der Annahme, dass 
dieses auch den Interrupt ausgelöst hat. Das muss aber nicht stimmen, 
genausogut könnte der Interrupt von einem anderen Bit ausgelöst worden 
sein - nur habe ich in meiner ISR eben als erstes auf das TXE Bit 
geprüft und daher niemals den Status der anderen Bits gesehen. Ich habe 
die ISR nun so modifiziert:
1
  // called on error conditions
2
  extern "C" void SPI1_IRQHandler()
3
  {
4
    bool TXE = SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE);
5
    bool RXNE = SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE);
6
    bool OVR = SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_OVR);
7
    bool MODF = SPI_I2S_GetFlagStatus(SPI1, SPI_FLAG_MODF);
8
    bool CRCERR = SPI_I2S_GetFlagStatus(SPI1, SPI_FLAG_CRCERR);
9
    bool TIFRFE = SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TIFRFE);
10
    bool UDR = SPI_I2S_GetFlagStatus(SPI1, I2S_FLAG_UDR);
11
    bool CHSIDE = SPI_I2S_GetFlagStatus(SPI1, I2S_FLAG_CHSIDE);
12
    if (OVR || MODF || CRCERR || TIFRFE || UDR || CHSIDE)
13
      while (1) {}
14
    if (TXE || RXNE)
15
      while (1) {}
16
  }
Auf diese Weise sieht man im Debugger den Status aller relevanten Bits. 
In meinem Fall war neben dem TXE Bit auch das OVR Bit gesetzt, welches 
den Interrupt ausgelöst hat und signalisiert, dass das Datenregister 
nicht gelesen wurde und dessen Inhalt durch neue Daten überschrieben 
wurde.
Das kommt daher, dass ich mit dem RX-DMA nur 4 Bytes gelesen habe, der 
TX DMA aber 49 Bytes schreibt. Daher hat der RX-DMA ab dem 5. Byte seine 
Arbeit eingestellt und beim 6. Byte wird dann das OVR flag gesetzt. Es 
muss also vom RX-DAM die gleiche Anzahl an Bytes gelesen werden, wie der 
TX-DMA schreibt.

Problem gelöst.

von Johannes N. (strangeman)


Lesenswert?

Noch ein Nachtrag: Der oben gepostete Code enthält verschiedene Fehler.

1)  Zum Zeitpunkt der Aktivierung des DMA muss der dazugehörige 
Peripherie-Baustein deaktiviert sein. Erst nachdem der DMA 
konfiguriert und aktiviert wurde, darf das SPI-Modul eingeschaltet 
werden, wodurch sofort die Übertragung gestartet wird. Das bedeutet, 
dass zwischen zwei aufeinanderfolgenden Übertragungen das SPI-Modul 
abgeschaltet werden muss. Es empfiehlt sich, das SPI-Modul nicht direkt 
nach dem Beenden einer Übertragung abzuschalten, sodern nur kurz vor dem 
Start einer neuen Übertragung kurz zu deaktivieren, da sonst zwischen 
den Übertragungen die Pins nicht mehr aktiv sind (z.B. wird die 
Clock-Leitung nicht auf ihrem Idle Zustand gehalten)

2)  Es empfiehlt sich, den FIFO im DMA zu benutzen, da der DMA sonst 
ohne Zwischenschritt sofort bei Eintreffen eines neuen Bytes Zugriff auf 
den Ziel-Speicher haben muss, damit das eingehende Byte nicht verloren 
geht. Der FIFO kann als Zwischenspeicher also den RX-Kanal 
ausfallsicherer machen. Außerdem arbeitet der SPI mit Bytes, der 
Speicher wird aber im Prozessor auf dem Speicherbus als 32bit Doppelwort 
addressiert. Der FIFO kann so konfiguriert werden, dass er immer 
Doppelwörter liest/schreibt, und so für 4 Bytes des SPI nur einen 
einzigen Speicherzugriff macht, was im Endeffekt den Speicherbus weniger 
belastet.

3)  Ich weiß nicht, ob es relevant ist, aber ich habe dem TX-DMA eine 
geringere Priorität gegeben als dem RX-DMA. Ich gehe davon aus, dass 
es im Zweifelsfall wichtiger ist, die eingehenden Daten zu lesen, bevor 
man das nächste Byte sendet und damit möglicherweise die eingelesenen 
Daten überschreibt.

4)  Bei mir hat der TX-DMA immer direkt bei seiner Aktivierung 
(DMA_Cmd(DMA2_Stream5, ENABLE)) einen FIFO Error ausgelöst. Das ist 
scheinbar ein bekanntes Problem (siehe diverse Foren-Threads). Die 
Lösung ist, das dazugehörige Flag einfach zu löschen, nachdem der DMA 
aktiviert wurde. Mein DMA Interrupt hat allerdings die höchste Priorität 
(ich will, dass der bei seinem Eintreten alles stoppt, damit ich solche 
Fehler bemerke). Das bedeutet, dass ich den DMA-Fifo-Error Interrupt 
deaktivieren muss, dann den DMA aktiviere, dann das FIFO-Error-Flag 
lösche und dann den Interrupt wieder aktiviere.

5)  Bevor man den DMA konfiguriert, sollte man ihn in jedem Falle 
erstmal deaktivieren. Das geschieht normalerweise nach dem 
Fertigstellen einer Übertragung automatisch, aber wer weiß...

So, hier nochmal der komplette Code:
Initialisierung
1
    // Init SPI1 ////////////////////////////////////////////////////
2
    SPI_InitTypeDef SPI_InitStruct;
3
    // enable peripheral clock
4
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
5
6
    /* configure SPI1
7
    */
8
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; 
9
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // transmit in master mode, NSS pin has to be always high
10
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; 
11
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_High; // clock is low when idle
12
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; 
13
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set; // set the NSS management to internal and pull internal NSS high
14
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
15
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
16
    SPI_Init(SPI1, &SPI_InitStruct);
17
18
    // enable error interrupts
19
    SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_ERR, ENABLE);
20
    SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE, DISABLE);
21
    SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, DISABLE);
22
23
    NVIC_InitTypeDef NVIC_InitStructure;
24
    NVIC_InitStructure.NVIC_IRQChannel = SPI1_IRQn;
25
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
26
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
27
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
28
    NVIC_Init(&NVIC_InitStructure);
29
30
    // Init GPIOs ///////////////////////////////////////////////////
31
    GPIO_InitTypeDef GPIO_InitStruct;
32
    // enable clock for used IO pins
33
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
34
35
    GPIO_InitStruct.GPIO_Pin = CS_PIN;
36
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
37
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
38
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
39
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
40
    GPIO_Init(GPIOA, &GPIO_InitStruct);
41
42
    GPIO_InitStruct.GPIO_Pin =  GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
43
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
44
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
45
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
46
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
47
    GPIO_Init(GPIOA, &GPIO_InitStruct);
48
49
    GPIOA->BSRRL = CS_PIN; // set CS high (
50
51
    // Enable Alternate functions on gpios //////////////////////////
52
    // connect SPI1 pins to SPI alternate function
53
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
54
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
55
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
56
57
    
58
    // Init DMA for transmitting and receiving
59
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
60
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);
61
62
    // enable DAM2_Stream2 (RX) interrupt for data processing and error checking
63
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream2_IRQn;
64
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 14;
65
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 6;
66
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
67
    NVIC_Init(&NVIC_InitStructure);
68
    // enable DAM2_Stream5 (TX) interrupt for error checking
69
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream5_IRQn;
70
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 14;
71
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 6;
72
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
73
    NVIC_Init(&NVIC_InitStructure);
74
75
    // enable interrupt for failure modes
76
    DMA_ITConfig(DMA2_Stream2, DMA_IT_FE, ENABLE);
77
    DMA_ITConfig(DMA2_Stream2, DMA_IT_TE, ENABLE);
78
    DMA_ITConfig(DMA2_Stream5, DMA_IT_FE, ENABLE);
79
    DMA_ITConfig(DMA2_Stream5, DMA_IT_TE, ENABLE);
80
81
    // enable DMA2 clock
82
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

*Starten einer Übertragung*:
1
      // disable SPI1
2
      SPI_Cmd(SPI1, DISABLE);
3
4
      // prepare data here !
5
6
      // start new transmission
7
      GPIOA->BSRRH = CS_PIN; // low
8
9
      // start shifting out data with the dma
10
      DMA_Cmd(DMA2_Stream5, DISABLE);
11
      while (DMA2_Stream5->CR & DMA_SxCR_EN); // wait for DMA to be disabled
12
      DMA_ClearITPendingBit(DMA2_Stream5, DMA_IT_TCIF5); // removing this breaks everything ?! Why?
13
14
      // DMA Stream for writing data
15
      DMA_InitTypeDef DMA_InitStruct;
16
      DMA_StructInit(&DMA_InitStruct);
17
      DMA_InitStruct.DMA_Channel = DMA_Channel_3;
18
      DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->DR);
19
      DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)(transmitbuffer);
20
      DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;
21
      DMA_InitStruct.DMA_BufferSize = 24*2 + 1;
22
      DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
23
      DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
24
      DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
25
      DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
26
      DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
27
      DMA_InitStruct.DMA_Priority = DMA_Priority_Low;
28
      DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Enable;
29
      DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
30
      DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
31
      DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
32
      DMA_Init(DMA2_Stream5, &DMA_InitStruct);
33
34
      // DMA Stream for reading button data
35
      DMA_Cmd(DMA2_Stream2, DISABLE);
36
      while (DMA2_Stream2->CR & DMA_SxCR_EN);
37
38
      DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);
39
      DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);
40
      DMA_ITConfig(DMA2_Stream2, DMA_IT_TC, ENABLE);
41
42
      DMA_StructInit(&DMA_InitStruct);
43
      DMA_InitStruct.DMA_Channel = DMA_Channel_3;
44
      DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->DR);
45
      DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)(&inputBuffer[transferStage]);
46
      DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;
47
      DMA_InitStruct.DMA_BufferSize = 24*2 + 1;
48
      DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
49
      DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
50
      DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
51
      DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
52
      DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
53
      DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;
54
      DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Enable;
55
      DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
56
      DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
57
      DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
58
      DMA_Init(DMA2_Stream2, &DMA_InitStruct);
59
60
      // disable Error ISRs
61
      DMA_ITConfig(DMA2_Stream2, DMA_IT_FE, DISABLE);
62
      DMA_ITConfig(DMA2_Stream5, DMA_IT_FE, DISABLE);
63
64
      // enable the DMAs and clear the appearing FIFO error flags (weird hack?!)
65
      DMA_Cmd(DMA2_Stream2, ENABLE);
66
      DMA_Cmd(DMA2_Stream5, ENABLE);
67
      DMA_ClearFlag(DMA2_Stream5, DMA_FLAG_FEIF5);
68
69
      // enable Error ISRs again
70
      DMA_ITConfig(DMA2_Stream2, DMA_IT_FE, ENABLE);
71
      DMA_ITConfig(DMA2_Stream5, DMA_IT_FE, ENABLE);
72
73
      // enable SPI1 and start transmission
74
      SPI_Cmd(SPI1, ENABLE);

Zum Schluss noch die ISRs
1
  // irq handler for incoming button data and error handling
2
  extern "C" void DMA2_Stream2_IRQHandler()
3
  {
4
    if (DMA_GetFlagStatus(DMA2_Stream2, DMA_FLAG_TCIF2))
5
    {
6
      DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);
7
      DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);
8
9
      GPIOA->BSRRL = CS_PIN; // high
10
11
      // process the data here !
12
    }
13
    else
14
    {
15
      /*  DMA_FLAG_TEIFx:  Streamx transfer error flag
16
       *  DMA_FLAG_DMEIFx: Streamx direct mode error flag
17
       *  DMA_FLAG_FEIFx:  Streamx FIFO error flag
18
       */
19
      bool TEIF = DMA_GetFlagStatus(DMA2_Stream2, DMA_FLAG_TEIF5);
20
      bool DMEIF = DMA_GetFlagStatus(DMA2_Stream2, DMA_FLAG_DMEIF5);
21
      bool FEIF = DMA_GetFlagStatus(DMA2_Stream2, DMA_FLAG_FEIF5);
22
      if (TEIF || DMEIF || FEIF)
23
      {
24
        while (1) {};
25
      }
26
    }
27
  }
28
29
  // irq handler for TX-channel error handling
30
  extern "C" void DMA2_Stream5_IRQHandler()
31
  {
32
    /*  DMA_FLAG_TEIFx:  Streamx transfer error flag
33
     *  DMA_FLAG_DMEIFx: Streamx direct mode error flag
34
     *  DMA_FLAG_FEIFx:  Streamx FIFO error flag
35
     */
36
    bool TEIF = DMA_GetFlagStatus(DMA2_Stream5, DMA_FLAG_TEIF5);
37
    bool DMEIF = DMA_GetFlagStatus(DMA2_Stream5, DMA_FLAG_DMEIF5);
38
    bool FEIF = DMA_GetFlagStatus(DMA2_Stream5, DMA_FLAG_FEIF5);
39
    if (TEIF || DMEIF || FEIF)
40
    {
41
      while (1) {};
42
    }
43
  }
44
45
  // called on error conditions
46
  extern "C" void SPI1_IRQHandler()
47
  {
48
    bool TXE = SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE);
49
    bool RXNE = SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE);
50
    bool OVR = SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_OVR);
51
    bool MODF = SPI_I2S_GetFlagStatus(SPI1, SPI_FLAG_MODF);
52
    bool CRCERR = SPI_I2S_GetFlagStatus(SPI1, SPI_FLAG_CRCERR);
53
    bool TIFRFE = SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TIFRFE);
54
    bool UDR = SPI_I2S_GetFlagStatus(SPI1, I2S_FLAG_UDR);
55
    bool CHSIDE = SPI_I2S_GetFlagStatus(SPI1, I2S_FLAG_CHSIDE);
56
    if (OVR || MODF || CRCERR || TIFRFE || UDR || CHSIDE)
57
      while (1) {}
58
    if (TXE || RXNE)
59
      while (1) {}
60
  }

So, vielleicht hilfts ja jemandem!

LG,
Johannes

von Peter F. (peter_da_steht_er)


Lesenswert?

Ja, vielen Dank für deine Mühe.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Peter Frun schrieb:
> Ja, vielen Dank für deine Mühe.

Dem schliesse ich mich gerne an. Seit einiger Zeit fummele ich an der 
DMA für die beiden I2S Schnittstellen des F407, die sich ja auch des SPI 
bedienen und stolperte immer wieder. Deine Erfahrung lasse ich da jetzt 
mal einfliessen. Vielen Dank und eine tiefe Verbeugung!

: Bearbeitet durch User
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.