Android Studio JNI Development (3) - Realize the control of hardware devices through the IIC protocol

Android Studio JNI Development (3) - Realize the control of hardware devices through the IIC protocol

  1. Create a new Java JNI class MyJni.java, and write the JNI native method.
public class MyJNI {
    
    

    static {
    
    
        System.loadLibrary("jni_native");
    }

    public static native String getStringFromNative();

    public static native int OpenDeviceNode();

    public static native int CloseDeviceNode();

    public static native int IICWriteRead(byte[] wBuf, int wLen, byte[] rBuf, int rLen);
    
}
  1. To generate the header file, enter the project path NdkDemo\app\src\main\java with cmd, and execute the command to generate the .h header file.
javah -classpath . -jni com.csu.jni.MyJNI

Generate a .h header file com_csu_jni_MyJNI.h in the same directory as the MyJNI.java file (main\java\com.csu.jni).

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <jni.h>
#include <android/log.h>
/* Header for class com_csu_jni_MyJNI */

#ifndef _Included_com_csu_jni_MyJNI
#define _Included_com_csu_jni_MyJNI
#ifdef __cplusplus
extern "C" {
    
    
#endif
/*
 * Class:     com_csu_jni_MyJNI
 * Method:    getStringFromNative
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_csu_jni_MyJNI_getStringFromNative
  (JNIEnv *, jclass);

/*
 * Class:     com_csu_jni_MyJNI
 * Method:    OpenDeviceNode
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_csu_jni_MyJNI_OpenDeviceNode
  (JNIEnv *, jclass);

/*
 * Class:     com_csu_jni_MyJNI
 * Method:    CloseDeviceNode
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_csu_jni_MyJNI_CloseDeviceNode
  (JNIEnv *, jclass);

/*
 * Class:     com_csu_jni_MyJNI
 * Method:    IICWriteRead
 * Signature: ([BI[BI)I
 */
JNIEXPORT jint JNICALL Java_com_csu_jni_MyJNI_IICWriteRead
  (JNIEnv *, jclass, jbyteArray, jint, jbyteArray, jint);

#ifdef __cplusplus
}
#endif
#endif

  1. Create a new jni directory in the same level of java directory under main, copy the .h file generated by step6 to the jni directory, and then create a new com_csu_jni_MyJNI.c file to implement the method in the .h file.
//
// Created by Admin on 2021/11/17.
//
#include "com_csu_jni_MyJNI.h"

#define LOG_TAG "MyJNI"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

/**
 * My device name
 */
#define DEVICE_NAME    "/dev/myjni"

/**
 * File descriptor
 */
static int fd = -1;

int file_read(jbyte *rbuf, jint rlen) {
    
    
    int ret;
    if (fd < 0) {
    
    
        LOGE("file_read(), JNI IS NOT OPENED\n");
    }
    LOGI("JNI file_read(), rlen=%d, rbuf=%d\n", rlen, rbuf[0]);

    ret = read(fd, rbuf, rlen);
    if (ret < 0) {
    
    
        LOGE("JNI read failed, ret=%d\n", ret);
    }
    LOGI("JNI read success, ret=%d\n", ret);

    return ret;
}

int file_write(jbyte *wbuf, jint wlen) {
    
    
    int ret;
    if (fd < 0) {
    
    
        LOGE("file_write(), JNI IS NOT OPENED\n");
    }
    LOGI("JNI file_write(), wlen=%d, wbuf=%d\n", wlen, wbuf[0]);

    ret = write(fd, wbuf, wlen);
    if (ret < 0) {
    
    
        LOGE("JNI write failed, ret=%d\n", ret);
    }
    LOGI("JNI write success, ret=%d\n", ret);

    return ret;
}

/*
 * Class:     com_csu_jni_MyJNI
 * Method:    getStringFromNative
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_csu_jni_MyJNI_getStringFromNative(JNIEnv *env, jclass clazz) {
    
    
    return (*env)->NewStringUTF(env, "My JNI string");
}

/*
 * Class:     com_csu_jni_MyJNI
 * Method:    OpenDeviceNode
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_csu_jni_MyJNI_OpenDeviceNode(JNIEnv *env, jclass clazz) {
    
    

    fd = open(DEVICE_NAME, O_RDWR|O_NONBLOCK);
    if (fd < 0) {
    
    
        LOGE("MyJNI open device failed, fd =%d\n", fd);
    }
    LOGI("MyJNI open device Success, fd =%d\n", fd);

    return fd;
}

/*
 * Class:     com_csu_jni_MyJNI
 * Method:    CloseDeviceNode
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_csu_jni_MyJNI_CloseDeviceNode(JNIEnv *env, jclass clazz) {
    
    
    close(fd);
    return 0;
}

/*
 * Class:     com_csu_jni_MyJNI
 * Method:    IICWriteRead
 * Signature: ([BI[BI)I
 */
JNIEXPORT jint JNICALL Java_com_csu_jni_MyJNI_IICWriteRead(JNIEnv *env, jclass clazz, jbyteArray wBuf, jint wLen, jbyteArray rBuf, jint rLen) {
    
    
    int ret = 0;

    // 将Java层的数据拷到缓冲区
    jbyte* bufw = (*env)->GetByteArrayElements(env, wBuf, NULL);
    //jbyte* bufr = (*env)->GetByteArrayElements(env, rBuf, NULL);
    //jint wlen = (*env)->GetArrayLength(env, wBuf);
    jint len = (*env)->GetArrayLength(env, rBuf);

    // 驱动会把要读取数据的寄存器放在首位返回,所以从驱动获取的数据buf长度要+1
    len = len + 1;

    // 新建一个数组,用于将驱动数据拷贝到缓冲区
    jbyteArray jarr = (*env)->NewByteArray(env, len);
    jbyte* jbuf = (*env)->GetByteArrayElements(env, jarr, NULL);

    LOGI("JNI IICWriteRead(), wlen=%d, rlen=%d\n", wLen, rLen);

    if (wLen > 1) {
    
    
        ret = file_write(bufw, wLen);
        LOGI("JNI IICWriteRead(), wlen=%d, ret=%d\n", wLen, ret);
    }

    // get register addr
    jbuf[0] = bufw[0];

    if (rLen > 0) {
    
    
        ret = file_read(jbuf, rLen);
        LOGI("JNI IICWriteRead(), rlen=%d, ret=%d, rbuf=%d.%d.%d.%d\n", rLen, ret, jbuf[1], jbuf[2], jbuf[3], jbuf[4]);

        // 驱动返回缓存区的数据,首位为寄存器地址,真正获取的数据从下标1开始
        memcpy(jbuf, jbuf + 1, len - 1);

        // 将缓冲区的数据返回给Java形参
        (*env)->SetByteArrayRegion(env, rBuf, 0, rLen, jbuf);

    }

    // 释放资源,避免内存泄漏
    (*env)->ReleaseByteArrayElements(env, rBuf, jbuf, 0);
    (*env)->DeleteLocalRef(env, jarr);

    LOGI("JNI IICWriteRead(), ret=%d\n", ret);

    return ret;
}
  1. Create a new Android.mk file under the jni directory.
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := jni_native
LOCAL_SRC_FILES := com_csu_jni_MyJNI.c

LOCAL_LDLIBS :=-llog

APP_ABI := all

include $(BUILD_SHARED_LIBRARY)
  1. Enter the project path NdkDemo\app\src\main\java with cmd, execute the command to compile and generate the .so file.
ndk-build

Two folders libs and obj will be generated in the same directory of jni.
insert image description here

  1. Copy the contents of the libs folder to the libs directory under the module project name.

  2. Add a reference to libs in the android{} node of the module's build.gradle file:

sourceSets {
    main {
        jniLibs.srcDirs = ['libs']
    }
}
  1. Call JNI method
    MainActivity.java in Activity
public class MainActivity extends AppCompatActivity {
    
    

    private static final String TAG = "lxy";

    private TextView tvInfo;

    private int id = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String str = MyJNI.getStringFromNative();
        Log.i(TAG, "onCreate: rs = " + str);


        Button btnOpen = findViewById(R.id.btn_open);
        Button btnClose = findViewById(R.id.btn_close);
        Button btnFwVersion = findViewById(R.id.btn_read_fw_version);
        Button btnWrite = findViewById(R.id.btn_write);
        Button btnRead = findViewById(R.id.btn_read);
        tvInfo = findViewById(R.id.tv_info);

        btnOpen.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                if (id > 0) {
    
    
                    Log.i(TAG, "onClick: device is opened!!!");
                    updateTextInfo("device is opened!!!");
                } else {
    
    
                    id = MyJNI.OpenDeviceNode();
                    updateTextInfo("device open, id = " + id);
                    Log.i(TAG, "onClick: device open id = " + id);
                }
            }
        });

        btnClose.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                if (id > 0) {
    
    
                    int rs = MyJNI.CloseDeviceNode();
                    Log.i(TAG, "onClick: close device rs = " + rs);
                    updateTextInfo("close device, rs = " + rs);
                    id = 0;
                } else {
    
    
                    Log.i(TAG, "onClick: device is not open");
                    updateTextInfo("device is not open");
                }
            }
        });

        btnFwVersion.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                if (id > 0) {
    
    

                    byte[] wBuf = {
    
    0x10};
                    byte[] rBuf = new byte[256];
                    int rs = MyJNI.IICWriteRead(wBuf, 1, rBuf, 4);
                    Log.i(TAG, "onClick: ret = " + rs + ",rBuf= "
                            + rBuf[0] + "." + rBuf[1] + "." + rBuf[2] + "." + rBuf[3]);
                    updateTextInfo("ret = " + rs + ",rBuf= "
                            + rBuf[0] + "." + rBuf[1] + "." + rBuf[2] + "." + rBuf[3]);
                }
            }
        });

        btnWrite.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                if (id > 0) {
    
    
                    write();
                }
            }
        });

        btnRead.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                if (id > 0) {
    
    
                    read();
                }
            }
        });

    }

    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();
        int rs = MyJNI.CloseDeviceNode();
        Log.i(TAG, "onClick: close device rs = " + rs);
    }

    private void write() {
    
    

        byte[] wbuf = {
    
    0x00, 0x00, 0x01, 0x02, 0x03};
        byte[] rbuf = new byte[10];
        int rs = MyJNI.IICWriteRead(wbuf, 5, rbuf, 0);
        Log.i(TAG, "write(): ret=" + rs);
        updateTextInfo("write to fw : " + Arrays.toString(wbuf));
    }

    private void read() {
    
    

        byte[] wbuf = {
    
    0x00};
        byte[] rbuf = new byte[256];
        int rs = MyJNI.IICWriteRead(wbuf, 1, rbuf, 6);
        Log.i(TAG, "read(): ret=" + rs + ", rbuf = " + Arrays.toString(rbuf));
        updateTextInfo("read from fw : " + Arrays.toString(Arrays.copyOf(rbuf, 6)));
    }

    private void updateTextInfo(final String s) {
    
    
        runOnUiThread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                tvInfo.setText(s);
            }
        });
    }

}

Layout xml file:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="My IIC JNI Test"
        android:textSize="24sp"
        android:textColor="#353535"
        android:layout_marginTop="20dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

    <Button
        android:id="@+id/btn_open"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="open"
        android:layout_marginTop="80dp"
        android:layout_marginStart="60dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"/>

    <Button
        android:id="@+id/btn_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="close"
        android:layout_marginStart="20dp"
        app:layout_constraintTop_toTopOf="@id/btn_open"
        app:layout_constraintStart_toEndOf="@id/btn_open"/>

    <Button
        android:id="@+id/btn_read_fw_version"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="fw_ver"
        android:layout_marginStart="20dp"
        app:layout_constraintTop_toTopOf="@id/btn_open"
        app:layout_constraintStart_toEndOf="@id/btn_close"/>

    <Button
        android:id="@+id/btn_write"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="w"
        app:layout_constraintTop_toBottomOf="@id/btn_open"
        app:layout_constraintStart_toStartOf="@id/btn_open"/>

    <Button
        android:id="@+id/btn_read"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="r"
        android:layout_marginStart="20dp"
        app:layout_constraintTop_toTopOf="@id/btn_write"
        app:layout_constraintStart_toEndOf="@id/btn_write"/>

    <TextView
        android:id="@+id/tv_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="40dp"
        app:layout_constraintTop_toBottomOf="@id/btn_write"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>


</androidx.constraintlayout.widget.ConstraintLayout>

insert image description here
insert image description here

Guess you like

Origin blog.csdn.net/tracydragonlxy/article/details/122303447