加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 1 硬件介紹
    • 2 開發(fā)環(huán)境搭建
    • 3 LCD驅(qū)動(dòng)的使用和測試
    • 4 制作圖片數(shù)據(jù)
    • 5 編寫應(yīng)用程序
    • 6 測試驗(yàn)證
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

Arduino應(yīng)用開發(fā)——LCD顯示圖片

10/03 10:55
3372
閱讀需 37 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

LCD是項(xiàng)目中比較常用的外設(shè),基于Arduino開發(fā)有個(gè)好處就是它很多相關(guān)的庫可用,這對(duì)于項(xiàng)目的開發(fā)或者前期的方案驗(yàn)證來說是非常方便的,缺點(diǎn)是靈活性較差。Arduino支持很多硬件,我們這一講主要基于ESP8266和ESP32來講解圖片的顯示。

1 硬件介紹

1.1 硬件配置

本文的硬件配置如下:

模塊 型號(hào) 說明
LCD ST7789 LCD常用的驅(qū)動(dòng)IC有很多,如:ILI9341、ST7735等,不同的驅(qū)動(dòng)IC,驅(qū)動(dòng)代碼也是有區(qū)別的
注:驅(qū)動(dòng)IC型號(hào)和屏幕型號(hào)是不同的,不管屏幕的廠家是哪個(gè),屏幕尺寸是多大,只有驅(qū)動(dòng)IC型號(hào)一樣,驅(qū)動(dòng)代碼都是一樣的
ESP8266 ESP-12F 這是安信可的一款模組,內(nèi)部主要是用樂鑫的ESP8266EX再加上一個(gè)片外FLASH組成
ESP32 ESP-WROOM-32 MCU是樂鑫的一款芯片,開發(fā)板型號(hào)ESP32 DEVKITV1

注:我這里以ESP8266和ESP32為例講解,實(shí)際上根據(jù)自己的MCU選擇一種即可,方法和原理都是一樣的。

1.2 硬件連接

ESP8266接線如下:

esp8266 lcd 說明
VCC VCC 電源
GND GND 電源負(fù)
GPIO14HSPI_CLK CLK SPI時(shí)鐘線
GPIO13HSPI_MOSI SDI SPI數(shù)據(jù)線,esp8266輸出,lcd輸入
GPIO12HSPI_MISO SDO SPI數(shù)據(jù)線,esp8266輸入,lcd輸出,注:該引腳一般可以不用,除非你要讀取LCD的信息
GPIO4 CS SPI片選
GPIO5 WR(D/C) 并口時(shí)作為寫入信號(hào)/SPI時(shí)作為命令或參數(shù)的選擇
RST RST LCD復(fù)位引腳,可以用普通IO控制,引腳不足的情況下也可以和esp8266的復(fù)位引腳接到一起
特別說明:不同廠家做的LCD對(duì)這幾個(gè)引腳的命名可能會(huì)有差異,但意思是一樣的。

ESP32接線如下:

esp32 lcd 說明
VCC VCC 電源正
GND GND 電源負(fù)
GPIO18SPI_CLK CLK SPI時(shí)鐘線
GPIO23SPI_MOSI SDI SPI數(shù)據(jù)線,esp32輸出,lcd輸入
GPIO19SPI_MISO SDO SPI數(shù)據(jù)線,esp32輸入,lcd輸出,注:該引腳一般可以不用,除非你要讀取LCD的信息
GPIO15 CS SPI片選
GPIO5 WR(D/C) 并口時(shí)作為寫入信號(hào)/SPI時(shí)作為命令或參數(shù)的選擇
GPIO4 RST LCD復(fù)位引腳,可以用普通IO控制,引腳不足的情況下也可以和esp32的復(fù)位引腳接到一起
特別說明:不同廠家做的LCD對(duì)這幾個(gè)引腳的命名可能會(huì)有差異,但意思是一樣的。

2 開發(fā)環(huán)境搭建

2.1 安裝開發(fā)板

關(guān)于ESP8266和ESP32的Arduino環(huán)境搭建我之前出過教程了,這里就不多說了,不懂的同學(xué)可以先看下我之前的博客。
esp8266開發(fā)入門教程(基于Arduino)——環(huán)境安裝
ESP32-S2 Arduino開發(fā)環(huán)境搭建

2.2 安裝庫

打開Arduino IDE,依次打開 工具 -> 管理庫…
在搜索框輸入需要安裝的庫名稱,找到對(duì)應(yīng)的庫,點(diǎn)擊安裝即可。

本文需要使用的Arduino庫如下:

Arduino庫 版本 說明
TFT_eSPI 2.4.2 該庫通過SPI方式驅(qū)動(dòng)LCD,支持多種LCD常用驅(qū)動(dòng)IC
JPEGDecoder 1.8.0 JPEG格式解碼庫,用來顯示JPEG圖片
注:TFT_eSPI庫本身就支持顯示圖片,但是只能是16進(jìn)制的位圖數(shù)據(jù),而JPEGDecoder是可以顯示JPEG格式圖片,兩種方式其實(shí)都是可以的。

3 LCD驅(qū)動(dòng)的使用和測試

LCD驅(qū)動(dòng)的方式一般是用SPI、并口或者IIC,我這里是以SPI為例。我之前也發(fā)布過一篇關(guān)于LCD驅(qū)動(dòng)講解的博客,有什么不懂的話也可以去看一下。
esp8266應(yīng)用教程——TFT LCD

3.1 修改TFT_eSPI庫基礎(chǔ)配置

安裝好TFT_eSPI庫之后需要根據(jù)自己電路實(shí)際的情況配置底層接口。

Arduino安裝的庫一般在C盤文檔目錄下,如:C:UsersxxxDocumentsArduinolibraries (xxx是你電腦的用戶名)

在這里插入圖片描述

找到TFT_eSPI文件夾,打開User_Setup.h文件,修改以下幾項(xiàng)參數(shù)。

在這里插入圖片描述

1)驅(qū)動(dòng)IC

根據(jù)自己使用的LCD驅(qū)動(dòng)IC打開對(duì)應(yīng)的宏。注意這些驅(qū)動(dòng)只能選擇一個(gè)打開,不用的要注釋掉。

在這里插入圖片描述

2)RGB數(shù)據(jù)格式

RGB格式指的是像素點(diǎn)顏色數(shù)據(jù)的排列方式,一般就BGR和RGB兩種,區(qū)別其實(shí)就是數(shù)據(jù)高位在前還是低位在前,這個(gè)主要是用于圖片顯示,要用哪種格式主要是看你要顯示的內(nèi)容是怎么排的,不確定的話可以先不改,調(diào)試的時(shí)候如果顏色不對(duì)的話再換過來就好了。

在這里插入圖片描述

3)像素

根據(jù)自己屏幕的像素修改,也可以先不改,直接在后面應(yīng)用的時(shí)候再改。

在這里插入圖片描述

4)GPIO

根據(jù)自己的電路設(shè)置引腳,除了幾個(gè)必要的引腳,有些引腳可以不配置,如:RST可以通過硬件和MCU的RST接到一起,軟件配置成-1即可。BL背光也可以硬件直接控制。

還有像ESP8266也可以不自定義SPI的幾個(gè)引腳,它默認(rèn)用的就是ESP8266硬件SPI的接口,你的接線保持一致即可。

注意:相同的GPIO定義只能打開一個(gè),默認(rèn)有些打開了的要注釋掉。

特別說明:如果你用的是ESP32,TFT_eSPI建議使用2.4.0以上版本,因?yàn)橹暗陌姹娟P(guān)于ESP32的引腳定義是分成HSPI和VSPI的,默認(rèn)使用VSPI,如果要用HSPI要打開USE_HSPI_PORT定義,但是這套框架兼容性不強(qiáng),不適用于ESP32-S2和ESP32-C2,而2.4.0以上版本取消了這個(gè)定義,直接都按引腳號(hào)來定義,這樣一來就不用區(qū)分HSPI和VSPI了。

esp8266的引腳如下圖所示,esp32的我就不貼出來了,都是差不多的。

在這里插入圖片描述

5)字庫

TFT_eSPI自帶的這些字庫你可以直接用,如果你有自己的字庫不用這里的話也可以注釋掉。flash空間足夠的情況下,這點(diǎn)代碼加不加都沒關(guān)系。

