您的位置 首页 元件

嵌入式Linux内核I2C子系统详解

嵌入式Linux内核I2C子系统详解-I2C总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。

1.1 I2C总线常识

1.1.1  I2C总线物理拓扑结构

 
    I2C总线在物理连接上十分简略,别离由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通讯原理是经过对SCL和SDA线凹凸电平时序的操控,来发生I2C总线协议所需求的信号进行数据的传递。在总线闲暇状况时,这两根线一般被上面所接的上拉电阻拉高,坚持着高电平。

1.1.2  I2C总线特征
    I2C总线上的每一个设备都能够作为主设备或许从设备,而且每一个设备都会对应一个仅有的地址(能够从I2C器材的数据手册得知),主从设备之间就经过这个地址来确认与哪个器材进行通讯,在一般的运用中,咱们把CPU带I2C总线接口的模块作为主设备,把挂接在总线上的其他设备都作为从设备。
    I2C总线上可挂接的设备数量受总线的最大电容400pF 约束,假如所挂接的是相同类型的器材,则还受器材地址位的约束。
    I2C总线数据传输速率在规范形式下可达100kbit/s,快速形式下可达400kbit/s,高速形式下可达3.4Mbit/s。一般经过I2C总线接口可编程时钟来完结传输速率的调整,一起也跟所接的上拉电阻的阻值有关。
    I2C总线上的主设备与从设备之间以字节(8位)为单位进行双向的数据传输。

1.1.3  I2C总线协议
    I2C协议规则,总线上数据的传输有必要以一个开端信号作为开端条件,以一个结束信号作为传输的中止条件。开端和结束信号总是由主设备发生。总线在闲暇状况时,SCL和SDA都坚持着高电平,当SCL为高电平而SDA由高到低的跳变,标明发生一个开端条件;当SCL为高而SDA由低到高的跳变,标明发生一个中止条件。在开端条件发生后,总线处于忙状况,由本次数据传输的主从设备独占,其他I2C器材无法拜访总线;而在中止条件发生后,本次数据传输的主从设备将开释总线,总线再次处于闲暇状况。如图所示:

在了解开端条件和中止条件后,咱们再来看看在这个进程中数据的传输是怎么进行的。前面咱们现已提到过,数据传输以字节为单位。主设备在SCL线上发生每个时钟脉冲的进程中将在SDA线上传输一个数据位,当一个字节按数据位从高位到低位的次序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应对位,此刻才认为一个字节真实的被传输完结。当然,并不是一切的字节传输都有必要有一个应对位,比方:当从设备不能再接纳主设备发送的数据时,从设备将回传一个否定应对位。数据传输的进程如图所示:

 
    在前面咱们还提到过,I2C总线上的每一个设备都对应一个仅有的地址,主从设备之间的数据传输是建立在地址的基础上,也便是说,主设备在传输有用数据之前要先指定从设备的地址,地址指定的进程和上面数据传输的进程相同,只不过大多数从设备的地址是7位的,然后协议规则再给地址增加一个最低位用来标明接下来数据传输的方向,0标明主设备向从设备写数据,1标明主设备向从设备读数据。如图所示:

 
1.1.4  I2C总线操作
    对I2C总线的操作实践便是主从设备之间的读写操作。大致可分为以下三种操作状况:
    榜首,主设备往从设备中写数据。数据传输格局如下:
   


    第二,主设备从从设备中读数据。数据传输格局如下:
   

第三,主设备往从设备中写数据,然后重启开端条件,紧接着从从设备中读取数据;或许是主设备从从设备中读数据,然后重启开端条件,紧接着主设备往从设备中写数据。数据传输格局如下:

第三种操作在单个主设备体系中,重复的敞开开端条件机制要比用STOP中止传输后又再次敞开总线更有用率。

1.2 I2C总线硬件接口电路示例

1.2.1 I2C总线硬件接口电路示例一

 
    这个电路是依据LPC2368 ARM7芯片进行规划的,运用其内部的I2C接口作为主设备,运用ADT75和SC16IS740作为两个从设备的I2C总线运用。

ADT75是一个带I2C接口的温度传感器器材,数据手册上对其地址的描绘如下:
    


    由此,其地址跟A0、A1、A2引脚的接法有关,咱们这儿的实例是将A0、A1、A2悉数接到高电平上,因而其地址是:1001111(即0x4F),又因依据协议再给地址增加一个最低位(方向位,默认给写方向),因而最终这个温度传感器作为从设备的地址是:10011110(即0x9E)。

SC16IS740是一个具有I2C或许SPI接口的扩展UART的器材(经过第8脚来决议运用I2C仍是SPI接口,咱们这儿要求运用I2C接口,因而将第8脚接到高电平)。依据数据手册,咱们相同的能够知道地址跟A0、A1的接法有关,咱们这儿的A0接高电平,A1接低电平。因而这个器材作为从设备的地址是:10010010(即0x92)。

1.2.2 I2C总线硬件接口电路示例二

 
    这个电路是Mini2440开发板上I2C总线接口的运用。咱们能够看到,SDA和SCL线上接了一个10K的上拉排阻。AT24C08是一个容量为8Kbit的EEPROM存储器材(留意是8Kbit,也便是1KB) ,依据数据手册中器材地址部分的描绘,AT24C08的地址是:1010+A2A1A0+方向位,其间1010是EEPROM的类型辨认符;仅仅运用A2来确认总线拜访本器材的从设备地址,这儿接的低电平,所认为0;A1和A0是器材内部页地址,在对器材擦除或许编程时运用,尽管这儿也接的低电平,但器材内部并不运用引脚的输入值,也便是说A1和A0的值是由软件进行设定的。

1.3 脱离操作体系的I2C总线驱动示例(以电路示例一为例)

1.3.1 LPC2368中I2C接口寄存器描绘
    LPC2368中有三个I2C总线接口,别离标明为I2C0、I2C1和I2C2,每个I2C接口都包括7个寄存器。它们别离是:
I2C操控置位寄存器(I2CONSET):8位寄存器,各位不同的设置是对I2C总线不同的操控。

符号

描绘

复位值

1:0

保存,用户软件不要向其写入1。从保存位读出的值未被界说

NA

2

AA

声明应对标志。为1时将为需求应对的状况发生一个应对

0

3

SI

I2C中止标志。当I2C状况改动时该方位位

0

4

STO

总线中止条件操控。1宣布一个中止条件,当总线检测到中止条件时,STO主动清零

0

5

STA

总线开端条件操控。1进入主形式并宣布一个开端条件

0

6

I2EN

总线使能操控。1为使能

0

7

保存,用户软件不要向其写入1。从保存位读出的值未被界说

NA


I2C操控清零寄存器(I2CONCLR):8位寄存器,对I2CONSET寄存器中的相应为清零。

符号

描绘

复位值

1:0

保存,用户软件不要向其写入1。从保存位读出的值未被界说

NA

2

AAC

声明应对标志清零位。向该位写入1清零I2CONSET寄存器中的AA位

0

3

SIC

中止标志清零位。向该位写入1清零I2CONSET寄存器中的SI位

0

4

保存,用户软件不要向其写入1。从保存位读出的值未被界说

NA

5

STAC

开端条件清零位。向该位写入1清零I2CONSET寄存器中的STA位

0

6

I2ENC

总线禁能操控。写入1清零I2CONSET寄存器中的I2EN位

0

7

保存,用户软件不要向其写入1。从保存位读出的值未被界说

NA


I2C状况寄存器(I2STAT):8位只读寄存器,用于监控总线的实时状况(或许存在26种状况)。

符号

描绘

复位值

2:0

这3个位不运用且总是为0

0

7:3

Status

这些位给出I2C接口的实时状况,不同的值代表不同的状况,状况码请参阅数据手册

0x1F


I2C数据寄存器(I2DAT):8位寄存器,在SI置位期间,I2DAT中的数据坚持稳定。

符号

描绘

复位值

7:0

Data

该寄存器保存现已接纳到或许预备要发送的数据值

0


I2C从地址寄存器(I2ADR):8位寄存器,I2C总线为从形式时才运用。主形式中该寄存器无效。

符号

描绘

复位值

0

GC

通用调用使能位

0

7:1

Address

从形式的I2C器材地址

0x00


SCH占空比寄存器(I2SCLH):16位寄存器,用于界说SCL高电平所坚持的PCLK周期数。

符号

描绘

复位值

15:0

SCLH

SCL高电平周期挑选计数

0x0004


SCL占空比寄存器(I2SCLL):16位寄存器,用于界说SCL低电平所坚持的PCLK周期数。

符号

描绘

复位值

15:0

SCLL

SCL低电平周期挑选计数

0x0004

在前面的I2C总线特征中咱们提到过,I2C总线的速率经过可编程时钟来调整,即有必要经过软件对I2SCLH和I2SCLL寄存器进行设置来挑选适宜的数据频率和占空比。 频率由下面的公式得出(fPCLK是PCLK的频率)。
 

1.3.2 LPC2368中I2C总线操作
    在1.1.4中咱们现已讲过了对I2C总线的操作,但那仅仅从协议和时序上的描绘,那咱们怎么从软件上去表现出来呢?接下来咱们就评论这个问题。
    对I2C总线上主从设备的读写可运用两种办法,一是运用轮询的办法,二是运用中止的办法。轮询办法便是在一个循环中判别I2C状况寄存器当时的状况值来确认总线当时所在的状况,然后依据这个状况来进行下一步的操作。中止办法便是使能I2C中止,注册I2C中止服务程序,在服务程序中读取I2C状况寄存器的当时状况值,再依据状况值来确认下一步的操作。
    不论运用哪种办法,看来I2C状况寄存器的值是至关重要的。这些状况值代表什么意思呢?下面咱们描绘一些常用的状况值(具体的状况值意义请参阅数据手册)。


0x08: 标明主设备向总线已宣布了一个开端条件;
0x10: 标明主设备向总线已宣布了一个重复的开端条件;
0x18: 标明主设备向总线已发送了一个从设备地址(写方向)而且接纳到从设备的应对;
0x20: 标明主设备向总线已发送了一个从设备地址(写方向)而且接纳到从设备的非应对;
0x28: 标明主设备向总线已发送了一个数据字节而且接纳到从设备的应对;
0x30: 标明主设备向总线已发送了一个数据字节而且接纳到从设备的非应对;
0x40: 标明主设备向总线已发送了一个从设备地址(读方向)而且接纳到从设备的应对;
0x48: 标明主设备向总线已发送了一个从设备地址(读方向)而且接纳到从设备的非应对;
0x50: 标明主设备从总线上已接纳一个数据字节而且回来了应对;
0x58: 标明主设备从总线上已接纳一个数据字节而且回来了非应对;

1.3.3 示例代码
一、 轮询办法读写总线:

关于代码中从设备内部寄存器的操作请参阅该设备的数据手册。例如,要读取温度传感器的温度值只需求调用:I2C0_ReadRegister(CHANNEL_TEMPERATURE, ADT75A_TEMP, &value),假如读取成功,则value中的数据便是经过I2C总线读取温度传感器中的温度数据。

二、 中止办法读写总线:
    这儿的从设备地址界说、I2C操控寄存器宏界说和I2C初始化与上面轮询中的相似,仅仅要在初始化函数中加上中止请求的代码,中止服务程序名称为:I2C0_Exception。这儿不再贴出以上代码了,这儿只贴出关键性的代码。

/*界说I2C状况标志*/
typedef enum
{
    I2C_IDLE = 0,
    I2C_STARTED = 1,
    I2C_RESTARTED = 2,
    I2C_REPEATED_START = 3,
    I2C_DATA_ACK = 4,
    I2C_DATA_NACK = 5
} I2C_STATUS_FLAG;

/*界说I2C数据传输缓冲区巨细和传输超时巨细*/
#define I2C_BUFSIZE 0x200
#define I2C_TIMEOUT 0x00FFFFFF

/*界说I2C当时状况标志*/
volaTIle I2C_STATUS_FLAG I2C_Flag;

/*I2C当时的形式,0为主发送器形式,1为主接纳器形式*/
volaTIle uint32 I2CMasterMode = 0;

/*别离界说I2C接纳和发送缓冲区、要发送或要接纳的字节数、实践发送或接纳的字节数*/
volaTIle uint8 I2CReadBuf[I2C_BUFSIZE], I2CWriteBuf[I2C_BUFSIZE];
volatile uint32 I2CReadLength, I2CWriteLength;
volatile uint32 I2C_RD_Index, I2C_WR_Index;

/****************************************************************************
** Function name: I2C0_Exception
** Descriptions : I2C0中止服务程序
** Input : 无
** Output : 无
** Created Date : 2011-03-24
*****************************************************************************/
void I2C0_Exception(void)
{
    volatile uint32 stat_value;
    stat_value = I20STAT;

    switch(stat_value)
    {
        case 0x08:
            /*宣布了一个开端条件,接下来将发送从地址然后清零SI位和STA位*/
            I2C_Flag = I2C_STARTED;
            I20DAT = I2CWriteBuf[I2C_WR_Index];
            I2C_WR_Index++;
            I20CONCLR = I2C_STA | I2C_SI;
            break;
        case 0x10:
            /*一个重复的开端条件发送完结,接下来要将发送从地址然后清零SI位和STA位*/
            I2C_Flag = I2C_RESTARTED;
            if(I2CMasterMode == 1)
            {
                /*留意I2CWriteBuf中的第0位是设备从地址和写方向位,因这儿是读操作,故将第0位的方向位变为读*/
                I20DAT = I2CWriteBuf[0] | 0x01;
            }
            I20CONCLR = I2C_STA | I2C_SI;
            break;
        case 0x18 /*(注:SLA+W标明从设备地址+写方向)*/
            /*发送SLA+W后已接纳到ACK,接下来开端发送数据字节到数据寄存器然后清零SI位*/
            if(I2C_Flag == I2C_STARTED)
            {
                I2C_Flag = I2C_DATA_ACK;
                I20DAT = I2CWriteBuf[I2C_WR_Index];
                I2C_WR_Index++;
            }
            I20CONCLR = I2C_SI;
            break;
        case 0x28:
            /*此状况标明已发送I2DAT中的字节且接纳到ACK,接下来持续发送下一个字节*/
        case 0x30:
            /*已发送I2DAT中的字节且接纳到非ACK,接下来或许宣布中止条件或重启开端条件*/
            if(I2C_WR_Index != I2CWriteLength)
            {
                /*实践发送的字节数与要发送的不相等则持续发送,但或许是最终一次*/
                I20DAT = I2CWriteBuf[I2C_WR_Index];
                I2C_WR_Index++;

                if(I2C_WR_Index != I2CWriteLength)
                {
                    I2C_Flag = I2C_DATA_ACK;
                }
                else
                {
                    /*假如实践发送与要发送的相等了,标明主发送端数据发送完结*/
                    I2C_Flag = I2C_DATA_NACK;

                    if(I2CReadLength != 0)
                    {
                        /*假如主发送端有等候接纳的字节,则切换为主接纳形式,重启开端条件*/
                        I2C_Flag = I2C_REPEATED_START;
                        I20CONSET = I2C_STA | I2C_SI;
                    }
                }
            }
            else
            {
                /*假如实践发送与要发送的相等了,标明主发送端数据发送完结*/
                I2C_Flag = I2C_DATA_NACK;

                if(I2CReadLength != 0)
                {
                    /*假如主发送端有等候接纳的字节,则标明需切换为主接纳形式,重启开端条件*/
                    I2C_Flag = I2C_REPEATED_START;
                    I20CONSET = I2C_STA;
                }
            }
            I20CONCLR = I2C_SI;
            break;
        case 0x40:
            /*此状况标明已发送SLA+R后已接纳到ACK*/
            I20CONCLR = I2C_SI;
            break;
        case 0x50:
            /*此状况标明已接纳数据字节后已接纳到ACK*/
        case 0x58:
            /*此状况标明已接纳数据字节后已接纳到非ACK*/
            I2CReadBuf[I2C_RD_Index] = I20DAT;
            I2C_RD_Index++;

            if(I2C_RD_Index != I2CReadLength)
            {
                /*假如实践接纳的字节与要接纳的不相等,则持续接纳*/
                I2C_Flag = I2C_DATA_ACK;
            }
            else
            {
                /*不然接纳结束*/
                I2C_RD_Index = 0;
                I2C_Flag = I2C_DATA_NACK;
            }
            I20CONCLR = I2C_AA | I2C_SI;
            break;
        case 0x20:
            /*此状况标明已发送SLA+W后已接纳到非ACK*/
        case 0x48:
            /*此状况标明已发送SLA+R后已接纳到非ACK*/
            I2C_Flag = I2C_DATA_NACK;
            I20CONCLR = I2C_SI;
            break;
        default:
            I20CONCLR = I2C_SI;
            break;
    }

    VICVectAddr = 0x00;
}

/****************************************************************************
** Function name: I2C0_Start
** Descriptions : 设置I2C0总线传输开端条件
** Input : 无
** Output : 回来TRUE/FALSE, FALSE为设置超时
** Created Date : 2011-03-24
*****************************************************************************/
BOOL I2C0_Start(void)
{
    uint32 timeout = 0;
    BOOL retVal = FALSE;

    /*设置装备寄存器STA位开端条件*/
    I20CONSET = I2C_STA | I2C_SI;
    I20CONCLR = I2C_SI;

    /*等候开端条件完结*/
    while(1)
    {
        if(I2C_Flag == I2C_STARTED)
        {
            retVal = TRUE;
            break;
        }

        if(timeout >= I2C_TIMEOUT)
        {
            retVal = FALSE;
            break;
        }

        timeout++;
    }

    return retVal;
}

/****************************************************************************
** Function name: I2C0_Stop
** Descriptions : 设置I2C0总线传输中止条件
** Input : 无
** Output : 回来TRUE
** Created Date : 2011-03-24
*****************************************************************************/
BOOL I2C0_Stop(void)
{
    /*设置装备寄存器STO位中止条件和铲除SI标志*/
    I20CONSET = I2C_STO;
    I20CONCLR = I2C_SI;

    /*等候中止条件完结*/
    while(I20CONSET & I2C_STO);

    return TRUE;
}

/****************************************************************************
** Function name: I2C0_Engine
** Descriptions : 完结I2C0总线从开端到中止的传输,传输进程在中止服务程序中进行
** Input : 无
** Output : 回来TRUE/FALSE
** Created Date: 2011-03-24
*****************************************************************************/
BOOL I2C0_Engine(void)
{
    I2C_Flag = I2C_IDLE;
    I2C_RD_Index = 0;
    I2C_WR_Index = 0;

    if(I2C0_Start() != TRUE)
    {
        I2C0_Stop();

        return FALSE;
    }

    while(1)
    {
        if(I2C_Flag == I2C_DATA_NACK)
        {
            I2C0_Stop();
            break;
        }
    }

    return TRUE;
}


    从上面代码中看,假如要运用I2C总线发动一次数据传输只需求先初始化好发送或接纳缓冲区,然后调用I2C0_Engine()函数即可。如下代码所示:

/****************************************************************************
** Function name: I2C0_ReadWriteTransmission
** Descriptions : I2C总线数据读写传输
** Input : read_buf-读数据缓冲区
                  read_len-读数据长度
                  write_buf-写数据缓冲区
                  write_len-写数据长度
** Output : 数据读写传输是否成功
** Created Date : 2011-03-24
*****************************************************************************/
BOOL I2C0_ReadWriteTransmission(uint8 **read_buf, uint32 read_len, uint8 *write_buf, uint32 write_len)
{
    uint32 i;
    BOOL result = FALSE;

    /*数据传输长度查看*/
    if(read_len > I2C_BUFSIZE || write_len > I2C_BUFSIZE)
    {
        return FALSE;
    }

    /*清空I2C接纳和发送缓冲区内容*/
    for(i = 0; i < I2C_BUFSIZE; i++)
    {
        I2CReadBuf[i] = 0;
        I2CWriteBuf[i] = 0;
    }

    /*确认I2C总线形式(0为主发送形式,1为主接纳形式)*/
    I2CMasterMode = (read_len == 0)? 0 : 1;
    I2CReadLength = read_len;
    I2CWriteLength = write_len;

    /*要写入I2C从设备的数据(榜首个字节包括从设备地址和方向位)*/
    for(i = 0; i < write_len; i++)
    {
        I2CWriteBuf[i] = write_buf[i];
    }

    /*发动I2C传输*/
    result = I2C0_Engine();

    /*假如有向从设备读取的数据*/
    if(read_len > 0 && result == TRUE)
    {
        uint8 *buf = (uint8 *)malloc(read_len * sizeof(uint8));

        for(i = 0; i < read_len; i++)
        {
            uf[i] = I2CReadBuf[i];
        }

        *read_buf = buf;
    }

    return result;
}

1.4 Linux下I2C子体系结构
    在Linux下要运用I2C总线并没有像无体系中的那样简略,为了表现Linux中的模块架构,Linux把I2C总线的运用进行了结构化。这种结构分三部分组成,他们别离是:I2C中心部分、I2C总线驱动部分和I2C设备驱动。结构图如下:
         

 
    由此看来,在Linux下驱动I2C总线不像单片机中那样简略的操作几个寄存器了,而是把I2C总线结构化、笼统化了,契合通用性和Linux设备模型。

/*I2C从设备地址*/
#define SC16IS740_ADDR 0x92 /*I2C转UART设备*/
#define ADT75A_ADDR 0x9E /*温度传感器设备*/
#define ADT75A_TEMP 0x00 /*温度传感器内部寄存器*/

/*从设备挑选标识*/
#define CHANNEL_GPRS 0
#define CHANNEL_TEMPERATURE 1

/*界说I2C操控寄存器各位操作宏*/
#define BIT(x) (1 << x)
#define I2C_EN BIT(6)
#define I2C_STA BIT(5)
#define I2C_STO BIT(4)
#define I2C_SI BIT(3)
#define I2C_AA BIT(2)

/*用作超时计数*/
#define SAFETY_COUNTER_LIMIT 3000

/******************************************************************
** Function name: I2C0_Init
** Descriptions : I2C0初始化
** Input : 无
** Output : 无
** Created Date : 2011-03-24
*******************************************************************/
void I2C0_Init(void)
{
    /*设置P0.0,P0.1为I2C0接口的SDA和SCL功用*/
    PINSEL0 |= (0x03 << 0) | (0x03 << 2);     /*设置I2C0接口功率/时钟操控位*/
    PCONP |= (0x01 << 7 );     /*清空I2C0装备寄存器的各位*/
    I20CONCLR = (0x01 << 2) | (0x01 << 3) | (0x01 << 5) | (0x01 << 6);     /*使能I2C0为主发送器形式*/
    I20CONSET = (0x01 << 6);     /*设置I2C0总线速率为100 KHz */
    I20SCLH = 0x5A;
    I20SCLL = 0x5A;
}

/****************************************************************************
** Function name: I2C0_ReadRegister
** Descriptions : 从I2C0总线上读从设备的数据
** Input : 从设备挑选标识、从设备内部寄存器地址、读出的字节数据
** Output : 读取是否成功
** Created Date : 2011-03-28
*****************************************************************************/
BOOL I2C0_ReadRegister(uint32 channel, uint8 registerAddress, uint8 *pData)
{
    /*用作延时等候计数*/
    uint32 loopSafetyCounter = 0;
    uint32 addressSendSafetyCounter = 0;

    /*运用循环判别I2C状况寄存器I20STAT 的值*/
    do
    {
        /*向总线发送I2C开端条件*/
        I20CONSET = I2C_STA | I2C_SI;
        I20CONCLR = I2C_SI;

        /*等候开端条件发送完结*/
        loopSafetyCounter = 0;
        while (~I20CONSET & I2C_SI)
        {
            loopSafetyCounter ++;
            if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
            {
                return FALSE; /*超时退出*/
            }
        }

        /*发送从设备地址*/
        if(channel == CHANNEL_GPRS)
            I20DAT = SC16IS740_ADDR;
        else if(channel == CHANNEL_TEMPERATURE)
            I20DAT = ADT75A_ADDR;

        I20CONCLR = I2C_STA | I2C_SI;

        /*等候从设备地址发送完结*/
        loopSafetyCounter = 0;
        while (~I20CONSET & I2C_SI)
        {
            loopSafetyCounter ++;
            if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
            {
                return FALSE; /*超时退出*/
            }
        }

        addressSendSafetyCounter ++;
        if (addressSendSafetyCounter > SAFETY_COUNTER_LIMIT)
        {
            return FALSE; /*超时退出*/
        }

    } while (I20STAT != 0x18); /*在前面现已描绘了0x18的意义*/

    /*发送从设备内部寄存器地址,依据数据手册描绘该内部地址要左移3位*/
    I20DAT = registerAddress << 3;
    I20CONCLR = I2C_SI;

    /*等候从设备内部寄存器地址发送完结*/
    loopSafetyCounter = 0;
    while (~I20CONSET & I2C_SI)
    {
        loopSafetyCounter ++;
        if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
        {
            return FALSE; /*超时退出*/
        }
    }

    /*重启I2C开端条件进行总线读*/
    I20CONSET = I2C_STA | I2C_SI;
    I20CONCLR = I2C_SI;

    /*等候重启条件发送完结*/
    loopSafetyCounter = 0;
    while (~I20CONSET & I2C_SI)
    {
        loopSafetyCounter ++;
        if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
        {
            return FALSE; /*超时退出*/
        }
    }

    /*发送从设备地址(方向位为读,留意与上0x01将地址最低位变为1即为读方向)*/
    if(channel == CHANNEL_GPRS)
        I20DAT = SC16IS740_ADDR | 0x01;
    else if(channel == CHANNEL_TEMPERATURE)
        I20DAT = ADT75A_ADDR | 0x01;

    I20CONCLR = I2C_STA | I2C_SI;

    /*等候从设备地址发送完结*/
    loopSafetyCounter = 0;
    while (~I20CONSET & I2C_SI)
    {
        loopSafetyCounter ++;
        if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
        {
            return FALSE; /*超时退出*/
        }
    }

    /*开端预备读取数据*/
    I20CONCLR = I2C_SI | I2C_AA;

    /*等候数据接纳*/
    loopSafetyCounter = 0;
    while (~I20CONSET & I2C_SI)
    {
        loopSafetyCounter ++;
        if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
        {
            return FALSE; /*超时退出*/
        }
    }

    /*数据接纳*/
    *pData = I20DAT;

    /*发送I2C中止条件*/
    I20CONSET = I2C_STO;
    I20CONCLR = I2C_SI;

    /*等候中止条件发送完结*/
    loopSafetyCounter = 0;
    while (I20CONSET & I2C_STO)
    {
        loopSafetyCounter ++;
        if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
        {
            return FALSE; /*超时退出*/
        }
    }

    return TRUE;
}

/****************************************************************************
** Function name: I2C0_WriteRegister
** Descriptions : 从I2C0总线上写从设备的数据
** Input : 从设备挑选标识、从设备内部寄存器地址、要写入的数据字节
** Output : 写入是否成功
** Created Date : 2011-03-28
*****************************************************************************/
BOOL I2C0_WriteRegister(uint32 channel, uint8 registerAddress, uint8 data)
{
    uint32 loopSafetyCounter = 0;
    uint32 addressSendSafetyCounter = 0;

    /*运用循环判别I2C状况寄存器I20STAT 的值*/
    do
    {
        /*向总线发送I2C开端条件*/
        I20CONSET = I2C_STA | I2C_SI;
        I20CONCLR = I2C_SI;

        /*等候开端条件发送完结*/
        loopSafetyCounter = 0;
        while (~I20CONSET & I2C_SI)
        {
            loopSafetyCounter ++;
            if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
            {
                return FALSE; /*超时退出*/
            }
        }

        /*发送从设备地址*/
        if(channel == CHANNEL_GPRS)
            I20DAT = SC16IS740_ADDR;
        else if(channel == CHANNEL_TEMPERATURE)
            I20DAT = ADT75A_ADDR;

        I20CONCLR = I2C_STA | I2C_SI;

        /*等候从设备地址发送完结*/
        loopSafetyCounter = 0;
        while (~I20CONSET & I2C_SI)
        {
            loopSafetyCounter ++;
            if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
            {
                return FALSE; /*超时退出*/
            }
        }

        addressSendSafetyCounter ++;
        if (addressSendSafetyCounter > SAFETY_COUNTER_LIMIT)
        {
            return FALSE; /*超时退出*/
        }

    } while (I20STAT != 0x18);

    /*发送从设备内部寄存器地址*/
    I20DAT = registerAddress << 3;
    I20CONCLR = I2C_SI;

    /*等候寄存器地址发送完结*/
    loopSafetyCounter = 0;
    while (~I20CONSET & I2C_SI)
    {
        loopSafetyCounter ++;
        if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
        {
            return FALSE; /*超时退出*/
        }
    }

    /*开端发送数据*/
    I20DAT = data;
    I20CONCLR = I2C_SI;

    /*等候数据发送完结*/
    loopSafetyCounter = 0;
    while (~I20CONSET & I2C_SI)
    {
        loopSafetyCounter ++;
        if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
        {
            return FALSE; /*超时退出*/
        }
    }

    /*发送I2C中止条件*/
    I20CONSET = I2C_STO;
    I20CONCLR = I2C_SI;

    /*等候中止条件发送完结*/
    loopSafetyCounter = 0;
    while (I20CONSET & I2C_STO)
    {
        loopSafetyCounter ++;
        if (loopSafetyCounter > SAFETY_COUNTER_LIMIT)
        {
            return FALSE; /*超时退出*/
        }
    }

    return TRUE;
}

 

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/xinpin/yuanjian/90014.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部