歡迎加入QQ討論群258996829

與設(shè)備的GPIO接口進(jìn)行交互的庫SwiftyGPIO

發(fā)布時(shí)間:2017-04-25 10:22  回復(fù):0  查看:4822  感興趣:12  贊:0   最后回復(fù):2017-04-25 10:22  

原標(biāo)題:SwiftyGPIO:詳解如何在開發(fā)板上使用 Swift

譯者:張新慧,技術(shù)之路,共同進(jìn)步,有優(yōu)質(zhì)移動開發(fā)、VR/AR/MR、物聯(lián)網(wǎng)原創(chuàng)技術(shù)文章歡迎發(fā)送郵件至 mobilehub@csdn.net。

SwiftyGPIO 是一個(gè)能夠通過 Swift 語言去控制基于 Linux 主板(比如:C.H.I.P. 和樹莓派)GPIO(General Purpose Input Output,通用型之輸入輸出,為嵌入式開發(fā)的概念,對應(yīng)嵌入式設(shè)備的物理接口)以完成簡單的工控功能(比如 LED 燈的顯示)的開源庫,簡單而言就是,這個(gè)庫能夠讓 Swift 與設(shè)備的 GPIO 進(jìn)行交互,從而實(shí)現(xiàn)開關(guān)功能。該項(xiàng)目基于 MIT 協(xié)議開源,其主要貢獻(xiàn)者為軟件工程師 Umberto Raimondi。

與設(shè)備的GPIO接口進(jìn)行交互的庫SwiftyGPIO

安裝

想要使用 SwiftyGPIO 庫,首先需要具備一個(gè)支持 Swift 3 及其以上版本的 Linux ARM(ARMv7 或 ARMv6)。如果你有一個(gè)基于 Ubuntu 或 Raspbian 操作系統(tǒng)的樹莓派(A、B、A+、B+、Zero、ZeroW、2、3),從這里獲取 Swift 3.0.2 或按文章介紹以及鏈接的腳本庫來進(jìn)行構(gòu)建。

如果你的 Swift 版本支持 SPM,只需將 SwiftyGPIO 作為依賴(dependency)添加到 Package.swift 中:

let package = Package(
    name: "MyProject",
    dependencies: [
        .Package(url: "https://github.com/uraimo/SwiftyGPIO.git", majorVersion: 0),
        ...
    ]
    ...
)

然后執(zhí)行 swift build 命令,編譯器會在 .build/ 下創(chuàng)建一個(gè)可執(zhí)行文件。

如果你的 Swift 版本不支持 Swift Package Manager,就手動下載所有所需文件:

wget https://raw.githubusercontent.com/uraimo/SwiftyGPIO/master/Sources/SwiftyGPIO.swift https://raw.githubusercontent.com/uraimo/SwiftyGPIO/master/Sources/Presets.swift https://raw.githubusercontent.com/uraimo/SwiftyGPIO/master/Sources/SunXi.swift

下載完畢,在同一目錄下創(chuàng)建一個(gè)包含應(yīng)用代碼的附加文件,命名為 main.swift。當(dāng)代碼就緒,就用 swiftc *.swift 來編譯,編譯器會創(chuàng)建一個(gè) main 可執(zhí)行文件。

注意:與 GPIO 交互時(shí),不論是通過 sysfs 還是 mmapped 寄存器,如果操作系統(tǒng)沒有預(yù)定義的用戶組來獲取這些函數(shù),就需要用 sudo ./main 動用 root 權(quán)限來運(yùn)行應(yīng)用。如果 RaspberryPi 使用 2016 年 11 月更新的最新 Raspbian 或者 16.04 Xenial 及以上的 Ubuntu,只需要./main 來啟動應(yīng)用?;旌舷到y(tǒng)的話,listeners(監(jiān)聽器)必須要求 root 權(quán)限。

替代方案是,具體用戶組獲取 GPIO 可手動設(shè)置(參考12)。遵照這些指南之后,別忘了用sudo usermod -aG gpio pi將用戶(比如 RaspberryPi)添加至 gpio 組,重啟以應(yīng)用更改的內(nèi)容。

小試牛刀:閃爍的 LED 燈和傳感器

有 Swfit 3.0 和最新 SwiftyGPIO 的話,Cameron Perry 的教程會手把手教你在 Raspberry Pi 進(jìn)行 Swift 設(shè)置,還教你怎么用 LED 燈和溫度傳感器。

若只有 Swift 2.x,比較實(shí)在的 SwiftyGPIO(來自于具體的 2.x 分支)使用教程可以搜 Joe(來自于獨(dú)立軟件開發(fā)者聯(lián)盟 iachievedit),他的教程很不錯(cuò),且有多個(gè)語言版本,中文版可點(diǎn)擊這里閱覽。

如何使用

SwiftyGPIO 可與 GPIO、SPI(若無可用位拆裂 VirtualSPI 替代)和 PWM,現(xiàn)在就逐一看看怎么使用它們吧。

GPIO

假設(shè)使用的是 Raspberry 2 開發(fā)板,GPIO pin(針腳) P2(電阻 1K Ohm 左右)和 GND 間連接 LED 燈。我們的目標(biāo)是讓燈亮起來。

首先要檢索開發(fā)板上可用的 GPIO,想修改哪個(gè)就設(shè)置一個(gè) reference:

let gpios = SwiftyGPIO.GPIOs(for:.RaspberryPi2)
var gp = gpios[.P2]!

以下是預(yù)定義開發(fā)板估計(jì)的值:

  • .RaspberryPiRev1 (Pi A,B Revision 1, 2012 年之前, 26 pin header)
  • .RaspberryPiRev2 (Pi A,B Revision 2, 2012 年之后, 26 pin header)
  • .RaspberryPiPlusZero (Raspberry Pi A+ 和 B+, Raspberry Zero, 均為 40 pin header)
  • .RaspberryPi2 (Raspberry Pi 2 或 3,40 pin header)
  • .BeagleBoneBlack (BeagleBone Black)
  • .CHIP (C.H.I.P.電腦,9 美元售價(jià)).
  • .BananaPi (RaspberryPi 的雙胞胎)
  • .OrangePi

GPIOs(for:)返回的地圖包括不同具體開發(fā)板所有 GPIO,如圖解所示。

另外一種方案是,如果開發(fā)板不受支持,可使用開發(fā)板的 SysFS GPIO Id 來手動安裝所有 GPIO 對象:

var gp = GPIO(name: "P2",id: 2)  // User defined name and GPIO Id

用戶已定義名稱以及 GPIO Id。

下一步是設(shè)置接口 direction,GPIODirection.IN 或 GPIODirection.OUT 均可,此處選擇后者:

gp.direction = .OUT
然后將 pin 值改為“1”——上升(HIGH):
gp.value = 1

LED 燈就亮了。

現(xiàn)在假設(shè)開關(guān)連接至 P2,讀取經(jīng)過 P2 接口的值,direction 必須設(shè)置為.IN,值可從 value property(值屬性)讀取。

gp.direction = .IN
let current = gp.value

GPIO 對象上其他屬性(如 edge 和 active low)屬于 GPIO 附加屬性,可自行設(shè)置,但不是必選。詳細(xì)描述參照內(nèi)核文件。

當(dāng) pin 值變化時(shí),GPIO 也支持閉包。添加閉包的指令有 onRaising(pin 值從 0 到 1)、onFalling(pin 值從 1 到 0)和onChange(只要 pin 值發(fā)生改變):

let gpios = SwiftyGPIO.GPIOs(for:.RaspberryPi2)var gp = gpios[.P2]!

gp.onRaising{
    gpio in
    print("Transition to 1, current value:" + String(gpio.value))
}
gp.onFalling{
    gpio in
    print("Transition to 0, current value:" + String(gpio.value))
}
gp.onChange{
    gpio in
    gpio.clearListeners()
    print("The value changed, current value:" + String(gpio.value))
}  

閉包接受的唯一參數(shù)是已更新的 GPIO 對象的 reference,因此無需使用外部變量。調(diào)用 clearListeners()移除所有負(fù)責(zé)監(jiān)聽的閉包并禁用 the changes handler。GPIO 檢查更新期間,pin direction 無法更改(設(shè)置為.IN)。但 listeners 全部移除后,不論在閉包內(nèi)部或其他位置,都可以自由更改。

SPI

若開發(fā)板上 SwiftyGPIO 已預(yù)設(shè) SPI 連接,就能在預(yù)定義開發(fā)板上調(diào)用 hardwareSPIs(for:)檢索可用 SPI(Swift 2.x 則調(diào)用 getHardwareSPIsForBoard)。

RaspberryPi 和其他開發(fā)板上,硬件 SPI SysFS 界面非默認(rèn)啟動。wiki 上有設(shè)置向?qū)Э晒﹨㈤啞?

再比如使用 RaspberryPi 2,其雙向 SPI 由 SwiftyGPIO 作為單項(xiàng) SPIObjects 來管理:

let spis = SwiftyGPIO.hardwareSPIs(for:.RaspberryPi2)
var spi = spis?[0]

第一個(gè)返回項(xiàng)是輸出信道,可在 SPIObject 上調(diào)用 isOut 方法來驗(yàn)證。

或者,可以用兩個(gè) GPIO 創(chuàng)建一個(gè)軟件 SPI。一個(gè) GPIO 作為 clock pin,另外一個(gè)用來發(fā)送數(shù)據(jù)。這種位拆裂 SPI 比硬件 SPI 慢,能不用就不用。

創(chuàng)建一個(gè)軟件 SPI,檢索兩個(gè) pin,創(chuàng)建一個(gè) VirtualSPI 對象。

let gpios = SwiftyGPIO.GPIOs(for:.RaspberryPi2)
var sclk = gpios[.P2]!
var dnmosi = gpios[.P3]!
var spi = VirtualSPI(dataGPIO:dnmosi,clockGPIO:sclk) 

兩個(gè)對象遵守同一 SPIObject 協(xié)議,因此提供同一方法。要區(qū)分硬件和軟件 SPIObject,要用 isHardware 方法。

在 SPI 上發(fā)送 1 字節(jié)及以上,使用 sendData 方法。最簡單的形式下,它只需一列 UInt8 作為參數(shù):

spi?.sendData([UInt(42)])
但對于軟件 SPI(硬件 SPI 忽略這些值),可自己決定字節(jié)順序(MSB、LSB —— 最高/最低有效位)和連續(xù)兩字節(jié)之間的延時(shí)(clock width,默認(rèn)值為 0):
spi?.sendData([UInt(42)], order:.LSBFIRST, clockDelayUsec:1000)

PWM

PWM 輸出信號可驅(qū)動伺服電機(jī)、RGB LED 燈及其他設(shè)備,或者只有數(shù)字 GPIO 端口時(shí),粗略估計(jì) analog 輸出值。

如果開發(fā)板有 PWM 端口且支持 SwiftyGPIO(RaspberryPi 開發(fā)板),用 hardwarePWMs 工廠方法來檢索可用的 PWMOutput 對象。

let pwms = SwiftyGPIO.hardwarePWMs(for:.RaspberryPi2)!
let pwm = (pwms[0]?[.P18])!

該方法返回所有支持 PWM 功能端口,由控制它們的 PWM 頻道進(jìn)行分類。

每個(gè)頻道只能使用一個(gè)端口,而 Raspberries 有兩個(gè)頻道,可以同時(shí)使用兩個(gè) PWM 輸出,比如 GPIO12 和 GPIO13 或 GPIO18 和 GPIO19。

計(jì)劃使用哪個(gè)端口,且檢索到 PWMOutput 時(shí),需要初始化來選擇 PWM 功能。在此類開發(fā)板上,端口往往不止一個(gè)功能(簡單的 GPIO、SPI、PWM 等),可以隨心選擇來配置專用的寄存器。

pwm.initPWM()
調(diào)用 startPWM 來開始 PWM 信號,該指令時(shí)長以納秒計(jì)(如有頻率,轉(zhuǎn)換為 1/frequency),工作周期以百分比計(jì)。
print("PWM from GPIO18 with 500ns period and 50% duty cycle")
pwm.startPWM(period: 500, duty: 50)

一旦調(diào)用此方法,ARM SoC 的 PWM 子系統(tǒng)會開始生成信號,無需干預(yù),程序會自己執(zhí)行;如果想等就插入休眠(sleep,以秒計(jì))指令。

調(diào)用 stopPWM()方法來停止 PWM 信號:

pwm.stopPWM()

若想改變信號,不需要停止之前發(fā)出的,只需要用不同參數(shù)調(diào)用 startPWM。

這一特征運(yùn)用 M/S 算法,已經(jīng)過 300ns(毫微秒)-200ms(毫秒)區(qū)間內(nèi)的信號測試,在此區(qū)間外生成信號可能導(dǎo)致大量抖動,一些應(yīng)用是受不了的。如果手頭有示波器,且想要在該區(qū)間兩頭生成信號,就要不間斷確認(rèn)生成的信號正常。

舉例

不同開發(fā)板的例子在 Examples 目錄下面,可以挑一些做改良實(shí)驗(yàn)。

接下來這個(gè)例子是在 C.H.I.P.開發(fā)板上運(yùn)行的,標(biāo)出了所有 GPIO0 屬性當(dāng)下的值,改了 direction 和值,然后顯示在屬性上:

let gpios = SwiftyGPIO.GPIOs(for:.CHIP)
var gp0 = gpios[.P0]!
print("Current Status")
print("Direction: "+gp0.direction.rawValue)
print("Edge: "+gp0.edge.rawValue)
print("Active Low: "+String(gp0.activeLow))
print("Value: "+String(gp0.value))

gp0.direction = .OUT
gp0.value = 1

print("New Status")
print("Direction: "+gp0.direction.rawValue)
print("Edge: "+gp0.edge.rawValue)
print("Active Low: "+String(gp0.activeLow))
print("Value: "+String(gp0.value))
第二個(gè)例子可以使 LED 燈以 150ms 的頻率閃動:
import Glibc
let gpios = SwiftyGPIO.GPIOs(for:.CHIP)var gp0 = gpios[.P0]!
gp0.direction = .OUT
repeat{
    gp0.value = (gp0.value == 0) ? 1 : 0
    usleep(150*1000)
}while(true) 
雖然不能用 CHIP 測試硬件 SPI,但 SwiftyGPIO 也提供了 SPI 界面的位拆裂軟件執(zhí)行,只要兩個(gè) GPIO 來初始化即可:
let gpios = SwiftyGPIO.GPIOs(for:.CHIP)
var sclk = gpios[.P0]!
var dnmosi = gpios[.P1]!

var spi = VirtualSPI(dataGPIO:dnmosi,clockGPIO:sclk) 

pi.sendData([UInt8(truncatingBitPattern:0x9F)]) 

注意:我們使用構(gòu)造函數(shù) UInt8(truncatingBitPattern:)來轉(zhuǎn)換 0x9F Int,雖然這里并非必選項(xiàng),但對于用戶提供(user-provided)或計(jì)算(calculated)整數(shù)很推薦,因?yàn)?Swift 不支持隱式截?cái)噢D(zhuǎn)化為更小整數(shù)型,如果想轉(zhuǎn)化的 Int 不合適 UInt8,Swift 會崩潰。

相關(guān)庫


相關(guān)開源代碼

您還未登錄,請先登錄

熱門帖子

最新帖子

?