找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 135|回复: 6
打印 上一主题 下一主题
收起左侧

关于单片机EEPROM读数据偶尔不正确的情况

[复制链接]
跳转到指定楼层
楼主
ID:675145 发表于 2025-6-2 11:06 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
一:问题描述
之前用的单片机是stc15w408a都是正常的,因为FLASH太小又需要增加其他功能费不得不改成stc15f2k60s2,但是EEPROM代码是差不多的。从原来保存在2,3扇区变成了0,1扇区(stc15f2k60s2只有0,1这两个扇区)
换完芯片后EEPROM读取大部分时候是正常的,随机出现读出来的数据是0xff,而且第0扇区和第1扇区随机出现,读取异常后无论如何重新上电都是异常的需要重新写入

二:看代码:
1.EEPROM.c
[code]#include "all.h"
void IapIdle()
{
        IAP_CONTR = 0;
        IAP_CMD = 0;
        IAP_TRIG = 0;
        IAP_ADDRH = 0x80;
        IAP_ADDRL = 0;
}
void EEPROM_Wipe512_Drive(u8 ADDRH)        //清空某个扇区
{
        
        IAP_CONTR|=0x82;
        IAP_CMD=0x03;
        IAP_ADDRH=ADDRH;
        IAP_TRIG=0x5A;
        IAP_TRIG=0xA5;
        _nop_();
        _nop_();
        _nop_();
        IapIdle();
}
u8 EEPROM_Read_Byte_Drive(u8 ADDRH,u8 ADDRL)        //读某个扇区数据
{
        u8 idata DATA;
        
        IAP_CONTR|=0x82;
        IAP_CMD=0x01;
        IAP_ADDRH=ADDRH;
        IAP_ADDRL=ADDRL;
        IAP_TRIG=0x5A;
        IAP_TRIG=0xA5;
        _nop_();
        _nop_();
        _nop_();
        DATA=IAP_DATA;
        IapIdle();
        
        return  DATA;
}
void EEPROM_Write_Byte_Drive(u8 ADDRH,u8 ADDRL,u8 Byte)//往某个扇区写数据
{
        IAP_CONTR|=0x82;
        IAP_CMD=0x02;
        IAP_ADDRH=ADDRH;
        IAP_ADDRL=ADDRL;
        IAP_DATA=Byte;
        IAP_TRIG=0x5A;
        IAP_TRIG=0xA5;
        _nop_();
        _nop_();
        _nop_();
        IapIdle();
}

void EEPROM_Write_Data(u8 ADDRH)
{
        EEPROM_Wipe512_Drive(ADDRH);
        
        if(ADDRH == EEPROM_ID)
        {
                EEPROM_Write_Byte_Drive(EEPROM_ID, EEPROM_Drive_ID0, Drive_ID[0]);
                EEPROM_Write_Byte_Drive(EEPROM_ID, EEPROM_Drive_ID1, Drive_ID[1]);
        }
        else if(ADDRH == EEPROM_SET)
        {
                EEPROM_Write_Byte_Drive(EEPROM_SET, EEPROM_Set_Num, Set_Num);
                EEPROM_Write_Byte_Drive(EEPROM_SET, EEPROM_Hall_Open, Hall_Open);
                EEPROM_Write_Byte_Drive(EEPROM_SET, EEPROM_Motor_Open, Motor_Open);
                EEPROM_Write_Byte_Drive(EEPROM_SET, EEPROM_Limit_Flag, Limit_Flag);
                EEPROM_Write_Byte_Drive(EEPROM_SET, EEPROM_Version, Version);
        }
}

2.EEPROM.h
#ifndef EEPROM
#define EEPROM

#define EEPROM_ID                                        0
#define EEPROM_SET                                        2

#define EEPROM_Drive_ID0                        0
#define EEPROM_Drive_ID1                        1

#define EEPROM_Set_Num                                0
#define EEPROM_Hall_Open                        1
#define EEPROM_Motor_Open                        2
#define EEPROM_Limit_Flag                        3
#define EEPROM_Version                                4

extern u8 EEPROM_Read_Byte_Drive(u8 ADDRH,u8 ADDRL);//读某个扇区数据
extern void EEPROM_Write_Data(u8 ADDRH);
        
#endif

