初入android驱动之字符设备(三)

回想当初在大学玩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>

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.编写应用程序。运行应用程序,可以看出,按下按键灯亮,松开灯灭。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-28 12:58:13

初入android驱动之字符设备(三)的相关文章

初入android驱动开发之字符设备(五-定时器)

这个字符设备系列,主要借助较容易上手的字符设备实例,去讲解linux的一些机制,以及驱动中比较常用到的中断.定时器.信号量等一些知识,由于本人自身的知识有限,对于arm的架构体系不太了解,这里,一般这里只讲,如何去用,对于一些原理性的东西不会深究,以后的文章会慢慢的加深. 想想我们当初玩51单片机的时候,那时候按键防抖是一个硬件.软件都需要处理的地方.软件一般就是加延时检测判断.当然,这里我们也可以用到定时器的这个机制,做按键驱动,这里主要还是以按键为例,但不是讲的按键防抖. 1. 定时器的一些

初入android驱动开发之字符设备(四-中断)

上一篇讲到android驱动开发中,应用是怎样去操作底层硬件的整个流程,实现了按键控制led的亮灭.当然,这是一个非常easy的实例,只是略微演变一下,就能够得到广泛的应用. 如开发扫描头,应用透过监听上报的按键的键值,监听到,则调用扫描头的模块.成功,则点亮LED灯,并把扫描头解码后的条码信息.通过广播的形式发出.又扯到其他地方,这里主要说说中断. 1. 中断的一些概念 中断,是什么? 中断.能够看成是cpu对特殊事件的一种处理的机制,这类特殊事件一般指紧急事件或者说异常事件.非常easy的一

【转】深入浅出:Linux设备驱动之字符设备驱动

深入浅出:Linux设备驱动之字符设备驱动 一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的字符设备有鼠标.键盘.串口.控制台和LED设备等. 块设备:是指可以从设备的任意位置读取一定长度数据的设备.块设备包括硬盘.磁盘.U盘和SD卡等. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.linux用户程序通过设备文件(或称

《驱动学习 - 字符设备驱动》

1.1字符设备驱动基础 字符设备驱动:设备对数据的处理是按照字节流的形式进行的. 在linux中,“一切皆文件”(除了网络设备),这表示设备最终都会体现为一个文件.设备文件通常位于/dev目录下. 内核通常用主设备号区别一类设备,次设备号用于区分同一类设备的不同个人或不同分区. 手动创建设备文件 mknod /dev/vser0 c 256 0 mknod是make node的缩写.用于创建一个节点(设备文件也叫设备节点).在linux系统中,一个节点代表一个文件. 原文地址:https://w

linux驱动之字符设备

第一部分:字符设备工作过程1.系统调用和驱动程序的关联关键结构体:struct file_operation:file_operation结构体的每一个成员的名字都对应着一个系统调用.用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数.这是linux的设备驱动程序工作的基本原理.编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域.

深入浅出~Linux设备驱动之字符设备驱动

一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的字符设备有鼠标.键盘.串口.控制台和LED设备等. 块设备:是指可以从设备的任意位置读取一定长度数据的设备.块设备包括硬盘.磁盘.U盘和SD卡等. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备和块设备

【驱动】——字符设备驱动程序

字符设备不得不说的那些事: 一: 设备号:主设备号,次设备号: 数据类型 dev_t(unsigned int) 定义设备号  高12位主设备号 低20位次设备号: 二: 设备号的作用: 应用程序通过主设备号找到驱动程序: 三:如何分配设备号: ①:静态分配: 1: cat /proc/devices 查看linux系统哪个设备号没有被占用: 2: dev_t dev_id = MKDEV(主设备号,次设备号)  根据你的设备个数分配次设备号 如果设备个数只有一个,一般此设备号从0开始: 3: 

Linux设备驱动之字符设备驱动

一.linux系统将设备分为3类:字符设备.块设备.网络设备. 应用程序调用的流程框图: 三种设备的定义分别如下, 字符设备:只能一个字节一个字节的读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后顺序进行.字符设备是面向流的设备,常见的字符设备如鼠标.键盘.串口.控制台.LED等. 块设备:是指可以从设备的任意位置读取一定长度的数据设备.块设备如硬盘.磁盘.U盘和SD卡等存储设备. 网络设备:网络设备比较特殊,不在是对文件进行操作,而是由专门的网络接口来实现.应用程序不能直接访

字符设备驱动体验,字符设备驱动学习

字符设备驱动学习 在Linux系统中,驱动程序通常采用内核模块的程序结构来进行编码.因此,编译/安装一个驱动程序,其实质就是编译/安装一个内核模块. 一.编译安装字符设备驱动程序 memdev文件中:在这个文件里和真实的硬件无关,只是虚构了一个数组 1 #include <linux/module.h> 2 #include <linux/fs.h> 3 #include <linux/init.h> 4 #include <linux/cdev.h> 5