调试器Flash编程原理#

调试器可以直接访问SRAM(通过Cortex-M内核内部的调试部件),但是无法直接访问Flash。

所以下载程序时,调试器会先将Flash编程算法(驱动程序)和应用程序下载到SRAM中,然后调试器调用该Flash编程算法就可以将应用程序写进Flash(其实就相当于MCU自己进行Flash编程操作)。

所以不难推断出:该Flash编程算法就是一个STM32程序。

有时应用程序比较大,不能一次性全部下载到SRAM,所以要将整个应用程序写到Flash中需要多次重复这个过程。

  • 参考《Cortex-M3 & M4权威指南》14.7.2节 下载地址
  • 参考JLinkLog.txt文件(Keil工程根目录下)

1. Keil中Flash编程配置#

我们以Keil为例,分析一下整个过程。

如下图所示,Keil中Flash Download的配置分为三个部分。

  • Download Function:主要是一些Flash操作,勾选后调试时调试器就行执行相应的动作。调试程序时,先将Flash编程算法下载到SRAM,然后调试器根据此处勾选情况,调用相应的Flash操作函数完成操作。

  • RAM for Algorithm:指定Flash编程算法在内存中的运行地址和所需的内存空间大小。运行地址可以是SRAM的任意地址(但是运行地址+Size不能超出SRAM最高地址),内存空间大小视算法的实际大小而定,可大不可小。

  • Program Algorithm:Flash编程算法列表,因为不同的芯片有不同的Flash编程算法。一个Flash编程算法能适用于多个MCU,因为它们只是Flash的大小不一样(STM32F10x系列Flash的起始地址都是0x0800 0000)。以STM32F103C8T6来说,其Flash大小为64KB(0x10000),而我们使用的Flash编程算法是128Kb(0x20000),所以我们将Size选项的值修改为0x10000。

2. Flash编程算法#

上文我们推断出:该Flash编程算法就是一个STM32程序。那么这个Flash编程算法是怎么样的呢?下面我们一起来看。

按照下图所示的方法,就可以找到Keil中自带的Flash编程算法工程路径。

按照上面找到的路径打开,可以看到如下目录结构:

  • STM32F10x文件夹中是Flash编程算法Keil项目。

  • /*.FLM 预先编译好的EFL文件,后缀FLM只是重命名而已。

可以看到该Flash编程算法一共有3个源文件(*.c)和一个链接脚本(Target.lin):

  • FlashOS.h 中是FlashDevice数据结构和Flash操作函数的声明,这些函数是必须要实现的(个别是可以选的),相当于一个规范。
struct FlashDevice  {
   unsigned short     Vers;    // Version Number and Architecture
   char       DevName[128];    // Device Name and Description
   unsigned short  DevType;    // Device Type: ONCHIP, EXT8BIT, EXT16BIT, ...
   unsigned long    DevAdr;    // Default Device Start Address
   unsigned long     szDev;    // Total Size of Device
   unsigned long    szPage;    // Programming Page Size
   ...
};

// Flash Programming Functions (Called by FlashOS)
extern          int  Init        (unsigned long adr,   // Initialize Flash
                                  unsigned long clk,
                                  unsigned long fnc);
extern          int  UnInit      (unsigned long fnc);  // De-initialize Flash
extern          int  BlankCheck  (unsigned long adr,   // Blank Check
                                  unsigned long sz,
                                  unsigned char pat);
extern          int  EraseChip   (void);               // Erase complete Device
extern          int  EraseSector (unsigned long adr);  // Erase Sector Function
extern          int  ProgramPage (unsigned long adr,   // Program Page Function
                                  unsigned long sz,
                                  unsigned char *buf);
extern unsigned long Verify      (unsigned long adr,   // Verify Function
                                  unsigned long sz,
                                  unsigned char *buf);
  • FlashDev.c 中是Flash设备的描述信息,如下所示:
#ifdef STM32F10x_128
struct FlashDevice const FlashDevice  =  {
   FLASH_DRV_VERS,             // Driver Version, do not modify!
   "STM32F10x Med-density Flash", // Device Name (128kB/64kB/32kB)
   ONCHIP,                     // Device Type
   0x08000000,                 // Device Start Address
   0x00020000,                 // Device Size in Bytes (128kB)
   1024,                       // Programming Page Size
   0,                          // Reserved, must be 0
   0xFF,                       // Initial Content of Erased Memory
   100,                        // Program Page Timeout 100 mSec
   500,                        // Erase Sector Timeout 500 mSec

// Specify Size and Address of Sectors
   0x0400, 0x000000,           // Sector Size 1kB (128 Sectors)
   SECTOR_END
};
#endif
  • FlashPrg.c 中是各种Flash操作的函数的实现(Flash初始化,擦除,读写)

  • Target.lin 是该项目的链接脚本

; Linker Control File (scatter-loading)
;

PRG 0 PI          ; Flash编程算法区, PI-位置无关
{
  PrgCode +0      ; Code
  {
    * (+RO)
  }
  PrgData +0      ; Data
  {
    * (+RW,+ZI)
  }
}

DSCR +0           ; Flash设备描述区,+0表示紧挨着PRG区域尾部存放
{
  DevDscr +0
  {
    FlashDev.o
  }
}

该Flash编程算法,最终在SRAM中的布局如下所示:

从链接脚本中可以看出,该项目最终编译出的程序是位置无关的(PI),所以其起始地址可以被任意改变,但是起始地址+Size不能超出SRAM最高地址。

3. 为新的Flash设备创建编程算法#

请参考ARM官方文档:

Creating a new Algorithm

4. 参考链接#

Open Flashloader