通信协议--软件模拟IIC

一、IIC简介

IIC(Inter-Integrated Circuit, 内部集成电路)总线是飞利浦公司开发的两线式串行总线,用于短距离传输,常用于微控制器及其外围设备,只有二根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,两条线可以挂多个设备。 IIC设备(绝大多数)里有个固化的地址,只有在两条线上传输的值等于IIC设备的固化地址时,其才会作出响应。通常我们为了方便把IIC设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备。
在这里插入图片描述I2C总线通过上拉电阻接正电源。即当总线空闲时,两根线均为高电平。如此,连在总线上的任一器件输出的低电平,都可以使得总线的信号变低,也就是说各器件的SDA和SCL都是线"与"关系。

I2C是同步串行总线,由SCL为所有设备提供统一的时钟信号。 即使多个节点发送时钟信号时,由于SCL"线与"的原因,SCL上为统一的时钟信号。

数据位(1\0)有效性规定:I2C总线进行数据传送时,时钟信号为高电平期间,SDA线上的数据必须保持稳定;只有在SCL线的信号为低电平器件,SDA线的才可进行高低电平状态变化。

在这里插入图片描述
起始信号、终止信号、应答信号

起始信号: SCL线为高电平期间,SDA线由高电平向低电平跳变(下降沿)----是一种电平跳变时序信号

终止信号:SCL线为高电平期间,SDA线由低电平向高电平跳变(上升沿)-----是一种电平跳变的时序信号

应答信号:在接收数据的IC(接收器)在接收到8bit数据后,向发送数据的IC(发送器)发出特定的低电平脉冲,表示已收到数据。即发送器在时钟脉冲9期间释放数据线,这样接收器就可以反馈一个应答信号。ACK(低电平)—规定为有效应答位,NACK(高电平),规定为非应答位,表示接收器接收该字节咩有成功。

为了时钟信号的统一,我们假定只有一个设备发出时钟信号,该设备称为主机。其他IIC设备作为从机存在。
在这里插入图片描述
在这里插入图片描述

二、IO口模拟IIC的驱动程序

底层驱动函数有:
IIC_Start
IIC_Stop
IIC_SendAck
IIC_SendNack
IIC_ReadAck:读取不到ACK信号时,要发送IIC_Stop
IIC_WriteByte
IIC_ReadByte

IO口初始化

假设使用的 IIC_SCL IIC_SDA 输出 以及READ_SDA输入

#define SDA_IN() 宏定义设置管脚为输入模式

#define SDA_OUT() 宏定义设置管脚为输出模式

起始信号

SCL线为高电平期间,SDA线由高电平向低电平跳变(下降沿)
在这里插入图片描述

void IIC_Starts(void)
{
       SDA_OUT();//--IO口配置为输出
       IIC_SCL = 1; //--拉高时钟线
       IIC_SDA = 1; //--拉高数据线
       dealy_us(5); //--延时》4.7us
       IIC_SDA = 0;   //当SCL线为高电平是, SDA线上由高到低电平跳变
       delay_us(5);  //--延时》4.7us
       IIC_SCL = 0;    //钳住,等待发送或接受
}

终止信号

SCL线为高电平期间,SDA线由低电平向高电平跳变(上升沿)
在这里插入图片描述

void IIC_Stop(void)
{
    SDA_OUT();//--IO口配置为输出    
    IIC_SCL  =  1;//--拉低信号线
    IIC_SDA  = 0; //--拉低数据线
    dealy_us(5);  //--延时》4.7us
    IIC_SDA = 1;   //当SCL线为高电平是, SDA线上由低到高电平跳变
    delay_us(5); //--延时》4.7us
}

接收ACK应答

先将数据线拉高,延时等待稳定,然后将时钟线拉高,延时等待稳定,最后采样数据线电平状态,如果是高电平,未应答,如果是低电平,应答。
在这里插入图片描述

uint8_t IIC_Wait_ACK(void)
{
      uint8_t ucTimeCnt= 0;
     SDA_IN()//SDA管脚设为输入模式
     IIC_SDA = 1;  //--将数据线拉高
     delay_us(1);  //--延时1us
    IIC_CLK = 1; //--将时钟线拉高
    delay_us(1);//-延时等待稳定
   while(READ_SDA)//--采样数据线电平变化
   {
             ucTimeCnt ++;
             if(ucTimeCnt>250)
             {
                      IIC_Stop();
                     return 1; //--无回应
             }
   }
  //等到应道地电平
 IIC_CLK = 0//钳住,等待发送或接受      
 return 0//--有回应
}

发送应答ACK

先将数据线和时钟线都拉低,延时等待稳定,然后将时钟线拉高,延时等待4us,将时钟线拉低
在这里插入图片描述

void IIC_ACK(void)
{
   SDA_OUT(); //--将IO口配置为输出
   IIC_CLK = 0; //--时钟线拉低
   IIC_SDA = 0; //--数据线拉低
   delay_us(2);//--延时等待
   IIC_CLK = 1;//--将时钟线拉高
   delay_us(2); //--延时等待
   IIC_CLK = 0; //--将时钟线拉低
}

发送非应答NACK

先把时钟线拉低,数据线拉高,延时等待稳定,然后把时钟线拉高,等待采样结束,然后把时钟线拉低
在这里插入图片描述

void IIC_NACK(void)
{
    SDA_OUT() ;
    IIC_CLK = 0;
    IIC_SDA = 1delay_us(2);
    IIC_CLK = 1;
    delay_us(2);
    IIC_CLK = 0;
}

发送一字节数据

void  IIC_Send_Byte(uint8 byte)
{
     uint8_t i = 0;
     SDA_OUT();//--将IO口配置为输出
     IIC_SCL = 0;   // 只有在SCL线为低电平时,SDA线才可以改变
    for(i=0;i<8;i++)
     {
        IIC_SDA = (byte&0x80)>>7;   //每次发最高位
        delay_us(2);
        IIC_CLK = 1;
        delay_us(2);
        IIC_CLK = 0;
        delay_us(2);
        byte<<1;    //更新最高位
   } 
}

读取一个字节,并发送ACK或NACK,发送NACK即通知从机发送器结束数据发送,释放SDA线

uint8IIC_Read_Byte(uint8_t  ack)
{
    uint8_t i, recvVal=0;
     SDA_IN(); //--将IO口配置为输入
    for(i=0; i<8; i++)
   {
          IIC_CLK = 0;//--时钟线拉低的时候,SDA才允许变化
          delay_us(2);  //等待输出
          IIC_CLK = 1//拉高时钟线,不允许SDA变化,可以读取SDA
          recvVal<<1;  //将最低位空出
          if(IIC_SDA()) recvVal++;   //高电平,则最低位为1
          delay_us(1);        
   }
   if(ack)
      IIC_ACK();//--通知主机继续发送
   else
      IIC_NACK();//--通知从机发送结束
  retrun recvVal;   //--返回读取的数据
}
  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值