|
一
由于ZigBee是用USB轉(zhuǎn)串口通訊,所以需要在內(nèi)核加上PL2303驅(qū)動(dòng)。進(jìn)入內(nèi)核源碼:make menuconfig ->
Device Drivers ->
<*> USB support ->
<*> USB Serial Converter support ->
<*>USB Prolific 2303 Single Port Serial Driver
啟用之后重新編譯,并燒錄到FS210中。
ZigBee 模塊是通過(guò)串口通信,需要發(fā)送什么數(shù)據(jù),只需要往串口寫(xiě)入數(shù)據(jù),里面的ZigBee即可發(fā)送數(shù)據(jù)。
Fs210 應(yīng)用程序測(cè)試: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、打開(kāi)串口
2、初始化串口
3、讀寫(xiě)串口數(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; // 忽略校驗(yàn)錯(cuò)誤
options.c_oflag = 0;// 無(wú)輸出模式
options.c_lflag = 0; //本地模式禁用
options.c_cc[VTIME] = 0; // 50 代表5秒 5秒內(nèi)沒(méi)有收到數(shù)據(jù)就返回
options.c_cc[VMIN] = 2; // 代表收到2個(gè)字節(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、打開(kāi)串口 */
fd = open("/dev/ttyUSB0", O_RDWR); // 因?yàn)樵撃K是USB轉(zhuǎn)串口
if(fd<0) return 0;
puts("串口初始化....");
/* 2、初始化串口 */
serial_init(fd);
/* 3、讀寫(xiě)數(shù)據(jù) */
// 往串口寫(xiě)數(shù)據(jù)
write(fd, a, strlen(a));
puts("開(kāi)始接受數(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作為控制中心,需要時(shí)時(shí)刻刻與M0通訊,并把數(shù)據(jù)顯示在apk應(yīng)用上,需要采用JNI與上層APK交互。
需要采用回調(diào)機(jī)制,Jin回調(diào)Java的方法。可以采用NDK或獨(dú)立的Jni庫(kù)形式。
這里,我考慮到日后更新方便,就采用分離的Jni庫(kù)形式。
控制中心與節(jié)點(diǎn)通信的數(shù)據(jù)結(jié)構(gòu):---> 后來(lái)想想,下面這些用聯(lián)合體會(huì)更加合適一些
-------------------------------------------------------------------------------------------------------
// 節(jié)點(diǎn)標(biāo)識(shí)
#define M0_NUM_1 1 // M0_1 標(biāo)識(shí)
#define M0_NUM_2 2 // M0_2 標(biāo)識(shí)
#define M0_NUM_3 3 // M0_3 標(biāo)識(shí)
#define M0_NUM_4 4 // M0_4 標(biāo)識(shí)
#define M0_NUM_5 5 // M0_5 標(biāo)識(shí)
#define M0_NUM_6 6 // M0_6 標(biāo)識(shí)
// 命令類(lèi)型
#define Get_Data 21 // 獲取數(shù)據(jù)
#define Set_Temp_Warning_val 22 // 設(shè)置溫度報(bào)警閥值
#define Set_Hum_Warning_val 23 // 設(shè)置濕度報(bào)警閥值
#define Set_Auto_Send 25 // 設(shè)置主動(dòng)上報(bào)數(shù)據(jù)
#define Set_NoAuto_Send 26 // 禁止主動(dòng)上報(bào)數(shù)據(jù)
// 設(shè)備狀態(tài)
#define FAN_ON 24 // 風(fēng)扇開(kāi)啟狀態(tài)
#define FAN_OFF 25 // 風(fēng)扇關(guān)閉狀態(tài)
#define BEEP_ON 26 // 蜂鳴器開(kāi)啟狀態(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ù)有可能第一個(gè)字節(jié)不是頭,所以需要判斷
int ID; // 節(jié)點(diǎn)標(biāo)識(shí)
char CmdType; // 命令類(lèi)型
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; // 校驗(yàn)碼
};
-------------------------------------------------------------------------------------------------------
Jni代碼
-------------------------------------------------------------------------------------------------------
在M0中提供兩種數(shù)據(jù)發(fā)送方式:
被動(dòng)獲取數(shù)據(jù):
APK通過(guò)Jni打開(kāi)串口發(fā)送 Get_Data 指令獲取數(shù)據(jù),M0收到指令就發(fā)送一次數(shù)據(jù)。
主動(dòng)獲取數(shù)據(jù):
APK通過(guò)Jni打開(kāi)串口發(fā)送 Set_Auto_Send 指令,M0收到指令就連續(xù)發(fā)送數(shù)據(jù)。
Jni的難點(diǎn)在于回調(diào)Java的方法和串口操作,如數(shù)據(jù)接收。
回調(diào)Java方法:
需要?jiǎng)?chuàng)建新的線程,去回調(diào)Java的方法,所以需要利用全局變量將Jni的 JNIEnv *,, jobject* 保存,
然后在線程里面使用。
實(shí)現(xiàn)步驟:
APK 要點(diǎn):
-------------------------------------------------------------------------------------------------------
步驟1:創(chuàng)建一個(gè)回調(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)
{
// 通過(guò)發(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();
// 添加這句是為了通過(guò)javap 命令獲取其類(lèi)型簽名 獲取完后可以刪除掉
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 可以獲取到類(lèi)型簽名 用于給Jni構(gòu)建映射表
-------------------------------------------------------------------------------------------------------
Jni 回調(diào)要點(diǎn):
-------------------------------------------------------------------------------------------------------
要點(diǎn)1:創(chuàng)建兩個(gè)全局變量。給線程備用。
JavaVM *gJavaVM = NULL;
jobject gJavaObj= NULL;
要點(diǎn)2:提供一個(gè)函數(shù),或在何時(shí)的地方保存全局變量
//注意,直接通過(guò)定義全局的JNIEnv和jobject變量,在此保存env和thiz的值是不可以在線程中使用的
//線程不允許共用env環(huán)境變量,但是JavaVM指針是整個(gè)jvm共用的,所以可通過(guò)下面的方法保存JavaVM指針,在線程中使用
env->GetJavaVM(&gJavaVM);
//同理,jobject變量也不允許在線程中共用,因此需要?jiǎng)?chuàng)建全局的jobject對(duì)象在線程中訪問(wèn)該對(duì)象
gJavaObj = env->NewGlobalRef(thiz);
要點(diǎn)3:在線程里面通過(guò)保存的全局變量去獲取Java環(huán)境變量、獲取Java層對(duì)應(yīng)類(lèi)、獲取回調(diào)函數(shù)。
//從全局的JavaVM中獲取到環(huán)境變量
gJavaVM->AttachCurrentThread(&env, NULL);
//獲取Java層對(duì)應(yīng)的類(lèi)
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);
-------------------------------------------------------------------------------------------------------
串口操作要點(diǎn):
-------------------------------------------------------------------------------------------------------
要點(diǎn)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; // 忽略校驗(yàn)錯(cuò)誤
options.c_oflag = 0; // 無(wú)輸出模式
options.c_lflag = 0; //本地模式禁用
options.c_cc[VTIME] = 10; // 表示 read() 超時(shí)值 50 表示 5秒后仍沒(méi)有數(shù)據(jù)則返回
options.c_cc[VMIN] = 1; // 表示讀到多少個(gè)字節(jié)就返回 5 表示讀完5個(gè)字節(jié)就返回
cfsetispeed(&options, B115200); // 設(shè)置波特率
cfsetospeed(&options, B115200);
tcsetattr(fd,TCSANOW,&options);
}
要點(diǎn)2:讀取數(shù)據(jù)的時(shí)候要注意,因?yàn)楣?jié)點(diǎn)發(fā)送數(shù)據(jù)的時(shí)候,控制中心的ZigBee已經(jīng)收到數(shù)據(jù),而我們還未 做好準(zhǔn)備,當(dāng)ZigBee往串口寫(xiě)數(shù)據(jù)寫(xiě)到一半的時(shí)候,我們才做好準(zhǔn)備收,這時(shí)候只能收到后半部分的數(shù)據(jù),
如果節(jié)點(diǎn)是一直發(fā)送數(shù)據(jù),那么我們?cè)谑諗?shù)據(jù)的時(shí)候就很可能變成 第一個(gè)數(shù)據(jù)包的后半部分與第二個(gè)數(shù)據(jù)的
前半部分組合成一個(gè)數(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)) // 直至接受完一個(gè)數(shù)據(jù)包
{
ret = read(fd, &rd, 1);
if((size == 0) && (rd != ZigBee_Cmd_HEAD)
continue;
if( ret == -1 || ret == 0) // 若出錯(cuò)則跳出循環(huán)
breka;
buf[size++] = rd; // 若考慮優(yōu)化性能,這里可以直接用結(jié)構(gòu)體存,
// 存之前只需要用char*p = (char*)ZData 存的時(shí)候 *p++ = rd;
}
// 將數(shù)據(jù)拷貝到結(jié)構(gòu)體
memcpy(&ZData, buf, sizeof(struct ZigBee_Cmd));
校驗(yàn)數(shù)據(jù)....
.........
LOGD("Revc Data OK``");
回調(diào)Java層的函數(shù),將數(shù)據(jù)回傳...
.........
}
|
|