员工入职 在哪个网站做招工,网站服务器租用资质,四平网站建设营销,高州网站建设公司目录
1. 查找设备
2. 打开设备
3. 写数据
4. 读数据
5. 设置频率
6 验证
6.1 遍历设备
6.2 开关设备
6.3 读写测试 I2C设备最多有6个#xff08;FT232H#xff09;#xff0c;其他为2个。和之前的设备一样#xff0c;定义个I2C结构体记录找到的设备。
#define FT…目录
1. 查找设备
2. 打开设备
3. 写数据
4. 读数据
5. 设置频率
6 验证
6.1 遍历设备
6.2 开关设备
6.3 读写测试 I2C设备最多有6个FT232H其他为2个。和之前的设备一样定义个I2C结构体记录找到的设备。
#define FTDI_DEVICE_MAX_INTEFACE_I2C 2
#define FTDI_DEVICE_MAX_I2C 6
struct ftdi_i2c_info {struct ftdi_i2c_info *next;int i2c_num[FTDI_DEVICE_MAX_INTEFACE_I2C][FTDI_DEVICE_MAX_I2C]; int pid;int vid;char serial_number[64];
};
FTDI设备和I2C设备对应的关系可以在/sys/bus/usb下找到ttyUSBn串口的那个文件夹内在这个文件夹内可以看到i2c设备的信息例如
:/sys/bus/usb/devices/2-1/2-1:1.0/ttyUSB0$ ls
driver i2c-1 latency_timer power subsystem uevent
event_char i2c-2 port_number spi_master tty
可以看到该设备FT4232H的接口0有2个i2c设备。
1. 查找设备
和串口类似先找到ttyUSB字符串然后在这个文件夹内找i2c-字符串。
DIR *i2c_dir;
struct dirent *i2c_entry;
int i2c_index 0;
sprintf(name_path, /sys/bus/usb/devices/%s:1.%d/%s, entry-d_name, interface, tty_entry-d_name);
i2c_dir opendir(name_path);
while ((i2c_entry readdir(i2c_dir)) ! NULL) {if (strstr(i2c_entry-d_name, i2c-) ! NULL) { printf(Found:%s\n, i2c_entry-d_name);sscanf(i2c_entry-d_name, i2c-%d, dev_list-i2c_num[interface][i2c_index]);i2c_index;}
}
closedir(i2c_dir);
2. 打开设备
分2种情况通过pid或通过串口号打开
int ftdi_sio_i2c::open_i2c(int pid, int n, int num)
int ftdi_sio_i2c::open_i2c(char *serial_number, int interface, int num)
参数
pid - FTDI设备的PID号
n - 需要打开的同PID号的第n个设备
num - 该设备的第num个i2c设备
返回i2c设备的设备句柄。
找到设备的方式和之前的方式一样。
char i2c_path[PATH_MAX];
int fd;
sprintf(i2c_path, /dev/i2c-%d, dev_list-i2c_num[interface][num]);
printf(open:%s\n, i2c_path);
if ((fd open(i2c_path, O_RDWR)) 0) {perror(Failed to open the i2c bus\n);
}
3. 写数据
int ftdi_sio_i2c::write_bytes(int fd, char slave_addr, char reg_addr_width, int reg_addr, unsigned char *pdat, int len)
参数
fd - open设备时返回的设备句柄
slave_addr - 从设备的地址
reg_addr_width - 从设备内部寄存器地址宽度有效参数为0/8/16
reg_addr - 从设备内部寄存器地址
pdat - 写入从设备的数据
len - 写入数据长度
写数据需要将地址和数据一起打包到i2c_msg类型的数据中一个信息就可以写入设备。
if(reg_addr_width 16) {outbuf[offset] (unsigned char)(reg_addr 8);outbuf[offset] (unsigned char)reg_addr;
} else if(reg_addr_width 8)outbuf[offset] (unsigned char)reg_addr;
memcpy(outbuf offset, pdat, len);
messages[0].addr slave_addr;
messages[0].flags 0;
messages[0].len total;
messages[0].buf outbuf;
packets.nmsgs 1;
packets.msgs messages; if(ioctl(fd, I2C_RDWR, packets) 0) {perror(i2cWrite ioctl fail);free(outbuf);return -1;
}
4. 读数据
int ftdi_sio_i2c::read_bytes(int fd, char slave_addr, char reg_addr_width, int reg_addr, unsigned char *pdat, int len)
参数意义与写数据一样的。
当需要写寄存器地址时需要2个msg写入设备第一个msg是写地址第二个msg是读数据。
messages[0].addr slave_addr;
messages[0].flags 0;
messages[0].len offset;
messages[0].buf outbuf;
/* The data will get returned in this structure */
messages[1].addr slave_addr;
messages[1].flags I2C_M_RD/* | I2C_M_NOSTART*/;
messages[1].len len;
messages[1].buf pdat;
/* Send the request to the kernel and get the result back */
packets.msgs messages;
packets.nmsgs 2;
如果没有寄存器的地址只需要1个msg写入设备。
messages[0].addr slave_addr;
messages[0].flags I2C_M_RD/* | I2C_M_NOSTART*/;
messages[0].len len;
messages[0].buf pdat;
/* Send the request to the kernel and get the result back */
packets.msgs messages;
packets.nmsgs 1;
5. 设置频率(失败)
一般的I2C设备并不能支持直接修改i2c的频率这里在内核驱动中添加频率的属性参数。由于之前是一个设备共用一个i2c_clk的参数所以只在ttyUSB设备里面增加i2c_clk属性。
static ssize_t ftdi_mpsse_show_i2c_clk(struct device *dev,struct device_attribute *attr, char *buf)
{struct usb_serial_port *port to_usb_serial_port(dev);struct ftdi_private *priv usb_get_serial_port_data(port);return sprintf(buf, %d\n, priv-i2c_clk - 1);
}static ssize_t ftdi_mpsse_set_i2c_clk(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{struct usb_serial_port *port to_usb_serial_port(dev);struct ftdi_private *priv usb_get_serial_port_data(port);priv-i2c_clk simple_strtoul(buf, NULL, 10) 1;return count;
}
static DEVICE_ATTR(i2c_clk, S_IWUSR | S_IRUSR, ftdi_mpsse_show_i2c_clk, ftdi_mpsse_set_i2c_clk);
注意i2c_clk的值要减一即i2c_clk的值为0时最快但是在驱动中的值是1。
在初始化中添加初始化这个属性
device_create_file(port-dev, dev_attr_i2c_clk);
在释放设备中删掉这个属性
device_remove_file(port-dev, dev_attr_i2c_clk);
这样就可以在ttyUSBn的文件夹中找到这个属性i2c_clk
:/sys/bus/usb/devices/1-2/1-2:1.0/ttyUSB0$ ls
driver i2c-1 i2c_clk port_number spi_master tty
event_char i2c-2 latency_timer power subsystem uevent
在/sys/class/tty/里面也可以看到这个属性
:/sys/class/tty/ttyUSB0/device$ ls
driver i2c-1 i2c_clk port_number spi_master tty
event_char i2c-2 latency_timer power subsystem uevent
只要写这个文件就可以改变设备的i2c频率和打开设备一样提供2个函数设置频率由于整个设备都是一个频率所以这里不区分interface如果需要区分interface或者每个i2c独立设置频率则需要修改ftdi_sio_i2c.c里面频率部分
int ftdi_sio_i2c::set_freq(int pid, int n, int freq)
int ftdi_sio_i2c::set_freq(char *serial_number, int freq)
这里有一个问题如果改动过频率读写就会提示错误ACK错误不知道原因所以这个设置频率的方式有问题。
6 验证
使用FT4232H模块验证。
6.1 遍历设备
ftdi_sio_i2c i2c;
i2c.find_devices();i2c.free_devices();
打印结果
$ sudo ./ftdi_sio_app
serial number:FT9PQ9R2
Found:i2c-1
Found:i2c-2
Found:i2c-3
Found:i2c-4
6.2 开关设备
打开FT4232H的第一个I2C。
fd i2c.open_i2c((char *)FT9PQ9R2, 0, 0);i2c.close_i2c(fd);
打印结果
$ sudo ./ftdi_sio_app
serial number:FT9PQ9R2
Found:i2c-1
Found:i2c-2
Found:i2c-3
Found:i2c-4
open:/dev/i2c-1
6.3 读写测试
将FT4232H的AD4和AD5分别接到EEPROM的SCL和SDA脚上。定义EEPROM的地址和数据长度
#define EEPROM_ADDR_WIDTH 16
#define I2C_LEN 16
写入数据随机产生然后再写入EEPROM
printf(i2c write data:\n);
srand(time(NULL));
for(int i 0; i (int)sizeof(wr_buf); i) {wr_buf[i] (unsigned char)rand();
}
printf( 0 1 2 3 4 5 6 7 8 9 a b c d e f);
for(int i 0; i I2C_LEN; i) {if((i % 16) 0) {printf(\n%2x: , i);}printf( %2x , wr_buf[i]);
}
printf(\n);ret i2c-write_bytes(fd, 0x50, EEPROM_ADDR_WIDTH, 0, wr_buf, sizeof(wr_buf));
if(ret 0) {printf(write eeprom fail\n);return;
}
再从EEPROM读出这笔数据并比较判断
for(int i 0; i I2C_LEN; i) {rd_buf[i] 0;
}
ret i2c-read_bytes(fd, 0x50, EEPROM_ADDR_WIDTH, 0, rd_buf, sizeof(rd_buf));
if(ret 0) {printf(read eeprom fail\n);return;
}
printf(Read value from register\n);
printf( 0 1 2 3 4 5 6 7 8 9 a b c d e f);
for(int i 0; i I2C_LEN; i) {if((i % 16) 0) {printf(\n%2x: , i);}printf( %2x , rd_buf[i]);
}
printf(\n);
测试速度可以看到速度大约是400KHz以下。