初入andorid驱动开发之字符设备(二)
来源:互联网 发布:淘宝卖家延长发货时间 编辑:程序博客网 时间:2024/06/05 03:11
上一部分,主要说了一下,最简单的字符设备,主要实现在内核中打印的功能,实际中,没有多大用处,这章主要讲如何点亮一个LED灯,并编写测试程序:
1 测试程序的编写:
1.1 Android.mk 文件:
LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS) LOCAL_SRC_FILES:= led.c LOCAL_MODULE:= led LOCAL_MODULE_TAGS := eng LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog LOCAL_SHARED_LIBRARIES += \ libcutils libutilsinclude $(BUILD_EXECUTABLE)简单的介绍一下.mk的内容:
LOCAL_PATH:= $(call my-dir) : 这是必须的,指明你当前的文件的路径。(一般用NDK或者在android源码下用mm编译)
include $(CLEAR_VARS): 必要的,主要清除一些模块的变量。
LOCAL_SRC_FILES:= led.c 必要的,编译该模块的源代码文件。
LOCAL_MODULE:= led : 必须的,给你编出的模块命名。如编译共享库,还会自动补充命名。
LOCAL_MODULE_TAGS := eng :可选的,user: 指该模块只在user版本下才编译 eng: 指该模块只在eng版本下才编译
tests: 指该模块只在tests版本下才编译 optional:指该模块在所有版本下都编译
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog :需要时用。指明编译该模块时,需要加载在目录下的库。
LOCAL_SHARED_LIBRARIES += \
libcutils libutils :需要时用。指明编译该模块所依赖的共享库。
include $(BUILD_EXECUTABLE) : 以一个可执行程序的方式编译。
这个mk文件就是编译一个可执行程序led。想详细了解mk相关的知识,可参考下面的博客:
http://blog.csdn.net/cs_lht/article/details/6803638
http://blog.csdn.net/hudashi/article/details/7059006
1.2 测试程序的编写:
#include <stdlib.h>#include <stdio.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h> /*Unix 标准函数定义*/#include <fcntl.h> /*文件控制定义*/#include <errno.h> #include <cutils/log.h>//#include <delay.h>int main(int argc,char*argv[]){int fd=-1;printf("open start \n");fd = open("/dev/led", O_RDWR);if (fd < 0){//open device failed printf("open fd error: %s\n", strerror(errno)); exit(-1); }printf("write 1 \n");write(fd,"1",1);sleep(2);printf("write 0 \n");write(fd,"0",1);close(fd);return 0;}
这里主要介绍main的一些知识:
当insmod xx.ko之后,若成功在/dev下会生成我们定义设备名的设备节点。
应该明确,我们做驱动开发,应该明确的明白,正在开发的模块,它的项目需求。而根据需求,具体去划分把实现该需求的方法在哪一层次实现,这需要具体模块具体分析。对于LED的项目需求,无非就是控制暗、灭。那谁去控制led的状态,肯定是由用户,或者上层的app。所以,下层驱动只需要提供write的方法即可,从上层读出控制信息,然后判断控制状态,从而控制led的亮灭。
此测试程序,就是一个最简单的测试流程。
1. 打开设备。fd = open("/dev/led", O_RDWR); 这里需要了解一些open函数的第二个参数,以什么方式打开,如读写、非阻塞等。
2. 对设备操作。无非就是read、write、ioctl、poll等。这里主要是写控制状态给驱动程序。1 : 灯亮(write(fd,"1",1););0 : 灯灭(write(fd,"0",1);)。
3. 关闭设备。 close(fd);
2. LED驱动程序的编写:
2.1 程序代码:
<span style="font-size:14px;">#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/input.h>#include <linux/platform_device.h>#include <linux/miscdevice.h>#include <mach/gpio.h>#include <linux/io.h>#include <mach/hardware.h>#include <linux/delay.h>#include <asm/irq.h>#include <asm/uaccess.h>static struct class *leddrv_class;static struct device *leddrv_class_dev;int major;static unsigned long gpio_va;//gpio ADDERSS#define GPIO_OFT(x) ((x) - 0xE0200000)//GPBCON add#define GPBCON (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0xE0200040)))//GPBDAT add#define GPBDAT (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0xE0200044)))static int led_drv_open(struct inode *inode, struct file *file){printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>led_drv_open\n");//set the GPB3 output GPBCON &= ~(0xf<<(4*3)); GPBCON |= (1<<(4*3)); //init the GPB3 output 0 LED OFF GPBDAT &= ~(1<<3);return 0;}static int led_drv_read(struct file *filp, char __user *buff, size_t count, loff_t *offp){ char val;char leds_status;leds_status=1; copy_to_user(buff, (const void *)&leds_status, 1); return 1;}static int led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos){ char val[1]; copy_from_user(val,buf,1);#if 0 //user the system interfaceprintk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>led_drv_write\n");gpio_set_value(S5PV210_GPB(3), ((val[0]=='1')?1:0));printk("led--%s %d \n",__FUNCTION__,(int)val[0]);#endif#if 1 printk("val[0]=%d >>>>>>>>>>>>>>>>>>>>\n",val[0]);if(val[0] == '1' || val[0] == "1"){printk("%s 1\n",__FUNCTION__);GPBDAT |= 1<<3; //output 1 led on}else{printk("%s 0\n",__FUNCTION__);</span>GPBDAT &= ~(1<<3);<span style="font-size:14px;">// out 0 led off}#endifreturn 0;}static struct file_operations led_drv_fops = { .owner = THIS_MODULE, .open = led_drv_open, .write=led_drv_write, .read=led_drv_read,};static int led_drv_init(void){printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>led_drv_init\n"); gpio_va = ioremap(0xE0200000, 0x100000);if (!gpio_va) {return -EIO;}major = register_chrdev(0, "led_drv", &led_drv_fops); leddrv_class = class_create(THIS_MODULE, "leddrv");leddrv_class_dev = device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "led"); return 0;}static void led_drv_exit(void){printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>led_drv_exit\n");unregister_chrdev(major, "led_drv"); device_unregister(leddrv_class_dev);class_destroy(leddrv_class);iounmap(gpio_va);}module_init(led_drv_init);module_exit(led_drv_exit);MODULE_LICENSE("GPL");</span>
2.2 对硬件的分析:
我的开发板设备是S5PV210的,其子GPB3连接的LED灯,GPB3输出高电平灯亮,反之则暗。
根据硬件原理图的分析,我们知道,首先需要把GPB3设为输出,然后根据读到的控制命令,控制引脚输出高低电平。
第一步:把GPB3设置为输出引脚,所以我们需要设置该引脚的控制寄存器, 1. GPBCON &= ~(0xf<<(4*3)) 把GPBCON[3]设为0 2. GPBCON |= (1<<(4*3)) 把GPBCON[3]设为1,即为输出。
第二步:控制GPB3输出高低电平,所以我们需要设置该引脚的数据寄存器,1. GPBDAT |= 1<<3 把GPBDAT[3]设置为1,输出高电平 2. GPBDAT &= ~(1<<3) 把GPBDAT[3]设置为0,输出低电平。
上面两步,这是我们当初学习51单片机最基本的操作,但是,当时是在裸版下玩的,可以直接控制寄存器,现在我们是基于android下玩,内核层是基于linux框架的,当然需要按照它的机制来。这里假如你现在需要了解详细的,你需要好好研究linux。
2.3 对代码几个关键点的讲解
1. ioremap、iounmap
要想真正的理解这两个函数,你需要对linux的内存的机制和操作有着较多的研究,了解现代的内存管理机制,页机制等等,这里只简单的介绍一下函数的使用和一些基本知识点。如需详细了解可看《深入理解linux内核》这本书。
ioremap(unsigned long phys_addr, unsigned long size)
入口: phys_addr:要映射的起始的IO地址;
size:要映射的空间的大小;
功能: 将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问;
在内核访问这些地址必须分配给这段内存以虚拟地址,这正是ioremap的意义所在 ,需要注意的是,物理内存已经"存在"了,无需alloc page给这段地址了. 为了使软件访问I/O内存,必须为设备分配虚拟地址.这就是ioremap的工作.这个函数专门用来为I/O内存区域分配虚拟地址(空间).对于直接映射的I/O地址ioremap不做任何事情。
2. copy_from_user copy_to_user
这里需要了解运行态,用户空间、内核空间及其的权限等。
static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
功能:将用户空间中的内容拷贝到内核空间中
参数:*to:目的地址,内核空间
*from:源地址,用户空间
n: 复制内容的字节数
这个led驱动程序,主要用到这个函数,即write中,从用户空间读数据,然后判断控制状态,从而控制引脚输出高低电平。
static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
功能:将内核空间中的内容拷贝到用户空间中
参数:*to:目的地址,用户空间
*from:源地址,内核空间
n: 复制内容的字节数
注意:在使用这两个函数和上面的函数时候,最好加上判断,若失败,则退出,有时还有释放之前申请的资源。在使用这两个函数,需特别注意,小心溢出的问题,容易引起oops的问题。
到此,LED的字符驱动基本完毕了,关于ioremap的大小,你可以自己按照你控制的需求来定义大小了,我这随便定义的。整套的测试,把驱动生成的KO,push到设备中,把生成的测试程序push到设备中,注意更改该权限(chmod 777 xxx),然后insmod xx.ko,最后./xxx,则你会看到你的led的变化。
- 初入andorid驱动开发之字符设备(二)
- 初入android驱动开发之字符设备(一)
- 初入android驱动开发之字符设备(四-中断)
- 初入android驱动开发之字符设备(五-定时器)
- 初入android驱动之字符设备(三)
- 设备驱动之二----字符设备驱动
- Linux 设备驱动之字符设备(二)
- Linux 设备驱动之字符设备(二)
- Linux驱动模型学习(二)---字符设备驱动模型之二---初窥字符设备驱动
- 初入android驱动开发之网络设备以太网(二)
- linux设备驱动(二)---字符设备之按键驱动
- 嵌入式Linux驱动开发(二)——字符设备驱动之控制LED
- 字符设备驱动(二)
- 字符设备驱动开发之数据结构
- 开发学习记录之字符设备驱动
- Linux驱动开发之字符设备
- linux驱动开发之字符设备框架
- linux驱动开发之字符设备框架
- Android NDK学习 <二> Android.mk的制作
- 怎样在Transformer+文档保护中使用数字签名
- 视频叠加字幕显示原理与实现方法
- .NET PDF转图片
- Android Studio - local path doesn't exist
- 初入andorid驱动开发之字符设备(二)
- Android.mk 文件语法详解
- 解决KindEditor富文本编辑器model弹出层上文本框不能输入的问题(二)
- CS0234: 命名空间“System.Web.Mvc”中不存在类型或命名空间名称“Ajax” 解决方法
- Android中级联列表ExpandableListView使用
- java代码--实心,空心图形
- /*+APPEND*/插入性能总结
- Laravel配置安装
- MongoDB导出、备份、恢复