这篇我们主要是学习下app部分固件的设计逻辑。

1. 初始化过程中开辟一个环形缓冲区,设置串口为中断接收,重写串口中断回调函数,也就是每次接收一个字节的数据就往环形缓冲区里丢一个数据。

void gizwitsInit(void)
{    pRb.rbCapacity = RB_MAX_LEN;pRb.rbBuff = rbBuf;if(0 == rbCreate(&pRb))     //初始化一个环形缓冲区,实际就是一个链表结构{GIZWITS_LOG("rbCreate Success \n");}else{GIZWITS_LOG("rbCreate Faild \n");}memset((uint8_t *)&gizwitsProtocol, 0, sizeof(gizwitsProtocol_t));
}int32_t gizPutData(uint8_t *buf, uint32_t len)  //往环形缓冲区里添加数据
{int32_t count = 0;if(NULL == buf){GIZWITS_LOG("ERR: gizPutData buf is empty \n");return -1;}count = rbWrite(&pRb, buf, len);if(count != len){GIZWITS_LOG("ERR: Failed to rbWrite \n");return -1;}return count;
}void HAL_UART_RxCpltCallback(UART_HandleTypeDef*UartHandle) //串口接收中断的回调函数{ if(UartHandle->Instance == USART2) { gizPutData((uint8_t *)&aRxBuffer, 1);  //串口接收到一个字节数据就存进环形缓冲区HAL_UART_Receive_IT(&huart2, (uint8_t *)&aRxBuffer, 1);//开启下一次接收中断 } } 

2.死循环里会一直尝试解析一个完整的数据包,如果发现环形缓冲区里有一个完整的数据包就取出并解析出对应的命令,然后根据串口协议完成数据的接收。例如和OTA相关的指令。

以下是关于OTA指令的部分代码。

int32_t gizwitsHandle(dataPoint_t *currentData){.........//判断是否有一包完整的数据ret = gizProtocolGetOnePacket(&pRb, gizwitsProtocol.protocolBuf, &protocolLen);if(0 == ret){GIZWITS_LOG("Get One Packet!\n");recvHead = (protocolHead_t *)gizwitsProtocol.protocolBuf;switch (recvHead->cmd)  //根据命令进行相应的操作{...............case CMD_ASK_BIGDATA:   //云端通知MCU准备进行OTAGIZWITS_LOG("CMD_ASK_BIGDATA \n");gizProtocolCommonAck(recvHead);if(0 == Pro_W2D_UpdateCmdHandle((uint8_t     *)gizwitsProtocol.protocolBuf + sizeof(protocolHead_t),                 protocolLen - sizeof(protocolCommon_t))){romUpdate.otaBusyModeFlag = 1;GIZWITS_LOG("System In OTA Mode BusyFlag = %d \n",     romUpdate.otaBusyModeFlag);}else{GIZWITS_LOG("Update Ask Handle Failed \n");Pro_D2W_UpdateSuspend();}break;case CMD_BIGDATA_SEND:  //下发OTA数据updatePieceResult = Pro_W2D_UpdateDataHandle((uint8_t*)gizwitsProtocol.protocolBuf +                                                     sizeof(protocolHead_t), protocolLen - sizeof(protocolCommon_t),HEX);gizProtocolCommonAck(recvHead);if(0 != updatePieceResult){romUpdate.otaBusyModeFlag = 0;GIZWITS_LOG("CMD_BIGDATA_SEND , Piece Handle Faild . System OTA Mode Over BusyFlag = %d \n",romUpdate.otaBusyModeFlag);Pro_D2W_UpdateSuspend();GIZWITS_LOG("System Go On \n");}break;case CMD_S_STOP_BIGDATA_SEND:    //停止下发OTA数据gizProtocolCommonAck(recvHead);romUpdate.otaBusyModeFlag = 0;GIZWITS_LOG("System OTA Mode Over BusyFlag = %d \n",romUpdate.otaBusyModeFlag);break;......}}
}

3. 官方的串口协议

4. 接收流程图

5. 下面来一步步分析下这几个函数是如何实现的,注意看我在关键部分添加的注释

1) 先来看看如何从环形缓冲区里取出一帧完整的数据


/**
* @brief Get a packet of data from the ring buffer
*
* @param [in]  rb                  : Input data address
* @param [out] data                : Output data address
* @param [out] len                 : Output data length
*
* @return : 0,Return correct ;-1,Return failure;-2,Data check failure
*/
static int8_t gizProtocolGetOnePacket(rb_t *rb, uint8_t *gizdata, uint16_t *len)
{int32_t ret = 0;uint8_t sum = 0;int32_t i = 0;uint8_t tmpData;uint8_t tmpLen = 0;uint16_t tmpCount = 0;static uint8_t protocolFlag = 0;static uint16_t protocolCount = 0;static uint8_t lastData = 0;   //静态变量,存储上一个读出来的字节static uint8_t debugCount = 0;uint8_t *protocolBuff = gizdata;protocolHead_t *head = NULL;if((NULL == rb) || (NULL == gizdata) ||(NULL == len)){GIZWITS_LOG("gizProtocolGetOnePacket Error , Illegal Param\n");return -1;}tmpLen = rbCanRead(rb);   //取出环形缓冲区中数据的长度if(0 == tmpLen){return -1;}for(i=0; i<tmpLen; i++){ret = rbRead(rb, &tmpData, 1);     //每次读取一个字节的数据if(0 != ret){if((0xFF == lastData) && (0xFF == tmpData))   //判断帧头 0xffff{if(0 == protocolFlag){protocolBuff[0] = 0xFF;protocolBuff[1] = 0xFF;protocolCount = 2;protocolFlag = 1;    //置1表明收到数据头,开始接收一帧数据}else{if((protocolCount > 4) && (protocolCount != tmpCount)){protocolBuff[0] = 0xFF;protocolBuff[1] = 0xFF;protocolCount = 2;}}}else if((0xFF == lastData) && (0x55 == tmpData)) //根据协议,中间数据部分出现0xff,要自动过滤掉后面添加的0x55{}else   {if(1 == protocolFlag)  //已接收到帧头,继续接收其余的数据{protocolBuff[protocolCount] = tmpData;protocolCount++;if(protocolCount > 4){head = (protocolHead_t *)protocolBuff;tmpCount = exchangeBytes(head->len)+4;if(protocolCount == tmpCount)  //如何接收的数据长度和发送的数据长度一致,终止接收,说明一帧数据接收完毕{break;}}}}lastData = tmpData;   //当前的一字节数据缓存起来debugCount++;}}if((protocolCount > 4) && (protocolCount == tmpCount)){sum = gizProtocolSum(protocolBuff, protocolCount);  //接收完一帧数据要计算校验和if(protocolBuff[protocolCount-1] == sum){memcpy(gizdata, protocolBuff, tmpCount);*len = tmpCount;protocolFlag = 0;protocolCount = 0;debugCount = 0;lastData = 0;return 0;}else{return -2;}}return 1;
}

2) 接收数据包

这里需要注意的是:每次分片接收多少字节的数据是由MCU端决定的,示例代码中是256字节,因为这关系到往flash写数据的地址计算(PIECE_MAX_LEN=256)

tempWFlashAddr = SYS_APP_BAK_SAVE_ADDR_BASE + (piecenum-1) * PIECE_MAX_LEN;

wFlashData((uint8_t *)pieceData.piececontent , dataLen - 4, tempWFlashAddr);

/**
* @brief Pro_W2D_UpdateDataHandle* update Piece Handle ,  Judge Last Piece* @param[in] indata   : Piece Data
* @param[in] dataLen    : Piece Length
* @param[in] formatType : Piece Data Format
* @param[out]
* @return  0,Handle Success
*                   -1,Input Param Illegal
*                   -2,Last Piece , MD5 Check Faild
*
*/
int8_t Pro_W2D_UpdateDataHandle(uint8_t *inData , uint32_t dataLen , otaDataType formatType)
{uint16_t piecenum = 0;uint16_t piececount = 0;uint32_t tempWFlashAddr = 0;updataPieceData_TypeDef pieceData;uint8_t md5_calc[SSL_MAX_LEN];//MD5 Calculate Factif(NULL == inData){return -1;}memcpy((uint8_t *)&pieceData, inData, dataLen);piecenum = exchangeBytes(pieceData.piecenum);   //分片数据包序号piececount = exchangeBytes(pieceData.piececount);  //总分片数GIZWITS_LOG("******piecenum = %d , piececount = %d, pieceSize = %d******** \r\n",piecenum,piececount,dataLen - 4);//计算要写入的flash地址tempWFlashAddr = SYS_APP_BAK_SAVE_ADDR_BASE + (piecenum-1) * PIECE_MAX_LEN;wFlashData((uint8_t *)pieceData.piececontent , dataLen - 4, tempWFlashAddr);//每个分片数据都进行MD5校验GAgent_MD5Update(&romUpdate.ctx, (uint8_t *)pieceData.piececontent, dataLen - 4);/*updata package data ,ack*/if(piecenum == piececount)  //分片数等于总片数,代表数据接收完成{memset(md5_calc,0,SSL_MAX_LEN);   GAgent_MD5Final(&romUpdate.ctx, md5_calc); //计算出MD5加密值并比对GIZWITS_LOG("MD5 Calculate Success , Will Check The MD5 ..\n ");if(0 != memcmp(romUpdate.update_param.ssl_data, md5_calc, SSL_MAX_LEN)){GIZWITS_LOG("Md5_Cacl Check Faild ,MCU OTA Faild\r\n ");memset((uint8_t *)&romUpdate.update_param,0,sizeof(updateParamSave_t));return -2;}else{//比对成功,将相关信息写入flag区,重启MCUGIZWITS_LOG("MD5 Check Success ,Storage  ROM Success , Write Update Flag\n ");flash_erase(sizeof(updateParamSave_t) , UPDATE_PARAM_SAVE_ADDR_BASE);romUpdate.update_param.rom_statue = 0xEEEE;  //设置有新固件需要更新的标志wFlashData((uint8_t *)&romUpdate.update_param, sizeof(updateParamSave_t), UPDATE_PARAM_SAVE_ADDR_BASE);GIZWITS_LOG("System Will Restart... \n");/****************************MCU RESTART****************************/mcuRestart();/********************************************************************///last package , updata ok//MD5 checkout :Failed clear updata,Success , write flash ,begin updata}}return 0;
}

app部分的OTA程序整个逻辑差不多就是这样了,总结一下就是:

首先协商收发数据的具体细节,然后发送方按分片发送,接收方按分片接收,接收完成后校验MD5值。如果校验成功,就将固件更新标志置位,同时将固件大小和MD5值存入Flag区。MCU重启后,会运行boot程序。

机智云OTA过程MCU端程序设计学习(二)相关推荐

  1. 机智云OTA过程MCU端程序设计学习(一)

    1.写在前面: 一般利用云端更新MCU端固件的机制是:云端通过一个能联网的模块(例如wifi,4G等)将固件信息传输到MCU端.整个过程也就是 云端(TCP)-->模块(串口)-->MCU ...

  2. 机智云OTA实践教程

    机智云的官方OTA帮助文档说得很不清楚,如果你是第一次尝试OTA看完文档你绝对晕了,这里SimonLiu梳理一下ESP8266 SOC模式的机智云OTA流程,也顺便当做记录,下次查找方便. 首先以下图 ...

  3. 机智云代码移植_【机智云Gokit3测评】设备接入-步骤二:程序移植

    [机智云Gokit3测评]设备接入-步骤二:程序移植 [复制链接] 1.写在前面 2.下载软件包 进入机智云官网的开发者中心后,点击"下载中心"(https://download. ...

  4. JavaScript高级程序设计学习(二)之基本概念

    任何语言的核心都必然会描述这门语言基本的工作原理.而描述的内容通常都要涉及这门语 言的语法.操作符.数据类型.内置功能等用于构建复杂解决方案的基本概念.如前所述, ECMA-262通过叫做 ECMAS ...

  5. 《近匠》专访机智云 CTO 刘琰—从 0到1 开启智能化硬件开发

    在物联网浪潮之下,智能硬件的火爆程度不断升温.未来十年,全球接入互联网的硬件设备将达到1万亿台.如今的智能硬件产品正成为下一个"台风口",同时这对于终端市场也是一个机遇.然而从创新 ...

  6. 【入门必看】机智云产品、功能、服务一览表

    前言 简单来说,传统电子设备接入云平台需要开发三个方面的能力: 1.是完成设备与云端的通信能力: 机智云提供了多种基于wifi(GPRS.BLE)模块的通信解决方案,企业开发者只需要在wifi模块上烧 ...

  7. leach协议c++代码_入门教程4:教你STM32F407标准库移植机智云代码(控制LED灯)

    我们知道,使用机智云自助开发平台会根据产品定义的数据点生成对应产品的设备端代码即MCU代码包.自动生成的代码实现了机智云通信协议的解析与封包.传感器数据与通信数据的转换逻辑,并封装成了简单的API,且 ...

  8. 32要烧写3个bin文件_入门教程3:如何给ESP8266烧录Gagent固件,快速接入机智云实现透传功能...

    上两文说到在机智云上创建自己的第一个设备和使用MCU代码自动生成器生成MCU代码包和修改的注意事项后,今天我们来谈谈如何快速从零开始接入机智云,实现简单的透传功能,以及常见的配网失败问题排查,这里主要 ...

  9. 机智云代码移植_IoT开发者 | 基于STM32F103的机智云宠物屋外加4路继电器开源教程...

    [ 写在前面 ] 自智云社区开辟IoT开源项目专区以来,一直有IoT开发者在贡献案例.玛莉甄选了一些具有代表性的案例分享给IoT爱好者们,本文亦如此. 若你有好的案例,想和IoT爱好者们分享,欢迎投稿 ...

最新文章

  1. 在视图控制器之间传递数据
  2. linux配置usb主从_杂集:浅谈关于Mongodb数据库主从复制
  3. scala基础之控制结构
  4. git add后取消_满满干货!——Git知识总结
  5. PHP随机生成中国人姓名的类
  6. 监听input框值得改变
  7. 解决 chrome 访问 https 网站出现“您的连接不是私密的问题”
  8. 2022「第二届中国RPA+AI开发者大赛」正式收官
  9. 数据库:sql 递归
  10. 运放选型、参数分析以及应用
  11. EZo UIBuilder中嵌入微博分享按钮【实例】
  12. Toggle Buttons(四)
  13. dart pub私有仓库搭建、私有仓库上传方法、上传工具定制
  14. 求伯君:从未停止写程序的程序员
  15. 微信视频使用的是什么协议?—— udp协议的介绍
  16. 触目惊心!2017年化工行业事故203起死亡238人!附重大事件回顾
  17. 驱动开发人员不足?经验不够?一招教你立即摆平!
  18. 大学物理实验报告 - 半导体热敏电阻温度特性的研究
  19. 大学计算机三级网络技术,考前数天如何突破性通过计算机等级考试之三级网络技术篇...
  20. python卸载不干净_mysql卸载不干净解决方法

热门文章

  1. Geek爱旅行 - 向日葵的花语 呼呼呼~~
  2. 行到水穷处,坐看云起时
  3. 浅谈金达莱花在朝鲜族家居装饰中的传承发展
  4. android旋转木马轮播图,vue实现旋转木马轮播
  5. GO Frame框架搭建一个web网站(一)
  6. 25页国有企业数字化转型ppt,解决4个典型痛点场景,实现提质增效
  7. c语言单片机计时程序,C51单片机秒表计时(C语言)
  8. 百度快照的SEO意义,别再跟风了
  9. while True learn()全金牌通关秘籍
  10. weka矿产分布文件_矿产资源国情调查项目组参加技术培训视频会议