Forum: Mikrocontroller und Digitale Elektronik STM32 F3 Discovery Probleme mit SPI und receive Interrupt


von Franky (Gast)


Lesenswert?

Hallo,

ich versuche mit einem STM32F3Discovery zwei SN74HC165 Shiftregister mit 
dem SPI Peripheral anzusprechen. Dazu möchte ich 16bit Daten über den 
MISO-Pin einlesen, MOSI ist unbeschaltet.

Diese Funktionen habe ich dazu geschrieben:
1
//init spi gpio for digital input shift register
2
void dig_in_init(){
3
    gpio_pin_config(CS_PORT,CS_PIN,OUT|PP|MHZ_50);  //chip select, connected to clk_inhibit, always low
4
    gpio_pin_config(SH_LD_PORT,SH_LD_PIN,OUT|PP|MHZ_50); //shift/load
5
    CS_PORT->BRR = (1<<CS_PIN); //chip select, connected to clk_inhibit, always low
6
7
    gpio_pin_config(CLK_PORT,CLK_PIN,AF|PP);  //spi clk to alternate function push/pull
8
    gpio_pin_config(MISO_PORT,MISO_PIN,AF|PP); //spi miso to alternate function push/pull
9
    CLK_PORT->AFR[1] |= (5<<((CLK_PIN-8)*4));  //AF5 (SPI2 CLK)
10
    MISO_PORT->AFR[1] |= (5<<((MISO_PIN-8)*4)); //AF5 (SPI2 MISO)
11
12
    RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; //enable clock for spi2
13
14
    SPI2->CR1 |= SPI_CR1_BR; //baudrate fPCLK/256
15
    SPI2->CR1 |= SPI_CR1_CPHA|SPI_CR1_CPOL; //clock 1 when idle, rising edge sample (to have some time between transmission start and first rising edge)
16
    SPI2->CR1 &= ~(SPI_CR1_BIDIMODE|SPI_CR1_RXONLY); //2-line mode, full duplex
17
    SPI2->CR2 |= SPI_CR2_DS; //16bit data size
18
    SPI2->CR1 &= ~SPI_CR1_LSBFIRST; //MSB first
19
    SPI2->CR1 &= ~SPI_CR1_SSM; //Software slave management disabled
20
    SPI2->CR2 &= ~SPI_CR2_SSOE; //disable SS output
21
    SPI2->CR2 &= ~SPI_CR2_FRF; //disable TI protocol
22
    SPI2->CR2 &= ~SPI_CR2_NSSP; //no NSS pulse mode
23
    SPI2->CR2 &= ~SPI_CR2_FRXTH; //FIFO threshold to 16bit
24
    SPI2->CR1 &= ~SPI_CR1_CRCEN; //no CRC
25
    SPI2->CR1 |= SPI_CR1_MSTR; //master mode
26
    SPI2->CR2 |= SPI_CR2_RXNEIE; //receive buffer not empty interrupt enable
27
    SPI2->CR1 |= SPI_CR1_SPE; //enable SPI
28
    NVIC_EnableIRQ(SPI2_IRQn); //enable SPI2 global Interrupt
29
30
    //start cycle
31
    dig_in_sr_state =  STATE_LOAD;
32
    SH_LD_PORT->BRR = (1<<SH_LD_PIN);  //shift/load to load
33
    SPI2->DR = 0xF0F0; //put dummy bytes into data register to start transmission
34
}
35
36
//spi2 ISR
37
void SPI2_IRQHandler(void){if(dig_in_sr_state==STATE_LOAD){
38
        not_used = SPI2->DR; //dummy read values from data register to reset interrupt
39
        SH_LD_PORT->BSRR = (1<<SH_LD_PIN);  //shift/load to shift
40
        SPI2->DR = 0x0F0F; //put dummy bytes into data register to start communication
41
        dig_in_sr_state=STATE_SHIFT;
42
    }
43
    else if(dig_in_sr_state==STATE_SHIFT){
44
        digital_values = SPI2->DR; //read values from data register (resets interrupt)
45
        SH_LD_PORT->BRR = (1<<SH_LD_PIN);  //shift/load to load
46
        SPI2->DR = 0xF0F0; //put dummy bytes into data register to start communication
47
        dig_in_sr_state=STATE_LOAD;
48
    }
49
}

