android gpio led 驱动与hal分析,Android GPIO LED 驱动与HAL分析(转)

转自: http://blog.csdn.net/lionfire/article/details/6725379

这个太强了,自己要控制gpio,搜到这篇文章,害怕忘记,赶紧转了过来,完全转载

前言:

以一个GPIO控制的GPIO LED为例,描述Android系统中,如何完成一个最简单的从软件控制硬件的示例:

l  如何完成一个最简单的驱动程序控制某个GPIO引脚

l  如何在Android系统中建立这个驱动程序对应的HAL

l  如何使上层应用程序通过HAL来控制驱动程序

1           总体结构

modkoid工程提供了一个LedTest示例程序,是台湾的Jollen用于培训的。

原始工程下载方法:

本文所使用的代码基于硬件(s5pc100开发板)做了部分修改。

0818b9ca8b590ca3270a3433284dd417.png

2           HAL在Android系统中的位置

0818b9ca8b590ca3270a3433284dd417.png

3           驱动程序

本部分共两个文件。一个是led_drv.ko,这是驱动程序;另一个是:main,这个实际上main.c生成的测试程序。它可以通过ioctl来控制驱动程序,测试驱动程序是否达到目标。

3.1          驱动程序初始化和退出

static int simple_major = 250;//默认的设备号码,如果为0则尝试自动分配

……

/*

* Set up the cdev structure for a device.

*/

static void simple_setup_cdev(struct cdev *dev, int minor,

struct file_operations *fops)//自编的函数,注册字符设备

{

int err, devno = MKDEV(simple_major, minor);//建立设备号

cdev_init(dev, fops);//初始化设备结构体struct cdev *dev

dev->owner = THIS_MODULE;

dev->ops = fops;//关联fops

err = cdev_add (dev, devno, 1);//注册一个字符设备

/* Fail gracefully if need be */

if (err)//注册失败处理

printk (KERN_NOTICE "Error %d adding simple%d", err, minor);

}

/*

* Our various sub-devices.

*/

/* Device 0 uses remap_pfn_range */

static struct file_operations simple_remap_ops = { //定义设备的fops

.owner   = THIS_MODULE,

.open    = simple_open,

.release = simple_release,

.read    = simple_read,

.write   = simple_write,

.ioctl   = simple_ioctl,

};

/*

* We export two simple devices.  There's no need for us to maintain any

* special housekeeping info, so we just deal with raw cdevs.

*/

static struct cdev SimpleDevs;

/*

* Module housekeeping.

*/

static struct class *my_class;

static int simple_init(void)

{

int result;

dev_t dev = MKDEV(simple_major, 0);//将设备号转化为dev_t的结构

/* Figure out our device number. */

if (simple_major)

result = register_chrdev_region(dev, 1, "simple");//尝试申请主设备号

else {

result = alloc_chrdev_region(&dev, 0, 1, "simple");//请求自动分配主设备号,起始值是0,总共分配1个,设备名simple

simple_major = MAJOR(dev);//将分配成功的设备号保存在simple_major变量中

}

if (result < 0) {//分配主设备号失败

printk(KERN_WARNING "simple: unable to get major %d\n", simple_major);

return result;

}

if (simple_major == 0)//将返回值记录为主设备号。需要么?

simple_major = result;

/* Now set up two cdevs. */

simple_setup_cdev(&SimpleDevs, 0, &simple_remap_ops);//调用自编的函数注册字符设备,有Bug没有返回注册是否成功。

printk("simple device installed, with major %d\n", simple_major);//Bug:打印前应该检查注册是否成功?

my_class= class_create(THIS_MODULE, "simple");//建立一个叫simple的内核class,目的是下一步创建设备节点文件

device_create(my_class, NULL, MKDEV(simple_major, 0),

NULL, "led");//创建设备节点文件

return 0;

}

static void simple_cleanup(void)

{

cdev_del(&SimpleDevs);//删除字符设备

unregister_chrdev_region(MKDEV(simple_major, 0), 1);//注销主设备号

device_destroy(my_class,MKDEV(simple_major,0));//删除设备节点

printk("simple device uninstalled\n");

}

module_init(simple_init);

module_exit(simple_cleanup);

3.2          驱动程序Open and release 函数

