/********************************** I2C总线驱动 ********************************
模块名:I2C总线驱动 型号:I2C
功能描述:
        此模块包括发送数据及接收数据,应答位发送,并提供了几个直接面对器件的操作
    函数,能很方便的与用户程序进行连接并扩展。需要注意的是,函数是采用延时方法产
    生 SCL 脉冲,对高晶振频率要做一定的修改!!在写E2PROM的时候一定要延时!!!
说明:
    1us机器周期,晶振频率要小于12MHz
    返回 1 则操作成功,返回 0 则操作失败。
    sla 为器件从地址,suba 为器件子地址。
*******************************************************************************/
#include "AT89X52.h"
#include <intrins.h>
#define SomeNOP(); {_nop_();_nop_();_nop_();_nop_();_nop_();}    //定义空指令

sbit SDA = P1^;    //模拟I2C数据传输位
sbit SCL = P1^;    //模拟I2C时钟控制位
bit bdata I2C_Ack;    //应答标志位

/************************************ I2C_Start *********************************
函数名:void I2C_Start() 入口: 出口:
功能描述:启动I2C总线,即发送I2C初始条件
解释: 在I2C总线协议中规定的起始位格式是:在SCL高电平期间,SDA发生从高到低的电平跳变.它与其它数据格式的区别在于,协议中规定有效的数据必须在SCL的高电平期间保持不变,只有在SCL的低电平期间才能发生跳变.所以这一有别与其它格式的数据才能做为起始位.
调用函数:
全局变量:
*******************************************************************************/
void I2C_Start()
{
    SDA = ;    //发送起始条件的数据信号
    _Nop();
    SCL = ;
    SomeNOP();    //起始条件建立时间大于4.7us,延时
    SDA = ;    //发送起始信号
    SomeNOP();    //起始条件建立时间大于4us,延时
    SCL = ;    //钳住I2C总线准备发送或接收数据
    /******解释:I2C总线在空闲状态下都是被上拉为高电平的,所以当它们处于低电平时就表示忙的状态.***/
    _nop_();
    _nop_();
}

/************************************ I2C_Stop *********************************
函数名:void I2C_Stop() 入口: 出口:
功能描述:结束I2C总线,即发送I2C结束条件
解释:
    同起始条件的格式类似,结束条件的格式是在SCL高电平期间,SDA由低电平向高电平跳变.
调用函数:
全局变量:
*******************************************************************************/
void I2C_Stop()
{
    SDA = ;    //发送结束条件的数据信号
    _Nop();
    SCL = ;    //发送结束条件的时钟信号
    SomeNOP();    //结束条件建立时间大于4us,延时
    SDA = ;    //发送I2C总线结束信号
    SomeNOP();
}                                        

/************************************ I2C_CheckAck *****************************
函数名:bit I2C_CheckAck(void) 入口: 出口:0(无应答),1(有应答)
功能描述:
    检验I2C总线应答信号,有应答则返回1,否则返回0,超时值取255.
解释:
    I2C总线协议中规定传输的每个字节之后必须跟一个应答位,所以从器件在接收到每个
字节之后必须反馈一个应答信号给主控制器,而主控制器就需要检测从器件回传的应答信号,
根据其信息做出相应的处理.另外,主从之别是相对的,接收数据的即为从,发送数据的及为主.
再看看应答信号的格式:在由发送器产生的时钟响应周期里,发送器先释放SDA(置高),然后由
接受器将SDA拉低,并在这个时钟脉冲周期的高电平期间保持稳定的低电平.即表示从器件做
出了应答.
调用函数:void I2C_Stop()
全局变量:
*******************************************************************************/
bit I2C_CheckAck(void)
{
    uchar errtime = ; // 因故障接收方无 Ack,超时值为255
    SDA = ; //发送器先释放SDA
    SomeNOP();
    SCL = ;
    SomeNOP(); //时钟电平周期大于 4 us
    while(SDA) //判断SDA是否被拉低
    {
        errtime--;
        )
        {
            I2C_Stop();
            );
        }
    }
    SCL = ;
    _nop_();
    );
}

/************************************ I2C_SendB ********************************
函数名:void I2C_SendB(uchar c)
入口:uchar 型数据
出口:
功能描述:
    字节数据传送函数,将数据 c 发送出去,可以是地址,也可以是数据,发完后等待
应答,并对 此状态位进行操作
注意:
    在传送数据时,数据(SDA)的改变只能发生在SCL的低电平期间,在SCL的高电平期间保持不变
调用函数:bit I2C_CheckAck()
全局变量:I2C_Ack
*******************************************************************************/
void I2C_SendB(uchar c)
{
    uchar BitCnt;
    ; BitCnt<; BitCnt++) //要传送的数据长度为8位
    {
        if((c<<BitCnt)&0x80)    //判断发送位(从高位起发送)
        {
            SDA = ;
        }
        else
        {
            SDA = ;
        }
        _nop_();
        _nop_();
        SCL = ;                //置时钟线为高通知被控器开始接收数据位
        SomeNOP();                //保证时钟高电平周期大于 4us
        SCL = ;
    }
    _nop_();
    _nop_();
    I2C_Ack = I2C_CheckAck();    //检验应答信号,作为发送方,所以要检测接收器反馈的应答信号.
    _nop_();
    _nop_();
}

