梁永恩,萬世明
?
基于Android的數(shù)字溫濕度傳感器驅(qū)動開發(fā)及應(yīng)用
梁永恩,萬世明
摘 要:研究了Android的架構(gòu)及硬件驅(qū)動的實現(xiàn)原理,以數(shù)字式溫濕度傳感器DHT11為例,結(jié)合Android的硬件抽象層模式,設(shè)計了DHT11在Android平臺下的驅(qū)動實現(xiàn)方法并編寫了用戶測試程序,測試結(jié)果表明,系統(tǒng)能正確獲取傳感器的溫濕度數(shù)據(jù),驅(qū)動程序運行正常。
關(guān)鍵詞:嵌入式;DHT11;溫濕度檢測;Android;硬件抽象層
Android是Google公司推出的完全開放的移動設(shè)備軟件開發(fā)平臺[1],目前的版本是5.0。Android最早是專門用于手機(jī)開發(fā)的,由于其開源、擴(kuò)展性好、開發(fā)方便、界面美觀等優(yōu)點使其得到在手機(jī)、平板電腦、智能電視、車載設(shè)備及其它移動設(shè)備領(lǐng)域中廣泛的應(yīng)用。本文以DHT11數(shù)字溫濕度傳感器為例,介紹Andriod系統(tǒng)硬件底層驅(qū)動設(shè)計及應(yīng)用的方法。
Android由下而上分為五個層次,其結(jié)構(gòu)圖如圖1所示:
圖 1 Android 系統(tǒng)架構(gòu)
最底層是Linux內(nèi)核層,Android4.0使用Linux Kernel 3.0.1;內(nèi)核層之上是硬件抽象層(Hardware Abstractor Layer,HAL),該層是對Linux內(nèi)核程序的封裝,向上提供接口,屏蔽底層的實現(xiàn)細(xì)節(jié);硬件抽象層之上是函數(shù)庫和運行時環(huán)境,包括標(biāo)準(zhǔn)C系統(tǒng)函數(shù)庫、Open GL /ES等,通過Android應(yīng)用程序框架為開發(fā)者提供服務(wù);函數(shù)庫之上是應(yīng)用程序框架層,包括一系列的系統(tǒng)服務(wù),如視圖系統(tǒng)、資源管理器、活動管理器、通知管理器等,用戶可以使用這些API開發(fā)應(yīng)用程序;最上層是應(yīng)用層,用戶開發(fā)的Android應(yīng)用程序和Android內(nèi)核應(yīng)用程序(如Email、聯(lián)系人等)均位于這一層。
DHT11數(shù)字溫濕度傳感器是一款價格低廉的溫濕度傳感器模塊[2]。它包含一個電阻式感濕元件和一個NTC測溫元件,并與一個高性能8位單片機(jī)相連,可輸出已校準(zhǔn)的溫濕度數(shù)據(jù)。DHT11的濕度測量范圍為20%~90% RH,測量誤差為±5%RH,溫度測量范圍為0~50℃,測量誤差為± 2℃,它與微處理器之間采用單總線(1-Wire Bus)進(jìn)行通信,大大節(jié)省了IO口資源。由于DHT11具有成本低、穩(wěn)定性好、數(shù)字信號輸出、結(jié)構(gòu)簡單、擴(kuò)展方便、自校準(zhǔn)、響應(yīng)速度快等優(yōu)點,在暖通空調(diào)、除濕器、汽車、濕度調(diào)節(jié)器、醫(yī)療、自動控制等領(lǐng)域得到了廣泛的應(yīng)用[3-4]。
本系統(tǒng)采用的嵌入式微處理器型號為S5PV210。DHT11有四個引腳,VDD為電源引腳,工作電壓為3.5V-5.5V,GND為接地引腳,DATA為雙向數(shù)據(jù)引腳,連接S5PV210的通用輸入輸出口GPH0_0,硬件連接原理圖如圖2所示:
圖 2 DHT11硬件連接原理圖
DHT11 器件采用簡化的單總線通信。一次傳送40位共5字節(jié)數(shù)據(jù)溫濕度數(shù)據(jù),由高位到低位依次送出,具體數(shù)據(jù)格式是:8bit濕度整數(shù)數(shù)據(jù)+8bit濕度小數(shù)數(shù)據(jù)++8bit溫度整數(shù)數(shù)據(jù)+8bit溫度小數(shù)數(shù)據(jù)++8bit校驗和。校驗和數(shù)據(jù)為前四個字節(jié)相加。
Android的版本為Android 4.0.4,Linux開發(fā)環(huán)境為Ubuntu12.04,交叉編譯工具為arm-none-linux-gnueabi-gcc。由于底層驅(qū)動程序是由C/C++語言設(shè)計的,上層應(yīng)用程序不能直接調(diào)用,必須通過JNI(Java Native Interface)的方式進(jìn)行調(diào)用。根據(jù)Android的分層原則,應(yīng)用程序訪問底層驅(qū)動的順序為:DHT11app->DTH11Service(Java)->DTH11Serv
-ice(Native)->HAL->Linux的驅(qū)動函數(shù)。DHT11的android驅(qū)動開發(fā)流程如下:
(1)編寫DHT11的Linux驅(qū)動程序(dht11_forlinux.c)。(2)編寫HAL接口(dht11.c)。
(3)編寫JNI接口(dht11Service.cpp),包裝第二步寫的HAL接口,提供框架層的JAVA服務(wù)對HAL層C接口的調(diào)用。通過NDK(Native Development Kit)生成動態(tài)連接庫。
(4)編寫框架層的JAVA服務(wù)程序(dht11Service.java),在該服務(wù)程序中包含第3步生成的動態(tài)鏈接庫文件,調(diào)用該動態(tài)鏈接庫里的函數(shù),獲取DHT11的溫濕度數(shù)據(jù)。
(5)編寫應(yīng)用程序(dht11App.java),通過調(diào)用DTH11Service訪問動態(tài)連接庫獲取數(shù)據(jù)。
3.1 DHT11硬件Linux內(nèi)核層驅(qū)動程序
Linux把外部設(shè)備看作一個文件來管理,它在設(shè)備驅(qū)動程序在與硬件設(shè)備之間建立了標(biāo)準(zhǔn)的抽象接口,用戶通過open,close,read,write等系統(tǒng)調(diào)用對設(shè)備進(jìn)行操作[5]。Linux將設(shè)備分為字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備3類,DHT11溫濕度傳感器屬于字符設(shè)備,它是按照字符流的方式被有序訪問。驅(qū)動程序的重點是定義file_operations結(jié)構(gòu)體中的相關(guān)成員函數(shù)。file_operations定義如下:
static struct file_operations DHT11_fops={
.owner = THIS_MODULE,
.read = DHT11_read,
.open = DHT11_open,
.release = DHT11_release,};
對于DHT11來說,主要操作是讀取數(shù)據(jù),DHT11_fops中的read函數(shù)對應(yīng)DHT11_read函數(shù)。編寫DHT11_read函數(shù)時要根據(jù)DHT11的讀寫時序來編寫實現(xiàn)過程。獲取DHT11傳感器數(shù)據(jù)的讀寫時序如圖3所示:
圖3 獲取DHT11傳感器數(shù)據(jù)的讀寫時序
主機(jī)發(fā)送開始信號后,延時等待20us-40us后讀取DH11T的回應(yīng)信號,讀取總線為低電平,說明DHT11發(fā)送響應(yīng)信號,DHT11發(fā)送響應(yīng)信號后,再把總線拉高,準(zhǔn)備發(fā)送數(shù)據(jù),每一bit數(shù)據(jù)都以低電平開始, 位數(shù)據(jù)“0”的格式為50μ s的低電平和26-28μ s的高電平,位數(shù)據(jù)“1”的格式為50μ s的低電平加70μ s的高電平,可通過中斷的方式計算每bit數(shù)據(jù)的持續(xù)時間判斷該為數(shù)據(jù)是“0”還是“1”。首先定義DHT11傳感器設(shè)備的結(jié)構(gòu),如下:
struct dht11_ dev{
struct cdev cdev; //定義字符型設(shè)備
unsigned long pin; //DATA引腳,這里是GPH0_0
unsigned char value[5]; //接收5個字節(jié)數(shù)據(jù)存放在數(shù)組中
unsigned int irq;
struct timeval lasttv;
…
void (*write_bit)(unsigned long pin, char bit); //向DHT11寫入1位數(shù)據(jù)的函數(shù)
}
static ssize_t dht11_sensor_read( struct file *filp,
char _user *buf,size_t size,loff_t *f_pos) //獲取DHT11傳感器的數(shù)據(jù)
{
int result = 0;
if(dht11_reset(dht11_dev)){ //調(diào)用復(fù)位函數(shù),獲取溫濕度數(shù)據(jù),傳送上一次的溫濕度數(shù)據(jù)
do_gettimeofday(&dht11_dev->lasttv); //獲取當(dāng)前的時間值
setup_interrupts();} //設(shè)置中斷模式,在中斷服務(wù)函數(shù)中判斷接收到的位數(shù)據(jù)是”0”還是”1”
msleep(20); //等待20ms
clear_interrupts(); //清除中斷
copy_to_user(buf, (char *)& dht11_dev->value, sizeof(dht11_dev->value));//將溫濕度數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間
…}
3.2 HAL設(shè)計
1.Android系統(tǒng)通過HAL訪問linux驅(qū)動的過程
(1)本地服務(wù)(Native Service)通過調(diào)用hw_get_module函數(shù)獲取hw_module_t結(jié)構(gòu)的實例module(獲取HAL stub);
(2)通過module中hw_module_methods_t結(jié)構(gòu)的實例指針methods獲得打開具體設(shè)備的HAL的open方法;
(3)在HAL的open方法的實現(xiàn)中調(diào)用C庫的open函數(shù)打開linux設(shè)備文件(獲取文件描述符),進(jìn)而通過其他的文件操作函數(shù)實現(xiàn)對設(shè)備的控制(如DHT11的read操作);
為此需要定義hw_module_t、hw_device_t結(jié)構(gòu)和其他相關(guān)的變量、函數(shù),這里通過自定義的HAL結(jié)構(gòu),將上述結(jié)構(gòu)等封裝起來。
2.實現(xiàn)過程
(1)HAL部分
1)在dht11.h文件,完成dht11_module_t、dht11_device_t的定義
struct dht11_module_t {struct hw_module_t common;};//繼承hw_module_t結(jié)構(gòu)
struct dht11_device_t { //自定義的一個針對dht11的結(jié)構(gòu),包含hw_device_t和支持的API操作
struct hw_device_t common;
int fd; //具體的設(shè)備描述符
int (*dht11_read)(struct dht11_device_t *dev, char *val, int count);//讀取DHT11數(shù)據(jù)的函數(shù)
};
2)在dht11.c文件完成以下內(nèi)容:定義HAL_MODULE_ INFO_SYM的自定義結(jié)構(gòu)體類型的變量,將methods指定為dht11_module_methods;定義hw_module_methods_t類型變量dht11_module_methods,該變量只有一個成員變量open,該變量指定為dht11_device_open;定義了dht11_device_open函數(shù),該函數(shù)用來打開設(shè)備,對設(shè)備對應(yīng)的hw_device_t結(jié)構(gòu)體的初始化,并指定設(shè)備相關(guān)的自定義函數(shù);定義具體的獲取DHT11數(shù)據(jù)的函數(shù),在該函數(shù)中調(diào)用Linux的底層驅(qū)動。
const struct dht11_module_t HAL_MODULE_INFO_
SYM={
common:{
tag:HARDWARE_MODULE_TAG,
id: "dht11",
name: "DHT11 Stud",
methods:&dht11_module_methods,//實現(xiàn)了一個open的方法供JNI層調(diào)用
}};
static struct hw_module_methods dht11_module_methods={open:dht11_device_open};
static int dht11_device_open(const struct hw_module_t * module,cost char * name,struct hw_device_t** device){
struct dht11_device_t *dev;
dev=(struct dht11_device_t *)malloc(sizeof(*dev)); memset(dev,0,sizeof(*dev));
dev->common.tag=HARDWARE_DEVICE_TAG;
dev->read= dht11_read;//實例化支持的操作,該操作調(diào)用Linux的底層驅(qū)動
*device=*dev->common;//將實例化的dht11_device_t地址返回給JNI層, JNI層可直接調(diào)用read函數(shù)。
fd=open(“dev/dht11”,O_RDWR) //打開硬件設(shè)備
}
(2)JNI部分
硬件訪問服務(wù)是使用JAVA語言實現(xiàn)的,為了訪問硬件,必須通過使用硬件抽象層(HAL)提供的接口,而硬件抽象層模塊是使用C++語言實現(xiàn)的,所以我們必須實現(xiàn)硬件訪問服務(wù)dht11Service的JNI方法。
進(jìn)入frameworks/base/services/jni目錄,創(chuàng)建dht11Service.cpp文件,在該文件中定義JNI方法,包括以下幾個:
1)初始化函數(shù),在該函數(shù)中根據(jù)DHT11_HARDWARE_ID獲取hw_module_t,并打開設(shè)備。
static jboolean dht11_init(JNIEnv *env,jclass clazz);
2)加載JNI庫函數(shù),在該函數(shù)中完成本地方法的注冊,F(xiàn)ramework層調(diào)用。
jint JNI_OnLoad(JavaVM* vm, void* reserved);//Framework層加載JNI庫時調(diào)用。
3)獲取DHT11的5個字節(jié)的數(shù)據(jù)的函數(shù)。
static void dht11_read(JNIEnv *env,jobject thiz, jbyteArray dhtdata );
4)注冊本地方法,F(xiàn)ramework層可以使用這些方法,包括初始化函數(shù)及讀取DHT11數(shù)據(jù)的函數(shù)。
static int registerMethods(JNIEnv* env);
3.3 Framework層的DHT11服務(wù)
在此層的dht11Service.java文件中,定義一個類DHT11Service,在此類中加載JNI的動態(tài)庫,并在構(gòu)造函數(shù)中調(diào)用本地_init()方法加載DHT11的驅(qū)動。同時提供一個對外讀取DHT11數(shù)據(jù)的函數(shù)。該函數(shù)定義如下:
public void read(byte[] bytes){
Log.i(”DHT read”); return _read(dht);}
3.4 應(yīng)用程序
應(yīng)用程序負(fù)責(zé)實例化框架層的服務(wù)DHT11Service,調(diào)用該類的read()方法讀寫DHT11,部分代碼如下:public void onCreate(Bundle savedInstanceState){ byte[] result = {0, 0, 0, 0, 0};
DHT11Service ds=DHT11Service();//實例化DHT11Service
ds.read(result);//通過DHT11Service提供的方法,控制底層硬件
…..}//對數(shù)據(jù)進(jìn)行處理并顯示。
將內(nèi)核代碼驅(qū)動編譯后生成dht11.ko,然后編譯JNI及HAL源代碼,最后通過Eclipse將Android應(yīng)用程序項目生成apk文件,在S5PV210硬件平臺進(jìn)行DHT11驅(qū)動測試,將測試的溫濕度數(shù)據(jù)與標(biāo)準(zhǔn)溫濕度值進(jìn)行比較,測試分析的結(jié)果如表1所示:
表 3 測試分析的結(jié)果
將內(nèi)核代碼驅(qū)動編譯后生成dht11.ko,然后編譯JNI及HAL源代碼,最后,通過Eclipse將Android應(yīng)用程序項目生成apk文件,在S5PV210硬件平臺進(jìn)行DHT11驅(qū)動測試,將測試的溫濕度數(shù)據(jù)與標(biāo)準(zhǔn)溫濕度值進(jìn)行比較,測試分析的結(jié)果如表1所示。
本文結(jié)合DHT11數(shù)字溫濕度傳感器實現(xiàn)了基于Android HAL 架構(gòu)的溫濕度傳感器的驅(qū)動程序。實驗結(jié)果表明,該驅(qū)動程序運行正常,滿足系統(tǒng)設(shè)計的要求。
參考文獻(xiàn)
[1] 胡偉.Android系統(tǒng)架構(gòu)及其驅(qū)動研究[J].廣州廣播電視大學(xué)學(xué)報.2010,41(04):96-101.
[2] 周云輝,王嬌,錢云飛. 基于嵌入式的環(huán)境溫濕度監(jiān)測系統(tǒng)設(shè)計[J].電子測量技術(shù),2012,35(9):80-82.
[3] 廣州奧松電子有限公司. 數(shù)字溫濕度傳感器DHT11[EB/OL]. http://www.aosong.com, 2015-2-12.
[4] 王志宏,白翠珍. 基于DHT1 的實驗室多點溫濕度報警系統(tǒng)的設(shè)計[ J ]. 山西電子技術(shù),2011(4):45-46.
[5] 姜遠(yuǎn)志.Linux的驅(qū)動開發(fā)分析[J].無線互聯(lián)科技.2014,(01):103.
Driver and Application of Digital Temperature and Humidity Sensor Based on Android
Liang Yongen,Wan Shiming
(The Electrical and Information Engineering School of Guangdong Baiyun University, Guangzhou 510450, China)
Abstract:This thesis illustrated the hardware architecture and driver principle of Android. The design of DHT11 driver in Android OS and the user application was given by using the hardware abstraction layer. The results show that the driver run normally and the application could obtain correct data of temperature and humidity from the sensor.
Key words:Embedded; DHT11; Temperature and Humidity Measurement; Android; Hardware Abstraction Layer
收稿日期:(2015.06.30)
作者簡介:梁永恩(1978-),男(漢族),廣州人,廣東白云學(xué)校,電氣與信息工程學(xué)院,講師,碩士,研究方向:嵌入式系統(tǒng),廣州,510450萬世明(1955-),男(漢族),武漢人,廣東白云學(xué)校,電氣與信息工程學(xué)院,教授,碩士,研究方向:系統(tǒng)工程,廣州,510450
文章編號:1007-757X(2016)02-0074-04
中圖分類號:TP274
文獻(xiàn)標(biāo)志碼:A