//寄存器地址,见CPU手册70页

#define pGPG3CON 0xE03001C0

#define pGPG3DAT 0xE03001C4

//寄存器操作指针

static void *vGPG3CON , *vGPG3DAT;

#define GPG3CON (*(volatile unsigned int *) vGPG3CON)

#define GPG3DAT (*(volatile unsigned int *) vGPG3DAT)

static int simple_major = 250;//默认的主设备号

module_param(simple_major, int, 0);//向内核申明一个参数,可以在insmod的时候传递给驱动程序

MODULE_AUTHOR("farsight");

MODULE_LICENSE("Dual BSD/GPL");

/*

* Open the device; in fact, there's nothing to do here.

*/

int simple_open (struct inode *inode, struct file *filp)

{

vGPG3CON=ioremap(pGPG3CON,0x10);//io remap地址pGPG3CON到变量

vGPG3DAT=vGPG3CON+0x04;//计算vGPG3DAT寄存器的地址

GPG3CON=0x1111;//使用宏设定寄存器初始值

GPG3DAT=0xff; //使用宏设定寄存器初始值

return 0;

}

ssize_t simple_read(struct file *file, char __user *buff, size_t count, loff_t *offp)

{

return 0;

}

ssize_t simple_write(struct file *file, const char __user *buff, size_t count, loff_t *offp)

{

return 0;

}

……

static int simple_release(struct inode *node, struct file *file)

{

return 0;

}

3.3          驱动程序ioctl函数

//ioctl命令值,LED ON ,LED OFF

#define LED_ON 0x4800

#define LED_OFF 0x4801

void led_off( void )

{

GPG3DAT=GPG3DAT|(1<<2);//通过宏设定寄存器的值,置1

//printk("stop led\n");

}

void led_on( void )

{

GPG3DAT=GPG3DAT&(~(1<<2)); //通过宏设定寄存器的值,置0

//printk("start led\n");

}

static int simple_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

{

switch ( cmd )

{

case LED_ON://判断命令

{

led_on();//执行命令

break;

}

case LED_OFF:

{

led_off();

break;

}

default:

{

break;

}

}

return 0;

}

3.4          驱动测试程序main.c

/*

* main.c : test demo driver

*/

#include

#include

#include

#include

#include

#include

#include

#define LED_ON 0x4800

#define LED_OFF 0x4801

int main()

{

int i = 0;

int dev_fd;

dev_fd = open("/dev/simple",O_RDWR | O_NONBLOCK);//打开设备文件

if ( dev_fd == -1 ) {

printf("Cann't open file /dev/simple\n");

exit(1);

}

while(1)

{

ioctl(dev_fd,LED_ON,0);//发送ioctl命令LED_ON

//     printf("on\n");

sleep(1);

ioctl(dev_fd,LED_OFF,0); //发送ioctl命令LED_OFF

//     printf("off\n");

sleep(1);

}

return 0;

}

4           HAL

4.1          Hardware Stub (运行在用户空间,直接操作设备文件,对上提供操作接口)

本层生成一个so文件,作为Android HAL层的Stub。按照Android HAL的要求,此文件必须放在固定的目录下面,并且具有特定的文件名。

规则如下:

# HAL module implemenation, not prelinked and stored in

# hw/..so

本例中:#define LED_HARDWARE_MODULE_ID "led"

所以生成:system/lib/hw/led.default.so

注:hardware\libled目录下的文件没用。只用hardware\modules目录下的文件用用。

4.1.1     头文件

#include

#include

#include

#include

#include

/********************************************************/

struct led_module_t {//定义了一个继承自hw_module_t的结构,记录本stub的基本信息和入口

struct hw_module_t common;

};

struct led_control_device_t {//定义一个继承自hw_device_t的结构记录本stub操作设备时需要包括的接口

struct hw_device_t common;

/* attributes */

int fd;//文件句柄

//下面是操作接口

/* supporting control APIs go here */

int (*set_on)(struct led_control_device_t *dev, int32_t led);

int (*set_off)(struct led_control_device_t *dev, int32_t led);

};

/**********************************************/

struct led_control_context_t {//定义一个继承自device结构的上下文结构

struct led_control_device_t device;

};

#define LED_HARDWARE_MODULE_ID "led"//定义HAL 的模块ID

4.1.2     C文件

4.1.2.1    入口定义

//定一个hw_module_methods_t结构体,关联入口函数

static struct hw_module_methods_t led_module_methods = {

open: led_device_open

};

//定义Stub入口

//注意必须使用:

//1。hw_module_t继承类

//2。必须使用HAL_MODULE_INFO_SYM这个名字

const struct led_module_t HAL_MODULE_INFO_SYM = {

common: {

tag: HARDWARE_MODULE_TAG,

version_major: 1,

version_minor: 0,

id: LED_HARDWARE_MODULE_ID,//模块ID,上层的Service通过这个ID应用当前Stub

name: "Sample LED Stub",

author: "The Mokoid Open Source Project",

methods: &led_module_methods,//入口函数管理结构体

}

/* supporting APIs go here */

};

4.1.2.2    Open & Close函数定义

static int led_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device)

{

struct led_control_device_t *dev;

//建立hw_device_t 继承类的dev变量,并初始化

dev = (struct led_control_device_t *)malloc(sizeof(*dev));

memset(dev, 0, sizeof(*dev));

dev->common.tag =  HARDWARE_DEVICE_TAG;

dev->common.version = 0;

dev->common.module = module;

dev->common.close = led_device_close;//关联关闭接口

dev->set_on = led_on;//关联操作接口

dev->set_off = led_off; //关联操作接口

*device = &dev->common;//common是hw_device_t结构体

if((fd=open("/dev/led",O_RDWR))==-1)//将fd初始化为设备文件

{

LOGE("LED open error");

}

else//这里没有失败后的处理,只是记录Log,这是Bug

LOGI("open ok");

success:

return 0;

}

int led_device_close(struct hw_device_t* device)

{

struct led_control_device_t* ctx = (struct led_control_device_t*)device;//强制转化,得到设备指针

if (ctx) {

free(ctx);//释放

}

close(fd);

return 0;

}

4.1.2.3    操作接口定义

#define GPG3DAT2_ON 0x4800

#define GPG3DAT2_OFF 0x4801

int led_on(struct led_control_device_t *dev, int32_t led)

{

//led参数可以用来控制打开哪个LED,但是没用到

LOGI("LED Stub: set %d on.", led);

ioctl(fd,GPG3DAT2_ON,NULL);//向设备驱动发送请求

return 0;

}

int led_off(struct led_control_device_t *dev, int32_t led)

{

//led参数可以用来控制打开哪个LED,但是没用到

LOGI("LED Stub: set %d off.", led);

ioctl(fd,GPG3DAT2_OFF,NULL); //向设备驱动发送请求

return 0;

}

4.2          Framework

本层共包括两个java包,一个是com.mokoid.server.LedService,另一个是mokiod.hardware.LedManager。这两个包编译在同一个jar文件中:mokoid.jar

另外还有一个/system/lib/libmokoid_runtime.so文件供JNI接口使用。详见下面jni一节。

这样上层的应用程序就可以通过调用LedServer或者LedManager来实现调用HAL的Stub了。

注意:这个jar文件需要通过xml描述文件注册到Android系统中。否则上层的应用程序会找不到需要的服务。

即:拷贝frameworks\base\service\com.mokoid.server.xml到目标系统的system/etc/permissions/目录下

4.2.1     Make file  (frameworks\base\Android.mk)

……

LOCAL_SRC_FILES := \

$(call all-subdir-java-files) #编译子目录下的所有Java文件

LOCAL_MODULE_TAGS := eng

LOCAL_MODULE := mokoid  #编译结果为mokoid.jar

# AIDL

LOCAL_SRC_FILES += \

core/java/mokoid/hardware/ILedService.aidl #接口定义文件

……

4.2.2     Service

本层负责通过JNI接口将C层的接口映射到Java层。其中C部分的内容以libmokoid_runtime.so文件存在,java部分以com.mokoid.server.LedServer的形式存在(编译在mokiod.jar文件中,见本章开始处Makefile的说明)

4.2.2.1    jni (com_mokoid_server_LedService.cpp)

4.2.2.1.1   JNI_OnLoad 入口函数

