找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1924|回复: 23
收起左侧

用C语言结构体写EEPROM数据保存的问题

[复制链接]
ID:997026 发表于 2022-5-12 00:30 | 显示全部楼层 |阅读模式

请教下各位,如下的结构体和结构体数组用法正确吗?
就是需要保存数据到EEPROM,数据每改动一次,就往EEPROM写一次,现在功能是没问题的,可以正常写入数据,但是数码管显示出问题了,每次设置min_change时,数码管会变暗。

整个程序有一万行,非常长,其他没有贴出来,只贴了结构体这部分,因为这部分以前不是用结构体写的,没问题,现在突发奇想,用结构体写了下,功能是实现了,但数码管变暗了,不知道为啥,看了好久也不知道为啥。

但我个人估计是结构体用法的问题,麻烦大家帮我看看,如下的结构体数组的用法是否正确,谢谢了

C语言源程序如下:
  1. unsigned char   min_change0=0,  //要保存的变量值第一组,每一次改变都会保存
  2. unsigned char   min_change_backup0=0; //该变量为中间变量,用于与min_change做对比,下同

  3. unsigned char   min_change1=0,  //要保存的变量值第二组,每一次改变都会保存
  4. unsigned char   min_change_backup1=0;

  5. unsigned char   min_change2=0,  //要保存的变量值第三组,每一次改变都会保存  
  6. unsigned char   min_change_backup2=0;

  7. void EEPROM_Write(timeset*, unsigned char, unsigned char) ; //数据保存并写入EEPROM
  8. EEPROM_write(unsigned char,  unsigned char, );  //内部EEPROM写入函数


  9. typedef struct  EEPROM_Save
  10. {
  11.         unsigned char* min_set;
  12.         unsigned char* min_set_backup;
  13. } timeset;


  14. timeset  EEPROM_SAVE[3] ={      //定义并初始化一个结构体数组,数组元素是指针,数组元素为三组需要保存的数据
  15. {&min_change0, &min_change_backup0},
  16. {&min_change1, &min_change_backup1},
  17. {&min_change2, &min_change_backup2}

  18. };

  19. int main(void)
  20. {
  21.    while(1)
  22.    {
  23.       for(int j=0; j <3; j++)
  24.       {
  25.          EEPROM_Write(EEPROM_SAVE,  j,  (j+1)) ;
  26.       }
  27.    }
  28. }


  29. void EEPROM_Write(timeset* table, unsigned char i, unsigned char addr)
  30. {
  31.         if (*(table[i].min_set)  != *(table[i].min_set_backup)) //如果min_change和min_change_backup不相等,则开始保存
  32.         {
  33.               *(table[i].min_set_backup) = *(table[i].min_set) ; //保存数据
  34.                   EEPROM_write(addr,  *(table[i].min_set_backup));  //写入EEPROM

  35.          }
  36. }
复制代码


回复

使用道具 举报

ID:879348 发表于 2022-5-12 08:21 | 显示全部楼层
如果你是用单片机的内部flash做EEPROM,那就无解,刷写过程需要比较长时间,而且要关闭中断
回复

使用道具 举报

ID:401564 发表于 2022-5-12 11:41 | 显示全部楼层
跟结构体是没有关系的
写入EEPROM的时候,不知道是不是先擦除再写入?
如果是的话,擦除和写入之后,不要一直在那等待完稿完成,FLASH擦除的时间是比较久的,一般都是以页面为单位的
写入和擦除应该是写入之后就去显示数码管,或者是数码管用中断的方式显示
回复

使用道具 举报

ID:997026 发表于 2022-5-12 12:17 来自手机 | 显示全部楼层
wufa1986 发表于 2022-5-12 08:21
如果你是用单片机的内部flash做EEPROM,那就无解,刷写过程需要比较长时间,而且要关闭中断

我是用单片机内部的EEPROM,不是flash,EEPROM读写很简单,不需要擦除,不存在逻辑上的问题。感觉就是结构体用法没搞清楚,我这里面用了好多指针,不知道对不对
回复

使用道具 举报

