树莓派 -- 输入设备驱动 (key)

本文介绍Raspberry Pi上基于GPIO的按键驱动实现过程,包括设备树配置、平台设备驱动探针函数详解及中断处理等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

输入设备(如按键,键盘,触摸屏等)是典型的字符设备,其一般工作原理是底层在按键或触摸等动作发生时产生一个中断,然后CPU通过SPI,I2C总线读取键值。
在这些工作中之后中断和读键值是与设备相关的,而输入事件的缓冲区管理,字符设备驱动的file_operations接口则对输入设备是通用的。因此内核设计了输入子系统,由核心层处理公共的工作。

先看gpio-keys platform_driver驱动代码, 源码
drivers/input/keyboard/gpio_keys.c

static int gpio_keys_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
    struct fwnode_handle *child = NULL;
    struct gpio_keys_drvdata *ddata;
    struct input_dev *input;
    size_t size;
    int i, error;
    int wakeup = 0;
    if (!pdata) {
        pdata = gpio_keys_get_devtree_pdata(dev);
        if (IS_ERR(pdata))
            return PTR_ERR(pdata);
    }
    size = sizeof(struct gpio_keys_drvdata) +
            pdata->nbuttons * sizeof(struct gpio_button_data);
    ddata = devm_kzalloc(dev, size, GFP_KERNEL);
    if (!ddata) {
        dev_err(dev, "failed to allocate state\n");
        return -ENOMEM;
    }
    ddata->keymap = devm_kcalloc(dev,
                     pdata->nbuttons, sizeof(ddata->keymap[0]),
                     GFP_KERNEL);
    if (!ddata->keymap)
        return -ENOMEM;
    //分配输入设备
    input = devm_input_allocate_device(dev);
    if (!input) {
        dev_err(dev, "failed to allocate input device\n");
        return -ENOMEM;
    }
    ddata->pdata = pdata;
    ddata->input = input;
    mutex_init(&ddata->disable_lock);
    platform_set_drvdata(pdev, ddata);
    input_set_drvdata(input, ddata);
    input->name = pdata->name ? : pdev->name;
    input->phys = "gpio-keys/input0";
    input->dev.parent = dev;
    input->open = gpio_keys_open;
    input->close = gpio_keys_close;
    input->id.bustype = BUS_HOST;
    input->id.vendor = 0x0001;
    input->id.product = 0x0001;
    input->id.version = 0x0100;
    input->keycode = ddata->keymap;
    input->keycodesize = sizeof(ddata->keymap[0]);
    input->keycodemax = pdata->nbuttons;
    /* Enable auto repeat feature of Linux input subsystem */
    if (pdata->rep)
        __set_bit(EV_REP, input->evbit);
    for (i = 0; i < pdata->nbuttons; i++) {
        const struct gpio_keys_button *button = &pdata->buttons[i];
        if (!dev_get_platdata(dev)) {
            child = device_get_next_child_node(dev, child);
            if (!child) {
                dev_err(dev,
                    "missing child device node for entry %d\n",
                    i);
                return -EINVAL;
            }
        }
        error = gpio_keys_setup_key(pdev, input, ddata,
                        button, i, child);
        if (error) {
            fwnode_handle_put(child);
            return error;
        }
        if (button->wakeup)
            wakeup = 1;
    }
    fwnode_handle_put(child);
    error = devm_device_add_group(dev, &gpio_keys_attr_group);
    if (error) {
        dev_err(dev, "Unable to export keys/switches, error: %d\n",
            error);
        return error;
    }
    //注册输入设备
    error = input_register_device(input);
    if (error) {
        dev_err(dev, "Unable to register input device, error: %d\n",
            error);
        return error;
    }
    device_init_wakeup(dev, wakeup);
    return 0;
}

key的硬件配置信息由设备树传入。
我们再看看设备树input的描述
Documentation\devicetree\bindings\input\gpio-keys.txt中给出的例子

Subnode properties:

    - gpios: OF device-tree gpio specification.
    - interrupts: the interrupt line for that input.
    - label: Descriptive name of the key.
    - linux,code: Keycode to emit.

