STC单片机内部FLASH读写程序
- 格式:doc
- 大小:17.50 KB
- 文档页数:5
单片机内的 Flash 与 EEPROM 作用及区别单片机运行时的数据都存在于 RAM (随机存储器中, 在掉电后 RAM 中的数据是无法保留的,那么怎样使数据在掉电后不丢失呢?这就需要使用 EEPROM 或FLASHROM 等存储器来实现。
在传统的单片机系统中, 一般是在片外扩展存储器, 单片机与存储器之间通过 IIC 或 SPI 等接口来进行数据通信。
这样不光会增加开发成本,同时在程序开发上也要花更多的心思。
在 STC 单片机中内置了 EEPROM (其实是采用 IAP 技术读写内部 FLASH 来实现 EEPROM ,这样就节省了片外资源,使用起来也更加方便。
下面就详细介绍 STC 单片机内置 EEPROM 及其使用方法。
flash 是用来放程序的,可以称之为程序存储器,可以擦出写入但是基本都是整个扇区进行的 .一般来说单片机里的 flash 都用于存放运行代码,在运行过程中不能改; EEPROM 是用来保存用户数据,运行过程中可以改变,比如一个时钟的闹铃时间初始化设定为 12:00,后来在运行中改为 6:00,这是保存在 EEPROM 里, 不怕掉电,就算重新上电也不需要重新调整到 6:00下面是网上详细的说法,感觉不错:FLASH 和 EEPROM 的最大区别是 FLASH 按扇区操作, EEPROM 则按字节操作, 二者寻址方法不同,存储单元的结构也不同, FLASH 的电路结构较简单,同样容量占芯片面积较小,成本自然比 EEPROM 低,因而适合用作程序存储器, EEPROM 则更多的用作非易失的数据存储器。
当然用 FLASH 做数据存储器也行, 但操作比EEPROM 麻烦的多,所以更“人性化”的 MCU 设计会集成 FLASH 和 EEPROM 两种非易失性存储器,而廉价型设计往往只有 FLASH ,早期可电擦写型 MCU 则都是EEPRM 结构,现在已基本上停产了。
在芯片的内电路中, FLASH 和 EEPROM 不仅电路不同,地址空间也不同,操作方法和指令自然也不同, 不论冯诺伊曼结构还是哈佛结构都是这样。
STC烧写程序一、背景介绍在嵌入式系统开发中,STC单片机是一种常用的微控制器。
对于STC单片机的开发,烧写程序是一个重要的环节。
本文将从STC烧写程序的概念、工作原理、烧写过程和常见问题等方面进行详细探讨。
二、概念解析2.1 STC烧写程序是什么?STC烧写程序指的是通过特定的方法将目标程序加载到STC单片机芯片中的过程。
这个过程通常包括将目标程序转换为可执行的机器码、连接目标程序与硬件设备,并将机器码逐步写入到STC单片机的闪存中。
2.2 STC烧写的重要性烧写程序是将开发完成的嵌入式系统部署到实际硬件中的关键步骤。
一个良好的烧写程序能够确保目标程序正确且稳定地运行在目标硬件上。
同时,烧写程序也对开发调试过程起到了至关重要的作用。
三、STC烧写程序的工作原理3.1 硬件连接烧写过程中,将STC单片机与计算机通过串口或USB接口进行连接。
通常需要使用专门的编程器或者下载器来实现这一步骤,这些硬件设备将计算机与STC单片机芯片连接起来。
3.2 目标程序生成在进行烧写之前,需要先生成目标程序。
目标程序通常由开发者使用高级编程语言编写,如C,C++等。
经过编译、链接等步骤,将源代码转换为可在目标硬件上运行的机器码。
3.3 烧写过程烧写程序的最主要任务是将目标程序写入到STC单片机的闪存中。
烧写过程一般包括以下几个步骤: 1. 打开编程软件,选择与目标硬件对应的端口。
2. 设置烧写参数,如烧写速度、烧写模式等。
3. 导入目标程序的机器码文件。
4. 连接计算机与STC单片机。
5. 启动烧写程序,将机器码逐行写入STC单片机的闪存中。
6. 等待烧写完成,断开计算机与STC单片机的连接。
3.4 烧写后的验证烧写程序完成后,为了确保目标程序的正确性,通常需要进行烧写后的验证。
验证的方法有多种,如读取STC单片机的闪存数据与目标程序进行对比,或者运行目标程序并观察其行为是否符合预期等。
四、STC烧写程序常见问题与解决方法4.1 硬件连接问题4.1.1 无法连接STC单片机解决方法:检查串口或USB接口连接是否正确,确认编程器是否工作正常。
stc单片机eeprom读写程序以下是STC单片机使用EEPROM进行读写的示例程序:#include <reg52.h>#include <intrins.h>#define EEPROM_ADDR 0xA0 // EEPROM的I2C地址sbit SDA = P2^0; // I2C的数据线sbit SCL = P2^1; // I2C的时钟线// I2C开始信号void I2C_Start(){SDA = 1;_nop_(); // 延时一段时间SCL = 1;_nop_(); // 延时一段时间SDA = 0;_nop_(); // 延时一段时间SCL = 0;_nop_(); // 延时一段时间}// I2C停止信号void I2C_Stop(){SDA = 0;_nop_(); // 延时一段时间SCL = 1;_nop_(); // 延时一段时间SDA = 1;_nop_(); // 延时一段时间}// I2C发送一个字节的数据void I2C_SendByte(unsigned char dat){unsigned char i;for (i = 0; i < 8; i++){SDA = dat & 0x80; // 获取最高位dat <<= 1;_nop_(); // 延时一段时间SCL = 1;_nop_(); // 延时一段时间SCL = 0;_nop_(); // 延时一段时间}SDA = 1;_nop_(); // 延时一段时间SCL = 1;_nop_(); // 延时一段时间SCL = 0;_nop_(); // 延时一段时间}// I2C接收一个字节的数据unsigned char I2C_ReceiveByte(){unsigned char i, dat = 0;SDA = 1;for (i = 0; i < 8; i++){_nop_(); // 延时一段时间SCL = 1;_nop_(); // 延时一段时间dat <<= 1;dat |= SDA;SCL = 0;}return dat;}// 在EEPROM中写入一个字节的数据void EEPROM_WriteByte(unsigned char addr, unsigned char dat) {I2C_Start();I2C_SendByte(EEPROM_ADDR | 0); // 发送写入指令I2C_SendByte(addr); // 发送地址I2C_SendByte(dat); // 发送数据I2C_Stop();}// 从EEPROM中读取一个字节的数据unsigned char EEPROM_ReadByte(unsigned char addr){unsigned char dat;I2C_Start();I2C_SendByte(EEPROM_ADDR | 0); // 发送写入指令 I2C_SendByte(addr); // 发送地址I2C_Start();I2C_SendByte(EEPROM_ADDR | 1); // 发送读取指令 dat = I2C_ReceiveByte(); // 读取数据I2C_Stop();return dat;}。
STC单片机EEPROM读写程序在单片机中,EEPROM(Electrically Erasable Programmable Read-Only Memory)是一种非易失性存储器,可以用于存储数据,即使在断电情况下,数据也会被保留。
因此,掌握STC单片机的EEPROM读写程序对于开发嵌入式系统非常重要。
一、EEPROM简介EEPROM是一种可重复擦写的存储器,可用于存储小量数据。
与Flash存储器相比,EEPROM具有更快的写入和擦除速度。
在STC单片机中,EEPROM的存储容量通常较小,一般在几个字节到几千字节之间。
二、EEPROM读操作在STC单片机中,进行EEPROM读操作需要按照以下步骤进行:1. 初始化I2C总线:STC单片机使用I2C总线进行EEPROM读写操作,因此需要先初始化I2C总线。
通过设置相关寄存器,设置I2C 总线的速度和地址。
2. 发送设备地址:确定要读取的EEPROM设备的地址,并发送到I2C总线。
3. 发送寄存器地址:确定要读取的EEPROM寄存器地址,并将其发送到I2C总线。
4. 发送读命令:向EEPROM发送读命令,以启动读操作。
5. 读取数据:从EEPROM中读取数据,并保存到变量中。
6. 结束读操作:完成读操作后,关闭I2C总线。
三、EEPROM写操作类似于读操作,进行EEPROM写操作也需要按照一定的步骤进行:1. 初始化I2C总线:同样地,首先需要初始化I2C总线。
2. 发送设备地址:确定要写入的EEPROM设备的地址,并发送到I2C总线。
3. 发送寄存器地址:确定要写入的EEPROM寄存器地址,并将其发送到I2C总线。
4. 发送写命令:向EEPROM发送写命令,以启动写操作。
5. 写入数据:将要写入EEPROM的数据发送到I2C总线。
6. 结束写操作:完成写操作后,关闭I2C总线。
四、注意事项在进行EEPROM读写操作时,需要注意以下几点:1. 确保正确的设备地址:要与EEPROM的地址匹配,否则无法进行有效的读写操作。
单⽚机内部flash读写1、⾃定义⼯程名称。
2、选择英⽂路径,否则会丢失启动⽂件⽽⽆法编译通过,需要⼿动添加启动⽂件: startup_stm32l431xx.s3、选择MDK-ARM V5开发软件,即KEIL5软件代码编写说明1、按键KEY1按下时,把数据写⼊MCU的flash2、按键KEY2按下时,把写⼊MCU的flash数据并读取出来,打印数据。
调⽤函数:1、HAL_FLASH_Unlock(void);//解锁2、FLASH_EraseInitTypeDef FLASH_EraseInitSturcture;//配置需要擦除的参数3、HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef pEraseInit, uint32_tPageError);//擦除4、HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);//写数据到flash5、HAL_FLASH_Lock(void);//锁住6、HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//检测按键7、void HAL_Delay(uint32_t Delay);//延时8、HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//点亮和熄灭LED9、uint8_t OneWord=(__IO uint32_t)addr;//读取地址数据10、printf();//打印数据到串⼝函数需要重新定义函数fputc();才能正常使⽤printf();int fputc(int ch,FILE*f){uint8_t temp[1]={ch};HAL_UART_Transmit(&huart1,temp,1,2);return HAL_OK;}代码编写说明• FLASH读写流程:1、对FLASH写⼊数据流程:1)解锁FLASH:调⽤HAL_FLASH_Unlock(void);2)擦除FLASH:调⽤HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef pEraseInit, uint32_t PageError);3)写⼊数据到FLASH:调⽤HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);4)锁住FLASH:调⽤HAL_FLASH_Lock(void);2、FLASH读取数据流程直接读取相应的FLASH地址即可:通过(__IO uint32_t)ADDR读取该地址数据代码编写实现#define strLen sizeof(strWriteToFlash1)/sizeof(uint8_t) //写⼊数据的长度#define ADDR 0x0803f800 //写地址写⼊FLASH数据:void writeflash(int addr, uint32_t* writeFlashData){uint32_t PageError = 0;int i=0; HAL_FLASH_Unlock();FLASH_EraseInitTypeDef f;f.Banks=1;f.NbPages=1;f.Page=255;f.TypeErase=0;HAL_FLASHEx_Erase(&f,&PageError);HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,addr,writeFlashData[i]);HAL_FLASH_Lock();}代码编写实现while(1)循环内程序:if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET){HAL_Delay(100);if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET){printf("KEY1 Press\r\n");writeflash(ADDR,(uint32_t*)&writeFlashData);//擦除并写⼊数据 HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);//LED电平翻转}}if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET){HAL_Delay(100);if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET){printf("KEY2 Press\r\n"); ReadFlashData(ADDR,sizeof(writeFlashData),R_OldFlash);//读取写⼊的数据HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin); //LED电平翻转 }}```**读取FLASH数据**:`void ReadFlashData(int addr,uint32_t strSize,uint8_t* ReadData){uint8_t OneWord; for(int i=0;i<strSize;i++){OneWord=*(__IO uint32_t*)addr;ReadData[i]=OneWord;addr++;}printf("Read the flash data:>>%s<<,address:%x\r\n",ReadData,addr-strSize);// 显⽰该地址的数据}`**扩展实验**:将移植flash.c,该⽂件有实现写数据到FLASH的多种⽅式,如写字符串数据到 FLASH等。
stc89le52rc内部EEPROM的C51读写程序/*stc89le52rc内部EEPROM的C51读写程序串口命令(9600)1.0x35可以写eeprom第一个字节数据0x0c2.0x34可以使用检查eeprom第一个字节的数据,正确就会反馈一串字符3.0x33可以读取eeprom前256个字节数据*/#include<math.h>#include<stdio.h>#include<reg52.h>#include <intrins.h>#define NOP5 _nop_();_nop_();_nop_(); _nop_();_nop_();#define uchar unsigned char#define uint unsigned intsfr AUXR = 0x8e;sfr AUXR1 = 0xa2;sfr P4 = 0xe8;sfr XICON = 0xc0;sfr IPH = 0xb7;sfr WDT_CONTR = 0xe1;sfr ISP_DATA = 0xe2;sfr ISP_ADDRH = 0xe3;sfr ISP_ADDRL = 0xe4;sfr ISP_CMD = 0xe5;sfr ISP_TRIG = 0xe6;sfr ISP_CONTR = 0xe7;#define ENABLE_ISP 0x81 //小于20MHZ使用#define DATA_FLASH_START_ADDR 0x2000//stc89le52rc eeprom起始地址#define TEST_CODE 0x34uchar code start_string[]={"EEPROM first data correct!\n"};uint i=0,k;uchar EEPROM_READ(uint flash_addres);void IAP_disable();void EEPROM_FORMAT(uint flash_addres);void EEPROM_WRITE(uint flash_addres, isp_data);void test_read();void send_byte(uchar bt);void send_string(uchar *ptr, uchar len);void read_string(uint start_addr,uint num);void read_string(uint start_addr, uint num){static uint j,temp;for(j=0;j<num;j++){temp=EEPROM_READ(start_addr+j);NOP5;send_byte(temp);}}void send_string(uchar * ptr, uchar len){static uint j;for(j=0;j<len;j++){send_byte(*(ptr+j));}void send_byte(uchar by){SBUF=by;while(!TI);TI=0;}void IAP_disable(){ISP_CONTR=0;ISP_CMD=0;ISP_TRIG=0;}uchar EEPROM_READ(uint flash_addres) //读取数据{uchar read;ISP_ADDRH=flash_addres>>8;ISP_ADDRL=flash_addres;ISP_CONTR=ENABLE_ISP;ISP_CMD=0x01 ;EA=0;ISP_TRIG=0x46;ISP_TRIG=0xb9 ;NOP5;read=ISP_DATA;IAP_disable();EA=1;return(ISP_DATA);}void EEPROM_WRITE(uint flash_addres,isp_data) //写入数据ISP_ADDRH=flash_addres>>8;ISP_ADDRL=flash_addres;ISP_CONTR=ENABLE_ISP;ISP_CMD=0x02 ;EA=0;ISP_DATA=isp_data;ISP_TRIG=0x46;ISP_TRIG=0xb9;IAP_disable();EA=1;}void EEPROM_FORMAT(uint flash_addres) //擦除{ISP_ADDRH=flash_addres>>8;ISP_ADDRL=flash_addres;ISP_CONTR=ENABLE_ISP;ISP_CMD=0x03;EA=0;ISP_TRIG=0x46;ISP_TRIG=0xb9;IAP_disable();EA=1;}void test_read(){if(RI){RI=0;switch(SBUF)case 0x34:k=EEPROM_READ(DATA_FLASH_START_ADDR);NOP5;if(0x0c==k){send_string(start_string,sizeof(start_string));}send_byte(k);k=0;break;case 0x33:read_string(DATA_FLASH_START_ADDR, 256);break;case 0x35:EEPROM_FORMAT(DATA_FLASH_START_ADDR); //写已经有数据的扇区前要写擦除NOP5;NOP5;NOP5; //可以适当增减延时NOP5;NOP5;NOP5;NOP5;EEPROM_WRITE(DATA_FLASH_START_ADDR,TEST_CODE);//写一个0x0cbreak;default:if(0xff==i)i=0;}k=EEPROM_READ(DATA_FLASH_START_ADDR+(i++)); send_byte(DATA_FLASH_START_ADDR+i);send_byte(k);k=0;break;}}}void init(){TMOD=0x20;SCON=0x50;TL1=0xfd;TH1=0xfd;TR1=1;ET1=1;ES=1;}void main(){init();while(1){test_read();}}。
/* STC89C54RD+的flash空间从0x4000~0xf3ff 共90个扇区,每扇区512字节*/// #define BaseAddr 0x1000 /* 51rc */// #define EndSectoraddr 0x3d00 /* 51rc */// #define EndAddr 0x3fff /* 51rc 12K eeprom */#define BaseAddr 0x4000#define EndSectoraddr 0xf200#define EndAddr 0xf3ff#define UseAddr 0x1000/* ------------- 定义扇区大小------------- */#define PerSector 512/* 用户程序需要记忆的数组, 用户实际使用了n-1个数据,数组长度规整到2 4 8 16 32 64 上*/uchar Ttotal[16] ={0x55, /* 作为判别引导头使用,用户程序请不要修改它*//* 用户保存记忆的数据*/0x01, /* 用途说明....*/0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,};uint timerForDelay, /* 专供延时用的变量*/i, /* 循环变量*/EepromPtr; /* eeprom读写指针*//* --------------- 命令定义--------------- */#define RdCommand 0x01 /* 字节读*/#define PrgCommand 0x02 /* 字节写*/#define EraseCommand 0x03 /* 扇区擦除*//* 定义常量*/#define Error 1#define Ok 0/* 定义Flash对应于20MHz晶振系统的操作等待时间*//* 时钟倍频时WaitTime用0x00*/#define WaitTime 0x01/* ================ 打开ISP,IAP 功能================= */ void ISP_IAP_enable(void){EA = 0; /* 关中断*/ISP_CONTR = ISP_CONTR & 0x18; /* 0001,1000 */ISP_CONTR = ISP_CONTR | WaitTime; /* 写入硬件延时*/ ISP_CONTR = ISP_CONTR | 0x80; /* ISPEN=1 */ }/* =============== 关闭ISP,IAP 功能================== */ void ISP_IAP_disable(void){ISP_CONTR = ISP_CONTR & 0x7f; /* ISPEN = 0 */ISP_TRIG = 0x00;EA = 1; /* 开中断*/}/* ================ 公用的触发代码==================== */ void ISPgoon(void){ISP_IAP_enable(); /* 打开ISP,IAP 功能*/ISP_TRIG = 0x46; /* 触发ISP_IAP命令字节1 */ ISP_TRIG = 0xb9; /* 触发ISP_IAP命令字节2 */ _nop_();}/* ==================== 字节读======================== */ uchar byte_read(uint byte_addr){ISP_ADDRH = (uchar)(byte_addr >> 8); /* 地址赋值*/ISP_ADDRL = (uchar)(byte_addr & 0x00ff);ISP_CMD = ISP_CMD & 0xf8; /* 清除低3位*/ ISP_CMD = ISP_CMD | RdCommand; /* 写入读命令*/ISPgoon(); /* 触发执行*/ISP_IAP_disable(); /* 关闭ISP,IAP功能*/return (ISP_DA TA); /* 返回读到的数据*/}/* ================== 扇区擦除======================== */ void SectorErase(uint sector_addr){uint iSectorAddr;iSectorAddr = (sector_addr & 0xfe00); /* 取扇区地址*/ISP_ADDRH = (uchar)(iSectorAddr >> 8);ISP_ADDRL = 0x00;ISP_CMD = ISP_CMD & 0xf8; /* 清空低3位*/ ISP_CMD = ISP_CMD | EraseCommand; /* 擦除命令3 */ISPgoon(); /* 触发执行*/ISP_IAP_disable(); /* 关闭ISP,IAP功能*/}/* ==================== 字节写======================== */ void byte_write(uint byte_addr, uchar original_data){ISP_ADDRH = (uchar)(byte_addr >> 8); /* 取地址*/ISP_ADDRL = (uchar)(byte_addr & 0x00ff);ISP_CMD = ISP_CMD & 0xf8; /* 清低3位*/ ISP_CMD = ISP_CMD | PrgCommand; /* 写命令2 */ISP_DA TA = original_data; /* 写入数据准备*/ISPgoon(); /* 触发执行*/ISP_IAP_disable(); /* 关闭IAP功能*/}/* =================== 字节写并校验=================== */ uchar byte_write_verify(uint byte_addr, uchar original_data){ISP_ADDRH = (uchar)(byte_addr >> 8); /* 取地址*/ISP_ADDRL = (uchar)(byte_addr & 0xff);ISP_CMD = ISP_CMD & 0xf8; /* 清低3位*/ISP_CMD = ISP_CMD | PrgCommand; /* 写命令2 */ISP_DA TA = original_data;ISPgoon(); /* 触发执行*//* 开始读,没有在此重复给地址,地址不会被自动改变*/ISP_DA TA = 0x00; /* 清数据传递寄存器*/ISP_CMD = ISP_CMD & 0xf8; /* 清低3位*/ISP_CMD = ISP_CMD | RdCommand; /* 读命令1 */ISP_TRIG = 0x46; /* 触发ISP_IAP命令字节1 */ ISP_TRIG = 0xb9; /* 触发ISP_IAP命令字节2 */_nop_(); /* 延时*/ISP_IAP_disable(); /* 关闭IAP功能*/if(ISP_DA TA == original_data){ /* 读写数据校验*/return Ok; /* 返回校验结果*/ }else{return Error;}}/* ===================== 数组写入===================== */ uchar ArrayWrite(uint begin_addr, uint len, uchar *array){uint i;uint in_addr;/* 判是否是有效范围,此函数不允许跨扇区操作*/if(len > PerSector){return Error;}in_addr = begin_addr & 0x01ff; /* 扇区内偏移量*/if((in_addr + len) > PerSector){return Error;}in_addr = begin_addr;/* 逐个写入并校对*/ISP_IAP_enable(); /* 打开IAP功能*/for(i = 0; i< len; i++){/* 写一个字节*/ISP_ADDRH = (uchar)(in_addr >> 8);ISP_ADDRL = (uchar)(in_addr & 0x00ff);ISP_DA TA= array[i]; /* 取数据*/ISP_CMD = ISP_CMD & 0xf8; /* 清低3位 */ISP_CMD = ISP_CMD | PrgCommand; /* 写命令2 */ISP_TRIG = 0x46; /* 触发ISP_IAP命令字节1 */ISP_TRIG = 0xb9; /* 触发ISP_IAP命令字节2 */_nop_();/* 读回来*/ISP_DA TA = 0x00;ISP_CMD = ISP_CMD & 0xf8; /* 清低3位 */ISP_CMD = ISP_CMD | RdCommand; /* 读命令1 */ISP_TRIG = 0x46; /* 触发ISP_IAP命令字节1 */ISP_TRIG = 0xb9; /* 触发ISP_IAP命令字节2 */_nop_();/* 比较对错*/if(ISP_DA TA != array[i]){ISP_IAP_disable();return Error;}in_addr++; /* 指向下一个字节*/ }ISP_IAP_disable();return Ok;}/* ========================= 扇区读出========================= */ /* 程序对地址没有作有效性判断,请调用方事先保证他在规定范围内*/void ArrayRead(uint begin_addr, uchar len){// uchar xdata data_buffer[]; /* 整个扇区读取缓存区*/ uint iSectorAddr;uint i;iSectorAddr = begin_addr; // & 0xfe00; /* 取扇区地址*/ISP_IAP_enable();for(i = 0; i < len; i++){ISP_ADDRH = (uchar)(iSectorAddr >> 8);ISP_ADDRL = (uchar)(iSectorAddr & 0x00ff);ISP_CMD = ISP_CMD & 0xf8; /* 清低3位 */ISP_CMD = ISP_CMD | RdCommand; /* 读命令1 */ISP_DA TA = 0;ISP_TRIG = 0x46; /* 触发ISP_IAP命令字节1 */ISP_TRIG = 0xb9; /* 触发ISP_IAP命令字节2 */_nop_();Ttotal[i] = ISP_DA TA;iSectorAddr++;}ISP_IAP_disable(); /* 关闭IAP功能*/}/* ==============================================================从eeprom中读取数据============================================================== */void DataRestore(){EepromPtr = BaseAddr; /* 指向eeprom的起始点*/while(EepromPtr < EndAddr) /* 在eeprom的可用区域内 */{if(byte_read(EepromPtr) == 0x55)/* 找到了上一次有效纪录*/{break; /* 寻找完成*/}EepromPtr += 0x10; /* 指向下一个小区*/ }if(EepromPtr >= EndAddr) /* 如果照遍都没有,是新片*/{EepromPtr = BaseAddr; /* 指向eeprom的起始点*/for(i=0;i<90;i++){SectorErase(EepromPtr+0x200*i); /* 全部扇区擦除*/}while(ArrayWrite(EepromPtr, 0x10, Ttotal)) /* 写默认值*/{ /* 写入失败才运行的部分*/byte_write(EepromPtr, 0); /* 该单元已经失效*/if(EepromPtr < EndAddr){EepromPtr += 0x10; /* 换一块新的小区*/}else{P1=0; /* 指示芯片内eeprom全坏 */EA= 0; /* 不再做任何事*/while(1); /* 死机*/}}}ArrayRead(EepromPtr, 16);}/* ==============================================================将需要记忆的数据保存到eeprom============================================================== */void DataSave(){uint wrPtr; /* 临时指针*/ NextArea:byte_write_verify(EepromPtr, 0); /* 将原来的标记清除*/wrPtr = EepromPtr & 0xfe00; /* 上一个扇区的起始地址*/EepromPtr += 0x10; /* 目标存入地址*//* ------------------ 判断是否启用新的扇区---------------- */if((EepromPtr & 0x1ff)==0){SectorErase(wrPtr); /* 将上一个扇区擦除,备用*/if(EepromPtr>=EndAddr) /* 已经用完了最后一个区域*/{EepromPtr = BaseAddr; /* 从头开始*/}}/* -------------------- 数据存入前的准备------------------ *//* 。
STC12C5A16S2内部Flash读写一、扇区经擦除后,扇区内的各字节均为oxff,可以进行写操作,即字节编程,若扇区的某一字节不是0xff,则无法将字节中为0的位写成1。
所以保险的方法是先进行扇区擦除,即只有扇区擦除才能将扇区的0变成1。
二、如果在一个扇区中存放了大量的数据,某次只需要修改其中的一个字节或一部分字节时,则另外的不需要修改的数据须先读出放在STC单片机的RAM中,然后擦除整个扇区,再将需要保留的数据和需修改的数据按字节逐字节写回该扇区中(只有字节写命令,无连续字节写命令)。
这时每个扇区使用的字节数是使用的越少越方便(不需读出一大堆需保留数据)。
三、扇区中进行多字节写入时,首先进行扇区擦除,然后进行字节编程,地址是次扇区内的首地址和末地址中的任何一个地址////////////////////内部flash操作函数sfr IAP_DATA=0x0c2;sfr IAP_ADDRH=0xc3;sfr IAP_ADDRL=0xc4;sfr IAP_CMD=0xc5;sfr IAP_TRIG=0xc6;sfr IAP_CONTR=0xc7;//扇区字节读unsigned char read(unsigned char adder_h,unsigned char adder_l){unsigned char dat;IAP_ADDRH=adder_h;IAP_ADDRL=adder_l;IAP_CONTR=0x82;IAP_CMD=0x01;IAP_TRIG=0x5a;IAP_TRIG=0xa5;// nop;dat=IAP_DATA;return dat;}//扇区字节写void write(unsigned char adder_h,unsigned char adder_l,unsigned char dat){IAP_DATA=dat;IAP_ADDRH=adder_h;IAP_ADDRL=adder_l;IAP_CONTR=0x82;IAP_CMD=0x02;IAP_TRIG=0x5a;IAP_TRIG=0xa5;// nop;}void del(unsigned char adder_h,unsigned char adder_l){IAP_ADDRH=adder_h;IAP_ADDRL=adder_l;IAP_CONTR=0x82;IAP_CMD=0x03;IAP_TRIG=0x5a;IAP_TRIG=0xa5;}//把设置的闹钟值存进单片机内部flash函数void restore(unsigned char hour,unsigned char min){del(0x00,0x00);//将扇区内的每个字节都变成0xffdelay_5ms();write(0x00,0x00,hour);//第一个扇区的第一个地址存放设置的小时值 delay_5ms();write(0x02,0x00,min);//第二个扇区的第一个地址存放设置的fen值}。
STC单片机内部FLASH读写程序(/admin/article/article_a dd.php)
电子伙伴发表于 2006-4-11 16:26:00
1
推荐
//#i nclude "STC89C51.h"
//#i nclude <intrins.h>
#define ERROR 0
#define OK 1
#define WAIT_TIME 0x00 //mcu clock 40mhz
//#define WAIT_TIME 0x01 //mcu clock 20mhz
//#define WAIT_TIME 0x02 //mcu clock 10mhz
//#define WAIT_TIME 0x03 //mcu clock 5mhz
#define HOUR 0x8000
#define MINUTE 0x8001
void ISP_EN(void)
{
EA = 0; // 关中断
ISP_CONTR = ISP_CONTR & 0x18; // 0001,1000
ISP_CONTR = ISP_CONTR | WAIT_TIME;
ISP_CONTR = ISP_CONTR | 0x80; // 1000,0000
}
void ISP_DI(void)
{
ISP_CONTR = ISP_CONTR & 0x7f; // 0111,1111
ISP_TRIG = 0x00;
EA = 1; // 开中断}
unsigned char Byte_read(unsigned int byte_addr)
{
ISP_ADDRH = (unsigned char)(byte_addr >> 8);
ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
ISP_CMD = ISP_CMD & 0xf8; // 1111,1000 ISP_CMD = ISP_CMD | 0x01; //
0000,0001 字节读数据存储区
ISP_EN();
ISP_TRIG = 0x46;
ISP_TRIG = 0xb9;
_nop_();
ISP_DI();
return (ISP_DATA);
}
void Sector_erase(unsigned int Sector_addr)
{
unsigned char addre;
addre = (unsigned char)(Sector_addr >> 8); //
1111,1110,0000,0000; 取扇区地址
ISP_ADDRH = addre&0xfe;
ISP_ADDRL = 0x00;
ISP_CMD = ISP_CMD & 0xf8; // 1111,1000
ISP_CMD = ISP_CMD | 0x03; // 0000,0011 扇区擦除数据存储区
ISP_EN();
ISP_TRIG = 0x46; // 触发ISP_IAP命令ISP_TRIG = 0xb9; // 触发ISP_IAP命令 _nop_();
ISP_DI();
}
void Byte_program(unsigned int byte_addr, unsigned char DataBuf) {
ISP_ADDRH = (unsigned char)(byte_addr >> 8);
ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
ISP_CMD = ISP_CMD & 0xf8; // 1111,1000
ISP_CMD = ISP_CMD | 0x02; // 0000,0010 字节编程
数据存储区
ISP_DATA = DataBuf;
ISP_EN();
ISP_TRIG = 0x46; // 触发ISP_IAP命令ISP_TRIG = 0xb9; // 触发ISP_IAP命令_nop_();
ISP_DI();
}
unsigned char ByteProgramVerify(unsigned int byte_addr, unsigned char DataBuf)
{
ISP_ADDRH = (unsigned char)(byte_addr >> 8);
ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
ISP_CMD = ISP_CMD & 0xf8; // 1111,1000
ISP_CMD = ISP_CMD | 0x02; // 0000,0010 字节编程数据存储区
ISP_DATA = DataBuf;
ISP_EN();
ISP_TRIG = 0x46;
ISP_TRIG = 0xb9;
_nop_();
ISP_DATA = 0x00;
ISP_CMD = ISP_CMD & 0xf8; // 1111,1000 ISP_CMD = ISP_CMD | 0x01; // 0000,0001
ISP_TRIG = 0x46; // 触发ISP_IAP命令ISP_TRIG = 0xb9; // 触发ISP_IAP命令_nop_();
ISP_DI();
if(ISP_DATA==DataBuf) return OK;
else return ERROR;
}。