ID:997026 发表于 2022-5-12 12:21 来自手机 | 显示全部楼层
Y_G_G 发表于 2022-5-12 11:41
跟结构体是没有关系的
写入EEPROM的时候,不知道是不是先擦除再写入?
如果是的话,擦除和写入之后,不要一直 ...

我这个是单片机内部的eeprom,不是flash。eeprom的读写非常的简单,我用过很多次了,比较熟,至少不是这种写法。没问题,这次换成结构体数组和指针方式就出了问题,我怀疑是结构体用法问题,因为这里面涉及到了数组传递参数和指针的问题,不知道用的对不对。另外你提到的写入延时问题,eeprom写入确实需要延时,这个程序里我没加进去,我改天加上试试
回复

使用道具 举报

ID:123289 发表于 2022-5-12 14:12 | 显示全部楼层
建议先仔细研读一下EEPROM的写入方式。
大多EEPROM是只能写0,不能写1的,即写之前,要将写入的位置的数据擦成FFH,而擦除是以页为单位的,即不能单字节擦。
程序中指向的地址中,原来数字是不是FFH,这里看不出。如果不是FFH,则平台编译此步写入是很复杂的。涉及:保存存整页全体到缓冲区,擦除本页面,在缓冲区中改写数据,再将缓冲区中的整页数据写入。这就需要较长的作业时间。在这段时间里,很可能照顾不到显示。
想知道正解也简单:打开编译生成的汇编程序,看看你在写入时,CPU干了什么?
如果你懂汇编,而且读透了EEPROM的性能及操作,是很容易想到问题之所在的。
回复

使用道具 举报

ID:401564 发表于 2022-5-12 14:29 | 显示全部楼层
hxdby 发表于 2022-5-12 12:21
我这个是单片机内部的eeprom,不是flash。eeprom的读写非常的简单,我用过很多次了,比较熟,至少不是这 ...

EEPROM_write写入函数是用了递归吗?还是代码没复制完
如果是递归就没有必要了,就写入一个字节而已,单片机最好不要用递归
回复

使用道具 举报

ID:844772 发表于 2022-5-12 14:31 | 显示全部楼层
我不习惯你这种写法,但觉得你指针用的没啥问题,难道指针的使用导致执行效率下降了,使得数码刷新率下降引起显示变暗吗?
回复

使用道具 举报

ID:997026 发表于 2022-5-12 14:46 | 显示全部楼层
Y_G_G 发表于 2022-5-12 14:29
EEPROM_write写入函数是用了递归吗?还是代码没复制完
如果是递归就没有必要了,就写入一个字节而已,单片 ...

EEPROM_write这个写入函数没用递归,里面非常简单,就是纯寄存器的写入,用的是官方的代码,就几行代码而已,所以没贴出来。大概是下面这样:
void EEPROM_write(unsigned int uiAddress, unsigned char ucData) //EEPROM写函数
{
        while(EECR & (1<<EEPE)); //等待上一次写操作结束
        EEAR = uiAddress;        //设置地址寄存器
        EEDR = ucData;           //设置数据寄存器
        EECR |= (1<<EEMPE);      //置位EEMWE 打开写使能
        EECR |= (1<<EEPE);       //置位EEWE以启动写操作
}
回复

使用道具 举报

ID:997026 发表于 2022-5-12 14:51 | 显示全部楼层
glinfei 发表于 2022-5-12 14:31
我不习惯你这种写法,但觉得你指针用的没啥问题,难道指针的使用导致执行效率下降了,使得数码刷新率下降引 ...

之前不是这种写法,之前是普通写法,每一个保存的参数要写一个函数,无法通用,100多个参数要写100多个函数,太麻烦了。这里用指针是为了做成通用模板,用一个函数循环调用不同参数就可以了,大大简化了代码。但是确实影响了显示,不知道为啥。我用很多方法写过,就是结构体这种写法有问题,所以怀疑是结构体用法问题。我之前用指针数组,把变量放到指针数组里面也能实现功能,没有问题,后来想用结构体写,没想到一直有问题。
回复

使用道具 举报

