访问 DragonBoard 410C 上的 GPIO 低功耗连接器 运行 Android

accessing GPIO low power connector on DragonBoard 410C running Android

我正在使用最近购买的 DragonBoard 410C 运行 Android 5.1 操作系统,并使用 Android Studio 和 Kotlin 生成一个示例应用程序来探索一些硬件例如 40 针低功率连接器。

我的问题是如何使用 Kotlin 和 Android Studio 通过 GPIO 引脚访问 40 引脚低功耗连接器。

根据我目前的研究,mraa 库似乎是通往成功的道路,但是我一直无法找到任何关于将库与 Kotlin 一起使用的文档。

如何开始使用带有 Kotlin 的 mraa 库来访问 40 针低功耗连接器?

或者有不同的方法吗?

我的第一个示例是 LED 应用程序的简单闪烁,但是我不知道如何使用 Kotlin 访问低功率连接器的引脚。

注释和资源

mraa documentation page

Libmraa is a C/C++ library with bindings to Python, Javascript and Java to interface with the I/O on Galileo, Edison & other platforms, with a structured and sane API where port names/numbering matches the board that you are on. Use of libmraa does not tie you to specific hardware with board detection done at runtime you can create portable code that will work across the supported platforms.

upm library for mraa GitHub repository

The UPM repository provides software drivers for a wide variety of commonly used sensors and actuators. These software drivers interact with the underlying hardware platform (or microcontroller), as well as with the attached sensors, through calls to MRAA APIs.

哪个 Android 运行哪个 Linux 内核? https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel

Android Version    |API Level  |Linux Version in AOSP                    |Header Version
----------------------------------------------------------------------------------------
4.4   Kit Kat      |19, 20     |(3.10)                                   |2.6.18
5.x   Lollipop     |21, 22     |(3.16.1)                                 |3.14.0
6.0   Marshmallow  |23         |(3.18.10)                                |3.18.10

sysfs is dead! long live libgpiod! (libgpiod for linux & CircuitPython)

This is basically code that will replace our Python DHT driver, and has the benefit of being forward compatible with any other Linux board that runs a 4.8+ kernel. We’ll slowly be replacing other CircuitPython code to use libgpiod, so that we can have broad support for CircuitPython on a Raspberry Pi, BeagleBone or Onion.io.

There’s not a lot of libgpiod code out there, and libgpiod doesn’t come stock on Linux distros yet which may be why its taking a little while to catch on. There’s bindings for C and Python. Here’s a script that can help you get started by compiling it for you https://github.com/adafruit/Raspberry-Pi-Installer-Scripts/blob/master/libgpiod.sh

GitHubhttps://github.com/IOT-410c/DragonBoard410c_GpioLibrary上的DragonBoard 410C GPIO库,写在Java中,使用Linux的“/sys/class/gpio”方法访问GPIO别针。这个貌似是Coursera物联网课程用的一组仓库中的一个仓库,有些用的是DragonBoard 410C。

The Linux Kernel: Legacy GPIO Interfaces

This provides an overview of GPIO access conventions on Linux.

These calls use the gpio_* naming prefix. No other calls should use that prefix, or the related _gpio* prefix.

Android Studio 和 adb

Android Studio 是用于开发 Android 应用程序的应用程序。可以从https://developer.android.com/studio/releases

下载安装

此外还有 Android 平台工具,需要单独下载。 adb shell 应用程序是这些工具的一部分。这些工具可以从 SDK Platform Tools release notes 下载安装。选择您的操作系统所需的特定版本(Windows、Linux、MacOS)。

Android 事情

虽然 Android 看起来很有帮助,但似乎实际上只支持几个板,而 DragonBoard 410C 不是其中之一。而且我不确定 Android 东西是否可以与 Android 5.1 一起使用。

https://developer.android.com/things/get-started

然而,DragonBoard 410C 有一个 Brillo(现在 Android 事物)端口 https://discuss.96boards.org/t/android-things-on-the-dragonboard/1128

Android Developers > Docs > Android Things > Guides > GPIO

In order to open a connection to a GPIO port, you need to know the unique port name. During the initial stages of development, or when porting an app to new hardware, it's helpful to discover all the available port names from PeripheralManager using getGpioList():

Android 事物 GitHub 存储库 https://github.com/androidthings/

另请参阅以下关于此主题的 Whosebug 帖子。也请参阅 Whosebug 中的标记 [android-things]。

