Not super well documented. Review doc for Keil FLASH algorithms to understand the background.
Several examples under the ExternalLoader directory that could be adapted.
Basically wrapping the BSP QSPI code, providing entry points to Initialize and map the memory, and then additional routines to erase device/sectors, and write memory.
If you have specific requirements I'll quote the work.
Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
My board has the exact same QSPI connections, configured for single flash/BK1/Quad. My board FW runs QSPI memory verification successfully, writes LCD image data to QSPI memory, and copy it to LCD w/o error.
My board has H750V and MT25QL256 QSPI memory. I got stldr file for CubeProg created. CubeProg can read QSPI correctly. Here are my issues:
1) At the first time target is power on, CubeProg cannot read QSPI. I have to load the loader hex file (that is used to create stldr file) to CubeProg, download it to target. Then CubeProg can read memory OK from here on, until target is power cycled. This means that CubeProg does not set the entry point in H750V RAM. I don't know how to fix this.
2) After CubeProg read memory successfully, CubeProg does sector erase and reports successfully. However, memory is not erased when CubeProg reads it again.
3) If I try to modify a location (a byte, or a word, or 4 bytes) of the memory after CubeProg read it, I'll get and error msg "Memory edition verification failed"
Attached are major files of my CubeProg project with STM32H750, MT25QL256 (32MB). CubeProg can read QSPI OK, not write/sector-erase correctly. I'd appreciate if someone review and provide feedback.
I went back to check. I don't have any 4_LINES instructionmode. Only 4_LINES mode I have is in code for the commands (0x38 and 0x6B) which require 1-4-4 or 1-1-4. For mapped/un-mapped mode, that is what I was concerned about, since I don't know how CubeProg behavors. So I added QSPI_AutoPollingMemReady() in Read, Write, SectorErase, Verify functions (in Loader_Src.c). That actually got rid lot of errors, and got me to this point.
I finally got this to work. In addition items I mentioned above, below items are also needed. They are not documented any where, so it took me long time to get it resolved. They are not guarantied to be correct solution for you if you use a different CPU. I hope this help you to save time. At least you know which areas to experiment.
1) memory location 0x24000004 must be used in stm32_flash.ld. STM32H750 has other 3 RAM areas, 0x20000000, 0x30000000, 0x38000000. None of these 3 works.
I'm in the middle of building an external loader for a target board based on L496 & QSPI, the same as used in the L496G-Disco board but the QSPI on my target sits under different lines then the disco.
I started by building an running successfully a loader based on the example from the CubeProgrammer install dir for the disco board, after I had this I thought moving it to the target will be as simple as change the lines but this did not worked and I have problem to activate the write.
After digging in this issue I saw this discussion and my main concern now is who makes the HAL ticks tick?
I saw that Clive mention more then once that interrupt should not be used, if so then how HAL_IncTick is pushed if not from SysTick_Handler?
Using the QSPI HAL API needs this as I see in the code:
>>I saw that Clive mention more then once that interrupt should not be used, if so then how HAL_IncTick is pushed if not from SysTick_Handler?
There is obviously more than one way to skin the cat... Your mind is trapped in a box of it's own creation.
The CPU has a cycle counter, and TIM2 can be used as a free running maximal 32-bit counter, both methods can determine the elapsed time between calls to HAL_GetTick()
Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
OK, I did this with TIM2 but it did not change the state and the loader on the target is still not working properly.
I have a situation where I have a working loader project that is built for the L496Disco board that have on it the same MX25R6435F 8M QSPI as I have on my target, but after I only change the QSPI lines to the ones that fits the target board the loader is not working properly when I try to write.
I have this phenomena:
I'm starting from an erased flash
I'm trying to write "12345" at address 0 from the CubeProgrammer GUI
I get this "CCCDEFCD" on this address after I try to write and a popup error message say "Memory edition verification failed"
When I erase a sector it is look like the job is being done properly.
Two things are different in my target in compare to the disco board, I'm working with 1.8V and the I have an external crystal of 16M, but I'm not setting the clock to use it in the loader project.
I have HAL_Delay in my final working code. below is MCU_Init() in main.c. You don't need Enter4ByteAddressMode() if you are not using 4-byte Addr mode.
/* USER CODE BEGIN 4 */
uint8_t MCU_Init(uint8_t memMappedMode) {
SystemInit();
memset(&hqspi, 0, sizeof(hqspi));
hqspi.Instance = QUADSPI;
HAL_Init();
MX_GPIO_Init();
SystemClock_Config();
//prepare QSPI peripheral for ST-Link Utility operations
if (HAL_QSPI_DeInit(&hqspi) != HAL_OK) {
return HAL_ERROR;
}
MX_QUADSPI_Init();
if (QSPI_ResetChip() != HAL_OK)
return HAL_ERROR;
HAL_Delay(1);
if (QSPI_AutoPollingMemReady() != HAL_OK)
return HAL_ERROR;
if (QSPI_WriteEnable() != HAL_OK)
return HAL_ERROR;
if (QSPI_Configuration() != HAL_OK)
return HAL_ERROR;
Enter4ByteAddressMode();
if (memMappedMode != 1) {
if (QSPI_WriteEnable() != HAL_OK)
return HAL_ERROR;
if (QSPI_AutoPollingMemReady() != HAL_OK)
return HAL_ERROR;
if (QSPI_EnableMemoryMappedMode() != HAL_OK)
return HAL_ERROR;
}
return HAL_OK;
}
Check HAL_QSPI_MspInit() (created by CubeMX) to make sure the IO lines are assigned to QSPI correctly for your target.
is your QSPI chip is exactly the same as the one on Disco board? if not, check the commands for read and write, and change it accordingly in the QSPI read, write functions.
I actually just found the problem now and it was that I used in my original loader project calls to BSP_QSPI_*** from within stm32l496g_discovery_qspi.c, and the problem was that the BSP_QSPI_Init has its own QSPI_MspInit that used the wrong lines setup and override my HAL_QSPI_MspInit that was created by CubeMX and fits my QSPI lines.
In my original project I used one of the samples under STM32CubeProgrammer\bin\ExternalLoader, after I saw your conversation here I was able to trace the source code of the project you used (the F769_discovery_QSPI) but when I built it and try to use the stsldr file it generated I was not able to access the F769 QSPI, I'm not sure I fully understand the structure of it since it uses main and the CubeProgrammer\STLinkUtility does not.
At the end, you will have two projects in your IDE, one for your normal application, one for the loader for CubeProgrammer\STLinkUtility for the QSPI chip on your target board. The loader project will likely have to change if QSPI chip changes and/or CPU changes and/or QPSI lines change. The loader project uses different entry point, Init(), other than main(). Main.c is still needed in loader project, since definitions of QSPI lines and QSPI functions are in main.c
If your application doesn't need pre-stored data on QSPI chip, such as image for LCD..., then you don't need loader project. Your application can still read/write to QSPI chip.