android中如何点亮LED灯

android中如何点亮LED灯
1. 框架层面看如何点亮LED灯
在android中如何点亮一个灯?
本文就android点灯的驱动开发过程做一个介绍,文中介绍的例子已经在4412的开发板上实验过,依照本文的介绍,完全可以在任何android版本上实现,要理解需要理解android源码结构,如何点亮一个LED灯,开门见山,直接上图:

上图展示了android源码架构,并说明在android点亮一个灯需要实现步骤,android上开发其他的步骤大体类同:
开发android APP
开发HAL层的驱动
开发linux kernel层的驱动
2. android中开发步骤
这里需要补充一点,由于android上开发应用是java语言开发的,而HAL层开发驱动是
采用C/C++开发的,所以这里需要再引入一层JNI层,将上面的步骤再次完善:
1. 开发android APP
2. 实现AIDL
3. 实现JNI层的代码,使APP能够访问HAL层驱动
4. 开发HAL层的驱动
5. 开发linux kernel层的驱动
看了上面的开发步骤对android开发有了了解,下面看点具体的,看app是如何一步步实现调用的,继续上图:

  1. Android代码调用过程
    此程序是android一个非常简单的应用,麻雀虽小五脏俱全,包括了应用层,JNI,HAL层,kernel,试图用最简单的程序说明应用程序调用过程。应用使用iLedService.ledCtrl(0, 1);来点亮第0盏灯,经过JNI层的中转,再经过HAL层函数的中转,最后调用内核层的 gpio_set_value(led_gpios[arg], !cmd)来实现GPIO的高低电平设置,导致LED被点亮和点灭,下图是截取每层的代码进行调用的过程分析。

  2. 各层代码分析
    这个是中为给的android 5.1.1给的源码架构

|– Makefile
|– abi (applicationbinary interface,应用程序二进制接口,生成libgabi++.so相关库文件)
|– art (google在4.4后加入用来代替Dalvik的运行时)
|– bionic (Android的C library,即C库文件)
|– bootable (启动引导相关代码)
|– build (存放系统编译规则及generic等基础开发配置包)
|– cts (Android兼容性测试套件标准)
|– dalvik (dalvik JAVA虚拟机)
|– developers (开发者用,存放几个例子)
|– development (开发者需要的一些例程及工具)
|– device (设备相关代码,这是各厂商需要配置和修改的代码)
|– docs (介绍开源相关文档)
|– external (android使用的一些开源的模组)
|– frameworks (核心框架——java及C++语言)
|– hardware (部分厂家开源的硬解适配层HAL代码)
|– kernel (驱动内核相关代码)
|– libcore (核心库相关)
|– libnativehelper (JNI用到的库)
|– ndk (ndk相关)
|– out (编译完成后的代码输出目录)
|– packages (应用程序包)
|– pdk (google用来减少碎片化的东西)
|– prebuilt (x86和arm架构下预编译的一些资源)
|– sdk (sdk及模拟器)
|– tools (工具)
|– system (底层文件系统库、应用及组件——C语言)
|– vendor (厂商定制代码)

我们经常需要修改的是:
|– frameworks (核心框架——java及C++语言)
|– hardware (部分厂家开源的硬解适配层HAL代码)
|– kernel (驱动内核相关代码)
这些目录底下的文件,这里强调这些源码文件架构是为了将自己开发的文件放入android源码对应的目录。

