找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

帖子
查看: 3016|回復: 6
收起左側

stc15f2k60s2單片機eeprom存取程序問題求助

[復制鏈接]
ID:842740 發(fā)表于 2021-4-7 00:46 | 顯示全部樓層 |閱讀模式
本人小白,初學單片機
測試數(shù)據(jù)根據(jù)菜單設置保存,上電后數(shù)據(jù)為上次菜單所保存的,
現(xiàn)遇到此問題,運行時候數(shù)據(jù)設置沒有問題,掉電重新上電后發(fā)現(xiàn)數(shù)據(jù)取出后第一個數(shù)據(jù)是第三個地址上面的,第二個數(shù)據(jù)是第一個地址上面的,第三個數(shù)據(jù)是第二個地址上面的。單獨存取一個地址的就沒問題,疑惑中?????
部分代碼

擦除、讀、寫等函數(shù)
void  cachu(uchar addrH,uchar addrL) //擦除指令
{      
IAP_ADDRL = addrL;  // ISP/IAP操作時的地址寄存器低八位,
    IAP_ADDRH = addrH;  // ISP/IAP操作時的地址寄存器高八位
    IAP_CONTR = 0x84;  
    IAP_CMD   = 0x03; // 用戶可以對"Data Flash/EEPROM區(qū)"進行扇區(qū)擦除
     EA =0;   
    IAP_TRIG  = 0x5A;         
    IAP_TRIG  = 0xA5;         
    _nop_();
     EA = 1;
    guanbi_IAP();     // 關閉ISP/IAP

}
/////////////////////////////////////////////////////////////////////////////////
void xie(uchar addrH,uchar addrL, xxx)   //寫
{
IAP_ADDRL = addrL;        
    IAP_ADDRH = addrH;  
    IAP_CONTR = 0x84;                  
    IAP_CMD   = 0x02;              // 用戶可以對"Data Flash/EEPROM區(qū)"進行字節(jié)編程        
    EA = 0;
    IAP_TRIG  = 0x5A;         
    IAP_TRIG  = 0xA5;         
    IAP_DATA  = xxx;          // 數(shù)據(jù)進ISP_DATA
    _nop_();
   EA = 1;
   guanbi_IAP();                                          // 關閉ISP/IAP
}

uchar du(uchar addrH,uchar addrL)  //讀
{   
IAP_ADDRL = addrL;         
    IAP_ADDRH = addrH;  
    IAP_CONTR = 0x84;                  
    IAP_CMD   = 0x01;         // 用戶可以對"Data Flash/EEPROM區(qū)"進行字節(jié)讀
IAP_TRIG  = 0x5A;         
    IAP_TRIG  = 0xA5;     
    _nop_();
    guanbi_IAP();                                          // 關閉ISP/IAP                  
    return IAP_DATA;
}

///////////////////////////////////////////////////////////////////////////////////



