首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

DM8168 GPIO 驅動開發與用戶態測試完整實錄

     編輯:C++ 入門

硬體環境與目標

  • SoC:TI DM8168 (ARM Cortex-A8)
  • 測試腳位:GPIO1[31] (GP1[31]),復用功能預設即 GPIO,無需配置 PINMUX。
  • 目標:內核模塊控制該腳位輸出高/低電平,用戶態程式通過 /dev/gpio 觸發。

關鍵寄存器地址 (TRM 參考)

寄存器物理地址偏移功能
GPIO1_DIR0x4804C1340x134方向控制:0=輸出, 1=輸入
GPIO1_DATAOUT0x4804C13C0x13C數據輸出寄存器

注意:DM8168 GPIO 每組 32 位,BIT31 對應 GP1[31]。

內核模塊代碼 (dm8168_gpio.c)

#include 
#include 
#include 
#include 
#include 
#include 
#include           // ioremap/iounmap
#include 

static struct class *gpio_class;
static volatile unsigned long *DIR_REG;
static volatile unsigned long *DAT_REG;

static int gpio_open(struct inode *inode, struct file *filp) {
    *DIR_REG = 0x0;                 // 全部設為輸出
    return 0;
}

static ssize_t gpio_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
    return 0;                       // 本範例不支援讀取
}

static ssize_t gpio_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
    char kbuf[2];
    if (copy_from_user(kbuf, buf, min(count, sizeof(kbuf)))) return -EFAULT;

    switch (kbuf[0]) {
        case '1': *DAT_REG = (1UL << 31); break;  // GP1[31] = 1 (3.3V)
        case '0': *DAT_REG = 0x0;       break;  // GP1[31] = 0 (0V)
        default:  return -EINVAL;
    }
    return count;
}

static const struct file_operations gpio_fops = {
    .owner   = THIS_MODULE,
    .open    = gpio_open,
    .read    = gpio_read,
    .write   = gpio_write,
};

static int __init gpio_init(void) {
    int major = register_chrdev(0, "DM8168_gpio", &gpio_fops);
    if (major < 0) return major;

    gpio_class = class_create(THIS_MODULE, "DM8168_gpio");
    device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "gpio"); // /dev/gpio

    DIR_REG = ioremap(0x4804C134, 4);
    DAT_REG = ioremap(0x4804C13C, 4);
    if (!DIR_REG || !DAT_REG) return -ENOMEM;

    pr_info("DM8168 GPIO driver loaded, major=%d\n", major);
    return 0;
}

static void __exit gpio_exit(void) {
    device_destroy(gpio_class, MKDEV(major, 0));
    class_destroy(gpio_class);
    unregister_chrdev(major, "DM8168_gpio");
    iounmap(DIR_REG);
    iounmap(DAT_REG);
    pr_info("DM8168 GPIO driver unloaded\n");
}

module_init(gpio_init);
module_exit(gpio_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Embedded Dev");
MODULE_DESCRIPTION("DM8168 GPIO1[31] control driver");

Makefile (交叉編譯)

obj-m := dm8168_gpio.o

CROSS_COMPILE := /opt/codesourcery/arm-2009q1/bin/arm-none-linux-gnueabi-
KDIR := /home/user/ti-ezsdk_dm816x-evm_5_03_01_15/board-support/linux-2.6.37-psp04.00.01.13.patch2

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

clean:
	rm -rf *.ko *.o .*.cmd *.mod.c .tmp_versions modules.order Module.symvers

用戶態測試程式 (gpio_test.c)

#include 
#include 
#include 
#include 

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <0|1>\n", argv[0]);
        return 1;
    }
    int fd = open("/dev/gpio", O_RDWR);
    if (fd < 0) {
        perror("open /dev/gpio");
        return 1;
    }
    if (write(fd, argv[1], 1) != 1) {
        perror("write");
        close(fd);
        return 1;
    }
    close(fd);
    printf("GPIO1[31] set to %s\n", argv[1]);
    return 0;
}

編譯與測試步驟

  1. 內核模塊:
    make                    # 產生 dm8168_gpio.ko
    scp dm8168_gpio.ko root@target:/tmp/
    ssh root@target "insmod /tmp/dm8168_gpio.ko"
    # 確認 /dev/gpio 出現
    
  2. 用戶態程式:
    arm-none-linux-gnueabi-gcc -o gpio_test gpio_test.c
    scp gpio_test root@target:/tmp/
    ssh root@target "/tmp/gpio_test 1"  # 輸出高電平 3.3V
    ssh root@target "/tmp/gpio_test 0"  # 輸出低電平 0V
    
  3. 示波器/萬用表驗證:測量 GP1[31] 腳位電壓變化。

常見問題排查

現象可能原因對策
insmod 後無 /dev/gpio主設備號衝突 / class_create 失敗查看 dmesg,改用動態分配主設備號
寫入無電壓變化寄存器映射地址錯 / PINMUX 被佔用核對 TRM 手冊,檢查 ioremap 返回值
權限拒絕/dev/gpio 權限 0600chmod 666 /dev/gpio 或 udev 規則
編譯報錯 implicit declaration缺少 #include <linux/io.h>補全頭文件

擴展方向

  • 支援 poll/select 中斷檢測輸入電平變化。
  • 實現 ioctl 批量配置多腳位方向/電平。
  • 整合到 sysfs (/sys/class/gpio/) 符合 Linux GPIO 子系統規範。
  • 使用 Device Tree 宣告 GPIO 控制器,驅動自動匹配,無硬編碼地址。

總結

DM8168 GPIO 驅動核心在於:
ioremap 映射物理寄存器 → 字符設備 write 處理用戶指令 → 直接操作 DIR/DATAOUT 寄存器。
本例以最小代碼驗證 GP1[31] 輸出控制,為後續完善 GPIO 子系統奠定基礎。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved