PC机:ubuntu 12.04.5
开发板:tiny4412
Android版本:5.0.2
Android IDE:Android Studio
前提:
PC机已经搭建好 Android 开发环境,已经安装好交叉编译器,
已经编译好 Android 内核 以及 Android 源码,
编写 Android APP,实现以下功能:
1、一个 button 控制全部 LED,按一下全开,按一下全关
2、四个 CheckBox 分别单独控制每个LED,按一下对应的LED开,再按一下关
------------------------------------------------------------------------
------------------------------------------------------------------------
使用 Android Studio 建立 APP 工程,具体建立过程请 Google
界面建立完成后,开始编写操作 LED 的代码
在 F:\AndroidTest\APP_0001_LEDDemo\app\src\main\java\com\example\wenjs\目录下,建立一个 hardlibrary 目录
在 hardlibrary 目录下,建立一个 HardLibrary.java 文件,用来编写 ledOpen、ledCtrl、ledClose 等本地方法
新建一个文件夹,用来保存动态链接库的源代码,参考之前的 jni 教程,建立一个本地的 .c 文件
按照 jni 的规则,编写 hardlibrary.c 文件,完成本地的 led_open、led_ctrl、led_close 等函数
编写完成后,把 hardlibrary.c 上传到 Linux 并进行交叉编译,执行以下命令:
arm-linux-gcc -fPIC -shared hardlibrary.c -o libhardlibrary.so -I/usr/lib/jvm/java-7-openjdk-amd64/include/
编译参数解释:
-I 表示指定 jni.h 的文件路径位置
-fPIC 表示生成位置无关码,参考博客: http://blog.sina.com.cn/s/blog_54f82cc201011op1.html
-shared 表示生成动态链接库 .so 文件
-o 表示生成目标文件
编译完成后,生成 libhardlibrary.so 文件,在 F:\AndroidTest\APP_0001_LEDDemo\app\libs 目录下新建 armeabi 文件夹
把 libhardlibrary.so 上传到 armeabi 文件夹中
同时,修改APP工程的 build.gradle(Module:app),添加以下内容:
sourceSets{
main{
jniLibs.srcDirs = ['libs']
}
}
这个表示我们的 jni lib 文件存放在 libs 目录里面,这种方式是会把.so文件编译进APK文件里
以上工作完成后,把应用程序在开发板上运行,程序出现崩溃,
查看调试信息,发现 could not load library "libc.so.6" needed by "libhardlibrary.so"
libhardlibrary.so 的运行依赖于 libc.so.6
查看开发板的终端,ls /vendor/lib 或者 ls /system/lib/libc.* 发现没有 libc.so.6
在Android源码目录 /opt/work/source_code/android-5.0.2/,查找libc.so
find -iname "libc.so*"
修改一下 hardlibrary.c 的编译选项,改为如下:
arm-linux-gcc -fPIC -shared hardlibrary.c -o libhardlibrary.so -I/usr/lib/jvm/java-7-openjdk-amd64/include/ \
-nostdlib /opt/work/source_code/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so
nostdlib表示不使用标准库,而使用 Android 源码目录下的libc库:
/opt/work/source_code/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so
修改完成后,重新编译运行 Android APP,程序运行正确。表明 Android APP 已经成功调用了本地的 c 库
继续修改 Android APP的代码,调用 ledOpen、ledCtrl、ledClose 等方法
修改 hardlibrary.c 文件,添加 Android 的打印信息,适用以下函数:
#include <android/log.h> //包含这个头文件
__android_log_print(ANDROID_LOG_INFO, "JniX431FileTest", "lsx_init"); //打印语句
ANDROID_LOG_INFO:是日志级别;
"JniX431FileTest":是要过滤的标签,可以在LogCat视图中过滤。
"lsx_init":是实际的日志内容。
再次执行以下命令编译 hardlibrary.c ,提示找不到 android/log.h
到 Android 源码目录查找一下,发现 android/log.h 在以下路径:
/opt/work/source_code/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include/android/log.h
更改编译选项为以下内容:
arm-linux-gcc -fPIC -shared hardlibrary.c -o libhardlibrary.so -I/usr/lib/jvm/java-7-openjdk-amd64/include/ \
-nostdlib /opt/work/source_code/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so \
-I/opt/work/source_code/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include/
hardlibrary.c 编译成功,重新上传到 Android APP的 armeabi 目录,再次运行 APP
运行失败,根据提示信息:dlopen failed: cannot locate symbol "__android_log_print" ,打开动态库失败
到 Android 源码目录查找一下,发现 liblog 在以下路径:
/opt/work/source_code/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/liblog.so
更改编译选项为以下内容:
arm-linux-gcc -fPIC -shared hardlibrary.c -o libhardlibrary.so -I/usr/lib/jvm/java-7-openjdk-amd64/include/ \
-nostdlib /opt/work/source_code/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so \
-I/opt/work/source_code/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include/ \
/opt/work/source_code/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/liblog.so
经过以上操作,Android APP 和 本地方法 libhardlibrary.so 编译成功
把 app 下载到开发板上运行,点击 button 和 checkbox,程序正确运行,但此时还没有编写硬件驱动,只有打印信息
--------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
编写 led 硬件驱动程序
参考韦东山Linux第二期视频,使用字符设备驱动模型,来开发led驱动程序,此处不再重复
以下是驱动源码:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <device.h>
static int major = 0;
static struct class *led_class;
static int led_gpios[] = {
EXYNOS4212_GPM4(0),
EXYNOS4212_GPM4(1),
EXYNOS4212_GPM4(2),
EXYNOS4212_GPM4(3),
};
static int led_open(void)
{
/* set gpio output */
int i = 0;
for(i=0;i<4;i++)
{
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
//gpio_set_value(led_gpios[i], 1);
}
return 0;
}
//app : ioctl(fd,cmd,arg)
static long led_ioctl(struct file *filp, unsigned int cmd,unsigned long arg)
{
if((cmd != 0) && (cmd != 1))
return -EINVAL;
if(arg > 4)
return -EINVAL;
gpio_set_value(led_gpios[arg], !cmd);
return 0;
}
static struct file_operastions leds_fops = {
.owner = THIS_MODULE,
.open = led_open,
.unlocked_ioctl = led_ioctl,
};
int leds_init(void)
{
major = register_chrdev(0,"leds",&leds_fops);
led_class = class_create(THIS_MODULE,"leds");
device_create(led_class, NULL, MKDEV(major,0), NULL, "leds");
return 0;
}
void leds_exit(void)
{
device_destroy(led_class, MKDEV(major,0));
class_destroy(led_class);
unregister_chrdev(major,"leds");
}
module_init(leds_init);
module_exit(leds_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("[email protected]");
把led驱动放入 Android kernel 的 device/char目录。
修改Android 内核的makefile,添加编译选项,重新编译 make zImage
重新烧写内核到开发板。
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
再次修改 hardlibrary.c 文件,修改里面的 led_open、led_close、led_ctrl 函数,
在里面的这些函数里,使用 open 、read 、write 函数来操作硬件
重新编译 hardlibrary.c 为 libhardlibrary.so ,并重新上传到Android app 的 armeabi 目录
重新编译 Android app 工程,运行,可以使用app操作开发板上的led了
整个层次关系如下:
Android APP ----> 加载 c 库
------------------------------------
JNI 本地接口函数(其实就是Linux的应用层)
--------------------------------------------
Linux 驱动程序