搜索
bottom↓
回复: 117

幽游梦蝶写的EEPROM读写函数,请大家指点

[复制链接]

出0入0汤圆

发表于 2005-6-19 20:37:03 | 显示全部楼层 |阅读模式
由于库函数的EEPROM写函数占据太长的系统时间,幽游梦蝶重写了EEPROM,但怕有别的问题,请大家来挑挑刺,谢谢!

PS:

早几天我在另外的帖子说把我的程序贴出来的:)



1> EEPROM.h:



#ifndef _EEPROM_H_

#define _EEPROM_H_



#ifdef  _EEPROM_C_

#define EXTERN

#else

#define EXTERN  extern

#endif



typedef enum

{

    EEPROM_WRITE_STAT_EMPTY,  // no data in the EEPROM Write buf

    EEPROM_WRITE_STAT_FILL,   // have data in the EEPROM Write but not full

    EEPROM_WRITE_STAT_FULL    // EEPROM Write buf full

}EEPROM_WRITE_STAT;



typedef enum

{

    EEPROM_SERVER_STAT_NULL,

    EEPROM_SERVER_STAT_WRITING

}EEPROM_SERVER_STAT;



typedef struct

{

    unsigned int  wAddress;

    unsigned char bVal;

}S_EEPROM_POSITION;



#define EEPROM_WRITE_BUF_SIZE   16

EXTERN S_EEPROM_POSITION _sEEPROMBuf[EEPROM_WRITE_BUF_SIZE];

EXTERN volatile EEPROM_WRITE_STAT _eEEPROMWriteStat;

EXTERN volatile U8  _bEEPROMWritePoint;

EXTERN volatile U8  _bEEPROMReadPoint;

EXTERN volatile EEPROM_SERVER_STAT _eEEPROMServerStat;



EXTERN U8 WriteDataToEEPROM(U16 wAddress, U8 bVal);

EXTERN U8 ReadDataFromEEPROM(U16 wAddress, U8 *pbVal);

EXTERN void EEPROMServer(void);

EXTERN void InitEEPROM(void);



#undef  EXTERN

#endif



2> EEPROM.c



#define _EEPROM_C_



#include    "DrvHead.h"



#pragma interrupt_handler EE_READY:iv_EE_READY

void EE_READY(void)

{

    if(_eEEPROMWriteStat != EEPROM_WRITE_STAT_EMPTY)

    {

        EEAR = _sEEPROMBuf[_bEEPROMReadPoint].wAddress;

        EEDR = _sEEPROMBuf[_bEEPROMReadPoint].bVal;

        EECR |= BIT(EEMWE);

        EECR |= BIT(EEWE);

        _eEEPROMServerStat = EEPROM_SERVER_STAT_WRITING;

        _bEEPROMReadPoint++;

        if(_bEEPROMReadPoint >= EEPROM_WRITE_BUF_SIZE)

        {

            _bEEPROMReadPoint = 0;

        }

        if(_bEEPROMReadPoint == _bEEPROMWritePoint)

        {

            _eEEPROMWriteStat = EEPROM_WRITE_STAT_EMPTY;

        }

        else

        {

            _eEEPROMWriteStat = EEPROM_WRITE_STAT_FILL;

        }

    }

    else

    {

        EECR &= ~BIT(EERIE);

        _eEEPROMServerStat = EEPROM_SERVER_STAT_NULL;

    }

}





U8 WriteDataToEEPROM(U16 wAddress, U8 bVal)

{

    if(_eEEPROMWriteStat == EEPROM_WRITE_STAT_FULL)

    {

        return RET_BUSY;

    }

    _sEEPROMBuf[_bEEPROMWritePoint].wAddress = wAddress;

    _sEEPROMBuf[_bEEPROMWritePoint].bVal = bVal;

    _bEEPROMWritePoint++;

    if(_bEEPROMWritePoint >= EEPROM_WRITE_BUF_SIZE)

    {

        _bEEPROMWritePoint = 0;

    }

    if(_bEEPROMWritePoint == _bEEPROMReadPoint)

    {

        _eEEPROMWriteStat = EEPROM_WRITE_STAT_FULL;

    }

    else

    {

        _eEEPROMWriteStat = EEPROM_WRITE_STAT_FILL;

    }

    return RET_SUCCESS;

}





U8 ReadDataFromEEPROM(U16 wAddress, U8 *pbVal)

{

    if(EECR & BIT(EEWE))

    {

        return RET_BUSY;

    }

    EEAR = wAddress;

    EECR |= BIT(EERE);

    *pbVal = EEDR;

    return RET_SUCCESS;

}





void EEPROMServer(void)

{

    if( (_eEEPROMServerStat == EEPROM_SERVER_STAT_NULL)

        && (_eEEPROMWriteStat != EEPROM_WRITE_STAT_EMPTY) )

    {

        EECR |= BIT(EERIE);

    }

}





void InitEEPROM(void)

{

    _eEEPROMWriteStat = EEPROM_WRITE_STAT_EMPTY;

    _bEEPROMWritePoint = 0;

    _bEEPROMReadPoint = 0;

    _eEEPROMServerStat = EEPROM_SERVER_STAT_NULL;

    EECR = 0x00;

}




-----此内容被Louis Bright于2005-06-20,08:53:09编辑过

出0入0汤圆

 楼主| 发表于 2005-6-20 08:58:29 | 显示全部楼层
