通用性I2C驱动接口的实现

目录

概述

1 认识I2C协议

1.1 初识I2C

1.2 I2C物理层

1.3 I2C协议分析

1.3.1 Start、Stop、ACK 信号

1.3.2 I2C协议的操作流程

1.3.3 操作I2C注意的问题

2 I2C驱动程序

2.1 驱动数据结构定义

2.2 编写驱动程序

2.2.1 start函数

 2.2.2 stop函数

2.2.3 写单个byte函数

2.2.4 读单个byte函数

2.2.5 等待应答信号(Wait ACK)

2.2.6  应答信号(ACK)

2.2.7  NO应答信号(NACK)

 2.2.8 监测设备在线

3 应用范例

3.1 初始化接口

3.2 应用程序范例

4 源代码

4.1 源代码:i2c_master.c

4.2 源代码:i2c_master.h


测试代码下载:

通用性I2C接口的应用之驱动SHT20(N32G45XVL-STB)资源-CSDN文库

概述

本文主要实现一个通用的I2C,其完全剥离了和硬件相关的接口,可实现多组硬件IO模拟多个I2C,彼此之间相关独立,但和i2c驱动相关的代码共用同一个架构体系。笔者还介绍I2C波形的相关内容,重点介绍i2c驱动的各个函数的功能,并做了详细的源码分析。

1 认识I2C协议

1.1 初识I2C

I2C 通讯协议(Inter-Integrated Circuit)是由 Philips 公司开发的一种简单、双向二线制同步串行总线, 只需要两根线即可在连接于总线上的器件之间传送信息。I2C 协议占用引脚特别少, 硬件实现简单, 可扩展型强, 现在被广泛地使用在系统内多个集成电路(IC)间的通讯。

1.2 I2C物理层

I2C 通讯设备之间的常用连接方式

物理层结构有如下特点:

1) 一条I2C总线上可以挂载多个设备,不同的设备地址必须不同

2)I2C总线由两条物理线路构成,分别为SCL和SDA,SCL为同步时钟线,SDA为数据线路

3)I2C可支持3中工作模式:标准模式(100k bit/ s),快速模式( 400k bit/ s),高速模式( 3.4M bit/ s)

1.3 I2C协议分析

完整的I2C工作时序图:

1.3.1 Start、Stop、ACK 信号

Start信号:

在空闲状态时,SDA为高电平,SCL也为高电平。当有数据需要传输时,Master首先发起start信号,SDA: 1-->0, SCL: 1

Stop信号:

数据传输完成后,SDA: 0-->1, SCL: 1

ACK信号:

在I2C协议中,数据传输的单位为byte, 传输完成一个数据时,需要8个bit, 在第9个bit( SCL电平: 0-->1)时,SDA : 0。该信号为ACK信号。

1.3.2 I2C协议的操作流程

需要注意的是I2C协议传输数据以字节为单位,每个字节有8个bit,传输完成一个字节后,还会发发送一个响应信号,即ACK信号,所以,其完成一个byte传输,实际需要9个bit。

Step-1:   Master 发起Start信号 , SDA: 1---> 0, SCL: 1

Step-2: 传输数据,当SCL: 0 ->1, SDA发送一个bit,总共8个bit

Step-3:    ACK信号,SCL: 0->1, SDA 1->0

Step-4:    传送下一个数据(循环执行: step-2 - > step-3)

Step-5:    Master 发起Stop信号,SDA: 0--->1, SCL: 1

1.3.3 操作I2C注意的问题

1)空闲状态时,SDA=1, SCL1 =1

2)  SCL 电平 0 ->1变化后,高电平保持期间,SDA上的数据才为有效bit

2 I2C驱动程序

2.1 驱动数据结构定义

代码第7行:读一个bit标志位

代码第8行:写一个bit标志位

代码第11行:初始化和I2C pin 相关的IO

代码第12行:i2c  SDA_PIN 置High函数

代码第13行:i2c  SDA_PIN 置Low函数

代码第14行:i2c  SCL_PIN 置High函数

代码第15行:i2c  SCL_PIN 置Low函数

代码第16行:读取i2c  SDA_PIN 状态

代码第17行:读取i2c  SCL_PIN 状态

2.2 编写驱动程序

2.2.1 start函数

起始信号变化特点:

在空闲状态时,SDA为高电平,SCL也为高电平。当有数据需要传输时,Master首先发起start信号,SDA: 1-->0, SCL: 1

 2.2.2 stop函数

stop信号变化特点:

数据传输完成后,SDA: 0-->1, SCL: 1

2.2.3 写单个byte函数

发送一个bit信号变化特点:

传输数据,当SCL: 0 ->1, SDA发送一个bit,总共8个bit

2.2.4 读单个byte函数

读一个bit信号变化特点:

step-1: SCL: 1 , SDA读取一个bit

step-2: SCL: 0 

step-3: 跳转到step-1,总共读取8个bit

2.2.5 等待应答信号(Wait ACK)

等待应答信号变化特点:

当mster向slave发送一个byte完成之后,master此时会等待slave发送一个应答信号,以确定slave接收到了数据。

2.2.6  应答信号(ACK)

应答信号变化特点:

发送该信号的目的是为了响应Wait ACK

ACK信号,SCL: 0->1, SDA 1->0

 

2.2.7  NO应答信号(NACK)

NO应答信号变化特点:

发送该信号的目的是为了告诉响应端,此时不需要等待ACK信号了,数据已经传输完成了。

ACK信号,SCL: 0->1, SDA 1,SCL: 1->0,

 2.2.8 监测设备在线

程序功能:

1) Master 发送地址数据给Slave

2) slave 如果能正常的接收到该数据后,会发送一个ACK

3)Master如果收到该ACK,说明设备挂载在I2C上

3 应用范例

3.1 初始化接口

1) i2c结构体定义

typedef struct _i2c {
    void     (*IIC_InitPort)();
    void     (*IIC_SDA_H)();
    void     (*IIC_SDA_L)();
    void     (*IIC_SCL_H)();
    void     (*IIC_SCL_L)();
    uint8_t  (*IIC_READ_SDA)();
    uint8_t  (*IIC_READ_SCL)();
}I2C;

使用时,需要把这些接口函数实现出来,然后填充到该结构体中。应用程序只需从该结构体中调用响应的接口函数

2)函数结构体的优点

将和i2c操作相关的接口,数据结构化,使其完全剥离和硬件的关系。这样只需简单的修改硬件接口就能实现新的驱动。便于实现多个I2C接口的功能。

I2C stru_OledDrv;
/******************************************************************************
* EXPORTED FUNCTIONS
******************************************************************************/

static void i2c_init_port()
{

}

static uint8_t i2c_SDA_READ()
{
    
    return  val;
}

static uint8_t i2c_SCL_READ()
{
    return  val;
}

static void set_i2c_scl_1( void )
{
    I2C_SCL_1();
}

static void set_i2c_scl_0( void )
{
    I2C_SCL_0();
}

static void set_i2c_sda_1( void )
{
    I2C_SDA_1();
}

static void set_i2c_sda_0( void )
{
    I2C_SDA_0();
}


void i2c_OledRegisterPort(void)
{
    stru_OledDrv.IIC_InitPort = i2c_init_port;
    stru_OledDrv.IIC_READ_SCL = i2c_SCL_READ;
    stru_OledDrv.IIC_READ_SCL = i2c_SDA_READ;
    
    stru_OledDrv.IIC_SCL_H = set_i2c_scl_1;
    stru_OledDrv.IIC_SCL_L = set_i2c_scl_0;
    
    stru_OledDrv.IIC_SDA_H = set_i2c_sda_1;
    stru_OledDrv.IIC_SDA_L = set_i2c_sda_0;
}

3.2 应用程序范例

实现一个写寄存器函数

step-1: 初始化i2c结构体

step-1:  调用2c结构体的函数,实现数据传输

4 源代码

4.1 源代码:i2c_master.c

/** \file

$Id: i2c_master.c 40486 2018-08-12 13:50:21Z tangmingfei2013@126.com $

Copyright (c)tangmingfei2013@126.com Holding B.V.
All Rights Reserved.

This source code and any compilation or derivative thereof is the proprietary
information of mingfei.tang Lighting Holding B.V. and is confidential in nature.
Under no circumstances is this software to be combined with any
Open Source Software in any way or placed under an Open Source License
of any type without the express written permission of mingfei.tang Holding B.V.
*/
#include "i2c_master.h"

static void i2c_Delay(void)
{
    uint8_t i;
    
    for (i = 0; i < 30; i++);
}

void i2c_master_Start( I2C *i2c )
{
    i2c->IIC_SDA_H();
    i2c->IIC_SCL_H();
    i2c_Delay();
    i2c->IIC_SDA_L();
    i2c_Delay();
    i2c->IIC_SCL_L();
    i2c_Delay();
}


void i2c_master_Stop( I2C *i2c )
{
    i2c->IIC_SDA_L();
    i2c->IIC_SCL_H();
    i2c_Delay();
    i2c->IIC_SDA_H();
}

void i2c_master_SendByte( I2C *i2c, uint8_t _ucByte)
{
    uint8_t i;

    /* 先发送字节的高位bit7 */
    for (i = 0; i < 8; i++)
    {
        if (_ucByte & 0x80)
        {
            i2c->IIC_SDA_H();
        }
        else
        {
            i2c->IIC_SDA_L();
        }
        i2c_Delay();
        i2c->IIC_SCL_H();
        i2c_Delay();
        i2c->IIC_SCL_L();
        
        if (i == 7)
        {
             i2c->IIC_SDA_H();  // 释放总线
        }
        _ucByte <<= 1;          // 左移一个bit 
        i2c_Delay();
    }
}

uint8_t i2c_master_ReadByte( I2C *i2c )
{
    uint8_t i;
    uint8_t value;

    // 读到第1个bit为数据的bit7 
    value = 0;
    for (i = 0; i < 8; i++)
    {
        value <<= 1;
        i2c->IIC_SCL_H();
        i2c_Delay();
                
        if ( i2c->IIC_READ_SDA() )
        {
            value++;
        }
                
        i2c->IIC_SCL_L();
        i2c_Delay();
    }
        
    return value;
}

uint8_t i2c_master_WaitAck( I2C *i2c )
{
    uint8_t re;

    i2c->IIC_SDA_H();          /* CPU释放SDA总线 */
    i2c_Delay();
    i2c->IIC_SCL_H();         /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
    i2c_Delay();
        
    if ( i2c->IIC_READ_SDA() )    /* CPU读取SDA口线状态 */
    {
        re = 1;
    }
    else
    {
        re = 0;
    }
    i2c->IIC_SCL_L();
    i2c_Delay();
        
    return re;
}

void i2c_master_Ack( I2C *i2c )
{
    i2c->IIC_SDA_L();    /* CPU驱动SDA = 0 */
    i2c_Delay();
    i2c->IIC_SCL_H();    /* CPU产生1个时钟 */
    i2c_Delay();
    i2c->IIC_SCL_L();
    i2c_Delay();
    i2c->IIC_SDA_H();    /* CPU释放SDA总线 */
}

void i2c_master_NAck( I2C *i2c )
{
    i2c->IIC_SDA_H();    /* CPU驱动SDA = 1 */
    i2c_Delay();
    i2c->IIC_SCL_H();    /* CPU产生1个时钟 */
    i2c_Delay();
    i2c->IIC_SCL_L();
    i2c_Delay();
}

uint8_t i2c_master_CheckDevice(I2C *i2c, uint8_t _Address)
{
    uint8_t ucAck;

    if ( i2c->IIC_READ_SDA() && i2c->IIC_READ_SCL())
    {
        i2c_master_Start( i2c );                           /* 发送启动信号 */

        /* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
        i2c_master_SendByte( i2c, _Address | I2C_MATER_R);
        ucAck = i2c_master_WaitAck( i2c );                 /* 检测设备的ACK应答 */

        i2c_master_Stop( i2c );                            /* 发送停止信号 */

        return ucAck;
    }
    
    return 1;                                              /* I2C总线异常 */
}


/* End of this file */


4.2 源代码:i2c_master.h

#ifndef __i2c_master_H
#define __i2c_master_H

#include "n32g45x.h"
#include <stdio.h>

#define I2C_MATER_R          0   /* read bit flag */
#define I2C_MATER_W          1   /* write bit flag */

typedef struct _i2c {
    void     (*IIC_InitPort)();
    void     (*IIC_SDA_H)();
    void     (*IIC_SDA_L)();
    void     (*IIC_SCL_H)();
    void     (*IIC_SCL_L)();
    uint8_t  (*IIC_READ_SDA)();
    uint8_t  (*IIC_READ_SCL)();
}I2C;

void i2c_master_Start( I2C *i2c );
void i2c_master_Stop( I2C *i2c );
void i2c_master_SendByte( I2C *i2c, uint8_t _ucByte);
uint8_t i2c_master_ReadByte( I2C *i2c );
uint8_t i2c_master_WaitAck( I2C *i2c );
void i2c_master_Ack( I2C *i2c );
void i2c_master_NAck( I2C *i2c );
uint8_t i2c_master_CheckDevice(I2C *i2c, uint8_t _Address);


#endif /*__i2c_master_H  */

  • 26
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值