4.1应用层代码解析
public void onCheckboxClicked(View view) {
// Is the view now checked?
boolean checked = ((CheckBox) view).isChecked();

    try {
        // Check which checkbox was clicked
        switch(view.getId()) {
            case R.id.LED1:
                if (checked) {
                    // Put some meat on the sandwich
                    Toast.makeText(getApplicationContext(), "LED1 on", Toast.LENGTH_SHORT).show();
                    iLedService.ledCtrl(0, 1);
                }
                else {
                    // Remove the meat
                    Toast.makeText(getApplicationContext(), "LED1 off", Toast.LENGTH_SHORT).show();
                    iLedService.ledCtrl(0, 0);
                }
                break;
            case R.id.LED2:
                if (checked) {
                    // Put some meat on the sandwich
                    Toast.makeText(getApplicationContext(), "LED2 on", Toast.LENGTH_SHORT).show();
                    iLedService.ledCtrl(1, 1);
                }
                else {
                    // Remove the meat
                    Toast.makeText(getApplicationContext(), "LED2 off", Toast.LENGTH_SHORT).show();
                    iLedService.ledCtrl(1, 0);
                }
                break;

            case R.id.LED3:
                if (checked) {
                    // Put some meat on the sandwich
                    Toast.makeText(getApplicationContext(), "LED3 on", Toast.LENGTH_SHORT).show();
                    iLedService.ledCtrl(2, 1);
                }
                else {
                    // Remove the meat
                    Toast.makeText(getApplicationContext(), "LED3 off", Toast.LENGTH_SHORT).show();
                    iLedService.ledCtrl(2, 0);
                }
                break;

            case R.id.LED4:
                if (checked) {
                    // Put some meat on the sandwich
                    Toast.makeText(getApplicationContext(), "LED4 on", Toast.LENGTH_SHORT).show();
                    iLedService.ledCtrl(3, 1);
                }
                else {
                    // Remove the meat
                    Toast.makeText(getApplicationContext(), "LED4 off", Toast.LENGTH_SHORT).show();
                    iLedService.ledCtrl(3, 0);
                }
                break;
            // TODO: Veggie sandwich
        }
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

    button = (Button) findViewById(R.id.BUTTON);

    iLedService = ILedService.Stub.asInterface(ServiceManager.getService("led"));

    checkBoxLed1 = (CheckBox) findViewById(R.id.LED1);
    checkBoxLed2 = (CheckBox) findViewById(R.id.LED2);
    checkBoxLed3 = (CheckBox) findViewById(R.id.LED3);
    checkBoxLed4 = (CheckBox) findViewById(R.id.LED4);

    button.setOnClickListener(new MyButtonListener());

}

应用层的代码很简单,就是在android界面上实现按下按键,然后提示相应的灯被点亮。
iLedService = ILedService.Stub.asInterface(ServiceManager.getService(“led”));是获取接口,调用后就可以使用iLedService.ledCtrl(1, 0);函数进行点灯。

4.2 AIDL
interface ILedService
{
int ledCtrl(int which, int status);
}
使用AIDL语言写一个供应用程序调用的接口。
4.3 JNI层代码解析
namespace android
{
static led_device_t* led_device;
jint ledOpen(JNIEnv *env, jobject cls)
{
jint err;
hw_module_t* module;
hw_device_t* device;
ALOGI(“native ledOpen …”);
/* 1. hw_get_module */
err = hw_get_module(“led”, (hw_module_t const**)&module);
if (err == 0) {
/* 2. get device : module->methods->open */
err = module->methods->open(module, NULL, &device);
if (err == 0) {
/* 3. call led_open */
led_device = (led_device_t *)device;
return led_device->led_open(led_device);
} else {
return -1;
}
}
return -1;
}

void ledClose(JNIEnv *env, jobject cls)
{
//ALOGI(“native ledClose …”);
//close(fd);
}

jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
ALOGI(“native ledCtrl %d, %d”, which, status);
return led_device->led_ctrl(led_device, which, status);
}

static const JNINativeMethod methods[] = {
{“native_ledOpen”, “()I”, (void *)ledOpen},
{“native_ledClose”, “()V”, (void *)ledClose},
{“native_ledCtrl”, “(II)I”, (void *)ledCtrl},
};

int register_android_server_LedService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, “com/android/server/LedService”,
methods, NELEM(methods));
}
}
解析:JNI 怎么使用 HAL
a. hw_get_module 获得一个hw_module_t结构体
b. 调用 module->methods->open(module, device_name, &device)
获得一个hw_device_t结构体
并且把hw_device_t结构体转换为设备自定义的结构体
4.4 HAL层代码解析
static int fd;
/* Close this device /
static int led_close(struct hw_device_t* device)
{
close(fd);
return 0;
}

static int led_open(struct led_device_t* dev)
{
fd = open(“/dev/leds”, O_RDWR);
ALOGI(“led_open : %d”, fd);
if (fd >= 0)
return 0;
else
return -1;
}

static int led_ctrl(struct led_device_t* dev, int which, int status)
{
int ret = ioctl(fd, status, which);
ALOGI(“led_ctrl : %d, %d, %d”, which, status, ret);
return ret;
}

static struct led_device_t led_dev = {
.common = {
.tag = HARDWARE_DEVICE_TAG,
.close = led_close,
},
.led_open = led_open,
.led_ctrl = led_ctrl,
};

static int led_device_open(const struct hw_module_t* module, const char* id,
struct hw_device_t** device)
{
*device = &led_dev;
return 0;
}

static struct hw_module_methods_t led_module_methods = {
.open = led_device_open,
};

struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.id = “led”,
.methods = &led_module_methods,
};
解析:HAL框架提供了三个结构体,分别为hw_device_t ,hw_module_t ,hw_module_methods_t,这三个结构体很重要。
编写HAL层驱动首先要依据这三个结构体作扩展,我们创建自己驱动的device_t,module_t代码,并且写led_module_methods这个结构体中方法的实现代码。*device = &led_dev;并没有做什么事情,只是实现了led_device_t转换成hw_device_t。
Android的HAL是为了保护一些硬件提供商的知识产权而提出的,是为了避开linux的GPL束缚。思路是把控制硬件的动作都放到了 Android HAL中,而linux driver仅仅完成一些简单的数据交互作用,甚至把硬件寄存器空间直接映射到user space。

4.5 Linux内核层代码解析
static int led_gpios[] = {
EXYNOS4212_GPM4(0),
EXYNOS4212_GPM4(1),
EXYNOS4212_GPM4(2),
EXYNOS4212_GPM4(3),
};
static int led_open(struct inode *inode, struct file *file)
{
int i;
for (i = 0; i < 4; i++)
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
return 0;
}

/* app : ioctl(fd, cmd, arg) */
static long led_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
/* cmd : 0-off, 1-on */
/* arg : 0-3, which led */
if ((cmd != 0) && (cmd != 1))
return -EINVAL;
if (arg > 4)
return -EINVAL;
gpio_set_value(led_gpios[arg], !cmd);
return 0;
}

