在用的这ARM芯片GPIO也够省的,不够用,只好外挂一颗SC16IS760,模拟i2c就过它的GPIO口:
GPIO1--->SCL
GPIO3--->SDA
模块向外曝露的接口:
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_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;
}