关于IIC我觉这个博客里面说的已经够清楚了

如下图所示的写操作的时序图:

其实像这种通信协议的要求是很精确的,一点点不对都可能导致在实际工程中无法读取数据。我就是被一个应答位耽误了好久,还好最后被我发现了。虽然程序不长,但是每一句话都是值得我们认真学习的,下面是我自己结合网上还有书中的程序综合的,亲测可用。最后用keil的逻辑分析仪测试了iic端口输出的波形。

iic.h

#ifndef __IIC_H
#define __IIC_H #include <reg52.h>
#include <intrins.h> sbit SCL = P2^;
sbit SDA = P2^; void iic_init(void);
void iic_start(void);
void iic_stop(void);
void iic_dalay_us(void);//iic延时专用
void iic_send_ack(bit _ack);//读取数据之后发送应答位
void iic_wait_ack(void);//写入数据之后,等待从机应答信号
void iic_write_byte(unsigned char _byte); //写一个字节
unsigned char iic_read_byte(void);//读取一个字节
void iic_write_addr_byte(unsigned char _addr,unsigned char _byte);//任意地址写一个字节
unsigned char iic_read_addr_byte(unsigned char _addr); //任意地址读一个字节 #endif

iic.c

//可以用keil自带的逻辑分析仪查看iic端口的输出波形,通过对波形的分析,判断iic协议有没有工作。
#include "iic.h"
#include "mpu6050.h" //---------5us,实际测试是8us--------
void iic_dalay_us(void)
{
unsigned char i; _nop_();//nop是1个指令周期 = 1机器周期 = 12时钟周期 = 12*1/f,对于12M晶振,1us
i = ;
while(--i); //执行一次while循环2us
} //-------起始信号,时钟高,数据高变低---------------
void iic_start(void)
{
SDA = ;
iic_dalay_us();
SCL = ;
iic_dalay_us();
SDA = ;
iic_dalay_us();
SCL = ; //钳住I2C总线,准备发送或接收数据
} //--------停止信号-------
void iic_stop(void)
{
SDA = ;
iic_dalay_us();
SCL = ;
iic_dalay_us();
SCL = ;
iic_dalay_us();
SDA = ;
iic_dalay_us();
} //-------------
void iic_init(void)
{
SCL = ;
iic_dalay_us();
SDA = ;
iic_dalay_us();
} //----读取数据完成后,发送应答位,
//----0->ack,应答,告诉从机我要继续读取下一个字节,从机收到这个信号后继续发送数据
//----1->not ack,不应答,告诉从机我不在继续接受数据,从机停止发送数据
void iic_send_ack(bit _ack)
{
SDA = _ack;//接收完成后,拉高SDA,发送非应答信号
SCL = ;
iic_dalay_us();
SCL = ; //拉低,完成应答位
iic_dalay_us();
} //-------------主机发送完成,等待应答----------------
//主机发送完一个字节的数据之后会把SDA拉高,等待从机的信号,如果从机不应答,SDA将一直拉高,此时while函数等待250us,
//如果在SCL高电平期间,SDA被从设备拉低表示从设备应答,此时while跳过,应答位结束,
void iic_wait_ack(void)
{
unsigned char i = ; SCL = ;
iic_dalay_us();
while((SDA==)&&(i<))i++;//应答时,SDA为0表示从机成功接受到数据,
SCL = ; //拉低,完成应答位
} //------------------------
//写一个字节,从高位往低位写
//主机给从机发送数据,从机是在时钟的下降沿采集SDA的数据
void iic_write_byte(unsigned char _byte)
{
unsigned char i; for(i=;i<;i++)
{
SDA = _byte&0X80; //先写高位
iic_dalay_us();
SCL = ;
iic_dalay_us();
SCL = ; //下降沿采集数据
iic_dalay_us();
_byte = _byte<<;
} SDA = ;//发送完毕后,释放数据线,检测从机应答
iic_wait_ack();//等待应答
} //----------------------------------
//任意地址写一个字节,因为我使用的时候从设备只有一个,所以不需要写入从设备地址,
//如果有多个从设备的话,可以增加一个参数,写入从设备地址
void iic_write_addr_byte(unsigned char _addr,unsigned char _byte)
{
iic_start();
iic_write_byte(SlaveAddress); //此处写入从机地址,我这里只有一个,没有在函数里面体现
iic_write_byte(_addr);
iic_write_byte(_byte);
iic_stop();
} //------------------------
//读字节,也是先读取的是高位,需要左移
//主机从从机读取数据,本质上是从机检测主机发出的时钟信号有8个上升沿
unsigned char iic_read_byte(void)
{
unsigned char i = ;
unsigned char read_byte = ; SDA = ; //先确保主机释放SDA
iic_dalay_us(); for(i = ;i<;i++)
{
SCL = ;
read_byte = (read_byte<<)|SDA ;
iic_dalay_us();
SCL = ; //拉低,采集数据
iic_dalay_us();
} return read_byte;
} //------------------
//在某一地址内读取数据
unsigned char iic_read_addr_byte(unsigned char _addr)
{
unsigned char read_data; iic_start();
iic_write_byte(SlaveAddress);//发送设备地址+写信号
iic_write_byte(_addr); //发送存储器单元
iic_start(); //必须从新启动IIC
iic_write_byte(SlaveAddress + );//发送设备地址+读信号
read_data = iic_read_byte();//读出数据
iic_send_ack();//发送非应答信号
/*
原来问题出在这里!!,如果后面跟的是iic_stop的话,就发送非应答,告诉从机不要在发数据了,注意这里没有连续读取
不然的话,从机收到0的应答位之后会一直发送数据,但是这时主机又没有接受,导致数据只能第一次发送成功,后面都收不到数据。
*/
iic_stop(); return read_data;
}

