从 C 程序读取 GPIO 端口

从 C 程序读取 GPIO 端口

我尝试了以下操作: - 我们有一个 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 驱动程序有两种方法。或者我们也可以称其为两种类型的驱动程序

  1. 内核空间驱动程序,内核树中的驱动程序或作为模块的驱动程序

    • 这是长期的解决方案,但并非易事
  2. 用户空间驱动程序,使用驱动程序libusb(其本身使用通用内核驱动程序)

    • 这是一种解决方法,但更快且相对容易
    • 人们倾向于使用这种方法的原因有很多:逆向工程、原型设计、最终用户项目的短发布周期(例如:适用于扫描仪的 sane)、作为缺少内核驱动程序的解决方法......

您可能发现一些尝试在没有内核驱动程序的情况下访问 USB 设备的帖子。

但是对于您的情况,CP210x 有一个内核空间驱动程序,并且已经实现了 GPIO 功能。可能它不是那么早,所以很多人都知道它。无需通过 libusb 路径,否则您必须按照 CP210x 数据表从头开始实现许多功能。

关于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”执行该文件。

相关内容