QSPI下载算法制作
了解到可以使用下载算法将代码直接下载到QSPI flash, 有以下三种工具
- 使用mdk keil下载
- 使用CubeProgrammer下载
- 使用ST-LINK Utility下载
个人觉得使用MDK下载比较方便,在使用keil的过程中,我们也会常使用到下载算法,如下,我们要制作的就是这个,只不过它是下载到外部QSPI flash上

在上一章节 调好了W25Q64的驱动 接下来就开始制作下载算法吧
在keil的安装目录下都有一个示例工程供我们制作自己的下载算法
搜索 _Template 可找到找到之后我们将它拷贝到此目录下



改写好如下,如下这是我的

打开工程就会有制作说明,但并不详细 这里给出了官方的说明:https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/flashAlgorithm.html

步骤一
更改为自己的芯片

步骤二
更改我们文件名

步骤三
更改QSPI配置参数
参考链接: https://blog.csdn.net/Simon223/article/details/109772910?ops_request_misc=&request_id=&biz_id=102&utm_term=QSPI%E4%B8%8B%E8%BD%BD%E7%AE%97%E6%B3%95&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-109772910.142^v96^pc_search_result_base5&spm=1018.2226.3001.4187
理解QSPI下载算法实现的本质, 实际上也就是一个工程,可以操作QSPI读写擦除等等,这里提供了一个模板,按照这个模板来就好了
FlashOS.H是一个头文件,它定义了用于编写Flash编程算法的结构和函数原型,我们需要适配为自己的

接着我们需要在FlashPrg这些函数中来添加对应的QSPI操作

需要添加的函数操作
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
/* Add your Code */
return (0); // Finished without Errors
}
/*
* De-Initialize Flash Programming Functions
* Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
int UnInit (unsigned long fnc) {
/* Add your Code */
return (0); // Finished without Errors
}
/*
* Erase complete Flash Memory
* Return Value: 0 - OK, 1 - Failed
*/
int EraseChip (void) {
/* Add your Code */
return (0); // Finished without Errors
}
/*
* Erase Sector in Flash Memory
* Parameter: adr: Sector Address
* Return Value: 0 - OK, 1 - Failed
*/
int EraseSector (unsigned long adr) {
/* Add your Code */
return (0); // Finished without Errors
}
/*
* Program Page in Flash Memory
* Parameter: adr: Page Start Address
* sz: Page Size
* buf: Page Data
* Return Value: 0 - OK, 1 - Failed
*/
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {
/* Add your Code */
return (0); // Finished without Errors
}整理如下: (我们要在对应的函数下添加对应的功能)
/* 添加flash初始化的函数 这里我们还要初始化系统时钟让芯片可以正常工作,下载算法实质也是驱动QSPI的擦除和写入操作 */
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
/* Add your Code */
return (0);
}
/*flash 解初始化*/
int UnInit (unsigned long fnc) {
/* Add your Code */
return (0); // Finished without Errors
}
/* 擦除全部chpi */
int EraseChip (void) {
/* Add your Code */
return (0); // Finished without Errors
}
/* 擦除扇区 */
int EraseSector (unsigned long adr) {
/* Add your Code */
return (0); // Finished without Errors
}
/* 写入页 */
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {
/* Add your Code */
return (0); // Finished without Errors
}既然要驱动QSPI,我们需要将原工程的文件包含进来, 找到上一个QSPI的工程打开,查看头文件路径以及包含对应文件进来,编译后无报错即可,这里不用添加启动文件

uint8_t Verify_Data[8*1024]; //校验时缓存的数据,不能小于一个 Programming Page Size 的大小
/*
* Initialize Flash Programming Functions
* Parameter: adr: Device Base Address
* clk: Clock Frequency (Hz)
* fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
int Init (unsigned long adr, unsigned long clk, unsigned long fnc)
{
SystemInit(); // 系统初始化
SystemClock_Config(); // 配置系统时钟,主频480MHz
MX_QUADSPI_Init(); /* QSPI初始化 * /
if ( QSPI_W25Qxx_Init() != QSPI_W25Qxx_OK ) // 初始化W25Q64
{
return 1;
}
return 0;
}
/*
* De-Initialize Flash Programming Functions
* Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
int UnInit (unsigned long fnc)
{
if ( QSPI_W25Qxx_Init() != QSPI_W25Qxx_OK ) // 初始化W25Q64
{
return 1;
}
return 0;
}
/*
* Erase complete Flash Memory
* Return Value: 0 - OK, 1 - Failed
*/
int EraseChip (void)
{
if ( QSPI_W25Qxx_ChipErase() != QSPI_W25Qxx_OK ) // 整片擦除
{
return 1;
}
return 0;
}
/*
* Erase Sector in Flash Memory
* Parameter: adr: Sector Address
* Return Value: 0 - OK, 1 - Failed
*/
int EraseSector (unsigned long adr)
{
adr = adr - W25Qxx_Mem_Addr;
if ( QSPI_W25Qxx_BlockErase_64K(adr) != QSPI_W25Qxx_OK ) // 擦除对应扇区
{
return 1;
}
return 0;
}
/*
* Blank Check Checks if Memory is Blank
* Parameter: adr: Block Start Address
* sz: Block Size (in bytes)
* pat: Block Pattern
* Return Value: 0 - OK, 1 - Failed
*/
int BlankCheck (unsigned long adr, unsigned long sz, unsigned char pat)
{
return (1); /* Always Force Erase */
}
/*
* Program Page in Flash Memory
* Parameter: adr: Page Start Address
* sz: Page Size
* buf: Page Data
* Return Value: 0 - OK, 1 - Failed
*/
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf)
{
QSPI_W25Qxx_WriteBuffer(buf, adr -W25Qxx_Mem_Addr,sz);
return (0);
}
/*
* Verify Flash Contents
* Parameter: adr: Start Address
* sz: Size (in bytes)
* buf: Data
* Return Value: (adr+sz) - OK, Failed Address
*/
/*
Verify function is obsolete because all other function leave
the SPIFI in memory mode so a memory compare could be used.
*/
unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf)
{
unsigned long i;
QSPI_W25Qxx_ReadBuffer(Verify_Data, adr - W25Qxx_Mem_Addr, sz);
for (i = 0; i < sz; i++)
{
if (Verify_Data[i] != buf[i])
{
return (adr + i); // Verification Failed (return address)
}
}
return (adr + sz); // Done successfully
}
生成下载算法如下,

虽然这一步生成了算法,但是似乎运行不起来,算法是加载到sram里面的,hal库太多文件了,我在尝试把ram加到最大也不行,他们居然能精简到一百多k,很显然这是直接操作寄存器了,对于这部分需要花大量时间去实现,目前先不探究了,因为我使用的平台有适配好的下载算法我就直接使用了

待解决
解决方法1:
- 需要使用寄存器来驱动芯片运行
- 需要使用寄存器来驱动QSPI
解决方法2:精简hal库
目前主要实现bootloader的跳转,它的优先级较高,后续补充