第12课第2.3节 字符设备驱动程序之LED驱动程序_操作LED(四)

第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;
}

猜你喜欢

转载自blog.csdn.net/xiaodingqq/article/details/80272460