Skip to content

Wrong SPI transfer in 16-bit mode on STM32F7xx #3445

@kristofmulier

Description

@kristofmulier

Description

  • Type: Bug
  • Priority: Blocker

Bug

Target
STM32F7xx

Toolchain:
GCC_ARM

mbed-cli version:
0.9.10

Expected behavior
The bug shows up when you try to send a single 16-bit 'word' over an SPI link which is configured in 16-bit mode:

    // Configure the SPI in 16-bit mode
    SPI spi(PA_7, PA_6, PA_5);
    DigitalOut cs(PD_14);
    cs = 1;
    
    // Send regularly a single 16-bit 'word' over the link
    while(true)
    {
        Thread::wait(40);
        
        cs = 0;
        int response = spi.write(0x8FA3);    // Transfer one single 16-bit 'word'
        cs = 1;
    }

This is what you would expect to see on the oscilloscope:

Actual behavior
Unfortunately, the oscilloscope shows that after outputting the intended 16-bit word, the chip outputs a second one (equal to 0x0000) that should not be there:

Steps to reproduce
To reproduce the error, your main.cpp file should be the following code:

#include "mbed.h"
DigitalOut led1(LED1);

int main()
{
    Serial logger(USBTX, USBRX);
    logger.baud(115200);
    logger.printf("Test application started\r\n");

    SPI spi(PA_7, PA_6, PA_5);
    DigitalOut cs(PD_14);
    cs = 1;
    
    spi.format(16, 0);         // 16-bit mode, pol = 0, pha = 0
    spi.frequency(2000000);    // 2 Mhz SPI speed


    int i = 0;
    while (true) {
        led1 = !led1;
        Thread::wait(40);
        
        cs = 0;
        int response = spi.write(0x8FA3);    // Transfer one single 16-bit 'word'
        cs = 1;
    }
}

Solution

I have traced the bug back to the file stm_spi_api.c . The function spi_master_write(..) is called to transfer the value. In turn, this function calls the ST HAL function HAL_SPI_TransmitReceive(..). One of the parameters handed over is << size >>.
Mbed-OS wrongly assumed that this parameter represents the "number of bytes" to be transmitted over the SPI link. However, this parameter actually represents the "number of words" to be transmitted. A single 16-bit value is just one "word" - assuming that your SPI is configured in 16-bit mode.

The function spi_master_write(..) should therefore be changed:

int spi_master_write(spi_t *obj, int value)
{
    uint16_t size, Rx, ret;
    struct spi_s *spiobj = SPI_S(obj);
    SPI_HandleTypeDef *handle = &(spiobj->handle);

    // ERROR: The << size >> parameter in the ST HAL_SPI_TransmitReceive(..)
    //        function does not represent the nr. of bytes but rather the
    //        number of words (a word is 1 byte in 8-bit mode and 2 bytes in
    //        16-bit mode).

    // size = (handle->Init.DataSize == SPI_DATASIZE_16BIT) ? 2 : 1;
    size = 1;

    /*  Use 10ms timeout */
    ret = HAL_SPI_TransmitReceive(handle,(uint8_t*)&value,(uint8_t*)&Rx,size,10);

    if(ret == HAL_OK) {
        return Rx;
    } else {
        DEBUG_PRINTF("SPI inst=0x%8X ERROR in write\r\n", (int)handle->Instance);
        return -1;
    }
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions