cancel
Showing results for 
Search instead for 
Did you mean: 

Why do I receive the wrong number of bytes from SPI with HAL_SPI_Receive_DMA?

Jan1
Associate II

With an STM32F764 I want to receive 33 bytes from 11 daisy-chained 24 bit ADCs via SPI in read-only mode. The received data looks okay but there are 37 to 39 bytes in the buffer. Also with other byte numbers I receive 2 to 6 bytes more than expected.

HAL_SPI_Receive_DMA is called by a GPIO interrupt from an external ADC when the 33 bytes are ready. After finishing the DMA the buffer content is processed in Sampling(). There is no other communication taking place on SPI3.

I have attached a scope screenshot which shows the excess data in red.0690X000008j363QAA.png

/*SPI init for external ADCs (only Rx)*/
uint8_t SPI3_Init_ADC(void)
{
    hspi3.Instance = SPI3;
    hspi3.Init.Mode = SPI_MODE_MASTER;
    hspi3.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
    hspi3.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;
    hspi3.Init.CLKPhase = SPI_PHASE_1EDGE;
    hspi3.Init.NSS = SPI_NSS_SOFT;
    hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; /*SPI3_CLK = 12.5 MHz*/
    hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi3.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi3.Init.CRCPolynomial = 7;
    hspi3.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
    hspi3.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
    if (HAL_SPI_Init(&hspi3) != HAL_OK)
        return ERR;
    return OK;
}
    /*SPI MSP and DMA Initialization*/
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    /*SPI3: ADCs*/
    if(hspi->Instance==SPI3)
    {
        __HAL_RCC_SPI3_CLK_ENABLE(); /* Peripheral clock enable */
        __HAL_RCC_GPIOB_CLK_ENABLE();
        /*PB3 --> SPI3_SCK*/
        /*PB4 --> SPI3_MISO*/
        GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
        /*DMA 1 / channel 0 / stream 0 transfers data from external ADC to sample registers*/
        __HAL_RCC_DMA1_CLK_ENABLE(); /*DMA clock*/
        hdma_spi3_rx.Init.Channel = DMA_CHANNEL_0;
        hdma_spi3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; /*from SPI to memory*/
        hdma_spi3_rx.Init.PeriphInc = DMA_PINC_DISABLE; /*no increment on SPI side*/
        hdma_spi3_rx.Init.MemInc = DMA_MINC_ENABLE; /*increment sample memory address*/
        hdma_spi3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_spi3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_spi3_rx.Init.Mode = DMA_NORMAL;
        hdma_spi3_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
        hdma_spi3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        hdma_spi3_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
        hdma_spi3_rx.Init.MemBurst = DMA_PBURST_SINGLE;
        hdma_spi3_rx.Init.PeriphBurst = DMA_PBURST_SINGLE;
        hdma_spi3_rx.Instance = DMA1_Stream0;
        HAL_DMA_Init(&hdma_spi3_rx);
        HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0); /*highest priority for DMA interrupt*/
        HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn); /*enable DMA interrupt*/
        __HAL_LINKDMA(hspi, hdmarx, hdma_spi3_rx); /*link DMA1 to SPI3*/
    }
 
/*INTERRUPT HANDLERS*/
 
/*Data ready interrupt (/ADC_/DRDY) of external ADCs*/
void EXTI9_5_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_7); /*clear interrupt*/
    HAL_SPI_Receive_DMA(&hspi3, SplRxBuff, 33); /*get 33 bytes ADC data*/
}
 
/*DMA1 stream0 global interrupt (Rx of external ADCs complete)*/
void SPI_DMA_Rx_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&hdma_spi3_rx);
    Sampling();
}

1 ACCEPTED SOLUTION

Accepted Solutions
Jan1
Associate II

Thank you.

So is there a bug in SPI_DIRECTION_2LINES_RXONLY?

I changed it to SPI_DIRECTION_2LINES and programmed a Tx DMA but so far I couldn't receive any bytes.

In HAL_SPI_Receive_DMA I found:

HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
{
  HAL_StatusTypeDef errorcode = HAL_OK;
 
  if ((hspi->Init.Direction == SPI_DIRECTION_2LINES) && (hspi->Init.Mode == SPI_MODE_MASTER))
  {
    hspi->State = HAL_SPI_STATE_BUSY_RX;
    /* Call transmit-receive function to send Dummy data on Tx line and generate clock on CLK line */
    return HAL_SPI_TransmitReceive_DMA(hspi, pData, pData, Size);
  }

It looks like HAL_SPI_TransmitReceive_DMA is called here to clock out the number of bytes in Size. But it doesn't.

View solution in original post

3 REPLIES 3
S.Ma
Principal

Don t use

SPI_DIRECTION_2LINES_RXONLY

Use

SPI_DIRECTION_2LINES

Even if you don t activate MOSI as gpio.

And program both dma rx for data collection And Dma tx to control how many sck periods to generate.

Jan1
Associate II

Thank you.

So is there a bug in SPI_DIRECTION_2LINES_RXONLY?

I changed it to SPI_DIRECTION_2LINES and programmed a Tx DMA but so far I couldn't receive any bytes.

In HAL_SPI_Receive_DMA I found:

HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
{
  HAL_StatusTypeDef errorcode = HAL_OK;
 
  if ((hspi->Init.Direction == SPI_DIRECTION_2LINES) && (hspi->Init.Mode == SPI_MODE_MASTER))
  {
    hspi->State = HAL_SPI_STATE_BUSY_RX;
    /* Call transmit-receive function to send Dummy data on Tx line and generate clock on CLK line */
    return HAL_SPI_TransmitReceive_DMA(hspi, pData, pData, Size);
  }

It looks like HAL_SPI_TransmitReceive_DMA is called here to clock out the number of bytes in Size. But it doesn't.

Jan1
Associate II

Now it works. I have added a dummy Tx DMA for the number of bytes I want to receive:

/*DMA 1 / channel 0 / stream 0 transfers data from external ADC to sample registers*/
        __HAL_RCC_DMA1_CLK_ENABLE(); /*DMA clock*/
        hdma_spi3_rx.Init.Channel = DMA_CHANNEL_0;
        hdma_spi3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; /*from SPI to memory*/
        hdma_spi3_rx.Init.PeriphInc = DMA_PINC_DISABLE; /*no increment on SPI side*/
        hdma_spi3_rx.Init.MemInc = DMA_MINC_ENABLE; /*increment sample memory address*/
        hdma_spi3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_spi3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_spi3_rx.Init.Mode = DMA_NORMAL;
        hdma_spi3_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
        hdma_spi3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        hdma_spi3_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
        hdma_spi3_rx.Init.MemBurst = DMA_PBURST_SINGLE;
        hdma_spi3_rx.Init.PeriphBurst = DMA_PBURST_SINGLE;
        hdma_spi3_rx.Instance = DMA1_Stream0;
        HAL_DMA_Init(&hdma_spi3_rx);
        HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0); /*highest priority for DMA interrupt*/
        HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn); /*enable DMA interrupt*/
        __HAL_LINKDMA(hspi, hdmarx, hdma_spi3_rx); /*link DMA1 to SPI3 Rx*/
        /*DMA 1 / channel 0 / stream 5 sends data to SPI (no GPIO pin existent, only needed for clocking in right number of bytes)*/
        __HAL_RCC_DMA2_CLK_ENABLE(); /*DMA clock*/
        hdma_spi3_tx.Init.Channel = DMA_CHANNEL_0;
        hdma_spi3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; /*from memory to SPI*/
        hdma_spi3_tx.Init.PeriphInc = DMA_PINC_DISABLE; /*no increment on SPI side*/
        hdma_spi3_tx.Init.MemInc = DMA_MINC_DISABLE; /*no increment sample memory address*/
        hdma_spi3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_spi3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_spi3_tx.Init.Mode = DMA_NORMAL;
        hdma_spi3_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
        hdma_spi3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        hdma_spi3_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
        hdma_spi3_tx.Init.MemBurst = DMA_PBURST_SINGLE;
        hdma_spi3_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;
        hdma_spi3_tx.Instance = DMA1_Stream5;
        HAL_DMA_Init(&hdma_spi3_tx);
        __HAL_LINKDMA(hspi, hdmatx, hdma_spi3_tx); /*link DMA2 to SPI3 Tx*/

The Tx DMA is called together with the Rx DMA:

/*Data ready interrupt (/ADC_/DRDY) of external ADCs*/
void EXTI9_5_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_7); /*clear interrupt*/
    HAL_SPI_Transmit_DMA(&hspi3, NULL, ChNo * 3); /*send dummy bytes via SPI*/
    HAL_SPI_Receive_DMA(&hspi3, SplRxBuff, ChNo * 3); /*get ADC data*/
}
/*DMA1 stream0 global interrupt (Rx of external ADCs complete)*/
void SPI_DMA_Rx_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&hdma_spi3_tx);
    HAL_DMA_IRQHandler(&hdma_spi3_rx);
    if (hspi3.RxXferCount == ChNo * 3)
        Sampling();
}