怎么大家都没有反应啊?我就不相信大家的项目中每个都能对库函数写一个Byte到EEPROM中要8.5ms能够忍受。而且库函数中没有关中断,容易写失败(datasheet中的例子倒是关中断了)。
头像被屏蔽

出0入0汤圆

发表于 2005-6-20 11:09:13 | 显示全部楼层
如果能将简单的测试方法列出来,估计会有更多的人感兴趣。



很多人一看到是一大段代码,没有详细的测试方法,就不去做试验了。

出0入0汤圆

发表于 2005-6-20 13:20:45 | 显示全部楼层
用汇编多简单啊!就那么寥寥几句话。。中断的程序中尽量不使用读写EEPROM时的寄存器就可以在不关中断的前提下读写EEPROM,不知道说得对不对啊,反正我用汇编写的程序中就没关中断,而且一直都运行得很好。。

出0入0汤圆

 楼主| 发表于 2005-6-20 15:58:15 | 显示全部楼层
to steven:

那么你有没有测试或者算过,你写一个字节要多少时间?!

出0入0汤圆

发表于 2005-6-20 16:56:13 | 显示全部楼层
写EEPROM的时间多少并不需要特别关注,因为很少用EEPROM来做实时记录。像CVAVR和WINAVR都有读写EEPROM的函数,使用时应该不会有什么问题吧。
-----此内容被bucker于2005-06-20,16:58:49编辑过

出0入0汤圆

 楼主| 发表于 2005-6-20 18:14:05 | 显示全部楼层
感觉秀才遇到了兵。

我一开始就提到EEPROM写字节需要时间的。

datasheet上说标准时间是8.5ms,

对于跑8M的晶振来说,这段时间能够跑8500×8 = 68000条指令。

一条指令占两个字节,那么这段时间能够跑的代码是:68000×2÷1024=132.8125K

即使是M128,也能够把所有程序跑遍!



这时你还认为这段时间能够随便忽略吗?



事实上我程序中实时性很强,决不容许在某个地方死等上1ms。所以一开始就知道EEPROM的库函数不能用。

还有steven提到的汇编,其实本质是一样的,写EEPROM时,送完数据和地址后,先要打开EEMWE,在4个机器周期内一定要打开EEWE,否则EEMWE就自动清零了,那么写操作就会失败;那么假如在打开EEMWE时刚好有个中断呢(虽然概率很低,但理论和实际中确实存在啊)?只要中断时间超过4个机器周期(这简直是一定的),那么即使中断中没有操作EEPROM的寄存器,写操作也会失败。
-----此内容被Louis Bright于2005-06-20,18:17:16编辑过

出0入0汤圆

发表于 2005-6-20 19:22:44 | 显示全部楼层
和和,不要生气。你可以再写EEPROM之前关中断,在发出了写EEPROM指令后,在开中断,然后不必等延时结束就先运行其它的程序。

出0入0汤圆

发表于 2005-6-21 08:19:02 | 显示全部楼层
同意bucker 沙漠的观点,看来我们真的误解了Louis Bright 幽游梦蝶的真正用意

出0入0汤圆

发表于 2005-6-21 08:28:55 | 显示全部楼层
eeprom写操作,cpu会暂停滴

出0入0汤圆

 楼主| 发表于 2005-6-21 09:12:40 | 显示全部楼层
To bucker & steven:

其实你们还是没有明白我的真正用意:



我们先看ICC库函数是怎么写的:

int EEPROMwrite( int location, unsigned char byte)

{

        unsigned char oldSREG;



        EEAR = location;



    EEDR = byte;



        oldSREG = SREG;

        SREG &= ~0x80;                // disable interrupt



    EECR |= 0x04;                       // Set MASTER WRITE enable

    EECR |= 0x02;                       // Set WRITE strobe

    while (EECR & 0x02);                // Wait until write is done



        SREG = oldSREG;

    return 0;                           // return Success.

                                        // Could be expanded so that

                                        // the routine checks that the address

                                        // is within the range of the chip.

}      



再看datasheet上是怎么写的:

void EEPROM_write(unsigned int uiAddress, unsigned char ucData)

{

  /* 等待上一次写操作结束 */

  while(EECR & (1 << EEWE))

   ;

  /* 设置地址和数据寄存器 */

  EEAR = uiAddress;

  EEDR = ucData;

  /* 置位EEMWE */

  EECR |= (1 << EEMWE);

  /* 置位EEWE以启动写操作E */

  EECR |= (1 << EEWE);

}



看到了吗?都有一个判断EEWE为0的while语句,这个while语句最长可以达到8.5ms!



分析:

如果使用bucker的方法,则有可能在8.5ms内操作两次写操作,可能两个数据都写不成功(至少第二个数据是写不成功的);

如果使用库函数,那个每写一次需要延时死等8.5ms;

如果使用datasheet上的方法,可能有延时,最长可能达到8.5ms;

对于实时性很强的系统,以上方法肯定是不行的,

是不是想知道我是怎么解决的?看上面的程序吧!

出0入0汤圆

发表于 2005-6-21 09:13:51 | 显示全部楼层
eeprom写操作,cpu会暂停吗?

我知道一点:写eeprom时候必须及时喂狗, 否则会复位.



楼主还是把自己的代码测试一下吧, 看看效率吧.



我记得CVAVR(或者是ICC)的函数在写eeprom时候关闭了中断, 上次有人贴出了代码.

8.5ms的时间的确让人头疼. 一旦涉及到精确的记时之类的, 误差就出来了.

出0入0汤圆

发表于 2005-6-21 10:14:26 | 显示全部楼层
程序最好加上注释

