文章目录

  • 1、IIC总线结构
  • 2、IIC总线传输协议
  • 3、完整工程代码

1、IIC总线结构

  1. IIC总线是philips公司在八十年代初推出的一种串行、半双工总线。主要用于近距离、低速的芯片之间通信;IIC总线有两根双向的信号线,一根数据线SDA用于收发数据一根时钟线SCL用于通信双方时钟同步。
  2. IIC总线硬件简单,成本较低,因此在各个领域得到广泛的应用。
  3. IIC总线是一种多主机总线,连接在IIC总线上的器件分为主机和从机。
  4. 主机有权发起和结束一次通信,而从机只能被主机呼叫。
  5. 当总线上有多个主机同时启用总线时,IIC也具备冲突检测和仲裁的功能防止错误产生。
  6. 每个连接IIC总线上的器件都有一个唯一的地址(7bit),且每个器件都可以作为8. 主机也可以作为从机(同一时刻只能有一个从机),总线上的器件增加和删除不影响其他器件正常工作。
  7. IIC总线在通信时总线上发送数据的器件位发送器,接收数据的器件位接收器
  8. 理论上一条IIC总线可以挂载127个从器件(由IIC地址觉定,8位地址减去1位广播位,2^7=128,但是0x00不用所以就是127个器件)

IIC完整通信过程
1、主机发送起始信号启动总线
2、主机发送一个字节数据指明从机地址后续字节传送的方向
3、被寻址的从机发送应答信号回应主机
4、发生器发送一个字节数据
(循环步骤4-5)
n、通信完成后发送停止信号释放总线

IIC总线寻址方式:
IIC总线上发送数据是广义的,既包括地址,又包括真正的数据
主机发送起始信号必须先发送一个字节数据,该数据的高7位为从机地址,最低位表示后续字节传送方向,‘0’表示主机发送数据,‘1’表示主机接收数据
总线上所有的从机接收的该字节数据后都将这7位地址与自己地址进行比较,如果相同,则认为自己被主机寻址,然后根据第八位将自己定义为发生器还是接收器。

2、IIC总线传输协议

  1. 数据位的有效性规定:SCL为高电平期间,数据线上的数据必须保持稳定,只有SCL信号为低电平期间,SDA状态才允许变化。
  2. IIC的起始和终止信号
    -SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;SCL线为高电平期间,SDA线由低电平向高电平变化表示终止信号。
    起始和停止条件一般由主机产生,总线在起始条件后被认为处于忙的状态,在停止条件的某段时间后,总线被认为再次处于空闲状态。

    模拟IIC参考代码
//I2C总线起始信号
void I2cStart(void)
{SCL = 1;SDA = 1;I2cDelay_5us();//状态保持5usSDA = 0;I2cDelay_5us();//状态保持5us
}//I2C总线停止信号
void I2cStop(void)
{SCL = 0;SDA = 0;SCL = 1;I2cDelay_5us();//状态保持5usSDA = 1;I2cDelay_5us();//状态保持5us
}
  1. 传输数据
    发送到SDA线上的每个字节必须为8位,每次传输可以发送的字节数量不受限制,每个字节后必须更上一个响应位。

    主机在发送数据时,都需要读取从机应答位,当从机空闲可以接受该字节数据时,从机发出应答(低电平),当从机正忙于其他工作处理来不及接受时,从机发出非应答(高电平),主机可以通过从机发出的应答位来判断从机是否成功接收数据。
    当主机接收数据接收到最后一个数据字节后,必须向从机发出一个结束传送的信号,这个信号是由从机的“非应答”来实现,然后释放SDA线,以允许主机产生终止信号。
  2. 数据帧格式
    在IIC总线上传送数据信号是广义的,既包括地址信号,有包括真实数据。
    在起始信号后必须传送一个从机地址(7位),第八位数数据时传送方向位(R/T),用‘0’表示主机发送数据,‘1’表示主机接收数据。每次数据传送总是由主机产生终止信号。
    若主机希望继续占用总线进行新的数据传送,则可以不产生终止,马上再次发送起始信号对另一从机进行寻址。

模拟IIC参考代码

/*********************************函数名称:ReadACK(void)*函数输入:无*函数返回:1非应答,0应答*函数说明:I2C总线读从机应答信号********************************/
bit ReadACK(void)
{SCL = 0;//拉低时钟总线,允许从机控制SDASCL = 1;//拉高,读SDAI2cDelay_5us();if(SDA)//非应答{SCL = 0;return(1);//返回1}else{SCL = 0;return(0);//返回0}
}/****************************************函数名称:SendACK(bit i)*函数输入:1主机发送非应答,0发送应答*函数返回:无*函数说明:主机发送应答信号***************************************/
void SendACK(bit i)
{SCL = 0;  //拉低时钟总线,允许主机控制SDAif(i)      //发送非应答SDA = 1;elseSDA = 0;SCL = 1;  //拉高总线,让从机读SDAI2cDelay_5us();SCL = 0;   //拉低时钟总线,允许SDA释放SDA = 1;    //释放数据总线
}/****************************************函数名称:I2cSendByte(uchar DAT)*函数输入:DAT需要发送的数据*函数返回:无*函数说明:I2C发送一个字节数据***************************************/
void I2cSendByte(uchar DAT)
{uchar i;for(i=0; i<8; i++)   //分别写8次,每次写1位{SCL = 0;      //拉低时钟总线,允许SDA变化if(DAT & 0x80)   //先写数据最高位SDA = 1;      //写1elseSDA = 0;   //写0SCL = 1;       //拉高时钟,让从机读SDADAT <<= 1;      //为发送下一位左移1位}SCL = 0;          //拉低时钟总线,允许SDA释放SDA = 1;            //释放数据总线
}/*====================================
函数  :I2cReadByte()
参数  :无
返回值 :返回读出的一字节数据
描述  :I2C总线读一字节数据
====================================*/
uchar I2cReadByte(void)
{uchar i, DAT;for(i=0; i<8; i++)//分别读8次,每次读一位{DAT <<= 1; //数据左移1位,准备接收一位SCL = 0;   //拉低时钟总线,允许从机控制SDA变化SCL = 1;   //拉高时钟总线,读取SDA上的数据if(SDA)DAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0}return(DAT); //返回读出的数据
}

基于EEPROM向4单元存数据(主机向从机写数据)

