cancel
Showing results for 
Search instead for 
Did you mean: 

Reading 1 byte only in SPI half duplex

Tøm
Associate II

Hi everyone,

I'm using a P-NUCLEO-WB55 for an SPI application with a CMT2219A (HF receiver) which works in Half duplex.

I try to read its configuration registers by sending the address with the R/W bit set, then reading the bus by clocking during a byte (see the following picture:)

0693W00000NqsGdQAJ.pngThe problem is that when reading, the MCU is clocking during 2 bytes instead of 1 as you can see:

0693W00000NqsFuQAJ.pngIf I try to read 2 bytes, then the MCU is clocking during 3 bytes and so on...

Actually, the values read on the picture are correct, but we're loosing time reading an extra byte each time (and this is not clean even if this doesn't affect the integrity of the data).

Here is the SPI's configuration:

void SPI_CMT2219A_Init(void)
{
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_1LINE;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
    hspi1.Init.NSS = SPI_NSS_SOFT;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi1.Init.CRCLength = 0; // hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
    // hspi1.Init.CRCPolynomial = 7;
    hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLED;
    if (HAL_SPI_Init(&hspi1) != HAL_OK)
    {
        Error_Handler();
    }
}

And here's the function that produces this result:

void SPI_CMT2219A_read_config(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
{
    HAL_StatusTypeDef err = HAL_OK;
    uint8_t addr = 0;
 
    for (uint8_t i = 0; i < Size; i++)
    {
        addr = i | 0x80;
 
        HAL_GPIO_WritePin(CSB_CMT2219A_PORT, CSB_CMT2219A_PIN, GPIO_PIN_RESET); // Toggle CSB
        err = HAL_SPI_Transmit(hspi, &addr, 1, 1);
        if (HAL_OK != err)
            asm("nop");
 
        err = HAL_SPI_Receive(hspi, pData + i, 1, 1);
        if (HAL_OK != err)
            asm("nop");
 
        HAL_GPIO_WritePin(CSB_CMT2219A_PORT, CSB_CMT2219A_PIN, GPIO_PIN_SET); // Reset CSB
    }
}

I want to read the 62 configuration bytes for test purposes and finding a solution to this problem could lead to the solution to the fifo-reading issue I'm facing too.

As you probably guessed, I'm using the HAL library from CubeIDE.

Thanks in advance for your help!

2 REPLIES 2

> As you probably guessed, I'm using the HAL library from CubeIDE.

Don't. The halfduplex=bidir SPI requires tight timing, if you don't want Rx overruns. Read the bidir section of SPI chapter in RM.

JW

Tøm
Associate II

I found that it is possible to avoid the extra clock by modifying the HAL lib.

Actually there's a comment on top of the stm32wbxx_hal_spi.c file that mentions:

/*
Master Receive mode restriction:
(#) In Master unidirectional receive-only mode (MSTR =1, BIDIMODE=0, RXONLY=1) or
bidirectional receive mode (MSTR=1, BIDIMODE=1, BIDIOE=0), to ensure that the SPI
does not initiate a new transfer the following procedure has to be respected:
(##) HAL_SPI_DeInit()
(##) HAL_SPI_Init()
*/

I managed to get the extra clock away but I don't like doing so and I'm sure there's better way to stop the reception when the last wanted byte has been read.

I adapted a solution I found on a blog with the following code:

uint8_t send_and_read_byte(uint8_t cmd)
{
    uint8_t result;
    GPIO_ResetBits(GPIOA, GPIO_Pin_4); // CS low
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); //wait buffer empty
    SPI_SendData8(SPI1, cmd);
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); //wait finish sending
    // Read receiving FIFO until it is empty
    while (SPI_GetReceptionFIFOStatus(SPI1) != SPI_ReceptionFIFOStatus_Empty)
        SPI_ReceiveData8(SPI1);
    SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Rx);
    while (!(SPI1->SR & SPI_I2S_FLAG_RXNE)) ; // wait data received
    GPIO_SetBits(GPIOA, GPIO_Pin_4); // CS high
    SPI1->CR1 |= SPI_Direction_Tx;  // Set Tx mode to stop Rx clock
    result = SPI_ReceiveData8(SPI1);
    return result;
}

The solution suggests to set it on Tx Mode to stop the Rx Clock, my version implemented in the HAL library gives something like that:

(In the HAL_SPI_Receive() function)

  /* Check if the SPI is already enabled */
    if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
    {
      /* Enable SPI peripheral */
        __HAL_SPI_ENABLE(hspi);
    }
 
    /* Receive data in 8 Bit mode */
    if (hspi->Init.DataSize <= SPI_DATASIZE_8BIT)
    {
      /* Transfer loop */
        while (hspi->RxXferCount > 0U)
        {
          /* Check the RXNE flag */
            if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE))
            {
                // HACK
                /* read the received data */
                (* (uint8_t *)hspi->pRxBuffPtr) = *(__IO uint8_t *) & hspi->Instance->DR;
                hspi->pRxBuffPtr += sizeof(uint8_t);
                if (0 == --hspi->RxXferCount)
                {
                    // hspi->Instance->CR1 |= SPI_CR1_BIDIOE_Msk; // Disable Rx Mode
                    break;
                }
            }
            else
            {
              /* Timeout management */
                if ((((HAL_GetTick() - tickstart) >= Timeout) && (Timeout != HAL_MAX_DELAY)) || (Timeout == 0U))
                {
                    errorcode = HAL_TIMEOUT;
                    goto error;
                }
            }
        }
    }

It provides some quite good results but the peripheral waits to reach the timeout every time while the data has been successfully read:

0693W00000Nr5KiQAJ.pngIs there a way to make the peripheral understand that the reception is complete?

Thanks in advance !