I2C组件

20 篇文章 0 订阅
20 篇文章 0 订阅

I2C.h

#ifndef _I2C_H
#define _I2C_H

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

sbit I2C_SCL = P3^7;
sbit I2C_SDA = P3^6;

void I2Cstart();
void I2Cstop();
bit I2Cwrite(uint8_t dat);
uint8_t I2CreadNak();
uint8_t I2CreadAck();

#endif // _I2C_H

I2C.c

/**
 * 文件名:I2C.c
 * 描  述:I2C总线驱动模块
 * 备  注:基于IO口模拟实现,总线时序延时等皆由软件方式实现
 *         时钟信号SCL的产生和延时很重要
 */
#include <reg52.h>
#include <intrins.h>
#include "stdint.h"
#include "I2C.h"

/* 产生总线起始信号 */
void I2Cstart() {
    I2C_SDA = 1;    //首先确保sda、scl都是高电平
    I2C_SCL = 1;
    I2Cdelay();     //保持状态一段时间
    I2C_SDA = 0;    //先拉低sda(在scl为高电平期间,sda出现下降沿)
    I2Cdelay();     //保持状态一段时间
    I2C_SCL = 0;    //再拉低scl(使得可以往数据线sda上写数据)
}
/* 产生总线停止信号 */
void I2Cstop() {
    //I2C_SCL = 0;    //首先确保sda、scl都是低电平(时钟信号scl之前已经置0了)
    I2C_SDA = 0;
    I2Cdelay();     //之前i2c已经工作过,是以时钟信号scl为0结束的,此时需要把该时钟状态保持一段时间
    I2C_SCL = 1;    //先拉高scl
    I2Cdelay();     //保持状态一段时间
    I2C_SDA = 1;    //再拉高sda(在scl为高电平期间,sda出现上升沿)
    I2Cdelay();     //保持状态一段时间
}
/* i2c总线写操作,dat-待写入字节,返回值-从机应答位的值 */
bit I2Cwrite(uint8_t dat) {
    bit ack;        //用于暂存应答位的值
    uint8_t mask;   //用于探测字节内某一位值的掩码变量
    //从高位到低位依次进行
    for (mask = 0x80; mask != 0; mask >>= 1) {
        if ((mask & dat) == 0)  //该位的值输出到sda上(之前已经保证scl为低电平了,此时可以写数据)
            I2C_SDA = 0;
        else
            I2C_SDA = 1;
        I2Cdelay();             //保持状态一段时间
        I2C_SCL = 1;            //拉高scl(使得从机可以从sda上读数据)
        I2Cdelay();             //保持状态一段时间
        I2C_SCL = 0;          //再拉低scl,完成一个位周期,为下一次写数据做好准备
    }
    I2C_SDA = 1;    //8位数据发送完后,主机释放sda,以检测从机应答
    I2Cdelay();     //保持状态一段时间,此时从机将应答值写到sda上
    I2C_SCL = 1;    //拉高scl
    ack = I2C_SDA;  //读取此时的sda值,即为从机的应答值
    I2Cdelay();     //保持状态一段时间
    I2C_SCL = 0;    //再拉低scl完成应答位,并保持住总线,为下一次写数据做好准备

    return (~ack);  //应答值取反以符合通常的逻辑:
                    //0=不存在或忙或写入失败,1=存在且空闲或写入成功
}
/* I2C总线读操作,并发送非应答信号,返回值-读到的字节 */
uint8_t I2CreadNak() {
    uint8_t mask, dat;

    I2C_SDA = 1;    //首先确保主机释放sda,之前已确保scl为0
    //从高位到低位依次进行
    for (mask = 0x80; mask != 0; mask >>= 1) {
        I2Cdelay();         //延时一段时间,此时从机将数据位写到sda上
        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;
}
/* I2C总线读操作,并发送应答信号,返回值-读到的字节 */
uint8_t I2CreadAck() {
    uint8_t mask, dat;

    I2C_SDA = 1;    //首先确保主机释放sda,之前已确保scl为0
    //从高位到低位依次进行
    for (mask = 0x80; mask != 0; mask >>= 1) {
        I2Cdelay();         //延时一段时间,此时从机将数据位写到sda上
        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 = 0;    //8位数据发送完后,主机拉低sda,发送应答信号
    I2Cdelay();     //保持状态一段时间
    I2C_SCL = 1;    //拉高scl,使得从机可以读这个应答信号
    I2Cdelay();     //保持状态一段时间
    I2C_SCL = 0;    //再拉低scl完成应答位,并保持住总线

    return dat;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值