xxkj2010 发表于 2025-7-8 17:35:50

解决stc8h2k12u读EEPROM的一点小问题,求大神们验证

以前用STC8H8K64U读写EEPROM,使用官方例程,一向都正常,但近日换用STC8H2K12U-SOP16时,却出现了一点小问题。

现象是当连续写入,再连续读出时,读出的第0字节是错误的,正确的数据跑到第1字节了,第1字节的正确数据跑到第2字节了----- 依此类推。


具体请参考:https://www.stcaimcu.com/thread-18985-1-1.html
源代码如下:

/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/*---------------------------------------------------------------------*/

#include "stc8h.h"
#include "intrins.h"
#include "stdio.h"

/*************本程序功能说明**************

对STC内部自带的EEPROM(FLASH)进行读写测试。

对FLASH做扇区擦除、写入、读出的操作。

通过串口打印读取EEPROM结果。

注意:下载时,下载界面"硬件选项"中设置用户EEPROM大小,

并确保擦除、写入、读出的地址在EEPROM设置的大小范围之内。

下载时, 选择时钟 11.0592MHz (用户可自行修改频率)。

******************************************/

#define   MAIN_Fosc       11059200L   //定义主时钟参数
#define   BAUD            115200
#define   TM            (65536 -(MAIN_Fosc/BAUD/4))

#define   OFFSET          12   //EEPROM起始地址

typedef   unsigned char   u8;
typedef   unsigned int    u16;
typedef   unsigned long   u32;

void PrintfInit(void)
{
      SCON = (SCON & 0x3f) | 0x40;
      AUXR |= 0x40;                //定时器时钟1T模式
      AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
      TL1= TM;
      TH1= TM>>8;
      TR1 = 1;                              //定时器1开始计时

//      SCON = (SCON & 0x3f) | 0x40;
//      T2L= TM;
//      T2H= TM>>8;
//      AUXR |= 0x15;   //串口1选择定时器2为波特率发生器
}

void UartPutc(unsigned char dat)
{
      SBUF = dat;
      while(TI == 0);
      TI = 0;
}

char putchar(char c)
{
      UartPutc(c);
      return c;
}

void IapIdle()
{
    IAP_CONTR = 0;                              //关闭IAP功能
    IAP_CMD = 0;                              //清除命令寄存器
    IAP_TRIG = 0;                               //清除触发寄存器
    IAP_ADDRH = 0x80;                           //将地址设置到非IAP区域
    IAP_ADDRL = 0;
}

unsigned char IapRead(int addr)
{
    char dat;

    IAP_CONTR = 0x80;                           //使能IAP
    IAP_CMD = 1;                              //设置IAP读命令
    IAP_ADDRL = addr;                           //设置IAP低地址
    IAP_ADDRH = addr >> 8;                      //设置IAP高地址
    IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
    _nop_();
    dat = IAP_DATA;                           //读IAP数据
    IapIdle();                                  //关闭IAP功能

    return dat;
}

void IapProgram(int addr, unsigned char dat)
{
    IAP_CONTR = 0x80;                           //使能IAP
    IAP_CMD = 2;                              //设置IAP写命令
    IAP_ADDRL = addr;                           //设置IAP低地址
    IAP_ADDRH = addr >> 8;                      //设置IAP高地址
    IAP_DATA = dat;                           //写IAP数据
    IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
    _nop_();
    IapIdle();                                  //关闭IAP功能
}

void IapErase(int addr)
{
    IAP_CONTR = 0x80;                           //使能IAP
    IAP_CMD = 3;                              //设置IAP擦除命令
    IAP_ADDRL = addr;                           //设置IAP低地址
    IAP_ADDRH = addr >> 8;                      //设置IAP高地址
    IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
    _nop_();                                    //
    IapIdle();                                  //关闭IAP功能
}

void Delay1000ms(void)      //@11.0592MHz
{
      unsigned char data i, j, k;

      i = 57;
      j = 27;
      k = 112;
      do
      {
                do
                {
                        while (--k);
                } while (--j);
      } while (--i);
}


