【.NET 與樹莓派】使用 GPIO 庫
上回老周在說準備工作的時候,提到過樹莓派用金屬盒散熱的事情。有朋友會說,加了金屬盒子接線不方便,就算用了「T」形板,畢竟是把導線延長了的。其實擴展板就是把原有的引腳引出(類似於延長),有的引出後使用並聯電路來「複製」出幾套接口。所以你買一塊插孔多的麵包板,自己也可以並聯出許多擴展接口來,何況樹莓派好一點的擴展板也比較貴。
如果你不運行桌面,不看島國片,只是執行命令和運行代碼,完全不用散熱措施。老周直接用裸板試驗過,只是運行程序的話,溫度平均在 40 度上下,受室溫影響不是很大,夏天的時候會高2度。這個溫度問題不大,掛不了。70 度以上才要考慮散熱。
最簡單的散熱方案是貼散熱片,不過你不要貼歪了,想撕下來再貼一次好像不那麼容易,弄不好可能會把芯片扯壞。曾經還聽說有人把處理器扯下來的,不知道是真是假。另外,加個小風扇的方式應該挺好,有點噪音也沒關係的(不要像轟炸機就行)。
好,現在說正題。我們知道,樹莓派有許多 GPIO 引腳,通用方式是發出,或者接收電平信號。信號就是兩種——高電平 or 低電平。我們可以簡單地認為,高電平是 3.3 V 電壓,低電平是 0V 電壓,更深層的電路原理你可以不理會,不影響你編程。
為什麼是高電平和低電平兩個信號呢?嗯,問得真TM地好。你想一下啊,計算機只認識兩個數字,哪兩個呢?0 和 1,你給計算機下命令時,其實只要 0 和 1 組合就行。1 開燈,0 關燈;1 向左轉,0 向右轉;0 翻牆,1 撞牆;0 引爆火藥,1 引爆自己……
喲西,這麼一來,兩個電平就可以對應兩個二進制數字:高電平—— 1,低電平—— 0,完事。
有了兩個明確的值,還需要明確方向。樹莓派自身把電平拉高或者拉低,以此來告訴連接的電子模塊要幹什麼活,這是發送信號;當一個觸摸開關被你的嬌美的小手觸碰後,模塊把電平拉高(高電平狀態),告訴樹莓派有人按了按鈕,這時候樹莓派的接口就是接收狀態,然後樹莓派可以控制另一個設備去做其他事情(比如,沖馬桶)。
由於樹莓派帶有 Linux 操作系統,所以,GPIO 接口的操作方法很多,可以直接調用相關的 lib,也可以用一些封裝好的庫。不過,系統集成了一種簡單的操作方法,類似於文件的方式,你只需要讀寫對應的路徑就能控制 IO 接口。
啟動系統後,你可以進入以下目錄:
cd /sys/class/gpio
然後執行 ls 命令,你會看到目錄下有兩個文件:
1、export:向這個文件寫入 GPIO 口的編號,就可以打開相關接口;
2、unexport:向這個文件寫入 GPIO 口的編號,關閉對應的接口。
你如果看到這兩個文件,就可以試驗一下了。不過在試之前,你還要做這件事:
sudo raspi-config
sudo 是為了防止權限不夠,raspi-config,是樹莓派系統專用的配置程序,在命令模式下也是圖形化操作的。
選第3項,接口選項,回車。
用上下箭頭鍵移到最下面的 P8,回車。
用 Tab 鍵選中 Yes,默認會選中,然後回車確認,收工。按 Esc 可以退出配置程序(或者選 Finish)。
Remote GPIO 選項必須打開的,不然你連接的電子模塊是沒有反應的。
接下來,老周用這個激光模塊來做演示。
這個模塊上面有一個激光頭,跟我們小時候在路邊買的裝鈕扣電池的差不多,發出的激光比小時候買的玩具還差。那這個模塊有什麼鳥用。其實這個模塊真沒什麼鳥用,不過用來學習演示挺不錯,現實用途可以拿來逗貓,不過注意不要讓貓碰到單片機。
這個模塊有三個引腳,PCB板上只標註了兩側的引腳。
為了提升拍照效果,我在下面墊了一張廁紙。最左邊的引腳旁標註了「-」,意思是接電源負極,即接樹莓派上的 GND 引腳。樹莓派有八個 GND 引腳,你可以隨便接;最右邊的引腳標註了「S」,表示這是信號腳,接樹莓派的 GPIO 腳,這個有很多了,當然了,最好選默認沒有分配功能的 GPIO 接口。比如下圖中畫了藍線的。
左右兩邊的引腳都知道用處了,所以,中間那個引腳就是接電源正極的,可以接樹莓派的 5V,上面的圖上你也看到了,樹莓派兩個 5V 腳都放在了一起(第2、4腳)。
下面開始接線,由於樹莓派沒有在板子印有引腳符號(無絲印),所以在接線時要小心,不要接錯。
信號線老周選用了 GPIO 17,在樹莓派上是 11 號引腳,也就是左邊一排,往下順數第六個腳。
接好線之後,我們回到終端,還記得前面提到的那兩個文件嗎?先用 cd 命令定位到 /sys/class/gpio 目錄,然後向 export 文件輸入文本「17」。
cd /sys/class/gpio echo 17 > export
然後用 ls 列出一下 gpio 下的子項,你是不是發現多了個子目錄?叫 gpio17。
這說明 GPIO 的第17口已經準備就緒,可以通信了。
注意一下,這裡的 GPIO 編號不是樹莓派板子上的順序,而是BCM編號,剛剛接線時接的是左排的第六腳,可以上 pinout.xyz 查看,這個腳的編號是17。
然後,cd gpio17,進入這個目錄,看看裏面有什麼。
這裏面有兩個文件我們需要用到的。
1、direction:配置 GPIO 口的通信方向。向其中寫入「in」為輸入,寫入「out」為輸出。
2、value:GPIO 口輸出的值,0 為低電平,1 為高電平。
激光頭模塊是收到高電平時發射激光的,所以這裡要把通信方向設為 out。
echo out > direction
向 value 寫入「1」,輸出高電平,發射激光,開始逗貓。
echo 1 > value
逗久了貓也覺得不好玩了,你手也累了,這時向 value 寫入「0」,輸出低電平,關閉激光發射。
echo 0 > value
最後,向 /sys/class/gpio/unexport 寫入GPIO口編號 17,就可以關閉這個接口了。這時候,gpio17 子目錄就不見了。
有了上面的試驗,我們可以開始使用微軟封裝的 GPIO 庫了。
這個 Nuget 庫叫 System.Device.Gpio,它已經封裝好了GPIO操作常用的類型。
1、對於Windows平台,用的是 UWP 的庫;
2、對於類 Unix 平台,封裝了以下幾種版本:
1) LibGpio;
2)SysFsDriver,這個就是上面舉例用的方式,以文件方式操作 /sys/class/gpio 路徑下的文件。
3)Raspberry Pi 專版。
另外,還有 HummingBoard 版本。
我們在使用時,其實並不需要去思考使用哪個平台的類型,在實例化 GpioController 類的時候,會自動選用合適的類型。
使用以下命令,在.NET項目中添加 System.Device.gpio 庫的引用。
dotnet add package system.device.gpio
在VS2019 中,你也可以用非常熟悉的 Nuget 包管理器來添加,操作方法省略,老周相信你會用的了。
在代碼中引入 System.Device.Gpio 命名空間,然後就可以歡暢地擼代碼了。
using System; using static System.Threading.Thread; using static System.Console; using System.Device.Gpio; namespace testA { class Program { static void Main(string[] args) { int pin = 17; GpioController gpio = new GpioController(); // 打開接口 gpio.OpenPin(pin); // 設置通信方向 gpio.SetPinMode(pin, PinMode.Output); gpio.Write(pin, PinValue.Low); //先置低電平 // 也可以這樣寫 //gpio.Write(pin, 0); // 發送信號 int x = 20; while (x-- > 0) { WriteLine("打開激光頭"); gpio.Write(pin, 1); Sleep(1000); WriteLine("關閉激光頭"); gpio.Write(pin, 0); Sleep(1000); } // 關閉接口 gpio.ClosePin(pin); gpio.Dispose(); // 退出 WriteLine("3166"); } } }
變量 pin 的值是17,前面接線的時候接的是 GPIO 17 引腳。
dotnet publish -r linux-arm -c release --no-self-contained
-r linux-arm,就是指定目標的運行時為 arm (32)的 Linux 系統。
-c 指定生成方案,有 Debug 和 Release,這個老周就不多解釋了,.NET 程序習慣模式。
–no-self-contained 表示不包含運行時庫,因為老周已在樹莓派的系統上裝了 dotnet 運行時;如果你不想在上面安裝運行時,可以去掉 –on-self-contained 選項,這樣默認會包含運行時庫。上傳到樹莓派上面,執行 chmod u+x xxxx 提升權限,然後就可以直接運行了。
要在樹莓派上安裝.NET 運行時也很簡單。
1、執行 cd /tmp 轉到臨時目錄。
2、下載時可以選擇 .NET runtime,或 ASP.NET Core Runtime。後者包含運行Web應用的庫。使用 wget 下載壓縮包(選擇 arm32 的 Linux 包)。
wget https://download.visualstudio.microsoft.com/download/pr/7aa18d1a-1c9a-4571-9668-3d76b5cda367/41a75d18282fa815c548be4dbc9dd55f/aspnetcore-runtime-5.0.2-linux-arm.tar.gz
3、在 /usr/local 目錄下創建一個目錄,叫 dotnet。
sudo mkdir /usr/local/dotnet
這個地方要加上 sudo ,不然權限不夠。/usr/local 這個目錄很適合,它就是讓我們安裝自己需要的第三方軟件用的。
4、解壓剛剛下載的壓縮包,放到 /usr/local/dotnet 目錄下面。
sudo tar -zxf aspnetcore-runtime-5.0.2-linux-arm.tar.gz -C /usr/local/dotnet
-z 參數表示使用 gz 算法,-x 表示解壓,-f 表示要解壓的文件,三個參數合起來就是 -zxf。注意 -C 參數是大寫的,表示工作目錄,也就是你要解壓到哪個目錄,此處解壓到 /usr/local/dotnet 目錄下。
5、在 /etc/profile 文件中加入以下內容。
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1)) # and Bourne compatible shells (bash(1), ksh(1), ash(1), ...). if [ "`id -u`" -eq 0 ]; then PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" else PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games" fi # .NET 的運行路徑 if [ -d /usr/local/dotnet ]; then PATH=$PATH:/usr/local/dotnet fi export PATH ……
位置是在 PATH 環境變量導出之前,將 dotnet 運行時的路徑追加上去。即在 export 命令之前。保存並退出就OK了。
設置 PATH 變量放在 /etc/profile 中比放在 bash 的資源文件中好一些。放在bash資源中需要你登錄終端時才執行,而且只在當前會話期間可用。放在 /etc/profile 中就成了「全局」了,只要順利登入系統都有效。
在任意路徑下執行 dotnet –info,如果有以下輸出,那就成功了。
好了,現在可以把我們已經發佈的程序上傳到樹莓派了。Win 10 自帶 scp 命令,不需要安裝傳輸工具。
在樹莓派上 cd 到 pi 用戶目錄下(如果你改了用戶名,可能是其他名字,可以用「~」代替)。
cd ~
創建一個新目錄,叫 app
mkdir app
回到 Windows 上,打開命令提示符窗口,cd 到程序發佈的目錄。
cd C:\Users\<用戶名>\...\testA\bin\release\net5.0\linux-arm\publish
執行 scp 命令上傳文件。
scp ** pi@192.168.200.15:/home/pi/app
其中,**(用一個星號也可以,通配符)表示把發佈目錄下的所有文件上傳到樹莓派;冒號後面是樹莓派上的路徑,把文件放到前面創建的 app 目錄下面,注意要用絕對路徑,不能用「~」。
用終端登入樹莓派,cd 到app下,能看到剛上傳的文件。
無比緊張的時刻來臨,執行我們寫好的程序。
dotnet testA.dll
如果沒有出其他問題的話,你會看到激光頭一開一關(其間暫停1秒)。
示例的源代碼可以點這裡下載:拚命點這裡
這個 System.device.gpio 庫還封裝了 i2c、SPI 等常見的硬件通信方式,後續文章中老周還會介紹。