找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1631|回复: 26
收起左侧

C语言数据分拆存储后读出不正确

  [复制链接]
ID:965189 发表于 2023-9-7 21:54 | 显示全部楼层 |阅读模式
之前用这个方法将16位数分拆为8位数后存储到片内,然后再读出来显示的时候“ 一部分 ”数据变了,也有一些是正确的。向各位请教正确的分拆和合并方法。
uint shu;                               //定义16位变量

IapErase(0x0000);                   //扇区擦除
IapProgram(0x0000,shu>>8);  //保存数据高8位
IapProgram(0x0001,shu);        //保存数据低8位

shu=IapRead(0x0000)<<8|IapRead(0x0001); //读出保存的数据
回复

使用道具 举报

ID:213173 发表于 2023-9-8 07:34 | 显示全部楼层
上述代码表面看没有问题,易出错可能是受中断干扰或放在不恰当的位置,亦或相关读写的函数体有问题。
回复

使用道具 举报

ID:123289 发表于 2023-9-8 08:32 | 显示全部楼层
试试 = 高位*256 + 低位
回复

使用道具 举报

ID:1079566 发表于 2023-9-8 09:33 | 显示全部楼层
uint 如果真是16位的话,应该没错。

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

写读 shu.x[0],shu.x[1], 用整体时,shu.i

}
回复

使用道具 举报

ID:384109 发表于 2023-9-8 10:38 | 显示全部楼层
看看错误和正确数据的规律,读出数据合并时分开两步走
回复

使用道具 举报

ID:69038 发表于 2023-9-8 10:51 | 显示全部楼层
shu=(IapRead(0x0000)<<8)|(IapRead(0x0001)); //读出保存的数据,这样有试过吗?
回复

使用道具 举报

ID:965189 发表于 2023-9-8 15:52 | 显示全部楼层
wulin 发表于 2023-9-8 07:34
上述代码表面看没有问题,易出错可能是受中断干扰或放在不恰当的位置,亦或相关读写的函数体有问题。

我在擦除前关中断
EA=0;
IapErase(0x0000);

完成写入后再恢复中断
回复

使用道具 举报

ID:965189 发表于 2023-9-8 15:55 | 显示全部楼层
yzwzfyz 发表于 2023-9-8 08:32
试试 = 高位*256 + 低位

这方法也试过,不行。以前用24C02的时候用过这个方法是可以的。今次试过不行。
回复

使用道具 举报

ID:965189 发表于 2023-9-8 15:58 | 显示全部楼层
ZSJM 发表于 2023-9-8 09:33
uint 如果真是16位的话,应该没错。

或者,你可以考虑union

这个未试过。谢谢你的建议,找时间试一下
回复

使用道具 举报

ID:965189 发表于 2023-9-8 15:59 | 显示全部楼层
zhuls 发表于 2023-9-8 10:51
shu=(IapRead(0x0000)

这个试过了,问题依旧。
回复

使用道具 举报

ID:1040201 发表于 2023-9-8 16:23 | 显示全部楼层
&0xff取低8位
回复

使用道具 举报

ID:1059013 发表于 2023-9-8 16:34 | 显示全部楼层
应该是第三行错了,16位shu存入8位的话,自动四舍五入,其实存的还是高8位
回复

使用道具 举报

ID:883242 发表于 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++);
复制代码
回复

使用道具 举报

ID:213173 发表于 2023-9-8 17:10 | 显示全部楼层
君工创 发表于 2023-9-8 15:52
我在擦除前关中断
EA=0;
IapErase(0x0000);

这是一款掉电中断数据保存EEPROM的测试程序,非常可靠,相关代码可以参考。
//测试环境:TX-1C实验板,MCU型号IAP15W4K58S4,系统时钟11.0592MHz
//注意:测试本示例时,需在ISP下载时将低压复位功能和低压时禁止EEPROM操作关闭
  1. #include <STC15F2K60S2.H>
  2. #include <intrins.h>                                //库头文件
  3. #define uint unsigned int                         //宏定义数据类型uint
  4. #define uchar unsigned char                 //宏定义数据类型uchar
  5. //宏定义ISP的操作命令
  6. #define CMD_IDLE    0               //空闲模式
  7. #define CMD_READ    1               //IAP字节读命令
  8. #define CMD_PROGRAM 2               //IAP字节编程命令
  9. #define CMD_ERASE   3               //IAP扇区擦除命令
  10. #define ENABLE_IAP  0x82            //CPU的等待时间
  11. #define IAP_ADDRESS 0x0800                        //测试地址
  12. sbit duan=P2^6;
  13. sbit wein=P2^7;

  14. //顺序共阴极数码管段码表,段码a-h顺序接PX0-PX7
  15. uchar code table[]={//共阴数码管段码"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];                //缓存数组
  21. uint num,sec;
  22. uchar i;

  23. void Timer0Init();                                        //定时器初始化声明
  24. void IapIdle();                                                //关闭IAP/EEPROM
  25. uchar IapReadByte(uint addr);                //读取EEPROM数据
  26. void IapProgramByte(uint addr, uchar dat);//写入EEPROM数据
  27. void IapEraseSector(uint addr);                //擦除EEPROM数据

  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);//读取保存的数据 用时11.75us
  47.         if(sec==0xffff)//如果没有保存数据
  48.                 sec=0;//变量为0
  49.         else IapEraseSector(IAP_ADDRESS);//擦除数据,为下次掉电保存数据做准备

  50.         PCON &= 0xDF;//清0掉电标志
  51.         ELVD = 1;//开低压中断
  52.         EA   = 1;//开总中断

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

  54.         while(1)
  55.         {
  56.                 if(TF0)//查询T0中断请求标志
  57.                 {               
  58.                         TF0=0;//T0中断请求标志清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;                //设置定时器模式
  80.         TL0 = 0xCD;                        //设置定时初始值
  81.         TH0 = 0xD4;                        //设置定时初始值
  82.         TF0 = 0;                        //清除TF0标志
  83.         TR0 = 1;                        //定时器0开始计时
  84. }


  85. /*----------------------------
  86.         关闭IAP功能
  87. ----------------------------*/
  88. void IapIdle()
  89. {
  90.     IAP_CONTR = 0;                  //关闭IAP功能
  91.     IAP_CMD = 0;                    //清除命令寄存器
  92.     IAP_TRIG = 0;                   //清除触发寄存器
  93.     IAP_ADDRH = 0x80;               //将地址设置到非IAP区域
  94.     IAP_ADDRL = 0;
  95. }
  96. /*----------------------------
  97. 从ISP/IAP/EEPROM区域读取一字节
  98. ----------------------------*/
  99. uchar IapReadByte(uint addr)
  100. {
  101.     uchar dat;                       //数据缓冲区

  102.     IAP_CONTR = ENABLE_IAP;         //使能IAP
  103.     IAP_CMD = CMD_READ;             //设置IAP命令
  104.     IAP_ADDRL = addr;               //设置IAP低地址
  105.     IAP_ADDRH = addr >> 8;          //设置IAP高地址
  106.     IAP_TRIG = 0x5a;                //写触发命令(0x5a)
  107.     IAP_TRIG = 0xa5;                //写触发命令(0xa5)
  108.     _nop_();                        //等待ISP/IAP/EEPROM操作完成
  109.     dat = IAP_DATA;                 //读ISP/IAP/EEPROM数据
  110.     IapIdle();                      //关闭IAP功能
  111.     return dat;                     //返回
  112. }
  113. /*-------------------------------
  114. 写一字节数据到ISP/IAP/EEPROM区域
  115. --------------------------------*/
  116. void IapProgramByte(uint addr, uchar dat)
  117. {
  118.     IAP_CONTR = ENABLE_IAP;         //使能IAP
  119.     IAP_CMD = CMD_PROGRAM;          //设置IAP命令
  120.     IAP_ADDRL = addr;               //设置IAP低地址
  121.     IAP_ADDRH = addr >> 8;          //设置IAP高地址
  122.     IAP_DATA = dat;                 //写ISP/IAP/EEPROM数据
  123.     IAP_TRIG = 0x5a;                //写触发命令(0x5a)
  124.     IAP_TRIG = 0xa5;                //写触发命令(0xa5)
  125.     _nop_();                        //等待ISP/IAP/EEPROM操作完成
  126.     IapIdle();                      //关闭IAP功能
  127. }
  128. /*----------------------------
  129. ISP/IAP/EEPROM扇区擦除
  130. ----------------------------*/
  131. void IapEraseSector(uint addr)
  132. {
  133.     IAP_CONTR = ENABLE_IAP;         //使能IAP
  134.     IAP_CMD = CMD_ERASE;            //设置IAP命令
  135.     IAP_ADDRL = addr;               //设置IAP低地址
  136.     IAP_ADDRH = addr >> 8;          //设置IAP高地址
  137.     IAP_TRIG = 0x5a;                //写触发命令(0x5a)
  138.     IAP_TRIG = 0xa5;                //写触发命令(0xa5)
  139.     _nop_();                        //等待ISP/IAP/EEPROM操作完成
  140.     IapIdle();                      //关闭IAP功能
  141. }
  142. void PowerLost() interrupt 6                //剩余电量从中断开始到完全断电2.5ms
  143. {
  144.         EA = 0;                                                //关闭总中断
  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);//写数据高8位到EEPROM
  154.         IapProgramByte(IAP_ADDRESS+1,sec);//写数据低8位到EEPROM  写两个字节用时215.25us

  155.         while((PCON & 0x20) != 0)         //复查低压标志
  156.         {
  157.                 PCON &= 0xDF;                  //清除低压标志
  158.                 _nop_();               
  159.                 _nop_();                            //坐等掉电
  160.         }
  161.         IAP_CONTR = 0x20;                 //发现是误报,重启单片机,恢复正常工作
复制代码
回复

使用道具 举报

ID:965189 发表于 2023-9-8 22:41 | 显示全部楼层
fishafish 发表于 2023-9-8 16:34
应该是第三行错了,16位shu存入8位的话,自动四舍五入,其实存的还是高8位

这个讲法有可能,因为某些数会错,而一些数不会错。
回复

使用道具 举报

ID:965189 发表于 2023-9-8 23:01 | 显示全部楼层

请教大侠,这一行,IapRead(0x0000,*p++); IapRead(0x0001,*p++);
读出数据不是读出来重新组合到shu吗?为什么又读到内存里了。
回复

使用道具 举报

ID:491577 发表于 2023-9-8 23:29 | 显示全部楼层
数据溢出了,shu=IapRead(0x0000)<<8|IapRead(0x0001); 其中IapRead(0x0000)<<8有问题,IapRead(0x0000)是8位的,左移数据会溢出。要强制数据转换:shu=(uint)IapRead(0x0000)<<8|IapRead(0x0001);
回复

使用道具 举报

ID:491577 发表于 2023-9-8 23:45 | 显示全部楼层
单片机很容易发生数据溢出错误,我就碰到过,怎么查找都不知道原因,特别是运算中间的溢出更加难查,比如16位数据*100/200,结果一定小于16位,但是这样写a=b*100/200;是不对的,这样才对a=b/200*100;先乘100可能会数据溢出,不小心很容易中招。
回复

使用道具 举报

ID:1041851 发表于 2023-9-9 09:34 | 显示全部楼层
Keil处理复杂的计算式时容易出现一些奇怪问题
将"shu=IapRead(0x0000)<<8|IapRead(0x0001); //读出保存的数据"
这句拆分成两步试试:
shu = apRead(0x0000) << 8;

shu |= IapRead(0x0001);

也可将每步的结果打印出来看看哪里不对

回复

使用道具 举报

ID:304306 发表于 2023-9-9 09:48 | 显示全部楼层
用联合体(uion)做方便,不用考虑这么多
回复

使用道具 举报

ID:965189 发表于 2023-9-9 12:33 | 显示全部楼层
2008_clz 发表于 2023-9-9 09:48
用联合体(uion)做方便,不用考虑这么多

请教:具体怎么写。
回复

使用道具 举报

ID:965189 发表于 2023-9-9 15:09 | 显示全部楼层
在以上各位大侠的指教下,问题已经解决。多谢各位的热心帮助。问题出在读出组合环节。
回复

使用道具 举报

ID:883242 发表于 2023-9-9 17:25 | 显示全部楼层
不要去运算这个数,知道地址组合数据即可。
回复

使用道具 举报

ID:1059013 发表于 2023-9-9 19:40 | 显示全部楼层
君工创 发表于 2023-9-8 22:41
这个讲法有可能,因为某些数会错,而一些数不会错。

好像还与不同编译软件有关,我遇到PIC编译软件是会得到意外结果的
回复

使用道具 举报

ID:77589 发表于 2023-9-12 15:08 | 显示全部楼层
hhh402 发表于 2023-9-8 23:29
数据溢出了,shu=IapRead(0x0000)

我认为也是这个问题
回复

使用道具 举报

ID:77589 发表于 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++单独提出来写,所有人都好理解。而且写与读都只需要一次p++。
读取后的数据自动组合到shu中了。
回复

使用道具 举报

ID:883242 发表于 2023-9-13 14:32 | 显示全部楼层
Longan.Wang 发表于 2023-9-12 15:16
方法可行,但是后两句错了
uint16_t shu;
uint8_t *p;

我用MinGW_w64 13.0验证无误,当然你水平难以理解改成你能理解的方式没问题,错了你有何依据?不要凭空想象。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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