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;
}
本阶段主要参考博客《环境配置之Android Studio开发NDK》:点击打开链接
3.0 准备工作
参照搭建《环境配置之Android Studio开发NDK》搭建NDK开发环境。
3.1建立工程Test
建立对应的类,新建package jni与class GPIO,在GPIO类中声明native函数并载入library GPIO(库的名字可随意取)。
public class GPIO {
public static native long tiny6410_leds_ioctl(int number, int state);
static{
System.loadLibrary("GPIO");
}
}
使用javah指令,进入工程文件夹的\app\src\main\java目录下,执行javah指令,格式为javah -jni 包名.类名。一般错误为进错文件夹造成的。
在android studio的src/main文件夹中新建JNI文件夹,将生成的.h文件移动到此文件夹中。
.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
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;
}
设置根目录中的local.properties与APP项目build.gradle,其中stl "stlport_static"与 ldLibs "log"的设置使得NDK中可以使用 __android_log_print()函数打印调试信息。
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代码如下:
参照博客的教程仍然出现两个错,解决之后便可成功运行程序。通过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文件来解决,详见点击打开链接