海思I2C总线操作
1.使用I2C命令:
i2c_write <i2c_num> <device_addr> <reg_addr> <value> <reg_width> <data_width>
i2c_read <i2c_num> <device_addr> <reg_addr> <end_reg_addr>
<reg_width> <data_width> <reg_step>
实例:
i2c_write 2 0x56 0xff 0x80 0x1 0x1 I2C写命令, 往总线2,地址为0x56的设备的寄存器
0xff里面写0x80, 寄存器位宽为1byte,数据位宽也为1byte
i2c_read 2 0x57 0x0 0x1 0x1 0x1 I2C读命令,从总线2,地址为0x56(读地址是写地址
+1),从这个设备0 1, 也就是读了0x0寄存器和0x1寄存器,读了两个寄存器。寄存器位宽为1byte,数据位宽也为1byte
reg_step:连续读外围设备寄存器操作时递增幅值, 默认为 1
2.关于I2C总线上面检测设备和查看设备的寄存器:
./i2cdetect -y -r 2 检测i2c总线2上面有哪些设备(海思的这个工具好像读取的地址不准)
./i2cdump -f -y 2 0x56 查看总线2,设备地址0x56上的设备的I2C寄存器的值
./i2cset -f -y 2 0x56 0x23 5 总线2,设备地址为0x56的设备,设置寄存器0x23的值为0x5
./i2cget -f -y 2 0x56 0x0 总线2,设备地址为0x56的设备,读取寄存器0x0的值
3.内核态 I2C 读写程序示例:
static struct i2c_board_info hi_info = {
I2C_BOARD_INFO("hi_test", 0x39),
};
step1: 调用 I2C 核心层的函数, 获得描述一个 I2C 控制器的结构体 i2c_adap
struct i2c_adapter * i2c_adap = i2c_get_adapter(0);
step2:
struct i2c_client * client = i2c_new_device(i2c_adap, &hi_info);
在exit函数中,要记得使用如下的函数进行注销:
i2c_unregister_device(&hi_info);
step3:
在非中断上下文中, 调用 I2C 核心层提供的标准读写函数对外围器件进行读写
ret = i2c_master_send(client, buf, count); 写函数
ret = i2c_transfer(client->adapter, msg, 2); 读函数
在中断上下文中, 调用 I2C 驱动层的读写函数对外围器件进行读写
ret = hi_i2c_master_send(client, buf, count); 写函数
ret = hi_i2c_transfer (client->adapter, msg, 2); 读函数
参数 client 为步骤 2 得到的描述 I2C 外围设备的客户端结构体 client。
参数 buf 为需要读写的寄存器和数据。
参数 count 为 buf 的长度。
参数 msg 为读操作时的两个 i2c_msg 的首地址。
int hi_i2c_write(uint8_t addr, uint8_t reg, uint8_t size, uint8_t *data)
{
int ret;
struct i2c_client * client = step2中获得的client;
uint8_t * write_buf = kmalloc(size + 1, GFP_KERNEL);
memset(write_buf, 0, size + 1);
write_buf[0] = reg;
memcpy(&write_buf[1], data, size);
ret = i2c_master_send(client, write_buf, size + 1);
ret = hi_i2c_master_send(client, write_buf, size + 1);
kfree(write_buf);
return ret;
}
int hi_i2c_read(uint8_t addr, uint8_t reg, uint8_t size, uint8_t *data)
{
int ret;
struct i2c_msg msg[2];
struct i2c_client *client = step2中获得的client;
memset(msg, 0x0, sizeof(struct i2c_msg) * 2);
msg[0].addr = client->addr;
msg[0].flags = client->flags;
msg[0].len = 1;
msg[0].buf = ®
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = size;
msg[1].buf = data;
ret = i2c_transfer(client->adapter, msg, 2);
ret = hi_i2c_transfer(client->adapter, msg, 2);
return ret;
}
static int hi_dev_init(void)
{
struct i2c_adapter *i2c_adap;
围设备挂载在编号为I2C控制器2上
i2c_adap = i2c_get_adapter(2);
就组成了一个客户端hi_client。
hi_client = i2c_new_device(i2c_adap, &hi_info);
i2c_put_adapter(i2c_adap);
return 0;
}
static void hi_dev_exit(void)
{
i2c_unregister_device(hi_client);
}
i2c_msg一些标志的解释:来源:http://blog.chinaunix.net/uid-31087949-id-5752092.html
I2C_M_IGNORE_NAK:
设置这个标志意味当前i2c_msg忽略I2C器件的ack和nack信号。
I2C_M_NOSTART:
设置这个标志意味当前i2c_msg不发送start信号。注意,其实调用bit_xfer的一开始就已经发了start信号了(程序第10 行),这个标记无非就是标志是否发送地址第18行。其次,如果一个i2c_msg没有定义I2C_M_NOSTART而且又不是msgs序列里的第一个 i2c_msg,则回发送重复start信号,我想这就是这个标志起这个名的原因。我们可以猜想,
1.msgs序列第一个数据必须是地址,同时必须不定义这个标志位
2.在进行读数据,要从写操作转变为读操作时,会发重复start信号和器件地址时,必须不定义这个标志位
3.其它情况下一的i2c_msg必须定义这个标志
以上只是我看完这个函数的理解,不一定正确。同时1和2总结下来就是发器件地址(注意,是器件地址,不是像EEPROM那样的EEPROM地 址,这个地址是当数据发的)时会不设置I2C_M_NOSTART, 发数据时就设置I2C_M_NOSTART这个标志。
I2C_M_NO_RD_ACK:
这个标识表示在正行读操作时不去ACK,我不知道其它芯片如果,如果是AT24C04则一定不能设这个标志位了。
(下面三个标志为均为bit_doAddress函数使用,结合上面的说明,也就是这时I2C_M_NOSTART一定没有设置。)
I2C_M_RD:
表示这是一个读操作,默认是把相应的位置1
I2C_M_REV_DIR_ADDR:
表示把读写标志位反转,也就是读是把相应位置0
I2C_M_TEN:
表示这个器件的器件地址是10Bit的。一定要搞清,这是器件地址。一般有10bit和7bit
4.用户态 I2C 读写程序示例
step1: 打开 I2C 总线对应的设备文件, 获取文件描述符
fd = open("/dev/i2c-0", O_RDWR);
step2: 进行数据读写
write(fd, buf, (reg_width + data_width));
用户态I2C读写函数实现:
int i2c_write(int fd, unsigned int dev, unsigned int reg_addr,
unsigned int data, unsigned int reg_width,
unsigned int data_width)
{
int retval = 0;
unsigned char buf[4];
int index = 0;
unsigned int dev_addr = dev >> 1;
retval = ioctl(fd, I2C_SLAVE_FORCE, dev_addr);
if(retval < 0) {
printf("address error!\n");
retval = -1;
goto exit;
}
if (reg_width == 2) {
buf[index] = (reg_addr >> 8) & 0xff;
index++;
buf[index] = reg_addr & 0xff;
index++;
} else {
buf[index] = reg_addr & 0xff;
index++;
}
if (data_width == 2) {
buf[index] = (data >> 8) & 0xff;
index++;
buf[index] = data & 0xff;
index++;
} else {
buf[index] = data & 0xff;
index++;
}
retval = write(fd, buf, (reg_width + data_width));
if(retval < 0) {
printf("i2c write fail!\n");
retval = -1;
goto exit;
}
retval = 0;
exit:
return retval;
}
int i2c_read(int fd, unsigned int dev, unsigned int reg_addr, unsigned int reg_width, unsigned int data_width, unsigned char *val)
{
int retval;
unsigned char buf[4];
static struct i2c_rdwr_ioctl_data rdwr;
static struct i2c_msg msg[2];
unsigned int data;
unsigned int dev_addr = dev >> 1;
retval = ioctl(fd, I2C_SLAVE_FORCE, dev_addr);
if (retval < 0) {
printf("addr error!\n");
return -1;
}
msg[0].addr = dev_addr;
msg[0].flags = 0;
msg[0].len = reg_width;
msg[0].buf = buf;
msg[1].addr = dev_addr;
msg[1].flags = 0;
msg[1].flags |= I2C_M_RD;
msg[1].len = data_width;
msg[1].buf = buf;
rdwr.msgs = &msg[0];
rdwr.nmsgs = (__u32)2;
{
if (reg_width == 2) {
buf[0] = (reg_addr >> 8) & 0xff;
buf[1] = reg_addr & 0xff;
} else {
buf[0] = reg_addr & 0xff;
}
retval = ioctl(fd, I2C_RDWR, &rdwr);
if (retval != 2) {
printf("CMD_I2C_READ error!\n");
return -1;
}
if (data_width == 2) {
data = buf[1] | (buf[0] << 8);
} else {
data = buf[0];
}
}
*val = (unsigned char)(data & 0xff);
retval = 0;
return retval;
}