ftdi_sio应用学习笔记 4 - I2C

目录

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以下。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/61641.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

04 - 尚硅谷 - MQTT 客户端编程

1.在Java中使用MQTT 1.1 Eclipse Paho Java Client 具体步骤&#xff1a; 1、创建一个Spring Boot项目&#xff0c;添加如下依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId>…

shell第一次作业

要求&#xff1a; 通过shell脚本分析部署nginx网络服务 1.接收用户部署的服务名称 2.判断服务是否安装 ​ 已安装&#xff1b;自定义网站配置路径为/www&#xff1b;并创建共享目录和网页文件&#xff1b;重启服务 ​ 没有安装&#xff1b;安装对应的软件包 3.测试 判断服务是…

1+X应急响应(网络)病毒与木马的处置:

病毒与木马的处置&#xff1a; 病毒与木马的简介&#xff1a; 病毒和木马的排查与恢复&#xff1a;

服务器数据恢复—热备盘未激活导致硬盘掉线的raid5阵列崩溃的数据恢复案例

服务器数据恢复环境&#xff1a; 某品牌X3850服务器中有一组由数块SAS硬盘组建的RAID5阵列&#xff0c;该阵列中有一块盘是热备盘。操作系统为linux redhat&#xff0c;上面跑着一个基于oracle数据库的oa。 服务器故障&#xff1a; 服务器raid5阵列中有一块硬盘离线&#xff0…

Eclipse 创建Dynamic web project项目-配置Tomcat服务器

1、new——>project: 2、选择web的 Dynamic web project项目: 3、 项目命名&#xff0c;选择new runtime(没有部署过web项目&#xff0c;一般tartget runtime选项里面是空的)&#xff1a; 4、完成1、2的路径选择&#xff1a; 5、完成两个选项操作后&#xff0c;点击finish &…

✅✅✅【Vue.js】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本

api.js //封装ajax方法 import $g from "../sg";//vue项目使用 import $ from jquery;//(提示&#xff1a;原生开发页面请前往https://jquery.com下载最新版jQuery) import { Message } from "element-ui";//element项目使用 // import axios from "…

理解折半查找法

理解折半查找法&#xff1a;高效的查找算法 折半查找法&#xff08;又称二分查找法&#xff09;是一种高效的查找算法&#xff0c;用于查找一个已排序数组中的目标元素。与线性查找方法不同&#xff0c;折半查找每次都将搜索范围减半&#xff0c;从而大幅提升查找效率。本文将详…

解决前端页面报错:Not allowed to load local resource

在前后端分离项目中&#xff0c;在前端页面里使用file://的绝对路径访问本地图片&#xff0c;在加载图片的 时候会报出Not allowed to load local resource 的错误。 这是因为浏览器出于安全因素&#xff0c;禁止通过绝对路径访问图片&#xff0c;需要通过虚拟路径进行访问。 …

VM虚拟机装MAC后无法联网,如何解决?

✨在vm虚拟机上&#xff0c;给虚拟机MacOS设置网络适配器。选择NAT模式用于共享主机的IP地址 ✨在MacOS设置中设置网络 以太网 使用DHCP ✨回到本地电脑上&#xff0c;打开 服务&#xff0c;找到VMware DHCP和VMware NAT&#xff0c;把这两个服务打开&#xff0c;专一般问题就…

力扣 LeetCode 236. 二叉树的最近公共祖先(Day10:二叉树)

解题思路&#xff1a; 后序遍历 注意&#xff1a; p和q其中一个就是它们的公共祖先的情况也考虑到了&#xff0c;假设q是公共祖先&#xff0c;遇到q就直接返回&#xff0c;相当于是下面一边为空&#xff0c;一边不为空的情况&#xff0c;返回不为空就一边即可 class Solutio…

前端速通(HTML)

