/dev/gpio 觸發。| 寄存器 | 物理地址 | 偏移 | 功能 |
|---|---|---|---|
| GPIO1_DIR | 0x4804C134 | 0x134 | 方向控制:0=輸出, 1=輸入 |
| GPIO1_DATAOUT | 0x4804C13C | 0x13C | 數據輸出寄存器 |
注意:DM8168 GPIO 每組 32 位,BIT31 對應 GP1[31]。
#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");
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
#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; }
make # 產生 dm8168_gpio.ko scp dm8168_gpio.ko root@target:/tmp/ ssh root@target "insmod /tmp/dm8168_gpio.ko" # 確認 /dev/gpio 出現
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
| 現象 | 可能原因 | 對策 |
|---|---|---|
| insmod 後無 /dev/gpio | 主設備號衝突 / class_create 失敗 | 查看 dmesg,改用動態分配主設備號 |
| 寫入無電壓變化 | 寄存器映射地址錯 / PINMUX 被佔用 | 核對 TRM 手冊,檢查 ioremap 返回值 |
| 權限拒絕 | /dev/gpio 權限 0600 | chmod 666 /dev/gpio 或 udev 規則 |
編譯報錯 implicit declaration | 缺少 #include <linux/io.h> | 補全頭文件 |
poll/select 中斷檢測輸入電平變化。ioctl 批量配置多腳位方向/電平。sysfs (/sys/class/gpio/) 符合 Linux GPIO 子系統規範。DM8168 GPIO 驅動核心在於:
ioremap 映射物理寄存器 → 字符設備 write 處理用戶指令 → 直接操作 DIR/DATAOUT 寄存器。
本例以最小代碼驗證 GP1[31] 輸出控制,為後續完善 GPIO 子系統奠定基礎。