Forum: Mikrocontroller und Digitale Elektronik STM32F4Discovery SPI slave


von Tobias S. (tryan)


Lesenswert?

Hallo,

ich bin dabei das STM32F4Discovery-Board als SPI slave zu nutzen, aber
irgendwie klappt es nicht.

Die Signale liegen alle richtig an, aber leider Springt das Programm
nicht in den Interrupt. Ich denke der Fehler ist irgendwo in der
Initialisierung...
1
#define SPI2_SCK_PIN              GPIO_Pin_13
2
#define SPI2_SCK_GPIO_PORT        GPIOB
3
#define SPI2_SCK_GPIO_CLK         RCC_AHB1Periph_GPIOB
4
#define SPI2_SCK_SOURCE           GPIO_PinSource13
5
#define SPI2_SCK_AF               GPIO_AF_SPI2
6
7
#define SPI2_MOSI_PIN             GPIO_Pin_15
8
#define SPI2_MOSI_GPIO_PORT       GPIOB
9
#define SPI2_MOSI_GPIO_CLK        RCC_AHB1Periph_GPIOB
10
#define SPI2_MOSI_SOURCE          GPIO_PinSource15
11
#define SPI2_MOSI_AF              GPIO_AF_SPI2
12
13
#define SPI2_MISO_PIN             GPIO_Pin_14
14
#define SPI2_MISO_GPIO_PORT       GPIOC
15
#define SPI2_MISO_GPIO_CLK        RCC_AHB1Periph_GPIOB
16
#define SPI2_MISO_SOURCE          GPIO_PinSource14
17
#define SPI2_MISO_AF              GPIO_AF_SPI2
18
19
#define SPI2_CS_PIN               GPIO_Pin_12
20
#define SPI2_CS_GPIO_PORT         GPIOB
21
#define SPI2_CS_GPIO_CLK          RCC_AHB1Periph_GPIOB
22
#define SPI2_CS_SOURCE            GPIO_PinSource12
23
#define SPI2_CS_AF                GPIO_AF_SPI2
24
25
void SPI2_Init (void){
26
/************************************************************************/
27
  SPI_InitTypeDef SPI_InitStructure;
28
  GPIO_InitTypeDef GPIO_InitStructure;
29
30
  GPIO_InitStructure.GPIO_Pin = SPI2_SCK_PIN;
31
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN|GPIO_Mode_AF ;
32
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
33
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
34
  GPIO_Init(GPIOB, &GPIO_InitStructure);
35
36
  GPIO_InitStructure.GPIO_Pin = SPI2_MOSI_PIN;
37
  GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_OUT|GPIO_Mode_AF;
38
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
39
  GPIO_Init(GPIOB, &GPIO_InitStructure);
40
41
  GPIO_InitStructure.GPIO_Pin = SPI2_MISO_PIN;
42
  GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IN|GPIO_Mode_AF ;
43
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
44
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
45
  GPIO_Init(GPIOB, &GPIO_InitStructure);
46
47
  GPIO_InitStructure.GPIO_Pin = SPI2_CS_PIN;
48
  GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IN|GPIO_Mode_AF ;
49
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
50
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
51
  GPIO_Init(GPIOB, &GPIO_InitStructure);
52
53
  GPIO_PinAFConfig(SPI2_SCK_GPIO_PORT, SPI2_SCK_SOURCE, SPI2_SCK_AF);
54
  GPIO_PinAFConfig(SPI2_MOSI_GPIO_PORT, SPI2_MOSI_SOURCE, SPI2_MOSI_AF);
55
  GPIO_PinAFConfig(SPI2_MISO_GPIO_PORT, SPI2_MISO_SOURCE, SPI2_MISO_AF);
56
  GPIO_PinAFConfig(SPI2_MISO_GPIO_PORT, SPI2_CS_SOURCE, SPI2_CS_AF);
57
58
  RCC_AHB1PeriphClockCmd(SPI2_SCK_GPIO_CLK, ENABLE);
59
  RCC_AHB1PeriphClockCmd(SPI2_MOSI_GPIO_CLK, ENABLE);
60
  RCC_AHB1PeriphClockCmd(SPI2_MISO_GPIO_CLK, ENABLE);
61
  RCC_AHB1PeriphClockCmd(SPI2_CS_GPIO_CLK, ENABLE);
62
63
/************************************************************************/
64
  // SPI2 configurieren
65
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
66
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
67
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
68
  SPI_InitStructure.SPI_CRCPolynomial = 0;
69
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
70
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
71
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
72
  SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
73
  SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;
74
  SPI_Init(SPI2, &SPI_InitStructure);
75
  SPI_Cmd(SPI2, ENABLE);
76
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
77
78
  NVIC_InitTypeDef NVIC_InitStructure;
79
/* Enable the USARTy Interrupt */
80
  NVIC_InitStructure.NVIC_IRQChannel            = SPI2_IRQn;
81
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
82
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
83
  NVIC_InitStructure.NVIC_IRQChannelCmd         = ENABLE;
84
  NVIC_Init(&NVIC_InitStructure);
85
  SPI_I2S_ITConfig(SPI2, SPI_I2S_FLAG_RXNE,ENABLE);
86
/************************************************************************/
87
  }
88
89
90
void spi_handleSPI2Interrupt(void){
91
  GPIOD->ODR ^= GPIO_Pin_14 ;
92
    }
Vielen Dank für eure Hilfe.

Viele Grüße Tobias

: Bearbeitet durch User
von Marcel (Gast)


Lesenswert?

Tobias S. schrieb:
> #define SPI2_MISO_GPIO_PORT       GPIOC
> #define SPI2_MISO_GPIO_CLK        RCC_AHB1Periph_GPIOB

Passt nicht zusammen. Später wird es auch auf GPIOB initialisiert.

Tobias S. schrieb:
> GPIO_InitStructure.GPIO_Pin = SPI2_MOSI_PIN;
>   GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_OUT|GPIO_Mode_AF;
>   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
>   GPIO_Init(GPIOB, &GPIO_InitStructure);
>
>   GPIO_InitStructure.GPIO_Pin = SPI2_MISO_PIN;
>   GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IN|GPIO_Mode_AF ;
>   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
>   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
>   GPIO_Init(GPIOB, &GPIO_InitStructure);

MOSI = Master Out, Slave In. Müsste für einen Slave also Input und nicht 
Output sein. Soweit ich weiss kannst du nur einen Pin-Mode gleichzeitig 
nutzen. Also AF oder IN, aber nicht beides zusammen. Bei einer AF 
Konfiguration entscheidet dann die SPI-Initialisierung darüber, ob der 
Pin IN oder OUT ist.

Guck dir mal http://diller-technologies.de/stm32_wide.html#spi als 
Referenz an. Ist zwar für den STM32F3, aber lässt sich fast 1:1 auf den 
STM32F4 übertragen.

von Tobias S. (tryan)


Lesenswert?

Hallo Marcel, danke für deine Antwort. Ich habe mir den Link angeschaut 
und den Code soweit angepasst, jedoch funktioniert es immer noch 
nicht...

Ich habe die Pins jetzt alle auf "AF" gestellt. Werde aber noch mal das 
Manual durchlesen...

Danke für deine Hilfe
1
#define SPI2_SCK_PIN              GPIO_Pin_13
2
#define SPI2_MOSI_PIN             GPIO_Pin_15
3
#define SPI2_MISO_PIN             GPIO_Pin_14
4
#define SPI2_CS_PIN               GPIO_Pin_12
5
6
7
void SPI2_Init (void){
8
/************************************************************************/
9
  // Clock, MOSI und MISO configurieren
10
  SPI_InitTypeDef SPI_InitStructure;
11
  GPIO_InitTypeDef GPIO_InitStructure;
12
13
  GPIO_InitStructure.GPIO_Pin = SPI2_SCK_PIN | SPI2_MISO_PIN| SPI2_MOSI_PIN|SPI2_CS_PIN;
14
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
15
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
16
  GPIO_Init(GPIOB, &GPIO_InitStructure);
17
18
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
19
20
21
/************************************************************************/
22
  /* Enable the SPI2 Interrupt */
23
  NVIC_InitTypeDef NVIC_InitStructure;
24
  NVIC_InitStructure.NVIC_IRQChannel            = SPI2_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
  SPI_I2S_ITConfig(SPI2, SPI_I2S_FLAG_RXNE,ENABLE);
30
31
  // SPI2 configurieren
32
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
33
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
34
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
35
  SPI_InitStructure.SPI_CRCPolynomial = 0;
36
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
37
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
38
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
39
  SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
40
  SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;
41
  SPI_Init(SPI2, &SPI_InitStructure);
42
  SPI_Cmd(SPI2, ENABLE);
43
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
44
45
/************************************************************************/
46
  }
47
48
49
void spi_handleSPI2Interrupt(void){
50
  GPIOD->ODR ^= GPIO_Pin_14 ;
51
    }

von m.n. (Gast)


Lesenswert?

Da fehlt doch irgendwo ein NVIC_EnableIRQ.

Die 32 Bitter sind doch sowas von easy.

von Tobias S. (tryan)


Lesenswert?

Hmm..

ich dachte, das der Interrupt so eingeschaltet wird
1
  /* Enable the SPI2 Interrupt */
2
  NVIC_InitTypeDef NVIC_InitStructure;
3
  NVIC_InitStructure.NVIC_IRQChannel            = SPI2_IRQn;
4
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
5
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
6
  NVIC_InitStructure.NVIC_IRQChannelCmd         = ENABLE;
7
  NVIC_Init(&NVIC_InitStructure);
8
  SPI_I2S_ITConfig(SPI2, SPI_I2S_FLAG_RXNE,ENABLE);

: Bearbeitet durch User
von Marcel (Gast)


Lesenswert?

Damit gibst du die Interrupts des SPI2 frei. Du musst voher noch mit
[c]
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
[\c]
das Interruptsystem aktivieren. Ist wie das sei() beim AVR.
Ich habe selber noch nicht mit den SPI Interrupts gearbeitet, aber bei 
den USART Interrupts musst du noch manuell das entsprechende Status 
Register auswerten, damit du weisst woher der IRQ kam (Senden, 
Empfangen, ...) und das Statusbit auch manuell löschen, damit die 
Interrput-Routine nicht ständig aufgerufen wird.

Nimm dir einen Abend Zeit die ganzen Beispiele von Diller durchzugucken. 
Auch wenn es nicht direkt mit SPI zu tun hat, wird dir das Prinzip klar, 
mit dem die Interrupts auf einem Cortex arbeiten.

von Tobias S. (tryan)


Lesenswert?

Achso...danke für die Info. Das mit dem 
"NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)" wusste ich nicht. Habe 
ich auch nicht in dem SPI Beispiel von "Diller" gefunden. Aber ich werde 
mir mal die Beispiele durchlesen. Danke für den Tipp.

Viele Grüße Tobias

von holger (Gast)


Lesenswert?

Ich weiss nicht ob es was bringt, aber ich schalte erst
die RCC Clock für das jeweilige Modul an und konfiguriere es dann.
Bei dir ist es irgendwie anders rum.

von Rainer R. (Firma: Reusch Elektronik) (reusch)


Lesenswert?

Die Betriebsart eines Pin als Eingang und Alternate Function ("AF") zu 
setzen, dürfte Unsinn sein. Hier mein funktionierender Kode für einen 
STM32F405 als SPI-Slave...

Initialisierung:
  // Taktversorgung der benötigten Peripherie
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);  // nur 
PreemptionPriority ist von Bedeutung

  // SPI-Ports
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 
| GPIO_Pin_15;  // SPI2_NSS, SPI2_SCK, SPI2_MISO, SPI2_MOSI
  GPIO_Init(GPIOB, &GPIO_InitStructure);

  GPIO_PinAFConfig(GPIOB,GPIO_PinSource12,GPIO_AF_SPI2);
  GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_SPI2);
  GPIO_PinAFConfig(GPIOB,GPIO_PinSource14,GPIO_AF_SPI2);
  GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_SPI2);

  // SPI2-Schnittstelle
  SPI_InitStruct.SPI_Direction=SPI_Direction_2Lines_FullDuplex;
  SPI_InitStruct.SPI_Mode=SPI_Mode_Slave;
  SPI_InitStruct.SPI_DataSize=SPI_DataSize_8b;
  SPI_InitStruct.SPI_CPOL=SPI_CPOL_Low;
  SPI_InitStruct.SPI_CPHA=SPI_CPHA_1Edge;
  SPI_InitStruct.SPI_NSS=SPI_NSS_Hard;
  SPI_InitStruct.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_8;
  SPI_InitStruct.SPI_FirstBit=SPI_FirstBit_MSB;
  SPI_InitStruct.SPI_CRCPolynomial=7;
  SPI_Init(SPI2,&SPI_InitStruct);
  SPI_I2S_ITConfig(SPI2,SPI_I2S_IT_RXNE,ENABLE);  // Interrupt, wenn 
Daten empfangen
  SPI_Cmd(SPI2,ENABLE);

  // SPI2-Interrupt
  NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0A;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x08;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

Interrupt-Routine:

void SPI2_IRQHandler(void)
{
  uint16_t d=SPI2->DR;
}

Wichtig ist, dass in der Interrupt-Routine das DR-Register gelesen wird, 
um das zugehörige Interrupt-Flag zu löschen.

von Tobias S. (tryan)


Lesenswert?

Hallo,

super danke für den Code Rainer, jetzt läuft es... Ist schon ein wenig 
anders als ein AVR zu programmieren ;)

Nochmal vielen Dank

Viele Grüße Tobias

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.