您的位置:首页 > 移动开发 > Android开发

android驱动学习---led实验

2014-06-03 22:11 387 查看
/article/7737637.html

驱动:

内核: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代码



#include <linux/module.h> /* For module specific items */

#include <linux/moduleparam.h> /* For new moduleparam's */

#include <linux/types.h> /* For standard types (like size_t) */

#include <linux/errno.h> /* For the -ENODEV/... values */

#include <linux/kernel.h> /* For printk/panic/... */

#include <linux/fs.h> /* For file operations */^M

#include <linux/ioport.h> /* For io-port access */

#include <linux/platform_device.h> /* For platform_driver framework */

#include <linux/init.h> /* For __init/__exit/... */

#include <linux/uaccess.h> /* For copy_to_user/put_user/... */

#include <linux/io.h> /* For inb/outb/... */

#include <linux/gpio.h>

#include <linux/device.h>

#include <linux/cdev.h>

#include <linux/slab.h> /*kamlloc */

//#include <asm-generic/ioctl.h>

//ioctl 命令码建议规范化。

#define CMD_FLAG 'i'

#define LED_ON _IOR(CMD_FLAG,0x00000000,__u32)

#define LED_OFF _IOR(CMD_FLAG,0x00000001,__u32)

static int major =0;

static struct class *led_class;

struct cdev_led {

struct cdev cdev;

}; //建议用2.6的注册方法,2.4的已经离我们越来越远了。

struct cdev_led *led_dev;

static int led_ioctl(struct file* filp,unsigned int cmd,unsigned long argv)

{

printk(KERN_INFO "entry kernel.... \n");

switch(cmd)

{

case LED_ON:

{

gpio_set_value(S3C64XX_GPK(4),0); //LED1 亮 gpkdat[4]

printk(KERN_INFO "led on \n");

break;

}

case LED_OFF:

{

gpio_set_value(S3C64XX_GPK(4),1); //LED1 灭 gpkdat[4]

printk(KERN_INFO "led off \n");

break;

}

default:

return -EINVAL;

}

return 0;

}

//open

static int led_open(struct inode* i_node,struct file* filp)

{

printk(KERN_INFO "open init.... \n");

int err;

err = gpio_request(S3C64XX_GPK(4),"led1"); //请求gpkcon0[4]控制权。

if(err<0)

{

printk(KERN_INFO "gpio request faile \n");

return err;

}

gpio_direction_output(S3C64XX_GPK(4),1); // 设置gpkcon0[4] 为输出模式

return 0;

}

//close

static void led_close(struct inode* i_node,struct file* filp)

{

printk(KERN_INFO "close init \n");

gpio_free(S3C64XX_GPK(4)); //释放

return ;

}

/* file operations */

struct file_operations fops={

.owner = THIS_MODULE,

.open = led_open,

.unlocked_ioctl = led_ioctl, // 特别注意从2.6.36以后ioctl已经移除,内核里面用unlocked_ioctl和compat_ioctl. 应用层不变,仍是ioctl调用。

.release= led_close,

};

static int __init led_init(void)

{

printk(KERN_INFO "init .... \n");

dev_t dev_no;

int result,err;

err = alloc_chrdev_region(&dev_no,0,1,"my_led"); //动态申请设备号

if(err<0)

{

printk(KERN_INFO "ERROR\n");

return err;

}

major = MAJOR(dev_no);

led_dev = kmalloc(sizeof(struct cdev_led),GFP_KERNEL);

if(!led_dev)

{

result = -ENOMEM;

goto fail_malloc;

}

memset(led_dev,0,sizeof(led_dev));

cdev_init(&led_dev->cdev,&fops); // 初始化cdev

led_dev->cdev.owner = THIS_MODULE;

result = cdev_add(&led_dev->cdev,dev_no,1); //加载设备

if(result <0)

{ printk(KERN_INFO "error\n");

goto fail_add;

}

led_class = class_create(THIS_MODULE,"myled"); //在sys/class下创建sysfs文件

device_create(led_class,NULL,MKDEV(major,0),NULL,"myled"); //动态创建设备文件 /dev/myled, 以后不用手动创建了

return 0;

fail_add:

kfree(led_dev);

fail_malloc:

unregister_chrdev_region(dev_no,1);

return result;

}

static void __exit led_exit(void)

{

dev_t dev_no=MKDEV(major,0);

unregister_chrdev_region(dev_no,1);

cdev_del(&led_dev->cdev);

kfree(led_dev);

device_destroy(led_class,dev_no);

class_destroy(led_class);

printk(KERN_INFO "exit........ \n");

}

module_init(led_init);

module_exit(led_exit);

MODULE_AUTHOR("koliy <xxxx@163.com>");

MODULE_DESCRIPTION("ARM test led");

MODULE_LICENSE("GPL");

个人建议:编写驱动最好尽量使用定义好的gpio接口:如:

