stm32学习总结:通信(1)IIC通信

一、 定义

IIC(Inter-Integrated Circuit,是IICBus的简称,中文:集成电路总线),它是一种符合IIC协议的串行通信总线。一般两根线,一根是双向的数据线SDA,另一根也是双向的时钟线SCL,它们都通过一个电流源或上拉电阻连接到正的电源电压。下图为IIC的硬件电路图。
在这里插入图片描述

二、 特点

  1. 通信双方分为主机与从机
    IIC总线两端分别为主机与从机,其中SCL信号一般由主机产生,作用是同步通信时间,保证信息传递的完整性与一致性,SDA信号为双向的,可由主机发送至从机,也可由从机发送至主机,但同一设备在某一时刻不可以既是发送者又是接收者,这一特性使得IIC总线实现一根数据线的串行半双工通信。
  2. 一组IIC总线可以挂载多个设备
    总线上可以有多个主机和从机,在数据传输时可通过冲突检测和仲裁防止数据被破坏。仲裁是指主机仲裁,也就是多个主机同时发起始信号,但是从机该听谁的,由主机仲裁机制决定,从机不参与,因为这时的从机不会去控制SDA或SCL。
  3. 总线上的设备须有一个ID
    总线上挂载的主机与从机均需要一个ID号,以便在通信时主机能找到与之通信的另一方,屏蔽其他设备。
  4. 通信时会有应答信号
    接收方会在接收到发送方传递的信息后,返回一个应答信号,发送方接收到后才会继续发消息。

三、 协议构成

IIC协议由空闲信号、起始位、再次起始位、从机地址(ID)、读写方向位、应答信号位(ACK)、片内地址、数据帧和停止位构成,下面分别介绍读和写的协议构成。

  1. 主机向从机写数据
    协议构成如图所示。
    在这里插入图片描述

空闲信号:空闲期间SDA不被占用为高电平;
起始位:1bit,SCL高电平期间,SDA从高电平切换到低电平,如图;
在这里插入图片描述

从机地址:7bits,从机的ID一般为7位,先写入最高位,SDA由主机控制;
读写方向位:1bit,该位为0时为主机向从机写入数据,SDA由主机控制;
应答信号位:1bit,该位为0时表示消息收到(ACK),为1表示无应答(NACK),SDA由从机控制;
片内地址:8bits,用来指定所操作外设的寄存器地址,此时SDA由主机控制,之后需要接收应答位;
数据帧:N Bytes,实际发送的数据,8bits为1个Byte,每发1个Byte数据,需要收1个应答位,发数据时SDA由主机控制,收应答位时SDA由从机控制;
停止位:1bit,SCL高电平期间,SDA从低电平切换到高电平,如图。
在这里插入图片描述

  1. 主机读取从机数据
    协议构成如图所示。
    在这里插入图片描述

    空闲信号:空闲期间SDA不被占用为高电平;
    起始位:1bit,SCL高电平期间,SDA从高电平切换到低电平,如图;
    在这里插入图片描述
    从机地址:7bits,从机的ID一般为7位,先写入最高位,SDA由主机控制;
    读写方向位:1bit,该位为0时为主机向从机写入数据,SDA由主机控制;
    应答信号位:1bit,该位为0时表示消息收到(ACK),为1表示无应答(NACK),SDA由从机控制;
    片内地址:8bits,用来指定所操作外设的寄存器地址,此时SDA由主机控制,之后需要接收应答位;
    再次起始位:1bit,此时SDA由主机控制,逻辑与起始位一致;
    再发从机地址和读写方向:8bits,主机再发一次从机ID,读写方向位改为1,即主机读取从机信息;
    数据帧:N Bytes,实际接收的数据,每发1个Byte数据,需要收1个应答位,发数据时SDA由从机控制,应答位时SDA由主机控制,最后1个Byte数据接收后主机需要发送无应答信号;
    停止位:1bit,SCL高电平期间,SDA从低电平切换到高电平,如图。
    在这里插入图片描述

四、 使用流程

STM32的寄存器提供了各种标志位,当产生某一事件时特定标志位会有变化,为我们使用该种通信方式提供了便利。

  1. 主机发送流程分析
    如下图所示为主机发送的流程,两行分别表示通信流程与产生事件,通信流程不再赘述,主要解释一下各个事件。
    在这里插入图片描述
    EV5:主机将SDA拉低后将产生主模式选择事件,检测到该事件后,可继续进行后续步骤;
    EV6:发送时将产生EV6事件,表示当前是主机发送模式;
    EV8:主机传输数据过程中产生EV8,此时SDA被占用;
    EV8_2:主机传输数据完成产生EV8_2,该事件作为停止位的前提使用。
  2. 主机接收流程分析
    如图所示为主机接收的流程,该流程由于不包含
    在这里插入图片描述
    EV5:主机将SDA拉低后将产生主模式选择事件,检测到该事件后,可继续进行后续步骤;
    EV6:发送时将产生EV6事件,表示当前是主机发送模式;
    EV8_2:主机传输数据完成产生EV8_2,该事件作为停止位的前提使用;
    EV7:主机接收消息完成后产生该事件,检测到后可发送停止位。

