You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetson Nano内核模块无法正确访问GPIO寄存器问题求助

问题描述

我出于学习目的开发了一个控制GPIO的内核模块,设计逻辑是向/proc/lll-gpio写入4,1时点亮GPIO引脚37。因为没有内核模块和寄存器访问经验,我先通过读取寄存器值验证内存地址是否正确:用Jetson.GPIO Python库切换GPIO37状态(引脚确实能正常切换),但内核模块读取的寄存器值完全没变化,求排查问题。

内核模块代码
#include <asm/io.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

#define LLL_MAX_USER_SIZE 1024

#define GPIO_ADDR                0x6000d000
#define GPIO_CONTROLLER_1_OFFSET 0x0
#define GPIO_CONTROLLER_2_OFFSET 0x100
#define GPIO_CONTROLLER_3_OFFSET 0x200
#define GPIO_CONTROLLER_4_OFFSET 0x300
#define GPIO_CONTROLLER_5_OFFSET 0x400
#define GPIO_CONTROLLER_6_OFFSET 0x500
#define GPIO_CONTROLLER_7_OFFSET 0x600

#define GPIO_PORT_1_OFFSET 0x00
#define GPIO_PORT_2_OFFSET 0x04
#define GPIO_PORT_3_OFFSET 0x08
#define GPIO_PORT_4_OFFSET 0x0C

#define GPIO_REG_GPIO_CNF      0x00
#define GPIO_REG_GPIO_OE       0x10
#define GPIO_REG_GPIO_OUT      0x20
#define GPIO_REG_GPIO_IN       0x30
#define GPIO_REG_GPIO_INT_STAT 0x40
#define GPIO_REG_GPIO_ENB      0x50
#define GPIO_REG_GPIO_LVL      0x60
#define GPIO_REG_GPIO_CLR      0x70

static unsigned int *gpio_registers    = NULL;
static struct proc_dir_entry *lll_proc = NULL;
static char data_buffer[LLL_MAX_USER_SIZE];

ssize_t lll_read(struct file *file, char __user *user, size_t size, loff_t *off);
ssize_t lll_write(struct file *file, const char __user *user, size_t size, loff_t *off);

static void gpio_on(unsigned int pin) {
    volatile unsigned int *GPIO_CNF_REG = gpio_registers + GPIO_CONTROLLER_1_OFFSET + GPIO_PORT_2_OFFSET +
                                          GPIO_REG_GPIO_CNF;
    volatile unsigned int *GPIO_OE_REG = gpio_registers + GPIO_CONTROLLER_1_OFFSET + GPIO_PORT_2_OFFSET +
                                         GPIO_REG_GPIO_OE;
    volatile unsigned int *GPIO_ENB_REG = gpio_registers + GPIO_CONTROLLER_1_OFFSET + GPIO_PORT_2_OFFSET +
                                          GPIO_REG_GPIO_ENB;

    volatile unsigned int *GPIO_IN_REG = gpio_registers + GPIO_CONTROLLER_1_OFFSET + GPIO_PORT_2_OFFSET +
                                          GPIO_REG_GPIO_IN;
    // *GPIO_CNF_REG              = *GPIO_CNF_REG | 1 << 4;
    // *GPIO_ENB_REG              = *GPIO_ENB_REG | 1 << 4;
    // *GPIO_OE_REG               = *GPIO_OE_REG | 1 << 4;
    printk("GPIO CNG addr : %X\n", *GPIO_CNF_REG);
    printk("GPIO OE addr : %X\n", *GPIO_OE_REG);
    printk("GPIO ENB addr : %X\n", *GPIO_ENB_REG);
    printk("GPIO IN addr : %X\n", *GPIO_IN_REG );
}

static void gpio_off(unsigned int pin) {
}

ssize_t lll_read(struct file *file, char __user *user, size_t size, loff_t *off) {
    int ret = 0;
    ret     = copy_to_user(user, "Hello!\n", 7);
    return 7;
}

ssize_t lll_write(struct file *file, const char __user *user, size_t size, loff_t *off) {
    int ret            = 0;
    unsigned int pin   = UINT_MAX;
    unsigned int value = UINT_MAX;

    memset(data_buffer, 0x0, sizeof(data_buffer));

    if (size > LLL_MAX_USER_SIZE)
        size = LLL_MAX_USER_SIZE;

    ret = copy_from_user(data_buffer, user, size);

    printk("You said '%s' !", data_buffer);

    if (sscanf(data_buffer, "%d,%d", &pin, &value) != 2) {
        printk("Improper format\n");
    }
    printk("You said pin %d value %d!\n", pin, value);
    if (pin > 0 && pin < 8) {
        gpio_on(pin);
    }

    return size;
}

static const struct file_operations lll_proc_fops = {
    .read  = lll_read,
    .write = lll_write,
};

static int __init gpio_driver_init(void) {
    printk("Welcome to my driver!\n");

    gpio_registers = (int *)ioremap(GPIO_ADDR, 1024);

    if (gpio_registers == NULL) {
        printk("Failed to initialize GPIO");
        return -1;
    }
    printk("Successfully GPIO");
    // Create proc file
    lll_proc = proc_create("lll-gpio", 0666, NULL, &lll_proc_fops);

    if (lll_proc == NULL)
        return -1;
    return 0;
}

static void __exit gpio_driver_exit(void) {
    printk("Leaving to my driver!\n");
    iounmap(gpio_registers);
    proc_remove(lll_proc);
    return;
}

module_init(gpio_driver_init);
module_exit(gpio_driver_exit);

MODULE_LICENSE("GPL");
dmesg输出
[  +0.000007] GPIO CNG addr : 40
[  +0.000005] GPIO OE addr : 0
[  +0.000005] GPIO ENB addr : 0
[  +0.000004] GPIO IN addr : 0
排查要点
  • 寄存器地址计算错误
    代码中unsigned int *gpio_registers是32位指针,直接加十六进制偏移值会按4字节为单位计算偏移,而非字节偏移。比如0x4的偏移会实际访问GPIO_ADDR + 0x4*4 = GPIO_ADDR + 0x10的地址,和预期的字节偏移完全不符。
    正确做法是先将基地址转为char *类型做字节偏移,再转成unsigned int *访问寄存器:

    volatile char *gpio_base = ioremap(GPIO_ADDR, 1024);
    volatile unsigned int *GPIO_CNF_REG = (volatile unsigned int *)(gpio_base + GPIO_CONTROLLER_1_OFFSET + GPIO_PORT_2_OFFSET + GPIO_REG_GPIO_CNF);
    
  • GPIO引脚映射错误
    代码假设GPIO37对应控制器1的端口2第4位,但Jetson的GPIO编号与寄存器组的映射需严格参考官方技术参考手册(TRM),比如Jetson Nano的GPIO37实际对应GPIO Controller 5的Port B第5位,地址偏移完全不同。

  • 寄存器定义与实际硬件不匹配
    代码中定义的GPIO_REG_GPIO_CNF、GPIO_REG_GPIO_ENB等寄存器偏移,可能和Jetson实际的GPIO寄存器布局不符。需参考官方TRM确认寄存器的正确偏移和功能。

  • 内核GPIO子系统的独占访问限制
    Jetson.GPIO库基于内核GPIO子系统实现,当它占用GPIO37时,内核可能禁止直接寄存器访问,或该引脚被复用为其他功能。可通过cat /sys/kernel/debug/gpio查看GPIO37的状态,确认是否被标记为"used",以及是否处于GPIO功能模式。

内容的提问来源于stack exchange,提问作者ForgottenTale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 07:47:03