这些天在做一个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; }
|