找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 3580|回復(fù): 26
收起左側(cè)

C語言數(shù)據(jù)分拆存儲后讀出不正確

  [復(fù)制鏈接]
ID:965189 發(fā)表于 2023-9-7 21:54 | 顯示全部樓層 |閱讀模式
之前用這個方法將16位數(shù)分拆為8位數(shù)后存儲到片內(nèi),然后再讀出來顯示的時候“ 一部分 ”數(shù)據(jù)變了,也有一些是正確的。向各位請教正確的分拆和合并方法。
uint shu;                               //定義16位變量

IapErase(0x0000);                   //扇區(qū)擦除
IapProgram(0x0000,shu>>8);  //保存數(shù)據(jù)高8位
IapProgram(0x0001,shu);        //保存數(shù)據(jù)低8位

shu=IapRead(0x0000)<<8|IapRead(0x0001); //讀出保存的數(shù)據(jù)
回復(fù)

使用道具 舉報

ID:213173 發(fā)表于 2023-9-8 07:34 | 顯示全部樓層
上述代碼表面看沒有問題,易出錯可能是受中斷干擾或放在不恰當(dāng)?shù)奈恢,亦或相關(guān)讀寫的函數(shù)體有問題。
回復(fù)

使用道具 舉報

ID:123289 發(fā)表于 2023-9-8 08:32 | 顯示全部樓層
試試 = 高位*256 + 低位
回復(fù)

使用道具 舉報

ID:1079566 發(fā)表于 2023-9-8 09:33 | 顯示全部樓層
uint 如果真是16位的話,應(yīng)該沒錯。

或者,你可以考慮union
union
{
    unsigned shor int i;
    unsigned char x[2];
}shu;

寫讀 shu.x[0],shu.x[1], 用整體時,shu.i

}
回復(fù)

使用道具 舉報

ID:384109 發(fā)表于 2023-9-8 10:38 | 顯示全部樓層
看看錯誤和正確數(shù)據(jù)的規(guī)律,讀出數(shù)據(jù)合并時分開兩步走
回復(fù)

使用道具 舉報

ID:69038 發(fā)表于 2023-9-8 10:51 | 顯示全部樓層
shu=(IapRead(0x0000)<<8)|(IapRead(0x0001)); //讀出保存的數(shù)據(jù),這樣有試過嗎?
回復(fù)

使用道具 舉報

ID:965189 發(fā)表于 2023-9-8 15:52 | 顯示全部樓層
wulin 發(fā)表于 2023-9-8 07:34
上述代碼表面看沒有問題,易出錯可能是受中斷干擾或放在不恰當(dāng)?shù)奈恢,亦或相關(guān)讀寫的函數(shù)體有問題。

我在擦除前關(guān)中斷
EA=0;
IapErase(0x0000);

完成寫入后再恢復(fù)中斷
回復(fù)

使用道具 舉報

ID:965189 發(fā)表于 2023-9-8 15:55 | 顯示全部樓層
yzwzfyz 發(fā)表于 2023-9-8 08:32
試試 = 高位*256 + 低位

這方法也試過,不行。以前用24C02的時候用過這個方法是可以的。今次試過不行。
回復(fù)

使用道具 舉報

ID:965189 發(fā)表于 2023-9-8 15:58 | 顯示全部樓層
ZSJM 發(fā)表于 2023-9-8 09:33
uint 如果真是16位的話,應(yīng)該沒錯。

或者,你可以考慮union

這個未試過。謝謝你的建議,找時間試一下
回復(fù)

使用道具 舉報

ID:965189 發(fā)表于 2023-9-8 15:59 | 顯示全部樓層

這個試過了,問題依舊。
回復(fù)

使用道具 舉報

ID:1040201 發(fā)表于 2023-9-8 16:23 | 顯示全部樓層
&0xff取低8位
回復(fù)

使用道具 舉報

ID:1059013 發(fā)表于 2023-9-8 16:34 | 顯示全部樓層
應(yīng)該是第三行錯了,16位shu存入8位的話,自動四舍五入,其實存的還是高8位
回復(fù)

使用道具 舉報

ID:883242 發(fā)表于 2023-9-8 16:37 | 顯示全部樓層
  1. uint16_t shu;
  2. uint8_t *p;
  3. p=&shu;
  4. IapProgram(0x0000,*p++);
  5. IapProgram(0x0001,*p++);
  6. p=&shu;
  7. IapRead(0x0000,*p++); IapRead(0x0001,*p++);
復(fù)制代碼
回復(fù)

使用道具 舉報

ID:213173 發(fā)表于 2023-9-8 17:10 | 顯示全部樓層
君工創(chuàng) 發(fā)表于 2023-9-8 15:52
我在擦除前關(guān)中斷
EA=0;
IapErase(0x0000);

這是一款掉電中斷數(shù)據(jù)保存EEPROM的測試程序,非常可靠,相關(guān)代碼可以參考。
//測試環(huán)境:TX-1C實驗板,MCU型號IAP15W4K58S4,系統(tǒng)時鐘11.0592MHz
//注意:測試本示例時,需在ISP下載時將低壓復(fù)位功能和低壓時禁止EEPROM操作關(guān)閉
  1. #include <STC15F2K60S2.H>
  2. #include <intrins.h>                                //庫頭文件
  3. #define uint unsigned int                         //宏定義數(shù)據(jù)類型uint
  4. #define uchar unsigned char                 //宏定義數(shù)據(jù)類型uchar
  5. //宏定義ISP的操作命令
  6. #define CMD_IDLE    0               //空閑模式
  7. #define CMD_READ    1               //IAP字節(jié)讀命令
  8. #define CMD_PROGRAM 2               //IAP字節(jié)編程命令
  9. #define CMD_ERASE   3               //IAP扇區(qū)擦除命令
  10. #define ENABLE_IAP  0x82            //CPU的等待時間
  11. #define IAP_ADDRESS 0x0800                        //測試地址
  12. sbit duan=P2^6;
  13. sbit wein=P2^7;

  14. //順序共陰極數(shù)碼管段碼表,段碼a-h順序接PX0-PX7
  15. uchar code table[]={//共陰數(shù)碼管段碼"0~f-."
  16.                 0x3f,0x06,0x5b,0x4f,
  17.                 0x66,0x6d,0x7d,0x07,
  18.                 0x7f,0x6f,0x77,0x7c,
  19.                 0x39,0x5e,0x79,0x71,0x40,0x80};
  20. uchar data dis_buf[8];                //緩存數(shù)組
  21. uint num,sec;
  22. uchar i;

  23. void Timer0Init();                                        //定時器初始化聲明
  24. void IapIdle();                                                //關(guān)閉IAP/EEPROM
  25. uchar IapReadByte(uint addr);                //讀取EEPROM數(shù)據(jù)
  26. void IapProgramByte(uint addr, uchar dat);//寫入EEPROM數(shù)據(jù)
  27. void IapEraseSector(uint addr);                //擦除EEPROM數(shù)據(jù)

  28. void main()                                                       
  29. {
  30.         P0M0 = 0x00;
  31.         P0M1 = 0x00;
  32.         P1M0 = 0x00;
  33.         P1M1 = 0x00;
  34.         P2M0 = 0x00;
  35.         P2M1 = 0x00;
  36.         P3M0 = 0x00;
  37.         P3M1 = 0x00;
  38.         P4M0 = 0x00;
  39.         P4M1 = 0x00;
  40.         P5M0 = 0x00;
  41.         P5M1 = 0x00;
  42.         P6M0 = 0x00;
  43.         P6M1 = 0x00;
  44.         P7M0 = 0x00;
  45.         P7M1 = 0x00;
  46.         sec=IapReadByte(IAP_ADDRESS)<<8|IapReadByte(IAP_ADDRESS+1);//讀取保存的數(shù)據(jù) 用時11.75us
  47.         if(sec==0xffff)//如果沒有保存數(shù)據(jù)
  48.                 sec=0;//變量為0
  49.         else IapEraseSector(IAP_ADDRESS);//擦除數(shù)據(jù),為下次掉電保存數(shù)據(jù)做準(zhǔn)備

  50.         PCON &= 0xDF;//清0掉電標(biāo)志
  51.         ELVD = 1;//開低壓中斷
  52.         EA   = 1;//開總中斷

  53.         Timer0Init();//初始化定時器

  54.         while(1)
  55.         {
  56.                 if(TF0)//查詢T0中斷請求標(biāo)志
  57.                 {               
  58.                         TF0=0;//T0中斷請求標(biāo)志清0
  59.                         if(++num>=1000)//1秒
  60.                         {
  61.                                 num=0;                               
  62.                                 sec++;
  63.                         }
  64.                         dis_buf[0]=table[sec/10000%10];
  65.                         dis_buf[1]=table[sec/1000%10];
  66.                         dis_buf[2]=table[sec/100%10];
  67.                         dis_buf[3]=table[sec/10%10];
  68.                         dis_buf[4]=table[sec%10];

  69.                         P0=0x00;duan=1;duan=0;
  70.                         P0=~(0x01<<i);wein=1;wein=0;
  71.                         P0=dis_buf[i];duan=1;duan=0;
  72.                         i=++i%5;
  73.                 }
  74.         }
  75. }

  76. void Timer0Init(void)        //1毫秒@11.0592MHz
  77. {
  78.         AUXR |= 0x80;                //定時器時鐘1T模式
  79.         TMOD &= 0xF0;                //設(shè)置定時器模式
  80.         TL0 = 0xCD;                        //設(shè)置定時初始值
  81.         TH0 = 0xD4;                        //設(shè)置定時初始值
  82.         TF0 = 0;                        //清除TF0標(biāo)志
  83.         TR0 = 1;                        //定時器0開始計時
  84. }


  85. /*----------------------------
  86.         關(guān)閉IAP功能
  87. ----------------------------*/
  88. void IapIdle()
  89. {
  90.     IAP_CONTR = 0;                  //關(guān)閉IAP功能
  91.     IAP_CMD = 0;                    //清除命令寄存器
  92.     IAP_TRIG = 0;                   //清除觸發(fā)寄存器
  93.     IAP_ADDRH = 0x80;               //將地址設(shè)置到非IAP區(qū)域
  94.     IAP_ADDRL = 0;
  95. }
  96. /*----------------------------
  97. 從ISP/IAP/EEPROM區(qū)域讀取一字節(jié)
  98. ----------------------------*/
  99. uchar IapReadByte(uint addr)
  100. {
  101.     uchar dat;                       //數(shù)據(jù)緩沖區(qū)

  102.     IAP_CONTR = ENABLE_IAP;         //使能IAP
  103.     IAP_CMD = CMD_READ;             //設(shè)置IAP命令
  104.     IAP_ADDRL = addr;               //設(shè)置IAP低地址
  105.     IAP_ADDRH = addr >> 8;          //設(shè)置IAP高地址
  106.     IAP_TRIG = 0x5a;                //寫觸發(fā)命令(0x5a)
  107.     IAP_TRIG = 0xa5;                //寫觸發(fā)命令(0xa5)
  108.     _nop_();                        //等待ISP/IAP/EEPROM操作完成
  109.     dat = IAP_DATA;                 //讀ISP/IAP/EEPROM數(shù)據(jù)
  110.     IapIdle();                      //關(guān)閉IAP功能
  111.     return dat;                     //返回
  112. }
  113. /*-------------------------------
  114. 寫一字節(jié)數(shù)據(jù)到ISP/IAP/EEPROM區(qū)域
  115. --------------------------------*/
  116. void IapProgramByte(uint addr, uchar dat)
  117. {
  118.     IAP_CONTR = ENABLE_IAP;         //使能IAP
  119.     IAP_CMD = CMD_PROGRAM;          //設(shè)置IAP命令
  120.     IAP_ADDRL = addr;               //設(shè)置IAP低地址
  121.     IAP_ADDRH = addr >> 8;          //設(shè)置IAP高地址
  122.     IAP_DATA = dat;                 //寫ISP/IAP/EEPROM數(shù)據(jù)
  123.     IAP_TRIG = 0x5a;                //寫觸發(fā)命令(0x5a)
  124.     IAP_TRIG = 0xa5;                //寫觸發(fā)命令(0xa5)
  125.     _nop_();                        //等待ISP/IAP/EEPROM操作完成
  126.     IapIdle();                      //關(guān)閉IAP功能
  127. }
  128. /*----------------------------
  129. ISP/IAP/EEPROM扇區(qū)擦除
  130. ----------------------------*/
  131. void IapEraseSector(uint addr)
  132. {
  133.     IAP_CONTR = ENABLE_IAP;         //使能IAP
  134.     IAP_CMD = CMD_ERASE;            //設(shè)置IAP命令
  135.     IAP_ADDRL = addr;               //設(shè)置IAP低地址
  136.     IAP_ADDRH = addr >> 8;          //設(shè)置IAP高地址
  137.     IAP_TRIG = 0x5a;                //寫觸發(fā)命令(0x5a)
  138.     IAP_TRIG = 0xa5;                //寫觸發(fā)命令(0xa5)
  139.     _nop_();                        //等待ISP/IAP/EEPROM操作完成
  140.     IapIdle();                      //關(guān)閉IAP功能
  141. }
  142. void PowerLost() interrupt 6                //剩余電量從中斷開始到完全斷電2.5ms
  143. {
  144.         EA = 0;                                                //關(guān)閉總中斷
  145.         P0M1 = 0xff;                                //所有端口高阻用時2.75us
  146.         P1M1 = 0xff;
  147.         P2M1 = 0xff;
  148.         P3M1 = 0xff;
  149.         P4M1 = 0xff;
  150.         P5M1 = 0xff;
  151.         P6M1 = 0xff;
  152.         P7M1 = 0xff;
  153.         IapProgramByte(IAP_ADDRESS,sec>>8);//寫數(shù)據(jù)高8位到EEPROM
  154.         IapProgramByte(IAP_ADDRESS+1,sec);//寫數(shù)據(jù)低8位到EEPROM  寫兩個字節(jié)用時215.25us

  155.         while((PCON & 0x20) != 0)         //復(fù)查低壓標(biāo)志
  156.         {
  157.                 PCON &= 0xDF;                  //清除低壓標(biāo)志
  158.                 _nop_();               
  159.                 _nop_();                            //坐等掉電
  160.         }
  161.         IAP_CONTR = 0x20;                 //發(fā)現(xiàn)是誤報,重啟單片機(jī),恢復(fù)正常工作
復(fù)制代碼
回復(fù)

使用道具 舉報

ID:965189 發(fā)表于 2023-9-8 22:41 | 顯示全部樓層
fishafish 發(fā)表于 2023-9-8 16:34
應(yīng)該是第三行錯了,16位shu存入8位的話,自動四舍五入,其實存的還是高8位

這個講法有可能,因為某些數(shù)會錯,而一些數(shù)不會錯。
回復(fù)

使用道具 舉報

ID:965189 發(fā)表于 2023-9-8 23:01 | 顯示全部樓層

請教大俠,這一行,IapRead(0x0000,*p++); IapRead(0x0001,*p++);
讀出數(shù)據(jù)不是讀出來重新組合到shu嗎?為什么又讀到內(nèi)存里了。
回復(fù)

使用道具 舉報

ID:491577 發(fā)表于 2023-9-8 23:29 | 顯示全部樓層
數(shù)據(jù)溢出了,shu=IapRead(0x0000)<<8|IapRead(0x0001); 其中IapRead(0x0000)<<8有問題,IapRead(0x0000)是8位的,左移數(shù)據(jù)會溢出。要強(qiáng)制數(shù)據(jù)轉(zhuǎn)換:shu=(uint)IapRead(0x0000)<<8|IapRead(0x0001);
回復(fù)

使用道具 舉報

ID:491577 發(fā)表于 2023-9-8 23:45 | 顯示全部樓層
單片機(jī)很容易發(fā)生數(shù)據(jù)溢出錯誤,我就碰到過,怎么查找都不知道原因,特別是運(yùn)算中間的溢出更加難查,比如16位數(shù)據(jù)*100/200,結(jié)果一定小于16位,但是這樣寫a=b*100/200;是不對的,這樣才對a=b/200*100;先乘100可能會數(shù)據(jù)溢出,不小心很容易中招。
回復(fù)

使用道具 舉報

ID:1041851 發(fā)表于 2023-9-9 09:34 | 顯示全部樓層
Keil處理復(fù)雜的計算式時容易出現(xiàn)一些奇怪問題
將"shu=IapRead(0x0000)<<8|IapRead(0x0001); //讀出保存的數(shù)據(jù)"
這句拆分成兩步試試:
shu = apRead(0x0000) << 8;

shu |= IapRead(0x0001);

也可將每步的結(jié)果打印出來看看哪里不對

回復(fù)

使用道具 舉報

ID:304306 發(fā)表于 2023-9-9 09:48 | 顯示全部樓層
用聯(lián)合體(uion)做方便,不用考慮這么多
回復(fù)

使用道具 舉報

ID:965189 發(fā)表于 2023-9-9 12:33 | 顯示全部樓層
2008_clz 發(fā)表于 2023-9-9 09:48
用聯(lián)合體(uion)做方便,不用考慮這么多

請教:具體怎么寫。
回復(fù)

使用道具 舉報

ID:965189 發(fā)表于 2023-9-9 15:09 | 顯示全部樓層
在以上各位大俠的指教下,問題已經(jīng)解決。多謝各位的熱心幫助。問題出在讀出組合環(huán)節(jié)。
回復(fù)

使用道具 舉報

ID:883242 發(fā)表于 2023-9-9 17:25 | 顯示全部樓層
不要去運(yùn)算這個數(shù),知道地址組合數(shù)據(jù)即可。
回復(fù)

使用道具 舉報

ID:1059013 發(fā)表于 2023-9-9 19:40 | 顯示全部樓層
君工創(chuàng) 發(fā)表于 2023-9-8 22:41
這個講法有可能,因為某些數(shù)會錯,而一些數(shù)不會錯。

好像還與不同編譯軟件有關(guān),我遇到PIC編譯軟件是會得到意外結(jié)果的
回復(fù)

使用道具 舉報

ID:77589 發(fā)表于 2023-9-12 15:08 | 顯示全部樓層
hhh402 發(fā)表于 2023-9-8 23:29
數(shù)據(jù)溢出了,shu=IapRead(0x0000)

我認(rèn)為也是這個問題
回復(fù)

使用道具 舉報

ID:77589 發(fā)表于 2023-9-12 15:16 | 顯示全部樓層

方法可行,但是后兩句錯了
uint16_t shu;
uint8_t *p;
p = &shu;
IapProgram(0x0000, *p);
p++;
IapProgram(0x0001, *p);
p = &shu;
*p = IapRead(0x0000);
p++;
*p = IapRead(0x0001);
把p++單獨(dú)提出來寫,所有人都好理解。而且寫與讀都只需要一次p++。
讀取后的數(shù)據(jù)自動組合到shu中了。
回復(fù)

使用道具 舉報

ID:883242 發(fā)表于 2023-9-13 14:32 | 顯示全部樓層
Longan.Wang 發(fā)表于 2023-9-12 15:16
方法可行,但是后兩句錯了
uint16_t shu;
uint8_t *p;

我用MinGW_w64 13.0驗證無誤,當(dāng)然你水平難以理解改成你能理解的方式?jīng)]問題,錯了你有何依據(jù)?不要憑空想象。
回復(fù)

使用道具 舉報

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

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

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

快速回復(fù) 返回頂部 返回列表