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 DEBUG0 #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
文章图片



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
文章图片

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


.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" #defineLOGI(...)__android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #defineLOGE(...)__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
文章图片
Tiny6410Android应用程序(JNI)控制GPIO
文章图片


Tiny6410Android应用程序(JNI)控制GPIO
文章图片




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】



    推荐阅读