STM32 Bootload 程序

STM32 Bootload 程序

先申明一下,我使用的是 STM32F103C8,内部有 64K FLASH, 使用外部 FLASH 更新,外部 FLASH 是 8M 空间,实际使用搓搓有余了,我分了三块,每块 1M 空间,第一块用于存放 新程序 (就是要更新的程序,由用户程序从串口事先写入进去的),第二块用于备份当前程序(旧程序),第三块用于放置一些设置,主要是新/旧程序的校验码、旧程序备份标志、新程序更新标志。
首先使用 Keil MDK 新建一个 STM32 模板工程,然后配置一下工程。
- 列表内容
STM32 Bootload 程序_第1张图片

STM32 Bootload 程序_第2张图片

以上是 Bootload 程序的配置,用户程序和要更新的程序也需要配置

STM32 Bootload 程序_第3张图片

STM32 Bootload 程序_第4张图片
配置好后就开始写代码了
STM32 Bootload 程序_第5张图片

// 定义几个常量
// 外部 SPI Flash 地址,我使用的是 8M 外部 Flash,
// 所以空间很大,分了三块出来,每块 1M 的空间
#define NEW_PROG_ADDR       (0x000000)      // 新程序存放的地址
#define OLD_PROG_ADDR       (0x100000)      // 就程序存放的地址
#define PROG_FLAG_ADDR      (0x200000)      // 程序标志位

#define  STARTADDR          0x08001000      // 用户程序地址

// 程序是否需要校验,取消 xPROG_VERIFY 的 x 就可以添加校验功能
#define xPROG_VERIFY

typedef  void (*pFunction)(void);  // 申明一个函数指针
pFunction Jump_To_Application;

// 添加几个变量
uint32_t JumpAddress;       // 跳转地址
u16 FlashID;                // 外部 SPI Flash ID
u8 ReadBuf[1024];           // 缓存,1024 , 每次更新 1024
u8 Read_MCU_Flash_Addr;     // MCU 地址

// 程序更新标志位
struct _Prog_Status_Flag
{
    u8  Backup_Flag;                    // 程序是否已经备份
    u8  Old_Prog_ADD_CheckSum;          // 备份程序(旧程序)校验和
    u8  Old_Prog_XOR_CheckSum;          // 备份程序(旧程序)异或校验
    u8  New_Prog_ADD_CheckSum;          // 要更新的程序(新程序)校验和
    u8  New_Prog_XOR_CheckSum;          // 要更新的程序(新程序)异或校验
    u8  Update_Flag;                    // 更新标志位,有更新这个值为0x55,更新完成校验后清0
};

struct _Prog_Status_Flag Prog_Status_Flag;

// 备分当前程序
void Backup_Current_Program()
{
    unsigned int i,j;
    unsigned char ADD_CheckSum = 0,XOR_CheckSum = 0;
    unsigned int Addr;

    JumpAddress = STARTADDR; // 当前程序的地址

    Prog_Status_Flag.Old_Prog_ADD_CheckSum = 0;
    Prog_Status_Flag.Old_Prog_XOR_CheckSum = 0;

    // 开始备份当前程序
    Addr = OLD_PROG_ADDR;       // 要备份到外部 Flash 的地址
    for(i=0;i<60;i++)           // 我使用的是 STM32F103C8,内部 Flash 是 64K,4K用来做 Bootload,所以只有 60K
    {
        for(j=0;j<1024;j++)     // 每次更新 1K
        {
            ReadBuf[j] = *(__IO uint8_t*)JumpAddress++;
            // 如果是 0x00,或者是 0xFF
            if((ReadBuf[j] != 0xFF)&&(ReadBuf[j] != 0x00))
            {
                // 计算校验和
                Prog_Status_Flag.Old_Prog_ADD_CheckSum += ReadBuf[j];
                // 计算异或校验
                Prog_Status_Flag.Old_Prog_XOR_CheckSum ^= ReadBuf[j];
            }
        }
        // 写入到外部 Flash 中
        SPI_Flash_Write(ReadBuf,Addr,1024);
        Addr += 1024;
    }

    // 校验程序
    Addr = OLD_PROG_ADDR;
    for(i=0; i<60; i++)
    {
        // 从 Flash 中读取程序
        SPI_Flash_Read(ReadBuf,Addr,1024);
        for(j=0; j<1024; j++)
        {
            // 计算校验码
            if((ReadBuf[j] != 0xFF)&&(ReadBuf[j] != 0x00))
            {
                ADD_CheckSum += ReadBuf[j];
                XOR_CheckSum ^= ReadBuf[j];
            }
        }
        Addr += 1024;
    }

    #ifdef PROG_VERIFY
    // 判断程序是否相同
    if((Prog_Status_Flag.Old_Prog_ADD_CheckSum == ADD_CheckSum)
    &&(Prog_Status_Flag.Old_Prog_XOR_CheckSum == XOR_CheckSum))
    {
        // 备份当前程序成功
        Prog_Status_Flag.Backup_Flag = 0xA5;
        Prog_Status_Flag.Update_Flag = 0x00;
        // 将校验码写入外部 Flash PROG_FLAG_ADDR 开始的地址中
        SPI_Flash_Write(&Prog_Status_Flag.Backup_Flag,PROG_FLAG_ADDR,sizeof(struct _Prog_Status_Flag));
    }
    #endif
}


// 更新新程序
void Updata_New_Program()
{
    unsigned int i,j;
    unsigned char ADD_CheckSum = 0,XOR_CheckSum = 0;
    unsigned int Addr;
    unsigned int WriteAddr;
    uint32_t *p; 

    FLASH_Unlock();                             // MCU 内部 Flash 擦写解除锁定
    Addr = NEW_PROG_ADDR;
    WriteAddr = STARTADDR;
    Prog_Status_Flag.New_Prog_ADD_CheckSum = 0;
    Prog_Status_Flag.New_Prog_XOR_CheckSum = 0;
    JumpAddress = STARTADDR;
    for(i=0; i<60; i++)         // 60 K
    {
        SPI_Flash_Read(ReadBuf,Addr,1024);              // 从外部 Flash 读取程序

        for(j=0; j<1024; j++)   // 1k 
        {
            //ReadBuf[j] = (u8)j;
            if((ReadBuf[j] != 0xFF)&&(ReadBuf[j] != 0x00))
            {
                ADD_CheckSum += ReadBuf[j];
                XOR_CheckSum ^= ReadBuf[j];
            }
        }
        // 擦除并写入内部 Flash
        if(FLASH_ErasePage(WriteAddr) == FLASH_COMPLETE)    // 擦除并写入
        {
            p = (uint32_t*)ReadBuf;
            for(j=0; j<256; j++)
            {
                FLASH_ProgramWord(WriteAddr,*p++);          // 每次写入 32 bit
                WriteAddr += 4;
            }

            Addr += 1024;
        }
        else
        {
            // 显示屏显示提示 Flash Error
        }
    }
    // 校验写入的程序
    for(i=0;i<60;i++)       // 60k
    {
        for(j=0;j<1024;j++) // 1k
        {
            ReadBuf[j] = *(__IO uint8_t*)JumpAddress++;
            // 如果是 0x00,或者是 0xFF
            if((ReadBuf[j] != 0xFF)&&(ReadBuf[j] != 0x00))
            {
                // 计算校验和
                Prog_Status_Flag.New_Prog_ADD_CheckSum += ReadBuf[j];
                // 计算异或校验
                Prog_Status_Flag.New_Prog_XOR_CheckSum ^= ReadBuf[j];
            }
        }
    }

    #ifdef PROG_VERIFY
    // 判断是否相同
    if((Prog_Status_Flag.New_Prog_ADD_CheckSum == ADD_CheckSum)
    &&(Prog_Status_Flag.New_Prog_XOR_CheckSum == XOR_CheckSum))
    {
        // 程序更新成功
        Prog_Status_Flag.Backup_Flag = 0xA5;
        Prog_Status_Flag.Update_Flag = 0x00;
        // 将校验码写入外部 Flash PROG_FLAG_ADDR 开始的地址中
        SPI_Flash_Write(&Prog_Status_Flag.Backup_Flag,PROG_FLAG_ADDR,sizeof(struct _Prog_Status_Flag));
    }
    #endif
}

/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */
int main(void)
{

    //uart_init(9600);          // 串口初始化为9600
    LED_Init();                 // 初始化与LED连接的硬件接口
    KEY_Init();                 // 按键初始化            
    SPI_Flash_Init();               // SPI FLASH 初始化

    FlashID = SPI_Flash_ReadID();   // 读取外部 FLASH ID,用于判断外部 FLASH 是否 OK

    if(FlashID == 0xE013)           // 我使用的FLASH ID 是0xE013,这个值根据实际读取到的值变更就可以
    {
        // Updata_New_Program();
        // 读取更新标志
        SPI_Flash_Read(&Prog_Status_Flag.Backup_Flag,PROG_FLAG_ADDR,sizeof(struct _Prog_Status_Flag));
        // 判断是否需要备份程序,新的 FLASH 读出来都是 0xFF
        if(Prog_Status_Flag.Backup_Flag != 0xA5)
        {
            // 如果检测到是新的 Flash 或者是第一次开机,则备份当前的程序,
            // 备份后在进行校验,校验OK后将校验码写入 PROG_FLAG_ADDR 开始的地址中
            Backup_Current_Program();
        }
        // 判断是否需要更新程序
        if(Prog_Status_Flag.Update_Flag == 0x55)
        {
            // 显示屏显示提示,正在更新程序....
            Backup_Current_Program();
            Updata_New_Program();
        }
    }
    else
    {
        // 显示屏显示提示 Flash Error
    }

    // 跳转到用户程序
    if (((*(__IO uint32_t*)STARTADDR) & 0x2FFE0000 ) == 0x20000000)
    { 
      /* Jump to user application */
      JumpAddress = *(__IO uint32_t*) (STARTADDR + 4);
      Jump_To_Application = (pFunction) JumpAddress;
      /* Initialize user application's Stack Pointer */
      __set_MSP(*(__IO uint32_t*) STARTADDR);
      Jump_To_Application();
    }
}

* 注意如果你使用的是 STM32F103RC 内部有 256 Kbytes Flash 的 MCU, 则需要注意一点, FLASH_ErasePage 每次擦除不是 1024 byte, 而是 2048 byte, 我实测是这个样子,所以这段程序要改成*

u8 ReadBuf[2048];           // 缓存,1024 , 每次更新 1024, 改成 2048

// 擦除并写入内部 Flash
        if(FLASH_ErasePage(WriteAddr) == FLASH_COMPLETE)    // 擦除并写入
        {
            p = (uint32_t*)ReadBuf;
            for(j=0; j<512; j++)              // 256 改成 512
            {
                FLASH_ProgramWord(WriteAddr,*p++);          // 每次写入 32 bit
                WriteAddr += 4;
            }

            Addr += 2048;                     // 1024 改成 2048
        }

类似的做法,将程序中的 1024 都改成 2048

最后附上项目的完整代码, STM32F103RC 内部有 256 Kbytes Flash 的请自行更改。
http://download.csdn.net/detail/longzhishen/9885477
好了,编译执行一下看看。

你可能感兴趣的:(stm32)