初入android驅動之字元裝置(三)

來源:互聯網
上載者:User

標籤:字元裝置 android驅動開發   jni   

回想當初在大學玩51單片機的時候,實驗室的老師第一個任務,就是設計一個基於51單片機的LED流水燈設計,並實現幾種樣式。第二個任務,就是設計一個基於51單片機的按鍵控制LED流水燈樣式的設計。需要自己設計硬體圖、畫protel電路圖,並設計出PCB,實現keil和proteus的聯調,然後焊接電路板,實現其功能。那時候什麼都不懂,秉這一股衝勁,各種百度、看書,那時候郭天祥的51單片機視頻超火,所以那時候基本以他的書和視頻學得,牛人,膜拜。

     所以,這主要講關於按鍵最簡單的字元驅動,通過設定串連該引腳為輸入,並讀取其資料寄存器,來判斷按鍵按下,然後在整體的講述android的應用如何調用led和按鍵的字元裝置,並實現簡單的功能測試(按鍵按下,led亮,按鍵鬆開,led滅)。下一節,也是最後一節,主要講字元裝置中一些關於定時器、中斷、競爭一些機制。廢話有點多,進入主題:

1. 按鍵最簡單的驅動程式:

<span style="font-size:14px;">#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/input.h>#include <linux/platform_device.h>#include <linux/miscdevice.h>#include <mach/gpio.h>#include <linux/io.h>#include <mach/hardware.h>#include <linux/delay.h>#include <asm/irq.h>#include <asm/uaccess.h>static struct class *buttondrv_class;static struct device *buttondrv_class_dev;int major;volatile unsigned long *GPCCON;volatile unsigned long *GPCDAT;static int button_drv_open(struct inode *inode, struct file *file){printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_open\n");*GPCCON &= ~((0xf<<(2*4)) | (0xf<<(3*4)));        *GPCCON |= (0<<(2*4) | (0<<(3*4)));return 0;}static int button_drv_read(struct file *filp, char __user *buf,                                          size_t count, loff_t *offp){printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_read\n");unsigned char k_val[2];int regval;regval = *GPCDAT;k_val[0] = (regval & (1<<2)) ? 1 : 0;k_val[1] = (regval & (1<<3)) ? 1 : 0;printk("keyvalue[0]=%02x keyvalue[1]=%02x \n",k_val[0],k_val[1]);copy_to_user(buf, k_val, sizeof(k_val));return sizeof(k_val);}static struct file_operations button_drv_fops = {    .owner  =   THIS_MODULE,       .open   =   button_drv_open,           .read=button_drv_read,};static int button_drv_init(void){printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_init\n");    GPCCON = (volatile unsigned long *)ioremap(0xE0200080, 8);GPCDAT= GPCCON + 1;if (!GPCCON) {return -EIO;}major = register_chrdev(0, "button_drv", &button_drv_fops); buttondrv_class = class_create(THIS_MODULE, "buttondrv");buttondrv_class_dev = device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); return 0;}static void button_drv_exit(void){printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_exit\n");unregister_chrdev(major, "button_drv"); device_unregister(buttondrv_class_dev);class_destroy(buttondrv_class);iounmap(GPCCON);}module_init(button_drv_init);module_exit(button_drv_exit);MODULE_LICENSE("GPL");</span><span style="font-size:24px;"></span>
關於demo的簡單說明

按鍵這裡有兩個,一個接GPC12,GPC13,想想最早操作51單片機的時候,檢測按鍵最簡單的做好就是,把該引腳設為輸入,然後讀取該引腳的狀態。這裡S5PV210的字元裝置,這裡也先試試。

首先,使用者或者上層APP,對於按鍵的功能需求,無非就是通過按下按鍵,讀取按鍵的不同狀態,從而去做某些事情。所以,按鍵,就是上報某種事件或者說是資訊。當然,不同的按鍵類型,各自的功能也會有差別。這裡所說的是最普通的按鍵。

1. ioremap:根據引腳映射其物理地址 2. 設定為輸入:*GPCCON &= ~((0xf<<(2*4)) | (0xf<<(3*4)));  *GPCCON |= (0<<(2*4) | (0<<(3*4)));

   3.在read函數中,讀取該引腳DAT的值。regval = *GPCDAT;
k_val[0] = (regval & (1<<2)) ? 1 : 0;
k_val[1] = (regval & (1<<3)) ? 1 : 0;

2. jni共用庫的編寫

2.1 源碼

Button_test.cpp

<span style="font-size:14px;">#define LOG_TAG "RfidSerialPort"#include "utils/Log.h"#include     <stdio.h>      #include     <stdlib.h>     #include     <unistd.h>     #include     <sys/types.h>  #include     <sys/stat.h>   #include     <fcntl.h>      #include     <termios.h>    #include     <errno.h>      #include     <string.h>#include  <jni.h>#include "button_test.h"#include <ctype.h>static int fd_led=-1;static int fd_button=-1;jint led_open(JNIEnv *env, jobject thiz){LOGE("led_open >>>>>>>>>>>>>>>>>>>>>>>>>>");fd_led = open("/dev/led", O_RDWR);if (fd_led < 0){ LOGE("open fd_lederror: %s\n", strerror(errno));  return -1;}return 0;}jint led_close(JNIEnv *env, jobject thiz){close(fd_led);return 0;}jint led_set(JNIEnv *env, jobject thiz, jint state){if(state==1)write(fd_led,"1",1);elsewrite(fd_led,"0",1);return 0;}jint button_open(JNIEnv *env, jobject thiz){LOGE("button_open >>>>>>>>>>>>>>>>>>>>>>>>>>");fd_button = open("/dev/button", O_RDWR);if (fd_button < 0){ LOGE("open fd_buttonerror: %s\n", strerror(errno));  return -1;}return 0;}jint button_close(JNIEnv *env, jobject thiz){close(fd_button);return 0;}jint button_get(JNIEnv *env, jobject thiz, jbyteArray state){LOGE("button_get >>>>>>>>>>>>>>>>>>>>>>>>>>");jbyte *jstate = env->GetByteArrayElements(state, NULL);int reply=-1;char value[2];memset(value,0,2);reply=read(fd_button,value,2);LOGE("value[0]=%02x value[1]=%02x ",value[0],value[1]);if(reply<0)return -1;memcpy(jstate,value,2);env->ReleaseByteArrayElements( state,  jstate, 0);return 0;}static JNINativeMethod gMethods[] = {{"led_open", "()I" , (void*)led_open},{"led_close", "()I" , (void*)led_close},{"led_set", "(I)I" , (void*)led_set},{"button_open", "()I" , (void*)button_open},{"button_close", "()I" , (void*)button_close},{"button_get", "([B)I" , (void*)button_get},}; /* * Register several native methods for one class. */ //noticestatic const char *classPathName = "com/example/button_test/ButtonTest";static int registerNatives(JNIEnv* env){    jclass clazz;    clazz = env->FindClass(classPathName);    if (clazz == NULL) {        LOGE("Native registration unable to find class '%s'", classPathName);        return JNI_FALSE;    }else{    LOGI("find class sucess");    }    if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods)/sizeof(gMethods[0])) < 0) {        LOGE("RegisterNatives failed for '%s'", classPathName);        return JNI_FALSE;    }    return JNI_TRUE;}/* * **This is called by the VM when the shared library is first loaded. * */jint JNI_OnLoad(JavaVM* vm, void* reserved){    jint result = -1;    JNIEnv* env = NULL;    LOGI("JNI_OnLoad");    if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {        LOGE("ERROR: GetEnv failed");        goto bail;    }    if (registerNatives(env) != JNI_TRUE) {        LOGE("ERROR: registerNatives failed");        goto bail;    }    result = JNI_VERSION_1_4;bail:    return result;}</span><span style="font-size:18px;"> </span>
Button_test.h

<span style="font-size:14px;">#include <jni.h>#ifndef _Included_button_test#define _Included_button_test#ifdef __cplusplusextern "C" {#endifjint led_open(JNIEnv *env, jobject thiz);jint led_close(JNIEnv *env, jobject thiz);jint led_set(JNIEnv *env, jobject thiz,int stat);jint button_open(JNIEnv *env, jobject thiz);jint button_close(JNIEnv *env, jobject thiz);jint button_get(JNIEnv *env, jobject thiz,jbyteArray state);#ifdef __cplusplus}#endif#endif</span><span style="font-size:18px;"></span>

Android.mk

<span style="font-size:14px;">LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS := engLOCAL_SRC_FILES:= button_test.cppLOCAL_SHARED_LIBRARIES := libutils libhardware libdl   LOCAL_C_INCLUDES +=         $(JNI_H_INCLUDE)   LOCAL_MODULE:= libButtonTestLOCAL_PRELINK_MODULE := falseinclude $(BUILD_SHARED_LIBRARY)</span>


2.2 源碼簡單的分析

先回想一下之前測試led的程式,可知道,led主要調用三個方法,open、write、close,同button也為三個,open、read、close。