五、 代码分析

void MYIIC_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
    uint32_t Timeout;
    Timeout = 10000;                                    //给定超时计数时间
    while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)  //循环等待指定事件
    {
        Timeout --;                                     //等待时,计数值自减
        if (Timeout == 0)                               //自减到0后,等待超时
        {
            /*超时的错误处理代码,可以添加到此处*/
            break;                                      //跳出等待,不等了
        }
    }
}

void MYIIC_WriteReg(uint8_t RegAddress, uint8_t Data)
{
    I2C_GenerateSTART(I2C2, ENABLE);                                        //硬件I2C生成起始条件
    MYIIC_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);                  //等待EV5
    
    I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);  //硬件I2C发送从机地址,方向为发送
    MYIIC_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);    //等待EV6
    
    I2C_SendData(I2C2, RegAddress);                                         //硬件I2C发送寄存器地址
    MYIIC_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);            //等待EV8
    
    I2C_SendData(I2C2, Data);                                               //硬件I2C发送数据
    MYIIC_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);             //等待EV8_2
    
    I2C_GenerateSTOP(I2C2, ENABLE);                                         //硬件I2C生成终止条件
}

uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
    uint8_t Data;
    
    I2C_GenerateSTART(I2C2, ENABLE);                                        //硬件I2C生成起始条件
    MYIIC_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);                  //等待EV5
    
    I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);  //硬件I2C发送从机地址,方向为发送
    MYIIC_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);    //等待EV6
    
    I2C_SendData(I2C2, RegAddress);                                         //硬件I2C发送寄存器地址
    MYIIC_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);             //等待EV8_2
    
    I2C_GenerateSTART(I2C2, ENABLE);                                        //硬件I2C生成重复起始条件
    MYIIC_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);                  //等待EV5
    
    I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);     //硬件I2C发送从机地址,方向为接收
    MYIIC_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);       //等待EV6
    
    I2C_AcknowledgeConfig(I2C2, DISABLE);                                   //在接收最后一个字节之前提前将应答失能
    I2C_GenerateSTOP(I2C2, ENABLE);                                         //在接收最后一个字节之前提前申请停止条件
    
    MYIIC_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);                //等待EV7
    Data = I2C_ReceiveData(I2C2);                                           //接收数据寄存器
    
    I2C_AcknowledgeConfig(I2C2, ENABLE);                                    //将应答恢复为使能,为了不影响后续可能产生的读取多字节操作
    
    return Data;
}
void MYIIC_Init(void)
{
    /*开启时钟*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);        //开启I2C2的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);       //开启GPIOB的时钟
    
    /*GPIO初始化*/
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);                  //将PB10和PB11引脚初始化为复用开漏输出
    
    /*I2C初始化*/
    I2C_InitTypeDef I2C_InitStructure;                      //定义结构体变量
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;              //模式,选择为I2C模式
    I2C_InitStructure.I2C_ClockSpeed = 50000;               //时钟速度,选择为50KHz
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;      //时钟占空比,选择Tlow/Thigh = 2
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;             //应答,选择使能
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;   //应答地址,选择7位,从机模式下才有效
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;               //自身地址,从机模式下才有效
    I2C_Init(I2C2, &I2C_InitStructure);                     //将结构体变量交给I2C_Init,配置I2C2
    
    /*I2C使能*/
    I2C_Cmd(I2C2, ENABLE);                                  //使能I2C2,开始运行
}

六、 总结

IIC是单片机通信常用的同步串行半双工通信方式,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。本文从协议构成及使用流程方面结合代码整理了该模块的使用方法。

参考链接

  1. IIC协议详解-CSDN博客
  2. IIC原理超详细讲解—值得一看-CSDN博客
  3. IIC初了解【小白入门】-CSDN博客
  4. 万变不离其宗之I2C总线要点总结_write programmable part of-CSDN博客
  5. 【通信协议】IIC通信协议详解-CSDN博客
  6. IIC通讯协议讲解,及软件模拟IIC通讯-CSDN博客
  7. IIC总线协议详解与总结-CSDN博客
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值