【Nano Framework ESP32篇】使用 LCD 屏幕

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

在開始主題之前,先介紹一個刷固件工具。這個工具在 idf 中是集成的,不過,樂鑫也單獨發佈了這個工具—— esptool。下載鏈接:Releases · espressif/esptool · GitHub。這貨是用 Python 寫的,只是封裝成了 exe,方便直接運行罷了。 在使用時,需要 -p ...


在開始主題之前,先介紹一個刷固件工具。這個工具在 idf 中是集成的,不過,樂鑫也單獨發佈了這個工具—— esptool。下載鏈接:Releases · espressif/esptool · GitHub。這貨是用 Python 寫的,只是封裝成了 exe,方便直接運行罷了。

在使用時,需要 -p 參數指定串口號,如 COM15,-b 指定波特率(可以省略)。下麵咱們嘗試用 flash_id 指令來獲取 ESP32 的 Flash 信息。

esptool -p com13 flash_id

輸出結果如下:

老周有很多塊 esp 開發板,如你所見,這塊板的 flash 是 16MB 的。請記住這個容量,待會刷 nanoCLR 時它會出事故。咱們再看看另一塊板的 flash 信息。

這個是 8MB 的,註意在晶元名稱後有個 revision 參數(修訂號),因為找固件時,要考慮這個參數,3 以上的才能選 rev3 的固件,其他只能選 rev0。

有時候,固件也分有 PSRAM 和 無 PSRAM 的,不過這個一般能通用。

接下來,老周先說明一下如何解決 bootloader 的 Hash 驗證導致的問題。咱們重現一下災難現場:

A、找一個固件,解壓。

B、咱們用上面 16MB 那個來刷。

esptool -p COM9 -b 115200 write_flash -fs 16MB -fm dio -ff 40m 0x1000 "E:\demo\bootloader.bin" 0x8000 "E:\demo\partitions_16mb.bin" 0x10000 "E:\demo\nanoCLR.bin"

write_flash 指令就是刷固件,把文件寫入 Flash。它常用這些選項:

1、-fs:flash大小,如 8MB、16MB;

2、-fm:SPI 模式,如 dio、qio、dout;

3、-ff:通信速率,如 40m(一般就這個值)。

選項之後就是文件列表,列表按照 <偏移地址> <文件路徑>的方式依次列出,比如上面的 0x1000 bootloader.bin 就是在0x1000處寫入 bootloader。在 0x8000 處寫入分區表。因為 flash 是 16MB 的,所以我就用 16MB 的分區表。

刷完後,打開串口讀取一下信息,看看有沒有正常啟動 CLR。咱們不需要安裝串口工具,在 VS Code 的擴展裡面就有,叫 Serial Monitor,屬於微軟大法的一種。安裝擴展後打開終端面板,你會看到有個串口監視器,切換過去,選擇串口號,點擊【開始監視】即可。

C、你會發現 ESP32 在無限重啟。停止串口監視後查看錯誤。

不是 16MB 的嗎,怎麼變成 4MB 的。這裡它是認為 Flash 是 4MB的,刷入了 16MB 的分區表,自然就會認為超出容量,所以後面的分區找不到了。

如果你的命令視窗還沒關閉,你可以回去看看剛纔執行 esptool 後輸出的警告:

Warning: Image file at 0x1000 is protected with a hash checksum, so not changing the flash size setting. Use the --flash_size=keep option instead of --flash_size=16MB in order to remove this warning, or use the --dont-append-digest option for the elf2image command in order to generate an image file without a hash checksum

bootloader 在生成的時候嵌入了 SHA256 的哈希值,也就是說這個鏡像在編譯時是配置為 4MB 大小的。由於無法通過校驗,只能按 4M 的大小來刷,16 MB 的分區當然超出範圍了。樂鑫團隊稱將來的版本可能會取消這個校驗,但目前是需要校驗的。

解決方案:

方案1:刷固件時,選 4MB 的分區表。這是最簡單的方案。

方案2:自己編譯固件。如果你有配置 IDF 環境(註意是 4xx 版本的,不是最新的),然後 clone 下 nf-interpreter 項目,可以自己把 SDK 配置為 16MB,編譯出來的 bootloader 就是匹配 16 MB 的。

方案3:這個好搞一些。直接編譯個 bootloader 替換掉原來的,就不用重新編譯 nanoCLR 了。

 下麵老周就演示一下如何進行方案3。bootloader 是通用的,不必考慮 IDF 版本。IDF 版本切換比較麻煩,可以用多個非安裝版 VS Code,來配置多個環境,或者用多個 WSL 來配置也行。反正你愛咋弄就咋弄。或者直接寫個腳本來配置環境變數,然後啟動相關程式。

不過,這裡咱們只用到 bootloader,可以用最新的 IDF 去編譯,體積就大一點點,能刷進去的,不影響,畢竟在 0x1000 - 0x8000 的空間範圍是夠用的,不用也白浪費。

打開 VS Code,點擊側邊欄上的樂鑫圖標,選擇“New Project Wizard”。

然後等待兩年半,會打開一個配置頁。

後面的串口號 ESP 晶元類型的可以不管,後面可以通過狀態欄圖標修改。然後點 Choose Template。

到了這裡,要選一個項目模板。分組選 ESP-IDF,找到 get-start,用 sample_project 模板就行了,這個最簡潔,比 hello XXX 還簡潔。

選擇好後,點 Create project using template <你選的模板> 按鈕,然後它會提示你是否打開創建的項目,如果 Yes,會用新視窗打開。如果不想這麼反人類,可以選 No,然後在 VS Code 中手動打開項目目錄即可。

這個項目咱們不用寫代碼,在狀態欄中找到 SDK 配置按鈕,點它,然後等待兩年半。

這個設置頁經常會無響應,如果等了三年半還沒打開配置頁,可以點取消,然後重新點配置按鈕。

配置頁打開後,找到“Serial flasher config” 節點,註意是頂層節點,不是 Bootloader config 下麵那個。把 Flash size 改為你要的大小,比如這裡我要 16 MB。

設置好後點擊頁面頂部的 【保存】 按鈕,最後關閉頁面。

直接編譯項目即可。這裡老周是圖方便,畢竟用項目來編譯 bootloader 可以少很多麻煩。當然,bootloader 是可以單獨編譯的,在IDF的 components\bootloader\subproject 目錄下就是獨立的 bootloader 項目。不要直接在這裡操作,而是把 subproject 目錄中的內容複製到其他目錄再編譯,這可以保證 SDK 目錄不受破壞。這種方法配置起來特煩,而且容易報一堆錯,所以還是直接編譯項目來得爽。

項目編譯後會生成 bootloader.bin 文件,把它複製並替換 nanoCLR 中的 bootloader(在 build\bootloader 目錄下找到 bootloader.bin 文件)。接著,重新刷一下 nanoCLR 固件。

esptool -c esp32 -p COM9 -b 115200 write_flash -fs 16MB -fm dio -ff 40m 0x1000 "E:\demo\bootloader.bin" 0x8000 "E:\demo\partitions_16mb.bin" 0x10000 "E:\demo\nanoCLR.bin"

刷寫完成後,再打開串口監視器,你能看到你想要的東西(也可以用 flash_download_tool 來燒錄的)。

啟動 VS,打開 Device explorer,點“Ping Device” 按鈕,如果看到 “XXX @ COM9 is active running nanoCLR.“ 的字樣就說明沒問題了。

 ----------------------------------------------------------------------------------------------------------------------------------

好了,正片現在開始。要在 .NET Nano 中使用 LCD 屏幕,必須使用帶有圖形驅動的固件,否則是無法運行的。因為 .NET 類庫不帶驅動。打開固件下載頁:Cloudsmith - Repositories - .NET nanoFramework (net-nanoframework) - nanoframework-images (nanoframework-images) - Packages

點擊 package groups,進入分組視圖,這樣找固件方便。

支持圖形驅動的有以下幾組:

a、ESP32_GenericDisplay_REV0:通用型,針對 revision < 3 的板子。

b、ESP32_PSRAM_BLE_GenericGraphic_REV3:通用版,支持 BLE,要求 Revision >= 3。

c、面向 M5Stack Core 或 Core 2 的固件;

d、面向 M5StickC 和 M5StickCPlus 的固件。

M5StickCPlus 2 是最新改進的,但 nanoCLR 沒有區配,不過,經過老周測試,M5StickC Plus 2 能正常使用。M5StuckC Plus 2 老周有這個,黃色外殼,跟U盤差不多大。

M5Stack 的東西,說實話,價格偏高,做工也一般般。唯一的好處是有外殼(雖然外殼也是歪的),做成品放到項目上用比較方便。當然,如果批量使用也可以找人設計外殼,再給工廠批量做,這比買 M5Stack 的性價比高。

不過,老周今天拿來演示的是另一款。這款是高仿 M5Stack Core 的,價格便宜了一半,唯一不同的是,比 M5Stack 少了功放晶元,不能用 i2S 輸出音頻,喇叭是直接連到 GPIO 25 的,用 DAC 來輸出。ESP32 的數模轉換隻有 8 位,所以音質嘛,也就是聽個響。

這個開發板只能刷通用固件,即 ESP32_GenericDisplay_REV0。剛纔老周已經用替換 bootloader 的方式刷了 16MB 的固件,待會咱們可以直接編程。

LCD 屏幕的驅動晶元,見得多的是 St77xx 和 iLi93xx。如 St7789、iLi9341 等。這些晶元雖然多,不過用法差不多,99.997% 用 SPI 協議,所以咱們也不用關心時序的事了。但有個別引腳也要註意的,如區分命令(Command)和數據(Data)的數據線,複位線等。

說是SPI 協議,但這些玩意兒有 N 多種接線方式,有單線、雙、四、八、十六線通信的接法。不過,以老周淺薄的經驗來看,單線和八線的見得多。

1、八線:即數據線有八根,D0 - D7,一根線發一個位,一起發送一次可以發一個位元組。八根線統一由時鐘線來控制,時鐘快慢決定了發數據的速度。這種接線法太浪費 IO 口,ESP32 本身引腳不多,所以,ESP 系列開發板很少這種連接,倒是 K210 開發板較多。

2、單線:即一根數據線,由時鐘線控制。ESP 系列開發板一般是這種接法。

由於只有一根線(MOSI),沒有 MISO 連接,所以寫的時候方便,讀的時候就難搞。如果真要讀,就得重新初始化 SPI,把連接數據線的引腳調為 MISO,讀完後又重新初始化為寫(MOSI)。想法是這樣,但老周從未試過,畢竟這樣折騰比較影響效率。最重要的是,LCD 屏最主要的任務是顯示,咱們儘管向它寫數據就夠了,很少會讀數據。

這裡順便解釋一個容易被誤解的事。很多大伙伴(不管你用C語言,Arduino 或別的)在入門的時候都遇到過屏幕無法點亮的事。然後大伙就各種自我檢討,是我協議設置不對嗎?是我用的這個庫封裝有錯?是我的板子掛了?還是……人品問題。如果你沒做過什麼見不得人的事,那不用懷疑人品。其實是大伙在看原理圖時沒認真看。K210 開發板一般不會單獨接背光的線,所以你在 K210 開發上可以寫寄存器來調光。可是,許多 ESP32 開發板是有一根專門的背光線的。例如,請看下麵這個原理圖。

這個圖告訴你,G7 控制 LCD 的背光。再看另一張圖。

這個比較複雜,背光開關 EN 接 G27,即 G27 是 LCD 背光控制線。SGM2578 控制電力分配(可能是帶電池的原因,電池和外部供電的均衡),WS4622 是控制LED的 RGB 通道調光用的,和 WS 2812 等是一類貨色。

這就是你點不亮屏幕的原因,背光線是獨立連接的,你寫驅動晶元的寄存器是不起作用的,你必須給背光線輸出高電平,LCD 屏才會亮起。當然了,你給它輸出 PWM 也行,還能調亮度呢,但可能會頻閃;不想頻閃的話可以用 DAC 給它輸出模擬電壓,也能達到調光的效果。

.NET Nano 封裝的 .NET API 在 Graphics 包中,所以,打開 Nuget 包管理器,安裝 nanoFramework.Graphics 包,另外,咱們要操作 SPI 和 GPIO(GPIO是那根背光線,我們要讓屏幕亮起),還要安裝以下三個包:

nanoFramework.System.Device.Spi;

nanoFramework.System.Device.Gpio;

nanoFramework.Hardware.Esp32

老周這款高仿板用的是 iLi9342C,用 iLi9341 的驅動也通用。還得安裝一個 iLi9642 的專用包:nanoFramework.Graphics.Ili9342。如果你用的是其他晶元,可以安裝對應的包,如 St7735 等。

先聲明一下要用到的引腳,這個你要按照你的開發板來,找賣家要原理圖。如果賣家不給或給的圖是錯的,可以退貨。老周就因為這個原因退過兩次貨。

const int PIN_CLK = 18;   // 時鐘線
const int PIN_MOSI = 23;  // 數據線
const int PIN_MISO = 34;  // 用不上,但需要指定
const int LCD_DC = 27;    // 命令/數據切換線
const int LCD_RESET = 33; // 複位線
const int LCD_CS = 14;    // 片選
const int LCD_BL = 32;    // 背光線

時鐘線和數據線就不說了,和標準 SPI 的含義一樣。有一條 D/C 線,有的叫 W/S 線,它的作用時:D/C 低電平時表示發送命令,D/C 高電平時表示發數據。複位線:高電平正常,低電平複位。在初始化時,先拉低複位線,然後進行各種初始化設置,完成後再把複位線拉高,複位完畢。

發送命令的過程:D/C線拉低 ----> 寫入命令(通常就是一個位元組);

發送數據的過程:D/C線拉高 ----> 寫入數據(可能是一個位元組,可能是多個,也可能是0個,如果沒有數據,這個過程直接忽略)。

這幾個驅動晶元用起來都差不多,就是寫寄存器,甚至連寄存器的編號都相同。

當然,咱們用封裝過的 iot 框架的目的,就是犧牲性能來換取開發應用的便捷,所以 .NET Nano 已經封裝好了,咱們不用去寫寄存器。使用 DisplayControl 類(nanoFramework.UI 命名空間)就能往 LCD 屏里寫入顏色。這個類公開的都是靜態成員,不用實例化。

1、初始化引腳功能。由於 ESP32 的引腳是復用的,所以對於 SPI 的時鐘線、數據線要設置。

Configuration.SetPinFunction(PIN_MOSI, DeviceFunction.SPI1_MOSI);
Configuration.SetPinFunction(PIN_CLK, DeviceFunction.SPI1_CLOCK);
Configuration.SetPinFunction(PIN_MISO, DeviceFunction.SPI1_MISO);

2、先給背光線來一波高電平,不然LCD不亮。

GpioController ctrl = new();
var pinbl = ctrl.OpenPin(LCD_BL);
pinbl.SetPinMode(PinMode.Output);
pinbl.Write(PinValue.High);

3、配置控制屏幕的 SPI 參數,類型是 SpiConfiguration, 也是在 UI 命名空間下。

SpiConfiguration spicfg = new(
        spiBus: 1,
        chipselect: LCD_CS,
        dataCommand: LCD_DC,
        reset: LCD_RESET,
        backLight: -1        // 這裡不用指定背光線,要單獨控制才有效
    );

註意不要在這裡指定 backLight 參數,點不亮的,因為許多板子,背光線不是集成在屏幕上,也就不會與屏幕直接連接,所以設置這個是無效的,我們剛剛單獨處理了。

4、用 ScreenConfiguration 類配置屏幕參數,如寬度、高度,還有x、y坐標的偏移。這個偏移是需要的,因為不同的屏幕不一樣,有的要偏移 45,有的則要偏移 52。這個可以通過實驗不斷調校,調到合適的值就好。主要是因為顯示的內容不一定是從屏幕左上角開始的,經常會跑到屏幕外面。K210 的板子不用調整這個,但 ESP32 的板子需要調整,原因未知。

// 獲取驅動
var driver = Ili9342.GraphicDriver;

// 自定義初始化
driver.InitializationSequence = new byte[]
{
    (byte)GraphicDriverCommandType.Command, 1, 0x21,
    (byte)GraphicDriverCommandType.Command, 2, 0x3a, 0x55,
    (byte)GraphicDriverCommandType.Command, 5, 0x2a, 0x00, 0x00, 0x01, 0x3f,
    (byte)GraphicDriverCommandType.Command, 5, 0x2b, 0x00, 0x00, 0x00, 0xef,
    (byte)GraphicDriverCommandType.Command, 1, 0x11,
    (byte)GraphicDriverCommandType.Command, 1, 0x29
};

// 配置屏幕
ScreenConfiguration scrcfg = new(
        0,
        0,
        320,
        240,
        driver
    );

通過 Ili9342.GraphicDriver 靜態成員可以獲得相關的驅動。如果你的板子是 St7789,那就改為對應的類。註意上面代碼中高亮的部分,即

driver.InitializationSequence = new byte[]
{
    (byte)GraphicDriverCommandType.Command, 1, 0x21,
    (byte)GraphicDriverCommandType.Command, 2, 0x3a, 0x55,
    (byte)GraphicDriverCommandType.Command, 5, 0x2a, 0x00, 0x00, 0x01, 0x3f,
    (byte)GraphicDriverCommandType.Command, 5, 0x2b, 0x00, 0x00, 0x00, 0xef,
    (byte)GraphicDriverCommandType.Command, 1, 0x11,
    (byte)GraphicDriverCommandType.Command, 1, 0x29
};

這裡是設置初始化指令,這個是因為老周這個板特別,iLi9342 預設上電是正色顯示的(即關閉反色),可是這塊鳥板正色時它顯示反色,反色時它卻顯示正色。所以,預設的初始化方式不適用,只能自己寫寄存器了。其實這些晶元上電時很多預設值都能用的,並不需要改太多的寄存器。

老周簡音介紹一下這個指令的格式。

1)這些指令就是 byte 數組;

2)多條指令可以連接寫到一個數據組中;

3)每條指令的第一個位元組代表指令類別。1 表示一條正常發送的指令(Command),0 表示 Sleep。你可別誤會,這個 Sleep 不是讓 LCD驅動晶元休眠,而是暫停一下(就像 Thread.Sleep 方法),SPI 不發送罷了。

4)如果是第一個位元組是 Command,那麼第二個位元組是長度(SPI要發多少位元組),從第三個位元組起就是真正要發送的。比如,0x01,0x03,0x22,0x15,0x8d。第一個 0x01 表明它是一條正常發出的指令,第二個是 03 表示後面有三個位元組,而 SPI 真正發送的是 0x22,0x15,0x8d。這三個位元組中,0x22 表示驅動命令(寄存器),0x15和0x8d表示要寫入寄存器的數據。

5)如果第一個位元組是 Sleep,後面需要跟一個位元組,表示暫停時長,單位是 10ms。比如,0x00,0x02,第一個 0x00 表示暫停,0x02 表示暫停 2 * 10 = 20 毫秒。

好,弄懂這個,咱們回頭看看老周剛寫的初始化命令:

=> 發送 0x21,單命令,沒有參數,所以只有一個位元組。0x21 本來是開啟反色顯示的,關閉反色顯示是 0x20 寄存器。由於老周這塊板不知怎麼回事,是反過來的。

=> 發送 0x3a,設置像素格式為 16 位,RGB565。0x55 上這麼來的:

第1-3位設置DBI,第5-7位設置DPI,請看下表:

 為了減少不必要的麻煩,通常咱們記憶體處理的像素格式和顯示屏顯示的一樣,所以左右兩邊都是 101,即5,合起來就是 0x55。

=> 發送 0x2a,設置列的坐標空間,即我們要寫入屏幕像素的水平範圍。這個屏幕的長是 320,所以,命令參數有四個位元組。前兩個表示起點,即0;後兩個表示終點,0x01,0x3f 組合的16位整數是 319。坐標從 0 起算,320就是319。

=> 發送 0x3b 命令,表示行的坐標空間,參數也是四個位元組,範圍 0- 239(240即239,要減1)。

=> 發送 0x11 命令,表示讓 LCD 離開休眠模式,從而喚醒屏幕,上電時預設休眠。

=> 發送 0x29 命令,打開顯示模式,正常呈現畫面。

在實例化 ScreenConfiguration 對象時,提供這些參數:

