一、概述
GPIO是非常典型的字符型设备,相对比较简单。
测试环境:TQ2440开发板+Linux2.6.30.4。
二、编写驱动程序实例
1、编写驱动程序
驱动程序源文件EmbedSky_gpio.c。
#include <linux/miscdevice.h> #include <linux/delay.h> #include <asm/irq.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/mm.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/delay.h> #include <linux/moduleparam.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/string.h> #include <linux/list.h> #include <linux/pci.h> #include <asm/uaccess.h> #include <asm/atomic.h> #include <asm/unistd.h> #define DEVICE_MAJOR 221 #define DEVICE_NAME "GPIO-Control" /* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */ #define IOCTL_GPIO_ON 1 #define IOCTL_GPIO_OFF 0 /* 用来指定LED所用的GPIO引脚 */ static unsigned long gpio_table [] = { S3C2410_GPB5, S3C2410_GPB6, S3C2410_GPB7, S3C2410_GPB8, }; /* 用来指定GPIO引脚的功能:输出 */ static unsigned int gpio_cfg_table [] = { S3C2410_GPB5_OUTP, S3C2410_GPB6_OUTP, S3C2410_GPB7_OUTP, S3C2410_GPB8_OUTP, };
/* cmd 1 打开LED |0 关闭LED; arg 操作LED的编号 取值范围1-4 */ static int tq2440_gpio_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { if (arg > 4) { return -EINVAL; } switch(cmd) { case IOCTL_GPIO_ON: // 设置指定引脚的输出电平为0 s3c2410_gpio_setpin(gpio_table[arg], 0); return 0; case IOCTL_GPIO_OFF: // 设置指定引脚的输出电平为1 s3c2410_gpio_setpin(gpio_table[arg], 1); return 0; default: return -EINVAL; } } static struct file_operations dev_fops = { .owner = THIS_MODULE, .ioctl = tq2440_gpio_ioctl, }; static int __init dev_init(void) { int ret; int i; for (i = 0; i < 4; i++) { s3c2410_gpio_cfgpin(gpio_table[i], gpio_cfg_table[i]); s3c2410_gpio_setpin(gpio_table[i], 1); } ret = register_chrdev(DEVICE_MAJOR, DEVICE_NAME, &dev_fops ); if (ret < 0) { printk("<1>"DEVICE_NAME " can't register major number.\n"); return ret; } printk("<1>"DEVICE_NAME " initialized.\n"); return 0; } static void __exit dev_exit(void) { unregister_chrdev(DEVICE_MAJOR, DEVICE_NAME); printk("<1>"DEVICE_NAME " removed.\n"); } module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("www.embedsky.net"); MODULE_DESCRIPTION("GPIO control for EmbedSky SKY2440/TQ2440 Board");
2、编写Makefile文件
KERN_DIR = /opt/EmbedSky/linux-2.6.30.4 all: make -C $(KERN_DIR) M=`pwd` modules clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order obj-m += EmbedSky_gpio.o
三、驱动程序测试
1、编译驱动前说明
驱动文件所在的目录不是Linux内核源码的子目录。
驱动文件所在目录为“opt/EmbedSky/work/test”。
Linux内核源码所在目录为“/opt/EmbedSky/linux-2.6.30.4”。
2、在Ubuntu下编译驱动
注意:在编译驱动之前要先编译Linux内核镜像,否则驱动编译不通过。
#cd /opt/EmbedSky/work/test
#make
编译成功后,会看到一个ko文件。
3、在Ubuntu下拷贝驱动到开发板
#cd /opt/EmbedSky/root_nfs/lib #cp /opt/EmbedSky/work/EmbedSky_hello/EmbedSky_gpio.ko ./
说明:开发板使用nfs根文件系统启动,使用Ubuntu共享的根文件系统所在目录为“/opt/EmbedSky/root_nfs“。
4、在开发板上安装驱动
开发板Linux系统从nfs根文件系统启动。
#cd /lib
#insmode EmbedSky_hello.ko
可以观察到开发板4盏LED灯都熄灭了。(因为驱动程序加载的时候,把4盏灯的初始胡状态设置为关闭状态)
5、查看驱动注册信息

四、应用程序测试驱动
1、编写应用程序代码
ledstest.c内容如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> int main(int argc, char **argv) { int cmd; int led_no; int fd; if (argc != 3) { fprintf(stderr, "Usage: leds led_no on|off\n"); exit(1); } if (sscanf(argv[1], "%d", &led_no) != 1 || led_no < 1 || led_no > 4) { fprintf(stderr, "Usage: leds led_no on|off\n"); exit(1); } if (!strcmp(argv[2], "on")) { cmd = 1; } else if (!strcmp(argv[2], "off")) { cmd = 0; } else { exit(1); } fd = open("/dev/leds", 0); if (fd < 0) { fprintf(stderr, "can't open!\n"); exit(1); } ioctl(fd, cmd, (led_no-1)); close(fd); return 0; }
2、在Ubuntu上编译应用程序
#arm-linux-gcc ledstest.c -o ledstest
通过这条命令,就能编译出一个上层应用ledstest。
3、在Ubuntu下拷贝上层应用测试程序到开发板
#cd /opt/EmbedSky/root_nfs/usr/bin #cp /opt/EmbedSky/work/test/ledstest ./
4、在开发板Linux系统上创建设备节点
#mknod /dev/hellodev c 231 0
5、执行测试代码
#cd /usr/bin
#./ledstest
测试结果:

参考资料:《嵌入式Linux应用开发完全手册》
浙公网安备 33010602011771号