4. GPIO点灯控制

任务目标

在瑞芯微RK系列平台上,通过内核模块方式控制一个GPIO引脚输出高低电平,驱动LED以1Hz频率闪烁,以此验证GPIO输出功能是否正常。

环境与依赖

  • 交叉编译工具链(如aarch64-linux-gnu-gcc)
  • RK平台Linux内核源码树(已配置好交叉编译环境)
  • 目标板已启动Linux系统,且具备root权限

硬件连接

将LED正极(长脚)通过220Ω限流电阻连接到目标GPIO引脚,LED负极(短脚)接地。本案例以RK3588为例,使用GPIO0_B7(对应引脚编号计算见下文),实际使用时请根据板卡原理图调整。

GPIO编号计算

RK平台GPIO编号公式:bank * 32 + group * 8 + index。其中group对应:A=0, B=1, C=2, D=3。GPIO0_B7即bank=0, group=B(1), index=7,编号为 0*32 + 1*8 + 7 = 15。

内核模块实现

创建 gpio_led.c:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/err.h>

#define LED_GPIO  15   /* GPIO0_B7 */

static struct task_struct *led_thread;
static int led_state;

static int led_blink_thread(void *data)
{
    while (!kthread_should_stop()) {
        led_state = !led_state;
        gpio_set_value(LED_GPIO, led_state);
        msleep(500);   /* 500ms翻转一次,周期1s */
    }
    return 0;
}

static int __init led_init(void)
{
    int ret;

    if (!gpio_is_valid(LED_GPIO)) {
        pr_err("Invalid GPIO: %d\n", LED_GPIO);
        return -EINVAL;
    }

    ret = gpio_request(LED_GPIO, "led");
    if (ret) {
        pr_err("gpio_request failed: %d\n", ret);
        return ret;
    }

    ret = gpio_direction_output(LED_GPIO, 0);
    if (ret) {
        pr_err("gpio_direction_output failed: %d\n", ret);
        gpio_free(LED_GPIO);
        return ret;
    }

    led_state = 0;

    led_thread = kthread_run(led_blink_thread, NULL, "led_blink");
    if (IS_ERR(led_thread)) {
        pr_err("kthread_run failed\n");
        gpio_free(LED_GPIO);
        return PTR_ERR(led_thread);
    }

    pr_info("LED module loaded, GPIO %d blinking started\n", LED_GPIO);
    return 0;
}

static void __exit led_exit(void)
{
    if (led_thread)
        kthread_stop(led_thread);

    gpio_set_value(LED_GPIO, 0);
    gpio_free(LED_GPIO);
    pr_info("LED module unloaded\n");
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("RK Dev");
MODULE_DESCRIPTION("RK GPIO LED Blink Driver");

Makefile

obj-m := gpio_led.o
KDIR := /path/to/kernel/source
PWD := $(shell pwd)
CROSS := aarch64-linux-gnu-

all:
	$(MAKE) ARCH=arm64 CROSS_COMPILE=$(CROSS) -C $(KDIR) M=$(PWD) modules

clean:
	$(MAKE) ARCH=arm64 CROSS_COMPILE=$(CROSS) -C $(KDIR) M=$(PWD) clean

将 KDIR 替换为实际内核源码路径。

编译与加载

make
adb push gpio_led.ko /data/
adb shell
# 在目标板执行
insmod /data/gpio_led.ko

验证方法

  1. 加载模块后,观察LED是否以约1Hz频率(每秒亮灭各一次)闪烁。
  2. 执行 dmesg | tail 查看内核日志,应包含 LED module loaded 信息。
  3. 使用万用表测量GPIO引脚电压,应观察到0V与3.3V(或板卡IO电平)交替变化。

通过条件:LED持续稳定闪烁,且dmesg无错误信息。

卸载模块

rmmod gpio_led

卸载后LED应熄灭,GPIO被释放。

常见问题

  • gpio_request失败:检查该GPIO是否已被其他驱动占用,或编号计算是否正确。
  • LED不亮:确认GPIO编号对应实际引脚,检查硬件连接和限流电阻。
  • 编译报错:确认内核源码路径正确,且已配置好交叉编译环境。