cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L4 qspi: reading registers

Posted on March 14, 2018 at 13:28

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:

  • READ STATUS REGISTER (05h),
  • READ FLAG STATUS REGISTER (70h),
  • READ NONVOLATILE CONFIGURATION REGISTER (B5h).

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-flash
43 REPLIES 43
john doe
Lead
Posted on March 14, 2018 at 18:38

not sure why you need a HAL function

uint32_t tmpreg = 0;

tmpreg = hqspi.Instance->SR;     //read the status register
Posted on March 14, 2018 at 20:45

Thanks for answer,

I didnt mean the status register of the QSPI peripheral of the MCU, but the status register of the external QSPI memory.

Posted on March 14, 2018 at 21:06

Look at the BSP code for Disco/Eval boards that have QSPI memory on them.  That should at least show you how to send a command and get a response from the external memory.

Posted on March 14, 2018 at 21:15

I have looked into:

STM32Cube_FW_L4_V1.8.0\Projects\STM32L476G_EVAL\Examples\QSPI

There are no registers reading examples. Even though I have managed to read the status register using HAL functions ( i think), the same function doesnt work for reading non volatile memory register... Here is the code:

/**

 * @brief    Reads the status register and saves it under \ref status pointer.

 * @param     status: pointer under which the status register will be saved.

 * @return    HAL_OK on succesfull status read.

 */

HAL_StatusTypeDef qspi_readStatusReg(uint8_t* const status)

{

    assert_param(status);

    QSPI_CommandTypeDef command;

    qspi_setCommonCmdAndCfg(&command, 0);

    command.Instruction            = e_qspiCmd_ReadStatusReg;

    command.NbData                = 1;

    // send the command

    if(HAL_OK != HAL_QSPI_Command(qspi.hqspi, &command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE))

        return HAL_ERROR;

    return HAL_QSPI_Receive(qspi.hqspi, status, HAL_QPSI_TIMEOUT_DEFAULT_VALUE);

}/**

 * @brief    Sets the common command and configuration parameters.

 * @param     cmd: pointer to the command structure.

 * @param     cfg: pointer to the configuration structure.

 */

static void qspi_setCommonCmdAndCfg(QSPI_CommandTypeDef* const cmd, QSPI_AutoPollingTypeDef* const cfg)

{

    if (cmd)

    {

        if (qspi.quadOn)

        {

            cmd->InstructionMode         = QSPI_INSTRUCTION_4_LINES;

            cmd->DataMode                 = QSPI_DATA_4_LINES;

            cmd->AddressMode             = QSPI_ADDRESS_4_LINES;

        }

        else

        {

            cmd->InstructionMode         = QSPI_INSTRUCTION_1_LINE;

            cmd->DataMode                 = QSPI_DATA_1_LINE;

            cmd->AddressMode             = QSPI_ADDRESS_1_LINE;

        }

        cmd->AlternateByteMode             = QSPI_ALTERNATE_BYTES_NONE;

        cmd->DummyCycles                 = 0;

        cmd->DdrMode                     = QSPI_DDR_MODE_DISABLE;

        cmd->DdrHoldHalfCycle             = QSPI_DDR_HHC_ANALOG_DELAY;

        cmd->SIOOMode                     = QSPI_SIOO_INST_EVERY_CMD;

        cmd->AddressSize                = QSPI_ADDRESS_24_BITS;

        cmd->Address                    = 0;

    }

    if (cfg)

    {

        cfg->MatchMode                     = QSPI_MATCH_MODE_AND;

        cfg->Interval                    = QSPI_DEFAULT_DUMMY_CYCLES;

        cfg->AutomaticStop                = QSPI_AUTOMATIC_STOP_ENABLE;

    }

}/**

 * @brief    Available QSPI commands

 */

typedef enum

{

    e_qspiCmd_ReadStatusReg                = 0x05,

    e_qspiCmd_WriteEnable                = 0x06,

    e_qspiCmd_ReadNonvolatileConfigReg    = 0xB5,

    e_qspiCmd_EnterQuadIoMode            = 0x35,

    e_qspiCmd_ResetEnable                = 0x66,

    e_qspiCmd_ResetMemory                = 0x99,

    e_qspiCmd_ReadId                    = 0x9F,

    e_qspiCmd_ReadIdMultiIo                = 0xAF,

} qspiCmd_t;

Now when I replace the command 0x05 with 0xB5 and read 2 bytes instead of 1, it should give me the non volatile register, but it doesnt. Is there any misunderstood logic in here?

Also, what does BSP stand for?

Pavel A.
Evangelist III
Posted on March 14, 2018 at 22:47

Does those functionalities need to be implemented by myself?

Generally yes unless you find a suitable example project. HAL for STM32 provides API for the SPI controller, not for any specific device behind it. Micron has

https://www.micron.com/products/nor-flash/parallel-nor-flash/m29ew/m29ew-software

, you can try to port them; at least - get the correct  sequence of commands.

-- pa

Posted on March 14, 2018 at 22:07

BSP - Board Support Package

I'd imagine there is response via the DR

void ResetMemory(QSPI_HandleTypeDef *hqspi)

{

/* Reset memory config, Cmd in 1 line */

/* Send RESET ENABLE command (0x66) to be able to reset the memory registers */

while(QSPIHandle.Instance->SR & QSPI_FLAG_BUSY); /* Wait for busy flag to be cleared */

QSPIHandle.Instance->CCR = 0x2166;

QSPIHandle.Instance->AR = 0;

QSPIHandle.Instance->ABR = 0;

QSPIHandle.Instance->DLR = 0;

__DSB();

/* Send RESET command (0x99) to reset the memory registers */

while(QSPIHandle.Instance->SR & QSPI_FLAG_BUSY); /* Wait for busy flag to be cleared */

QSPIHandle.Instance->CCR = 0x2199;

QSPIHandle.Instance->AR = 0;

QSPIHandle.Instance->ABR = 0;

QSPIHandle.Instance->DLR = 0;

__DSB();

..

Reading/Writing 1-byte CMD, Data

/**

* @brief This function configure the dummy cycles on memory side.

* @param hqspi: QSPI handle

* @retval None

*/

static void QSPI_DummyCyclesCfg(QSPI_HandleTypeDef *hqspi)

{

QSPI_CommandTypeDef sCommand;

uint8_t reg;

/* Read Volatile Configuration register --------------------------- */

sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;

sCommand.Instruction = READ_VOL_CFG_REG_CMD;

sCommand.AddressMode = QSPI_ADDRESS_NONE;

sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;

sCommand.DataMode = QSPI_DATA_1_LINE;

sCommand.DummyCycles = 0;

sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;

sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;

sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;

sCommand.NbData = 1;

if (HAL_QSPI_Command(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

{

Error_Handler();

}

if (HAL_QSPI_Receive(&QSPIHandle, &reg, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

{

Error_Handler();

}

/* Enable write operations ---------------------------------------- */

QSPI_WriteEnable(&QSPIHandle);

/* Write Volatile Configuration register (with new dummy cycles) -- */

sCommand.Instruction = WRITE_VOL_CFG_REG_CMD;

MODIFY_REG(reg, 0xF0, (DUMMY_CLOCK_CYCLES_READ_QUAD << POSITION_VAL(0xF0)));

if (HAL_QSPI_Command(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

{

Error_Handler();

}

if (HAL_QSPI_Transmit(&QSPIHandle, &reg, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

{

Error_Handler();

}

}
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on March 15, 2018 at 02:26

So was able to us READ ID (0x9F) on the N25Q128A13EF840E used on the STM32F746G-DISCO

https://www.micron.com/~/media/documents/products/data-sheet/nor-flash/serial-nor/n25q/n25q_128mb_3v_65nm.pdf

 

20 BA 18 10 00 00 23 42-34 09 23 00 25 00 35 18  07 14 EF 27

extern QSPI_HandleTypeDef QSPIHandle;

static void QSPI_TestReadId(QSPI_HandleTypeDef *hqspi)

{

    QSPI_CommandTypeDef sCommand;

    uint8_t reg[20]; // N25Q128A13EF840E 0x20, 0xBA, 0x18

    /* Read Id register --------------------------- */

    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;

    sCommand.Instruction = 0x9F; // READ ID

    sCommand.AddressMode = QSPI_ADDRESS_NONE;

    sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;

    sCommand.DataMode = QSPI_DATA_1_LINE;

    sCommand.DummyCycles = 0;

    sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;

    sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;

    sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;

    sCommand.NbData = sizeof(reg);

    if (HAL_QSPI_Command(hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

    {

        puts('ERROR:HAL_QSPI_Command');

        Error_Handler();

    }

    memset(reg, 0, sizeof(reg));

    

    if (HAL_QSPI_Receive(hqspi, &reg[0], HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

    {

        puts('ERROR:HAL_QSPI_Receive');

        Error_Handler();

    }

    DumpData(sizeof(reg), (void *)reg);

    putchar('\n');

}

void QSPITest(void)

{

  BSP_QSPI_Init();

   

  QSPI_TestReadId(&QSPIHandle);

}
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on March 15, 2018 at 07:19

Turvey.Clive

You have played a large role in my electronics and programming saga. This time you have helped as well. I did miss this configuration:

sCommand.AddressMode = QSPI_ADDRESS_NONE;

Instead I had QSPI_DATA_1_LINE.

Now the read device ID is correct. Thank you for taking the time and testing this setup on your hardware.

John3
Associate II

Hi,

I am using stm32l486 and interface MT25QL128xx Micron memory using QSPI, but getting an issue in API BSP_QSPI_Init => QSPI_DummyCyclesCfg => QSPI_WriteEnable => HAL_QSPI_AutoPolling=> QSPI_WaitFlagStateUntilTimeout.

It doesn't get flag is in expected state in API "QSPI_WaitFlagStateUntilTimeout".

Could you please help to me figure out. Appreciate, if you could share your working source code.

Thanks,

John