現場升級方案:LPC1788採用U盤方式進行程序IAP升級功能的實現

現場升級方案:LPC1788採用U盤方式進行程序IAP升級功能的實現
閒來無事,總結一下前段時間做過的U盤升級項目。一個新手的成長之路在於善於總結,生活也是一樣扯遠了,我準備了兩個軟件環境,一個帶操作系統(UCOS)的,另一個裸機版的。隨後我會附上兩個程序代碼。U盤升級可以分爲兩部分代碼:U盤讀取bin文件和IAP功能兩部分。大概說一下實現過程,具體IAP網上都玩壞了。

硬件環境:NXP 1788

軟件環境:KEIL

實現過程:上面說了我準備了兩個程序,就用裸機版的代碼說一下實現流程。帶操作系統的原理都是一樣的。只是多創建幾個任務而已。USB_HOST實現IAP升級,總的思路就是:複製bin文件到U盤->目標板斷電,插上U盤->目標板上電,進入升級->運行升級程序。其實可以更具體,比如說設置升級標誌或者按鍵。

拿到一個程序先從main開始,直接貼代碼,說一大堆廢話有什麼用。

 

int main()
{
  int32_t  rc;
 uint32_t  numBlks,blkSize;
 uint8_t inquiryResult[INQUIRY_LENGTH];

   SystemInit();              

   UART_Init(57600);         
   Host_Init();              
    rc =Host_EnumDev();      
    if (rc ==OK) {
  
       rc = MS_Init( &blkSize, &numBlks, inquiryResult );
       if (rc == OK) {
           rc = FAT_Init();  
           if (rc == OK) {
               Bin_Read();   
           } else {
               return (0);
           }
       } else {
           return (0);
       }
    } else{       
       return (0);
    }
   while(1);
}

下面分模塊說一下, 前面的硬件初始化函數很簡單,USB設備枚舉和FAT文件系統NXP官網上都有,只需改硬件接口,Host_Init函數如下:

void  Host_Init(void)
{
 uint32_t HostBaseAddr;

   LPC_SC->PCONP   |=0x80000000;
   LPC_USB->OTGClkCtrl   =0x00000019; 
    while((LPC_USB->OTGClkSt & 0x00000019) != 0x19);
 
 LPC_USB->StCtrl =0x1; 

  LPC_IOCON->P0_29 &= ~(0x07UL << 0);
   LPC_IOCON->P0_30 &= ~(0x07UL << 0);
   LPC_IOCON->P1_28 &= ~(0x07UL << 0);
   LPC_IOCON->P1_29 &= ~(0x07UL << 0);

   LPC_IOCON->P0_29 |= 0x01UL <<0;                                   
   LPC_IOCON->P0_30 |= 0x01UL <<0;                                   
   LPC_IOCON->P1_28 |= 0x01UL <<0;                                   
   LPC_IOCON->P1_29 |= 0x01UL <<0;                                   //  P1.29 --USB_SDA1     
   PRINT_Log("Initializing HostStack\n");

 
 HostBaseAddr = HOST_BASE_ADDR;

   Hcca      = (volatile HCCA      *)(HostBaseAddr+0x000);
   TDHead    = (volatile HCTD      *)(HostBaseAddr+0x100);
   TDTail    = (volatile HCTD      *)(HostBaseAddr+0x110);
   EDCtrl    = (volatile HCED      *)(HostBaseAddr+0x120);
   EDBulkIn   =(volatile HCED      *)(HostBaseAddr+0x130);
   EDBulkOut  = (volatile HCED      *)(HostBaseAddr+0x140);
   TDBuffer   =(volatile  uint8_t *)(HostBaseAddr+0x150);
   FATBuffer  = (volatile  uint8_t*)(HostBaseAddr+0x1D0);
    UserBuffer =(volatile  uint8_t*)(HostBaseAddr+0x1000);

   
   Host_EDInit(EDCtrl);
   Host_EDInit(EDBulkIn);
   Host_EDInit(EDBulkOut);
   Host_TDInit(TDHead);
   Host_TDInit(TDTail);
   Host_HCCAInit(Hcca);

   Host_DelayMS(50);               
   LPC_USB->Control      = 0;   
   LPC_USB->ControlHeadED =0;   
   LPC_USB->BulkHeadED   = 0;   
   
   
   LPC_USB->CommandStatus = OR_CMD_STATUS_HCR;
   LPC_USB->FmInterval   = DEFAULT_FMINTERVAL; 

                                                   
   LPC_USB->Control  = (LPC_USB->Control &(~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
   LPC_USB->RhStatus =OR_RH_STATUS_LPSC;       
   
   LPC_USB->HCCA = (uint32_t)Hcca;
   LPC_USB->InterruptStatus |=LPC_USB->InterruptStatus;                  

   LPC_USB->InterruptEnable  = OR_INTR_ENABLE_MIE|
                        OR_INTR_ENABLE_WDH |
                        OR_INTR_ENABLE_RHSC |
                        OR_INTR_ENABLE_UE;


   
 NVIC_EnableIRQ(USB_IRQn);              
 NVIC_SetPriority (USB_IRQn,0);   

   PRINT_Log("Host Initialized\n");
}

 這段主要是USB引腳配置和USB主機初始化。Bin_Read()函數如下:

void  Bin_Read(void)
{
     int32_t  fdr;
     uint32_t  bytes_read,writelen;
     uint32_t dstaddr;
     SelSector(APP_START_SECTOR,APP_END_SECTOR);        //選擇扇區
     EraseSector(APP_START_SECTOR,APP_END_SECTOR);                         
     BlankCHK(APP_START_SECTOR,APP_END_SECTOR);                            
     SelSector(APP_START_SECTOR,APP_END_SECTOR);    
     PRINT_Log("\r\nstart file operations...\r\n");

   fdr = FILE_Open(FILENAME_R, RDONLY);
    if (fdr >0) {
       PRINT_Log("Reading from %s...\n", FILENAME_R);
  for(writelen=0;writelen<(APP_END_ADDR-APP_START_ADDR)/1024;writelen++)
   {
    bytes_read =FILE_Read(fdr, UserBuffer,MAX_BUFFER_SIZE);           
    dstaddr=  (uint32_t)(APP_START_ADDR +(writelen)*1024);//dst address.  
   SelSector(APP_START_SECTOR,APP_END_SECTOR);    
   RamToFlash(dstaddr,(uint32_t)UserBuffer, 1024);
   Compare(dstaddr, (uint32_t)UserBuffer, 1024);
   }  
  // printf("%x",writelen);
  PRINT_Log("\r\n write filesuccessful\r\n");
   SCB->VTOR  =APP_START_ADDR;                                             
   ExceuteApplication();                                                      
   FILE_Close(fdr);                                                            
    } else{
       PRINT_Log("\r\n write file failed\r\n");
    }
}   

   上面的代碼可以分爲兩部分:1.從U盤讀取bin文件2.IAP功能。先說IAP部分,IAP實現方法有UART,GPRS,USB等方式。要進行IAP設計,先劃分FLASH扇區。LPC1788的FLASH劃分如下:

現場升級方案:採用U盤方式進行程序IAP升級功能的實現
 
將flash劃分爲兩個區,bootloader和APP區,bootloader存放升級引導程序,即我們的USB_HOST_IAP代碼,根據具體的Code大小確定bootloader的扇區,APP就是用戶程序即需要升級的程序代碼。APP需要配置後面再說。這是我的扇區劃分:
#define    IAP_START_ADDR 0x00000000                                 // IAP開始地址
#define    IAP_LOCATION    0x1FFF1FF1                                  

#defineAPP_START_ADDR      0x00A000             // 用戶程序起始地址
#defineAPP_END_ADDR       0x78000               //LPC1788 512KFlash         
//#defineAPP_SIZE      0x10000 

#defineAPP_START_SECTOR     10
#defineAPP_END_SECTOR        29                  // LPC1788  512K Flash扇區
下面分別概括一下實現IAP命令的函數,IAP功能命令有準備編程扇區,複製RAM到FLASH,擦除扇區,扇區查空,讀器件ID,讀BOOT代碼版本,比較等指令。程序要進行IAP升級,必須要先選擇扇區擦除扇區之後才能寫進Flash。先需要定義系統時鐘,參數和一些變量。
#define    IAP_FCCLK      48000 
uint32_t   paramin[8];                                             
uint32_t   paramout[8];                                              
unsigned long command[5];
unsigned long result[5];
typedef void (*IAP) (unsigned int [ ] , unsigned int []);      
寫數據之前,必須要選擇需要寫入的扇區,選擇扇區部分代碼:
 uint32_t   SelSector(uint8_t   sec1,uint8_t   sec2)
{
    paramin[0] =IAP_SELECTOR;                                         
    paramin[1] =sec1;                                                 
    paramin[2] =sec2;
   (*(void(*)())IAP_LOCATION)(paramin,paramout);                     
   return(paramout[0]);                                               
}
選中扇區之後,要檢查該扇區是否已經有數據,所以要擦除扇區,附代碼:
uint32_t   EraseSector(uint32_t sec1, uint32_t sec2)
{
    paramin[0] =IAP_ERASESECTOR;                                      
    paramin[1] =sec1;                                                 
    paramin[2] =sec2;
    paramin[3] =IAP_FCCLK;
   (*(void(*)())IAP_LOCATION)(paramin,paramout);                     
   return(paramout[0]);                                               
}
下來就是向flash寫入數據,flash起始地址必須以256字節爲分界,調用函數
uint32_t   RamToFlash(uint32_t dst, uint32_t src, uint32_t no)
{
    paramin[0] =IAP_RAMTOFLASH;                                       
    paramin[1] =dst;                                                  
    paramin[2] =src;
    paramin[3] =no;
    paramin[4] =IAP_FCCLK;
   (*(void(*)())IAP_LOCATION)(paramin,paramout);                     
   return(paramout[0]);                                               
寫完之後要進行比較,將RAM讀出來的數據和寫入到flash的數據進行比較,注意flash起始地址必須字對齊,字節個數必須能被4整除,當源或目標地址包含從地址0開始的前64個字節中的任意一個地址時, 比較的結果可能不準確。因爲前64個字節可被重新映射到RAM
uint32_t   Compare(uint32_t   dst, uint32_t   src, uint32_t   no)
{
    paramin[0] =IAP_COMPARE;                                          
    paramin[1] =dst;                                                  
    paramin[2] =src;
    paramin[3] =no;
   (*(void(*)())IAP_LOCATION)(paramin,paramout);                     
   return(paramout[0]);                                               
還有ExceuteApplication()部分的代碼,程序寫入flash之後,要重新映射向量表,從bootloader跳轉到APP執行,這就要獲取程序的入口地址和SP堆棧的值。如下:

__asm void ExceuteApplication(void)
{
  ldr r0, =0x00A000
  ldr r0, [r0]
  mov sp, r0
  ldr r0, =0x00A004
  ldr r0, [r0]
       BX  r0
}

最後關閉文件系統,main裏面最主要讀取bin文件調用IAP功能的Bin_Read()函數說完了。最後說一下APP程序產生bin文件的配置。
KEIL中Target Options配置:
1.將程序入口定位到App即用戶程序的入口地址;2.User選項:Run#1填寫產生bin文件路徑:C:\Keil\ARM\ARMCC\bin\fromelf.exe--bin --outputoutput\FLASH\test.binoutput\FLASH\LPC177x_8x.axf;3.C/c++選項:Optimization選擇高優先級:Level3;4.Asm選項:Define填NO_CRP;不產生空文件夾5.Linker選項:勾選UseMemory layout from TargetDialog.整個工程就算建立起來了。附兩個版本的代碼,僅限參考:

裸機版:http://download.csdn.net/download/u012246376/8453395 

帶操作系統UCOS版本:http://download.csdn.net/download/u012246376/8453349 

 

更多技術文章瀏覽請關注:

百家號:
https://author.baidu.com/home?context=%7B%22app_id%22%3A%221646108714303504%22%7D&wfr=bjh

頭條號:
https://www.toutiao.com/c/user/8115738721/#mid=1646025109246987


發佈了39 篇原創文章 · 獲贊 20 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章