1. HTML HTML基础&#xff1a; 什么是HTML&#xff1f; 超文本&#xff1a; "超文本"是指通过链接连接不同网页或资源的能力。HTML支持通过<a>标签创建超链接&#xff0c;方便用户从一个页面跳转到另一个页面。 标记语言&#xff1a; HTML使用一组预定义的标签…

python语言基础-5 进阶语法-5.4 正则表达式

声明&#xff1a;本内容非盈利性质&#xff0c;也不支持任何组织或个人将其用作盈利用途。本内容来源于参考书或网站&#xff0c;会尽量附上原文链接&#xff0c;并鼓励大家看原文。侵删。 5.4 正则表达式 5.4.1 正则表达式 正则表达式的概念&#xff1a; 正则表达式是用来…

论文阅读——Performance Evaluation of Passive Tag to Tag Communications(一)

文章目录 摘要一、互耦对监听器标签输入阻抗的影响A. 无限细偶极子互阻抗的理论研究B. 电细偶极子的情况&#xff1a;理论与模拟C. 印刷偶极子的情况&#xff1a;电磁模拟与测量 二、T2T 通信系统的性能评估总结 论文来源&#xff1a;https://ieeexplore.ieee.org/document/970…

Palo Alto Networks PAN-OS身份认证绕过漏洞复现(CVE-2024-0012)

0x01 产品描述: PAN-OS 是运行 Palo Alto Networks 下一代防火墙的软件。通过利用 PAN-OS 本机内置的关键技术(App-ID、Content-ID、设备 ID 和用户 ID),可以在任何时间、任何地点完全了解和控制所有用户和设备中正在使用的应用程序。0x02 漏洞描述: PAN-OS 设备管理 Web …

使用ENSP实现静态路由

一、双路由器静态路由 1.项目拓扑 2.项目实现 (1)路由器AR1配置 进入系统试图 sys将路由器命名为R1 sysname R1进入g0/0/0接口 int g0/0/0将g0/0/0接口IP地址配置为1.1.1.1/24 ip address 1.1.1.1 24进入g0/0/1接口 int g0/0/1将g0/0/1接口IP地址配置为192.168.1.1/24 ip ad…

版本控制和idea简体中文教程

版本控制是一种管理软件开发过程中代码变更的技术。它允许多个开发者协作编辑同一个项目&#xff0c;同时跟踪每个变更的历史记录&#xff0c;以便在需要时恢复到之前的版本。版本控制系统&#xff08;Version Control System, VCS&#xff09;是实现版本控制的具体工具。 16.…

自制游戏:监狱逃亡

第一个游戏&#xff0c;不喜勿喷&#xff1a; #include<bits/stdc.h> #include<windows.h> using namespace std; int xz; int ruond_1(int n){if(xz1){printf("撬开了&#xff0c;但站在你面前的是俄罗斯内务部特种部队的奥摩大帝&#xff0c;你被九把加特…

Claude3.5-Sonnet和GPT-4o怎么选(附使用链接)

随着人工智能模型的不断进化&#xff0c;传统的评估标准已经逐渐变得陈旧和不再适用。以经典的“喝水测试”为例&#xff0c;过去广泛应用于检测模型能力&#xff0c;但现如今即便是国内的一些先进模型&#xff0c;也能够轻松答对这些简单的问题。因此&#xff0c;我们亟需引入…

uniapp+vue3+ts H5端使用Quill富文本插件以及解决上传图片反显的问题

uniappvue3ts H5端使用Quill富文本插件以及解决上传图片反显的问题 1.在项目中安装Quill npm i quill1.3.72.需要显示富文本的页面完整代码 <template><view><div ref"quillEditor" style"height: 65vh"></div></view> &…

QML —— 3种等待指示控件(附源码)

效果如下 说明 BusyIndicator应用于指示在加载内容或UI被阻止等待资源可用时的活动。BusyIndicator类似于一个不确定的ProgressBar。两者都可以用来指示背景活动。主要区别在于视觉效果,ProgressBar还可以显示具体的进度(当可以确定时)。由于视觉差异,繁忙指示器和不确定的…