ZigBee学习笔记_osal_nv_init()
新年过完了,又可以继续学习了,进度好慢那感觉,今天看哈osal_nv_init()函数,代码很简单如下,
[cpp] view plaincopy
- void osal_nv_init( void *p )
- {
- (void)p; // Suppress Lint warning.
- (void)initNV(); // Always returns TRUE after pages have been erased.
- }
明明没有用到参数,还给传递进来一个参数,不晓得在搞什么,其中重点函数式initNVIDIA()函数,
[cpp] view plaincopy
- static uint8 initNV( void )
- {
- osalNvPgHdr_t pgHdr;
- uint8 oldPg = OSAL_NV_PAGE_NULL;
- uint8 newPg = OSAL_NV_PAGE_NULL;
- uint8 findDups = FALSE;
- uint8 pg;
- pgRes = OSAL_NV_PAGE_NULL;
- 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 ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
- /* First the old page is erased, and then the new page is put into use.
- * So if a transfer was in progress, the new page will always not yet be
- * marked as in use, since that is the last step to ending a transfer.
- */
- if ( newPg != OSAL_NV_PAGE_NULL )
- {
- /* If there is already a fallow page reserved, keep it and put the newPg in use.
- * An unfinished compaction will finish to the new reserve page and the old page
- * will be erased and reserved.
- */
- if ( pgRes != OSAL_NV_PAGE_NULL )
- {
- setPageUse( newPg, TRUE );
- }
- /* If setting old page to 'xfer' failed or board reset before it was effected, there is no way
- * to know which page was the 'old page' - so just reset all NV pages to start clean.
- */
- else if ( oldPg != OSAL_NV_PAGE_NULL )
- {
- pgRes = newPg;
- }
- /* 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.
- */
- if ( oldPg != OSAL_NV_PAGE_NULL )
- {
- compactPage( oldPg );
- }
- }
- /* 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?
- */
- if ( pgRes == OSAL_NV_PAGE_NULL )
- {
- for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
- {
- erasePage( pg );
- }
- initNV();
- }
- return TRUE;
- }
看注释是擦除非易失性存储器页,总是返回TRUE,首先用到osalNvPgHdr_t结构变量,定义如下
[cpp] view plaincopy
- typedef struct
- {
- uint16 active;
- uint16 inUse;
- uint16 xfer;
- uint16 spare;
- } osalNvPgHdr_t;
用于指示存储器页面的一些信息
[cpp] view plaincopy
- #define OSAL_NV_PAGE_NULL 0
oldPg以及newPg均初始化为0,
[cpp] view plaincopy
- #define OSAL_NV_PAGES_USED HAL_NV_PAGE_CNT
- #define OSAL_NV_PAGE_BEG HAL_NV_PAGE_BEG
- #define OSAL_NV_PAGE_END (OSAL_NV_PAGE_BEG + OSAL_NV_PAGES_USED - 1)
- #define HAL_NV_PAGE_CNT 6
- #define HAL_NV_PAGE_BEG (HAL_NV_PAGE_END-HAL_NV_PAGE_CNT+1)
- #define HAL_FLASH_LOCK_BITS 16
- #define HAL_NV_PAGE_END 126
这个宏定义看着挺绕的,结果是OSAL_NV_PAGE_BEG为121,OSAL_NV_PAGE_END为126,在CC2530中有128个页,最后一个页的最后16个字节是用来flash lock bits,这里用的的是121-126不晓得为啥,再循环中调用了HalFlashRead()函数,其中参数定义如下
[cpp] view plaincopy
- #define OSAL_NV_PAGE_HDR_OFFSET 0
- #define OSAL_NV_HDR_SIZE 8
osalNvPgHdr_t结构体占据了8个字节的大小,
[cpp] view plaincopy
- void HalFlashRead(uint8 pg, uint16 offset, uint8 *buf, uint16 cnt)
- {
- // Calculate the offset into the containing flash bank as it gets mapped into XDATA.
- uint8 *ptr = (uint8 *)(offset + HAL_FLASH_PAGE_MAP) +
- ((pg % HAL_FLASH_PAGE_PER_BANK) * HAL_FLASH_PAGE_SIZE);
- uint8 memctr = MEMCTR; // Save to restore.
- #if !defined HAL_OAD_BOOT_CODE
- halIntState_t is;
- #endif
- pg /= HAL_FLASH_PAGE_PER_BANK; // Calculate the flash bank from the flash page.
- #if !defined HAL_OAD_BOOT_CODE
- HAL_ENTER_CRITICAL_SECTION(is);
- #endif
- // Calculate and map the containing flash bank into XDATA.
- MEMCTR = (MEMCTR & 0xF8) | pg;
- while (cnt--)
- {
- *buf++ = *ptr++;
- }
- MEMCTR = memctr;
- #if !defined HAL_OAD_BOOT_CODE
- HAL_EXIT_CRITICAL_SECTION(is);
- #endif
- }
该函数是从flash中读取cnt个字节,访问flash数据用的是XDATA地址空间,flash映射到XDATA的高32KB地址空间中,因此,需要计算是flash哪个区映射到该地址空间,用到的宏定义如下
[cpp] view plaincopy
- #define HAL_FLASH_PAGE_MAP 0x8000
- #define HAL_FLASH_PAGE_PER_BANK 16
- #define HAL_FLASH_PAGE_SIZE 2048
通过计算最终得到地址,并保存MEMCTR的值,将pg赋值为所映射额区号,保存EA的值关闭中断,为MEMCTR赋新值,就可以开始字节拷贝了,cnt个字节拷贝完成之后恢复MEMCTR的值,重新开中断恢复EA的值,该函数就完成了。
返回到initNV()函数中,读取的8个字节的数据存放在了pgHdr中,
[cpp] view plaincopy
- #define OSAL_NV_ERASED_ID 0xFFFF
接下来判断active的值是否是0xFFFF,如果是继而判断pgRes是否等于OSAL_NV_PAGE_NULL,在第一次进来时pgRes是等于OSAL_NV_PAGE_NULL的,将pgRes赋值为pg,当第二次进入循环中则会进入setPageUse()函数如下
[cpp] view plaincopy
- static void setPageUse( uint8 pg, uint8 inUse )
- {
- 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 header的active/inUse状态,
[cpp] view plaincopy
- #define OSAL_NV_ZEROED_ID 0x0000
设置active为OSAL_NV_ZEROED_ID,然后判断inUse参数,如果为TRUE则设置inUse为OSAL_NV_ZEROED_ID,为FALSE设置inUse为OSAL_NV_ZERASED_ID,最后调用writeWord()函数保存pgHdr的值,
[cpp] view plaincopy
- static void writeWord( uint8 pg, uint16 offset, uint8 *buf )
- {
- offset = (offset >> 2) + ((uint16)pg << 9);
- if ( OSAL_NV_CHECK_BUS_VOLTAGE )
- {
- HalFlashWrite(offset, buf, 1);
- }
- else
- {
- failF = TRUE;
- }
- }
这个地址的计算可以从手册中看出
手册中说道32位字是可以写入的闪存的最小可写单元,闪存页面是存储器内科擦除的最小单元,因此对flash的写是写4个字节,这样这个地址的计算就可以容易明白了。
[cpp] view plaincopy
- #define OSAL_NV_CHECK_BUS_VOLTAGE (HalAdcCheckVdd( HAL_ADC_VDD_LIMIT_4 ))
在if中去检测了供电电压,HalFlashWrite()函数式利用DMA操作将数据写入flash中,不多说了就。
回到initNV()函数中,当所读取的active不是OSAL_NV_ERASED_ID时,(如果清楚osalNvPgHdr_t结构体的各个变量的意义就能清晰明白这里的逻辑了)即页面没有被擦除,判断inUse是否为OSAL_NV_ERASED_ID,是则将newPg赋值为pg,xfer貌似是指明是否有传输在进行,当有传输在发生时将oldPg赋值为pg。之后有个initPage()函数,是浏览页面项,计算校验和以及丢失的字节和页面偏移量,
[cpp] view plaincopy
- #define OSAL_NV_ITEM_NULL 0
[cpp] view plaincopy
- 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 );
- // A bad 'len' write has blown away the rest of the page.
- 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 )
- {
- /* This trick allows function to do double duty for findItem() without
- * compromising its essential functionality at powerup initialization.
- */
- if ( id != OSAL_NV_ITEM_NULL )
- {
- /* This trick allows asking to find the old/transferred item in case
- * of a successful new item write that gets interrupted before the
- * old item can be zeroed out.
- */
- 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;
- }
- }
- }
- // When invoked from the osal_nv_init(), verify checksums and find & zero any duplicates.
- else
- {
- if ( hdr.chk == calcChkF( pg, offset, hdr.len ) )
- {
- if ( findDups )
- {
- if ( hdr.stat == OSAL_NV_ERASED_ID )
- {
- /* The trick of setting the MSB of the item Id causes the logic
- * immediately above to return a valid page only if the header 'stat'
- * indicates that it was the older item being transferred.
- */
- uint16 off = findItem( (hdr.id | OSAL_NV_SOURCE_ID) );
- if ( off != OSAL_NV_ITEM_NULL )
- {
- setItem( findPg, off, eNvZero ); // Mark old duplicate as invalid.
- }
- }
- }
- // Any "old" item immediately exits and triggers the N^2 exhaustive initialization.
- 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;
- }
挺长的一大坨,
[cpp] view plaincopy
- 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;
- #define OSAL_NV_HDR_SIZE 8
- #define OSAL_NV_PAGE_FREE HAL_FLASH_PAGE_SIZE
- #define HAL_FLASH_PAGE_SIZE 2048
- #define OSAL_NV_SOURCE_ID 0x8000
- #define OSAL_NV_DATA_SIZE( LEN ) \
- ((((LEN) + OSAL_NV_WORD_SIZE - 1) / OSAL_NV_WORD_SIZE) * OSAL_NV_WORD_SIZE)
- #define OSAL_NV_WORD_SIZE HAL_FLASH_WORD_SIZE
- #define HAL_FLASH_WORD_SIZE 4
进来之后先是读取8个字节数据到hdr结构变量中,当读取的id变量为OSAL_NV_ERASED_ID时跳出循环,偏移量增加OSAL_NV_HDR_SIZE大小,OSAL_NV_DATA_SIZE()宏定义感觉应该是为了实现4个字节对齐才这样写的,最后得出的sz和len的关系应该是(len+4)> sz >= len,当offset+sz的和大于页面的最大字节即2048时,超出本页面了就,len的值就是错的,lost的值更新,offset赋值为OSAL_NV_PAGE_FREE,退出循环。当offset+sz的和是合理的值时,检测id属性,当hdr.id不等于OSAL_NV_ZEROED_ID且传递进来的id不等于OSAL_NV_ITEM_NULL时,这次调用传递进来的id是OSAL_NV_ITEM_NULL,不符合条件,就不看了先,跳过去看else里面的内容,上面的注释也说了,当该函数是被osal_nv_init()函数调用时,是在这里处理的,调用了calcChkF()函数计算校验和,就不多介绍了,当计算的校验和与hdr.chk相等时,进而判断findDups,参数传递进来的是FALSE,当stat不等于OSAL_NV_ERASED_ID时返回OSAL_NV_ERASED_ID,当校验和不相等时,标记校验和无效,最后一个pgOff和pgLost数组记录了offset和lost变量,返回OSAL_NV_ITEM_NULL,这个函数有很多逻辑搞不清楚,希望有人帮忙讲解一下总体逻辑,这样看着毫无思绪可言那,看了跟没看差不多,返回到上一级函数中。
剩下一些新页和旧页的一些操作,搞的这么麻烦,看注释说首先旧页被擦除,然后新页投入使用,如果有个一个传输在进行中,那么新页将总是不投入使用,传输结束才可以。当有一个空闲的页面预留时,将新页投入使用,后面这些个都有些看不懂了,看了注释也不甚了解,日后再说吧。功力太低了。。。
新年?
哦,这个是我刚才查资料时,感觉不错,转载的,我标注了。