现场升级方案:采用U盘方式进行程序IAP升级功能的实现

现场升级方案:采用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 Host Stack\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("HostInitialized\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 fileoperations...\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 file successful\r\n");
    SCB->VTOR  =APP_START_ADDR;                                             
    ExceuteApplication();                                                      
    FILE_Close(fdr);                                                            
    } else {
        PRINT_Log("\r\n write filefailed\r\n");
    }
}

上面的代码可以分为两部分:1.从U盘读取bin文件2.IAP功能。先说IAP部分,IAP实现方法有UART,GPRS,USB等方式。要进行IAP设计,先划分FLASH扇区。LPC1788的FLASH划分如下:



将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              // 用户程序起始地址
#define APP_END_ADDR        0x78000                //LPC1788512K Flash         
//#define APP_SIZE       0x10000 

#define APP_START_SECTOR     10
#define APP_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_tsec1, 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_tdst, 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.bin output\FLASH\LPC177x_8x.axf;3.C/c++选项:Optimization选择高优先级:Level3;4.Asm选项:Define填NO_CRP;不产生空文件夹5.Linker选项:勾选UseMemory layout from Target Dialog.整个工程就算建立起来了。附两个版本的代码,仅限参考:

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

带操作系统UCOS版本:http://download.csdn.net/download/u012246376/8453349

 Blog:         http://blog.sina.com.cn/u/5147581417
                    http://bbs.ednchina.com/BLOG_976455580_2004323.HTM
 嵌入式开发交流:本人QQ:976455580

 

 

hhhhhh

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值