ftdi_sio应用学习笔记 4 - I2C

news2026/2/16 3:18:48

目录

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.coloradmin.cn/o/2245914.html

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

相关文章

实践篇:青果IP助理跨境电商的高效采集

写在前面&#xff1a; 近年来&#xff0c;跨境电商行业迅速崛起&#xff0c;成为全球贸易的重要组成部分。据市场调研机构Statista数据显示&#xff0c;2024年全球跨境电商市场规模预计将突破5万亿美元&#xff0c;覆盖数十亿消费者。跨境电商的竞争日益激烈&#xff0c;商家不…

电子应用设计方案-16:智能闹钟系统方案设计

智能闹钟系统方案设计 一、系统概述 本智能闹钟系统旨在为用户提供更加个性化、智能化和便捷的闹钟服务&#xff0c;帮助用户更有效地管理时间和起床。 二、系统组成 1. 微控制器 - 选用低功耗、高性能的微控制器&#xff0c;如 STM32 系列&#xff0c;负责整个系统的控制和数据…

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

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

Leetcode448. 找到所有数组中消失的数字(HOT100)+Leetcode139. 单词拆分(HOT100)

链接 链接2 这两道题略微有点难&#xff0c;其中第一道题我自己解出来了&#xff0c;还补充了一个更好的解法&#xff0c;在空间上做了优化。 第二道题看了别人的题解&#xff0c;我正在努力理解。 题目一&#xff1a; 题意&#xff1a;为什么有n个元素&#xff0c;但是还有…

通过轻易云平台实现聚水潭数据高效集成到MySQL的技术方案

聚水潭数据集成到MySQL的技术案例分享 在本次技术案例中&#xff0c;我们将详细探讨如何通过轻易云数据集成平台&#xff0c;将聚水潭的数据高效、可靠地集成到MySQL数据库中。具体方案为“聚水谭-店铺查询单-->BI斯莱蒙-店铺表”。这一过程不仅需要处理大量数据的快速写入…

华为云容器监控平台

首先搜索CCE,点击云容器引擎CCE 有不同的测试&#xff0c;生产&#xff0c;正式环境 工作负载--直接查询服务名看监控 数据库都是走的一个 Redis的查看

机器学习系列----关联分析

目录 1. 关联分析的基本概念 1.1定义 1.2常用算法 2.Apriori 算法的实现 2.1 工作原理 2.2 算法步骤 2.3 优缺点 2.4 时间复杂度 2.5实际运用----市场购物篮分析 3. FP-Growth 算法 3.1 工作原理 3.2 算法步骤 3.3 优缺点 3.4 时间复杂度 3.5实际运用——网页点…

前端面试vue篇:Vue2 和 Vue3 在设计和性能上有显著区别

Vue3 相对于 Vue2 的主要改进和性能提升体现在以下几个关键领域 1.响应式系统&#xff1a; (1)Vue2 使用 Object.defineProperty 遍历对象的所有属性来实现响应式&#xff0c;这在大型应用中可能导致性能瓶颈&#xff0c;尤其是在组件初次渲染和大量数据变化时。 (2)Vue3 引入了…

Cisco Catalyst 9800-40 Wireless Controller配置修改以及状态信息查看操作指引

1、查看AP的个性化信息、如IP地址、序列号、射频信息、信道信息、干扰等等AP信息 点击Monitoring---->AP Statistics---->找到想看的AP&#xff0c;点击进去查看 可以看得到IP地址、AP型号、POWER的状态、版本号、boot版本号、UP time时间和LED状态灯等等 继续往下拉可以…

k8s集群加入node节点为ubuntu 22.04

文章目录 1.环境准备1.1 关闭无用服务1.2 环境和网络1.3 apt源1.4 系统优化 2. 装containerd3. 接入k8s集群3.1 kubelet、kubeadm、kubectl安装3.2 缺少一个镜像3.3 接入k8s集群 4. 一些相关问题 1.环境准备 rootcto-gpu-pro-n01:~# lsb_release -a No LSB modules are availa…

低速接口项目之串口Uart开发(七)——如何在FPGA项目中实现自适应波特率串口功能

本节目录 一、自适应波特率串口 二、自适应波特率串口设计思路 三、自适应波特率串口测试验证 四、往期文章链接本节内容 一、自适应波特率串口 串口波特率Baud,具体定义此处不再进行描述&#xff0c;常用的波特率数值有115200、57600、38400、19200、9600等&#xff0c;FPG…

JS中的正则表达式简要梳理

文章目录 一、结构1、匹配模式2、字符组3、量词4、贪婪匹配和惰性匹配5、多选分支6、匹配模式关键词 二、位置1、位置锚点 三、括号分组1、分组2、分支结构3、引用分组4、括号嵌套 四、回溯模式1、回溯匹配 正则表达式&#xff08;Regular Expression&#xff09;‌是一种强大的…

【风水】-- 买车和用车的风水知识

目录 一、阴阳与车的风水 1. 车的属性&#xff1a;阴阳平衡 2. 车主与车的阴阳互补 二、五行与车的风水 1. 车的颜色与五行 2. 车主的五行与车的五行匹配 3. 五行相生相克的应用 三、八卦与车的风水 1. 车位选择与八卦方位 2. 车内布置与八卦 四、总结 结合阴阳、五…

深度剖析Linux进程控制

文章目录 &#x1f355;前言&#x1f95d;进程创建fork 函数fork函数返回值写时拷贝fork 使用场景fork调用失败的原因 &#x1f965;进程终止进程退出码进程正常退出return 退出exit 函数_exit 函数return VS exit VS _exit 进程异常退出 &#x1f347;进程等待进程等待的必要性…

网络安全,文明上网(6)网安相关法律

列举 1. 《中华人民共和国网络安全法》&#xff1a; - 这是中国网络安全的基本法律&#xff0c;于2017年6月1日开始实施。该法律明确了网络运营者的安全保护义务&#xff0c;包括采取数据分类、重要数据备份和加密等措施。 2. 《中华人民共和国数据安全法》&#xff1a; …

IDEA2023 SpringBoot整合MyBatis(三)

一、数据库表 CREATE TABLE students (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(100) NOT NULL,age INT,gender ENUM(Male, Female, Other),email VARCHAR(100) UNIQUE,phone_number VARCHAR(20),address VARCHAR(255),date_of_birth DATE,enrollment_date DATE,cours…

【Vue】 npm install amap-js-api-loader指南

前言 项目中的地图模块突然打不开了 正文 版本太低了&#xff0c;而且Vue项目就应该正经走项目流程啊喂&#xff01; npm i amap/amap-jsapi-loader --save 官方说这样执行完&#xff0c;就这结束啦&#xff01;它结束了&#xff0c;我还没有&#xff0c;不然不可能记录这篇文…

八股文:适合背诵的基础知识进行整理

四、IO进程&#xff1a;IPC 1. 标准IO和系统IO的区别 标准文件IO 概念&#xff1a;C库中定义的一组用于输入输出的函数 特点 &#xff08;1&#xff09;有缓存机制&#xff0c;减少系统调用 &#xff08;2&#xff09;围绕文件流进行操作 &#xff08;3&#xff09;默认…

面试干货:软件测试常见面试题(附答案)

1、文档测试主要包含什么内容? 参考答案&#xff1a; 在国内软件开发管理中&#xff0c;文档管理几乎是最弱的一项&#xff0c;因而在测试工作中特别容易忽略文档测试也就不足为奇了。要想给用户提供完整的产品&#xff0c;文档测试是必不可少的。文档测试一般注重下面几个方…

查询 linux相关信息

文章目录 前言查询 linux相关信息1. 查询内存大小&#xff08;mem&#xff09;2. 统计 当前系统的 CPU 核心数3. 查看系统的操作系统信息4. Ubuntu 系统版本的版本号 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff…