关于CC1310的空中升级,让我折腾了特别久,我几乎绝望了。仅有的两个参考资料都遇到了困难:
1.CC2640 OAD,其中的BIM部分,TI只提供了IAR的版本,这导致关键性的存储映射无从参考,缺少了最关键的资料。
2.CC1350 BLE OAD Project 0,只有一个开发文档。
但是,身负物联网世界振兴重任的我又怎么能说不行呢!IoT小能手的字典中从来就没有‘不行’这两个字,因为我连字典都没有。
你看,山重水复疑无路,柳暗花明又一村。TI给了我一个CC1310的内部例程WSN OAD Example。别问我怎么得到的。
本文作者twowinter,转载请注明作者:http://blog.csdn.net/iotisan/
这个工程包含了 rfWsnConcentratorOadServer_CC1350_LAUNCHXL_TI_CC1350F128 和 rfWsnNodeOadClient_CC1310_LAUNCHXL_TI_CC1310F128 projects,以及用来将hex转化成OAD bin,下载镜像的脚本。
例程中,集中器存储节点的OAD镜像,通过OAD方式发送给子节点,子节点将镜像存储到外部flash中,子节点BIM程序把固件拷贝到内部flash,之后运行新程序。
需要如下工具:
- CCSv7
- simplelink_cc13x0_sdk_1_00_00_13
- Python 2.7
- Python intelhex-2.1
- Python crcmod-1.7
只有rfWsnNodeOadClient_CC1310_LAUNCHXL_TI_CC1310F128需要BIM。
为了第一次使用时正常工作,集中器和子节点的外部flash需要清空。给两个LP boards烧写bin和CC1350LaunchPad_ExtFlashErase.hex。当擦写时LED会闪烁,烧写完成时LED就停止闪烁。除非外部flash擦除,LED停止闪烁。
集中器的OAD Server和子节点的固件通过SmartRF Flash Programmer来下载。
集中器将在串口显示如下信息:
*Available FW: None
Nodes FwVer SW RSSI
0xfe 0850 0 -025
Press BTN-2 to update available node FW
The "Available FW", or node selection is indicated by the '*'
子节点要用的OAD镜像可以使用oad_wrtie_bin.py 通过集中器的串口写入到外部flash。可用固件必须先按BTN-1来选中。
当”Available FW”被选中,BTN-2按下,则终端会显示:
Waiting for Node FW update...
串口terminal立即关掉,然后运行脚本将bin写入。
升级完后,绿色LED会闪烁,之后就可以重新打开串口terminal。
现在使用BTN-1来选择节点,显示屏会显示节点来运行的程序:
Available FW: rfWsnNode v02.00.00
Nodes Value SW RSSI
*0x7d 0850 0 -027
FW: rfWsnNode v01.00.00
Press BTN-2 to OAD node FW
如果新上电一个节点,过几秒会显示出它的固件版本。
当选中节点后再按下BTN-2,则会开始向那个节点下发OAD。当节点接下来发送一个传感器读取,OAD就会启动,可以按下它自身的 BTN-1 来唤醒节点(它可能进入了50s的慢速上报)。节点固件会显示总包数和当前升级的包数:
Available FW: rfWsnNode v02.00.00
Nodes Value SW RSSI
*0x7d 0850 0 -028
FW: Block 03f6/0ed8
Press BTN-2 to OAD node FW
一旦OAD完成,节点会自动重启。
集中器会显示一个新的节点,该新节点会显示更新的固件版本。
python tools/oad_image_tool.py -v 0x[version] -i remoteapp bin/rfWsnNodeOadClient_CC1310_LAUNCHXL_tirtos_ccs.hex -ob bin/rfWsnNodeOadClient_CC1310_LAUNCHXL_TI_CC1310F128_app-[version].bin -m 0x1000
空中交互大致有6条,源码最主要体现在 \source\common\wsnprotocol.h 底下
WSNProtocol_sendFwVersionReq
WSNProtocol_sendFwVersionRsp
WSNProtocol_sendImgIdentifyReq
WSNProtocol_sendOadIdentifyImgRsp
WSNProtocol_sendOadImgBlockReq
WSNProtocol_sendOadImgBlockRsp
整体OAD的交互流程梳理如下:
Concentrator — fwVersionReq –> Node
Concentrator <– fwVersionRsp — Node
Concentrator — oadImgIdentifyReq –> Node
Concentrator <– oadImgIdentifyRsp — Node
Concentrator <– oadBlockReq — Node
Concentrator — oadBlockRsp –> Node
空中交互之后就到了镜像下载部分,也是特别关键的一部分,每次下载16字节。每次在oadBlockRspCb()中收到镜像分包,然后进行写入,边下载边写入FLASH。下载完镜像之后,再做下CRC校验:
/*********************************************************************
* @fn OAD_imgBlockWrite
*
* @brief Process the Image Block Write.
*
* @param pValue - pointer to data to be written
*
* @return none
*/
void OAD_imgBlockWrite(uint8_t *pValue)
{
// N.B. This must be left volatile.
volatile uint16_t blkNum = BUILD_UINT16(pValue[0], pValue[1]);
// Check that this is the expected block number.
if (oadBlkNum == blkNum)
{
// Calculate address to write as (start of OAD range) + (offset into range)
uint32_t addr = imageAddress + (oadBlkNum * OAD_BLOCK_SIZE);
// If address starts a new page, erase that page first.
if ((addr % flashPageSize) == 0)
{
OADTarget_eraseFlash(addr / flashPageSize);
}
// Write a 16 byte block to Flash.
OADTarget_writeFlash(imagePage, (blkNum * OAD_BLOCK_SIZE), pValue+2,
OAD_BLOCK_SIZE);
// Increment received block count.
oadBlkNum++;
}
else
{
// Overflow, abort OAD
oadBlkNum = 0;
#ifndef FEATURE_OAD_ONCHIP
flagRecord = 0;
#endif
// Close the target device
OADTarget_close();
// Send status
OAD_sendStatusCb(OAD_BUFFER_OFL);
return;
}
// Check if the OAD Image is complete.
if (oadBlkNum == oadBlkTot)
{
#if FEATURE_OAD_ONCHIP
// Handle CRC verification in BIM.
OADTarget_systemReset();
#else // !FEATURE_OAD_ONCHIP
// Run CRC check on new image.
if (checkDL())
{
// Store the flag of the downloaded image.
flagRecord |= getImageFlag();
// Store the image information.
saveImageInfo();
// Indicate a successful download and CRC
OAD_sendStatusCb(OAD_SUCCESS);
}
else
{
// CRC error
OAD_sendStatusCb(OAD_CRC_ERR);
}
flagRecord = 0;
#endif //FEATURE_OAD_ONCHIP
OADTarget_close();
oadBlkNum = 0;
}
else
{
// Request the next OAD Image block.
OAD_getNextBlockReqCb(oadBlkNum);
}
}