/*************************************************************************************
标 题: Flash的读写操作
* 项 目 名: W-R_FLASH_TEST
* 开发环境:ChipON IDE
* 使用芯片:KF8F4156
* 作 者:上海芯旺微电子技术有限公司
* 功能简述: 如程序所示,对相应的地址进行操作,操作结果通过断电 使用PRO读取。
*
* 跳线接法:只需给单片机供电即可,为了观看数据操作是否完成,可以连接P12和J21 ,当D4灯闪烁时表示操作完成。可断电读取数据。
*
* 特殊说明: 程序会占用不确定的FLASH地址,因此你需要确定用于自写的块或确保当前使用地址为非程序区
* 当前芯片的可自写范围:800H~1FDFH,
* 实现方法在该项目目录中建立occupancy.txt的文件,添加地址占用描述使编译器跳过该段区域的实现
* 格式:
* 一行一条记录,一条记录由3部分组成标识、起始地址、结束地址。
* 文件可以包含任意多条记录
* 标识用于标记占用数据段还是代码段,可以是code或者是data。
* 起始地址结束地址使用十六进制表示。
* 占用包含起始和结束地址。
*
* 例如
* data 0x180 0x190
* code 0x800 0x810
*************************************************************************************/
#include <KF8F4156.h>
//====================////////////////////FLASH相关声明/////////////////==============//////////
/*参数传递说明见函数实现位置*/
#define FLASH_BUFFER_MAX 32 // 数据的大小与单次操作量相关,这里int型,16,32,除非不用写操作或不关心一些数据。
#define LED P1LR2
unsigned int FLASH_BUFFER[FLASH_BUFFER_MAX];
unsigned int FLASH_READ_BUF; //当单字读函数使用非全嵌汇编模式时,使用该值获取读取结果值
unsigned int FLASH_READ_ONE (unsigned int address); //读一个字
void FLASH_READ_FUN (unsigned int address,unsigned char length);//读多个数据到数据缓存区,建议最多量为1个页即32个数据,同时建议不跨页操作,与写函数对应
void FLASH_WRITE_ONE (unsigned int address,unsigned int value); // 过程封装
void FLASH_WRITE_FUN (unsigned int address,unsigned char length); //将缓存区数据写到对应位置,从第一个开始
///=========================================================================================///
void init_mcu()
{
OSCCTL = 0x60; //设置为8M
/*********端口初始化***********/
TR0 = 0x04; //设置P0端口 1100 1111
TR1 = 0x00; //设置P1端口 0000 0000
TR2 = 0x00; //设置P2端口 0011 1111
P0 = 0x00;
P1 = 0x80; //P1.6 P1.7 指示运行结束灯
P2 = 0xF0;
}
void main()
{
unsigned char i;
unsigned int temp=0;
init_mcu();
//--- 准备待写入的数据0x0000 0x1111 0x2222...0xFFFF 0x1110 0x2221 0x3332 ...0xFFFE 0x110F
for (i = 0; i < FLASH_BUFFER_MAX; i++)
{
FLASH_BUFFER[i] = temp;
temp += 0x1111;
}
//---setp 1 写入到0x1E60 和0x1E80的FLASH空间 (页的首地址)
FLASH_WRITE_FUN(0x1E60,FLASH_BUFFER_MAX);
FLASH_WRITE_FUN(0x1E80,FLASH_BUFFER_MAX);
//--- setp 2 读取 0x1E71,并写入到0x1EB5
// 读取所需到缓存
temp=FLASH_READ_ONE(0x1E71);
// 非需要段的任意数据填充
for (i = 0; i < 16; i++)
{
FLASH_BUFFER[i] = 0x0000;
}
//应该擦除0x1EA0页的数据 以便后续以0x1EB0开始的块写,如果确定该页为0xFFFF,可以跳过
FLASH_WRITE_FUN(0x1EA0,16);
// 修改要写入的值
FLASH_BUFFER[5]=temp;
FLASH_BUFFER[6]=0xFF33; //验证 用 0x1FB6
// 执行0x0F98块的写入
FLASH_WRITE_FUN(0x1EB0,16);
//--- setp 3 将OX0F69中的数据修改为0X55AA
#if 1 // 0 或 1
FLASH_WRITE_ONE(0x1E69,0x55AA);
#else
FLASH_READ_FUN(0x1E60,FLASH_BUFFER_MAX); //缓存必须满足操作对应大小,不满足整页时,后续数据被删空
FLASH_BUFFER[9]=0x55AA;
FLASH_WRITE_FUN(0x1E60,FLASH_BUFFER_MAX);
#endif
//----------------------------------------- setp 4 end display
while(1)
{
temp++;
if(temp>30000)
{
temp=0;
LED = !LED;
}
}
}
/***********************************************************************************
* 函数名 :FLASH_READ_ONE
* 函数功能:以设定地址开始,读取一个数的FLASH数据并返回结果
* 入口参数:起始地址
* 返回 :无
**********************************************************************************/
unsigned int FLASH_READ_ONE(unsigned int address)
{
#if 1 // 1 选用C语言表达 0 选用嵌汇编表达,嵌汇编效率更高,但提示传递参数未使用和函数无返回,可以忽略
// FLASH_READ_BUF; 需要定义全局的该变量,获取值用于返回,或直接使用
//; 参数的使用
NVMADDRH=(unsigned char)(address>>8);
NVMADDRL=(unsigned char)address;
__asm
;//备份中断使能寄存器
BANKSEL _INTCTL
MOV R1,_INTCTL
;//关闭中断,操作不可打断
CLR _INTCTL,_AIE
JNB _INTCTL,_AIE
JMP $-2
;//时钟频率备份及降频操作,建议降频到1M,此时中断已被关
MOV R0,#0x30
MOV R2,_OSCCTL
MOV _OSCCTL,R0
;//硬件使能读操作
BANKSEL _NVMCTL0
MOV R5,#0x81
MOV _NVMCTL0,R5
NOPZ
NOPZ
NOPZ
NOPZ
;//时钟与中断使能的还原
MOV _OSCCTL,R2
AND R1,#0xC0 ;//中断使能仅关系高2位
ORL INTCTL,R1
;//操作结果赋值到变量,用于返回
BANKSEL _NVMDATAL
MOV R6,_NVMDATAL
MOV R7,_NVMDATAH
BANKSEL _FLASH_READ_BUF
MOV (_FLASH_READ_BUF),R6
MOV (_FLASH_READ_BUF+1),R7
__endasm;
return FLASH_READ_BUF;
#else
// 参数的传递使用 编译器自动变量 STK00 和 R0 ,其中R0为高位,返回使用STK00 和 R0 ,其中R0为高位
__asm
;//传递操作地址
BANKSEL _NVMADDRH
MOV _NVMADDRH,R0
BANKSEL STK00
MOV R0,STK00
BANKSEL _NVMADDRL
MOV _NVMADDRL,R0
;//备份中断使能寄存器
BANKSEL _INTCTL
MOV R1,_INTCTL
;//关闭中断,操作不可打断
CLR _INTCTL,_AIE
JNB _INTCTL,_AIE
JMP $-2
;//时钟频率备份及降频操作,建议降频到1M,此时中断已被关
MOV R0,#0x30
MOV R2,_OSCCTL
MOV _OSCCTL,R0
;//硬件使能读操作
BANKSEL _NVMCTL0
MOV R5,#0x81
MOV _NVMCTL0,R5
NOPZ
NOPZ
NOPZ
NOPZ
;//时钟与中断使能的还原
MOV _OSCCTL,R2
AND R1,#0xC0 ;//中断使能仅关系高2位
ORL _INTCTL,R1
;//操作结果提供形式 整型数据返回结果使用 编译器自动变量 STK00 和 R0 ,其中R0为高位
BANKSEL _NVMDATAL
MOV R0,NVMDATAL
BANKSEL STK00
MOV STK00,R0
BANKSEL _NVMDATAH
MOV R0,NVMDATAH
__endasm;
#endif
}
/***********************************************************************************
* 函数名 :FLASH_READ_FUN
* 函数功能:以设定地址开始,读取一定个数的FLASH数据放置到数值FLASH_BUFFER[x]中
* 入口参数:起始地址,操作个数
* 返回 :无
**********************************************************************************/
void FLASH_READ_FUN(unsigned int address,unsigned char length)
{
#if 1 // 1时效率比0小 1 使用传递参数 0 按编译器全嵌汇编实现,但编译提示参数未使用,可以忽略
//; 参数的使用
NVMADDRH=(unsigned char) (address>>8);
NVMADDRL=address;
__asm
;//备份中断使能寄存器
BANKSEL _INTCTL
MOV R1,_INTCTL
;//关闭中断,操作不可打断
CLR _INTCTL,_AIE
JNB _INTCTL,_AIE
JMP $-2
;//时钟频率备份及降频操作,建议降频到1M,此时中断已被关
MOV R0,#0x30
MOV R2,_OSCCTL
MOV _OSCCTL,R0
;//读取结果的存放起始RAM地址,即数组缓存区 FLASH_BUFFER[x]
MOV R3,#_FLASH_BUFFER
__endasm;
while(length--) // 仅使用R0,不改变R1
{
__asm
;//硬件读
BANKSEL _NVMCTL0
MOV R5,#0x81
MOV _NVMCTL0,R5
NOPZ
NOPZ
NOPZ ;//至少2条
NOPZ
;//读结果处理
BANKSEL _NVMDATAL
MOV R6,_NVMDATAL
MOV R7,_NVMDATAH
BANKSEL _FLASH_BUFFER
ST [R3],R6
INC R3
ST [R3],R7
INC R3
;//指向下一操作地址,建议每次操作内容在一个块中,此时不需要处理_BADDRL进位的_BADDRH+1操作
BANKSEL _NVMADDRL
INC _NVMADDRL
JNB _PSW,_Z
INC _NVMADDRH
__endasm;
}
__asm
;//时钟与中断使能的还原
BANKSEL _OSCCTL
MOV _OSCCTL,R2
AND R1,#0xC0 ;//中断使能仅关系高2位
ORL _INTCTL,R1
__endasm;
#else
// 参数1的传递使用 编译器自动变量 STK00 和 R0 ,其中R0为高位
// 参数2的传递使用 编译器自动变量 STK01
// 整体实现的嵌汇编会提示参数未被使用,可以忽略
__asm
;//传递操作地址
BANKSEL _NVMADDRH
MOV _NVMADDRH,R0
BANKSEL STK00
MOV R0,STK00
BANKSEL _NVMADDRL
MOV _NVMADDRL,R0
;//备份中断使能寄存器
BANKSEL _INTCTL
MOV R1,_INTCTL
;//关闭中断,操作不可打断
CLR _INTCTL,_AIE
JNB _INTCTL,_AIE
JMP $-2
;//时钟频率备份及降频操作,建议降频到1M,此时中断已被关
MOV R0,#0x30
MOV R2,_OSCCTL
MOV _OSCCTL,R0
;//读取结果的存放起始RAM地址,即数组缓存区 FLASH_BUFFER[x]
MOV R0,#_FLASH_BUFFER
FLASH_READ_FUN_LOOP:
;//硬件读
BANKSEL _NVMCTL0
MOV R5,#0x81
MOV _NVMCTL0,R5
NOPZ
NOPZ
NOPZ ;//至少2条
NOPZ
;//读结果处理
BANKSEL _NVMDATAL
MOV R6,_NVMDATAL
MOV R7,_NVMDATAH
BANKSEL _FLASH_BUFFER
ST [R0],R6
INC R0
ST [R0],R7
INC R0
;//指向下一操作地址,建议每次操作内容在一个块中,此时不需要处理_BADDRL进位的_BADDRH+1操作
BANKSEL _NVMADDRL
INC _NVMADDRL
JNB _PSW,_Z
INC _NVMADDRH
;//读数量的判断
BANKSEL STK01
DECJZ STK01
JMP FLASH_READ_FUN_LOOP
;//时钟与中断使能的还原
BANKSEL _OSCCTL
MOV _OSCCTL,R2
AND R1,#0xC0 ;//中断使能仅关系高2位
ORL _INTCTL,R1
__endasm;
#endif
}
/***********************************************************************************
* 函数名 :FLASH_WRITE_FUN
* 函数功能:按块或按页写入数据到FLASH,个数参数只能为16,32 ,地址必须为块的首地址 如十六进制下结尾00 20 40 60 80 A0 C0 E0
* 如果地址不是页的首地址,必须确定后续块结果为0xFFFF,或前面操作过块首写,使后续块值被0xFFFF,否则写结果异常。
* 入口参数:待写地址,待写地址的数据
* 返回 :无
* 写时间说明:除去代码,以整页FLASH为例,操作完第一块需要6ms,另外3块需要3ms。即第一块执行整页的擦除后写自身块,其他块直接写。
**********************************************************************************/
void FLASH_WRITE_FUN(unsigned int address,unsigned char length)
{
#if 1 // 1时效率比0小 1 使用传递参数 0 按编译器全嵌汇编实现,但编译提示参数未使用,可以忽略
//; 参数的使用
NVMADDRH=(unsigned char) (address>>8);
NVMADDRL=address;
__asm
;//备份中断使能寄存器
BANKSEL _INTCTL
MOV R1,_INTCTL
;//关闭中断,操作不可打断
CLR _INTCTL,_PUIE
CLR _INTCTL,_AIE
JNB _INTCTL,_AIE
JMP $-2
;//时钟频率备份及降频操作,建议降频到1M,此时中断已被关
MOV R0,#0x30
MOV R2,_OSCCTL
MOV _OSCCTL,R0
;//读取结果的存放起始RAM地址,即数组缓存区 FLASH_BUFFER[x]
MOV R3,#_FLASH_BUFFER
__endasm;
while(length--) // 仅使用R0,不改变R1
{
__asm
;//加载待写数据
BANKSEL _FLASH_BUFFER
LD R6,[R3]
INC R3
LD R7,[R3]
INC R3
BANKSEL _NVMDATAH
MOV _NVMDATAH,R7
MOV _NVMDATAL,R6
;//硬件写
MOV R5 ,#0x84
MOV _NVMCTL0,R5
MOV R5,#0x69
MOV _NVMCTL1,R5
MOV R5,#0x96
MOV _NVMCTL1,R5
SET _NVMCTL0 , 1 ;// 写存在高压,高压还原添加空指令确保后续运行正常
NOPZ
NOPZ
NOPZ
NOPZ
NOPZ
NOPZ ;// 建议10条,至少2条
NOPZ
NOPZ
NOPZ
NOPZ
MOV R5,#0X80
MOV _NVMCTL0,R5
;//指向下一操作地址,这里不考虑高位,特性要求只能操作一页内的数据,不能跨页
BANKSEL _NVMADDRL
INC _NVMADDRL
__endasm;
}
__asm
;//时钟与中断使能的还原
BANKSEL _OSCCTL
MOV _OSCCTL,R2
AND R1,#0xC0 ;//中断使能仅关系高2位
ORL _INTCTL,R1
__endasm;
#else
// 参数1的传递使用 编译器自动变量 STK00 和 R0 ,其中R0为高位
// 参数2的传递使用 编译器自动变量 STK01
// 整体实现的嵌汇编会提示参数未被使用,可以忽略
__asm
;//传递操作的地址(块首)
BANKSEL _NVMADDRH
MOV _NVMADDRH,R0
BANKSEL STK00
MOV R0,STK00
BANKSEL _NVMADDRL
MOV _NVMADDRL,R0
;//备份中断使能寄存器
BANKSEL _INTCTL
MOV R1,_INTCTL
;//关闭中断,操作不可打断
CLR _INTCTL,_PUIE
CLR _INTCTL,_AIE
JNB _INTCTL,_AIE
JMP $-2
;//时钟频率备份及降频操作,建议降频到1M,此时中断已被关
MOV R0,#0x30
MOV R2,_OSCCTL
MOV _OSCCTL,R0
;//待写数据的存放起始RAM地址,即数组缓存区 FLASH_BUFFER[x]
MOV R0,#_FLASH_BUFFER
FALSH_WRITE_PAGE_LOOP:
;//加载待写数据
BANKSEL _FLASH_BUFFER
LD R6,[R0]
INC R0
LD R7,[R0]
INC R0
BANKSEL _NVMDATAH
MOV _NVMDATAH,R7
MOV _NVMDATAL,R6
;//硬件写
MOV R5 ,#0x84
MOV _NVMCTL0,R5
MOV R5,#0x69
MOV _NVMCTL1,R5
MOV R5,#0x96
MOV _NVMCTL1,R5
SET _NVMCTL0 , 1 ;// 写存在高压,高压还原添加空指令确保后续运行正常
NOPZ
NOPZ
NOPZ
NOPZ
NOPZ
NOPZ ;// 建议10条,至少2条
NOPZ
NOPZ
NOPZ
NOPZ
MOV R5,#0X80
MOV _NVMCTL0,R5
;//指向下一操作地址,这里不考虑高位,特性要求只能操作一页内的数据,不能跨页
BANKSEL _NVMADDRL
INC _NVMADDRL
BANKSEL STK01
DECJZ STK01
JMP FALSH_WRITE_PAGE_LOOP
;//时钟与中断使能的还原
BANKSEL _OSCCTL
MOV _OSCCTL,R2
AND R1,#0xC0 ;//中断使能仅关系高2位
ORL _INTCTL,R1
__endasm;
#endif
}
/***********************************************************************************
* 函数名 :FLASH_WRITE_ONE
* 函数功能:向某一地址写入一个字,不建议使用,实际执行从页首读出,经对应地址的缓存修改,在页起始回写,因此地址与页首偏移量需要数组大小满足需求。
* 建议: 可采用FLASH_WRITE_FUN的传递个数为16,32 对应连续操作地址开始的第一块 第二块
* 但地址必须是某块的开始
* 入口参数:待写地址,待写地址的数据
* 返回 :无
**********************************************************************************/
void FLASH_WRITE_ONE(unsigned int address,unsigned int value)
{
// 读出当前页到数据缓存,要求缓存大小必须满足写地址的偏移量要求
FLASH_READ_FUN(address&0xFFE0,FLASH_BUFFER_MAX);
// 修改待写数据在按照页排序中位置数据结果
FLASH_BUFFER[address&0x1F] = value;
// 整页数据回写
FLASH_WRITE_FUN(address&0xFFE0,FLASH_BUFFER_MAX);
}