dig_in_init wird in Main aufgerufen und soll den SPI initialisieren und 
eine Übertragung starten. Wenn die Übertragung beendet ist soll in der 
ISR wieder eine Übertragung gestartet werden, sodass das ganze im 
Hintergrund weiterläut und immer wieder Daten aus den Shiftregistern 
ausließt und in der Variable digital_values speichert.

Jedoch werden scheinbar nie daten empfangen und es wird daher nie ein 
Interrupt ausgelöst.

Woran könnte das liegen?

liebe Grüße
Franky

von holger (Gast)


Lesenswert?

>Woran könnte das liegen?

Nimm ein Osci und miss nach ob die Clockleitung
auch Takte ausgiebt.

von Franky (Gast)


Lesenswert?

Nein, keine Takte auf der Clockleitung.
Das könnte natürlich an meiner gpio_pin_config Funktion liegen, die den 
Pin nicht richtig auf Alternate Function setzt aber selbst wenn sie 
nicht Funktioniert sollte der SPI doch nach 16 Flanken (die dann hald 
nicht bis zum Pin kommen) den Interrupt auslösen, oder?

von Franky (Gast)


Lesenswert?

Ich habe auch probiert folgenden Code am Ende der dig_in_init Funktion 
einzufügen:
1
while( !(SPI2->SR & 128) ); // wait until busy

Laut Debugger bleibt der Controller in dieser Schleife hängen, was mMn. 
zeigt dass keine Übertragung begonnen wird.

von Walleby (Gast)


Lesenswert?

Hat das einen bestimmten Grund, dass du die einzelnen Register manuell 
setzt? ST bietet doch sehr komfortable libries für z. B. den SPI.

MfG

Walleby

von Franky (Gast)


Lesenswert?

Der einzige Grund ist eigentlich dass ich es so gewohnt bin (habe vorher 
AVR verwendet) und es "zwingt" mich, mich mehr mit der Funktionsweise 
des Controllers zu beschäftigen.

Die vorher gepostete Zeile sollte eigentlich so aussehen:
1
while( !(SPI2->SR & SPI_SR_BSY) ); // wait until busy

von Walleby (Gast)


Lesenswert?

Hast du denn die Taktversorgung für die GPIO's und den SPI aktiviert?
Bei dem Umstieg von AVR zu STM vergisst man leicht, dass die einzelnen 
Komponenten generell nicht versorgt werden.

von Franky (Gast)


Lesenswert?

GPIO-Clock ist aktiviert (Pins ansteuern funktioniert auch):
1
RCC -> AHBENR |= RCC_AHBENR_GPIOAEN | //enable clock for GPIOA
2
                 RCC_AHBENR_GPIOBEN | //enable clock for GPIOB
3
                 RCC_AHBENR_GPIOCEN | //enable clock for GPIOC
4
                 RCC_AHBENR_GPIODEN | //enable clock for GPIOD
5
                 RCC_AHBENR_GPIOEEN | //enable clock for GPIOE
6
                 RCC_AHBENR_GPIOFEN ; //enable clock for GPIOF
Den SPI2-Clock aktiviere ich in der oben gezeigten Funktion mit 
folgender Zeile:
1
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; //enable clock for spi2

Passt das so?

von Walleby (Gast)


Lesenswert?

Ich kenne die einzelnen Register des STM32f3 zwar nicht, sieht aber 
jedenfalls nicht falsch aus. Um sicher zu gehen, könntest du einen Blick 
in FW-Lib werfen.

Wenn du keinen Takt am SPI_CLK hast, kann es eigentlich nurnoch eine 
Fehlgescheschlagene Initialisierung bzw. Start haben.

Was mir auffällt ist:
1
 SPI2->CR1 &= ~(SPI_CR1_BIDIMODE|SPI_CR1_RXONLY); //2-line mode, full duplex

Solltest du nicht noch in dem Register das Senden aktivieren, oder habe 
ich das übersehen?

von Franky (Gast)


Lesenswert?

Das Senden sollte im 2-Line Full-Duplex-Mode, den ich verwende, aktiv 
sein. Für den Half-Duplex-Mode kann man das Senden Deaktivieren um in 
den Empfangsmodus zu wechseln.

