【硬件通讯协议】IIC总线协议以及模拟(软件)IIC

参考资料

NXP 官方网站提供的IIC总线规范

I2C-bus specification and user manuahttps://www.nxp.com/docs/en/user-guide/UM10204.pdf别再去下那些狗东西不知道从哪里弄来还要下载积分的烂玩意了!

本篇所有图片均摘自 IIC总线规范 。


IICBus

IIC一共只有两条线,双向串行数据线SDA、串行时钟线SCL。IIC是半双工的!

  • SDA:Serial Data(数据信号线)
  • SCL: Serial Colck Line(时钟信号线)

IIC应用电路

【硬件通讯协议】IIC总线协议以及模拟(软件)IIC_第1张图片

IIC 总线要求每个设备的SCL/SDA线都是漏极开路(OD)模式,因此总线必须带有上拉电阻才能正常工作。

上拉电阻的大小也会影响电平转换的时间,4.7K 就行。(是我在5V通讯下验证的值,应用需注意)

什么是漏极开路模式?

想想 51 单片机的 P0 口,就是很典型的漏极开路输出,因此要作为准双向口时,需要上拉电阻。

对于晶体管和场效应管来说:就是将 OUTPUT 端口接在漏极:

【硬件通讯协议】IIC总线协议以及模拟(软件)IIC_第2张图片

图中电阻即为上拉电阻,因 MOSFET 导通时,只有将 OUTPUT 拉低的能力而没有将 OUTPUT 抬升的能力,所以需要上拉电阻拉高 OUTPUT 的电平。

这种办法应用在驱动电路上是有很大好处的,驱动电流也不用很大就能控制大负载。

IIC 可在元件不同电压下进行通讯

各种电源电压共享同一IIC总线的例子

【硬件通讯协议】IIC总线协议以及模拟(软件)IIC_第3张图片

数据有效性

SDA 线上的数据要在时钟信号为高电平时保持稳定,在时钟信号为低电平时进行切换!

【硬件通讯协议】IIC总线协议以及模拟(软件)IIC_第4张图片

起始和停止

【硬件通讯协议】IIC总线协议以及模拟(软件)IIC_第5张图片

当 SCL(时钟信号线) 为高电平时,SDA 下降沿表示为起始信号,SDA 上升沿表示停止信号。

在这个起始和停止检测时,对硬件IIC很简单,软件IIC作为从机时,需要注意一下。

数据位

手册规定。SDA 上的数据必须为8位每字节。但是每次传输的字节数不受限制,每字节后要跟一个应答位(ACK)。数据先由最高位(MSB)传输。从机或主机暂时无法传输或接收完整数据时,可将SCL保持为低,进入等待状态。待时钟恢复时继续传输。

【硬件通讯协议】IIC总线协议以及模拟(软件)IIC_第6张图片

关于应答位:

每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据。发送完一个字节后,主机释放 SDA 的控制权,以便从机发送应答位。

  • ACK:SCL高电平时,SDA为低电平,表示数据被接收了。
  • NACK:(无应答)SCL高电平时,SDA为高电平

从机地址

开始后,发送7位地址,第8位为读/写标志位(0:W、1:R)。

【硬件通讯协议】IIC总线协议以及模拟(软件)IIC_第7张图片

开始后的第一个字节:

【硬件通讯协议】IIC总线协议以及模拟(软件)IIC_第8张图片

这样有几种情况,从机向主机发送信息,主机向从机发送信息,发送接收交替进行。

主机向从机写入的情况

【硬件通讯协议】IIC总线协议以及模拟(软件)IIC_第9张图片

主机读取从机情况

【硬件通讯协议】IIC总线协议以及模拟(软件)IIC_第10张图片

改变数据流方向

【硬件通讯协议】IIC总线协议以及模拟(软件)IIC_第11张图片

十位地址寻址方式

第一字节的前七位为 1111 0XX ,最后两位是十位地址的最高有效位(MSB)。第8位为 R/W 位。

有这种使用方法:

10 位寻址未广泛使用,不做过多介绍。

软件IIC时还应注意电平跳变不是瞬间完成的,需要一定的延时!!!


IIC总线接口的软件实现-89C51

为了方便理解,将软件实现分为几个部分:

sbit I2C_SDA = P1^0;
sbit I2C_SCL = P1^1;

延时函数

nop 代表运行一个时钟周期,此函数常常用在某些协议中的一个短延时!

