I2C通信

简介

STM32 内部集成了硬件 I2C 收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻 CPU 的负担
支持多主机模型
支持 7 /10 位地址模式
支持不同的通讯速度,标准速度 ( 高达 100 kHz) ,快速 ( 高达 400 kHz)
支持 DMA
兼容 SMBus 协议
STM32F103C8T6 硬件 I2C 资源: I2C1 I2C2

硬件电路 

所有 I2C 设备的 SCL 连在一起, SDA 连在一起
设备的 SCL SDA 均要配置成开漏输出模式
SCL SDA 各添加一个上拉电阻,阻值一般为 4.7KΩ 左右

 I2C通信_第1张图片I2C通信_第2张图片

SDA是半双工,主机和从机之间会切换输入和输出。

SCL主机设置开漏输出(强下拉/浮空),从机设置浮空输入。

 I2C时序基本单元

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

I2C通信_第3张图片

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

I2C通信_第4张图片

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

I2C通信_第5张图片

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

I2C通信_第6张图片

 I2C时序

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

I2C通信_第7张图片

代码部分

#include "stm32f10x.h"                  // 导入STM32F10x设备头文件
#include "Delay.h"                      // 导入延迟函数头文件

void MyI2C_W_SCL(uint8_t BitValue)
{
    GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);  // 写入SCL引脚的值
    Delay_us(10);  // 延迟10微秒
}

void MyI2C_W_SDA(uint8_t BitValue)
{
    GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);  // 写入SDA引脚的值
    Delay_us(10);  // 延迟10微秒
}

uint8_t MyI2C_R_SDA(void)
{
    uint8_t BitValue;
    BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);  // 读取SDA引脚的值
    Delay_us(10);  // 延迟10微秒
    return BitValue;  // 返回读取到的值
}

void MyI2C_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  // 使能GPIOB时钟

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  // 将GPIO模式设置为开漏输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;  // 设置SCL和SDA引脚
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // 设置GPIO速度为50MHz
    GPIO_Init(GPIOB, &GPIO_InitStructure);  // 初始化GPIOB

    GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);  // 设置SCL和SDA引脚的初始电平为高电平
}

void MyI2C_Start(void)
{
    MyI2C_W_SDA(1);  // 发送起始信号时,先将SDA引脚设置为高电平
    MyI2C_W_SCL(1);  // 然后将SCL引脚设置为高电平
    MyI2C_W_SDA(0);  // 接着将SDA引脚设置为低电平
    MyI2C_W_SCL(0);  // 最后将SCL引脚设置为低电平
}

void MyI2C_Stop(void)
{
    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) { Byte |= (0x80 >> i); }  // 逐位读取字节数据,从高位到低位
        MyI2C_W_SCL(0);  // 最后将SCL引脚设置为低电平
    }
    return Byte;  // 返回读取到的字节数据
}

void MyI2C_SendAck(uint8_t AckBit)
{
    MyI2C_W_SDA(AckBit);  // 发送应答位,根据AckBit的值设置SDA引脚的电平
    MyI2C_W_SCL(1);  // 将SCL引脚设置为高电平
    MyI2C_W_SCL(0);  // 然后将SCL引脚设置为低电平
}

uint8_t MyI2C_ReceiveAck(void)
{
    uint8_t AckBit;
    MyI2C_W_SDA(1);  // 读取应答位时,先将SDA引脚设置为高电平
    MyI2C_W_SCL(1);  // 然后将SCL引脚设置为高电平
    AckBit = MyI2C_R_SDA();  // 读取应答位的值
    MyI2C_W_SCL(0);  // 最后将SCL引脚设置为低电平
    return AckBit;  // 返回读取到的应答位的值
}

 

你可能感兴趣的:(STM32,单片机,嵌入式硬件)