Den Software Slave select habe ich jetzt aktiviert (das war vorher 
falsch, denke ich), das Problem bleibt aber:
1
SPI2->CR1 |= SPI_CR1_SSM; //Software slave select management enabled

von Walleby (Gast)


Lesenswert?

Besteht denn das Problem noch, dass du kein SPI_CLOCK bekommst? Wenn das 
so ist, hast du glaube ich noch Konfigurationsprobleme mit deinem SPI.

Kommst du mit dem Oszi an die MOSI-Leitung? Dann könntest du zur 
Funktionsprüfung Daten senden. Wenn du dann nichts siehst, liegt der 
Fehler in der Konf.

von Franky (Gast)


Lesenswert?

So, jetzt funktioniert alles!
Das software slave select management muss aktiviert sein, wie in meinem 
letzten Post schon vermutet. Zusätzlich muss man folgendes Bit setzen:
1
SPI2->CR1 |= SPI_CR1_SSI; //enable internal slave select
Scheinbar wird nichts gesendet, wenn nicht irgendwo der Slave aktiviert 
wird.

der Init-Code sieht jetzt so aus (CPOL und CPHA hab ich noch an den 
Slave angepasst):
1
void dig_in_init(){
2
    gpio_pin_config(CS_PORT,CS_PIN,OUT|PP|MHZ_50);  //chip select, connected to clk_inhibit, always low
3
    gpio_pin_config(SH_LD_PORT,SH_LD_PIN,OUT|PP|MHZ_50); //shift/load
4
    CS_PORT->BRR = (1<<CS_PIN); //chip select, connected to clk_inhibit, always low
5
6
    gpio_pin_config(CLK_PORT,CLK_PIN,AF|PP);  //spi clk to alternate function push/pull
7
    gpio_pin_config(MISO_PORT,MISO_PIN,AF|PP); //spi miso to alternate function push/pull
8
    CLK_PORT->AFR[1] |= (5<<((CLK_PIN-8)*4));  //AF5 (SPI2 CLK)
9
    MISO_PORT->AFR[1] |= (5<<((MISO_PIN-8)*4)); //AF5 (SPI2 MISO)
10
11
    RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; //enable clock for spi2
12
13
    SPI2->CR1 &= ~SPI_CR1_SPE; //disable SPI
14
    SPI2->I2SCFGR &= ~SPI_I2SCFGR_I2SMOD; //switch peripheral to spi mode
15
    SPI2->CR1 |= SPI_CR1_BR; //baudrate fPCLK /256 TODO optimize
16
    SPI2->CR1 &= ~SPI_CR1_CPOL; //sample on first edge
17
    SPI2->CR1 &= ~SPI_CR1_CPHA;//clock low while idle
18
    SPI2->CR1 &= ~(SPI_CR1_BIDIMODE|SPI_CR1_RXONLY); //2-line mode, full duplex
19
    SPI2->CR2 |= SPI_CR2_DS; //16bit data size
20
    SPI2->CR1 |= SPI_CR1_LSBFIRST; //LSB first
21
    SPI2->CR1 |= SPI_CR1_SSM; //Software slave select management enabled
22
    SPI2->CR1 |= SPI_CR1_SSI; //enable internal slave select
23
    SPI2->CR2 &= ~SPI_CR2_SSOE; //disable SS output
24
    SPI2->CR2 &= ~SPI_CR2_FRF; //disable TI protocol
25
    SPI2->CR2 &= ~SPI_CR2_NSSP; //no NSS pulse mode
26
    SPI2->CR2 &= ~SPI_CR2_FRXTH; //FIFO threshold to 16bit
27
    SPI2->CR1 &= ~SPI_CR1_CRCEN; //no CRC
28
    SPI2->CR1 |= SPI_CR1_MSTR; //master mode
29
    SPI2->CR2 |= SPI_CR2_RXNEIE; //receive buffer not empty interrupt enable
30
    SPI2->CR1 |= SPI_CR1_SPE; //enable SPI
31
    NVIC_EnableIRQ(SPI2_IRQn); //enable SPI2 global Interrupt
32
33
    //start cycle
34
    dig_in_sr_state =  STATE_LOAD;
35
    SH_LD_PORT->BRR = (1<<SH_LD_PIN);  //shift/load to load
36
    SPI2->DR = 0xF0F0; //put dummy bytes into data register to start transmission
37
}

Danke für die Ideen!

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.