在這里插入圖片描述

6)SPI通訊速率

SPI通訊速率一般默認(rèn)即可,默認(rèn)的這個(gè)速率一般是足夠了的,如果需要更快的話可以自己修改。

在這里插入圖片描述

7)ESP32的特殊定義

TFT_eSPI舊版本關(guān)于ESP32的SPI接口是分為HSPI和VSPI兩種的,默認(rèn)使用VSPI,如果要用HSPI要打開USE_HSPI_PORT定義,如果你只是用ESP32,那這個(gè)框架是沒什么問題的。

但是我之前因?yàn)轫?xiàng)目需要從ESP32改用ESP32-S2,結(jié)果發(fā)現(xiàn)ESP32-S2就沒有HSPI和VSPI,所有接口都是FSPI,于是我就要把底層很多東西都改掉才能正常使用。

不過現(xiàn)在TFT_eSPI庫2.4.0以上版本就已經(jīng)把這個(gè)問題改掉了,兼容了ESP32-S2和ESP-C3,取消了USE_HSPI_PORT這個(gè)定義,SPI接口都以GPIO引腳號(hào)來定義。

所以,我建議都用新版本的庫吧,兼容性更好,也不用去考慮用HSPI還是VSPI。

3.2 LCD驅(qū)動(dòng)測試

TFT_eSPI庫配置好了之后可以先燒錄一個(gè)簡單的程序測試一下硬件和代碼是否能正常運(yùn)行。

PS:當(dāng)然,到了后面把圖片數(shù)據(jù)做好直接顯示圖片也是可以的。

#include <SPI.h>
#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI(); 

void setup() 
{
  Serial.begin(115200);
  tft.begin();
  tft.setRotation(0);
  tft.fillScreen(TFT_BLACK);
}

void loop()
{
    tft.fillScreen(TFT_GREEN);
    delay(1000);
    tft.fillScreen(TFT_BLUE);
    delay(1000);
    tft.fillScreen(TFT_RED);
    delay(1000);
}

4 制作圖片數(shù)據(jù)

圖片數(shù)據(jù)可以按位圖的形式保存和顯示,也可以按jpg格式保存。區(qū)別在于位圖的方式占用的內(nèi)存都是固定的,只跟像素大小有關(guān),而相同像素下jpg的內(nèi)存一般要更小,具體占用的內(nèi)存大小跟圖片的色彩復(fù)雜度有關(guān),但是jpg的缺點(diǎn)是需要解碼,而位圖數(shù)據(jù)則不需要解碼。

兩種方式都可以選,根據(jù)自己的需求來用就行了。位圖的話直接用TFT_eSPI庫就行了,而jpg格式還需要使用JPEGDecoder庫解碼。

4.1 制作圖片素材

網(wǎng)上隨便找一張圖片,借助WIN10自帶圖片編輯器或者其他圖片處理軟件把圖片處理一下,裁剪出自己想要顯示的內(nèi)容之后,再把分辨率調(diào)整成適合的大小,圖片以jpg,bmp或者其他格式保存都是可以的。

4.2 生成圖片數(shù)據(jù)

我們可以借助一些工具來實(shí)現(xiàn)圖片到數(shù)據(jù)的轉(zhuǎn)換。我們的數(shù)據(jù)要以C語言數(shù)組的形式存儲(chǔ),像素顏色數(shù)據(jù)以16進(jìn)制的形式保存。

4.2.1 位圖數(shù)據(jù)生成方法

位圖數(shù)據(jù)的工具很多,比如:Img2Lcd,lcd-image-converter等。
工具可以在下面的鏈接下載,也可以在網(wǎng)上自己查找。
工具鏈接:https://pan.baidu.com/s/1f2rgD1a9PY-_hboPdbfaow
提取碼:4h6h

1)Img2Lcd使用方法

打開一張圖片。
設(shè)置配置如下:
輸出數(shù)據(jù)類型:C語言數(shù)組
掃描方式:水平掃描
輸出灰度:16位真彩色
最大寬度和高度:自定義

