天天看點

STM32序列槽更新STM32更新功能分為2部分,一部分是bootloader程式,另一部分是使用者應用程式組成(以STM32F103C8為例來說明 )應用程式部分

  • STM32更新功能分為2部分,一部分是bootloader程式,另一部分是使用者應用程式組成(以STM32F103C8為例來說明 )

bootloader工程分析

啟動檔案的複位部分

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
     IMPORT  __main
     IMPORT  SystemInit
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP
           

系統複位後會調用SystemInit函數,此函數在system_stm32f10x.c檔案中,其中有個宏決定程式flash或SRAM偏移定義的宏

/*!< Uncomment the following line if you need to relocate your vector Table in
     Internal SRAM. */ 
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET  0x0 /*!< Vector Table base offset field. 
                                  This value must be a multiple of 0x200. */
           

在SystemInit函數的最後設定這個偏移值

#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif 
           

啟動方試有SRAM和FLASH兩種,由宏VECT_TAB_SRAM來決定,SRAM_BASE基位址和FLASH_BASE基位址定義如下

#define FLASH_BASE            ((uint32_t)0x08000000) /*!< FLASH base address in the alias region */
#define SRAM_BASE             ((uint32_t)0x20000000) /*!< SRAM base address in the alias region */
           

通常bootloader程式會放在偏移為0開始的一段空間,剛好是FLASH_BASE基址與偏移值為0加起來的位址值,在keil中位址配置如下

STM32序列槽更新STM32更新功能分為2部分,一部分是bootloader程式,另一部分是使用者應用程式組成(以STM32F103C8為例來說明 )應用程式部分

程式SystemInit執行完後進入main函數

int main(void) 	
{   
	  
	FLASH_Unlock();
	UART_Init();
	BspTim2Init();
	Main_Menu ();
	
	if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
         {

                BspTim2Close();     
                JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
                Jump_To_Application = (pFunction) JumpAddress;
              
                __set_MSP(*(__IO uint32_t*) ApplicationAddress);
                Jump_To_Application();
            }
            else
            {
                SerialPutString("no user Program\r\n\n");
            }
	
  while(1); 
}
           
  1. 首先調用FLASH_Unlock函數解鎖flash操作
  2. UART_Init函數初時化,為接收更新檔案操作作準備
  3. BspTim2Init函數初時化定時器,提供逾時計數用
  4. Main_Menu函數更新菜單操作功能實作
  5. 測試使用者app位址是不是在APPLICATION_ADDRESS位置。檢測棧頂的位址,來檢驗app是否下載下傳成功

    if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000){}

        其中ApplicationAddress為使用者應用程式起始位址,假如從0x8003000開始,則定義如下

#define ApplicationAddress    0x8003000
           

      6. APPLICATION_ADDRESS + 4對應的是app中斷向量表的第二項,複位位址 

          JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);

      7.把位址強轉為函數指針

          Jump_To_Application = (pFunction) JumpAddress;

        JumpAddress 和Jump_To_Application定義如下

typedef  void (*pFunction)(void);
pFunction Jump_To_Application;
uint32_t JumpAddress;
           

8.  設定主函數棧指針 

 __set_MSP(*(__IO uint32_t*) ApplicationAddress);

9.調用函數,跳轉到app複位位址去執行複位操作

Jump_To_Application();

寫更新内容到flash,資料接收采用ymode協定進行資料接收,具體協定可找度娘了解

/*******************************************************************************
  * @函數名稱:Ymodem_Receive
  * @函數說明:通過 ymodem協定接收一個檔案
  * @輸入參數:buf: 首位址指針
  * @輸出參數:無
  * @傳回參數:檔案長度
  * @曆史記錄:     
     <作者>    <時間>      <修改記錄>
*******************************************************************************/
int32_t Ymodem_Receive (uint8_t *buf)
{
    uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], file_size[FILE_SIZE_LENGTH], *file_ptr, *buf_ptr;
    int32_t i, j, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;

    //初始化Flash位址變量
    FlashDestination = ApplicationAddress;

    for (session_done = 0, errors = 0, session_begin = 0; ;)
    {
        for (packets_received = 0, file_done = 0, buf_ptr = buf; ;)
        {
            switch (Receive_Packet(packet_data, &packet_length, NAK_TIMEOUT))
            {
            case 0:
                errors = 0;
                switch (packet_length)
                {
                    //發送端終止
                case - 1:
                    Send_Byte(ACK);
                    return 0;
                    //結束傳輸
                case 0:
                    Send_Byte(ACK);
                    file_done = 1;
                    break;
                    //正常的資料包
                default:
                    if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff))
                    {
                        Send_Byte(NAK);
                    }
                    else
                    {
                        if (packets_received == 0)
                        {
                            //檔案名資料包
                            if (packet_data[PACKET_HEADER] != 0)
                            {
                                //檔案名資料包有效資料區域
                                for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);)
                                {
                                    file_name[i++] = *file_ptr++;
                                }
                                file_name[i++] = '\0';
                                for (i = 0, file_ptr ++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);)
                                {
                                    file_size[i++] = *file_ptr++;
                                }
                                file_size[i++] = '\0';
                                Str2Int(file_size, &size);

                                //測試資料包是否過大
                                if (size > (FLASH_SIZE - 1))
                                {
                                    //結束
                                    Send_Byte(CA);
                                    Send_Byte(CA);
                                    return -1;
                                }

                                //計算需要擦除Flash的頁
                                NbrOfPage = FLASH_PagesMask(size);

                                //擦除Flash
                                for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
                                {
                                    FLASHStatus = FLASH_ErasePage(FlashDestination + (PageSize * EraseCounter));
                                }
                                Send_Byte(ACK);
                                Send_Byte(CRC16);
                            }
                            //檔案名資料包空,結束傳輸
                            else
                            {
                                Send_Byte(ACK);
                                file_done = 1;
                                session_done = 1;
                                break;
                            }
                        }
                        //資料包
                        else
                        {
                            memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
                            RamSource = (uint32_t)buf;
                            for (j = 0; (j < packet_length) && (FlashDestination <  ApplicationAddress + size); j += 4)
                            {
                                //把接收到的資料編寫到Flash中
                                FLASH_ProgramWord(FlashDestination, *(uint32_t*)RamSource);

                                if (*(uint32_t*)FlashDestination != *(uint32_t*)RamSource)
                                {
                                    //結束
                                    Send_Byte(CA);
                                    Send_Byte(CA);
                                    return -2;
                                }
                                FlashDestination += 4;
                                RamSource += 4;
                            }
                            Send_Byte(ACK);
                        }
                        packets_received ++;
                        session_begin = 1;
                    }
                }
                break;
            case 1:
                Send_Byte(CA);
                Send_Byte(CA);
                return -3;
            default:
                if (session_begin > 0)
                {
                    errors ++;
                }
                if (errors > MAX_ERRORS)
                {
                    Send_Byte(CA);
                    Send_Byte(CA);
                    return 0;
                }
                Send_Byte(CRC16);
                break;
            }
            if (file_done != 0)
            {
                break;
            }
        }
        if (session_done != 0)
        {
            break;
        }
    }
    return (int32_t)size;
}
           

1.收到檔案名及長度會計算大小然後擦除flash

//計算需要擦除Flash的頁
 NbrOfPage = FLASH_PagesMask(size);

 //擦除Flash
 for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
 {
  FLASHStatus = FLASH_ErasePage(FlashDestination + (PageSize * EraseCounter));
 }
           

2.接收到檔案内容寫對應的flash,從使用者程式 ApplicationAddress位址開始寫

uint32_t FlashDestination = ApplicationAddress;
           

寫操作如下:

memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
 RamSource = (uint32_t)buf;
 for (j = 0; (j < packet_length) && (FlashDestination <  ApplicationAddress + size); j += 4)
{
  //把接收到的資料編寫到Flash中
   FLASH_ProgramWord(FlashDestination, *(uint32_t*)RamSource);

    if (*(uint32_t*)FlashDestination != *(uint32_t*)RamSource)
     {
                                    //結束
        Send_Byte(CA);
        Send_Byte(CA);
        return -2;
      }
    FlashDestination += 4;
    RamSource += 4;
    }
    Send_Byte(ACK);
           

寫入後會讀回内容,如果寫入的與讀回的内容不一緻,則會終止更新操作

  • 應用程式部分

此部分隻需注意以下幾點即可

1.  起始位址設定,即bootloader跳轉到使用者程式的位址

STM32序列槽更新STM32更新功能分為2部分,一部分是bootloader程式,另一部分是使用者應用程式組成(以STM32F103C8為例來說明 )應用程式部分

2.偏移位址設定

/*!< Uncomment the following line if you need to relocate your vector Table in
     Internal SRAM. */ 
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET  0x3000 /*!< Vector Table base offset field. 
                                  This value must be a multiple of 0x200. */
           

更新時要使用有ymode協定的序列槽工具才能正常更新,最後,demo下載下傳位址:

https://download.csdn.net/download/mygod2008ok/12082368