3.EEPROM写应用:设备通过RS485进行参数设置时才会用到写EEPROM,一般情况不会随意修改
void RS485_RX_Drive()        //RS485接收底层函数
{
        u8 i;
        
        send_cnt++;    //只要有数据接收,send_cnt每次都被串口中断清零
        if(send_cnt>200)   //延时一段时间,确认缓冲区没有继续接收数据
        {
                send_cnt=0;
                RS485_Busy=0;
                RxLen=0;
                for(i=0;i<7;i++)
                {
                        //检验数据头(D6  F7)
                        if(RS485_Up_Num_Buffer==0xD6 && RS485_Up_Num_Buffer[i+1]==0xF7)
                        {                                
                                switch(RS485_Up_Num_Buffer[i+4])
                                {
                                        case Cmd_Hall:
                                                {
                                                        if(RS485_Up_Num_Buffer[i+3]==Drive_ID[1] || RS485_Up_Num_Buffer[i+3]==0)
                                                        {
                                                                Hall_Open = RS485_Up_Num_Buffer[i+2];                //是否打开霍尔电流开关        
                                                                EEPROM_Write_Data(EEPROM_SET);
                                                        }
                                                }
                                                break;
                                        case Cmd_Motor:
                                                {
                                                        if(RS485_Up_Num_Buffer[i+3]==Drive_ID[1] || RS485_Up_Num_Buffer[i+3]==0)
                                                        {
                                                                Motor_Open = RS485_Up_Num_Buffer[i+2];                //是否打开电机电流开关
                                                                EEPROM_Write_Data(EEPROM_SET);
                                                        }
                                                }
                                                break;
                                        case Cmd_Limit:
                                                {
                                                        if(RS485_Up_Num_Buffer[i+3]==Drive_ID[1] || RS485_Up_Num_Buffer[i+3]==0)
                                                        {
                                                                Limit_Flag = RS485_Up_Num_Buffer[i+2];        //是否打开限位开关
                                                                EEPROM_Write_Data(EEPROM_SET);
                                                        }
                                                }
                                                break;
                                        case Cmd_Run_Num:
                                                        {
                                                                if(Run_Flag==0)        //停止状态才能更改运行次数
                                                                {
                                                                        Set_Num=RS485_Up_Num_Buffer[i+2];
                                                                        EEPROM_Write_Data(EEPROM_SET);
                                                                }
                                                        }
                                                        break;
                                        case Cmd_Version:
                                                        {
                                                                if(Run_Flag==0)        //停止状态才能更改产品版本
                                                                {
                                                                        Version = RS485_Up_Num_Buffer[i+2];                                                                        
                                                                        EEPROM_Write_Data(EEPROM_SET);
                                                                        
                                                                        if(Version==2)
                                                                                Stable_Motor_Current=550;        //电机稳定电流
                                                                        else if(Version==3)
                                                                                Stable_Motor_Current=800;        //电机稳定电流
                                                                }
                                                        }
                                                        break;
                                        case Cmd_EXIT:
                                                        {
                                                                if(Run_Flag==0)        
                                                                {
                                                                        Run_Flag = 3;
                                                                }
                                                        }
                                                        break;
                                }        
                        }
                }
        }
}

4.EEPROM读应用:在初始化时用Get_SET_Parameter函数读取数据,防止上电不充分在读取数据前加了500MS延时,后来没办法在EEPROM读取的数据错误时直接对变量进行赋值

void Get_SET_Parameter(void)
{
        Drive_ID[0] = EEPROM_Read_Byte_Drive(EEPROM_ID,        EEPROM_Drive_ID0);
        Drive_ID[1] = EEPROM_Read_Byte_Drive(EEPROM_ID,        EEPROM_Drive_ID1);
        
        Set_Num = EEPROM_Read_Byte_Drive(EEPROM_SET,        EEPROM_Set_Num);                //初始化运行次数        
        Hall_Open = EEPROM_Read_Byte_Drive(EEPROM_SET,        EEPROM_Hall_Open);                //是否打开霍尔电流开关        
        Motor_Open = EEPROM_Read_Byte_Drive(EEPROM_SET,        EEPROM_Motor_Open);                //是否打开霍尔电流开关        
        Limit_Flag = EEPROM_Read_Byte_Drive(EEPROM_SET,        EEPROM_Limit_Flag);                //是否打开霍尔电流开关
        
        Version = EEPROM_Read_Byte_Drive(EEPROM_SET,EEPROM_Version);
        if(Version==2)
        {
                Stable_Motor_Current=550;        //电机稳定电流
        }
        else if(Version==3)
        {
                Stable_Motor_Current=800;        //电机稳定电流
                Hall_Open = Close;
        }
        
        //        //设置设备编号
        Drive_ID[0] = 'G';
        Drive_ID[1] = 1;
        EEPROM_Write_Data(EEPROM_ID);
        
        //如果出现EEPROM错误,直接赋值
        if(Set_Num == 0xFF)
        {
                Set_Num = 100;                //初始化运行次数        
                Hall_Open = Open;                //是否打开霍尔电流开关        
                Motor_Open = Open;                //是否打开霍尔电流开关        
                Limit_Flag = Open;                //是否打开霍尔电流开关
                Version = 2;
                Stable_Motor_Current=550;        //电机稳定电流
        }
}
void main()
{
        P0M1=0;P0M0=0;
        P1M1=0;P1M0=0;
        P2M1=0;P2M0=0;
        P3M1=0;P3M0=0;
        P4M1=0;P4M0=0;        
        P5M1=0;P5M0=0;

        ADC_Init();
        Cylinder_Init();
        RS485_Init();
        IIC_Init();
        OLED_Init();
        KEY_Init();        
        WS2812B_Init();
        Lock_Init();

        Delay500ms();                                                        //延时500ms,等待系统稳定
        ADC_Reference = ADC_Filter_Result();        //获取ADC偏置值
        Get_SET_Parameter();                                        //获取EEPROM中的系统参数
        
        P2M1=0;P2M0=0x0E;        //P2.1/P2.2/P2.3推挽输出                //气缸输出
        P1M1=0;P1M0=0x30;        //P1.4/P1.5推挽输出                        //电机输出
        while(1)
        {
                RS485_Data_Drive();        //RS485数据处理
                OLED_Allot();                //OLED屏幕显示
                Mach_Run_Scan();        //电机运行扫描        
                KEY_Allot();                //按键扫描服务
                ADC_Allot();                //电流检测服务
                WS2812B_Allot();        //灯
        }
}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:675145 发表于 2025-6-2 14:06 | 只看该作者
stc15f2k60s2的工作频率范围在0-28M,而我的晶振是30M,大概率是这个问题了,先运行一段时间看看还会不会出现问题
回复

使用道具 举报

板凳
ID:1109793 发表于 2025-6-2 15:37 | 只看该作者
会不会是频率设置不对
IAP_CONTR|=0x82;
11.0592用0X83比较好吧。
另外,为啥是|=?
IAP_CONTR=0x83;可以不
回复

使用道具 举报

地板
ID:1109793 发表于 2025-6-2 15:41 | 只看该作者
张小不懂 发表于 2025-6-2 14:06
stc15f2k60s2的工作频率范围在0-28M,而我的晶振是30M,大概率是这个问题了,先运行一段时间看看还会不会出 ...

30M是0x80吧
不过用到22.1184就比较稳定,用到30M你比较大胆
回复

使用道具 举报

5#
ID:1121801 发表于 2025-6-2 16:22 | 只看该作者
IAP我算小白,我不明白IAP_CONTL|=0X82,为什么用"|=",直接赋值用"="不好吗?还是有什么玄机?
回复

使用道具 举报

6#
ID:57657 发表于 2025-6-2 21:49 | 只看该作者
EEPROM二进制1可以写为0,反过来就不能。
只有扇区擦除,没有字节擦除,必须擦除后才可以恢复为1,一次擦1扇区(512字节),扇区擦除的地址必须是512的倍数:0,512,1024,1536等。
另外此型号可以使用unsigned char code *指针(汇编MOVC指令)读取EEPROM数据。
回复

使用道具 举报

7#
ID:675145 发表于 2025-6-4 15:52 | 只看该作者
xiaobendan001 发表于 2025-6-2 15:41
30M是0x80吧
不过用到22.1184就比较稳定,用到30M你比较大胆

之前是STC15F408AS用的30M的晶振,改了单片机但是没注意晶振,现在改用内部晶振24M,暂时没问题
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表