I2C

发布于 2023-08-11  1092 次阅读


内容纲要

I2C笔记

[TOC]

I2C简介

•I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线

•两根通信线:SCL(Serial Clock)、SDA(Serial Data)

•同步,半双工

•带数据应答

•支持总线挂载多设备(一主多从、多主多从)

硬件电路

•所有I2C设备的SCL连在一起,SDA连在一起

•设备的SCL和SDA均要配置成开漏输出模式

•SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右

I2C时序基本单元

起始与终止条件

•起始条件:SCL高电平期间,SDA从高电平切换到低电平

•终止条件:SCL高电平期间,SDA从低电平切换到高电平

发送与接收字节

•发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节

•接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)

发送与接收应答

•发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答

•接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)

I2C时序

指定地址写

•对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)

当前地址读

•对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)

指定地址读

•对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)

I2C外设简介

•STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担

•支持多主机模型

•支持7位/10位地址模式

•支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)

•支持DMA

•兼容SMBus协议

•STM32F103C8T6 硬件I2C资源:I2C1、I2C2

I2C框图

简单了解即可


I2C基本结构

使用软件I2C的步骤

一、初始化I2C

void MyI2C_Init(void) // I2C初始化
{

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // 时钟初始化

    GPIO_InitTypeDef GPIO_InitStructure; // 初始化GPIO口
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 采用开漏输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&GPIO_InitStructure);

    GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); // 初始默认高电平

}

为方便之后代码编写,先对SCL读,SDA读写函数进行封装

void MyI2C_W_SCL(uint8_t BitValue) // 封装SCL写函数
{
    GPIO_WriteBit(GPIOB,GPIO_Pin_6,(BitAction)BitValue);
    Delay_us(10);
}

void MyI2C_W_SDA(uint8_t BitValue) // 封装SDA写函数
{
    GPIO_WriteBit(GPIOB,GPIO_Pin_7,(BitAction)BitValue);
    Delay_us(10);
}

uint8_t MyI2C_R_SDA(void) // 封装SDA读函数
{
    uint8_t Bitvalue;
    Bitvalue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7); // 读取电平
    Delay_us(10);
    return Bitvalue;
}

二、编写I2C起始/停止信号函数

void MyI2C_Start(void) // 产生I2C起始信号
{
    MyI2C_W_SDA(1); // 释放SDA
    MyI2C_W_SCL(1); // 释放SCL
    MyI2C_W_SDA(0); // 拉低SDA
    MyI2C_W_SCL(0); // 拉低SCL
}

void MyI2C_Stop(void) // 产生I2C停止信号
{
    MyI2C_W_SDA(0); // 拉低SDA
    MyI2C_W_SCL(1); // 释放SCL
    MyI2C_W_SDA(1); // 释放SDA
}

三、编写发送/接收字节函数

void MyI2C_SendByte(uint8_t Byte) // 发送字节函数
{
    uint8_t i;
    for( i = 0 ; i < 8 ; i++ )
    {   
        MyI2C_W_SDA(Byte & (0x80 >> i) ); // 发送当前最高位数据
        MyI2C_W_SCL(1); // 释放SCL
        MyI2C_W_SCL(0); // 拉低SCL
    }
}

uint8_t MyI2C_ReceiveByte(void) // 接收字节函数
{
    uint8_t i,Byte = 0x00;
    MyI2C_W_SDA(1); // 释放SDA
    for( i = 0 ; i < 8 ; i++ )
    {
        MyI2C_W_SCL(1); // 释放SCL
        if(MyI2C_R_SDA() == 1) // 如果读取SDA为1
        {
            Byte |= 0x80 >> i; // 写入数据给Byte(接收一个字节)
        }
        MyI2C_W_SCL(0); // 拉低SCL
    }
    return Byte; // 返回接收的Byte
}

四、编写发送/接收应答函数

void MyI2C_SendAck(uint8_t AckBit) // 发送应答(发送字节为8位,发送应答为1位,在此基础修改即可)
{
        MyI2C_W_SDA(AckBit);
        MyI2C_W_SCL(1);
        MyI2C_W_SCL(0);
}

uint8_t MyI2C_ReceiveAck(void) // 接收应答(发送字节为8位,发送应答为1位,在此基础修改即可)
{
    uint8_t AckBit;
    MyI2C_W_SDA(1);
    MyI2C_W_SCL(1);
    AckBit = MyI2C_R_SDA();
    MyI2C_W_SCL(0);
    return AckBit;
}

使用硬件I2C步骤

一、RCC开启时钟

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); // 开启I2C2时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 开启GPIO时钟

二、初始化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);

三、配置I2C

    I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; // 选择I2C
    I2C_InitStructure.I2C_ClockSpeed = 50000; // 配置SCL时钟频率(设置为50kHz)
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; // 低电平时间和高电平时间设置为2比1
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; // 应答位配置(设置为给应答)
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; // 响应七位地址
    I2C_InitStructure.I2C_OwnAddress1 = 0x00; // 指定STM32自身地址
    I2C_Init(I2C2, &I2C_InitStructure);

四、使能I2C

    I2C_Cmd(I2C2, ENABLE); // 使能I2C2

部分API

void I2C_DeInit(I2C_TypeDef* I2Cx);
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);
void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct); // 初始化结构体
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState); // 使能或失能I2C外设
void I2C_DMACmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_DMALastTransferCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState); // 生成起始条件
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState); // 生成终止条件
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState); // 配置收到一个字节后是否给从机应答
void I2C_OwnAddress2Config(I2C_TypeDef* I2Cx, uint8_t Address);
void I2C_DualAddressCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_GeneralCallCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_ITConfig(I2C_TypeDef* I2Cx, uint16_t I2C_IT, FunctionalState NewState);
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data); // 发送数据(写数据到数据寄存器DR)
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx); // 接收数据(读取DR)
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction); // 发送七位地址  
uint16_t I2C_ReadRegister(I2C_TypeDef* I2Cx, uint8_t I2C_Register);
void I2C_SoftwareResetCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_NACKPositionConfig(I2C_TypeDef* I2Cx, uint16_t I2C_NACKPosition);
void I2C_SMBusAlertConfig(I2C_TypeDef* I2Cx, uint16_t I2C_SMBusAlert);
void I2C_TransmitPEC(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_PECPositionConfig(I2C_TypeDef* I2Cx, uint16_t I2C_PECPosition);
void I2C_CalculatePEC(I2C_TypeDef* I2Cx, FunctionalState NewState);
uint8_t I2C_GetPEC(I2C_TypeDef* I2Cx);
void I2C_ARPCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_StretchClockCmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_FastModeDutyCycleConfig(I2C_TypeDef* I2Cx, uint16_t I2C_DutyCycle);
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);
uint32_t I2C_GetLastEvent(I2C_TypeDef* I2Cx);
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG); // 读取标志位
void I2C_ClearFlag(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG); // 清理标志位
ITStatus I2C_GetITStatus(I2C_TypeDef* I2Cx, uint32_t I2C_IT); // 读取中断标志位
void I2C_ClearITPendingBit(I2C_TypeDef* I2Cx, uint32_t I2C_IT); // 清理中断标志位

主机发送与接收

主机发送

主机接收

软件/硬件波形对比

世界のネズミは彼らが望むものに依存し、彼らは彼ら自身から誰も求めません
最后更新于 2023-08-27