android控制gpio实现对小灯读写(一)

本实验通过GPIO口拉高拉低控制小灯的亮灭,作为刚刚从应用层转framework的小兵,写这篇文章希望对大家的学习有帮助。

##什么是GPIO
GPIO,英文全称为General-Purpose IO ports,也就是通用IO口。嵌入式系统中常常有数量众多,但是结构却比较简单的外部设备/电路,对这些设备/电路有的需要CPU为之提供控制手段,有的则需要被CPU用作输入信号。而且,许多这样的设备/电路只要求一位,即只要有开/关两种状态就够了,比如灯亮与灭。对这些设备/电路的控制,使用传统的串行口或并行口都不合适。

所以在微控制器芯片上一般都会提供一个“通用可编程IO接口”,即GPIO。
通俗地说,就是一些引脚,可以通过它们输出高低电平或者通过它们读入引脚的状态-是高电平或是低电平。

##内核中生成设备
###原理
关于GPIO口字符设备驱动我们要做如下步骤:
1 找到原理图对应的GPIO口并配置它为输出管脚
gpio口配置(不同平台配置不一样)但目的是一样的 设置GPIO口输出,并默认低电平,我们接的是57管脚
<&range 56 1 0x1500>
<&range 57 1 0x1500> 代表57管脚做为gpio口使用,并默认低电平
这个io口寄存器地址:0xe46002e4

但是调试过程中发现

#define gpio_lp 57 
gpio_request(gpio_lp,"pos_pwr");
gpio_set_value(gpio_lp,1);

GPIO调用request报错,导致GPIO不能用但是换个GPIO口后换个gpio口就不报错了,
这种原因是由于intel的特殊性:gpio在vmm的地方被调用,在kernel下就不能操作它(一般平台这样操作没问题的)

后续,想到了另外一种方法

void __iomem *ldo_mmio_base = ioremap(0xe46002e4, 4);
iowrite32(0x1700, ldo_mmio_base);   //1700代表设置寄存器(0xe46002e4)为GPIO口,输出为高
iowrite32(0x1500, ldo_mmio_base);//1500代表设置寄存器(0xe46002e4)为GPIO口,输出为低  

实现了IO口的控制
###实现
源代码我放到kenel-3.10/drivers/char下面让系统生成设备节点:/dev/mtgpio,具体需要修改两个地方。
首先进入到kernel-3.10/drivers/char目录,新增文件,我这里命名为lp6735_switch.c ,具体代码如下:
kernel-3.10/drivers/char$ vim lp6735_switch.c

#include <linux/module.h>               /* For module specific items */    
#include <linux/moduleparam.h>          /* For new moduleparam's */    
#include <linux/types.h>                /* For standard types (like size_t) */    
#include <linux/errno.h>                /* For the -ENODEV/... values */    
#include <linux/kernel.h>               /* For printk/panic/... */    
#include <linux/fs.h>                   /* For file operations */^M    
#include <linux/ioport.h>               /* For io-port access */    
#include <linux/platform_device.h>      /* For platform_driver framework */    
#include <linux/init.h>                 /* For __init/__exit/... */    
#include <linux/uaccess.h>              /* For copy_to_user/put_user/... */    
#include <linux/io.h>                   /* For inb/outb/... */    
#include <linux/gpio.h>    
#include <linux/device.h>    
#include <linux/cdev.h>    
#include <linux/slab.h>               /*kamlloc */    
//#include <asm-generic/ioctl.h>    
     
 //ioctl   
#define CMD_FLAG  'i'    
#define led_PWR_ON      _IOR(CMD_FLAG,0x00000001,__u32)      
#define led_PWR_OFF     _IOR(CMD_FLAG,0x00000000,__u32)   
#define gpio_lp         57   
    
static int  major =0;    
static struct classclass *led_class;    
struct cdev_led {    
    struct cdev cdev;    
};     
struct cdev_led *led_dev;    
    
static int led_ioctl(struct file* filp,unsigned int cmd,unsigned long argv)    
{    
    printk(KERN_INFO "entry kernel.... \n");    
    printk(KERN_INFO "%d\n", led_PWR_ON);  
    void __iomem *ldo_mmio_base = ioremap(0xe46002e4, 4);
    
    switch(cmd)    
    {    
        case led_PWR_ON:    
        {    
#if 0  
            gpio_set_value(gpio_lp,1);  //   
            printk(KERN_INFO "led on\n");   
#endif  
            iowrite32(0x1700, ldo_mmio_base)  
            break;    
        }    
        case led_PWR_OFF:    
        {    
#if 0  
            gpio_set_value(gpio_lp,0);  
            printk(KERN_INFO "led off \n");  
#endif
            iowrite32(0x1500, ldo_mmio_base);  
            break;    
        }    
        default:    
            return -EINVAL;    
    }    
    return 0;    
}    
    
    
//open    
static int led_open(struct inode* i_node,struct file* filp)    
{    
    printk(KERN_INFO "larsonzhong open init.... \n");    
    int err;  
// larsonzhong	 Content between #if 0 #endif would comment, because there is no actual device
 #if 0     
    err = gpio_request(gpio_lp,"led_pwr");  
    if(err<0)    
    {    
        printk(KERN_INFO "gpio request faile \n");    
        return err;    
    }    
    gpio_direction_output(gpio_lp,1);  
 #endif  
    return 0;    
}    
    
//close    
static void led_close(struct inode* i_node,struct file* filp)    
{    
printk(KERN_INFO "larsonzhong close init \n"); 
// larsonzhong	 Content between #if 0 #endif would comment, because there is no actual device
#if 0    
    gpio_free(gpio_lp);   
#endif  
    return ;    
}    
    
/* file operations */    
struct file_operations fops={    
    .owner  = THIS_MODULE,    
    .open   = led_open,    
    .unlocked_ioctl = led_ioctl,   
    .release= led_close,    
};    
    
static int __init led_init(void)    
{    
    printk(KERN_INFO "init .... \n");    
    dev_t dev_no;    
    int result,err;    
    err = alloc_chrdev_region(&dev_no,0,1,"my_led"); //dynamic request device number    
    if(err<0)    
    {    
        printk(KERN_INFO "ERROR\n");    
        return err;    
    }    
    major = MAJOR(dev_no);    
    led_dev = kmalloc(sizeof(struct cdev_led),GFP_KERNEL);    
    if(!led_dev)    
    {    
        result = -ENOMEM;    
        goto fail_malloc;    
    }    
    memset(led_dev,0,sizeof(led_dev));    
        
    cdev_init(&led_dev->cdev,&fops);     
    led_dev->cdev.owner = THIS_MODULE;    
    result = cdev_add(&led_dev->cdev,dev_no,1);     
    if(result <0)    
    {   printk(KERN_INFO "error\n");    
        goto fail_add;    
    }    
    led_class = class_create(THIS_MODULE,"mtgpio");  //in sys/class create sysfs file    
    device_create(led_class,NULL,MKDEV(major,0),NULL,"mtgpio"); //dynamic create device file  /dev/myled    
    return 0;    
fail_add:    
    kfree(led_dev);    
fail_malloc:    
    unregister_chrdev_region(dev_no,1);    
    return result;    
    
}    
    
static void __exit led_exit(void)    
{    
    dev_t dev_no=MKDEV(major,0);    
    
    unregister_chrdev_region(dev_no,1);    
    cdev_del(&led_dev->cdev);    
    kfree(led_dev);    
    device_destroy(led_class,dev_no);    
    class_destroy(led_class);    
    printk(KERN_INFO "exit........ \n");    
}    
module_init(led_init);    
module_exit(led_exit);    
MODULE_AUTHOR("larsonzhong@gmail.com");    
MODULE_DESCRIPTION("control_led_power");    
MODULE_LICENSE("GPL");   

保存退出,然后我们还需要修改一个文件,就是同级目录下的makefile文件。
在kernel-3.10/drivers/char$ vim Makefile 里面新增一段:
+obj-y += lp6735_switch.o
具体代码,因为csd对一些特俗符号作了处理,只能传图:
makefile文件

这样加入lp6735_switch.c并修改Makefile后固件生成就会在 /dev/ 下生成节点 /dev/mtgpio
要让这个节点让别人可读写,还必须修改节点的系统所有者以及他的权限,这个步骤我们一版在 init.rc中进行

我的代码是system/core/rootdir/init.rc

   ...
   # added by larsonzhong@163.com change my devices mqgpio
   chown system system /dev/mtgpio
   chmod 0766 /dev/mtgpio
   ...

##开始实验
我们假设我们要用的小灯的设备是dev/mtgpio##Jni头文件
新建一个安卓工程,然后新建类,我这里是GpioLED.java。

public class GpioLED {
	
	    //控制LED上电
	    public native static void ledPowerOn();
	    // 控制LED下电
	    public native static void ledPowerOff();
	}

生成头文件

方法1:简单粗暴
使用javah生成,请注意java环境已经搭建好(包括环境配置classpath配置)。
进入到源码下的bin目录下的classes目录,使用javah -jni 包名.类名,我这里是javah -jni com.coban.ledsimple.LEDGpio
我们再目录下就能看到头文件,打开头文件。把里面的声明copy出来。

方法2:更便捷
点击编译按钮旁边带工具箱的编译按钮旁边的小三角,弹出下拉列表,点击External Tools Configurations,弹出了一个对话框,
我们选中Program然后点击上面的新建按钮。然后如下填写:

Main卡:
	Name:javah
	Location:选择javah所在目录。我的是C:\Program Files\Java\jdk1.8.0_102\bin\javah.exe
	Working Diractory:点击variables按钮弹出列表,选择project_loc确定,然后再后面加上\src,我的结果是${project_loc}\src
	Arguments:-classpath ${project_loc}\bin\classes -d ${project_loc}\jni -jni ${java_type_name}
Refresh卡:
	勾选 refresh Resource upon completion
Common卡:
	勾选 External Tools 然后点击apply

###jni实现

新建c文件和mk文件。如果你使用的是eclipse并且配置了ndk(注意不仅在eclipse要配置ndk,在环境变量也要配置)否则可能出现Unable to launch cygpath. Is Cygwin on the path?] 错误。
右击Project->Android Tools->Add Native Support
然后你会看到工程目录下多了一个文件夹和两个文件,我们把刚刚copy的头文件声明粘贴过来。

我这里的代码实现是这样的:ledcontrol.c

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<errno.h>
#include<unistd.h>
#include<sys/ioctl.h>
#include<jni.h>  // 一定要包含此文件
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include <android/log.h>
//驱动里的命令码.
#define CMD_FLAG 'i'
#define LED_ON      _IOR(CMD_FLAG,0x00000001,__u32)
#define LED_OFF     _IOR(CMD_FLAG,0x00000000,__u32)

#define DEVICE_NAME "/dev/mtgpio"
int fd;


static const char *TAG="012";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)

/* * Class:     Linuxc
* Method:    openled
* Signature: ()I
*/

JNIEXPORT void JNICALL Java_com_coban_ledsimple_LEDGpio_ledPowerOn(JNIEnv* env, jclass mc)
{
  LOGI("POWER ON BY LARSON");
  LOGI("LED_ON:%d   LED_OFF:%d",LED_ON,LED_OFF);
  fd=open(DEVICE_NAME,O_RDWR);
  if(fd<0)
      {
            LOGI("don't open dev");
        }
        else
            {
            ioctl(fd,LED_ON,NULL) ;
            LOGI("open success");
            }


}

/* * Class:     Linuxc
* Method:    clsoeled
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_coban_ledsimple_LEDGpio_ledPowerOff(JNIEnv* env, jclass mc)
{
    LOGI("POWER Off BY LARSON");
    ioctl(fd,LED_OFF,NULL) ;
    close(fd);

}

请注意,这里我用到了日志和一些其他的头文件,我们需要把相关的库包含进来。修改Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_LDLIBS    := -lm -llog 
LOCAL_MODULE    := ledcontrol
LOCAL_SRC_FILES := ledcontrol.c

include $(BUILD_SHARED_LIBRARY)

要注意的是LOCAL_LDLIBS := -lm -llog 不要添加在include $(CLEAR_VARS)的前面,否则会被清掉,导致无效。

Application.mk
APP_ABI := all

这里的意思是编译所有平台的so文件,如果想指定某个编译
APP_ABI := armeabi armeabi-v7a x86
中间用空格隔开

###生成so文件
我们直接把软件往设备上推或者build一把。会在工程目录下看到多出了一个obj文件夹,
而且在libs下多出了好几个文件夹,这些文件夹对应不同的平台。

###APP实现
这里我就直接贴代码了
HelloJniLED.java

package com.example.hellojni;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class HelloJniLED extends Activity {
	private Button power_on;
	private Button power_off;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		power_on = (Button) findViewById(R.id.power_on);
		power_off = (Button) findViewById(R.id.power_off);

		
		power_on.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Log.d("012", "power_on by android\n");
				LEDGpio.ledPowerOn();

			}
		});
		power_off.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Log.d("012", "power_off by android\n");
				LEDGpio.ledPowerOff();

			}
		});

	}
	
}

LEDGpio.java

package com.coban.ledsimple;

import android.util.Log;

public class LEDGpio {
	static {
		try {
			Log.i("012", "try to load ledcontrol.so");
			System.loadLibrary("ledcontrol");
		} catch (UnsatisfiedLinkError ule) {
			Log.e("012", "WARNING: Could not load ledcontrol.so");
		}
	}

	public native static void ledPowerOn();

	public native static void ledPowerOff();
}

布局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/position"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text=" power control "
        android:textColor="#ff0000"
        android:textSize="25sp" />

    <Button
        android:id="@+id/power_on"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/position"
        android:layout_centerHorizontal="true"
        android:layout_toLeftOf="@+id/position"
        android:text="power_on"
        android:textSize="18sp" />

    <Button
        android:id="@+id/power_off"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/position"
        android:layout_toRightOf="@+id/position"
        android:text="power_off"
        android:textSize="18sp" />

</RelativeLayout>
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯人院的院长大人

给点实际性的支持不?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值