Hilfestellung: STM32 - SPI - TX/RX-DMA

Gast #2740516
Lesenswert?

Hallo,

ich verwende auf dem STM32F207 das SPI Interface mit DMA. Das Senden von 
SPI Frames ohne DMA funktioniert.
Die Daten werden auch korrekt über den RX DMA empfangen. Nun möchte ich 
das Senden über den TX DMA realisieren.
Ein Kommando besteht aus zwei 16bit SPI Frames. Je nach Bedarf sollen 
zwei 16bit SPI Frames via TX DMA über die SPI Schnittstelle versendet 
werden. Hierfür habe ich für den TX DMA einen Puffer bereitgestellt, der 
2*16Bit groß ist. Für den RX DMA habe ich einen 64*16Bit Puffer 
angelegt. Ja nach Kommando können auch mehr als zwei 16Bit SPi Frames 
empfangen werden. Müsste der Puffer für TX DMA ebenfalls 64*16Bit groß 
sein ? Wie kann man in der Software veranlassen, dass das dritte und 
vierte 16Bit SPI Frame gesendet werden soll ? Gibt es hierfür eine 
inkrement Funktion die das ermöglichen kann ?
1
uint16_t SPI_MASTER_Buffer_Rx[64];
2
uint16_t SPI_MASTER_Buffer_Tx[2];
3

4

5
static void SPI_Config(void)
6
{
7
  GPIO_InitTypeDef GPIO_InitStructure;
8
  NVIC_InitTypeDef NVIC_InitStructure;
9
 
10
  /* Enable the SPI clock */
11
  SPIx_CLK_INIT(SPIx_CLK, ENABLE);
12

13
  /* Enable GPIO clocks */
14
  RCC_AHB1PeriphClockCmd(SPIx_SCK_GPIO_CLK | SPIx_MISO_GPIO_CLK | SPIx_MOSI_GPIO_CLK, ENABLE);
15

16
  /* Connect SPI pins to AF5 */  
17
  GPIO_PinAFConfig(SPIx_SCK_GPIO_PORT, SPIx_SCK_SOURCE, SPIx_SCK_AF);
18
  GPIO_PinAFConfig(SPIx_MOSI_GPIO_PORT, SPIx_MOSI_SOURCE, SPIx_MOSI_AF);
19
  GPIO_PinAFConfig(SPIx_MISO_GPIO_PORT, SPIx_MISO_SOURCE, SPIx_MISO_AF);
20

21
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
22
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
23
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
24
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
25

26
  /* SPI SCK pin configuration */
27
  GPIO_InitStructure.GPIO_Pin = SPIx_SCK_PIN;
28
  GPIO_Init(SPIx_SCK_GPIO_PORT, &GPIO_InitStructure);
29

30
  /* SPI  MOSI pin configuration */
31
  GPIO_InitStructure.GPIO_Pin =  SPIx_MOSI_PIN;
32
  GPIO_Init(SPIx_MOSI_GPIO_PORT, &GPIO_InitStructure);
33
  
34
  /* SPI  MISO pin configuration */
35
  GPIO_InitStructure.GPIO_Pin =  SPIx_MISO_PIN;
36
  GPIO_Init(SPIx_MISO_GPIO_PORT, &GPIO_InitStructure);
37
 
38
  /* SPI configuration -------------------------------------------------------*/
39
  SPI_I2S_DeInit(SPIx);
40
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
41
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
42
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
43
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
44
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
45
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
46
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
47
  SPI_InitStructure.SPI_CRCPolynomial = 7;
48
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
49
  
50
  SPI_Init(SPIx, &SPI_InitStructure);
51
   
52
  /* Configure the Priority Group to 1 bit */                
53
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
54
  /* Configure the SPI interrupt priority */
55
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
56
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
57
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
58
  NVIC_InitStructure.NVIC_IRQChannel = SPI_DMA_RX_IRQn;
59
  NVIC_Init(&NVIC_InitStructure);
60
  
61
  SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);
62
   
63
  /* The Data transfer is performed in the SPI interrupt routine */
64
  /* Enable the SPI peripheral */
65
  SPI_Cmd(SPIx, ENABLE);
66
}
67

