小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

物聯(lián)網(wǎng)應(yīng)用中,STM32在線升級的設(shè)計思路

 袁先森lemon 2019-04-11

在線升級流程:stm32程序的第一條指令,存儲地址的基地址0x8000000開始執(zhí)行。IAP程序升級的執(zhí)行是在BootLoader引導(dǎo)文件執(zhí)行后,進行加載、跳轉(zhuǎn)APP程序。所以每次上電后進入BootLoader判斷是否升級,如果升級則將外部FLASH中的bin文件復(fù)制寫入到ROM中,再接跳轉(zhuǎn),如果不升級則直接跳轉(zhuǎn)app程序。

BootLoader和app程序的FLASH大小需要根據(jù)自己的程序情況自由的分配大小就可以了。

一、在線獲取BIN文件

        獲取bin文件方式:設(shè)備端利用wifi或者gprs通訊模組,使用MQTT協(xié)議(基于TCP/IP),通過消息隊列的方式接受服務(wù)端的升級命令。當(dāng)接收到升級指令時,設(shè)備端會新增一個TCP/IP通道,專門用來傳輸bin文件數(shù)據(jù),并將接收的bin文件存入外部flash中,同時將升級標(biāo)志位置1。升級標(biāo)志位存儲在外部flash中。

二、IAP到APP應(yīng)用程序的跳轉(zhuǎn)   

STM32的內(nèi)部閃存(FLASH)地址起始于0x08000000,一般情況下,程序文件就從此地址開始寫入。

STM32F103ZET6的內(nèi)部閃存(FLASH)地址為0x8000000-0x0807ffff,一共512KB空間。將512KB的空間分成兩塊,一塊存放BootLoader的程序,用來判斷升級跳轉(zhuǎn)APP,根據(jù)實際代碼量來合理分配空間。本例程BootLoader分配了0x8000000-0x0800ffff的地址,64KB大小的空間。用戶程序APP地址為0x8010000-0x807ffff。

    IAP(BootLoader)程序運行先判斷外部flash的升級標(biāo)志位是否有置1,若為1,將外部flash存儲的bin文件復(fù)制到內(nèi)部ROM中,同時將升級標(biāo)志位清0。然后判斷寫入ROM的文件是否為正確的應(yīng)用程序,再完成跳轉(zhuǎn)。

三、IAP程序代碼

1.讀取升級標(biāo)志位和bin文件的大小

// 讀升級標(biāo)志位  

 W25QXX_Read(ADDR_UPDATE_TAG,tagBuff1,1);

 printf(' ADDR_UPDATE_TAG is 0x%04X \r\n',tagBuff1[0]);

delay_ms(100);

// 讀bin文件長度                

 W25QXX_Read(ADDR_UPDATE_length,lengthBuff,3);

 for(i=0;i<3;i++)

         {

          len=(u32)lengthBuff[0]<<16;

    len|=(u32)lengthBuff[1]<<8;

    len|=(u32)lengthBuff[2]<<0;                 

         }

 printf(' len is %d \r\n',len);

2.判斷是否升級。如果升級則開始將外部flash的程序復(fù)制到內(nèi)部ROM中

// 如果升級標(biāo)志位等于1開始轉(zhuǎn)移程序

          if(tagBuff1[0]==0x31)

         {

             u16 filelen=0;

                  u16 filepacketnum=0;

                  u16 remain = 0;

                  filepacketnum=len/2048;

                  if(len%2048) filepacketnum++;

                  for(i=0; i<filepacketnum; i++)

                  {

                                    if(len / 2048)

                                    {

                                             remain= 2048;       

                                    }

                                    else

                                    {

                                             remain= len % 2048;

                                    }

                                    printf('read flash to  rBuff1 \r\n');

                                    W25QXX_Read(ADDR_CODE+i*2048,rBuff1,remain);

//判斷是否為flash應(yīng)用程序,如果是就執(zhí)行更新,如果不是不更新

 if(i==0)

         {     printf('Theaddress of 20001000 is %08x\r\n',(*(vu32*)0X20001000));

if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)

                  {        

                              printf('UpdateFirmware...\r\n'); 

                  }

else

                {

                            printf('Non-flashapplications!\r\n');

                            tagBuff1[0]=0x30;

                     W25QXX_Write(ADDR_UPDATE_TAG,tagBuff1,1);

                                                      break;

                  }

               }      

//如果是應(yīng)用程序,將數(shù)據(jù)寫入STMflash

                           iap_write_appbin(FLASH_APP1_ADDR+i*2048,rBuff1,remain);  

                     filelen -= 2048;

         }      

 3.升級到內(nèi)部ROM完成,將標(biāo)志位清除

                  if(i == filepacketnum)

                  {

                         printf('-----Updatecompleted-------!\r\n');                

                                    tagBuff1[0]=0x30;

                                    W25QXX_WAKEUP();

                                    W25QXX_Erase_Sector(ADDR_UPDATE_TAG); 

               W25QXX_Write(ADDR_UPDATE_TAG,tagBuff1,1);

         //測試寫入標(biāo)志位是否成功

                                    W25QXX_Read(ADDR_UPDATE_TAG,tagBuff2,1);

                printf(' ADDR_UPDATE_TAGis 0x%04X \r\n',tagBuff2[0]);

                     delay_ms(200);

                  }      

         4.升級完成后判斷寫入的bin文件是否正確,若正確則跳轉(zhuǎn)到FLASH_APP1_ADDR

                           delay_ms(200);

                           printf('Judgingwhether the application is correct!\r\n');

 if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)

          {      

            printf('Firmware packagecorrect!\r\n');

            iap_load_app(FLASH_APP1_ADDR);

           }else

            {

             printf('Firmware packageerror\r\n');

            }      

5.跳轉(zhuǎn)之前,需要將使用過的外設(shè)關(guān)閉,如中斷定時器串口等

voidiap_load_app(u32 appxaddr)

{

         if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)//檢查棧頂?shù)刂肥欠窈戏?/p>

         {

                  jump2app=(iapfun)*(vu32*)(appxaddr+4);                              

                  MSR_MSP(*(vu32*)appxaddr);                                          

        __disable_irq();

                  TIM_DeInit(TIM3);

                  USART_DeInit(USART1);

                  SPI_I2S_DeInit(SPI1);

                  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,DISABLE); 

                  GPIO_DeInit(GPIOF);

                  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,DISABLE);

                  GPIO_DeInit(GPIOG);

                  jump2app();                                                                            

         }

}      

注意:

(1)

__disable_irq();

                  TIM_DeInit(TIM3);

                  USART_DeInit(USART1);

                  SPI_I2S_DeInit(SPI1);

                  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,DISABLE); 

                  GPIO_DeInit(GPIOF);

                  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,DISABLE);

                  GPIO_DeInit(GPIOG);

跳轉(zhuǎn)前,使用過的定時器、中斷、串口、GPIO都需要關(guān)閉,否則跳轉(zhuǎn)會失敗。只關(guān)閉了部分,可能能跳轉(zhuǎn),但是會有一定概率的產(chǎn)生死機,多次復(fù)位才能正常運行。

(2)

u8 rBuff1[2048]__attribute__((at(0X20001000)));

if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)

定義了數(shù)組分配的內(nèi)存地址是0X20001000起,用來從外部flash向stmflash轉(zhuǎn)移bin文件。

而bin文件的第4-7的4個字節(jié)的值是app程序復(fù)位中斷向量的值。固定格式是08xxxxxxxx。

(3)

#defineFLASH_APP1_ADDR            0x08010000

if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)//檢查棧頂?shù)刂肥欠窈戏?/p>

即取0x80010000開始到0x8010003的4個字節(jié)的值,因為我們的應(yīng)用程序APP中設(shè)置把中斷向量表放置在0x08010000開始的位置; 而中斷向量表里第一個放的就是棧頂?shù)刂返闹怠_@句話即通過判斷棧頂?shù)刂分凳欠裾_來判斷是否應(yīng)用程序已經(jīng)下載了,因為應(yīng)用程序的啟動文件剛開始就去初始化化??臻g,如果棧頂值對了,說明應(yīng)用程序已經(jīng)下載了啟動文件,初始化也執(zhí)行了。

四、APP程序代碼

   1.通過JSON解析MQTT消息隊列下發(fā)的升級命令

CLR_CMD_UPDATE_BIN,獲取bin文件包的信息。

       case CLR_CMD_UPDATE_BIN:

       updateBinVersion = cJSON_GetObjectItem(pRoot,'version')->valuestring;

       updateBinFileName = cJSON_GetObjectItem(pRoot,'fileName')->valuestring;

       countPackage = atoi(cJSON_GetObjectItem(pRoot,'countPackage')->valuestring);

       pJsonNode = cJSON_GetObjectItem(pRoot, 'totalLength');//實際文件大小

       totalLength = atoi(pJsonNode->valuestring);

       updateBinPort = cJSON_GetObjectItem(pRoot, 'fileServerPort')->valuestring;

       pJsonNode = cJSON_GetObjectItem(pRoot, 'fileServerIp');

       if (pJsonNode != NULL)

       {

         updateBinIp = pJsonNode->valuestring;

         rt_kprintf('version:%s\n', updateBinVersion);

         rt_kprintf('IP:%s\n', updateBinIp);

         rt_kprintf('Port:%s\n', updateBinPort);

         rt_kprintf('updateBinFilenName:%s\n', updateBinFileName);

         rt_kprintf('countPackage:%d\n', countPackage);

         rt_kprintf('totalLength:%d\n', totalLength);

          param_buf.ip = updateBinIp;

         param_buf.port = updateBinPort;

         param_buf.fileName = updateBinFileName;

         param_buf.countPackage = countPackage;

         param_buf.totalLength = totalLength;

2.     關(guān)閉原有的TCP通道,刪除GPRS任務(wù)(本次使用的是GPRS模組),創(chuàng)建新的TCP任務(wù)新建TCP連接(此TCP在本例中專用于升級BIN)。

         close_tcp();

         /********* delete mqtt thread ************/

         rt_thread_delay(50);

         rt_err_t rt_err;

         rt_err = rt_thread_delete(gprs_thread);

#ifdef KT_PRINTF_DEBUG

          if (rt_err != RT_EOK)

         {

           rt_kprintf('\r\n mqtt task delete failed!\r\n');

         }

         else

         {

           rt_kprintf('\r\n mqtt task delete successed!\r\n');

           /********* 創(chuàng)建 tcp 任務(wù) ************/

            tcp_thread =rt_thread_create('tcp',

                                         tcp_thread_entry, /* tcp_thread_entry */

                                         RT_NULL,          /* 入口參數(shù)是RT_NULL */

                                         2048,             /* 分配空間大小 */

                                         1,                /* 任務(wù)優(yōu)先級 */

                                         20);              /* 時間片 */

           if (tcp_thread != RT_NULL)

              rt_thread_startup(tcp_thread);

           else

              rt_kprintf('\r\n createtcp_thread failed!!\r\n');

         }

#endif

       }

       break;

3. 接收bin文件,存入外部flash中,接收完將升級標(biāo)志位置1

//擦除bin存儲地址

  for(int i = 0; i < countPackage; i++)

  {

   int offset = i * 0x1000;

   W25Q128FV_Erase_Sector((Addr_UPDATE_BIN + offset) / 4096);

  }

  //接收數(shù)據(jù)包

  for(int j = 1; j <= countPackage; j++)

  {

   TCP_DATA_TRANSFORM(j, updateBinFileName);

   rt_thread_delay(50);

  }

 //將升級標(biāo)志1存入flash中

 uint8_t *tag = '1';

 W25Q128FV_Erase_Sector(ADDR_UPDATE_TAG / 4096);

 SPI_FLASH_PageWrite(tag, ADDR_UPDATE_TAG, 1);

 uint8_t rBuff[1];

 SPI_FLASH_Read(rBuff, ADDR_UPDATE_TAG, 1);

 rt_kprintf(' ADDR_UPDATE_TAG is %s \r\n', rBuff);

 //將bin包大小存入flash中

  intptl = param_buf.totalLength;

 uint8_t d[3];

  d[0]= ptl >> 16 & 0xff;

 d[1] = ptl >> 8 & 0xff;

 d[2] = ptl & 0xff;

 SPI_FLASH_PageWrite( d, ADDR_UPDATE_TAG + 1, 3);

//斷開tcp連接,關(guān)閉看門狗電路讓單片機斷電復(fù)位復(fù)位

 disconnectToServer();

 rt_kprintf('^^^^^^^^^^^^^^^^^^^^^^^^^^^tcp_thread

is died!^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n');

 rt_thread_delete(watchdog_thread);

4.接收單個bin數(shù)據(jù)分包

 TCP數(shù)據(jù)單包交互方法

 [0],[1] => 當(dāng)前包序號

 [2],[3] => 當(dāng)前包長度

 [4].... => 包內(nèi)容

 末尾4位是crc校驗位

 1、組需要發(fā)送的數(shù)據(jù)包

 2、通過串口發(fā)送數(shù)據(jù)包

 3、接受服務(wù)器返回的數(shù)據(jù)包

 4、對比從串口獲取的數(shù)據(jù)包實際長度和 從服務(wù)器數(shù)據(jù)包內(nèi)定義的長度,  不相等則重發(fā)

  int返回值一次讀取的數(shù)據(jù)長度

int TCP_DATA_TRANSFORM(int packageIndex,char *fileName)

{

 buildSendP   ackage((char*)content, packageIndex, fileName);

 rt_kprintf(' \r\n content  is  %s \r\n', content);

 transport_gprs_sendPacketBuffer(content, strlen((char *)content));

  intpIndex, pLength;

 wait_for_tcp_data(CRT_HEAD_LENGTH, 10);

 char *buf = buf_uart2.buf;

 memcpy(tcpReceiveBuf, buf + 11, buf_uart2.index);

  //包序號

 pIndex = (tcpReceiveBuf[0] & 0xff) << 8 | tcpReceiveBuf[1]& 0xff;

  //包長度

 pLength = (tcpReceiveBuf[2] & 0xff) << 8 | tcpReceiveBuf[3]& 0xff;

 rt_kprintf(' \r\n  server saypIndex is _____%d____   pLength   is ____%d____  \r\n', pIndex,pLength);

 unsigned char *reBuf = tcpReceiveBuf;

 uint32_t deCodeCRC = my_crc32(reBuf, pLength - 4);

 char crcStr[4] = {0};

 unsigned char crcArray[4] = {tcpReceiveBuf[pLength - 4],tcpReceiveBuf[pLength - 3], tcpReceiveBuf[pLength - 2], tcpReceiveBuf[pLength -1]};

 byteArrayToHexStr(crcArray, crcStr);

 uint32_t serverCRC = htoi(crcStr);

 rt_kprintf(' \r\n deCodeCRC is %d , serverCRC   is %d  \r\n', deCodeCRC,serverCRC);

  if(serverCRC > 0 && deCodeCRC > 0 && serverCRC ==deCodeCRC)

  {

   // for (int i = 4; i < pLength; i++)

   // {

   //   rt_kprintf('%02x',tcpReceiveBuf[i]);

   // }

   // rt_kprintf('\r\n');

   int pOffset = 0X400 * (packageIndex - 1);

   SPI_FLASH_PageWrite(reBuf + 4, Addr_UPDATE_BIN + pOffset, 1024);

//////////////////////////////

   uint8_t binBuff2[256];

   SPI_FLASH_Read(binBuff2, Addr_UPDATE_BIN + pOffset, 256);

   rt_kprintf(' \r\n binBuff2 is  \r\n');

   for (size_t i = 0; i < 256; i++)

    {

     rt_kprintf('%02x', binBuff2[i]);

    }

   rt_kprintf('\r\n');

   return pLength - 8;

  }

 else

  {

   return TCP_DATA_TRANSFORM(packageIndex, fileName);

  }

}

五、APP程序配置

中斷向量偏移設(shè)置

修改代碼存放的地址空間

 

六、服務(wù)器側(cè),程序代碼

1.立即執(zhí)行固件更新 1、查詢數(shù)據(jù)庫bin 2、獲取所有設(shè)備sn列表 3、通知設(shè)備開始更新了,內(nèi)容包括:文件名,大小,版本號

     */

    @RequestMapping(value = '/update')

    @ResponseBody

    public Object update(LongdeviceBinId) {

        DeviceBinbin = deviceBinService.selectById(deviceBinId);

        if(bin == null)

            throw newGunsException(BizExceptionEnum.UPDATE_BIN_NOT_EXISTED);

        if(MinaConfig.fileCacheInit(bin)) {

            deviceBinService.setBinActive(deviceBinId);

        }else

            throw newGunsException(BizExceptionEnum.UPDATE_BIN_FAILD);

 List<byte[]>cacheList = CacheKit.get(Cache.BIN_GROUP, CacheKey.BIN_NAME);

        StringfilenName = bin.getFileName();

     Stringversion = filenName.substring(filenName.indexOf('_') + 1,filenName.lastIndexOf('.'));

        StringcountPackage = String.valueOf(cacheList.size());

        StringtotalLength = String.valueOf(bin.getFileData().length);

        StringfileServerIp = env.getProperty('spring.tcp.host');

        StringfileServerPort = env.getProperty('spring.tcp.port');              

        JSONObjectobj = new JSONObject();

        obj.put('method','updateBin');

        obj.put('fileName',filenName);

        obj.put('version',version);

        obj.put('countPackage',countPackage);

        obj.put('totalLength',totalLength);

        obj.put('fileServerIp',fileServerIp);

        obj.put('fileServerPort',fileServerPort);

        logger.info('更新固件下發(fā)命令:{}',obj.toJSONString());

List<Device>list= deviceService.selectList(newEntityWrapper<Device>().eq('is_onLine', 1));

        Message<String>message;

        Device  device;

        for(int i = 0,size=list.size() ; i < size; i++) {

            device = list.get(i);

if (device != null &&StringUtils.isNotBlank(device.getSnNum())) {

   message= MessageBuilder.withPayload(obj.toJSONString())

                        .setHeader(MqttHeaders.TOPIC,MqttConst.getCourrentIssueCmd(device.getSnNum())).build();

                mqtt.handleMessage(message);

                if(i%10== 0) {

                    try {Thread.sleep(1000);} catch(InterruptedException e) {e.printStackTrace();}  

                }

            }

        }

        returnSUCCESS_TIP;

    }

2.分片bin數(shù)據(jù)包,存入數(shù)據(jù)緩存 2 + 2 + data + 4 分包序號 +發(fā)送數(shù)據(jù)包總長度 + 數(shù)據(jù)包 + crc校驗碼

     *

     *@param bin

     */

    public static booleanfileCacheInit(DeviceBin bin) {

        intfileSizeSlice = FILE_SLICE_SIZE;

        // 分片大小等于1k

        if(bin != null) {

            byte[] bytes = bin.getFileData();

            int bytesSize = bytes.length;

            int totalDataPackage = (int)Math.ceil((float) bytesSize / fileSizeSlice);

            logger.info('需要發(fā)送的總包數(shù):' + totalDataPackage);

            int currentFileSizeSlice, offsize,floorDataPackage, j, sliceIndex, packageLength, crcLength = 4;

            floorDataPackage = bytesSize /fileSizeSlice;

            List<byte[]> bufferArray = new ArrayList<>(128);

            for (int i = 1; i <= totalDataPackage;i++) {

                currentFileSizeSlice= fileSizeSlice;// 當(dāng)前文件分包大小

                if(i > floorDataPackage) {

                    currentFileSizeSlice = bytesSize %fileSizeSlice;

                }

                byte[]preSendData = new byte[2 + 2 + currentFileSizeSlice];

                // 低位在前,高位在后

                // 分包序號

                sliceIndex= i;

                j =0;

                preSendData[j++]= (byte) ((sliceIndex >> 8) & 0xff);

                preSendData[j++]= (byte) (sliceIndex & 0xff);

                // 分包長度

                packageLength= preSendData.length + crcLength;

                preSendData[j++]= (byte) ((packageLength >> 8) & 0xff);

                preSendData[j++]= (byte) (packageLength & 0xff);

                offsize= fileSizeSlice * (i - 1);// 分包偏移量

                System.arraycopy(bytes,offsize, preSendData, j, currentFileSizeSlice);

                //crc32校驗

                CRC32crc32 = new CRC32();

                crc32.update(preSendData);

                ByteBufferbuffer = ByteBuffer.allocate(8);

                buffer.putLong(crc32.getValue());

                logger.info('packageindex {},crc32 code is {}', i, crc32.getValue());

                byte[]sendData = new byte[packageLength];

                System.arraycopy(preSendData,    0, sendData, 0, preSendData.length);

                System.arraycopy(buffer.array(),4, sendData, preSendData.length, crcLength);

                bufferArray.add(sendData);

            }

            CacheKit.put(Cache.BIN_GROUP,CacheKey.BIN_NAME, bufferArray);

            logger.info('bin file cached success!');

            return true;

        }

        returnfalse;

    }

3.往設(shè)備端發(fā)送bin的分包

    public void messageReceived(IoSessionsession, Object message) throws Exception {

        StringstrMsg = message.toString();

        logger.info('服務(wù)端接收到的數(shù)據(jù)為:  {}',strMsg);

        if(strMsg.trim().equalsIgnoreCase(EXIT)) {

            session.closeOnFlush();

            return;

        }

        JSONObjectobj = JSON.parseObject(strMsg);

        //String fileName = obj.getString('fileName');

        intpackageIndex = obj.getIntValue('packageIndex');

        List<byte[]>cacheList = CacheKit.get(Cache.BIN_GROUP, CacheKey.BIN_NAME);

        if(packageIndex > cacheList.size()) {

            return;

        }

        byte[]buf = cacheList.get(packageIndex - 1);

        IoBufferio = IoBuffer.wrap(buf, 0, buf.length);

        session.write(io);

    }

作者:馮美文、應(yīng)曉川

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多