IIC(I2C)通讯协议详解(7位寻址)

文章目录

  • 写在前面
    • 内容
  • 什么是IIC
    • SDA 、SCL线
    • IIC是多主机总线
    • IIC通讯流程
      • 数据有效性
      • 起始和停止符
      • 传输数据和响应
      • ★★ 数据格式(先就7位地址格式)
        • 主机- -发送器 发送到 从机- -接收器
        • 在第一个字节后,主机立刻读从机。
        • 复合模式。改变传输方向或从机。
  • 软件模拟IIC(stm32代码)
  • 总结不易,若有错误希望及时指出。若有收获,点个赞吧。

写在前面

IIC总线(在有些资料中也写作I2C总线),之前在很多模块中都有用到。初步接触的时候,看了其定义和逻辑,最后在使用上时却直接使用了现车的库函数。
最近在别人问上关于什么是IIC,具体的机制等等时,我却一时间没办法答上来,只能支支吾吾的将脑海生成的内容翻出来,没法让人满意和信服,故这里准备将关于IIC 和 SPI(之后的计划)通讯协议进行一个整理。方便初学者初步掌握以及我之后的学习回顾。
注:之后通称IIC。

在我的下一篇博客(DS3231时钟模块使用,IIC协议实践。(基于STM32))有关于IIC的实践项目,结合起来看更容易理解,相当详细易懂。

内容

后文有附在stm32下的软件模拟IIC代码,若需要可以直接查阅。

什么是IIC

IIC( Inter-Integrated Circuit (集成电路总线 ) ),是IICBus的简称。为一种串行通讯总线,采用多主从架构。

由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。I²C的正确读法为“I平方C”(“I-squared-C”),而“I二C”(“I-two-C”)则是另一种错误但被广泛使用的读法。自2006年10月1日起,使用I²C协议已经不需要支付专利费,但制造商仍然需要付费以获取I²C从属设备地址。

PS:我们一般读作“ I 方 C”。
注意上面的关键字,连接低速周边设备 。以及需要注意IIC为半双工通讯。

  1. 低速:IIC数据传输速率有标准模式(100kbps)、快速模式(400kbps)和高速模式(3.4Mbps)。
  2. 连接:物理上,IIC是两线制,一根时钟线SCL,一根数据线SDA。
  3. 周边设备:即多用与外围模块与MCU的数据通讯,比如常用的MPU6050、0.96寸左右大小OLED显示屏模块、DS3231时钟模块等,都可以通过IIC模块实现数据和指令半双工通讯。

SDA 、SCL线

IIC总线只需要这两根线,用于数据传输,SDA即数据线,SCL即时钟线,值得注意的是,两根总线需要用上拉电阻拉高,即默认为高电平,主机和从机只能通过IO将其拉低,来实现传输数据。即若下文中出现释放SDA线即SDA线因为上拉电阻变为高电平;占用SCL,即刻意拉低SCL。

如果从机要完成一些其他功能后,例如一个内部中断服务程序 ,能接收或发送下一个完整的数据字节,可以使时钟线 SCL 保持低电平迫使主机进入等待状态,当从机准备好接收下一个数据字节并释放时钟线 SCL 后 数据传输继续。

IIC(I2C)通讯协议详解(7位寻址)_第1张图片

IIC是多主机总线

IIC总线是一个多主机的总线。就是说可以连接多于一个能控制总线的器件到总线。
即是说,要实现一个控制器控制多个外围模块或设备,将控制器和支持IIC的模块接在同一条IIC总线上即可。这样也极大的节约了IO口资源。
IIC(I2C)通讯协议详解(7位寻址)_第2张图片

IIC通讯流程

了解IIC通讯协议具体,首先得知道如何将数据有效的传出,参考下面数据有效性。

数据有效性

SDA线上的数据必须在时钟线SCL高电平周期保持稳定。即在SCL低电平时候控制SDA线改变,在SCL高电平的时候保持SDA有效电平稳定。
IIC(I2C)通讯协议详解(7位寻址)_第3张图片

起始和停止符

IIC(I2C)通讯协议详解(7位寻址)_第4张图片
在SCL是高电平期间:

  • SDA从高向低电平切换,即一个下降沿,表示起始符、起始条件。
  • SDA从低向高电平切换,即一个上升沿,表示结束符、结束条件。

注意SCL和SDA皆为总线,故总线上的设备都可以检测到这个起始符。总线在起始条件后被认定为处于的状态。在坚持到结束条件后,系统再次被认为处于空闲状态。

传输数据和响应

发送到SDA线上的每个字节必须是8位,但每次传输的字节数量却是不受限制的。而每个字节之后需要跟一个响应位。,数据传输从最高位(MSB)开始传输。

数据传输必须带响应 相关的响应时钟脉冲由主机产生 在响应的时钟脉冲期间 发送器释放 SDA 线,即使SDA线为高。

在响应的时钟脉冲期间,接收器(从机)需将SDA线拉低,使它在这个时钟脉冲高电平期间保持稳定的低电平,即完成了响应。

★★ 数据格式(先就7位地址格式)

首先在起始条件(S)后,发送一个从机地址+数据方向位(R/W,0为W,1为R)。数据传输以主机产生的停止位(P)终止。
主机可以在不终止的情况下,寻找另一个从机,它可以产生重复的起始条件(Sr)和寻址另一个从机,不用先产生一个停止条件。
另外:

从机地址由一个固定和一个可编程的部分构成 。由于很可能在一个系统中有几个同样的器件,从机地址的可编程部分使最大数量的这些器件可以连接到 I2C 总线上。器件可编程地址位的数量由它可使用的管脚决定,例如 如果器件有 4 个固定的和 3 个可编程的地址位,那么相同的总线上共可以连接 8 (2^3)个相同的器件。

