有一段时间没有碰Z-STACK了,有点生疏,不过没关系!我们继续Z-STACK的驱动之旅!今天着重分析一下
z-stack的flash驱动。在分析flash驱动之前,需要熟读cc2530的datasheet关于flash controller那一章节!
我们先从hal_flash.c文件中的HalFlashRead函数开始:
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
}
在讲解这个函数之前,先说一下z-stack中对flash的布局。z-stack将256KBflash的最末八个字节作为zigbee中的IEEE地址,当然在这八个字节之后还有16个字节lock bits,这些flash的lock所需要用的,每2kb(1page)的flash有1bit的lock位,那么256kb的flash有128个2kb,当然也就有128(16X8)位的lock bits了。从F8w2530.xcl文件中可以看出:
-D_IEEE_ADDRESS_SPACE_START=(((_NR_OF_BANKS+1)*_FIRST_BANK_ADDR)-0x18)
-D_IEEE_ADDRESS_SPACE_END=(_IEEE_ADDRESS_SPACE_START+7)
-Z(CODE)IEEE_ADDRESS_SPACE=_IEEE_ADDRESS_SPACE_START-_IEEE_ADDRESS_SPACE_END
注:0x18 = 24 = 16 +8;
_IEEE_ADDRESS_SPACE_START容易算出来,_NR_OF_BANKS值为0x07,_FIRST_BANK_ADDR值为0x8000,这些值是在options...里面设置的。所以_IEEE_ADDRESS_SPACE_START的值为0x40000 - 0x18 = 0x3FFE8,
_IEEE_ADDRESS_SPACE_END值就为0x3FFE8+7 = 0x3FFEF。
Z-STACK将flash的最末处的12KB(6page)用来作为Nv存储的,具体看:
-D_ZIGNV_ADDRESS_SPACE_START=(((_NR_OF_BANKS+1)*_FIRST_BANK_ADDR)-0x3800)
-D_ZIGNV_ADDRESS_SPACE_END=(_ZIGNV_ADDRESS_SPACE_START+0x2FFF)
-Z(CODE)ZIGNV_ADDRESS_SPACE=_ZIGNV_ADDRESS_SPACE_START-_ZIGNV_ADDRESS_SPACE_END
我们知道0x3800为7个page即14KB,0x2FFF为6个page,则Z-STACK将最末的7个page的前6个page用来作为Nv存储,最后一个page用来存储其他信息,如IEEE地址。
好!我们接下来看这个函数。
uint8 *ptr = (uint8 *)(offset + HAL_FLASH_PAGE_MAP) +
((pg % HAL_FLASH_PAGE_PER_BANK) * HAL_FLASH_PAGE_SIZE);
首先计算了这个地址映射到XDATA中之后的值,HAL_FLASH_PAGE_MAP为0X8000,HAL_FLASH_PAGE_PER_BANK值为16,HAL_FLASH_PAGE_SIZE值为2024即2KB,这个计算很显然了。
z-stack中的page是从0开始的,一直到127,总共128page,假如我们要读某一page中的数据,那么我们必须先把这个page所在的bank映射到XDATA的0x8000~0xFFFF中,我们读取page值为100,偏移量为20的数据时,这个地址通过上面的表达式就很容易计算出来为20+0x8000+(100%16)*2024 = 0x9FB4,注意这个地址转换成了(uint8 *),即为XDATA空间的地址。
pg /= HAL_FLASH_PAGE_PER_BANK;
计算page所在的bank,如果pg为100,则其所在bank值为100/16= 6;
MEMCTR = (MEMCTR & 0xF8) | pg;
这句是将刚才计算所得的bank映射到XDATA的0x8000~0xFFFF中去,MEMCTR的低三位为XDATA的bank选择位
while (cnt--)
{
*buf++ = *ptr++;
}
很明显,将ptr指针指向的数据复制到buf指针所指向的地址去,这个buf和ptr变量是在运行时堆栈上的,只不过是将数据从XDATA的一个地放复制到sram中某个地方!
那么这个函数的最用就很明显了:它是将flash内部的某个page,相对其偏移量为offet地址处的数据,读取cnt个字节到buf中去。
接下来看看HalFlashInit这个函数:
void HalFlashInit(void)
{
// Load the code to run from RAM into its reserved area of RAM once at startup.
HalFlashRead(PAGE_OF_RAM_CODE, OSET_OF_RAM_CODE, ramCode, SIZE_OF_RAM_CODE);
}
这个flash初始化函数直接调用了HalFlashRead函数,我们看看这几个实参数据,PAGE_OF_RAM_CODE值为51,OSET_OF_RAM_CODE值为0x6DD,SIZE_OF_RAM_CODE为0x23,ramCode为:
#pragma location="RAM_CODE_XDATA"
static __no_init uint8 ramCode[SIZE_OF_RAM_CODE];
这个ramCode是不能初始化的静态数组
这个ramCode是在RAM_CODE_XDATAsegment,打开F8w2530.xcl文件:
-D_RAM_CODE_XDATA_START=0x01EDD
-D_RAM_CODE_XDATA_END=(_RAM_CODE_XDATA_START+0x22)
-Z(XDATA)RAM_CODE_XDATA=_RAM_CODE_XDATA_START-_RAM_CODE_XDATA_END
很明显RAM_CODE_XDATA这段范围是8kb的sram中最末的23个字节,
那么这个初始化函数的作用就是将flash中23个字节的代码加载到ram去运行。那么这个有什么用途呢?而且为什么是23字节呢?看到这个注释,
Any code that will be run from RAM by setting XMAP of MEMCTL must have the same bank-relative
address as the address in RAM to which the CODE will be copied to run.
Thus, any part of the first 8k of any bank can be dedicated to code that will run from RAM as
long as the corresponding relative address range is reserved in RAM by RAM_CODE_XDATA.
这时候我们看到这几句:
#pragma location="RAM_CODE_FLASH"
#if defined HAL_OAD_BOOT_CODE
static void HalFlashWriteTrigger(void);
#else
static __monitor void HalFlashWriteTrigger(void);
#endif
__monitor表示此函数不能被中断!这个函数的位置就是在RAM_CODE_FLASH处,
-Z(CODE)RAM_CODE_FLASH=_RAM_CODE_FLASH_START-_RAM_CODE_FLASH_END
-D_RAM_CODE_FLASH_START=0x39EDD
-D_RAM_CODE_FLASH_END=(_RAM_CODE_FLASH_START+0x22)
这个表示的code段RAM_CODE_FLASH范围,为0x39EDD~0x39EFF,这个跟之前的那个51page,0x6DD是什么关系呢?我们先看这个地址值出在什么位置,51page出在第三个bank里(注意bank是从0开始起的),那么将其映射到code memory(64KB)中就是在32KB+3page+0x6DD处,记住code空间的第一个为root bank,接着我们将
39EDD映射到code空间中,其bank为7,其值为115page+0x6DD(注意page也是从0开始的),16page为1个bank,映射之后再code中的值为32KB+3page+0x6DD,看到了吗?
这两个值在映射之后竟然为同一地址,而且这一地址都是在switch bank的开始8kb末端23字节处!这说明什么呢?
而且,从上面代码看出,这23个字节存储的就是HalFlashWriteTrigger这个函数的实现代码,不信反汇编代码去看看,这个函数代码就是23个字节。
那么在系统初始化flash的时候,0x39DEE映射到code空间,flash控制器将这23个字节的函数代码复制到sram(8KB)的末端23字节的地址处,这样当系统执行这个函数时,将这8KBsram映射到code空间的0x8000~
0x8000+sram_size-1,cpu就在sram中执行这个函数,很显然,速度是超快的!那么正好这个函数要求不能被中断,那么它最好是越快越好!当然就把它放在sram中执行啦~
flash驱动先分析到这!下章介绍flash write函数!