回想当初在大学玩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_led error: %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); else write(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_button error: %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. */ //notice static 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 __cplusplus extern "C" { #endif jint 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 := eng LOCAL_SRC_FILES:= button_test.cpp LOCAL_SHARED_LIBRARIES := libutils libhardware libdl LOCAL_C_INCLUDES += $(JNI_H_INCLUDE) LOCAL_MODULE:= libButtonTest LOCAL_PRELINK_MODULE := false include $(BUILD_SHARED_LIBRARY)</span>
先回想一下之前测试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.编写应用程序。运行应用程序,可以看出,按下按键灯亮,松开灯灭。
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/yanleizhouqing/article/details/47416597