/*====================================
函数  :At24c02Read(uchar ADDR)
参数  :ADDR 单元地址   0-255
返回值 :返回指定单元的数据
描述  :读AT24C02指定单元内数据
====================================*/
uchar At24c02Read(uchar ADDR)
{uchar DAT;I2cStart();//I2C起始信号I2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位if(ReadACK())     //读从机应答AckFlag = 1;    //NOACKelseAckFlag = 0;    //ACKI2cSendByte(ADDR);//I2C发送一个字节ReadACK();//读从机应答I2cStart();//再次产生I2C起始信号I2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读if(ReadACK())//读从机应答AckFlag = 1;    //NOACKelseAckFlag = 0;    //ACKDAT = I2cReadByte();//读一字节SendACK(1);//主机发送非应答I2cStop(); //I2C停止信号return(DAT);//返回读出数据}

主函数调用情况

 //写数据At24c02Write(4,'1');//给第1单元写入数据'1'Delay_ms(1);//延时一段时间等待AT24C02处理

运行示波器解码图

A0表示AT24C02的硬件地址
EOT表示ASCLL表结束字符,十进制“04”
1表示存储‘1’这个字符

由此可以主机向从机发送数据,格式如下:

————————
读出EEPROM代码部分

/*====================================
函数  :At24c02Read(uchar ADDR)
参数  :ADDR 单元地址   0-255
返回值 :返回指定单元的数据
描述  :读AT24C02指定单元内数据
====================================*/
uchar At24c02Read(uchar ADDR)
{uchar DAT;I2cStart();//I2C起始信号I2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位if(ReadACK())     //读从机应答AckFlag = 1;    //NOACKelseAckFlag = 0;    //ACKI2cSendByte(ADDR);//I2C发送一个字节ReadACK();//读从机应答I2cStart();//再次产生I2C起始信号I2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读if(ReadACK())//读从机应答AckFlag = 1;    //NOACKelseAckFlag = 0;    //ACKDAT = I2cReadByte();//读一字节SendACK(1);//主机发送非应答I2cStop(); //I2C停止信号return(DAT);//返回读出数据}

主函数调用情况

 //读数据ch = At24c02Read(4);//读出第1单元内数据送给显示变量

A0表示AT24C02的硬件地址
EOT表示ASCLL表结束字符,十进制“04”
A1表示AT24C02的硬件地址 +1 (读)
1表示读取到的数据

数据格式如下

3、完整工程代码

STC89C52RC读取EEPROM向例程串口打印结果
main.c

#include <reg52.h>
#include <intrins.h>
#include "STC89C52RC_I2C.h"/*数据类型宏定义*/
#define uchar unsigned char
#define uint unsigned int/*常用变量宏定义*/
#define At24c02ADDR     0xa0        //AT24C02硬件地址/*全局变量定义*/
bit AckFlag;    //应答标志位void Delay1ms(void)      //@11.0592MHz
{unsigned char i, j;_nop_();i = 2;j = 199;do{while (--j);} while (--i);
}void Delay_ms(uint timer)
{uint i;for(i=0; i<timer; i++)Delay1ms();
}//使用定时T1工作方式2,波特率9600,晶振11.0592MHZ
//禁止接收,不启动串口中断,波特率不加倍
void InitUART(void)
{TMOD = 0x20;SCON = 0x40;TH1 = 0xFD;TL1 = TH1;PCON = 0x00;TR1 = 1;
}//向串口发送一个字符
void putchar(char ch)
{SBUF = ch;while(!TI);TI = 0;
}//向串口发送一段字符串
void prints(char *s)
{while(*s != '\0')//发送字符串,直到遇到0才结束{SBUF = *s++;while(!TI);TI = 0;}
}/*====================================
函数  :At24c02Write(uchar ADDR, DAT)
参数  :ADDR 单元地址0-255,DAT 需要输入的数据0-255
返回值 :无
描述  :At24c02指定单元写入一个字节数据
====================================*/
void At24c02Write(uchar ADDR, DAT)
{I2cStart();                                //I2C起始信号I2cSendByte(At24c02ADDR + I2cWrite);  //发送器件地址加读写方向位if(ReadACK())         //读从机应答AckFlag = 1;    //NOACKelseAckFlag = 0;    //ACKI2cSendByte(ADDR); //发送储存单元地址字节if(ReadACK())       //读从机应答AckFlag = 1;    //NOACKelseAckFlag = 0;    //ACKI2cSendByte(DAT);  //发送一字节数据if(ReadACK())      //读从机应答AckFlag = 1;    //NOACKelseAckFlag = 0;    //ACKI2cStop();         //I2C停止信号
}/*====================================
函数  :At24c02Read(uchar ADDR)
参数  :ADDR 单元地址   0-255
返回值 :返回指定单元的数据
描述  :读AT24C02指定单元内数据
====================================*/
uchar At24c02Read(uchar ADDR)
{uchar DAT;I2cStart();//I2C起始信号I2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位if(ReadACK())     //读从机应答AckFlag = 1;    //NOACKelseAckFlag = 0;    //ACKI2cSendByte(ADDR);//I2C发送一个字节ReadACK();//读从机应答I2cStart();//再次产生I2C起始信号I2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读if(ReadACK())//读从机应答AckFlag = 1;    //NOACKelseAckFlag = 0;    //ACKDAT = I2cReadByte();//读一字节SendACK(1);//主机发送非应答I2cStop(); //I2C停止信号return(DAT);//返回读出数据}void main(void)
{char ch;InitUART();//串口初始化prints("OK!\n");//串口初始化完成,向串口发送“OK”字符串//写数据
//  At24c02Write(4,'1');//给第1单元写入数据'1'
//  Delay_ms(1);//延时一段时间等待AT24C02处理//读数据ch = At24c02Read(4);//读出第1单元内数据送给显示变量if(AckFlag)//当从机非应答P1 = 0;//亮P1所有灯elseP1 = 0XFF;//灭P1所有灯prints("AT24C02 data:");putchar(ch);while(1);
}

STC89C52RC_I2C.c

#include "STC89C52RC_I2C.h"/*I2C硬件接口定义*/
sbit SCL = P2^1;       //I2C时钟总线
sbit SDA = P2^0;       //I2C数据总线/**********************************
89C52RC单片机模拟IIC通信代码
***********************************///5us延时函数
void I2cDelay_5us(void)
{_nop_();
}//I2C总线起始信号
void I2cStart(void)
{SCL = 1;SDA = 1;I2cDelay_5us();//状态保持5usSDA = 0;I2cDelay_5us();//状态保持5us
}//I2C总线停止信号
void I2cStop(void)
{SCL = 0;SDA = 0;SCL = 1;I2cDelay_5us();//状态保持5usSDA = 1;I2cDelay_5us();//状态保持5us
}/*********************************函数名称:ReadACK(void)*函数输入:无*函数返回:1非应答,0应答*函数说明:I2C总线读从机应答信号********************************/
bit ReadACK(void)
{SCL = 0;//拉低时钟总线,允许从机控制SDASCL = 1;//拉高,读SDAI2cDelay_5us();if(SDA)//非应答{SCL = 0;return(1);//返回1}else{SCL = 0;return(0);//返回0}
}/****************************************函数名称:SendACK(bit i)*函数输入:1主机发送非应答,0发送应答*函数返回:无*函数说明:主机发送应答信号***************************************/
void SendACK(bit i)
{SCL = 0;  //拉低时钟总线,允许主机控制SDAif(i)      //发送非应答SDA = 1;elseSDA = 0;SCL = 1;  //拉高总线,让从机读SDAI2cDelay_5us();SCL = 0;   //拉低时钟总线,允许SDA释放SDA = 1;    //释放数据总线
}/****************************************函数名称:I2cSendByte(uchar DAT)*函数输入:DAT需要发送的数据*函数返回:无*函数说明:I2C发送一个字节数据***************************************/
void I2cSendByte(uchar DAT)
{uchar i;for(i=0; i<8; i++)   //分别写8次,每次写1位{SCL = 0;      //拉低时钟总线,允许SDA变化if(DAT & 0x80)   //先写数据最高位SDA = 1;      //写1elseSDA = 0;   //写0SCL = 1;       //拉高时钟,让从机读SDADAT <<= 1;      //为发送下一位左移1位}SCL = 0;          //拉低时钟总线,允许SDA释放SDA = 1;            //释放数据总线
}/*====================================
函数  :I2cReadByte()
参数  :无
返回值 :返回读出的一字节数据
描述  :I2C总线读一字节数据
====================================*/
uchar I2cReadByte(void)
{uchar i, DAT;for(i=0; i<8; i++)//分别读8次,每次读一位{DAT <<= 1; //数据左移1位,准备接收一位SCL = 0;   //拉低时钟总线,允许从机控制SDA变化SCL = 1;   //拉高时钟总线,读取SDA上的数据if(SDA)DAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0}return(DAT); //返回读出的数据
}
/*****************************************************************/

STC89C52RC_I2C.h

#ifndef __STC89C52RC_H__
#define __STC89C52RC_H__#include <reg52.h>
#include <intrins.h>/*数据类型宏定义*/
#define uchar unsigned char
#define uint unsigned int/*I2C常用变量宏定义*/
#define I2cRead         1           //I2C读方向位
#define I2cWrite        0           //I2C写方向//5us延时函数
extern void I2cI2cDelay_5us(void);//I2C总线起始信号
extern void I2cStart(void);//I2C总线停止信号
extern void I2cStop(void);//I2C总线读从机应答信号
extern bit ReadACK(void);//主机发送应答信号
extern void SendACK(bit i);//I2C发送一个字节数据
extern void I2cSendByte(uchar DAT);//I2C总线读一字节数据
extern uchar I2cReadByte(void);#endif

【单片机基础】I2C通信-基于STC89C52RC相关推荐

  1. 【单片机】I2C通信详解介绍(初学,浅显易懂,基本知识)

    介绍:I2C通讯协议(Inter-Integrated Circuit)引脚少,硬件实现简单,可扩展性强,不需要USART.CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC) ...

  2. 51单片机基础——串口通信字符串控制流水灯

    #include <reg52.h> #include<intrins.h> //位移头文件_crol_ #define uchar unsigned char unsigne ...

  3. 51单片机基础——串口通信发送字符ab控制流水灯

    /* 当串口调试助手发送a时执行5ms流水灯功能, 当串口调试助手发送b时执行50ms流水灯功能 */#include <reg52.h> #include<intrins.h> ...

  4. 温度自动控制系统c语言,基于STC89C52RC单片机的大棚温湿度自动控制系统毕业论文+外文翻译及原文+C程序+proteus仿真文件+Altium Designer原理图...

    大棚温湿度自动控制系统设计 摘  要:本设计是基于STC89C52RC单片机的大棚温湿度自动控制系统,采用SHT10作为温湿度传感器,LCD1602液晶屏进行显示.SHT10使用类似于I2C总线的时序 ...

  5. EEPW单片机C语言程序设计,基于CH340T的STC89C52RC编程器设计

    2.3 系统原理图 原理图如图2 所示. 图中,USB 接口P1 除用于数据传输外,还经过LM7805 稳压后为CH340T 芯片和单片机和其他部件供电,当使用5V 工作电压时,CH340 芯片的VC ...

  6. 感应开关垃圾桶小项目的完成及问题解决——基于STC89C52RC单片机

    感应开关垃圾桶小项目的完成及问题解决--基于STC89C52RC单片机 分为五个步骤,如下: 01代码_舵机 注意:舵机的使用不能自己用手将其旋转,可能导致舵机的损坏(本人因此弄坏一个,或许是使用的舵 ...

  7. 基于距离传感器的I2C通信(一)

    基于距离传感器的I2C通信(一) 一.软硬件准备 (一)硬件 (二)软件 ST-Link驱动的下载安装 二.keil软件的配置 (一)初始配置 (二)程序下载及调试 三.器件的初步了解 四.接线方法 ...

  8. 基于STC89C52RC单片机制作的红外循迹小车(4个传感器)

    单片机:基于STC89C52RC单片机制作的红外循迹小车(4个传感器) 个人感觉红外循迹小车和那些遥控小车的区别就是信号发射不同:遥控小车是通过遥控器发出信号执行相应的动作,而红外循迹是通过红外传感器 ...

  9. 基于STC89C52RC单片机的密码门锁

    基于STC89C52RC单片机的密码门锁 门锁介绍 门锁软硬件介绍 STC8989C52单片机 LCD1602显示屏 矩阵键盘 L298N电机驱动和电动推杆 门锁实物展示 门锁代码展示 参考文档 总结 ...

最新文章

  1. J2ee分布式框架--技术介绍文档
  2. 2016年3月23日作业(项目进度管理、项目成本管理)
  3. 066_xhtml简介
  4. keil添加hal库_使用Keil uVision5创建stm32 hal库项目过程
  5. php添加管理员页面完整代码,php – Opencart管理员端的空白页面
  6. 0经验跨行,我怎么拿到谷歌offer的?
  7. 判断输入的字符串是否为回文_刷题之路(九)--判断数字是否回文
  8. 前端学习(3234):react生命周期1
  9. 系统延时任务及定时任务
  10. Monthly Expense( POJ-3273 )
  11. mac安装和卸载mysql_mysql的安装与卸载
  12. purrr 0.2.0
  13. F. Gourmet and Banquet(贪心加二分求值)
  14. 省地县级联操作 http://www.popub.net/script/pcas.html
  15. MPC5744P-时钟模块
  16. vue 项目 跨域问题
  17. 二极管 在低温下 压降 很大
  18. 基于redis实现活跃用户统计功能
  19. 云计算是什么?云计算基础概念讲解
  20. 安装完Tensorflow却找不到指定模块

热门文章

  1. linux i386 amd64,i386下载与amd64的区别?
  2. CSS3动画 3DBOX
  3. 坚果pro2s android 8,坚果Pro2s和小米8se哪个好?坚果Pro2s对比小米8se区别评测
  4. php 海关对接 进口商品_GitHub - baishaok/haiguan: 海关对接 订单申报 php xml加签
  5. 历史上的今天:Win10 七周年;微软和雅虎的搜索协议;微软发行 NT 4.0
  6. 江苏工程职业技术学院计算机专业,江苏工程职业技术学院
  7. 【ZT】粤语发音练习
  8. [转]mitmproxy套件使用攻略及定制化开发
  9. 影像科dsa为什么必须买维修保险_大型数字减影血管成像系统(DSA)的日常保养和维护...
  10. Fritzing软件绘制Arduino面包板接线图传感器模块库文件169