找回密码
 立即注册
查看: 60|回复: 8

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

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:592
  • 最近打卡:2025-07-12 09:18:24

118

主题

2053

回帖

5981

积分

论坛元老

积分
5981
发表于 4 天前 | 显示全部楼层 |阅读模式
以前用STC8H8K64U读写EEPROM,使用官方例程,一向都正常,但近日换用STC8H2K12U-SOP16时,却出现了一点小问题。

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

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

  1. /*---------------------------------------------------------------------*/
  2. /* --- Web: www.STCAI.com ---------------------------------------------*/
  3. /*---------------------------------------------------------------------*/
  4. #include "stc8h.h"
  5. #include "intrins.h"
  6. #include "stdio.h"
  7. /*************  本程序功能说明  **************
  8. 对STC内部自带的EEPROM(FLASH)进行读写测试。
  9. 对FLASH做扇区擦除、写入、读出的操作。
  10. 通过串口打印读取EEPROM结果。
  11. 注意:下载时,下载界面"硬件选项"中设置用户EEPROM大小,
  12. 并确保擦除、写入、读出的地址在EEPROM设置的大小范围之内。
  13. 下载时, 选择时钟 11.0592MHz (用户可自行修改频率)。
  14. ******************************************/
  15. #define     MAIN_Fosc       11059200L   //定义主时钟参数
  16. #define     BAUD            115200
  17. #define     TM              (65536 -(MAIN_Fosc/BAUD/4))
  18. #define     OFFSET          12     //EEPROM起始地址
  19. typedef     unsigned char   u8;
  20. typedef     unsigned int    u16;
  21. typedef     unsigned long   u32;
  22. void PrintfInit(void)
  23. {
  24.         SCON = (SCON & 0x3f) | 0x40;
  25.         AUXR |= 0x40;                //定时器时钟1T模式
  26.         AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
  27.         TL1  = TM;
  28.         TH1  = TM>>8;
  29.         TR1 = 1;                                //定时器1开始计时
  30. //        SCON = (SCON & 0x3f) | 0x40;
  31. //        T2L  = TM;
  32. //        T2H  = TM>>8;
  33. //        AUXR |= 0x15;   //串口1选择定时器2为波特率发生器
  34. }
  35. void UartPutc(unsigned char dat)
  36. {
  37.         SBUF = dat;
  38.         while(TI == 0);
  39.         TI = 0;
  40. }
  41. char putchar(char c)
  42. {
  43.         UartPutc(c);
  44.         return c;
  45. }
  46. void IapIdle()
  47. {
  48.     IAP_CONTR = 0;                              //关闭IAP功能
  49.     IAP_CMD = 0;                                //清除命令寄存器
  50.     IAP_TRIG = 0;                               //清除触发寄存器
  51.     IAP_ADDRH = 0x80;                           //将地址设置到非IAP区域
  52.     IAP_ADDRL = 0;
  53. }
  54. unsigned char IapRead(int addr)
  55. {
  56.     char dat;
  57.     IAP_CONTR = 0x80;                           //使能IAP
  58.     IAP_CMD = 1;                                //设置IAP读命令
  59.     IAP_ADDRL = addr;                           //设置IAP低地址
  60.     IAP_ADDRH = addr >> 8;                      //设置IAP高地址
  61.     IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
  62.     IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
  63.     _nop_();
  64.     dat = IAP_DATA;                             //读IAP数据
  65.     IapIdle();                                  //关闭IAP功能
  66.     return dat;
  67. }
  68. void IapProgram(int addr, unsigned char dat)
  69. {
  70.     IAP_CONTR = 0x80;                           //使能IAP
  71.     IAP_CMD = 2;                                //设置IAP写命令
  72.     IAP_ADDRL = addr;                           //设置IAP低地址
  73.     IAP_ADDRH = addr >> 8;                      //设置IAP高地址
  74.     IAP_DATA = dat;                             //写IAP数据
  75.     IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
  76.     IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
  77.     _nop_();
  78.     IapIdle();                                  //关闭IAP功能
  79. }
  80. void IapErase(int addr)
  81. {
  82.     IAP_CONTR = 0x80;                           //使能IAP
  83.     IAP_CMD = 3;                                //设置IAP擦除命令
  84.     IAP_ADDRL = addr;                           //设置IAP低地址
  85.     IAP_ADDRH = addr >> 8;                      //设置IAP高地址
  86.     IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
  87.     IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
  88.     _nop_();                                    //
  89.     IapIdle();                                  //关闭IAP功能
  90. }
  91. void Delay1000ms(void)        //@11.0592MHz
  92. {
  93.         unsigned char data i, j, k;
  94.         i = 57;
  95.         j = 27;
  96.         k = 112;
  97.         do
  98.         {
  99.                 do
  100.                 {
  101.                         while (--k);
  102.                 } while (--j);
  103.         } while (--i);
  104. }
  105. void main()
  106. {
  107.     unsigned char i;
  108.     unsigned char a[10];
  109.    
  110.     P0M1 = 0;   P0M0 = 0;   //设置为准双向口
  111.     P1M1 = 0;   P1M0 = 0;   //设置为准双向口
  112.     P2M1 = 0;   P2M0 = 0;   //设置为准双向口
  113.     P3M1 = 0;   P3M0 = 0;   //设置为准双向口
  114.     P4M1 = 0;   P4M0 = 0;   //设置为准双向口
  115.     P5M1 = 0;   P5M0 = 0;   //设置为准双向口
  116.    
  117.     IAP_TPS = 11;       //设置EEPROM操作等待参数(11.0592MHz),初始化设置一次即可
  118.     PrintfInit();
  119.         Delay1000ms();        //@11.0592MHz
  120.         Delay1000ms();        //@11.0592MHz
  121.     printf("Read1=");   //读取EEPROM原先的内容
  122.     for(i=0;i<10;i++)
  123.     {
  124.         a[i] = IapRead(OFFSET+i);
  125.         printf("0x%02bx ",a[i]);
  126.         if(a[i] == 0xff) a[i] = i;  //如果内容为空,则写入初始化数据
  127.         else a[i]++;    //如果内容非空,在原先基础上加1
  128.     }
  129.     printf("\r\n");
  130.     IapErase(OFFSET);   //如果擦写范围跨扇区,需要擦除两个扇区的空间
  131.     for(i=0;i<10;i++)
  132.     {
  133.         IapProgram(OFFSET+i, a[i]);
  134.     }
  135.     printf("Read2=");   //擦除、重写后,读取EEPROM现在的内容
  136.     for(i=0;i<10;i++)
  137.     {
  138.         a[i] = IapRead(OFFSET+i);
  139.         printf("0x%02bx ",a[i]);
  140.     }
  141.     printf("\r\n");
  142.     while (1);
  143. }
