Android应用控制LED(动态编译LED驱动)

此文章是Android应用控制底层硬件的小实验,记录下来,以防后面忘记如何操作。后面也可以按照此流程进行其他开发

开发平台:DMATEK PAD-4412

内核:Linux3.2.0

系统:Android4.0

作者:lyp461340781


Android系统中上层UI是使用Java语言完成的,涉及到底层驱动的话,需要SO库(JNI层)的连接。所以针对LED控制,将从底层驱动、JNI层SO连接库和上层UI界面进行设计。

为了操作方便,此处LED驱动程式采用动态编译,可随时进行修改、编译和载入驱动。

1、首先编写底层的LED驱动程序led.c,源码如下:

#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/irq.h>
#include <linux/wakelock.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include <mach/gpio-dma4412.h>
#include <plat/gpio-cfg.h>
#include <plat/adc.h>
#include <linux/memory.h>
#include <linux/io.h>

#include <linux/miscdevice.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

#include <asm/uaccess.h>


#define LED_ON  1
#define LED_OFF 0

#define LED_MAJOR 230
#define led_name "led"

#define GPIO_GPK3_LED18       EXYNOS4_GPK3(3)     //led 18
#define GPIO_GPK3_LED19       EXYNOS4_GPK3(4)     //led 19


static void led_off(int led_num)
{
 switch(led_num)
 {
  case 1:  //led1
   gpio_direction_output(GPIO_GPK3_LED18,0);
   break;
  case 2:  //led2
   gpio_direction_output(GPIO_GPK3_LED19,0);
   break;
  default:
   break;
 }

}

static void led_on(int led_num)
{
 switch(led_num)
 {
  case 1:  //led1
   gpio_direction_output(GPIO_GPK3_LED18,1);
   break;
  case 2:  //led2
   gpio_direction_output(GPIO_GPK3_LED19,1);
   break;
  default:
   break;
 }
}

static ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{  
 return count;
}

static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
 return 0;
}


static int led_open(struct inode *inode, struct file *file)
{
 printk("led_open +++++++1\n" );

 gpio_direction_output(GPIO_GPK3_LED18,0);
 gpio_direction_output(GPIO_GPK3_LED19,0);


 printk("led_open -------1\n" );
 return 0;
 
}

/* release command for led device file */
static int led_close(struct inode *inode, struct file *file)

// printk("led_close +++1\n"); 
 
 return 0;
}

/* ioctl command for led device file */


//static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
static int led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)


 int ret = 0;
 int num;
 printk("cmd=%d\n",cmd);
 switch(cmd)
 {
  case LED_ON:
   //printk("&&&&&&&&&&   LED ON  &&&&&&&&&&&&&&\n");
   ret = copy_from_user(&num, (int *)arg, sizeof(int));
   if(ret != 0)
   {
    printk("gpio_ioctl: copy_from_user failed\n");
    return(-EFAULT);
   }
   printk("--------LED ON,num = %d-----------\n",num);
   led_on(num);
   break;
  case LED_OFF:
   ret = copy_from_user(&num, (int *)arg, sizeof(int));
   if(ret != 0)
   {
    printk("gpio_ioctl: copy_from_user failed\n");
    return(-EFAULT);
   }
   printk("********LED OFF,num = %d**********\n",num);
   led_off(num);
   break;
  default:
   break;
 }
 return 0;
}
static const struct file_operations led_fops = {
 .owner = THIS_MODULE,
 .read = led_read,
 .write = led_write,
 .open = led_open,
 .release = led_close,
 .unlocked_ioctl = led_ioctl,
};


static int __init led_init(void)
{
 int retval;
 retval = register_chrdev(LED_MAJOR,led_name,&led_fops);
 if(retval < 0)
 {
  printk(KERN_WARNING "can't register major number %d\n",LED_MAJOR);
  return retval;
 }

 printk("LED driver register success!\n");
 return 0;
}

static void __exit led_exit(void)
{
 unregister_chrdev(LED_MAJOR,led_name);
 printk("LED driver release success!\n");
}


module_init(led_init);
module_exit(led_exit);

MODULE_AUTHOR("lyp");
MODULE_DESCRIPTION("led driver");
MODULE_LICENSE("GPL");


2、编写LED驱动程序的Makefile(与led.c同目录),源码如下:

#obj-$(CONFIG_PAD4412_LEDS) += led.o

CC =/usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-gcc
4412X_KERNEL_DIR = /home/liuyipin/4412/PAD4412/android_kernel_dma-pad4412_V1.1

ifneq ($(KERNELRELEASE),)
 obj-m := led.o
else
 KERNELDIR ?= $(4412X_KERNEL_DIR)
 
 PWD := $(shell pwd)
 
default:
 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
 
clean:
 rm -f *.ko *.o *.bak *.mod.*
endif


3、进入LED驱动程序的目录,编译LED驱动程序,使用make命令进行编译。编译成功后会生成led.ko文件,即LED的驱动程式。


4、进行LED界面的设计,在Eclipse中新建一个工程Led_Control,主要代码如下:

1)main.xml代码如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="top"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <ImageView
        android:id="@+id/led_imageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:src="@drawable/index" />

    <Button
        android:id="@+id/ledon_button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/led_text"
        android:layout_marginTop="25dp"
        android:layout_toLeftOf="@+id/led_text"
        android:text="LED1开" />

    <Button
        android:id="@+id/ledoff_button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/led_imageView"
        android:layout_alignRight="@+id/ledoff_button1"
        android:layout_marginBottom="28dp"
        android:text="LED2关" />

    <Button
        android:id="@+id/ledon_button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/ledoff_button2"
        android:layout_alignBottom="@+id/ledoff_button2"
        android:layout_toLeftOf="@+id/led_text"
        android:text="LED2开" />

    <Button
        android:id="@+id/ledoff_button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/ledon_button1"
        android:layout_alignBottom="@+id/ledon_button1"
        android:layout_toRightOf="@+id/led_text"
        android:text="LED1关" />

    <TextView
        android:id="@+id/led_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/led_imageView"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="25dp"
        android:text="LED  TEST" />

</RelativeLayout>


2)MainActivity.java代码如下:

package com.dmatek.ledcontral;


import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;


public class MainActivity extends Activity {

 private Button mButton1;
    private Button mButton2;
    private Button mButton3;
    private Button mButton4;
    //private Button mButton5;
   
    public int fd = 0;
    public int led_on = 1;
    public int led_off = 0;
   

    private Button.OnClickListener button1_listener= new Button.OnClickListener()
 {
  public void onClick(View v)
        { 
         Linuxc.send(1, led_on);
        }
 };
 
 private Button.OnClickListener button2_listener= new Button.OnClickListener()
 {
  public void onClick(View v)
        { 
         Linuxc.send(1, led_off);
        }
 };
 
 private Button.OnClickListener button3_listener= new Button.OnClickListener()
 {
  public void onClick(View v)
        { 
         Linuxc.send(2, led_on);
        }
 };
 
 private Button.OnClickListener button4_listener= new Button.OnClickListener()
 {
  public void onClick(View v)
        { 
         Linuxc.send(2, led_off);
        }
 };
 
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  mButton1=(Button)findViewById(R.id.ledon_button1);
        mButton2 =(Button) findViewById(R.id.ledoff_button1);
        mButton3 =(Button) findViewById(R.id.ledon_button2);
        mButton4 =(Button) findViewById(R.id.ledoff_button2);
        //mButton5 =(Button) findViewById(R.id.ledoff_button2);
       
        fd = Linuxc.openled();
       
        if (fd < 0){
         setTitle("open device false!");
            // 打开设备文件失败的话,就退出
         finish();
        }
        else {
         setTitle("open device success!");
        }
       
        /*使用setOnClickListener来监听事件*/
        mButton1.setOnClickListener(button1_listener);
        mButton2.setOnClickListener(button2_listener);
        mButton3.setOnClickListener(button3_listener);
        mButton4.setOnClickListener(button4_listener);
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

}

3)添加Class类Linuxc,Linuxc.java代码如下:

package com.dmatek.ledcontral;

import android.util.Log;