void I2C_Delay(void)
{
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
}

IICBus发出起始信号

起始信号是在SCL高电平时SDA实现下降沿。

void I2C_Start(void)
{
    I2C_SDA = 1;
    I2C_SCL = 1;
    I2C_Delay();
    I2C_SDA = 0;
    I2C_Delay();
    I2C_SCL = 0;
}

IICBus发出结束信号

结束信号是在SCL高电平时SDA实现上升沿。

void I2C_Stop(void)
{
    I2C_SDA = 0;
    I2C_SCL = 1;
    I2C_Delay();
    I2C_SDA = 1;
    I2C_Delay();
    I2C_SCL = 0;
}

IICBus发出应答信号

SCL高电平时,SDA为低电平,表示数据被接收了。

void I2C_ASK(void)
{
    I2C_SDA = 0;
    I2C_SCL = 1;
    I2C_Delay();
    I2C_SDA = 1;
    I2C_SCL = 0;
}

IICBus发出非应答信号

void I2C_NOASK(void)
{
    I2C_SDA = 1;
    I2C_SCL = 1;
    I2C_Delay();
    I2C_SDA = 0;
    I2C_SCL = 0;
}

IICBus上发送一字节的数据

void I2C_SendByte(unsigned char c)
{
    unsigned char i;
    for(i=8; i>0; i--)
    {
        if(c & 0x80)
            I2C_SDA = 1;
        else
            I2C_SDA = 0;
        I2C_SCL = 1;
        I2C_Delay();
        I2C_SCL = 0;
        C = C<<1;
    }
    I2C_SDA = 1;    //释放 I2C_SDA 数据线,准备接收应答信号
    I2C_SCL = 1;
    I2C_Delay();
    while(!(0 == I2C_SDA && 1 == I2C_SCL));
    // 这里可加入超时执行其他函数的功能
}

把上述代码后四行改成检测主机应答位的情况

void I2C_ChickACK(void)
{
    I2C_SDA = 1;
    I2C_SCL = 1;
    F0 = 0;
    if(I2C_SDA == 1)
        F0 = 1;    //无应答
    I2C_SCL = 0;
}

IICBus上接收一字节的数据

unsigned char I2C_ReceiveByte(void)
{
    unsigned char i;
    unsigned char r;
    I2C_SDA = 1;
    for(i=8; i>0; i--)
    {
        r = r<<1;
        I2C_SCL = 1;
        I2C_Delay();
        if(I2C_SDA)
            r = r|0x01;
        I2C_SCL = 0;
    }
    return r;
}

实际例子

读 ADS1100 的 A/D 转换值。

#define READ_ADDR 0X90      // 1001 0000 // 读寄存器地址
#define WRITE_ADDR 0X91     // 1001 0001 // 写寄存器地址
#define CFG_WORD 0X8F       // 1000 1111 // 配置寄存器预设
unsigned char AD_H;         // 存储AD转换高八位
unsigned char AD_L;         // 存储AD转换低八位
bit SYS_ERR;                // 从机错误标志位

/* 配置寄存器进行设置
 * @ setting_data: 要配置的参数
 */
void ADS1100_INIT(unsigned char setting_data)
{
    I2C_Start();
    I2C_SendByte(WRITE_ADDR);
    I2C_CheckACK();
    if(FO == 1)
    {
        SYS_ERR = 1;
        return;
    }
    I2C_SendByte(setting_data);
    I2C_CheckACK();
    if(FO == 1)
    {
        SYS_ERR = 1;
        return;
    }
    I2C_Stop();
}

/* 用于读取A/D转换的结果
 */
void READ_ADS1100(void)
{
    I2C_Start();
    I2C_SendByte(READ_ADDR);
    I2C_CheckACK();
    {
        SYS_ERR = 1;
        return;
    }
    AD_H = I2C_ReceiveByte();
    I2C_ASK();
    AD_L = I2C_ReceiveByte();
    I2C_NOACK();
    I2C_Stop();
}


void main()
{
    ADS1100_INIT(CFG_WORD);
    READ_ADS1100();
}

现在基本上芯片都有硬件I2C接口了,此只是作为一个了解,另此部分代码为自己推理,位编译进行验证,仅供参考用。

今日学习达成!

【硬件通讯协议】IIC总线协议以及模拟(软件)IIC_第12张图片

 

你可能感兴趣的:(开发项目小知识点,软件协议,通讯协议,单片机,嵌入式硬件)