好久没有在csdn上面做笔记了,主要是最近琐碎的事情太多,乱七八糟的事情让自己不能坚定下来做自己喜欢做的事情。上了星期花了两天的时间模拟了I2C的主机和从机通信。一般都是主机模拟,从机直接用硬件I2C的,但是由于所谓的项目里面没有I2C,但是要用到I2C了,因此就不得不用I/O口去模拟I2C了。

1、I2C协议

  I2C的协议相信网上已经有很多资料了,这里就不做详细介绍,只做简单说明即可。  

  a、I2C协议有两根总线:SDA和SCL。SDA为数据线,而SCL就是主机的时钟线。

  b、I2C是主机控制从机,时钟线只能主机改变。

  c、每个从机都有唯一的地址,主机通过发送从机地址来选择从机。

  d、I2C开始信号:SCL为高电平的时候,SDA由高电平向低电平跳变。

  e、I2C结束信号:SCL为高电平的时候,SDA由低电平向高电平跳变。

  f、主机传输信号的时候,SCL为高电平的时候,传输信号,SCL为低电平的时候改变信号。

  g、主机接收信号的时候,SCL为高电平的时候,接收信号。

2、如果用I/O模拟I2C的时候,一定要记住,是主机控制从机,从机根据主机SCL信号的改变而改变。

3、主机代码:

/****************************************************************************

I2C模拟条件:
1、HOST先发地址和控制命令给SLAVE;
2、地址和控制命令占一个字节;
3、字节格式:
    7~2             1                     0
   地址       单/多字节(0/1)       读/写(1/0)
4、发送多字节时候,第一个字节是地址和控制命令、第二个字节是长度、接下来是数据
5、发送多字节时候,第一个字节是地址和控制命令、第二个字节是要发送的

******************************************************************************/
#include "ioCC1110.h"
#include "hal.h"

#define SCL               P1_2
#define SDA               P1_3
#define IN                  0
#define OUT               1
BYTE ACK_Flag = ;
BYTE I2C_count;//计数器
BYTE receive_slave[] = {0x00}; //接收从机的字节
BYTE send_slave[] = {0xaa,0x55,0xbb,0x55,0xaa}; //发送字节给从机
/*初始化I2C*/

void SDA_(BYTE input)
{
    )        //SDA输出,p1.3
        P1DIR |= 0X08;
    else
        P1DIR &= 0XF7;    //SDA输入,p1.3
}

void SCL_(BYTE input)
{
    )        //SCL输出,P1.2
        P1DIR |= 0X04;
    else                  //SCL输入,P1.2
        P1DIR &= 0XFB;
}
/*启动I2C工作*/
void START_I2C(void)
{
  SDA = ;
  SCL = ;
//  Delay_us(20);  //这个没有多大影响,可以不要
  SCL = ;
  Delay_us();    //最开始50,5us太短了,不能判断,10us可以。
  SDA = ;
  Delay_us();   //最开始50,
  SCL = ;
  Delay_us();  //最开始50,这个延时和上面的延时可以不要,但是为了SLAVE有足够时间退出中断,就加上

}

/*停止I2C工作*/
void STOP_I2C(void)
{
//   SDA_OUT;
   SDA = ;
   Delay_us();;
   SCL = ;
   Delay_us();;
   SDA = ;
   Delay_us();;
   SCL = ;
   Delay_us();;
}

/*收到从器件的ACK帧,用于写完一个字节后检查*/
void Receive_SLAVE_ACK(void)
{
        SCL = ;
//        Delay_us(50);     //这里没有必要
        SDA = ;
        SDA_(IN);
        SCL = ;
        Delay_us();      //15us短了,经常出错

         == SDA)    // 若SDA=1表明非应答,置位非应答标志ACK_Flag
                ACK_Flag = ;
        SDA_(OUT);
        SCL = ;
//        Delay_us(50); //这里也没有必要
}

/*主器件往从器件里写一个字节*/
void WriteByte(BYTE writedata)
{
//SDA_OUT;
  SCL = ;            //SCL为低电平的时候可以改变数据状态
  ;i<;i++){
    )&0x01) == 0x01){//先写最高位
      SDA = ;
    }
    else{
      SDA = ;
    }
//    Delay_us(10);              //这个可以不要
    SCL = ;
    Delay_us();               //这个是等待SLAVE进入中断并接收数据,退出中断,15us不行,要20us
    writedata = writedata << ;//写完一位后将低位移到高位
    SCL = ;
//    Delay_us(50);            //这个也可以不要
  }
//    Delay_us(50);           //这个其实可以不要
    SCL = ;
}

/*主器件从从器件里面读取一个字节*/
BYTE ReadByte(void)
{
  BYTE TempData = ;
  SCL = ;
  ;i<;i++){
    SDA = ;
    SDA_(IN);
//    Delay_us(10);           //这个没有什么影响
    SCL = ;
    Delay_us();           //这个时间不能太短,不然的话就会读错1位
    TempData <<= ;
     == SDA)
        TempData |= 0x01;
    else
        TempData |= 0x00;
    SCL = ;
  }
  SCL = ;
  SDA_(OUT);
//  Delay_us(50);
  return (TempData);
}

void I2C_Bytes_Test(void)
{

/***********************写单字节正常*****************************/
#if 0
    START_I2C();
    WriteByte(0xa4);//写单字节的命令
    Receive_SLAVE_ACK();
    ){
        return;
    }
    WriteByte(0xaa);
    Receive_SLAVE_ACK();
    ){
      return;
    }
    STOP_I2C();
#endif
/***********************************************************/
 /*****************************写多字节********************/
#if 0
    START_I2C();
    WriteByte(0xa6);//写字多节的命令
    Receive_SLAVE_ACK();
    ){
        return;
    }
    WriteByte(0x05);//写多字节长度
    Receive_SLAVE_ACK();
    ){
        return;
    }
    ;i<;i++){   //开始写多字节
       WriteByte(send_slave[i]);
       Receive_SLAVE_ACK();
       ){
          return;
      }
    }
    STOP_I2C();
#endif
/**********************************************************/
/*****************I2C读正常**************************/

    START_I2C();
    WriteByte(0xa5);
    Receive_SLAVE_ACK();
    ){

      return;
    }
    WriteByte(0x03);            //读的长度
    Receive_SLAVE_ACK();
    ){
      return;
    }
    ;i<;i++){
        receive_slave[i] = ReadByte();
        Receive_SLAVE_ACK();
        )
          return;
//        UART1_Send_BYTE(receive_slave[i]);
    }
    STOP_I2C();
    LED0 = ;
    UART1_Send_String(receive_slave,);

/**********************************************************/
}

4、从机是在中断里面接收的,每次SCL上升沿的时候进入中断。代码:

#define START_STATE  0           //开始
#define CONTROL_STATE 1        //控制命令
#define ACK_STATE    2           //ACK应答
#define NOACK_STATE 3        //非ACK应答
#define WRITE_STATE 4       //写从机
#define READ_STATE 5        //读从机
#define STOP_STATE 6        //停止

BYTE STATE = ;
BYTE address = ;          //接收到的从机地址
BYTE count = ;            //接收到一位计数,产生一个字节的计数
BYTE receive_BYTE;         //从机接收主机单个字节
BYTE receive_buf[] = {0x00};         //从机接收主机数据的buffer
BYTE write_buf[] = {0x47,0x55,0x11};//从机发送数据给主机的buffer
BYTE receive_len = ;     //从接接收主机多字节时的长度
BYTE send_len = ;        //从机要发送给主机字节的长度
BYTE write_end = ;       //主机是否对从机写完
BYTE read_end = ;        //主机是否接收完从机发送的数据
BYTE length_ACK = ;      //从机是否接收到了要发送数据给主机的长度
BYTE read_num = ;
BYTE Temp = ;    

void PORT1_InterruptInit(void)
{
  EA = ;
  P1DIR &= 0XFB;//P1.2输入,scl
  IEN2 |= 0X10;//P1中断使能,scl
  P1IEN |= 0x04;//P1.2中断使能,scl
  PICTL &= ~0X02; //P1上升沿中断,0:rising edge
  P1IFG &= ~0x04;
}

