您的位置:首页 > 其它

ZigBee学习笔记_osal_nv_init()

2013-02-18 17:39 337 查看
新年过完了,又可以继续学习了,进度好慢那感觉,今天看哈osal_nv_init()函数,代码很简单如下,

void osal_nv_init( void *p )
{
(void)p;  // Suppress Lint warning.
(void)initNV();  // Always returns TRUE after pages have been erased.
}


明明没有用到参数,还给传递进来一个参数,不晓得在搞什么,其中重点函数式initNVIDIA()函数,

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结构变量,定义如下

typedef struct
{
uint16 active;
uint16 inUse;
uint16 xfer;
uint16 spare;
} osalNvPgHdr_t;


用于指示存储器页面的一些信息

#define OSAL_NV_PAGE_NULL       0


oldPg以及newPg均初始化为0,

#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()函数,其中参数定义如下

#define OSAL_NV_PAGE_HDR_OFFSET 0
#define OSAL_NV_HDR_SIZE  8


osalNvPgHdr_t结构体占据了8个字节的大小,

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哪个区映射到该地址空间,用到的宏定义如下

#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中,

#define OSAL_NV_ERASED_ID       0xFFFF


接下来判断active的值是否是0xFFFF,如果是继而判断pgRes是否等于OSAL_NV_PAGE_NULL,在第一次进来时pgRes是等于OSAL_NV_PAGE_NULL的,将pgRes赋值为pg,当第二次进入循环中则会进入setPageUse()函数如下

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状态,

#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的值,

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个字节,这样这个地址的计算就可以容易明白了。

#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()函数,是浏览页面项,计算校验和以及丢失的字节和页面偏移量,

#define OSAL_NV_ITEM_NULL       0


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;
}


挺长的一大坨,

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,这个函数有很多逻辑搞不清楚,希望有人帮忙讲解一下总体逻辑,这样看着毫无思绪可言那,看了跟没看差不多,返回到上一级函数中。

剩下一些新页和旧页的一些操作,搞的这么麻烦,看注释说首先旧页被擦除,然后新页投入使用,如果有个一个传输在进行中,那么新页将总是不投入使用,传输结束才可以。当有一个空闲的页面预留时,将新页投入使用,后面这些个都有些看不懂了,看了注释也不甚了解,日后再说吧。功力太低了。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: