|
一
由于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ù)回傳...
.........
}
|
|