当前位置: 代码迷 >> Android >> s5p4418 android 驱动 hal 当用之led 串口 rs485 can总线应用(JNI层)
  详细解决方案

s5p4418 android 驱动 hal 当用之led 串口 rs485 can总线应用(JNI层)

热度:783   发布时间:2016-04-28 01:50:03.0
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_REGISTERstatic jint Java_realarm_hardware_HardwareControl_LedSetState  	(JNIEnv *env, jobject thiz,jint ledNum,jint ledState)#elseJNIEXPORT 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_REGISTERstatic 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_REGISTER	JNIEnv *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.sh    oneshoton property:net.can.change=yes    restart 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 downip 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_REGISTERstatic 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_REGISTER	JNIEnv *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这个结构体,百度谷歌更好。我这里只解释上面代码中红色部分。

一般的类型有如下:

字符  Java类型 C类型

V       void            void
Z        jboolean     boolean
I         jint              int
J        jlong            long
D       jdouble       double
F       jfloat            float
B       jbyte            byte
C       jchar           char
S       jshort          short

数组则以"["开始,用两个字符表示

[I        jintArray      int[]
[F      jfloatArray    float[]
[B      jbyteArray    byte[]
[C     jcharArray    char[]
[S     jshortArray   short[]
[D     jdoubleArray double[]
[J     jlongArray     long[]
[Z     jbooleanArray boolean[]

Ljava/lang/String; String jstring
Ljava/net/Socket; Socket jobject
我们需要的结构是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

  相关解决方案