好久没有在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. mac tips

    1. Mac Terminal color for different types 在 ~ 先建立一个文件  ~/.bash_profile 加入下面的两行:export CLICOLOR=1expo ...

  2. Ubuntu上安装jdk,Jboss

    Ubuntu上安装jdk 1.使用wget命令或sft方式从oracle官方下载tar.gz格式的jdk1.7,由于ubuntu不支持rpm安装,需要转换,所以不选择rpm格式的jdk 2.使用tar ...

  3. 快速记忆JavaScript中exec和match的使用

    JS模式匹配中exec,match用得非常多,所以掌握其用法对我们进行字符串的处理帮助非常大 1.exec的定义其用法        exec与match主要的不同是,exec是正则表达式里面的方法. ...

  4. Unity3D 游戏的碰撞

    首先创建两个精灵,然后都绑定上碰撞方法(这个是在上一篇文章的基本上): 不过 要注意一点就是碰撞器需要挂一个重力组件,不然无效 所以添加了差不多就能够实现物体碰撞了: 接下来技术写代码,让碰撞的时候进 ...

  5. linux教程:配置Tomcat开机启动

    我们在linux下安装好tomcat之后:经常是需要配置到开机启动的: 这样的话就不需要我们每次重启linux服务器之后自己在登陆运行startup.sh文件启动tomcat了 本次的演示环境是在ce ...

  6. Java 获取字符串中第N次出现的字符位置

    public static int getCharacterPosition(String string){    //这里是获取"/"符号的位置    Matcher slash ...

  7. IOS 生成设备唯一标识

    前言 iOS设备5.0以上放弃使用[[UIDevice currentDevice] uniqueIdentifier]来获得设备唯一ID iOS设备私有方法禁止用户获取和使用IMEI 需求 需要一个 ...

  8. X3850 Linux 下DSA日志收集办法

    收集工具下载 RHEL 6: 32bit-- [IBM 下载]http://delivery04.dhe.ibm.com/sar/CMA/XSA/03tza/1/ibm_utl_dsa_dsytb7x ...

  9. ETLLib库走读

    以前从来也没想过要去看库里的代码,咱想,那怎么也是很底层的代码,居然发现库里的代码也可以这样写,那就先读懂这个ETLLib(跨windows与Linux平台,封装了一些基本的系统调用函数,然后实现定制 ...

  10. 你好,C++(2)1.3 C++世界版图1.4 如何学好C++

    1.3  C++世界版图 C++语言的发展过程,不仅是一个特性不断增加.内容不断丰富的过程,更是一个在应用领域中不断攻城略地的过程.在其30余年的发展过程中,C++在多个应用领域都得到了广泛的应用和发 ...