(二)SWD协议移植
前言
IO口模拟的SWD时序,我参考的是这位大神的文章。基于CMSIS-DAP,实现通过DAP读写目标芯片的内存、内核寄存器,这部分功能已经由ARM公司的CMSIS-DAP代码实现。我把主要移植到STM32G070RB上,把标准库改成了HAL库。
一、将下载算法加载到目标芯片SRAM
uint8_t swd_flash_syscall_exec(const program_syscall_t *sysCallParam, uint32_t entry, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4)
那么,我们只要把编程算法(一段在目标芯片上执行的代码,里面有Flash_Erase、Flash_Write两个函数)通过SWD写入目标芯片的SRAM,然后再通过SWD调用目标芯片SRAM里面的Flash_Erase、Flash_Write两个函数,不就能实现通过SWD给目标芯片编程了吗?
/*-------------------------------------------------*/
/*函数名:SWD下载 */
/*参 数:无 */
/*返回值:返回1下载成功,返回0下载失败 */
/*-------------------------------------------------*/
uint8_t Program_Target(void)
{
if(swd_init_debug()) //初始化下载端口
{
//初始化
if(user_target_flash_init(Flash_Start_Addr,Fire_Info.Fire_ver) != ERROR_SUCCESS) //初始化目标MCU的FLASH
{
printf("FLASH Init Error!\r\n");
return 0;
}
//擦除
for(uint32_t addr = 0; addr < Fire_Info.Firelen[SelectFlash]; addr += 1024) //擦除MCU的目标FLASH
{
if(user_target_flash_erase_sector(0x08000000 + addr,Fire_Info.Fire_ver) != ERROR_SUCCESS)
{
printf("Erase FLASH Error!\r\n");
return 0;
}
}
if(Fire_Info.Firelen[SelectFlash] %1024 != 0){ //判断是否还有不满1扇区1024字节的数据,如果有进入if擦除
if( user_target_flash_erase_sector(0x08000000 + (Fire_Info.Firelen[SelectFlash]/1024)*1024,Fire_Info.Fire_ver) != ERROR_SUCCESS)
{
printf("Erase FLASH Error!\r\n");
return 0;
}
}
#if DEBUG_INFO
// 读取擦除后的FLASH
for(uint32_t addr = 0; addr < Fire_Info.Firelen[SelectFlash]; addr += 1024)
{
if(swd_read_memory(0x08000000 + addr, readbuff, 1024) == ERROR_SUCCESS){
printf("Read FLASH Error!\r\n");
return 0;
}
for(uint32_t i = 0; i < 1024; i++) printf("%02X ", readbuff[i]);
printf("\r\n\r\n\r\n");
}
#endif /*DEBUG_INFO*/
//将程序下载到目标MCU
for(uint32_t addr = 0; addr < Fire_Info.Firelen[SelectFlash]; addr += 1024)
{
W25QXX_Read(DataSta.databuff,FLASH_CAP * SelectFlash + addr,1024); //从W25Q32中读取程序
if(user_target_flash_program_page(0x08000000 + addr, DataSta.databuff, 1024,Fire_Info.Fire_ver) != ERROR_SUCCESS){ //下载到目标MCU
printf("Program FLASH1 Error!\r\n");
return 0;
}
}
//再次读取MCU观察程序是否正确
for(uint32_t addr = 0; addr < Fire_Info.Firelen[SelectFlash]; addr += 1024)
{
if(swd_read_memory(0x08000000 + addr, readbuff, 1024) == ERROR_SUCCESS){
printf("Read FLASH Error!\r\n");
return 0;
}
#if DEBUG_INFO
for(uint32_t i = 0; i < 1024; i++) printf("%02X ", readbuff[i]);
printf("\r\n\r\n\r\n");
#endif /*DEBUG_INFO*/
}
#if DEBUG_INFO
if(Fire_Info.Firelen[SelectFlash] %1024 != 0){
if(swd_read_memory(0x08000000 +(Fire_Info.Firelen[SelectFlash]/1024)*1024, readbuff, Fire_Info.Firelen[SelectFlash]%1024) == ERROR_SUCCESS) {
printf("Read FLASH Error!\r\n");
return 0;
}
for(uint32_t j = 0; j < Fire_Info.Firelen[SelectFlash]%1024; j++)
printf("%02X ",DataSta.databuff[j]);
}
#endif /*DEBUG_INFO*/
swd_set_target_reset(0);//复位运行
}
else{
printf("SWD Init Error!\r\n");
return 0;
}
return 1; //成功返回1
}
以上是下载步骤,在FLASH初始化中把下载算法加载到目标的SRAM,然后通过SWD的擦除,写入函数进行编程,下载完成后对目标芯片进行复位。
二、SWD的移植
1.改变移植芯片的主频
首先将源文件SW_DP.c,DAP.c,SWD_flash.c,SWD_host.c,error.c加入到工程中,然后将头文件DAP.h,DAP_config.h,error.h,SWD_flash.h,SWD_host.h,也加入到工程中,然后打开DAP_config.h文件,如下图所示:
CPU_CLOCK对应的是你移植芯片的主频,是多少就改成多少,我用的是STM32G070RB主频是64M,剩下的都搞成一样。
2.更改SWD下载引脚
GPIOC的GPIO_PIN_0对应的是SWDIO,GPIOC的GPIO_PIN_1对应的是SWCLK,这个自己想换成什么引脚都可以。如果是移植到标准库可能要改的东西多点,SWD协议目前已经移植完毕,这部分主要是DAP已经开源了。
三、下载算法的实现
既然下载程序需要目标芯片的下载算法,而每个芯片的算法都不一样,所以我们该怎么得到下载算法呢?我们知道,Keil针对每一颗芯片都有一个Flash编程算法,这个算法存在一个后缀为.FLM的文件里面,要是我们能把.FLM文件里面的算法内容抽取出来给我们用,那不就完美了吗其实这个功能也已经有国外大神给实现了,GitHub上的FlashAlgo项目里面有个flash_algo.py文件,它就是用来实现这个功能的,我已经把常用芯片的算法已经提取出来了放到这里下载算法
自己去提取想要的算法只需要把.FLM文件放在和FlashAlgo放在同一路径下,双击FlashAlgo即可生成对应的芯片.c文件下载算法
总结
整个编程烧写过程占用了目标芯片4K SRAM,其中SRAM起始地址为0x20000000,栈顶指向4K SRAM的末尾,,编程算法占用4K SRAM的前1K,,待烧写数据占用4K SRAM的中间2K,,静态变量和栈共用4K SRAM的最后1K这种设计对绝大多数Cortex-M芯片是没有问题的,所以对于有些比较特殊的芯片,,需要先修改一下flash_algo.py和c_blob.tmpl,然后再生成算法文件对应的.c文件,不过还好,生成是一次行的。最后将介绍若何使用上位机传输程序。
上位机操作(三)