找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2903|回复: 0
打印 上一主题 下一主题
收起左侧

控制中心 - 笔记 -> S5PV210中ZigBee的Jni回调要点

[复制链接]
跳转到指定楼层
楼主
ID:71922 发表于 2015-1-10 20:10 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

由于ZigBee是用USB转串口通讯,所以需要在内核加上PL2303驱动。进入内核源码:make menuconfig ->
                     Device Drivers ->
                           <*> USB support ->
                                   <*> USB Serial Converter support ->
                                            <*>USB Prolific 2303 Single Port Serial Driver



启用之后重新编译,并烧录到FS210中。

ZigBee 模块是通过串口通信,需要发送什么数据,只需要往串口写入数据,里面的ZigBee即可发送数据。

Fs210 应用程序测试:app.c
-------------------------------------------------------------------------------------------------------
#include <termios.h>    //串口相关头文件
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


流程:
    1、打开串口

    2、初始化串口

    3、读写串口数据

    4、关闭串口



/* 初始化串口 */
void  serial_init(int fd)
{
        struct termios options;
        tcgetattr(fd, &options);
        options.c_cflag |= ( CLOCAL | CREAD );/*input mode flag:ignore modem
        options.c_cflag &= ~CSIZE;
        options.c_cflag &= ~CRTSCTS;
        options.c_cflag |= CS8;
        options.c_cflag &= ~CSTOPB; //停止位
        options.c_iflag |= IGNPAR;        // 忽略校验错误
        options.c_oflag = 0;// 无输出模式
        options.c_lflag = 0; //本地模式禁用
        options.c_cc[VTIME] = 0;                // 50 代表5秒 5秒内没有收到数据就返回
        options.c_cc[VMIN] = 2;                        // 代表收到2个字节就返回
        cfsetispeed(&options, B115200);
        cfsetospeed(&options, B115200);
        tcsetattr(fd,TCSANOW,&options);
}
/* 主函数 */
int main(int argc, char **args)
{
        int fd = 0;
        char a[3] = "v";
        int size = 0;
        /* 1、打开串口 */

        fd = open("/dev/ttyUSB0", O_RDWR);    // 因为该模块是USB转串口
        if(fd<0) return 0;
        puts("串口初始化....");


        /* 2、初始化串口 */

        serial_init(fd);


        /* 3、读写数据 */
        // 往串口写数据
        write(fd, a, strlen(a));
        puts("开始接受数据");
        while(1)
        {       
                memset(a, 0, sizeof(a));
                // 从串口读数据

                size = read(fd, a, sizeof(a));
                printf("a = %s\n", a);               
        }


        /* 4、关闭串口 */       
        close(fd);
        return 0;
}

-------------------------------------------------------------------------------------------------------

编译脚本:Android.mk

-------------------------------------------------------------------------------------------------------

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


LOCAL_SRC_FILES := app.c
LOCAL_MODULE := app_c


LOCAL_SHARED_LIBRARIES := \
        libcutils


LOCAL_MODULE_TAGS := optional


include $(BUILD_EXECUTABLE)

-------------------------------------------------------------------------------------------------------


Fs210作为控制中心,需要时时刻刻与M0通讯,并把数据显示在apk应用上,需要采用JNI与上层APK交互。
需要采用回调机制,Jin回调Java的方法。可以采用NDK或独立的Jni库形式。
这里,我考虑到日后更新方便,就采用分离的Jni库形式。

控制中心与节点通信的数据结构:---> 后来想想,下面这些用联合体会更加合适一些
-------------------------------------------------------------------------------------------------------

// 节点标识  
#define M0_NUM_1        1                // M0_1 标识
#define M0_NUM_2        2                // M0_2 标识
#define M0_NUM_3        3                // M0_3 标识
#define M0_NUM_4        4                // M0_4 标识
#define M0_NUM_5        5                // M0_5 标识
#define M0_NUM_6        6                // M0_6 标识


// 命令类型
#define Get_Data                        21                  // 获取数据
#define Set_Temp_Warning_val                22                // 设置温度报警阀值
#define Set_Hum_Warning_val                23                // 设置湿度报警阀值
#define Set_Auto_Send                        25                // 设置主动上报数据
#define Set_NoAuto_Send                        26                // 禁止主动上报数据
                                                  
// 设备状态
#define FAN_ON                                24                // 风扇开启状态
#define FAN_OFF                                25                // 风扇关闭状态
#define BEEP_ON                                26                // 蜂鸣器开启状态
#define BEEP_OFF                        27                // 蜂鸣器关闭状态


#define ZigBee_Cmd_HEAD        '$'
       
// 命令/数据 结构体
struct ZigBee_Cmd
{
        char Head;                        // 包头 由于从串口接数据有可能第一个字节不是头,所以需要判断
       
        int ID;                                // 节点标识
        char CmdType;                          // 命令类型
        char TempH;                        // 温度整数部分               
        char TempL;                        // 温度小数部分               
        char HumH;                        // 湿度整数部分               
        char HumL;                        // 湿度小数部分
        char AdvH;                        // 电压整数
        char AdvL;                        // 电压小数
        signed char x;                         // 三轴传感器 x
        signed char y;                        // 三轴传感器 y
        signed char z;                        // 三轴传感器 z
        int Light;                         // 亮度
        char FAN;                         // 风扇状态       
        char BEEP;                        // 蜂鸣器状态
        char Used;                        // CPU使用率
                       
        unsigned  int chksum;                // 校验码
};

-------------------------------------------------------------------------------------------------------

Jni代码
-------------------------------------------------------------------------------------------------------

在M0中提供两种数据发送方式:
被动获取数据:
    APK通过Jni打开串口发送 Get_Data 指令获取数据,M0收到指令就发送一次数据。
主动获取数据:
    APK通过Jni打开串口发送 Set_Auto_Send 指令,M0收到指令就连续发送数据。


Jni的难点在于回调Java的方法和串口操作,如数据接收。

回调Java方法:
            需要创建新的线程,去回调Java的方法,所以需要利用全局变量将Jni的 JNIEnv *,, jobject* 保存,
        然后在线程里面使用。
实现步骤:
APK 要点:
-------------------------------------------------------------------------------------------------------
步骤1:创建一个回调方法:
        private void ZigBee_Call(int ID, char TempH, char TempL, char HumH, char HumL, char AdvH,
            char AdvL, int x, int y, int z, int Light, char FAN, char BEEP, char CPUUSed)
        {
                // 通过发送Handler消息刷新APK的GUI

                return;
        }



步骤2:声明本地方法
        public native int ZigBee_Open();
        public native int ZigBee_Cmd(char cmd, int arg1, int arg2);
        public native int ZigBee_Close();
        public native int ZigBee_StartRevc();
        public native int ZigBee_StopRevc();

        // 添加这句是为了通过javap 命令获取其类型签名 获取完后可以删除掉

    public native void ZigBee_Call(int ID, char TempH, char TempL, char HumH, char HumL, char AdvH, char AdvL,
                        int x, int y, int z, int Light, char FAN, char BEEP, char CPUUSed);

步骤2:利用  javap -s bin\classes\com\lmx\zigbee\MainActivity 可以获取到类型签名 用于给Jni构建映射表
-------------------------------------------------------------------------------------------------------


Jni 回调要点:
-------------------------------------------------------------------------------------------------------

要点1:创建两个全局变量。给线程备用。
        JavaVM *gJavaVM = NULL;

        jobject gJavaObj= NULL;



要点2:提供一个函数,或在何时的地方保存全局变量
        //注意,直接通过定义全局的JNIEnv和jobject变量,在此保存env和thiz的值是不可以在线程中使用的
        //线程不允许共用env环境变量,但是JavaVM指针是整个jvm共用的,所以可通过下面的方法保存JavaVM指针,在线程中使用
        env->GetJavaVM(&gJavaVM);
        //同理,jobject变量也不允许在线程中共用,因此需要创建全局的jobject对象在线程中访问该对象
        gJavaObj = env->NewGlobalRef(thiz);


要点3:在线程里面通过保存的全局变量去获取Java环境变量、获取Java层对应类、获取回调函数。
        //从全局的JavaVM中获取到环境变量
        gJavaVM->AttachCurrentThread(&env, NULL);
        //获取Java层对应的类
        jclass javaClass = env->GetObjectClass(gJavaObj);;
        //获取Java层被回调的函数
        jmethodID javaCallback = env->GetMethodID(javaClass,"ZigBee_Call","(ICCCCCCIIIICCC)V");
        //回调Java层的函数
        env->CallVoidMethod(gJavaObj, javaCallback, ZData.ID, ZData.TempH, ZData.TempL,ZData.HumH,
        ZData.HumL, ZData.AdvH, ZData.AdvL, ZData.x, ZData.y, ZData.z, ZData.Light, ZData.FAN,
        ZData.BEEP,ZData.Used);
-------------------------------------------------------------------------------------------------------


串口操作要点:
-------------------------------------------------------------------------------------------------------

要点1:串口初始化,需要关注  .c_cc[VTIME]、.c_cc[VMIN]
void  serial_init(int fd)
{
        struct termios options;
        tcgetattr(fd, &options);
             options.c_cflag |= ( CLOCAL | CREAD );                                                                                  options.c_cflag &= ~CSIZE;
        options.c_cflag &= ~CRTSCTS;
        options.c_cflag |= CS8;                // 8位数据

        options.c_cflag &= ~CSTOPB;             //停止位

        options.c_iflag |= IGNPAR;                // 忽略校验错误
        options.c_oflag = 0;                   // 无输出模式
        options.c_lflag = 0;                   //本地模式禁用
        options.c_cc[VTIME] = 10;          // 表示 read() 超时值 50 表示 5秒后仍没有数据则返回
        options.c_cc[VMIN] = 1;           // 表示读到多少个字节就返回 5 表示读完5个字节就返回
        cfsetispeed(&options, B115200);        // 设置波特率

        cfsetospeed(&options, B115200);
        tcsetattr(fd,TCSANOW,&options);
}

要点2:读取数据的时候要注意,因为节点发送数据的时候,控制中心的ZigBee已经收到数据,而我们还未             做好准备,当ZigBee往串口写数据写到一半的时候,我们才做好准备收,这时候只能收到后半部分的数据,
             如果节点是一直发送数据,那么我们在收数据的时候就很可能变成 第一个数据包的后半部分与第二个数据的
             前半部分组合成一个数据包,那么解析的数据自然就不是正确的。
        //线程循环
        while(gIsThreadExit)
        {
                LOGD("ZigBee_Call...");
                memset(&ZData, 0, sizeof(struct ZigBee_Cmd));
                memset(&buf, 0, sizeof(buf));
                size = 0;
                while(size != sizeof(struct ZigBee_Cmd))  // 直至接受完一个数据包
                {
                        ret = read(fd, &rd, 1);
                        if((size == 0) && (rd != ZigBee_Cmd_HEAD)
                                continue;
                        if( ret == -1 || ret == 0)      // 若出错则跳出循环
                                breka;
                        buf[size++] = rd;               // 若考虑优化性能,这里可以直接用结构体存,
                                                        // 存之前只需要用char*p = (char*)ZData  存的时候 *p++ = rd;
                }
                // 将数据拷贝到结构体

                memcpy(&ZData, buf, sizeof(struct ZigBee_Cmd));
                校验数据....
                .........

                LOGD("Revc Data OK``");       
                回调Java层的函数,将数据回传...   
                .........

        }


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表