51单片机实现对24C02进行页写、顺序读取并显示验证

源: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^3;        //注意P1、P2、P3口有内部上拉电阻,可直接连SDA和SCL,若想用P0需外接上拉电阻,否则连上无法输出高电平!
sbit SDA = P3^4;
uchar j;                //用于计数50ms的个数的全局变量
uchar code ToSDAdataBuffer[8] = {0xfe,0xfc,0xf8,0xf0,0xe0,0xc0,0x80,0x00}; //写入24C02的一组数据,8个字节对应24C02的一页(共32页),这里把这些要验证的常数放到程序存储区
uchar ReceivedData[8];    //用于存储接收的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_();
}

//50ms定时器0中断函数
void timer0() interrupt 1         //j是个全局变量,不是返回值,所以这里还是void。
{
    TH0 = (65536-46080)/256;    //11.0592MHz时每50ms一次定时器中断
    TL0 = (65536-46080)%256;
    j++;                         //也可以把判断j到20,并给P0口送显示数据的程序放在中断里处理
}

//延时1秒的子程序,用于将读取的数据每隔一秒显示在LED上
void delay1s(void)
{
    j = 0;
    TMOD = 0x01;                 //方式1的16位计数器
    TH0 = (65536-46080)/256;
    TL0 = (65536-46080)%256;
    EA = 1;
    ET0 = 1;
    TR0 = 1;                     //启动定时器0工作
    while(j < 20)                //j达到20之前空操作,达到20时说明已到1s,下面关中断和定时器0
    ;
    EA = 0;
    ET0 = 0;
    TR0 = 0;
}

//约2ms的延时
void delay(uchar t)
{
    uchar x,y;
    for(x=0;x<t;x++)
    for(y=0;y<250;y++)
    ;
}

//I2C初始化
void InitI2C(void)
{
    SDA = 1;                     //总线空闲时,因各设备都是集电极或漏极开路,上拉电阻使SDA和SCL线都保持高电平。
    SCL = 1;
    delay5us();
}

//产生I2C开始信号
void StartI2C(void)
{
    SDA = 1;         //SDA在SCL为高期间由高变低表示开始,所以先要高
    SCL = 1;
    delay5us();     //时序图要求tSU.STA(Start Set-up Time)大于4.7us
    SDA = 0;         //注意SDA拉低前后都要维持5us以上!
    delay5us();     //tHD.STA(Start Hold Time)大于4us
    SCL = 0;         //拉低SCL,准备发送或接收数据(这两句也可在写或读字节的程序中先将SCL置0,延时)
    delay5us();
}

//产生I2C结束信号
void StopI2C(void)
{
    SDA = 0;         //SDA在SCL为高期间由低变高,说明结束
    SCL = 1;
    delay5us();
    SDA = 1;
    delay5us();
}

//发送方在发完一个字节后检测接收方有没有应答。返回应答成功否。
bit ChkAck(void)
{
    bit SDAtemp;
    SDA = 1;         //释放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 = 1;         //WriteI2CByte中写完一字节后又将SCL拉低,这里拉高产生第9个时钟上升沿,然后在SCL为高期间对SDA进行检测
    delay5us();
    SDAtemp = SDA;    //如果不用暂存变量,直接return SDA,就不会执行后面的SCL = 0,检测期间的第9个时钟就不完整了
    SCL = 0;
    delay5us();
    return SDAtemp;
}

//51作为主机时,如果接收数据,模拟产生应答时序。形参Ack为0,则应答0,为1不应答。
void AckAsMaster(bit Ack)
{
    if(!Ack)
      SDA = 0;
    else
      SDA = 1;
    delay5us();
    SCL = 1;         //主机控制SCL时序。关键是保证在SCL脉冲上升沿之前SDA数据已稳定即可。
    delay5us();
    SCL = 0;
    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()){
            for(i = 0; i < 8; i++){
                WriteI2CByte(ToSDAdataBuffer[i]);
                if(ChkAck()){
                    //这里可添加错误处理代码。如用几个LED的亮灭组合表示此I2C器件有问题,类似主板错误提示。
                    return 1;//一般返回1表示异常,且遇到return就退出整个子程序。
                }
            }
            StopI2C(); //写完发送结束信号。
            return 0; //一般返回0表示程序正常
        }
        else{
            return 1; //之前可添加错误处理代码。
        }
    }
    else{
        return 1;
    }
}

