linux app调用 jni 加载 led驱动点亮led等

本文详细介绍了如何在Linux环境下,通过app调用JNI接口来加载C++编写的JNI库,进而操作LED驱动以点亮LED灯。首先展示了app部分的图形界面activity代码,接着是JNI的C++实现,包括基本的代码框架和Android.mk文件。LED驱动的实现参考了作者之前的文章,并提示调试时需注意printk的日志级别。最终,经过调试验证,app成功点亮了平板上的LED灯。

1.app部分编写代码流程

jni : java native interface 
java代码调用jni的方式:
1, 加载jni的动态库
	static{
		System.loadLibrary("led_jni"); // /system/lib/libled_jni.so 
	}
2, 声明本地native方法
	public native int openDev();
	public native int devOn();
	public native int devOff();
	public native int closeDev();
3, 直接调用native方法
	mynative.openDev();

示例代码如下:

LedActive.java
package com.example.ducks;

import com.hq.lowlevel.LedNative;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.Toast;
import android.view.View;
import android.view.View.OnClickListener;

public class LedActivity extends Activity{

	LedNative mynative;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		
		setContentView(R.layout.activity_led);
		initUI();
		
		mynative = new LedNative();
		
		mynative.openDev();
	}
	
	private void initUI() {
		// TODO Auto-generated method stub
		Button btn_on = (Button) findViewById(R.id.led1_off);
		Button btn_off = (Button) findViewById(R.id.led1_on);
		
		btn_on.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				Log.i("LedActivity", "led on");
				Toast.makeText(LedActivity.this, "狼来了,请闭眼", 3000).show();
				mynative.devOn();
			}
		});
		
		btn_off.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				Log.i("LedActivity", "led off");
				Toast.makeText(LedActivity.this, "天亮了,请睁眼", 3000).show();
				
				mynative.devOff();
			}
		});
	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		mynative.closeDev();
	}
	
}

led图形界面activity代码如下:

package com.example.keeprun;

import com.hq.lowlevel.LedNative;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class LedActivity extends Activity {
	Button btn_on;
	Button btn_off;
	LedNative mynative;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_led);
		initUI();
		mynative = new LedNative();
		//调用本地方法
		mynative.openDev();
	}

	private void initUI() {
		// TODO Auto-generated method stub
		btn_on = (Button) findViewById(R.id.btn_on);
		btn_off = (Button) findViewById(R.id.btn_off);
		btn_on.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				Log.i("LedActivity", "led on");
				Toast.makeText(LedActivity.this, "灯亮了", 1000).show();
				mynative.devOn();
			}
		});
		
		btn_off.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				Log.i("LedActivity", "led off");
				Toast.makeText(LedActivity.this, "灯灭了", 1000).show();
				
				mynative.devOff();
			}
		});
	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		
		mynative.closeDev();
	}

}

至此,app调用jni静态库部分代码完成。

2.jni c++代码
基本代码框架思想流程:

Ubuntu去编写jni:
模板:
	development/samples/SimpleJNI/jni/
编写工具:sourceinsight
	只需要一个头文件:
		dalvik\libnativehelper\include\nativehelper\jni.h 

jni中的映射表:
typedef struct {
    const char* name; //java的方法名
    const char* signature; //方法的描述
    void*       fnPtr; //c/c++的函数名
} JNINativeMethod;

//构建一个映射表
JNINativeMethod  led_methods[] = {
		{"openDev", "()I", (void*)open_jni_led},
		{"devOn", "()I", (void*)led_jni_on},
		{"devOff", "()I", (void*)led_jni_off},
		{"closeDev", "()I", (void*)close_jni_led},
};
// 实现一个函数--jni_onload(), 在函数中注册映射表
//返回值--正确返回JNI_VERSION_1_4, 出错返回负数
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
	int ret;
	//通过一个环境变量对象来注册, 环境变量对象能够提供各种函数: 数据转换,注册映射表,回调java方法
	JNIEnv *env = NULL;
	//初始化环境变量对象
	ret = vm->GetEnv((void * * )&env, JNI_VERSION_1_4);
	if(ret != JNI_OK)
	{
		LOGE("vm->GetEnv error");
		return -1;
	}
	jclass clz = env->FindClass("com/hq/lowlevel/LedNative");
	if(clz == NULL)
	{
		LOGE("env->FindClass error");
		return -1;
	}
	//参数1--java本地方法对应的类
	ret = env->RegisterNatives(clz,  led_methods,  ARRAY_SIZE(led_methods));
	if(ret <  0)
	{
		LOGE("env->RegisterNatives error");
		return -1;
	}
	return  JNI_VERSION_1_4;
}
还需要编写 Android,mk(类似于Makefile)用于生成led_jni.so动态库。

jni示例代码如下:



#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>

#define LOG_TAG "led_jni_log"
#include <utils/Log.h>


#include "jni.h"

#define LED_NUM_ON		_IOW('L',0x1122,int)
#define LED_NUM_OFF		_IOW('L',0x3344,int)
#define LED_ALL_ON		_IO('L',0x1234)
#define LED_ALL_OFF		_IO('L',0x5678)

#define ARRAY_SIZE(x)   sizeof(x)/sizeof(x[0])


static jint fd;
jint open_jni_led()
{
	LOGD("--------%s----------", __FUNCTION__);
	fd = open("/dev/led1",O_RDWR);
	if(fd < 0)
	{
		LOGE("open :  %s", strerror(errno));
		return -1;
	}
	
	return 0;
}

jint led_jni_on()
{
	LOGD("--------%s----------", __FUNCTION__);

	int ret;
	
	ret = ioctl(fd,LED_NUM_ON,1);
	if(ret < 0)
	{
		LOGE("ioctl on :  %s", strerror(errno));
		return -1;
	}
	
	return 0;
}

jint led_jni_off()
{
	LOGD("--------%s----------", __FUNCTION__);

	int ret;
	
	ret = ioctl(fd,LED_NUM_OFF,1);
	if(ret < 0)
	{
		LOGE("ioctl off :  %s", strerror(errno));
		return -1;
	}

	
	return 0;
}

jint close_jni_led()
{
	LOGD("--------%s----------", __FUNCTION__);

	close(fd);
	
	return 0;
}

//构建一个映射表
JNINativeMethod  led_methods[] = {
		{"openDev", "()I", (void*)open_jni_led},
		{"devOn", "()I", (void*)led_jni_on},
		{"devOff", "()I", (void*)led_jni_off},
		{"closeDev", "()I", (void*)close_jni_led},
};
// 实现一个函数--jni_onload(), 在函数中注册映射表
//返回值--正确返回JNI_VERSION_1_4, 出错返回负数
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
	int ret;
	//通过一个环境变量对象来注册, 环境变量对象能够提供各种函数: 数据转换,注册映射表,回调java方法
	JNIEnv *env = NULL;
	//初始化环境变量对象
	ret = vm->GetEnv((void * * )&env, JNI_VERSION_1_4);
	if(ret != JNI_OK)
	{
		LOGE("vm->GetEnv error");
		return -1;
	}
	jclass clz = env->FindClass("com/hq/lowlevel/LedNative");
	if(clz == NULL)
	{
		LOGE("env->FindClass error");
		return -1;
	}
	//参数1--java本地方法对应的类
	ret = env->RegisterNatives(clz,  led_methods,  ARRAY_SIZE(led_methods));
	if(ret <  0)
	{
		LOGE("env->RegisterNatives error");
		return -1;
	}
	return  JNI_VERSION_1_4;
}

Android.mk代码如下:

====================================================
Android.mk 

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)


LOCAL_MODULE_TAGS := optional

#注意:目标文件的名字一定要java中加载的so的名字保持一致
LOCAL_MODULE:= libled_jni

LOCAL_SRC_FILES:= led_jni.cpp

#因为使用了LOGD()
LOCAL_SHARED_LIBRARIES := \
        libcutils

#指定jni.h所在的路径
LOCAL_C_INCLUDES += \
        $(JNI_H_INCLUDE)

include $(BUILD_SHARED_LIBRARY)

============================================
运行Android.mk生成动态库.so文件

clover@ubuntn:~/Clover/android4.4$ 
clover@ubuntn:~/Clover/android4.4$ source build/envsetup.sh 
including device/generic/x86/vendorsetup.sh
including device/generic/armv7-a-neon/vendorsetup.sh
including device/generic/mips/vendorsetup.sh
including device/samsung/manta/vendorsetup.sh
including device/softwinner/polaris-common/vendorsetup.sh
including device/softwinner/fspad-723/vendorsetup.sh
including device/asus/deb/vendorsetup.sh
including device/asus/tilapia/vendorsetup.sh
including device/asus/flo/vendorsetup.sh
including device/asus/grouper/vendorsetup.sh
including device/lge/mako/vendorsetup.sh
including device/lge/hammerhead/vendorsetup.sh
including sdk/bash_completion/adb.bash
clover@ubuntn:~/Clover/android4.4$ 
clover@ubuntn:~/Clover/android4.4$ lunch

You're building on Linux

Lunch menu... pick a combo:
     1. aosp_arm-eng
     2. aosp_x86-eng
     3. aosp_mips-eng
     4. vbox_x86-eng
     5. mini_x86-userdebug
     6. mini_armv7a_neon-userdebug
     7. mini_mips-userdebug
     8. aosp_manta-userdebug
     9. fspad_723-eng
     10. aosp_deb-userdebug
     11. aosp_tilapia-userdebug
     12. aosp_flo-userdebug
     13. aosp_grouper-userdebug
     14. aosp_mako-userdebug
     15. aosp_hammerhead-userdebug

Which would you like? [aosp_arm-eng] 9	//选择对应平板产品

============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=4.4.2
TARGET_PRODUCT=fspad_723
TARGET_BUILD_VARIANT=eng
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
TARGET_CPU_VARIANT=cortex-a7
HOST_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-3.13.0-32-generic-x86_64-with-Ubuntu-14.04-trusty
HOST_BUILD_TYPE=release
BUILD_ID=KVT49L
OUT_DIR=out
============================================

clover@ubuntn:~/Clover/android4.4$ mmm /home/clover/Desktop/led/led_jni	//Android.mk和jni.cpp程序的路径
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=4.4.2
TARGET_PRODUCT=fspad_723
TARGET_BUILD_VARIANT=eng
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
TARGET_CPU_VARIANT=cortex-a7
HOST_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-3.13.0-32-generic-x86_64-with-Ubuntu-14.04-trusty
HOST_BUILD_TYPE=release
BUILD_ID=KVT49L
OUT_DIR=out
============================================
No private recovery resources for TARGET_DEVICE fspad-723
make: Entering directory `/home/clover/Clover/android4.4'
target thumb C++: libled_jni <= /home/clover/Desktop/led/led_jni/led_jni.cpp
target SharedLib: libled_jni (out/target/product/fspad-723/obj/SHARED_LIBRARIES/libled_jni_intermediates/LINKED/libled_jni.so)
target Symbolic: libled_jni (out/target/product/fspad-723/symbols/system/lib/libled_jni.so)
target Strip: libled_jni (out/target/product/fspad-723/obj/lib/libled_jni.so)
Install: out/target/product/fspad-723/system/lib/libled_jni.so
make: Leaving directory `/home/clover/Clover/android4.4'
clover@ubuntn:~/Clover/android4.4$ 


3.led驱动代码

参考上一篇文章
如果需要查看驱动的printk打印信息需要给予最高优先级
printk("<0>" “hello world! 0\n”);
详看
https://blog.csdn.net/shi18363123750/article/details/83346855

4.调试验证结果 (app点亮平板的led灯)

=================================
运行代码:

1, 安装apk:
2, libjni_led.so ==> /system/lib/
把jni动态库libjni_led.so通过adb push到arm板子上路径为/system/lib/
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190712103219662.PNG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjM5OTc1Mg==,size_16,color_FFFFFF,t_70)

3, 驱动-》装载
	如果驱动加载失败,查看对应的字符设备例如led,是否卸载响应的驱动。就可以正确加载。
	uid=0 gid=1007@android:/ # insmod  led_drv.ko
<kernel>call led_init()
major=203

#chmod 777 /dev/led1		//给文件所有权限,不然驱动,打不开字符设备

    uid=0 gid=1007@android:/ # logcat
    --------- beginning of /dev/log/main
    D/AudioHardware( 2148): AudioHardware pcm playback is exiting standby.
    D/AudioHardware( 2148): openPcmOut_l() mPcmOpenCnt: 0
    E/AudioHardware( 2148): openPcmOut_l() cannot open pcm_out driver: cannot open device '/dev/snd/pcmC0D0p': No such file or directory
    --------- beginning of /dev/log/system
    I/ActivityManager( 2249): START {cmp=com.example.keeprun/.LedActivity} from pid 3009
    D/led_jni_log( 3009): --------open_jni_led----------
    E/led_jni_log( 3009): open :  Permission denied  //解决:chmod 777 /dev/led1
    D/dalvikvm( 2249): GC_CONCURRENT freed 525K, 8% free 9313K/10055K, paused 2ms+30ms
    I/WindowManager( 2249): WIN DEATH: Window{412ecb50 com.example.keeprun/com.example.keeprun.MainActivity paused=false}
    I/ActivityManager( 2249): Process com.example.keeprun (pid 3009) has died.
    W/ActivityManager( 2249): Force removing ActivityRecord{41333150 com.example.keeprun/.LedActivity}: app died, no saved state
    I/WindowManager( 2249): WIN DEATH: Window{413c0eb0 Toast paused=false}
    W/ActivityManager( 2249): Force removing ActivityRecord{412eb708 com.example.keeprun/.MainActivity}: app died, no saved state
    D/Zygote  ( 2146): Process 3009 exited cleanly (1)
    binder: 2500:2500 transaction failed 29189, size 92-0
    W/InputManagerService( 2249): Got RemoteException sending setActive(false) notification to pid 3009 uid 10054
    binder: 2249:2263 transaction failed 29189, size 80-0
    W/NotificationService( 2249): Object died trying to hide notification android.app.ITransientNotification$Stub$Proxy@412d0ce8 in package com.example.keeprun
    W/ActivityManager( 2249): setProcessForeground called on unknown pid: 3009
    
    uid=0 gid=1007@android:/ # logcat -c
    uid=0 gid=1007@android:/ # logcat
    --------- beginning of /dev/log/system
    I/ActivityManager( 2249): START {cmp=com.example.keeprun/.LedActivity} from pid 3092
    --------- beginning of /dev/log/main
    D/dalvikvm( 3092): GC_CONCURRENT freed 135K, 4% free 7070K/7303K, paused 2ms+11ms
    D/led_jni_log( 3092): --------open_jni_led----------
    <kernel>45 call led_open
    
    I/ActivityManager( 2249): Displayed com.example.keeprun/.LedActivity: +229ms
    D/OpenGLRenderer( 3092): Flushing caches (mode 0)
    I/LedActivity( 3092): led on
    D/led_jni_log( 3092): --------led_jni_on----------
    <kernel> call led_ioctl
    LED_NUM_ON 1074027810
    
    I/LedActivity( 3092): led off
    D/led_jni_log( 3092): --------led_jni_off----------
    <kernel> call led_ioctl
    ========================================



开机就装载驱动:
修改init.rc 

283         insmod  /led_drv.ko
284         chmod 777 /dev/led1
285         setprop vnc.srv.start 1
286         setprop service.adb.tcp.port 77888
287
288 on boot
289 # basic network init



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值