zoukankan      html  css  js  c++  java
  • android驱动学习---led实验

    ======================== 
    驱动: 
    内核:android-kernel 2.6.36  (必须对应你的板子上内核,不然会出现insmod错误) 
    目的:通过android应用层用户空间访问内核空间驱动程序。 
    实验:Button控件,点亮和熄灭LED。 
    注明:android应用层访问驱动,一般有2种方法来访问到硬件驱动程序。 
    这里讲解个人看法,各位别介意。 
    1: 应用层 ---> framwork层JNI ---> 驱动c 
    2:  应用层 ---> framwork层JNI ---> 硬件抽象层HAL ----> 驱动c 

    2种方法,各有各的好处,第1种,方便与驱动调试实验,只要编译好ko文件和libxxx.so文件,放入小机就可以立即调试了。 
    第2种JNI方法有些改变和增加了HAl,对驱动有了封装,简单来讲,硬件驱动程序一方面分布在Linux内核中,另一方面分布在用户空间的硬件抽象层中。不要误会android把驱动分成两半了,其实android只是在驱动的调用接口上在增加一层对应接口功能来让framwork层JNI来调用。比如,驱动有read接口,HAl层就是封装read接口,做一个 hal_read接口,里面调用read接口,然后把hal_read接口给JNI调用。 
    明白了吗?这方面有人讲得非常好,请点击此: 

    http://blog.csdn.net/luoshengyang/article/details/6575988 
    好处就是对对厂家来说能把商业秘密隐藏起来,我们做驱动实验的话,操作会极其复杂,不过对理解android整个系统都是极其用用的,因为它从下到上涉及到了android系统的硬件驱动层,硬件抽象层,运行时库和应用程序框架层等等。 

    这里目前只将第1种方法的实现:在此之前,请大家了解下JNI的编程方法,JNI是一种为JAVA和C、C++之间能互相访问所提供的编程接口。推荐此教材(见附件): 

    =============================================================== 
    驱动: 

    Java代码  收藏代码
    1. #include <linux/module.h>               /* For module specific items */  
    2. #include <linux/moduleparam.h>          /* For new moduleparam's */  
    3. #include <linux/types.h>                /* For standard types (like size_t) */  
    4. #include <linux/errno.h>                /* For the -ENODEV/... values */  
    5. #include <linux/kernel.h>               /* For printk/panic/... */  
    6. #include <linux/fs.h>                   /* For file operations */^M  
    7. #include <linux/ioport.h>               /* For io-port access */  
    8. #include <linux/platform_device.h>      /* For platform_driver framework */  
    9. #include <linux/init.h>                 /* For __init/__exit/... */  
    10. #include <linux/uaccess.h>              /* For copy_to_user/put_user/... */  
    11. #include <linux/io.h>                   /* For inb/outb/... */  
    12. #include <linux/gpio.h>  
    13. #include <linux/device.h>  
    14. #include <linux/cdev.h>  
    15. #include <linux/slab.h>               /*kamlloc */  
    16. //#include <asm-generic/ioctl.h>  
    17.    
    18.  //ioctl 命令码建议规范化。  
    19. #define CMD_FLAG  'i'  
    20. #define LED_ON          _IOR(CMD_FLAG,0x00000000,__u32)    
    21. #define LED_OFF     _IOR(CMD_FLAG,0x00000001,__u32)  
    22.   
    23. static int  major =0;  
    24. static struct class *led_class;  
    25. struct cdev_led {  
    26.     struct cdev cdev;  
    27. }; //建议用2.6的注册方法,2.4的已经离我们越来越远了。  
    28. struct cdev_led *led_dev;  
    29.   
    30. static int led_ioctl(struct file* filp,unsigned int cmd,unsigned long argv)  
    31. {  
    32.     printk(KERN_INFO "entry kernel....  ");  
    33.   
    34.     switch(cmd)  
    35.     {  
    36.         case LED_ON:  
    37.         {  
    38.             gpio_set_value(S3C64XX_GPK(4),0);  //LED1 亮  gpkdat[4]  
    39.             printk(KERN_INFO "led on  ");  
    40.             break;  
    41.         }  
    42.         case LED_OFF:  
    43.         {  
    44.             gpio_set_value(S3C64XX_GPK(4),1);  //LED1 灭   gpkdat[4]  
    45.             printk(KERN_INFO "led off  ");  
    46.             break;  
    47.         }  
    48.         default:  
    49.             return -EINVAL;  
    50.     }  
    51.     return 0;  
    52. }  
    53.   
    54.   
    55. //open  
    56. static int led_open(struct inode* i_node,struct file* filp)  
    57. {  
    58.     printk(KERN_INFO "open init....  ");  
    59.     int err;  
    60.     err = gpio_request(S3C64XX_GPK(4),"led1");  //请求gpkcon0[4]控制权。  
    61.     if(err<0)  
    62.     {  
    63.         printk(KERN_INFO "gpio request faile  ");  
    64.         return err;  
    65.     }  
    66.     gpio_direction_output(S3C64XX_GPK(4),1); // 设置gpkcon0[4] 为输出模式  
    67.       
    68.     return 0;  
    69. }  
    70.   
    71. //close  
    72. static void led_close(struct inode* i_node,struct file* filp)  
    73. {  
    74. printk(KERN_INFO "close init  ");  
    75.     gpio_free(S3C64XX_GPK(4)); //释放  
    76.     return ;  
    77. }  
    78.   
    79. /* file operations */  
    80. struct file_operations fops={  
    81.     .owner  = THIS_MODULE,  
    82.     .open   = led_open,  
    83.     .unlocked_ioctl = led_ioctl, // 特别注意从2.6.36以后ioctl已经移除,内核里面用unlocked_ioctl和compat_ioctl. 应用层不变,仍是ioctl调用。  
    84.     .release= led_close,  
    85. };  
    86.   
    87. static int __init led_init(void)  
    88. {  
    89. printk(KERN_INFO "init ....  ");  
    90.     dev_t dev_no;  
    91.     int result,err;  
    92.     err = alloc_chrdev_region(&dev_no,0,1,"my_led"); //动态申请设备号  
    93.     if(err<0)  
    94.     {  
    95.         printk(KERN_INFO "ERROR ");  
    96.         return err;  
    97.     }  
    98.     major = MAJOR(dev_no);  
    99.     led_dev = kmalloc(sizeof(struct cdev_led),GFP_KERNEL);  
    100.     if(!led_dev)  
    101.     {  
    102.         result = -ENOMEM;  
    103.         goto fail_malloc;  
    104.     }  
    105.     memset(led_dev,0,sizeof(led_dev));  
    106.       
    107.     cdev_init(&led_dev->cdev,&fops); // 初始化cdev  
    108.     led_dev->cdev.owner = THIS_MODULE;  
    109.     result = cdev_add(&led_dev->cdev,dev_no,1); //加载设备  
    110.     if(result <0)  
    111.     {   printk(KERN_INFO "error ");  
    112.         goto fail_add;  
    113.     }  
    114.     led_class = class_create(THIS_MODULE,"myled");  //在sys/class下创建sysfs文件  
    115.     device_create(led_class,NULL,MKDEV(major,0),NULL,"myled"); //动态创建设备文件  /dev/myled, 以后不用手动创建了  
    116.     return 0;  
    117. fail_add:  
    118.     kfree(led_dev);  
    119. fail_malloc:  
    120.     unregister_chrdev_region(dev_no,1);  
    121.     return result;  
    122.   
    123. }  
    124.   
    125. static void __exit led_exit(void)  
    126. {  
    127.     dev_t dev_no=MKDEV(major,0);  
    128.   
    129.     unregister_chrdev_region(dev_no,1);  
    130.     cdev_del(&led_dev->cdev);  
    131.     kfree(led_dev);  
    132.     device_destroy(led_class,dev_no);  
    133.     class_destroy(led_class);  
    134.       printk(KERN_INFO "exit........  ");  
    135. }  
    136. module_init(led_init);  
    137. module_exit(led_exit);  
    138. MODULE_AUTHOR("koliy <xxxx@163.com>");  
    139. MODULE_DESCRIPTION("ARM test led");  
    140. MODULE_LICENSE("GPL");  


    个人建议:编写驱动最好尽量使用定义好的gpio接口:如: 
    gpio_direction_output(S3C64XX_GPK(4),1) 
    gpio_set_value(S3C64XX_GPK(4),1); 
    方面让人一看就明白。对于程序的规范有很大的帮助。 

    ------------------------------------------------------------------ 
    Makefile: 

    Java代码  收藏代码
    1. KERN_DIR = /android/linux-2.6.36-android  
    2. all:  
    3.     make -C $(KERN_DIR) M=`pwd` modules  
    4. clean:  
    5.     make -C $(KERN_DIR) M=`pwd` clean  
    6.   
    7. obj-m   += android_led.o  


    : make  -C  :进入指定的内核目录,使用内核里面的Makefile文件 这里须指定好你的内核存放路径。 
    M=`pwd`  modules : 表示编译当前目录为模块  此处不是单引号,是键盘上数值1键左边的按键,请注意了。 
        Obj-m +=android_led.o   : 要编译的模块名称。此文件由内核里的Makefile生成,改好文件名相同就行。 
    编译:(你的驱动目录路径下)# make 
    生成xxx,ko 模块文件。 
    ------------------------------------------------------------ 
    应用层:android 控件 
    这方面,请安装好 eclipse  和android -sdk。不了解,请网上查找资料。 

    Java代码  收藏代码
    1. public class LedControl extends Activity implements OnClickListener {  
    2.     private Button led1_on;  
    3.     private Button led1_off;  
    4.     public int led_on = 1;  
    5.     public int led_off = 2;     
    6.     public int fd =0;  
    7.       
    8.     static {  
    9.         try{  
    10.             Log.i("JNI","try to load libled.so");  
    11.             System.loadLibrary("led");    
    12.     //加载本地库,也就是JNI生成的libxxx.so文件,下面再说。  
    13.         }catch (UnsatisfiedLinkError ule){  
    14.             Log.e("JNI","WARNING: Could not load libled.so");  
    15.         }  
    16.     }  
    17.     //JNI 接口,为JAVA能跟C 交互。Java 怎么使用这些接口,请了解下资料。这点很重要,这里只是简单的方法。  
    18.     public static native int openled();    
    19.     public static native void closeled();  
    20.     public static native int ioctl(int led_num,int on_off);  
    21.   
    22.     /** Called when the activity is first created. */  
    23.     @Override  
    24.     public void onCreate(Bundle savedInstanceState) {  
    25.         super.onCreate(savedInstanceState);  
    26.         setContentView(R.layout.main);  
    27.       
    28.         fd =this.openled(); // 调用JNI 接口来打开/dev/myled 设备 ,注意前面要加个对象。  
    29.         if(fd<0)  
    30.         {  
    31.             setTitle("open device faile!");  
    32.             finish();  
    33.         }else{  
    34.             setTitle("open device success!");  
    35.         }  
    36.         findById();  
    37.         setOnClickListener();  
    38.     }  
    39.       
    40.     private void findById(){  
    41.         led1_on = (Button)findViewById(R.id.bt_led1);  
    42.         led1_off=(Button)findViewById(R.id.bt_led2);  
    43.     }  
    44.     private void setOnClickListener(){  
    45.         led1_on.setOnClickListener(this);  
    46.         led1_off.setOnClickListener(this);  
    47.     }  
    48.   
    49.     @Override  
    50.     public void onClick(View v) {  
    51.         // TODO Auto-generated method stub  
    52.         switch(v.getId()){  
    53.               
    54.             case R.id.bt_led1: {  
    55.                 this.ioctl(1,led_on); // led1  on  
    56.                 break;  
    57.             }  
    58.             case R.id.bt_led2:{  
    59.                 this.ioctl(1,led_off);  // led1 off  
    60.                 break;  
    61.             }  
    62.         }  
    63.           
    64.     }  
    65.       
    66.     protected void onDestroy(){  
    67.         super.onDestroy();  
    68.         this.closeled();  
    69.     }  
    70.       
    71.     }  
    72.   
    73. 布局:  
    74.  <?xml version="1.0" encoding="utf-8"?>  
    75. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    76.     android:orientation="vertical"  
    77.     android:layout_width="fill_parent"  
    78.     android:layout_height="fill_parent"  
    79.     >  
    80. <TextView    
    81.     android:id="@+id/position"  
    82.     android:layout_centerInParent="true"  
    83.     android:layout_width="wrap_content"   
    84.     android:layout_height="wrap_content"   
    85.     android:textSize="25sp"  
    86.     android:text=" led "  
    87.     />  
    88. <Button   
    89.     android:id="@+id/bt_led1"  
    90.     android:layout_width="wrap_content"  
    91.     android:layout_height="wrap_content"  
    92.     android:textSize="18sp"  
    93.     android:text="LED1 on"  
    94.     android:layout_toLeftOf="@+id/position"  
    95.     android:layout_centerHorizontal="true"  
    96.     android:layout_alignTop="@+id/position"  
    97.     />  
    98. <Button   
    99.     android:id="@+id/bt_led2"  
    100.     android:layout_width="wrap_content"  
    101.     android:layout_height="wrap_content"  
    102.     android:textSize="18sp"  
    103.     android:text="LED1 off"  
    104.     android:layout_toRightOf="@+id/position"  
    105.     android:layout_alignTop="@+id/position"  
    106.     />  
    107. </RelativeLayout>  


    ------------------------------------------------------------ 
    这些都是简单的,下面讲解下JNI: 
    JNI:在工程文件目录下,新建 jni目录,里面新建led.c 文件,里面编写JNI方法。 

    Java代码  收藏代码
    1. #include<stdio.h>  
    2. #include<stdlib.h>  
    3. #include<fcntl.h>  
    4. #include<errno.h>  
    5. #include<unistd.h>  
    6. #include<sys/ioctl.h>  
    7. #include<jni.h>  // 一定要包含此文件  
    8. #include<string.h>  
    9. #include<sys/types.h>  
    10. #include<sys/stat.h>  
    11. #include "android/log.h"  
    12. //驱动里的命令码.  
    13. #define CMD_FLAG 'i'  
    14. #define LED_ON      _IOR(CMD_FLAG,0x00000000,__u32)    
    15. #define LED_OFF     _IOR(CMD_FLAG,0x00000001,__u32)  
    16.   
    17. #define DEVICE_NAME "/dev/myled"  
    18. int fd ;  
    19.   
    20. static const char *TAG="led";  
    21. #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)  
    22. #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)  
    23. #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)  
    24.   
    25. /* * Class:     Linuxc  
    26. * Method:    openled 
    27. * Signature: ()I  
    28. */  
    29.  JNIEXPORT Jint JNICALL Java_tw_com_mini6410_led_LedControl_openled(JNIEnv* env, jclass mc)  
    30. {  
    31.     fd = open(DEVICE_NAME,O_RDWR);  
    32.     if(fd<0)  
    33.     {  
    34.         LOGI("don't open dev");  
    35.         return -1;    
    36.     }  
    37.     else  
    38.         LOGI("open success");  
    39.     return fd;  
    40. }  
    41. /* * Class:     Linuxc  
    42. * Method:    clsoeled 
    43. * Signature: ()V 
    44. */  
    45. JNIEXPORT void JNICALL Java_tw_com_mini6410_led_LedControl_closeled(JNIEnv* env, jclass mc)  
    46. {  
    47.     LOGI("dev close");  
    48.     close(fd);    
    49. }  
    50.   
    51.   
    52. JNIEXPORT jint JNICALL Java_tw_com_mini6410_led_LedControl_ioctl  
    53. (JNIEnv* env,jclass mc, jint a, jint b)  
    54. {       int tmp = b;  
    55.     if(tmp==1){  
    56.          
    57.         //int err= write(fd,&tmp,sizeof(tmp));  
    58.         LOGI("led on");  
    59.         ioctl(fd,LED_ON,NULL);  
    60.     }  
    61.     else{  
    62. //int err= write(fd,&tmp,sizeof(tmp));  
    63.     LOGI("led1 off");  
    64.     ioctl(fd,LED_OFF,NULL);  
    65.     }  
    66.     return 0;     
    67. }  



    Java_tw_com_mini6410_led_LedControl_ioctl : 

    tw_com_mini6410_led_LedControl: 包名+ 类名: 
    在这里特别说明下,JNI的格式规范要注意的地方: 
    1.函数声明的格式: 
      因JNI会把 '_' 转换成' . ' 所以在类名和函数接口中不要出现' _ ',以免应用层调用不到JNI接口,这方面对初学者来说极其重要,所以用eclipse生成的android类文件,最好改下类名。不了解对实验的热情打击比较重。 
    2.JNI函数分本地方法和静态方法。 
      本地方法: 
            public native int jni();  // 不带static 声明. 
      对应的 JNI 函数中参数的定义有改动: 
            Java_xx_xx_LedControl_jni(JNIEnv*env, jobject obj) 
      静态方法: 
             public static native int jni();  // 带static 声明. 
      对应的 JNI 函数中参数的定义有改动: 
            Java_xx_xx_LedControl_jni(JNIEnv*env, jclass cls) 

    注意 jobject 和jclass的变动。 

    请一定要去了解下JNI。 
    --------------------------------------------------- 
    编译: 
         网上有别的方法javah -jni来编译生成此 JNI接口的头文件,可以根据头文件声明好的接口来完善。 
         不过,本人怎么编译都无法生成,没办法,只要手打了,还要JNI格式都很规律。 
    由于不用命令来生成LIbxxx.so文件。所以必须要用到NDK工具。请网上下载。 
    在你的Ndk 目录下创建一个JNI文件目录。 

    把整个应用工程文件,放入JNI下。 
    进入 (ndk目录路径)/JNI/(应用工程文件)/jni/  
    新建  Android.mk 文件 


    Java代码  收藏代码
    1. LOCAL_PATH := $(call my-dir)  
    2.   
    3. include $(CLEAR_VARS)  
    4.   
    5. TARGET_PLATFORM := android-3  
    6. LOCAL_MODULE    := led   //模块名  libled.so  
    7. LOCAL_SRC_FILES := led.c   //要编译的文件。  
    8. LOCAL_LDLIBS    := -llog  
    9.   
    10. include $(BUILD_SHARED_LIBRARY)  


    ------------------------------------- 
    ----- 
    回到 (ndk目录路径)/JNI/(应用工程文件)/  路径下 
    输入命令 : ../../ndk-build 

    会生成 libs 和obj 2个文件。 Libled.so文件放在 libs /armeabi/ 下 

    ========================================================== 
    实验: 
         好了所有的需要的文件已经完成,现在开始实验了。再此之前,请确认自己的 
    开发板里的android里加入好了busybox工具。因需要insmod rmmod 等命令。 
    没有的话,请网上查找教材。 
    Adb shell 
    #Mkdir  mydev 
    #exit 
    Adb push  d:android_led.ko    /mydev 
    Adb shell 
    #cd mydev 
    #chmod 777 android_led.ko 
    #insmod android_led.ko 
    #lsmod  //查看是否加载上了。卸载命令 rmmod  android_led  不要加.ko 
    # cat /proc/devices   //也可以查看设备号和设备名。 
    #ls -l /dev/myled     //同样。 
    此时myled 权限需要修改。 
    #chmod 777 /dev/myled 

    ---------- 
    Libled.so:  JNI所生成的so文件 
    Adb push d:libled.so /system/lib/hw/ 一定要放在这里,应用层才能加载上 
    #chmod 766 /system/lib/hw/libled.so 
    -------------- 
    好了,用eclipse把应用apk安装到开发板上,启动开看效果。或者用Sd开也可以。 
    这里说下,可能会出现的问题,当应用调用JNI接口时,会提示找不到native的接口。 
    仔细检查,方法格式多对,就是掉用不到,可以重新编写下JNI接口,和换接口名。对于这种问题,实在是让人调试到崩溃。复杂点的话,最好在jni方法里面指定好class。 
    这方面最好了解下JNI资料。 
    在开始时总会遇到很多问题,请多使用网络。 

  • 相关阅读:
    Gradle 配置国内镜像
    Spring_Boot 简单例子
    IDEA中代码不小心删除,或者改了半天想回退到某个特定时间怎么办?
    Java 装饰模式
    Spring中 aop的 xml配置(简单示例)
    Spring使用@AspectJ开发AOP(零配置文件)
    Redis 的简单运算
    Redis 安装 与 使用
    touch-action属性引起的探索
    select下拉框的探索(<option></option>标签中能嵌套使用其它标签吗)
  • 原文地址:https://www.cnblogs.com/Ph-one/p/4146328.html
Copyright © 2011-2022 走看看