【Nano Framework ESP32篇】WS2812 彩色燈帶實驗

来源:https://www.cnblogs.com/tcjiaan/p/18138995
-Advertisement-
Play Games

地球人皆知,許多物聯網教程作者的心中都深愛著一燈大師,所以第一個常式總喜歡點燈,高級一點的會來個“一閃一閃亮晶晶”。老周今天要扯的也是和燈有關的,但不單純地點個燈,那樣實在不好玩,缺乏樂趣。老周打算舞個龍燈,哦不,是用 LED 彩色燈帶給伙伴們整點炫酷樂子。 說到這LED彩燈,咱們常見到的有兩類: ...


地球人皆知,許多物聯網教程作者的心中都深愛著一燈大師,所以第一個常式總喜歡點燈,高級一點的會來個“一閃一閃亮晶晶”。老周今天要扯的也是和燈有關的,但不單純地點個燈,那樣實在不好玩,缺乏樂趣。老周打算舞個龍燈,哦不,是用 LED 彩色燈帶給伙伴們整點炫酷樂子。

說到這LED彩燈,咱們常見到的有兩類:

1、一捲一捲的燈帶,燈帶是軟的,底部有背膠,可以隨意貼(貼電腦機箱,貼儲物櫃,貼手辦展示盒……);

2、點陣屏,其實跟燈帶一個樣,只是有個框架,做成矩陣。如 3 * 3、4 * 4 等。

驅動 IC 一般是 WS2812,此貨體積甚小,便於寄生於每個燈珠內。所以,每個LED燈珠都可以單獨控制。而且 WS2812 允許你把燈珠串聯起來,發送給它的數據可以連續設置多個燈珠。顏色由 RGB 控制,即 24 位——設置燈珠顏色要向 WS2812 發送3個位元組的數據。

其實,WS2812的驅動協議不那麼複雜,隨便查查資料就能懂的了。當然,Nano Framework 已經有封裝好的驅動,咱們不需要自己寫協議。對於 ESP32,有兩種驅動方案:

1、SPI 方式,此法各種開發板通用。曾記否?老周寫過在樹莓派生用 SPI 驅動 WS2812 的水文;

2、RMT 方式,許多 ESP32 模組都支持RMT,可以用它來驅動 WS2812。RMT 說白了就是用來發送和接收紅外編碼的,比如,電視遙控器、電動馬桶遙控器等(空調遙控器好像特殊一些,很多模塊都解碼不了)。RMT 由於可以在同一周期內設定高、低電平的持續時間,使得它也能用於驅動 WS2812。

實際上,PWM 也可以的,因為一個周期內的占空比也能設定高、低電平的持續時間。只是,在 PWM 連續發生時要頻繁地更改占空比,對開發板的速度有要求,還要確保代碼執行得夠快。Nano Fw 畢竟是封裝過的,性能上會有損失的。雖然使用第三方框加會帶來一些性能上的削弱,但在應用層可以提升開發效率,就像寫普通 .NET 程式那樣。兩者總是要有一個平衡的。當然了,還是那句話,如果用其他框架做不到的事情,就必須用官方的 idf 了。性能上肯定比腳本語言高的。

好了,基本理論準備完畢,接下來,咱們開始整活。

先來看看如何用 RMT 來驅動 WS2812 的。因為這個比較新奇,所以先介紹它。紅外編碼協議可能會勸退不少伙伴,但,你不必擔心,畢竟咱們這裡不是真的用紅外通信,只是藉助介面協議來給 WS2812 發數據罷了,不會涉及協議編碼的。而且,Nano Framework 已經封裝好了,用起來很省事,初始化只需要一條 new 語句就完事了。

A、硬體部分,ESP32 模塊你可隨意,只要有引出相關IO口就行,介面少也不要緊的,因為咱們頂多用一兩根線(不含供電)。然後就是RGB燈帶或者點陣屏。其實這兩者本質上一樣。老周這個燈帶就是當初寫樹莓派文章時用的那個,ESP32 也是 3.6 年前買的,放在柜子里吃了幾年塵蟎。現在拿出來給大伙做演示,居然還能用,這質量可以的。

現在新點的開發板很多是 Type-C 介面的,不過老周這個畢竟是幾年前的,是 Micro USB 介面的。啥介面沒關係,可能就是找數據線麻煩些,現在很多手機是 C 口的。不過老周家裡啥線都有,全都是綠聯的。你沒看錯,老周所有數據線和轉換器都是用綠聯的。這不是廣告,就是質量好。老周家裡就是這樣的:風扇是美的,因為電機靜音;線材綠聯的,牆插排插都是公牛的;音響器材鐵三角或漫步者……老周不買小米的,小米其實就跟京東京造差不多,萬能貼牌。老周記得,買硬碟唯一一次翻車的就是京造的。那個硬碟現在搭在樹莓派上,專門放魔法少女動畫片。

又扯遠了,下麵是步驟:

1、啟動 VS;

2、新建 Nano 項目,選 Blank Application 項目模板就行了;

3、打開 Nuget 包管理器(方法自己百度),搜索 ws28xx esp32,然後你會找到一個【nanoFramework.Iot.Device.Ws28xx.Esp32】包。沒錯,它是專為 ESP32 封裝,開箱即用(使用時引入 Iot.Device.Ws28xx.Esp32 命名空間)。

4、安裝 nanoFramework.Iot.Device.Ws28xx.Esp32 包及相關依賴(一般自動安裝);

這裡介紹一下幾個關鍵類。首先是 WS 晶元的公共基類 Ws28xx,在實例化時,咱們可以根據所使用的晶元選擇派生類:Ws2812b 、Ws2812c、Sk6812 等,WS2812B 和 WS2812C 比較常見。至於你買的燈帶是哪個 IC,還真不好說,比如老周這個其實是 WS2812C,可以賣家的商品介紹標註的是 WS2812B。問客服也沒用的,多數是一問搖頭三不知。客服客服,毫不客氣地讓你服得三觀倒置。

這個可以上機測試,如果燈珠點亮後與你設置的顏色不對,可以換個類,比如,用 WS2812b 的結果不正確,可以換成 WS2812c 來試。WS2812b 和 WS2812c 的紅色和綠色好像是反過來的。

接線就簡單了,一根接 5V(沒5V介面就接 3.3,或者單獨供電),數據線只有一根,這裡老周用 0 口,即 GPIO 0。你可以隨便選其他介面,比如官方示例用的 GPIO 15。

public class Program
{
    const int PxCount = 16;     // 有多少個燈珠
    const int DelayMS = 10;     // 延時多少ms
    const int DataPin = 0;      // 使用哪個IO口

    // 入口點
    public static void Main()
    {
        Ws28xx ws28xx = new Ws2812c(DataPin, PxCount);
        BitmapImage bmp = ws28xx.Image;
        //ws28xx.ClockDivider = 2;
        //ws28xx.ResetCommand = new RmtCommand(1800, false, 1850, false);
        //ws28xx.OnePulse = new RmtCommand(35, true, 16, false);
        //ws28xx.ZeroPulse = new(14, true, 34, false);
        //Debug.WriteLine($"分頻:{ws28xx.ClockDivider}");

        int i = default;
        int index1, index2;     // 要設置的燈珠索引
        while (true)
        {
            // 從兩邊向中間靠攏
            for (i = 0; i < PxCount / 2; i++)
            {
                // 每次設置兩顆燈珠
                index1 = i;
                index2 = PxCount - 1 - i;
                bmp.SetPixel(index1, 0, Color.Red);
                bmp.SetPixel(index2, 0, Color.Red);
                ws28xx.Update();    // 更新
                Thread.Sleep(DelayMS);
            }
            // 從中間向兩側擴散
            for (i = PxCount / 2 - 1; i >= 0; i--)
            {
                index1 = i;
                index2 = PxCount - 1 - i;
                bmp.SetPixel(index1, 0, Color.Blue);
                bmp.SetPixel(index2, 0, Color.Blue);
                // 更新
                ws28xx.Update();
                Thread.Sleep(DelayMS);
            }
        }
    }
}

Ws2812c 類的構造函數有三個參數:1、你用的IO號;2、寬度;3、高度。這裡的寬高即燈珠個數,此處老周只點16個燈,多了怕供電不足(其實可以點更多燈珠)。第三個參數預設是1,所以如果高為1可以忽略。這個寬和高啥意思呢?對於燈帶來說,你可以認為它永遠只有一行,但有 N 列(N 是無限大正整數),即 width = N, height = 1。而點陣屏是矩陣,所以用點陣屏就可以設置寬和高。

Ws28xx類公開 Image 屬性,類型為 BitmapImage 類(也是個通用基類)。咱們可以把LED彩燈視作一張點陣圖,每個燈珠就是一個像素。所以,設置某個燈的顏色就要調用 SetPixel 方法,參數是x、y坐標,以及顏色。這個和普通 .NET 程式操作一樣。

這個示例,老周實現的效果是:

1、紅燈的點亮順序是從兩邊往中間靠攏;

2、藍燈的點亮順序是從中間向兩邊擴展。

每當你修改了點陣圖的數據,只是存在記憶體中,只有調用 WsXXX 實例的 Update 方法才會正式將數據發送出去。

這個點燈演算法其實很簡單,咱們學過數學,在等差數列中,1+n=2+(n-1)=3+(n-3)。本例中,16個燈,一半就是8,而索引是從0開始的,所以是7,即全序列的索引是 0 到 15,於是得到:0+15=15,1+14=15,2+13=15,3+12=15 …… 7+8=15。每次迴圈我們就設置相加等於最大索引的那兩個燈。這樣就能實現從中間向兩邊展開,順序反過來就實現從兩邊向中間收攏。

 運行的效果如下圖所示。

細心的大伙伴會發現,上面的代碼中有幾行被註釋掉了。這些代碼用來設置參數的。不過 WS2812x 類會預設為咱們設置,除非發現預設設置的參數不正確時才要修改,

1、設置分頻的分母。

ws28xx.ClockDivider = 2;

ClockDivider 用於設置分頻,預設是2。目前支持的時鐘是APB(外設的高級匯流排),頻率是 80 MHz,分頻2表示除以2=40MHz。也就是一個 Tick 的時間為 1/40000000 = 0.000000025 秒,換算為 0.025 微秒(us)。為什麼分頻不用 80 呢,這樣一 Tick 就是 1 us豈不美哉?因為 WS28XX 的時序很短,比如發送 1 時,高電平持續時間為 0.7 us,低電平持續時間為 0.6 us,總時長在 1.3 us 左右。不過這類IC的時序有很多種版本,時序沒有精確的時長。不管怎麼說,這時間是要精確到 0.01 us的,所以,分頻為80只精確到 1 us 顯然不夠用。分頻為2精確到 0.025 us,基本能對付過去了。

2、設置高、低電平的持續時間。

ws28xx.ResetCommand = new RmtCommand(1800, false, 1850, false);
ws28xx.OnePulse = new RmtCommand(35, true, 16, false);
ws28xx.ZeroPulse = new(14, true, 34, false);

RmtCommand 類的構造函數是這樣的:

RmtCommand(ushort duration1, bool level1, ushort duration2, bool level2)

咱們可以這樣理解:一個周期內有兩個電平,level1 和 level2,如果是true就是高電平否則低電平;duration1 描述 level1 的持續時間,duration2 描述 level2 的持續時間。假設持續時間是 100,如果分頻是80,那麼正好是 1us,可是咱們分頻是2,就變成 100*0.025 us 了。畢竟分頻後會變慢。

 

接下來看看 SPI 實現。

要安裝這幾個 Nuget 包:

1、nanoFramework.Iot.Device.Ws28xx

2、nanoFramework.Hardware.Esp32

3、nanoFramework.System.Device.Spi

其他的因為依賴關係會自動安裝。

這個 iot 庫裡面,沒有 Ws2812c 類,只有 ws2812b 類,但目前測試來看,能正常使用。

SPI 方案主要是用到了 MOSI 介面,雖然在初始化 SPI 匯流排時會設置幾個引腳,但實際上只連接 MOSI 即可。

 // 設置引腳的功能
 Configuration.SetPinFunction(23, DeviceFunction.SPI1_MOSI);
 Configuration.SetPinFunction(19, DeviceFunction.SPI1_MISO);
 Configuration.SetPinFunction(18, DeviceFunction.SPI1_CLOCK);
 // 這裡有兩個參數:第一個是SPI匯流排ID,第二個是片選引腳,-1表示不使用
 SpiConnectionSettings cset = new(1, -1)
 {
     Mode = SpiMode.Mode0,
     ClockFrequency = 2400000,   // 通信頻率
     DataBitLength = 8
 };
 // 初始化SPI設備
 SpiDevice spidev = SpiDevice.Create(cset);

 Ws28xx ws2812 = new Ws2812b(spidev, PxCount);
 // 獲取圖像對象
 var bmp = ws2812.Image;

 int i = default;
 // 進入迴圈
 while (true)
 {
     for(i=0; i<PxCount; i++)
     {
         bmp.SetPixel(i, 0, Color.Blue);
         ws2812.Update();
         Thread.Sleep(DelayMS);
     }
     for(i=PxCount -1 ; i >= 0;i--)
     {
         bmp.SetPixel(i, 0, Color.Red);
         ws2812.Update();
         Thread.Sleep(DelayMS);
     }
     for(i=0; i<PxCount; i++)
     {
         bmp.SetPixel(i,0,Color.Green);
         ws2812.Update();
         Thread.Sleep(DelayMS);
     }
     for(i=PxCount -1 ;i >= 0;i--)
     {
         bmp.SetPixel(i, 0, Color.WhiteSmoke);
         ws2812.Update();
         Thread.Sleep(DelayMS);
     }

 }