gpio_direction_output(S3C64XX_GPK(4),1)

gpio_set_value(S3C64XX_GPK(4),1);

方面让人一看就明白。对于程序的规范有很大的帮助。

------------------------------------------------------------------

Makefile:

Java代码



KERN_DIR = /android/linux-2.6.36-android

all:

make -C $(KERN_DIR) M=`pwd` modules

clean:

make -C $(KERN_DIR) M=`pwd` clean

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代码



public class LedControl extends Activity implements OnClickListener {

private Button led1_on;

private Button led1_off;

public int led_on = 1;

public int led_off = 2;

public int fd =0;

static {

try{

Log.i("JNI","try to load libled.so");

System.loadLibrary("led");

//加载本地库,也就是JNI生成的libxxx.so文件,下面再说。

}catch (UnsatisfiedLinkError ule){

Log.e("JNI","WARNING: Could not load libled.so");

}

}

//JNI 接口,为JAVA能跟C 交互。Java 怎么使用这些接口,请了解下资料。这点很重要,这里只是简单的方法。

public static native int openled();

public static native void closeled();

public static native int ioctl(int led_num,int on_off);

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

fd =this.openled(); // 调用JNI 接口来打开/dev/myled 设备 ,注意前面要加个对象。

if(fd<0)

{

setTitle("open device faile!");

finish();

}else{

setTitle("open device success!");

}

findById();

setOnClickListener();

}

private void findById(){

led1_on = (Button)findViewById(R.id.bt_led1);

led1_off=(Button)findViewById(R.id.bt_led2);

}

private void setOnClickListener(){

led1_on.setOnClickListener(this);

led1_off.setOnClickListener(this);

}

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

switch(v.getId()){

case R.id.bt_led1: {

this.ioctl(1,led_on); // led1 on

break;

}

case R.id.bt_led2:{

this.ioctl(1,led_off); // led1 off

break;

}

}

}

protected void onDestroy(){

super.onDestroy();

this.closeled();

}

}

布局:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<TextView

android:id="@+id/position"

android:layout_centerInParent="true"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textSize="25sp"

android:text=" led "

/>

<Button

android:id="@+id/bt_led1"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textSize="18sp"

android:text="LED1 on"

android:layout_toLeftOf="@+id/position"

android:layout_centerHorizontal="true"

android:layout_alignTop="@+id/position"

/>

<Button

android:id="@+id/bt_led2"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textSize="18sp"

android:text="LED1 off"

android:layout_toRightOf="@+id/position"

android:layout_alignTop="@+id/position"

/>

</RelativeLayout>

------------------------------------------------------------

这些都是简单的,下面讲解下JNI:

JNI:在工程文件目录下,新建 jni目录,里面新建led.c 文件,里面编写JNI方法。

Java代码



#include<stdio.h>

#include<stdlib.h>

#include<fcntl.h>

#include<errno.h>

#include<unistd.h>

#include<sys/ioctl.h>

#include<jni.h> // 一定要包含此文件

#include<string.h>

#include<sys/types.h>

#include<sys/stat.h>

#include "android/log.h"

//驱动里的命令码.

#define CMD_FLAG 'i'

#define LED_ON _IOR(CMD_FLAG,0x00000000,__u32)

#define LED_OFF _IOR(CMD_FLAG,0x00000001,__u32)

#define DEVICE_NAME "/dev/myled"

int fd ;

static const char *TAG="led";

#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)

#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)

#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)

/* * Class: Linuxc

* Method: openled

* Signature: ()I

*/

JNIEXPORT Jint JNICALL Java_tw_com_mini6410_led_LedControl_openled(JNIEnv* env, jclass mc)

{

fd = open(DEVICE_NAME,O_RDWR);

if(fd<0)

{

LOGI("don't open dev");

return -1;

}

else

LOGI("open success");

return fd;

}

/* * Class: Linuxc

* Method: clsoeled

* Signature: ()V

*/

JNIEXPORT void JNICALL Java_tw_com_mini6410_led_LedControl_closeled(JNIEnv* env, jclass mc)

{

LOGI("dev close");

close(fd);

}

JNIEXPORT jint JNICALL Java_tw_com_mini6410_led_LedControl_ioctl

(JNIEnv* env,jclass mc, jint a, jint b)

{ int tmp = b;

if(tmp==1){

//int err= write(fd,&tmp,sizeof(tmp));

LOGI("led on");

ioctl(fd,LED_ON,NULL);

}

else{

//int err= write(fd,&tmp,sizeof(tmp));

LOGI("led1 off");

ioctl(fd,LED_OFF,NULL);

}

return 0;

}

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代码



LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

TARGET_PLATFORM := android-3

LOCAL_MODULE := led //模块名 libled.so

LOCAL_SRC_FILES := led.c //要编译的文件。

LOCAL_LDLIBS := -llog

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资料。

在开始时总会遇到很多问题,请多使用网络。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: