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

来源:互联网 发布:淘宝 大麻暗语 编辑:程序博客网 时间:2024/06/08 07:30

本实验通过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 completionCommon卡:    勾选 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    := ledcontrolLOCAL_SRC_FILES := ledcontrol.cinclude $(BUILD_SHARED_LIBRARY)

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

Application.mkAPP_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>
5 0