Forum: Mikrocontroller und Digitale Elektronik STM32F103 - SPI mit DMA liefert nur Nullen


von Dirk K. (dekoepi)


Lesenswert?

Moin Moin zusammen,

ich stehe mal wieder vor einer Hürde, bei der mir die 
STM32F103-Reference und StdPeriph-Bibliothek nicht weiterhelfen. Ich 
versuche mich an DMA, um damit Datenpuffer in SPI-RAM zu schreiben. Laut 
Doku, Beispielcode im Netz und so weiter ein Fingerschnipp. Funktioniert 
augenscheinlich erst mal auch ganz gut und fix. Nur bekomme ich als 
Daten immer nur "0" anstatt des gesendeten Puffer-Inhalts zurück. 
Weitere Info: Wenn ich auf Transfer Complete warte, bleibt der µC 
stehen, das kommt nicht.

Hier mal zusammengekürzt mein Code, der nicht richtig tutet:
1
#define buffer_size 256
2
volatile uint8_t buffer1_rx[buffer_size];
3
volatile uint8_t buffer1_tx[buffer_size];
4
5
// DMA related names
6
#define DMA_Channel_SPI1_RX    DMA1_Channel2  // SPI1
7
#define DMA_Channel_SPI1_TX    DMA1_Channel3  // SPI1
8
#define DMA_FLAG_SPI1_TC_RX    DMA1_FLAG_TC2
9
#define DMA_FLAG_SPI1_TC_TX    DMA1_FLAG_TC3
10
11
void main (void) {
12
  SystemInit();
13
14
  // Enable the needed peripheral clocks
15
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
16
      RCC_APB2Periph_USART1 | RCC_APB2Periph_SPI1 |
17
      RCC_APB2Periph_AFIO, ENABLE);
18
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
19
20
  // Not using JTAG or SW debug. Disable them so that PA13-15, PB3-4 can be remapped.
21
  GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable , ENABLE);
22
23
24
// ----------------------------------------------------------------------------
25
26
// SPI setup
27
  // SCK and MISO are Output, Alternate Function floating (PP) as Master
28
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
29
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
30
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
31
  GPIO_Init(GPIOA, &GPIO_InitStructure);
32
33
  // MISO -> input (as Master, input floating!)
34
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
35
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
36
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
37
  GPIO_Init(GPIOA, &GPIO_InitStructure);
38
39
  // (N)SS needs to be Output; for hardware SPI to work set it HIGH and AF_PP
40
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
41
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
42
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
43
  GPIO_Init(GPIOA, &GPIO_InitStructure);
44
45
  GPIOA->BSRR |= GPIO_Pin_4;  // GPIOA Pin 4 (NSS) is HIGH (for hardware SPI support)
46
47
  // Mux Chip Select - deselect all (all pins high)
48
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;  // bitmask 0x07
49
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
50
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
51
  GPIO_Init(GPIOA, &GPIO_InitStructure);
52
53
  chip_deselect();
54
55
  // SPI initial setup - 23LC1024 SPI mode 0
56
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
57
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;  // SPI_Mode 0 / High=1
58
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // SPI_Mode 0 / 2EDGE=1
59
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // set the NSS management to internal  | SPI_NSSInternalSoft_Set and pull internal NSS high
60
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
61
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
62
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
63
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
64
  SPI_Init(SPI1, &SPI_InitStructure);
65
66
  // tell SPI1 to use DMA
67
  SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx, ENABLE);
68
69
  SPI_Cmd(SPI1, ENABLE);
70
71
  // for SPI transfers, get the function address for pointer arithmetics
72
  get_spixbase();
73
74
// ----------------------------------------------------------------------------
75
76
  // DMA RX SPI1 channel
77
  DMA_DeInit(DMA_Channel_SPI1_RX);
78
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
79
//  DMA_InitStructure.DMA_PeripheralBaseAddr = spixbase;
80
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer1_rx[0];
81
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
82
  DMA_InitStructure.DMA_BufferSize = buffer_size;
83
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
84
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
85
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
86
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
87
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
88
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
89
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
90
  DMA_Init(DMA_Channel_SPI1_RX, &DMA_InitStructure);
91
92
  // DMA TX SPI1 channel
93
  DMA_DeInit(DMA_Channel_SPI1_TX);
94
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
95
//  DMA_InitStructure.DMA_PeripheralBaseAddr = spixbase;
96
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer1_tx[0];
97
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
98
  DMA_InitStructure.DMA_BufferSize = buffer_size;
99
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
100
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
101
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
102
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
103
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
104
  DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
105
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
106
  DMA_Init(DMA_Channel_SPI1_TX, &DMA_InitStructure);
107
108
  // Interrupt at DMA transfer complete
109
//  DMA_ITConfig(DMA_Channel_SPI1_TX, DMA_IT_TC, ENABLE);
110
  DMA_ITConfig(DMA_Channel_SPI1_RX, DMA_IT_TC, ENABLE);
111
112
// ----------------------------------------------------------------------------
113
114
115
  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
116
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
117
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
118
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
119
  NVIC_Init(&NVIC_InitStructure);
120
121
122
// ----------------------------------------------------------------------------
123
124
// Check the RAM first
125
  for (int i=0; i<buffer_size; i++) buffer1_tx[i]=i;
