模板网站可以优化吗,烟台网站建设学校,sem竞价代运营公司,中国建设银行三峡分行网站本章解读Z-STACK中关于Nv操作的源码#xff0c;以及z-stack中Nv的使用#xff01; 在Z-STACK中Nv存储器主要用于保存网络的配置参数#xff0c;如网络地址#xff0c;使 系统在掉电重启仍然能读取一些参数#xff0c;自动加入到原来的网络中#xff0c;这样其网络地址没有… 本章解读Z-STACK中关于Nv操作的源码以及z-stack中Nv的使用 在Z-STACK中Nv存储器主要用于保存网络的配置参数如网络地址使 系统在掉电重启仍然能读取一些参数自动加入到原来的网络中这样其网络地址没有变化 在z-stack中每一个参数的配置对应的是一个Nv条目(item),每一个item都有自己的IDz-stack中使用的条目ID范围如下 0x0000 保留 0x0001~0x0020 操作系统抽象层OSAL 0x0021~0x0040 网络层NWK 0x0041~0x0060 应用程序支持子层APS 0x0061~0x0080 安全Security 0x0081~0x00A0 Zigbee设备对象ZDO 0x00A1~0x0200 保留 0x0201~0x0FFF 应用程序 0x1000~0xFFFF 保留 如果是我们自己的应用程序中需要使用Nv则定义其ID在0x0201~0x0FFF 范围内 Z-STACK真正提供给用户使用的是五个函数(在OSAL_Nv.h中声明) 1 void osal_nv_init( void *p ); 2 uint8 osal_nv_item_init( uint16 id, uint16 len, void *buf ); 3 uint8 osal_nv_read( uint16 id, uint16 offset, uint16 len, void *buf ); 4 uint8 osal_nv_write( uint16 id, uint16 offset, uint16 len, void *buf ); 5 uint16 osal_nv_item_len( uint16 id ); 第1个函数在系统初始化的时候被调用我们在应用程序中不用管 第2个函数是我们在使用Nv时初始化某个条目如osal_nv_item_init(TEST_NV,1,NULL); 第3个函数是Nv读取某一个条目的数据将其存储在buf中 第4个函数创建一个Nv条目如果条目的ID不存在如果存在就将原来的item数据部分覆盖并向其中写入数据 第5个函数是查询某一个item的数据长度。 真正我们使用的是第2~4个函数。使用如下 unsigned char value_read; unsigned char value 0x18; osal_nv_item_init(TEST_NV,1,NULL);//NULL表示初始化的时候item数据部分为空 osal_nv_item_write(TEST_NV,0,1,value); osal_nv_item_read(TEST_NV,0,1,value_read); value_read的值便是0x18记住在write之前必须要初始化item即调用osal_nv_item_init函数 下面我们打开OSAL_Nv.c源文件通过分析源代码就知道Z-STACK是如何抽象的封装出以上几个API这对我们以后写程序还是很有帮助的 在解读源码之前必须要知道存储Nv条目的6个page如何存储Nv的即其item在page中的结构和布局 首先每一个page都有一个osalNvPgHdr_t结构体的头 typedef struct { uint16 active; uint16 inUse; uint16 xfer; uint16 spare; } osalNvPgHdr_t; 其中的几个成员稍后在做解释 在这8个字节的page头部之后才是item的存储位置。而每一个item都有一个8字节的头部 typedef struct { uint16 id; uint16 len; // Enforce Flash-WORD size on len. uint16 chk; // Byte-wise checksum of the len data bytes of the item. uint16 stat; // Item status. } osalNvHdr_t; 从后面注释就知道了每一个成员变量的含义 然后我们还必须得知道几个全局变量和数组的含义OSAL_NV_PAGES_USED值为6即6个page uint16 pgOff[OSAL_NV_PAGES_USED]; Offset into the page of the first available erased space. 每一个page的可用数据的偏移量 uint16 pgLost[OSAL_NV_PAGES_USED]; Count of the bytes lost for the zeroed-out items. 为0数据的item的字节 uint8 pgRes; Page reserved for item compacting transfer. item 压缩传输的 保留page uint8 findPg; Saving ~100 code bytes to move a uint8* parameter/return value from findItem() to a global. 用一个全局变量能节省100字节的空间指示某一个item对应的page uint8 failF; 这个变量最用最后再解释! 在系统初始的时候调用osal_nv_init函数它有调用initNV()函数这个函数的作用就是初始化NV flash page那在初始化中都做了什么呢 for ( pg OSAL_NV_PAGE_BEG; pg OSAL_NV_PAGE_END; pg ) { HalFlashRead(pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8 *)(pgHdr), OSAL_NV_HDR_SIZE); if ( pgHdr.active OSAL_NV_ERASED_ID ) { if ( pgRes OSAL_NV_PAGE_NULL ) { pgRes pg; } else { setPageUse( pg, TRUE ); } } else // Page is active. { // If the page is not yet in use, it is the tgt of items from an xfer. if ( pgHdr.inUse OSAL_NV_ERASED_ID ) { newPg pg; } // An Xfer from this page was in progress. else if ( pgHdr.xfer ! OSAL_NV_ERASED_ID ) { oldPg pg; } } // Calculate page offset and lost bytes - any old item triggers an N^2 re-scan from start. if ( initPage( pg, OSAL_NV_ITEM_NULL, findDups ) ! OSAL_NV_ITEM_NULL ) { findDups TRUE; pg OSAL_NV_PAGE_BEG-1; continue; } } 先看看这个for循环循环每一个page然后读取其page头部存储在pgHdr中①如果其active成员为 OSAL_NV_ERASED_ID0xFFFF表示此page还没有被激活想想我们的flash中没写的数据每一位为1一字节就为0xFFactive占2个字节。如果此页没有激活且此时pgRes为OSAL_NV_PAGE_NULL0,则我们不激活此page而是将此页作为后面压缩的保留页如果pgRes不为0即已经有了保留页则将此page激活且使此页投入以后使用中调用setPageUse( pg, TRUE );我们看看这个函数 osalNvPgHdr_t pgHdr; pgHdr.active OSAL_NV_ZEROED_ID; if ( inUse ) { pgHdr.inUse OSAL_NV_ZEROED_ID; } else { pgHdr.inUse OSAL_NV_ERASED_ID; } writeWord( pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8*)(pgHdr) ); 调用此函数激活page即使active为OSAL_NV_ZEROED_ID为0x0000如果inUse为TRUE则置其inUse为OSAL_NV_ZEROED_ID0x0000表示此页投入使用中否则置为OSAL_NV_ERASED_ID0xFFFF表示弃用该页最后调用writeWord将pgHdr头写进page的头部位置 ①与上面的①对应表示if和else 如果该page 的active为OSAL_NV_ZEROED_ID0x0000此page 为激活状态此时检查此page是否投入使用中如果其inUse为OSAL_NV_ERASED_ID0xFFFF即没有投入到使用中那么If the page is not yet in use, it is the tgt of items from an xfer.//将其作为后面压缩传输的目标即使newPg pg; 如果此页的xfer不为OSAL_NV_ERASED_ID0xFFFF表明其处于Xfer的过程中有时候机器意外断电而此时刚好有page在Xfer过程那么page的xfer位就为非0xFFFF即0x0000。这个时候 我们使 oldPg pg; 然后调用了initPage( pg, OSAL_NV_ITEM_NULL, findDups )这个函数有什么用呢我们先看其代码 static uint16 initPage( uint8 pg, uint16 id, uint8 findDups ) { uint16 offset OSAL_NV_PAGE_HDR_SIZE; uint16 sz, lost 0; osalNvHdr_t hdr; do { HalFlashRead(pg, offset, (uint8 *)(hdr), OSAL_NV_HDR_SIZE); if ( hdr.id OSAL_NV_ERASED_ID ) { break; } offset OSAL_NV_HDR_SIZE; sz OSAL_NV_DATA_SIZE( hdr.len ); if ( (offset sz) OSAL_NV_PAGE_FREE ) { lost (OSAL_NV_PAGE_FREE - offset OSAL_NV_HDR_SIZE); offset OSAL_NV_PAGE_FREE; break; } if ( hdr.id ! OSAL_NV_ZEROED_ID ) { if ( id ! OSAL_NV_ITEM_NULL ) { if ( (id 0x7fff) hdr.id ) { if ( (((id OSAL_NV_SOURCE_ID) 0) (hdr.stat OSAL_NV_ERASED_ID)) || (((id OSAL_NV_SOURCE_ID) ! 0) (hdr.stat ! OSAL_NV_ERASED_ID)) ) { return offset; } } } else { if ( hdr.chk calcChkF( pg, offset, hdr.len ) ) { if ( findDups ) { if ( hdr.stat OSAL_NV_ERASED_ID ) { uint16 off findItem( (hdr.id | OSAL_NV_SOURCE_ID) ); if ( off ! OSAL_NV_ITEM_NULL ) { setItem( findPg, off, eNvZero ); // Mark old duplicate as invalid. } } } else if ( hdr.stat ! OSAL_NV_ERASED_ID ) { return OSAL_NV_ERASED_ID; } } else { setItem( pg, offset, eNvZero ); // Mark bad checksum as invalid. lost (OSAL_NV_HDR_SIZE sz); } } } else { lost (OSAL_NV_HDR_SIZE sz); } offset sz; } while ( TRUE ); pgOff[pg - OSAL_NV_PAGE_BEG] offset; pgLost[pg - OSAL_NV_PAGE_BEG] lost; return OSAL_NV_ITEM_NULL; } 代码有点长其实这个函数的最用通过注释就知道了Walk the page items; calculate checksums, lost bytes page offset. 对于某个page逐个item地计算其checksumslost bytes然后计算page offset再看下其返回值 If id is non-NULL and good checksums are found, return the offset of the data corresponding to item Id; else OSAL_NV_ITEM_NULL. 如果id值不为0且校验和正确就返回和此item的数据的偏移量否则返回OSAL_NV_ITEM_NULL0 那么在initNV的for循环中 if ( initPage( pg, OSAL_NV_ITEM_NULL, findDups ) ! OSAL_NV_ITEM_NULL ) { findDups TRUE; pg OSAL_NV_PAGE_BEG-1; continue; } 这个if语句干什么的呢知道了initPage的返回值不难理解其用途如果if为真即initPage返回的值为OSAL_NV_ERASED_ID0xFFFF initPage执行到下面一句 else if ( hdr.stat ! OSAL_NV_ERASED_ID ) { return OSAL_NV_ERASED_ID; } 此时Any old item immediately exits and triggers the N^2 exhaustive initialization.为什么呢因为如果是id为0那么该处的hdr.stat值应该为0xFFFF如果某种意外情况导致其不为0xFFFF则说明出了问题得重新去初始化所有的item即检查他们的头部 回归到上面如果initPage返回值为OSAL_NV_ERASED_ID0xFFFF则 findDups TRUE; pg OSAL_NV_PAGE_BEG-1; continue; 置findDups为TRUE那么在下次调用initPage的时候就会去初始化所有item然后pg OSAL_NV_PAGE_BEG-1 for循环从开头执行 这就是for循环中的代码重要的是记住newPg 和oldPg 接下来 if ( newPg ! OSAL_NV_PAGE_NULL ) { if ( pgRes ! OSAL_NV_PAGE_NULL ) { setPageUse( newPg, TRUE ); } else if ( oldPg ! OSAL_NV_PAGE_NULL ) { pgRes newPg; } if ( oldPg ! OSAL_NV_PAGE_NULL ) { compactPage( oldPg ); } } newPage保存的是inUse为OSAL_NV_ERASED_ID0xFFFF即还没有投入使用中的页如果有这样的page我们再进行下一步判断pgRes如果其值不为OSAL_NV_PAGE_NULL即保留了某一个page为compact xfer page。 这个时候调用setPageUse( newPg, TRUE );即使其inUse为OSAL_NV_ZEROED_ID0x0000此页将投入使用中。如果pgReg为OSAL_NV_PAGE_NULL(此时所有的page均激活了)且某一页其xfer为OSAL_NV_ZEROED_ID其保存在oldPg中此时们将newPg 赋值给pgRes即将newPg作为compact的保留page此时newPg没有投入使用中接下来如果oldPg中保存了xfer被打断了的page则调用compactPage( oldPg )将其进行压缩 有这段注释 /* If a page compaction was interrupted and the page being compacted is not * yet erased, then there may be items remaining to xfer before erasing. */ 看下这个函数代码 static void compactPage( uint8 srcPg ) { uint16 dstOff pgOff[pgRes-OSAL_NV_PAGE_BEG]; uint16 srcOff OSAL_NV_ZEROED_ID; osalNvHdr_t hdr; writeWordH( srcPg, OSAL_NV_PG_XFER, (uint8*)(srcOff) ); srcOff OSAL_NV_PAGE_HDR_SIZE; do { uint16 sz; HalFlashRead(srcPg, srcOff, (uint8 *)(hdr), OSAL_NV_HDR_SIZE); if ( hdr.id OSAL_NV_ERASED_ID ) { break; } srcOff OSAL_NV_HDR_SIZE; if ( (srcOff hdr.len) OSAL_NV_PAGE_FREE ) { break; } sz OSAL_NV_DATA_SIZE( hdr.len ); if ( hdr.id ! OSAL_NV_ZEROED_ID ) { if ( hdr.chk calcChkF( srcPg, srcOff, hdr.len ) ) { setItem( srcPg, srcOff, eNvXfer ); writeBuf( pgRes, dstOff, OSAL_NV_HDR_SIZE, (byte *)(hdr) ); dstOff OSAL_NV_HDR_SIZE; xferBuf( srcPg, srcOff, pgRes, dstOff, sz ); dstOff sz; } setItem( srcPg, srcOff, eNvZero ); // Mark old location as invalid. } srcOff sz; } while ( TRUE ); pgOff[pgRes-OSAL_NV_PAGE_BEG] dstOff; erasePage( srcPg ); setPageUse( pgRes, TRUE ); pgRes srcPg; } 首先 Mark page as being in process of compaction. 标志该页正在压缩处理中 然后依次读取srcPg中的每一个item然后对每一个item进行处理处理过程如下 1如果item的id不为OSAL_NV_ZEROED_ID0x0000如果id为0x0000则直接跳到步骤4 对其进行和校验如果正确的话转下一步如果不正确转到步骤3 2调用setItem( srcPg, srcOff, eNvXfer );设置item 的状态位为激活状态即使其stat位为OSAL_NV_ACTIVE0x00然后调用writeBuf( pgRes, dstOff, OSAL_NV_HDR_SIZE, (byte *)(hdr) );将该item头部八字节写进pgRes页的dstOff处此页为保留页记住此时我们已经从前面的步骤中划分出了一个page为pgRes。最后调用xferBuf( srcPg, srcOff, pgRes, dstOff, sz );将该item的数据部分从srcPg中转移到pgRes中其中sz为item的数据长度。转下一步 3调用setItem( srcPg, srcOff, eNvZero );标记srcPg中这些被转移的item为invalid即将他们的id全部置0函数中最后调整了pgLost数组中该page的lost bytes即为该item的数据长度 4调整srcOff, srcOff sz;即指向下一个srcPg的item。 经过上述步骤就处理完了srcPg中的所有item将他们都转移到pgRes中其实就是压缩的是其中那些id为0x0000的item。 pgOff[pgRes-OSAL_NV_PAGE_BEG] dstOff调整pgRes的pgOff erasePage( srcPg );擦出被compact的page setPageUse( pgRes, TRUE ); // Mark the reserve page as being in use. pgRes srcPg; // Set the reserve page to be the newly erased page. 这样compactPage就完成了还记得它前后完成的工作吧 继续回到initNV函数最后一个if语句 if ( pgRes OSAL_NV_PAGE_NULL ) { for ( pg OSAL_NV_PAGE_BEG; pg OSAL_NV_PAGE_END; pg ) { erasePage( pg ); } initNV(); } /* If no page met the criteria to be the reserve page: * - A compactPage() failed or board reset before doing so. * - Perhaps the user changed which Flash pages are dedicated to NV and downloaded the code * without erasing Flash? */ 如果没有一个page满足“标准”称为the reserve page 那么将所有Nv page擦出掉然后重新初始化NV。 至此initNV()函数完成