下面的幾個(gè)選項(xiàng)只勾選“高位在前(MSB First)”即可。(說明一下:高位在前的意思是指數(shù)據(jù)以GBR方式導(dǎo)出,如果不勾選的話則是以RGB方式導(dǎo)出,用哪種其實(shí)取決于你后面顯示圖片的那個(gè)函數(shù)處理數(shù)據(jù)的方式,保持一致即可。)

在這里插入圖片描述

配置好參數(shù)之后點(diǎn)擊保存,以.h文件保存即可。保存好這個(gè)頭文件,后面會(huì)用到。

保存的這個(gè)頭文件數(shù)據(jù)格式如下:

在這里插入圖片描述

這個(gè)數(shù)組的定義我們可以改一下,因?yàn)閳D片的數(shù)據(jù)一般比較多,全部用RAM來存的話內(nèi)存可能不足,所以我們可以把這些數(shù)據(jù)存放到flash里面。

修改定義之前:

const unsigned char gImage_demo_image1[115200] = { /* 0X10,0X10,0X00,0XF0,0X00,0XF0,0X01,0X1B, */

修改定義之后:

#ifndef PROGMEM
#define PROGMEM
#endif
const uint8_t gImage_demo_image1[115200] PROGMEM = { /* 0X10,0X10,0X00,0XF0,0X00,0XF0,0X01,0X1B, */

PROGMEM的用法具體我就不說了,不懂的可以自己去查。

2)lcd-image-converter使用方法

打開一張圖片,點(diǎn)擊 Options -> Conversion…

在這里插入圖片描述

參數(shù)配置好了之后點(diǎn)擊"Show Preview"即可看到圖片轉(zhuǎn)換后的位圖數(shù)據(jù)。

這個(gè)軟件不能直接生成頭文件,需要自己另外新建一個(gè)頭文件,然后定義一個(gè)數(shù)組,再把這些數(shù)據(jù)拷貝進(jìn)去。

4.2.2 JPEG格式圖片數(shù)據(jù)生成方法

我這里用的轉(zhuǎn)換工具是在GitHub上面找到的一個(gè)代碼,你們想要的話可以在下面的鏈接下載。這個(gè)工具使用起來稍微有點(diǎn)麻煩,如果你有更好的工具也可以推薦給博主。

工具鏈接:https://pan.baidu.com/s/1f2rgD1a9PY-_hboPdbfaow
提取碼:4h6h

運(yùn)行方法如下:

第一步:把要轉(zhuǎn)換的圖片放到這個(gè)工具的目錄下。

在這里插入圖片描述

第二步:打開電腦的運(yùn)行窗口(win10可以使用win+R快捷鍵),輸入“cmd”打開命令窗口。

在這里插入圖片描述

第三步:在命令窗口中輸入跳轉(zhuǎn)命令,跳轉(zhuǎn)到轉(zhuǎn)換工具所在的目錄下。

例如:

cd C:UserszDesktop圖片處理工具image_to_cdistWindows

在這里插入圖片描述

第四步:運(yùn)行應(yīng)用程序。

格式:應(yīng)用程序名+空格+圖片名+空格+>+空格+轉(zhuǎn)換后的文件名。
例如:

image_to_c64.exe demo-image1.jpg > demo-image1.h

在這里插入圖片描述

運(yùn)行成功的話就會(huì)生成相應(yīng)的頭文件。

在這里插入圖片描述

5 編寫應(yīng)用程序

使用Arduino IDE新建并保存一個(gè)工程,把圖片數(shù)據(jù)(.h文件)放到工程目錄下。然后編寫應(yīng)用代碼。

示例代碼如下:

特別說明:該代碼是用ESP32-S2進(jìn)行測試的,ESP32和ESP8266我之前也調(diào)試過,不是下面的這個(gè)代碼,不過寫法基本都是一樣的。主要是TFT_eSPI庫的引腳改一下即可。

本文所用源碼和圖片素材都上傳到云盤了,可以在文章底部鏈接下載。

#include <SPI.h>
#include <TFT_eSPI.h>
// #include "Arduino.h"

#ifdef ESP8266
#include <avr/pgmspace.h>
#else
#include <pgmspace.h>
#endif

// 圖片位圖數(shù)據(jù),注意:下面這幾個(gè)只是示例文件,而且都是240x240分辨率的圖片,內(nèi)存比較大,如果你用的FLASH內(nèi)存不足的話編譯會(huì)出錯(cuò)
#include "demo_image1.h"
#include "demo_image2.h"
#include "demo_image3.h"
#include "test_image.h"

// #define JPEG_ENABLE   // 打開該宏使用JPEG解碼

TFT_eSPI tft = TFT_eSPI(); 

// JPEG圖片顯示相關(guān)函數(shù)
#ifdef JPEG_ENABLE
// JPEG decoder library
#include <JPEGDecoder.h>
#include "demo_jpg1.h"
#include "demo_jpg2.h"
#include "demo_jpg3.h"

// Return the minimum of two values a and b
#define minimum(a,b)     (((a) < (b)) ? (a) : (b))

//####################################################################################################
// Draw a JPEG on the TFT pulled from a program memory array
//####################################################################################################
void drawArrayJpeg(const uint8_t arrayname[], uint32_t array_size, int xpos, int ypos) {

  int x = xpos;
  int y = ypos;

  JpegDec.decodeArray(arrayname, array_size);
  
  jpegInfo(); // Print information from the JPEG file (could comment this line out)
  
  renderJPEG(x, y);
  
  Serial.println("#########################");
}

//####################################################################################################
// Draw a JPEG on the TFT, images will be cropped on the right/bottom sides if they do not fit
//####################################################################################################
// This function assumes xpos,ypos is a valid screen coordinate. For convenience images that do not
// fit totally on the screen are cropped to the nearest MCU size and may leave right/bottom borders.
void renderJPEG(int xpos, int ypos) {

  // retrieve infomration about the image
  uint16_t *pImg;
  uint16_t mcu_w = JpegDec.MCUWidth;
  uint16_t mcu_h = JpegDec.MCUHeight;
  uint32_t max_x = JpegDec.width;
  uint32_t max_y = JpegDec.height;

  // Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs)
  // Typically these MCUs are 16x16 pixel blocks
  // Determine the width and height of the right and bottom edge image blocks
  uint32_t min_w = minimum(mcu_w, max_x % mcu_w);
  uint32_t min_h = minimum(mcu_h, max_y % mcu_h);

  // save the current image block size
  uint32_t win_w = mcu_w;
  uint32_t win_h = mcu_h;

  // record the current time so we can measure how long it takes to draw an image
  uint32_t drawTime = millis();

  // save the coordinate of the right and bottom edges to assist image cropping
  // to the screen size
  max_x += xpos;
  max_y += ypos;

  // read each MCU block until there are no more
  while (JpegDec.readSwappedBytes()) {
	  
    // save a pointer to the image block
    pImg = JpegDec.pImage ;

    // calculate where the image block should be drawn on the screen
    int mcu_x = JpegDec.MCUx * mcu_w + xpos;  // Calculate coordinates of top left corner of current MCU
    int mcu_y = JpegDec.MCUy * mcu_h + ypos;

    // check if the image block size needs to be changed for the right edge
    if (mcu_x + mcu_w <= max_x) win_w = mcu_w;
    else win_w = min_w;

    // check if the image block size needs to be changed for the bottom edge
    if (mcu_y + mcu_h <= max_y) win_h = mcu_h;
    else win_h = min_h;

    // copy pixels into a contiguous block
    if (win_w != mcu_w)
    {
      uint16_t *cImg;
      int p = 0;
      cImg = pImg + win_w;
      for (int h = 1; h < win_h; h++)
      {
        p += mcu_w;
        for (int w = 0; w < win_w; w++)
        {
          *cImg = *(pImg + w + p);
          cImg++;
        }
      }
    }

    // draw image MCU block only if it will fit on the screen
    if (( mcu_x + win_w ) <= tft.width() && ( mcu_y + win_h ) <= tft.height())
    {
      tft.pushRect(mcu_x, mcu_y, win_w, win_h, pImg);
    }
    else if ( (mcu_y + win_h) >= tft.height()) JpegDec.abort(); // Image has run off bottom of screen so abort decoding
  }

  // calculate how long it took to draw the image
  drawTime = millis() - drawTime;

  // print the results to the serial port
  Serial.print(F(  "Total render time was    : ")); Serial.print(drawTime); Serial.println(F(" ms"));
  Serial.println(F(""));
}

//####################################################################################################
// Print image information to the serial port (optional)
//####################################################################################################
void jpegInfo() {
  Serial.println(F("==============="));
  Serial.println(F("JPEG image info"));
  Serial.println(F("==============="));
  Serial.print(F(  "Width      :")); Serial.println(JpegDec.width);
  Serial.print(F(  "Height     :")); Serial.println(JpegDec.height);
  Serial.print(F(  "Components :")); Serial.println(JpegDec.comps);
  Serial.print(F(  "MCU / row  :")); Serial.println(JpegDec.MCUSPerRow);
  Serial.print(F(  "MCU / col  :")); Serial.println(JpegDec.MCUSPerCol);
  Serial.print(F(  "Scan type  :")); Serial.println(JpegDec.scanType);
  Serial.print(F(  "MCU width  :")); Serial.println(JpegDec.MCUWidth);
  Serial.print(F(  "MCU height :")); Serial.println(JpegDec.MCUHeight);
  Serial.println(F("==============="));
}

//####################################################################################################
// Show the execution time (optional)
//####################################################################################################
// WARNING: for UNO/AVR legacy reasons printing text to the screen with the Mega might not work for
// sketch sizes greater than ~70KBytes because 16 bit address pointers are used in some libraries.

// The Due will work fine with the HX8357_Due library.

void showTime(uint32_t msTime) {
  //tft.setCursor(0, 0);
  //tft.setTextFont(1);
  //tft.setTextSize(2);
  //tft.setTextColor(TFT_WHITE, TFT_BLACK);
  //tft.print(F(" JPEG drawn in "));
  //tft.print(msTime);
  //tft.println(F(" ms "));
  Serial.print(F(" JPEG drawn in "));
  Serial.print(msTime);
  Serial.println(F(" ms "));
}
#endif

void setup() 
{
  Serial.begin(115200);
  tft.begin();
  tft.setRotation(0);
  tft.fillScreen(TFT_BLACK);
}

void loop()
{
#if 1
  tft.pushImage(0, 0, 240, 240, (uint16_t*)test_image);
  delay(1000);
  tft.pushImage(0, 0, 240, 240, (uint16_t*)gImage_demo_image1);
  delay(1000);
  tft.pushImage(0, 0, 240, 240, (uint16_t*)gImage_demo_image2);
  delay(1000);
  tft.pushImage(0, 0, 240, 240, (uint16_t*)gImage_demo_image3);
  delay(1000);
#endif

#ifdef JPEG_ENABLE
  drawArrayJpeg(demo_image1, sizeof(demo_image1), 0, 0);
  delay(1000);
  drawArrayJpeg(demo_image2, sizeof(demo_image2), 0, 0);
  delay(1000);
  drawArrayJpeg(demo_image3, sizeof(demo_image3), 0, 0);
  delay(1000);
#endif
}

6 測試驗(yàn)證

4張圖片間隔輪1秒流播放的效果如下,手機(jī)拍屏幕會(huì)有很大的色差,這個(gè)沒辦法,將就著看吧,反正上面例程效果大概就是這樣的。不管是用位圖數(shù)據(jù)顯示還是jpg格式顯示,最終的結(jié)果是一樣的。

結(jié)束語

這一講簡單介紹了在Arduino環(huán)境下使用LCD顯示圖片,主要介紹了位圖和JPEG格式的顯示,其他格式比如PNG其實(shí)也是可以解碼的,不過這里就不再講解了,感興趣的同學(xué)自己去找相應(yīng)的庫吧。整個(gè)流程總的來說還是不難的,把驅(qū)動(dòng)調(diào)好之后直接凋庫顯示就完了。如果還有什么問題,歡迎在評(píng)論區(qū)留言或者私信給我。

想要源代碼或者圖片處理工具的自行下載:
鏈接:https://pan.baidu.com/s/1f2rgD1a9PY-_hboPdbfaow
提取碼:4h6h

Arduino開發(fā)教程匯總:
https://blog.csdn.net/ShenZhen_zixian/article/details/121659482

相關(guān)推薦

電子產(chǎn)業(yè)圖譜