新年过完了,又可以继续学习了,进度好慢那感觉,今天看哈osal_nv_init()函数,代码很简单如下,
void osal_nv_init( void *p ) { (void)p; // Suppress Lint warning. (void)initNV(); // Always returns TRUE after pages have been erased. }
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; }
typedef struct { uint16 active; uint16 inUse; uint16 xfer; uint16 spare; } osalNvPgHdr_t;
#define OSAL_NV_PAGE_NULL 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
#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 }
#define HAL_FLASH_PAGE_MAP 0x8000 #define HAL_FLASH_PAGE_PER_BANK 16 #define HAL_FLASH_PAGE_SIZE 2048
返回到initNV()函数中,读取的8个字节的数据存放在了pgHdr中,
#define OSAL_NV_ERASED_ID 0xFFFF
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) ); }
#define OSAL_NV_ZEROED_ID 0x0000
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 ))
回到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,这个函数有很多逻辑搞不清楚,希望有人帮忙讲解一下总体逻辑,这样看着毫无思绪可言那,看了跟没看差不多,返回到上一级函数中。
剩下一些新页和旧页的一些操作,搞的这么麻烦,看注释说首先旧页被擦除,然后新页投入使用,如果有个一个传输在进行中,那么新页将总是不投入使用,传输结束才可以。当有一个空闲的页面预留时,将新页投入使用,后面这些个都有些看不懂了,看了注释也不甚了解,日后再说吧。功力太低了。。。