STM32 SPI Data Packing

1.3k Views Asked by At

I can't get the SPI on my STM32f3 discovery board (Datasheet) to work with gyroscope sensor (I3G4250D) on the register level. I know I'm sending data since I'm in full duplex and receiving dummy bytes from sensor using 16 bit data packing but when I try to receive using 8 bit access to DR register I get inconsistent values from sensor, sometimes returning one byte 0xff and other times returning 2 bytes 0xffff (at least I think that's what's happening) but no real values from from the sensor register I want to read. I think this has to do with automatic packing of STM32 SPI on my chip but I think I am addressing that by accessing DR register with uint8_t* but it doesn't seem to work. I also want to ask that when I compare the SPI protocol on sensor (datasheet page 24) and STM32 datasheet (page 729) I infer that both CPOL (clock polarity) and CPHA (clock phase) bits in STM32 SPI should be set but I seem to be able to at least send data with or without these bits set...

Here is my SPI Initialization function which includes trying to read bytes at the end of it and a write a byte to sensor register function:

void SPI_Init() {

/* Peripheral Clock Enable */
RCC->AHBENR |= RCC_AHBENR_GPIOEEN|RCC_AHBENR_GPIOAEN;
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;

/* GPIO Configuration */
GPIOA->MODER |= GPIO_MODER_MODER5_1|GPIO_MODER_MODER6_1|GPIO_MODER_MODER7_1;                            //Alternate function
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5|GPIO_OSPEEDER_OSPEEDR6|GPIO_OSPEEDER_OSPEEDR7;     //High speed
GPIOA->AFR[0] |= 0x00500000|0x05000000|0x50000000;                                                                              //AF for SCK,MISO,MOSI

GPIOE->MODER |= GPIO_MODER_MODER3_0;                                                                                                            //Port E for NSS Pin
GPIOE->MODER |= GPIO_MODER_MODER3_0;

/* SPI Configuration */
SPI1->CR2 |= SPI_CR2_FRXTH|SPI_CR2_RXDMAEN;     //Enable DMA but DMA is not used
// not sure if I need this?|SPI_CR1_CPOL|SPI_CR1_CPHA; 
SPI1->CR1 |= SPI_CR1_BR_1|SPI_CR1_SSM|SPI_CR1_SSI|SPI_CR1_MSTR|SPI_CR1_SPE;   //big endian, SPI@6MH, since using software set SPI_CR1_SSI to high for master mode


/* Slave Device Initialization */
SPI_WriteByte(CTRL_REG1_G,0x9f);
SPI_WriteByte(CTRL_REG4_G,0x10);
SPI_WriteByte(CTRL_REG5_G,0x10);

//receive test
uint8_t test =0xff;
uint8_t* spiDrPtr = (__IO uint8_t*)&SPI1->DR;
*spiDrPtr = 0x80|CTRL_REG1_G;
while(!(SPI1->SR & SPI_SR_TXE)){}
//SPI1->CR2 &= ~(SPI_CR2_FRXTH); //this is done in HAL not sure why though
*spiDrPtr = test;                                                   //Send dummy
while(!(SPI1->SR & SPI_SR_RXNE)){}
test = *spiDrPtr;
}

static void SPI_WriteByte(uint8_t regAdd, uint8_t data) {
  uint8_t arr[2] = {regAdd,data}; //16 bit data packing
  SPI1->DR = *((uint16_t*)arr);
}

Any suggestions?

2

There are 2 best solutions below

3
On
  1. Do not enable DMA if you do not use it.
  2. You need to force 16 bit access (not 32 bits)
static void SPI_WriteByte(uint8_t regAdd, uint8_t data) {
  uint8_t arr[2] = {regAdd,data}; //16 bit data packing
  *(volatile uint16_t *)&SPI1->DR = *((volatile uint16_t*)arr);
}
1
On

Try using FRXTH = 0, and do all DR reads and writes in 16-bit words, then just discard the first byte.

To the later question, about CPOL/CPHA. These bits control SPI transfer format on the bit level. Refer to the following image (from wikipedia). SPI message format image from wikipedia

CPOL = 0, CPHA = 0 is also called "SPI mode 0", data bits are sampled on the SCK rising edge

CPOL = 1, CPHA = 1 is also called "SPI mode 3", data bits are also sampled on the rising edge.

The difference between two modes is SCK level between transfers, and an extra falling edge before the first bit.

Some chips states explicitly that both mode 0 and mode 3 are supported. Yet in the I3G4250D datasheet, section 5.2: "SDI and SDO are, respectively, the serial port data input and output. These lines are driven at the falling edge of SPC and should be captured at the rising edge of SPC."

When data is sent to the chip from mcu in mode 0, mcu drives the MOSI line before the first rising edge. Thus a slave chip can recieve valid data in both mode 0 and mode 3. But when data is transfered from chip to the mcu, chip may need the first falling SCK edge to shift/latch first data bit to the MISO line and with mode 0 you'll receive the readings shifted on one bit.

I've emphasised 'may' word, because the chip could still work correcly in both modes, latching the first bit with falling nSS-edge, the manufacturer just didn't do the testings, or doesn't guarantees that it'll work with other revisions and in all conditions.