【单片机基础】I2C通信-基于STC89C52RC
文章目录
- 1、IIC总线结构
- 2、IIC总线传输协议
- 3、完整工程代码
1、IIC总线结构
- IIC总线是philips公司在八十年代初推出的一种串行、半双工总线。主要用于近距离、低速的芯片之间通信;IIC总线有两根双向的信号线,一根数据线SDA用于收发数据一根时钟线SCL用于通信双方时钟同步。
- IIC总线硬件简单,成本较低,因此在各个领域得到广泛的应用。
- IIC总线是一种多主机总线,连接在IIC总线上的器件分为主机和从机。
- 主机有权发起和结束一次通信,而从机只能被主机呼叫。
- 当总线上有多个主机同时启用总线时,IIC也具备冲突检测和仲裁的功能防止错误产生。
- 每个连接IIC总线上的器件都有一个唯一的地址(7bit),且每个器件都可以作为8. 主机也可以作为从机(同一时刻只能有一个从机),总线上的器件增加和删除不影响其他器件正常工作。
- IIC总线在通信时总线上发送数据的器件位发送器,接收数据的器件位接收器
- 理论上一条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总线传输协议
- 数据位的有效性规定:SCL为高电平期间,数据线上的数据必须保持稳定,只有SCL信号为低电平期间,SDA状态才允许变化。
- 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
}
- 传输数据
发送到SDA线上的每个字节必须为8位,每次传输可以发送的字节数量不受限制,每个字节后必须更上一个响应位。
主机在发送数据时,都需要读取从机应答位,当从机空闲可以接受该字节数据时,从机发出应答(低电平),当从机正忙于其他工作处理来不及接受时,从机发出非应答(高电平),主机可以通过从机发出的应答位来判断从机是否成功接收数据。
当主机接收数据接收到最后一个数据字节后,必须向从机发出一个结束传送的信号,这个信号是由从机的“非应答”来实现,然后释放SDA线,以允许主机产生终止信号。 - 数据帧格式
在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相关推荐
- 【单片机】I2C通信详解介绍(初学,浅显易懂,基本知识)
介绍:I2C通讯协议(Inter-Integrated Circuit)引脚少,硬件实现简单,可扩展性强,不需要USART.CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC) ...
- 51单片机基础——串口通信字符串控制流水灯
#include <reg52.h> #include<intrins.h> //位移头文件_crol_ #define uchar unsigned char unsigne ...
- 51单片机基础——串口通信发送字符ab控制流水灯
/* 当串口调试助手发送a时执行5ms流水灯功能, 当串口调试助手发送b时执行50ms流水灯功能 */#include <reg52.h> #include<intrins.h> ...
- 温度自动控制系统c语言,基于STC89C52RC单片机的大棚温湿度自动控制系统毕业论文+外文翻译及原文+C程序+proteus仿真文件+Altium Designer原理图...
大棚温湿度自动控制系统设计 摘 要:本设计是基于STC89C52RC单片机的大棚温湿度自动控制系统,采用SHT10作为温湿度传感器,LCD1602液晶屏进行显示.SHT10使用类似于I2C总线的时序 ...
- EEPW单片机C语言程序设计,基于CH340T的STC89C52RC编程器设计
2.3 系统原理图 原理图如图2 所示. 图中,USB 接口P1 除用于数据传输外,还经过LM7805 稳压后为CH340T 芯片和单片机和其他部件供电,当使用5V 工作电压时,CH340 芯片的VC ...
- 感应开关垃圾桶小项目的完成及问题解决——基于STC89C52RC单片机
感应开关垃圾桶小项目的完成及问题解决--基于STC89C52RC单片机 分为五个步骤,如下: 01代码_舵机 注意:舵机的使用不能自己用手将其旋转,可能导致舵机的损坏(本人因此弄坏一个,或许是使用的舵 ...
- 基于距离传感器的I2C通信(一)
基于距离传感器的I2C通信(一) 一.软硬件准备 (一)硬件 (二)软件 ST-Link驱动的下载安装 二.keil软件的配置 (一)初始配置 (二)程序下载及调试 三.器件的初步了解 四.接线方法 ...
- 基于STC89C52RC单片机制作的红外循迹小车(4个传感器)
单片机:基于STC89C52RC单片机制作的红外循迹小车(4个传感器) 个人感觉红外循迹小车和那些遥控小车的区别就是信号发射不同:遥控小车是通过遥控器发出信号执行相应的动作,而红外循迹是通过红外传感器 ...
- 基于STC89C52RC单片机的密码门锁
基于STC89C52RC单片机的密码门锁 门锁介绍 门锁软硬件介绍 STC8989C52单片机 LCD1602显示屏 矩阵键盘 L298N电机驱动和电动推杆 门锁实物展示 门锁代码展示 参考文档 总结 ...
最新文章
- J2ee分布式框架--技术介绍文档
- 2016年3月23日作业(项目进度管理、项目成本管理)
- 066_xhtml简介
- keil添加hal库_使用Keil uVision5创建stm32 hal库项目过程
- php添加管理员页面完整代码,php – Opencart管理员端的空白页面
- 0经验跨行,我怎么拿到谷歌offer的?
- 判断输入的字符串是否为回文_刷题之路(九)--判断数字是否回文
- 前端学习(3234):react生命周期1
- 系统延时任务及定时任务
- Monthly Expense( POJ-3273 )
- mac安装和卸载mysql_mysql的安装与卸载
- purrr 0.2.0
- F. Gourmet and Banquet(贪心加二分求值)
- 省地县级联操作 http://www.popub.net/script/pcas.html
- MPC5744P-时钟模块
- vue 项目 跨域问题
- 二极管 在低温下 压降 很大
- 基于redis实现活跃用户统计功能
- 云计算是什么?云计算基础概念讲解
- 安装完Tensorflow却找不到指定模块
热门文章
- linux i386 amd64,i386下载与amd64的区别?
- CSS3动画 3DBOX
- 坚果pro2s android 8,坚果Pro2s和小米8se哪个好?坚果Pro2s对比小米8se区别评测
- php 海关对接 进口商品_GitHub - baishaok/haiguan: 海关对接 订单申报 php xml加签
- 历史上的今天:Win10 七周年;微软和雅虎的搜索协议;微软发行 NT 4.0
- 江苏工程职业技术学院计算机专业,江苏工程职业技术学院
- 【ZT】粤语发音练习
- [转]mitmproxy套件使用攻略及定制化开发
- 影像科dsa为什么必须买维修保险_大型数字减影血管成像系统(DSA)的日常保养和维护...
- Fritzing软件绘制Arduino面包板接线图传感器模块库文件169