在审查了一些备选方案后,似乎访问 DragonBoard 410C 运行 Android 5.1 的 GPIO 引脚的最简单方法是使用遗留的 sysfs 特殊设备文件方法。

我不确定这是否是唯一可行的解​​决方案。使用 Android Things 以及使用 libgpiod 似乎都需要比 Android 5.1 使用更新的 Linux 内核。

我在 CodeProject.com 上写了一篇文章,提供了有关制定此解决方案的详细信息。参见 Using Windows 10 for Development with DragonBoard 410C and Android

哪个 Android 运行哪个 Linux 内核? https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel

Android Version    |API Level  |Linux Version in AOSP                    |Header Version
----------------------------------------------------------------------------------------
4.4   Kit Kat      |19, 20     |(3.10)                                   |2.6.18
5.x   Lollipop     |21, 22     |(3.16.1)                                 |3.14.0
6.0   Marshmallow  |23         |(3.18.10)                                |3.18.10

这种方法似乎也是最简单的,因为所使用的库也是用 Kotlin 编写的。

使用遗留的sysfs特殊设备GPIO接口

查看此 Whosebug post 关于 Linux 伪文件和特殊设备文件以及 GPIO 引脚的旧版 sysfs 接口, .

我找到了一个用 Java 编写的简单 GPIO 库,它提供了必要的源代码。 Android Studio 有一个工具可以将 Java 转换为 Kotlin,我将其包含在我的项目中。源代码在下面的文件 Gpio.kt 和 GpioProcessor.kt.

然而,为了让它工作,我必须更改我的 DragonBoard 启动脚本,以确保必要的特殊设备文件已创建并可用适当的权限允许用户程序操作 GPIO别针。

以下过程来自 Coursera class Internet of Things: Sensing and Actuation from Devices,第 5a 课:通过程序访问 GPIO (Android) 视频 #2,修改启动脚本。程序是:

  • 使用 adb 将 /etc/init.qcom.post_boot.sh 从 DragonBoard 复制到我的电脑上
  • 使用记事本修改shell脚本创建特殊设备文件
  • 使用adb将修改后的副本推送回Dragonboard
  • 使用 adb 重启 DragonBoard

添加到/etc/init.qcom.post_boot.sh底部的附加shell代码如下。 但是这些特殊设备文件仅适用于 Android 5.1。 Linux 使用不同的 GPIO 引脚名称。

set -A pins 938 915 1017 926 937 930 914 971 901 936 935
for i in 0 1 2 3 4 5 6 7 8 9 10
do
    echo ${pins[i]} > /sys/class/gpio/export;
    chmod 777 /sys/class/gpio/gpio${pins[i]};
    chmod 777 /sys/class/gpio/gpio${pins[i]}/value;
    chmod 777 /sys/class/gpio/gpio${pins[i]}/direction;
done

关于 sysfs 设备属性的注释

这是 kernel.org 中关于 GPIO Sysfs Inferface for Userspace 的一些文档。除了我使用的两个属性 directionvalue 之外,还有其他几个属性,例如 edgeactive_low.

“direction” … reads as either “in” or “out”. This value may normally be written. Writing as “out” defaults to initializing the value as low. To ensure glitch free operation, values “low” and “high” may be written to configure the GPIO as an output with that initial value.

Note that this attribute will not exist if the kernel doesn’t support changing the direction of a GPIO, or it was exported by kernel code that didn’t explicitly allow userspace to reconfigure this GPIO’s direction.

“value” … reads as either 0 (low) or 1 (high). If the GPIO is configured as an output, this value may be written; any nonzero value is treated as high.

If the pin can be configured as interrupt-generating interrupt and if it has been configured to generate interrupts (see the description of “edge”), you can poll(2) on that file and poll(2) will return whenever the interrupt was triggered. If you use poll(2), set the events POLLPRI and POLLERR. If you use select(2), set the file descriptor in exceptfds. After poll(2) returns, either lseek(2) to the beginning of the sysfs file and read the new value or close the file and re-open it to read the value.

“edge” … reads as either “none”, “rising”, “falling”, or “both”. Write these strings to select the signal edge(s) that will make poll(2) on the “value” file return.

This file exists only if the pin can be configured as an interrupt generating input pin.

“active_low” … reads as either 0 (false) or 1 (true). Write any nonzero value to invert the value attribute both for reading and writing. Existing and subsequent poll(2) support configuration via the edge attribute for “rising” and “falling” edges will follow this setting.

使用sysfs的Kotlin源代码

我用来探索将 DragonBoard 410C 与 Android 结合使用这一主题的完整测试应用程序在我的 GitHub 存储库中,https://github.com/RichardChambers/dragonboard_410c

文件来源 Gpio.kt

package com.example.myapplication

import java.io.*

/**
 * Created by Ara on 7/21/15.
 * From https://www.instructables.com/id/DragonBoard-How-to-Access-GPIOs-Using-Java/
 *   Java source from the article was converted to Kotlin using Android Studio.
 *
 * See as well https://github.com/IOT-410c/DragonBoard410c_GpioLibrary
 *
 */
class Gpio(pin: Int) {
    private val pin: Int

    /*
     *  The GPIO pins are represented by folders in the Linux file system
     *  within the folder /sys/class/gpio. Each pin is represented by a folder
     *  whose name is the prefix "gpio" followed by the pin number.
     *  Within the folder representing the pin are two files, "value" used to
     *  set or get the value of the pin and "direction" used to set or get
     *  the direction of the pin.
     *
     *  This function creates the path to the Linux file which represents a particular
     *  GPIO pin function, "value" or "direction".
     */
    private fun MakeFileName(pin: Int, op: String): String {
        return "/sys/class/gpio/gpio$pin$op"
    }

    /*
     * Get or set the current direction of a pin.
     * A pin may be either an Input pin or an Output pin.
     */
    var direction: String
        get() {
            println("Getting Direction")
            var line = ""
            try {
                val br = BufferedReader(FileReader(MakeFileName(pin, "/direction")))
                line = br.readLine()
                br.close()
            } catch (e: Exception) {
                println("Error: " + e.message)
            }
            return line
        }
        private set(direction) {
            println("Setting Direction")
            try {
                val out = BufferedWriter(FileWriter(MakeFileName(pin, "/direction"), false))
                out.write(direction)
                out.close()
            } catch (e: IOException) {
                println("Error: " + e.message)
            }
        }

    /**
     * Get or Set pin value.
     * @param value Value of pin.
     * 0 -> Low Level.
     * 1 -> High Level
     */
    var value: Int
        get() {
            println("Getting Value")
            var line = ""
            try {
                val br = BufferedReader(FileReader(MakeFileName(pin, "/value")))
                line = br.readLine()
                br.close()
            } catch (e: Exception) {
                println("Error: " + e.message)
            }
            return line.toInt()
        }
        private set(value) {
            println("Setting Value")
            try {
                val out = BufferedWriter(FileWriter(MakeFileName(pin, "/value"), false))
                out.write(Integer.toString(value))
                out.close()
            } catch (e: IOException) {
                println("Error: " + e.message)
            }
        }

    /**
     * Set pin as high.
     */
    fun pinHigh() {
        value = HIGH
    }

    /**
     * Set pin as low.
     */
    fun pinLow() {
        value = LOW
    }

    /**
     * Set pin as output.
     */
    fun pinOut() {
        direction = "out"
    }

    /**
     * Set pin as input.
     * @param pin - Desirable pin.
     */
    fun pinIn() {
        direction = "in"
    }

    fun exportPin() {
        println("Exporting Ping")
        try {
            val out = BufferedWriter(FileWriter("$PATH/export", false))
            out.write(pin.toString())
            out.close()
        } catch (e: IOException) {
            println("Error: " + e.message)
        }
    }

    /**
     * Disable access to GPIO.
     * @param pin GPIO pin to disable access.
     */
    fun unexportPin() {
        println("unExporting Ping")
        try {
            val out = BufferedWriter(FileWriter("$PATH/unexport", false))
            out.write(pin.toString())
            out.close()
        } catch (e: IOException) {
            println("Error: " + e.message)
        }
    }

    companion object {
        const val HIGH = 1
        const val LOW = 0
        private const val PATH = "/sys/class/gpio"
    }

    /**
     * Set desirable pin for the GPIO class.
     */
    init {
        println("Initializing pin $pin")
        this.pin = pin
    }
}

来源 GpioProcessor.kt

package com.example.myapplication

import java.io.BufferedWriter
import java.io.FileWriter
import java.io.IOException
import java.util.*