ID:997026 发表于 2022-5-12 15:01 | 显示全部楼层
glinfei 发表于 2022-5-12 14:31
我不习惯你这种写法,但觉得你指针用的没啥问题,难道指针的使用导致执行效率下降了,使得数码刷新率下降引 ...

这部分EEPROM数据保存我用很多方法写过,普通写法不用说了,代码量很大,但是没问题。也用指针数组的方法写过,就是把两个待比较的变量分别放到两个指针数组里,通过指针传递参数,这种同样用指针,也没问题。这次我用结构体指针就有问题了,估计是结构体哪里用法没搞对,指针原则上效率更高才对啊
回复

使用道具 举报

ID:1012735 发表于 2022-5-12 15:37 | 显示全部楼层
将函数原型
void EEPROM_Write(timeset*, unsigned char, unsigned char) ; //数据保存并写入EEPROM
改成
void EEPROM_Write(timeset,unsigned char)

就是:
void EEPROM_Write(timeset  table,        unsigned char addr)
{
        if ( *(table.min_set)!= *(table.min_set_backup)) //如果min_change和min_change_backup不相等,则开始保存
        {
              *(table.min_set_backup) = *(table.min_set) ; //保存数据
                  EEPROM_write(addr, *( table.min_set_backup));  //写入EEPROM

         }
}调用处:
      for(int j=0; j <3; j++)
      {
         EEPROM_Write(EEPROM_SAVE[ j],       (j+1)) ;
      }

试试不一定能成!
回复

使用道具 举报

ID:997026 发表于 2022-5-12 15:53 | 显示全部楼层
同志们,我找到问题所在了,这种赋值方法
if (*(table[i].min_set)  != *(table[i].min_set_backup))

*(table[i].min_set) 和*(table[i].min_set_backup)是同一个结构体数组变量里的元素,也就是:
timeset  EEPROM_SAVE[3] ={      //定义并初始化一个结构体数组,数组元素是指针,数组元素为三组需要保存的数据
{&min_change0, &min_change_backup0},
{&min_change1, &min_change_backup1},
{&min_change2, &min_change_backup2}

};

也就是相当于我把&min_change_backup0赋值给&min_change0了,而这两个成员是同一个结构体里的成员,我的疑问是同一个结构体里的成员相互之间不能赋值吗?

我重新定义了一个结构体,现在有两个结构体,然后把&min_change_backup0这个变量放到一个结构体中,把&min_change0放到另一个结构体中,相当于调用两个结构体中的变量做对比了,这样改动后测试没有问题了。

所以同一个结构中的变量不能相互赋值吗?
回复

使用道具 举报

ID:401564 发表于 2022-5-12 16:15 | 显示全部楼层
hxdby 发表于 2022-5-12 15:53
同志们,我找到问题所在了,这种赋值方法
if (*(table.min_set)  != *(table.min_set_backup))

不知道为什么要这么复杂的操作
但是,一般结构成员指向变量的话,是用圆点  .
你这个是指向指针的,你用->试一下
回复

使用道具 举报

ID:1012735 发表于 2022-5-12 16:48 | 显示全部楼层
数组初始化赋值应该赋常量值吧
回复

使用道具 举报

ID:997026 发表于 2022-5-12 16:53 | 显示全部楼层
Y_G_G 发表于 2022-5-12 16:15
不知道为什么要这么复杂的操作
但是,一般结构成员指向变量的话,是用圆点  .
你这个是指向指针的,你用-> ...

主要是我保存的参数太多,如果只有几个参数的话,普通写法就可以应付了,但是后面可能会有几百个参数,如果普通写法需要重复写同样的函数几百次,导致代码冗长。这种结构体封装数组和指针的写法,可以写出一个通用模板函数,然后后面增加参数,往数组里面添加就行了,便于维护,以前普通写法保存30个参数差不多写了1000行代码,现在用结构体数组封装后30行代码就搞定了。

-> 主要用于结构体指针取值,但是我这个涉及到通用性,还是使用for循环+结构体数组的方式好些
回复

使用道具 举报

ID:844772 发表于 2022-5-12 16:55 | 显示全部楼层
hxdby 发表于 2022-5-12 15:53
同志们,我找到问题所在了,这种赋值方法
if (*(table.min_set)  != *(table.min_set_backup))

原来你是赋值不对,还以为是数码管亮度问题。而且同一个结构中变量一定能相互赋值。
赋值那句,我一般会这么写: *((table+i)->min_set_backup) = *((table+i)->min_set) ;
回复

使用道具 举报

ID:997026 发表于 2022-5-12 16:56 | 显示全部楼层
hy47566398 发表于 2022-5-12 16:48
数组初始化赋值应该赋常量值吧

普通变量确实需要赋值常量,但是我这个数组的元素是指针,这个指针是全局变量赋初值后通过取地址符得到他的地址,所以也算是常量,只不过这值是指针而已
回复

使用道具 举报

ID:844772 发表于 2022-5-12 17:01 | 显示全部楼层
hxdby 发表于 2022-5-12 16:53
主要是我保存的参数太多,如果只有几个参数的话,普通写法就可以应付了,但是后面可能会有几百个参数,如 ...

要是不用->,应该多加个*,
你赋值那句也可以这么写 *((*table[ i]).min_set_backup) = *((*table[ i]).min_set) ; //保存数据
其他的也要改。
回复

使用道具 举报

ID:997026 发表于 2022-5-12 17:06 | 显示全部楼层
glinfei 发表于 2022-5-12 16:55
原来你是赋值不对,还以为是数码管亮度问题。而且同一个结构中变量一定能相互赋值。
赋值那句,我一般会 ...

谢谢!我说的赋值不对是在同一个结构体中的两个变量相互赋值,我什么都没改,就把相互赋值的这两个变量分别放到了不同的结构体中,然后再赋值,结果就正常了。我也很奇怪,所以我刚才才会问同一结构中变量不能相互赋值,如果你说同一结构中两个变量可以相互赋值的话,那我真没理解为啥我的不行,定义两个结构体就行了,是不是还有其他问题,我也不知道了。你的这种方法我后面再试试,谢谢
回复

使用道具 举报

ID:997026 发表于 2022-5-12 17:08 | 显示全部楼层
glinfei 发表于 2022-5-12 17:01
要是不用->,应该多加个*,
你赋值那句也可以这么写 *((*table).min_set_backup) = *((*table).min_set) ...

你这种写法,我之前调的时候试过,结果编译器直接报错了。后来我就定义了两个结构体,其他什么都没改,就正常了,赋值方式也还是我之前的那种方式
回复

使用道具 举报

ID:997026 发表于 2022-5-13 15:42 | 显示全部楼层
glinfei 发表于 2022-5-12 16:55
原来你是赋值不对,还以为是数码管亮度问题。而且同一个结构中变量一定能相互赋值。
赋值那句,我一般会 ...

我试过了,你这种写法非常棒,可以把函数的形参减少到1个,我之前为了实现通用化,在for循环里不断调用函数,我要传递四个参数,需要这个函数要有四个形参,用你这种写法,只需传递一个参数就可以了,一个形参全部搞定,其他的都可以在函数内部用你这种方法实现。指针的这些玩法太灵活了,写法好多种
回复

使用道具 举报

ID:997026 发表于 2022-5-13 15:51 | 显示全部楼层
hy47566398 发表于 2022-5-12 15:37
将函数原型
void EEPROM_Write(timeset*, unsigned char, unsigned char) ; //数据保存并写入EEPROM
改成 ...

这种应该不行把,这种方式void EEPROM_Write(timeset  table,        unsigned char addr),你把timeset的指针类型拿掉了,那table只能传入一个结构体普通变量,传不进一个数组了,但你后面又传入数组EEPROM_Write(EEPROM_SAVE[ j],       (j+1)) ; 编译器应该会报错。用指针主要是用数组名表示传入的是数组的首地址,所以用指针
回复

使用道具 举报

ID:1026496 发表于 2022-5-14 19:06 | 显示全部楼层
EEPROM_write 不建议频繁的写入,建议掉电写入一次
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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