#pragma vector = P1INT_VECTOR
 __interrupt void P1_ISR(void)
{
/*        if(P1IFG>0)         //按键中断
        {
          P1IFG = 0;
          LED1 = ~LED1;
        }
        P1IF = 0;          //清中断标志
 */
   )
   {
     P1IFG = ;
     switch(STATE){
        case START_STATE:
              SDA_(IN);
              if(SDA){
                  while(SDA);
                  while(SCL);
                  STATE = CONTROL_STATE; 

              }
              break;
        case CONTROL_STATE:
              address <<= ;
               == SDA)
                  address |= 0x01;
              else
                  address |= 0x00;
              count++;
               == count){
                  count = ;  

                  if((address & 0xfc) == 0xa4){
                      STATE = ACK_STATE;
                  }
                  else
                      STATE = NOACK_STATE;
              }
              break;
        case ACK_STATE:

              SDA_(OUT);//SDA设置为输出
              SDA = ;
              ) || (read_end == ) ){  //已经读完或者写完
                  STATE = STOP_STATE;
                  write_end = ;
                  read_end = ;
              }
              else{
                  if((address & 0x01) == 0x00)       //主机写SLAVE
                      STATE = WRITE_STATE;
                  else{
                      STATE = READ_STATE;
//                      UART1_Send_BYTE(STATE);
                  }
              }
              break;
        case NOACK_STATE:
              SDA_(OUT);
              SDA = ;
              address = ;
                  STATE = START_STATE;
              break;
        case WRITE_STATE:                      //主机写从机
              SDA_(IN);                        //这里将SDA置为输入,是因为发送ACK时候置为输出了
              if((address & 0x02) == 0x00){    //写单字节
                  receive_BYTE <<= ;
                   == SDA)
                      receive_BYTE |= 0X01;
                  else
                      receive_BYTE |= 0X00;

                  count++;
                   == count){
                      STATE = ACK_STATE;
//                      UART1_Send_BYTE(receive_BYTE);
                      count = ;
                      write_end = ;
                  }
              }
              else{
                  receive_buf[receive_len] <<= ;
                   == SDA)
                      receive_buf[receive_len] |= 0x01;
                  else
                      receive_buf[receive_len] |= 0x00;
                  count++;
                   == count){                     //接收到了8个字节
                      count = ;
                      receive_len++;
                      UART1_Send_BYTE(receive_buf[receive_len-]);
                      ]+)){  //这里+1是因为要先写长度
                          write_end = ;
                          receive_len = ;
                      }
                      STATE = ACK_STATE;
                  }
              }
              break;
        case READ_STATE:                            //主机读从机
              if(!length_ACK){                      //主机发送过从机长度

                  SDA_(IN);
                  send_len <<= ;
                   == SDA)
                      send_len |= 0x01;
                  else
                      send_len |= 0x00;

                  count++;
                   == count){
                      length_ACK = ;           //主机发送长度给从机
                      LED0 = ;
//                      UART1_Send_BYTE(send_len);
                      count = ;
                      STATE = ACK_STATE;
                  }
              }
              else{
                  SDA_(OUT);
                  Temp = write_buf[read_num];
                  Temp <<= count;
                  UART1_Send_BYTE(Temp);
                  if((Temp & 0x80) == 0x80)
                      SDA = ;
                  else
                      SDA = ;
                  count++;
                  ){    //移了7位,正好读一个字节
                      count = ;
                      read_num++;
                      if(read_num >= send_len){//读完了所有数据
                          read_num = ;
                          read_end = ;
                          length_ACK = ;    //将接收长度置0
                      }
                      STATE = ACK_STATE;
                  }
              }
              break;
        case STOP_STATE:
              SDA_(IN);
              while(!SDA);
              address = ;
//              receive_BYTE = 0;
              receive_len = ;
              send_len = ;
              LED1 = ~LED1;
              STATE = START_STATE;
              break;
        default:
              break;
     }
   }
   P1IF = ;
 }

I2C的主机从机模拟的更多相关文章

  1. 如何通过WiFi来进行Android的真机模拟

    我们知道,在使用模拟机模拟的时候会出现较多的问题,所以如果有一部Android手机的话进行真机模拟是极好的. 准备工作: 第一种方法:使用数据线,具体操作百度.略(非WIFI操作的真机模拟) 第二方法 ...

  2. I2C总线协议的软件模拟实现方法

    I2C总线协议的软件模拟实现方法 在上一篇博客中已经讲过I2C总线通信协议,本文讲述I2C总线协议的软件模拟实现方法. 1. 简述 所谓的I2C总线协议的软件模拟实现方法,就是用软件控制GPIO的输入 ...

  3. MS10-087微软OFFICE漏洞【参考拿机模拟】

    [声明:以下测试仅仅为了学习用途,模仿尝试与博主无关] 工  具:metasploit 目标机:windows xp sp3  步骤: 1.使用msf创建特殊代码的doc文档 命令: msfconso ...

  4. vb上位机模拟电压监测系统

    vb作为一种古老的语言,在工作中已经用不到了,但这门语言也是我在校期间研究比较多的一种,基本的通讯,数据库,界面等模块已经比较了解,马上要进单位实习了,研究的是电机的变频器,软件这块,希望在以后的工作 ...

  5. XCode请求数据中接收类型的后台与前台处理(本机模拟)

      Xcode报错问题如下:   解决办法如下: 0x1 ->请求数据时加上缺少的类型 AFHTTPSessionManager *manager = [selfAFHTTPSessionMan ...

  6. 街机模拟.samsho2

    1.sdyrugal https://www.douyu.com/599516 2.红中哥:西瓜视频  搜索 "红中哥游戏解说" 我搜索到的地址:https://live.ixig ...

  7. redis的主从复制,以及使用sentinel自动处理主机宕机问题,集群

    以下部分想看懂得有一定的redis基础,且步骤是连贯的,错一步都不行.redis运行多个实例,不懂得自行百度. 1. redis主从同步 原理: 从服务器向主服务器发送 SYNC 命令. 接到 SYN ...

  8. I2C总线以及GPIO模拟I2C

    ·I2C总线的一些特征: 1. 只要求两条总线,一条串行数据线(SDA),一条串行时钟线(SCL) 2. 两个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机/从机系统软件设定的地址:主机可 ...

  9. MySQL 系列(四) 主从复制、读写分离、模拟宕机、备份恢复方案生产环境实战

    本章内容: 主从复制 简介原理 备份主库及恢复从库,配置从库生效 读写分离 如果主宕机了,怎么办? 双主的情况 MySQL 备份及恢复方案 备份单个及多个数据库 mysqldump 的常用参数 如何增 ...

随机推荐

  1. C#系列之值类型和引用类型

    前言 这几天一直在思考这章讨论什么, 在上一章讨论string的时候牵涉到引用类型,那么我们这一章讨论讨论一下,值类型和引用类型. 值类型和引用类型,它们的区别来源于传值方式.有人会认为值类型就存在栈 ...

  2. ZFS+Dtrace+Zones+KVM=SMARTOS + dtrace 详细文档

    https://smartos.org/ http://dtrace.org/guide/chp-io.html http://blog.csdn.net/babyfacer/article/deta ...

  3. [转] ReactNative Animated动画详解

    http://web.jobbole.com/84962/     首页 所有文章 JavaScript HTML5 CSS 基础技术 前端职场 工具资源 更多频道▼ - 导航条 - 首页 所有文章 ...

  4. [转] JavaScript 和事件

    与浏览器进行交互的时候浏览器就会触发各种事件.比如当我们打开某一个网页的时候,浏览器加载完成了这个网页,就会触发一个 load 事件:当我们点击页面中的某一个“地方”,浏览器就会在那个“地方”触发一个 ...

  5. Fragment的懒加载

    我们在做应用开发的时候,一个Activity里面可能会以viewpager(或其他容器)与多个Fragment来组合使用,而如果每个fragment都需要去加载数据,或从本地加载,或从网络加载,那么在 ...

  6. Linux系统下查看USB设备名及使用USB设备

    1.系统插入USB设备后,从控制台界面有如下提示: 从控制台信息可以看出插入的USB设备名. 从上图可以看出,插入的USB设备为sde4. 但是,如果是CRT工具远程连接过去,可以使用下面的命令来查看 ...

  7. NUll在oracle与sqlserver中使用相同与区别

    最近在使用Oracle进行开发,遇到很多与以前使用sqlserver的不同语法.今天遇到null在两种数据库上面操作上的差别,在此记录两种数据库上的差异. null 与字符串相加 1.在oracle中 ...

  8. Extjs ——radiogroup子元素宽度调整

    配置项 类型 说明 allowBlank Boolean 设置是否必须选择至少一项,true表示可以不选,false表示不能为空至少选一项,默认为true blankText String 当allo ...

  9. 把nc v6的源码看懂

    看懂nc v6的源码! 碧桂园全部的正式环境的补丁都在我手里. 2015-11-18 2:33 谢谢两位一起努力的兄弟 谢谢超哥,谢谢祈冰哥,谢谢连老师,陈明大哥,谢谢龙哥,珍玉,谢谢廖生哥,谢谢林春 ...

  10. (转)VS2012网站发布详细步骤

    2.弹出网站发布设置面板,点击<新建..>,创建新的发布配置文件: 4. 在配置中,要选择“Release”——发布模式(Release   称为发布版本,它往往是进行了各种优化,使得程序 ...