在Android中,JNI部分采用JNI_OnLoad作为入口的方式实现,即:所有的C实现的so文件以JNI_OnLoad为入口。

/*

*

* This is called by the VM when the shared library is first loaded.

*/

jint JNI_OnLoad(JavaVM* vm, void* reserved) {

JNIEnv* env = NULL;

jint result = -1;

LOGI("JNI_OnLoad LED");

if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {//获取当前的VM的环境,保存在env变量中,稍候通过这个变量

LOGE("ERROR: GetEnv failed\n");

goto fail;

}

assert(env != NULL);

if (registerMethods(env) != 0) {//自己写的函数,向当前JAVA环境中注册接口

LOGE("ERROR: PlatformLibrary native registration failed\n");

goto fail;

}

/* success -- return valid version number */

result = JNI_VERSION_1_4;

fail:

return result;

}

4.2.2.1.2   registerMethods 注册函数

static int registerMethods(JNIEnv* env) {

static const char* const kClassName =

"com/mokoid/server/LedService";

jclass clazz;

/* look up the class */

clazz = env->FindClass(kClassName);//查找被注册的类

if (clazz == NULL) {

LOGE("Can't find class %s\n", kClassName);

return -1;

}

/* register all the methods */

if (env->RegisterNatives(clazz, gMethods,

sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)//向类中注册本SO中Native的接口,接口定义在gMethods数组中

{

LOGE("Failed registering methods for %s\n", kClassName);

return -1;

}

/* fill out the rest of the ID cache */

return 0;

}

gMethods的定义如下:

/*

* Array of methods.

*

* Each entry has three fields: the name of the method, the method

* signature, and a pointer to the native implementation.

*/

static const JNINativeMethod gMethods[] = {

{ "_init",            "()Z",        (void *)mokoid_init },

{ "_set_on",        "(I)Z", (void *)mokoid_setOn },

{ "_set_off",       "(I)Z", (void *)mokoid_setOff },

};

/*

*JNINativeMethod是jni层注册的方法,Framework层可以使用这些方法

*_init 、_set_on、_set_off是在Framework中调用的方法名称,函数的类型及返回值如下:

*()Z   无参数    返回值为bool型

* (I)Z   整型参数  返回值为bool型

*/

4.2.2.1.3   mokoid_init 初始化函数

在本层的java文件frameworks\base\service\java\com\mokoid\server\ LedService.java中,构造函数public LedService()会通过_init接口调用到本函数static jboolean mokoid_init(JNIEnv *env, jclass clazz),来完成本模块的初始化。

struct led_control_device_t *sLedDevice = NULL;

……

static jboolean mokoid_init(JNIEnv *env, jclass clazz)

{

led_module_t* module;

LOGI("jni init-----------------------.");

if (hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) {//调用Android HAL标准函数hw_get_module,通过LED_HARDWARE_MODULE_ID获取LED Stub的句柄,句柄保存在module变量中

LOGI("LedService JNI: LED Stub found.");

if (led_control_open(&module->common, &sLedDevice) == 0) {//通过自定义函数调用stub中的open接口,并将stub中的devices handle保存到变量sLedDevice中。

LOGI("LedService JNI: Got Stub operations.");

return 0;

}

}

LOGE("LedService JNI: Get Stub operations failed.");

return -1;

}

//自定义函数如下

static inline int led_control_open(const struct hw_module_t* module,

struct led_control_device_t** device) {

return module->methods->open(module,

LED_HARDWARE_MODULE_ID, (struct hw_device_t**)device);//通过标准的Open接口,调用stub中的open函数

}

4.2.2.1.4   mokoid_setOn / Off 操作接口函数

static jboolean mokoid_setOn(JNIEnv* env, jobject thiz, jint led)

{

LOGI("LedService JNI: mokoid_setOn() is invoked.");

if (sLedDevice == NULL) {

LOGI("LedService JNI: sLedDevice was not fetched correctly.");

return -1;

} else {

return sLedDevice->set_on(sLedDevice, led);//通过set_on函数指针,调用stub中的led_on函数

}

}

static jboolean mokoid_setOff(JNIEnv* env, jobject thiz, jint led)

{

LOGI("LedService JNI: mokoid_setOff() is invoked.");

if (sLedDevice == NULL) {

LOGI("LedService JNI: sLedDevice was not fetched correctly.");

return -1;

} else {

return sLedDevice->set_off(sLedDevice, led); //通过set_off函数指针,调用stub中的led_off函数

}

}

4.2.2.2    java (向下调用runtime so来调用Stub,向上暴露Java接口)

public final class LedService extends ILedService.Stub {

static {

System.load("/system/lib/libmokoid_runtime.so");//加载runtime so文件

}

public LedService() {

Log.i("LedService", "Go to get LED Stub...");

_init();//构造函数,通过_init指针调用JNI的CPP文件中的mokoid_init函数

}

/*

* Mokoid LED native methods.

*/

public boolean setOn(int led) {

Log.i("MokoidPlatform", "LED On");

return _set_on(led);//调用JNI层的mokiod_setOn

}

public boolean setOff(int led) {

Log.i("MokoidPlatform", "LED Off");

return _set_off(led);//调用JNI层的mokiod_setOff

}

//申明native层的接口

private static native boolean _init();

private static native boolean _set_on(int led);

private static native boolean _set_off(int led);

}

4.2.3     Core (LedManager)

本层是对上一节中的LedService另一种形式的封装。

我没有开发过Android上的Java应用程序,不知道为什么要这样做。也许是因为权限原因?非Root权限不能直接访问Service?

此层分为两部分:

ILedService.aidl 接口定义

LedManager.java 实现LedManager

4.2.3.1    ILedService.aidl

package mokoid.hardware;

//定义ILedService接口

interface ILedService

{

boolean setOn(int led);

boolean setOff(int led);

}

4.2.3.2    LedManager

/**

* Class that lets you access the Mokoid LedService.

*/

public class LedManager

{

private static final String TAG = "LedManager";

private ILedService mLedService;//申明ILedService接口的变量

public LedManager() {

mLedService = ILedService.Stub.asInterface(

ServiceManager.getService("led")); //从ServiceManage中获取一个LedService的实例

if (mLedService != null) { //获取失败

Log.i(TAG, "The LedManager object is ready.");

}

}

public boolean LedOn(int n) {

boolean result = false;

try {

result = mLedService.setOn(n); //调用Service中的setOn

} catch (RemoteException e) {

Log.e(TAG, "RemoteException in LedManager.LedOn:", e);

}

return result;

}

public boolean LedOff(int n) {

boolean result = false;

try {

result = mLedService.setOff(n); //调用Service中的setOff

} catch (RemoteException e) {

Log.e(TAG, "RemoteException in LedManager.LedOff:", e);

}

return result;

}

}

5           应用程序

本理中共有两个应用程序示例。一个是LedClient,直接调用LedService来实现控制硬件;另一个是LedTest,通过 LedManager来访问LedService实现控制硬件。

由于我没有开发过Android Java应用程序,所以不太清楚两种方式的异同。

5.1          LedClient

package com.mokoid.LedClient;

import com.mokoid.server.LedService;

import android.app.Activity;

import android.os.Bundle;

import android.widget.TextView;

public class LedClient extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

// Call an API on the library.

LedService ls = new LedService();//建立LedService

ls.setOn(1);//操作硬件

ls.setOff(2); //操作硬件

TextView tv = new TextView(this);

tv.setText("LED 1 is on. LED 2 is off.");//显示文字

setContentView(tv);

}

}

5.2          LedTest

5.2.1     Xml

android:process=".LedSystemServer" >

从XML中可以看到申明了一个应用程序和一个名为com.mokoid.systemserver的server。

5.2.2     LedSystemServer

public class LedSystemServer extends Service {

@Override

public IBinder onBind(Intent intent) {

return null;

}

public void onStart(Intent intent, int startId) {

Log.i("LedSystemServer", "Start LedService...");

/* Please also see SystemServer.java for your interests. */

LedService ls = new LedService();//建立LedService

try {

ServiceManager.addService("led", ls);//向ServiceManager增加一个Service

} catch (RuntimeException e) {

Log.e("LedSystemServer", "Start LedService failed.");

}

}

}

5.2.3     LedTest

public class LedTest extends Activity implements View.OnClickListener {

private LedManager mLedManager = null;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

// Start LedService in a seperated process.

startService(new Intent("com.mokoid.systemserver"));//启动了自己写的com.mokiod.systemserver

Button btn = new Button(this);//建立一个按钮

btn.setText("Click to turn LED 1 On");

btn.setOnClickListener(this);

setContentView(btn);

}

public void onClick(View v) {

// Get LedManager.

if (mLedManager == null) {

Log.i("LedTest", "Creat a new LedManager object.");

mLedManager = new LedManager();//建立LedManager,此时LedManager的构造函数会从ServiceManager获取LedService的一个实例

}

if (mLedManager != null) {

Log.i("LedTest", "Got LedManager object.");

}

/** Call methods in LedService via proxy object

* which is provided by LedManager.

*/

mLedManager.LedOn(1);//通过mLedManager的接口调用LedService

TextView tv = new TextView(this);

tv.setText("LED 1 is On.");

setContentView(tv);

}

}

