Skip to main content
Mikas Longbardi
Associate III
August 21, 2017
Question

SPI bugs in F446/429?

  • August 21, 2017
  • 14 replies
  • 2452 views
Posted on August 21, 2017 at 03:58

Surely this issue has been beaten to death already?

SPI3:

Are there any known 'end of byte/word transmit' function (soft/hardware)

working on SPI3 in master mode? Actually in 'any' mode,  im just doing

simple plain periodic (plenty of dead time in between transmits, not

constant that would hold BSY low) transmit with no interference (i hope)

of  NSS/SSI crap, only GPIO pin as strobe for external shift register.

I cant detect any SPI BSY being set during transmission as it should be!

Is there any specific setup flow and init of SPI logic needed?

SPI3->DR = ledbuff;                                  //Write SPI transmit reg 

while((SPI3->SR & SPI_SR_TXE) == 0)  //Test until TXE flag is 1

{}

while((SPI3->SR & SPI_SR_BSY) == 1)  //Test until Busy flag is 0

{}

According to errata for rev A of F446 and rev11 for F429 there are complex

bugs in SPI module and equal complex suggestions for workarounds that

apparently dont work ,BSY mentioned.

Is there a workaround that one could use RXNE (MISO-MOSI loopback)

or something else as a detector?

Contemplating use USART instead since it works out of the box by detecting