a、屏幕左上角坐標,我這裡設置為 0,0,剛剛好,沒有偏。如果你測試發現顯示的內容跑到屏幕外了,就要適當設置一下偏移坐標,如x=45,y=52。

b、屏幕寬度和高度,這裡是 320 * 240。

c、驅動對象,就是剛從 Ili9342.GraphicDriver 返回的。

 

5、初始化 DisplayControl。

_ = DisplayControl.Initialize(spicfg, scrcfg, 10240);

最後的參數 10240 是預先分配的記憶體大小,不要弄太大,開發板的運行記憶體小到無語,分配太大了容易爆。Initialize 方法返回實際可分配的記憶體,如果記憶體不夠,返回的值可能比你指定的小。這裡我不理它,直接忽略。

6、如果能正常使用,這個時候已經可以向屏幕寫數據了,咱們把全屏幕填充為藍色。

 // 清空屏幕
 DisplayControl.Clear();
 ushort color = Color.Red.ToBgr565();
 // 寬高
 ushort dw = 80, dh = 80;
 ushort[] bf = new ushort[dw * dh];

 for(int i = 0; i < bf.Length; i++)
 {
     bf[i] = color;
 }

 while (true)
 {
     for(ushort x = 0; x < 320; x+=80 )
     {
         for(ushort y = 0; y < 239; y += 80)
         {
             DisplayControl.Write(x, y, dw, dh, bf);
             Thread.Sleep(300);
         }
     }
     Thread.Sleep(1000);
     DisplayControl.Clear();
 }

像素格式是 16 位的,即,RGB 加起來16位,正好用一個 uint16 (ushort)可以表示。565表示 R 占5位,G 占6位,剩下5位留給 B。這裡明顯綠色多占了一位,難道 LCD 屏看上去有些綠。

DisplayControl 類雖然封裝後調用方便,但這種封裝……反正老周有意見。原因有:1、只能在初始化時修改寄存器,顯示內容後無法改了;2、這東西耗記憶體。

所以,創建用來表示像素的 ushort 數組不能太大,否則會因為記憶體溢出而無法運行。320 * 240 個 ushort 值會報錯。這樣就不能一次性填充整個屏幕了,只能分塊來填,每塊 80 * 80,所以,橫著填四塊,堅著填三塊。在填充完一塊後,老周故意 Sleep 一下,這樣我們在運行階段能看到分塊填充的效果。

 

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


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

-Advertisement-
Play Games
更多相關文章
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 在Linux文件系統中,每一個文件都有三個時間屬性,它們分別是atime,mtime,ctime,一般來說,atime比較好理解,但是很多時候,我們往往會混淆mtime和ctime這兩個時間屬性,或者搞不清楚兩者的區別。在展開介紹之前,我們先來看看如何查看文件的atime,mtime,ctime屬性 ...
  • 樹(tree) [一] 基本概念: ​ 日常生活中,很多數據的組織形式本質上是一棵樹。比如一個公司中的職員層級關係、一個學校中的院系層級關係、淘汰賽中的各次比賽隊伍、一個家族中的族譜成員關係等都是樹狀邏輯結構。由於樹狀結構表現出來都是具有層次的,因此也被稱為層次結構。 樹是一種非線性結構(一對多), ...
  • 數據結構 二叉樹 筆試題: 解題思路: 1.判斷是否為空樹,若為空樹,則返回0; 2.定義兩個指針備份根結點地址,定義兩個整型變數a,b並初始化為0,記錄左右子樹的深度;先對根結點的左子樹進行遍歷,若根結點的左結點不為NULL,則a++,把根結點的左結點賦值為新的根結點,再進行上述操作,若根結點的左 ...
  • 目錄通用許可權的管理擴展許可權的管理特殊許可權的管理許可權掩碼 在Linux中,許可權管理是通過文件系統中的許可權位來實現的。 通用許可權的管理 每個文件或目錄都有一個所有者、一個用戶組和其他用戶的許可權設置。主要的許可權包括讀取、寫入和執行。 文件許可權包括讀、寫和執行許可權: 讀許可權(r):允許用戶讀取文件的內容或列 ...
一周排行
    -Advertisement-
    Play Games
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...