6           备注

6.1          insmod的时候如何传递参数

例如:insmod hello.ko count=0 string="hello"

注:在程序中通过如下宏来申明一个接受参数的变量

module_param(simple_major, int, 0);//向内核申明一个参数,可以在insmod的时候传递给驱动程序

http://blog.csdn.net/xdw1985829/article/details/7687917

1、编写GPIO驱动并编译为模块

完全按照linux驱动的编写方式,封装为标准的linux字符驱动

2、使用C语言编写测试程序

编写、编译一个操作led的程序进行驱动测试

以上两步可参考《s5pv210-gpio》:

s5pv210-gpio.c

s5pv210-gpio.h

s5pv210-gpio_test.c

其编译必须在linux环境下完成,交叉编译测试程序时使用静态编译

3、在eclipose中设计java接口

在eclipose中新建一个android工程用于设计底层jni的操作接口(windows、linux均可)

可参考《s5pv210-gpio》:

s5pv210-gpio\src\com\edu\gpio\s5pv210_gpio_options.java

4、由javah 生成.h文件

我们最终的目的是要实现s5pv210_gpio_options.java中设计的接口

由jdk自带的javah组件生成s5pv210_gpio_options.h文件

打开终端,进入刚才编译的Java程序的目标文件夹(如果是在Eclipse中,则进入工程的bins\classes目录),执行下面的命令:

javah com.edu.gpio.s5pv210_gpio_options

其中,com.edu.gpio.是package的名字,s5pv210_gpio_options是声明了native接口的Java类。

之后,将会生成一个形如com_edu_gpio_s5pv210_gpio_options.h的头文件。

此头文件中便声明了可以被Java调用的C/C++的函数形式。

5、由.h文件设计.c文件接口

自己根据s5pv210_gpio_options.h构建相应的.c文件

复制一个跟s5pv210_gpio_options.h同名的.c文件,将里面的声明全部改为对应的实现

可参考:s5pv210_gpio_options.c

注意java中数据类型与c语言数据类型之间的转换规则

设计.c文件的实现时可参考第2步的测试程序设计

7、编写makefile文件

C程序编写完毕后,在文件夹中创建一个名为Android.mk的文件,文件名不能修改,内容如下:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:=com_sapp_gpio.c

LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)

LOCAL_MODULE := libgpio

LOCAL_SHARED_LIBRARIES := libutils

LOCAL_MODULE_TAGS := eng

LOCAL_PRELINK_MODULE := false

include $(BUILD_SHARED_LIBRARY)

其中,LOCAL_SRC_FILES后面的文件是我们的C语言的源程序文件名

LOCAL_MODULE后面的名字是我们将来生成的目标文件名

include $(BUILD_SHARED_LIBRARY)表示将来要生成动态链接库。

OK,写好之后,回到Android源码的根目录,执行:

在android源码目录的external下建立libs5pv210-gpio文件夹

将com_edu_gpio_s5pv210_gpio_options.c

com_edu_gpio_s5pv210_gpio_options.h

Android.mk

libs5pv210-gpio.so

拷到此目录下

回到android源码根目录,执行以下命令make libgpio

其中,libgpio是我们的Android.mk文件中LOCAL_MODULE的名字。

等待编译结束,在out/target/product/generic/system/lib/libgpio.so目录下即可找到编译好的文件。

8、将动态库拷入根文件系统/system/lib文件

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值