LPC2138微控制器之I2C

Posted justin-y-lin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LPC2138微控制器之I2C相关的知识,希望对你有一定的参考价值。

第八章 I2C控制器

      前面一章讲了LPC2138 UART控制器,它的硬件实现的核心是Tx/Rx FIFO,但是I2C控制器硬件上没有Tx/Rx FIFO,个人猜测这个原因可能是UART的读写速率比I2C要高,另外I2C协议有参考时钟SCL,而UART的TXD/RXD都是靠约定波特率和数据帧的奇偶校验位确保数据完整性。

特性

      I2C控制器可以工作在Master模式或者Slave模式。(本文仅介绍Master模式)

      I2C总线支持多个Master之间仲裁。

      I2C总线参考PCLK时钟源,I2C时钟频率可配置。

I2C总线协议

      关于I2C总线协议,有很多公开的资料介绍,而且比较简单,此处就不予复述。

其中Start信号、Stop信号、ACK信号、NACK信号、数据发送、数据接收各原子操作的时序都是固定的。但是具体到不同的I2C器件,各I2C器件对I2C的具体时序有具体的要求(比如随机读写和按页读写、按页读写时支持跨页操作或者不支持跨页操作、I2C偏移地址的位宽为1位或者2位等),需要结合I2C器件的datasheet进行编码。

I2C控制器寄存器

1) I2CCONSET
    [2] AA Assert ACK Flag
         当AA位置1时,Master接收到一个字节的数据时,会发送ACK信号
         反之,当AA位置0时,Master接收到一个字节的数据之后,会发送NACK信号
    [3] SI I2C Interrupt Flag
         当I2C状态发生变化时,SI置位(除了I2CSTAT为0xF8时)
         当SI置位时,SCL低电平拉长,数据传输暂停,但是SCL拉高不受SI位影响
         SI位只能通过往I2CCONCLR的SIC位写1清除
    [4] STO STOP Flag
         STO位发送完Stop信号之后,自动清除
    [5] STA START Flag
         如果不处于Master模式,则进入Master模式并发送Start信号(如果此时总线忙,则等待Stop信号半个时钟)
         如果处于Master模式,则发送ReStart信号
         如果STA和STO同时置位,则先发送Stop再发送Start
    [6] I2EN (不要使用I2EN位去释放I2C总线,而应该用AA位)

2) I2CCONCLR
    [2] AAC Assert ACK Clear Bit
    [3] SIC I2C Interrupt Clear Bit
    [5] STAC
    [6] I2ENC

3) I2CSTAT
    一共有26种状态码,I2CSTAT值在SI置位期间一直保持。

4) I2CDAT
    只有当SI置位时,才可以读写I2CDAT。
    在SI置位期间,数据一直保持

 

EEPROM读写实例

使用I2C 0控制器和AT24C1024 EEPROM芯片实现,I2C控制器工作在Master模式。

在编码的过程中,最仔细研究的是I2C控制器的寄存器以及Master发送和接收的时序图:

Master Transmit模式

技术图片

 Master Receive模式

技术图片

还有AT24C1024芯片的读写时序图:

技术图片

 技术图片

另外需要注意的是,每次清SI位进行状态切换时,软件需要插入延迟才能进行数据读写,因为CPU的工作频率原高于I2C控制器的工作频率,需要通过一定的延迟等待硬件执行完成。 

i2c.c

#include <lpc213x.h>
#include "i2c.h"
#include "serial.h"

#define I2C_DEBUG 0
#define I2C_ERROR 1

#define I2C_WRITE (0x00)
#define I2C_READ (0x01)

void i2c_delay(void)
{
    int i = 1000;

    while(i--);

}

void i2c_init(void)
{
    /* Configure P0.2~3 as SCL0 & SDA0 */
    PINSEL0 &= ~0x000000F0;
    PINSEL0 |= 0x00000050;
    
    /* Configure I2C Freq to 150KHz while PCLK is 30MHz */
    I2C0SCLH = 0x64;
    I2C0SCLL = 0x64;

    /* Enable I2C Bus */
    I2C0CONSET = 0x40;

}

int i2c_start(void)
{
    /* Start */
    I2C0CONSET = 0x20;

    i2c_delay();

    if ((I2C0CONSET & 0x08) && (I2C0STAT == 0x08))
    {
        /* Clear STA bit */
        I2C0CONCLR = 0x20;

        /* return 0 if success */
        return 0;
    }
    else
    {
        /* Clear STA bit */
        I2C0CONCLR = 0x20;

        if (I2C_ERROR)
        {
            sendstr("i2c_start I2C0STAT=");
            sendhex(I2C0STAT);
            sendstr("
");
        }

        /* return -1 is failed */
        return -1;
    }
}

int i2c_restart(void)
{
    /* ReStart */
    I2C0CONSET = 0x20;

    i2c_delay();

    if ((I2C0CONSET & 0x08) && (I2C0STAT == 0x10))
    {
        /* Clear STA bit */
        I2C0CONCLR = 0x20;

        /* return 0 if success */
        return 0;
    }
    else
    {
        /* Clear STA bit */
        I2C0CONCLR = 0x20;

        sendstr("i2c_start I2C0STAT=");
        sendhex(I2C0STAT);
        sendstr("
");

        /* return -1 is failed */
        return -1;
    }
}

int i2c_stop(void)
{
    /* Stop, STO is cleared automatically */
    I2C0CONSET = 0x10;

    return 0;
}

int i2c_send_addr(unsigned char addr)
{
    if (I2C_DEBUG)
    {
        sendstr(" I2CSTAT=");
        sendhex(I2C0STAT);
        sendstr("
");
        sendstr("i2c_send_addr ");
        sendhex(addr);
        sendstr("
");
    }

    I2C0DAT = addr;

    if ((I2C0CONSET & 0x08) && (I2C0STAT == 0x08 || I2C0STAT == 0x10))
    {
        /* Clear SI bit to send out addr */
        I2C0CONCLR = 0x08;
    }
    else
    {
        sendstr("i2c bus 0 not started
");
        return -1;
    }

    i2c_delay();

    if (addr & I2C_READ)
    {
        /* Master Read */
        if (I2C0STAT == 0x40)
        {
            /* ACK */
            return 0;
        }
        else if (I2C0STAT == 0x48)
        {
            /* NACK */
            return -1;
        }
    }
    else
    {
        /* Master Write */
        if (I2C0STAT == 0x18)
        {
            /* ACK */
            return 0;
        }
        else if (I2C0STAT == 0x20)
        {
            /* NACK */
            return -1;
        }
    }

    /* other exception */
    if (addr & I2C_READ)
    {
        sendstr("i2c bus 0 read exception, I2CSTAT=");
        sendhex(I2C0STAT);
        sendstr("
");
    }
    else
    {
        sendstr("i2c bus 0 write exception, I2CSTAT=");
        sendhex(I2C0STAT);
        sendstr("
");
    }

    return -2;
}

int i2c_send_byte(unsigned char data)
{
    if (I2C_DEBUG)
    {
        sendstr(" I2CSTAT=");
        sendhex(I2C0STAT);
        sendstr("
");
        sendstr("i2c_send_byte ");
        sendhex(data);
        sendstr("
");
    }

    /* Master Write */
    if ((I2C0CONSET & 0x08) && (I2C0STAT == 0x18 || I2C0STAT == 0x28))
    {
        I2C0DAT = data;

        /* Clear SI bit to send out addr */
        I2C0CONCLR = 0x08;
    }
    else
    {
        sendstr("i2c_send_byte exception, I2CSTAT=");
        sendhex(I2C0STAT);
        sendstr("
");
        return -2;
    }

    i2c_delay();

    if (I2C0STAT == 0x28)
    {
        /* ACK */
        return 0;
    }
    else if (I2C0STAT == 0x30)
    {
        /* NACK */
        return -1;
    }
    else
    {
        /* exception */
        sendstr("i2c bus 0 write data exception, I2CSTAT=0x");
        sendhex(I2C0STAT);
        sendstr("
");

        return -2;
    }
}

int i2c_recv_byte(unsigned char *data, unsigned int ack)
{
    unsigned char recv_byte;

    if (I2C_DEBUG)
    {
        sendstr("i2c_recv_byte I2CSTAT=");
        sendhex(I2C0STAT);
        sendstr("
");
    }

    if ((I2C0CONSET & 0x08) && (I2C0STAT == 0x40))
    {
        /* Assert ACK bit, send out ACK/NACK after one byte received */
        if (ack)
            I2C0CONSET = 0x04; /* ACK */
        else
            I2C0CONCLR = 0x04; /* NACK */
    
        /* Clear SI bit to release I2CDAT */
        I2C0CONCLR = 0x08;

        i2c_delay();

        recv_byte = I2C0DAT;
    }

    *data = recv_byte;
    if (I2C_DEBUG)
    {
        sendstr("i2c_recv_byte ");
        sendhex(recv_byte);
        sendstr("
");
    }

    if ((I2C0CONSET & 0x08) && (I2C0STAT == 0x50))
    {
        /* ACK Send Out */
        return 0;
    }
    else if ((I2C0CONSET & 0x08) && (I2C0STAT == 0x58))
    {
        /* NACK Send Out */
        return -1;
    }
    else
    {
        /* exception occurs */
        sendstr("i2c bus 0 recv data exception, I2CSTAT=");
        sendhex(I2C0STAT);
        sendstr("
");

        return -2;
    }
}

int i2c_write(unsigned char addr,
    unsigned int offset,
    unsigned int off_len,
    unsigned char *data,
    unsigned int len)
{
    int ret = 0;
    int i = 0;
    unsigned char byte;

    if (addr & 0x80)
    {
        sendstr("i2c address invalid
");
        return -3;
    }

    if (off_len > sizeof(offset))
    {
        sendstr("i2c offset length out of range
");
        return -3;
    }

    if (0 == data)
    {
        sendstr("i2c data pointer is NULL
");
        return -3;
    }

    if (len <= 0)
    {
        sendstr("i2c data length should be lager than zero
");
        return -3;
    }

    ret = i2c_start();
    if (ret < 0)
    {
        sendstr("i2c_write start failed
");
        return -1;
    }

    ret = i2c_send_addr((addr << 1) | I2C_WRITE);
    if (ret == -2)
    {
        sendstr("i2c_write send addr failed
");
        return -2;
    }
    else if (ret == -1)
    {
        /* NACK */
        i2c_stop();
        return -1;
    }

    for (i = 0; i < off_len; i++)
    {
        byte = (unsigned char)(offset >> (8 * (off_len - i - 1)));
        ret = i2c_send_byte(byte);
        if (ret == -2)
        {
            sendstr("i2c_write send offset failed
");
            return -2;
        }
        else if (ret == -1)
        {
            i2c_stop();
            return -1;
        }
    }

    for (i = 0; i < len; i++)
    {
        ret = i2c_send_byte(data[len - i - 1]);
        if (ret == -2)
        {
            sendstr("i2c_write send data failed
");
            return -2;
        }
        else if (ret == -1)
        {
            i2c_stop();
            return 0;
        }
    }

    i2c_stop();

    return 0;
}

int i2c_read(unsigned char addr,
    unsigned int offset,
    unsigned int off_len,
    unsigned char *data,
    unsigned int len)
{
    int ret = 0;
    int i = 0;
    unsigned char byte = 0;

    if (addr & 0x80)
    {
        sendstr("i2c address invalid
");
        return -3;
    }

    if (off_len > sizeof(offset))
    {
        sendstr("i2c offset length out of range
");
        return -3;
    }

    if (0 == data)
    {
        sendstr("i2c data pointer is NULL
");
        return -3;
    }

    if (len <= 0)
    {
        sendstr("i2c data length should be lager than zero
");
        return -3;
    }

    ret = i2c_start();
    if (ret < 0)
    {
        sendstr("i2c_read start failed
");
        return -1;
    }

    ret = i2c_send_addr((addr << 1) | I2C_WRITE);
    if (ret == -2)
    {
        sendstr("i2c_read send write addr failed
");
        return -2;
    }
    else if (ret == -1)
    {
        /* NACK */
        i2c_stop();
        return -1;
    }

    for (i = 0; i < off_len; i++)
    {
        byte = (unsigned char)(offset >> (8 * (off_len - i - 1)));
        ret = i2c_send_byte(byte);
        if (ret == -2)
        {
            sendstr("i2c_read send offset failed
");
            return -2;
        }
        else if (ret == -1)
        {
            i2c_stop();
            return -1;
        }
    }

    ret = i2c_restart();
    if (ret < 0)
    {
        sendstr("i2c_read restart failed
");
        return -1;
    }

    ret = i2c_send_addr((addr << 1) | I2C_READ);
    if (ret == -2)
    {
        sendstr("i2c_read send read addr failed
");
        return -2;
    }
    else if (ret == -1)
    {
        /* NACK */
        i2c_stop();
        return -1;
    }

    i = 0;

    do {

        if (i == (len - 1))
        {
            /* Send Out NACK after last byte */
            ret = i2c_recv_byte(&byte, 0);
        }
        else
        {
            /* Send Out ACK except last byte */
            ret = i2c_recv_byte(&byte, 1);
        }

        data[len - i - 1] = byte;
        if (ret == -2)
        {
            /* exception occurs */
            return ret;
        }

        if (I2C_DEBUG)
        {
            sendstr("after recv byte, I2CSTAT=");
            sendhex(I2C0STAT);
            sendstr(" I2CDAT=");
            sendhex(I2C0DAT);
            sendstr("
");
        }

        i++;

    } while (i < len);

    i2c_stop();

    return 0;
}

eeprom.c

#include "i2c.h"
#include "eeprom.h"

#define AT24C1024_OFFSET_LEN (2)

#define AT24C1024_REG_LEN (1)

int eeprom_write_byte(unsigned int offset, unsigned char data)
{
    return i2c_write(I2C_ADDR, offset, AT24C1024_OFFSET_LEN, &data, AT24C1024_REG_LEN);
}

int eeprom_read_byte(unsigned int offset, unsigned char *data)
{
    return i2c_read(I2C_ADDR, offset, AT24C1024_OFFSET_LEN, data, AT24C1024_REG_LEN);
}

 

以上是关于LPC2138微控制器之I2C的主要内容,如果未能解决你的问题,请参考以下文章

LPC2146芯片解密 烧录方法

LPC2138串口屏幕显示的横竖屏切换问题

从 MPU9150 读取传感器数据的 C I2C 代码不起作用

在两个 arduino 微控制器上使用 i2c 通信发送字符串

ARM7LPC2138做串口中断实验的时候,发送中断一直进不去。接收中断一切正常,代码改了n遍了,求解释啊

我需要帮助构建lpc1768的openvibe源代码