TC flag unfortunately USART's in synchronous mode is much slower then SPI.
    This topic has been closed for replies.

    14 replies

    Tesla DeLorean
    Guru
    August 21, 2017
    Posted on August 21, 2017 at 08:13

    Don't people use the RXNE flag to indicate the last bit out finished?

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    William Warnots
    Associate II
    August 21, 2017
    Posted on August 21, 2017 at 09:45

    uint8_t dSPIN_Write_Byte(uint8_t byte)

    {

    /* Buffer used for transmission */

    uint8_t aTxBuffer[] = {0x00};

    /* Buffer used for reception */

    uint8_t aRxBuffer[] = {0xF};

    aTxBuffer[0] = byte;

    HAL_GPIO_WritePin(dSPIN_nSS_Port, dSPIN_nSS_Pin, 0);

    HAL_Delay(0.000003); //3000ns

    if(HAL_SPI_TransmitReceive(&dSPIN_SPI, (uint8_t *)aTxBuffer, (uint8_t *)aRxBuffer, 1, 250) != HAL_OK)

    { }

    while (__HAL_SPI_GET_FLAG(&dSPIN_SPI, SPI_FLAG_BSY) != 0);

    HAL_GPIO_WritePin(dSPIN_nSS_Port, dSPIN_nSS_Pin, 1);

    HAL_Delay(0.0008);

    return aRxBuffer[0];

    }
    Mikas Longbardi
    Associate III
    August 21, 2017
    Posted on August 21, 2017 at 20:25

    >>>Don't people use the RXNE flag to indicate the last bit out finished?

    That's what i thought could work in a 

    MOSI-MISO loopback l

    ast bit of transmit detect

    in some modes other then master MOSI only but apparently it's not needed since this

    works in

    master MOSI only 

    :

    SPI3->DR = count;                                          //Write SPI transmit reg with counter value

    while((SPI3->SR & SPI_SR_RXNE) == 0) {}  //Test RXNE flag is 1

    GPIOC->BSRR |= GPIO_BSRR_BS_11;       //Set PC11 high, strobe signal start

    GPIOC->BSRR |= GPIO_BSRR_BR_11;       //Set PC11 low, strobe signal end

    dummy = SPI3->DR;                                       //Dummy read SPI RD to clear RXNE flag

    Note:

    CPOL=0 and CPHA=0 , data captured on rising CLK edge.

    CLK rate lowest, then PC11 strobe are 2us late after last rising CLK edge

    .

    CLK rate highest, then PC11 strobe are ca 2uS late after 

    last falling CLK edge.

    Why? Havent investigated and frankly i dont care, wasted 

    way 

    to much time

    on this crappy/buggy ST SPI design.

    But it seams to be enough time for various HC shift registers other CMOS

    shifters needs longer/delayed strobe times which can be fixed by adding

    software as usual.

    >>>

    aRxBuffer, 1, 250) != HAL_OK)

    Sorry dont use HAL only direct register but

     parts of your code is blocking by nature. 

    Noticed ST seams to have 'redesigned' the SPI module in the upcoming H7, about time!

    Tesla DeLorean
    Guru
    August 21, 2017
    Posted on August 21, 2017 at 21:20

    The peripheral is synchronous and symmetrical, same clock moving data in/out, with selectable phase

    Don't do this (ie RMW)

    GPIOC->BSRR |= GPIO_BSRR_BS_11;       //Set PC11 high, strobe signal start

    GPIOC->BSRR |= GPIO_BSRR_BR_11;       //Set PC11 low, strobe signal end

    Just write the register, it uses combinational logic to set or clear the specific bits you write into the BSRR

    ie equivalent to GPIOC->ODR = (GPIOC->ODR | BSRRL) & ~BSRRH in a single atomic action

    GPIOC->BSRR = (1 << 11); // Set bit 11

    GPIOC->BSRR = (1 << (11 + 16)); // Clear bit 11

    The USART and SPI have design complexity that an undergrad IC design major should be able to implement using 1990's schematic capture tools, and they didn't really understand practical uses cases for chip select. Definitely needed someone on the design team who had a better grip of logic, writing software drivers and actually using an assortment of SPI parts. They went to town on the I2C peripheral, but I'm not sure it helped the software implementation

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    Mikas Longbardi
    Associate III
    August 21, 2017
    Posted on August 21, 2017 at 23:31

    >>The peripheral is synchronous and symmetrical, same clock moving data in/out, with selectable phase

    The problem with BSY according to errata is that it loses synchronicity with bus clock, BSY can become

    sporadic therefore ST recommends TXE, RXNE as detectors. Seams ST uninterested to fix it. 

    >>Don't do this (ie RMW)

    >>GPIOC->BSRR = (1 << (11+16));  //Set bit 11

    But...

    GPIOC->BSRR = (1 << 27);  //Clear bit 11..  is clearer.

    Instead, can i do?:

    GPIOC->BSRR = GPIO_BSRR_BS_11;  

     //Set PC11 high

    GPIOC->BSRR = GPIO_BSRR_BR_11;  //Set PC11 low

    It gives the same disassembly and timing as your suggestion.

    And for those who wonder about the difference in timing.

    This makes PC11 c:a 1.5uS late and 0.7uS wide. 10 instructions

    GPIOC->BSRR |= GPIO_BSRR_BS_11; //Set PC11 high

    GPIOC->BSRR |= GPIO_BSRR_BR_11; //Set PC11 low

    This makes PC11 c:a 1.2uS late and 0.45uS wide. 6 instructions

    GPIOC->BSRR = (1 << 11); // Set bit 11

    GPIOC->BSRR = (1 << (11 + 16)); // Clear bit 11

    Disclaimer:! I have an old uncalibrated shabby scope and no compiler optimization!

    Back in c:a 1986-90+'ish we had 68HC11F1  who had splendid auto address,  auto CS toggle SPI

    well working bug free and superb documentation, ST designers should read that manual

    to get inspiration.

    Tesla DeLorean
    Guru
    August 22, 2017
    Posted on August 22, 2017 at 01:51

    >>

    Seams ST uninterested to fix it. 

    I think it is a stance of not creating dozens of stepping to have to special case, and not having people with legacy golden firmware images suddenly not behaving in a constant/predictable way from one order of chips to another.

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    Mikas Longbardi
    Associate III
    August 22, 2017
    Posted on August 22, 2017 at 20:19

    >>>I think it is a stance of not creating dozens of stepping to have to special case, and not having

    >>>people with legacy golden firmware images suddenly not behaving in a constant/predictable

    >>>way from one order of chips to another.

    It might be so, but perhaps a bit of a contradiction since customers are  recommended to use RXNE

    instead of BSY so if they fix BSY it would still be compatible and transparent with ST suggested

    RXNE etc workarounds.

    I bet it's about economics and not firmware, ST screwed it up so they thought it's cheaper to use

    RXNE then to fix BSY logic and that makes perfect sense out of an economic situation.

    If i would be the ST top head boss of MCU design i would indeed make the same decision.

    So did ST ever fix I2C logic  or should i just go soft pin toggling instead? Pherhaps they fixed in H7?

    mckenney
    Associate
    August 25, 2017
    Posted on August 25, 2017 at 18:35

    while((SPI3->SR & SPI_SR_BSY) == 1)  //Test until Busy flag is 0

    Um, SPI_SR_BSY is 0x80, not 0x01.

    Mikas Longbardi
    Associate III
    August 26, 2017
    Posted on August 26, 2017 at 12:15

    Good Loordy yes!

    Tesla DeLorean
    Guru
    August 26, 2017
    Posted on August 26, 2017 at 14:58

    That == 1 is not the right test for the bit mask chosen, != 0 would work

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    Mikas Longbardi
    Associate III
    August 27, 2017
    Posted on August 27, 2017 at 23:55

    >>>That == 1 is not the right test for the bit mask chosen, != 0 would work

    Sorry to say but that (not equal to zero) is as stone dead as my  ==1

    //Test until Busy flag is 0

    >>>Um, SPI_SR_BSY is 0x80, not 0x01.

    Hum, what's this 0x80? It's address?

    Then this ST manufactured code also wrong?

    while((RCC->CR & RCC_CR_PLLRDY) == 0) {}
    Tesla DeLorean
    Guru
    August 28, 2017
    Posted on August 28, 2017 at 01:51

    You're doing a bit wise AND so 0x40 & 0x80 = 0, so a comparison with 0 or not 0 will work

    0x80 & 0x80 = 0x80

    It is a bit in a register, ie bit 7, or (1 << 7)

    0690X000006042KQAQ.jpg
    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    Mikas Longbardi
    Associate III
    August 28, 2017
    Posted on August 28, 2017 at 03:31

    Right, had completely forgot , thanks Clive and Vangelis!

    Late night edit:

    Went back to SPI and spoked around a bit more why

    while((SPI3->SR & SPI_SR_BSY) !=0) {}  //Test BSY if it's 1, if 0 then exit test.

    didnt work and pondered if there is some kind of delay issues with BSY 

    (errata talks about sync issues with internal buses) or the cpu is to quick

    to test BSY and sure enough 12 nop's is what it took to detect BSY.

    asm('nop; '); // nop instruction as delay

    asm('nop; '); // nop instruction as delay

    asm('nop; '); //nop instruction as delay

    asm('nop; '); //nop instruction as delay

    asm('nop; '); // nop instruction as delay

    asm('nop; '); // nop instruction as delay

    asm('nop; '); // nop instruction as delay

    asm('nop; '); // nop instruction as delay

    asm('nop; '); // nop instruction as delay

    asm('nop; '); // nop instruction as delay

    asm('nop; '); // nop instruction as delay

    asm('nop; '); // nop instruction as delay

    while((SPI3->SR & SPI_SR_BSY) !=0) {} //Test BSY if it's 1, if 0 then exit test.

    or 2 dummy reads/writes to 'something' also works such as

    GPIOC->BSRR = GPIO_BSRR_BR_11; //Set PC11 low, used as delay

    GPIOC->BSRR = GPIO_BSRR_BR_11; //Set PC11 low, used as delay

    while((SPI3->SR & SPI_SR_BSY) !=0) {} //Test BSY if it's 1, if 0 then exit test.

    I noted that detecting BSY is 'SPI clock rate sensitive' nop's bails out if rate

    to high while dummy's obviously longer and behaves more like detecting

    NRXE.

    Perhaps also why Williams HAL code above works, loads of delays checking

    HALs dependencies etc, etc what not's.