keil仿真波形:

51单片机之IIC通信原理及软件仿真的更多相关文章

  1. 4-51单片机WIFI学习(开发板51单片机自动冷启动下载原理)

    上一篇链接 http://www.cnblogs.com/yangfengwu/p/8743936.html 这一篇说一下自己板子的51单片机自动冷启动下载原理,我挥舞着键盘和鼠标,发誓要把世界写个明 ...

  2. 2-51单片机ESP8266学习-AT指令(开发板51单片机自动冷启动下载原理)

    前言:了解就行,不必深究 上一篇链接 http://www.cnblogs.com/yangfengwu/p/8720148.html 源码链接:https://pan.baidu.com/s/1wT ...

  3. ESP8266 AT指令开发(基于STC89C52单片机): 测试下诱人的程序(51单片机,8266,MQTT远程通信控制)

    前言 实现的功能,APP通过SmartConfig给Wi-Fi模块配网并绑定设备,然后通过MQTT远程控制开发板的继电器, 简而言之: 51单片机+ESP8266用AT指令实现实现MQTT,(连接的本 ...

  4. 基于51单片机IIC通信的PCF8591学习笔记

    引言 PCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入.一个输出和一个串行I2C 总线接口.3 个地址引脚A0.A1 和A2 用于编程硬件地址,允许将最多8 个器件连接 ...

  5. 基于51单片机IIC通信的AT24C02学习笔记

    引言 最近在学习几种串行通信协议,感觉收获很多,这篇文章是学习IIC总线协议的第一篇文章,以后还会再写一篇关于PCF8591 IIC通信的ADDA转换芯片的文章. 关于IIC总线 IIC 即Inter ...

  6. 基于web的IM软件通信原理分析

    关于IM(InstantMessaging)即时通信类软件(如微信,QQ),大多数都是桌面应用程序或者native应用较为流行,而网上关于原生IM或桌面IM软件类的通信原理介绍也较多,此处不再赘述.而 ...

  7. 51单片机GPIO口模拟串口通信

    51单片机GPIO口模拟串口通信 标签: bytetimer终端存储 2011-08-03 11:06 6387人阅读 评论(2) 收藏 举报 本文章已收录于:   分类: 深入C语言(20) 作者同 ...

  8. 51单片机的TXD、 RXD 既接了 232 又接了 485芯片 ,会导致通信失败!

    51单片机的TXD. RXD 既接了 232 又接了 485 ,会导致通信失败! 下面是绘制电路板用的部分电路图: 通信现象: 1.我使用了USB-232的下载模块,把它接到P4上,发现单片机只能发送 ...

  9. STM32—IIC通信(软件实现底层函数)

    使用GPIO引脚模拟SDA和SCL总线实现软件模拟IIC通信,IIC的具体通信协议层和物理层链接:IIC #ifndef __BSP_IIC_H #define __BSP_IIC_H #includ ...

随机推荐

  1. Bootstrap 各种进度条详解

    一:默认的进度条 创建一个基本的进度条的步骤如下: 添加一个带有 class .progress 的 <div>. 接着,在上面的 <div> 内,添加一个带有 class . ...

  2. HTTP 请求未经客户端身份验证方案“Anonymous”授权。

    今天调取WebService的时候报: HTTP 请求未经客户端身份验证方案“Anonymous”授权. 解决办法: 配置文件里改: <basicHttpBinding> <bind ...

  3. Git项目协同开发学习笔记2:项目库开发协作相关命令

    之前介绍了如何用git构建项目库及其后续操作的问题,但主要还是个人的操作问题,不太涉及到项目协作方面的问题,所以来说下这块.传送门在这里(后面的可以不用看了). 1.同步 首先就式同步问题:在项目协作 ...

  4. linux环境中如何删除文件的前n行?

    需求描述: 今天看了一个系统的临时文件,有5.6G的大小,这个文件也没有用了,想要将大部分的文件都删除掉. 在此记录下删除的过程.删除前n行的记录. 操作过程: 对于数据量比较大的情况(本例5800万 ...

  5. c# 通过.net自带的chart控件绘制饼图pie chart

    c# 通过.net自带的chart控件绘制饼图pie chart   需要实现的目标是: 1.将数据绑定到pie的后台数据中,自动生成饼图. 2.生成的饼图有详细文字的说明. 具体的实现步骤: > ...

  6. c#系统消息类封装

    今天封装了一个返回json的消息类 using System; using System.Collections.Generic; using System.Linq; using System.Te ...

  7. xcode 5.1打包iOS 7.1应用问题笔记

    XCODE 5.1默认情况下是要求应用都通过64位编译.但是往往有些第三方的类库还是32位.还木有更新64位类库.使得项目编译出错. 解决办法: BuildSetting 的Valid Archite ...

  8. 扫盲 -- What's MOOC ?

    FAQ 1. MOOC是什么? 2. xMOOC又是什么? 它与之前在中国大陆网络上风靡一时的国外大学"公开课"有什么区别?3. xMOOC什么时候, 怎样出现的? 4. 有哪些网 ...

  9. 8 -- 深入使用Spring -- 4...2 使用AspectJ实现AOP

    8.4.2 使用AspectJ实现AOP AspectJ是一个基于Java语言的AOP框架.Spring 4.0 的AOP对AspectJ很好的集成. AspectJ是Java 语言的一个AOP实现, ...

  10. GC--垃圾收集器

    把周末的文章放在现在才来写,是自己太忙了?还是堕落了? 好吧直接进入主题吧,简单干脆的理解会让自己记忆深刻: 首先说明:GC垃圾收集器关注两件事情: 第一件:查找所有存活对象. 第二件:抛弃死对象(不 ...