STC8H1K08存储数据问题
使用STC8H1K08存储数据问题,先擦除,然后写入,再打印确认,结果发现打印的数据不对.u8DatastoreTest;
u8DataPrintTmp;
u8xdata DataStore;
const u8 DataStoreT = {0xCC, 0xCC, 0xCC, 0xCC, 0xBB, 0xBB, 0xBB, 0xBB};
u8 xdata DataPrint;
#defineDataAddres 0x0004
//********测试将数据存入EEPROM**********
void StoreBattery_value(void)
{
++ DatastoreTest;
if (DatastoreTest > 100)
{
DatastoreTest = 0;
}
EEPROM_SectorErase(DataAddres); //擦除扇区
delay_ms(10);
EEPROM_write_n(DataAddres, DataStoreT, 8);
printf("*****DatastoreTest = 0x%04X\n", DatastoreTest);
printf("DataStoreT = 0x%02X\n", DataStoreT);
printf("DataStoreT = 0x%02X\n", DataStoreT);
printf("DataStoreT = 0x%02X\n", DataStoreT);
printf("DataStoreT = 0x%02X\n", DataStoreT);
printf("DataStoreT = 0x%02X\n", DataStoreT);
printf("DataStoreT = 0x%02X\n", DataStoreT);
printf("DataStoreT = 0x%02X\n", DataStoreT);
printf("DataStoreT = 0x%02X\n", DataStoreT);
}
//********test 从EEPROM打印电压数据**********
void ReadBattery_value(void)
{
EEPROM_read_n (DataAddres,DataPrint,8);
DataPrintTmp = (DataPrint << 8) | DataPrint; // 从DataStore和DataStore还原BatValueLast
DBG_PRINTF("****EEPROM ReadBattery_value =: %d\n",Battery_value);
// 打印存储后的结果
printf("*****DataPrintTmp = 0x%04X\n", DataPrintTmp);
printf("----- 1111 DataRead From EEprom-----\r\n");
printf("DataPrint = 0x%02X\n", DataPrint);
printf("DataPrint = 0x%02X\n", DataPrint);
printf("DataPrint = 0x%02X\n", DataPrint);
printf("DataPrint = 0x%02X\n", DataPrint);
printf("DataPrint = 0x%02X\n", DataPrint);
printf("DataPrint = 0x%02X\n", DataPrint);
printf("DataPrint = 0x%02X\n", DataPrint);
printf("DataPrint = 0x%02X\n", DataPrint);
}
运行后,打印的却不是完成的写入数据:
接收←*****DatastoreTest = 0x4CEC
DataStoreT = 0xCCEC
DataStoreT = 0xCCEC
DataStoreT = 0xCCEC
DataStoreT = 0xCCEC
DataStoreT = 0xBBEC
DataStoreT = 0xBBEC
DataStoreT = 0xBBEC
DataStoreT = 0xBBEC
-------- StoreBattery_value: 1
****EEPROM ReadBattery_value =: 0
*****DataPrintTmp = 0x8800
----- 1111 DataRead From EEprom-----
DataPrint = 0x8800
DataPrint = 0x8800
DataPrint = 0x8800
DataPrint = 0x8800
DataPrint = 0xBB00
DataPrint = 0xBB00
DataPrint = 0xBB00
DataPrint = 0xBB00
关于您在使用STC8H1K08进行EEPROM数据存储过程中出现的“写入后读取数据不一致”的问题,本文将从硬件原理、代码逻辑、常见问题及调试建议等方面进行全面分析,以帮助您定位问题并提出解决方案。
一、问题描述
您在使用STC8H1K08的EEPROM功能时,执行如下操作流程:
1. 擦除指定地址的扇区;
2. 写入固定数据;
3. 打印写入的数据与读取的数据,发现数据不一致。
代码中涉及如下关键点:
使用xdata关键字定义了DataStore和DataPrint数组;
定义了一个常量数组DataStoreT用于写入;
使用宏定义DataAddres为0x0004;
调用EEPROMSectorErase(DataAddres)进行扇区擦除;
调用EEPROMwriten(DataAddres, DataStoreT, 8)进行写入;
使用printf()打印写入内容和测试变量。
二、EEPROM写入机制分析
STC8H系列单片机内置EEPROM模块,其工作原理与Flash类似,具有以下特点:
1. 擦写限制:每个扇区必须先擦除后写入,不能直接覆盖;
2. 写入单位:通常以字节或页为单位进行写入;
3. 擦除单位:通常以扇区为单位(如512字节);
4. 写入前必须擦除:否则写入失败或数据错误;
5. 写入速度较慢:需适当延时确保完成;
6. 地址对齐要求:部分型号要求写入地址为页对齐或扇区对齐。
三、代码分析与潜在问题
1. 擦除操作是否正确?
您使用了EEPROMSectorErase(DataAddres),其中DataAddres = 0x0004。需要确认:
该地址是否属于某个扇区的起始地址?如果不是,擦除可能影响到相邻数据;
STC8H1K08的EEPROM扇区大小是多少?例如是否为512字节?如果扇区大小为512字节,擦除0x0004将擦除整个0x0000~0x01FF扇区;
擦除后是否延时足够?虽然您使用了delayms(10),但建议查阅芯片手册确认最小擦除时间。
2. 写入操作是否正确?
使用EEPROMwriten(DataAddres, DataStoreT, 8)写入8字节数据,需注意:
EEPROMwriten()函数是否支持非页对齐地址写入?
是否存在写入缓冲区未刷新或未完成的情况?
是否有写入保护机制或校验失败?
3. 数据定义是否正确?
const u8 DataStoreT为常量数组,理论上不会被修改,但需确认是否在EEPROM写入前被正确初始化;
DataStoreT的地址是否在程序运行期间被优化或覆盖?
4. 打印输出是否及时?
printf()函数是否为同步输出?在某些系统中,printf()可能为异步或缓存输出,导致打印结果与实际执行顺序不一致;
建议在打印后加入fflush(stdout)或等效操作,确保输出立即刷新。
四、常见问题与解决方案
问题1:写入地址未对齐
现象:写入失败或数据混乱
解决方法:确认写入地址为页或扇区对齐,例如页大小为8字节,则写入地址应为0x0000、0x0008、0x0010等。
问题2:写入前未正确擦除
现象:写入失败或旧数据残留
解决方法:确保写入前已擦除目标地址所在的整个扇区,并加入足够延时。
问题3:写入函数未正确实现
现象:数据写入不完整或错误
解决方法:检查EEPROMwriten()函数的实现,确认其是否按芯片手册要求操作,包括状态检查、等待写入完成标志等。
问题4:EEPROM寿命耗尽
现象:写入失败或数据不稳定
解决方法:EEPROM擦写次数有限(通常为10万次),若频繁擦写可能导致扇区损坏。建议更换扇区测试或使用磨损均衡算法。
问题5:变量定义与作用域问题
现象:打印数据与预期不符
解决方法:检查DataPrint是否被正确读取,是否与写入数据地址一致,是否被其他函数修改。
五、建议改进代码
c
void StoreBatteryvalue(void)
{
++DatastoreTest;
if (DatastoreTest > 100)
{
DatastoreTest = 0;
}
// 确保写入地址为扇区起始地址
EEPROMSectorErase(DataAddres & (~0x01FF)); // 假设扇区大小为512字节
delayms(100); // 增加延时,确保擦除完成
EEPROMwriten(DataAddres, DataStoreT, 8);
// 延时确保写入完成
delayms(10);
// 读取回读数据
EEPROMreadn(DataAddres, DataPrint, 8);
printf("DatastoreTest = 0x%02X\n", DatastoreTest);
printf("Written Data = 0x%02X\n", DataStoreT);
printf("Written Data = 0x%02X\n", DataStoreT);
printf("Written Data = 0x%02X\n", DataStoreT);
printf("Written Data = 0x%02X\n", DataStoreT);
printf("Readback Data = 0x%02X\n", DataPrint);
printf("Readback Data = 0x%02X\n", DataPrint);
printf("Readback Data = 0x%02X\n", DataPrint);
printf("Readback Data = 0x%02X\n", DataPrint);
// 刷新输出缓冲区(如有)
// fflush(stdout);
}
六、调试建议
1. 使用逻辑分析仪或示波器:观察EEPROM写入时序是否符合手册要求;
2. 使用调试器读取EEPROM内容:绕过程序读取方式,直接查看物理地址内容;
3. 更换写入地址:尝试写入其他扇区,排除扇区损坏可能;
4. 减少写入频率:避免频繁擦写,延长EEPROM寿命;
5. 加入错误检测机制:如CRC校验、数据对比等,增强系统鲁棒性。
七、总结
您遇到的“写入EEPROM后数据不一致”问题,可能是由地址未对齐、擦除不完整、写入函数实现不当、变量作用域冲突或EEPROM硬件损坏等原因引起。建议从以下几个方面着手排查:
确认写入地址是否对齐;
增加擦写延时;
检查写入函数实现;
加入回读验证;
使用调试工具辅助分析。
通过系统性地排查与验证,可有效定位问题根源并加以解决。
如需进一步协助,建议提供以下信息:
EEPROMSectorErase()和EEPROMwriten()的具体实现;
STC8H1K08的EEPROM扇区大小与写入页大小;
实际打印输出结果;
使用的开发环境与编译器版本。
祝您调试顺利!
[本文内容由国芯人工智能辅助生成,仅供参考] 我们读写擦除的地址代码是:
IAP_ADDRH = EE_address / 256;// 实际应写地址高8位
IAP_ADDRL = EE_address % 256;// 实际应写地址低8位
为何不是:
IAP_ADDRH = (EE_address >> 8) & 0xFF;// 取地址高8位
IAP_ADDRL = EE_address & 0xFF; // 取地址低8位
/*---------------------------------------------------------------------*/
/* --- STC MCU Limited ------------------------------------------------*/
/* --- STC 1T Series MCU Demo Programme -------------------------------*/
/* --- Mobile: (86)13922805190 ----------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- Web: www.STCMCUDATA.com---------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/* --- QQ:800003751 -------------------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序 */
/*---------------------------------------------------------------------*/
/*************本程序功能说明**************
本例程基于STC8H8K64U为主控芯片的实验箱9进行编写测试,STC8G、STC8H系列芯片可通用参考.
通过串口2(P4.6 P4.7)对STC内部自带的EEPROM(FLASH)进行读写测试。
对FLASH做扇区擦除、写入、读出的操作,命令指定地址。
默认波特率:115200,8,N,1.
默认主时钟:22.1184MHz.
串口命令设置: (命令字母不区分大小写)
W 0x0040 1234567890--> 对0x0040地址写入字符1234567890.
R 0x0040 10 --> 对0x0040地址读出10个字节数据.
注意:下载时,下载界面"硬件选项"中设置用户EEPROM大小,
并确保串口命令中的地址在EEPROM设置的大小范围之内。
******************************************/
#include "stc8h.h" //包含此头文件后,不需要再包含"reg51.h"头文件
#include "intrins.h"
#include "gloabe.h"
#include "main.h"
#define MAIN_Fosc 22118400L //定义主时钟(精确计算115200波特率)
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
#define checkEEpromLog1 0XAAAA //读写EEPROM缓冲长度
#define checkEEpromLog2 0XBBBB //读写EEPROM缓冲长度
#define Baudrate2 115200L
#define Tmp_Length 100 //读写EEPROM缓冲长度
#define UART2_BUF_LENGTH (Tmp_Length+9)//串口缓冲长度
u8RX2_TimeOut;
u8TX2_Cnt; //发送计数
u8RX2_Cnt; //接收计数
bit B_TX2_Busy; //发送忙标志
u8xdata RX2_Buffer; //接收缓冲
u8xdata tmp; //EEPROM操作缓冲
//u16xdata DataStore;
u8 DataStore;
//u8 DataStoreT;
const u8 DataStoreT = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};//默认688,满电
const u8 DataStoreT1 = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB};
u8 DataPrint;
const u8 DataFactory = {0xAA, 0xBB, 0x02, 0xB0, 0xFF, 0xFF, 0xFF, 0xFF};//默认688,满电
//#defineDataAddres 0x0E00
#defineDataAddres 0x0000
void UART2_config(u8 brt); // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
void PrintString2(u8 *puts);
void UART2_TxByte(u8 dat);
void delay_ms(u8 ms);
u8 CheckData(u8 dat);
u16 GetAddress(void);
u8 GetDataLength(void);
void EEPROM_SectorErase(u16 EE_address);
void EEPROM_read_n(u16 EE_address,u8 *DataAddress,u8 length);
u8 EEPROM_write_n(u16 EE_address,u8 *DataAddress,u8 length);
void CheckEEpromData(void);
/********************* 主函数 *************************/
//void main(void)
//{
// u8i,j;
// u16 addr;
// u8status;
// P_SW2 |= 0x80;//扩展寄存器(XFR)访问使能
//// P0M1 = 0x30; P0M0 = 0x30; //设置P0.4、P0.5为漏极开路(实验箱加了上拉电阻到3.3V)
//// P1M1 = 0x30; P1M0 = 0x30; //设置P1.4、P1.5为漏极开路(实验箱加了上拉电阻到3.3V)
//// P2M1 = 0x3c; P2M0 = 0x3c; //设置P2.2~P2.5为漏极开路(实验箱加了上拉电阻到3.3V)
//// P3M1 = 0x50; P3M0 = 0x50; //设置P3.4、P3.6为漏极开路(实验箱加了上拉电阻到3.3V)
//// P4M1 = 0x3c; P4M0 = 0x3c; //设置P4.2~P4.5为漏极开路(实验箱加了上拉电阻到3.3V)
//// P5M1 = 0x0c; P5M0 = 0x0c; //设置P5.2、P5.3为漏极开路(实验箱加了上拉电阻到3.3V)
//// P6M1 = 0xff; P6M0 = 0xff; //设置为漏极开路(实验箱加了上拉电阻到3.3V)
//// P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
// UART2_config(2); // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
// EA = 1; //允许总中断
// PrintString2("STC8H8K64U系列单片机EEPROM测试程序,串口命令设置如下:\r\n"); //UART2发送一个字符串
// PrintString2("W 0x0040 1234567890--> 对0x0040地址写入字符1234567890.\r\n"); //UART2发送一个字符串
// PrintString2("R 0x0040 10 --> 对0x0040地址读出10个字节内容.\r\n"); //UART2发送一个字符串
// while(1)
// {
// delay_ms(1);
// if(RX2_TimeOut > 0) //超时计数
// {
// if(--RX2_TimeOut == 0)
// {
// for(i=0; i<RX2_Cnt; i++) UART2_TxByte(RX2_Buffer); //把收到的数据原样返回,用于测试
// status = 0xff;//状态给一个非0值
// if((RX2_Cnt >= 10) && (RX2_Buffer == ' ') && (RX2_Buffer == ' ')) //最短命令为10个字节
// {
// for(i=0; i<8; i++)
// {
// if((RX2_Buffer >= 'a') && (RX2_Buffer <= 'z')) RX2_Buffer = RX2_Buffer - 'a' + 'A';//小写转大写
// }
// addr = GetAddress();
// if(addr < 63488) //限制在0~123扇区
// {
// if(RX2_Buffer == 'W') //写入N个字节
// {
// j = RX2_Cnt - 9;
// if(j > Tmp_Length)j = Tmp_Length; //越界检测
// EEPROM_SectorErase(addr); //擦除扇区
// i = EEPROM_write_n(addr,&RX2_Buffer,j); //写N个字节
// if(i == 0)
// {
// PrintString2("已写入");
// if(j >= 100) {UART2_TxByte(j/100+'0'); j = j % 100;}
// if(j >= 10) {UART2_TxByte(j/10+'0'); j = j % 10;}
// UART2_TxByte(j%10+'0');
// PrintString2("字节!\r\n");
// }
// else PrintString2("写入错误!\r\n");
// status = 0; //命令正确
// }
// else if(RX2_Buffer == 'R') //PC请求返回N字节EEPROM数据
// {
// j = GetDataLength();
// if(j > Tmp_Length)j = Tmp_Length; //越界检测
// if(j > 0)
// {
// PrintString2("读出");
// UART2_TxByte(j/10+'0');
// UART2_TxByte(j%10+'0');
// PrintString2("个字节内容如下:\r\n");
// EEPROM_read_n(addr,tmp,j);
// for(i=0; i<j; i++)UART2_TxByte(tmp);
// UART2_TxByte(0x0d);
// UART2_TxByte(0x0a);
// status = 0; //命令正确
// }
// }
// }
// }
// if(status != 0) PrintString2("命令错误!\r\n");
// RX2_Cnt= 0; //清除字节数
// }
// }
// }
//}
//========================================================================
//========================================================================
// 函数: void delay_ms(u8 ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
// 返回: none.
// 版本: VER1.0
// 日期: 2021-3-9
// 备注:
//========================================================================
void delay_ms(u8 ms)
{
u16 i;
do{
i = MAIN_Fosc / 10000;
while(--i); //10T per loop
}while(--ms);
}
//========================================================================
// 函数: u8 CheckData(u8 dat)
// 描述: 将字符"0~9,A~F或a~f"转成十六进制.
// 参数: dat: 要检测的字符.
// 返回: 0x00~0x0F为正确. 0xFF为错误.
// 版本: V1.0, 2012-10-22
//========================================================================
u8 CheckData(u8 dat)
{
if((dat >= '0') && (dat <= '9')) return (dat-'0');
if((dat >= 'A') && (dat <= 'F')) return (dat-'A'+10);
return 0xff;
}
//========================================================================
// 函数: u16 GetAddress(void)
// 描述: 计算各种输入方式的地址.
// 参数: 无.
// 返回: 16位EEPROM地址.
// 版本: V1.0, 2013-6-6
//========================================================================
u16 GetAddress(void)
{
u16 address;
u8i,j;
address = 0;
if((RX2_Buffer == '0') && (RX2_Buffer == 'X'))
{
for(i=4; i<8; i++)
{
j = CheckData(RX2_Buffer);
if(j >= 0x10) return 65535; //error
address = (address << 4) + j;
}
return (address);
}
return65535;//error
}
/**************** 获取要读出数据的字节数 ****************************/
u8 GetDataLength(void)
{
u8i;
u8length;
length = 0;
for(i=9; i<RX2_Cnt; i++)
{
if(CheckData(RX2_Buffer) >= 10)break;
length = length * 10 + CheckData(RX2_Buffer);
}
return (length);
}
//========================================================================
// 函数: void UART2_TxByte(u8 dat)
// 描述: 发送一个字节.
// 参数: 无.
// 返回: 无.
// 版本: V1.0, 2014-6-30
//========================================================================
void UART2_TxByte(u8 dat)
{
S2BUF = dat;
B_TX2_Busy = 1;
while(B_TX2_Busy);
}
//========================================================================
// 函数: void PrintString2(u8 *puts)
// 描述: 串口2发送字符串函数。
// 参数: puts:字符串指针.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void PrintString2(u8 *puts) //发送一个字符串
{
for (; *puts != 0;puts++) UART2_TxByte(*puts); //遇到停止符0结束
}
//========================================================================
// 函数: void SetTimer2Baudraye(u16 dat)
// 描述: 设置Timer2做波特率发生器。
// 参数: dat: Timer2的重装值.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void SetTimer2Baudraye(u16 dat)// 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
{
AUXR &= ~(1<<4); //Timer stop
AUXR &= ~(1<<3); //Timer2 set As Timer
AUXR |=(1<<2); //Timer2 set as 1T mode
T2H = dat / 256;
T2L = dat % 256;
IE2&= ~(1<<2); //禁止中断
AUXR |=(1<<4); //Timer run enable
}
//========================================================================
// 函数: void UART2_config(u8 brt)
// 描述: UART2初始化函数。
// 参数: brt: 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void UART2_config(u8 brt) // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
{
/*********** 波特率固定使用定时器2 *****************/
if(brt == 2)
{
SetTimer2Baudraye(65536UL - (MAIN_Fosc / 4) / Baudrate2);
S2CON &= ~(1<<7); // 8位数据, 1位起始位, 1位停止位, 无校验
IE2 |= 1; //允许中断
S2CON |= (1<<4); //允许接收
P_SW2 &= ~0x01;
P_SW2 |= 1; //UART2 switch to: 0: P1.0 P1.1,1: P4.6 P4.7
RX2_TimeOut = 0;
B_TX2_Busy = 0;
TX2_Cnt = 0;
RX2_Cnt = 0;
}
}
//========================================================================
// 函数: void UART2_int (void) interrupt UART2_VECTOR
// 描述: UART2中断函数。
// 参数: nine.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void UART2_int (void) interrupt 8
{
if((S2CON & 1) != 0)
{
S2CON &= ~1; //Clear Rx flag
RX2_Buffer = S2BUF;
if(++RX2_Cnt >= UART2_BUF_LENGTH) RX2_Cnt = 0;
RX2_TimeOut = 5;
}
if((S2CON & 2) != 0)
{
S2CON &= ~2; //Clear Tx flag
B_TX2_Busy = 0;
}
}
#define IAP_STANDBY() IAP_CMD = 0 //IAP空闲命令(禁止)
#define IAP_READ() IAP_CMD = 1 //IAP读出命令
#define IAP_WRITE() IAP_CMD = 2 //IAP写入命令
#define IAP_ERASE() IAP_CMD = 3 //IAP擦除命令
#define IAP_ENABLE() IAP_CONTR = IAP_EN; IAP_TPS = MAIN_Fosc / 1000000
#define IAP_DISABLE() IAP_CONTR = 0; IAP_CMD = 0; IAP_TRIG = 0; IAP_ADDRH = 0xff; IAP_ADDRL = 0xff
#define IAP_EN (1<<7)
#define IAP_SWBS (1<<6)
#define IAP_SWRST (1<<5)
#define IAP_CMD_FAIL (1<<4)
//========================================================================
// 函数: void DisableEEPROM(void)
// 描述: 禁止EEPROM.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2014-6-30
//========================================================================
void DisableEEPROM(void) //禁止访问EEPROM
{
IAP_CONTR = 0; //关闭 IAP 功能
IAP_CMD = 0; //清除命令寄存器
IAP_TRIG = 0; //清除触发寄存器
IAP_ADDRH = 0xff; //将地址设置到非 IAP 区域
IAP_ADDRL = 0xff;
}
//========================================================================
// 函数: void EEPROM_Trig(void)
// 描述: 触发EEPROM操作.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2014-6-30
//========================================================================
void EEPROM_Trig(void)
{
F0 = EA; //保存全局中断
EA = 0; //禁止中断, 避免触发命令无效
IAP_TRIG = 0x5A;
IAP_TRIG = 0xA5; //先送5AH,再送A5H到IAP触发寄存器,每次都需要如此
//送完A5H后,IAP命令立即被触发启动
//CPU等待IAP完成后,才会继续执行程序。
_nop_();
_nop_();
EA = F0; //恢复全局中断
}
//========================================================================
// 函数: void EEPROM_SectorErase(u16 EE_address)
// 描述: 擦除一个扇区.
// 参数: EE_address:要擦除的EEPROM的扇区中的一个字节地址.
// 返回: none.
// 版本: V1.0, 2014-6-30
//========================================================================
void EEPROM_SectorErase(u16 EE_address)
{
IAP_ENABLE(); //设置等待时间,允许IAP操作,送一次就够
IAP_ERASE(); //宏调用, 送扇区擦除命令,命令不需改变时,不需重新送命令
//只有扇区擦除,没有字节擦除,512字节/扇区。
//扇区中任意一个字节地址都是扇区地址。
IAP_ADDRH = EE_address / 256; //送扇区地址高字节(地址需要改变时才需重新送地址)
IAP_ADDRL = EE_address % 256; //送扇区地址低字节
EEPROM_Trig(); //触发EEPROM操作
DisableEEPROM(); //禁止EEPROM操作
}
//========================================================================
// 函数: void EEPROM_read_n(u16 EE_address,u8 *DataAddress,u8 lenth)
// 描述: 读N个字节函数.
// 参数: EE_address:要读出的EEPROM的首地址.
// DataAddress: 要读出数据的指针.
// length: 要读出的长度
// 返回: 0: 写入正确.1: 写入长度为0错误.2: 写入数据错误.
// 版本: V1.0, 2014-6-30
//========================================================================
void EEPROM_read_n(u16 EE_address,u8 *DataAddress,u8 length)
{
IAP_ENABLE(); //设置等待时间,允许IAP操作,送一次就够
IAP_READ(); //送字节读命令,命令不需改变时,不需重新送命令
do
{
IAP_ADDRL = EE_address / 256; //送地址高字节(地址需要改变时才需重新送地址)
IAP_ADDRH = EE_address % 256; //送地址低字节
EEPROM_Trig(); //触发EEPROM操作
*DataAddress = IAP_DATA; //读出的数据送往
EE_address++;
DataAddress++;
}while(--length);
DisableEEPROM();
}
//========================================================================
// 函数: u8 EEPROM_write_n(u16 EE_address,u8 *DataAddress,u8 length)
// 描述: 写N个字节函数.
// 参数: EE_address:要写入的EEPROM的首地址.
// DataAddress: 要写入数据的指针.
// length: 要写入的长度
// 返回: 0: 写入正确.1: 写入长度为0错误.2: 写入数据错误.
// 版本: V1.0, 2014-6-30
//========================================================================
u8 EEPROM_write_n(u16 EE_address,u8 *DataAddress,u8 length)
{
u8i;
u16 j;
u8*p;
if(length == 0) return 1; //长度为0错误
IAP_ENABLE(); //设置等待时间,允许IAP操作,送一次就够
i = length;
j = EE_address;
p = DataAddress;
IAP_WRITE(); //宏调用, 送字节写命令
do
{
IAP_ADDRL = EE_address / 256; //送地址高字节(地址需要改变时才需重新送地址)
IAP_ADDRH = EE_address % 256; //送地址低字节
IAP_DATA= *DataAddress; //送数据到IAP_DATA,只有数据改变时才需重新送
EEPROM_Trig(); //触发EEPROM操作
EE_address++; //下一个地址
DataAddress++; //下一个数据
}while(--length); //直到结束
EE_address = j;
length = i;
DataAddress = p;
i = 0;
IAP_READ(); //读N个字节并比较
do
{
IAP_ADDRH = EE_address / 256; //送地址高字节(地址需要改变时才需重新送地址)
IAP_ADDRL = EE_address % 256; //送地址低字节
EEPROM_Trig(); //触发EEPROM操作
if(*DataAddress != IAP_DATA) //读出的数据与源数据比较
{
i = 2;
break;
}
EE_address++;
DataAddress++;
}while(--length);
DisableEEPROM();
return i;
}
//********测试将数据存入EEPROM**********
void StoreBattery_value(void)
{
u16DatastoreTest;
u16DatastoreTest1;
u16DataPrintTmp;
u16DataPrintTmp0;
u16DataPrintTmp1;
u16DataPrintTmp2;
u16DataPrintTmp3;
u16DataPrintTmp4;
u16DataPrintTmp5;
u16DataPrintTmp6;
u16DataPrintTmp7;
u16DataPrintTmp8;
u16DataPrintTmp9;
u16DataPrintTmp10;
u16DataPrintTmp11;
u16DataPrintTmp12;
u16DataPrintTmp13;
u16DataPrintTmp14;
u16DataPrintTmp15;
u16DataPrintTmp16;
// DataStore = DatastoreTest;
EEPROM_SectorErase(DataAddres); //擦除扇区
delay_ms(100);
// DatastoreTest1 = 0xAAAA;
printf("*****DatastoreTest = 0d%04d\n", DatastoreTest);
// DataStore = (DatastoreTest >> 8) & 0xFF; // 高位字节
// DataStore = DatastoreTest & 0xFF; // 低位字节
// EEPROM_write_n (DataAddres,&DatastoreTest,2);
// EEPROM_write_n (DataAddres+8,&DatastoreTest,2); DataStoreT
// EEPROM_write_n (DataAddres,DataStore,32);
DatastoreTest ++;
if(DatastoreTest > 20) //100ms
{
EEPROM_write_n (DataAddres,DataStoreT,8);
delay_ms(100);
DatastoreTest = 18;
printf("***** DatastoreTest > 100 DatastoreTest = 0d%04d\n", DatastoreTest);
} else
{
EEPROM_write_n (DataAddres,DataStoreT1,8);
printf("*****DatastoreTest < 100 DatastoreTest = 0d%04d\n", DatastoreTest);
delay_ms(100);
}
EEPROM_read_n (DataAddres,DataPrint,8);
delay_ms(100);
printf("DataPrint = 0d%04d\n", DataPrint);
printf("DataPrint = 0d%04d\n", DataPrint);
printf("DataPrint = 0x%02X\n", DataPrint);
printf("DataPrint = 0x%02X\n", DataPrint);
printf("DataPrint = 0x%02X\n", DataPrint);
printf("DataPrint = 0x%02X\n", DataPrint);
printf("DataPrint = 0x%02X\n", DataPrint);
printf("DataPrint = 0x%02X\n", DataPrint);
}
//********test 从EEPROM打印电压数据**********
void ReadBattery_value(void)
{
}
//*************开机启动检测电压数据*********
void CheckEEpromData(void)
{
u16i;
u8 tmp;
WDT_Clean();
EEPROM_read_n (DataAddres,DataStore,40);
// printf("----- 0000 EEPROM_read_n-----\r\n");
// for (i = 0; i < 5; i++)
// {
// printf(" 0x%02x\t",DataStore);
// }
// printf("\n");
//*******检查是否第一次烧录**********
if ((DataStore==0XAA)&&(DataStore==0XBB))
{
ReadBattery_value();
// LED3 = 0;
}else
{
EEPROM_write_n (DataAddres,DataFactory,32); //烧录出厂预设值
for (i = 0; i <8; i++ )
{
DataStore = DataFactory;
}
Battery_value = (DataStore << 8) | DataStore; // 从DataStore和DataStore还原BatValueLast
DBG_PRINTF("-----Factory Battery_value =: %d\n",Battery_value);
// LED6 = 0;
}
}
用上面测试程序去写EEPROM,却没有成功, 优先使用例程测试,注意型号间EEPROM大小差异
https://www.stcaimcu.com/data/download/DemoCode/STC8H8K64U-DEMO-CODE-V9.6.zip
printf格式差异,C51单字节打印带上b。
【新提醒】printf函数 的 格式参数; USB接口 使用 printf 打印数据 - 串行口,DMA支持的4组串口,RS232,RS485,Modbus, CRC16 国芯人工智能技术交流网站 - AI32位8051交流社区
可以先使用实验箱例程测试一下 migao 发表于 2025-9-27 16:27
我们读写擦除的地址代码是:
IAP_ADDRH = EE_address / 256;// 实际应写地址高8位
IAP_ADDRL = EE_address ...
这两个在同样是16位数据下是等效的 原来的读写SDK有问题,换用新的擦除读写SDK后就可以了.谢谢eric!
页:
[1]