126
127
  for (int bank=0; bank<5; bank++) {
128
    bool is_ok=true;
129
    chip_select(bank);
130
131
    // Send WRITE MODE register command to chip
132
    spi1_tx(WRMR);
133
    spi1_tx(SEQUENTIAL_MODE);
134
    chip_deselect();
135
    chip_select(bank);
136
137
    // Send WRITE command to chip
138
    spi1_tx(WRITE);
139
140
    // as default sequential mode, thus address has to be sent only at start
141
    address = 0;
142
    spi1_tx((address>>16));   //send even MoreMSByte address first
143
    spi1_tx((address>>8));   //send MSByte address first
144
    spi1_tx((address));      //send LSByte address
145
146
    for (int i=0; i < (ram_size/buffer_size); i++) {
147
      spi1_dma_tx();
148
    }
149
    chip_deselect();
150
151
    chip_select(bank);
152
153
    // Send WRITE MODE register command to chip
154
    spi1_tx(WRMR);
155
    spi1_tx(SEQUENTIAL_MODE);
156
    chip_deselect();
157
    chip_select(bank);
158
159
    // Send READ command to chip
160
    spi1_tx(READ);
161
162
    // as default sequential mode, thus address has to be sent only at start
163
    address = 0;
164
    spi1_tx((address>>16));   //send even MoreMSByte address first
165
    spi1_tx((address>>8));   //send MSByte address first
166
    spi1_tx((address));      //send LSByte address
167
168
    for (int i=0; i < (ram_size/buffer_size); i++) {
169
      // if( (spi1_tx(0xFF)) != (i%0xFF)) is_ok=false;
170
      spi1_dma_tx();
171
      for (int i=0; i<buffer_size; i++) {
172
        if (buffer1_rx[i] != buffer1_tx[i]) is_ok=false;
173
      }
174
    }
175
    sprintf(text,"RAM chip %i is %s\n", bank, (is_ok? "OK." : "not OK!"));
176
    uart_tx(text);
177
    for (int i=0; i<10; i++) {
178
      sprintf(text, "[%i] %i  ", i, buffer1_rx[i]);
179
      uart_tx(text);
180
    }
181
    chip_deselect();
182
  }
183
}
184
185
void spi1_dma_tx(void) {
186
187
  // Wait until DMA transfer complete
188
//  while (!((DMA1->ISR) & DMA_FLAG_SPI1_TC_RX )) ;
189
190
  DMA_Cmd(DMA_Channel_SPI1_TX, DISABLE);
191
  DMA_Cmd(DMA_Channel_SPI1_RX, DISABLE);
192
  DMA_SetCurrDataCounter(DMA_Channel_SPI1_TX, buffer_size);
193
  DMA_SetCurrDataCounter(DMA_Channel_SPI1_RX, buffer_size);
194
  DMA_Cmd(DMA_Channel_SPI1_TX, ENABLE);
195
  DMA_Cmd(DMA_Channel_SPI1_RX, ENABLE);
196
}
197
198
// taken from ST library for 8bit SPI writes
199
void get_spixbase(void)
200
{
201
    spixbase = (uint32_t)SPI1;
202
    spixbase += 0x0C;
203
}


Ausgabe ...
1
Starting program.
2
RAM chip 0 is not OK!
3
[0] 0  [1] 0  [2] 0  [3] 0  [4] 0  [5] 0  [6] 0  [7] 0  [8] 0  [9] 0  RAM chip 1 is not OK!
4
[0] 0  [1] 0  [2] 0  [3] 0  [4] 0  [5] 0  [6] 0  [7] 0  [8] 0  [9] 0  RAM chip 2 is not OK!
5
[0] 0  [1] 0  [2] 0  [3] 0  [4] 0  [5] 0  [6] 0  [7] 0  [8] 0  [9] 0  RAM chip 3 is not OK!
6
[0] 0  [1] 0  [2] 0  [3] 0  [4] 0  [5] 0  [6] 0  [7] 0  [8] 0  [9] 0  RAM chip 4 is not OK!
7
[0] 0  [1] 0  [2] 0  [3] 0  [4] 0  [5] 0  [6] 0  [7] 0  [8] 0  [9] 0

Sieht jemand den offensichtlichen Fehler? Oder hat einen Tipp? Ich würde 
sagen, das trifft RM0008 ganz gut.

Danke schön schon mal!

von Simon K. (simon) Benutzerseite


Lesenswert?

Dirk K. schrieb:
> DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer1_rx[0];

Autsch... Du musst die Adresse des Arrays in das Register schreiben und 
nicht den Inhalt des Arrays an Index 0...

von Dirk K. (dekoepi)


Lesenswert?

Hallo Simon,

aua, Danke sehr :)

Wir kommen der Sache näher. Ich habe jetzt wieder einen Pointer auf die 
Puffer verwendet - beim Herumprobieren ist dieser Fehler reingekommen 
und stehengeblieben.
1
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&(buffer1_rx);
respektive
1
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&(buffer1_tx);

Zudem habe ich Interrupts und Bearbeitungsroutinen hinzugefügt. Mit 
letzterem läuft der Code dann sogar. (Interessanterweise ohne 
NVIC-Initialisierung für den tx-Interrupt.)
1
  // Interrupt at DMA transfer complete
2
  DMA_ITConfig(DMA_Channel_SPI1_TX, DMA_IT_TC, ENABLE);
3
  DMA_ITConfig(DMA_Channel_SPI1_RX, DMA_IT_TC, ENABLE);
