基础自学-关于基于单片机的模拟I2C的学习

1.什么是协议
协议就是双方在进行通讯时,都遵循的一系列规则,举个例子,在使用手机的时候,只有你的指纹才能打开手机,其他人的不行,这就是手机和人之间的通讯协议,(有些不恰当)
2.I2C协议的简单理解:
1) 他的作用
将主设备和从设备联系起来进行数据通讯(前提是设备有I2C通讯的能力)
2) 目前遇到的通讯设备
MPU6050,MFRC522,单片机
3) 目前遇到的用处
单片机来模拟I2C协议和外界设备通讯,通过单片机和外设备通过I2C协议通讯,去读取或者修改外设备的寄存器的值,从而去控制外设备的运行状态,或者让来感知外界的某种物理量的结果。
3.I2C协议的接口:
1) SDA数据通讯接口
2) SCK时钟通讯接口
4.I2C协议的基础规则:
1) 通讯的起始信号
2) 通讯的关闭信号
3) 写入1Byte的值
4) 等待从设备应答(继续 or 终止)
5) 主动向从设备应答(继续 or 终止)
6) 读取1 Byte的值
5.规则的具体分析: 规则图 起始信号
1) 通讯的起始信号
1、 首先将SCK拉低,
2、 然后将SDA 拉高,(为了确保在SCK高电平期间,SDA有高到底的过程),
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),
4、 然后SDA拉低(延迟一下,为了确保能读取到这个跳变),
5、 然后将SCK拉低。
那么开启这过程就完成了
void Start_I2C(void)
{
    SDA_OUT();      

    SCK_State=0;          //1.拉低时钟

    Delay_1ms();

    SDA_State=1;          //2.拉高数据

    Delay_1ms();

    SCK_State=1;          //3.拉高时钟

    Delay_1ms();

    SDA_State=0;            //4.拉低数据            下降沿

    Delay_1ms();

    SCK_State=0;          //5.拉低时钟
}
/*------------------I2C-开始信号函数----------*/
关闭信号
2) 通讯的关闭信号
1、 首先将SCK拉低,
2、 然后将SDA 拉低(为了确保在SCK高电平期间,SDA有低到高的过程),
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),然后SDA拉低(延迟一下,为了确保能读取到这个跳变),
4、 然后将SCK拉低。
void Stop_I2C(void)
{
    SCK_State=0;    //1.拉低时钟

    SDA_OUT();

    Delay_1ms();

    SDA_State=0;    //2.拉低数据

  Delay_1ms(); 

    SCK_State=1;      //3.拉高时钟  

    Delay_1ms();

    SDA_State=1;    //4.拉高数据        上升沿

    Delay_1ms();

    SCK_State=0;    //5.拉低时钟
}
/*-------------------------------------------I2C-停止信号函数-------------------------------------------*/
写入1Byte的值
3.1) 主动向从设备应答(继续 )
1、 首先将SCK拉低,
2、 然后将SDA 拉低(为了确保在SCK高电平期间,SDA一直是低电平) ,
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),
4、 然后SCK拉低,
那么主动发送应答过程完成。
主动向从设备应答(终止)
3.2) 主动向从设备应答(终止 )
1、 首先将SCK拉低,
2、 然后将SDA 拉高(为了确保在SCK高电平期间,SDA一直是高电平) ,
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),
4、 然后SCK拉低,
那么主动发送应答过程完成。
void Do_Ack(bool select)  //1为终止,0为继续
{
    SCK_State=0;          //1.拉低时钟

  SDA_OUT();

    Delay_1ms();

    SDA_State=select;     //2.拉X数据

    Delay_1ms();

    SCK_State=1;          //3.拉高时钟

    Delay_1ms();

    SCK_State=0;          //4.拉低时钟
}
/*-------------------------------------------I2C-反馈-回应信号函数-------------------------------------------*/
主动发送1Byte的值
4) 主动发送1Byte的值
1、 首先将SCK拉低,
2、 然后将SDA 拉X(为了确保在SCK高电平期间,SDA一直是X电平) (X为数据电平),
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),
4、 1~3步骤执行8次
5、 然后SCK拉低,
那么主动发送BYTE过程完成。
void Write_Byte(u8 Date)
{
    u8 times=8;                                //数据的移位读取变量

    SDA_OUT();                                 //数据口设置为输出态

    Delay_1ms();


    while(times)                               //循环8次,1Byte
    {
         SCK_State=0;                            //1.拉低时钟

         Delay_1ms();

         SDA_State=(bool)(Date&(1<<(times-1)));  //2.送入数据

         Delay_1ms();

         SCK_State=1;                            //3.拉高时钟

         Delay_1ms();

        times--;

    }

    SCK_State=0;                              //4.拉低时钟

    Delay_1ms();

}
/*--------------------------------I2C-写-BYTE函数---------------------*/
等待从设备应答(继续 )
5.1) 等待从设备应答(继续 )
1、 首先将SCK拉低,
2、 然后将SDA 设置为上拉输入态,(为了确保在SCK高电平期间,SDA有高到底的过程),
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),
4、 然后通过询问的方式,在规定的检测时间内检测到SDA被拉低,
5、 然后将SCK拉低。
接受应答的过程(继续)就完成了
这里写图片描述
5.2) 等待从设备应答( 终止 )
1、 首先将SCK拉低(延迟一下,为了确保读SCK已经拉低)
2、 然后将SDA 设置为上拉输入态,(为了确保在SCK高电平期间,SDA有高到底的过程),
3、 然后将SCK拉高(延迟一下,为了确保读取之前被拉高),
4、 然后通过询问的方式,在规定的检测时间内SDA永远保持高电平
5、 然后将SCK拉低。
接受应答的过程(终止)就完成了
bool Wait_Ack(void)        //1为失败 0为成功
{
    int wait=100;

    SCK_State=0;                              //1.拉低时钟

    SDA_State=1;                              //2.拉高--设置为上拉输入,看网页收藏

    Delay_1ms();    

    SDA_IN();                                 //数据口设置为输入模式             

    Delay_1ms();    

    SCK_State=1;                              //3.拉高时钟

    while(SDA_Read)
    {
        wait--;     

        if(wait==0)                             //因为应答超时
        {           
            Stop_I2C();                           //关闭通讯

            SCK_State=0;                          //4.拉低时钟

            return 1;                 
        }
    }

    SCK_State=0;                              //4.拉低时钟

    return 0;                                 //答应成功
}
/*------------ ------I2C-等待-回应信号函数------- ---*/
读取1 Byte的值
6) 读取1 Byte的值
1、 首先将SCK拉低(延迟一下,为了确保读SCK已经拉低)
2、 然后将SDA 设置为输入模式(延迟一下,保证已经设为输入模式)
3、 首先将SCK拉低(延迟一下,为了确保读SCK已经拉低)
4、 将SDA设置为上拉模式(在输入状态下,调节他的ODR寄存器为1是是上拉),(延迟一下,保证已经设为上拉模式)
5、 先默认该位数据为0,所以DATA左移一位
6、 然后将SCK拉高(延迟一下,为了确保读SCK已经拉高,并且确保从设备已经将SDA总线电平修改结束,主设备能读到正确的数据)
7、 读取数据,然后若为1则数据+1,否则不变
8、 3~7步骤执行8次
9、 然后SCK拉低(延迟一下,为了确保读SCK已经拉低)
那么被动接受BYTE过程完成。
u8 Read_Byte(void)
{
    u8 Data=0,Read_time=8;                

    SCK_State=0;                    //1.拉低时钟

    Delay_1ms();

    SDA_IN();                       //2.数据口设置为输入(没上拉,在下面的循环里设置了)

    Delay_1ms();           


    while(Read_time)                //3.8次循环读数据
    {

    SCK_State=0;                  //3_1.拉低时钟

        Delay_1ms();

        SDA_State=1;                  //3_2.拉高数据(为了做到上拉输入)

        Delay_1ms();

        Data<<=1;                     //3_3.先默认送0

        SCK_State=1;                  //3_4.拉高时钟,准备读数

        Delay_1ms();

        if(SDA_Read)                  //3_5.读数,若为1,则数据+1
            Data++;

        Read_time--;

    }



    SCK_State=0;                    //4.拉低时钟

    Delay_1ms();                  

    return Data;                    //送出数据

}
/*------- -----I2C-读-BYTE函数----- -------*/