Example nodes:

    gpio-keys {
            compatible = "gpio-keys";
            autorepeat;

            up {
                label = "GPIO Key UP";
                linux,code = <103>;
                gpios = <&gpio1 0 1>;
            };

            down {
                label = "GPIO Key DOWN";
                linux,code = <108>;
                interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
            };
            ...

打算使用Interrupts的方式。
首先看一下RaspberryPi 3-b的中断在设备树中的描述
arch\arm\boot\dts\bcm283x.dtsi

interrupt-parent = <&intc>;
    soc {
        compatible = "simple-bus";
        //...
        intc: interrupt-controller@7e00b200 {
            compatible = "brcm,bcm2835-armctrl-ic";
            reg = <0x7e00b200 0x200>;
            interrupt-controller;
            #interrupt-cells = <2>;
        };

        //...
        gpio: gpio@7e200000 {
            compatible = "brcm,bcm2835-gpio";
            reg = <0x7e200000 0xb4>;
            interrupts = <2 17>, <2 18>, <2 19>, <2 20>;

            gpio-controller;
            #gpio-cells = <2>;

            interrupt-controller;
            #interrupt-cells = <2>;

            //...

arch\arm\boot\dts\bcm2837.dtsi

    soc {
        //...
        local_intc: local_intc@40000000 {
            compatible = "brcm,bcm2836-l1-intc";
            reg = <0x40000000 0x100>;
            interrupt-controller;
            #interrupt-cells = <1>;
            interrupt-parent = <&local_intc>;
        };
    };
/* Make the BCM2835-style global interrupt controller be a child of the
 * CPU-local interrupt controller.
 */
&intc {
    compatible = "brcm,bcm2836-armctrl-ic";
    reg = <0x7e00b200 0x200>;
    interrupt-parent = <&local_intc>;
    interrupts = <8>;
};

arch\arm\boot\dts\bcm270x.dtsi

    soc: soc {
        //...

        gpio@7e200000 { /* gpio */
            interrupts = <2 17>, <2 18>;
        };

在arch\arm\boot\dts\bcm2708-rpi.dtsi中添加gpio_keys nodes.

    gpio_keys: gpio_keys {
        compatible = "gpio-keys";
    };

在arch\arm\boot\dts\bcm2710-rpi-3-b.dts中添加gpio_keys nodes.

&gpio_keys {
    pinctrl-names = "default";

    key1_key: key1 {
      interrupt-parent = <&gpio>;
      interrupts = <2 20>;
      gpios = <&gpio 20 GPIO_ACTIVE_LOW>;
      linux,code = <100>;
      label = "key1";
      debounce-interval = <10>;
      wakeup-source;
    };
};

重新编译设备树

$ make bcm2709_defconfig
$ make dtbs

并将新的设备树copy到SD卡boot下, 启动raspberryPi
查看platform devices

pi@raspberrypi:/sys/devices/platform $ cd gpio_keys/
pi@raspberrypi:/sys/devices/platform/gpio_keys $ ls
disabled_keys      driver           input  modalias  power      switches
disabled_switches  driver_override  keys   of_node   subsystem  uevent
pi@raspberrypi:/sys/devices/platform/gpio_keys $ cd driver
pi@raspberrypi:/sys/devices/platform/gpio_keys/driver $ ls
bind  gpio_keys  module  uevent  unbind
pi@raspberrypi:/sys/devices/platform/gpio_keys/driver $ 
<think>好的,我现在需要帮助用户解决树莓派RPI-RP2连接Wi-Fi和以太网的问题。首先,我需要确认用户提到的RPI-RP2是什么。树莓派型号中有4B、5等,但RPI-RP2可能是指某个特定版本或者用户可能的拼写错误。不过,根据常见的树莓派型号,可能是指树莓派Pico或者某个变种,但根据用户后续提到的连接网络,更可能是指树莓派4B或5,因为Pico本身没有网络功能。或者,RPI-RP2可能是指树莓派的某种配置模式,比如USB Gadget模式,这时候可能需要参考引用中的rpi-gadget-image-creator项目。不过用户的问题核心是连接Wi-Fi和以太网,所以应该以常规方法为主。 接下来,我需要回忆树莓派连接网络的常规方法。对于有线以太网,通常直接插入网线即可自动获取IP,但可能需要配置静态IP。对于Wi-Fi,通常需要在系统中配置,比如通过raspi-config工具,或者修改wpa_supplicant配置文件。 用户提供的引用中有提到在VMware安装树莓派系统,可能需要配置网络设置。引用3提到的rpi-gadget-image-creator项目可能与通过USB模拟网络设备有关,可能适用于无有线网络的情况,比如通过USB共享网络。但用户的问题可能更偏向于常规连接,所以需要分步骤说明两种方式。 首先,处理Wi-Fi连接。树莓派系统通常是Raspberry Pi OS,基于Debian,所以配置Wi-Fi可以通过图形界面或命令行。对于无头安装(没有显示器),用户需要预先配置wpa_supplicant.conf文件。插入SD卡后,在boot分区创建该文件,写入SSID和密码。这样启动后会自动连接。 其次,以太网连接相对简单,插入网线即可。但用户可能需要设置静态IP,这时候需要修改dhcpcd.conf文件或者interfaces文件,具体取决于系统版本。 另外,用户提到RPI-RP2,可能需要确认是否需要特殊驱动或配置。例如,某些树莓派型号的无线模块可能需要额外固件,但通常Raspberry Pi OS已经包含。如果有问题,可能需要更新系统或安装firmware包。 还需要考虑用户是否在VMware中安装树莓派系统,这时候网络配置可能与物理机不同。例如,VMware的网络桥接或NAT设置会影响树莓派虚拟机的网络连接方式。但用户的问题可能更偏向物理树莓派的连接,所以应区分情况。 在回答中需要引用提供的参考资料。引用1是关于VMware安装树莓派的教程,可能涉及网络配置部分。引用2是树莓派官方文档,应该包含网络配置的详细说明。引用3提到的USB Gadget可能用于通过USB共享网络,适合没有有线或无线的情况,可以作为补充方法。 可能的问题点:用户是否在初次启动时需要配置网络?是否需要启用SSH?引用中提到开启SSH远程连接,这可能需要在SD卡中创建ssh文件,或者在系统中启用。但用户的问题主要是连接网络,所以SSH可能作为后续步骤。 总结步骤: 1. 连接以太网:插入网线,检查接口状态,配置静态IP(可选)。 2. 连接Wi-Fi:通过命令行工具或预配置文件。 3. 验证连接:使用ping命令检查网络连通性。 4. 其他方法:如USB Gadget模式(引用3中的项目)。 需要确保LaTeX格式正确,例如命令中的代码部分使用行内公式$...$,但根据用户示例,代码块应使用代码围栏,而独立公式用$$...$$,但网络配置中可能较少涉及数学公式,所以可能不需要使用。 最后,生成相关问题,比如静态IP配置、SSH连接、网络故障排查等。</think>### 树莓派 RPI-RP2 连接 Wi-Fi 和以太网教程 #### 一、以太网连接(有线网络) 1. **物理连接** 将网线插入树莓派的以太网接口,路由器会自动分配 IP 地址(DHCP 模式)。 2. **验证连接** 在终端输入以下命令查看 IP 地址: ```bash $ ifconfig eth0 ``` 若显示类似 `inet 192.168.x.x` 的字段,则连接成功。 3. **静态 IP 配置(可选)** 修改 `/etc/dhcpcd.conf` 文件: ```bash $ sudo nano /etc/dhcpcd.conf ``` 添加以下内容(根据实际网络环境修改): ```bash interface eth0 static ip_address=192.168.1.100/24 static routers=192.168.1.1 static domain_name_servers=8.8.8.8 ``` 重启网络服务: ```bash $ sudo service dhcpcd restart ``` #### 二、Wi-Fi 连接(无线网络) 1. **图形界面配置** 在树莓派桌面右上角点击网络图标,选择 Wi-Fi 名称并输入密码即可[^2]。 2. **命令行配置(适用于无显示器场景)** - **预配置法**: 在 SD 卡的 `boot` 分区创建 `wpa_supplicant.conf` 文件,写入以下内容: ```bash ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev update_config=1 country=CN network={ ssid="你的Wi-Fi名称" psk="你的Wi-Fi密码" key_mgmt=WPA-PSK } ``` 插入 SD 卡启动后,树莓派会自动连接 Wi-Fi。 - **运行时配置**: 使用 `raspi-config` 工具: ```bash $ sudo raspi-config ``` 选择 **System Options > Wireless LAN**,输入 SSID 和密码。 3. **验证连接** ```bash $ ping www.baidu.com ``` 若返回数据包,则连接成功。 #### 三、通过 USB Gadget 连接网络(特殊场景) 若树莓派无网络接口,可通过 **USB 模拟以太网设备** 共享主机网络: 1. 使用工具 `rpi-gadget-image-creator` 生成支持 USB Gadget 的系统镜像[^3]。 2. 通过 USB 线连接树莓派与电脑,配置网络共享(详见项目文档)。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值