68
void DMA_Config(void)
69
{
70
  /* Dma config */
71
  RCC_AHB1PeriphClockCmd(SPI_DMAx_CLK, ENABLE);
72
  
73
  /* DMA RX */
74
  DMA_StructInit(&DMA_InitStructure);
75
  DMA_DeInit(SPI_RX_DMA_STREAM);
76
  DMA_InitStructure.DMA_PeripheralBaseAddr = SPI_DR_ADDRESS;
77
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
78
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
79
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
80
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
81
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
82
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
83
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
84
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
85
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
86
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
87
  DMA_InitStructure.DMA_Channel = SPI_RX_DMA_CHANNEL;
88
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;  
89
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SPI_MASTER_Buffer_Rx;
90
  DMA_InitStructure.DMA_BufferSize = 64;
91
  DMA_Init(SPI_RX_DMA_STREAM, &DMA_InitStructure);     
92

93
  DMA_Cmd(SPI_RX_DMA_STREAM, ENABLE);  
94
  
95
  /* DMA TX */
96
  DMA_StructInit(&DMA_InitStructure);
97
  DMA_DeInit(SPI_TX_DMA_STREAM);
98
  DMA_InitStructure.DMA_PeripheralBaseAddr = SPI_DR_ADDRESS;
99
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
100
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
101
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
102
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
103
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
104
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
105
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
106
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
107
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
108
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
109
  DMA_InitStructure.DMA_Channel = SPI_TX_DMA_CHANNEL;
110
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;  
111
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SPI_MASTER_EmptyBuffer_Tx;
112
  DMA_InitStructure.DMA_BufferSize = 2;
113
  DMA_Init(SPI_TX_DMA_STREAM, &DMA_InitStructure);      
114

115
  DMA_Cmd(SPI_TX_DMA_STREAM, ENABLE); 
116
  
117
DMA_ITConfig(SPI_RX_DMA_STREAM, DMA_IT_TC | DMA_IT_TE | DMA_IT_FE , ENABLE);
118
}
Gast #2740593
Lesenswert?

Wenn der TxDma Puffer 64*16Bit groß ist und in der Applikation wird 
derte den TxDma gestartet, dann wird der Inhalt des gesamten Puffers 
gesendet.
1
      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, DISABLE);
2
      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, DISABLE);
3
      DMA_Config();
4
      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);
5
      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);

Was kann man da tun, damit immer nur die nächsten 2*16Bit gesendet 
werden ?
Gast #2740615
Lesenswert?

Die Kommunikation läuft folgendermaßen ab:

STM32 sendet 2 Spi Frames an den Fpga. Fpga löst daraufhin auf dem STm32 
einen externen Interrupt aus. In dem externen Interrupt Handler sende 
ich nochmals 2 Spi Frames allerdings mit den Dateninhalten NULL. Der 
FPGA sendet dann seine Daten entweder 2 Spi Frames oder 64 Spi Frames 
zum STM32.
Gast #2740681
Lesenswert?

Hi,

der STM ist der Master, d.h. er generiert den Takt für die 
Datenübertragung (es wird dabei immer gleichzeitig wird gesendet und 
empfangen). Das tut er nur solange er Daten sendet. D.h. wenn du 128 
Byte empfangen willst musst du auch 128 Byte hinschicken (können ja 
lauter 0'er sein o.ä.).

Viele Grüße,
Narfie
Gast #2741487
Lesenswert?

Guten Morgen,

vielen Dank für die Beiträge. Mir geht es eigentlich nur darum, wie kann 
man byteweise oder wordweise die Daten im Tx Dma Puffer versenden ? Gibt 
es hier eine Inkrementfunktion, die das bewältigen kann ? Wenn ich dich 
den Tx Dma aktiviere, swerden alle Daten im Tx Puffer auf einmal 
versendet. Dies möchte ich aber nicht.

Beispiel - TxPuffer 8 Byte:

Initialisierung:
TxPuffer[0] = 0;
TxPuffer[1] = 0;
TxPuffer[2] = 0;
TxPuffer[3] = 0;
TxPuffer[4] = 0;
TxPuffer[5] = 0;
TxPuffer[6] = 0;
TxPuffer[7] = 0;

--> TxPuffer[0] und TxPuffer[1] senden
Nächste Aktion:
--> TxPuffer[2] und TxPuffer[3] senden
usw.
Gast #2741632
Lesenswert?

Ich habe jetzt mal für das Senden und Empfangen jeweils 2*16Bit 
angelegt.
Sobald der external Interrupt ausgelöst wird, werden zum erstenmal die 
2*16Bit gesendet. Das funktioniert hier nur einmal. Was müsste ich genau 
tun, damit ich nochmals 2*16Bit mit dem Tx Dma senden kann ?

[c]

void main(void)
{
...
...
  while (1)
  {
    if (STM_EVAL_PBGetState(BUTTON_WAKEUP) != 0)
    {
      while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE)==RESET);
      SPI_I2S_SendData(SPIx, Command[0]);

      __no_operation();
      __no_operation();
      __no_operation();
      __no_operation();
      __no_operation();

      while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE)==RESET);
      SPI_I2S_SendData(SPIx, Command[1]);

      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, DISABLE);
      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, DISABLE);
      DMA_Config();
      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);
    }
  }
}

