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



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

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

从我迄今为止的研究来看,mraa库似乎是通往成功的道路,但我一直找不到任何关于与Kotlin一起使用该库的文档。

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

还是有不同的方法?

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

票据和资源

mraa文档页面

Libmraa是一个绑定到Python、Javascript和Java与Galileo、Edison&其他平台,具有结构合理的API,其中端口名称/编号与使用libmraa不会将您与特定的在运行时完成板检测的硬件,您可以创建便携式将在支持的平台上工作的代码。

mraa GitHub存储库的upm库

UPM存储库为各种常用的传感器和致动器。这些软件驱动程序相互作用与底层硬件平台(或微控制器),以及通过对MRAA API的调用。

哪个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死了!libgpiod万岁!(适用于linux和CircuitPython的libgpiod)

这基本上是将取代我们的Python DHT驱动程序的代码,并且具有与任何其他Linux板向前兼容的优点运行4.8以上内核。我们将慢慢取代其他CircuitPython使用libgpiod的代码,这样我们就可以对树莓派、BeagleBone或洋葱上的CircuitPython。

没有太多的libgpiod代码,libgpiod也没有在Linux发行版上上市,这可能是它需要一点时间的原因同时继续。有C和Python的绑定。这是一个剧本它可以帮助您开始为自己编译https://github.com/adafruit/Raspberry-Pi-Installer-Scripts/blob/master/libgpiod.sh

GitHub上的DragonBoard 410C GPIO库https://github.com/IOT-410c/DragonBoard410c_GpioLibrary它是用Java编写的/sys/class/gpio";Linux访问GPIO引脚的方法。这看起来像是一组用于物联网Coursera课程的存储库中的一个存储库,其中一些使用DragonBoard 410C。

Linux内核:传统GPIO接口

这提供了Linux上GPIO访问约定的概述。

这些调用使用gpio_*命名前缀。不应使用其他呼叫该前缀或相关的_gpio*前缀。

Android Studio和adb

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

此外,还有Android平台工具,它们是单独下载的。adbshell应用程序就是这些工具的一部分。这些工具可以从SDK平台工具发布说明下载进行安装。选择操作系统(Windows、Linux、MacOS)所需的特定版本。

安卓系统

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

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

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

安卓开发者>文档>安卓设备>辅助线>GPIO-

为了打开到GPIO端口的连接,您需要知道唯一的端口名。在开发的初始阶段,或将应用程序移植到新硬件上,发现所有使用getGpioList():的PeripheralManager的可用端口名

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

另请参阅以下stackoverflow的帖子,这些帖子对这个主题有一些看法。请参阅stackoverflow中的标签[android things]。

GpioCallback如何注册";false";连续两次?

Android Things Pin命名约定

PeripheralManagerService抛出NoClassDefFoundError

在查看了许多替代方案后,访问运行Android 5.1的DragonBoard 410C的GPIO引脚的最简单方法似乎是使用传统的sysfs特殊设备文件方法。

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

我在CodeProject.com上写了一篇文章,提供了有关开发此解决方案的详细信息。请参阅使用Windows 10与DragonBoard 410C和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接口

请参阅StackOverFlow关于Linux伪文件和特殊设备文件以及GPIO引脚的传统sysfs接口的文章,/sys/class/GPIO/export和'sys/class/GPIO/unport机制是什么,底层sysfs功能是什么。

我发现了一个用Java编写的简单GPIO库,它提供了必要的源代码。安卓工作室有一个将Java转换为Kotlin的工具,我将其包含在我的项目中。源代码位于Gpio.kt和GpioProcessor.kt.文件中

然而,为了实现这一点,我不得不更改我的DragonBoard启动脚本,以确保创建了必要的特殊设备文件,并具有适当的权限,允许用户程序操作GPIO引脚。

以下程序来自Coursera课程物联网:从设备感知和驱动,第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.1Linux使用不同的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">…读作"in"或"out"。此值通常可能被书写。写入为"out"默认将值初始化为低的为确保无故障运行,值"低"one_answers"高"可能为被写入以将GPIO配置为具有该初始值的输出。

请注意,如果内核不支持,则此属性将不存在更改GPIO的方向,或者由内核代码导出没有明确允许用户空间重新配置这个GPIO方向

"value">…读取为0(低)或1(高)。如果GPIO该值被配置为输出,可以被写入;任何非零值被视为高。

如果引脚可以配置为产生中断的中断,并且它已配置为生成中断(请参阅"edge"),则可以对该文件进行轮询(2),并且每当中断被触发。如果使用轮询(2),请设置事件POLLPRI和POLLERR。如果使用select(2),请在中设置文件描述符例外。轮询(2)返回后,lseek(2)到打开sysfs文件并读取新值,或者关闭该文件并重新打开它以读取该值。

"edge">…读作"none"、"rising"、"falling"或"both"。写这些字符串选择将进行轮询(2)的信号边缘返回"value"文件。

只有当引脚可以配置为中断时,此文件才存在生成输入引脚。

"active_low">…读取为0(false)或1(true)。写任意非零值以反转读取和写通过"上升"one_answers"下降"边缘的边缘属性将遵循此背景

Kotlin使用sysfs的源代码

我用来探索将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类的示例源

我在一个片段中使用了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 Offn")
}
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 Onn")
}
}

最新更新