8

nanoFramework 筆記 - ESP32 連接 I2C 裝置

 3 years ago
source link: https://blog.darkthread.net/blog/nanoframework-esp32-i2c-tips/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
ESP32 連接 I2C 裝置-黑暗執行緒

昨天介紹完可以用 C# 寫 ESP32 開發板程式的美妙開源平台 - nanoFramework,我迫不及待想把先前 Arduino C++ 寫的作品移植過來,但光是想接 I2C OLED 顯示器輸出文字就卡住三、四個小時...

其實早有心理準備,nanoFramework 仍在發展初期,用的 NuGet 套件全是 Preview 版,一方面參考文件、討論少,常缺乏現成可用的程式庫;另一方面找到的教學或範例也常因 API 規格修改失效,這是當先鋒部隊一定會面對的挑戰。跟寫 Arduino C++ 相比,過去那種「遇到任何問題,隨手一查就有滿滿的教學範例跟程式庫任你挑」的尊榮體驗不復存在,像是貴公子逃家,開始粗茶淡飯過日子。但是! 能用 C# 寫程式就是爽,付出一些代價也是值得的。

網路上 nanoFramework 整合 I2C 範例不多,用 ESP32 的更少。找到網友三年前寫的 SSD1306 OLED nanoFramework NuGet 程式庫,但因核心版本差異過大無法執行,加上原作者只測過 128x32 解析度的版本,不確定跟手上在用的 128x64 是否有差異。於是改了抓原始碼回來改,參考已在 Arduino C++ 驗證 OK 的 Adafruit 程式庫寫法進行調整,但怎麼改 OLED 顯示器都毫無動靜。搞了很久,直到我回頭寫了小程式去掃 I2C 位址,才發現問題出在 ESP32 偵測不到 I2C 裝置。朝此方向深入研究,這才知道,ESP32 因開發板種類眾多,Pin 腳設定依版本不同,故要參照 nanoFramework.Hardware.Esp32 套件使用 Configuration.SetPinFunction(..., DeviceFunction.I2C_DATA) 重新定義 I2C 腳位(我的板子,I2C1_DATA 是 21、I2C1_CLOCK 是 22),這才測試成功,感動~

thumbnail

整理這次的心得:

  • nanoFramework 處理 I2C 有兩套 NuGet 程式庫,nanoFramework.Windows.Devices.I2cnanoFramework.System.Device.I2c,Windows.Devices.I2c 下載次數多,更新日期新,網路範例幾乎也都是用 Windows.Devices.I2c,但它已被標註為過時(This package has been deprecated),這類 UWP API 將由 .NET IoT API 取代,使用時別西瓜偎大邊,建議改用 System.Device.I2c。
  • 寫 nanoFramework 不像 Arduino C++ 有一堆現成範例、程式庫裝了就可以跑。故測試時請從根本開始做起,例如要接 I2C 設備,建議從偵測 I2C 位址是否存在開始,不要像我誤以為是程式庫與裝罝不相容,查了大半天才發現是漏設 Pin 腳沒設,白花時間。
    附上掃瞄 I2C 位址的程式範例:
      using nanoFramework.Hardware.Esp32;
      using System;
      using System.Collections;
      using System.Device.I2c;
    
      namespace Guineapig.nfEsp32Lib.I2C
      {
          /// <summary>
          /// I2C device address scanner
          /// </summary>
          public class Scanner
          {
              /// <summary>
              /// Scan I2C device addresses
              /// </summary>
              /// <param name="busId">I2C bus id, 1 or 2. default 1</param>
              /// <param name="clockPin">Clock pin</param>
              /// <param name="dataPin">Data pin</param>
              /// <returns></returns>
              public static byte[] Scan(int busId = 1, int clockPin = 22, int dataPin = 21)
              {
                  switch (busId) 
                  {
                      case 1:
                          Configuration.SetPinFunction(clockPin, DeviceFunction.I2C1_CLOCK);
                          Configuration.SetPinFunction(dataPin, DeviceFunction.I2C1_DATA);
                          break;
                      case 2:
                          Configuration.SetPinFunction(clockPin, DeviceFunction.I2C2_CLOCK);
                          Configuration.SetPinFunction(dataPin, DeviceFunction.I2C2_DATA);
                          break;
                      default:
                          throw new ArgumentException($"Unsupported busId - {busId}");
                  }
    
                  ArrayList list = new();
                  SpanByte b = new SpanByte(new byte[1]);
                  // I2C address range from https://learn.adafruit.com/i2c-addresses/the-list
                  for (byte addr = 0x0E; addr <= 0x77; addr++)
                  {
                      using (var dev = I2cDevice.Create(new I2cConnectionSettings(busId, addr)))
                      {
                          var r = dev.Read(b);
                          if (r.Status == I2cTransferStatus.SlaveAddressNotAcknowledged) 
                              continue;
                          if (r.Status == I2cTransferStatus.FullTransfer ||
                              r.Status == I2cTransferStatus.PartialTransfer)
                          {
                              list.Add(addr);
                          }
                      }
                  }
                  return list.ToArray(typeof(byte)) as byte[];
              }
          }
      }
    
    實測如下:(雖然是很簡單的範例,我還是預先切好類別程式庫專案方便未來共用。這才是寫專案的正確姿勢,換回 C# 我終於擺脫老鳥魔咒了)
  • 關於常見的 I2C 位址,Adafruit 有份完整清單可以參考,不愧是 Arduino 界的善心員外。
  • 受限於嵌入式裝置的運算能力及記憶體、儲存空間,加上尚在發展初期,nanoFramework 與完整 .NET 功能有很大的差距,沒有 LINQ 在預期之內,連泛型(如 List<T>)也仍在開發中,未來才會加入,現階段要先用古老的 ArrayList 頂著,甚至也沒有 StringBuilder,有重回 .NET 1.0 的感覺。但老話一句,能用 C# 就是開心。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK