Tiny6410Android应用程序(JNI)控制GPIO

Android与驱动的新手,因为最近需要学习为开发板编写Android驱动,因此参考了网上的一些教程,实现了最基本的GPIO驱动以及简单的APP界面来入个门。

开发环境如下:FriendlyArm-TIny6410开发板,Android-2.3.4,android-kernel-2.6.36,Android Studio 1.4。


1、编写驱动

本程序控制的是GPF14,参考友善之臂的GPIO-LED源码,修改如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define S3C64XX_GPFCON (S3C64XX_GPF_BASE + 0x00) 
#define S3C64XX_GPFDAT (S3C64XX_GPF_BASE + 0x04) 
#define DEBUG            0
#define DEVICE_NAME      "tiny6410_leds"

static long tiny6410_leds_ioctl(struct file *flip,unsigned int cmd, unsigned long arg){
    /* arg:which io port */
    switch(cmd) {
       unsigned tmp;
    case 0:
    case 1:
       if (arg > 4) {
           return -EINVAL;
       }
       tmp =readl(S3C64XX_GPFDAT); // read data register
       tmp&= ~(1 << (14+arg));  // clear bit
       tmp |=((!cmd) << (14+arg));  // set bitwith cmd
       writel(tmp,S3C64XX_GPFDAT);  // write data register
       return 0;
    default:
       return -EINVAL;
    }
}

static struct file_operations dev_fops = {
    .owner =THIS_MODULE,
    .unlocked_ioctl= tiny6410_leds_ioctl,
};

static struct miscdevice misc = {
    .minor =MISC_DYNAMIC_MINOR,
    .name =DEVICE_NAME,
    .fops =&dev_fops,
};

static int __init dev_init(void){
    int ret;
    unsigned tmp;

    /*configure config GPFCON register : 01 output */
    tmp =readl(S3C64XX_GPFCON);
    tmp = (tmp&~ (0x3<<28)) | (0x1U<<28);
    writel(tmp,S3C64XX_GPFCON);

    /*configure GPFDAT data register : init dark */
    tmp =readl(S3C64XX_GPFDAT);
    tmp |=(0x1 << 14);
    writel(tmp,S3C64XX_GPFDAT);
    ret =misc_register(&misc);
    printk(DEVICE_NAME"\tinitialized\n");

    return ret;
}

static void __exit dev_exit(void){
    misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zx");


2、验证驱动

在虚拟机中通过arm-linux-gcc编译之后生成可执行文件GPIO_test,通过adb push传入开发板,在adb shell中执行chmod,更改可执行文件权限为777之后运行。

【遇到问题】:android缺少一些linux的库,因此执行GPIO_test会报错"system/bin/sh: ./GPIO_test:not found"

【解决方法】为选择静态编译或者将Linux的库拷贝到Android中,具体请见该博客《Android系统运行动态编译的程序》:点击打开链接

#include 
#include 
#include 
#include 

#define DEVICE_NAME "/dev/tiny6410_leds"
 
int main(int argc,char**argv){
    int on;
    int led_no;
    int fd;

    if (argc != 3 || sscanf(argv[1], "%d",&led_no) != 1 || sscanf(argv[2],"%d", &on) != 1 || on < 0|| on > 1 || led_no < 0 || led_no > 3) {
       fprintf(stderr,"Usage: leds led_no 0|1\n");
       exit(1);
    }

    fd =open(DEVICE_NAME, 0);
    if (fd < 0) {
       perror("opendevice leds");
       exit(1);
    }
    ioctl(fd,on, led_no);
    close(fd);

    return 0;

}


3、Android Studio编写NDK代码

本阶段主要参考博客《环境配置之Android Studio开发NDK》:点击打开链接

3.0 准备工作

参照搭建《环境配置之Android Studio开发NDK》搭建NDK开发环境。

3.1建立工程Test

建立对应的类,新建package jni与class GPIO,在GPIO类中声明native函数并载入library GPIO(库的名字可随意取)。

Tiny6410Android应用程序(JNI)控制GPIO_第1张图片

public class GPIO {
    public static native long tiny6410_leds_ioctl(int number, int state);
    static{
        System.loadLibrary("GPIO");
    }
}


3.2 准备.h文件

使用javah指令,进入工程文件夹的\app\src\main\java目录下,执行javah指令,格式为javah -jni 包名.类名。一般错误为进错文件夹造成的。

Tiny6410Android应用程序(JNI)控制GPIO_第2张图片
在android studio的src/main文件夹中新建JNI文件夹,将生成的.h文件移动到此文件夹中。

Tiny6410Android应用程序(JNI)控制GPIO_第3张图片

.h文件中的JNIEXPORT jlong JNICALL Java_com_example_wangtao_test_jni_GPIO_tiny6410_1leds_1ioctl与GPIO类中native long tiny6410_leds_ioctl(int number, int state);对应,具体如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_example_wangtao_test_jni_GPIO */

#ifndef _Included_com_example_wangtao_test_jni_GPIO
#define _Included_com_example_wangtao_test_jni_GPIO
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_wangtao_test_jni_GPIO
 * Method:    tiny6410_leds_ioctl
 * Signature: (II)J
 */
JNIEXPORT jlong JNICALL Java_com_example_wangtao_test_jni_GPIO_tiny6410_1leds_1ioctl
  (JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif


3.3实现native方法

JNI文件夹中新建com_example_wangtao_test_jni_GPIO.c.文件实现JNIEXPORT jlong JNICALL Java_com_example_wangtao_test_jni_GPIO_tiny6410_1leds_1ioctl。在native函数中,使用 __android_log_print()函数代替printf函数,具体见博文《在android 输出log 信息 用于调试》,点击打开链接

代码如下:

#include "com_example_wangtao_test_jni_GPIO.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define LOG_TAG "LED"       //android logcat
#define DEVICE_NAME "/dev/tiny6410_leds"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__    )
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS_    _)

JNIEXPORT jlong JNICALL Java_com_example_wangtao_test_jni_GPIO_tiny6410_1leds_1ioctl
        (JNIEnv *env, jclass cls, jint number, jint state) {
        int fd;
        fd = open(DEVICE_NAME, 0);
        if(fd < 0)
                __android_log_print(ANDROID_LOG_INFO, "JNI_GPIO","open fail!\n");
        else
                __android_log_print(ANDROID_LOG_INFO, "JNI_GPIO","open successfully!\n");

        ioctl(fd, number, state);
        close(fd);

        return 0;
}


3.4设置项目

设置根目录中的local.properties与APP项目build.gradle,其中stl "stlport_static"与 ldLibs "log"的设置使得NDK中可以使用 __android_log_print()函数打印调试信息。

Tiny6410Android应用程序(JNI)控制GPIO_第4张图片

Tiny6410Android应用程序(JNI)控制GPIO_第5张图片


3.5 在MainActivity中调用native方法

MainActivity代码如下:

package com.example.wangtao.test;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

import com.example.wangtao.test.jni.GPIO;

public class MainActivity extends AppCompatActivity {
    Button LedOn;
    Button LedOff;

    static{
        System.loadLibrary("GPIO");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        LedOn  = (Button) findViewById(R.id.led_on);
        LedOff = (Button) findViewById(R.id.led_off);

        LedOn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                GPIO.tiny6410_leds_ioctl(0, 0);
                System.out.println("Led_on Done!");
            }
        });

        LedOff.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                GPIO.tiny6410_leds_ioctl(1,0);
                System.out.println("Led_off Done!");
            }
        });
    }

}
layout代码如下:




    


3.6 编译并运行

参照博客的教程仍然出现两个错,解决之后便可成功运行程序。通过APP控制GPIO电平高低,用万用表检测结果正确。

【遇到问题】出现的错误Error:(14, 0) Error: NDK integration is deprecated in the current plugin.  Consider trying the new experimental plugin.  ·
【解决方法】设置 gradle.properties,参见:点击打开链接

【遇到问题】解决上面的问题之后之后出现了另外的错误Execution failed for task ':app:compileDebugNdk'

【解决方法】这个是Android Stuido 1.X版本的一个知名BUG,可以在JNI目录中新建一个.C文件来解决,详见点击打开链接









你可能感兴趣的:(Tiny6410Android应用程序(JNI)控制GPIO)