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;
}
#endif
jint 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
oneshot
on 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/sh
setprop net.can.change no
new_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;
}
#endif
jint 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这个结构体,百度谷歌更好。我这里只解释上面代码中红色部分。
一般的类型有如下:
字符
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
更多推荐
所有评论(0)