Android驱动例子(LED灯控制)

来源:互联网 发布:全球网络摄像头直播 编辑:程序博客网 时间:2024/04/29 06:31

文章来源:http://blog.csdn.net/ok138ok/article/details/6317212


本例子,讲述在android2.1上完全自已开发一个驱动去控制硬件口并写应用测试该驱动,通过这样一个例子,解析Android下的驱动开发流程的应用调用流程,可以说是很好的入门引导

要达到的效果:通过android的应用,调用驱动程序,在开发板上控制4个LED的亮灭。

一、硬件原理

clip_image001

如上图,通过4个IO口控制这LED,低电平LED亮,

这4个IO口分别是GPM1, GPM2, GPM3, GPM4,

二、驱动程序

1、在kernel文件夹下的driver目录,新键驱动文件夹

# cd kernel_android_2.6.28.6/drivers

进到开发板的kernel目录,建驱动文件夹

#mkdir ledtest

2、在/driver/ledtest目录下,新建leddriver.c ,leddriver.h , Kconfig, Makefile 等4个文件

leddriver.c

#include <linux/kernel.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/init.h>            /* For __init/__exit/... */#include <linux/module.h>#include <mach/hardware.h>#include <asm/io.h>#include <asm/uaccess.h>#include <plat/gpio-cfg.h>#include <plat/regs-clock.h>#include <plat/regs-lcd.h>#include <plat/regs-gpio.h>#include <mach/map.h>#include <linux/gpio.h>#include <plat/gpio-bank-m.h>#include <linux/cdev.h>#include <linux/fs.h> //for register_chrdev()#include <linux/device.h>#include <mach/map.h>#include "leddriver.h"#include <linux/miscdevice.h>        /* For MODULE_ALIAS_MISCDEV                            (WATCHDOG_MINOR) */#include <linux/watchdog.h>        /* For the watchdog specific items */#include <linux/fs.h>            /* For file operations */#define Viberator_MAJOR        97  //主设备号#define SCULL_NR_DEVS           4#define SCULL_QUANTUM         4000#define SCULL_QSET             1000//---do as the GIO driver#define DEVCOUNT                4#define GIO_MINOR            2    /* GIO minor no. */static dev_t dev;        //申  请  到的主  设备  号static struct cdev *cdev_p;static int openCnt;//--全局变量------------int VIB_major  = 97;//we asigment it for testint VIB_minor = 0;int VIB_nr_devs = SCULL_NR_DEVS;int VIB_quantum = SCULL_QUANTUM;int VIB_qset = SCULL_QSET;static struct class *vib_dev_class;#define GPNCON  S3C64XX_GPNCON #define GPNDAT  S3C64XX_GPNDAT#define GPNPUD  S3C64XX_GPNPUD#define GPMCON S3C64XX_GPMCON#define GPMDAT  S3C64XX_GPMDAT#define GPMPUD S3C64XX_GPMPUD#define VIB_ON 0x11#define  VIB_OFF 0x22static const struct file_operations GPIO_Viberator_ctl_ops={    .owner        = THIS_MODULE,    .open    = GPIO_VIB_open,    .read    =GPIO_VIB_read,    .write    =GPIO_VIB_write,    .ioctl     = GPIO_VIB_ioctl,    .release     =GPIO_VIB_release,};ssize_t GPIO_VIB_read(struct file * file,char * buf,size_t count,loff_t * f_ops){       char sdas[5]={1,3,8,1,6};       ssize_t            status = 5;    unsigned long    missing;    missing = copy_to_user(buf, sdas, status);    if (missing == status)        status = -EFAULT;    else        status = status - missing;          printk(" GPIO_VIB_read = %d ,count = %d/r/n",status,count);    gpio_direction_output(S3C64XX_GPM(3), 0);// return status ; }ssize_t GPIO_VIB_write (struct file * file,const char * buf, size_t count,loff_t * f_ops){    unsigned long missing;     char inbuffer[512]={0};    int i;     missing = copy_from_user(inbuffer, buf, count);    printk(" GPIO_VIB_write count = %d missing =%d /r/n",count,missing);    gpio_direction_output(S3C64XX_GPM(3), 1);//     for(i=0;i<count;i++)        {            printk("Write:inbuffer[%d]=%d /r/n",i,inbuffer[i]);        } return count;}//ssize_t GPIO_VIB_ioctl(struct inode *  inode,struct file * file,unsigned int cmd, long data)static int GPIO_VIB_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){    printk(KERN_ERR"VIB:GPIO_VIB_ioctl --CMD=%x /n",cmd);    switch(cmd)        {           case VIB_ON:            gpio_direction_output(S3C64XX_GPM(1), 0);//             gpio_direction_output(S3C64XX_GPM(2), 0);//             gpio_direction_output(S3C64XX_GPM(3), 0);//             gpio_direction_output(S3C64XX_GPM(4), 1);//             printk(KERN_ERR"VIB:GPIO_VIB_ioctl --VIB_ON/n");            break;         case VIB_OFF:            {            printk(KERN_ERR"VIB:GPIO_VIB_ioctl --VIB_OFF/n");            gpio_direction_output(S3C64XX_GPM(1), 1);//             gpio_direction_output(S3C64XX_GPM(2), 1);//             gpio_direction_output(S3C64XX_GPM(3), 1);//             gpio_direction_output(S3C64XX_GPM(4), 0);//             break;         }         default:break;                    }        //gpio_free(S3C64XX_GPN(7));}ssize_t GPIO_VIB_open(struct inode * inode,struct file * file){//模块数  //MOD_INC_USE_COUNT;  printk("GPIO_VIB_open() /n");     return 0;}ssize_t GPIO_VIB_release(struct inode * inode, struct file * file){   // MOD_DEC_USE_COUNT;//模块数减1     printk("GPIO_VIB_release() /n");       return 0;}static int GPIO_VIB_CTL_init(void){    int ret  = -ENODEV;   int error ;    printk("---------------------------------------------- /r/n");    //初始化端口        s3c_gpio_cfgpin(S3C64XX_GPM(1), S3C_GPIO_SFN(1));//GPM1 output        s3c_gpio_cfgpin(S3C64XX_GPM(2), S3C_GPIO_SFN(1));//GPM2 output        s3c_gpio_cfgpin(S3C64XX_GPM(3), S3C_GPIO_SFN(1));//GPM3 output        s3c_gpio_cfgpin(S3C64XX_GPM(4), S3C_GPIO_SFN(1));//GPM4 output#if 1 /*静态方式注册驱动*/ret = register_chrdev(Viberator_MAJOR, "viberator", &GPIO_Viberator_ctl_ops);if (ret < 0) {    printk(KERN_ERR "VIB: unable to get major %d/n", ret);    return ret;}//创建classvib_dev_class = class_create(THIS_MODULE, "viberator");if (IS_ERR(vib_dev_class)) {    unregister_chrdev(Viberator_MAJOR, "capi20");    return PTR_ERR(vib_dev_class);}//创建节点,device_create(vib_dev_class, NULL, MKDEV(Viberator_MAJOR, 0), NULL, "vib");// create a point under /dev/class/vib  //通过上面这两步,驱动加载后,就会在/dev/class/下面生成vib节点,应用程序可以像操作文件那样操作这个节点,通过open ,write,read 等函数操作,详情可以看后面的应用示例程序。  return 0;    #endif#if 0/* 注册动态 */    if ((error = alloc_chrdev_region(&dev, 0, DEVCOUNT, "vibrate")) < 0)        {        printk(KERN_ERR               "VIB: Couldn't alloc_chrdev_region, error=%d/n",               error);        return 1;    }         printk("dev = %d  /n",dev);    cdev_p = cdev_alloc();    cdev_p->ops = &GPIO_Viberator_ctl_ops;    error = cdev_add(cdev_p, dev, DEVCOUNT);    if (error) {        printk(KERN_ERR               "VIB: Couldn't cdev_add, error=%d/n", error);        return 1;    }    vib_dev_class = class_create(THIS_MODULE, "vib-dev");    if (IS_ERR(vib_dev_class)) {        res = PTR_ERR(vib_dev_class);        goto out_unreg_class;    }    return 0;#endifout_unreg_class:    class_destroy(vib_dev_class);return 1;}static int __init S3C6410_VIB_init(void){    int ret = -ENODEV;    //调用函数            printk(KERN_ERR "Auly: S3C6410_VIB_init---/n");    ret = GPIO_VIB_CTL_init();    if(ret)        {        printk(KERN_ERR "Auly: S3C6410_VIB_init--Fail !!!/n");        return ret;    }    return 0;}static void __exit cleanup_GPIO_VIB(void){    //注销设备  //  devfs_unregister_chrdev(Viberator_MAJOR,"gpio_vib_ctl"); #if 0       cdev_del(cdev_p);    unregister_chrdev_region(dev, DEVCOUNT);    class_destroy(vib_dev_class);#endif device_destroy(vib_dev_class, MKDEV(Viberator_MAJOR, 0));class_destroy(vib_dev_class);unregister_chrdev(Viberator_MAJOR, "viberator");}MODULE_LICENSE("GPL");MODULE_DESCRIPTION("Peter first driver");MODULE_ALIAS_CHARDEV(Viberator_MAJOR, 0);module_init(S3C6410_VIB_init);module_exit(cleanup_GPIO_VIB);

leddriver.h文件

leddriver.h文件
  1. ssize_t GPIO_VIB_read(struct file * file,char * buf,size_t count,loff_t * f_ops);
  2. ssize_t GPIO_VIB_write (struct file * file,constchar * buf, size_t count,loff_t * f_ops);
  3. static int GPIO_VIB_ioctl(struct inode *inode,struct file *file,unsignedint cmd,unsignedlong arg);
  4. ssize_t GPIO_VIB_open(struct inode * inode,struct file * file);
  5. ssize_t GPIO_VIB_release(struct inode * inode,struct file * file);
  6. staticint GPIO_VIB_CTL_init(void);

Kconfig 文件

Kconfig 文件
  1. config LEDTEST
  2.   tristate "LED test for ARMeasy"
  3.   default n
  4.   help
  5.   this is a LED driver for ARMEASY with S3C6410

Makefile文件

Makefile文件
  1. obj-$(CONFIG_LEDTEST)+=leddriver.o

3、在内核配置里菜单里加入本驱动的配置项

达到的效果是,可以通过内核配置来选择是否把本驱动编译进内核里,也就是出现在make menuconfig 后出来的界面里,可以参考9.8章<android添加新驱动方法>

1)在arch/arm/Kconfig文件里 menu "Device Drivers"与 endmenu之间添加

  1. source "drivers/ledtest/Kconfig"

