51单片机实现对24C02进行页写、顺序读取并显示验证
//************************************************************************************* //**程序名称:51单片机实现对24C02进行页写、顺序读取并显示验证 //**编写人:**** //**修改人:**** //**程序目的:熟悉I2C总线协议,实现51模拟I2C时序和24C02通信 //**功能描述:51单片机将8个字节数据写入24C02的一页中,然后顺序读出,每隔1秒送P0口LED显示 //**其他说明:本程序是采用某51开发板,若在其他地方验证可更改相关端口及延时程序等。 //** 程序编写前曾参考过多个教程,最终自己编程通过,并详加注释。 //** 可供初学者参考,并不对程序的可靠性等作保证。 //**开发工具:keil 7.50 (C51) //**日期: //************************************************************************************* #include <REGX51.H> #include <intrins.h> //因为用到_nop_(); typedef unsigned char uchar; sbit SCL = P3^; //注意P1、P2、P3口有内部上拉电阻,可直接连SDA和SCL,若想用P0需外接上拉电阻,否则连上无法输出高电平! sbit SDA = P3^; uchar j; //用于计数50ms的个数的全局变量 uchar code ToSDAdataBuffer[] = {0xfe,0xfc,0xf8,0xf0,0xe0,0xc0,0x80,0x00}; //写入24C02的一组数据,8个字节对应24C02的一页(共32页),这里把这些要验证的常数放到程序存储区 uchar ReceivedData[]; //用于存储接收的8个字节数据(1页)的数组 //本例51为单主机,24C02为从机,不需要总线裁决 //延时5us子程序 void delay5us(void) { _nop_(); //时序图要求开始建立时间tSU.STA大于4.7us,开始保持时间tHD.STA大于4us。51中每个_nop_();延时1个CPU cycle,即1us。 _nop_(); //如考虑不同CPU频率不同,可用带参数的延时,参数在前面宏定义。 _nop_(); _nop_(); _nop_(); } //j是个全局变量,不是返回值,所以这里还是void。 { TH0 = (-)/; //11.0592MHz时每50ms一次定时器中断 TL0 = (-)%; j++; //也可以把判断j到20,并给P0口送显示数据的程序放在中断里处理 } //延时1秒的子程序,用于将读取的数据每隔一秒显示在LED上 void delay1s(void) { j = ; TMOD = 0x01; //方式1的16位计数器 TH0 = (-)/; TL0 = (-)%; EA = ; ET0 = ; TR0 = ; //启动定时器0工作 ) //j达到20之前空操作,达到20时说明已到1s,下面关中断和定时器0 ; EA = ; ET0 = ; TR0 = ; } //约2ms的延时 void delay(uchar t) { uchar x,y; ;x<t;x++) ;y<;y++) ; } //I2C初始化 void InitI2C(void) { SDA = ; //总线空闲时,因各设备都是集电极或漏极开路,上拉电阻使SDA和SCL线都保持高电平。 SCL = ; delay5us(); } //产生I2C开始信号 void StartI2C(void) { SDA = ; //SDA在SCL为高期间由高变低表示开始,所以先要高 SCL = ; delay5us(); //时序图要求tSU.STA(Start Set-up Time)大于4.7us SDA = ; //注意SDA拉低前后都要维持5us以上! delay5us(); //tHD.STA(Start Hold Time)大于4us SCL = ; //拉低SCL,准备发送或接收数据(这两句也可在写或读字节的程序中先将SCL置0,延时) delay5us(); } //产生I2C结束信号 void StopI2C(void) { SDA = ; //SDA在SCL为高期间由低变高,说明结束 SCL = ; delay5us(); SDA = ; delay5us(); } //发送方在发完一个字节后检测接收方有没有应答。返回应答成功否。 bit ChkAck(void) { bit SDAtemp; SDA = ; //释放SDA(置1),然后等待接收方应答将它拉低。确切的说,应是24C02发送字节最后一位的第8个时钟周期下降沿后经tAA //(SCL变低到SDA OUT有效的时间)约0.1-4.5us后拉低SDA,并随第9个时钟后结束。所以24C02正常时,SDA为1并不体现 //(第8脉冲后马上被拉低了),但若器件坏了,就需要靠这个置1后不变来判断!(若不置1而上次发的数据最后一位为0就不好判断了) //从24C02的Block Diagram看,它只能在SDA为1时通过控制内部的Dout来把SDA拉低,但不能在SDA为0时将其置高!故主机要常将SDA置1,而SCl置0。 SCL = ; //WriteI2CByte中写完一字节后又将SCL拉低,这里拉高产生第9个时钟上升沿,然后在SCL为高期间对SDA进行检测 delay5us(); SDAtemp = SDA; //如果不用暂存变量,直接return SDA,就不会执行后面的SCL = 0,检测期间的第9个时钟就不完整了 SCL = ; delay5us(); return SDAtemp; } //51作为主机时,如果接收数据,模拟产生应答时序。形参Ack为0,则应答0,为1不应答。 void AckAsMaster(bit Ack) { if(!Ack) SDA = ; else SDA = ; delay5us(); SCL = ; //主机控制SCL时序。关键是保证在SCL脉冲上升沿之前SDA数据已稳定即可。 delay5us(); SCL = ; delay5us(); } void WriteI2CByte(uchar); uchar ReadI2CByte(); //页写。输入两参数,一个为首字地址,另一个是指向待写入数据数组的指针(括号内第二个参数也可写作uchar ToSDAdataBuffer[],即数组名代表首地址)。 bit PageWrite(uchar WordAddress,uchar *ToSDAdataBuffer) { //下面的程序我用的if嵌套,网上有些程序是顺序结构,但因为遇到return就返回主程序不再往下执行,所以效果是一样的。 uchar i; StartI2C(); WriteI2CByte(0xa0);//之所以没设DeviceAddress这个参数,是因为最后一位不属于地址。E2PROM一般前四位为1010,这里A2~A0接地,为0,最后一位0表示写 if(!ChkAck()){ //检查应答函数返回0说明从机应答0成功。 WriteI2CByte(WordAddress); //写8-bit data word address,即写到哪个存储单元(24C02有2kbits,所以数据字有2048/8=256个,故地址线有8位) if(!ChkAck()){ ; i < ; i++){ WriteI2CByte(ToSDAdataBuffer[i]); if(ChkAck()){ //这里可添加错误处理代码。如用几个LED的亮灭组合表示此I2C器件有问题,类似主板错误提示。 ;//一般返回1表示异常,且遇到return就退出整个子程序。 } } StopI2C(); //写完发送结束信号。 ; //一般返回0表示程序正常 } else{ ; //之前可添加错误处理代码。 } } else{ ; } } //不能用Current Address Read,因为那是24C02数据字地址计数器上次操作后加1的值;而SEQUENTIAL_READ如果不给一个要读取的开始地址,会从头输出, //所以需要Random Read的开始部分,但不要停止信号。 bit SequentialRead(uchar WordAddress) { uchar i; StartI2C(); WriteI2CByte(0xa0); if (!ChkAck()){ WriteI2CByte(WordAddress); if (!ChkAck()){ StartI2C(); //the microcontroller must generate another start condition WriteI2CByte(0xa1); //Device Address后紧跟的那一位R/W^是1说明是读,24C02内部就是根据最后这位来判断是从SDA上读数,还是往SDA上送数 //之所以设为1是读,是因为根据WriteI2CByte子程序,最后给SDA赋1,P3^4就维持1,这样24C02内部Dout为高就将SDA拉低; //如果最后一位是0,24C02没能力拉高! if (!ChkAck()){ ;i < ;i++){ ReceivedData[i] = ReadI2CByte(); AckAsMaster(); //51此时接收数据,调用应答的函数(置SDA为0) } AckAsMaster(); //NO ACK.The microcontroller does not respond with a zero but does generate a following stop condition. StopI2C(); ; } else{ ; //之前可添加错误处理代码。 } } else{ ; } } else{ ; } } int main(void) { uchar i; P0 = 0xff; InitI2C(); //注意在24C02中用到的页写和顺序读的地址是同一个,且必须是8的整数倍,即每页的首地址才行,如0x08,0x20等。因为24C02页写时后三位地址自动加1, //When the word address,internally generated, reaches the page boundary, the following byte is placed at the beginning of the same page. //而顺序读时只有在达到整个存储区边界时才会roll over。所以,如读写都用0x32这个地址,由于不是8的整数倍,只有前6个数显示是正确的,最后两个数 //虽然又从头写在了该页的前面,但SequentialRead确读到了该页之外的两个存储单元,造成错误。 ) { //先执行页写操作,设从地址00开始,没问题就延迟一下再从同一地址读回来。 delay(); //等待24C02页写操作完毕 ){ //如果顺序读操作成功,则每隔1秒送P0口显示一个字节 ; i < ; i++){ P0 = ReceivedData[i]; delay1s(); } } } ) ; ; } //往I2C总线写一个字节的数据(即将一个字节的数据发送到SDA上) void WriteI2CByte(uchar ByteData) { uchar i,temp; temp = ByteData; // (StartI2C()最后已经先将SCL变0了): ;i<;i++){ temp <<= ; //左移一位,I2C要求由MSB最高位开始,移出的CY即要发送到SDA上的数据。下面考虑时序: SDA = CY; //此时SCL已为低,每次移一位送出去(下次进循环后SDA还保持着上次发出去的数据) delay5us(); //SDA IN数据变化中点SCL上升沿中点的一段时间是tSU.DAT,即数据建立时间Data In Setup Time,需大于200ns,多延无所谓 SCL = ; delay5us(); //tHIGH即Clock Pulse Width High,最小4us SCL = ; delay5us(); //tLOW即Clock Pulse Width Low,最小4.7us } } //读取I2C总线一个字节的数据 uchar ReadI2CByte() //串行总线,51一位位接收从机发送到SDA上的数据,这里只考虑数据已在SDA上时如何存下来这几位,组成一个字节 { uchar i,ByteData; SDA = ; //SCL在ChkAck中已经置0了。注意SCL时序仍然由主机控制!24C02只能将SDA由高拉低,象橡皮筋松手又恢复高,而下面只是读SDA,没赋值 //其实程序中多处给SDA置1都可省,因为检查应答时为0就正常,无所谓,写字节时也无所谓,就是在读之前要保证SDA为1! //因之前有WriteI2CByte(0xa1); 其实这句也可省略。 delay5us(); //24C02作为发送方在第9个时钟的negative edge clocks data out of each device,所以现在SDA上为新数据 ;i<;i++){ SCL = ; //置时钟线为高使数据线上数据有效 delay5us(); ByteData = (ByteData<<)|SDA; //SDA上已是新数据了,读之。data不管以前多少,左移后最右边为0,和SDA“按位或”后MLB就是SDA SCL = ; delay5us(); } return ByteData; }
51单片机实现对24C02进行页写、顺序读取并显示验证的更多相关文章
- python 使用win32com实现对word文档批量替换页眉页脚
最近由于工作需要,需要将70个word文件的页眉页脚全部进行修改,在想到这个无聊/重复/没有任何技术含量的工作时,我的内心是相当奔溃的.就在我接近奔溃的时候我突然想到完全可以用python脚本来实现这 ...
- [编译] 3、在Linux下搭建51单片机的开发烧写环境(makefile版)
星期二, 10. 七月 2018 01:01上午 - beautifulzzzz 一.SDCC(Small Device C Compiler)编译环境搭建 SDCC是一个小型设备的C语言编译器,该编 ...
- 单片机成长之路(51基础篇) - 006 在Linux下搭建51单片机的开发烧写环境
在Linux下没有像keli那样好用的IDE来开发51单片机,开发环境只能自己搭建了. 第一步:安装交叉编译工具 a) 安装SDCC sudo apt-get install sdcc b)测试SDC ...
- 写出java8实现对List<User>中的username字段过滤出不等于张三的数据
写出java8实现对List<User>中的username字段过滤出不等于张三的数据... 对...这个是一道面试题.当时没有看过java8的新特性...所以有点懵. 看完之后感觉 真. ...
- 使用内存地址点亮LED—跟51单片机一样写代码教学(初步入门)
51单片机点亮一个小灯 #include <rge52.h> sbit LED = P0^ void main(void) { P0 = 0XFE; // 总线操作 sfr P0 0X80 ...
- 用 Python 脚本实现对 Linux 服务器的监控
目前 Linux 下有一些使用 Python 语言编写的 Linux 系统监控工具 比如 inotify-sync(文件系统安全监控软件).glances(资源监控工具)在实际工作中,Linux 系统 ...
- 在应用程序中实现对NandFlash的操作
以TC58NVG2S3ETA00 为例: 下面是它的一些物理参数: 图一 图二 图三 图四 图五 图6-0 图6-1 说明一下,在图6-1中中间的那个布局表可以看做是实际的NandFlash一页数据的 ...
- [学习笔记]15个QA让你快速入门51单片机开发
一.C语言相关 Q1:sbit与sfr代表是什么?有什么作用? Q2:#define OSC_FREQ 22118400L这句宏命令里的“L”是什么意思? Q3:我粘贴了别人的代码,怎么发现没有un ...
- 一个简单的QQ隐藏图生成算法 通过jQuery和C#分别实现对.NET Core Web Api的访问以及文件上传
一个简单的QQ隐藏图生成算法 隐藏图不是什么新鲜的东西,具体表现在大部分社交软件中,预览图看到的是一张图,而点开后看到的又是另一张图.虽然很早就看到过这类图片,但是一直没有仔细研究过它的原理,今天 ...
随机推荐
- Keil C51 详细设置
一.target名更改 打开Keil后,左侧Project Workspace中的target可改,方法:右击Target——Manage Compnents——双击待修改项即可,若要添加,使用对话框 ...
- VS2012 快捷键 VS Resharper 设置
原文 http://www.cnblogs.com/skyangell/archive/2013/03/24/2979835.html 一直用Resharper插件,最近发现Ctrl+E,C快捷见被R ...
- Android导航菜单横向左右滑动并和下方的控件实现联动
这个是美团网个人订单的效果,找了很多地方都没找到,自己研究了两天终于弄出来了^_^,有什么问题希望大家指出来,谢谢. 实现原理是上方使用HorizontalScrollView这个可以水平横向拖动的控 ...
- shell 中如何判断前一个命令是否执行成功
shell 中如何判断前一个命令是否执行成功 通过判断返回值来解决: if [ $? -eq 0 ];then 命令正确的分支 else 命令失败的分支 fi
- wikioi1191 数轴染色
题目描述 Description 在一条数轴上有N个点,分别是1-N.一开始所有的点都被染成黑色.接着 我们进行M次操作,第i次操作将[Li,Ri]这些点染成白色.请输出每个操作执行后 剩余黑色点的个 ...
- libeXosip2(1-2) -- How-To initiate, modify or terminate calls.
How-To initiate, modify or terminate calls. The eXtented eXosip stack eXosip2 offers a flexible API ...
- asp.net mvc4中自定义404
原文地址:http://www.chuchur.com/asp-net-mvc4-404/ 定义404 方法当然有很多种.不同的方法所展现的形式也不一样,用户所体验也不一样.以下提供2两种 方法一: ...
- Palindrome Subarrays
给定输入字符串,要求判断任意子字符串是否对称. 基本思路就是DP 写出DP表达式为 dp[i][j] = dp[i + 1][j - 1] && (s[i] == s[j]) dp[i ...
- July 【补题】
A(zoj 3596) bfs,记忆搜都可以, 按余数来记录状态. B(zoj 3599) 博弈,跳过 C(zoj 3592) 简单dp,题意不好懂 D(zoj 3602) 子树哈希, 对根的左右儿子 ...
- OSX: 私人定制Dock默认程序图标
不论什么一个新用户第一次登陆后,OSX都会自己主动地在用户的Dock中列出系统默认的应用程序图标,这些图标随着OSX版本号的不同而不同. 系统管理员有的时候须要改变这些系统默认图标,或者加入自己的或者 ...