SPI 方式稍麻煩一點,用到的IO口有 18、19、23,而燈帶只需連接 23 即可。註意在設置引腳功能時,如果選擇 SPI1_MOSI、SPI1_CLOCK 等值,那說明用的是 SPI_1,在實例化 SpiConnectionSettings 對象時,busid 參數就是 1;如果設置功能時使用的是 SPI2_MOSI、SPI2_MISO、SPI2_CLOCK,那麼實例化 SpiConnectionSettings 時 busid 是 2。雖然 ESP32 有四路 SPI,但前兩路內部保留的,外設只用後兩個,即 HSPI 和 VSPI,這兩個名字也夠奇葩的,其實用起來是一樣。這破名字容易使人誤認為 VSPI 是虛擬SPI,HSPI 是硬體SPI。

咱們在程式代碼中指定的 SPI_1 和 SPI_2 就是 HSPI 和 VSPI。

本示例使用 32 個燈珠,在一輪迴圈中做四次填充:

1、從頭到尾,填充藍色;

2、從尾到頭,填充紅色;

3、從頭到尾,填充綠色;

4、從尾到頭,填充煙白色。

效果如下:

 

好了,今天就水到這裡了。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 試用阿裡雲GPU伺服器進行深度學習模型訓練 最近在用PyTorch時發現在本地訓練模型速度一言難盡,然後發現阿裡雲可以白嫖gpu伺服器,只要沒有申請過PAI-DSW資源的新老用戶都可以申請5000CU*H的免費額度,三個月內有效。 阿裡雲免費試用活動頁面 一、申請試用並創建實例 點擊試用,完成註冊、 ...
  • Problem: 28. 找出字元串中第一個匹配項的下標 目錄解題方法思路構建next數組回溯查找複雜度Code 解題方法 構建next串 回溯查找next串,最後下標 思路 通過最大首碼尾碼能找到下一次未查找到後要回溯的位置 構建next數組 無論如何第一個數的下一次回溯位置肯定是0,因此next ...
  • 背景及問題說明 使用 Kafka client 版本 3.4.0 目前的預設分區策略如下: NOTE this partitioner is deprecated and shouldn't be used. To use default partitioning logic remove part ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 在Web開發中,文件上傳是一個常見的功能需求。Spring框架提供了MultipartFile介面,用於處理文件上傳請求。MultipartFile可以代表一個多部分文件上傳請求中的一個文件,提供了一系列方法用於獲取文件的各種屬性和內容,使得在後端處理文件上傳變得十分方便。下麵我們將介紹Multip ...
  • 本文將重點比較Bokeh和Altair這兩個常用的Python數據可視化庫,探討它們的優缺點以及在不同場景下的適用性。 ...
  • 隨著汽車的普及,車輛信息查詢變得越來越重要。無論是買車、賣車還是維修保養,瞭解車輛的詳細信息是必不可少的。而如何高效快捷地查詢車輛信息成為了很多車主的需求。幸運的是,我們有一個非常實用的介面可以滿足這個需求,而這就是挖數據平臺提供的車輛信息查詢介面。 這個介面的主要功能是通過車架號vin來查詢車輛的 ...
  • 左手編程,右手年華。大家好,我是一點,關註我,帶你走入編程的世界。 公眾號:一點sir,關註領取編程資料 介紹 函數跳轉是要給IDE中非常重要也非常常用的功能,而原生的 Vim 並不提供這個功能,這個確定有點讓人遺憾,按理說這麼常用的功能應該是要提供的。但是沒有關係,有插件可以實現這樣的功能更,藉助 ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...