复制代码
经过多次测试,发现加长读函数的延时,即可解决,
改后的读函数如下:
  1. unsigned char IapRead(int addr)
  2. {
  3.     char dat;
  4.     IAP_CONTR = 0x80;                           //使能IAP
  5.     IAP_CMD = 1;                                //设置IAP读命令
  6.     IAP_ADDRL = addr;                           //设置IAP低地址
  7.     IAP_ADDRH = addr >> 8;                      //设置IAP高地址
  8.     IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
  9.     IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
  10.     _nop_();_nop_();
  11.     dat = IAP_DATA;                             //读IAP数据
  12.     IapIdle();                                  //关闭IAP功能
  13.     return dat;
  14. }
复制代码


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






回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:592
  • 最近打卡:2025-07-12 09:18:24

118

主题

2053

回帖

5981

积分

论坛元老

积分
5981
发表于 4 天前 | 显示全部楼层
在读函数中延时没有加长的情况下,测试时,可以将

#define     OFFSET          12     //EEPROM起始地址

中的12改为0

刚才没有改成0,有一次是没有问题的,然后改为0,又出现了问题
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:234
  • 最近打卡:2025-07-12 08:41:50
已绑定手机

76

主题

2119

回帖

4784

积分

荣誉版主

无情的代码机器

积分
4784
发表于 昨天 10:04 | 显示全部楼层
看AiCube生成的和8H手册里都是四个NOP。
1L这个例程可能时间长了没维护吧。使用AiCube一键生成~



截图202507111003219683.jpg

截图202507111003409530.jpg

三天不学习,赶不上刘少奇~
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:592
  • 最近打卡:2025-07-12 09:18:24

118

主题

2053

回帖

5981

积分

论坛元老

积分
5981
发表于 昨天 13:48 | 显示全部楼层
erci*** 发表于 2025-7-11 10:04
看AiCube生成的和8H手册里都是四个NOP。
1L这个例程可能时间长了没维护吧。使用AiCube一键生成~

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

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:40
  • 最近打卡:2025-07-12 09:26:11
已绑定手机

4

主题

10

回帖

190

积分

注册会员

积分
190
发表于 昨天 15:45 | 显示全部楼层
我一般直接读取地址。addr是eeprom多少字节,IAP_OFFSET是eeprom的基址
char IAP_Read_MOVC(int addr)        //读取EEPROM
{
        addr += IAP_OFFSET;
        return *(char code *)(addr);
}
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:592
  • 最近打卡:2025-07-12 09:18:24

118

主题

2053

回帖

5981

积分

论坛元老

积分
5981
发表于 昨天 16:20 | 显示全部楼层
_依*** 发表于 2025-7-11 15:45
我一般直接读取地址。addr是eeprom多少字节,IAP_OFFSET是eeprom的基址
char IAP_Read_MOVC(int addr)        //读 ...

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

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:592
  • 最近打卡:2025-07-12 09:18:24

118

主题

2053

回帖

5981

积分

论坛元老

积分
5981
发表于 昨天 18:03 | 显示全部楼层
_依*** 发表于 2025-7-11 15:45
我一般直接读取地址。addr是eeprom多少字节,IAP_OFFSET是eeprom的基址
char IAP_Read_MOVC(int addr)        //读 ...

请问eeprom的基址怎样计算?
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:125
  • 最近打卡:2025-07-12 08:36:49

755

主题

1万

回帖

1万

积分

管理员

积分
17824
发表于 昨天 22:20 | 显示全部楼层
截图202507112220006879.jpg

IAP 是从 0000H 开始
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:592
  • 最近打卡:2025-07-12 09:18:24

118

主题

2053

回帖

5981

积分

论坛元老

积分
5981
发表于 10 小时前 | 显示全部楼层
神*** 发表于 2025-7-11 22:20
IAP 是从 0000H 开始

好的,我试试
回复 支持 反对

使用道具 举报 送花

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

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-7-12 19:31 , Processed in 0.138997 second(s), 103 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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