Skip to main content
Graduate
September 28, 2025
Question

Optimization breaks SPI code on L562

  • September 28, 2025
  • 3 replies
  • 741 views

L562 processor.  SPI1.  Blocking mode.  Two sequential writes, second write variably returns error.

code:

// uses CS if USE_CS is set to CS_ACTIVE (default)
// CS is not used if USE_CS is set to CS_INACTIVE

uint32_t HAL_SPI::Send(
						uint8_t* cmdptr,
						uint32_t cmd_count,
						uint8_t* dataptr,
						uint32_t data_count,
						bool USE_CS,
						enum SPI_TRANSFER_MODE Override)
{

	enum SPI_TRANSFER_MODE		NEW_mode = OUT_mode;
	int							error_code;

//	int							old_divisor;

	// ********************************** check input parameters***********************************
	// need to check overrides
	switch (Override)
	{
		case SPI_DMA:			// TRANSMIT IS DMA FROM BUFFER, RECEIVE IS IRQ QUEUE (MAY CHANGE)
		case SPI_IRQ:			// TRANSMIT IS IRQ DRIVEN
		case SPI_BLOCKING:		// TRANSMIT AND RECEIVE ARE BYTE BLOCKING
		{
			NEW_mode = Override;
			break;
		}
		default:
		{
			NEW_mode = OUT_mode;
			// leave mode as is, cannot override
		}
	}

	switch (NEW_mode)
	{
		case SPI_BLOCKING:		// TRANSMIT AND RECEIVE ARE BYTE BLOCKING
		{
			time_start = _MICROSECOND_TIMER.Instance->CNT;
			// always semaphore protected
			if (!dedicated) xSemaphoreTake(busy, portMAX_DELAY);
			// direct call to STMICRO driver, blocking mode
			hspi->Instance->CR1 &= ~SPI_BAUDRATEPRESCALER_256;// reset to highest baudrate
			hspi->Instance->CR1 |= transmit_divisor;

			if (RESET.port != nullptr) RESET.set();				// make sure reset if high
			if (USE_CS) RESET_CS();								// CS low if needed
			/*
				HAL_OK = 0x00,
				HAL_ERROR = 0x01,
				HAL_BUSY = 0x02,
				HAL_TIMEOUT = 0x03
 	 	 	 */


			if ((cmdptr != nullptr) && (cmd_count != 0))
			{
				// command pointer says to send data with A0 down
				if (A0.port != nullptr) A0.reset();
				result = HAL_SPI_Transmit(hspi, cmdptr, cmd_count, timeout);
				// place to add delay if available
			}


			if ((dataptr != nullptr) && (data_count != 0))
			{
				// command pointer says to send data with A0 down
				if (A0.port != nullptr) A0.set();
				result = HAL_SPI_Transmit(hspi, dataptr, data_count, timeout);
				// place to add delay if available
//				while (hspi->State == HAL_SPI_STATE_BUSY_TX)
//				{
//					HAL_Delay(1);
//				}
			}
			if (USE_CS) SET_CS();								// CS high if needed
			// error reporting
			ERROR_CODE = hspi->ErrorCode;
			ERROR_REASON = (enum COMM_ERROR_TYPE) ERROR_CODE;
			operations++;
			// record errors if needed
			switch (result)
			{
				case HAL_ERROR: 					//= 0x01U,
				{
					fail++;
					break;
				}
				case HAL_BUSY: 					//= 0x02U,
				{
					fail++;
					break;
				}
				case HAL_TIMEOUT: 					//= 0x03U
				{
					fail++;
					break;
				}
				case HAL_OK:
				{
					// was OK
					succeed++;
					break;
				}
			}

			// return semaphore for next operation
			if (!dedicated) xSemaphoreGive(busy);
			time_stop = _MICROSECOND_TIMER.Instance->CNT;
			delta_time = time_stop - time_start;
			break;
		}
		

 

Using optimization for debug, this code works perfectly. 

Using G0 (no optimization), the code fails either at line #70 after executing line #61 (write two bytes), or the second call to the routine at line #61 (writing one byte per call).

This is a display driver routine.  Using version 1.19.0

The rest of the code in this routine simply returns HAL_OK.

 

    This topic has been closed for replies.

    3 replies

    Technical Moderator
    September 29, 2025

    Hello @Harvey White 

    The issue stems from your custom application code, not from CubeMX or the IDE.

    It is most likely related to timing or the SPI peripheral not being ready between sequential writes and it becomes apparent when changing the code optimization level due to differences in execution speed.

    To address this, add small delays after toggling the CS and A0 pins, and ensure the SPI state is ready before each transfer.

    THX

    Ghofrane

     

    Technical Moderator
    October 2, 2025

    Hello @Harvey White 


    @Harvey White wrote:

    Using G0 (no optimization), the code fails either at line #70 after executing line #61 (write two bytes), or the second call to the routine at line #61 (writing one byte per call).


    So, what "issues", exactly, do you encounter? what do you mean by fails?

    What tests/investigations/debugging have you done to find out what's going on?

    Graduate
    October 2, 2025

    Broadly, the issue is that it doesn't work. 

    Specifically, with G0 optimization the first write (these are all writes to a display, the display cannot be read) returns 0, the second write returns 1 (unspecified error).  

    With Optimization for debug, the program does not return an error from the second write.

    Further, any other optimization other than debug has the same result as G0.

    On display writes that do not use data (command only), the SPI driver returns to the calling display driver, and the command portion (always needed in this scenario) fails with the same error.

    I remember putting a test to see if the driver is busy and waiting until it's not busy between the command and data writes.  This did not fix the problem.

    The display driver writes either a command, or command and data as needed.

    Thanks

     

     

     

    Technical Moderator
    October 3, 2025

    Hello @Harvey White 

    Did you check the SPI transfer without OS?

    Graduate
    October 5, 2025

    I use project/properties/optimization to pick the mode rather than relying on memory.  -Og is Optimize for Debug.

    I'm aware that blocking mode is not best for displays, and it doesn't matter for now.  I'm looking for operational rather then speed.  The driver has IRQ and DMA modes as well.  I do consider the blocking mode to be valid, and if it doesn't work, something is broken, IMHO.  

    I'm currently working on a driver configuration that eliminates some code, which may be a workaround.

    What I do not understand is how changing the optimization level causes two calls to the blocking driver to either work or not work.

    Putting taskENTER_CRITICAL and taskEXIT_CRITICAL around the two calls does not help, so I conclude that it's not the OS interrupting the actual transfer.  

    This perhaps points to the difficulty that hal level drivers have with an operating system, but hal2 fixes that, right?  Fully tested and integrated with FreeRTOS and other OS?

    Still working on the changed code.

     

    Graduate II
    October 5, 2025

    @Harvey White wrote:

    Using G0 (no optimization), the code fails either at line #70

     


    i repeat G0 isnt optimization ! But your issue isnt optimization, but code style. C++ and HAL and RTOS etc.

    Are you sure you change optimization in C++ section or in C or bpth etc.

    Graduate
    October 5, 2025

    No optimization (G0) is a level of optimization, just none.  Regardless, it did not work.

    I generally change the level of optimization in both C and C++ sections, and have changed one if not the other to try to see what the problem is.  

    If you have an idea where code style is the problem, I'd love to hear where and why you think so.  

    The simplified driver structure (display driver, i.e. ST7735R with direct interfaces to the low level hal drivers (ST drivers) does work, using -Og optimization.  I'm going to test the concept a bit more, its not complete, needs semaphores, for one, to protect the driver (interface is dedicated, hence the driver semaphore protects it as well).