在开发板上,有三个LED灯.如何通过应用程序点亮这三个灯如何编写驱动程序
操作硬件的时候,我们需要准备开发板的原理图和开发手册,,根据这两个文档来进行配置
在source insight 编写代码
1 第一个led驱动程序
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> static struct class *firstled_drv_class; static struct class_device *firstled_drv_class_dev; volatile unsigned long *gpfcon = NULL;//指向控制寄存器的地址的指针 volatile unsigned long *gpfdat = NULL;//指向数据寄存器的地址的指针 static int firstled_drv_open(struct inode *inode, struct file *file) { //printk("firstled_drv_open\n"); /* 配置GPF4,5,6为输出 */ *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));//将 4 5 6 位清零 *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));// 将 4 5 6 设为1 输出引脚 return 0; } static ssize_t firstled_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { int val; //printk("firstled_drv_write\n"); copy_from_user(&val, buf, count); // copy_to_user(); if (val == 1) { // 点灯 *gpfdat &= ~((1<<4) | (1<<5) | (1<<6)); } else { // 灭灯 *gpfdat |= (1<<4) | (1<<5) | (1<<6); } return 0; } /*定义一个file_operations结构体 怎么用 告诉内核? */ static struct file_operations firstled_drvfops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = firstled_drv_open, .write = firstled_drv_write, }; int major; //驱动的入口 写出来是一般的函数 需要修饰 static int firstled_drv_init(void) { //注册告诉内核 主设备号 名字(随便写) 结构体 //app找到这个驱动 不是根据名字 是根据 设备类型 主设备号 //主设备号 随便写个123 写 0会自动分配 major = register_chrdev(0, "firstled_drv", &firstled_drv_fops); // 注册, 告诉内核 firstled_drv_class = class_create(THIS_MODULE, "firstled_drv"); firstled_drv_class_dev = class_device_create(firstled_drv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); gpfdat = gpfcon + 1; return 0; } static void firstled_drv_exit(void) { //123 unregister_chrdev(major, "firstled_drv"); // 卸载 class_device_unregister(firstled_drv_class_dev); class_destroy(firstled_drv_class); iounmap(gpfcon); } //通过修饰 成为入口函数 内核会找到这个函数 //对与驱动 是c module_init(firstled_drv_init); //通过修饰 成为出口函数 内核来调用 //用宏来定义一个指针 module_exit(firstled_drv_exit); MODULE_LICENSE("GPL");
2 编写makefile文件
KERN_DIR = /work/system/linux-2.6.22.6 all: make -C $(KERN_DIR) M=`pwd` modules clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order obj-m += firstled_drv.o
将代码上传到linux服务器,编译 放到网络文件系统上,进入开发板,挂接驱动,
insmod firstled_drv.ko
# cat /proc/devices Character devices: 1 mem 2 pty 3 ttyp 4 /dev/vc/0 4 tty 4 ttyS 5 /dev/tty 5 /dev/console 5 /dev/ptmx 6 lp 7 vcs 10 misc 13 input 14 sound 29 fb 90 mtd 99 ppdev 116 alsa 128 ptm 136 pts 180 usb 189 usb_device 204 s3c2410_serial 252 firstled_drv 253 usb_endpoint 254 rtc Block devices: 1 ramdisk 7 loop 8 sd 31 mtdblock 65 sd 66 sd 67 sd 68 sd 69 sd 70 sd 71 sd 128 sd 129 sd 130 sd 131 sd 132 sd 133 sd 134 sd 135 sd 179 mmc
创建设备目录节点
# mknod /dev/xyz c 252 0
3 编写测试程序
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> /* firstleddrvtest on * firstleddrvtest off */ int main(int argc, char **argv) { int fd; int val = 1; fd = open("/dev/xyz", O_RDWR); if (fd < 0) { printf("can't open!\n"); } if (argc != 2) { printf("Usage :\n"); printf("%s <on|off>\n", argv[0]); return 0; } if (strcmp(argv[1], "on") == 0) { val = 1; } else { val = 0; } write(fd, &val, 4); return 0; }
上传到服务器,编译,放到网络文件系统
arm-linux-gcc ledtest firstleddevtest.c cp firstleddevtest /work/nfs_root/first_fs
在开发板上运行程序
# cd /mnt/ # ./firstled_drv_test first_drv_open Usage : ./firstled_drv_test <on|off> # ./firstled_drv_test off first_drv_open first_drv_write # ./firstled_drv_test on first_drv_open first_drv_write # ./firstled_drv_test off first_drv_open first_drv_write #
看到开发板灯亮灯灭
4 继续编写LED驱动代码
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> #define DEVICE_NAME "leds" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */ #define LED_MAJOR 231 /* 主设备号 */ static struct class *leds_class; static struct class_device *leds_class_devs[4]; /* bit0<=>D10, 0:亮, 1:灭 * bit1<=>D11, 0:亮, 1:灭 * bit2<=>D12, 0:亮, 1:灭 */ static char leds_status = 0x0; static DECLARE_MUTEX(leds_lock); // 定义赋值 //static int minor; static unsigned long gpio_va; #define GPIO_OFT(x) ((x) - 0x56000000) #define GPFCON (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000050))) #define GPFDAT (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000054))) /* 应用程序对设备文件/dev/leds执行open(...)时, * 就会调用s3c24xx_leds_open函数 */ static int s3c24xx_leds_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev); switch(minor) { case 0: /* /dev/leds */ { // 配置3引脚为输出 //s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP); GPFCON &= ~(0x3<<(4*2)); GPFCON |= (1<<(4*2)); //s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP); GPFCON &= ~(0x3<<(5*2)); GPFCON |= (1<<(5*2)); //s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP); GPFCON &= ~(0x3<<(6*2)); GPFCON |= (1<<(6*2)); // 都输出0 //s3c2410_gpio_setpin(S3C2410_GPF4, 0); GPFDAT &= ~(1<<4); //s3c2410_gpio_setpin(S3C2410_GPF5, 0); GPFDAT &= ~(1<<5); //s3c2410_gpio_setpin(S3C2410_GPF6, 0); GPFDAT &= ~(1<<6); down(&leds_lock); leds_status = 0x0; up(&leds_lock); break; } case 1: /* /dev/led1 */ { s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP); s3c2410_gpio_setpin(S3C2410_GPF4, 0); down(&leds_lock); leds_status &= ~(1<<0); up(&leds_lock); break; } case 2: /* /dev/led2 */ { s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP); s3c2410_gpio_setpin(S3C2410_GPF5, 0); leds_status &= ~(1<<1); break; } case 3: /* /dev/led3 */ { s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP); s3c2410_gpio_setpin(S3C2410_GPF6, 0); down(&leds_lock); leds_status &= ~(1<<2); up(&leds_lock); break; } } return 0; } static int s3c24xx_leds_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) { int minor = MINOR(filp->f_dentry->d_inode->i_rdev); char val; switch (minor) { case 0: /* /dev/leds */ { copy_to_user(buff, (const void *)&leds_status, 1); break; } case 1: /* /dev/led1 */ { down(&leds_lock); val = leds_status & 0x1; up(&leds_lock); copy_to_user(buff, (const void *)&val, 1); break; } case 2: /* /dev/led2 */ { down(&leds_lock); val = (leds_status>>1) & 0x1; up(&leds_lock); copy_to_user(buff, (const void *)&val, 1); break; } case 3: /* /dev/led3 */ { down(&leds_lock); val = (leds_status>>2) & 0x1; up(&leds_lock); copy_to_user(buff, (const void *)&val, 1); break; } } return 1; } static ssize_t s3c24xx_leds_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { //int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev); int minor = MINOR(file->f_dentry->d_inode->i_rdev); char val; copy_from_user(&val, buf, 1); switch (minor) { case 0: /* /dev/leds */ { s3c2410_gpio_setpin(S3C2410_GPF4, (val & 0x1)); s3c2410_gpio_setpin(S3C2410_GPF5, (val & 0x1)); s3c2410_gpio_setpin(S3C2410_GPF6, (val & 0x1)); down(&leds_lock); leds_status = val; up(&leds_lock); break; } case 1: /* /dev/led1 */ { s3c2410_gpio_setpin(S3C2410_GPF4, val); if (val == 0) { down(&leds_lock); leds_status &= ~(1<<0); up(&leds_lock); } else { down(&leds_lock); leds_status |= (1<<0); up(&leds_lock); } break; } case 2: /* /dev/led2 */ { s3c2410_gpio_setpin(S3C2410_GPF5, val); if (val == 0) { down(&leds_lock); leds_status &= ~(1<<1); up(&leds_lock); } else { down(&leds_lock); leds_status |= (1<<1); up(&leds_lock); } break; } case 3: /* /dev/led3 */ { s3c2410_gpio_setpin(S3C2410_GPF6, val); if (val == 0) { down(&leds_lock); leds_status &= ~(1<<2); up(&leds_lock); } else { down(&leds_lock); leds_status |= (1<<2); up(&leds_lock); } break; } } return 1; } /* 这个结构是字符设备驱动程序的核心 * 当应用程序操作设备文件时所调用的open、read、write等函数, * 最终会调用这个结构中指定的对应函数 */ static struct file_operations s3c24xx_leds_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = s3c24xx_leds_open, .read = s3c24xx_leds_read, .write = s3c24xx_leds_write, }; /* * 执行insmod命令时就会调用这个函数 */ static int __init s3c24xx_leds_init(void) //static int __init init_module(void) { int ret; int minor = 0; gpio_va = ioremap(0x56000000, 0x100000); if (!gpio_va) { return -EIO; } /* 注册字符设备 * 参数为主设备号、设备名字、file_operations结构; * 这样,主设备号就和具体的file_operations结构联系起来了, * 操作主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数 * LED_MAJOR可以设为0,表示由内核自动分配主设备号 */ ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops); if (ret < 0) { printk(DEVICE_NAME " can't register major number\n"); return ret; } leds_class = class_create(THIS_MODULE, "leds"); if (IS_ERR(leds_class)) return PTR_ERR(leds_class); leds_class_devs[0] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, 0), NULL, "leds"); /* /dev/leds */ for (minor = 1; minor < 4; minor++) /* /dev/led1,2,3 */ { leds_class_devs[minor] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, minor), NULL, "led%d", minor); if (unlikely(IS_ERR(leds_class_devs[minor]))) return PTR_ERR(leds_class_devs[minor]); } printk(DEVICE_NAME " initialized\n"); return 0; } /* * 执行rmmod命令时就会调用这个函数 */ static void __exit s3c24xx_leds_exit(void) { int minor; /* 卸载驱动程序 */ unregister_chrdev(LED_MAJOR, DEVICE_NAME); for (minor = 0; minor < 4; minor++) { class_device_unregister(leds_class_devs[minor]); } class_destroy(leds_class); iounmap(gpio_va); } /* 这两行指定驱动程序的初始化函数和卸载函数 */ module_init(s3c24xx_leds_init); module_exit(s3c24xx_leds_exit); /* 描述驱动程序的一些信息,不是必须的 */ MODULE_LICENSE("GPL");
编写makefile代码
KERN_DIR = /work/system/linux-2.6.22.6 all: make -C $(KERN_DIR) M=`pwd` modules clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order obj-m += myleds.o
测试程序
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> /* * ledtest <dev> <on|off> */ void print_usage(char *file) { printf("Usage:\n"); printf("%s <dev> <on|off>\n",file); printf("eg. \n"); printf("%s /dev/leds on\n", file); printf("%s /dev/leds off\n", file); printf("%s /dev/led1 on\n", file); printf("%s /dev/led1 off\n", file); } int main(int argc, char **argv) { int fd; char* filename; char val; if (argc != 3) { print_usage(argv[0]); return 0; } filename = argv[1]; fd = open(filename, O_RDWR); if (fd < 0) { printf("error, can't open %s\n", filename); return 0; } if (!strcmp("on", argv[2])) { // 亮灯 val = 0; write(fd, &val, 1); } else if (!strcmp("off", argv[2])) { // 灭灯 val = 1; write(fd, &val, 1); } else { print_usage(argv[0]); return 0; } return 0; }
运行程序
# mknod /dev/leds c 231 0 # mknod /dev/led1 c 231 1 # mknod /dev/led2 c 231 2 # mknod /dev/led3 c 231 3 # ./ledtest 0 Usage: ./ledtest <dev> <on|off> eg. ./ledtest /dev/leds on ./ledtest /dev/leds off ./ledtest /dev/led1 on ./ledtest /dev/led1 off # ./ledtest 1 Usage: ./ledtest <dev> <on|off> eg. ./ledtest /dev/leds on ./ledtest /dev/leds off ./ledtest /dev/led1 on ./ledtest /dev/led1 off # ./ledtest of Usage: ./ledtest <dev> <on|off> eg. ./ledtest /dev/leds on ./ledtest /dev/leds off ./ledtest /dev/led1 on ./ledtest /dev/led1 off # ./ledtest ooff Usage: ./ledtest <dev> <on|off> eg. ./ledtest /dev/leds on ./ledtest /dev/leds off ./ledtest /dev/led1 on ./ledtest /dev/led1 off # ./ledtest off Usage: ./ledtest <dev> <on|off> eg. ./ledtest /dev/leds on ./ledtest /dev/leds off ./ledtest /dev/led1 on ./ledtest /dev/led1 off # ./ledtest on Usage: ./ledtest <dev> <on|off> eg. ./ledtest /dev/leds on ./ledtest /dev/leds off ./ledtest /dev/led1 on ./ledtest /dev/led1 off # ./ledtest /dev/led1 off # ./ledtest /dev/led1 on # ./ledtest /dev/led2 off # ./ledtest /dev/led2 on # ./ledtest /dev/leds on # ./ledtest /dev/leds off # ./ledtest /dev/led3 off # ./ledtest /dev/led3 on # ./ledtest /dev/leds on