访问 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 访问低功率连接器的引脚。
注释和资源
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 的一些文档。除了我使用的两个属性 direction
和 value
之外,还有其他几个属性,例如 edge
和 active_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")
}
}
我正在使用最近购买的 DragonBoard 410C 运行 Android 5.1 操作系统,并使用 Android Studio 和 Kotlin 生成一个示例应用程序来探索一些硬件例如 40 针低功率连接器。
我的问题是如何使用 Kotlin 和 Android Studio 通过 GPIO 引脚访问 40 引脚低功耗连接器。
根据我目前的研究,mraa 库似乎是通往成功的道路,但是我一直无法找到任何关于将库与 Kotlin 一起使用的文档。
如何开始使用带有 Kotlin 的 mraa 库来访问 40 针低功耗连接器?
或者有不同的方法吗?
我的第一个示例是 LED 应用程序的简单闪烁,但是我不知道如何使用 Kotlin 访问低功率连接器的引脚。
注释和资源
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
usinggetGpioList()
:
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 的一些文档。除了我使用的两个属性 direction
和 value
之外,还有其他几个属性,例如 edge
和 active_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")
}
}