void SPI_DMA_RX_IRQHandler(void)
{
  if (SET == DMA_GetITStatus(SPI_RX_DMA_STREAM, SPI_RX_DMA_FLAG_TCIF))
  {
    /* clear int pending bit */
    DMA_ClearITPendingBit(SPI_RX_DMA_STREAM, SPI_RX_DMA_FLAG_TCIF);

    RxBuffer[RxCounter] = SPI_MASTER_Buffer_Rx[0];
    RxCounter = RxCounter + 1;
    RxBuffer[RxCounter] = SPI_MASTER_Buffer_Rx[1];
    RxCounter = RxCounter + 1;

    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, DISABLE);
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, DISABLE);
    DMA_Config();
  }
}

void EXTI15_10_IRQHandler(void)
{
  if(EXTI_GetITStatus(EXTI_Line15) != RESET)
  {
      /* Clear the EXTI line 15 pending bit */
      EXTI_ClearITPendingBit(EXTI_Line15);

      SPIx->DR;

      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);
      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
  }
}
Gast #2741716
Lesenswert?

Nachdem Du die Kapitel "13 Direct memory access controller (DMA)"
und "25 Serialperipheral interface (SPI)" des
ST Dokuments "RM0008 Reference manual", Doc ID 13902
gelesen hast, sollten alle Deine Fragen beantwortet sein.
Gast #2742270
Lesenswert?

Meine Applikation läuft nun fast so wie ich es möchte. Ich habe für den 
Tx Dma und auch für den Rx Dma jeweils 2*16bit reserviert. Nun habe ich 
noch ein kleines Problem. Nachdem die ersten 2*16bit gesendet werden, 
steht im RxPuffer vom Rx Dma NULL drin. Diesen Inhalt möchte ich 
verwerfen. Welche Funktion gibt es hierfür ? Ohne Dma müsste ich diese 
Codezeile einfügen SPIx->DR bevor ich wieder 2*16bit Daten sende.
Gast #2742958
Lesenswert?

Im folgenden habe ich mal einen Ausschnitt aus meiner Apllikation 
angehängt. Das Problem besteht nach wie vor.

[c]
while(1)
{
    if (STM_EVAL_PBGetState(BUTTON_WAKEUP) != 0)
    {

      SPI_MASTER_Buffer_Tx[0] = Command[0];
      SPI_MASTER_Buffer_Tx[1] = Command[1];

      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, DISABLE);
      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, DISABLE);
      DMA_Config();
      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
    }
}

/* External Interrupt Handler - Request ausgelöst von Slave */
void EXTI15_10_IRQHandler(void)
{
  if(EXTI_GetITStatus(EXTI_Line15) != RESET)
  {
    /* Clear the EXTI line 15 pending bit */
    EXTI_ClearITPendingBit(EXTI_Line15);

    /* dummy read - Clear flag RXNE */
    SPI2->DR; // dummy = SPI_I2S_ReceiveData( SPI2 );

    SPI_MASTER_Buffer_Tx[0] = 0;
    SPI_MASTER_Buffer_Tx[1] = 0;

    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, DISABLE);
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, DISABLE);
    DMA_Config();
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
  }
}
Gast #2742990
Lesenswert?

Hier nochmal mit richtiger Code Formatierung:
1
while(1)
2
{
3
    if (STM_EVAL_PBGetState(BUTTON_WAKEUP) != 0)
4
    {
5

6
      SPI_MASTER_Buffer_Tx[0] = Command[0];
7
      SPI_MASTER_Buffer_Tx[1] = Command[1];
8

9
      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, DISABLE);
10
      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, DISABLE);