1
void DMA1_Channel2_IRQHandler(void){
2
  DMA_ClearFlag(DMA1_FLAG_TC2);
3
}
4
5
void DMA1_Channel3_IRQHandler(void){
6
  DMA_ClearFlag(DMA1_FLAG_TC3);
7
}

Ausgabe:
1
Starting program.
2
RAM chip 0 is not OK!
3
[0] 133  [1] 245  [2] 129  [3] 242  [4] 135  [5] 245  [6] 134  [7] 250  [8] 128  [9] 235  RAM chip 1 is not OK!
4
[0] 111  [1] 246  [2] 123  [3] 239  [4] 106  [5] 241  [6] 121  [7] 243  [8] 135  [9] 244  RAM chip 2 is not OK!
5
[0] 243  [1] 126  [2] 239  [3] 96  [4] 227  [5] 160  [6] 235  [7] 146  [8] 252  [9] 125  RAM chip 3 is not OK!
6
[0] 127  [1] 250  [2] 113  [3] 244  [4] 134  [5] 243  [6] 135  [7] 250  [8] 121  [9] 249  RAM chip 4 is not OK!
7
[0] 184  [1] 199  [2] 179  [3] 222  [4] 139  [5] 237  [6] 117  [7] 236  [8] 119  [9] 218

Das Ergebnis bleibt gleich/vrgleichbar, wenn ich die "spixbase" anstatt 
SPI1->DR für 8bit-Transfers verwende (beim F103 nötig.) Das scheinen die 
Daten zu sein, die ich im vorherigen Durchgang am Ende tatsächlich per 
Polling hineingeschrieben habe; das DMA-tx (auch mit NVIC-Init) scheint 
nicht zu klappen?

Immerhin keine Nullen :) Aber leider noch nicht richtig ...

von leluno (Gast)


Lesenswert?

>DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
ist M2M richtig? Beim LPC wäre es M2P

von Dirk K. (dekoepi)


Lesenswert?

Ja, bei STm32 geht das so. M2M_Disable sagt implizit M2P/P2M aus; die 
Richtung bestimmt man dann in der init-structure:
1
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
2
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

Auch der Gegentest bestätigt das - kommt nur 255 zurück, und bleibt nach 
einem Speicherchip hängen.

von (prx) A. K. (prx)


Lesenswert?

Dirk K. schrieb:
> DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&(buffer1_rx);

Auch nicht viel richtiger. Entweder
  (uint32_t)&buffer1_rx[0];
oder
  (uint32_t)buffer1_rx;
aber nicht
  (uint32_t)&buffer1_rx;

von Dirk K. (dekoepi)


Lesenswert?

Ok, selbst, wenn ich das korrigiere - bekomme ich Quatsch zurück.
Grade wieder 0 oder 255 (wenn ich "spixbase" als Ziel verwende, sonst 
das, was ich vorher per Polling reingeschrieben habe); das ist zum 
Mäusemelken.

von Dirk K. (dekoepi)


Lesenswert?

Ich komme ein wenig voran, aber so wirklich erbaulich ist das noch 
nicht. Die RAM-Test-Routine ist jetzt reines DMA, ohne Polling. Die 
Ergebnisse lassen vermuten, dass da irgendwas geschrieben wird - 
allerdings halt quatsch:
1
// Reset SPI Interface
2
    SPI_I2S_DeInit(SPI1);
3
 
4
    // SPI initial setup - 23LC1024 SPI mode 0
5
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
6
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;  // SPI_Mode 0 / High=1
7
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // SPI_Mode 0 / 2EDGE=1
8
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // set the NSS management to internal  | SPI_NSSInternalSoft_Set and pull internal NSS high
9
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
10
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
11
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
12
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
13
    SPI_Init(SPI1, &SPI_InitStructure);
14
 
15
    // for SPI transfers, get the function address for pointer arithmetics
16
    get_spixbase();
17
 
18
 
19
// ----------------------------------------------------------------------------
20
 
21
    // DMA RX SPI1 channel
22
    DMA_DeInit(DMA_Channel_SPI1_RX);
23
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
24
//  DMA_InitStructure.DMA_PeripheralBaseAddr = spixbase;
25
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer1_rx;
26
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
27
    DMA_InitStructure.DMA_BufferSize = buffer_size;
28
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
29
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
30
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
31
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
32
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
33
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
34
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
35
    DMA_Init(DMA_Channel_SPI1_RX, &DMA_InitStructure);
36
 
37
//  DMA_Cmd(DMA_Channel_SPI1_RX, ENABLE);
38
 
39
    // DMA TX SPI1 channel
40
    DMA_DeInit(DMA_Channel_SPI1_TX);
41
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
42
//  DMA_InitStructure.DMA_PeripheralBaseAddr = spixbase;
43
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer1_tx;
44
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
45
    DMA_InitStructure.DMA_BufferSize = buffer_size;
46
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
47
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
48
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
49
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
50
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
51
    DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
52
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
53
    DMA_Init(DMA_Channel_SPI1_TX, &DMA_InitStructure);
54
 
55
//  DMA_Cmd(DMA_Channel_SPI1_TX, ENABLE);
56
 
57
    SPI_Cmd(SPI1, ENABLE);
58
 
59
    // tell SPI1 to use DMA
