ftdi_sio应用学习笔记 3 - GPIO

目录

1. 查找gpiochip

2. 打开GPIO

2.1 libgpiod库方式

2.2 系统方式

3. 关闭GPIO

3.1 libgpiod库方式

3.2 系统方式

4. 设置方向

4.1 libgpiod库方式

4.2 系统方式

5. 设置GPIO电平

5.1 libgpiod库方式

5.2 系统方式

6. 读取GPIO电平

6.1 libgpiod库方式

6.2 系统方式

7. 验证

7.1 测试读写

7.2 速度测试

7.3 测试MPSSE GPIO


对于FTDI全速设备普遍有CBUS管脚,例如FT230X,有4路CBUS型GPIO。

通过官方工具FT_PROG将对应的CBUS改为GPIO的模式,否则在Linux中是看不到对应的GPIO的,例如将CBUS2和CBUS3改为GPIO。

然后在Linux里面进入root模式,可以先看一下/sys/bus/gpio/devices下有几个gpiochip

:/sys/bus/gpio# ls devices
gpiochip0

这里是gpiochip0,然后运行gpioinfo 0查看该chip的gpio使用信息。

:/sys/bus/gpio# gpioinfo 0
gpiochip0 - 12 lines:
	line   0:      unnamed       kernel   input  active-high [used]
	line   1:      unnamed       kernel   input  active-high [used]
	line   2:      unnamed       unused   input  active-high 
	line   3:      unnamed       unused   input  active-high 
	line   4:      unnamed       unused   input  active-high 
	line   5:      unnamed       unused   input  active-high 
	line   6:      unnamed       unused   input  active-high 
	line   7:      unnamed       unused   input  active-high 
	line   8:      unnamed       unused   input  active-high 
	line   9:      unnamed       unused   input  active-high 
	line  10:      unnamed       unused   input  active-high 
	line  11:      unnamed       unused   input  active-high 

对于X系列芯片来说,一共可以支持12个GPOI,而FT230X只有前面4个有效。这里的line0和line1都已经被使用(因为FT_PROG设置为非GPIO模式了)。

1. 查找gpiochip

首先要找到FTDI设备对应的gpiochip,和串口一样,先在USB设备中找到gpiochip关键字。例如下面的信息:

:/sys/bus/usb/devices/1-2:1.0# ls
authorized          bInterfaceSubClass  gpio       subsystem
bAlternateSetting   bNumEndpoints       gpiochip0  supports_autosuspend
bInterfaceClass     driver              interface  ttyUSB0
bInterfaceNumber    ep_02               modalias   uevent
bInterfaceProtocol  ep_81               power

可以看到ttyUSB0和gpiochip0,所以只需要将查找uart的方式中的信息改为“gpiochip"就可以了。将uart部分改为gpio,注意GPIOCHIP最大只会有2个(FT2232H或FT4232H)。

#define FTDI_DEVICE_MAX_GPIOCHIP    2
#define FTDI_DEVICE_MAX_GPIO        20
struct ftdi_gpio_info {
    struct ftdi_gpio_info *next;
    char gpio_name[FTDI_DEVICE_MAX_GPIOCHIP][12];  //max gpio: 2(FT2232H/FT4232H), max string: "gpiochip999"
    int base[FTDI_DEVICE_MAX_GPIOCHIP];
    int pid;
    int vid;
    char serial_number[64];
};

将串口查找设备对比"ttyUSB"关键字的部分改为"gpiochip"

while ((gpiochip_entry = readdir(gpiochip_dir)) != NULL) {
    //printf("        entry%s\n", gpiochip_entry->d_name);
    if (strstr(gpiochip_entry->d_name, "gpiochip") != NULL) {  
        printf("Found: %s\n", gpiochip_entry->d_name);  
        sprintf(dev_list->gpio_name[interface], "%s", gpiochip_entry->d_name);
    }
}

例如FT4232H的设备可以找到2个gpiochip设备

Found: gpiochip0
Found: gpiochip1

对于GPIO还需要获取在Linux系统中的起始编号。可以获取到gpiochip的目录内的gpio目录,里面可以获取到起始编号。例如FT4232H的信息如下:

:/sys/bus/usb/devices/2-1/2-1:1.0/gpio$ ls
gpiochip512
:/sys/bus/usb/devices/2-1/2-1:1.1/gpio$ ls
gpiochip520

 FT4232H的2路MPSSE通道中GPIO对应的起始编号为512和520。

while ((gpiochip_entry = readdir(gpiochip_dir)) != NULL) {
    //printf("        entry%s\n", gpiochip_entry->d_name);
    if (strstr(gpiochip_entry->d_name, "gpiochip") != NULL) {  
         DIR *gpio_dir;
         struct dirent *gpio_entry;
                    
         sprintf(dev_list->gpio_name[interface], "%s", gpiochip_entry->d_name);
         sprintf(name_path, "/sys/bus/usb/devices/%s:1.%d/gpio", entry->d_name, interface);
         //printf("gpio folder:%s\n", name_path);
         gpio_dir = opendir(name_path);
         while ((gpio_entry = readdir(gpio_dir)) != NULL) {
             if (strstr(gpio_entry->d_name, "gpiochip") != NULL) { 
                 //printf("gpio:%s\n", gpio_entry->d_name);
                 sscanf(gpio_entry->d_name, "gpiochip%d", &dev_list->base[interface]);
             }
        }
        closedir(gpio_dir);
        printf("Found: %s, base=%d\n", gpiochip_entry->d_name, dev_list->base[interface]);  
    }
}

2. 打开GPIO

操作gpio一般有2种方式,一种是通过libgpiod库操作,另外一种是通过直接通过/sys/class/gpio文件系统操作。设计2种参数打开设备,一种是通过PID过滤,如果有相同PID的多个设备,通过参数n指定。

int ftdi_sio_gpio::open_gpio(int pid, int n, int gpiochip, int gpio_num)

另外一种就是通过系列号,因为序列号是唯一的,所以不需要参数n

int ftdi_sio_gpio::open_gpio(char *serial_number, int gpiochip, int gpio_num)

参数:

pid - 设备的pid号

n - 第n个相同pid号设备

gpiochip - 需要打开的第几个gpiochip

gpio_num - 需要打开该gpiochip里面第几个gpio

serial_number - 序列号字符串

2.1 libgpiod库方式

gpiod_chip_open打开gpiochip(即整个芯片),而gpiod_chip_get_line打开的对应的gpio口。

char name_path[buffer_size];
sprintf(name_path, "/dev/%s", dev_list->gpio_name[gpiochip]);
printf("Open device: %s\n", name_path);
if(chip[gpiochip] == NULL)
    chip[gpiochip] = gpiod_chip_open(name_path); 
if(gpio[gpio_num] == NULL)
    gpio[gpio_num] = gpiod_chip_get_line(chip[gpiochip], gpio_num);
fd = gpiochip * FTDI_DEVICE_MAX_GPIO + gpio_num;

这里返回gpio的内部编号fd,后面其他操作需要把这个编号传回。

2.2 系统方式

系统方式只需要把对应的gpio暴露处理,将gpio编号写入export文件

char buffer[4];
int len;
fd = open(GPIO_EXPORT_FILE, O_WRONLY);
if (fd < 0) {
    perror("gpio/export open");
    return -1;
}
len = snprintf(buffer, sizeof(buffer), "%d", dev_list->base[gpiochip] + gpio_num);
if (write(fd, buffer, len) < 0) {
    perror("gpio/export write");
}
close(fd);
fd = dev_list->base[gpiochip] + gpio_num;

返回实际的gpio编号,所以这里加上起始编号。

3. 关闭GPIO

void ftdi_sio_gpio::close_gpio(int fd)

参数:

fd - GPIO编号 (open返回的值)

3.1 libgpiod库方式

通过gpiod_line_release和gpiod_chip_close释放GPIO和GPIOCHIP。如果所有的GPIO都释放了才释放掉GPIOCHIP。而参数fd是之前打开时返回的值。

int gpiochip = fd / FTDI_DEVICE_MAX_GPIO;
int gpio_num = fd % FTDI_DEVICE_MAX_GPIO;
int i;

gpiod_line_release(gpio[gpio_num]);
gpio[gpio_num] = NULL;
for(i = 0; i < FTDI_DEVICE_MAX_GPIO; i++) {
    if(gpio[i] != NULL)
        return;
}
gpiod_chip_close(chip[gpiochip]);
chip[gpiochip] = NULL;

3.2 系统方式

把对应的gpio隐藏,即gpio编号写入unexport文件。

int pin = fd;
fd = open(GPIO_UNEXPORT_FILE, O_WRONLY);
if (fd >= 0) {
    char buffer[4];
    int len;
    len = snprintf(buffer, sizeof(buffer), "%d", pin);
    if(write(fd, buffer, len) < 0) {
        perror("gpio/unexport");
    }
    close(fd);
}

4. 设置方向

int ftdi_sio_gpio::set_direction(int fd, int direction, int default_value)

参数说明

fd - GPIO编号(open返回的值)

direction - 方向,0表示输出,1表示输入。

default_value - 当设置为输出时,默认的电平,0表示低电平,1表示高电平。 

返回值:返回0表示成功,反之为失败。

4.1 libgpiod库方式

通过API函数gpiod_line_request_output和gpiod_line_request_input设置方向。

sprintf(name, "gpio%d", fd);
if(direction == 0) {
    gpiod_line_request_output(gpio[fd % FTDI_DEVICE_MAX_GPIO], name, default_value);
} else {
    gpiod_line_request_input(gpio[fd % FTDI_DEVICE_MAX_GPIO], name);
}

4.2 系统方式

通过写入字符串”out"或“in"到属性文件direction设置GPIO方向。

int pin = fd;
int ret = 0;
snprintf(name, sizeof(name), GPIO_PIN_DIR(pin));
fd = open(name, O_WRONLY);
if (fd < 0) {
    perror("gpio/direction");
    return -1;
}
if(direction == 0) {
    ret = write(fd, "out", strlen("out"));
} else {
    ret = write(fd, "in", strlen("in"));
}
close(fd);

5. 设置GPIO电平

int ftdi_sio_gpio::set_value(int fd, int value)

参数:

fd - GPIO编号(open返回的值)

value - 0表示低电平,1表示高电平

返回0表示设置成功。

5.1 libgpiod库方式

通过gpiod_line_set_value设置。

if(value > 0) {
    return gpiod_line_set_value(gpio[fd % FTDI_DEVICE_MAX_GPIO], 1);
} else {
    return gpiod_line_set_value(gpio[fd % FTDI_DEVICE_MAX_GPIO], 0);
}

5.2 系统方式

写0或1到文件value即可。

char path[buffer_size];
int pin = fd;
int ret = 0;
snprintf(path, sizeof(path), GPIO_PIN_VALUE(pin));
fd = open(path, O_WRONLY);
if (fd < 0) {
    perror("gpio/write open");
    return -1;
}
if(value > 0) {
    ret = write(fd, "1", strlen("1"));
} else {
    ret = write(fd, "0", strlen("0"));
}
close(fd);

6. 读取GPIO电平

int ftdi_sio_gpio::get_value(int fd)

参数:

fd - GPIO编号(open返回的值)

返回GPIO电平,0表示低电平,1表示高电平。

6.1 libgpiod库方式

通过函数gpiod_line_get_value获取GPIO电平。

return gpiod_line_get_value(gpio[fd % FTDI_DEVICE_MAX_GPIO]);

6.2 系统方式

和设置类似,从属性文件value中读入数值即可。

char path[buffer_size];
char value_str[3];
int pin = fd;
snprintf(path, sizeof(path), GPIO_PIN_VALUE(pin));
//printf("gpio/read open %s\n", path);
fd = open(path, O_RDONLY);
if (fd < 0) {
    perror("gpio/read open");
    return -1;
}
if(read(fd, value_str, sizeof(value_str)) < 0) {
    perror("gpio/read read");
}
close(fd);
return atoi(value_str);

7. 验证

先遍历设备

ftdi_sio_gpio gpio;
int fd1, fd2;
gpio.find_devices();

打开和关闭gpio

fd1 = gpio.open_gpio(0x6015, 0, 0, 2);  //CBUS2
fd2 = gpio.open_gpio((char *)"FTWJFB9L", 0, 3);  //CBUS3

//测试部分

gpio.close_gpio(fd1);
gpio.close_gpio(fd2);
gpio.free_devices();

7.1 测试读写

将FT230X的CBUS2和CBUS3短接,CBUS2输出,CBUS3输入,

gpio->set_direction(fd1, 0, 0);
gpio->set_direction(fd2, 1, 0);

通过CBUS2发送字符串,然后CBUS3读入IO数据。

    char wr_data[] = "GPIO Data\n";
    char rd_data[128];

    for(int i = 0; i < (int)sizeof(wr_data); i++)
    {
        char level = wr_data[i];
        printf("%2x ", level);
        int rd;
        rd_data[i] = 0;
        for(int j = 0; j < 8; j++)
        {
            gpio->set_value(fd1, level & 0x01);
            level >>= 1;
            rd = gpio->get_value(fd2);
            if(rd > 0)
                rd = 1;
            else
                rd = 0;
            rd_data[i] |= rd << j;
        }
        printf("= %2x\n", rd_data[i]);
    }
    rd_data[sizeof(wr_data) + 1] = 0;
    printf("\nGPIO Read : %s\n", rd_data);

7.2 速度测试

测试CBUS2输出频率,在while(1)循环中交替输出。

while(1) {
    gpio->set_value(fd1, i & 0x01);
    i++;
}

使用gpiod库的方式可以测试到频率为60Hz左右,而系统方式频率也差不多时60Hz。

7.3 测试MPSSE GPIO

使用FT4232H测试,将AD0短接BD1(即TXD0接RXD1),速度方面,最快可以做到5KHz左右。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序登录注册页面设计(小程序项目)

需求 在微信小程序设计并实现登录页面&#xff0c;并填写相关登录注册函数 实现效果 代码实现 html代码 <view class"top" style"border-bottom-style: none;background-color:#FF8C69;"><!-- <view class"back" bind:tap"…

神经网络(系统性学习三):多层感知机(MLP)

相关文章&#xff1a; 神经网络中常用的激活函数 神经网络&#xff08;系统性学习一&#xff09;&#xff1a;入门篇 神经网络&#xff08;系统性学习二&#xff09;&#xff1a;单层神经网络&#xff08;感知机&#xff09; 多层感知机&#xff08;MLP&#xff09; 多层感…

Android 14 screenrecord录制视频失败的原因分析

文章目录 1. 权限问题2. 存储空间不足3. 命令被中断4. 目标路径问题5. Android 14 的新限制6. 文件系统同步问题7. 录制失败检查步骤总结&#xff1a; 在 Android 14 系统上&#xff0c;使用 screenrecord 命令录制视频后&#xff0c;生成的文件大小为 0&#xff0c;可能的原因…

Uniapp 简单配置鸿蒙

Uniapp 简单配置鸿蒙 前言下载并配置鸿蒙IDEHbuilder X 配置基本的信息生成相关证书登录官网获取证书IDE配置证书添加调试设备可能出现的问题前言 如今鸿蒙的盛起,作为多端开发的代表也是开始兼容鸿蒙应用的开发,接下来我将介绍如何在uniapp中配置鸿蒙。 注意:hbuilder X的…

git使用(一)

git使用&#xff08;一&#xff09; 为什么学习git?两种版本控制系统在github上创建一个仓库&#xff08;repository&#xff09;windows上配置git环境在Linux上配置git环境 为什么学习git? 代码写了好久不小心删了&#xff0c;可以使用git防止&#xff0c;每写一部分代码通…

C# 数据结构之【树】C#树

以二叉树为例进行演示。二叉树每个节点最多有两个子节点。 1. 新建二叉树节点模型 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace DataStructure {class TreeNode{public int Data { get;…

HarmonyOs鸿蒙开发实战(20)=>一文学会基础使用组件导航Navigation

敲黑板&#xff0c;以下是重点技巧。文章末尾有实战项目效果截图及代码截图可参考 1.概要 Navigation是路由导航的根视图容器Navigation组件主要包含​导航页&#xff08;NavBar&#xff09;和子页&#xff08;NavDestination&#xff09;&#xff0c;导航页不存在页面栈中&am…

python从入门到精通:pyspark实战分析

前言 spark&#xff1a;Apache Spark是用于大规模数据&#xff08;large-scala data&#xff09;处理的统一&#xff08;unified&#xff09;分析引擎。简单来说&#xff0c;Spark是一款分布式的计算框架&#xff0c;用于调度成本上千的服务器集群&#xff0c;计算TB、PB乃至E…

Ubuntu从入门到精通(二)远程和镜像源配置齐全

Ubuntu从入门到精通(二) 1 常见操作配置 1.1 英文语言配置 1.1.1 打开设置 1.1.2 设置语言为英文 1.1.3 重启生效 1.1.4 再次进入,选择更新名字 1.1.5 再次进入,发现已经变成了英文 1.2 输入法配置 1.3 rustdesk安装 1.3.1 Windows系统配置 登陆:https://github.com…

HTML5拖拽API学习 托拽排序和可托拽课程表

文章目录 前言拖拽API核心概念拖拽式使用流程例子注意事项综合例子&#x1f330; 可拖拽课程表拖拽排序 前言 前端拖拽功能让网页元素可以通过鼠标或触摸操作移动。HTML5 提供了标准的拖拽API&#xff0c;简化了拖放操作的实现。以下是拖拽API的基本使用指南&#xff1a; 拖拽…

华为Ensp模拟器配置OSPF路由协议

目录 简介 实验步骤 Pc配置 路由器配置 OSPF配置 交换机配置 简介 开放式最短路径优先 (OSPF) 协议深度解析 简介 开放式最短路径优先&#xff08;Open Shortest Path First, OSPF&#xff09;是一种内部网关协议&#xff08;IGP&#xff09;&#xff0c;用于在自治系统…

【最新鸿蒙应用开发】——合理使用自定义弹框

自定义弹窗选型 合理选择不同的系统能力实现弹窗&#xff0c;有利于提升应用开发效率&#xff0c;实现更好的功能需求&#xff0c;因此了解自定义弹窗的选型和差异非常重要。在应用开发中&#xff0c;为了选择出合适的弹窗选型&#xff0c;从使用场景上&#xff0c;需要重点关…

自动化爬虫Selenium

自动化爬虫Selenium 这篇文章, 我们将要学习自动化爬虫的知识啦。 目录 1.Selenium的基本操作 2.用Selenuim获取数据 3.当当网数据获取 4.实战 一、Selenium的基本操作 首先, 我们在使用Selenium之前, 需要做两件事情。第一件事情, 就是安装第三方库, 第二件事情, 就是…

开源可视化工具对比:JimuReport VS DataEase

在当今数据驱动的时代&#xff0c;高效的数据可视化工具成为企业洞察业务、做出决策的关键利器。那对于企业来讲如何选择BI产品呢&#xff1f; 在开源可视化工具的领域中&#xff0c;JimuReport和DataEase 以其独特的优势脱颖而出&#xff0c;究竟谁更胜一筹呢&#xff1f;让我…

Jenkins的环境部署

day22 回顾 Jenkins 简介 官网Jenkins Jenkins Build great things at any scale The leading open source automation server, Jenkins provides hundreds of plugins to support building, deploying and automating any project. 用来构建一切 其实就是用Java写的一个项目…

Ubuntu22.04配置强化学习环境及运行相关Demo

什么是强化学习 强化学习&#xff08;Reinforcement Learning&#xff0c;简称 RL&#xff09;是机器学习中的一个重要分支&#xff0c;属于一种基于试错机制的学习方法。它通过让智能体&#xff08;Agent&#xff09;与环境&#xff08;Environment&#xff09;进行交互&…

AI 写作(一):开启创作新纪元(1/10)

一、AI 写作&#xff1a;重塑创作格局 在当今数字化高速发展的时代&#xff0c;AI 写作正以惊人的速度重塑着创作格局。AI 写作在现代社会中占据着举足轻重的地位&#xff0c;发挥着不可替代的作用。 随着信息的爆炸式增长&#xff0c;人们对于内容的需求日益旺盛。AI 写作能够…

丹摩征文活动 | AI创新之路,DAMODEL助你一臂之力GPU

目录 前言—— DAMODEL&#xff08;丹摩智算&#xff09; 算力服务 直观的感受算力提供商的强大​ 平台功能介绍​ 镜像选择 云磁盘创建 总结 前言—— 只需轻点鼠标,开发者便可拥有属于自己的AI计算王国 - 从丰富的GPU实例选择,到高性能的云磁盘,再到预配置的深度学习…

echarts的图例换行并对齐

现状&#xff1a; 期望&#xff1a; 实现方式&#xff1a; 通过对legend的formatter和textStyle组合设置宽度来实现&#xff0c;代码如下 这里会出现一个问题&#xff0c;发现设置了width没有效果&#xff0c;刚开始以为是宽度给的不够&#xff0c;然后发现并不是&#xff0c…

go-zero(二) api语法和goctl应用

go-zero api语法和goctl应用 在实际开发中&#xff0c;我们更倾向于使用 goctl 来快速生成代码。 goctl 可以根据 api快速生成代码模板&#xff0c;包括模型、逻辑、处理器、路由等&#xff0c;大幅提高开发效率。 一、构建api demo 现在我们通过 goctl 创建一个最小化的 HT…