public class Linuxc {
 static{
  try{
   Log.i("JNI","Trying to load libled.so");
   System.loadLibrary("led");
  }
  catch(UnsatisfiedLinkError ule){
   Log.e("JNI","WARNING:Could not load libled.so");
  }
 }
 
 public static native int openled();
 public static native int closeled();
 public static native int send(int led_num,int on_off);
}


5、LED JNI设计

1)在Led_Control工程目录中新建jni目录,然后输入cmd进入DOS界面,进入Led_Control工程的jni目录下。如下图所示:


2)执行命令:javah -classpath "H:\Android\Android_Project\Led_Contral\bin" com.dmatek.ledcontarl.Linuxc

3)会在当前目录下生成一个com.dmatek.ledcontral.Linuxc.h文件

4)编写com.dmatek.ledcontral.Linuxc.c文件,源码如下:

#include <jni.h>
#include "com_dmatek_ledcontral_Linuxc.h"
#include <stdio.h>
#include <stdlib.h> 
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define DEVICE_BLTEST "/dev/led"     

int fd;

JNIEXPORT jint JNICALL Java_com_dmatek_ledcontral_Linuxc_openled
 (JNIEnv *env, jclass mc)
{
 fd= open(DEVICE_BLTEST,O_RDONLY);
 return fd;
}

JNIEXPORT jint JNICALL Java_com_dmatek_ledcontral_Linuxc_closeled
 (JNIEnv *env, jclass mc)
{
 close(fd);
}

JNIEXPORT jint JNICALL Java_com_dmatek_ledcontral_Linuxc_send
(JNIEnv *env, jclass mc, jint a, jint b)
{
 ioctl(fd,b,&a);              
}

5)生成SO链接库

打开Cygwin终端,进入Led_Contral目录,执行ndk-bulid命令,会在Led_Contarl工程libs\armeabi目录下生成libled.so

6)重新编译Android应用程序即可。


6、测试:

1)将led.ko拷贝到SD卡中,将SD卡插入开发平台,打开PC的串口工具,进入SD卡目录进行led驱动的挂载。

# insmod led.ko

# busybox mknod /dev/led c 230 0

2)安装APK到开发平台,打开Led_Contral应用程序进行测试。


内核源码下载链接:http://download.csdn.net/detail/lyp461340781/9490210

APK源码下载链接:http://download.csdn.net/detail/lyp461340781/9490216



备注:

不同版本内核的ioctl函数实现

Linux-2.6.36之前的内核:
[objc] view plain copy print ?
  1. int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)  
  2. (1)inode和file:ioctl的操作有可能是要修改文件的属性,或者访问硬件。要修改  
  3. 文件属性的话,就要用到这两个结构体了,所以这里传来了它们的指针;  
  4. (2)cmd:从用户空间传下来的命令参数;  
  5. (3)arg:可选参数,主要用于定义应用层和驱动层进行命令调用时候是否涉及数据读写;</span></span>  
Linux-2.3.36之后的内核:
[objc] view plain copy print ?
  1. int (*ioctl)(struct file *filp,unsigned int cmd,unsigned long arg)  
  2. (1)file:ioctl的操作有可能是要修改文件的属性,或者访问硬件。要修改  
  3. 文件属性的话,就要用到这两个结构体了,所以这里传来了它们的指针;  
  4. (2)cmd:从用户空间传下来的命令参数;  
  5. (3)arg:可选参数,主要用于定义应用层和驱动层进行命令调用时候是否涉及数据读写;</span> 

从linux-2.6.36之后,已经由unlocked_ioctl替代原来的ioctl。其中驱动的变化就是函数参数去掉inode参数。这也是我前面提及到的我在项目开发过程中遇到的问题所在。那时候项目开发时,旧平台的内核是Linux-2.6.32的,新平台的内核是Linux-3.6.5的,所以要修改ioctl函数,去掉struct inode *inode这个参数就可以了。在驱动开发中,如果大家遇到像我这样ioctl调用失效的问题,可以查看一下内核版本,然后看看是否是ioctl函数实现不一样导致的!

备注内容来自http://blog.csdn.net/zengxianyang/article/details/51008746







你可能感兴趣的:(linux,jni,内核,移植,android应用)