60
    SPI_I2S_DMACmd(SPI1,  SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
61
 
62
    // Interrupt at DMA transfer complete
63
    DMA_ITConfig(DMA_Channel_SPI1_TX, DMA_IT_TC, ENABLE);
64
    DMA_ITConfig(DMA_Channel_SPI1_RX, DMA_IT_TC, ENABLE);

1
void spi1_dma_tx(int size) {
2
    while (!(SPI1->SR & SPI_SR_TXE)); // Wait for bus free
3
    while (SPI1->SR & SPI_SR_BSY);
4
    // tell SPI1 not to use DMA
5
    SPI_I2S_DMACmd(SPI1,  SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, DISABLE);
6
    SPI_Cmd(SPI1, DISABLE);
7
    DMA_Cmd(DMA_Channel_SPI1_TX, DISABLE);
8
    DMA_Cmd(DMA_Channel_SPI1_RX, DISABLE);
9
 
10
    DMA_SetCurrDataCounter(DMA_Channel_SPI1_RX, size);
11
    DMA_SetCurrDataCounter(DMA_Channel_SPI1_TX, size);
12
//  DMA_Channel_SPI1_TX->CMAR=(uint32_t)buffer1_tx;
13
 
14
    DMA_Cmd(DMA_Channel_SPI1_RX, ENABLE);
15
    DMA_Cmd(DMA_Channel_SPI1_TX, ENABLE);
16
    // tell SPI1 to use DMA
17
    SPI_I2S_DMACmd(SPI1,  SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
18
    SPI_Cmd(SPI1, ENABLE);
19
}
20
21
void DMA1_Channel2_IRQHandler(void){
22
    if(DMA_GetITStatus(DMA1_IT_TC2))
23
        DMA_ClearFlag(DMA1_FLAG_TC2);
24
//  while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) );  // wait until receive complete
25
}
26
 
27
void DMA1_Channel3_IRQHandler(void){
28
    if(DMA_GetITStatus(DMA1_IT_TC3))
29
        DMA_ClearFlag(DMA1_FLAG_TC3);
30
//  while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) );  // wait until receive complete
31
}

Der Speichertest dann so:
1
  uart_tx("Starting program.\n");
2
3
// Check the RAM first
4
5
  for (int bank=0; bank<5; bank++) {
6
    bool is_ok=true;
7
    chip_select(bank);
8
9
    // Send WRITE MODE register command to chip
10
    buffer1_tx[0]=WRMR;
11
    buffer1_tx[1]=SEQUENTIAL_MODE;
12
    spi1_dma_tx(2);
13
    chip_deselect();
14
    chip_select(bank);
15
16
    // Send WRITE command to chip
17
    buffer1_tx[0]=WRITE;
18
19
    // as default sequential mode, thus address has to be sent only at start
20
    buffer1_tx[1]=0;   //send even MoreMSByte address first
21
    buffer1_tx[2]=0;   //send MSByte address first
22
    buffer1_tx[3]=0;   //send LSByte address
23
    spi1_dma_tx(4);
24
25
    for (int i=0; i<buffer_size; i++) {
26
      buffer1_tx[i]=i;
27
      buffer1_rx[i]=0;
28
    }
29
30
    for (int i=0; i < (ram_size/buffer_size); i++) {
31
      spi1_dma_tx(buffer_size);
32
    }
33
    chip_deselect();
34
35
    chip_select(bank);
36
37
    // Send WRITE MODE register command to chip
38
    buffer1_tx[0]=WRMR;
39
    buffer1_tx[1]=SEQUENTIAL_MODE;
40
    spi1_dma_tx(2);
41
    chip_deselect();
42
    chip_select(bank);
43
44
    // Send READ command to chip
45
    buffer1_tx[0]=READ;
46
47
    // as default sequential mode, thus address has to be sent only at start
48
    buffer1_tx[1]=0;   //send even MoreMSByte address first
49
    buffer1_tx[2]=0;   //send MSByte address first
50
    buffer1_tx[3]=0;   //send LSByte address
51
    spi1_dma_tx(4);
52
53
    for (int i=0; i<buffer_size; i++) {
54
      buffer1_tx[i]=0xFF;
55
      buffer1_rx[i]=0;
56
    }
57
58
    for (int i=0; i < (ram_size/buffer_size); i++) {
59
      spi1_dma_tx(buffer_size);
60
      for (int i=0; i<buffer_size; i++) {
61
        if (buffer1_rx[i] != i) is_ok=false;
62
      }
63
    }
64
    sprintf(text,"RAM chip %i is %s ->  ", bank, (is_ok? "OK." : "not OK!"));
65
    uart_tx(text);
66
    for (int i=0; i<10; i++) {
67
      sprintf(text, "[%i] %i  ", i, buffer1_rx[i]);
68
      uart_tx(text);
69
    }
70
    uart_tx("\n");
71
    chip_deselect();
72
  }



Das Ergebnis sieht dann so aus:
1
Starting program.
2
RAM chip 0 is not OK! ->  [0] 254  [1] 126  [2] 255  [3] 127  [4] 128  [5] 0  [6] 129  [7] 1  [8] 130  [9] 2 
3
RAM chip 1 is not OK! ->  [0] 254  [1] 126  [2] 255  [3] 127  [4] 128  [5] 0  [6] 129  [7] 1  [8] 130  [9] 2 
4
RAM chip 2 is not OK! ->  [0] 254  [1] 126  [2] 255  [3] 127  [4] 128  [5] 0  [6] 129  [7] 1  [8] 130  [9] 2 
5
RAM chip 3 is not OK! ->  [0] 254  [1] 126  [2] 255  [3] 127  [4] 128  [5] 0  [6] 129  [7] 1  [8] 130  [9] 2 
6
RAM chip 4 is not OK! ->  [0] 123  [1] 234  [2] 109  [3] 228  [4] 143  [5] 232  [6] 73  [7] 238  [8] 108  [9] 216

Was nicht ganz der Reihe 0 1 2 3 4 5 6 7 8 9 entspricht, was dort aber 
ein wenig versteckt vorkommt. Und nichts mit den Werten gemein hat, die 
ich per Polling im vorherigen Durchgang reingeschrieben habe. Ich komme 
nicht drauf, wieso das nicht will :(

von holger (Gast)


Lesenswert?

>Ich komme nicht drauf, wieso das nicht will :(

Vieleicht solltest du mal darauf warten das dein
DMA Transfer auch beendet ist.

von Dirk K. (dekoepi)


Lesenswert?

Dank für den Tipp Holger,

ich dachte, ich hätte das mit dem SPI-BSY-while-loop abgegolten. Habe 
eine Stützvariable "transfer" eingeführt, die bei den 
DMA-Transfer-Complete-Interrupts zurückgesetzt und beim Start gesetzt 
wird.
1
void spi1_dma_tx(int size) {
2
  while (transfer);  // DMA transfer ready
3
  while (!(SPI1->SR & SPI_SR_TXE)); // Wait for bus free
4
  while (SPI1->SR & SPI_SR_BSY);
5
  // tell SPI1 not to use DMA
6
  SPI_I2S_DMACmd(SPI1,  SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, DISABLE);
7
  SPI_Cmd(SPI1, DISABLE);
8
  DMA_Cmd(DMA_Channel_SPI1_TX, DISABLE);
9
  DMA_Cmd(DMA_Channel_SPI1_RX, DISABLE);
10
11
  DMA_SetCurrDataCounter(DMA_Channel_SPI1_RX, size);
12
  DMA_SetCurrDataCounter(DMA_Channel_SPI1_TX, size);
13
14
  DMA_Cmd(DMA_Channel_SPI1_RX, ENABLE);
15
  DMA_Cmd(DMA_Channel_SPI1_TX, ENABLE);
16
  // tell SPI1 to use DMA
17
  SPI_I2S_DMACmd(SPI1,  SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
18
  SPI_Cmd(SPI1, ENABLE);
19
  transfer=true;
20
}
21
22
void DMA1_Channel2_IRQHandler(void){
23
  if(DMA_GetITStatus(DMA1_IT_TC2)) {
24
    DMA_ClearFlag(DMA1_FLAG_TC2);
25
    transfer=false;
26
  }
27
//  while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) );  // wait until receive complete
28
}
29
30
void DMA1_Channel3_IRQHandler(void){
31
  if(DMA_GetITStatus(DMA1_IT_TC3)) {
32
    DMA_ClearFlag(DMA1_FLAG_TC3);
33
    transfer=false;
34
  }
35
//  while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) );  // wait until receive complete
36
}

Ausgabe:
1
Starting program.
2
RAM chip 0 is not OK! ->  [0] 254  [1] 126  [2] 255  [3] 127  [4] 128  [5] 0  [6] 129  [7] 1  [8] 130  [9] 2  
3
RAM chip 1 is not OK! ->  [0] 254  [1] 126  [2] 255  [3] 127  [4] 128  [5] 0  [6] 129  [7] 1  [8] 130  [9] 2  
4
RAM chip 2 is not OK! ->  [0] 215  [1] 223  [2] 231  [3] 239  [4] 247  [5] 248  [6] 0  [7] 8  [8] 16  [9] 24  
5
RAM chip 3 is not OK! ->  [0] 222  [1] 238  [2] 255  [3] 15  [4] 31  [5] 47  [6] 63  [7] 79  [8] 95  [9] 111  
6
RAM chip 4 is not OK! ->  [0] 67  [1] 138  [2] 128  [3] 246  [4] 16  [5] 186  [6] 18  [7] 95  [8] 90  [9] 59

Das Warten auf SPI-BSY hat offenbar denselben Effekt und (zugegeben, 
unpräzise und von hinten durch die Brust ins Auge) das Ende des 
DMA-Transfers angezeigt. :-/

von holger (Gast)


Lesenswert?

>Dank für den Tipp Holger,

Hat nur nicht viel geholfen und du nichts verstanden.

Sieh dir z.B. mal diese Stelle an:

    spi1_dma_tx(2);
    chip_deselect();

Du ziehst dem SPI die Chipselect Leitung
unterm Arsch weg während der DMA gerade erst
angefangen hat zu laufen. Also viel zu früh!

von Dirk K. (dekoepi)


Lesenswert?

Ah - das habe ich wirklich nicht gesehen, Danke!

Edit: Ändert am Ergebnis aber gar nichts. Ausgabe bleibt gleich. Auch 
bei langsameren SPI-Takt. Nur schneller geht immer noch nicht, will 
nicht mit >18 MHz ;)

von Phantomix X. (phantomix)


Lesenswert?

Bitte schau dir einmal an - am besten mit Logic Analyzer, 2Kanal-oszi 
geht auch - was über den Bus geht. Damit kann man 90% solcher Probleme 
aus der Welt schaffen. Entweder es überträgt nicht richtig, dann hast du 
was in der Initialisierung falsch, oder es überträgt richtig, dann 
stimmt was mit der Abspeicherung/Auswertung nicht.

von Dirk K. (dekoepi)


Lesenswert?

Danke PhantomX, LA ist unterwegs. Spielzeug-Scope hilft leider nicht 
weiter hierbei (habe 'nen DSO Nano bis jetzt).

von Ralph (Gast)


Lesenswert?

Sieh dir mit einem Debugger mal die ganzen Puffer und Register an.
Was steht zu welchem Zeitpunkt wo.

Mir kommt es so vor als ob du noch nicht einmal weißt wo in deinem 
ganzen Datenfluß das Problem steckt.

Ohne das zu wissen ist es nur stochern im Nebel.

von Dirk K. (dekoepi)


Lesenswert?

Die Feststellung ist vollkommen korrekt, ich stochere im Nebel. Das ist 
das erste Mal, das ich mich mit DMA programmiertechnisch 
auseinandersetze. Habe mir dazu die Datenblätter und AppNotes von STM 
bis kurz vorm Umfallen zu Gemüte geführt, die Samples der STM-Library 
viele Male untersucht, aber solch einen einfachen "Standardfall" 
nirgends gefunden. Da werden in der Regel einmalig um die 20 Bytes 
irgendwo herumgeschoben, DMA via Funktionsaufruf habe ich etwa noch 
nirgends gesehen. Von daher taste ich mich langsam vor, bis es mal 
läuft.

Klappt eigentlich ganz gut, eine solche Herangehensweise - so konnte ich 
SPI, I2C und USART gut auseinanderpflücken und lernen. Nur der DMA wehrt 
sich grad mächtig. Wie schon geschrieben, Logic Analyzer ist in Kürze 
einsatzbereit, dann kann ich gucken, was tatsächlich auf dem Bus 
passiert. Ohne das geht es glaube ich nicht sinnvoll weiter.

Hatte halt auf Tipps wie von Holger gesetzt, dass ich irgendwo einen 
dummen Fehler im Ablauf übersehe (etwa wie dem DMA-Transfer die Luft 
abzuschnüren, indem ich CS frühzeitig HIGH setze).

Es sieht ja soweit schonmal fast gut aus, nur ein wenig so, als ob meine 
Transfers einen 8-Bit-Wert gerne mal eine Adresse zu weit schreiben (die 
Zahlen dort sind ja schon halb-korrekt aufsteigend).

von Grundschüler (Gast)


Lesenswert?

Dirk K. schrieb:
> 8-Bit-Wert gerne mal eine Adresse zu weit

schau dir im Handbuch mal an, wie der Datentransfer abläüft. Es werden 
immer 32 bits übertragen.
über burstsize, srcwidth,dstwidth und endian kann man einstellen, wie 
diese 32bit in src und dest aufgeteilt werden.

Nimm notfalls das LPC-Manual UM10360. Auf Seite 589 ist dazu eine 
einigermaßen verständliche Übersicht.

von Phantomix X. (phantomix)


Lesenswert?

> Es werden immer 32 bits übertragen.

Nein, es werden immer

DMA_PeripheralDataSize * DataNumber
bzw.
DMA_MemoryDataSize * DataNumber

(je nach Richtung)

übertragen.

> Nimm notfalls das LPC-Manual UM10360. Auf Seite 589 ist dazu eine
einigermaßen verständliche Übersicht.

Es geht hier um ST, nicht um NXP

von Grundschüler (Gast)


Lesenswert?

Phantomix Ximotnahp schrieb:
>> Es werden immer 32 bits übertragen.
>
> Nein, es werden immer

Mit jedem DMA-Takt werden 32 bits übertragen. Das ist mit einiger 
Sicherheit bei allen Cortex-M3-32Bittern das gleiche.

Wenn die Erklärung des Cortex-M3-DMA im ST-Manual nicht verständlich 
sein sollte, kann man "notfalls" im LPC manual nachlesen, weil dort das 
Cortex-M3 System verständlich erklärt wird.

von (prx) A. K. (prx)


Lesenswert?

Grundschüler schrieb:
> Mit jedem DMA-Takt werden 32 bits übertragen. Das ist mit einiger
> Sicherheit bei allen Cortex-M3-32Bittern das gleiche.

Der DMA Controller ist nicht Teil des Cortex M3 Core.

von Phantomix X. (phantomix)


Lesenswert?

Achso. Das war ein wenig missverständlich und klang so, als könnte man 
über den SPI-Bus mit DMA nur 32bit-Worte schieben. Das wär aber für NXP 
auch ein Armutszeugnis ;-)

A. K. schrieb:
> Der DMA Controller ist nicht Teil des Cortex M3 Core.

thx. da habe ich auch grad intern gezweifelt...

von (prx) A. K. (prx)


Lesenswert?

Grundschüler schrieb:
> Nimm notfalls das LPC-Manual UM10360. Auf Seite 589 ist dazu eine
> einigermaßen verständliche Übersicht.

Seitenangaben ändern sich schneller als Kapitel/Abschnitt/... In der 
aktuellen Version lande ich bei Seite 589 im ADC.

von Grundschüler (Gast)


Lesenswert?

A. K. schrieb:
> Seitenangaben ändern sich

31.4.1.6.2 endian behavior

von (prx) A. K. (prx)


Lesenswert?

Grundschüler schrieb:
> über burstsize, srcwidth,dstwidth und endian kann man einstellen, wie
> diese 32bit in src und dest aufgeteilt werden.

Wenn du mir jetzt noch zeigst, wo man im STM32F10x die burst size 
einstellt... Der LPC kann wohl 4 Bytes nacheinander von der Peripherie 
lesen und akkumuliert als 1 Wort ins RAM schreiben, der STM32F10x kann 
das jedoch nicht.

STM32F10x: 13.3.4  Programmable data width, data alignment and endians
LPC: 31.4.1.6.2 Endian behavior

von Dirk K. (dekoepi)


Lesenswert?

Naja, dem STM32F103 kannste auch WORD, HALFWORD oder BYTE als Größe 
mitgeben. Wenn PeriphSize != MemSize, bricht der DMA automatisch um. Von 
fix 32Bit steht da jedoch nichts. Wäre in meinem Fall nur beim Senden 
des SEQUENTIAL_MODE von Belang, die anderen Fälle sind alle 4-Byte 
(=WORD)-Aligned - selbst, wenn der SPI-Transfer als Byte läuft.

von (prx) A. K. (prx)


Lesenswert?

Dirk K. schrieb:
> Naja, dem STM32F103 kannste auch WORD, HALFWORD oder BYTE als Größe
> mitgeben. Wenn PeriphSize != MemSize, bricht der DMA automatisch um.

Wenn man beim STM32F10x aus der UART byteweise n Bytes liest und als 
Zielgrösse Worte angibt, dann kriegt man am Ziel 4*n Bytes. Ein Byte pro 
Wort. Das ist wohl selten das, was man haben will. Der LPC hingegen kann 
offenbar akkumulieren und schreibt ggf. 1 Wort mit allen 4 Bytes am 
Stück - byteweise kann er aber auch.

Der DMA Controller des LPC sieht nach der ARM PrimeCell PL080 aus:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0417a/index.html
Vielleicht hat "Grundschüler" deshalb angenommen, die seien alle gleich. 
Allerdings hat ST diese PrimeCell nicht verwendet.

von Grundschüler (Gast)


Lesenswert?

A. K. schrieb:
> der STM32F10x kann
> das jedoch nicht.

du hast sicher recht, dass das Stm-DMA etwas anders aufgebaut ist als 
bei NXP.
die Stm-Tabelle 13.3.4       Programmable data width, data alignment and 
endians

scheint der von mir angeführten LPC-Tabelle aber zumindest ähnlich zu 
sein. Es gibt dort verschiedene Modi, die zum Vertauschen der 
Byte-Reihenfolge führen, was mit dem von Dirk angesprochenen Problem in 
Verbindung stehen könnte.

von (prx) A. K. (prx)


Lesenswert?

Grundschüler schrieb:
> scheint der von mir angeführten LPC-Tabelle aber zumindest ähnlich zu
> sein.

Klar. Beides sind DMA-Controller in einem 32-Bit System mit 3 
verschiedenen Transferbreiten pro Seite. Da stellt sich diese Frage 
zwangsläufig. Nur ist der Inhalt völlig verschieden, wenn source-width 
!= dest-width.

> Es gibt dort verschiedene Modi, die zum Vertauschen der
> Byte-Reihenfolge führen

Kann ich darin nicht wiederfinden, zumal er das DMA im ersten Beitrag 
auf byte-to-byte konfiguriert hat, was zu steng bytesequentieller 
Operation auf Peripherie und Speicher führt. Eine Vertauschung durch den 
DMA-Controller scheint mir folglich ausgeschlossen zu sein. Bei 
source-width != dest-width sähe die Sache anders aus.

PS/@Dirk: Ich habe nicht alle Aspekte der Konfiguration untersucht. 
Nicht zuletzt weil ich kein Freund der StdPerLib bin.

von Dirk K. (dekoepi)



Lesenswert?

So, ich habe den LA erfolgreich in Betrieb genommen. Geht leider nur bis 
16 MHz, aber scheint zu reichen.

Von außen betrachtet sieht das eigentlich richtig aus: CLK rennt los, 
MISO/MOSI laufen richtig, hinten erkennt man schön das langsame Polling 
und die 10 Byte, die ich ganz am Schluss zurücklese.

Muss noch herausfinden, wie ich mit dem SPI-Dissector  da wieder 
menschenlesbare Daten draus mache, also ob ich 0..1..2..3.... übertrage 
da am Anfang (zweites Bild, Zoom.png).

Edit: kann irgendwie keine Bilder löschen, habe enen korrigierten 
Mitschnitt bei 9MHz SPI noch angehängt.

von (prx) A. K. (prx)


Lesenswert?

Dirk K. schrieb:
> Geht leider nur bis 16 MHz, aber scheint zu reichen.

Eher nicht. Wenn du einen 9MHz SPI Takt mit einem 16MHz LA abtastest, 
dann fehlt ab und zu schon mal was. Siehe Taktsignal.

Geh testweise mit den SPI Takt deutlich runter, wenn du das Zeug 
dekodiert kriegen willst.

von Dirk K. (dekoepi)


Lesenswert?

Etwa mit Prescaler 64 wird die "Trennschärfe" besser. Man kann gut 
erkennen, dass auf MOSI korrekt 0..1..2..3..4..5.... und so weiter 
rausgehen. (SCK zählen und dann die Flanken auf MOSI zählen, ist das 
Bitmuster.)
Allerdings passiert beim DMA auf MISO gar nichts mehr. Bleibt auf 
HIGH-Pegel. Erst ganz am Ende beim Polling läuft auch das wieder. Oder 
eben bei höherem SPI-Takt.

von (prx) A. K. (prx)


Lesenswert?

Kontrolliere vorsichshalber mal, was exakt in den SPI und DMA Registern 
drin steht. Und genau im Manual nachlesen, was das bedeutet. Nur um 
sicher zu gehen, dass die Hardware auch das sieht, was du per StdPerLib 
zu tun hoffst.

Wie sieht es mit der Taktversorgung aus? Haben alle Busse den zulässigen 
Takt. Einer der Peripheriebusse ist u.U. auf halben Takt begrenzt.

von Dirk K. (dekoepi)


Lesenswert?

Ohje, jetzt JTAG noch fertig basteln bin ich schon zu durch für, morgen 
mal da ran setzen (WUA, JTAG musste ich deaktivieren, damit ich die 
PB-Pins nutzen kann...). Habe mir jetzt auch das MISO im Zoom angesehen 
- die Quatsch-Werte, die ich in der Ausgabe sehe, zeigt auch der LA an, 
das bekomme ich auf der Leitung wirklich zurück.

Meine Takte sind System-Standard; das einzig angepasste ist der SPI mit 
dem Prescaler.

Da MISO erst ab irgendwo 4MHz losläuft, vermute ich da irgendwo einen 
Fehler. Das mit den Registern prüfen scheint eine gute Idee!

von holger (Gast)


Lesenswert?

>Ohje, jetzt JTAG noch fertig basteln bin ich schon zu durch für

Den aktuellen Code solltest du auch noch mal posten.

von Dirk K. (dekoepi)


Lesenswert?

1
Starting program.
2
RAM chip 0 is not OK! ->  [0] 251  [1] 252  [2] 253  [3] 254  [4] 0  [5] 1  [6] 2  [7] 3  [8] 4  [9] 5  [10] 6  [11] 7  [12] 8  [13] 9  [14] 10  [15] 11  [16] 12  [17] 13  [18] 14  [19] 15  
3
RAM chip 1 is not OK! ->  [0] 251  [1] 252  [2] 253  [3] 254  [4] 0  [5] 1  [6] 2  [7] 3  [8] 4  [9] 5  [10] 6  [11] 7  [12] 8  [13] 9  [14] 10  [15] 11  [16] 12  [17] 13  [18] 14  [19] 15  
4
RAM chip 2 is not OK! ->  [0] 251  [1] 252  [2] 253  [3] 254  [4] 0  [5] 1  [6] 2  [7] 3  [8] 4  [9] 5  [10] 6  [11] 7  [12] 8  [13] 9  [14] 10  [15] 11  [16] 12  [17] 13  [18] 14  [19] 15  
5
RAM chip 3 is not OK! ->  [0] 251  [1] 252  [2] 253  [3] 254  [4] 0  [5] 1  [6] 2  [7] 3  [8] 4  [9] 5  [10] 6  [11] 7  [12] 8  [13] 9  [14] 10  [15] 11  [16] 12  [17] 13  [18] 14  [19] 15  
6
RAM chip 4 is not OK! ->  [0] 251  [1] 252  [2] 253  [3] 254  [4] 0  [5] 1  [6] 2  [7] 3  [8] 4  [9] 5  [10] 6  [11] 7  [12] 8  [13] 9  [14] 10  [15] 11  [16] 12  [17] 13  [18] 14  [19] 15

Ich habe jetzt noch mal angepasst, die zu übertragende Datenmenge mit 
"-1" anzugeben, also zählt der DMA doch von 0 bis "zu übertragende 
Bytes"! :)

Was mit den 4 Bytes am Anfang jetzt noch falsch ist, muss ich noch 
rausfinden. Aber ich komme der Lösung näher :)

von Dirk K. (dekoepi)


Lesenswert?

Noch ein Nachtrag: Ich lasse mir jetzt alle fehlhaften Speicherstellen 
ausgeben.

Edit: Blödsinn gelöscht ...

Ein wenig hat das noch mit dem Warten zwischen DMA-Übertragung und neu 
Beschreiben der Buffer zu tun; die Transfer-Größe ist mit (buffer_size) 
ohne ein -1 schon korrekt.

Warte ich jetzt nach dem read+Adresse und write+Adresse auf das Ende der 
Übetragung, bevor ich die Puffer fülle, komme ich schon auf dieses 
Ergebnis - 1 Byte ist falsch:
1
Starting program.
2
RAM chip 0 is OK!
3
[255]!=0  RAM chip 1 is NOT OK!
4
[255]!=0  RAM chip 2 is NOT OK!
5
[255]!=0  RAM chip 3 is NOT OK!
6
[255]!=0  RAM chip 4 is NOT OK!

Derselbe Code, mit Prescaler 4 statt 8 ...
1
Starting program.
2
RAM chip 0 is OK!
3
RAM chip 1 is OK!
4
RAM chip 2 is OK!
5
RAM chip 3 is OK!
6
RAM chip 4 is OK!

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.