cancel
Showing results for 
Search instead for 
Did you mean: 

How to calibrate the delay block with the OCTOSPI interface

KDJEM.1
ST Employee

Introduction

The delay block is an independent peripheral integrated within an STM32 MCU, for example, an STM32U5. Its role is to introduce a phase shift relative to the input clock. The user application configures the phase of the output clock.

This output clock is subsequently used to synchronize data received by an SDMMC or OCTOSPI interface.

However, for the OCTOSPI interface, the delay block can be configured for external serial memory controllers to fine-tune the data-received sampling clock. Additionally, it applies a phase shift to the clock or DQS signal when reading from external memory.

Two methods of calibration process utilizing the delay block are offered:

  • Fast tuning: the advantage is that it takes less time to be configured. 
  • Exhaustive tuning: the advantage is that there are fewer chances to face a data transfer error. 

For more information about these two types of calibration, you can refer to AN5050 application note and examples: DLYB_OSPI_NOR_FastTuning, DLYB_OSPI_PSRAM_ExhaustiveTuning.

The main objective of this article is to provide a step-by-step guide on how to calibrate the delay block using a fast tuning method. This demo example implementation uses a STM32U5A9J-DK and OCTOSPI interface, but it can be easily tailored for other microcontrollers and memory types. 

If you want to have the results of the edited files at hand immediately, then you can download the extractable file attached at the bottom of the article.

1. Prerequisites

Hardware:

  • Micro USB cable to power and program the board
  • STM32U5A9J-DK

KDJEM1_0-1732889486131.png

Software:

The versions used in this article are

2. Steps

1. Open STM32CubeMX
2. Create a new project using the STM32U5A9J-DK board

KDJEM1_1-1733238322003.png

3. Initialize all peripherals with their default Mode?
Click [Yes].

KDJEM1_3-1733238469964.png

4. Do you want to create a new project?

Click [without TrustZone activated] and then click [OK].

KDJEM1_5-1733238557266.png

5. OCTOSPI1 Mode and configuration

KDJEM1_4-1732891032581.png

KDJEM1_0-1733240315797.png

For more details about this step, users can refer to section 7.2.4 "OCTOSPI Configuration and Parameter Settings" of AN5050 or this FAQ and memory datasheet.

6. Generate code

KDJEM1_1-1733301343073.png

7. Edit main.c

Some functions must be added in the "main.c" file to configure the OCTOSPI1 interface in Indirect/Memory-mapped mode and the Macronix flash memory to allow a communication in DTR mode.

  • Insert private variables declarations
/* USER CODE BEGIN PV */
/* Private variables --------------------------------------------------*/
uint8_t aTxBuffer[]=" Programming in indirect mode - Reading in memory-mapped mode ";
LL_DLYB_CfgTypeDef dlyb_cfg,dlyb_cfg_test;
__IO uint8_t *nor_memaddr = (__IO uint8_t *)(OCTOSPI1_BASE);
__IO uint8_t aRxBuffer[BUFFERSIZE] ="";
/* USER CODE END PV *​
  • Insert the private functions prototypes
/* USER CODE BEGIN PFP */
void WriteEnable(void);
void OctalWriteEnable(void);
void OctalDTR_MemoryCfg(void);
void OctalSectorErase(void);
void OctalDTR_MemoryWrite(void);
void AutoPollingWIP(void);
void OctalPollingWEL(void);
void OctalPollingWIP(void);
void EnableMemMapped(void);
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
/* USER CODE END PFP */
  • Insert the functions to be called in the main
