第12课第2.3节 字符设备驱动程序之LED驱动程序_操作LED(四)
写一个点LED的驱动:
一、框架
二、完善、硬件的操作:
a.看原理图、确定引脚;
b.看2440手册;
c.写代码:
单片机:操作物理地址
驱动:用ioremap函数来映射虚拟地址
★★★
void *ioremap(unsigned long phys_addr, unsigned long size)
phys_addr:要映射的物理地址
size:要映射的长度,单位是字节
ioremap是以页为单位进行映射,你的长度写为4,16,32等等,都会映射到4096字节的空间。
★★★
下面的代码中:
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); gpfdat = gpfcon + 1;①先映射GPFCON为虚拟地址。F寄存器为GPFCON:0x56000050,GPFDAT:0x56000054,GPFUP:0x56000058,Reserved:0x5600005c共4个,2440为32位CPU,所以4个4字节为16字节,故这里映射16字节。
②gpfdat=gpfcon+1;这里指针加1是以上面"unsigned long "为单位的。unsigned long的字节数是4个字节。
驱动程序first_drv.c
#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 *firstdrv_class; //一个类 static struct class_device *firstdrv_class_dev; //一个类里面再建立一个设备 volatile unsigned long *gpfcon = NULL; volatile unsigned long *gpfdat = NULL; static int first_drv_open(struct inode *inode, struct file *file) { //printk("first_drv_open\n"); /* 配置GPF4,5,6为输出 */ *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2))); *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2))); return 0; } static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { int val; //printk("first_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; } static struct file_operations first_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = first_drv_open, .write = first_drv_write, }; int major; /* 在VFS(虚拟文件系统)中实际是用一个数组来管理文件,而这个数组就是以主设备号作为索引 在注册驱动的时候,register_chrdev函数会按照major作为索引在对应位置填入file_operation */ int first_drv_init(void) { //第一个参数写0:让系统给我们自动分配,在数组中找个空缺,返回主设备号 major = register_chrdev(0, "first_drv", &first_drv_fops); //注册驱动程序,告诉内核 /* 下面这两条信息就会在sys目录下,建立firstdrv这个类,再firstdrv这个类下,会创建xyz这个设备 */ /* 然后mdev就会创建/dev/xyz这个节点 */ //创建一个类 firstdrv_class = class_create(THIS_MODULE, "firstdrv"); //在这个类下面再创建一个设备 firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); //映射:物理地址->虚拟地址 gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); //指向的是虚拟地址,第一个参数是物理开始地址,第二个是长度(字节) gpfdat = gpfcon + 1; //加1,实际加4个字节 return 0; } void first_drv_exit(void) { unregister_chrdev(major, "first_drv"); //卸载驱动 class_device_unregister(firstdrv_class_dev); class_destroy(firstdrv_class); iounmap(gpfcon); } module_init(first_drv_init); module_exit(first_drv_exit); MODULE_LICENSE("GPL");
用户程序firstdevtest.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> /* firstdrvtest on 打开 * firstdrvtest 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"); } //argc:传进来参数的个数 if (argc != 2) { printf("Usage :\n"); //Usage用法不对 //在linux,<>尖括号表示参数不能省略,|或符号表示任选一个(on或者off) printf("%s <on|off>\n",argv[0]); return 0; } //第二个参数 等于 on if (strcmp(argv[1], "on") == 0) { val = 1; } else { val = 0; } write(fd, &val, 4); return 0; }