static struct file_operations leds_ops = {
.owner = THIS_MODULE,
.open = led_open,
.unlocked_ioctl = led_ioctl,
};

static int major;
static struct class *cls;
int leds_init(void)
{
major = register_chrdev(0, “leds”, &leds_ops);

cls = class_create(THIS_MODULE, "leds");
device_create(cls, NULL, MKDEV(major, 0), NULL, "leds"); /* /dev/leds */
return 0;

}

void leds_exit(void)
{
device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, “leds”);
}

module_init(leds_init);
module_exit(leds_exit);
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“WHY”);

解析:三星SDK开发包中定义了M组的第1个到第4个管脚使用宏EXYNOS4212_GPM4,EXYNOS4212_GPM4(0)表示第一个管脚。在static int led_open(struct inode *inode, struct file *file)函数实现配置GPIO为输出引脚;在tatic long led_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)中根据传入的参数设置GPIO;int leds_init(void)中为了让系统udev,mdev给我们创建设备节点,创建类, 在类下创建设备 : /sys,device_create(cls, NULL, MKDEV(major, 0), NULL, “leds”);是在/dev/leds创建设备节点,这样应用程序可以使用open(” /dev/leds”,..)来访问驱动。

  1. 代码开发方法与编译
    5.1 应用层开发与编译
    应用层使用Android studio开发程序,将附件给的源码,使用Android studio编译并push到具体开发板。
    5.2 AIDL文件
    本文新建一个IledService.aidl,然后使用mmm命令产生一个ILedService.java文件给应用层使用。
  2. 把 ILedService.aidl 放入 frameworks/base/core/java/android/os
  3. 修改 frameworks/base/Android.mk 添加一行
    core/java/android/os/ILedService.aidl \
  4. mmm frameworks/base
  5. 它会生成:
    ./out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/ILedService.java
    5.3 JNI层文件
    1.建立com_android_service_ledservice.cpp,放入JNI对应的目录。
    frameworks/base/services/core/jni/com_android_server_LedService.cpp
    5.4 HAL层文件
    1.建立hal_led.c hal_led.h放入HAL对应的目录。
    hardware/libhardware/include/hardware/led_hal.h
    hardware/libhardware/modules/led/led_hal.c
    2.修改hardware/libhardware/modules/led/Android.mk
    Android.mk内容如下:
    LOCAL_PATH := (callmydir)include ( c a l l m y − d i r ) i n c l u d e (CLEAR_VARS)
    LOCAL_MODULE := led.default
    LOCAL_MODULE_RELATIVE_PATH := hw
    LOCAL_C_INCLUDES := hardware/libhardware
    LOCAL_SRC_FILES := led_hal.c
    LOCAL_SHARED_LIBRARIES := liblog
    LOCAL_MODULE_TAGS := eng
    include $(BUILD_SHARED_LIBRARY)
    5.5 Linux kernel层文件
    1.建立leds_4412.c,放入linux内核驱动对应的源码目录下,对应内核 drivers/char
    2.修改 drivers/char/Makefile,添加:
    obj-y += leds_4412.o
    3.重新编译内核

5.6 其他文件
1修改android源码SystemServer.java增加LedService
不需要修改 frameworks/base/services/core/Android.mk
它的内容里已经把该目录下所有JAVA文件自动包含进去了:
LOCAL_SRC_FILES += \
(callalljavafilesunder,java)2.androidonload.cppLedService3.:frameworks/base/services/core/jni/onload.cppframeworks/base/services/core/jni/comandroidserverLedService.cppframeworks/base/services/core/jni/Android.mk: ( c a l l a l l − j a v a − f i l e s − u n d e r , j a v a ) 2. 修 改 a n d r o i d 源 码 o n l o a d . c p p 增 加 L e d S e r v i c e 3. 把 新 文 件 上 传 目 录 : f r a m e w o r k s / b a s e / s e r v i c e s / c o r e / j n i / o n l o a d . c p p f r a m e w o r k s / b a s e / s e r v i c e s / c o r e / j n i / c o m a n d r o i d s e r v e r L e d S e r v i c e . c p p 修 改 f r a m e w o r k s / b a s e / s e r v i c e s / c o r e / j n i / A n d r o i d . m k : (LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_LedService.cpp \

5.7 编译
mmmframeworks/base/services m m m f r a m e w o r k s / b a s e / s e r v i c e s mmm hardware/libhardware/modules/led
makesnod m a k e s n o d ./gen-img.sh

你可能感兴趣的