/////////////////////////////////////////////////////////////////////////////////////////////
void guanbi_IAP()
{

    IAP_CONTR = 0;      //關閉IAP 功能
    IAP_CMD   = 0;      //清命令寄存器,使命令寄存器無命令,此句可不用
    IAP_TRIG = 0;      //清命令觸發(fā)寄存器,使命令觸發(fā)寄存器無觸發(fā),此句可不用
    IAP_ADDRH = 0;
    IAP_ADDRL = 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////


菜單設置函數(shù)
if(open==0)
    {
    if(anjianxiaodou<600)
    {
   mk=3;
    if(mk>130){mk=1;}
    anjianxiaodou=700;
    }
    cachu(0,0);  // 擦除第1個扇區(qū)
    xxx=mk;
    xie(0,0,xxx);
    xxx=tjs;
    xie(0,1,xxx);
    xxx=wjs;
    xie(0,2,xxx);          // 對EEPROM區(qū)寫入

   }
   if(close==0)
    {
    if(anjianxiaodou<600)
    {
    mk=3;
    if(mk<1){mk=130;}
    anjianxiaodou=700;
    }
    cachu(0,0);  // 擦除第1個扇區(qū)
      xxx=mk;
      xie(0,0,xxx);
    xxx=tjs;
    xie(0,1,xxx);
    xxx=wjs;
    xie(0,2,xxx);                 // 對EEPROM區(qū)2002h寫入

   }
   }
  
   break;
  }
  case 2:
  { if(xiajibiaozhi==0)
   {
   qian=15;
   bai=19;
   shi=0;
   ge=1;
   shumaguanxianshi();
     }
     if(caidan==0&&anjianxiaodou>500&&anjianxiaodou<600)
   {  
    xiajibiaozhi=~xiajibiaozhi;
    anjianxiaodou=700;
   }
   if(xiajibiaozhi==1)
   {
    qian=tjs/1000;
    bai=((tjs%1000)/100);
    shi=(((tjs%1000)%100)/10);
    ge=((((tjs%1000)%100)%10)/1);
    shumaguanxianshi();
    if(open==0)
     {
      if(anjianxiaodou<600)
      {
      tjs=6;
      if(tjs>15){tjs=1;}
      anjianxiaodou=700;
      }
    cachu(0,1);  // 擦除第1個扇區(qū)
    xxx=mk;
    xie(0,0,xxx);
    xxx=tjs;
    xie(0,1,xxx);
    xxx=wjs;
    xie(0,2,xxx);          // 對EEPROM區(qū)寫入

    }
   if(close==0)
    {
     if(anjianxiaodou<600)
      {
      tjs=6;
      if(tjs<1){tjs=15;}
      anjianxiaodou=700;
      }
   cachu(0,1);  // 擦除第1個扇區(qū)
     xxx=mk;
      xie(0,0,xxx);
    xxx=tjs;
    xie(0,1,xxx);
    xxx=wjs;
    xie(0,2,xxx);          // 對EEPROM區(qū)寫入

   }
   }
    break;
  }
    case 3:
  {
  if(xiajibiaozhi==0)
   {
   qian=15;
   bai=19;
   shi=0;
   ge=2;
   shumaguanxianshi();
     }
     if(caidan==0&&anjianxiaodou>500&&anjianxiaodou<600)
   {  
    xiajibiaozhi=~xiajibiaozhi;
    anjianxiaodou=700;
   }
   if(xiajibiaozhi==1)
   {
    qian=wjs/1000;
    bai=((wjs%1000)/100);
    shi=(((wjs%1000)%100)/10);
    ge=((((wjs%1000)%100)%10)/1);
    shumaguanxianshi();
    if(open==0)
     {
      if(anjianxiaodou<600)
      {
     wjs=9;
      if(wjs>15){wjs=1;}
      anjianxiaodou=700;
      }
      cachu(0,2);  // 擦除第1個扇區(qū)
       xxx=mk;
      xie(0,0,xxx);
    xxx=tjs;
    xie(0,1,xxx);
    xxx=wjs;
    xie(0,2,xxx);          // 對EEPROM區(qū)寫入

    }
   if(close==0)
    {
     if(anjianxiaodou<600)
      {
     wjs=9;
      if(wjs<1){wjs=15;}
      anjianxiaodou=700;
      }
    cachu(0,2);  // 擦除第1個扇區(qū)
      xxx=mk;
      xie(0,0,xxx);
    xxx=tjs;
    xie(0,1,xxx);
    xxx=wjs;
    xie(0,2,xxx);          // 對EEPROM區(qū)寫入

   }
   }
    break;


上電main函數(shù)直接根據(jù)地址讀出
void main()
{ i=0;y=0;
led=0;
hrzhi= P0 & 0x07 ;
hezhi1=hrzhi;
mk=du(0,0);
tjs=du(0,1);
wjs=du(0,2);

maichong=0;

//////////////////////
讀出來的三個數(shù)據(jù)是相互錯的,數(shù)值不錯,位置錯。



回復

使用道具 舉報

ID:213173 發(fā)表于 2021-4-7 07:00 | 顯示全部樓層
stc15系列EEPROM一個扇區(qū)512字節(jié)。第一扇區(qū)首地址0x0000,第二扇區(qū)首地址0x0200,第三扇區(qū)首地址0x0400,第四.........。
寫數(shù)據(jù)前必須以此扇區(qū)的首地址開始擦除整個扇區(qū)。再按指定扇區(qū)內任意地址寫數(shù)據(jù),通常是按扇區(qū)首地址順序寫數(shù)據(jù)。樓主把地址參數(shù)分開寫也沒啥毛病,但對第1個扇區(qū)的擦除在任何情況都應該這樣寫成cachu(0,0);  其它cachu(0,1); cachu(0,2);寫法都是錯誤的。

評分

參與人數(shù) 1黑幣 +20 收起 理由
admin + 20 回帖助人的獎勵!

查看全部評分

回復

使用道具 舉報

ID:57657 發(fā)表于 2021-4-7 07:44 | 顯示全部樓層
寫eeprom,1可以寫成0,0不能寫成1。
擦除扇區(qū)可以將0擦除成1,一次擦除512字節(jié)。
只有扇區(qū)擦除,沒有字節(jié)擦除。

另外讀eeprom可以改成:
  1. uchar code  *eeprom = 0xF000;
復制代碼

du() 函數(shù)可以注釋不要。

評分

參與人數(shù) 1黑幣 +20 收起 理由
admin + 20 回帖助人的獎勵!

查看全部評分

回復

使用道具 舉報

ID:842740 發(fā)表于 2021-4-7 19:47 | 顯示全部樓層
本帖最后由 18668938020 于 2021-4-7 19:58 編輯
wulin 發(fā)表于 2021-4-7 07:00
stc15系列EEPROM一個扇區(qū)512字節(jié)。第一扇區(qū)首地址0x0000,第二扇區(qū)首地址0x0200,第三扇區(qū)首地址0x0400,第 ...

嗯,明白了,但是還是這個問題弄不明白
程序內部進入菜單1或2或3
改變mk或tjs或wjs變量后執(zhí)行以下:
cachu(0,0);           // 擦除第1個扇區(qū)
      xxx=mk;         //把mk賦值給XXX
      xie(0,0,xxx);   //將XXX也就是mk值寫入到0x0000地址里面
     xxx=tjs;          //然后把tjs賦值給XXX
     xie(0,1,xxx);    //將XXX也就是tjs值寫入到0x0001地址里面
     xxx=wjs;         //然后把wjs賦值給XXX
     xie(0,2,xxx);    //將XXX也就是wjs值寫入到0x0002地址里面



main函數(shù)里面
mk=du(0,0);
tjs=du(0,1);
wjs=du(0,2);
讀出數(shù)據(jù)來后
mk設定值在0x01地址里面
tjs設定值在0x02地址里面
wjs設定值在0x00地址里面


不知哪一步出的錯



回復

使用道具 舉報

ID:213173 發(fā)表于 2021-4-7 21:16 | 顯示全部樓層
18668938020 發(fā)表于 2021-4-7 19:47
嗯,明白了,但是還是這個問題弄不明白
程序內部進入菜單1或2或3
改變mk或tjs或wjs變量后執(zhí)行以下:

這是基于STC官方例程改寫的多字節(jié)EEPROM連續(xù)讀寫測試程序,可以對照查錯。對于int型數(shù)據(jù)可以拆為兩個char型數(shù)據(jù)保存,讀取后再合并為一個int型數(shù)據(jù)。
  1. //測試EEPROM多字節(jié)讀寫,串口發(fā)送數(shù)據(jù)。
  2. #include <STC15F2K60S2.H>
  3. #include <intrins.h>
  4. #define uint unsigned int                         //宏定義無符號整型數(shù)據(jù)
  5. #define uchar unsigned char                        //宏定義無符號字符型數(shù)據(jù)
  6. //----------宏定義ISP的操作命令---------------------
  7. #define CMD_IDLE    0               //空閑模式
  8. #define CMD_READ    1               //IAP字節(jié)讀命令
  9. #define CMD_PROGRAM 2               //IAP字節(jié)編程命令
  10. #define CMD_ERASE   3               //IAP扇區(qū)擦除命令
  11. //----------宏定義定時器2作為波特率發(fā)生器------------------
  12. #define     URMD    1               //0:使用定時器2作為波特率發(fā)生器
  13.                                     //1:使用定時器1的模式0(16位自動重載模式)作為波特率發(fā)生器
  14.                                     //2:使用定時器1的模式2(8位自動重載模式)作為波特率發(fā)生器
  15. /***************CPU的等待時間******************/
  16. //#define ENABLE_IAP 0x80           //if SYSCLK<30MHz
  17. //#define ENABLE_IAP 0x81           //if SYSCLK<24MHz
  18. #define ENABLE_IAP  0x82            //if SYSCLK<20MHz
  19. //#define ENABLE_IAP 0x83           //if SYSCLK<12MHz
  20. //#define ENABLE_IAP 0x84           //if SYSCLK<6MHz
  21. //#define ENABLE_IAP 0x85           //if SYSCLK<3MHz
  22. //#define ENABLE_IAP 0x86           //if SYSCLK<2MHz
  23. //#define ENABLE_IAP 0x87           //if SYSCLK<1MHz

  24. sbit key=P3^4;                                                                //按鍵1端口定義       

  25. uchar table1[12]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C};//測試數(shù)據(jù)
  26. uchar table2[12];                                        //讀取EEPROM緩存
  27. uchar a=0;
  28. /*********************子函數(shù)聲明********************/
  29. void IapIdle();                                                                        //關閉IAP功能
  30. uchar IapReadByte(uint addr);                                        //讀取一個字節(jié)
  31. void IapProgramByte(uint addr, uchar dat);                //寫一個字節(jié)
  32. void IapEraseSector(uint addr);                                        //ISP/IAP/EEPROM扇區(qū)擦除
  33. void InitUart();                                                                //初始化串口
  34. void SendData(uchar dat);                                                //發(fā)送串口數(shù)據(jù)
  35. void keyscan();
  36. void Delay10ms();

  37. void main()                                                                                //主函數(shù)
  38. {                                                                       
  39.         uchar j;
  40.         InitUart();                                //初始化串口
  41.         while(1)
  42.         {
  43.                 keyscan();                                //按鍵程序
  44.                 if(a==1)
  45.                 {
  46.                         for(j=0;j<12;j++)
  47.                         {
  48.                                 SendData(table2[j]);//串口發(fā)送
  49.                         }
  50.                         a=0;
  51.                 }
  52.         }
  53. }

  54. void keyscan()                                        //按鍵掃描程序
  55. {
  56.         uchar i;                                                //臨時變量
  57.         if(key==0)
  58.         {
  59.                 Delay10ms();
  60.                 if(key==0)
  61.                 {
  62.                         IapEraseSector(0x0000);//擦除EEPROM扇區(qū)中的數(shù)據(jù)
  63.                         for(i=0;i<12;i++)
  64.                         {
  65.                                 IapProgramByte(0x0000+i,table1[i]);//重新寫入密碼
  66.                         }
  67.                         for(i=0;i<12;i++)
  68.                         {
  69.                                 table2[i]=IapReadByte(0x0000+i);//讀取EEPROM中數(shù)據(jù)保存在table2
  70.                         }
  71.                         while(!key)//等待按鍵抬起
  72.                         a=1;
  73.                 }
  74.         }
  75. }
  76. /*----------------------------
  77.         關閉IAP功能
  78. ----------------------------*/
  79. void IapIdle()
  80. {
  81.     IAP_CONTR = 0;                  //關閉IAP功能
  82.     IAP_CMD = 0;                    //清除命令寄存器
  83.     IAP_TRIG = 0;                   //清除觸發(fā)寄存器
  84.     IAP_ADDRH = 0x80;               //將地址設置到非IAP區(qū)域
  85.     IAP_ADDRL = 0;
  86. }
  87. /*----------------------------
  88. 從ISP/IAP/EEPROM區(qū)域讀取一字節(jié)
  89. ----------------------------*/
  90. uchar IapReadByte(uint addr)
  91. {
  92.     uchar dat;                       //數(shù)據(jù)緩沖區(qū)

  93.     IAP_CONTR = ENABLE_IAP;         //使能IAP
  94.     IAP_CMD = CMD_READ;             //設置IAP命令
  95.     IAP_ADDRL = addr;               //設置IAP低地址
  96.     IAP_ADDRH = addr >> 8;          //設置IAP高地址
  97.     IAP_TRIG = 0x5a;                //寫觸發(fā)命令(0x5a)
  98.     IAP_TRIG = 0xa5;                //寫觸發(fā)命令(0xa5)
  99.     _nop_();                        //等待ISP/IAP/EEPROM操作完成
  100.     dat = IAP_DATA;                 //讀ISP/IAP/EEPROM數(shù)據(jù)
  101.     IapIdle();                      //關閉IAP功能

  102.     return dat;                     //返回
  103. }
  104. /*-------------------------------
  105. 寫一字節(jié)數(shù)據(jù)到ISP/IAP/EEPROM區(qū)域
  106. -------------------------------*/
  107. void IapProgramByte(uint addr, uchar dat)
  108. {
  109.     IAP_CONTR = ENABLE_IAP;         //使能IAP
  110.     IAP_CMD = CMD_PROGRAM;          //設置IAP命令
  111.     IAP_ADDRL = addr;               //設置IAP低地址
  112.     IAP_ADDRH = addr >> 8;          //設置IAP高地址
  113.     IAP_DATA = dat;                 //寫ISP/IAP/EEPROM數(shù)據(jù)
  114.     IAP_TRIG = 0x5a;                //寫觸發(fā)命令(0x5a)
  115.     IAP_TRIG = 0xa5;                //寫觸發(fā)命令(0xa5)
  116.     _nop_();                        //等待ISP/IAP/EEPROM操作完成
  117.     IapIdle();                      //關閉IAP功能
  118. }
  119. /*----------------------------
  120. ISP/IAP/EEPROM扇區(qū)擦除
  121. ----------------------------*/
  122. void IapEraseSector(uint addr)
  123. {
  124.     IAP_CONTR = ENABLE_IAP;         //使能IAP
  125.     IAP_CMD = CMD_ERASE;            //設置IAP命令
  126.     IAP_ADDRL = addr;               //設置IAP低地址
  127.     IAP_ADDRH = addr >> 8;          //設置IAP高地址
  128.     IAP_TRIG = 0x5a;                //寫觸發(fā)命令(0x5a)
  129.     IAP_TRIG = 0xa5;                //寫觸發(fā)命令(0xa5)
  130.     _nop_();                        //等待ISP/IAP/EEPROM操作完成
  131.     IapIdle();                      //關閉IAP功能
  132. }
  133. /*-------------------------------------
  134. 初始化串口 晶振18.432MHz 波特率115200
  135. --------------------------------------*/
  136. void InitUart()
  137. {
  138.         SCON = 0x5a;                //設置串口為8位可變波特率
  139. #if URMD == 0
  140.         T2L = 0xd8;                 //設置波特率重裝值
  141.         T2H = 0xff;                 //115200 bps(65536-18432000/4/115200)
  142.         AUXR = 0x14;                //T2為1T模式, 并啟動定時器2
  143.         AUXR |= 0x01;               //選擇定時器2為串口1的波特率發(fā)生器
  144. #elif URMD == 1
  145.         AUXR = 0x40;                //定時器1為1T模式
  146.         TMOD = 0x00;                //定時器1為模式0(16位自動重載)
  147.         TL1 = 0xd8;                 //設置波特率重裝值
  148.         TH1 = 0xff;                 //115200 bps(65536-18432000/4/115200)
  149.         TR1 = 1;                    //定時器1開始啟動
  150. #else
  151.         TMOD = 0x20;                //設置定時器1為8位自動重裝載模式
  152.         AUXR = 0x40;                //定時器1為1T模式
  153.         TH1 = TL1 = 0xfb;           //115200 bps(256 - 18432000/32/115200)
  154.         TR1 = 1;
  155. #endif
  156. }
  157. /*----------------------------
  158. 發(fā)送串口數(shù)據(jù)
  159. ----------------------------*/

  160. void SendData(uchar dat)
  161. {
  162.     while (!TI);                 //等待前一個數(shù)據(jù)發(fā)送完成
  163.     TI = 0;                      //清除發(fā)送標志
  164.     SBUF = dat;                  //發(fā)送當前數(shù)據(jù)
  165. }
  166. /*-------------------------------
  167.   10ms延時子程序(@18.432MHz)
  168. -------------------------------*/
  169. void Delay10ms()
  170. {
  171.         unsigned char i, j, k;

  172.         _nop_();
  173.         _nop_();
  174.         i = 1;
  175.         j = 180;
  176.         k = 71;
  177.         do
  178.         {
  179.                 do
  180.                 {
  181.                         while (--k);
  182.                 } while (--j);
  183.         } while (--i);
  184. }




復制代碼



回復

使用道具 舉報

ID:842740 發(fā)表于 2021-4-8 20:39 | 顯示全部樓層
問題已解決,原因是讀寫子程序里面應該先寫IAP_CONTR 和IAP_CMD ,再寫地址,顛倒后相當于把設置數(shù)值送到下一個地址了,謝謝以上兩位老師指導
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復 返回頂部 返回列表