#STM32CubeIDE下载算法原理
#创建下载算法工程
#修改代码
#测试算法
#结束
STM32CubeIDE的下载算法和STM32CubeProg是一样的,通过创建与地址信息无关的算法文件,实现
初始化 int Init(void);
读 int Read (uint32_t Address, uint32_t Size, uint8_t* buffer);
写 int Write (uint32_t Address, uint32_t Size, uint8_t* buffer);
擦除全部Flash int MassErase (void);
扇区擦除int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
校验 uint64_t Verify (uint32_t MemoryAddr, uint32_t RAMBufferAddr, uint32_t Size, uint32_t missalignement)
ST的下载算法模板在STLINK安装目录下的STMicroelectronics\STM32 ST-LINK Utility\ST-LINK Utility\ExternalLoader里,我这里用的是STM32H750和W25Q64,所以选择用N25Q512A_STM32F769I-EVAL这个模板
算法的大致流程:
图片中为MDK的下载算法,STM32CubeIDE基本上一样(没有Uninit函数)
擦除:
– 加载算法到芯片RAM
– 执行初始化函数 int Init(void) 初始化芯片时钟和外设等。
– 执行擦除函数(擦除扇区或整个Flash)
– 执行Uinit函数
– 完成
写Flash
– 加载算法到芯片RAM
– 执行初始化函数 int Init(void) 初始化芯片时钟和外设等。
– 加载要写的数据到RAM缓冲
– 执行Flash写函数
– 执行Uinit函数
– 完成
校验
校验这部分没写,因为STM32CubeIDE在下载中如果没有校验函数,会用Read函数将数据读出后进行CRC计算和RAM中加载的程序文件数据进行比较。
复制 N25Q512A_STM32F769I-EVAL 文件到自己的文件夹下,打开MDK文件,修改器件为STM32H750XB
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。使用Read-Write position independence同理,表示的可读可写数据段。
修改Linker选项如图所示,修改 .sct文件(没有就自己创建一个空的)为:
FLASH_LOADER 0x20000004 PI ; FlashLoader Functions
{
PrgCode +0 ; Code
{
* (+RO)
}
PrgData +0 ; Data
{
* (+RW,+ZI)
}
}
DEVICE_INFO +0 ; Device Info
{
DevInfo +0 ; Info structure
{
dev_inf.o
}
}
其中 PI 是指与地址无关码,详见MDK的sct文件详解。
Misc controls 里要填写 --diag_suppress L6305用于屏蔽L6503类型警告信息。
算法的加载地址由自己根据自己的芯片定义,我定义在了0x20000000,毕竟速度快。
将HAL库替换为自己用到的库文件(可以在STM32CubeMX里生成)
我自己添加的有些多,其实好多可以删掉。
在模板里有的Loader_Src.c里有
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
return HAL_OK;
}
函数和
void HAL_Delay(__IO uint32_t Delay)
{
for(int i=0;i<Delay*480000;i++);
}
函数
这是因为在下载算法中无法使用使用Systick时钟,所有给这个函数做了重定向。
在模板里将SystemClock_Config替换为自己的时钟配置
修改Dev_Inf.c为
其中“STM32H750_W25Q64”是在STM32CubeIDE或STM32CubeProg识别的算法名字,把他改成自己喜欢的名字。
初始化函数Init
完成工程的创建后,开始修改Loader_Src.c中关于下载相关的代码
int Init (void)
{
SystemInit();
/* Zero Init structs */
SystemClock_Config();//STM32CubeMX生成的时钟配置
//Stm32_Clock_Init(192,5,2,4); //设置时钟,400Mhz
/* Initialize QuadSPI ------------------------------------------------------ */
MX_GPIO_Init();//LED GPIO配置
W25QXX_Init();//W25Q64初始化函数
//QUADSPI_MappedMode();//下载代码,就不要用到内存映射模式了,不太好用
return 1;
}
关于W25Q64使用内存映射模式,就不需要 Read 函数了,IDE会自动从总线读出flash的内容,不过每个函数最后都需要把QSPI设置为内存映射模式。用内存映射模式,STM32CubeProg 每次上电连接后,读取0X90000000(W25Q64)处的数据只能读取一次,然后就会出错误,暂时没有找到原因。
整个芯片擦除函数MassErase
int MassErase(void)
{
W25QXX_Init();
W25QXX_ChipErase();
//W25Q_Memory_Mapped_Enable();
return 1;
}
扇区擦除函数SectorErase
int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
{
uint32_t BlockAddr;
EraseStartAddress -= 0x90000000;
EraseEndAddress -= 0x90000000;
EraseStartAddress = EraseStartAddress - (EraseStartAddress % 0x1000);
W25QXX_Init();
while (EraseEndAddress >= EraseStartAddress)
{
//防止超过256M空间
BlockAddr = EraseStartAddress & 0x0FFFFFFF;
W25QXX_SectorErase(BlockAddr);
EraseStartAddress +=0x1000;
}
// W25Q_Memory_Mapped_Enable();
return 1;
}
这个函数传入的地址是带有基地址(0x90000000)的,所以要减去这个基地址
在上文中定义了Dev_Inf.c中扇区大小为0x1000,所以这里每次擦除后地址要加上0x1000。
写函数 int Write
int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
{
if (Address < 0x90000000 || Address >= 0x90000000 + 8*1024*1024)
{
return 0;
}
Address -= 0x90000000;
W25QXX_Init();
W25QXX_Write_NoCheck(buffer,Address,Size);
/* 内存映射 */
// W25Q_Memory_Mapped_Enable();
return 1;
}
读函数 int Write
int Read(uint32_t Address, uint32_t Size, uint8_t* buffer)
{
uint32_t i;
uint32_t Adr;
HAL_GPIO_TogglePin(LED_R_GPIO_Port,LED_R_Pin);
Adr = Address - 0x90000000;
W25QXX_Init();
//W25Q_Memory_Mapped_Enable();
// for(i=0;i
// {
// *buffer++ = *(uint8_t*)Adr++;
// }
W25QXX_Read(buffer, Adr, Size);
//W25QXX_Init();
return 1;
}
校验函数 uint64 Verify()
//uint64_t Verify (uint32_t MemoryAddr, uint32_t RAMBufferAddr, uint32_t Size, uint32_t missalignement)
//{
// uint32_t VerifiedData = 0, InitVal = 0;
// uint64_t checksum;
// Size *= 4;
//
//
// W25QXX_Init();
// QUADSPI_MappedMode();
// checksum = CheckSum((uint32_t)MemoryAddr + (missalignement & 0xF), Size - ((missalignement >> 16) & 0xF), InitVal);
// while (Size>VerifiedData)
// {
// if ( *(uint8_t*)MemoryAddr++ != *((uint8_t*)RAMBufferAddr + VerifiedData))
// return ((checksum<<32) + (MemoryAddr + VerifiedData));
//
// VerifiedData++;
// }
//
// return (checksum<<32);
//}
全部注释掉就行,反正用不到
QSPI初始化以及W25Q64的初始化
代码搬运……就不放了
将生成的stldr文件放在
C:\ST\STM32CubeIDE_1.4.0\STM32CubeIDE\plugins\com.st.stm32cube.ide.mcu.externaltools.cubeprogrammer.win32_1.5.0.202011040924\tools\bin\ExternalLoader
文件夹下……太长了
打开STM32CubeIDE中工程的Debug_Configurations,选择调试器,添加下载算法,如图
看那个下载算法的名字,和我在Dev_Inf.c中定义的内容一样。
在工程文件的 STM32H750XBHX_FLASH.ld文件中添加代码
因为我开启的TouchGFX工程,所以在最后添加
编译工程,可以看到QSPIFLASH中占用了一些空间
点击下载,出现
并且 main函数中的LED也开始闪烁,表示下载成功,也可以用STM32CubeProg查看0x90000000中的内容与elf文件中的是一样的。
本文部分内容来自https://blog.csdn.net/Simon223/article/details/109772910