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 ); }