实时性强确实要用中断喂数据,8.5ms能做好多事呢

出0入0汤圆

 楼主| 发表于 2005-6-25 17:46:58 | 显示全部楼层
最近特忙,今天周末抽出了点时间,验证了一下我自己写的EEPROM读写程序,发现非常好用!



To josn-lu:

关于程序的注释,以前讨论过,我的建议和别人有些不同:程序尽量减少注释,支持变量/函数/文件命名自注释。

以上程序,我只对EEPROM的写缓冲的三种状态做了注释,其它我认为没有必要做注释。

出0入0汤圆

 楼主| 发表于 2006-2-25 14:32:14 | 显示全部楼层
更新我的EEPROM程序,这个程序验证没有什么问题的,自己极力推荐;不过在读EEPROM的时候,程序员要自己保证这时没有在写EEPROM。另外,有想知道我程序架构的XDJM可以再仔细看看我的这个Eeprom模块(个人比较懒,在另外的帖子中提到了程序架构,有XDJM感兴趣),相信你能找到你想要的。



1>DrvEeprom.h



#ifndef _DRVEEPROM_H_

#define _DRVEEPROM_H_



#ifdef  _DRVEEPROM_C_

#define EXTERN

#else

#define EXTERN  extern

#endif



typedef struct

{

    WORD wAddress;

    BYTE bVal;

}S_EEPROM_POSITION;



#define EEPROM_WRITE_BUF_SIZE   64



EXTERN BYTE bWriteData2Eeprom(WORD wAddress, BYTE bVal);

EXTERN BYTE bReadDataFromEeprom(WORD wAddress, BYTE *pbVal);

EXTERN BYTE bGetEepromWrBuf(void);

EXTERN void bClrEepromBuf(void);

EXTERN BOOL fgEepromWizard(void);

EXTERN void vInitEeprom(void);



#undef  EXTERN

#endif



2>DrvEeprom.c



#define _DRVEEPROM_C_



#include    "DrvHead.h"



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



STATIC S_EEPROM_POSITION volatile _sEepromBuf[EEPROM_WRITE_BUF_SIZE];

STATIC BYTE volatile _bEepromBufWrPtr;

STATIC BYTE volatile _bEepromBufRdPtr;

STATIC BYTE volatile _bEepromBufNs;

#define fgEepromBufEmpty()      (_bEepromBufNs == 0)

#define fgEepromBufFull()       (_bEepromBufNs == EEPROM_WRITE_BUF_SIZE)



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



#pragma interrupt_handler vIvEeReady:iv_EE_READY

void vIvEeReady(void)

{

    if(!fgEepromBufEmpty())

    {

        EEAR = _sEepromBuf[_bEepromBufRdPtr].wAddress;

        EEDR = _sEepromBuf[_bEepromBufRdPtr].bVal;

        EECR |= BIT(EEMWE);

        EECR |= BIT(EEWE);

        _bEepromBufRdPtr++;

        if(_bEepromBufRdPtr >= EEPROM_WRITE_BUF_SIZE)

        {

            _bEepromBufRdPtr = 0;

        }

        _bEepromBufNs--;

    }

    else

    {

        EECR &= ~BIT(EERIE);

    }

}



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



BYTE bWriteData2Eeprom(WORD wAddress, BYTE bVal)

{

    if(fgEepromBufFull())

    {

        return RET_BUSY;

    }



    CLI();

    _sEepromBuf[_bEepromBufWrPtr].wAddress = wAddress;

    _sEepromBuf[_bEepromBufWrPtr].bVal = bVal;

    _bEepromBufWrPtr++;

    if(_bEepromBufWrPtr >= EEPROM_WRITE_BUF_SIZE)

    {

        _bEepromBufWrPtr = 0;

    }

    _bEepromBufNs++;

    EECR |= BIT(EERIE); // Sure the EEPIE is enable even if do the thing before

    SEI();



    return RET_SUCCESS;

}





// Notice! The programer must ensure the EEPROM is NOT in writing state.

BYTE bReadDataFromEeprom(WORD wAddress, BYTE *pbVal)

{

    if(EECR & BIT(EEWE))

    {

        return RET_BUSY;

    }



    EEAR = wAddress;

    EECR |= BIT(EERE);

    *pbVal = EEDR;

    return RET_SUCCESS;

}





BYTE bGetEepromWrBuf(void)

{

    return _bEepromBufNs;

}





void bClrEepromBuf(void)

{

    CLI();

    _bEepromBufNs = 0;

    _bEepromBufRdPtr = _bEepromBufWrPtr;

    EECR &= ~BIT(EERIE);

    SEI();

}





BOOL fgEepromWizard(void)

{

    BOOL fgRet = FALSE;



    return fgRet;

}





void vInitEeprom(void)

{

    EECR = 0x00;

    _bEepromBufWrPtr = 0;

    _bEepromBufRdPtr = 0;

    _bEepromBufNs    = 0;

}



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

出0入0汤圆

发表于 2006-2-25 18:43:36 | 显示全部楼层
Louis Bright



多谢你的奉献

出0入4汤圆

发表于 2006-2-26 13:34:11 | 显示全部楼层
但是事实上写eeprom  一个字节 还是需要8.5 ms的,从第第一字节写开始到第n个字节写完

需要花n*8.5ms的时间,除非页写  快4倍   只不过一个用查询的方式,一个用中断驱动+缓冲器的方式,各有个的优缺点。



用查询方式  软件编写简单,代码小 RAM占用空间小,但是实时性不高

用中断方式  软件编写稍复杂 代码稍大 需要一定的RAM空间(AVR不成问题,RAM大),实时性好。

出0入0汤圆

发表于 2006-4-8 16:50:59 | 显示全部楼层
有没有用PWI方式写的c程序  (icc编译器)

小弟在此谢了

出0入0汤圆

发表于 2006-4-8 20:07:40 | 显示全部楼层
帖子够老的,不过既然被人翻出来了,就提醒楼主一件事好了



中断+缓冲方式无法保证eeprom写入事务的原子性,而一个不能保证原子性的eeprom写入函数,基本上就是失败的



PS:个人认为AVR一个重大的设计失败就是不支持EEPROM的页写入方式——其实只要允许以至少8字节(最好32字节)为单位进行页写,就能够非常有效的提高程序速度和安全性

出0入0汤圆

发表于 2006-4-14 10:15:35 | 显示全部楼层
请楼主可以把"Drvhead.h"也帖出来可以吗?程序调不了啊

出0入0汤圆

 楼主| 发表于 2006-4-20 12:54:35 | 显示全部楼层
To 19楼  watercat:

    我程序考虑了原子性的,你可以看到Eeprom的读函数中有注释:

// Notice! The programer must ensure the EEPROM is NOT in writing state.

并且函数中有特殊处理:

    if(EECR & BIT(EEWE))

    {

        return RET_BUSY;

    }

    原子性是数据库中的概念,它的本质是为了保存的数据读出来的时候是完整的,我这样做也能保证读到的数据是完整的.

    事实上我在上面的帖子中就提到,我的系统不能够在某个地方死等上1ms的,我至少要保证我的系统能正常跑起来,菜会去考虑别的东西,否则就是白搭.而事实上我也能保证我的程序在读Eeprom的时候不会处于写的状态.所以程序跑起来没有问题.

    如果你觉得我这样还有问题,麻烦你指点我写一个既能保证Eeprom写入事务的原子性,而又能让其它程序不延时跑起来的函数.否则我只会认为你是站着说话不腰疼.

出0入0汤圆

发表于 2006-6-16 19:48:03 | 显示全部楼层
TO Louis Bright 幽游梦:



寫的不錯好用.頂!!!

出0入0汤圆

发表于 2006-8-15 13:45:16 | 显示全部楼层
谢谢各位了,

这个和串口通信差不多呀,可用查寻,也可以用中断哦,

对于自己的不同要求,大家可以自己选择的嘛!

出0入0汤圆

发表于 2006-8-17 14:25:12 | 显示全部楼层
这些个问题都简单,主要是实时性问题,设一个标记,等待的时候没有到就退出,等下一个循环,这样写起来慢一点,但是不会死等。

出0入0汤圆

发表于 2006-9-14 16:02:34 | 显示全部楼层
楼主,我是AVR新手,敢问你的函数如何使用?是不是保存在AVR的某个目录下啊?请不吝赐教!

出0入0汤圆

发表于 2006-9-14 17:35:01 | 显示全部楼层
好贴,顶一顶

出0入0汤圆

发表于 2006-9-14 18:20:26 | 显示全部楼层
事实上可以用查询的方式来写EEPROM,不用死等的,做一个标志就好了。实时性是可以保证的。关键的问题是,如果采用楼主的方法来做,那么程序就会很大,占用很多空间,对于单片机,如果要处理很多事情的话,这样的代码也许写不了多少功能就把flash占满了。



极度讨厌AVR的代码编译器。 一个nop竟然要占用2bytes

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

出0入0汤圆

发表于 2006-9-15 09:25:48 | 显示全部楼层
软件中写EEPROM不会经常发生吧?通常只是设置的时候会写,不然的话,早把芯片写坏了。而且AVR的EEPROM没有保护,我做过实验,只要程序中有写EEPROM的函数,程序跑飞以后,里面的数据就变成乱七八糟的了。我用干扰使AVR死机,EEPROM被乱写的概率是100%。而且有时候写的不是我规定写的地址。我是不敢在产品里用AVR的EEPROM了

出0入0汤圆

发表于 2006-9-15 10:40:32 | 显示全部楼层
看了csgbf的发言,让我不得不加一个外部存贮器了!我程序需要把报警值写入EEPROM,以便开机可以读出来!

出0入0汤圆

发表于 2006-9-15 11:23:19 | 显示全部楼层
【28楼】 csgbf ?????

出0入0汤圆

发表于 2006-9-28 10:16:09 | 显示全部楼层
可惜是ICC的。有GCC版的吗?

出0入0汤圆

发表于 2006-10-25 08:31:36 | 显示全部楼层
to: csgbf



不会吧,真有那么不可靠吗?以前写过,没注意它的可靠性,但现在要做产品了,如果真如你所说,我可得重新考虑了.



请给点意见!谢谢!

出0入0汤圆

发表于 2006-10-25 14:24:46 | 显示全部楼层
我在我的程序里加上 #include <avr/eeprom.h> 后,一编译程序 AVR studio就出现 没有响应的情况,不加 那个头文件可以编译~!

出0入0汤圆

发表于 2006-10-25 14:38:17 | 显示全部楼层
求助:请问各位编写一个程序,程序包括对EEPROM的读写、A/D转换、PWM波形的产生(及控制PWM占空比的参数计算)、串行通信这几个模块。怎样把这些模块组织到一起,它的主程序要做那些事情?

出0入0汤圆

发表于 2006-10-25 16:10:26 | 显示全部楼层
的确好用,支持楼主~~~~~~

出0入0汤圆

发表于 2006-10-26 10:37:08 | 显示全部楼层
有哪位好心人帮帮我,我是AVR初学者,以前学的51。现在正用M16编程,遇到些麻烦。急需各位大哥大姐们帮助。先谢谢了。我的QQ号:290553375。

出0入0汤圆

发表于 2006-11-6 12:50:26 | 显示全部楼层
ding

出10入120汤圆

发表于 2006-11-6 14:26:01 | 显示全部楼层
楼主的程序风格不错



下面是我的24C04读写程序

//向AT24C04里写小于一页的数据

BOOL f24C04wri(uInt whAddr,uChar *rgbhBuf,uChar bhLength)

{

    uChar ih;

    uChar bhDevAddr=0xa0;                               //针对AT24C04

    BOOL  fWriteOk=cfFail;



    DelayUs();

    sbpWP=0;

    IicStart();

    if(whAddr>=0x0100)                                  //计算高256字节地址

        bhDevAddr|=0x02;

    for(;;)

    {

        if(cfFail==fIICsend(bhDevAddr))         break;

        if(cfFail==fIICsend(   whAddr))         break;

        for(ih=0;ih<bhLength;ih++)

            if(cfFail==fIICsend(rgbhBuf[ih]))   break;

        if(ih==bhLength)

            fWriteOk=cfTrue;

        break;

    }

    IicStop();

    vf05mS=cfFail;

    while(cfFail==vf05mS);

    vf05mS=cfFail;

    while(cfFail==vf05mS);

    sbpWP=1;

    return(fWriteOk);

}



//从AT24C04连续读数据

BOOL f24C04read(uInt whAddr,uChar *rgbhBuf,uChar bhLength)

{

    uChar ih;

    uChar bhDevAddr=0xa1;                               //针对AT24C04

    BOOL  fReadOk=cfFail;



    IicStart();

    if(whAddr>=0x0100)                                  //计算高256字节地址

        bhDevAddr|=0x02;

    for(;;)

    {

        if(cfFail==fIICsend(bhDevAddr&0xfe))    break;

        if(cfFail==fIICsend(   whAddr))         break;

        IicStart();

        if(cfFail==fIICsend(bhDevAddr))         break;

        for(ih=0;ih<bhLength;ih++)

        {

            rgbhBuf[ih]=bhIICscan();

            if(ih==(bhLength-1))                IICsendNotAck();

            else                                IICsendAck();

        }

        fReadOk=cfTrue;

        break;

    }

    IicStop();

    return(fReadOk);

}

出0入0汤圆

发表于 2006-11-7 14:28:51 | 显示全部楼层
好贴,顶。下一步正好要用eeprom.Thanks very  much!

出0入0汤圆

发表于 2006-12-30 10:07:11 | 显示全部楼层
好贴,支持一下

出0入0汤圆

发表于 2007-2-12 17:42:44 | 显示全部楼层
搂主的这个程序始终给人一种“小马拉大车”的感觉。

放着Flash不用,追求EEPROM的读写速度!你想把小马累死吗?

而且写出这么一大段代码,明明就是在用空间换时间嘛。

追求实时性的方法很多,死钻牛角尖的人还觉得很有成就感!

我还真是怀疑做什么产品的人会想到去弄这样一段程序呢?

你们老板一小时给你开多少钱让你没事儿在这里瞎弄啊?

出0入0汤圆

 楼主| 发表于 2007-3-7 11:35:25 | 显示全部楼层
To 楼上 ATmega128:

    我不是很明白你想批判我什么。

    首先我承认我写这么一大段代码,就是用空间换时间。我用的是ATmega128,有足够的空间给我用;然后我就不明白“放着Flash不用,追求EEPROM的读写速度!”这句话的意思了。因为我项目真的很在乎这一点时间,否则的话,系统运行的质量会差很多;同时数据需要掉电存储。你指的Flash是指ATmega128本身的片内Flash还是指片外的Flash?如果是ATmega128本身片内的Flash,那么要檫写的时间将比EEPROM更多,流程也会更复杂;如果使用片外的Flash,则会增加成本,同时,增加一个芯片,PCB基本摆不下了,会给PCB增加更大的压力。你没有见过需要用这么一段程序的产品,并不意味着它不存在;更让我奇怪的是,你怎么知道我是为老板打工而不是为自己打工?

出10入210汤圆

发表于 2007-3-7 13:06:05 | 显示全部楼层
TO: Louis Bright

41楼的应该是对EEPROM的概念不太了解,不要计较。

你的做法如果不好,ARMOK也不会给你“裤子”。

出0入0汤圆

发表于 2007-3-9 23:14:41 | 显示全部楼层
To  ATmega128 :

  批判也要有个度

  有时我们不要不懂装懂

  这是论坛 大家分享快乐的地方

  即使程序有毛病我们也要批判的接受

  和平共处

出0入0汤圆

发表于 2007-4-30 01:14:04 | 显示全部楼层
我正要用到EEPROM的读写,阅帖毕.受益良深.

我的程序对定时又有要求,受到它的影响会不会大?

         

                                  AVR初学者

出0入0汤圆

发表于 2007-5-10 22:21:53 | 显示全部楼层
支持一下楼主吧,谢谢

出0入0汤圆

发表于 2007-5-23 17:03:30 | 显示全部楼层
楼主的思路是建立了一个读写缓存区,读写实际都是在对这个区域进行操作,用查询标志代替查询等待延时 在一些接口模块的例程中也见过类似用法的

不知道我的理解对不.... 请高手指正!!!



本人是AVR初学者,有个问题请高手扫盲:

什么情况下可以执行这个中断,也就是说,到底满足什么条件才可以叫做EEPROM_REDAY.....,中断源是什么???

出0入0汤圆

发表于 2007-5-25 17:37:28 | 显示全部楼层
楼主的思路是建立了一个环形读写缓存区,读写实际都是在对这个区域进行操作的。



在实际应用中,要写的数据个数一般不定,那么按照楼主的程序,还必须再建立一个结构体缓冲区buffer,该缓冲区存放要写的数据及地址。

  在主程序中如果有“写标志”存在,就从缓冲区buffer[]中取出地址和数据,然后调用楼主的子程序bWriteData2Eeprom(wAddress,bVal),随即触发EEPROM中断。



    if (write_mark)

    {

          wAddress = buffer[ptr].addr;

          bVal = buffer[ptr].bVal;

          bWriteData2Eeprom(wAddress,bVal);

          ptr++;   // 指针

          if (ptr > 发送缓冲区个数)  

          {

             write_mark = FALSE;

             ptr =0;

          }

    }

出0入0汤圆

发表于 2007-6-5 15:49:32 | 显示全部楼层
与CVAVR的生成的UART读写函数有异曲同工之妙.

这个程序很有显示意义啊...

出0入0汤圆

发表于 2007-8-14 16:04:00 | 显示全部楼层
有没有用I2C指令写过, 读1302的程序呀, 我急用呀, 又不用, 是BASCOM语言的

有的话, 请大家发表一下

出0入0汤圆

发表于 2007-10-15 14:22:44 | 显示全部楼层
to LZ
这个缓冲池 写的不错 本来我也想该成这样的
现在有了
就照抄了 多谢多谢!!!

出0入0汤圆

发表于 2007-10-15 15:09:41 | 显示全部楼层
幽游梦蝶写的EEPROM读写函数确实不错,在工业控制中采用最多的协议是MODBUS协议,
MODBUS协议有预置单个寄存器和预置多个寄存器命令,因此每次写几个字节是不定的,
因此必须定义一个队列(或堆栈)存放要写的数据,既然必须定义这个队列或者堆栈,
为节省CPU资源,当然最好采用EEPROM就绪中断写EEPROM了。

struct ELEMENT
{
 unsigned char bVal;//待写数值
 unsigned int Addr;//EEPROM地址
};
//堆栈用来存储待写数据
struct EEPROM_STACK
{
        struct ELEMENT element[100];
        unsigned char stack_SP;
};


以下是我的EEPROM程序,采用EEPROM就绪中断方式写EEPROM。
#pragma interrupt_handler vIvEeReady:iv_EE_READY
void vIvEeReady(void)  
{  
  struct ELEMENT element;

  if (!STACK_EMPTY())//堆栈是否为空
    {  
    STACK_POP(&element);//从堆栈中弹出地址和数值
        EEAR = element.Addr;  
        EEDR = element.bVal;  
        EECR |= BIT(EEMWE);  
        EECR |= BIT(EEWE);  
    }  
    else //已经写完
    {  
        EECR &= ~BIT(EERIE); //关闭EEPROM就绪中断
    }  
}  


////触发EEPROM就绪中断
void EEPROM_START(void)  
{  
  if (!STACK_EMPTY())//如果堆栈不空
    {
        CLI();
        EEAR = 0;
        EECR |= BIT(EERIE); // Sure the EEPIE is enable even if do the thing before
        SEI();
  }
}


void main()
{
   //系统初始化
   while (1)
   {
         if (有写EEPROM标志)
         {
             EEPROM_START();//触发EEPROM就绪中断
             //清除写EEPROM标志
          }
   }
}

出0入0汤圆

发表于 2007-10-25 21:41:43 | 显示全部楼层
有没有访问flash的读写程序?

出0入0汤圆

发表于 2007-10-25 22:38:06 | 显示全部楼层
我用自己写的读写函数,另外我不等待,其实也算是采用了缓存。写完后立刻去做别的事情,相当于分时操作。有要写的数据会产生一个标志,在时间到的时候调用写函数,如果EEPROM不忙,则写入数据返回,忙则等待下一个循环。另外对于FLASH读不是非常复杂,用库函数也行,但要是写就麻烦了。自己看一下手册。关键是SPM指令的位置和页写入的情况。

出0入0汤圆

发表于 2008-4-9 18:13:34 | 显示全部楼层
感谢分享~~~~~~~~~

出0入42汤圆

发表于 2008-4-9 19:21:39 | 显示全部楼层
恩,记号

出0入0汤圆

发表于 2008-4-11 16:01:22 | 显示全部楼层
我在IAR里面调了下都几好用的。谢谢了!
c文件
#define _DRVEEPROM_C_

#include "DRVEEPROM.h"
#include "main.h"

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

typedef struct
{
    INT16U wAddress;
    INT8U bVal;
}S_EEPROM_POSITION;

S_EEPROM_POSITION volatile _sEepromBuf[EEPROM_WRITE_BUF_SIZE];
INT8U volatile _bEepromBufWrPtr;
INT8U volatile _bEepromBufRdPtr;
INT8U volatile _bEepromBufNs;
#define RET_BUSY 0
#define RET_SUCCESS 1
#define FALSE 0
#define fgEepromBufEmpty()      (_bEepromBufNs == 0)
#define fgEepromBufFull()       (_bEepromBufNs == EEPROM_WRITE_BUF_SIZE)

extern float frequency;

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

#pragma vector=EE_RDY_vect
__interrupt void vIvEeReady(void)
{
    frequency++;
    if(!fgEepromBufEmpty())     //如果有数据写入,开始写操作
    {
        EEAR = _sEepromBuf[_bEepromBufRdPtr].wAddress;
        EEDR = _sEepromBuf[_bEepromBufRdPtr].bVal;
        EECR |= (1<<EEMWE);    //EEPROM主机写使能
        EECR |= (1<<EEWE);     //EEPROM写使能
        _bEepromBufRdPtr++;    //个数递加
        if(_bEepromBufRdPtr >= EEPROM_WRITE_BUF_SIZE)
        {
            _bEepromBufRdPtr = 0;
        }
        _bEepromBufNs--;       //要写标记数减一
    }
    else                       //关EEPROM中断使能
    {
        EECR &= ~(1<<EERIE);   
    }
}

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

INT8U bWriteData2Eeprom(INT16U wAddress, INT8U bVal)
{
    if(fgEepromBufFull())     //要写标记数满。
    {
        return RET_BUSY;
    }

    _CLI();
    _sEepromBuf[_bEepromBufWrPtr].wAddress = wAddress;
    _sEepromBuf[_bEepromBufWrPtr].bVal = bVal;
    _bEepromBufWrPtr++;
    if(_bEepromBufWrPtr >= EEPROM_WRITE_BUF_SIZE) //缓存满,清零
    {
        _bEepromBufWrPtr = 0;
    }
    _bEepromBufNs++;                              //写标记数递加
    EECR |= (1<<EERIE); // Sure the EEPIE is enable even if do the thing before
    _SEI();

    return RET_SUCCESS; //写成功
}


// Notice! The programer must ensure the EEPROM is NOT in writing state.
INT8U bReadDataFromEeprom(INT16U wAddress, INT8U *pbVal)
{
    if(EECR & (1<<EEWE)) //当正在写的时候推出
    {
        return RET_BUSY;
    }

    EEAR = wAddress;
    EECR |= (1<<EERE);
    *pbVal = EEDR;
    return RET_SUCCESS;
}


INT8U bGetEepromWrBuf(void) //取写的个数
{
    return _bEepromBufNs;
}


void bClrEepromBuf(void)   //清理缓存区
{
    _CLI();
    _bEepromBufNs = 0;
    _bEepromBufRdPtr = _bEepromBufWrPtr;
    EECR &= ~(1<<EERIE);
    _SEI();
}


INT8U fgEepromWizard(void) //失败
{
    INT8U fgRet = FALSE;

    return fgRet;
}


void vInitEeprom(void)    //初始化EEPROM读写
{
    EECR = 0x00;
    _bEepromBufWrPtr = 0;
    _bEepromBufRdPtr = 0;
    _bEepromBufNs    = 0;
}
头文件:
#ifndef _DRVEEPROM_H_
#define _DRVEEPROM_H_

#ifdef  _DRVEEPROM_C_
#define EXTERN
#else
#define EXTERN  extern
#endif



#define EEPROM_WRITE_BUF_SIZE   64

EXTERN unsigned char bWriteData2Eeprom(unsigned int wAddress, unsigned char bVal);
EXTERN unsigned char bReadDataFromEeprom(unsigned int wAddress, unsigned char *pbVal);
EXTERN unsigned char bGetEepromWrBuf(void);
EXTERN void bClrEepromBuf(void);
EXTERN unsigned char fgEepromWizard(void);
EXTERN void vInitEeprom(void);

#undef  EXTERN
#endif

出0入0汤圆

发表于 2008-4-17 14:53:39 | 显示全部楼层
刚开始学,还真是什么都不懂啊!以后拜托大家多多关照啦!o(∩_∩)o...

出0入0汤圆

发表于 2008-9-5 20:16:34 | 显示全部楼层
严重支持LZ

出0入0汤圆

发表于 2008-9-9 10:37:32 | 显示全部楼层
坚决支持楼主

出0入0汤圆

发表于 2008-9-22 17:22:54 | 显示全部楼层
还是在多任务中使用Eprom比较爽&nbsp;写完一个字节之后就OSTimeDly&nbsp;去忙其它事情

出0入0汤圆

发表于 2008-9-23 00:32:52 | 显示全部楼层
标记!!!

出0入0汤圆

发表于 2008-11-5 20:37:01 | 显示全部楼层
有时间在认真研读

出0入0汤圆

发表于 2008-11-23 11:32:07 | 显示全部楼层

楼主写得不错

出0入0汤圆

发表于 2008-11-25 15:04:30 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-1-9 10:37:39 | 显示全部楼层
坚决支持楼主

出0入0汤圆

发表于 2009-1-10 00:46:44 | 显示全部楼层
小弟目前程序中还没有涉及到与EEPROM有关的操作(^_^)。日后有时间再回头拜读楼主的大帖!

出0入0汤圆

发表于 2009-6-9 09:46:22 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-6-9 13:04:51 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-6-27 16:29:05 | 显示全部楼层
学习

出0入0汤圆

发表于 2009-6-28 12:47:57 | 显示全部楼层
学习下

出0入0汤圆

发表于 2009-6-29 22:36:35 | 显示全部楼层
Mark ,要用时再翻

出0入0汤圆

发表于 2009-9-15 16:31:19 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-9-20 14:52:04 | 显示全部楼层
m

出0入0汤圆

发表于 2009-9-20 16:49:54 | 显示全部楼层
MMMMM

出0入0汤圆

发表于 2009-10-26 11:17:25 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-10-27 17:07:37 | 显示全部楼层
好方法

出0入0汤圆

发表于 2009-11-30 17:13:45 | 显示全部楼层
好贴!
实时性要求比较高的条件下,还是要考虑这个时间的!
当然,在提前知道的情况下,可以考虑加硬件EEPROM(画PCB之前),

出0入0汤圆

发表于 2009-11-30 21:30:01 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-11-30 21:30:02 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-22 15:27:08 | 显示全部楼层
做一下标记

出0入0汤圆

发表于 2009-12-22 17:09:43 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-22 21:01:39 | 显示全部楼层
【41楼】 ATmega128
积分:1
派别:
等级:------
来自:
搂主的这个程序始终给人一种“小马拉大车”的感觉。
放着Flash不用,追求EEPROM的读写速度!你想把小马累死吗?
而且写出这么一大段代码,明明就是在用空间换时间嘛。
追求实时性的方法很多,死钻牛角尖的人还觉得很有成就感!
我还真是怀疑做什么产品的人会想到去弄这样一段程序呢?
你们老板一小时给你开多少钱让你没事儿在这里瞎弄啊?  

这位老兄有点过了吧。楼主的方法还是非常科学的。
而且窃以为这是最好的方案之一了。

出0入0汤圆

发表于 2009-12-22 21:11:54 | 显示全部楼层
标记。。。。

出0入0汤圆

发表于 2010-1-2 00:01:10 | 显示全部楼层
最近比较有空,又想起这个话题,楼主提到方法其实就是异步操作;而其他朋友使用的库函数是同步操作(相信大家对同步和异步的区别是了解的,这儿不多说)。
对于嵌入系统,同步和异步操作都存在,比如,对于端口的置位和清位,多是同步操作,为啥? 因为这个操作非常快。函数可以马上返回。而外部总线访问,eeprom访问由于外部控制器或者eeprom control的原因,往往无法在函数调用返回的时候得到执行结果。上面分析的写eeprom 需要8.5ms。也就是说,对于同步操作CPU要等待慢外设完成工作。如果是异步模式,函数可以马上返回,但是在8.5ms后该操作才完成。这个操作可以交给后台(eeprom_task来完成),这样eeprom的读写也就简化为把读写缓冲区以及地址,和操作请求发给eeprom_Task,在下一次eeprom_task调度的时候,开始真正的工作。
watercat质疑这样操作的原子性,当然,原子性是要付出时间代价的。当然了,一个良好的eeprom读写驱动应该有机制保证操作的可靠性。有兴趣的朋友可以参考autosar 的关于eeprom driver &handler的相关规范。

出0入0汤圆

发表于 2010-1-3 19:38:17 | 显示全部楼层
标记

出0入0汤圆

发表于 2010-1-3 21:22:46 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-4 15:15:09 | 显示全部楼层
回复【1楼】Louis Bright 幽游梦蝶
-----------------------------------------------------------------------

你好,我现在在遇到一个问题EEPROM不行,请教高手指导一下吧,你有别的邮箱,或QQ联系吗

出0入0汤圆

发表于 2010-1-4 23:51:56 | 显示全部楼层
【88楼】 caqxx923
积分:8
派别:
等级:------
来自:
回复【1楼】Louis Bright 幽游梦蝶
-----------------------------------------------------------------------

你好,我现在在遇到一个问题EEPROM不行,请教高手指导一下吧,你有别的邮箱,或QQ联系吗  


把问题直接写在这里呗。

出0入0汤圆

发表于 2010-1-5 08:32:17 | 显示全部楼层
玛瑞咖。

出0入0汤圆

发表于 2010-1-5 16:43:35 | 显示全部楼层

出0入0汤圆

发表于 2010-2-9 22:34:10 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-2-10 09:37:17 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-3-9 15:38:57 | 显示全部楼层
马克

出0入0汤圆

发表于 2010-3-10 13:41:01 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-15 15:18:17 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-15 16:12:39 | 显示全部楼层
回复【85楼】modelfly  
最近比较有空,又想起这个话题,楼主提到方法其实就是异步操作;而其他朋友使用的库函数是同步操作(相信大家对同步和异步的区别是了解的,这儿不多说)。
对于嵌入系统,同步和异步操作都存在,比如,对于端口的置位和清位,多是同步操作,为啥? 因为这个操作非常快。函数可以马上返回。而外部总线访问,eeprom访问由于外部控制器或者eeprom control的原因,往往无法在函数调用返回的时候得到执行结果。上面分析的写eeprom 需要8.5ms。也就是说,对于同步操作CPU要等待慢外设完成工作。如果是异步模式,函数可以马上返回,但是在8.5ms后该操作才完成。这个操作可以交给后台(eeprom_task来完成),这样eeprom的读写也就简化为把读写缓冲区以及地址,和操作请求发给eeprom_Task,在下一次eeprom_task调度的时候,开始真正的工作。
watercat质疑这样操作的原子性,......
-----------------------------------------------------------------------

牛!兄弟是汽车行业的啊。

出0入0汤圆

发表于 2010-5-15 00:07:38 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-6-9 14:45:27 | 显示全部楼层
写EEPROM过来看看

出0入0汤圆

发表于 2010-6-9 18:02:00 | 显示全部楼层
jihao
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-6 09:58

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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