微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 无线和射频 > 射频无线通信设计 > ZigBee学习笔记_osal_nv_init()

ZigBee学习笔记_osal_nv_init()

时间:10-02 整理:3721RD 点击:

新年过完了,又可以继续学习了,进度好慢那感觉,今天看哈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,这个函数有很多逻辑搞不清楚,希望有人帮忙讲解一下总体逻辑,这样看着毫无思绪可言那,看了跟没看差不多,返回到上一级函数中。

剩下一些新页和旧页的一些操作,搞的这么麻烦,看注释说首先旧页被擦除,然后新页投入使用,如果有个一个传输在进行中,那么新页将总是不投入使用,传输结束才可以。当有一个空闲的页面预留时,将新页投入使用,后面这些个都有些看不懂了,看了注释也不甚了解,日后再说吧。功力太低了。。。

新年?

哦,这个是我刚才查资料时,感觉不错,转载的,我标注了。

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top