Skip to main content
Explorer
November 19, 2025
Question

DMA Circular mode misalignment

  • November 19, 2025
  • 8 replies
  • 890 views

Hi everyone!

I'm trying to figure out what's wrong with my SPI Peripheral continous transmitting/receiving via DMA in Circular Mode. I think I have coded a pretty basic "Hello, World" for working with DMA, I've just generated code from .ioc file and then added a simple logic. This is my main.c:

 

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file : main.c
 * @brief : Main program body
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2025 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "spi.h"
#include "usart.h"
#include "usb_otg.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define SPI3_BUFFER_SIZE 2

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
volatile uint8_t spi3_tx_buffer[SPI3_BUFFER_SIZE] = {0};
volatile uint8_t spi3_rx_buffer[SPI3_BUFFER_SIZE] = {0};

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
void SPI3_Start_Comm(void);

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
 * @brief The application entry point.
 * @retval int
 */
int main(void)
{

 /* USER CODE BEGIN 1 */

 /* USER CODE END 1 */

 /* MCU Configuration--------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();

 /* USER CODE BEGIN Init */

 /* USER CODE END Init */

 /* Configure the system clock */
 SystemClock_Config();

 /* USER CODE BEGIN SysInit */

 /* USER CODE END SysInit */

 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_DMA_Init();
 MX_USART3_UART_Init();
 MX_USB_OTG_FS_PCD_Init();
 MX_SPI3_Init();
 /* USER CODE BEGIN 2 */

 SPI3_Start_Comm();

 /* USER CODE END 2 */

 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
 /* USER CODE END WHILE */

 /* USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */
}

/**
 * @brief System Clock Configuration
 * @retval None
 */
void SystemClock_Config(void)
{
 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

 /** Configure LSE Drive Capability
 */
 HAL_PWR_EnableBkUpAccess();

 /** Configure the main internal regulator output voltage
 */
 __HAL_RCC_PWR_CLK_ENABLE();
 __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

 /** Initializes the RCC Oscillators according to the specified parameters
 * in the RCC_OscInitTypeDef structure.
 */
 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
 RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
 RCC_OscInitStruct.PLL.PLLM = 4;
 RCC_OscInitStruct.PLL.PLLN = 216;
 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
 RCC_OscInitStruct.PLL.PLLQ = 9;
 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
 {
 Error_Handler();
 }

 /** Activate the Over-Drive mode
 */
 if (HAL_PWREx_EnableOverDrive() != HAL_OK)
 {
 Error_Handler();
 }

 /** Initializes the CPU, AHB and APB buses clocks
 */
 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK)
 {
 Error_Handler();
 }
}

/* USER CODE BEGIN 4 */

/**
 * @brief This function starts SPI Communication for Peripheral device
 * @retval None
 */
void SPI3_Start_Comm(void)
{
	HAL_SPI_TransmitReceive_DMA(&hspi3,(uint8_t*)spi3_tx_buffer,
			(uint8_t*)spi3_rx_buffer,SPI3_BUFFER_SIZE);
}

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{

	if(hspi->Instance == SPI3)
	{
		spi3_tx_buffer[0] = spi3_rx_buffer[0];
		spi3_tx_buffer[1] = spi3_rx_buffer[1];
	}

}

/* USER CODE END 4 */

/**
 * @brief This function is executed in case of error occurrence.
 * @retval None
 */
void Error_Handler(void)
{
 /* USER CODE BEGIN Error_Handler_Debug */
 /* User can add his own implementation to report the HAL error return state */
 __disable_irq();
 while (1)
 {
 }
 /* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/**
 * @brief Reports the name of the source file and the source line number
 * where the assert_param error has occurred.
 * file: pointer to the source file name
 * line: assert_param error line source number
 * @retval None
 */
void assert_failed(uint8_t *file, uint32_t line)
{
 /* USER CODE BEGIN 6 */
 /* User can add his own implementation to report the file name and line number,
 ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
 /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

 

As you can see, I just start communication in SPI3_Start_Comm() and then in HAL_SPI_TxRxCpltCallback I prepare the bytes received at N-time to echo back at N+1-time.

What I get is the peripheral echoing back bytes at N+x-time, after three or four garbage results at first. If I print results from controller point of view this is what I get:

    COPI CIPO
0: 0000 XXXX (garbage)
1: 0000 XXXX (garbage)
2: 0800 XXXX (garbage)
3: 0800 XXXX (garbage)
4: 1000 0000 (N+3 echo back - it should be N+1 or 0800). Missing N = 0 echo
5: 1000 0800 (and so on...)
6: 1800 0800
7: 1800 1000
8: 2000 1000
9: 2000 1800
10: 2800 1800
11: 2800 2000
12: 3000 2000
13: 3000 2800
14: 3800 2800
15: 3800 3000
16: 0000 3000
17: 0000 3800
18: 0800 3800
19: 0800 0000
20: 1000 0000
...

Peripheral device is STM32F722ZE. Controller is a black box.

Here's my spi.c and dma.c files generated from .ioc:

spi.c:

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file spi.c
 * @brief This file provides code for the configuration
 * of the SPI instances.
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2025 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "spi.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

SPI_HandleTypeDef hspi3;
DMA_HandleTypeDef hdma_spi3_rx;
DMA_HandleTypeDef hdma_spi3_tx;

/* SPI3 init function */
void MX_SPI3_Init(void)
{

 /* USER CODE BEGIN SPI3_Init 0 */

 /* USER CODE END SPI3_Init 0 */

 /* USER CODE BEGIN SPI3_Init 1 */

 /* USER CODE END SPI3_Init 1 */
 hspi3.Instance = SPI3;
 hspi3.Init.Mode = SPI_MODE_SLAVE;
 hspi3.Init.Direction = SPI_DIRECTION_2LINES;
 hspi3.Init.DataSize = SPI_DATASIZE_8BIT;
 hspi3.Init.CLKPolarity = SPI_POLARITY_HIGH;
 hspi3.Init.CLKPhase = SPI_PHASE_2EDGE;
 hspi3.Init.NSS = SPI_NSS_HARD_INPUT;
 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)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN SPI3_Init 2 */

 /* USER CODE END SPI3_Init 2 */

}

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

 GPIO_InitTypeDef GPIO_InitStruct = {0};
 if(spiHandle->Instance==SPI3)
 {
 /* USER CODE BEGIN SPI3_MspInit 0 */

 /* USER CODE END SPI3_MspInit 0 */
 /* SPI3 clock enable */
 __HAL_RCC_SPI3_CLK_ENABLE();

 __HAL_RCC_GPIOA_CLK_ENABLE();
 __HAL_RCC_GPIOB_CLK_ENABLE();
 /**SPI3 GPIO Configuration
 PA4 ------> SPI3_NSS
 PB3 ------> SPI3_SCK
 PB4 ------> SPI3_MISO
 PB5 ------> SPI3_MOSI
 */
 GPIO_InitStruct.Pin = 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(GPIOA, &GPIO_InitStruct);

 GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;
 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);

 /* SPI3 DMA Init */
 /* SPI3_RX Init */
 hdma_spi3_rx.Instance = DMA1_Stream0;
 hdma_spi3_rx.Init.Channel = DMA_CHANNEL_0;
 hdma_spi3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
 hdma_spi3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
 hdma_spi3_rx.Init.MemInc = DMA_MINC_ENABLE;
 hdma_spi3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
 hdma_spi3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
 hdma_spi3_rx.Init.Mode = DMA_CIRCULAR;
 hdma_spi3_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
 hdma_spi3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
 if (HAL_DMA_Init(&hdma_spi3_rx) != HAL_OK)
 {
 Error_Handler();
 }

 __HAL_LINKDMA(spiHandle,hdmarx,hdma_spi3_rx);

 /* SPI3_TX Init */
 hdma_spi3_tx.Instance = DMA1_Stream5;
 hdma_spi3_tx.Init.Channel = DMA_CHANNEL_0;
 hdma_spi3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
 hdma_spi3_tx.Init.PeriphInc = DMA_PINC_DISABLE;
 hdma_spi3_tx.Init.MemInc = DMA_MINC_ENABLE;
 hdma_spi3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
 hdma_spi3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
 hdma_spi3_tx.Init.Mode = DMA_CIRCULAR;
 hdma_spi3_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
 hdma_spi3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
 if (HAL_DMA_Init(&hdma_spi3_tx) != HAL_OK)
 {
 Error_Handler();
 }

 __HAL_LINKDMA(spiHandle,hdmatx,hdma_spi3_tx);

 /* SPI3 interrupt Init */
 HAL_NVIC_SetPriority(SPI3_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(SPI3_IRQn);
 /* USER CODE BEGIN SPI3_MspInit 1 */

 /* USER CODE END SPI3_MspInit 1 */
 }
}

void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{

 if(spiHandle->Instance==SPI3)
 {
 /* USER CODE BEGIN SPI3_MspDeInit 0 */

 /* USER CODE END SPI3_MspDeInit 0 */
 /* Peripheral clock disable */
 __HAL_RCC_SPI3_CLK_DISABLE();

 /**SPI3 GPIO Configuration
 PA4 ------> SPI3_NSS
 PB3 ------> SPI3_SCK
 PB4 ------> SPI3_MISO
 PB5 ------> SPI3_MOSI
 */
 HAL_GPIO_DeInit(GPIOA, GPIO_PIN_4);

 HAL_GPIO_DeInit(GPIOB, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5);

 /* SPI3 DMA DeInit */
 HAL_DMA_DeInit(spiHandle->hdmarx);
 HAL_DMA_DeInit(spiHandle->hdmatx);

 /* SPI3 interrupt Deinit */
 HAL_NVIC_DisableIRQ(SPI3_IRQn);
 /* USER CODE BEGIN SPI3_MspDeInit 1 */

 /* USER CODE END SPI3_MspDeInit 1 */
 }
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

 

dma.c:

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file dma.c
 * @brief This file provides code for the configuration
 * of all the requested memory to memory DMA transfers.
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2025 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "dma.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/*----------------------------------------------------------------------------*/
/* Configure DMA */
/*----------------------------------------------------------------------------*/

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/**
 * Enable DMA controller clock
 */
void MX_DMA_Init(void)
{

 /* DMA controller clock enable */
 __HAL_RCC_DMA1_CLK_ENABLE();

 /* DMA interrupt init */
 /* DMA1_Stream0_IRQn interrupt configuration */
 HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
 /* DMA1_Stream1_IRQn interrupt configuration */
 HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
 /* DMA1_Stream3_IRQn interrupt configuration */
 HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);
 /* DMA1_Stream5_IRQn interrupt configuration */
 HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);

}

/* USER CODE BEGIN 2 */

/* USER CODE END 2 */


What am I missing?

Thanks to anyone who will try to help!

    This topic has been closed for replies.

    8 replies

    Graduate
    November 19, 2025

    In the code above, you start SPI transfer only once and you don't print anything.

    Show the code in which you experience the problem - with transfer restart and result printing.

    Super User
    November 19, 2025

    As @gbm said above, debug from the STM32 perspective, as you appear not to know how exactly does the master behave. A good way to start would be to observe the SPI waveforms using oscilloscope/logic analyz

    Note, that SPI has a FIFO. How is SPIx_CR2.FRXTH set?

    JW

    g_timpAuthor
    Explorer
    November 19, 2025

    thanks @waclawek.jan and @gbm, since controller is a black box I can only debug the STM32 (even if I can see print from master, as I posted).
    HAL_SPI_TxRxCpltCallback is properly triggered each time the peripheral receives a request from the controller, so I am assuming that it is something about DMA circular buffer pointer (buffer collect indeed the received bytes, but the DMA sends those bytes back after two more transactions). Does it sound reasonable to you?
    Also, does my code look correct to you?
    Thanks,

    GT

    Super User
    November 19, 2025

    > DMA sends those bytes back after two more transactions

    Now it dawned on me.

    SPI has a 4-byte (32-bit) Tx FIFO. As soon as you enable SPI (and DMA in it), it sets TXE and that remains to be sent until FIFO is filled. DMA, when it sees TXE set, transfers data into SPI, so as soon as SPI/DMA is enabled, the FIFO is immediately filled up. That's your "garbage" at the beginning, the 4 extra bytes you see.

    If I am not mistaken, there's no workaround.

    JW

    Explorer
    November 19, 2025

    > If I am not mistaken, there's no workaround.

    Perhaps by pre-filling the FIFO manually, and subtracting the 4 bytes from the DMA transfer.

    But I rarely dealt with DMA in connection to SPI, most of my use cases were short transfer sequences based upon previous transfers, and thus interrupt-driven.

    Super User
    November 19, 2025

    > Perhaps by pre-filling the FIFO manually, and subtracting the 4 bytes from the DMA transfer.

    That won't help with the lag - those 4 bytes would get "into the queue", regardless of whether it's through DMA or manually.

    What would work is to transfer bytes to Tx not based on TXE, but as they arrive - using a single DMA triggered by RXNE and transferring from SPI_DR to SPI_DR - but this would be just an exercise anyway with no practical application, as the data then could not be stored in RAM and processed further.

    JW

    g_timpAuthor
    Explorer
    November 19, 2025

    I'm trying different things here, but I wasn't able to edit SPI registers (maybe I should set some bit somewhere to enable SPI register write?).

    Anyway, out of hope I tried to edit SPI data size and observed the behavior change significantly. Now, setting data size = 4 bits, I don't have shifted transaction anymore, but MSB and LSB seems to be inverted:

    /* SPI3 init function */
    void MX_SPI3_Init(void)
    {
    
     /* USER CODE BEGIN SPI3_Init 0 */
    
     /* USER CODE END SPI3_Init 0 */
    
     /* USER CODE BEGIN SPI3_Init 1 */
    
     /* USER CODE END SPI3_Init 1 */
     hspi3.Instance = SPI3;
     hspi3.Init.Mode = SPI_MODE_SLAVE;
     hspi3.Init.Direction = SPI_DIRECTION_2LINES;
     hspi3.Init.DataSize = SPI_DATASIZE_4BIT;
     hspi3.Init.CLKPolarity = SPI_POLARITY_HIGH;
     hspi3.Init.CLKPhase = SPI_PHASE_2EDGE;
     hspi3.Init.NSS = SPI_NSS_HARD_INPUT;
     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)
     {
     Error_Handler();
     }
     /* USER CODE BEGIN SPI3_Init 2 */
    
     /* USER CODE END SPI3_Init 2 */
    
    }

    Output now is:
       COPI CIPO
    0: 0000 0038
    1: 0000 0000
    2: 0800 0000
    3: 0800 0008
    4: 1000 0008
    5: 1000 0010
    6: 1800 0010
    7: 1800 0018
    8: 2000 0018
    9: 2000 0020

    I really don't understand and yes, I even tried to set first bit LSB and nothing change (even stranger).

    Any clues?

    Super User
    November 20, 2025

    > I don't have shifted transaction anymore

    You still do, and the reason is still the Tx FIFO, except this time the "garbage" is four 4-bit nibble (which in STM32 is stored/transferred as 4 bytes with only LSB containing the bits coming from/going to SPI), showing up in your printout as two bytes; the third byte (which is two 4-bit nibble) is the "natural" delay given by the 2-byte Rx-DMA-to-Tx-DMA transfer (i.e. the size of buffers in memory and the DMA transfer size).

    What you should do is:

    - replace the master with a master written by you, over which you have full control; it may even be (and it would even be better) a bit-banged one on the same STM32

    - after each frame, observe e.g. in debugger, what is the state of the DMA (mainly NDTR) and SPI (mainly the FIFO status bits) registers, and what is in the buffers in memory

    - draw yourself a diagram with the individual items in the chain (i.e. SPI receiver, SPI Rx FIFO, DMA/Rx memory buffer, DMA/Tx memory buffer, SPI Tx FIFO, SPI transmitter), and after each step, write down what data do you have in which item according to your understanding, and then compare with reality

    JW

    g_timpAuthor
    Explorer
    November 20, 2025

    Thanks JW, replace controller is not an option. I have tried to debug but that's not been really helpful and confused me a bit, to be honest. Sure thing I must study more these peripherals I want to use.

    It still seems strange to me that with 4-bit datasize it kind of works, but with inverted endianness:

       COPI CIPO
    0: 0065 6538
    1: 0065 6538
    2: 0865 6500
    3: 0865 6508
    4: 1065 6508
    5: 1065 6510
    6: 1865 6510
    7: 1865 6518
    8: 2065 6518
    9: 2065 6520

    Both controller and peripheral devices should be little-endian.

    Explorer
    November 20, 2025

    > Both controller and peripheral devices should be little-endian.

    The SPI peripheral has no endianness.
    You can configure it both ways.

    Ozone_0-1763648903484.png

     

     

    Super User
    November 20, 2025

    > replace controller is not an option

     

    It is always an option, when you want to gain an understanding of the details, to use some of the lowcost devboards, such as a Nucleo.

    JW

    Explorer
    November 20, 2025

    I read, that G4 & H7 have USART capable to run SPI mode and switch FIFO off. Same time usart has lower speed  compare to common spi, 12.5 - 21 MHz.

    In one of my project, when I need internal adc binary code to be pushed over spi as fast as possible I used dual timer approach.

    Tim-1 sets adc sampling and start tim-2 in OnePulse repetitive x4 mode , than tim-2 trigger spi master that  pushed "dummy" 3 bytes till 4-st right sample arrived. Tim-1 may generate phase shifted CS (NSS) that control external spi stream receiver to latch last byte only. Latency I get ~400 nsec at the best on G4, with spi 42 MHz

    g_timpAuthor
    Explorer
    November 20, 2025

    Guys thank you but I feel we are totally going off the rail here.

    JW: > It is always an option
    It's not, if you're asked to develop a SPI Peripheral continuously receiving from a specific controller :)

    Ozone: > You can configure it both ways.
    Yes, I know, that was my point. Don't you consider strange that if I change configuration nothing happens?

    MasterT: thanks but I was looking for a far simpler code for this purpose.

    Let me reformulate my question: how would you program a SPI Peripheral for continuously receiving data from a black box controller and echoing data back, such as when Controller sends N+1 data Peripheral responds with N data?
    Controller calls in a loop a function like TransmitReceive(spi_hdlr,txBuffer,rxBuffer,size), where buffers are of uint8_t type and size = 2, then a delay of a few microseconds.

    Super User
    November 20, 2025

    > how would you program a SPI Peripheral for continuously receiving data from a black box controller and echoing data back, such as when Controller sends N+1 data Peripheral responds with N data?

    1) Send a single byte of dummy data to seed the TXFIFO. Might need to be 2 bytes, but I think only one will work. DMA should be fast enough to turn around data within a single SPI clock.

    2) Set up DMA to trigger on RXNE and transfer that data the TXFIFO.

     

    Not a particularly useful setup in my opinion, but it will do what you asked--echoing bytes back as they are received. Byte N output is the same as received byte N - 1 (or N-2 if two bytes are needed).

    g_timpAuthor
    Explorer
    November 21, 2025

    Thanks @TDK, I've tried but surely I am still missing something. This is how I have changed my SPI3_Start_Comm function:

    void SPI3_Start_Comm(void)
    {
    	uint8_t dummy = 0xFF;
    	HAL_SPI_Transmit(&hspi3, (uint8_t*)&dummy,1, HAL_MAX_DELAY);
    
    	HAL_SPI_TransmitReceive_DMA(&hspi3,(uint8_t*)spi3_tx_buffer,
    			(uint8_t*)spi3_rx_buffer,SPI3_BUFFER_SIZE);
    }

     

    spi.c data size is back to 8-bit:

    /* SPI3 init function */
    void MX_SPI3_Init(void)
    {
    
     /* USER CODE BEGIN SPI3_Init 0 */
    
     /* USER CODE END SPI3_Init 0 */
    
     /* USER CODE BEGIN SPI3_Init 1 */
    
     /* USER CODE END SPI3_Init 1 */
     hspi3.Instance = SPI3;
     hspi3.Init.Mode = SPI_MODE_SLAVE;
     hspi3.Init.Direction = SPI_DIRECTION_2LINES;
     hspi3.Init.DataSize = SPI_DATASIZE_8BIT;
     hspi3.Init.CLKPolarity = SPI_POLARITY_HIGH;
     hspi3.Init.CLKPhase = SPI_PHASE_2EDGE;
     hspi3.Init.NSS = SPI_NSS_HARD_INPUT;
     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)
     {
     Error_Handler();
     }
     /* USER CODE BEGIN SPI3_Init 2 */
    
     /* USER CODE END SPI3_Init 2 */
    
    }

     

    The only effect is that byte order is reversed, delay is still there:
       COPI CIPO
    0: 0000 0030
    1: 0000 0038
    2: 0800 0038
    3: 0800 0000
    4: 1000 0008
    5: 1000 0008
    6: 1800 0010
    7: 1800 0010
    8: 2000 0018
    9: 2000 0018
    10: 2800 0020

    I have tried with two dummy bytes, and byte order is not reversed.

    Not a particularly useful setup in my opinion
    I don't get it, why it is not useful to you and what do you suggest?