我尝试了以下操作: - 我们有一个 AXIS 控制器,它通过 CP2102 芯片的 USB 端口连接到 Linux (18.04)。我们想通过 Linux 机器上的 C 程序读取 GPIO 0-3 位的值。
在我的 C 程序中我确实看到了 USB 端口,当我读取配置时,它显示了所有正确的信息:
bLength: 12
bDescriptorType: 01
bcdUSB: 0200
bDeviceClass: 00
bDeviceSubClass: 00
bDeviceProtocol: 00
bMaxPacketSize: 40
idVendor: 10C4
idProduct: EA60
bcdDevice: 0100
biManufacturer: 01
iProduct: 02
iSerialNumber: 0003
iNumConfigurations: 01
Config descriptor CP210x
bLength: 09
bDescriptorType: 02
wTotalLength: 0020
bNumInterfaces: 01
bConfigurationValue: 01
iConfiguration: 00
bmAttributes: 0080
MaxPower: 32
对我来说,这意味着我连接到了正确的串行设备(在我的情况下是 /dev/ttyUSB0)。
现在我不清楚我可以进行哪些调用来读取这些 GPIO 位。到目前为止,我找不到任何可以在我的程序中使用的 Linux API 调用。
我写的源代码(不完美,但仅供测试。当它工作时我会清理它)
#include <stdio.h>
#include <stdlib.h>
#include <usb.h>
int main()
{
int nr_of_busses;
int nr_of_devices;
int j, r;
struct usb_bus *usb_busses;
struct usb_bus *ptr_usb_busses;
struct usb_device *ptr_usb_devices;
struct usb_device_descriptor desc;
uint8_t path[8];
usb_init();
perror( "Init LibUSB" );
nr_of_busses = usb_find_busses();
fprintf( stderr, "Nr of busses found: %i\n", nr_of_busses );
nr_of_devices = usb_find_devices();
fprintf( stderr, "Nr of devices found: %i\n", nr_of_devices );
usb_busses = usb_get_busses();
if ( usb_busses == NULL ) {
fprintf(stderr, "USB_busses = NULL\n");
perror( "Get busses" );
}
else {
ptr_usb_busses = usb_busses;
while ( ptr_usb_busses != NULL ) {
fprintf( stderr, "USB Buss : %s\n", ptr_usb_busses->dirname );
fflush( stderr );
ptr_usb_devices = ptr_usb_busses->devices;
while (ptr_usb_devices != NULL) {
fprintf( stderr, "FileName : %s\n", ptr_usb_devices->filename );
fflush( stderr );
sleep(3);
// Check if this is the cp210x-device I´m looking for. If so print the information, just to be sure and to compare with the documentation
if (ptr_usb_devices->descriptor.idProduct == 0xEA60 ) {
fprintf(stderr, "bLength: %02X\nbDescriptorType: %02X\nbcdUSB: %04X\nbDeviceClass: %02X\nbDeviceSubClass: %02X\n",
ptr_usb_devices->descriptor.bLength,
ptr_usb_devices->descriptor.bDescriptorType,
ptr_usb_devices->descriptor.bcdUSB,
ptr_usb_devices->descriptor.bDeviceClass,
ptr_usb_devices->descriptor.bDeviceSubClass );
fprintf(stderr, "bDeviceProtocol: %02X\nbMaxPacketSize: %02X\nidVendor: %04X\nidProduct: %04X\nbcdDevice: %04X\n",
ptr_usb_devices->descriptor.bDeviceProtocol,
ptr_usb_devices->descriptor.bMaxPacketSize0,
ptr_usb_devices->descriptor.idVendor,
ptr_usb_devices->descriptor.idProduct,
ptr_usb_devices->descriptor.bcdDevice );
fprintf(stderr, "biManufacturer: %02X\niProduct: %02X\niSerialNumber: %04X\niNumConfigurations: %02X\n",
ptr_usb_devices->descriptor.iManufacturer,
ptr_usb_devices->descriptor.iProduct,
ptr_usb_devices->descriptor.iSerialNumber,
ptr_usb_devices->descriptor.bNumConfigurations );
fprintf ( stderr, "Config descriptor CP210x\n");
fprintf( stderr, "bLength: %02X\nbDescriptorType: %02X\nwTotalLength: %04X\nbNumInterfaces: %02X\n",
ptr_usb_devices->config->bLength,
ptr_usb_devices->config->bDescriptorType,
ptr_usb_devices->config->wTotalLength,
ptr_usb_devices->config->bNumInterfaces );
fprintf( stderr, "bConfigurationValue: %02X\niConfiguration: %02X\nbmAttributes: %04X\nMaxPower: %02X\n",
ptr_usb_devices->config->bConfigurationValue,
ptr_usb_devices->config->iConfiguration,
ptr_usb_devices->config->bmAttributes,
ptr_usb_devices->config->MaxPower );
fflush( stderr );
usb_dev_handle *handle = usb_open( ptr_usb_devices );
// dev_dbg( ptr_usb_devices, "Test");
perror( "usb_open : ");
int stat = usb_set_configuration( handle, ptr_usb_devices->config->bConfigurationValue );
perror( "usb_set_configuration : ");
int interfaceNum = ptr_usb_devices->config->interface->altsetting->bInterfaceNumber;
stat = usb_claim_interface( handle, interfaceNum );
int altNum = ptr_usb_devices->config->interface->altsetting->bAlternateSetting;
stat = usb_set_altinterface( handle, altNum );
perror( "usb_set_altinterface : ");
// Now I need forever to read the GPIO-bits. How do I do that?
while (1) {
}
}
fprintf(stderr, "\n");
fflush( stderr );
ptr_usb_devices = ptr_usb_devices->next;
}
ptr_usb_busses = ptr_usb_busses->next;
}
fprintf( stderr, "All bus-names printed" );
fflush( stderr );
}
exit(0);
}
编译后,我通过运行上述程序sudo ./a.out
。
现在我遇到了以下问题:
- 我收到以下错误消息:
- usb_open :: 设备的 ioctl 不合适
- usb_set_configuration::设备或资源繁忙
- usb_set_altinterface::设备或资源繁忙
另外,我真的找不到可以用来读取此程序中的 GPIO 位的 C 调用。
是否有一个可以链接的库,其中描述了正确的调用?
可能有一个非常简单的解决方案,但我找不到它,所以我可能在错误的地方寻找。
答案1
我正在寻找并尝试了许多不同的应用程序,而这个应用程序让我很开心:
https://github.com/ondrej1024/crelay
无需对我的树莓派上内核 4.19 上的 cp210x 模块进行任何修改即可工作!
答案2
这并不是说你理解错了,只是你遇到了时间问题。建议,互联网上有很多过时的文档,你最好在阅读之前先检查一下上次修改日期。
制作 USB 驱动程序有两种方法。或者我们也可以称其为两种类型的驱动程序
内核空间驱动程序,内核树中的驱动程序或作为模块的驱动程序
- 这是长期的解决方案,但并非易事
用户空间驱动程序,使用驱动程序
libusb
(其本身使用通用内核驱动程序)- 这是一种解决方法,但更快且相对容易
- 人们倾向于使用这种方法的原因有很多:逆向工程、原型设计、最终用户项目的短发布周期(例如:适用于扫描仪的 sane)、作为缺少内核驱动程序的解决方法......
您可能发现一些尝试在没有内核驱动程序的情况下访问 USB 设备的帖子。
但是对于您的情况,CP210x 有一个内核空间驱动程序,并且已经实现了 GPIO 功能。可能它不是那么早,所以很多人都知道它。无需通过 libusb 路径,否则您必须按照 CP210x 数据表从头开始实现许多功能。
内核空间驱动程序
用户空间驱动程序(仅作为示例,不具备全部功能)
https://github.com/VCTLabs/cp210x-program 首次提交于2010 年 9 月 9 日
关于Silicon Labs已经记录下来
如何在运行时控制 CP210x 的 GPIO
2018 年 7 月 25 日 | 上午 4:56
在 Linux 上
自 v2.6.12 起,CP210x 驱动程序已作为 Linux 内核的一部分分发,并且 Linux 4.10.0 内核或更高版本也支持 GPIO 操作。
如果您使用的是较旧的内核版本,请从以下链接下载最新的 Linux VCP 驱动程序,并将与 GPIO 操作相关的源代码合并到您的内核中。.zip 包包含 VCP 驱动程序和示例代码,展示如何控制 GPIO。
https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers 。
合并VCP驱动cp210x.c后,使用make命令编译,使用insmod命令安装。注意,如果VCP驱动已经存在,在安装支持GPIO控制的驱动之前,需要先执行rmmod cp210x.ko命令。
在cp210x_gpio_example.c中,使用以下函数打开CP210x设备。
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
使用下面的函数来读取latch,其中第三个参数gpioread是一个字节长度的缓冲区地址,用于保存读取的latch值。
ioctl(fd, IOCTL_GPIOGET, &gpioread);
返回的读取锁存器值表示如下:位 0-7:当前锁存器状态,其中位 0 为 GPIO0,位 1 为 GPIO1,等等,直到 GPIOn,其中 n 是接口支持的 GPIO 引脚总数。
使用下面的函数来写入闩锁,其中第三个参数gpio是写入闩锁的值。
ioctl(fd, IOCTL_GPIOSET, &gpio);
数据阶段提供的写锁存器值表示如下:
位 0-7:要写入的锁存器状态掩码(在位 8-15 中),其中位 0 为 GPIO0,位 1 为 GPIO1,等等。最多为 GPIOn,其中 n 是接口支持的 GPIO 引脚总数。
位 8-15:要写入的锁存状态,其中位 8 为 GPIO0,位 9 为 GPIO1,等等。最多为 GPIOn,其中 n 是接口支持的 GPIO 引脚总数。
从 AN571 获取更多信息。
使用“gcc cp210x_gpio_example.c”编译应用程序,生成可执行文件“a.out”。使用“./a.out”执行该文件。