|
发表于
2010-3-18 12:43:51
|
显示全部楼层
<div class=\"memproto\"><table class=\"memname\"><tbody><tr><td class=\"memname\">static __inline__ void eeprom_write_block </td><td>(</td><td class=\"paramtype\">const void * </td><td class=\"paramname\"><em>__src</em>, </td></tr><tr><td class=\"paramkey\"></td><td></td><td class=\"paramtype\">void * </td><td class=\"paramname\"><em>__dst</em>, </td></tr><tr><td class=\"paramkey\"></td><td></td><td class=\"paramtype\">size_t </td><td class=\"paramname\"><em>__n</em></td><td> </td></tr><tr><td></td><td>)</td><td></td><td></td><td><code>[static]</code></td></tr></tbody></table></div><div class=\"memdoc\"><p>Write a block of <em>__n</em> bytes to EEPROM address <em>__dst</em> from <em>__src</em>. </p><dl class=\"note\" compact=\"compact\"><dt><b>Note:</b>
</dt><dd>The argument order is mismatch with common functions like <a class=\"el\" title=\"Copy a string.\" href=\"http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html#g54e4f23104fa6f722f9459d2673a1eba\">strcpy()</a>. </dd></dl></div><p></p><p>[转贴]</p><p><table class=\"center_tdbgall\" cellspacing=\"0\" cellpadding=\"0\" width=\"760\" align=\"center\" border=\"0\" style=\"WORD-BREAK: break-all;\"><tbody><tr valign=\"middle\" align=\"center\"><td class=\"main_ArticleTitle\" colspan=\"2\" height=\"50\" style=\"WORD-BREAK: break-all;\">AVR单片机EEPROM的操作</td></tr><tr valign=\"middle\" align=\"center\"><td class=\"main_ArticleSubheading\" colspan=\"2\" style=\"WORD-BREAK: break-all;\">AVR单片机EEPROM的操作</td></tr><tr align=\"center\"><td class=\"Article_tdbgall\" colspan=\"2\">作者:未知 AVR单片机来源:<a href=\"file:///D:/ShowCopyFrom.asp?ChannelID=1002&SourceName=网络\">网络</a> 点击数:<script language=\"javascript\" src=\"/avr/GetHits.asp?ArticleID=808\"></script> 更新时间:2007-10-11 <a title=\"收藏的网页将被永久地保存到新浪ViVi收藏夹http://vivi.sina.com.cn\" href=\"javascript:d=document;t=d.selection?(d.selection.type!=\'None\'?d.selection.createRange().text:\'\') d.getSelection?d.getSelection():\'\');void(vivi=window.open(\'http://vivi.sina.com.cn/collect/icollect.php?pid=2008&title=\'+escape(d.title)+\'&url=\'+escape(d.location.href)+\'&desc=\'+escape(t),\'vivi\',\'scrollbars=no,width=480,height=480,left=75,top=20,status=no,resizable=yes\'));vivi.focus();\"><img src=\"file:///D:/images/vivi_coop1.gif\" align=\"absMiddle\" border=\"0\" alt=\"\"/></a></td></tr><tr><td class=\"main_tdbg_760\" id=\"fontzoom\" valign=\"top\" colspan=\"2\" height=\"300\" style=\"WORD-BREAK: break-all;\"><table cellspacing=\"0\" cellpadding=\"10\" align=\"left\" border=\"0\"><tbody><tr><td><!--Element not supported - Type: 8 Name: #comment--></td></tr></tbody></table>本程序简单的示范了如何使用ATMEGA16的EERPOM<br/> EEPROM的简介<br/> EEPROM的写操作<br/> EEPROM的读操作 <p>出于简化程序考虑,各种数据没有对外输出,学习时建议使用JTAG ICE硬件仿真器<br/> 在打开调试文件到JTAG后,<br/> 打开Debug -> JTAG ICE Options菜单,<br/> 然后在JTAG ICE Properties中点击Dbug页面,将preserve eeprom选项选中。<br/> 在每次仿真调试时候,就保护EEPROM内容了。<br/> 否则,会按照默认设置擦除EEPROM的内容。 <br/> <br/> 由于定义了EEPROM变量,JTAG调试时会询问是否初始化EEPROM,请选择[否]<br/> <br/> EEPROM的数据也可以在view->memory,选Eeprom窗口下察看<br/>*/</p><p>#include <avr/io.h><br/>#include <avr/eeprom.h><br/>////时钟定为内部1MHz,F_CPU=1000000 时钟频率对程序的运行没什么影响<br/>/*<br/>GCCAVR(avr-libc)里面自带了EEPROM的读写函数。<br/>下面列举部分常用函数(原型)</p><p>#define eeprom_is_ready() bit_is_clear(EECR, EEWE)<br/>检测EEPROM是否准备好。OK返回1(返回EEWE位)</p><p>#define eeprom_busy_wait() do {} while (!eeprom_is_ready())<br/>等待EEPROM操作完成</p><p>extern uint8_t eeprom_read_byte (const uint8_t *addr);<br/>读取指定地址的一个字节8bit的EEPROM数据</p><p>extern uint16_t eeprom_read_word (const uint16_t *addr);<br/>读取指定地址的一个字16bit的EEPROM数据</p><p>extern void eeprom_read_block (void *buf, const void *addr, size_t n);<br/>读取由指定地址开始的指定长度的EEPROM数据</p><p>extern void eeprom_write_byte (uint8_t *addr, uint8_t val);<br/>向指定地址写入一个字节8bit的EEPROM数据</p><p>extern void eeprom_write_word (uint16_t *addr, uint16_t val);<br/>向指定地址写入一个字16bit的EEPROM数据</p><p>extern void eeprom_write_block (const void *buf, void *addr, size_t n);<br/>由指定地址开始写入指定长度的EEPROM数据</p><p>但不支持部分AVR,原文如下:<br/>\\note This library will \\e not work with the following devices since these<br/> devices have the EEPROM IO ports at different locations:</p><p> - AT90CAN128<br/> - ATmega48<br/> - ATmega88<br/> - ATmega165<br/> - ATmega168<br/> - ATmega169<br/> - ATmega325<br/> - ATmega3250<br/> - ATmega645<br/> - ATmega6450<br/>*/</p><p>/*<br/>在程序中对EEPROM 操作有两种方式<br/>方式一:直接指定EERPOM 地址<br/> 即读写函数的地址有自己指定,用于需要特定数据排列格式的应用中<br/>方式二:先定义EEPROM 区变量法<br/> 在这种方式下变量在EEPROM <b style=\"FONT-WEIGHT: normal; CURSOR: hand; COLOR: #0000ff; TEXT-DECORATION: underline;\">存储器</b>内的具体地址由编译器自动分配。<br/> 相对方式一,数据在EEPROM 中的具体位置是不透明的。<br/> 为EEPROM 变量赋的初始值,编译时被分配到.eeprom 段中,<br/> 可用avr-objcopy 工具从.elf文件中提取并产生ihex 或binary 等格式的文件,<br/> 从而可以使用编程器或下载线将其写入到器件的EEPROM 中。<br/> 实际上WINAVR 中MFILE 生成的MAKEFILE 已经为我们做了这一切。<br/> 它会自动生成以 “.eep” 为后缀的文件,通常它是iHex 格式<br/> (这次测试发现 分配地址是从0x0000开始的,故增加了一个EEPROM变量Evalvoid[16])<br/> <br/>如果同时使用方式1和2,请注意防止地址重叠,自己指定的地址应该选在后面。<br/>*/</p><p>//全局变量<br/>unsigned char <a class=\"channel_keylink\" href=\"http://www..com/Article/Index.html\">EDA</a>TA;</p><p>unsigned char ORGDATA[16]={0x00,0x02,0x04,0x06,0x08,0x0A,0x0C,0x0E,<br/> 0x01,0x03,0x05,0x07,0x09,0x0B,0x0D,0x0F}; //原始数据</p><p>unsigned char CMPDATA[16]; //比较数据</p><p>//仿真时在watch窗口,监控这些全局变量。</p><p>//EEPROM 变量定义<br/>unsigned char Evalvoid[16] __attribute__((section(\".eeprom\"))); //这个没用到<br/>unsigned char Eval[16] __attribute__((section(\".eeprom\")));</p><p><font color=\"#ff0000\">int main(void)<br/>{<br/> eeprom_write_byte (0x40,0xA5); //向EEPROM的0x40地址写入数据 0xA5<br/> <a class=\"channel_keylink\" href=\"http://www..com/Article/Index.html\">EDA</a>TA=eeprom_read_byte (0x40); //读出,然后看看数据对不对?<br/> //上面两句编译是有如下警告,但不必理会<br/> //EEPROM_main.c:103: warning: passing arg 1 of `eeprom_write_byte\' makes pointer from integer without a cast<br/> //EEPROM_main.c:104: warning: passing arg 1 of `eeprom_read_byte\' makes pointer from integer without a cast<br/></font> <br/> eeprom_write_block (&ORGDATA[0], &Eval[0], 16); //块写入<br/> //看看EEPROM数据是否是能失电永久保存,可以注释上面这句程序(不写入,只是读出),然后编译,烧写,断电(一段时间),上电,调试。<br/> eeprom_read_block (&CMPDATA[0],&Eval[0], 16); //块读出,然后看看数据对不对?<br/> <br/> while (1); <br/>}</p><p>/*<br/>ATmega16 包含512 字节的EEPROM 数据存储器。<br/>它是作为一个独立的数据空间而存在的,可以按字节读写。<br/>EEPROM的寿命至少为100,000 次擦除周期。<br/>EEPROM的访问由地址寄存器EEAR、数据寄存器EEDR和控制寄存器EECR决定。<br/>也可以通过ISP和JTAG及并行电缆来固化EEPROM数据</p><p>EEPROM数据的读取:<br/> 当EEPROM地址设置好之后,需置位EERE以便将数据读入EEDR。<br/> EEPROM数据的读取需要一条指令,且无需等待。<br/> 读取EEPROM后CPU 要停止4 个时钟周期才可以执行下一条指令。<br/>注意:用户在读取EEPROM 时应该检测EEWE。如果一个写操作正在进行,就无法读取EEPROM,也无法改变寄存器EEAR。</p><p>EEPROM数据的写入:<br/>1 EEPROM的写访问时间(自定时时间,编程时间)<br/> 自定时功能可以让用户软件监测何时可以开始写下一字节。(可以采用中断方式)<br/> 经过校准的1MHz片内振荡器用于EEPROM定时,不倚赖CKSEL熔丝位的设置。<br/> 改变OSCCAL寄存器的值会影响内部RC振荡器的频率因而影响写EEPROM的时间。<br/> EEPROM自定时时间约为8.5 ms 即1MHz片内振荡器的8448个周期<br/>注意:这个时间是硬件定时的,数值比较保险,其实真正的写入时间根本就用不了8.5mS那么长,而且跟电压有关,但芯片没有提供其他的检测编程完成的方法 <br/> 这个问题表现在旧版的AT90S系列上面,由于没有自定时,数值定得太短,ATMEL给人投诉到头都爆,呵呵!<br/>参考: 《用ATmega8535替换AT90S8535》文档里面的<br/> 写EEPROM定时的改进<br/> 在AT90S8535中写EEPROM的时间取决于供电电压,通常为<a href=\"mailto:2.5ms@VCC=5V\"><font color=\"#0000ff\">2.5ms@VCC=5V</font></a>,<a href=\"mailto:4ms@VCC=2.7V\"><font color=\"#0000ff\">4ms@VCC=2.7V</font></a>。<br/> ATmega8535中写EEPROM的时间为8448个校准过的RC振荡器周期 (与系统时钟的时钟源和频率无关)。<br/> 假定校准过的RC振荡器为1.0MHz,则写时间的典型值为8.4ms,与VCC 无关。<br/> <br/>2 为了防止无意识的EEPROM 写操作,需要执行一个特定的写时序<br/> (如果使用编译器的自带函数,无须自己操心)<br/> 写时序如下( 第3 步和第4 步的次序并不重要):<br/> 1. 等待EEWE 位变为零<br/> 2. 等待SPMCSR 中的SPMEN 位变为零<br/> 3. 将新的EEPROM 地址写入EEAR( 可选)<br/> 4. 将新的EEPROM 数据写入EEDR( 可选)<br/> 5. 对EECR 寄存器的EEMWE 写\"1\",同时清零EEWE<br/> 6. 在置位EEMWE 的4 个周期内,置位EEWE<br/> 经过写访问时间之后,EEWE 硬件清零。<br/> 用户可以凭借这一位判断写时序是否已经完成。<br/> EEWE 置位后,CPU要停止两个时钟周期才会运行下一条指令。</p><p><br/>注意:<br/> 1:在CPU 写Flash 存储器的时候不能对EEPROM 进行编程。<br/> 在启动EEPROM 写操作之前软件必须检查 Flash 写操作是否已经完成<br/> 步骤(2) 仅在软件包含引导程序并允许CPU对Flash 进行编程时才有用。<br/> 如果CPU 永远都不会写Flash,步骤(2) 可省略。<br/> <br/> 2:如果在步骤5 和6 之间发生了中断,写操作将失败。<br/> 因为此时EEPROM 写使能操作将超时。<br/> 如果一个操作EEPROM的中断打断了另一个EEPROM操作,EEAR 或EEDR寄存器可能被修改,引起EEPROM 操作失败。<br/> 建议此时关闭全局中断标志I。<br/> 经过写访问时间之后,EEWE 硬件清零。用户可以凭借这一位判断写时序是否已经完成。<br/> EEWE 置位后,CPU要停止两个时钟周期才会运行下一条指令。<br/> </p><p>在掉电休眠模式下的EEPROM写操作<br/> 若程序执行掉电指令时EEPROM 的写操作正在进行, EEPROM 的写操作将继续,并在指定的写访问时间之前完成。<br/> 但写操作结束后,振荡器还将继续运行,<a class=\"channel_keylink\" href=\"http://www..com/\">单片机</a>并非处于完全的掉电模式。因此在执行掉电指令之前应结束EEPROM 的写操作。<br/> <br/>防止EEPROM数据丢失<br/> 若电源电压过低,CPU和EEPROM有可能工作不正常,造成EEPROM数据的毁坏(丢失)。<br/> **这种情况在使用独立的EEPROM 器件时也会遇到。因而需要使用相同的保护方案。<br/> 由于电压过低造成EEPROM 数据损坏有两种可能:<br/> 一是电压低于EEPROM 写操作所需要的最低电压;<br/> 二是CPU本身已经无法正常工作。<br/> EEPROM 数据损坏的问题可以通过以下方法解决:<br/> 当电压过低时保持AVR RESET信号为低。<br/> 这可以通过使能芯片的掉电检测电路BOD来实现。如果BOD电平无法满足要求则可以使用外部复位<a class=\"channel_keylink\" href=\"http://www..com/\">电路</a>。<br/> 若写操作过程当中发生了复位,只要电压足够高,写操作仍将正常结束。<br/> (EEPROM在2V低压下也能进行写操作---有可以工作到1.8V的AVR芯片)<br/> <br/>掉电检测BOD的误解<br/> AVR自带的BOD(Brown-out Detection)<a class=\"channel_keylink\" href=\"http://www..com/\">电路</a>,作用是在电压过低(低于设定值)时产生复位信号,防止CPU意外动作.<br/> 对EEPROM的保护作用是当电压过低时保持RESET信号为低,防止CPU意外动作,错误修改了EEPROM的内容<br/> <br/> 而我们所理解的掉电检测功能是指 具有预测功能的可以进行软件处理的功能。<br/> 例如,用户想在电源掉电时把SRAM数据转存到EEPROM,可行的方法是 <br/> 外接一个在4.5V翻转的电压比较器(VCC=5.0V,BOD=2.7V),输出接到外部中断引脚(或其他中断)<br/> 一但电压低于4.5V,马上触发中断,在中断服务程序中把数据写到EEPROM中保护起来<br/> 注意: 写一个字节的EEPROM时间长达8mS,所以不能写入太多数据,电源滤波电容也要选大一些</p></td></tr></tbody></table></p> |
|