IIC(I2C)通讯协议详解(7位寻址)_第5张图片
传输情况有:

主机- -发送器 发送到 从机- -接收器

IIC(I2C)通讯协议详解(7位寻址)_第6张图片

在第一个字节后,主机立刻读从机。

在第一次响应后,主机- - 发送器 变为了主机 - -接收器,从机 - -接收器变为从机- -发送器。第一次响应仍由从机产生。之前发送了一个不响应信号 A 的主机产生停止条件。
IIC(I2C)通讯协议详解(7位寻址)_第7张图片

复合模式。改变传输方向或从机。

传输方向改变的时候,起始条件以及从机的地址都会重复,而R/W位被取反。

IIC(I2C)通讯协议详解(7位寻址)_第8张图片

软件模拟IIC(stm32代码)

一下在基于stm32标准库函数的软件模拟IO实现IIC代码,供参考。
宏定义:

//位带操作,实现51类似的GPIO控制功能
//IO口操作宏定义
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 


#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))  
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define  IIC_SDA	 PAout(10)
#define  IIC_SCL 	 PAout(11)



主要函数:

void IIC_Init(void)  //初始化
{
    GPIO_InitTypeDef GPIO_InitStructer;
    SDA_RCCPeriphClockcmd(SDASCL_GPIO_RCC, ENABLE);
    GPIO_InitStructer.GPIO_Pin=SCL_GPIO_Pin | SDA_GPIO_Pin; //10--SCL   11--SDA			//PB10 PB11
    GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP;

    GPIO_Init(SDA_GPIOx, &GPIO_InitStructer);
}
 void SDA_OUT(void) //设置主机SDA输出模式
{
    GPIO_InitTypeDef GPIO_InitStructer;
    GPIO_InitStructer.GPIO_Pin= SDA_GPIO_Pin;
    GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP;
    GPIO_Init(SDA_GPIOx, &GPIO_InitStructer);
}
 void SDA_IN(void) //设置主机SDA输入上拉模式
{
    GPIO_InitTypeDef GPIO_InitStructer;
    GPIO_InitStructer.GPIO_Pin= SDA_GPIO_Pin;
    GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_InitStructer.GPIO_Mode=GPIO_Mode_IPU; //输入上拉模式
    GPIO_Init(SDA_GPIOx, &GPIO_InitStructer);
}


 void IIC_Start(void)  //产生起始条件(符)
{
    SDA_OUT();
    IIC_SDA=1;   //先确保 SDA为高
    IIC_SCL=1;   //拉高SCL
    SysTick_Delay_us(4);
    IIC_SDA=0;	//产生下降沿
    SysTick_Delay_us(4);
    IIC_SCL=0; //将时钟线拉低,只有时钟线拉低才运行SDA数据变化有效
    SysTick_Delay_us(4);
}

 void IIC_Stop(void)	//产生结束条件(符)
{
    SDA_OUT();
    IIC_SCL = 0;
    IIC_SDA=0;	//先让SDA 为低电平
    IIC_SCL=1;	//将SCL拉高
    SysTick_Delay_us(4);	// 延时,保证时长
    IIC_SDA=1;	//SDA拉高 产生上升沿
    SysTick_Delay_us(4);  // 结束后释放SDA , SDA变高
}
// 下面两个 wait_Ask 函数,用来等待从机的ACK, 第一个不检测只等待,第二个检测且等待,没有就返回错误。
// uint8_t IIC_Wait_Ask(void)
//{
//    SDA_IN();
//    IIC_SCL=1;
//    SysTick_Delay_us(4);
//    IIC_SCL=0;
//    SysTick_Delay_us(4);
//    return 0;
//}

uint8_t IIC_Wait_Ask(void)  
{
    uint16_t tempTime = 0;
    SDA_IN();
    IIC_SDA = 1;   //释放数据总线,交由从机控制
    SysTick_Delay_us(4);
    IIC_SCL = 1;
    SysTick_Delay_us(1);
    while (GPIO_ReadInputDataBit(IIC_SDA_GPIOx,IIC_SDA_GPIO_Pin)) //读到 0 ,即接收到ACK,循环跳出
    {
        tempTime++;
        if(tempTime > 300)
        {
            IIC_Stop();
						printf("no ack\r\n");
            return 1;			//超时返回1
        }
    }
    IIC_SCL = 0;
    return 0;							//接收到 ACK 返回0
}

//主机产生应答信号ACK
void I2C_Ack(void)
{
    IIC_SCL = 0 ;
    SDA_OUT();
    IIC_SDA = 0;
    SysTick_Delay_us(2);
    IIC_SCL = 1;
    SysTick_Delay_us(5);
    IIC_SCL = 0;
}
//主机不产生应答信号NACK
void I2C_NAck(void)
{
    IIC_SCL = 0;
    SDA_OUT();
    IIC_SDA = 1;
    SysTick_Delay_us(2);
    IIC_SCL = 1;
    SysTick_Delay_us(5);
    IIC_SCL = 0;
}

 void IIC_WriteByte(u8 data)
{
    u8 i;
    SDA_OUT();
    for(i=0;i<8;i++)
    {
        IIC_SCL=0;
        SysTick_Delay_us(4);
        if(data & 0x80)
            IIC_SDA=1;
        else
            IIC_SDA=0;
        IIC_SCL=1;
        SysTick_Delay_us(4);
        IIC_SCL=0;
        data<<=1;

    }
}

总结不易,若有错误希望及时指出。若有收获,点个赞吧。

主要参考文献:IIC协议中文版.pdf (广州周立功单片机发展有限公司),下载也是从CSDN下载的。
代码部分主要参考正点原子的IIC代码,以及从其他博主那参考。

你可能感兴趣的:(学习笔记,嵌入式,物联网)