//不能用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()){
                for(i = 0;i < 8;i++){
                    ReceivedData[i] = ReadI2CByte();
                    AckAsMaster(0); //51此时接收数据,调用应答的函数(置SDA为0)
                }
                AckAsMaster(1);     //NO ACK.The microcontroller does not respond with a zero but does
                generate a following stop condition.
                StopI2C();
                return 0;
            }
            else{
                return 1;             //之前可添加错误处理代码。
            }
        }
        else{
            return 1;
        }
    }
    else{
        return 1;
    }
}

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确读到了该页之外的两个存储单元,造成错误。
    if (PageWrite(0x08,ToSDAdataBuffer) == 0) { //先执行页写操作,设从地址00开始,没问题就延迟一下再从同一地址读回来。
        delay(100); //等待24C02页写操作完毕
        if(SequentialRead(0x08) == 0){ //如果顺序读操作成功,则每隔1秒送P0口显示一个字节
            for(i = 0; i < 8; i++){
                P0 = ReceivedData[i];
                delay1s();
            }
        }
    }
    while(1)
    ;
    return 0;
}

//往I2C总线写一个字节的数据(即将一个字节的数据发送到SDA上)
void WriteI2CByte(uchar ByteData)
{
    uchar i,temp;
    temp = ByteData;
    // (StartI2C()最后已经先将SCL变0了):
    for(i=0;i<8;i++){
        temp <<= 1;     //左移一位,I2C要求由MSB最高位开始,移出的CY即要发送到SDA上的数据。下面考虑时序:
        SDA = CY;       //此时SCL已为低,每次移一位送出去(下次进循环后SDA还保持着上次发出去的数据)
        delay5us();     //SDA IN数据变化中点SCL上升沿中点的一段时间是tSU.DAT,即数据建立时间Data In Setup Time,需大于200ns,多延无所谓
        SCL = 1;
        delay5us();     //tHIGH即Clock Pulse Width High,最小4us
        SCL = 0;
        delay5us();     //tLOW即Clock Pulse Width Low,最小4.7us
    }
}

//读取I2C总线一个字节的数据
uchar ReadI2CByte()      //串行总线,51一位位接收从机发送到SDA上的数据,这里只考虑数据已在SDA上时如何存下来这几位,组成一个字节
{
    uchar i,ByteData;
    SDA = 1;             //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上为新数据
    for(i=0;i<8;i++){
        SCL = 1;         //置时钟线为高使数据线上数据有效
        delay5us();
        ByteData = (ByteData<<1)|SDA; //SDA上已是新数据了,读之。data不管以前多少,左移后最右边为0,和SDA“按位或”后MLB就是SDA
        SCL = 0;
        delay5us();
    }
    return ByteData;
}
时间: 2024-08-29 11:15:26

51单片机实现对24C02进行页写、顺序读取并显示验证的相关文章

串口屏(触摸屏)组态软件+多台51单片机MODBUS RTU多机串口通信程序源码

串口屏(触摸屏)组态软件+多台51单片机MODBUS RTU多机串口通信程序源码实现触摸屏(串口屏)与单片机的通讯,主要是解决通讯协议的问题.本文使用开放的Modbus通讯协议,以广州易显的HMImaker触摸屏作主机(Master),单片机作从机(Slaver).HMImaker触摸屏本身支持Modbus通讯协议,只要单片机按照Modbus协议进行收发数据,就可以进行通信了.触摸屏与单片机之间采用RS-485标准接口直接连接,与多台51单片机MODBUS RTU多机串口通信一.包括如下实例:二

[51单片机] EEPROM 24c02 [I2C代码封装-保存实现流水灯]

这里把EEPROM 24c02封装起来,今后可以直接调用,其连线方式为:SDA-P2.1;SCL-P2.0;WP-VCC >_<:i2c.c 1 /*----------------------------------------------- 2 名称:IIC协议 3 内容:函数是采用软件延时的方法产生SCL脉冲,固对高晶振频率要作 一定的修改....(本例是1us机器 4 周期,即晶振频率要小于12MHZ) 5 ---------------------------------------

[51单片机] EEPROM 24c02 + 数码管 + 中断 [统计开机次数]

>_<:24c02的SCL连P2.0;SDA连P2.1;WP接GND;P0接8位数码管的8针;P2.2连段码;P2.3连位码; >_<:delay.c 1 #include "delay.h" 2 /*------------------------------------------------ 3 uS延时函数,含有输入参数 unsigned char t,无返回值 4 unsigned char 是定义无符号字符变量,其值的范围是 5 0~255 这里使用

用 Python 脚本实现对 Linux 服务器的监控

hon 分享到:8 原文出处: 曹江华 目前 Linux 下有一些使用 Python 语言编写的 Linux 系统监控工具 比如 inotify-sync(文件系统安全监控软件).glances(资源监控工具)在实际工作中,Linux 系统管理员可以根据自己使用的服务器的具体情况编写一下简单实用的脚本实现对 Linux 服务器的监控. 本文介绍一下使用 Python 脚本实现对 Linux 服务器 CPU 内存 网络的监控脚本的编写. Python 版本说明 Python 是由 Guido va

51单片机特别功用存放器和位界说

接下来的几节,我们会带着人人编写第一个单片机程序.在此之前,我们先来理解一些 51 单片机特有的程序语法以及 Keil 软件的根本操作步调,请人人必定要有耐烦.我们次要是用 C 言语来对单片机编程,而有的单片机有那么几条很特别的独有的编程语句,51 单片机就有,先引见 2 条.第一条语句是:sfr  P0 = 0x80;sfr 这个症结字,是 51 单片机特有的,他的感化是界说一个单片机特别功用存放器(special function register).51 单片机外部有许多个小模块,每一个模

51单片机

STARTUP.A51//启动文件. 清理RAM.设置堆栈等.即执行完start.a51后跳转到.c文件的main函数 <reg51.h>  //特殊寄存器的字节地址和位地址,sfr定义字节变量.sbit定义位变量,用通俗名作为变量名,并赋地址值,从而用名称来使用这些特殊寄存器. <intrins.h> //定义了一些外部函数,在C51单片机编程中,头文件INTRINS.H的函数使用起来,就会让你像在用汇编时一样简便. /*-----------------------------

51单片机使用中缀表示法实现计算器

-------------------------------------------- 开发板:畅学51单片机学习板 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 使用元件: STC51单片机芯片 51单片机核心板 LCD1602 矩阵键盘 11.

51单片机第五弹---外部中断

写了半天掉线了... 不写了上定义... 什么叫中断 中断是指CPU在执行当前程序的过程中,由于某种随机出现的外设请求或CPU内部的异常事件,使CPU暂停正在执行的程序而转去 执行相应的服务处理程序:当服务处理程序运行完毕后,CPU再返回到暂停处继续执行原来的程序. 51单片机的中断 80c51提供有5个中断源,分别为:2个外部中断,2个定时/计数器中断,1个串口发送/接收中断.并且具有2个中断优先级,可以实现2级中断服务程序嵌套. 首先要弄中断设置 ,中断原理图如下: 以 设置INT0为例:I

单片机(simple chip microcomputer)概论、51单片机、MCS-51单片机、STM32、ARM

catalogue 1. 单片机概论 2. 51单片机 3. MCS-51单片机 4. QX-MINI51开发板(STC89C52芯片) 5. STM32单片机 6. stm32f103zet6开发板实验 1. 单片机概论 0x1: 仿真器 单片机应用系统的软硬件调试需要专门的开发工具,称为"单片机开发系统"或"仿真机".常用的开发方式是把开发系统(例如PC)中的CPU和RAM暂时出借给用户系统,利用开发系统对用户系统的软硬件进行调试(又称仿真),然后将调试好的程序