cancel
Showing results for 
Search instead for 
Did you mean: 

Understanding SPI on the STM32F3

William Huang
Associate II
Posted on December 29, 2016 at 19:44

Hi, I have a NUCLEO-F303 board which I would like to use to communicate to two devices using SPI. The NUCLEO would be sending and receiving data to and from the devices via MOSI and MISO.

Looking through the datasheet, I'm very confused by the slave select management bits (SSM and SSI) under master mode, and how I should configure them for my application so that I can manually drive a GPIO pin low for slave select and then clock in/out the data.

Since I have multiple slaves, I'm assuming SSM = 1? On page 964 of the reference manual, it also says '... slave select information is driven internally by the SSI bit value in the register SPIx_CR1...'. So going by that, after I pull a GPIO chip select pin LOW, I also set SSI low to indicate that a slave has been selected? When I tried doing this, I get a frame error in the status register. I also tried leaving the SSI bit high, but the contents of the data register always reads 0xFFFF.

Any clarifications would be greatly appreciated! Thanks!

Below is my code for SPI:

// Configures GPIOA pins to use SPI1

// PB3 - SPI1_CLK (AF5)

// PB4 - SPI1_MISO (AF5)

// PB5 - SPI1_MOSI (AF5)

void SPI1_Init(void)

{

uint8_t ch;

if(RCC->APB2ENR & RCC_APB2ENR_SPI1EN) // SPI1 already enabled

{

return;

}

// Disable SPI

while(SPI1->SR & SPI_SR_FTLVL); // Wait until no more data to transmit

while(SPI1->SR & SPI_SR_BSY); // Wait until SPI is not busy

SPI1->CR1 &= ~SPI_CR1_SPE; // Disable SPI1 peripheral

while(SPI1->SR & SPI_SR_FRLVL) // Read all data deceived

{

if(SPI1->SR & SPI_SR_RXNE)

{

ch = SPI1->DR;

}

}

// Initializae GPIOA and GPIOB peripheral clocks

GPIOAClock(1);

GPIOBClock(1);

// Set PB1 and PA12 as outputs

// PB1 - DAC Chip select

// PA12 - BLE Chip Select

GPIOA->MODER &= ~GPIO_MODER_MODER12;

GPIOA->MODER |= GPIO_MODER_MODER12_0; // PA12 - Output

GPIOA->OTYPER &= ~GPIO_OTYPER_OT_12; // Push pull

GPIOB->MODER &= ~GPIO_MODER_MODER1;

GPIOB->MODER |= GPIO_MODER_MODER1_0; // PB1 - Output

GPIOB->OTYPER &= ~GPIO_OTYPER_OT_1; // Push pull

GPIOA->ODR |= GPIO_ODR_12; // Set PA12 high

GPIOB->ODR |= GPIO_ODR_1; // Set PB1 high

// Enable SPI1 Clock

RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;

// First, configure GPIO port to for SPI use

// ======================================================

// Configure PB3, PB4, PB5 to be alternate function pins

GPIOB->MODER &= ~(GPIO_MODER_MODER3 | GPIO_MODER_MODER4 | GPIO_MODER_MODER5);

GPIOB->MODER |= GPIO_MODER_MODER3_1 | GPIO_MODER_MODER4_1 | GPIO_MODER_MODER5_1;

// Output type as Push-pull

GPIOB->OTYPER &= ~(GPIO_OTYPER_OT_3 | GPIO_OTYPER_OT_4 | GPIO_OTYPER_OT_5);

// High speed for PB3, PB4, PB5

GPIOB->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR3 | GPIO_OSPEEDER_OSPEEDR4 | GPIO_OSPEEDER_OSPEEDR5);

GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR3 | GPIO_OSPEEDER_OSPEEDR4 | GPIO_OSPEEDER_OSPEEDR5;

// No pull up or pull down for CLK, MOSI. Pull up for MISO

GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPDR3 | GPIO_PUPDR_PUPDR4 | GPIO_PUPDR_PUPDR5);

GPIOB->PUPDR |= GPIO_PUPDR_PUPDR4_0;

// Set alternative function to AF5 (SPI Mode)

GPIOB->AFR[0] &= ~(GPIO_AFRL_AFRL3 | GPIO_AFRL_AFRL4 | GPIO_AFRL_AFRL5);

GPIOB->AFR[0] |= ( (5U << 12) | (5U << 16) | (5U << 20));

// Now configure SPI settings:

SPI1->CR1 = 0x00; // Reset SPI CR1

SPI1->CR2 = 0x0700; // Reset SPI CR2: Default settings for CR2 okay for use

SPI1->CR1 |= SPI_CR1_BR_0; // Spi Clock is 8 MHz / 4 = 2 MHz

SPI1->CR1 &= ~(SPI_CR1_CPOL | SPI_CR1_CPHA);

SPI1->CR1 &= ~SPI_CR1_RXONLY; // Full duplex mode

SPI1->CR1 &= ~SPI_CR1_BIDIMODE; // 2-line unidirectional data

SPI1->CR1 |= SPI_CR1_BIDIOE; // Output enabled MOSI pin

SPI1->CR1 &= ~SPI_CR1_LSBFIRST; // MSB First

SPI1->CR1 |= SPI_CR1_SSM; // Enable software slave management

SPI1->CR1 |= SPI_CR1_MSTR; // Master Configuration

SPI1->CR1 |= SPI_CR1_SSI; // Set SSI High

SPI1->CR2 |= SPI_CR2_FRXTH; // Reception threshold >= 8-bits

}

// Sends a single byte of data out SPI1

void SPI1_WriteByte(uint8_t data)

{

SPI1->CR1 |= SPI_CR1_SPE; // Enable SPE

while(SPI1->SR & SPI_SR_BSY); // Wait until bus is not busy

SPI1->DR = data; // write data

while(!(SPI1->SR & SPI_SR_TXE));

SPI1->CR1 &= ~SPI_CR1_SPE; // Disable SPE

}

// Writes a string of bytes to the selected chip

void SPI1_WriteString(SPI_CHIPS chipSelect, uint8_t *s)

{

// Pull appropriate chip select line LOW to start transfer

if(chipSelect == BLE)

{

GPIOA->ODR &= ~GPIO_ODR_12;

}

else if (chipSelect == DAC_MCP4821)

{

GPIOB->ODR &= ~GPIO_ODR_1;

}

else if (chipSelect == DAC_MCP4921)

{

GPIOB->ODR &= ~GPIO_ODR_1;

}

while(*s)

{

SPI1_WriteByte(*s++);

}

// Pull appropriate chip line HIGH to end transfer

if(chipSelect == BLE)

{

GPIOA->ODR |= GPIO_ODR_12;

}

else if (chipSelect == DAC_MCP4821)

{

GPIOB->ODR |= GPIO_ODR_1;

}

else if (chipSelect == DAC_MCP4921)

{

GPIOB->ODR |= GPIO_ODR_1;

}

#nucleo-f303 #spi #stm32f3
1 ACCEPTED SOLUTION

Accepted Solutions
Seb
ST Employee
Posted on December 29, 2016 at 20:03

0690X00000605kBQAQ.png

This is from STM32L4 reference manual. NSS is not needed at master side (unless a rare multi-master SPI system is used)

The note 1 should give clues.

Reminder: The SPI Master communication is completed when RXNE = 1 and BUSY = 0.

When using DMA TX and RX, the interrupt should be on the RX channel as it will come up last.

Good luck!

View solution in original post

5 REPLIES 5
Seb
ST Employee
Posted on December 29, 2016 at 20:03

0690X00000605kBQAQ.png

This is from STM32L4 reference manual. NSS is not needed at master side (unless a rare multi-master SPI system is used)

The note 1 should give clues.

Reminder: The SPI Master communication is completed when RXNE = 1 and BUSY = 0.

When using DMA TX and RX, the interrupt should be on the RX channel as it will come up last.

Good luck!

Posted on December 30, 2016 at 00:16

Thanks for pointing out the SSM and SSI settings.

Reminder: The SPI Master communication is completed when RXNE = 1 and BUSY = 0.

When using DMA TX and RX, the interrupt should be on the RX channel as it will come up last.

I'm a confused at this point. If I'm sending data out from the master to slave, a RXNE =1 and BUSY=0 would indicate that communication is completed? Why is it not TXE?

Posted on December 30, 2016 at 10:25

If only transmitting, yes, you are right.

When using 4 wire, RX and TX are done simultaneously. In this case, RXNE will tell that the transfer in both direction is completed (and NSS can be raised).

T J
Lead
Posted on December 31, 2016 at 00:31

Are you still getting framing errors ?

what speed are you running ?

you may want to try a 1k ohm pullup on the MISO pin

I would strongly recommend a 27R or 47R in series with the MOSI pin and CLK pins to reduce heat on the processor chip itself

If you still have trouble, please let me know.

Posted on December 31, 2016 at 08:46

ST has a very weird implementation of SPI with a bunch of features that you don't find on slave chips. The NSS line is used when you have multiple masters on the same bus. I've never seen multiple masters in all of the decades that I've been in this industry, but what do I know. Ignore the section of the manual dealing with playing around with multiple masters and NSS. Use SPI_NSS_SOFT.

I figured I must be dreaming, surely I read the docs wrong. Surely, SPI_NSS_HARD_OUTPUT would do what everybody wants. So I grepped all of the sample code from ST and they use hardware slave select exactly never. I'll have to investigate this later.

Set up your SPI as a master and use the NSS line as another GPIO that you manually toggle (WHY ST? WHY?) along with a couple of extras. You have one master and many slaves.

Don't dick around with pull ups or current limiters either, the slaves are very high impedance and aren't going to be drawing any appreciable current on MOSI or CLK. The values stated above will limit the current available to 80 & 120mA. Since the pins on the processor go poof at 25mA, the processor is not going to be protected by these resistors. Plus, the impedance of the slaves is in the MOhms, so they can't draw more than a few microamps. That is the nature of a digital load.

Andrei