eCos Bit-Bang I2C

1 I2C總線協議
I2C協議
2條雙向串行線,一條數據線SDA,一條時鐘線SCL。
SDA傳輸數據是大端傳輸(字節高位先傳),每次傳輸8bit,即一字節。
支持多主控(multimastering),任何時間點只能有一個主控。
總線上每個設備都有自己的一個addr,共7個bit,廣播地址全0.

I2C開始和結束信號
開始信號:SCL爲高電平時,SDA由高電平向低電平跳變,開始傳送數據。
結束信號:SCL爲高電平時,SDA由低電平向高電平跳變,結束傳送數據。

I2C位傳輸
數據傳輸:SCL爲高電平時,SDA線必須保持穩定,SDA上傳輸一個bit數據;
數據改變:SCL爲低電平時,SDA線才能改變電平

I2C應答信號
主控每發送完8bit數據後等待從設備ACK。
即在第9個clock,若從設備發回ACK,SDA會被拉低。
若沒有ACK,SDA會被置高,這會引起主控發生RESTART或STOP流程

I2C寫流程
寫寄存器的標準流程爲:
1. Master發起START
2. Master發送I2C addr(7bit)和w操作0(1bit),等待ACK
3. Slave發送ACK
4. Master發送reg addr(8bit),等待ACK
5. Slave發送ACK
6. Master發送data(8bit),即要寫入寄存器中的數據,等待ACK
7. Slave發送ACK
8. 第6步和第7步可以重複多次,即順序寫多個寄存器
9. Master發起STOP

I2C讀流程
讀寄存器的標準流程爲:
1. Master發送I2C addr(7bit)和w操作1(1bit),等待ACK
2. Slave發送ACK
3. Master發送reg addr(8bit),等待ACK
4. Slave發送ACK
5. Master發起START
6. Master發送I2C addr(7bit)和r操作1(1bit),等待ACK
7. Slave發送ACK
8. Slave發送data(8bit),即寄存器裏的值
9. Master發送ACK
10. 第8步和第9步可以重複多次,即順序讀多個寄存器

基本通信流程
1、 主設備發出開始信號(Start)。
2、 主設備發出1字節的從設備地址信息,其中最低位爲讀寫控制碼(0爲寫,1爲讀),高7位爲從機器地址碼。
3、 從設備發出認可信號。
4、 主設備開始對從設備進行讀寫操作。如果是讀操作,則每讀取1字節,主設備會發送一個應答信號(Ack)給從設備,如果是寫操作,則每寫入1字節,從設備會發送一個應答信號(Ack)給主設備。
5、 主設備發出結束信號(Stop)。

2 DECLARE_I2C_GPIO

/*
 * BUS IDLE: SDA & SCL HIGH
 * SCL HIGH: data stable, sample SDA
 * SCL LOW:  data change, disable sample
 */

typedef struct {
    uint16_t sda;
    uint16_t scl;
} i2c_gpio_t;

#define DECLARE_I2C_GPIO(NAME, SDA, SCL, FREQ)  \
    static const i2c_gpio_t i2c_gpio_##NAME =   \
{SDA, SCL};

DECLARE_I2C_GPIO(gpiomux, 12, 11, 100);

static void i2c_writebit(i2c_gpio_t *handle, u8 a);

static inline u32 translate_addr(u32 addr)
{
    u32 phy_addr = addr;

    // kseg1: 0xA000 0000 - 0xBFFF FFFF(512M)
    phy_addr &= 0x0FFFFFFC;
    //phy_addr &= 0x0FFFFFFF;
    phy_addr |= 0xb0000000;

    return phy_addr;
}

static int8_t gpio_get_value_v2(u8 gpio)
{
    uint32_t addr;

    if (gpio <= 31) {
        // GPIO_DATA_0
        addr = translate_addr(0x10000620);
    } else if (gpio >= 32 && gpio <= 63) {
        // GPIO_DATA_1
        addr = translate_addr(0x10000624);
        gpio = gpio - 32;
    } else {
        // 64 ... 95
        // GPIO_DATA_2
        addr = translate_addr(0x10000628);
        gpio = gpio - 64;
    }

    return ((HAL_REG32(addr) >> gpio) & 0x1);
}

static void gpio_set_value_v2(u8 gpio, u8 value)
{
    uint32_t addr, reg_val;

    if (gpio <= 31) {
        if (value > 0) {
            // GPIO_DSET_0
            addr = translate_addr(0x10000630);
        } else {
            // GPIO_DCLR_0
            addr = translate_addr(0x10000640);
        }
    } else if (gpio >= 32 && gpio <= 63) {
        if (value > 0) {
            // GPIO_DSET_1
            addr = translate_addr(0x10000634);
        } else {
            // GPIO_DCLR_1
            addr = translate_addr(0x10000644);
        }
        gpio = gpio - 32;
    } else {
        // 64 ... 95
        if (value > 0) {
            // GPIO_DSET_2
            addr = translate_addr(0x10000638);
        } else {
            // GPIO_DCLR_2
            addr = translate_addr(0x10000648);;
        }
        gpio = gpio - 64;
    }

    reg_val = HAL_REG32(addr);
    reg_val |= (1 << gpio);
    HAL_REG32(addr) = reg_val;
}

static void SDAIN(i2c_gpio_t *handle)
{
    u8 gpio = handle->sda;
    u32 addr, reg_val;

    if (gpio <= 31) {
        // GPIO_CTRL_0
        addr = translate_addr(0x10000600);
    } else if (gpio >= 32 && gpio <= 63) {
        // GPIO_CTRL_1
        addr = translate_addr(0x10000604);
        gpio = gpio - 32;
    } else {
        // 64 ... 95
        // GPIO_CTRL_2
        addr = translate_addr(0x10000608);
        gpio = gpio - 64;
    }

    reg_val = HAL_REG32(addr);
    reg_val &= ~(1 << gpio);
    HAL_REG32(addr) = reg_val;
}

