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-02 07:49 AM
The CubeL4 code trees should be full of working examples, for working boards.
2018-10-02 09:26 AM
@John Last time i checked this code worked (havent used it for a while):
/**
* @brief Pools a command and receives data. Finishes after the \ref mask and \ref match are OK.
* @param cmd: command to auto pool with.
* @param bytes: amount of bytes to pool.
* @param mask: masking bits.
* @param match: match bits.
* @return HAL_OK on success.
*/
HAL_StatusTypeDef qspi_autoPool(const qspiCmd_t cmd, const uint32_t bytes,
const uint32_t mask, const uint32_t match)
{
// check how many status bytes to read
if ((bytes < QSPI_MIN_STATUS_BYTES) || (bytes > QSPI_MAX_STATUS_BYTES))
return HAL_ERROR;
QSPI_CommandTypeDef command;
QSPI_AutoPollingTypeDef config;
qspi_setCommonCmdAndCfg(&command, &config);
// Configure automatic polling mode to wait for memory ready
command.Instruction = cmd;
command.AddressMode = QSPI_ADDRESS_NONE;
config.Match = match;
config.Mask = mask;
config.StatusBytesSize = bytes;
if (util_isDmaAvailable()) // wait in non blocking mode
{
osSemaphoreWait(qspi.semaStat, HAL_QPSI_TIMEOUT_DEFAULT_VALUE);
if (HAL_OK != HAL_QSPI_AutoPolling_IT(qspi.hqspi, &command, &config))
return HAL_ERROR;
osSemaphoreWait(qspi.semaStat, HAL_QPSI_TIMEOUT_DEFAULT_VALUE);
osSemaphoreRelease(qspi.semaStat);
return HAL_OK;
}
return HAL_QSPI_AutoPolling(qspi.hqspi, &command, &config, HAL_QPSI_TIMEOUT_DEFAULT_VALUE);
}
2018-10-05 12:32 AM
Hi,
Thank you so much for the reply.
I am able to read ID (READ_ID_CMD2 0x9F) . I am getting 0xC2 0x20 0x18.
Now I am trying to write and read some data to/from MT25QL128 memory using Quad spi.
I am using API: "BSP_QSPI_Write" to write 256 bytes of data at 0x90000000 address and API "BSP_QSPI_Read" to read data from memory. But I am getting all 0xFF data.
Could you please help me to figure out the issue.
Thanks,
John
2018-10-06 12:46 PM
Hi,
The address 0x90000000 is only valid when operating in XIP mode. You are operating with the memory "manually". It means if you want to write something to the beginning of the memory, you need to address the address 0x00000000.
2018-10-06 10:04 PM
Hi,
Thank you so much,
I tried to write and read from address 0x00000000 also getting 0xFF. what could be the issue?
Thanks,
John
2018-10-06 10:08 PM
2018-10-07 04:42 AM
Hi,
I am using STMcubeL4 example code for read/write operation.
Below is the code using to write/read data. I am able to read ID.
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;
//QSPIHandle.Init.FlashID = QSPI_FLASH_ID_1;
//QSPIHandle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
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 cucles on QSPI memory side */
if (QSPI_DummyCyclesCfg(&QSPIHandle) != QSPI_OK)
{
return QSPI_NOT_SUPPORTED;
}
if (QSPI_TestReadId(&QSPIHandle) != QSPI_OK)
{
return QSPI_NOT_SUPPORTED;
}
for (int i = 0; i < 16; i++)
wbuf[i] = i;
if (BSP_QSPI_Write(&wbuf[0], 0x00000000, 16) != QSPI_OK)
{
return QSPI_ERROR;
}
if (BSP_QSPI_Read(&rbuf[0], 0x00000000, 16) != QSPI_OK)
{
return QSPI_ERROR;
}
return QSPI_OK;
}
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_1_LINE;
sCommand.Instruction = EXT_QUAD_IN_FAST_PROG_CMD;
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_1_LINE;
sCommand.Instruction = QUAD_INOUT_FAST_READ_CMD;
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 = MT25Q128A_DUMMY_CYCLES_READ_QUAD;
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 write and read API, is there anything wrong ?
Thanks,
john
2018-10-07 04:48 AM
2018-10-08 02:40 AM
Hi,
Now I am able to read 0x88 data not the data I am writing it. I have checked all dummy cycle and command mode, but still no luck.
Please help to check below code:
/**
* @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_1_LINE;
sCommand.Instruction = PAGE_PROG_CMD;
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_1_LINE;
sCommand.Instruction = READ_CMD;
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 = MT25Q128A_DUMMY_CYCLES_READ_QUAD;
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;
}
Thanks,
John
2018-10-20 09:51 PM
Hi Lukasz,
Still I am not able to read and write from qspi MT25ql128 serial flash.
could you please help me out or share example code?
Thanks,
John