Android Gpio use cases by controlling LED
Tags: gpio, android, mcu
Published: 2021-01-14 09:53:02

This experiment uses the GPIO port to pull up and down to control the on and off of the small lights. As a soldier who has just switched from the application layer to the framework, writing this article hopes to help you learn.

##What is GPIO

GPIO, the full English name is General-Purpose IO ports, that is, general-purpose IO ports. Embedded systems often have a large number of external devices/circuits with relatively simple structures. Some of these devices/circuits require the CPU to provide control means, and some need to be used by the CPU as an input signal. Moreover, many of these devices/circuits only require one bit, that is, as long as there are two states of on/off, such as light on and off. For the control of these devices/circuits, it is not appropriate to use traditional serial ports or parallel ports.

Therefore, a "universal programmable IO interface" is generally provided on the microcontroller chip, that is, GPIO.
In layman's terms, some pins can be used to output high and low levels or read the state of the pins through them-whether they are high or low.

##Generate devices in the kernel
###principle
Regarding GPIO port character device driver, we have to do the following steps:
1 Find the GPIO port corresponding to the schematic and configure it as an output pin
GPIO port configuration (different platform configuration is different) but the purpose is the same. Set the GPIO port output, and the default low level, we are connected to 57 pins
<&range 56 1 0x1500>
<&range 57 1 0x1500> means 57 pins are used as gpio ports, and default low level
This io port register address: 0xe46002e4

But during the debugging process

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

The GPIO call request reports an error, which causes the GPIO to be unusable, but after changing the GPIO port, changing the gpio port will not report the error.
This reason is due to the particularity of intel: gpio is called in the place of vmm, and it cannot be operated under the kernel (the general platform is no problem with this operation)

Later, I thought of another method

void __iomem *ldo_mmio_base = ioremap(0xe46002e4, 4);
iowrite32(0x1700, ldo_mmio_base); //1700 represents the setting register (0xe46002e4) is GPIO port, and the output is high
iowrite32(0x1500, ldo_mmio_base);//1500 represents the setting register (0xe46002e4) is GPIO port, the output is low

Realize the control of IO port
###achieve
I put the source code under kenel-3.10/drivers/char to let the system generate a device node: /dev/mtgpio, which needs to be modified in two places.
First enter the kernel-3.10/drivers/char directory, add a new file, I named it lp6735_switch.c here, the specific code is as follows:
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");

```
Save and exit, and then we need to modify a file, which is the makefile in the same directory.
Add a paragraph in kernel-3.10/drivers/char$ vim Makefile:
+obj-y += lp6735_switch.o
Specific code, because csd has processed some special symbols, only pictures can be uploaded:
![makefile file](https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcwNDIxMTIxMzIyNDI5?x-oss-process=image/format,png)

After adding lp6735_switch.c and modifying the Makefile, the firmware generation will generate a node under /dev/ /dev/mtgpio
To make this node readable and writable by others, you must also modify the system owner of the node and his permissions. This step is carried out in init.rc.

My code is system/core/rootdir/init.rc
```
   ...
   # added by larsonzhong@163.com change my devices mqgpio
   chown system system /dev/mtgpio
   chmod 0766 /dev/mtgpio
   ...
```

##Start experiment
We assume that the device of the small lamp we want to use is dev/mtgpio##Jni header file
Create a new Android project, and then create a new class, here is GpioLED.java.
To
```
public class GpioLED {
To
//Control LED power on
public native static void ledPowerOn();
// Control the LED to power off
public native static void ledPowerOff();
}
```
To
Generate header file

Method 1: Simple and rude
Use javah to generate, please note that the java environment has been set up (including environment configuration classpath configuration).
Go to the classes directory under the bin directory under the source code, and use the javah -jni package name. Class name, here is javah -jni com.coban.ledsimple.LEDGpio
We can see the header file in the directory and open the header file. Copy the statement inside.

Method 2: More convenient
Click the small triangle next to the compile button with toolbox next to the compile button, a drop-down list pops up, click External Tools Configurations, a dialog box pops up,
We select Program and click the new button above. Then fill in as follows:

Main card:
Name: javah
Location: Select the directory where javah is located. Mine is C:\Program Files\Java\jdk1.8.0_102\bin\javah.exe
Working Diractory: Click the variables button to pop up the list, select project_loc to confirm, and then add \src at the end, my result is ${project_loc}\src
Arguments: -classpath ${project_loc}\bin\classes -d ${project_loc}\jni -jni ${java_type_name}
Refresh card:
Check refresh Resource upon completion
Common card:
Check External Tools and click apply

###jni implementation

 Create a new c file and mk file. If you are using eclipse and ndk is configured (note that ndk must be configured not only in eclipse, but also in environment variables) otherwise there may be Unable to launch cygpath. Is Cygwin on the path?] error.
Right click Project->Android Tools->Add Native Support
Then you will see an additional folder and two files in the project directory, we paste the header file declaration just copied.

My code implementation here is like this: ledcontrol.c

```
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<errno.h>
#include<unistd.h>
#include<sys/ioctl.h>
#include<jni.h> // Must include this file
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include <android/log.h>
//Command code in the driver.
#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);

}
```

Please note that I used the log and some other header files here, we need to include the relevant libraries. Modify 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)
```

It should be noted that LOCAL_LDLIBS := -lm -llog should not be added in front of include $(CLEAR_VARS), otherwise it will be cleared and invalidated.

```
Application.mk
APP_ABI := all
```

The meaning here is to compile the so files of all platforms, if you want to specify a compilation
APP_ABI := armeabi armeabi-v7a x86
Separated by a space

###Generate so file
We directly push or build the software to the device. You will see an extra obj folder in the project directory,
And there are several more folders under libs, these folders correspond to different platforms.


###APP Implementation
I will post the code directly here
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);

To
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();

}
});

}
To
}

```
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();
}

```
Layout file 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>
```