static void SDAOUT(i2c_gpio_t *handle)
{
    int gpio = handle->sda;
    u32 addr, reg_val;

    if (gpio <= 31) {
        // GPIO_CTRL_0
        addr = translate_addr(0x10000600);
    } else if (gpio >= 32 && gpio <= 63) {
        // GPIO_CTRL_1
        addr = translate_addr(0x10000604);
        gpio = gpio - 32;
    } else {
        // 64 ... 95
        // GPIO_CTRL_2
        addr = translate_addr(0x10000608);
        gpio = gpio - 64;
    }

    reg_val = HAL_REG32(addr);
    reg_val |= 1 << gpio;
    HAL_REG32(addr) = reg_val;
}

/*
 * MT7628K CPU freq = 580/575 MHz, about 1.72 ns, 1 us = 581 clock
 * ESP8266 CPU freq = 80 MHz, about 12.5 ns, 1 us = 80 clock
 *
 * 100 kHz = 100,000, about 10 us
 * 400 kHz = 400,000, about 2.5 us
 *
 */
static void delay5us(void)
{
    uint16_t i = 50;

    while(i--);
}

static void sendack(i2c_gpio_t *handle)
{
    i2c_writebit(handle, 0);
}

static void sendnack(i2c_gpio_t *handle)
{
    i2c_writebit(handle, 1);
}

static void i2c_start(i2c_gpio_t *handle)
{
    SDAOUT(handle);
    gpio_set_value_v2(handle->sda, 1);
    delay5us();
    gpio_set_value_v2(handle->scl, 1);
    delay5us();
    gpio_set_value_v2(handle->sda, 0);
    delay5us();
    gpio_set_value_v2(handle->scl, 0);
    delay5us();
}

static void i2c_stop(i2c_gpio_t *handle)
{
    SDAOUT(handle);
    gpio_set_value_v2(handle->sda, 0);
    delay5us();
    gpio_set_value_v2(handle->scl, 1);
    delay5us();
    gpio_set_value_v2(handle->sda, 1);
    delay5us();
    gpio_set_value_v2(handle->scl, 0);
    delay5us();
}

static void wait_ack(i2c_gpio_t *handle)
{
    uint16_t i;

    gpio_set_value_v2(handle->scl, 0);
    delay5us();
    SDAIN(handle);
    delay5us();
    gpio_set_value_v2(handle->scl, 1);
    delay5us();
    while(gpio_get_value_v2(handle->sda) && (i < 0x2b0)) {
        i++;
    }

    gpio_set_value_v2(handle->scl, 0);
    delay5us();
}

static void i2c_writebyte(i2c_gpio_t *handle, u8 a)
{
    uint16_t i;

    SDAOUT(handle);
    for (i = 0; i < 8; i++) {
        gpio_set_value_v2(handle->scl, 0);
        delay5us();
        if (a & 0x80) {
            gpio_set_value_v2(handle->sda, 1);
        } else {
            gpio_set_value_v2(handle->sda, 0);
        }
        a = (a << 1);
        delay5us();
        gpio_set_value_v2(handle->scl, 1);
        delay5us();
    }
}

static void i2c_writebit(i2c_gpio_t *handle, u8 a)
{
    SDAOUT(handle);
    gpio_set_value_v2(handle->scl, 0);
    delay5us();
    if (a == 0) {
        gpio_set_value_v2(handle->sda, 0);
    } else {
        gpio_set_value_v2(handle->sda, 1);
    }
    delay5us();
    gpio_set_value_v2(handle->scl, 1);
    delay5us();
    gpio_set_value_v2(handle->scl, 0);
    delay5us();
}

static u8 i2c_readbyte(i2c_gpio_t *handle)
{
    u8 i, temp = 0;

    SDAIN(handle);
    gpio_set_value_v2(handle->scl, 0);
    delay5us();
    for (i = 0; i < 8; i++) {
        gpio_set_value_v2(handle->scl, 1);
        delay5us();
        temp =(temp << 1)|gpio_get_value_v2(handle->sda);
        delay5us();
        gpio_set_value_v2(handle->scl, 0);
        delay5us();
    }
    return temp;
}

void i2c_init(i2c_gpio_t *handle)
{
    // pin mux
    // Reference to "MT7628 PROGRAMMING GUIDE", p15 of System Control
    // GPIO1_MODE 0x10000060
    // GPIO2_MODE 0x10000064
    gpio_set_value_v2(handle->sda, 1);
    gpio_set_value_v2(handle->scl, 1);
}

void i2c_byte_write(i2c_gpio_t *handle, u8 saddr, u8 reg, u8 val)
{
    i2c_start(handle);
    i2c_writebyte(handle, saddr);
    wait_ack(handle);
    i2c_writebyte(handle, reg);
    wait_ack(handle);
    i2c_writebyte(handle, val);
    wait_ack(handle);
    i2c_stop(handle);
}

u8 i2c_byte_read(i2c_gpio_t *handle, u8 saddr, u8 reg)
{
    u8 temp;

    i2c_start(handle);
    i2c_writebyte(handle, saddr);
    wait_ack(handle);
    delay5us();
    i2c_writebyte(handle, reg);
    wait_ack(handle);
    i2c_start(handle);
    i2c_writebyte(handle, saddr + 1);
    wait_ack(handle);
    temp = i2c_readbyte(handle);
    sendnack(handle);
    i2c_stop(handle);

    return temp;
}

static void acc_i2c_init(void)
{
    i2c_init(&i2c_gpio_gpiomux);
}
module_init(acc_i2c_init);
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章