软件模拟I2C

这些天在做一个h.264设备,用的ARM芯片是带I2C接口,但这I2C一来只支持400K,二来没有可调的时序空间,没法支持TVP5150这种有点瑕疵的I2C,至此只好借用两个GPIO口通过软件来模拟i2c操作。

在用的这ARM芯片GPIO也够省的,不够用,只好外挂一颗SC16IS760,模拟i2c就过它的GPIO口:
GPIO1--->SCL
GPIO3--->SDA

模块向外曝露的接口:
int swi2c_write(uint8_t i2caddr, uint8_t subaddr, uint8_t data);
int swi2c_blkwrite(uint8_t i2caddr, uint8_t subaddr, uint8_t *data, uint32_t blksize);
int swi2c_read(uint8_t i2caddr, uint8_t subaddr, uint8_t *data);
int swi2c_blkread(uint8_t i2caddr, uint8_t subaddr, uint8_t *data, uint32_t blksize);

模块需要外界提供的接口:
#define swi2c_udelay(us)
#define swi2c_scl_low()
#define swi2c_scl_high()

#define swi2c_sda_input()
#define swi2c_sda_output()            
#define swi2c_sda_low()                
#define swi2c_sda_high()            
#define swi2c_sda()                    


//------------------------------------------------------------------------------ 
//  Routine:    Start    
//  Inputs:     none    
//  Outputs:    none    
//  Purpose:    Sends I2C Start Trasfer - "S"    
//------------------------------------------------------------------------------ 
// 在执行任何一次i2c操作前和i2c操作后,应保证:
// 1: sda, 输出, 高电平(必须保证)
// 2: scl, 输出, 高电平()
void swi2c_start(void)   
{
    swi2c_scl_high();    // 常态下scl应该是输出高电平,此处是为了预防万一
    swi2c_udelay(5);            // tSU, Min:4.7us

    swi2c_sda_low();
    swi2c_udelay(5);            // tHD, Min:4.0us 

    swi2c_scl_low();

    // 让退前出, scl(low)至少已维持了tLOW时间, 而sda(low)则至少已维持tLOW+tSU时间
    swi2c_udelay(5);            // tLOW, Min:4.7us

    return;
}   

//------------------------------------------------------------------------------ 
//  Routine:    Stop    
//  Inputs:     none    
//  Outputs:    none    
//  Purpose:    Sends I2C Stop Trasfer - "P"    
// stop之前不用延时
//------------------------------------------------------------------------------ 
void swi2c_stop(void)   
{
    swi2c_sda_low();
    swi2c_udelay(5);            // tSU, Min:4.7us

    swi2c_scl_high();    
    swi2c_udelay(5);            // tSU, Min:4.7us

    swi2c_sda_high();
    return;
}

//------------------------------------------------------------------------------ 
//  Routine:    Write    
//  Inputs:     data
//  Outputs:    int    
//  Purpose:    Writes data over the I2C bus and return status.    
//  返回值: 
//    0: 有ACK
//    -1: 无ACK
//------------------------------------------------------------------------------ 
     
int swi2c_wdata(uint8_t data)   
{
    uint8_t        ibit;   
    int            retval = 0;

    // An I2C output byte is bits 7-0 (MSB to LSB). Shift one bit at a time to 
    // the SDATA output, and then clock the data to the I2C Slave device.    
    // Send 8 bits out the port     
    for(ibit = 0; ibit < 8; ibit++) {
        if (data & 0x80) {
            swi2c_sda_high();
        } else {
            swi2c_sda_low();
        }
        swi2c_udelay(5);    // tSU, Min:4.7us
             
        swi2c_scl_high();
        swi2c_udelay(5);    // tHIGH, Min:4.0us

        swi2c_scl_low();
        swi2c_udelay(5);    // tLOW, Min:4.7us

        data <<= 1;        //shift the byte by one bit    
    }
    
    // 我不能确定是否一定需在把sda置为high
    // 置为high是考虑到一种情况:
    // 1.原先sda就输出low
    // 2.置为输入后,从设备不动sda,以至sda还是low,这样会使主设备误判时有ACK!
    // 但是第二点似乎不能站住脚,为什么呢?要是从设备不处理,也就是意味着释放sda,sda应该被上拉电阻拉高!
    // 先这么办,把以下这个注释掉
    // swi2c_sda_high();

    swi2c_sda_input();
    swi2c_scl_high();
    swi2c_udelay(5);    // tSU, Min:4.7us

    if (!swi2c_sda()) {
        retval = 0;        // ACK from slave    
    } else {
        retval = -1;    // NACK from slave    
    }

    // 退出写一个字节,scl置低,sda置高
    swi2c_scl_low(); // 这个置低会通常会使sda有一个由低的到高的跳变(我怀疑是slave检测到scl变低,于是就释放了sda)
    swi2c_sda_output();
    swi2c_sda_high();

    // 让退前出, scl(low)/sda(high)至少已维持了tLOW时间
    swi2c_udelay(5);    // tLOW, Min:4.7us

    return retval;
}

//------------------------------------------------------------------------------ 
//  Routine:    Read    
//  Inputs:     *data_in, send_ack (if true send the ACK signal else send NACK)    
//  Outputs:    bool    
//  Purpose:    Reads data from the I2C bus and return it in data_in.    
//              Returns status.    
//------------------------------------------------------------------------------ 
     
void swi2c_rdata(uint8_t *data, int ack)
{
    uint8_t        ibit;

    swi2c_sda_input();
    // 这里可能产生一个脉冲
     
    // Get 8 bits from the device    
    for (*data = 0, ibit = 0; ibit < 8; ibit ++) {
        swi2c_scl_high();
        swi2c_udelay(5);    // tHIGH, Min:4.7us

        if (swi2c_sda()) {
            *data = *data | (1 << (7 - ibit));
        }

        swi2c_scl_low();    // 这第8个置低会通常会使sda有一个由低的到高的跳变(我怀疑是slave检测到scl变低,于是就释放了sda)
        swi2c_udelay(5);    // tLOW, Min:4.7us
    }
    
    swi2c_sda_output();
    // 这里可能产生一个脉冲
    // 一旦设为一个输出,原因值是1,又要ACK
    if (ack) {
        swi2c_sda_low();    // Set data pin to output/low to ACK the read
    } else {
        swi2c_sda_high();    // Set data pin to input/high to NACK the read
    }
    swi2c_udelay(5);    // tSU, Min:4.7us
     
    swi2c_scl_high();    // Set SCLK high    
    swi2c_udelay(5);    // tHIGH, Min:4.0us

    swi2c_scl_low();    // Set SCLK high    
    swi2c_sda_high();

    // 让退前出, scl(low)/sda(high)至少已维持了tLOW时间
    swi2c_udelay(5);    // tLOW, Min:4.7us

    return;
}   
//------------------------------------------------------------------------------ 
    
// Procedure:   swi2c_write
// Inputs:      i2caddr, subaddr, data
// Outputs:     int
// Description: Writes a byte to the given address and return status.    
//------------------------------------------------------------------------------ 
int swi2c_write(uint8_t i2caddr, uint8_t subaddr, uint8_t data)
{
    // posix_print("swi2c_write, (%02x, %02x, %02x)\n", i2caddr, subaddr, data);

    swi2c_start();    // Send start signal    
    if (swi2c_wdata(i2caddr & 0xfe)) {    // Send identifier I2C address
        posix_print("------<swi2c_write>, swi2c_wdata, i2caddr fail\n");
        swi2c_stop();    // Send I2C Stop Transfer    
        return -1;   
    }
    if (swi2c_wdata(subaddr)) {    // Send address to device
        posix_print("------<swi2c_read>, swi2c_wdata, subaddr fail\n");
        swi2c_stop();    // Send I2C Stop Transfer
        return -1;   
    }
    if (swi2c_wdata(data)) {    // Send address to device
        posix_print("------<swi2c_read>, swi2c_wdata, data fail\n");
        swi2c_stop();    // Send I2C Stop Transfer
        return -1;   
    }
    swi2c_stop();    // Send I2C Stop Transfer
    return 0;   
}

int swi2c_blkwrite(uint8_t i2caddr, uint8_t subaddr, uint8_t *data, uint32_t blksize)
{
    uint32_t        blk;

    swi2c_start();    // Send start signal    
    if (swi2c_wdata(i2caddr & 0xfe)) {    // Send identifier I2C address
        swi2c_stop();    // Send I2C Stop Transfer    
        return -1;   
    }
    if (swi2c_wdata(subaddr)) {    // Send address to device
        swi2c_stop();    // Send I2C Stop Transfer
        return -1;   
    }
    for (blk = 0; blk < blksize; blk ++) {
        if (swi2c_wdata(data[blk])) {    // Send data to device
            swi2c_stop();    // Send I2C Stop Transfer
            return -1;   
        }
    }
    swi2c_stop();    // Send I2C Stop Transfer
    return 0;   
}

//------------------------------------------------------------------------------ 
    
// Procedure:   I2C_Read    
// Inputs:      *data_in, address    
// Outputs:     bool    
// Description: Reads a byte from the given address and return status.    
//------------------------------------------------------------------------------ 
int swi2c_read(uint8_t i2caddr, uint8_t subaddr, uint8_t *data)
{   
    swi2c_start();    // Send start signal    
    if (swi2c_wdata(i2caddr & 0xfe)) {    // Send identifier I2C address
        posix_print("------<swi2c_read>, swi2c_wdata, i2caddr fail\n");
        swi2c_stop();    // Send I2C Stop Transfer    
        return -1;   
    }
    if (swi2c_wdata(subaddr)) {    // Send address to device
        posix_print("------<swi2c_read>, swi2c_wdata, subaddr fail\n");
        swi2c_stop();    // Send I2C Stop Transfer
        return -1;   
    }
    swi2c_start();    // Send start signal
    if (swi2c_wdata(i2caddr | 0x01)) {    // Send identifier I2C address
        swi2c_stop();    // Send I2C Stop Transfer    
        posix_print("------<swi2c_read>, swi2c_wdata, re-i2caddr fail\n");
        return -1;   
    }   
    swi2c_rdata(data, 0);
    swi2c_stop();    // Send I2C Stop Transfer
    return 0;
}

int swi2c_blkread(uint8_t i2caddr, uint8_t subaddr, uint8_t *data, uint32_t blksize)
{   
    uint32_t        blk;

    swi2c_start();    // Send start signal    
    if (swi2c_wdata(i2caddr & 0xfe)) {    // Send identifier I2C address
        swi2c_stop();    // Send I2C Stop Transfer    
        return -1;   
    }
    if (swi2c_wdata(subaddr)) {    // Send address to device
        swi2c_stop();    // Send I2C Stop Transfer
        return -1;   
    }
    swi2c_start();    // Send start signal    
    if (swi2c_wdata(i2caddr | 0x01)) {    // Send identifier I2C address
        swi2c_stop();    // Send I2C Stop Transfer    
        return -1;   
    }   
    for (blk = 0; blk < blksize; blk ++) {
        swi2c_rdata(data + blk, (blk != blksize - 1)? 1: 0);
    }
    swi2c_stop();    // Send I2C Stop Transfer
    return 0;
}

你可能感兴趣的:(c,input,byte,Signal,output,h.264)