2025-08-14 7:00 AM - edited 2025-08-14 9:33 AM
I am writing a custom driver for the mt25ql512abb as the one on STM's github doesnt work for my STM32L412KB, I have gotten reading, writing and erasing working but when I try enter memory mapped mode DQ[0-3] on the flash just goes high
Here is my function that im using, I am setting the XIP bit on the Volatile configuration register first (i have confirmed this is working) then I enter memory mapped mode, i have set the dummy cycles to 10 on the volatile and non-volatile configuration registers as well.
uint8_t CSP_QSPI_EnableMemoryMappedMode(void) {
QSPI_CommandTypeDef sCommand;
QSPI_MemoryMappedTypeDef sMemMappedCfg;
/* Enable Memory-Mapped mode-------------------------------------------------- */
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.AddressSize = QSPI_ADDRESS_32_BITS;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
sCommand.AddressMode = QSPI_ADDRESS_4_LINES;
sCommand.DataMode = QSPI_DATA_4_LINES;
sCommand.NbData = 0;
sCommand.Address = 0;
sCommand.Instruction = 0xeb;
sCommand.DummyCycles = 10;
sMemMappedCfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;
uint8_t config = 0xa3;
if(QSPI_WriteVCR(&config) != HAL_OK) {
return HAL_ERROR;
}
if (HAL_QSPI_MemoryMapped(&hqspi, &sCommand, &sMemMappedCfg) != HAL_OK) {
return HAL_ERROR;
}
return HAL_OK;
}
Here is my working read memory function
uint8_t CSP_QSPI_ReadMemory(uint8_t* buffer, uint32_t address, uint32_t buffer_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 <= address) {
current_addr += MEMORY_PAGE_SIZE;
}
current_size = current_addr - address;
/* Check if the size of the data is less than the remaining place in the page */
if (current_size > buffer_size) {
current_size = buffer_size;
}
/* Initialize the adress variables */
current_addr = address;
end_addr = address + buffer_size;
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.AddressSize = QSPI_ADDRESS_32_BITS;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
sCommand.Instruction = 0xeb;
sCommand.AddressMode = QSPI_ADDRESS_4_LINES;
sCommand.DataMode = QSPI_DATA_4_LINES;
sCommand.NbData = buffer_size;
sCommand.Address = address;
sCommand.DummyCycles = 10;
/* Perform the write page by page */
do {
sCommand.Address = current_addr;
sCommand.NbData = current_size;
if (current_size == 0) {
return HAL_OK;
}
/* Configure the command */
if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
!= HAL_OK) {
return HAL_ERROR;
}
/* Transmission of the data */
if (HAL_QSPI_Receive(&hqspi, buffer, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
return HAL_ERROR;
}
/* Configure automatic polling mode to wait for end of program */
if (QSPI_AutoPollingMemReady(HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
return HAL_ERROR;
}
/* Update the address and size variables for next page programming */
current_addr += current_size;
buffer += current_size;
current_size =
((current_addr + MEMORY_PAGE_SIZE) > end_addr) ?
(end_addr - current_addr) : MEMORY_PAGE_SIZE;
} while (current_addr <= end_addr);
return HAL_OK;
}
any help would be great thanks
Solved! Go to Solution.
2025-08-14 10:00 AM
I mean you print the content to the console using a pointer to 0x90000000, so you can observe it reading content.
Or you inspect in the debugger AFTER the memory has been mapped.
On the CM7 you need to configure the MPU for code/execution access, I don't recall if the L4 / CM4 needs this, and found no example on the CubeL4 GitHub
Have a viable Hard Fault Handler to report / dump issue if you end up there.
The linker script just directs what content is placed in external memory, getting code there and calling it is more involved. It does need to be mapped first. Generally one would build a boot loader to bring up the system, pins and memory and then transfer control.
The CM4 not being a particularly good choice as it lacks a cache, so code/data from external memory will be relatively slow.
2025-08-14 8:31 AM
Use the Code Format </> icon to post code inline, that way it retains formatting and indentation, and doesn't appear as a wall of text. Use the 'V' icon in upper right of pane to edit you initial post to fix it.
QSPI_CommandTypeDef sCommand = {0};
QSPI_MemoryMappedTypeDef sMemMappedCfg = {0};
I'd clear the local/auto variables to ensure there's not random stack junk in them.
If the Read method works properly, the template for the Memory Mapped should too. I don't think you need to set the VCR to XIP for this to work.
If you dump the 0x90000000 memory content from your app what does that look like? Does it Hard Fault?
You might want to configure the MPU for execution, but there's probably an example in CubeL4
2025-08-14 9:36 AM
sorry fixed the code import, could you explain what you mean by dumping the memory content, and do you mean configure the MPU for external flash as i have put this QSPI (rx) : ORIGIN = 0x90000000, LENGTH = 64M in my linker script, along with this
.extFlashMem :
{
*(.extFlashMem)
} >QSPI
2025-08-14 10:00 AM
I mean you print the content to the console using a pointer to 0x90000000, so you can observe it reading content.
Or you inspect in the debugger AFTER the memory has been mapped.
On the CM7 you need to configure the MPU for code/execution access, I don't recall if the L4 / CM4 needs this, and found no example on the CubeL4 GitHub
Have a viable Hard Fault Handler to report / dump issue if you end up there.
The linker script just directs what content is placed in external memory, getting code there and calling it is more involved. It does need to be mapped first. Generally one would build a boot loader to bring up the system, pins and memory and then transfer control.
The CM4 not being a particularly good choice as it lacks a cache, so code/data from external memory will be relatively slow.
2025-08-15 1:36 AM
Having inspected in the debugger all i see is the data is there, slightly corrupt but i think that is due to dodgy wiring, thanks
still i don't know why this memcmp function isnt working as thats the reason i thought my memory mapped mode wasnt working
if (memcmp(read_buffer, (uint8_t*) (0x90000000 + MEMORY_PAGE_SIZE), MEMORY_PAGE_SIZE) != HAL_OK) {
while (1)
; //breakpoint - error detected - otherwise QSPI works properly
}
I keep ending up in the while loop and this function is what the STM recommend to test the external flash
Also will switching to the F412 give me better performance or make no difference as it is also CM4
2025-08-15 3:04 AM
never mind i was being *** didnt realise it was mem compare instead of memcpy, was failing due to using high frequency on a breadboard
2025-08-15 1:43 PM
Plus memcmp() doesn't return HAL statuses..
Also memory mapping can only support READ, it doesn't support WRITE. Most QSPI NOR FLASH uses WRITE PAGE where up to 256 bytes can be written and you can't cross 256-byte page boundaries. An you must wait for BUSY/READY for Write-In-Progress bit in status register