/**
 * Created by Ara on 7/21/15.
 * From https://www.instructables.com/id/DragonBoard-How-to-Access-GPIOs-Using-Java/
 *   Java source from the article was converted to Kotlin using Android Studio.
 *
 * See as well https://github.com/IOT-410c/DragonBoard410c_GpioLibrary
 *
 * Simple example main()
 *
 * public class Main {
 *
 * public static void main(String[] args) {
 * int count = 0;
 * int buttonValue = 0;
 *
 * GpioProcessor gpioProcessor = new GpioProcessor();
 *
 * // Get reference of GPIO27 and GPIO29.
 *
 * Gpio gpioPin27 = gpioProcessor.getPin27();
 * Gpio gpioPin29 = gpioProcessor.getPin29();
 *
 * // Set GPIO27 as output.Set GPIO29 as input.
 * gpioPin27.pinOut();
 * gpioPin29.pinIn();
 *
 * while(count<20){
 * count++;
 * // Read value of GPIO29.
 * buttonValue=gpioPin29.getValue();
 *
 * if(buttonValue == 0){
 * // Set GPIO27 as low level.
 * gpioPin27.pinLow();
 * } else{
 * // Set GPIO27 as high level.
 * gpioPin27.pinHigh();
 * }
 *
 * try {
 * Thread.sleep(1000);
 * } catch(InterruptedException e){
 * // TODO Auto-generated catch block
 * e.printStackTrace();
 * }
 * }
 *
 * // Disable access GPIO27 and GPIO29.
 * gpioProcessor.closePins();
 * }
 * }
 */ /*
 This class abstracts the use of the gpio pins. This class can be utilized on any linux operating
 system that has gpio pins defined in the /sys/class/gpio directory. It is required that the gpio
 pins themselves are available for access by the user of this application, and may require a
 change of permissions.
 */
class GpioProcessor {
    private val PATH = "/sys/class/gpio"
    private val pins: MutableList<Int> = ArrayList()

    // mapping of physical pin number to GPIO file number.
    // the mapping varies depending on the operating system
    private val  androidPin23 = 938
    private val  androidPin24 = 914
    private val  androidPin25 = 915
    private val  androidPin26 = 971
    private val  androidPin27 = 1017
    private val  androidPin28 = 901   // GPIO pin borrowed from MPP. supports PWM. support analog I/O.
    private val  androidPin29 = 926   // (input only)
    private val  androidPin30 = 927
    private val  androidPin31 = 937
    private val  androidPin32 = 936
    private val  androidPin33 = 930
    private val  androidPin34 = 935

    private val  linuxPin23 = 36
    private val  linuxPin24 = 12
    private val  linuxPin25 = 13
    private val  linuxPin26 = 69
    private val  linuxPin27 = 115
    private val  linuxPin28 = 4     // GPIO pin borrowed from MPP. supports PWM. support analog I/O.
    private val  linuxPin29 = 24    // (input only)
    private val  linuxPin30 = 25
    private val  linuxPin31 = 35
    private val  linuxPin32 = 34
    private val  linuxPin33 = 28
    private val  linuxPin34 = 33

    private val  physicalPin23 = androidPin23
    private val  physicalPin24 = androidPin24
    private val  physicalPin25 = androidPin25
    private val  physicalPin26 = androidPin26
    private val  physicalPin27 = androidPin27
    private val  physicalPin28 = androidPin28    // GPIO pin borrowed from MPP. supports PWM. support analog I/O.
    private val  physicalPin29 = androidPin29    // (input only)
    private val  physicalPin30 = androidPin30
    private val  physicalPin31 = androidPin31
    private val  physicalPin32 = androidPin32
    private val  physicalPin33 = androidPin33
    private val  physicalPin34 = androidPin34

    /**
     * Get function of specific pin.
     * @param pin Desirable pin.
     */
    fun getPin(pin: Int): Gpio {
        exportPin(pin)
        pins.add(pin)
        return Gpio(pin)
    }

    /**
     * Get pin 23;
     * @returns {Gpio}
     */
    val pin23: Gpio
        get() = getPin(physicalPin23)

    /**
     * Get pin 24.
     * @returns {Gpio}
     */
    val pin24: Gpio
        get() = getPin(physicalPin24)

    /**
     * Get pin 25.
     * @returns {Gpio}
     */
    val pin25: Gpio
        get() = getPin(physicalPin25)

    /**
     * Get pin 26.
     * @returns {Gpio}
     */
    val pin26: Gpio
        get() = getPin(physicalPin26)

    /**
     * Get pin 27.
     * @returns {Gpio}
     */
    val pin27: Gpio
        get() = getPin(physicalPin27)

    /**
     * Get pin 28.
     * @returns {Gpio}
     */
    val pin28: Gpio
        get() = getPin(physicalPin28)

    /**
     * Get pin 29.
     * @returns {Gpio}
     */
    val pin29: Gpio
        get() = getPin(physicalPin29)

    /**
     * Get pin 30.
     * @returns {Gpio}
     */
    val pin30: Gpio
        get() = getPin(physicalPin30)

    /**
     * Get pin 31.
     * @returns {Gpio}
     */
    val pin31: Gpio
        get() = getPin(physicalPin31)

    /**
     * Get pin 32.
     * @returns {Gpio}
     */
    val pin32: Gpio
        get() = getPin(physicalPin32)

    /**
     * Get pin 33.
     * @returns {Gpio}
     */
    val pin33: Gpio
        get() = getPin(physicalPin33)

    /**
     * Get pin 34.
     * @returns {Gpio}
     */
    val pin34: Gpio
        get() = getPin(physicalPin34)

    /**
     * Get all GPIO's pins.
     * @return List of pins.
     */
    val allPins: Array<Gpio?>
        get() {
            val allPins = arrayOfNulls<Gpio>(12)   // android       linux
            allPins[0] = pin23                          // GPIO 938     GPIO 36
            allPins[1] = pin24                          // GPIO 914     GPIO 12
            allPins[2] = pin25                          // GPIO 915     GPIO 13
            allPins[3] = pin26                          // GPIO 971     GPIO 69
            allPins[4] = pin27                          // GPIO 1017    GPIO 115
            allPins[5] = pin28                          // Reserved
            allPins[6] = pin29                          // GPIO 926     GPIO 24 (input only)
            allPins[7] = pin30                          // GPIO 927     GPIO 25
            allPins[8] = pin31                          // GPIO 937     GPIO 35
            allPins[9] = pin32                          // GPIO 936     GPIO 34
            allPins[10] = pin33                         // GPIO 930     GPIO 28
            allPins[11] = pin34                         // GPIO 935     GPIO 33
            return allPins
        }

    /**
     * Enable access to GPIO.
     * @param pin GPIO pin to access.
     */
    private fun exportPin(pin: Int) {
        println("Exporting Ping")
        try {
            val out = BufferedWriter(FileWriter("$PATH/export", false))
            out.write(pin.toString())
            out.close()
        } catch (e: IOException) {
            println("Error: " + e.message)
        }
    }

    /**
     * Disable access to GPIO.
     * @param pin GPIO pin to disable access.
     */
    private fun unexportPin(pin: Int) {
        println("unExporting Ping")
        try {
            val out = BufferedWriter(FileWriter("$PATH/unexport", false))
            out.write(pin.toString())
            out.close()
        } catch (e: IOException) {
            println("Error: " + e.message)
        }
    }

    fun closePins() {
        for (pin in pins) {
            unexportPin(pin)
        }
        pins.clear()
    }

    companion object {
        const val TAG = "GpioProcessor"
    }
}

使用 GpioProcessor 的示例源 class

我通过将按钮按下链接到侦听器,在片段内的 Android 应用程序中使用了 GPIO sysfs 接口库。我有两个按钮,一个通过将引脚驱动为高电平来打开 LED,第二个通过将引脚驱动为低电平来关闭 LED。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    view.findViewById<Button>(R.id.button_second).setOnClickListener {
        findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment)
    }

    val txtScroll = view.findViewById(R.id.LedStatus) as TextView

    // find the button whose id is button_Location and then set an listener for
    // any clicks on that button. In the following listener we are going to have
    // the "Location" button, defined in the file fragment_first.xml, generate a
    // list of the GPS service providers by creatinga LocationManager object to
    // generate a list.
        val gpioProcessor_x =  GpioProcessor()
        // Get reference of GPIO23.
        val gpioPin23_x = gpioProcessor_x.pin23
        gpioPin23_x.exportPin()

    view.findViewById<Button>(R.id.button_led_off).setOnClickListener {
        val gpioProcessor =  GpioProcessor()
        // Get reference of GPIO27.
        val gpioPin23 = gpioProcessor.pin23

        // Set GPIO23 as output.
        gpioPin23.pinOut()
        gpioPin23.pinLow()    // drive pin low to turn off LED.
        txtScroll.append("LED Off\n")
    }

    view.findViewById<Button>(R.id.button_led_on).setOnClickListener {
        val gpioProcessor =  GpioProcessor()
        // Get reference of GPIO27.
        val gpioPin23 = gpioProcessor.pin23

        // Set GPIO23 as output.
        gpioPin23.pinOut()
        gpioPin23.pinHigh()    // drive pin high to turn on LED
        txtScroll.append("LED On\n")
    }
}