11
      DMA_Config();
12
      SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
13
    }
14
}
15

16
/* External Interrupt Handler - Request ausgelöst von Slave */
17
void EXTI15_10_IRQHandler(void)
18
{
19
  if(EXTI_GetITStatus(EXTI_Line15) != RESET)
20
  {
21
    /* Clear the EXTI line 15 pending bit */
22
    EXTI_ClearITPendingBit(EXTI_Line15);
23

24
    /* dummy read - Clear flag RXNE */
25
    SPI2->DR; // dummy = SPI_I2S_ReceiveData( SPI2 );
26

27
    SPI_MASTER_Buffer_Tx[0] = 0;
28
    SPI_MASTER_Buffer_Tx[1] = 0;
29

30
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, DISABLE);
31
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, DISABLE);
32
    DMA_Config();
33
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);
34
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
35
  }
36
}
Gast #2748170
Lesenswert?

Wenn ich ein neues Kommando sende, bestehend aus 2x16Bit, wie kann man 
das erste empfangene 16Bit Word ignorieren ? Es sollte nachdem das 
Kommando gesendet wurde im SPI_MASTER_Buffer_Rx[0] nichts gespeichert 
werden.
Gast #2748335
Lesenswert?

>Es sollte nachdem das
>Kommando gesendet wurde im SPI_MASTER_Buffer_Rx[0] nichts gespeichert
>werden.

Was ist das Problem, ab &SPI_MASTER_Buffer_Rx[2] zu kopieren?

Wenn Dir das zu einfach ist...

Du könntest aber auch Deinen SPI_MASTER_Buffer_Rx
so in's RAM legen, dass die ersten 2 Bytes in's Leere
geschrieben werden.
#2776020
Lesenswert?

Ich versuche aktell etwas ähnliches zu realisieren...

Das Einlesend der SPI mittels DMA funktioniert wunderbar mit 8-Bit. Wenn 
ich die Paketgrösse jedoch auf 16 Bit erhöhe, so sind die oberen 8 bit 
immer 0. Mir scheint da ist noch ein kleines Problem...

Im RM0008 steht unter 25.2 SPI and I2S main features
1-byte transmission and reception buffer with DMA capability: Tx and Rx 
requests

Kann es sein dass es gar nicht möglich ist 16 Bit zu übertragen?
1
uint16_t SPI_SLAVE_Buffer_Rx[BufferSize_RX];   // DMA-Buffer
2
#define BufferSize_RX       64
3

4
void InitSPI2(void)
5
{
6
  SPI_InitTypeDef  SPI_InitStructure;
7
  DMA_InitTypeDef  DMA_InitStructure;
8
  
9
   /* SPI_SLAVE_Rx_DMA_Channel configuration ------------------*/
10
  DMA_DeInit(SPI_SLAVE_Rx_DMA_Channel);
11
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SPI2->DR);
12
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI_SLAVE_Buffer_Rx;
13
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
14
  DMA_InitStructure.DMA_BufferSize = BufferSize_RX;
15
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
16
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
17
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_HalfWord;
18
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
19
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
20
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
21
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
22
  DMA_Init(SPI_SLAVE_Rx_DMA_Channel, &DMA_InitStructure);
23
  
24
  /* Enable SPI_SLAVE_Rx_DMA_Channel Transfer Complete & Half interrupt */
25
  DMA_ITConfig(SPI_SLAVE_Rx_DMA_Channel, DMA_IT_TC, ENABLE);
26
  DMA_ITConfig(SPI_SLAVE_Rx_DMA_Channel,DMA_IT_HT, ENABLE);  
27
  
28
  /* SPI_SLAVE configuration ------------------------------------------------------*/
29
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
30
  SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
31
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
32
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
33
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
34
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
35
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
36
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB;
37
  SPI_InitStructure.SPI_CRCPolynomial = 7;
38
  SPI_Init(SPI2, &SPI_InitStructure);
39
    
40
  SPI_Cmd(SPI2, ENABLE);                              // Enable SPI 2
41
  DMA_Cmd(SPI_SLAVE_Rx_DMA_Channel, ENABLE);          // Enable DMA1 Channel4
42
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren