ZigBee学习笔记_osal_nv_init()

新年过完了,又可以继续学习了,进度好慢那感觉,今天看哈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;
  }
}

这个地址的计算可以从手册中看出

ZigBee学习笔记_osal_nv_init()_第1张图片

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

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

你可能感兴趣的:(ZigBee学习笔记_osal_nv_init())