这里写图片描述
这里写图片描述

5.I2C协议的上层函数的调用: 这里写图片描述
1.1.主机发送寄存器访问信号
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、主机发送命令指令(一般直接是寄存器地址,具体看器件说明书)
4、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
5、若成功,也关闭通讯
bool send_one_Byte(u8 address,u8 data)    //返回值的含义  1为失败   0为成功 
{
    Start_I2C();                         //开启信号

    Write_Byte(address<<1|0);            //写入IIC地址

    if(Wait_Ack())                       //等待应答
    {
        Stop_I2C();

        return 1;                         //若没有,直接输出 1
    }

    Write_Byte(data);                   //写数据或者寄存器 

    if(Wait_Ack())                      //等待应答
    {
        Stop_I2C();

        return 1;                         //若没有,直接输出 1
    }

        Stop_I2C();

   return 0;                            //若有,直接输出 0
}
/*-------------------------------------------发送单个BYTE给从机 -------------------------------------------*/
这里写图片描述
1.2.主机向从机的某个寄存器写值
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、主机发送命令指令(一般直接是寄存器地址,具体看器件说明书)
4、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
5、若成功,主机发送要写的8bit数据
6、等待应答(有一个时间上的等待,若超时直接退出并且关闭通讯)
7、若成功,也关闭通讯
bool New_Write_DATA_to_Reg(u8 reg,u8 data)
{

    Start_I2C();

    Write_Byte(I2C_Address<<1|0);

    if(Wait_Ack())                       //等待应答
    {
        Stop_I2C();

        return 1;                         //若没有,直接输出 1
    }

    Write_Byte(reg);    

    if(Wait_Ack())                       //等待应答
    {
        Stop_I2C();

        return 1;                         //若没有,直接输出 1
    }   

    Write_Byte(data);   

    if(Wait_Ack())                       //等待应答
    {
        Stop_I2C();

        return 1;                         //若没有,直接输出 1
    }   

    Stop_I2C(); 

    return 0;
}


/**/
这里写图片描述
1.3.主机向从机的两个连续的寄存器写值()
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、主机发送命令指令(一般直接是寄存器地址,具体看器件说明书)
4、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
5、若成功,主机发送要写的8bit数据
4、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
5、若成功,主机的输送对象自动变为原来的寄存器地址+1,主机发送另外要写的8bit数据
6、等待应答(有一个时间上的等待,若超时直接退出并且关闭通讯)
7、若成功,也关闭通讯
这里写图片描述
2.1.主机接受上次寄存器访问的结果
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
4、若成功,主机接受上次主机发送访问后的数据(这个函数与1.1函数一般要连用)
5、主机主动发送一个应答(终止)信号
6、关闭通讯
u8 read_one_Byte(u8 address)       //默认的返回值为0xff ,一般0xff为失败,其他值为寄存器的数据,会有误差,因为0xff有可能是寄存器的值 //欠缺一个标志位ERROR 
{
    u8 result=0XFF;                  //1.默认的返回值为0xff

    Start_I2C();                     //2.开启信号

    Write_Byte(address<<1|1);        //3.写入IIC地址        

    if(!Wait_Ack())                  //4.等待应答
    {
        result=Read_Byte();          //若有应答,接受结果

        Do_Ack(1);                     //自动给出应答
    }

    Stop_I2C();                      //5.关闭信号

    return result;                   //6.返回结果

}