/************************************ I2C_RcvB *********************************
函数名:uchar I2C_RcvB()
入口:
出口:uchar型数据
功能描述:
    接收从器件传来的数据,并判断总线错误(不发应答信号),收完后需要调用应答函数。
调用函数:
全局变量:
*******************************************************************************/
uchar I2C_RcvB()
{
    uchar retc;
    uchar BitCnt; //位
    retc = ;
    SDA = ; //置数据总线为输入方式,作为接收方要释放SDA.
    ;BitCnt<;BitCnt++)
    {
        _nop_();
        SCL = ;            //置时钟线为低准备接收数据位
        SomeNOP();            //时钟低电平周期大于4.7us
        SCL = ;            //置时钟线为高使数据有效
        _nop_();
        _nop_();
        retc = retc<<; 

        )
        {
            retc = retc + ;//读数据位,接收的数据放入retc中
        }
        _nop_();
        _nop_();
    }
    SCL = ;
    _nop_();
    _nop_();
    return(retc);
}        

/************************************ I2C_Ackn *********************************
函数名:void I2C_Ackn(bit a)
入口:0或1
出口:
功能描述:
    主控制器进行应答信号(可以是应答或非应答信号)
说明:
    作为接收方的时候,必须根据当前自己的状态向发送器反馈应答信号
调用函数:
全局变量:
*******************************************************************************/
void I2C_Ackn(bit a)
{
    )        //在此发送应答或非应答信号
    {
        SDA = ;
    }
    else
    {
        SDA = ;
    }
    SomeNOP();
    SCL = ;
    SomeNOP();        //时钟电平周期大于 4 us
    SCL = ;        //清时钟线钳住I2C总线以便继续接收
    _nop_();
    _nop_();
}

/******************************** I2C_ISendB ***********************************
函数名:bit I2C_ISendB(uchar sla, uchar suba, uchar c)
入口:从器件地址 sla,子地址 suba, 发送字节 c
出口:0(操作有误),1(操作成功)
功能描述:
从启动总线到发送地址、数据,结束总线的全过程, 如果返回1,表示操作成功,否则操作有误。
调用函数:I2C_Start(),I2C_SendB(uchar c),I2C_Stop()
全局变量:I2C_Ack
*******************************************************************************/
bit I2C_ISendB(uchar sla, uchar suba, uchar c)
{
    I2C_Start();        //启动总线
    I2C_SendB(sla);        //发送器件地址
    if(!I2C_Ack)
    {
        );
    }
    I2C_SendB(suba);    //发送器件子地址
    if(!I2C_Ack)
    {
        );
    }
    I2C_SendB(c);        //发送数据
    if(!I2C_Ack)
    {
        );
    }                     

    I2C_Stop();            //结束总线
    );
}

/********************************** I2C_IRcvB **********************************
函数名:bit I2C_IRcvB(uchar sla, uchar suba, uchar *c)
入口:从器件地址 sla, 子地址 suba, 收到的数据在 c
出口:1(操作成功),0(操作有误)
功能描述:从启动总线到发送地址、读数据,结束总线的全过程。
调用函数: I2CS_tart(), I2C_SendB(uchar c), I2C_RcvB(), I2C_Ackn(bit a), I2C_Stop()
全局变量:I2C_Ack
创建者:陈曦
日期:2005-5-15
修改者:
日期:
*******************************************************************************/
bit I2C_IRcvB(uchar sla, uchar suba, uchar *c)
{
    I2C_Start();        //启动总线
    I2C_SendB(sla);
    if(!I2C_Ack)
    {
        );
    }
    I2C_SendB(suba);    //发送器件子地址
    if(!I2C_Ack)
    {
        );
    }
    I2C_Start();        //重复起始条件
    I2C_SendB(sla+);    //发送读操作的地址
    if(!I2C_Ack)
    {
        );
    }
    *c = I2C_RcvB();    //读取数据
    I2C_Ackn();        //发送非应答位
    I2C_Stop();            //结束总线
    );
}

C51单片机模拟I2C总线驱动程序设计的更多相关文章

  1. 51单片机模拟I2C总线的C语言实现

    电路原理图   EEPROM为ATMEL公司的AT24C01A.单片机为ATMEL公司的AT89C51. 软件说明 C语言为Franklin C V3.2.将源程序另存为testi2c.c,用命令 C ...

  2. 51单片机 | 基于I2C总线的秒表模拟应用

    ———————————————————————————————————————————— 参考地址: http://blog.csdn.net/junyeer/article/details/4648 ...

  3. i2c总线驱动,总线设备(适配器),从设备,从设备驱动的注册以及匹配

    常用链接 我的随笔 我的评论 我的参与 最新评论 我的标签 随笔分类 ARM裸机(13) C(8) C++(8) GNU-ARM汇编 Linux驱动(24) Linux应用编程(5) Makefile ...

  4. I2C总线驱动框架详解

    一.I2C子系统总体架构 1.三大组成部分 (1)I2C核心(i2c-core):I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册.注销方法,I2C通信方法(”algorithm”)上层的,与 ...

  5. STM32F4XX中断方式通过IO模拟I2C总线Master模式

    STM32的I2C硬核为了规避NXP的知识产权,使得I2C用起来经常出问题,因此ST公司推出了CPAL库,CPAL库在中断方式工作下仅支持无子地址 的器件,无法做到中断方式完成读写大部分I2C器件.同 ...

  6. I2C裸机驱动程序设计

    ① I2C(Inter-Integrated Circuit)总线是由飞利浦公司开发的两线式串行总线,用于连接微控制器及其外围设备 ② I2C总线有两根双向信号线 (1)SDA:Serial Data ...

  7. gpio模拟I2C,驱动pcf8574T

    一.pcf8574T介绍 查看pcf8574T的数据手册, A表示读或写,当A为1的时候表示读,当A为0的时候表示写.现把地址控制线,即A2.A1.A0全部接地,可以得到读控制指令为0x41,写控制指 ...

  8. 利用C51单片机模拟SPI进行双机通信

    SPI协议简述 SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口.由Motorola首创.SPI接口主要应用在 EEPROM,FLASH,实时时 ...

  9. I2C总线完全版——I2C总线的结构、工作时序与模拟编程

    I2C总线的结构.工作时序与模拟编程 I2C总线的结构.工作时序与模拟编程I2C总线(Inter Integrated Circuit)是飞利浦公司于上个世纪80年代开发的一种"电路板级&q ...

随机推荐

  1. HDU 4009 Transfer water

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4009 题意:给出一个村庄(x,y,z).每个村庄可以挖井或者修建水渠从其他村庄得到水.挖井有一个代价, ...

  2. android批量文件上传(android批量图片上传)

    项目中多处用到文件批量上传功能,今天正好解决了此问题,在此写出来,以便日后借鉴. 首先,以下架构下的批量文件上传可能会失败或者不会成功:   1.android客户端+springMVC服务端:服务端 ...

  3. 2013第50周二eclipse工具尝试

    今天更深入的认识了eclipse开发工具,出现了各种问题在网络的帮助下最终都解决了,感觉似乎明白了很多道理需要总结一下,现在发现晚了,那就先记录下我印象深的几个问题吧: 1.eclipse编辑器设置. ...

  4. sourceforge软件下载方式

    访问http://www.mirrorservice.org/sites/download.sourceforge.net/pub/sourceforge/ 根据软件名称在文件列表中查找

  5. 2014.11.12模拟赛【美妙的数字】| vijos1904学姐的幸运数字

    美妙的数字(number.c/.cpp/.pas) 题目描述 黄巨大认为非负整数是美妙的,并且它的数值越小就越美妙.当然0是最美妙的啦. 现在他得到一串非负整数,对于每个数都可以选择先对它做二进制非运 ...

  6. Linux web性能优化

    1,

  7. pyqt 正则表达式例子学习

    def rex01(self): username=QtCore.QRegExp('[a-zA-Z0-9_]{2,10}') self.names.setValidator(QtGui.QRegExp ...

  8. Session,有没有必要使用它?

    阅读目录 开始 Session的来龙去脉 Session对并发访问的影响 Session的缺点总结 不使用Session的替代方法 Asp.net MVC 中的Session 现有的代码怎么办? 今天 ...

  9. 把一个select查询结果插入到一个表(可选指定字段和值实例)

    把一个select查询结果插入到一个表(可选指定字段和值实例) insert into  bak (cc,yf) select cc,9 from ket insert into bak (cc,yf ...

  10. Rhythmbox乱码的解决的方法

    近期尝试 Listen 和 Banshee 才发现,Rhythmbox 上出现的 mp3乱码问题依然,并且更加严重,想要彻底弄清和解决必须搞清两点,第一, mp3 标签类型和编码,第二,各种播放器对 ...