armA9单片机liunxOS下led驱动开发

led控制除了在裸机开发中使用寄存器编程以外,当板子运行linux操作系统时,需要在内核进行驱动,应用程序通过设备文件的IO接口,操作内核驱动中的相关函数,通过地址映射,进而控制寄存器的地址的值。


步骤:

1.实现模块加载和卸载入口函数

2.在模块加载函数中实现 a.申请设备号(register_chrdev()) b.常见设备文件(class_create()和device_create()) c.将寄存器物理地址映射为内核虚拟地址(ioremap())、申请中断以及配置寄存器(readl()和writel())

3.实现file_operations结构体中的相关open、read、 write等函数。

驱动代码

#include 
#include 
#include 
#include 
#include 

#include 
#include 

//设计一个类型,描述一个设备的信息
struct led_desc{
	unsigned int dev_major; //设备号
	struct class *cls;
	struct device *dev; //创建设备文件
	void *reg_virt_base; //表示是寄存器地址到基准值
};


//物理地址
#define GPX2_CON 0x11000C40  
#define GPX2_SIZE 8



struct led_desc *led_dev;//表示一个全局的设备对象


static int kernel_val = 555;


//  read(fd, buf, size);
ssize_t led_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
{
	printk("-------%s-------\n", __FUNCTION__);

	int ret;

	ret = copy_to_user(buf,  &kernel_val, count);
	if(ret > 0)
	{
		printk("copy_to_user error\n");
		return -EFAULT;
	}
	
	
	return 0;

}
ssize_t led_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
	//printk("-------%s-------\n", __FUNCTION__);


	int ret;
	int value;

	
	ret = copy_from_user(&value,  buf, count);
	if(ret > 0)
	{
		printk("copy_to_user error\n");
		return -EFAULT;
	}

	if(value){
		writel( readl(led_dev->reg_virt_base + 4) | (1<<7),   led_dev->reg_virt_base + 4 );
		
	}else{
		writel( readl(led_dev->reg_virt_base + 4) & ~(1<<7),   led_dev->reg_virt_base + 4 );
	}
	
	return 0;
	
}
int led_drv_open(struct inode *inode, struct file *filp)
{
	printk("-------%s-------\n", __FUNCTION__);
	
	return 0;
}


int led_drv_close(struct inode *inode, struct file *filp)
{
	printk("-------%s-------\n", __FUNCTION__);
	return 0;

}

const struct file_operations my_fops = {
	.open = led_drv_open,
	.read = led_drv_read,
	.write = led_drv_write,
	.release = led_drv_close,
};

static int __init led_dev_init(void)
{
	int ret;

	// 0, 实例化全局的设备对象--分配空间
	led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);
	if(led_dev == NULL)
	{
		printk(KERN_ERR "malloc error\n");
		return -ENOMEM;
	}


	// 1, 一般都是申请设备号资源
	// 申请设备号
	led_dev->dev_major = register_chrdev(0, "led_dev_test", &my_fops);
	if(led_dev->dev_major < 0)
	{
		printk(KERN_ERR "register_chrdev error\n");
		ret = -ENODEV;
		goto err_0;
	}

	// 2,创建设备文件
	led_dev->cls = class_create(THIS_MODULE, "led_cls");
	if(IS_ERR(led_dev->cls))
	{
		printk(KERN_ERR "class_create error\n");
		ret = PTR_ERR(led_dev->cls); //将指针出错的具体原因转换成一个出错码
		goto err_1;
	}

	// /dev/led0
	led_dev->dev = device_create(led_dev->cls, NULL, 
				MKDEV(led_dev->dev_major, 0), NULL, "led%d", 0);
	if(IS_ERR(led_dev->dev))
	{
		printk(KERN_ERR "device_create error\n");
		ret = PTR_ERR(led_dev->dev); //将指针出错的具体原因转换成一个出错码
		goto err_2;
	}

	// 3,硬件初始化
	// 对地址进行映射
	led_dev->reg_virt_base = ioremap(GPX2_CON, GPX2_SIZE);
	if(led_dev->reg_virt_base == NULL)
	{
		
		printk(KERN_ERR "ioremap error\n");
		ret = -ENOMEM;
		goto err_3;
	}

	// gpio的输出功能的配置
	u32 value = readl(led_dev->reg_virt_base);
	value &= ~(0xf<<28);
	value |= (0x1<<28);
	writel(value, led_dev->reg_virt_base);	
	
	return 0;
	

err_3:
	device_destroy(led_dev->cls, MKDEV(led_dev->dev_major, 0));

err_2:
	class_destroy(led_dev->cls);

err_1:
	unregister_chrdev(led_dev->dev_major, "led_dev_test");

err_0:
	kfree(led_dev);
	return ret;

}

static void __exit led_dev_exit(void)
{
	//一般都是释放资源
	iounmap(led_dev->reg_virt_base);
	device_destroy(led_dev->cls, MKDEV(led_dev->dev_major, 0));
	class_destroy(led_dev->cls);
	unregister_chrdev(led_dev->dev_major, "led_dev_test");
	kfree(led_dev);

}

//驱动模块加载和卸载函数申明
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");

编写Makefile将驱动程序编译成ko文件

Makefile


ROOTFS_DIR = /opt/4412/rootfs
MODULE_NAME = led_drv
APP_NAME = led_test
CROSS_COMPILE = /home/george/Linux_4412/toolchain/gcc-4.6.4/bin/arm-none-linux-gnueabi-
CC = $(CROSS_COMPILE)gcc
ifeq ($(KERNELRELEASE), )
KERNEL_DIR = /home/george/Linux_4412/kernel/linux-3.14
CUR_DIR = $(shell pwd)
all :
	make -C  $(KERNEL_DIR) M=$(CUR_DIR) modules
	$(CC) $(APP_NAME).c  -o $(APP_NAME)
clean :
	make -C  $(KERNEL_DIR) M=$(CUR_DIR) clean
	rm -rf $(APP_NAME)	
install:
	cp -raf *.ko $(APP_NAME)   $(ROOTFS_DIR)/drv_module
else
	obj-m += $(MODULE_NAME).o
endif

在板子上的linux系统上输入

insmod led_drv.ko 并且 cat /proc/devices查看设备号

应用程序

#include 
#include 
#include 
#include 
#include 
#include 
#include 


int main(int argc, char *argv[])
{
	//调用驱动

	int fd;
	int value = 0;

	fd = open("/dev/led0", O_RDWR);
	if(fd < 0)
	{
		perror("open");
		exit(1);
	}

	read(fd, &value, 4);

	printf("___USER___:  value = %d\n", value);


	//应用程序去控制灯到亮和灭

	while(1)
	{
		value = 0;
		write(fd, &value, 4);
		sleep(1);

		value = 1;
		write(fd, &value, 4);
		sleep(1);
		
	}


	close(fd);

	return 0;

}




你可能感兴趣的:(驱动开发)