Skip to main content
tau
Associate III
February 28, 2017
Solved

SPI 8bit data length

  • February 28, 2017
  • 8 replies
  • 4576 views
Posted on February 28, 2017 at 16:56

Hi, I'm setting up SPI comms with a slave device. (ADXL350 accelerometer) This is on an STM32F767ZIT6. I should point out I put this on a nucleo -'F746 board but it's all working fine so far at 200MHz with other peripherals presenting no problem.

I've selected 8 bit data size, but am seeing 16 clocks on a scope. I use a read function to read address 0x00, the device's ''who_am_I'' register, and get 0xE5 back. In theory this is ok.

However, I see the image here:

0690X00000603hHQAQ.jpg

Yellow = MOSI

Cyan = MISO

Red = CLK

Green = _CS

The accelerometer is replying <0xE5> 3 times (trace 2 in cyan) because of this 16bit data size. If I set it to 4bit data size, I get 8 data clocks. But then I get a nybble in the low byte and a nybble in the high byte.

https://community.st.com/0D50X00009XkeupSAB

http://www.st.com/content/ccc/resource/technical/document/technical_note/58/17/ad/50/fa/c9/48/07/DM00054618.pdf/files/DM00054618.pdf/jcr:content/translations/en.DM00054618.pdf

I use my own transfer code, but copied HAL initialisation code as follows:

void spiSetup(void)

{

   hspi4.Instance = SPI4;

   hspi4.Init.Mode = SPI_MODE_MASTER;

   hspi4.Init.Direction = SPI_DIRECTION_2LINES;

   hspi4.Init.DataSize = SPI_DATASIZE_8BIT;

   hspi4.Init.CLKPolarity = SPI_POLARITY_HIGH;

   hspi4.Init.CLKPhase = SPI_PHASE_2EDGE;

   hspi4.Init.NSS = SPI_NSS_SOFT;

   hspi4.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;

   hspi4.Init.FirstBit = SPI_FIRSTBIT_MSB;

   hspi4.Init.TIMode = SPI_TIMODE_DISABLE;

   hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

   hspi4.Init.CRCPolynomial = 7;

   hspi4.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;

   hspi4.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

   if (HAL_SPI_Init(&hspi4) != HAL_OK)

   {

      //Error_Handler();

      while(1){};

   }

   SPI4->CR1 |= SPI_CR1_SPE;

}

All I'm doing is writing to SPI4->DR and waiting for the TXNE or BUSY flags to clear.

Any help would be appreciated.

null
    This topic has been closed for replies.
    Best answer by waclawek.jan
    Posted on February 28, 2017 at 18:32

    Of course it should've been *(uint8_t *)& ... :blushing: Mea culpa.

    JW

    [EDIT] and more like *(__IO uint8_t *)&SPI->DR

    [EDIT2] have you read the Data packing subchapter of SPI chapter in RM?

    8 replies

    waclawek.jan
    Super User
    February 28, 2017
    Posted on February 28, 2017 at 17:07

    You have to write only 8 bits to the SPI_DR, see 'data packing' in the SPI chapter of RM.

    *(uint8_t *)SPI->DR = data;

    There may be some function or macro for this in Cube; I don't Cube so I don't know.

    JW

    tau
    tauAuthor
    Associate III
    February 28, 2017
    Posted on February 28, 2017 at 17:18

    So after just showing a colleague, we tried transmitting just one byte. I still get the 0xE5 in the data register as expected, and this time there are only 16 clock cycles, as expected normally.

    But what if I want to read 2 bytes?

    Address +byte1 + byte2 = 3 bytes. But I would be sending 4, or most probably 6, because any write to the DR incurs a 16clock transmit overhead.

    Please tell me this is not a bug, so I can continue to investigate this part for our inverters. DP FPU, 200MHz, triple interleaved 12bit ADC ticks all my other boxes.

    S.Ma
    Principal
    February 28, 2017
    Posted on February 28, 2017 at 17:19

    It seems that a physical write on the DR must be 8 bit, otherwise, the MSB and LSB will be queued onto the FIFO and 2x8bit will be transmitted.

    tau
    tauAuthor
    Associate III
    February 28, 2017
    Posted on February 28, 2017 at 18:28

    Thanks for the suggestions. Not fixed it yet. I've resorted to hardcoding values in the test function.

    So this bit (in bold):

    *(uint8_t *)SPI4->DR = data;

    breaks my function. I have a working SPI transmit that writes two bytes at once out the fifo, and when I downcast anything to a uint8 ptr, it breaks. CS goes low, and then I get an unhandled interrupt which I'm going to guess is a hard fault / misalignment issue.

    Here's my actual code:

    /*

    * Accelerometer SPI layer data transfers

    * */

    uint8_t readByte(uint8_t addr)

    {

          ACCL_CSLO();

          delayLoop(5); // small CS wait

          SPI4->DR = addr|0x80; // Reading from addr

          while(!(SPI4->SR & SPI_SR_TXE)){}; // Wait for empty

          bSpiData = SPI4->DR; // Read rcvd data

          SPI4->DR = 0xFF; // Write 2nd byte

          while(SPI4->SR & SPI_SR_BSY){}; //

          bSpiData = SPI4->DR; //

          ACCL_CSHI();

          delayLoop(10); // post CS delay

          return bSpiData;

    }

    Putting *(uint8_t*) anywhere before SPI4->DR breaks the function. Reads or writes.

    Any more suggestions?

    waclawek.jan
    waclawek.janBest answer
    Super User
    February 28, 2017
    Posted on February 28, 2017 at 18:32

    Of course it should've been *(uint8_t *)& ... :blushing: Mea culpa.

    JW

    [EDIT] and more like *(__IO uint8_t *)&SPI->DR

    [EDIT2] have you read the Data packing subchapter of SPI chapter in RM?

    waclawek.jan
    Super User
    February 28, 2017
    Posted on February 28, 2017 at 18:39

    As I've said I don't Cube, but judging from

    https://community.st.com/0D50X00009XkdsnSAB

      I'd say that that function should have used the 8-bit write, given

     hspi4.Init.DataSize = SPI_DATASIZE_8BIT;

    I am not going to investigate why it didn't.

    JW

    tau
    tauAuthor
    Associate III
    February 28, 2017
    Posted on February 28, 2017 at 18:40

    Fixed it:

    /*

    * Accelerometer SPI layer data transfers

    * */

    uint8_t readByte(uint8_t addr)

    {

    static uint8_t* spiDrPtr = (uint8_t*)&SPI4->DR;

    ACCL_CSLO();

    delayLoop(5); // small CS wait

    *spiDrPtr = addr|0x80; // Reading from addr

    while(!(SPI4->SR & SPI_SR_TXE)){}; // Wait for empty

    bSpiData = *spiDrPtr; // Read rcvd data

    *spiDrPtr = 0xFF; // Write 2nd byte

    while(SPI4->SR & SPI_SR_BSY){}; //

    bSpiData = *spiDrPtr; //

    ACCL_CSHI();

    delayLoop(10); // post CS delay

    return bSpiData;

    }

    Resulting in:

    0690X00000603hRQAQ.jpg

    And as for why it replies 0xE5 during the first byte, I don't know/care atm.

    Thanks for the ''pointers'' guys, hope this helps someone else

    waclawek.jan
    Super User
    February 28, 2017
    Posted on February 28, 2017 at 18:43

    And as for why it replies 0xE5 during the first byte, I don't know/care atm.

    Maybe that's a remainder from previous communication.

    Try to reset the accelerometer (if it does not have an explicit reset method then power off/on) whether it makes any difference.

    JW

    tau
    tauAuthor
    Associate III
    March 1, 2017
    Posted on March 01, 2017 at 09:14

    Thanks for the help, and I missed the address ampersand too Jan - until it came to creating a pointer myself.

    I'll probably end up using your method as I want to write and test this code across several MCUs (STM32F7 / STM32H7 / KV58F / SAMV71) and having something I can stick in a header file based on MCU definition is good.

    Amel NASRI
    ST Technical Moderator
    March 2, 2017
    Posted on March 02, 2017 at 15:48

    Hi

    Slater.Tau

    ‌,

    Your question is marked as answered thanks to the help of

    Waclawek.Jan

    ‌ and

    Centauris.Alpha

    ‌. But I would like to understand the following:

    I should point out I put this on a nucleo -'F746 board but it's all working fine so far at 200MHz with other peripherals presenting no problem.

    Other than using different chips, are there other differences for SPI communication tested with STM32F767? Were you using same code?

    It is interesting to understand this because both products have same SPI features.

    Thanks for sharing these details.

    -Amel

    To give better visibility on the answered topics, please click on "Best Answer" on the reply which solved your issue or answered your question.
    tau
    tauAuthor
    Associate III
    March 2, 2017
    Posted on March 02, 2017 at 17:35

    To be honest this code has been developed entirely on the '767. The process has been:

    1. Change '746ZG to 767ZI on Nucleo board

    2. Create a project in SW4STM32 for the '767

    3. Use CubeMX to generate initialisation code for a '767 chip - no board selected.

    4. Set up debugging and get coding...

    I do have an unadulterated '746 nucleo board, so could in theory just port/copy the code over. (assuming 746's SPI4 is of the same type) Would be a valuable step for me as I could see just how portable my code is already. I think I might even be using the SystemClock_Config() code from a 746 disco board on one of my other projects actually.

    If I have any more info, I'll post back / blog it / link to it or something.

    Regards