找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 3408|回復(fù): 0
收起左側(cè)

控制中心 - 筆記 -> S5PV210中ZigBee的Jni回調(diào)要點

[復(fù)制鏈接]
ID:71922 發(fā)表于 2015-1-10 20:10 | 顯示全部樓層 |閱讀模式

由于ZigBee是用USB轉(zhuǎn)串口通訊,所以需要在內(nèi)核加上PL2303驅(qū)動。進(jìn)入內(nèi)核源碼:make menuconfig ->
                     Device Drivers ->
                           <*> USB support ->
                                   <*> USB Serial Converter support ->
                                            <*>USB Prolific 2303 Single Port Serial Driver



啟用之后重新編譯,并燒錄到FS210中。

ZigBee 模塊是通過串口通信,需要發(fā)送什么數(shù)據(jù),只需要往串口寫入數(shù)據(jù),里面的ZigBee即可發(fā)送數(shù)據(jù)。

Fs210 應(yīng)用程序測試:app.c
-------------------------------------------------------------------------------------------------------
#include <termios.h>    //串口相關(guān)頭文件
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


流程:
    1、打開串口

    2、初始化串口

    3、讀寫串口數(shù)據(jù)

    4、關(guān)閉串口



/* 初始化串口 */
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秒內(nèi)沒有收到數(shù)據(jù)就返回
        options.c_cc[VMIN] = 2;                        // 代表收到2個字節(jié)就返回
        cfsetispeed(&options, B115200);
        cfsetospeed(&options, B115200);
        tcsetattr(fd,TCSANOW,&options);
}
/* 主函數(shù) */
int main(int argc, char **args)
{
        int fd = 0;
        char a[3] = "v";
        int size = 0;
        /* 1、打開串口 */

        fd = open("/dev/ttyUSB0", O_RDWR);    // 因為該模塊是USB轉(zhuǎn)串口
        if(fd<0) return 0;
        puts("串口初始化....");


        /* 2、初始化串口 */

        serial_init(fd);


        /* 3、讀寫數(shù)據(jù) */
        // 往串口寫數(shù)據(jù)
        write(fd, a, strlen(a));
        puts("開始接受數(shù)據(jù)");
        while(1)
        {       
                memset(a, 0, sizeof(a));
                // 從串口讀數(shù)據(jù)

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


        /* 4、關(guān)閉串口 */       
        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通訊,并把數(shù)據(jù)顯示在apk應(yīng)用上,需要采用JNI與上層APK交互。
需要采用回調(diào)機(jī)制,Jin回調(diào)Java的方法?梢圆捎肗DK或獨立的Jni庫形式。
這里,我考慮到日后更新方便,就采用分離的Jni庫形式。

控制中心與節(jié)點通信的數(shù)據(jù)結(jié)構(gòu):---> 后來想想,下面這些用聯(lián)合體會更加合適一些
-------------------------------------------------------------------------------------------------------

// 節(jié)點標(biāo)識  
#define M0_NUM_1        1                // M0_1 標(biāo)識
#define M0_NUM_2        2                // M0_2 標(biāo)識
#define M0_NUM_3        3                // M0_3 標(biāo)識
#define M0_NUM_4        4                // M0_4 標(biāo)識
#define M0_NUM_5        5                // M0_5 標(biāo)識
#define M0_NUM_6        6                // M0_6 標(biāo)識


// 命令類型
#define Get_Data                        21                  // 獲取數(shù)據(jù)
#define Set_Temp_Warning_val                22                // 設(shè)置溫度報警閥值
#define Set_Hum_Warning_val                23                // 設(shè)置濕度報警閥值
#define Set_Auto_Send                        25                // 設(shè)置主動上報數(shù)據(jù)
#define Set_NoAuto_Send                        26                // 禁止主動上報數(shù)據(jù)
                                                  
// 設(shè)備狀態(tài)
#define FAN_ON                                24                // 風(fēng)扇開啟狀態(tài)
#define FAN_OFF                                25                // 風(fēng)扇關(guān)閉狀態(tài)
#define BEEP_ON                                26                // 蜂鳴器開啟狀態(tài)
#define BEEP_OFF                        27                // 蜂鳴器關(guān)閉狀態(tài)


#define ZigBee_Cmd_HEAD        '$'
       
// 命令/數(shù)據(jù) 結(jié)構(gòu)體
struct ZigBee_Cmd
{
        char Head;                        // 包頭 由于從串口接數(shù)據(jù)有可能第一個字節(jié)不是頭,所以需要判斷
       
        int ID;                                // 節(jié)點標(biāo)識
        char CmdType;                          // 命令類型
        char TempH;                        // 溫度整數(shù)部分               
        char TempL;                        // 溫度小數(shù)部分               
        char HumH;                        // 濕度整數(shù)部分               
        char HumL;                        // 濕度小數(shù)部分
        char AdvH;                        // 電壓整數(shù)
        char AdvL;                        // 電壓小數(shù)
        signed char x;                         // 三軸傳感器 x
        signed char y;                        // 三軸傳感器 y
        signed char z;                        // 三軸傳感器 z
        int Light;                         // 亮度
        char FAN;                         // 風(fēng)扇狀態(tài)       
        char BEEP;                        // 蜂鳴器狀態(tài)
        char Used;                        // CPU使用率
                       
        unsigned  int chksum;                // 校驗碼
};

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

Jni代碼
-------------------------------------------------------------------------------------------------------

在M0中提供兩種數(shù)據(jù)發(fā)送方式:
被動獲取數(shù)據(jù):
    APK通過Jni打開串口發(fā)送 Get_Data 指令獲取數(shù)據(jù),M0收到指令就發(fā)送一次數(shù)據(jù)。
主動獲取數(shù)據(jù):
    APK通過Jni打開串口發(fā)送 Set_Auto_Send 指令,M0收到指令就連續(xù)發(fā)送數(shù)據(jù)。


Jni的難點在于回調(diào)Java的方法和串口操作,如數(shù)據(jù)接收。

回調(diào)Java方法:
            需要創(chuàng)建新的線程,去回調(diào)Java的方法,所以需要利用全局變量將Jni的 JNIEnv *,, jobject* 保存,
        然后在線程里面使用。
實現(xiàn)步驟:
APK 要點:
-------------------------------------------------------------------------------------------------------
步驟1:創(chuàng)建一個回調(diào)方法:
        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)
        {
                // 通過發(fā)送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構(gòu)建映射表
-------------------------------------------------------------------------------------------------------


Jni 回調(diào)要點:
-------------------------------------------------------------------------------------------------------

要點1:創(chuàng)建兩個全局變量。給線程備用。
        JavaVM *gJavaVM = NULL;

        jobject gJavaObj= NULL;



要點2:提供一個函數(shù),或在何時的地方保存全局變量
        //注意,直接通過定義全局的JNIEnv和jobject變量,在此保存env和thiz的值是不可以在線程中使用的
        //線程不允許共用env環(huán)境變量,但是JavaVM指針是整個jvm共用的,所以可通過下面的方法保存JavaVM指針,在線程中使用
        env->GetJavaVM(&gJavaVM);
        //同理,jobject變量也不允許在線程中共用,因此需要創(chuàng)建全局的jobject對象在線程中訪問該對象
        gJavaObj = env->NewGlobalRef(thiz);


要點3:在線程里面通過保存的全局變量去獲取Java環(huán)境變量、獲取Java層對應(yīng)類、獲取回調(diào)函數(shù)。
        //從全局的JavaVM中獲取到環(huán)境變量
        gJavaVM->AttachCurrentThread(&env, NULL);
        //獲取Java層對應(yīng)的類
        jclass javaClass = env->GetObjectClass(gJavaObj);;
        //獲取Java層被回調(diào)的函數(shù)
        jmethodID javaCallback = env->GetMethodID(javaClass,"ZigBee_Call","(ICCCCCCIIIICCC)V");
        //回調(diào)Java層的函數(shù)
        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:串口初始化,需要關(guān)注  .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位數(shù)據(jù)

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

        options.c_iflag |= IGNPAR;                // 忽略校驗錯誤
        options.c_oflag = 0;                   // 無輸出模式
        options.c_lflag = 0;                   //本地模式禁用
        options.c_cc[VTIME] = 10;          // 表示 read() 超時值 50 表示 5秒后仍沒有數(shù)據(jù)則返回
        options.c_cc[VMIN] = 1;           // 表示讀到多少個字節(jié)就返回 5 表示讀完5個字節(jié)就返回
        cfsetispeed(&options, B115200);        // 設(shè)置波特率

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

要點2:讀取數(shù)據(jù)的時候要注意,因為節(jié)點發(fā)送數(shù)據(jù)的時候,控制中心的ZigBee已經(jīng)收到數(shù)據(jù),而我們還未             做好準(zhǔn)備,當(dāng)ZigBee往串口寫數(shù)據(jù)寫到一半的時候,我們才做好準(zhǔn)備收,這時候只能收到后半部分的數(shù)據(jù),
             如果節(jié)點是一直發(fā)送數(shù)據(jù),那么我們在收數(shù)據(jù)的時候就很可能變成 第一個數(shù)據(jù)包的后半部分與第二個數(shù)據(jù)的
             前半部分組合成一個數(shù)據(jù)包,那么解析的數(shù)據(jù)自然就不是正確的。
        //線程循環(huán)
        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))  // 直至接受完一個數(shù)據(jù)包
                {
                        ret = read(fd, &rd, 1);
                        if((size == 0) && (rd != ZigBee_Cmd_HEAD)
                                continue;
                        if( ret == -1 || ret == 0)      // 若出錯則跳出循環(huán)
                                breka;
                        buf[size++] = rd;               // 若考慮優(yōu)化性能,這里可以直接用結(jié)構(gòu)體存,
                                                        // 存之前只需要用char*p = (char*)ZData  存的時候 *p++ = rd;
                }
                // 將數(shù)據(jù)拷貝到結(jié)構(gòu)體

                memcpy(&ZData, buf, sizeof(struct ZigBee_Cmd));
                校驗數(shù)據(jù)....
                .........

                LOGD("Revc Data OK``");       
                回調(diào)Java層的函數(shù),將數(shù)據(jù)回傳...   
                .........

        }


回復(fù)

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表