本文主要是介绍s5p4418 android 驱动 hal 应用之led 串口 rs485 can总线应用(JNI层),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本篇文章用于记录Android开发学习过程中的一些理解和遇到的一些问题解决,以防忘记,好记性不如烂博客,O(∩_∩)O。
本篇相对于前面介绍的led操作增加了串口、485和can的应用。对于led的驱动、hal、app我这里就不再介绍,可以参考前面的文章Android应用开发 led 驱动层 hal硬件抽象层 应用层 详细教程记录(含源码)。
一般来说,Linux系统下对串口、485和can的操作都是基于节点的操作,如tty***、can*这样的节点,所以对于应用程序来说,只要有这些设备节点即可完成操作了,所以本篇文章介绍的串口和485应用可以兼容任何一款Android设备或者开发板,can不一定,因为can在系统服务中做了固定设置,默认使用的是can0,一般来系统中只有一个can节点,且为can0,当然不排除其他can1的情况,读者在使用时这个要注意。由于485跟串口区别不是很大,所以直接可以按照串口协议来进行操作,使用的也是tty***这样的节点设备,当然如果要像使用串口一样来使用485,那么在硬件上是要做特别设计的,可以参考下图所示的电路。
下面就主要介绍hal和app这两层,驱动层是由设备节点完成,只要系统中的设备节点没有问题,那么通信就是正常的,测试节点可在Linux系统下使用nfs进行调试测试(nfs个人认为有助于加快调试速度,因为无需下载等繁琐的操作)。
对于hal层我这里就直接贴代码,不再一步一步做出解释,代码后边会做一些必要的说明,主要是有疑问,需要注意的地方。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "jni.h"
#include "JNIHelp.h"
#include <assert.h>
#include "can.h"
#include <sys/socket.h>
#include <net/if.h>
#include <cutils/properties.h>
#include <sys/wait.h>
// 引入log头文件
#include <android/log.h>
// log标签
#define TAG "Led_Load_JNI"
// 定义info信息
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
// 定义debug信息
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
// 定义error信息
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)#define DEVICE_NAME "/dev/real_led"//#define NO_REGISTER
/*LED JNI*/
#ifndef NO_REGISTER
static jint Java_realarm_hardware_HardwareControl_LedSetState(JNIEnv *env, jobject thiz,jint ledNum,jint ledState)
#else
JNIEXPORT jint JNICALL Java_realarm_hardware_HardwareControl_LedSetState(JNIEnv *env, jobject thiz,jint ledNum,jint ledState)
#endif
{int fd = open(DEVICE_NAME, 0);if (fd == -1){LOGE("led open error");return 1; }if(ledState == 0)LOGD("Led close success");else if(ledState == 1)LOGD("Led open success");else {LOGD("Led ledState parameters ERROR.Only 0 or 1.");return 1;}ledState &= 0x01;ioctl(fd, ledState, 0);close(fd);return 0;
}/*LED JNI*//*UART JNI*/
int serialfd=-1;
static speed_t getBaudrate(jint baudrate)
{switch(baudrate) {case 0: return B0;case 50: return B50;case 75: return B75;case 110: return B110;case 134: return B134;case 150: return B150;case 200: return B200;case 300: return B300;case 600: return B600;case 1200: return B1200;case 1800: return B1800;case 2400: return B2400;case 4800: return B4800;case 9600: return B9600;case 19200: return B19200;case 38400: return B38400;case 57600: return B57600;case 115200: return B115200;case 230400: return B230400;case 460800: return B460800;case 500000: return B500000;case 576000: return B576000;case 921600: return B921600;case 1000000: return B1000000;case 1152000: return B1152000;case 1500000: return B1500000;case 2000000: return B2000000;case 2500000: return B2500000;case 3000000: return B3000000;case 3500000: return B3500000;case 4000000: return B4000000;default: return -1;}
}/** Class: cedric_serial_SerialPort* Method: open* Signature: (Ljava/lang/String;)V*/
static jobject Java_realarm_hardware_HardwareControl_OpenSerialPort(JNIEnv *env, jobject thiz, jstring path, jint baudrate, jint flags)
{int fd;speed_t speed;jobject mFileDescriptor;LOGD("baudrate %d", baudrate);/* Check arguments */{speed = getBaudrate(baudrate);if (speed == -1) {/* TODO: throw an exception */LOGE("Invalid baudrate");return NULL;}}/* Opening device */{jboolean iscopy;const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);LOGD("Opening serial port %s", path_utf);fd = open(path_utf, O_RDWR);serialfd=fd;LOGD("open() fd = %d", fd);(*env)->ReleaseStringUTFChars(env, path, path_utf);if (fd == -1){/* Throw an exception */LOGE("Cannot open port");/* TODO: throw an exception */return NULL;}}/* Configure device */{struct termios cfg;LOGD("Configuring serial port");if (tcgetattr(fd, &cfg)){LOGE("tcgetattr() failed");close(fd);/* TODO: throw an exception */return NULL;}cfmakeraw(&cfg);cfsetispeed(&cfg, speed);cfsetospeed(&cfg, speed);if (tcsetattr(fd, TCSANOW, &cfg)){LOGE("tcsetattr() failed");close(fd);/* TODO: throw an exception */return NULL;}}// Create a corresponding file descriptor {jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);}return mFileDescriptor;
}/** Class: cedric_serial_SerialPort* Method: close* Signature: ()V*/
static void Java_realarm_hardware_HardwareControl_CloseSerialPort(JNIEnv *env, jobject thiz)
{/* jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);ALOGD("close(fd = %d)", descriptor);close(descriptor);
*/LOGE("close(fd = %d)", serialfd);if(-1!=serialfd)close(serialfd);
}/*UART JNI*//*RS485 JNI*/
/*realarm开发板对485的使能做了硬件处理,所以485操作与串口并无区别*/
/*RS485 JNI*//*CAN JNI*/int canfd=-1;
struct sockaddr_can addr;void my_strcpy(char *dest, char *src, size_t n)
{char i = 0;while(i < n){ *(dest++) = *(src++);i++;}
}
int my_system(const char * cmd)
{ FILE * fp; int res; char buf[1024]; if (cmd == NULL) { LOGD("my_system cmd is NULL!\n");return -1;} if ((fp = popen(cmd, "r") ) == NULL) { LOGE("popen");LOGE("popen error: %s/n", strerror(errno)); return -1; } else{while(fgets(buf, sizeof(buf), fp)) { LOGD("%s", buf); } if ( (res = pclose(fp)) == -1) { LOGE("close popen file pointer fp error!\n"); return WEXITSTATUS(res);} else if (res == 0) {return WEXITSTATUS(res);} else { LOGD("popen res is :%d\n", res); return WEXITSTATUS(res); } }LOGI("popen success!\n");return -1;
}
static void Java_realarm_hardware_HardwareControl_InitCan(JNIEnv *env, jobject thiz, jint baudrate)
{/* Check arguments */switch (baudrate){case 5000 :case 10000 :case 20000 :case 50000 :case 100000 :case 125000 :LOGI("Can Bus Speed is %d",baudrate);break;default:LOGI("Can Bus Speed is %d.if it do not work,try 5000~125000",baudrate);}/* Configure device */if(baudrate!=0){char str_baudrate[16];sprintf(str_baudrate,"%d", baudrate);property_set("net.can.baudrate", str_baudrate); LOGI("str_baudrate is:%s", str_baudrate);property_set("net.can.change", "yes");/*//下面的方法无法实现波特率的设置,命令未正确执行char cmd[100];sprintf(cmd, "%s%s", "su ip link set can0 up type can bitrate ", str_baudrate);LOGI("cmd is:%s", cmd);my_system("busybox ifconfig can0 down");my_system(cmd); my_system("busybox ls");*/}
}
static jint Java_realarm_hardware_HardwareControl_OpenCan(JNIEnv *env, jobject thiz)
{struct ifreq ifr;int ret; /* Opening device */canfd = socket(PF_CAN,SOCK_RAW,CAN_RAW);if(canfd==-1){LOGE("Can Write Without Open"); return 0;}strcpy((char *)(ifr.ifr_name),"can0");ioctl(canfd,SIOCGIFINDEX,&ifr);addr.can_family = AF_CAN;addr.can_ifindex = ifr.ifr_ifindex;bind(canfd,(struct sockaddr*)&addr,sizeof(addr));return canfd;
}static jint Java_realarm_hardware_HardwareControl_CanWrite(JNIEnv *env, jobject thiz, jint canId, jstring data)
{int nbytes;int num = 0, i = 0;struct can_frame frame;jboolean iscopy;const char *send_data = (*env)->GetStringUTFChars(env, data, &iscopy); frame.can_id = canId;if(strlen(send_data) > 8)//用于支持当输入的字符大于8时的情况,分次数发送{num = strlen(send_data) / 8;for(i = 0;i < num;i++){my_strcpy((char *)frame.data, &send_data[8 * i], 8);frame.can_dlc = 8;sendto(canfd,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,sizeof(addr));}memset((char *)frame.data, 0, 8);my_strcpy((char *)frame.data, &send_data[8 * i], strlen(send_data) - num * 8);frame.can_dlc = strlen(send_data) - num * 8;sendto(canfd,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,sizeof(addr));nbytes = strlen(send_data);}else{ my_strcpy((char *)frame.data, send_data, strlen(send_data));frame.can_dlc = strlen(send_data);sendto(canfd,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,sizeof(addr));nbytes = strlen(send_data);}(*env)->ReleaseStringUTFChars(env, data, send_data);LOGD("write nbytes=%d",nbytes);return nbytes;
}static jobject Java_realarm_hardware_HardwareControl_CanRead(JNIEnv *env, jobject thiz, jobject obj, jint time)
{unsigned long nbytes,len;struct can_frame frame = {0};int k=0;jstring jstr; char temp[16];fd_set rfds;int retval;struct timeval tv;tv.tv_sec = time; tv.tv_usec = 0;bzero(temp,16);if(canfd==-1){LOGE("Can Read Without Open");frame.can_id=0;frame.can_dlc=0;}else{FD_ZERO(&rfds);FD_SET(canfd, &rfds);retval = select(canfd+1 , &rfds, NULL, NULL, &tv);if (retval == -1){LOGE("Can Read slect error");frame.can_dlc=0;frame.can_id=0;}else if (retval){nbytes = recvfrom(canfd, &frame, sizeof(struct can_frame), 0, (struct sockaddr *)&addr,&len);for(k = 0;k < frame.can_dlc;k++)temp[k] = frame.data[k];temp[k] = 0;frame.can_id = frame.can_id - 0x80000000;//读得的id比实际的有个80000000差值,这里需要处理一下LOGD("Can Read slect success.");}else{frame.can_dlc=0;frame.can_id=0;//LOGD("Can no data.");}}jclass objectClass = (*env)->FindClass(env,"realarm/hardware/CanFrame");jfieldID id = (*env)->GetFieldID(env,objectClass,"can_id","I");jfieldID leng = (*env)->GetFieldID(env,objectClass,"can_dlc","C");jfieldID str = (*env)->GetFieldID(env,objectClass,"data","Ljava/lang/String;");if(frame.can_dlc) { LOGD("can_id is :%d", frame.can_id);LOGD("can read nbytes=%d", frame.can_dlc);LOGD("can data is:%s", temp);}(*env)->SetCharField(env, obj, leng, frame.can_dlc);(*env)->SetObjectField(env, obj, str, (*env)->NewStringUTF(env,temp));(*env)->SetIntField(env, obj, id, frame.can_id);return obj;
}static void Java_realarm_hardware_HardwareControl_CloseCan(JNIEnv *env, jobject thiz)
{if(canfd!=-1)close(canfd);canfd=-1;LOGD("close can0");
}/*CAN JNI*/#ifndef NO_REGISTER
static JNINativeMethod gMethods[] = { {"LedSetState", "(II)I", (void *)Java_realarm_hardware_HardwareControl_LedSetState}, {"OpenSerialPort", "(Ljava/lang/String;II)Ljava/io/FileDescriptor;", (void *)Java_realarm_hardware_HardwareControl_OpenSerialPort},{"CloseSerialPort", "()V", (void *)Java_realarm_hardware_HardwareControl_CloseSerialPort},{"InitCan", "(I)V", (void *)Java_realarm_hardware_HardwareControl_InitCan}, {"OpenCan", "()I", (void *)Java_realarm_hardware_HardwareControl_OpenCan},{"CanWrite", "(ILjava/lang/String;)I", (void *)Java_realarm_hardware_HardwareControl_CanWrite}, {"CanRead", "(Lrealarm/hardware/CanFrame;I)Lrealarm/hardware/CanFrame;", (void *)Java_realarm_hardware_HardwareControl_CanRead},{"CloseCan", "()V", (void *)Java_realarm_hardware_HardwareControl_CloseCan},
}; static int register_android_realarm_test(JNIEnv *env)
{ jclass clazz;static const char* const kClassName = "realarm/hardware/HardwareControl";/* look up the class */clazz = (*env)->FindClass(env, kClassName);//clazz = env->FindClass(env,kClassBoa);if (clazz == NULL) {LOGE("Can't find class %s\n", kClassName);return -1;}/* register all the methods */if ((*env)->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)//if (env->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK){LOGE("Failed registering methods for %s\n", kClassName);return -1;}/* fill out the rest of the ID cache */return 0;
}
#endifjint JNI_OnLoad(JavaVM* vm, void* reserved) {#ifndef NO_REGISTERJNIEnv *env = NULL;if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) { //if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) { LOGI("Error GetEnv\n"); return -1; } assert(env != NULL); if (register_android_realarm_test(env) < 0) { printf("register_android_realarm_test error.\n"); LOGE("register_android_realarm_test error."); return -1; }
#endif/* success -- return valid version number */LOGI("/*****************realarm**********************/");return JNI_VERSION_1_4;
}
led需要注意的地方没什么可说的,串口部分我会在can中做说明。
下面是第一个需要说明的知识点:
// 引入log头文件
#include <android/log.h>
// log标签
#define TAG "Led_Load_JNI"
// 定义info信息
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
// 定义debug信息
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
// 定义error信息
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
可以看到这部分是一些宏定义,定义了LOGI、LOGD、LOGE这三个宏。目的是能够在Android调试的时候可以通过logcat查看到调试信息,对应的Android层的api分别是Log.i、Log.d、Log.e,所以是很必要的。
可以看到要使用这个功能需要__android_log_print这个函数,当然也需要#include <android/log.h>这个头文件。__android_log_print这个函数有三个参数,这里拿LOGI来说明,可以看到LOGI的实际代码是__android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__),第一个参数ANDROID_LOG_INFO是告诉Android系统当前是属于信息类log,第二个参数TAG是指示是哪一个部分的信息(这里是#define TAG "Led_Load_JNI",这样易区分是哪里打印的信息,也有助于调试),第三个参数__VA_ARGS__就是LOGI输入的要打印的信息了。具体的解释,大家谷歌吧。
当然了,如果不想这么定义直接使用__android_log_print函数也是可以的,不过看起应该很烦吧。
下面是第二个解释的函数:
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name="code" class="cpp">static void Java_realarm_hardware_HardwareControl_InitCan(JNIEnv *env, jobject thiz, jint baudrate)
{/* Check arguments */switch (baudrate){case 5000 :case 10000 :case 20000 :case 50000 :case 100000 :case 125000 :LOGI("Can Bus Speed is %d",baudrate);break;default:LOGI("Can Bus Speed is %d.if it do not work,try 5000~125000",baudrate);}/* Configure device */if(baudrate!=0){char str_baudrate[16];<span style="color:#ff0000;">sprintf(str_baudrate,"%d", baudrate);property_set("net.can.baudrate", str_baudrate); LOGI("str_baudrate is:%s", str_baudrate);property_set("net.can.change", "yes");</span><span style="color:#3333ff;">/*//下面的方法无法实现波特率的设置,命令未正确执行char cmd[100];sprintf(cmd, "%s%s", "su ip link set can0 up type can bitrate ", str_baudrate);LOGI("cmd is:%s", cmd);my_system("ifconfig can0 down");my_system(cmd); my_system("busybox ls");*/</span>}
}
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">首先来看上面函数的红色部分,sprintf和LOGI函数我这里就不说了,自行谷歌或百度。这里重要说明的是property_set这个函数,该函数是用于访问设置系统的环境属性,该函数是用于c/c++中的,在java中有其他的函数来代替,具体的可以参考http://blog.sina.com.cn/s/blog_55465b470101ngpv.html、http://blog.csdn.net/jackyu613/article/details/6136620等文章,百度或谷歌有很多。</span>
现在关键的是使用这个环境变量设置怎么就能够能够完成can的波特率设置呢。说起还是比较曲折呢,思路是这样的,在Android系统的时候设置一个can波特率设置的服务,并设置一个启动该服务的一个条件,当启动条件变成yes的时候,can波特率设置的服务就会重启一次,进而完成设置。
具体实现方法下面介绍。
/device/nexell/realarm目录下的init.realarm.rc最后的代码如下:
##############################**CAN**####################################
chmod 0777 /system/bin/can.sh
service can /system/bin/can.shoneshoton property:net.can.change=yesrestart can
#########################################################################
可以看到添加了一个can服务,以及net.can.change这个系统属性,那么启动can服务就是通过net.can.change这个系统属性是否为yes来触发的。
前面的红色代码最后一句可以看到就是设置net.can.change为yes属性。
那么net.can.baudrate这个属性怎么用的呢,可以看到红色代码中设置了这个属性,它是存储波特率的。它的使用时在启动的服务can.sh中,代码如下(位置在/device/nexell/realarm/can):
#!/system/bin/shsetprop net.can.change nonew_baudrate=`getprop net.can.baudrate`ifconfig can0 down
ip link set can0 up type can bitrate $new_baudrate
从can.sh代码中可以知道,先设置net.can.change为no以关闭触发,然后通过getprop(Android系统提供getprop和setprop来读取和设置系统属性的值)命令来获取net.can.baudrate属性的值(在上面红色代码中所设置的值),然后通过ip link set can0 up type can bitrate $new_baudrate命令来完成can的设置和启动。
这样can的设置就可以完成了。
另外前面代码还有蓝色部分,该部分被注释掉了,这个是我在尝试另一种设置波特率的方法,只是未成功,不过这里我也说一下原理,若有朋友找到了原因,可以告知我,感激不敬。
蓝色代码部分主要想通过使用my_system这个函数来执行shell命令完成can的设置,理论上是可以的,可是实际上却不行,因为无论怎么样都无法正确执行ifconfig can0 down和ip命令,但是ls等其他指令却可以正常执行,搞不懂了。如果ifconfig can0 down和ip能够正常执行,那么代码就更简单了。
下面是my_system()函数的实现:
int my_system(const char * cmd)
{ FILE * fp; int res; char buf[1024]; if (cmd == NULL) { LOGD("my_system cmd is NULL!\n");return -1;} if ((fp = popen(cmd, "r") ) == NULL) { LOGE("popen");LOGE("popen error: %s/n", strerror(errno)); return -1; } else{while(fgets(buf, sizeof(buf), fp)) { LOGD("%s", buf); } if ( (res = pclose(fp)) == -1) { LOGE("close popen file pointer fp error!\n"); return WEXITSTATUS(res);} else if (res == 0) {return WEXITSTATUS(res);} else { LOGD("popen res is :%d\n", res); return WEXITSTATUS(res); } }LOGI("popen success!\n");return -1;
}
关于popen和pclose的使用,以及c提供的system()函数,大家百度谷歌吧。
下面是第三个要解释的部分:
#ifndef NO_REGISTER
static JNINativeMethod gMethods[] = { {"LedSetState", "(II)I", (void *)Java_realarm_hardware_HardwareControl_LedSetState}, {"OpenSerialPort", "(Ljava/lang/String;II)Ljava/io/FileDescriptor;", (void *)Java_realarm_hardware_HardwareControl_OpenSerialPort},{"CloseSerialPort", "()V", (void *)Java_realarm_hardware_HardwareControl_CloseSerialPort},{"InitCan", "(I)V", (void *)Java_realarm_hardware_HardwareControl_InitCan}, {"OpenCan", "()I", (void *)Java_realarm_hardware_HardwareControl_OpenCan},{"CanWrite", "(ILjava/lang/String;)I", (void *)Java_realarm_hardware_HardwareControl_CanWrite}, <span style="color:#ff0000;">{"CanRead", "(Lrealarm/hardware/CanFrame;I)Lrealarm/hardware/CanFrame;", (void *)Java_realarm_hardware_HardwareControl_CanRead},</span>{"CloseCan", "()V", (void *)Java_realarm_hardware_HardwareControl_CloseCan},
}; static int register_android_realarm_test(JNIEnv *env)
{ jclass clazz;static const char* const kClassName = "realarm/hardware/HardwareControl";/* look up the class */clazz = (*env)->FindClass(env, kClassName);//clazz = env->FindClass(env,kClassBoa);if (clazz == NULL) {LOGE("Can't find class %s\n", kClassName);return -1;}/* register all the methods */if ((*env)->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)//if (env->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK){LOGE("Failed registering methods for %s\n", kClassName);return -1;}/* fill out the rest of the ID cache */return 0;
}
#endifjint JNI_OnLoad(JavaVM* vm, void* reserved) {#ifndef NO_REGISTERJNIEnv *env = NULL;if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) { //if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) { LOGI("Error GetEnv\n"); return -1; } assert(env != NULL); if (register_android_realarm_test(env) < 0) { printf("register_android_realarm_test error.\n"); LOGE("register_android_realarm_test error."); return -1; }
#endif/* success -- return valid version number */LOGI("/*****************realarm**********************/");return JNI_VERSION_1_4;
}
这部分是关于jni向上层提供api注册的部分。关于注册可以参考文章http://blog.csdn.net/wang_shuai_ww/article/details/44456755,关于JNINativeMethod这个结构体,百度谷歌更好。我这里只解释上面代码中红色部分。
一般的类型有如下:
字符
V
Z
I
J
D
F
B
C
S
数组则以"["开始,用两个字符表示
[I
[F
[B
[C
[S
[D
[J jlongArray
[Z
Ljava/net/Socket;
我们需要的结构是CanFrame结构,这个在Android系统中并未提供,而是我们应用的java代码中自定义的一个类,所在的包是realarm.hardware,类名是CanFrame,所以红色代码的写法就是Lrealarm/hardware/CanFrame,指定包和类名。从下图可以看到我们定义的类:
所以对于这类自定义的类型,在native注册时,参数(JNINativeMethod第二个参数)说明的方法是L+包名+类名。这个一定要理解,否则对于写jni有很大障碍,不懂的多谷歌JNINativeMethod这个结构的说明。
下面是第四个要解释的部分:
从第三部分的CanRead函数可知,Java_realarm_hardware_HardwareControl_CanRead函数要接收一个参数jobject obj,是jobject 类型的,实际上是CanFrame类型的,从第三个解释的部分可以知道。CanFrame和jni这层定义的can_frame怎么联系的呢。
先贴出代码,下面慢慢说。
static jobject Java_realarm_hardware_HardwareControl_CanRead(JNIEnv *env, jobject thiz, jobject obj, jint time)
{unsigned long nbytes,len;struct can_frame frame = {0};int k=0;jstring jstr; char temp[16];fd_set rfds;int retval;struct timeval tv;tv.tv_sec = time; tv.tv_usec = 0;bzero(temp,16);if(canfd==-1){LOGE("Can Read Without Open");frame.can_id=0;frame.can_dlc=0;}else{FD_ZERO(&rfds);FD_SET(canfd, &rfds);retval = select(canfd+1 , &rfds, NULL, NULL, &tv);if (retval == -1){LOGE("Can Read slect error");frame.can_dlc=0;frame.can_id=0;}else if (retval){nbytes = recvfrom(canfd, &frame, sizeof(struct can_frame), 0, (struct sockaddr *)&addr,&len);for(k = 0;k < frame.can_dlc;k++)temp[k] = frame.data[k];temp[k] = 0;frame.can_id = frame.can_id - 0x80000000;//读得的id比实际的有个80000000差值,这里需要处理一下LOGD("Can Read slect success.");}else{frame.can_dlc=0;frame.can_id=0;//LOGD("Can no data.");}}<span style="color:#ff0000;"> jclass objectClass = (*env)->FindClass(env,"realarm/hardware/CanFrame");jfieldID id = (*env)->GetFieldID(env,objectClass,"can_id","I");jfieldID leng = (*env)->GetFieldID(env,objectClass,"can_dlc","C");jfieldID str = (*env)->GetFieldID(env,objectClass,"data","Ljava/lang/String;");</span>if(frame.can_dlc) { LOGD("can_id is :%d", frame.can_id);LOGD("can read nbytes=%d", frame.can_dlc);LOGD("can data is:%s", temp);}<span style="color:#ff0000;"> </span><span style="color:#3333ff;">(*env)->SetCharField(env, obj, leng, frame.can_dlc);(*env)->SetObjectField(env, obj, str, (*env)->NewStringUTF(env,temp));(*env)->SetIntField(env, obj, id, frame.can_id);</span>return obj;
}
在上面代码中最不好理解的可能就是红色部分了,而这部分就是关键的部分。
(*env)->FindClass:获得CanFrame结构,并保存于objectClass
(*env)->GetFieldID:或者objectClass类中的成员,也就是CanFrame结构里的成员。该函数前两个参数不解释了容易理解,第三个参数和第四个参数比较重要,第三个参数标示CanFrame结构里的成员名称(需要名字一致,否则会出错的),第四个是该成员的类型。
下面举个例子
(*env)->GetFieldID(env,objectClass,"can_id","I")意思是获取objectClass类中的成员can_id,且类型为int型。与下图中CanFrame的定义对比:
can_id为Int型,是对应的。其他两个也同理,都是对应的。获得了java的类型后,下面看看是怎么联系的。
数据交换就是上面蓝色部分的代码了。
(*env)->SetCharField(env, obj, leng, frame.can_dlc);第二个参数就是Java_realarm_hardware_HardwareControl_CanRead接收的jobject obj参数,leng就是指can_dlc的fieldID,frame.can_dlc就是can读取到的字节数数据了。通过这样一句话,就把frame.can_dlc数据放到了obj中的can_dlc变量中了。
其他两个函数都类似,只是数据类型不同而已。最后把obj返回给java层,那么一个完整测CanFrame帧就读取完成了。
到此本篇介绍的JNI层,以及需要注意的地方就介绍完了,下一篇是app应用。
源码下载地址:http://download.csdn.net/detail/u010406724/8539263
转载请注明出处:http://blog.csdn.net/wang_shuai_ww/article/details/44672531
这篇关于s5p4418 android 驱动 hal 应用之led 串口 rs485 can总线应用(JNI层)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!