I2C的主机从机模拟

发布者:csZhou最新更新时间:2018-05-20 来源: eefocus关键字: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、主机代码:


[html] view plain copy

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

  

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 = 0;  

BYTE I2C_count;//计数器  

BYTE receive_slave[100] = {0x00}; //接收从机的字节  

BYTE send_slave[5] = {0xaa,0x55,0xbb,0x55,0xaa}; //发送字节给从机  

/*初始化I2C*/  

  

void SDA_(BYTE input)  

{  

    if(input == 1)        //SDA输出,p1.3  

        P1DIR |= 0X08;  

    else  

        P1DIR &= 0XF7;    //SDA输入,p1.3  

}  

  

void SCL_(BYTE input)  

{  

    if(input == 1)        //SCL输出,P1.2  

        P1DIR |= 0X04;  

    else                  //SCL输入,P1.2  

        P1DIR &= 0XFB;      

}  

/*启动I2C工作*/  

void START_I2C(void)  

{  

  SDA = 1;           

  SCL = 0;  

//  Delay_us(20);  //这个没有多大影响,可以不要  

  SCL = 1;  

  Delay_us(10);    //最开始50,5us太短了,不能判断,10us可以。  

  SDA = 0;    

  Delay_us(2);   //最开始50,  

  SCL = 0;    

  Delay_us(5);  //最开始50,这个延时和上面的延时可以不要,但是为了SLAVE有足够时间退出中断,就加上  

  

}  

  

/*停止I2C工作*/  

void STOP_I2C(void)  

{  

//   SDA_OUT;   

   SDA = 0;  

   Delay_us(50);;  

   SCL = 1;  

   Delay_us(50);;  

   SDA = 1;  

   Delay_us(50);;  

   SCL = 0;  

   Delay_us(50);;  

}  

  

/*收到从器件的ACK帧,用于写完一个字节后检查*/  

void Receive_SLAVE_ACK(void)  

{         

        SCL = 0;  

//        Delay_us(50);     //这里没有必要  

        SDA = 1;   

        SDA_(IN);  

        SCL = 1;   

        Delay_us(20);      //15us短了,经常出错  

          

        if(1 == SDA)    // 若SDA=1表明非应答,置位非应答标志ACK_Flag  

                ACK_Flag = 1;  

        SDA_(OUT);  

        SCL = 0;  

//        Delay_us(50); //这里也没有必要  

}  

  

  

  

  

/*主器件往从器件里写一个字节*/  

void WriteByte(BYTE writedata)  

{    

//SDA_OUT;   

  SCL = 0;            //SCL为低电平的时候可以改变数据状态  

  for(int i=0;i<8;i++){       

    if(((writedata>>7)&0x01) == 0x01){//先写最高位  

      SDA = 1;  

    }  

    else{  

      SDA = 0;  

    }  

//    Delay_us(10);              //这个可以不要  

    SCL = 1;  

    Delay_us(20);               //这个是等待SLAVE进入中断并接收数据,退出中断,15us不行,要20us  

    writedata = writedata << 1;//写完一位后将低位移到高位      

    SCL = 0;  

//    Delay_us(50);            //这个也可以不要  

  }  

//    Delay_us(50);           //这个其实可以不要  

    SCL = 0;  

}  

  

/*主器件从从器件里面读取一个字节*/  

BYTE ReadByte(void)  

{  

  BYTE TempData = 0;  

  SCL = 0;  

  for(int i=0;i<8;i++){  

    SDA = 1;  

    SDA_(IN);  

//    Delay_us(10);           //这个没有什么影响  

    SCL = 1;  

    Delay_us(150);           //这个时间不能太短,不然的话就会读错1位  

    TempData <<= 1;  

    if(1 == SDA)  

        TempData |= 0x01;  

    else  

        TempData |= 0x00;    

    SCL = 0;  

  }  

  SCL = 0;  

  SDA_(OUT);  

//  Delay_us(50);  

  return (TempData);  

}  

  

void I2C_Bytes_Test(void)  

{  

  

/***********************写单字节正常*****************************/  

#if 0   

    START_I2C();  

    WriteByte(0xa4);//写单字节的命令  

    Receive_SLAVE_ACK();  

    if(ACK_Flag == 1){  

        return;   

    }  

    WriteByte(0xaa);  

    Receive_SLAVE_ACK();  

    if(ACK_Flag == 1){  

      return;  

    }  

    STOP_I2C();  

#endif  

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

 /*****************************写多字节********************/  

#if 0   

    START_I2C();  

    WriteByte(0xa6);//写字多节的命令  

    Receive_SLAVE_ACK();  

    if(ACK_Flag == 1){  

        return;   

    }  

    WriteByte(0x05);//写多字节长度  

    Receive_SLAVE_ACK();  

    if(ACK_Flag == 1){  

        return;   

    }  

    for(int i=0;i<5;i++){   //开始写多字节  

       WriteByte(send_slave[i]);  

       Receive_SLAVE_ACK();  

       if(ACK_Flag == 1){  

          return;   

      }  

    }  

    STOP_I2C();  

#endif      

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

/*****************I2C读正常**************************/  

  

    START_I2C();  

    WriteByte(0xa5);  

    Receive_SLAVE_ACK();  

    if(ACK_Flag == 1){  

       

      return;  

    }  

    WriteByte(0x03);            //读的长度  

    Receive_SLAVE_ACK();  

    if(ACK_Flag == 1){  

      return;  

    }  

    for(int i=0;i<3;i++){  

        receive_slave[i] = ReadByte();  

        Receive_SLAVE_ACK();  

        if(ACK_Flag == 1)  

          return;  

//        UART1_Send_BYTE(receive_slave[i]);        

    }  

    STOP_I2C();  

    LED0 = 0;  

    UART1_Send_String(receive_slave,3);  

  

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

}  



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


[html] view plain copy

#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 = 0;  

BYTE address = 0;          //接收到的从机地址  

BYTE count = 0;            //接收到一位计数,产生一个字节的计数  

BYTE receive_BYTE;         //从机接收主机单个字节  

BYTE receive_buf[20] = {0x00};         //从机接收主机数据的buffer  

BYTE write_buf[20] = {0x47,0x55,0x11};//从机发送数据给主机的buffer  

BYTE receive_len = 0;     //从接接收主机多字节时的长度  

BYTE send_len = 0;        //从机要发送给主机字节的长度  

BYTE write_end = 0;       //主机是否对从机写完  

BYTE read_end = 0;        //主机是否接收完从机发送的数据  

BYTE length_ACK = 0;      //从机是否接收到了要发送数据给主机的长度  

BYTE read_num = 0;  

BYTE Temp = 0;      

  

  

void PORT1_InterruptInit(void)  