void main()
{
    unsigned char i;
    unsigned char a;
   
    P0M1 = 0;   P0M0 = 0;   //设置为准双向口
    P1M1 = 0;   P1M0 = 0;   //设置为准双向口
    P2M1 = 0;   P2M0 = 0;   //设置为准双向口
    P3M1 = 0;   P3M0 = 0;   //设置为准双向口
    P4M1 = 0;   P4M0 = 0;   //设置为准双向口
    P5M1 = 0;   P5M0 = 0;   //设置为准双向口
   
    IAP_TPS = 11;       //设置EEPROM操作等待参数(11.0592MHz),初始化设置一次即可
    PrintfInit();
      Delay1000ms();      //@11.0592MHz
      Delay1000ms();      //@11.0592MHz
    printf("Read1=");   //读取EEPROM原先的内容
    for(i=0;i<10;i++)
    {
      a = IapRead(OFFSET+i);
      printf("0x%02bx ",a);
      if(a == 0xff) a = i;//如果内容为空,则写入初始化数据
      else a++;    //如果内容非空,在原先基础上加1
    }
    printf("\r\n");

    IapErase(OFFSET);   //如果擦写范围跨扇区,需要擦除两个扇区的空间
    for(i=0;i<10;i++)
    {
      IapProgram(OFFSET+i, a);
    }

    printf("Read2=");   //擦除、重写后,读取EEPROM现在的内容
    for(i=0;i<10;i++)
    {
      a = IapRead(OFFSET+i);
      printf("0x%02bx ",a);
    }
    printf("\r\n");

    while (1);
}

经过多次测试,发现加长读函数的延时,即可解决,
改后的读函数如下:
unsigned char IapRead(int addr)
{
    char dat;

    IAP_CONTR = 0x80;                           //使能IAP
    IAP_CMD = 1;                              //设置IAP读命令
    IAP_ADDRL = addr;                           //设置IAP低地址
    IAP_ADDRH = addr >> 8;                      //设置IAP高地址
    IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
    _nop_();_nop_();
    dat = IAP_DATA;                           //读IAP数据
    IapIdle();                                  //关闭IAP功能

    return dat;
}

该问题请大家测试一下。谢谢!






xxkj2010 发表于 2025-7-8 20:46:58

在读函数中延时没有加长的情况下,测试时,可以将

#define   OFFSET          12   //EEPROM起始地址

中的12改为0

刚才没有改成0,有一次是没有问题的,然后改为0,又出现了问题

ercircle 发表于 2025-7-11 10:04:09

看AiCube生成的和8H手册里都是四个NOP。
1L这个例程可能时间长了没维护吧。使用AiCube一键生成~







xxkj2010 发表于 2025-7-11 13:48:42

ercircle 发表于 2025-7-11 10:04
看AiCube生成的和8H手册里都是四个NOP。
1L这个例程可能时间长了没维护吧。使用AiCube一键生成~


难怪。
我是加长延时解决问题的。
估计STC8H2K12U在读取EEPROM时,要比STC8H8K64U稍微快了那么一点点

_依如画 发表于 2025-7-11 15:45:31

我一般直接读取地址。addr是eeprom多少字节,IAP_OFFSET是eeprom的基址
char IAP_Read_MOVC(int addr)        //读取EEPROM
{
        addr += IAP_OFFSET;
        return *(char code *)(addr);
}

xxkj2010 发表于 2025-7-11 16:20:31

_依如画 发表于 2025-7-11 15:45
我一般直接读取地址。addr是eeprom多少字节,IAP_OFFSET是eeprom的基址
char IAP_Read_MOVC(int addr)        //读 ...

那么,写入又如何操作?用官方的函数void IapProgram(unsigned int addr, unsigned char dat)吗?

xxkj2010 发表于 2025-7-11 18:03:59

_依如画 发表于 2025-7-11 15:45
我一般直接读取地址。addr是eeprom多少字节,IAP_OFFSET是eeprom的基址
char IAP_Read_MOVC(int addr)        //读 ...

请问eeprom的基址怎样计算?

神农鼎 发表于 2025-7-11 22:20:21



IAP 是从 0000H 开始

xxkj2010 发表于 2025-7-12 09:23:07

神农鼎 发表于 2025-7-11 22:20
IAP 是从 0000H 开始

好的,我试试
页: [1]
查看完整版本: 解决stc8h2k12u读EEPROM的一点小问题,求大神们验证