armA9单片机liunxOS下led驱动开发

匿名 (未验证) 提交于 2019-12-03 00:30:01

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


步骤:

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

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

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

驱动代码

#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/slab.h>  #include <asm/uaccess.h> #include <asm/io.h>  //设计一个类型,描述一个设备的信息 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 <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h>   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;  }    

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!