{  

  EA = 1;  

  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;          //清中断标志  

 */  

   if(P1IFG>0)  

   {   

     P1IFG = 0;  

     switch(STATE){  

        case START_STATE:  

              SDA_(IN);  

              if(SDA){  

                  while(SDA);                

                  while(SCL);  

                  STATE = CONTROL_STATE;   

                    

              }  

              break;  

        case CONTROL_STATE:  

              address <<= 1;  

              if(1 == SDA)  

                  address |= 0x01;  

              else  

                  address |= 0x00;                   

              count++;  

              if(8 == count){  

                  count = 0;    

                    

                  if((address & 0xfc) == 0xa4){  

                      STATE = ACK_STATE;                                              

                  }  

                  else  

                      STATE = NOACK_STATE;                                    

              }  

              break;  

        case ACK_STATE:  

                

              SDA_(OUT);//SDA设置为输出  

              SDA = 0;  

              if((write_end == 1) || (read_end == 1) ){  //已经读完或者写完  

                  STATE = STOP_STATE;  

                  write_end = 0;  

                  read_end = 0;  

              }  

              else{  

                  if((address & 0x01) == 0x00)       //主机写SLAVE  

                      STATE = WRITE_STATE;  

                  else{  

                      STATE = READ_STATE;  

//                      UART1_Send_BYTE(STATE);  

                  }  

              }  

              break;  

        case NOACK_STATE:  

              SDA_(OUT);  

              SDA = 1;  

              address = 0;  

                  STATE = START_STATE;  

              break;  

        case WRITE_STATE:                      //主机写从机  

              SDA_(IN);                        //这里将SDA置为输入,是因为发送ACK时候置为输出了  

              if((address & 0x02) == 0x00){    //写单字节  

                  receive_BYTE <<= 1;  

                  if(1 == SDA)  

                      receive_BYTE |= 0X01;  

                  else  

                      receive_BYTE |= 0X00;  

                    

                  count++;  

                  if(8 == count){  

                      STATE = ACK_STATE;  

//                      UART1_Send_BYTE(receive_BYTE);  

                      count = 0;  

                      write_end = 1;  

                  }               

              }  

              else{  

                  receive_buf[receive_len] <<= 1;  

                  if(1 == SDA)  

                      receive_buf[receive_len] |= 0x01;  

                  else  

                      receive_buf[receive_len] |= 0x00;  

                  count++;  

                  if(8 == count){                     //接收到了8个字节  

                      count = 0;  

                      receive_len++;  

                      UART1_Send_BYTE(receive_buf[receive_len-1]);  

                      if(receive_len >= (receive_buf[0]+1)){  //这里+1是因为要先写长度  

                          write_end = 1;  

                          receive_len = 0;  

                      }      

                      STATE = ACK_STATE;  

                  }  

              }  

              break;   

        case READ_STATE:                            //主机读从机  

              if(!length_ACK){                      //主机发送过从机长度  

                    

                  SDA_(IN);                            

                  send_len <<= 1;  

                  if(1 == SDA)  

                      send_len |= 0x01;  

                  else  

                      send_len |= 0x00;  

                    

                  count++;  

                  if(8 == count){  

                      length_ACK = 1;           //主机发送长度给从机        

                      LED0 = 0;  

//                      UART1_Send_BYTE(send_len);  

                      count = 0;  

                      STATE = ACK_STATE;  

                  }  

              }  

              else{  

                  SDA_(OUT);  

                  Temp = write_buf[read_num];  

                  Temp <<= count;  

                  UART1_Send_BYTE(Temp);  

                  if((Temp & 0x80) == 0x80)  

                      SDA = 1;  

                  else  

                      SDA = 0;  

                  count++;  

                  if(count == 8){    //移了7位,正好读一个字节  

                      count = 0;  

                      read_num++;  

                      if(read_num >= send_len){//读完了所有数据  

                          read_num = 0;  

                          read_end = 1;  

                          length_ACK = 0;    //将接收长度置0  

                      }      

                      STATE = ACK_STATE;  

                  }     

              }  

              break;  

        case STOP_STATE:  

              SDA_(IN);  

              while(!SDA);                

              address = 0;  

//              receive_BYTE = 0;  

              receive_len = 0;  

              send_len = 0;  

              LED1 = ~LED1;  

              STATE = START_STATE;  

              break;  

        default:          

              break;  

     }  

   }  

   P1IF = 0;  

 }  


关键字:I2C  主机从机模拟 引用地址:I2C的主机从机模拟

上一篇:GPIO实现I2C从机的设计
下一篇:GPIO模拟I2C程序实现

推荐阅读最新更新时间:2024-03-16 16:03

S3C2440裸机------I2C_S3C2440的I2C控制器控制时序
1.寄存器 S3C2440的I2C主要由以下四个寄存器来控制。 2.数据传输流程
[单片机]
S3C2440裸机------I2C_S3C2440的<font color='red'>I2C</font>控制器控制时序
无人机设计中STM32库实现的模拟i2c代码
目前发现国内正儿八经机器人、无人机并且还能活跃地上网关注行业前沿动向、热爱写科普文章的研究人员原来越少。因此所有的研究回答里都没有人真正说明白无人机到底是什么,而理解无人机到底是什么才是回答这个问题的先决条件。 什么是无人机 首先,无人机就是不载人的飞行器,而说到飞行器,通常我们又可以把飞行器分为三类。 1、固定翼(fixed wing)。平时坐的波音747空客A380,还有F-16歼-15之类的都是固定翼飞机。顾名思义就是翅膀形状固定,靠流过机翼的风提供升力。动力系统包括桨和助推发动机。固定翼根据机翼尺寸的不同还有很多小的分类,在此不细说。固定翼飞行器的优点是在三类飞行器里续航时间最长、飞行效率最高、载荷最大,缺点是起飞的时
[单片机]
无人机设计中STM32库实现的<font color='red'>模拟</font><font color='red'>i2c</font>代码
利用数字示波器调试嵌入式I2C总线
I2C总线是PHLIPS公司上世纪80年代推出的一种两线式串行总线,最初为音频、视频设备所开发,如今则多在各种嵌入式系统中用于连接微控制器及其外围设备。 I2C总线仅需采用两根通信线(一根为串行数据线“SDA”,一根为串行时钟线“SCL”),而传输速率在高速模式下可达3.4Mbit/s,并且是多主总线。每一个挂接在I2C总线上的I2C器件均可通过唯一的地址进行访问。 在嵌入式系统开发中应用I2C总线可有效缩减元器件面积、改善抗干扰能力及增强设计的兼容性。当然,在享受其设计便利性的同时,信号的复杂性也将提高系统调试的难度。 本文阐述了在实际开发中所遇到的I2C通信问题及使用示波器分析问题和解决问题的方法。 分析过程中
[嵌入式]
S5PV210开发 -- I2C 你知道多少?(二)
上一篇主要是介绍了下芯片手册 I2C 部分,都应该看些什么,以及上拉电阻取值和传输速率模式选择。 这一篇该来点程序了,首先以 AT24C02 (EEPROM)为基础介绍一下I2C设备驱动编程,然后以 MT9P031 为基础介绍 LINUX 下内核配置。 最后是 MPU6050 为基础的单片机下 I2C 通信程序。 一、I2C设备驱动编程 该部分我会以嵌入式Linux软硬件开发详解第 12 章,和Linux设备驱动开发详解第 15 章为参考来展开。 (1)I2C 设备驱动程序结构 1. I2C设备驱动层次结构 从系统的角度来看,Linux中 I2C 设备程序所处的位置如下图所示。 I2C设备驱动程序包含总线驱动层和
[单片机]
S5PV210开发 -- <font color='red'>I2C</font> 你知道多少?(二)
数字车载音响系统设计
  随汽车电子技术的迅速发展, 车载音频领域正在经历一个前所未有的技术变革, 使用者对车载音频提出了功能多样化、操作人性化的要求, 主要包括以下三个方面:(1) 具备更好的电台接收效果以及更简便的数字式调台操作;(2) 支持多种外加存储设备, 如支持大容量的U 盘和SD 卡等;(3) 提供更加丰富的音效处理, 如高音、重低音、等响度、平衡度等的调节以及提供流行、摇滚、爵士、古典等音效处理。以这些需求为出发点, 设计了一款数字车载音响系统。   1 I2C 协议   I2C 总线作为同步串行数据输出总线, 由一条串行数据线(SDA) 和一条串行时钟线(SCL) 组成。它是一个真正的多主机总线, 如果多个主机同时进行初始化数
[嵌入式]
STM32 I2C总线 自我总结学习
现在打算彻底搞清楚STM32的I2C总线通信----首先是对AT24C02的读写----手上有PCF8574的IO扩展芯片,也是I2C协议的,希望实现多个控制! STM32的I2C有自带的硬件驱动,也可以使用GPIO模拟-----先总结一下硬件驱动下的问题。 ----------------------------------硬件下-----以AT24C02与PCF8574为例--------------- ------第一部分是简单宏定义------- #define I2C_Speed 300000 //传输速率--挂载原件多时要求降低速率,自己在调试过程中出现过这样问题 #define I2C1_OWN
[单片机]
STM32 <font color='red'>I2C</font>总线 自我总结学习
STC8系列单片硬件I2C使用教程(一)
一、I2C相关的寄存器 ① I2C 配置寄存器 ② I2C 主机控制寄存器 ③ I2C 主机辅助控制寄存器 ④ I2C 主机状态寄存器 ⑤ I2C 数据寄存器 ⑥ 外设端口切换控制寄存器 1 ⑦ 外设端口切换控制寄存器 2 二、程序编写 ① 寄存器和相关宏定义 sfr P_SW2 = 0xBA; //外设端口切换寄存器 2 #define I2CCFG (*(unsigned char volatile xdata *)0xfe80) #define I2CMSCR (*(unsigned char volatile xdata *)0xfe81) #define I2CMSST
[单片机]
STC8系列单片<font color='red'>机</font>硬件<font color='red'>I2C</font>使用教程(一)
电源技术中I2C及PM Bus总线
     1 前言   随着IT技术对工业电源技术的渗透,数字工业电源技术应运而生,由于数字工业电源的控制灵活、结构变化灵活、调节、维护方便和造价低的一系列优点,代表了工业电源技术的发展方向。而在数字工业电源中,总线技术发挥了很重要的作用,本文结合数字工业电源中常用的总线技术加以介绍。   2 I2C总线   I2C总线是英文“Inter Integrated Circuit Bus”的缩写,常译为“集成电路间总线”或“内部集成电路总线”。I2C总线以它强大的控制能力和精巧的电路结构,得到各生产厂家的认可。目前,I2C总线在许多电子产品中得到了广泛应用。   I2C总线接口的有关技术指标最早在1982年确定。Phi
[嵌入式]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved