2016-12-29 10:44 AM
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 #stm32f3Solved! Go to Solution.
2016-12-29 11:03 AM
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!
2016-12-29 11:03 AM
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!
2016-12-29 04:16 PM
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?
2016-12-30 02:25 AM
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).
2016-12-30 03:31 PM
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.
2016-12-30 11:46 PM
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