2)在drivers/Kconfig menu "Device Drivers" 和 endmenu之间添加

  1. source "drivers/ledtest/Kconfig"

3)修改/drivers/Makefile文件

  1. Obj-$(CONFIG_LEDTEST) +=ledtest/

4、编译驱动

在kernel目录下,终端输入

  1. #make menuconfig

“Device Drivers” 下面会看到 “LED test for ARMeasy”,选择它,

保存并退出

  1. #make

这样,就会在/drivers/ledtest目录下得到leddrivr.ko文件,它就是驱动目标文件,已级编译进了zImage 里了,只要用这个kernel烧录到开发板,开机就会自动加载本驱动,

PS,如果发现如上编译,没有在ledtest目录下生成leddriver.ko文件,也就是本根没有去编译本驱动,那就把ledtest目录入到/drivers/misc目录下,相应的修改misc下面,然后,在makefile里,不用选直接写成obj-y += leddriver.o,这样强制的包含进去编译。

三、 应用程序(测试驱动)

用C语言写一个应用,测试上面写的驱动是否工作正常,这是一般的驱动开发流程

主要有两个文件:Android.mk和ledtest.c

1、

在$(YOUR_ANDROID)/external/LEDTEST/目录编写ledtest.c文件,

ledtest.c

ledtest.c
  1. #include <stdio.h>
  2. #include<stdlib.h>
  3. #include <fcntl.h>// contact the open(),close(),read(),write() and so on!
  4.   #define DEVICE_NAME"/dev/vib"//device point
  5. #define LED_ON     0x11
  6. #define LED_OFF     0x22
  7. int main(int argc,char **argv)
  8. {   
  9.     int fd;
  10.     int ret;
  11.     char *i;
  12.     printf("\n start gpio_led_driver test \r\n");
  13.     fd = open(DEVICE_NAME,O_RDWR);//Open device ,get the handle
  14.     printf("fd = %d \n",fd);
  15.     if(fd == -1) //open fail
  16.     {
  17.       printf("open device %s error \n",DEVICE_NAME);
  18.     }
  19.     else
  20.     {
  21.         while(1)
  22.         {
  23.           ioctl(fd,LED_OFF); //call the output function to off LEDs
  24.           sleep(1);//wait 1 second
  25.           ioctl(fd,LED_ON);
  26.           sleep(1);
  27.         }
  28.         ret = close(fd); //close device
  29.         printf("ret = %d \n",ret);
  30.         printf("close gpio_led test \n");
  31.     }
  32.   return 0;
  33. }

2、在$(YOUR_ANDROID)/external/ LEDTEST /目录编写Android.mk文件。这是Android Makefile的标准命名,不要更改。Android.mk文件的格式和内容可以参考其他已有的Android.mk文件的写法,针对helloworld程序的Android.mk文件内容如下:

Android.mk
  1.  
  2. LOCAL_PATH:= $(call my-dir)
  3. include $(CLEAR_VARS)
  4. LOCAL_SRC_FILES:= \/par     hello.c
  5. LOCAL_MODULE := ledtestapp
  6. include $(BUILD_EXECUTABLE)

注意上面LOCAL_SRC_FILES用来指定源文件;

LOCAL_MODULE指定要编译的模块的名字,下一步骤编译时就要用到;

include $(BUILD_EXECUTABLE)表示要编译成一个可执行文件,如果想编译成动态库则可用BUILD_SHARED_LIBRARY,这些可以在

$(YOUR_ANDROID)/build/core/config.mk查到。

3、 回到Android源代码顶层目录进行编译:

# cd $(YOUR_ANDROID) && make ledtestapp

例如我这里的实际操作是

#cd android2.1
#make ledtestapp

注意make ledtestapp中的目标名ledtestapp就是上面Android.mk文件中由LOCAL_MODULE指定的模块名。编译结果如下

make ledtestapp
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=2.1-update1
TARGET_PRODUCT=generic
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=ECLAIR
============================================
/bin/bash: line 0: cd: sdk/layoutopt/app/src/resources: No such file or directory
target thumb C: ledtestapp <= external/LEDTEST/ledtest.c
Install: <span class="kwrd">out</span>/host/linux-x86/bin/apriori
Install: out/host/linux-x86/bin/soslim
target Executable: ledtestapp (<span class="kwrd">out</span>/target/product/generic/obj/EXECUTABLES/ledtestapp_intermediates/LINKED/ledtestapp)
target Non-prelinked: ledtestapp (out/target/product/generic/symbols/system/bin/ledtestapp)
target Strip: ledtestapp (<span class="kwrd">out</span>/target/product/generic/obj/EXECUTABLES/ledtestapp_intermediates/ledtestapp)
Install: out/target/product/generic/system/lib/libdl.so
Install: <span class="kwrd">out</span>/target/product/generic/system/lib/libc.so
Install: out/target/product/generic/system/lib/libstdc++.so
Install: <span class="kwrd">out</span>/target/product/generic/system/lib/libm.so
Install: out/target/product/generic/system/bin/ledtestapp

4、如上面的编译结果所示,编译后的可执行文件存放在

out/target/product/generic/system/bin/目录 可执行文件是ledtestapp

5、拷贝ledtestapp可执行文件到T卡,插入开发板

6、开电,在启动进入android的过程中,串口终端 按回车,进入命令行模式

7、拷贝可执行文件到根目录

#cp /sdcard/ledtestapp /   

8、执行程序

#./ledtestapp

通过上面的步骤,可以看到开发板上相应的LED在闭,每秒钟闪一次!


  推荐使用 著名开源项目:树莓派3 卡片电脑,我个人觉得挺不错的,也非常的便宜,

 介绍看这里

 http://www.superpi.org/chanpin/superpi3/superpi3.htm


0 0
原创粉丝点击