1.浅释E2Write函数
宋老师的例程lesson14_3和lesson14_4里的“E2Write(unsigned char *buf, unsigned char addr, unsigned char len)”书写内容是不一样的,lesson14_4的E2Write函数比lesson14_3的E2Write函数运行高效,lesson14_3的E2Write函数是每写入一个字节就要经历起始信号,停止信号,写入下一个字节又要把这些步骤经历一遍。而lesson14_4的E2Write函数支持EEPROM页写入(参考《手把手教你学51单片机》文档14.3.3节),所以在建立起文件时我们选用lesson14_4的E2Write函数。
大家新建两个文件“iic.c”和“iic.h”,我们把IIC的相关函数和EEPROM的相关函数都合成在“iic.c”这个单独文件里。
2.iic.c的代码
#include #include #include #define I2CDelay() {_nop_();_nop_();_nop_();_nop_();} /* 产生总线起始信号 */ void I2CStart() { I2C_SDA = 1; //首先确保SDA、SCL都是高电平 I2C_SCL = 1; I2CDelay(); I2C_SDA = 0; //先拉低SDA I2CDelay(); I2C_SCL = 0; //再拉低SCL } /* 产生总线停止信号 */ void I2CStop() { I2C_SCL = 0; //首先确保SDA、SCL都是低电平 I2C_SDA = 0; I2CDelay(); I2C_SCL = 1; //先拉高SCL I2CDelay(); I2C_SDA = 1; //再拉高SDA I2CDelay(); } /* I2C总线写操作,dat-待写入字节,返回值-从机应答位的值 */ unsigned char I2CWrite(unsigned char dat) { unsigned char ack; //用于暂存应答位的值 unsigned char mask; //用于探测字节内某一位值的掩码变量 for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行 { if ((mask&dat) == 0) //该位的值输出到SDA上 I2C_SDA = 0; else I2C_SDA = 1; I2CDelay(); I2C_SCL = 1; //拉高SCL I2CDelay(); I2C_SCL = 0; //再拉低SCL,完成一个位周期 } I2C_SDA = 1; //8位数据发送完后,主机释放SDA,以检测从机应答 I2CDelay(); I2C_SCL = 1; //拉高SCL ack = I2C_SDA; //读取此时的SDA值,即为从机的应答值 I2CDelay(); I2C_SCL = 0; //再拉低SCL完成应答位,并保持住总线 return (!ack); //应答值取反以符合通常的逻辑: //0=不存在或忙或写入失败,1=存在且空闲或写入成功 } /* I2C总线读操作,并发送应答或者非应答信号,返回值-读到的字节 */ unsigned char I2CReadNAK_OR_ACK(unsigned char nak_or_ack) { unsigned char mask; unsigned char dat; I2C_SDA = 1; //首先确保主机释放SDA for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行 { I2CDelay(); I2C_SCL = 1; //拉高SCL if(I2C_SDA == 0) //读取SDA的值 dat &= ~mask; //为0时,dat中对应位清零 else dat |= mask; //为1时,dat中对应位置1 I2CDelay(); I2C_SCL = 0; //再拉低SCL,以使从机发送出下一位 } I2C_SDA = nak_or_ack; //8位数据发送完后,传入的参数NAK_OR_ACK决定是否应答,为1不应答,为0应答 I2CDelay(); I2C_SCL = 1; //拉高SCL I2CDelay(); I2C_SCL = 0; //再拉低SCL完成非应答位,并保持住总线 return dat; } /* E2读取函数,buf-数据接收指针,addr-E2中的起始地址,len-读取长度 */ void E2Read(unsigned char *buf, unsigned char addr, unsigned char len) { do { //用寻址操作查询当前是否可进行读写操作 I2CStart(); if (I2CWrite(0x50<<1)) //应答则跳出循环,非应答则进行下一次查询 { break; } I2CStop(); } while(1); I2CWrite(addr); //写入起始地址 I2CStart(); //发送重复启动信号 I2CWrite((0x50<<1)|0x01); //寻址器件,后续为读操作 while (len > 1) //连续读取len-1个字节 { *buf++ = I2CReadNAK_OR_ACK(0); //最后字节之前为读取操作+应答 len--; } *buf = I2CReadNAK_OR_ACK(1); //最后一个字节为读取操作+非应答 I2CStop(); } /* E2写入函数,buf-源数据指针,addr-E2中的起始地址,len-写入长度 */ void E2Write(unsigned char *buf, unsigned char addr, unsigned char len) { while (len > 0) { //等待上次写入操作完成 do { //用寻址操作查询当前是否可进行读写操作 I2CStart(); if (I2CWrite(0x50<<1)) //应答则跳出循环,非应答则进行下一次查询 { break; } I2CStop(); } while(1); //按页写模式连续写入字节 I2CWrite(addr); //写入起始地址 while (len > 0) { I2CWrite(*buf++); //写入一个字节数据 len--; //待写入长度计数递减 addr++; //E2地址递增 if ((addr&0x07) == 0) //检查地址是否到达页边界,24C02每页8字节, { //所以检测低3位是否为零即可 break; //到达页边界时,跳出循环,结束本次写操作 } } I2CStop(); } } 3.iic.h的代码 #ifndef __IIC_H__ #define __IIC_H__ sbit I2C_SCL = P3^7; sbit I2C_SDA = P3^6; void I2CStart();//产生总线起始信号 void I2CStop(); //产生总线停止信号 unsigned char I2CWrite(unsigned char dat);//I2C总线写操作,dat-待写入字节,返回值-从机应答位的值 unsigned char I2CReadNAK_OR_ACK(unsigned char nak_or_ack);//I2C总线读操作,并发送应答或者非应答信号,返回值-读到的字节 void E2Read(unsigned char *buf, unsigned char addr, unsigned char len); //E2读取函数,buf-数据接收指针,addr-E2中的起始地址,len-读取长度 void E2Write(unsigned char *buf, unsigned char addr, unsigned char len);//E2写入函数,buf-源数据指针,addr-E2中的起始地址,len-写入长度 #endif 4.部分代码的修改 “unsigned char I2CReadACK()”和“unsigned char I2CReadNAK()”这里我们合成了一个函数为 “unsigned char I2CReadNAK_OR_ACK(unsigned char nak_or_ack)”,利用参数的传递决定是否产生应答。 那么在main.c中,几乎也是只需要出现EEPROM的写函数“E2Write()”和读函数“E2Read()”而已了。 记得把“iic”添加到工程文件中 5.main.c测试代码 #include #include #include #include void main() { unsigned char buf[]={"We can learn SCM well!"};//我们可以学好单片机 unsigned char str[sizeof(buf)]; //数组长度与buf的一样 InitLcd1602(); //初始化液晶 E2Write(buf,0x8E,sizeof(buf));//把buf数组里面的内容在EEPROM中从地址0x8E开始写,直到把数组里的内容全部写完进去,在EEPROM中保存起来 delay_ms(1000); //过1秒之后再读出里面的内容显示在液晶屏上 E2Read(str,0x8E,sizeof(buf)); //用另一个数组存取从EEPROM中读出的内容 LcdShowStr_len(0, 0,str, 16); LcdShowStr(0, 1, str+16+1); while(1); }
上一篇:51单片机-EEPROM简单使用
下一篇:51单片机-红外遥控
推荐阅读最新更新时间:2024-11-17 23:05
设计资源 培训 开发板 精华推荐
- BSP76智能低边电源开关典型应用电路
- LTC2266-12、12 位、80Msps 低功耗双通道 ADC 的典型应用
- 纯洁PCB尺子系列
- LTC2209UP 演示板,高中频,LVDS 输出,160Msps,16 位 ADC,80MHz < <160MHz
- 使用 Nuvoton Technology Corporation 的 W83L351YG 的参考设计
- M9328MX21ADS,专为 i.MX21 (MC9328MX21) 多媒体应用处理器设计的应用开发系统 (ADS)
- DC323A-A,用于 LTC1751EMS8-5 稳压电荷泵 DC/DC 转换器的演示板 @ 3V 至 5.5V 输入,5Vout @ 100mA
- esp8266_强电1路继电器
- 用于有线网络的 3.3V DC 到 DC 单路输出电源
- AS4950_TB67H450FNG 四路有刷电机H桥驱动板