現在知道native需要給上層app提供6個方法,那怎麼去實現了?

這裡需要知道jni(java native interface),想詳細瞭解其機制,可看《深入理解android卷》第二章(若需要一些資料,可以留言)。這裡主要是如何去做,具體的原理性東西,需要你事後去學習。簡單來說,jni就是在native(c、c++)和java之間的一座橋樑。同時,用時一定要注意代碼的嚴謹性,容易出錯。jni有靜態註冊和動態註冊的兩種方式,這裡用的是動態註冊。

button_test.cpp

首先從後面看起:

JNI_OnLoad:動態註冊才用到,在java 調用system.loadlibrary方法時,就會調用JNI_OnLoad,在這個函數裡面,主要調用registerNatives方法。

registerNatives:1. clazz = env->FindClass(classPathName) 這個是關聯native和java的class,很主要,路徑錯誤的話,在java載入so之後,會出現找不到native方法的錯誤。

2. env->RegisterNatives(clazz, gMethods, sizeof(gMethods)/sizeof(gMethods[0])) < 0 這個主要是註冊函數的關聯關係,可以串連為建立了一直從native方法到java class方法的一直雙向映射。

JNINativeMethod:這個就是方法映射表,這裡得注意參數和返回值的簽名資訊。看到這,基本的架構已經出來了。

button_test.h

#ifdef __cplusplus
extern "C" {
#endif


#ifdef __cplusplus
}
#endif

這裡因為是c++的代碼,所以考慮到與C的相容問題,故加上這一部分。

Android.mk

上一部分已經介紹過了,這裡 include $(BUILD_SHARED_LIBRARY),就是把模組編譯成動態共用程式庫。

那麼,你在android環境下或者ndk下編譯,會產生libButtonTest.so.


3 APP的編寫

這裡主要有兩部分,一部分匯入native函數,一部分調用native方法。

ButtonTest.java

package com.example.button_test;import android.content.Context;import android.util.Log;public class ButtonTest {static{ System.loadLibrary("ButtonTest");}public native int led_open();public native int led_close();public native int led_set(int state);public native int button_open();public native int button_close();public native int button_get(byte state[]);}
這裡System.loadLibrary("ButtonTest"),不需要寫成libButtonTest.so。下面那些就是映射表裡面的native函數,注意參數、返回值的一致。

package com.example.button_test; 與jni中的static const char *classPathName = "com/example/button_test/ButtonTest" 一致。

MainActivity.java

package com.example.button_test;import android.support.v7.app.ActionBarActivity;import android.os.Bundle;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import com.example.button_test.ButtonTest;public class MainActivity extends ActionBarActivity {ButtonTest Test=new ButtonTest(); // notice byte state[]={0,0};int flag=1;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Test.led_open();        Test.button_open();                for(;;)        {        Test.button_get(state);        if(state[1] == 1 || state[0] == 1)        {        Test.led_set(1);        flag++;            if(flag>20)            break;        }        else         {        Test.led_set(0);        flag++;        if(flag>20)        break;        }        try {        Thread.sleep(500);        } catch (InterruptedException e) {        e.printStackTrace();        }        }        Test.button_close();        Test.led_close();    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.main, menu);        return true;    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        // Handle action bar item clicks here. The action bar will        // automatically handle clicks on the Home/Up button, so long        // as you specify a parent activity in AndroidManifest.xml.        int id = item.getItemId();        if (id == R.id.action_settings) {            return true;        }        return super.onOptionsItemSelected(item);    }    }
這裡需要對eclipse的瞭解,如何建立一個android的項目。

1.import com.example.button_test.ButtonTest; 這裡匯入ButtonTest到本地

2.ButtonTest Test=new ButtonTest(); 定義一個ButtonTest的class。

3. 在函數中調用native方法即可。


備忘:這整個過程中,不可能一次性寫好,這裡主要用到列印的調試手段,對於一些比較難解決的問題,可能就需要藉助一些工具。整個流程:

1.把兩個驅動編號,把ko push到android裝置中,insmod載入兩個驅動,並更改其裝置節點的許可權,chmod 777 /dev/led && chmod 777 dev/button ,不然jni中的open方法,會開啟失敗。

2.編寫jni庫。把產生的so庫push到system/lib下,當然,你也可以放在你eclipse的項目libs/armeabi/下,一般建議放在工程項目下。

3.編寫應用程式。運行應用程式,可以看出,按下按鍵燈亮,鬆開燈滅。




著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

初入android驅動之字元裝置(三)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.