历史上的今天
返回首页

历史上的今天

今天是:2024年08月27日(星期二)

正在发生

2021年08月27日 | 51单片机-EEPROM简单使用

2021-08-27 来源:eefocus

我们这一讲简要讲解如何往EEPROM的地址0x55写入一个数据,然后读出这个数据的内容。本讲代码围绕的是宋老师的lesson14_2例程代码讲解。


1.写入一个字节内容


如果要在EEPROM的某个地址里写入内容,那么步骤的实现归为:


起始信号→找到这个器件是否存在(寻址),发送的字节最低位要为0意为要往这个器件写内容→选择EEPROM的哪个地址去写→写入8位的数据→停止信号。


宋老师写的“void E2WriteByte(unsigned char addr, unsigned char dat)”函数里面,上一讲都讲解过里面的函数了,写入一个字节内容的讲解我们就介绍完了。


2.读出一个字节内容


在“unsigned char E2ReadByte(unsigned char addr)”中前面三个函数与“void E2WriteByte(unsigned char addr, unsigned char dat)”都是一样的操作步骤,选定好要读出哪个地址的内容,然后还需再重新发送起始信号,接着是把寻址的字节最低位设置为1意为要读出EEPROM的某个地址里面的内容,因为只读一个字节,所以单片机在接收完EEPROM发送回来的数据(这个数据就是当初写进去的数据)之后,不产生拉低应答(是单片机不产生应答,不是说EEPROM不产生应答),这样EEPROM就不会再发送数据回来了,达到了只读一个字节的功能。


所谓单片机读EEPROM的数据出来,其实就是EEPROM在SDA线上不停地拉高拉低变化,而单片机就是不断地判断第一位是0或者1,第二位是0或者1······,这个细节过程大家有能力的话可以一步步去解读宋老师写的“unsigned char I2CReadNAK()”和“unsigned char I2CReadACK()”,其实这两个函数只有一处不同,那就是“I2C_SDA = 1;”和“I2C_SDA = 0;”是否产生应答。如果大家一句句地很难理解这个函数,那么不妨直接省去理解,拿来运用就可以了。


3.独立代码


在main.c里复制以下代码,编译下载进开发板,我们做的实验就是先在EEPROM的0x55的地址里写入数据71(也就是字符‘G’的ASCII码值),然后再去读出这个数据,将字符‘G’显示在液晶屏上。


之前的章节有提过为了兼容性我们不打算使用bit型的数据类型,所以修改了一下宋老师的“bit I2CWrite(unsigned char dat)”函数,改为unsigned char类型。


那么我们就不能像宋老师那样写为“return (~ack);”,而是写为“return (!ack);”。unsigned char类型下,ack如果是0,那么~ack就为0xFF;ack如果是1,~ack就为0xFE,所以“~ack”的书写方式会导致返回值不能限定在0或者1之间,也就会使代码功能失效。


而“!ack”的表达就是说,当ack等于0时,!ack只会等于1,不会等于0xFF这些。当ack不等于0时(比如“ack=0xFE;”这些),那么!ack只会等于0,这样保证了返回值只有0和1。


#include  

#include //详见第六章第8讲

#include      //详见第十一章第3讲

#include

  

#define I2CDelay()  {_nop_();_nop_();_nop_();_nop_();}

sbit I2C_SCL = P3^7;

sbit I2C_SDA = P3^6;

  

/* 产生总线起始信号 */

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-待写入字节,返回值-从机应答位的值 */

u8 I2CWrite(unsigned char dat)

{

    u8 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()

{

    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 = 1;   //8位数据发送完后,拉高SDA,发送非应答信号

    I2CDelay();

    I2C_SCL = 1;   //拉高SCL

    I2CDelay();

    I2C_SCL = 0;   //再拉低SCL完成非应答位,并保持住总线

   

    return dat;

}

  

void main()

{  

    u8 str[2];

    InitLcd1602();//初始化液晶屏 

    while (1)

    {     

        I2CStart();

        I2CWrite(0x50<<1); //寻址器件,后续为写操作

        I2CWrite(0x55);    //写入要存储的地址为0x55

        I2CWrite('G');     //写入71这个数据,'G'的表达更加直观地表示要在液晶屏上显示的内容

        I2CStop();

     

        delay_ms(1000);    //等待一秒之后,准备读出这个数据

     

        I2CStart();

        I2CWrite(0x50<<1); //寻址器件,后续为写操作

        I2CWrite(0x55);    //选择要读出内容的地址为0x55

        I2CStart();        //发送重复启动信号

        I2CWrite((0x50<<1)|0x01); //寻址器件,后续为读操作

        str[0] = I2CReadNAK();    //读取这个器件0x55的地址里面的内容存放在数组的0号元素里

        I2CStop();

     

        LcdShowStr_len(7, 0, str, 1);//显示str[0]的内容

        while (1);//程序暂停运行

    }

}


为了更加直观了解这些过程,笔者并没有像宋老师那样给大家封装好写函数和读函数,这是为了大家不用在各个函数中跳来跳去地分析,在主函数里直接一步到位地讲解这些操作步骤,方便大家对这些细节的了解。


推荐阅读

史海拾趣

广东华裕(GDHY)公司的发展小趣事

机顶盒,全称为数字视频变换盒,是现代家庭娱乐中不可或缺的重要设备。从广义上讲,凡是与电视机连接的网络终端设备均可称为机顶盒,其发展历程涵盖了从模拟频道增补器到数字卫星、有线及IPTV机顶盒等多种形态。其核心功能在于接收并转换数字电视信号,使之能在传统或智能电视上播放,极大地丰富了用户的观看体验。

机顶盒不仅支持高清、4K乃至更高分辨率的视频播放,还具备强大的网络交互能力。用户可通过机顶盒接入互联网,享受在线购物、视频点播、游戏娱乐、社交媒体互动等多种服务。此外,部分机顶盒还集成了智能语音助手、家庭影院功能及存储能力,让家庭娱乐更加便捷、个性化。

随着科技的进步,机顶盒正朝着高清化、智能化、多功能化方向发展。高清及超高清技术的普及,使得画面更加细腻逼真;人工智能技术的应用,则让机顶盒具备了更强大的内容推荐和交互能力。同时,市场竞争的加剧也促使厂商不断创新,推出更多符合用户需求的产品和服务。

总之,机顶盒作为连接电视与互联网的桥梁,其重要性日益凸显。在未来,随着技术的不断进步和市场的持续扩大,机顶盒将继续为用户带来更加丰富、便捷、智能的观影体验。

安路科技(Anlogic)公司的发展小趣事

安路科技(Anlogic)的创立可以追溯到XXXX年,由一群具有深厚技术背景的工程师共同创立。起初,公司规模虽小,但怀揣着在国产FPGA(现场可编程门阵列)领域实现技术突破的梦想。早期,安路科技面临着资金短缺、市场竞争激烈等挑战,但团队凭借着对技术的执着和不懈的努力,逐步研发出了具有竞争力的FPGA产品,并在市场上获得了一定的认可。

Blue Creation公司的发展小趣事

在市场竞争日益激烈的背景下,Blue Creation公司意识到单打独斗难以立足。于是,公司积极寻求与其他企业建立合作关系。通过与一家知名芯片制造商的深度合作,Blue Creation成功将其电源管理技术集成到对方的芯片产品中,双方共同开拓市场,实现了互利共赢。这一举措不仅提升了Blue Creation的知名度,也为其带来了稳定的收入来源。

E-Mark Inc公司的发展小趣事

DEF公司是一家跨国电子企业,在欧洲和中国都有生产基地。为了提升产品竞争力,DEF公司决定将其中国生产基地的产品进行E-Mark认证。通过跨国合作和技术交流,DEF公司成功地将中国生产的产品提升到符合欧洲标准的水平,并获得了E-Mark认证。这一认证不仅增强了DEF公司产品的市场竞争力,也促进了中欧之间的经贸合作。

Enterpoint公司的发展小趣事

为了进一步扩大市场份额,Enterpoint公司开始积极拓展国际市场。他们通过参加国际电子展会、建立海外销售网络等方式,不断提升品牌知名度和影响力。同时,公司还积极寻求与国际知名企业的合作机会,通过技术交流和产品合作,不断提升自身的技术实力和产品质量。

Deltron / DEM Manufacturing公司的发展小趣事

随着公司实力的不断增强,DEM Manufacturing开始积极拓展国际市场。公司凭借优质的产品和专业的服务,成功打入欧洲、北美等多个地区的市场,并建立了稳定的销售网络。同时,公司还积极参与国际展览和论坛,与国际同行进行交流和合作,不断提升自身的国际影响力。

问答坊 | AI 解惑

VHDL学习很有用的100例

不知道论坛里有没有人发过,全当交流交流了。…

查看全部问答>

在新一代智能家电中采用 FPGA 实现节能电机控制

家电是现代生活方式的核心。消费者希望家电产品不但 “智能”、“绿色”,当然还要价格合适。所有这些 需求都促使当今的家电设计人员推出真正的高技术产品。 多年以来,智能家电采用微控制器单元(MCU)和数字信号处理(DSP)模块等通用电子器件来管 ...…

查看全部问答>

wince下混音器编程mixer函数的一些问题

wince下希望通过程序控制录音的音量,使用mixer一组的函数,遇到了一些棘手的问题: 1,mixerGetNumDevs的返回值竟然是0,不解; 2,mixerOpen执行总是失败,返回值为MMSYSERR_BADDEVICEID; 我用的是EVC4.0,在wince5.0下运行。 请问大家是否 ...…

查看全部问答>

Wince + S3C2440休眠唤醒问题

1,我的Wince+2440休眠唤醒后系统运行明显变慢,直至渐渐死机,请各位大侠推测下可能是什么原因; 2,能否设置休眠唤醒源为多个?比如我是否能设置EXTINT0,EXTINT1均能把系统从休眠中唤醒; 3,电源管理项是如何取得空闲时间的,目前我设置1分钟 ...…

查看全部问答>

WINCE6可以支持GAPI吗?

想必各位已经知道WINCE5平台可以支持GAPI. 请问WINCE6平台可以支持GAPI吗?…

查看全部问答>

再问个关于操作系统任务的问题

在看一个项目的代码 底层初始化的不说了,进入root任务之后,创建task1,task2,task3等几个任务,创建任务的时候设定优先级, task1,task2等的代码如下 例如 void task1() { ....   while(1)    {     proce ...…

查看全部问答>

救命,USB-RNDIS编程

接到紧急任务:需要在pc上做一个和USB-RNDIS设备通讯的程序,由于小弟之前没接触过,不知如何下手,请各位大虾指条明路,最好有源码,万分感激!…

查看全部问答>

请大家帮忙看段程序

这是DSP2407的程序,尤其红色的几句的含义是什么?谢谢呀 void DA_OUT(unsigned CHANNEL,unsigned int RNG,unsigned int SPI_DATA) {       unsigned char flag=0;     SPITXBUF=(CHANNEL…

查看全部问答>

能到M级以上开关频率mosfet的型号有什么啊 ?

想用耐压500v以上,耐流几十A的   频率1M以上 的mosfet有什么型号啊  能推荐几个么…

查看全部问答>

DSP2812的仿真状态 和 从H0引导 求解惑

这两种状态的过程有什么区别 仿真状态下.reset段是不是放到了H0的0x3f8000上 仿真状态下的详细执行过程是什么样的呢?? 从H0引导的详细执行过程是社么样的呢??求高手解惑啊 困扰了很久了 。。。。。。…

查看全部问答>