I've spent a couple of days to find answer to my problem. Currently I'm working on a project where an STM32L432KC microcontroller board and a Raspberry Pi 3B need to communicate over SPI. The OS on the PI is Rasbian stretch, the microcontroller code is bare metal.
The microcontroller's code is the following:
#include <stm32l432xx.h>
#include "spi.h"
#include "uart.h"
#include "systick.h"
#include "clocks.h"
uint8_t rx_byte = 0x3;
uint8_t tx_byte = 0x5;
int main(void)
{
init_PLL(20, 2, 0, HSI16); //set the system clock to 80MHz
uart_init(); //for serial monitoring
spi1_slave_init();
while(1)
{
// send a byte
if(SPI1->SR & SPI_SR_TXE)
{
//printf("%x\n\r", tx_byte);
SPI1->DR = tx_byte++;
}
//receive a byte
if(SPI1->SR & SPI_SR_RXNE)
{
rx_byte = SPI1->DR;
//printf("%x\n\r", rx_byte);
}
}
return 0;
}
Edit: I forgot to share the SPI initialization code, here it is:
#include "spi.h"
void spi1_slave_init(void)
{
/***** clock configuration ******/
//enable clock for port B
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN;
//enable clock for port A
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;
// enable clock to the peripheral
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
/*****pin configuration********/
/*set alternate function mode*/
//MISO: PA_11
GPIOA->MODER &= ~(0b11 << (2 * 11));
GPIOA->MODER |= (0b10 << (2 * 11));
//MOSI: PA_12
GPIOA->MODER &= ~(0b11 << (2 * 12));
GPIOA->MODER |= (0b10 << (2 * 12));
//SCKL: PA_5
GPIOA->MODER &= ~(0b11 << (2 * 5));
GPIOA->MODER |= (0b10 << (2 * 5));
//SSEL: PB_0
GPIOB->MODER &= ~(0b11);
GPIOB->MODER |= 0b10;
/*set the output speed to very high*/
GPIOA->OSPEEDR &= ~((0b11 << (2 * 11)) | (0b11 << (2 * 12)) | (0b11 << (2 * 5)));
GPIOA->OSPEEDR |= ((0b11 << (2 * 11)) | (0b11 << (2 * 12)) | (0b11 << (2 * 5)));
GPIOB->OSPEEDR &= ~(0b11);
GPIOB->OSPEEDR |= 0b11;
/* set alternate function 5 */
GPIOA->AFR[1] &= ~((0xF << (4 * 3)) | (0xF << (4 * 4)));
GPIOA->AFR[1] |= ((0x5 <<(4 * 3)) | (0x5 << (4 * 4)));
GPIOA->AFR[0] &= ~(0xF << (4 * 5));
GPIOA->AFR[0] |= (0x5 << (4 * 5));
GPIOB->AFR[0] &= ~(0xF);
GPIOB->AFR[0] |= 0x5;
/*****SPI1 configuration********/
// disable SPI1
SPI1->CR1 &= ~(SPI_CR1_SPE);
// set CPHA and CPOL to zero
SPI1->CR1 &= ~(SPI_CR1_CPHA | SPI_CR1_CPOL);
// disable software slave management
SPI1->CR1 &= ~(SPI_CR1_SSM);
// recive and send message with MSB first configuration
SPI1->CR1 &= ~(SPI_CR1_LSBFIRST);
// configure the MCU as slave device
SPI1->CR1 &= ~(SPI_CR1_MSTR);
// select 8bit datasize
SPI1->CR2 |= (0x7 << SPI_CR2_DS_Pos);
// set RXNE (recive buffer is not empty) event's threshold to 8 bit
SPI1->CR2 |= SPI_CR2_FRXTH;
// TX DMA request enable
SPI1->CR2 |= SPI_CR2_TXDMAEN;
// enable the peripherial
SPI1->CR1 |= SPI_CR1_SPE;
}
the python code which runs on the PI is the following:
import spidev
import struct
from time import sleep
#setup SPI
spi_bus = 0
spi_device = 0
spi_max_rate_hz = 100000
spi = spidev.SpiDev()
spi.open(spi_bus, spi_device)
spi.max_speed_hz = spi_max_rate_hz
#spi.bits_per_word = 8
spi.mode = 0b00
byte2send = 0xA
recv_byte = spi.xfer2([byte2send], spi_max_rate_hz, 200)
#recv_byte = spi.xfer2([byte2send])
print(hex(recv_byte[0]))
And now the problem: the received bytes on the PI side are not what I would expect after I ran the code repeatedly: enter image description here
Why this sequence is not 0x5, 0x6, 0x7...? Is this a correct behaviour of the SPI communication? I even don't know where is the problem, the PI side with SPIdev or my code on the STM32 side? Is there any way that I could achieve a syncronous communication without these 0x0 bytes between the correct ones?
I really appriciate your help!
1 Answer 1
Your micro has TX & RX FIFOs and you need to force the correct access size to place the data (ot read the data from) to the FIFO.
*(volatile uint8_t *)&SPI1->DR = tx_byte++
same reading
tr_byte = *(volatile uint8_t *)&SPI1->DR;
You need init code to:
- Initialize GPIOs as Alternate to the proper mode.
- Start the SPI clock
- Set the FIFO threshold
- Set the SPI mode + data length.
Best show us your SPI init code.
-
\$\begingroup\$ This won't matter in case the DR is set to 16 bits which I suspect. You'll then get data according to the MSB/LSB setting, big or little endian. \$\endgroup\$Lundin– Lundin2022年02月07日 15:02:24 +00:00Commented Feb 7, 2022 at 15:02
-
\$\begingroup\$ @Lundin it makes a lot of difference. 1. Default RPI spidev && STM32 SPI word length is 8 bits. 2. Using 32 bits access will place 32 bits to the FIFO. \$\endgroup\$0___________– 0___________2022年02月07日 16:10:03 +00:00Commented Feb 7, 2022 at 16:10
-
1\$\begingroup\$ Hello! Sorry for the missing SPI initialization code, I added to my question. \$\endgroup\$Nightek– Nightek2022年02月07日 17:34:34 +00:00Commented Feb 7, 2022 at 17:34
if(SPI1->SR & SPI_SR_TXE)
probably evaluates to true out of reset, so that value in the DR register will be pre-loaded, assuming the MCU gets there first, before the PC master sends something. Isn't the problem rather that SPI1->DR is 16 bits or such? That's what I would bet on. \$\endgroup\$spi1_slave_init()
code. \$\endgroup\$