/*---------------读出单个BYTE从从机 -------------*/
这里写图片描述
2.2.主机向单个寄存器读取值
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
4、若成功,主机发送命令指令(一般直接是寄存器地址,具体看器件说明书)
5、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
6、若成功,主机发送起始信号
7、主机发送从设备的I2C地址7位(第八位用于读写控制)
8、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
9、若成功,主机接受该寄存器内的数据
10、主机主动发送一个应答(终止)信号
11、关闭通讯
u8  New_Read_DATA_in_Reg(u8 reg)
{
    static u8 DATA;

    Start_I2C();

    Write_Byte(I2C_Address<<1|0);

    if(Wait_Ack())                       //等待应答
    {
        Stop_I2C();

        return 0XFF;                         //若没有,直接输出 1
    }       

    Write_Byte(reg);

    if(Wait_Ack())                       //等待应答
    {
        Stop_I2C();

        return 1;                         //若没有,直接输出 1
    }

    Start_I2C();

    Write_Byte(I2C_Address<<1|1);

    if(Wait_Ack())                       //等待应答
    {
        Stop_I2C();

        return 0XFF;                         //若没有,直接输出 1
    }

    DATA=Read_Byte();

    Do_Ack(1);

    Stop_I2C(); 

    return DATA;    
}

/**/
这里写图片描述
2.3.主机向连续的2个寄存器读取值
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
4、若成功,主机发送命令指令(一般直接是寄存器地址,具体看器件说明书)
5、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
6、若成功,主机发送起始信号
7、主机发送从设备的I2C地址7位(第八位用于读写控制)
8、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
9、若成功,主机接受该寄存器内的数据
10、主机发送一个主动应答(继续)
11、若成功,主机接受寄存器+1后地址的数据
10、主机主动发送一个应答(终止)信号
11、关闭通讯
u16  New_Read_DATAS_in_Reg(u8 reg)
{
    static u16 DATA;

    Start_I2C();

    Write_Byte(I2C_Address<<1|0);

    if(Wait_Ack())                       //等待应答
    {
        Stop_I2C();

        return 0XFF;                         //若没有,直接输出 1
    }       

    Write_Byte(reg);

    if(Wait_Ack())                       //等待应答
    {
        Stop_I2C();

        return 0XFF;                         //若没有,直接输出 1
    }

    Start_I2C();

    Write_Byte(I2C_Address<<1|1);

    if(Wait_Ack())                       //等待应答
    {
        Stop_I2C();

        return 0XFF;                         //若没有,直接输出 1
    }

    DATA=(Read_Byte())<<8;

    Do_Ack(0);

    DATA=DATA|Read_Byte();

    Do_Ack(1);

    Stop_I2C(); 

    return DATA;    
}

/**/
这里写图片描述
2.4.主机向连续的多个寄存器读取值(部分器件可用)
1、主机发送起始信号
2、主机发送从设备的I2C地址7位(第八位用于读写控制)
3、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
4、若成功,主机发送命令指令(一般直接是寄存器地址,具体看器件说明书)
5、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
6、若成功,主机发送起始信号
7、主机发送从设备的I2C地址7位(第八位用于读写控制)
8、等待回应(有一个时间上的等待,若超时直接退出并且关闭通讯)
9、若成功,主机接受该寄存器内的数据
10、主机发送一个主动应答(继续)
11、9~10的步骤,循环N-1次
12、主机接受该第N个寄存器内的数据
13、主机发送一个主动应答(终止)
14、关闭通讯
u32 New_Read_DATASS_in_Reg(u8 reg)
{
    static u32 DATA;
    int i=1;

    Start_I2C();

    Write_Byte(I2C_Address<<1|0);

    if(Wait_Ack())                       //等待应答
    {
        Stop_I2C();

        return 0XFF;                         //若没有,直接输出 1
    }       

    Write_Byte(reg);

    if(Wait_Ack())                       //等待应答
    {
        Stop_I2C();

        return 0XFF;                         //若没有,直接输出 1
    }

    Start_I2C();

    Write_Byte(I2C_Address<<1|1);

    if(Wait_Ack())                       //等待应答
    {
        Stop_I2C();

        return 0XFF;                         //若没有,直接输出 1
    }


    while(1)
    {
        DATA=DATA|((Read_Byte())<<(32-8*i));

        i++;

        if(i==5) break;

      Do_Ack(0);

    }

    Do_Ack(1);

    Stop_I2C(); 

    return DATA;    


}
/**/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值