星期五, 5月 08, 2009

[C/C++]Linux I2C 與 AD71471

AD71471 的 Device address 是 0x58,Linux driver 在處理這個時,其實會自行左移一位,因此,在 I2C_SLAVE_FORCE 的 ioctl 裡,應該是要傳 0x2c。這邊因為我暈頭,把 0x58>>1 算成 0x4c,導致我搞了好一陣子,直到 M 同事指正以後,才弄對。

再來,AD71471 在做讀寫時,Register address 與 data 都是 2 bytes,而 i2ctools 裡,處理 Register address 都只傳 1 byte(I2C_SMBUS),因此不適用在 AD71471 上。我把 Linux kernel i2c-core.c 裡的 code 翻出來改寫,改用 I2C_RDWR 來處理:

bool Write( uint16_t address, uint16_t value ) {
int res=0;
struct i2c_rdwr_ioctl_data msg_rdwr;
char msgbuf0[I2C_SMBUS_BLOCK_MAX+4];
char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
struct i2c_msg msg[1] = { { _address, 0, 4, msgbuf0 } };
uint8_t* pAddr = (uint8_t*)&address;
uint8_t* pValue = (uint8_t*)&value;

msg_rdwr.msgs = &msg[0];
msg_rdwr.nmsgs = 1; // write // read = 2

// 因為 little endian,所以要作調整
msgbuf0[0] = *(pAddr+1);
msgbuf0[1] = *(pAddr+0);
msgbuf0[2] = *(pValue+1); // (1)
msgbuf0[3] = *(pValue+0);

res = ioctl( _file, I2C_RDWR, &msg_rdwr );
usleep(10000);

return true;
}
bool Read( uint16_t address, uint16_t& data ) {
int res=0;
struct i2c_rdwr_ioctl_data msg_rdwr;
char msgbuf0[I2C_SMBUS_BLOCK_MAX+2];
char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]={0};
struct i2c_msg msg[2] = { { _address, 0, 2, msgbuf0 },
{ _address, I2C_M_RD, 2, msgbuf1 }
};
uint8_t* pAddr = (uint8_t*)&address;

msg_rdwr.msgs = &msg[0];
msg_rdwr.nmsgs = 1; // read = 2
// 因為 little endian,所以要作調整
msgbuf0[0] = *(pAddr+1);
msgbuf0[1] = *(pAddr+0);
res = ioctl( _file, I2C_RDWR, &msg_rdwr );
usleep(10000);

msg_rdwr.msgs = &msg[1];
msg_rdwr.nmsgs = 1; // read = 2
res = ioctl( _file, I2C_RDWR, &msg_rdwr ); #ifdef DEBUG
data = msgbuf1[0] | (msgbuf1[1] << 8);

return true;
}
int main( int argc, char* argv[] )
{
int data=0;

Read( 0x17, data );
printf("data=%d\n", data );

// 這邊其實不好,實際上寫 0x0052會比較清楚,這邊必須寫 0x5200,因為我在 Write() 裡有作對調,參看(1)
Write( 0x00, 0x5200 );
}

沒有留言: