IIC简单总结

协议上的主要信号

IIC硬件上只有两根线:时钟线SCL与数据线SDA。协议上上有以下几个信号:start,stop,ack,nack。

  • start:由主机发出,当SCL位于高电平时,SDA发生一个下降沿信号(电平由高到低)
    在这里插入图片描述

  • stop:由主机发出,当SCL处于高电平时,SDA发生一个上升沿信号(电平由低到高)
    在这里插入图片描述

  • ack:由数据接收方发出。当主机发送完1 Byte数据后,释放SDA线,在第九个时钟时,从机将SDA拉低;或者当主机接收完1 Byte数据后,仍然想继续接收数据,则在第九个时钟时,将SDA拉低。
    在这里插入图片描述

  • nack:由主机发出,当主机接收完1Byte数据后,若不想继续接收数据,则在第九个时钟时,主机将SDA拉高
    在这里插入图片描述

发送与接收数据流程
  • 因为IIC频率支持100—400Khz,即SCL周期为2.5—10us,所以SCL一个周期中高电平时间约为1.25—5us。
  • IIC传输数据的过程如下:
  1. 发送一个start开始信号
  2. 发送1Byte数据,高7位为设备地址,最后一位是读写位,此时是0(表示写,因为下一步要写入寄存器地址)
  3. 等待从机发出ack应答信号
  4. 发送一个start开始信号
  5. 发送寄存器地址(寄存器地址可能是8位、10位或16位,这里以8位举例)
  6. 等待从机发出ack应答信号
  7. 当主机是写入1Byte数据操作时
    (1). 发送1Byte数据
    (2). 等待从机发出ack应答信号
    (3). 发送stop信号
  8. 当主机是读取1Byte数据操作时
    (1). 发送一个start开始信号
    (2). 发送1Byte数据,高7位为设备地址,最后一位是读写位,此时是1(表示读,因为下一步要读取设备寄存器的值)
    (3). 等待从机发出ack应答信号
    (4). 接收1Byte数据(寄存器的数值)
    (5). 发送nack信号
    (6). 发送stop信号
  • 数据传输时,在SCL处于高电平时,SDA电平不能变化;只有SCL处于低电平时,SDA电平才可以变化。
    在这里插入图片描述
附带模拟IIC代码(默认寄存器地址为8位,默认一次只传输1Byte数据)
#ifndef __ANALOG_IIC_H_
#define __ANALOG_IIC_H_

#define IIC_ACK_ERR     (1)
#define IIC_ACK_OK      (0)
#define IIC_WRITE       (0)
#define IIC_READ        (1)
#define IIC_TRAN_ERR    (2)
#define IIC_TRAN_OK     (3)

/*IIC结构体*/
typedef struct
{
    unsigned char Drive_Address;
    unsigned char Reg_Address;
    unsigned char Byte;
    unsigned char Dir;
    void (*delay)(void);
    void (*SCL_Out)(void);
    void (*SCL_In)(void);
    void (*SCL_High)(void);
    void (*SCL_Low)(void);
    void (*SDA_Out)(void);
    void (*SDA_In)(void);
    void (*SDA_High)(void);
    void (*SDA_Low)(void);
    unsigned char (*SDA_State)(void);
}IIC_t;

/*IIC传输一个字节*/
unsigned char IIC_Tranfer_Byte(IIC_t *iic,unsigned char dir);
#endif
#include "Analog_iic.h"

/*IIC初始化,按实际开发环境自行编写, 即映射IIC_t结构体中的操作函数*/

/*IIC开始信号*/
void IIC_Start(IIC_t *iic)
{
    iic->SCL_Out();
    iic->SDA_Out();
    iic->SCL_Low();
    iic->SDA_High();
    iic->delay();

    /*在SCL高电平时,拉低SDA*/
    iic->SCL_High();
    iic->delay();
    iic->SDA_Low();

    iic->delay();
    iic->SCL_Low();
    iic->delay();
    iic->delay();
}

/*IIC停止信号*/
void IIC_Stop(IIC_t *iic)
{
    iic->SCL_Out();
    iic->SDA_Out();
    iic->SCL_Low();
    iic->SDA_Low();
    iic->delay();

    /*在SCL高电平时,拉高SDA*/
    iic->SCL_High();
    iic->delay();
    iic->SDA_High();

    iic->delay();
    iic->SCL_Low();
    iic->delay();
    iic->delay();
}

/*IIC发送Nack信号*/
void IIC_Send_Nack(IIC_t *iic)
{
    /*释放SDA*/
    iic->SCL_Out();
    iic->SDA_Out();
    iic->SCL_Low();
    iic->delay();

    /*拉高SDA并保持*/
    iic->SDA_High();

    iic->SCL_High();
    iic->delay();
    iic->delay();
    iic->SCL_Low();
    iic->delay();
    iic->delay();
}

/*IIC等待Ack信号*/
unsigned char IIC_Wait_Ack(IIC_t *iic)
{
    unsigned short time = 0;

    /*释放SDA*/
    iic->SCL_Out();
    iic->SDA_Out();
    iic->SCL_Low();
    iic->SDA_High();
    iic->SDA_In();
    iic->delay();

    /*等待从机拉低SDA*/
    iic->SCL_High();
    iic->delay();
    while(iic->SDA_State())
    {
        time ++;
        if(time >= 0xffff)
        {
            IIC_Stop(iic);
            return IIC_ACK_ERR;
        }
    }

    iic->delay();
    iic->SCL_Low();
    iic->delay();
    iic->delay();
    return IIC_ACK_OK;
}

/*发送一个字节*/
void IIC_Send_Byte(IIC_t *iic,unsigned char Byte)
{
    unsigned char i;
    iic->SCL_Out();
    iic->SDA_Out();
    iic->SCL_Low();
    iic->SDA_Low();
    iic->delay();

    for(i = 0;i < 8;i ++)
    {
        /*一位一位发送Byte*/
        if( Byte & (0x80) )
            iic->SDA_High();
        else
            iic->SDA_Low();

        /*送出SCL时钟*/
        iic->SCL_High();
        iic->delay();
        iic->delay();
        iic->SCL_Low();
        iic->delay();
        iic->delay();
        Byte <<= 1;
    }
}

/*接收一个字节*/
unsigned char IIC_Recive_Byte(IIC_t *iic)
{
    unsigned char byte = 0;
    unsigned char i;

    iic->SCL_Out();
    iic->SDA_In();
    iic->SCL_Low();
    iic->delay();

    for(i = 0;i < 8;i ++)
    {
        iic->SCL_High();
        iic->delay();

        byte <<= 1;
        if(iic->SDA_State())
            byte |= 0x01;
        else
            byte &= (0xFE);

        iic->delay();
        iic->SCL_Low();
        iic->delay();
        iic->delay();
    }
	return byte;
}

/*IIC传输一个字节*/
unsigned char IIC_Tranfer_Byte(IIC_t *iic,unsigned char dir)
{
    unsigned char _Drive_Addre = 0;
    iic->Dir = dir;

    /*发送开始信号*/
    IIC_Start(iic);

    /*发送器件地址--写*/
    _Drive_Addre = ( (iic->Drive_Address << 1) | (0x00) );
    IIC_Send_Byte(iic,_Drive_Addre);

    /*等待ACK信号*/
    if(IIC_Wait_Ack(iic))
    {
        return IIC_ACK_ERR;
    }

    /*重新开始*/
    IIC_Start(iic);

    /*发送寄存器地址*/
    IIC_Send_Byte(iic,iic->Reg_Address);

    /*等待ACK信号*/
    if(IIC_Wait_Ack(iic))
    {
        return IIC_ACK_ERR;
    }

    /*器件地址*/
    _Drive_Addre = ( (iic->Drive_Address << 1) | (iic->Dir) );

    /*如果是读,则需要再次写入器件地址*/
    if(iic->Dir == IIC_READ)
    {
        /*发送开始信号*/
        IIC_Start(iic);
        /*发送器件地址*/
        IIC_Send_Byte(iic,_Drive_Addre);
        /*等待ACK信号*/
        if(IIC_Wait_Ack(iic))
        {
            return IIC_ACK_ERR;
        }
        iic->Byte = IIC_Recive_Byte(iic);

        IIC_Send_Nack(iic);     //发送Nack
    }
    /*如果是写,则直接写入数据*/
    else if(iic->Dir == IIC_WRITE)
    {
        /*写入数据*/
        IIC_Send_Byte(iic,iic->Byte);
        /*等待ACK信号*/
        if(IIC_Wait_Ack(iic))
        {
            return IIC_ACK_ERR;
        }
    }

    /*停止信号*/
    IIC_Stop(iic);

    return IIC_TRAN_OK;
}

个人意见,因为模拟IIC需要保证通信时序要求并且延时函数要求比较高,所以尽量使用硬件IIC。以上是我对IIC的简单总结(实际上IIC的要点还有很多),不足之处请在评论区指出。
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值