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](https://img.it610.com/image/info8/7dfedac9333843d2b899aec3517c2894.jpg)
文章图片
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](https://img.it610.com/image/info8/9b98694709c846c0a7406782a0efb397.jpg)
文章图片
在android studio的src/main文件夹中新建JNI文件夹,将生成的.h文件移动到此文件夹中。
![Tiny6410Android应用程序(JNI)控制GPIO](https://img.it610.com/image/info8/649068fd46d74866a26f7f29e37da429.jpg)
文章图片
.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](https://img.it610.com/image/info8/1fc978a9f21b49a89f358b47ba86b26b.jpg)
文章图片
![Tiny6410Android应用程序(JNI)控制GPIO](https://img.it610.com/image/info8/5f1ad755929d4d3fa7200aec4e1b8cb0.jpg)
文章图片
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】
推荐阅读
- VueX(Vuex|VueX(Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式)
- Android|Android JNI之静态注册(android studio)
- 热点文章|鸢尾花预测(如何创建机器学习Web应用程序())
- Android|Android JNI 篇 - 编译 bilibili/ijkPlayer
- 启动优化
- 推荐一个基于Dapr的|推荐一个基于Dapr的 Red Dog 的完整微服务应用程序
- 应用程序启动过程
- 译文|借助|译文|借助 Pulsar Functions 迁移到无服务应用程序
- GCLocker介绍
- PWA|PWA 技术落地!让你的站点(Web)秒变APP(应用程序)