2018-03-14 05:28 AM
Hello there,
I am using STM32L4 series MCU and I am trying to interface with a M25Q Micron memory using QSPI. Following the examples given from ST I have noticed that there are no HAL functions allowing registers reading, that is:
and etc.
In the examples the &sharpdefines for those commands are defined, but never used. Looking into the stm32l4xx_hal_qspi.h/c files, there is no read register command. All I could find is AutoPolling, which I am not sure either it works.
Does those functionalities need to be implemented by myself? I would appreciate all help.
#quad-spi #qspi-flash2018-10-20 10:15 PM
2018-10-21 08:18 AM
Hi,
Ok thanks. I have checked all address, data and dummy cycles but didn't find anything wrong. Below I am sharing MT25ql128 data sheet command details and code I am using to write and read from serial flash. I have tried both READ & FAST READ command and dummy cycles as 0 and 10 respectively but in both cases getting 0x88 data.
/**
* @brief Writes an amount of data to the QSPI memory.
* @param pData: Pointer to data to be written
* @param WriteAddr: Write start address
* @param Size: Size of data to write
* @retval QSPI memory status
*/
uint8_t BSP_QSPI_Write(uint8_t *pData, uint32_t WriteAddr, uint32_t Size)
{
QSPI_CommandTypeDef sCommand;
uint32_t end_addr, current_size, current_addr;
/* Calculation of the size between the write address and the end of the page */
current_addr = 0;
while (current_addr <= WriteAddr)
{
current_addr += MT25Q128A_PAGE_SIZE;
}
current_size = current_addr - WriteAddr;
/* Check if the size of the data is less than the remaining place in the page */
if (current_size > Size)
{
current_size = Size;
}
/* Initialize the adress variables */
current_addr = WriteAddr;
end_addr = WriteAddr + Size;
/* Initialize the program command */
sCommand.InstructionMode = QSPI_INSTRUCTION_4_LINES;
sCommand.Instruction = PAGE_PROG_CMD; // 0x02
sCommand.AddressMode = QSPI_ADDRESS_4_LINES;
sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = QSPI_DATA_4_LINES;
sCommand.DummyCycles = 0;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
/* Perform the write page by page */
do
{
sCommand.Address = current_addr;
sCommand.NbData = current_size;
/* Enable write operations */
if (QSPI_WriteEnable(&QSPIHandle) != QSPI_OK)
{
return QSPI_ERROR;
}
/* Configure the command */
if (HAL_QSPI_Command(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
/* Transmission of the data */
if (HAL_QSPI_Transmit(&QSPIHandle, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
/* Configure automatic polling mode to wait for end of program */
if (QSPI_AutoPollingMemReady(&QSPIHandle, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != QSPI_OK)
{
return QSPI_ERROR;
}
/* Update the address and size variables for next page programming */
current_addr += current_size;
pData += current_size;
current_size = ((current_addr + MT25Q128A_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : MT25Q128A_PAGE_SIZE;
}
while (current_addr < end_addr);
return QSPI_OK;
}
/**
* @brief Reads an amount of data from the QSPI memory.
* @param pData: Pointer to data to be read
* @param ReadAddr: Read start address
* @param Size: Size of data to read
* @retval QSPI memory status
*/
uint8_t BSP_QSPI_Read(uint8_t *pData, uint32_t ReadAddr, uint32_t Size)
{
QSPI_CommandTypeDef sCommand;
/* Initialize the read command */
sCommand.InstructionMode = QSPI_INSTRUCTION_4_LINES;
sCommand.Instruction = READ_CMD; //0x03
sCommand.AddressMode = QSPI_ADDRESS_4_LINES;
sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
sCommand.Address = ReadAddr;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = QSPI_DATA_4_LINES;
sCommand.DummyCycles = 0;
sCommand.NbData = Size;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
/* Configure the command */
if (HAL_QSPI_Command(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
/* Reception of the data */
if (HAL_QSPI_Receive(&QSPIHandle, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
Could you please help to check, am I doing anything wrong in terms of address, data and dummy cycles.
Thanks,
John
2018-10-21 09:58 AM
Is this a custom board? How well validated is the design?
Define specific L4 part involved.
Define pins used in the QSPI interface.
Define pins and USART available for debugging output.
Define clock source being used for HSE, or MSI/PLL settings.
Please also note that there is a code pasting tool "</>" see icon below, and also a paper clip icon for attaching files.
Don't attach a dozen files. Where appropriate ZIP up a complete/viable project an attach as a single file.
2018-10-21 10:27 AM
Reading the status registers of the external memory in the mode of performing a write operation or erasing data is a very, very fast process for the user and insanely long for the processor. You read the status registers yourself, but by this time their state is already changing - this is a very fast process for a living person.
It sounds strange, but if you want to surprise the transients, then you have to read the status registers in a loop, and save the data to the log. So that you can read them later.
Also.
In order not to load the processor in the standby mode in the useless cycle of software reading of status registers, the QSPI peripheral block has a stream reading mode for data from one register, with automatic comparison of the result of reading through a mask. The mask has two registers - the bits that are significant for the operation are selected first (the remaining bits do not affect the result), the second register contains an expression for comparison. Registers have 32 bits. When reading data - they are stored and constantly shifted to a full byte, so you can catch the state of one-two-three-four bytes.
There are memory chips that give up one significant byte of the state - then you need to place 4 copies of this state in the comparison mask.
So there are memory chips that give from two to four bytes of status.
At multiple quantities you need to have copies. When not a multiple of everything is much more fun. The state BEFORE the event is placed in the higher bytes, the lower one - AFTER the event. The comparison is continuous, and fully automatic. The trigger will automatically trigger an interrupt when a match event occurs.
And yet - you have to read the documentation for two chips: an external memory chip and a microprocessor. In order to programmatically access the periphery registers QSPI.
2018-10-21 08:05 PM
void SystemClock_Config(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
/* Enable HSE Oscillator and activate PLL with HSE as source */
/* (Default MSI Oscillator enabled at system reset remains ON) */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 20;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLP = 7;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
/* Initialization Error */
while(1);
}
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
clocks dividers */
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
/* Initialization Error */
while(1);
}
}
__weak void HAL_QSPI_MspInit(QSPI_HandleTypeDef *hqspi)
{
/* Prevent unused argument(s) compilation warning */
GPIO_InitTypeDef GPIO_InitStruct;
/*##-1- Enable peripherals and GPIO Clocks #################################*/
/* Enable the QuadSPI memory interface clock */
__HAL_RCC_QSPI_CLK_ENABLE();
/* Reset the QuadSPI memory interface */
__HAL_RCC_QSPI_FORCE_RESET();
__HAL_RCC_QSPI_RELEASE_RESET();
/* Enable GPIO clocks */
__HAL_RCC_GPIOE_CLK_ENABLE();
/* QSPI CS GPIO pin configuration */
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/* QSPI CLK GPIO pin configuration */
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/* QSPI D0 GPIO pin configuration */
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/* QSPI D1 GPIO pin configuration */
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/* QSPI D2 GPIO pin configuration */
GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/* QSPI D3 GPIO pin configuration */
GPIO_InitStruct.Pin = GPIO_PIN_15;
GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/* GPIO to power up qspi flash */
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pin = GPIO_PIN_9;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9, GPIO_PIN_SET);
}
Hi all,
Noted. Many thanks for the response.
I am checking both docs in details.
Define specific L4 part involved
Define pins used in the QSPI interface -
Define pins and USART available for debugging output.
Define clock source being used for HSE, or MSI/PLL settings.
Thanks,
John
2018-10-24 02:54 AM
Hi,
Could anyone please help me to solve the issue?
Thanks,
John
2018-10-25 07:03 PM
Hi all,
After reading both documents of external memory chip and a microprocessor, I am able to write and read data . but I am getting an first 2 bytes of data always as 0xFF.
what could cause an issue.
Thanks,
John
2018-10-25 10:21 PM
2018-10-29 01:34 AM
Hi,
I am able to write and read the data from mt25ql memory. but the problem is I am able to read correct only when I erase the block I am reading from. I have disabled the reset memory command, even if I enable also gets same response.
I am not sure why I am getting an issue without calling "BSP_QSPI_Erase_Block" API.
I have pasted the code sequence:
uint8_t BSP_QSPI_Init(void)
{
QSPIHandle.Instance = QUADSPI;
/* Call the DeInit function to reset the driver */
if (HAL_QSPI_DeInit(&QSPIHandle) != HAL_OK)
{
return QSPI_ERROR;
}
/* QSPI initialization */
QSPIHandle.Init.ClockPrescaler = 1; /* QSPI clock = 80MHz / (ClockPrescaler+1) = 40MHz */
QSPIHandle.Init.FifoThreshold = 4;
QSPIHandle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
QSPIHandle.Init.FlashSize = POSITION_VAL(MT25Q128A_FLASH_SIZE) - 1;
QSPIHandle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE;
QSPIHandle.Init.ClockMode = QSPI_CLOCK_MODE_0;
if (HAL_QSPI_Init(&QSPIHandle) != HAL_OK)
{
return QSPI_ERROR;
}
/* QSPI memory reset */
if (QSPI_ResetMemory(&QSPIHandle) != QSPI_OK)
{
return QSPI_NOT_SUPPORTED;
}
/* Configuration of the dummy cycles on QSPI memory side */
if (QSPI_DummyCyclesCfg(&QSPIHandle) != QSPI_OK)
{
return QSPI_NOT_SUPPORTED;
}
if (QSPI_TestReadId(&QSPIHandle) != QSPI_OK)
{
return QSPI_NOT_SUPPORTED;
}
//works only when call BSP_QSPI_Erase_Block()
for (int i = 0; i < 256; i++)
wbuf[i] = i;
if (BSP_QSPI_Write(&wbuf[0], 0x00000000, 256) != QSPI_OK)
{
return QSPI_ERROR;
}
if (BSP_QSPI_Read(&rbuf[0], 0x00000000, 256) != QSPI_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
Thanks,
Jonh
2018-11-07 08:16 PM
Hi Luka,
I have fixed the write and read data from mt25ql memory. I am able to write and read data from mt25ql memory.
Currently I am operating with the memory manually, mean to say beginning the address of memory from 0x00000000. I want to change the starting address from 0x90000000, as i know I need to change operating mode in XIP mode during power on time.
What are the other things need to be take care when I operate in XIP mode.
Thanks,
John