|
今天被mega16片内eeprom烦了一天,程序错误莫名其妙。让我感到抓狂的是我的程序是直接复制了用户手册上的C程序。程序如下:(Page19)
void EEPROM_write(unsigned int uiAddress, unsigned char ucData)
{
while(EECR & (1<<EEWE)); /* 等待上一次写操作结束 */
EEAR = uiAddress; /* 设置地址和数据寄存器*/
EEDR = ucData; /* 置位EEMWE */
EECR |= (1<<EEMWE); /* 置位EEWE 以启动写操作*/
EECR |= (1<<EEWE);
}
去网上找资料,多为推荐使用winavr自带的函数。后来反复修改编译烧写一度使我的M16擦写寿命少了一百多次,依然无果。
后来再仿真的时候,忽然发现EEMWE和EEWE永远不可能会同时置位,也就是说,EEWE置位永远在EEMWE清零之后。
不管改成 EECR |= (1<<EEMWE)|(1<<EEWE);还是将两者换位都不行;
(虽然资料上说在EEMWE置位后4个周期就会被清零,所以EEWE必须在EEMWE置位后立即将EEWE置位,但是大家注意上面的程序这两句是上下行的。。)
于是上网搜类似的情况,终于发现有位仁兄和我有同样的疑问 http://bbs.avrvi.com/simple/t16903.html
按照他的说法“于是变半信半疑的用AVR Simulator看看EECR|=_BV(EEWE)这条语句所消耗的时间,一看发现其用了5个时钟周期,这时我便豁然开朗了,但是我还是想不通为什么EECR|=_BV(EEWE)这条语句会要5个时钟周期这么长,于是便看了这条语句的反汇编,发现Winavr读写I/O寄存器(包括EECR)全是用的LDI,STD指令,即把I/O寄存器当做SRAM处理,而没有用OUT指令,导致其需要花费5个时钟周期,这里要提醒大家注意,Winavr把所有的I/O寄存器都当做SRAM来处理,其花费的时钟周期较长。至此问题已经找出,于是便开始想解决办法,我想到的是在C里面嵌入汇编来做。”
就上下两行的程序,编译之后竟然要花大于4周期的时间。。。。。
于是我根据他的程序做了点修改,嵌入汇编,程序如下:
void EEPROM_Write(uint waddr,uchar wdata)
{
while(EECR & (1 << EEWE));
EEAR = waddr;
EEDR = wdata;
asm volatile("PUSH R16" "\n\t"
"LDI R16,0x04" "\n\t"
"OUT 0x1C,R16" "\n\t" //EECR |= (1 << EEMWE);
"LDI R16,0x06" "\n\t"
"OUT 0x1C,R16" "\n\t" //EECR |= (1 << EEWE);
"POP R16");
}
就可以用了,由于没有学过AVR的指令集,结合了一些51的汇编,先将R16原来的压栈保护再谈栈,也不知写没写对,反正编译通过,可以正确写操作。说这个是BUG也许不恰当,但是对于初学者来说是相当悲剧的,明明是用户手册上的程序,写在ICC、IAR上可以用,在GCC上就不行给人一种编译器有问题的感觉、、、
关于eeprom的中断,做了之后发现,eeprom的中断貌似仅限于写操作的中断,假设I和EERIE置一后,只有当EEWE清零的时候的时候才会触发中断,即只有“写完”之后才会中断,而“读完”之后不会触发中断,这个只是作为初学者的个人理解,也许是错误的,仅供讨论。 |
阿莫论坛20周年了!感谢大家的支持与爱护!!
一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。
|