英飞凌XC886单片机Flash读写总结

最近工作上需要对英飞凌XC886这款单片机的Flash进行读写,以下为简要的几点总结:

一、Flash存储器结构:
XC886共有32KFlash,地址映射如下图所示:

英飞凌XC886单片机Flash读写总结_第1张图片
共三块P-Flash用来存储程序,两块D-Flash既可用来存储程序也可以存储数据,每块D-Flash映射到两个不同的地址,如Bank1映射到6000-6FFF和B000-BFFF,通常存储程序的时候采用低地址,存储数据的时候采用高地址。此处需注意每个Flash bank虽然映射到了两个地址,但实际在物理上是同一块Flash,不要把两组地址当做两块Flash来操作,否则会相互影响数据。

二、Flash bank的分区如下图所示:
英飞凌XC886单片机Flash读写总结_第2张图片
D-Flash分区较为灵活。因为Flash每次擦除要擦除整个扇区(Sector),所以扇区划分越细越有利于我们操作。Flash写之前都需要先擦除,擦除后的状态为全0,写的时候可以从0写为1,不能从1写为0。

三、写Flash:
写Flash的时候每次要写一个字线地址(WL),对于P-Flash来说是64个字节,D-Flash来说是32个字节,下图为手册中给出的字线地址划分:
英飞凌XC886单片机Flash读写总结_第3张图片
英飞凌XC886单片机Flash读写总结_第4张图片
芯片手册中对于Flash的写给出了如下说明:

对于 P- Flash bank,由于 Flash 单元只能承受一个门干扰,因此已编程的字线在重新编程前必须先擦除。因为不可能单独擦除一条字线,这即意味着包৿该字线的整个扇区必须全部被擦除。
对于 D- Flash bank,由于 Flash 单元能承受两个门干扰,同一条字线在擦除前可编程两次。 这即意味着如果需要编程的数据字节的个数小于 32 字节(最小编程宽度), 用户可选择先编程这些数据字节(x; x 可以是 1-31 之间的任意整数),之后再编程其余字节(32-x)。 不过,由于 D-Flash 的最小编程宽度始终为 32 个字节,因此在每个编程周期中᳾被编程的字节必须写入全 0。

没有能够很透彻地理解它的这段话,但实际试验结果是对于D-Flash来说,一个WL在擦除前可被编程多次,即只要原来为0x00的字节都可以被写入非0值,对于原来已有数据的字节,已经被写入1的数据位不能再被写入0,但如果有某一位没有被写入过1,通常情况下可以被写入1,例如0x80(10000000)可以被写为0x81(10000001),但这并不代表Flash可以按位操作,例如实测0x03(00000011)想写为0x07(00000111)时就会写入失败,原因没有深入研究。

四、Flash模拟EEPROM
因为Flash使用寿命有限,只针对Flash的一个区域进行操作会造成其快速老化,使用效率过低,所以可以通过算法来模拟EEPROM,循环使用Flash的多个Sector,延长寿命,同时也避免存储数据时相互冲突和影响。英飞凌官方提供了三种Flash模拟EEPROM的算法,例程代码和说明见
https://download.csdn.net/download/weixin_42967006/11356942
其中操作Flash的函数是用汇编写的,如下图所示,给出了在C语言中调用的接口。 英飞凌XC886单片机Flash读写总结_第5张图片
官方给出的三个算法都略为复杂,我只需存储一个标志位,所以在实际应用的时候自己简单做了个简单的小算法,能够实现标志位的存储,并且循环使用了bank1的每一个sector,把代码贴出来供大家参考。

char selectedLanguage=0; //
unsigned char code * piWLAddress;
unsigned char code * piWLAddressToCover=-1;
char idata ucSectorNumberToRead=0;
char idata ucSectorNumberToErase=-1;
unsigned char data storedLanguage[32]=0;
char code * code iSectorAddress[10]={0xB000,0xB400,0xB800,0xBA00,0xBC00,0xBD00,0xBE00,0xBE80,0xBF00,0xBF80};

//查找有效的Sector
SearchSector:
	piWLAddress=iSectorAddress[ucSectorNumberToRead];
SearchWL:
	if(*piWLAddress==0x81)//用过,失效
		{
			if(piWLAddress==0xBFE0 )//到达最后一个WL
			{
				selectedLanguage=0; 
				ucSectorNumberToErase=0;		//	ucSectorNumber无效sector,待擦除写入有效值 	
				piWLAddress=iSectorAddress[ucSectorNumberToErase];
				goto SearchEnd;
			}
			else
				{
				piWLAddress+=0x20;
				if(ucSectorNumberToRead<9 && piWLAddress==iSectorAddress[ucSectorNumberToRead+1])//判断是否到达下一个sector
					{
					ucSectorNumberToRead++;
					goto SearchSector;
					}
				else goto SearchWL;
				}
		}
		else if(*piWLAddress==0x80)//有效
		{
			selectedLanguage=*(piWLAddress+1);
			if(selectedLanguage<0||selectedLanguage>2) selectedLanguage=0;
			if(piWLAddress==0xBFE0)//已经是最后一个WL
				{
				ucSectorNumberToErase=0;
				piWLAddressToCover=piWLAddress;
				piWLAddress=iSectorAddress[ucSectorNumberToErase];
				}
			else
				{
				piWLAddressToCover=piWLAddress;
				piWLAddress+=0x20;
				if(piWLAddress==iSectorAddress[ucSectorNumberToRead+1])//是否到达下一个sector
					{
					ucSectorNumberToErase=ucSectorNumberToRead+1;//擦除下一个sector
					}
				}
		}
		else//无有效sector
		{
			//	ucSectorNumber无效sector,待擦除写入有效值 					
				selectedLanguage=0; 
				ucSectorNumberToErase=ucSectorNumberToRead;		//	ucSectorNumber无效sector,待擦除写入有效值 	
				piWLAddress=iSectorAddress[ucSectorNumberToErase];
		}
SearchEnd:
……
//写入有效数据
//其中NMISR为单片机NMI状态寄存器,当Flash动作执行完毕后第3位会置1
	if(piWLAddressToCover!=-1)//将已用WL置为已用
	{
		storedLanguage[0]=0x81;
		FLASH_PROG(piWLAddressToCover,(char data*)storedLanguage);
		while(0==(NMISR & 0x04));
		NMISR &= ~(ubyte)0x04;
	}
	
	//erase sector
	if(ucSectorNumberToErase!=-1)
		{
//								lEraseSector=0x00000001<>16),(char)(lEraseSector>>24),//D-Flash bank 0 sector 0-9
//														(char)lEraseSector,(char)(lEraseSector>>8),//D-Flash bank 1 sector 0-9
//														0,0,0);//P-Flash
		FLASH_ERASE(0,0,//D-Flash bank 0 sector 0-9
								(char)(0x0001<

以上。其实大部分内容是参考的芯片手册,这里只是将我觉得有用的地方做了记录和提炼,方便日后查询,也欢迎大家交流指正。

你可能感兴趣的:(嵌入式,英飞凌,XC886,Flash)