/* USER CODE BEGIN 1 */ 
uint16_t index;
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
/* Delay block configuration ------------------------------------------------ */
  if (HAL_OSPI_DLYB_GetClockPeriod(&hospi1,&dlyb_cfg) != HAL_OK)
    {
	  Error_Handler();
   }
  /*when DTR, PhaseSel is divided by 4 (emperic value)*/
    dlyb_cfg.PhaseSel /=4;

    /* save the present configuration for check*/
    dlyb_cfg_test = dlyb_cfg;

    /*set delay block configuration*/
    HAL_OSPI_DLYB_SetConfig(&hospi1,&dlyb_cfg);

    /*check the set value*/
    HAL_OSPI_DLYB_GetConfig(&hospi1,&dlyb_cfg);
  if ((dlyb_cfg.PhaseSel != dlyb_cfg_test.PhaseSel) || (dlyb_cfg.Units != dlyb_cfg_test.Units))
    {
	  Error_Handler();
    }
  /*----------------------------------------------------------------------*/
  /*-------------- MX25UM51245G memory configuration --------------*/
  /* Configure MX25UM51245G memory to DTR Octal I/O mode */
  OctalDTR_MemoryCfg();
  /*----------------------------------------------------------------------*/
  /*----------------------- Erasing the first sector ----------------------*/
  /* Enable writing to memory using Octal Write Enable cmd */
  OctalWriteEnable();
  /* Enable Octal Software Polling to wait until WEL=1 */
  OctalPollingWEL ();
  /* Erasing first sector using Octal erase cmd */
  OctalSectorErase();
  /* Enable Octal Software Polling to wait until memory is ready WIP=0*/
  OctalPollingWIP();
  /*----------------------------------------------------------------------*/
  /*--------------------- Programming operation ----------------------*/
  /* Enable writing to memory using Octal Write Enable cmd */
  OctalWriteEnable();
  /* Enable Octal Software Polling to wait until WEL=1 */
  OctalPollingWEL();
  /* Writing (using CPU) the aTxBuffer to the memory */
  OctalDTR_MemoryWrite();
  /* Enable Octal Software Polling to wait until memory is ready WIP=0*/
  OctalPollingWIP();
  /*---------------------------------------------------------------------*/
  /*------ Configure memory-mapped Octal SDR Read/write ------*/
  EnableMemMapped();
  /*----------------------------------------------------------------------*/
  printf("\r\n\r\n-----------Reading from the NOR memory ------------------\r\n");
  for(index = 0; index < BUFFERSIZE; index++)
  {
	  /* Reading back the written aTxBuffer in memory-mapped mode */
	  aRxBuffer[index] = *nor_memaddr;
	  if(aRxBuffer[index] != aTxBuffer[index])
	  {
		  printf("\r\nFAILURE index 0x%08X Expect 0x%X Output 0x%X", index, aTxBuffer[index], aRxBuffer[index]);
	  } else {
		  printf("\r\nSUCCESS index 0x%08X Expect 0x%X Output 0x%X", index, aTxBuffer[index], aRxBuffer[index]);
	  }
	  nor_memaddr++;
  }
  /*----------------------------------------------------------------------*/
  /* USER CODE END 2 */
  • Insert the function definitions
/* USER CODE BEGIN 4 */
/* This function Enables writing to the memory: write enable cmd is sent in
single SPI mode */
void WriteEnable(void)
{
OSPI_RegularCmdTypeDef sCommand;
OSPI_AutoPollingTypeDef sConfig;
/* Initialize the Write Enable cmd in single SPI mode */
sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
sCommand.Instruction = WRITE_ENABLE_CMD;
sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
sCommand.AddressMode = HAL_OSPI_ADDRESS_NONE;
sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = HAL_OSPI_DATA_NONE;
sCommand.DummyCycles = 0;
sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;
sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
/* Send Write Enable command in single SPI mode */
if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK)
{
Error_Handler();
}
/* Initialize Automatic-Polling mode to wait until WEL=1 */
sCommand.Instruction = READ_STATUS_REG_CMD;
sCommand.DataMode = HAL_OSPI_DATA_1_LINE;
sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_DISABLE;
sCommand.NbData = 1;
if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK)
{
Error_Handler();
}
/* Set the mask to 0x02 to mask all Status REG bits except WEL */
/* Set the match to 0x02 to check if the WEL bit is set */
sConfig.Match = WRITE_ENABLE_MATCH_VALUE;
sConfig.Mask = WRITE_ENABLE_MASK_VALUE;
sConfig.MatchMode = HAL_OSPI_MATCH_MODE_AND;
sConfig.Interval = AUTO_POLLING_INTERVAL;
sConfig.AutomaticStop = HAL_OSPI_AUTOMATIC_STOP_ENABLE;

/* Start Automatic-Polling mode to wait until WEL=1 */
if (HAL_OSPI_AutoPolling(&hospi1, &sConfig, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
Error_Handler();
}
}
/* This functions Enables writing to the memory: write enable cmd is sent in
Octal SPI mode */
void OctalWriteEnable(void)
{
OSPI_RegularCmdTypeDef sCommand;
/* Initialize the Write Enable cmd */
sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
sCommand.Instruction = OCTAL_WRITE_ENABLE_CMD;
sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES;
sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS;
sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE;
sCommand.AddressMode = HAL_OSPI_ADDRESS_NONE;
sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = HAL_OSPI_DATA_NONE;
sCommand.DummyCycles = 0;
sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;
sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
/* Send Write Enable command in Octal mode */
if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK)
{
Error_Handler();
}
}
/* This function Configures Software polling to wait until WEL=1 */
void OctalPollingWEL(void)
{
OSPI_AutoPollingTypeDef sConfig;
OSPI_RegularCmdTypeDef sCommand;
/* Initialize Indirect read mode for Software Polling to wait until WEL=1 */
sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
sCommand.Instruction = OCTAL_READ_STATUS_REG_CMD;
sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES;
sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS;
sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE;
sCommand.Address = 0x0;
sCommand.AddressMode = HAL_OSPI_ADDRESS_8_LINES;
sCommand.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE;
sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = HAL_OSPI_DATA_8_LINES;
sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE;
sCommand.NbData = 2;
sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_READ_REG;
sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;
sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
/* Set the mask to 0x02 to mask all Status REG bits except WEL */
/* Set the match to 0x02 to check if the WEL bit is Set */
sConfig.Match = WRITE_ENABLE_MATCH_VALUE;
sConfig.Mask = WRITE_ENABLE_MASK_VALUE;
sConfig.MatchMode = HAL_OSPI_MATCH_MODE_AND;
sConfig.Interval = 0x10;
sConfig.AutomaticStop = HAL_OSPI_AUTOMATIC_STOP_ENABLE;
if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK)
{
Error_Handler();
}
/* Start Automatic-Polling mode to wait until the memory is ready WEL=1 */
if (HAL_OSPI_AutoPolling(&hospi1, &sConfig, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
Error_Handler();
}
}
/* This function Configures Automatic-polling mode to wait until WIP=0 */
void AutoPollingWIP(void)
{
OSPI_RegularCmdTypeDef sCommand;
OSPI_AutoPollingTypeDef sConfig;
/* Initialize Automatic-Polling mode to wait until WIP=0 */
sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
sCommand.Instruction = READ_STATUS_REG_CMD;
sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
sCommand.AddressMode = HAL_OSPI_ADDRESS_NONE;
sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.DummyCycles = 0;
sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;
sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
sCommand.DataMode = HAL_OSPI_DATA_1_LINE;
sCommand.NbData = 1;
sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_DISABLE;
/* Set the mask to 0x01 to mask all Status REG bits except WIP */
/* Set the match to 0x00 to check if the WIP bit is Reset */
sConfig.Match = MEMORY_READY_MATCH_VALUE;
sConfig.Mask = MEMORY_READY_MASK_VALUE;
sConfig.MatchMode = HAL_OSPI_MATCH_MODE_AND;
sConfig.Interval = 0x10;
sConfig.AutomaticStop = HAL_OSPI_AUTOMATIC_STOP_ENABLE;
if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK)
{
Error_Handler();
}
/* Start Automatic-Polling mode to wait until the memory is ready WIP=0 */
if (HAL_OSPI_AutoPolling(&hospi1, &sConfig, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK)
{
Error_Handler();
}
}
/* This function Configures Software polling mode to wait the memory is
ready WIP=0 */
void OctalPollingWIP(void)
{
	OSPI_RegularCmdTypeDef sCommand;
	OSPI_AutoPollingTypeDef sConfig;
	/* Initialize Automatic-Polling mode to wait until WIP=0 */
	sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
	sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
	sCommand.Instruction = OCTAL_READ_STATUS_REG_CMD;
	sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES;
	sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS;
	sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE;
	sCommand.Address = 0x0;
	sCommand.AddressMode = HAL_OSPI_ADDRESS_8_LINES;
	sCommand.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
	sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE;
	sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
	sCommand.DataMode = HAL_OSPI_DATA_8_LINES;
	sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE;
	sCommand.NbData = 2;
	sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_READ_REG;
	sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;
	sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
	/* Set the mask to 0x01 to mask all Status REG bits except WIP */
	/* Set the match to 0x00 to check if the WIP bit is Reset */
	sConfig.Match = MEMORY_READY_MATCH_VALUE;
	sConfig.Mask = MEMORY_READY_MASK_VALUE;
	sConfig.MatchMode = HAL_OSPI_MATCH_MODE_AND;
	sConfig.Interval = 0x10;
	sConfig.AutomaticStop = HAL_OSPI_AUTOMATIC_STOP_ENABLE;
	if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
	HAL_OK)
	{
		Error_Handler();
	}
	/* Start Automatic-Polling mode to wait until the memory is ready WIP=0 */
	if (HAL_OSPI_AutoPolling(&hospi1, &sConfig, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)
	!= HAL_OK)
	{
		Error_Handler();
	}
}
	/*** This function configures the MX25LM51245G memory ***/
void OctalDTR_MemoryCfg(void)
{
	OSPI_RegularCmdTypeDef sCommand;
	uint8_t tmp;
	/* Enable writing to memory in order to set Dummy */
	WriteEnable();
	/* Initialize Indirect write mode to configure Dummy */
	sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
	sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
	sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
	sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
	sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;	
	sCommand.Instruction = WRITE_CFG_REG_2_CMD;
	sCommand.Address = CONFIG_REG2_ADDR3;
	sCommand.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
	sCommand.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
	sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_DISABLE;		
	sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
	sCommand.DataMode = HAL_OSPI_DATA_1_LINE;
	sCommand.DataDtrMode= HAL_OSPI_DATA_DTR_DISABLE;	
	sCommand.NbData = 1;
	sCommand.DummyCycles = 0;
	sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;

	sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
	if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
	HAL_OK)
	{
		Error_Handler();
	}
	/* Write Configuration register 2 with new dummy cycles */
	tmp = CR2_DUMMY_CYCLES_66MHZ;
	if (HAL_OSPI_Transmit(&hospi1, &tmp, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
	HAL_OK)
	{
		Error_Handler();
	}
	AutoPollingWIP();
	/* Enable writing to memory in order to set Octal DTR mode */
	WriteEnable();
	/* Initialize OCTOSPI1 to Indirect write mode to configure Octal mode */
	sCommand.Instruction = WRITE_CFG_REG_2_CMD;
	sCommand.Address = CONFIG_REG2_ADDR1;
	sCommand.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
	if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
	HAL_OK)
	{
		Error_Handler();
	}
	/* Write Configuration register 2 with with Octal mode */
	tmp = CR2_DTR_OPI_ENABLE;
	if (HAL_OSPI_Transmit(&hospi1, &tmp, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
	HAL_OK)
	{
		Error_Handler();
	}
}		//eloop OctalDTR_MemoryCfg

/* This function erases the first memory sector */
void OctalSectorErase(void)
{
OSPI_RegularCmdTypeDef sCommand;
/* Initialize Indirect write mode to erase the first sector */
sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
sCommand.Instruction = OCTAL_SECTOR_ERASE_CMD;
sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES;
sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS;
sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE;
sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = HAL_OSPI_DATA_NONE;
sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE;
sCommand.DummyCycles = 0;
sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;
sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE;
sCommand.AddressMode = HAL_OSPI_ADDRESS_8_LINES;
sCommand.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
sCommand.Address = 0;
/* Send Octal Sector erase cmd */
if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
HAL_OK)
{
Error_Handler();
}
}
/* This function writes the memory */
void OctalDTR_MemoryWrite(void)
{
	OSPI_RegularCmdTypeDef sCommand;
	/* Initialize Indirect write mode for memory programming */
	sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
	sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
	sCommand.Instruction = OCTAL_PAGE_PROG_CMD;
	sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES;
	sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS;
	sCommand.AddressMode = HAL_OSPI_ADDRESS_8_LINES;
	sCommand.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
	sCommand.Address = 0x00000000;
	sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
	sCommand.DataMode = HAL_OSPI_DATA_8_LINES;
	sCommand.NbData = BUFFERSIZE;
	sCommand.DummyCycles = 0;
	sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
	sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE;
	sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE;
	sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE;
	sCommand.DQSMode = HAL_OSPI_DQS_ENABLE;
	if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
	HAL_OK)
	{
	Error_Handler();
	}
	/* Memory Page programming */
	if (HAL_OSPI_Transmit(&hospi1, aTxBuffer, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)!=
	HAL_OK)
	{
		Error_Handler();
	}
}
	/* This function enables memory-mapped mode for Read and Write */
void EnableMemMapped(void)
{
	OSPI_RegularCmdTypeDef sCommand;
	OSPI_MemoryMappedTypeDef sMemMappedCfg;
	/* Initialize memory-mapped mode for read operations */
	sCommand.OperationType = HAL_OSPI_OPTYPE_READ_CFG;
	sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
	sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES;
	sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS;
	sCommand.AddressMode = HAL_OSPI_ADDRESS_8_LINES;
	sCommand.AddressSize = HAL_OSPI_ADDRESS_32_BITS;
	sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
	sCommand.DataMode = HAL_OSPI_DATA_8_LINES;
	sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_READ;
	sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
	sCommand.Instruction = OCTAL_IO_DTR_READ_CMD;
	sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE;
	sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE;
	sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE;
	sCommand.DQSMode = HAL_OSPI_DQS_ENABLE;
	if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
	HAL_OK)
	{
	Error_Handler();
	}
	/* Initialize memory-mapped mode for write operations */
	sCommand.OperationType = HAL_OSPI_OPTYPE_WRITE_CFG;
	sCommand.Instruction = OCTAL_PAGE_PROG_CMD;
	sCommand.DummyCycles = 0;
	if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) !=
	HAL_OK)
	{
		Error_Handler();
	}
	/* Configure the memory mapped mode with TimeoutCounter Disabled*/
	sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;
	if (HAL_OSPI_MemoryMapped(&hospi1, &sMemMappedCfg) != HAL_OK)
	{
		Error_Handler();
	}
}
PUTCHAR_PROTOTYPE
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
  return ch;
}
/* USER CODE END 4 */

8. Edit main.h

The private definition can be added to main.h

/* USER CODE BEGIN Private defines */
/* MX25UM51245G Macronix memory */
/* Flash commands */
#define OCTAL_IO_DTR_READ_CMD 0xEE11
#define OCTAL_IO_READ_CMD 0xEC13
#define OCTAL_PAGE_PROG_CMD 0x12ED
#define OCTAL_READ_STATUS_REG_CMD 0x05FA
#define OCTAL_SECTOR_ERASE_CMD 0x21DE
#define OCTAL_WRITE_ENABLE_CMD 0x06F9
#define READ_STATUS_REG_CMD 0x05
#define WRITE_CFG_REG_2_CMD 0x72
#define WRITE_ENABLE_CMD 0x06
/* Dummy clocks cycles */
#define DUMMY_CLOCK_CYCLES_READ 6	
#define DUMMY_CLOCK_CYCLES_READ_REG 4
/* Auto-polling values */
#define WRITE_ENABLE_MATCH_VALUE 0x02
#define WRITE_ENABLE_MASK_VALUE 0x02
#define MEMORY_READY_MATCH_VALUE 0x00
#define MEMORY_READY_MASK_VALUE 0x01
#define AUTO_POLLING_INTERVAL 0x10
/* Memory registers address */
#define CONFIG_REG2_ADDR1 0x0000000
#define CR2_STR_OPI_ENABLE 0x01
#define CR2_DTR_OPI_ENABLE 0x02
#define CONFIG_REG2_ADDR3 0x00000300
#define CR2_DUMMY_CYCLES_66MHZ 0x07
/* Exported macro ------------------------------------------------------*/
#define COUNTOF(__BUFFER__) (sizeof(__BUFFER__)/sizeof(*(__BUFFER__)))
/* Size of buffers */
#define BUFFERSIZE (COUNTOF(aTxBuffer) - 1)
/* USER CODE END Private defines */

9. Compile and flash

  • Click on "build" button 
     
    KDJEM1_6-1733301697604.png 
     
  • Click in "debug" button KDJEM1_9-1733301820457.png
     
  • Open a console emulator such as Tera Term. Configure the console baud rate. The COM port name may differ on your PC.
    KDJEM1_10-1733301900469.png
  •  Click on "resume" button  KDJEM1_11-1733301968547.png
  •  Tera Term displays that the results are successful.

KDJEM1_0-1733300070488.png

After following this guide, you should now be able to calibrate the delay block. If you encounter any issues, we suggest that you create a post in the ST community for further assistance.

If you want to have the results of the edited files at hand immediately, then you can download the extractable file attached at the bottom of the article.

Related links

Version history
Last update:
‎2024-12-11 02:37 AM
Updated by: