赞
踩
目录
系统采用Ubuntu 22,X86 64。
新版本的Linux内核是自带hid-ft260.ko的。
sudo modprobe hid-ft260
然后执行lsmod查看:
- $ lsmod
- Module Size Used by
- hid_ft260 45056 0
- usbhid 77824 1 hid_ft260
- hid 180224 2 usbhid,hid_ft260
下载ft260驱动源代码
git clone https://github.com/MichaelZaidman/hid-ft260.git
进入hid-ft260,编译
make
编辑一下makefile文件,增加install部分:
- install:
- rmmod hid-ft260 || true
- insmod hid-ft260.ko || true
- mkdir -p /usr/lib/modules/$(shell uname -r)/kernel/drivers/hid/ || true
- cp -f ./hid-ft260.ko /usr/lib/modules/$(shell uname -r)/kernel/drivers/hid/ || true
- depmod -a
可以在shell里面先执行(每次拔插后都要运行这个脚本),这个脚本在hid-ft260的源文件夹里面。
- $ . ./setenv.sh
- sysfs_i2c_11
- sysfs_ttyFT0
返回2个设备,sysfs_i2c_xx表示i2c的接口,sysfs_ttyFTx表示uart的接口。这个接口类型由硬件跳线DCNF0和DCNF1决定,当前设置是0b11的配置。
注意,不管哪种配置,返回的都是2个接口,因为0b00和0b11是一样的,0b01和0b10是只有一个接口,要么是串口,要么是i2c.
查看2个接口的信息:
- $ echo $sysfs_i2c_11
- /sys/bus/hid/drivers/ft260/0003:0403:6030.0007
- $ echo $sysfs_ttyFT0
- /sys/bus/hid/drivers/ft260/0003:0403:6030.0008/tty
查看接口的所有属性:
- $ ls $sysfs_i2c_11
- chip_mode driver gpioa_func hid_over_i2c_en i2c_reset power_saving_en subsystem uart_mode
- clock gpio gpiochip0 i2c-11 modalias pwren_status suspend_status uevent
- clock_ctl gpio2_func gpiog_func i2c_enable power report_descriptor uart_dcd_ri
以chip_mode为例,查看该属性
- ls -l $sysfs_i2c_11/chip_mode
- -r--r--r-- 1 root root 4096 4月 28 15:37 /sys/bus/hid/drivers/ft260/0003:0403:6030.0007/chip_mode
这个属性只读,然后输出内容:
- $ cat $sysfs_i2c_11/chip_mode
- 3
对应DCNF0和DCNF1的设置0b11。
FT260的IO都是多功能,但是大部分是2个功能复用,当默认功能禁止时,自动变为GPIO,例如pin10可以是RXD和GPIOC,RXD是默认功能,当UART功能关闭时,这个管脚自动设置为GPIOC。FT260有3个特殊的多功能GPIO,他们是GPIO 2(pin 14), GPIOA (pin 7), and GPIOG (pin 27),它们可以通过eFuse、EEPROM或USB命令配置。
接口的所有属性中gpio2_func、gpioa_func、gpiog_func分别对应这3个GPIO的功能配置。默认功能是:
3个GPIO的功能如下:
GPIO2的功能设定值含义如下:
0 - GPIO2,1 - SUSPOUT_N, 2 - PWREN, 4 - TX_LED
GPIOA的功能设定值含义如下:
0 - GPIOA,3 - TX_ACTIVE, 4 - TX_LED
GPIOG的功能设定值含义如下:
0 - GPIOG,2 - PWREN,5 - RX_LED, 6 - BCD_DET
读取对应gpio的func结果如下:
- $ . ./setenv.sh
- sysfs_i2c_11
- sysfs_ttyFT0
- $ cat $sysfs_i2c_11/gpio2_func
- 1
- $ cat $sysfs_i2c_11/gpioa_func
- 3
- $ cat $sysfs_i2c_11/gpiog_func
- 6
配置其他参数,例如将pin 14配置为GPIO2
sudo bash -c "echo 0 > $sysfs_i2c_11/gpio2_func"
运行结果如下:
- $ sudo bash -c "echo 0 > $sysfs_i2c_11/gpio2_func"
- $ cat $sysfs_i2c_11/gpio2_func
- 0
其他的GPIO可以通过DCNF0、DCNF1 配置UART和I2C关闭来使能GPIO。
正常使用sysfs操作gpio是通过echo
命令将GPIO引脚导出到用户空间:
sudo bash -c "echo <GPIO_NUMBER> > $sysfs_i2c_11/gpio/export"
注意,gpio编号不是2,a,g,但是这样无效。要先控制GPIO,需要先将对应的GPIO配置为GPIO模式,默认是没有gpio的。
可以先列一下/sys/class/gpio/
- $ ls /sys/class/gpio
- export gpiochip512 unexport
gpiochip512, 偏移值是512,GPIO2的编号是514,GPIOA的编号为512+6=518, GPIOG的编号为512+12=525
- sudo bash -c 'echo 514 > /sys/class/gpio/export'
- sudo bash -c 'echo 518 > /sys/class/gpio/export'
- sudo bash -c 'echo 524 > /sys/class/gpio/export'
设置为输出
- sudo bash -c 'echo out > /sys/class/gpio/gpio514/direction'
- sudo bash -c 'echo out > /sys/class/gpio/gpio518/direction'
- sudo bash -c 'echo out > /sys/class/gpio/gpio524/direction'
输出高电平:
- sudo bash -c 'echo 1 > /sys/class/gpio/gpio514/value'
- sudo bash -c 'echo 1 > /sys/class/gpio/gpio518/value'
- sudo bash -c 'echo 1 > /sys/class/gpio/gpio524/value'
sudo bash -c 'echo <clk> > $sysfs_i2c_11/clock'
其中<clk>表示设置的频率,单位kHz,例如设置为400KHz
sudo bash -c 'echo 400 > $sysfs_i2c_11/clock'
不过这样写无效,没有提示错误。但是量频率一直是100KHz。从github的issue里面也有人问这个问题,需要在sysfs下找出USB总线上的ft260设备。
- $ ls /sys/bus/usb/devices
- 1-0:1.0 1-1:1.0 2-1 2-1:1.0 2-1.3 2-1.3:1.1 3-4 3-4:1.1 usb1 usb3
- 1-1 2-0:1.0 2-1.1 2-1.1:1.0 2-1.3:1.0 3-0:1.0 3-4:1.0 4-0:1.0 usb2 usb4
然后通过lsusb看一下ft260在哪个bus上
Bus 003 Device 026: ID 0403:6030 Future Technology Devices International, Ltd FT260
结合lsusb和ls /sys/bus/usb/devices的结果,bus3上有2个设备,3-0和3-4,一般3-0是hub本身,所以3-4应该是FT260
- $ cat /sys/bus/usb/devices/3-4/idProduct
- 6030
- $ cat /sys/bus/usb/devices/3-4/idVendor
- 0403
找到对应文件clock
- $ cat /sys/bus/usb/devices/3-4:1.0/0003:0403:6030.0023/clock
- 100
操作这个文件即可
- $ sudo bash -c 'echo 400 > /sys/bus/usb/devices/3-4:1.0/0003:0403:6030.0023/clock'
- $ cat /sys/bus/usb/devices/3-4:1.0/0003:0403:6030.0023/clock
- 400
对于UART功能,操作比较简单,和普通的串口使用一样,只是设备名变为ttyFT0了。例如使用cutecom就可以使用。
sudo apt-get install i2c-tools
如之前的信息,本例中i2c设备是i2c_11,所以通过i2cdetect探测设备
- $ sudo i2cdetect -y 11
- Warning: Can't use SMBus Quick Write command, will skip some addresses
- 0 1 2 3 4 5 6 7 8 9 a b c d e f
- 00:
- 10:
- 20:
- 30: -- -- -- -- -- -- -- --
- 40:
- 50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
- 60:
- 70:
这里的-y
选项用于关闭交互模式,这样在运行时不会显示警告信息。数字11
代表I2C总线的编号,根据你的系统配置,这个编号可能会有所不同。
输出结果是遍历所有的I2C地址,因为总线上只有一个AT24C02的设备,所以可以看到输出结果只有0x50这个设备。
假设I2C总线上接的设备是AT24C02(UMFT260EV1A板子上默认自带),EEPROM,设备地址为0x50。
$ sudo i2cdump -y 11 0x50 No size specified (using byte-data access) 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 00: 45 31 03 04 30 60 00 00 a0 32 46 54 44 49 2c 0a E1??0`..?2FTDI,? 10: 36 0c 00 00 60 20 cf be 00 00 00 00 00 00 00 00 6?..` ??........ 20: 40 00 00 00 00 00 00 00 00 00 00 00 0a 03 46 00 @...........??F. 30: 54 00 44 00 49 00 0c 03 46 00 54 00 32 00 36 00 T.D.I.??F.T.2.6. 40: 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0............... 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8 14 ..............??
sudo i2cget -y <bus> <device-address> <register-address> [w]
将<device-address>
替换为你要操作的设备的地址,<register-address>
替换为你要读取或写入的寄存器的地址,<value>
替换为你要写入的值(如果是写入操作的话)。[w]
表示值的位宽,可以是b
(字节)、w
(字)或l
(长整数),根据寄存器的大小来选择。
- $ sudo i2cget -y 11 0x50 0x10 b
- 0x36
sudo i2cset -y <bus> <device-address> <register-address> <value> [w]
参数含义等同读取。
- $ sudo i2cset -y 11 0x50 0x80 0x55 b
- $ sudo i2cget -y 11 0x50 0x80 b
- 0x55
- $ sudo i2cset -y 11 0x50 0x80 0x00 b
- $ sudo i2cget -y 11 0x50 0x80 b
- 0x00
前面的命令中,地址都是8位地址,如果是16位地址,需要通过i2ctransfer实现。
i2ctransfer [-f] [-y] [-v] [-V] [-a] I2CBUS DESC [DATA] [DESC [DATA]]...
-f: 强制模式,如果目标 I2C 设备未响应,则不等待超时并立即返回。
-y: 对于读取操作,如果读取的数据少于请求的字节数,则不会报错。
-v: 详细模式,显示更多输出信息。
-V
:版本信息,显示 i2ctransfer
的版本。
-a
:在每次 I/O 操作后,显示 I2C 总线的地址和值。
I2CBUS: 指定要使用的 I2C 总线。通常是一个数字,例如 0
、1
等,可以使用 ls /dev/i2c-*
来查看可用的 I2C 总线。
DES: 描述符,用于指定 I2C 消息的属性。例如写的格式:w[len]@[addr],读的格式:r[len]@[addr]。
DATA:可选,数据,一般写的时候需要写。
比如从16位地址0x0000读入4字节的命令:
sudo i2ctransfer -y 11 w2@0x50 0x00 0x00 r4
从16位地址0x0000写4字节0x11 0x22 0x33 0x44的命令:
sudo i2ctransfer -y 11 w6@0x50 0x00 0x00 0x11 0x22 0x33 0x44
如果是8位地址,只要把后面接的写地址部分改为1个字节就可以。
- sudo i2ctransfer -y 11 w1@0x50 0x00 r4
- sudo i2ctransfer -y 11 w5@0x50 0x00 0x11 0x22 0x33 0x44
sudo apt-get install i2c-tools libi2c-dev
- sudo modprobe i2c-core
- sudo modprobe i2c-dev
- sudo modprobe i2c-smbus
不知道为什么,lsmod只能看到i2c-smbus。
- #include <stdio.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <sys/ioctl.h>
- #include <linux/i2c-dev.h>
定义宏定义:
- #define DEVICE_DIR "/sys/bus/i2c/devices/"
- #define BUFFER_SIZE 256
- #define TARGET_NAME "FT260 usb-i2c bridge\n"
创建函数findFT260, 返回总线编号,这个函数只能找到第一个FT260设备,如果是多个FT260设备,需要增加辨别判断,可以通过libusb获取serial number识别。
- int findFT260(void)
- {
- DIR *dir;
- struct dirent *entry;
- char device_path[PATH_MAX];
- char name_path[PATH_MAX];
- char buffer[BUFFER_SIZE];
- ssize_t bytesRead;
- int fd;
-
- // 打开目录
- dir = opendir(DEVICE_DIR);
- if (dir == NULL)
- {
- perror("opendir");
- return -1;
- }
-
- // 遍历目录条目
- while ((entry = readdir(dir)) != NULL)
- {
-
- }
- }
while循环中逐个读入name判断。
- // 构建设备名称文件的路径
- snprintf(name_path, sizeof(name_path), "%s%s/name", DEVICE_DIR, entry->d_name);
- // 打开设备名称文件
- fd = open(name_path, O_RDONLY);
- if (fd == -1) {
- perror("open");
- continue;
- }
-
- // 读取设备名称
- bytesRead = read(fd, buffer, BUFFER_SIZE - 1);
- close(fd);
- if (bytesRead > 0) {
- buffer[bytesRead] = '\0'; // 确保字符串以null结尾
- printf("Device name: %s\n", buffer);
- } else {
- perror("read");
- // 关闭文件
- return -2;
- }
-
- if (strcmp(buffer, TARGET_NAME) == 0)
- {
- int number = 0;
- int is_number = 0; // 标志位,表示是否开始读取数字
-
- // 遍历字符串
- for (size_t i = 0; entry->d_name[i] != '\0'; ++i)
- {
- if (isdigit(entry->d_name[i]))
- { // 如果字符是数字
- if (!is_number)
- { // 如果之前还没读取过数字,开始读取
- is_number = 1;
- number = 0; // 重置number为0,准备读取新的数字
- }
- number = number * 10 + (entry->d_name[i] - '0'); // 将数字添加到number中
- }
- else
- {
- is_number = 0; // 如果不是数字,则停止读取数字
- }
- }
- return number;
- }
- int file;
- if ((file = open(i2c_path, O_RDWR)) < 0)
- {
- perror("Failed to open the i2c bus\n");
- exit(1);
- }
通过ioctl设置。
- if (ioctl(file, I2C_SLAVE, addr) < 0)
- {
- perror("Failed to acquire bus access and/or talk to slave");
- close(file);
- exit(1);
- }
- int i2cRead(int fd, unsigned char slave_addr, unsigned char reg_addr_width,
- unsigned int reg_addr, unsigned char *pdat, unsigned int len)
fd - 设备句柄
slave_addr - 从机地址,7位地址
reg_addr_width - 从机内部寄存器地址宽度,有效值为0,8,16
reg_addr - 从机内部寄存器地址,reg_addr_width为0时这个参数无效
pdat - 读入数据的缓存
len - 读入字节数
读写都是可以通过ioctl,对于读来说,需要先写寄存器地址,在读入数据。
- unsigned char outbuf[2];
- int offset = 0;
- struct i2c_rdwr_ioctl_data packets;
- struct i2c_msg messages[2];
根据寄存器地址宽度配置写寄存器地址的数据
- 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;
如果有寄存器地址需要发送,需要发送2个信息给驱动,注意2个信息的flag的区别,0表示写。
- if (reg_addr_width > 0)
- {
- 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;
- }
如果没有寄存器地址,则直接读数据即可。
- else
- {
- 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;
- }
最后发送出去
- if(ioctl(fd, I2C_RDWR, &packets) < 0)
- {
- perror("i2cRead ioctl fail");
- return -1;
- }
- return 0;
写数据必须一笔信息发送出去,其他类似读操作。
- int i2cWrite(int fd, unsigned char slave_addr, unsigned char reg_addr_width,
- unsigned int reg_addr, unsigned char *pdat, unsigned int len)
- {
- struct i2c_rdwr_ioctl_data packets;
- struct i2c_msg messages[1];
- unsigned char *outbuf = NULL;
- int offset = 0;
- unsigned int total = len;
- if(reg_addr_width == 16)
- total = len + 2;
- else if(reg_addr_width == 8)
- total = len + 1;
- else
- total = len;
- outbuf = malloc(total);
- if (!outbuf)
- {
- perror("Error: No memory for buffer");
- return -1;
- }
-
- 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");
- return -1;
- }
- return 0;
- }
参考3.3的方式设置,首先是要找到设备的文件夹位置。
先建一个函数用于根据VID、PID找到设备的文件夹位置。在文件夹下读取idVendor和idProduct文件,判断VID和PID即可。
- int check_usb_device(const char *path, const char *vid, const char *pid)
- {
- char vid_path[1024];
- char pid_path[1024];
- char vid_buf[16];
- char pid_buf[16];
- ssize_t bytes_read;
-
- snprintf(vid_path, sizeof(vid_path), "%s/idVendor", path);
- snprintf(pid_path, sizeof(pid_path), "%s/idProduct", path);
-
- int vid_fd = open(vid_path, O_RDONLY);
- int pid_fd = open(pid_path, O_RDONLY);
-
- if (vid_fd == -1 || pid_fd == -1) {
- perror("open");
- if (vid_fd != -1) close(vid_fd);
- if (pid_fd != -1) close(pid_fd);
- return -1;
- }
-
- bytes_read = read(vid_fd, vid_buf, sizeof(vid_buf) - 1);
- if (bytes_read <= 0) {
- perror("read");
- close(vid_fd);
- close(pid_fd);
- return -1;
- }
- if(bytes_read > 4)
- bytes_read = 4;
- vid_buf[bytes_read] = '\0'; // Ensure string is null-terminated
-
- bytes_read = read(pid_fd, pid_buf, sizeof(pid_buf) - 1);
- if (bytes_read <= 0) {
- perror("read");
- close(vid_fd);
- close(pid_fd);
- return -1;
- }
- if(bytes_read > 4)
- bytes_read = 4;
- pid_buf[bytes_read] = '\0'; // Ensure string is null-terminated
-
- close(vid_fd);
- close(pid_fd);
-
- // Compare VID and PID
- if (strcmp(vid, vid_buf) == 0 && strcmp(pid, pid_buf) == 0) {
- return 1; // Found a match
- }
-
- return 0; // No match
- }
找个这个文件夹后继续打开这个文件夹下名字带1.0的文件夹。
- int findClockPath(char *path, int len)
- {
- DIR *dir;
- struct dirent *entry;
- char full_path[1024];
- snprintf(full_path, sizeof(full_path), "%s:1.0/", path);
- dir = opendir(full_path);
- if (dir == NULL)
- {
- perror("opendir");
- return -1;
- }
- while ((entry = readdir(dir)) != NULL)
- {
- // 忽略.和..目录项
- if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
- {
- continue;
- }
-
- // 构建完整路径
- char *last_slash = strrchr(path, '/');
- snprintf(full_path, sizeof(full_path), "%s/%s:1.0/%s", path, last_slash, entry->d_name);
- printf("full path:%s\n", full_path);
- // 检查是否是目录,并且名称包含指定的vendor_product_id
- struct stat st;
- if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode))
- {
- // 检查目录名是否包含指定的vendor_product_id
- if (strstr(entry->d_name, "0403:6030") != NULL)
- {
- printf("Found directory: %s\n", full_path);
- snprintf(path, len, "%s", full_path);
- closedir(dir);
- return 0;
- }
- }
- }
- return -1;
- }
设置频率的函数,将设置的频率写入clock文件即可。
- int i2cSetFreq(int freq)
- {
- DIR *dir;
- struct dirent *entry;
- char path[1024];
-
- dir = opendir("/sys/bus/usb/devices/");
- if (dir == NULL) {
- perror("opendir");
- return 1;
- }
-
- while ((entry = readdir(dir)) != NULL) {
- if (entry->d_type == DT_DIR && entry->d_name[0] != '.') {
- snprintf(path, sizeof(path), "/sys/bus/usb/devices/%s", entry->d_name);
- if (check_usb_device(path, VID, PID) == 1) {
- printf("Found FT260 device at: %s\n", path);
-
- closedir(dir);
- if(findClockPath(path, sizeof(path)) == 0)
- {
- int fd;
- char buffer[6];
- char clockFilePath[2048];
- snprintf(buffer, sizeof(buffer), "%d\n", freq);
- snprintf(clockFilePath, sizeof(clockFilePath), "%s/clock", path);
- // 尝试以写入模式打开文件
- printf("clock:%s\n", clockFilePath);
- fd = open(clockFilePath, O_WRONLY);
- if (fd == -1)
- {
- // 如果打开失败,打印错误并退出
- perror("open");
- return -2;
- }
- // 写入数据到文件
- ssize_t bytes_written = write(fd, buffer, strlen(buffer));
- if (bytes_written == -1) {
- // 如果写入失败,打印错误并关闭文件
- perror("write");
- close(fd);
- return -3;
- }
- // 关闭文件
- if (close(fd) == -1) {
- // 如果关闭失败,打印错误但忽略,因为数据已经写入
- perror("close");
- return -4;
- }
- return 0;
- }
- }
- }
- }
-
- closedir(dir);
- return 0;
- }
进入这个文件夹,应该以:0403:6030为关键字找到这个特殊的文件夹
sudo apt-get install libgpiod-dev
- $ ls /sys/class/gpio/
- export gpiochip512 unexport
- $ ls /sys/class/gpio/gpiochip512
- base device label ngpio power subsystem uevent
- $ cat /sys/class/gpio/gpiochip512/label
- ft260_0003:0403:6030.000F
- $ cat /sys/class/gpio/gpiochip512/base
- 512
- $ cat /sys/class/gpio/gpiochip512/ngpio
- 14
只要找到base的值。
- int findGpio(int *base)
- {
- DIR *dir;
- struct dirent *entry;
- char full_path[1024];
- dir = opendir("/sys/class/gpio/");
- if (dir == NULL)
- {
- perror("opendir");
- return -1;
- }
- while ((entry = readdir(dir)) != NULL)
- {
- // 忽略.和..目录项
- if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
- {
- continue;
- }
- printf("folder:%s\n", entry->d_name);
- if (strstr(entry->d_name, "gpiochip") != NULL)
- {
- snprintf(full_path, sizeof(full_path), "/sys/class/gpio/%s/label", entry->d_name);
- printf("full path:%s\n", full_path);
- int fd;
- fd = open(full_path, O_RDONLY);
- if (fd == -1)
- {
- // 如果打开失败,打印错误
- perror("open label");
- continue;
- }
- char buffer[256];
- ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
- if (bytes_read == -1)
- {
- // 如果写入失败,打印错误并关闭文件
- perror("");
- close(fd);
- continue;
- }
- // 关闭文件
- if (close(fd) == -1)
- {
- // 如果关闭失败,打印错误但忽略,因为数据已经写入
- perror("close");
- continue;
- }
- printf(" label:%s\n", buffer);
- if (strstr(buffer, "ft260") != NULL)
- {
- snprintf(full_path, sizeof(full_path), "/sys/class/gpio/%s/base", entry->d_name);
- fd = open(full_path, O_RDONLY);
- if (fd == -1)
- {
- // 如果打开失败,打印错误
- perror("open");
- continue;
- }
- ssize_t bytes_read = read(fd, buffer, strlen(buffer));
- if (bytes_read == -1)
- {
- // 如果写入失败,打印错误并关闭文件
- perror("");
- close(fd);
- continue;
- }
- buffer[bytes_read] = '\0';
- // 关闭文件
- if (close(fd) == -1)
- {
- // 如果关闭失败,打印错误但忽略,因为数据已经写入
- perror("close");
- continue;
- }
- char *endptr;
- *base = strtol(buffer, &endptr, 10);
- printf("gpio base=%d\n", *base);
- return 0;
- }
- }
- }
- return -1;
- }
路径在/dev/中,类似“/dev/gpiochip0”
- struct gpiod_chip *gpiochipFT;
- gpiochipFT = gpiod_chip_open("/dev/gpiochip0");
- if (!gpiochipFT)
- {
- perror("gpio open fail");
- return;
- }
关闭:
gpiod_chip_close(gpiochipFT);
获取某个GPIO的句柄
- struct gpiod_line *gpio2;
- gpio2= gpiod_chip_get_line(gpiochipFT, 2);
- if (!gpio2)
- {
- gpiod_chip_close(gpiochipFT);
- perror("gpio2 get line fail");
- return;
- }
注意对应的GPIO要先设置为GPIO模式,否则会返回错误。
用完要释放:
gpiod_line_release(gpio2, &req);
设置为输出:
- req = gpiod_line_request_output(gpio2, "blink", 0);
- if (req)
- {
- gpiod_chip_close(gpiochipFT);
- fprintf(stderr, "GPIO2 request error.\n");
- return;
- }
字符串“blink”表示该GPIO的用户名,0表示默认电平为低电平。
可以通过gpiod_line_request_input设置为输入
req = gpiod_line_request_input(gpio2, "blink");
- while (1)
- {
- /* 设置引脚电平 */
- gpiod_line_set_value(gpio2, 1);
- printf("set GPIO2 to 0\n");
- usleep(500 * 1000);
- gpiod_line_set_value(gpio2, 0);
- printf("set GPIO2 to 1\n");
- usleep(500 * 1000);
- }
- while (1)
- {
- int value;
- /* 设置引脚电平 */
- gpiod_line_set_value(gpio2, 1);
- value = gpiod_line_get_value(gpio2);
- printf("set GPIO2 to %d\n", value);
- usleep(500 * 1000);
- gpiod_line_set_value(gpio2, 0);
- value = gpiod_line_get_value(gpio2);
- printf("set GPIO2 to %d\n", value);
- usleep(500 * 1000);
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。