IIC协议(以STM32为例,软件iic,第一部分时序讲解,第二部分代码实现)

iic有两条线,节省了硬件资源,是半双工通信,因为有时钟线所以是实时通信。

以为iic节省了硬件资源,所以在硬件设计上较为巧妙

因为端口设置为开漏模式,在开漏模式下是可以读输入电平的(如下图),是一种弱驱动,无法输出高电平,所以在硬件电路上连接了一个4.7k的上拉电阻,当cpu输出高电平时,因为是开漏模式,pmos关闭,此时高电平由外接上拉电阻提供,又因为是上拉电阻,对数据线或时钟线拉高不是很灵敏。

iic协议主机对时钟线有绝对的控制权,对数据线(sda)可以拉高(释放总线)交由从机控制。

iic起始条件

SCL高电平期间,SDA从高电平切换到低电平(主机先把数据线拉低,再把时钟线拉低。

iic终止条件

SCL高电平期间,SDA从低电平切换到高电平(主机先释放时钟线,再释放数据线

发送一个字节:

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

接收一个字节:

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

一句话总结因为主机从机都遵循IIC协议,所以对主机来说在时钟线低电平期间放数据,在时钟线高电平期间读数据

同理对于从机也是在低电平期间放数据,在高电平期间读数据。

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

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

指定地址写(以mpu6050为例)

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

以上图为例,s代表start 起始信号,在scl高电平期间拉低sda(先拉低sda,再低scl),

第一个字节: 一般为从机的 7位寻址地址(可从厂家手册查找)+一位读写位(写0,读1),共八位,然后等待应答   

第二个字节 :为从机寄存器地址, 等待应答,

第三个字节:要写入的数据

最后终止信号 : 在scl高电平期间拉高sda(先拉高scl,再拉高sda)

当前地址读

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

起始信号:在scl高电平期间拉低sda(先拉低sda,再低scl),

第一个字节: 一般为从机的 7位寻址地址(可从厂家手册查找)+一位读写位(写0,读1),共八位,然后等待应答   。(读最后寻址8位最后位 读写位,置1为读,同时拉高释放了sda,此时sda由从机拉低,从机控制sda)

第二个字节:要接收的数据,

等待应答,

最后停止条件在scl高电平期间拉高sda(先拉高scl,再拉高sda)

指定地址读

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

起始信号:在scl高电平期间拉低sda(先拉低sda,再低scl

第一个字节: 一般为从机的 7位寻址地址(可从厂家手册查找)+一位读写位(写0,读1),共八位,然后等待应答   

第二个字节 :为从机寄存器地址, 等待应答,

重复起始条件

第三个字节: 一般为从机的 7位寻址地址(可从厂家手册查找)+一位读写位(写0,读1),共八位,然后等待应答   。(读最后寻址8位最后位 读写位,置1为读,同时拉高释放了sda,此时sda由从机拉低,从机控制sda)

第四个字节:要接收的数据,等待应答

终止信号在scl高电平期间拉高sda(先拉高scl,再拉高sda)

代码实现

基于上面的时序编写软件代码(模拟i2c)

//初始化io口,并同时拉高(起始条件都是由高变低)
void myi2c_init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    GPIO_InitTypeDef gpio_initstruct;
    gpio_initstruct.GPIO_Mode=GPIO_Mode_Out_OD;
    gpio_initstruct.GPIO_Pin=GPIO_Pin_10|GPIO_Pin_11;
    gpio_initstruct.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&gpio_initstruct);
    GPIO_SetBits(GPIOB, GPIO_Pin_10|GPIO_Pin_11);
}

//定义scl 写sda 读 sda 开漏模式下是可以读取输入引脚电平
static void myi2c_w_scl(uint8_t value)
{
    GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)value);
}
static void myi2c_w_sda(uint8_t value)
{
    GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)value);
}
static uint8_t myi2c_r_sda(void)
{
    return GPIO_ReadInputDataBit(GPIOB,  GPIO_Pin_11);
}

//起始条件
void myi2c_start(void)
{
    myi2c_w_sda(1);
    myi2c_w_scl(1);
    myi2c_w_sda(0);
    myi2c_w_scl(0);
}
//终止条件
void myi2c_stop(void)
{
    myi2c_w_sda(0);
    myi2c_w_scl(1);
    myi2c_w_sda(1);
}
//发送一个字节,因为iic 协议高位先行,所以高位移向地位
void myi2c_sendbyte(uint8_t byte)
{
    for(int i=0;i<8;i++)
    {
        myi2c_w_sda(byte&(0x80>>i));
        myi2c_w_scl(1);
        myi2c_w_scl(0);
    }
}
//接受一个字节 高位先行
uint8_t myi2c_recvbyte(void)
{
    uint8_t byte=0x00;
    for(int i=0;i<8;i++)
    {
        myi2c_w_sda(1);
        myi2c_w_scl(1);
        if(myi2c_r_sda()){byte|=(0x80>>i);}
        myi2c_w_scl(0);
    }
    return byte;
}
//发送应答
void myi2c_sendack(uint8_t ack)
{
    myi2c_w_sda(ack);
    myi2c_w_scl(1);
    myi2c_w_scl(0);
}
//接收应答
uint8_t myi2c_recvack(void)
{
    uint8_t ack;
    myi2c_w_sda(1);
    myi2c_w_scl(1);
    ack=myi2c_r_sda();
    myi2c_w_scl(0);
    return ack;
}


起始条件为兼容指定地址读的重复起始 为保证在scl高电平期间sda拉低,所以先拉高sda。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值