博客 / 詳情

返回

《ESP32-S3使用指南—IDF版 V1.6》第二十二章 SPI_LCD實驗

第二十二章 SPI_LCD實驗

1)實驗平台:正點原子DNESP32S3開發板

2)章節摘自【正點原子】ESP32-S3使用指南—IDF版 V1.6

3)購買鏈接:https://detail.tmall.com/item.htm?&id=768499342659

4)全套實驗源碼+手冊+視頻下載地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32S3.html

5)正點原子官方B站:https://space.bilibili.com/394620890

6)正點原子DNESP32S3開發板技術交流羣:132780729

本章,我們將學習ESP32-S3的硬件SPI接口,將會大家如何使用SPI接口去驅動LCD屏。在本章中,實現和LCD屏之間的通信,實現ASCII字符、彩色、圖片和圖形的顯示。
本章分為如下幾個小節:
22.1 SPI與LCD簡介
22.2 硬件設計
22.3 程序設計
22.4 下載驗證

22.1 SPI與LCD簡介

22.1.1 SPI介紹

SPI,SerialPeripheral interface,顧名思義,就是串行外圍設備接口,是由原摩托羅拉公司在其MC68HCXX系列處理器上定義的。SPI是一種高速的全雙工、同步、串行的通信總線,已經廣泛應用在眾多MCU、存儲芯片、AD轉換器和LCD之間。
SPI通信跟IIC通信一樣,通信總線上允許掛載一個主設備和一個或者多個從設備。為了跟從設備進行通信,一個主設備至少需要4跟數據線,分別為:
MOSI(Master Out/ Slave In):主數據輸出,從數據輸入,用於主機向從機發送數據。
MISO(Master In /Slave Out):主數據輸入,從數據輸出,用於從機向主機發送數據。
SCLK(SerialClock):時鐘信號,由主設備產生,決定通信的速率。
CS(Chip Select):從設備片選信號,由主設備產生,低電平時選中從設備。
多從機SPI通信網絡連接如下圖所示。

圖22.1.1.1 多從機SPI通信網絡圖
從上圖可以知道,MOSI、MISO、SCLK引腳連接SPI總線上每一個設備,如果CS引腳為低電平,則從設備只偵聽主機並與主機通信。SPI主設備一次只能和一個從設備進行通信。如果主設備要和另外一個從設備通信,必須先終止和當前從設備通信,否則不能通信。
SPI通信有4種不同的模式,不同的從機可能在出廠時就配置為某種模式,這是不能改變的。通信雙方必須工作在同一模式下,才能正常進行通信,所以可以對主機的SPI模式進行配置。SPI通信模式是通過配置CPOL(時鐘極性)和CPHA(時鐘相位)來選擇的。
CPOL,詳稱ClockPolarity,就是時鐘極性,當主從機沒有數據傳輸的時候即空閒狀態,SCL線的電平狀態,假如空閒狀態是高電平,CPOL=1;若空閒狀態時低電平,那麼CPOL= 0。
CPHA,詳稱Clock Phase,就是時鐘相位,實質指的是數據的採樣時刻。CPHA = 0表示數據的採樣是從第1個邊沿信號上即奇數邊沿,具體是上升沿還是下降沿的問題,是由CPOL決定的。CPHA=1表示數據採樣是從第2個邊沿即偶數邊沿。
SPI的4種模式對比圖,如下圖所示。

圖22.1.1.2 SPI的4種模式對比圖
l 模式0,CPOL=0,CPHA=0;空閒時,SCL處於低電平,數據採樣在第1個邊沿,即SCL由低電平到高電平的跳變,數據採樣在上升沿,數據發送在下降沿。
l 模式1,CPOL=0,CPHA=1;空閒時,SCL處於低電平,數據採樣在第2個邊沿,即SCL由高電平到低電平的跳變,數據採樣在下升沿,數據發送在上降沿。
l 模式2,CPOL=1,CPHA=0;空閒時,SCL處於高電平,數據採樣在第1個邊沿,即SCL由高電平到低電平的跳變,數據採樣在下升沿,數據發送在上降沿。
l 模式3,CPOL=1,CPHA=1;空閒時,SCL處於高電平,數據採樣在第2個邊沿,即SCL由低電平到高電平的跳變,數據採樣在上升沿,數據發送在下降沿。

22.1.2 SPI控制器介紹

ESP32-S3芯片集成了四個SPI控制器,分別為SPI0、SPI1、SPI2和SPI3。SPI0和SPI1控制器主要供內部使用以訪問外部FLASH和PSRAM,所以只能使用SPI2和SPI3。SPI2又稱為HSPI,而SPI3又稱為VSPI,這兩個屬於GP-SPI。
GP-SPI特性:

   1,支持主機模式和從機模式
   2,支持半雙工通信和全雙工通信
   3,支持多種數據模式:

l SPI2:1-bit SPI模式、2-bit Dual SPI模式、4-bit Quad SPI模式、QPI模式、8-bit Octal模式、OPI模式
l SPI3:1-bit SPI模式、2-bit Dual SPI模式、4-bit Quad SPI模式、QPI模式

   4,時鐘頻率可配置:

l 在主機模式下:時鐘頻率可達80MHz
l 在從機模式下:時鐘頻率可達60MHz
5,數據位的讀寫順序可配置
6,時鐘極性和相位可配置
7,四種SPI時鐘模式:模式0 ~ 模式3
8,在主機模式下,提供多條CS線
l SPI2:CS0 ~ CS5
l SPI3:CS0 ~ CS2
9,支持訪問SPI接口的傳感器、顯示屏控制器、flash或RAM芯片
SPI2和SPI3接口相關信號線可以經過GPIO交換矩陣和IO_MUX實現與芯片引腳的映射,IO使用起來非常靈活。

22.1.3 LCD介紹

本例程僅支持兩款屏幕,一款是正點原子的1.3寸顯示模塊ATK-MD0130,另一款是正點原子2.4寸顯示模塊ATK-MD0240。這兩款顯示模塊的LCD分辨率分別為240 240和320 240,支持16位真彩色顯示。模塊採用ST7789V作為LCD的驅動芯片,該芯片自帶RAM,無需外加驅動器或存儲器。使用外接的主控芯片時,僅需使用SPI接口就可以輕鬆地驅動這兩個顯示模塊。
屏幕模塊通過2 * 4的排針(2.54m間距)同外部相連接,該模塊可直接與正點原子DNESP32-S3開發板的WIRELESS接口(SPI 接口)連接,而對於沒有板載WIRELESS接口的開發板,可以通過杜邦線連接。
ATK-MD0130模塊的外觀,如下圖所示。

圖22.1.3.1 ATK-MD0130模塊實物圖
模塊的原理圖,如下圖所示。

圖22.1.3.2 ATK-MD0130模塊原理圖
模塊通過一個2 * 4的排針(2.54mm間距)同外部電路連接,各引腳的詳細描述,如下表所示。

表22.1.3.1 ATK-MD0130和ATK-MD0240模塊引腳説明

22.1.4 模塊SPI時序介紹

ATK-MD0130和ATK-MD0240模塊在四線SPI通訊模式下,最少僅需四根信號線(CS、SCK、SDA、WR(DC))就能夠完成與這兩個顯示模塊的通訊,四線SPI接口時序如下圖所示。

圖22.1.4.1 四線SPI接口時序圖
上圖中各個時間參數,如下圖所示。

圖22.1.4.2 四線SPI接口時序時間參數
從上圖中可以看出,ATK-MD0130和ATK-MD0240模塊四線SPI的寫週期是非常快的(TSCYCW = 66ns),而讀週期就相對慢了很多(TSCYCR =150ns)。
更詳細的時序介紹,可以參考ST7789V的數據手冊《ST7789V_SPEC_V1.4.pdf》。

22.1.5 模塊驅動説明

ATK-MD0130和ATK-MD0240模塊採用ST7789V作為LCD驅動器,LCD的顯存可直接存放在ST7789V的片上RAM中,ST7789V的片上RAM有240 320 3字節,並且ST7789V會在沒有外部時鐘的情況下,自動將其片上RAM的數據顯示至LCD上,以最小化功耗。
在每次初始化顯示模塊之前,必須先通過RST引腳對顯示模塊進行硬件復位,硬件復位要求RST至少被拉低10微秒,拉高RST結束硬件復位後,須延時120毫秒等待復位完成後,才能夠往顯示模塊傳輸數據。
PWR引腳用於控制顯示模塊的LCD背光,該引腳自帶下拉電阻,當PWR引腳被拉低或懸空時,ATK-MD0130模塊的LCD背光都處於關閉狀態,當PWR引腳被拉高時,顯示模塊的LCD背光才會點亮。
ST7789V最高支持18位色深(262K色),但一般在希納是模塊上使用16位色深(65K色)的RGB565格式,這樣可以在16位色深下達到最快的速度。在16位色深模式下,ST7789V採用RGB565格式傳輸、存儲顏色數據,如下圖所示。

圖22.1.5.1 16位色深模式(RGB565)傳輸顏色數據
如上圖所示,一個像素的顏色數據需要使用16比特來傳輸,這16比特數據中,高5比特用於表示紅色,低5比特用於表示藍色,中間的6比特用於表示綠色。數據的數值越大,對應表示的顏色就越深。
ST7789V支持連續讀寫RAM中存放的LCD上顏色對應的數據,並且連續讀寫的方向(LCD的掃描方向)是可以通過命令0x36進行配置的,如下圖所示。

圖22.1.5.2 命令0x36
從上圖中可以看出,命令0x36可以配置6個參數,但對於配置LCD的掃描方向,僅需關心MY、MX和MV這三個參數,如下表所示。

表22.1.5.1 命令0x36配置LCD掃描方向
這樣一來,就能夠大大地提高ATK-MD0130和ATK-MD0240模塊在刷屏時的效率,僅需設置一次座標,然後連續地往ATK-MD0130和ATK-MD0240模塊傳輸顏色數據即可。
在往ATK-MD0130和ATK-MD0240模塊寫入顏色數據前,還需要設置地址,以確定隨後寫入的顏色數據對應LCD上的哪一個像素,通過命令0x2A和命令0x2B可以分別設置ATK-MD0130和ATK-MD0240模塊顯示顏色數據的列地址和行地址,命令0x2A的描述,如下圖所示。

圖22.1.5.3 命令0x2A
命令0x2B的描述,如下圖所示。

圖22.1.5.4 命令0x2B
以默認的LCD掃描方式(從左到右,從上到下)為例,命令0x2A的參數XS和XE和命令0x2B的參數YS和YE就在LCD上確定了一個區域,在連讀讀寫顏色數據時,ST7789V就會按照從左到右,從上到下的掃描方式讀寫設個區域的顏色數據。

22.2 硬件設計

22.2.1 例程功能

本章實驗功能簡介:使用開發板的SPI接口連接正點原子 SPI LCD模塊(僅限SPI顯示模塊),實現SPI LCD模塊的顯示。通過把LCD模塊插入底板上的WIRELESS接口(SPI 接口),按下復位之後,就可以看到SPI LCD模塊不停的顯示一些信息並不斷切換底色。LED閃爍用於提示程序正在運行。

22.2.2 硬件資源

  1. LED
    LED - IO1
  2. 正點原子1.3/2.4寸SPI LCD模塊

    22.2.3 原理圖

    本章實驗使用了正點原子的SPI LCD模塊(兼容正點原子1.3/2.4寸的SPI LCD模塊),該模塊與板載的WIRELESS接口進行連接,該接口與板載MCU的連接原理圖,如下圖所示:

    圖22.2.3.1 SPILCD模塊與MCU的連接原理圖

    22.3 程序設計

    22.3.1 程序流程圖

    程序流程圖能幫助我們更好的理解一個工程的功能和實現的過程,對學習和設計工程有很好的主導作用。下面看看本實驗的程序流程圖:

    圖22.3.1.1 SPI_LCD實驗程序流程圖

    22.3.2 SPI_LCD函數解析

    ESP-IDF提供了一套API來配置SPI。要使用此功能,需要導入必要的頭文件:

#include "driver/spi_master.h"

接下來,作者將介紹一些常用的ESP32-S3中的SPI函數,以及IO擴展芯片中用到的函數,這些函數的描述及其作用如下:
1,初始化和配置
該函數用於初始化SPI總線,並配置其GPIO引腳和主模式下的時鐘等參數,該函數原型如下所示:

esp_err_t spi_bus_initialize(spi_host_device_thost_id,
                              const spi_bus_config_t *bus_config,
spi_dma_chan_tdma_chan);

該函數的形參描述如下表所示:

表22.3.2.1spi_bus_initialize()函數形參描述
返回值:ESP_OK配置成功。其他配置失敗。
該函數使用spi_bus_config_t類型的結構體變量傳入,筆者此處列舉了我們需要用到的結構體,該結構體的定義如下所示:

typedef struct {
    int miso_io_num;        /* MISO引腳號 */     
    int mosi_io_num;        /* MOSI引腳號 */     
    int sclk_io_num;        /* 時鐘引腳號 */   
    int quadwp_io_num;      /* 用於Quad模式的WP引腳號,未使用時設置為-1 */  
    int quadhd_io_num;      /* 用於Quad模式的HD引腳號,未使用時設置為-1 */  
int max_transfer_sz;    /* 最大傳輸大小 */
…                        /* 其他特定的配置參數 */
}spi_bus_config_t;

完成上述結構體參數配置之後,可以將結構傳遞給spi_bus_initialize () 函數,用以實例化SPI。
2,設備配置
該函數用於在SPI總線上分配設備,函數原型如下所示:

esp_err_t spi_bus_add_device(spi_host_device_t host_id,
                              const spi_device_interface_config_t *dev_config,                               spi_device_handle_t *handle);

該函數的形參描述,如下表所示:

表22.3.2.2 函數spi_bus_add_device()形參描述
返回值:ESP_OK配置成功。其他配置失敗。
該函數使用spi_host_device_t類型以及spi_device_interface_config_t類型的結構體變量傳入SPI外圍設備的配置參數,該結構體的定義如下所示:

/**
* @brief 帶有三個SPI外圍設備的枚舉,這些外圍設備可通過軟件訪問
*/
typedef enum {
    /* SPI1只能在ESP32上用作GPSPI */
    SPI1_HOST = 0,    /* SPI1 */
    SPI2_HOST = 1,    /* SPI2 */
#if SOC_SPI_PERIPH_NUM > 2
    SPI3_HOST = 2,    /* SPI3 */
#endif
SPI_HOST_MAX,   /* 無效的主機值 */
}spi_host_device_t
typedef struct {
    uint32_t command_bits;      /* 命令階段的位數 */
    uint32_t address_bits;      /* 地址階段的位數 */
    uint32_t dummy_bits;        /* 虛擬階段的位數 */
    int clock_speed_hz;         /* 時鐘速率 */
    uint32_t mode;              /* SPI模式(0-3) */
    int spics_io_num;           /* CS引腳號 */
    ...                         /* 其他設備特定的配置參數 */
}spi_device_interface_config_t;

3,數據傳輸
根據函數功能,以下函數可以歸為一類進行講解,下面將以表格的形式逐個介紹這些函數的作用與參數。

表22.3.2.3 SPI數據傳輸函數描述

22.3.3 SPI_LCD驅動解析

在IDF版的12_spilcd例程中,作者在12_spilcd\components\BSP路徑下新增了一個SPI文件夾和一個LCD文件夾,分別用於存放spi.c、spi.h和lcd.c以及lcd.h這四個文件。其中,spi.h和lcd.h文件負責聲明SPI以及LCD相關的函數和變量,而spi.c和lcd.c文件則實現了SPI以及LCD的驅動代碼。下面,我們將詳細解析這四個文件的實現內容。
1,spi.h文件

/* 引腳定義 */
#define SPI_MOSI_GPIO_PIN   GPIO_NUM_11         /* SPI2_MOSI */
#define SPI_CLK_GPIO_PIN    GPIO_NUM_12         /* SPI2_CLK */
#define SPI_MISO_GPIO_PIN   GPIO_NUM_13         /* SPI2_MISO */

該文件下定義了SPI的時鐘引腳與通訊引腳。
2,spi.c文件

/**
*@brief       初始化SPI
*@param       無
*@retval      無
*/
void spi2_init(void)
{
   esp_err_t ret = 0;
   spi_bus_config_t spi_bus_conf = {0};
    /* SPI總線配置 */
   spi_bus_conf.miso_io_num = SPI_MISO_GPIO_PIN;  /* SPI_MISO引腳 */                             
   spi_bus_conf.mosi_io_num = SPI_MOSI_GPIO_PIN;  /* SPI_MOSI引腳 */                             
   spi_bus_conf.sclk_io_num = SPI_CLK_GPIO_PIN;   /* SPI_SCLK引腳 */                             
   spi_bus_conf.quadwp_io_num = -1;   /* SPI寫保護信號引腳,該引腳未使能 */                                         
   spi_bus_conf.quadhd_io_num = -1;    /* SPI保持信號引腳,該引腳未使能 */                                          
   spi_bus_conf.max_transfer_sz = 320 * 240 * 2;/* 配置最大傳輸大小,以字節為單位 */                             
   
    /* 初始化SPI總線 */
    ret=spi_bus_initialize(SPI2_HOST, &spi_bus_conf, SPI_DMA_CH_AUTO);        
   ESP_ERROR_CHECK(ret);               /* 校驗參數值 */                                                      
}
/**
*@brief       SPI發送命令
*@param       handle : SPI句柄
*@param       cmd    : 要發送命令
*@retval      無
*/
voidspi2_write_cmd(spi_device_handle_thandle, uint8_t cmd)
{
   esp_err_t ret;
   spi_transaction_t t = {0};
    t.length = 8;                                   /* 要傳輸的位數 一個字節 8位 */
    t.tx_buffer = &cmd;                             /* 將命令填充進去 */
    ret=spi_device_polling_transmit(handle, &t);  /* 開始傳輸 */
   ESP_ERROR_CHECK(ret);                          /* 一般不會有問題 */
}
/**
*@brief       SPI發送數據
*@param       handle : SPI句柄
*@param       data   : 要發送的數據
*@param       len    : 要發送的數據長度
*@retval      無
*/
voidspi2_write_data(spi_device_handle_thandle, const uint8_t *data, int len)
{
   esp_err_t ret;
   spi_transaction_t t = {0};
    if (len == 0)
    {
       return;                                     /* 長度為0 沒有數據要傳輸 */
    }
    t.length = len * 8;                             /* 要傳輸的位數 一個字節 8位 */
    t.tx_buffer = data;                             /* 將命令填充進去 */
    ret=spi_device_polling_transmit(handle, &t);  /* 開始傳輸 */
   ESP_ERROR_CHECK(ret);                          /* 一般不會有問題 */
}
/**
*@brief       SPI處理數據
*@param       handle       : SPI句柄
*@param       data         : 要發送的數據
*@retval      t.rx_data[0] : 接收到的數據
*/
uint8_t spi2_transfer_byte(spi_device_handle_t handle, uint8_t data)
{
   spi_transaction_t t;
   memset(&t, 0, sizeof(t));
    t.flags =SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA;
    t.length = 8;
    t.tx_data[0] = data;
   spi_device_transmit(handle, &t);
    return t.rx_data[0];
}

在spi2_init()函數中主要工作就是對於SPI參數的配置,如SPI管腳配置和數據傳輸大小以及SPI總線配置等,通過該函數就可以完成SPI初始化。
SPI驅動中對SPI的各種操作,請讀者結合SPI的時序規定查看本實驗的配套實驗源碼。
3,lcd.h文件
lcd.c和lcd.h文件是驅動函數和引腳接口宏定義以及函數聲明等。lcdfont.h頭文件存放了4種字體大小不一樣的ASCII字符集(12 12、16 16、24 24和32 32)。這個跟oledfont.h頭文件一樣的,只是這裏多了32 * 32的ASCII字符集,製作方法請回顧OLED實驗。下面我們還是先介紹lcd.h文件,首先是LCD的引腳定義:

/* 引腳定義 */
#define LCD_NUM_WR      GPIO_NUM_40
#define LCD_NUM_CS      GPIO_NUM_21
/* IO操作 */
#define LCD_WR(x)       do{ x ? \
                           (gpio_set_level(LCD_NUM_WR, 1)):   \
                           (gpio_set_level(LCD_NUM_WR, 0));   \
                        }while(0)
#define LCD_CS(x)       do{ x ? \
                           (gpio_set_level(LCD_NUM_CS, 1)):   \
                           (gpio_set_level(LCD_NUM_CS, 0));   \
                        }while(0)
#define LCD_PWR(x)       do{ x ? \
                           (xl9555_pin_write(SLCD_PWR_IO, 1)): \
                           (xl9555_pin_write(SLCD_PWR_IO, 0)); \
                        }while(0)
#define LCD_RST(x)       do{ x ? \
                           (xl9555_pin_write(SLCD_RST_IO, 1)): \
                           (xl9555_pin_write(SLCD_RST_IO, 0)); \
                        }while(0)
/* 常用顏色值 */
#define WHITE           0xFFFF      /* 白色 */
#define BLACK           0x0000      /* 黑色 */
#define RED             0xF800      /* 紅色 */
#define GREEN           0x07E0      /* 綠色 */
#define BLUE            0x001F      /* 藍色 */
#define MAGENTA         0XF81F      /* 品紅色/紫紅色 = BLUE +RED */
#define YELLOW          0XFFE0      /* 黃色 = GREEN +RED */
#define CYAN            0X07FF      /* 青色 = GREEN +BLUE */  
/* 非常用顏色 */
#define BROWN           0XBC40      /* 棕色 */
#define BRRED           0XFC07      /* 棕紅色 */
#define GRAY            0X8430      /* 灰色 */
#define DARKBLUE        0X01CF      /* 深藍色 */
#define LIGHTBLUE       0X7D7C      /* 淺藍色 */
#define GRAYBLUE        0X5458      /* 灰藍色 */
#define LIGHTGREEN      0X841F      /* 淺綠色 */  
#define LGRAY           0XC618      /* 淺灰色(PANNEL),窗體背景色 */
#define LGRAYBLUE       0XA651      /* 淺灰藍色(中間層顏色) */
#define LBBLUE          0X2B12      /* 淺棕藍色(選擇條目的反色) */
/* 掃描方向定義 */
#define L2R_U2D         0           /* 從左到右,從上到下 */
#define L2R_D2U         1           /* 從左到右,從下到上 */
#define R2L_U2D         2           /* 從右到左,從上到下 */
#define R2L_D2U         3           /* 從右到左,從下到上 */
#define U2D_L2R         4           /* 從上到下,從左到右 */
#define U2D_R2L         5           /* 從上到下,從右到左 */
#define D2U_L2R         6           /* 從下到上,從左到右 */
#define D2U_R2L         7           /* 從下到上,從右到左 */
#define DFT_SCAN_DIR    L2R_U2D    /* 默認的掃描方向 */
/* 屏幕選擇 */
#define LCD_320X240     0
#define LCD_240X240     1
/* LCD信息結構體 */
typedef struct _lcd_obj_t
{
    uint16_t        width;         /* 寬度 */
    uint16_t        height;        /* 高度 */
    uint8_t         dir;           /* 橫屏還是豎屏控制:0,豎屏;1,橫屏。 */
    uint16_t        wramcmd;       /* 開始寫gram指令 */
    uint16_t        setxcmd;       /* 設置x座標指令 */
    uint16_t        setycmd;       /* 設置y座標指令 */
    uint16_t        wr;            /* 命令/數據IO */
    uint16_t        cs;            /* 片選IO */
} lcd_obj_t;
/* LCD緩存大小設置,修改此值時請注意!!!!修改這兩個值時可能會影響以下函數 lcd_clear/lcd_fill/lcd_draw_line */
#define LCD_TOTAL_BUF_SIZE      (320 * 240 * 2)
#define LCD_BUF_SIZE            15360
/* 導出相關變量 */
extern lcd_obj_t lcd_self;
extern uint8_t lcd_buf[LCD_TOTAL_BUF_SIZE];

第一部分的宏定義是對WR/CS引腳的定義,第二部分宏定義是LCD_WR/CS/PWR/RST引腳操作的定義,接下來的部分是對一些常用顏色的RGB數值以及LCD信息結構體的定義。
4,lcd.c文件

/**
* @brief       LCD初始化
* @param       無
* @retval      無
*/
void lcd_init(void)
{
    int cmd = 0;
    esp_err_t ret = 0;
   
    lcd_self.dir = 0;
    lcd_self.wr = LCD_NUM_WR;                                   /* 配置WR引腳 */
    lcd_self.cs = LCD_NUM_CS;                                   /* 配置CS引腳 */
   
    gpio_config_tgpio_init_struct;
    /* SPI驅動接口配置 */
   spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 60 * 1000 * 1000,                 /* SPI時鐘 */
        .mode = 0,                                             /* SPI模式0 */
        .spics_io_num = lcd_self.cs,                       /* SPI設備引腳 */
        .queue_size = 7,                                      /* 事務隊列尺寸 7個 */
    };
   
    /* 添加SPI總線設備 */
    ret = spi_bus_add_device(SPI2_HOST, &devcfg, &MY_LCD_Handle);
    ESP_ERROR_CHECK(ret);
   gpio_init_struct.intr_type = GPIO_INTR_DISABLE;    /* 失能引腳中斷 */
   gpio_init_struct.mode = GPIO_MODE_OUTPUT;              /* 配置輸出模式 */
   gpio_init_struct.pin_bit_mask = 1ull << lcd_self.wr;   /* 配置引腳位掩碼 */
   gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE; /* 失能下拉 */
   gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE;      /* 使能上拉 */
    gpio_config(&gpio_init_struct);                     /* 引腳配置 */
    lcd_hard_reset();                                       /* LCD硬件復位 */
    /* 初始化代碼,對2.4寸LCD寄存器進行設置 */
#if SPI_LCD_TYPE
    lcd_init_cmd_tili_init_cmds[] =
    {
        (……此處省略初始化代碼……)
    };
#else   /* 不為0則視為使用1.3寸SPILCD屏,那麼屏幕將不會反顯 */
    lcd_init_cmd_tili_init_cmds[] =
    {
       (……此處省略初始化代碼……)    };
#endif
    /* 循環發送設置所有寄存器 */
    while (ili_init_cmds[cmd].databytes != 0xff)
    {
       lcd_write_cmd(ili_init_cmds[cmd].cmd);
       lcd_write_data(ili_init_cmds[cmd].data,
                       ili_init_cmds[cmd].databytes & 0x1F);
        
        if (ili_init_cmds[cmd].databytes & 0x80)
        {
           vTaskDelay(120);
        }
        
        cmd++;
    }
    lcd_display_dir(1);                                     /* 設置屏幕方向 */
    LCD_PWR(1);
    lcd_clear(WHITE);                                       /* 清屏 */
}

從上的代碼中可以看出,本章實驗的SPILCD驅動是兼容了正點原子的1.3寸與2.4寸SPILCD模塊的,因此在加載完SPI設備後,會與SPILCD進行通訊,確定SPILCD的型號,然後根據型號針對性地對SPILCD模塊進行配置。
SPILCD驅動中與SPILCD模塊通訊的函數,如下所示:

/**
* @brief       發送命令到LCD,使用輪詢方式阻塞等待傳輸完成(由於數據傳輸量很少,因此在輪詢方               式處理可提高速度。使用中斷方式的開銷要超過輪詢方式)
* @param       cmd 傳輸的8位命令數據
* @retval      無
*/
void lcd_write_cmd(const uint8_t cmd)
{
    LCD_WR(0);
    spi2_write_cmd(MY_LCD_Handle, cmd);
}
/**
* @brief       發送數據到LCD,使用輪詢方式阻塞等待傳輸完成(由於數據傳輸量很少,因此在輪詢方               式處理可提高速度。使用中斷方式的開銷要超過輪詢方式)
* @param       data 傳輸的8位數據
* @retval      無
*/
void lcd_write_data(const uint8_t *data, int len)
{
    LCD_WR(1);
    spi2_write_data(MY_LCD_Handle, data, len);
}
/**
* @brief        發送數據到LCD,使用輪詢方式阻塞等待傳輸完成(由於數據傳輸量很少,因此在輪詢方式處理可提高速度。使用中斷方式的開銷要超過輪詢方式)
* @param       data 傳輸的16位數據
* @retval      無
*/
void lcd_write_data16(uint16_t data)
{
    uint8_t dataBuf[2] = {0,0};
    dataBuf[0] = data >> 8;
    dataBuf[1] = data & 0xFF;
    LCD_WR(1);
    spi2_write_data(MY_LCD_Handle, dataBuf,2);
}

在上述代碼中,lcd_write_cmd()和lcd_write_data()在調用spi的驅動函數前,按照LCD時序圖,前者需要先將WR引腳電平信號置0,後者則需要置1。
通過上面介紹的驅動函數就能夠與SPILCD模塊進行通訊了,而在SPILCD模塊的顯示屏上顯示出特定的圖案或字符或設置SPILCD模塊的顯示方向等等的操作都是能夠通過SPILCD模塊規定的特定命令來完成的,想深究的讀者可以產看正點原子SPILCD模塊的用户手冊或查看實際使用的SPILCD模塊的相關文檔。

22.3.4 CMakeLists.txt文件

打開本實驗BSP下的CMakeLists.txt文件,其內容如下所示:

set(src_dirs
           IIC
           LCD
           LED
           SPI
           XL9555)
set(include_dirs
           IIC
           LCD
           LED
           SPI
           XL9555)
set(requires
           driver)
idf_component_register(SRC_DIRS ${src_dirs}
INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})
component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)

上述的紅色LCD與SPI驅動需要由開發者自行添加,以確保SPI_LCD驅動能夠順利集成到構建系統中。這一步驟是必不可少的,它確保了SPI_LCD驅動的正確性和可用性,為後續的開發工作提供了堅實的基礎。

22.3.5 實驗應用代碼

打開main/main.c文件,該文件定義了工程入口函數,名為app_main。該函數代碼如下。

i2c_obj_t i2c0_master;
/**
* @brief       程序入口
* @param       無
* @retval      無
*/
void app_main(void)
{
    uint8_t x = 0;
    esp_err_t ret;
    ret = nvs_flash_init();               /* 初始化NVS */
if (ret ==ESP_ERR_NVS_NO_FREE_PAGES
|| ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
       ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    led_init();                             /* 初始化LED */
    i2c0_master = iic_init(I2C_NUM_0);      /* 初始化IIC0 */
    spi2_init();                            /* 初始化SPI2 */
    xl9555_init(i2c0_master);            /* IO擴展芯片初始化 */
    lcd_init();                             /* 初始化LCD */
    while (1)
    {
        switch (x)
        {
            case 0:
            {
               lcd_clear(WHITE);
                break;
            }
            case 1:
            {
               lcd_clear(BLACK);
                break;
            }
            case 2:
            {
               lcd_clear(BLUE);
                break;
            }
            case 3:
            {
               lcd_clear(RED);
                break;
            }
            case 4:
            {
               lcd_clear(MAGENTA);
                break;
            }
            case 5:
            {
               lcd_clear(GREEN);
                break;
            }
            case 6:
            {
               lcd_clear(CYAN);
                break;
            }
            case 7:
            {
               lcd_clear(YELLOW);
                break;
            }
            case 8:
            {
               lcd_clear(BRRED);
                break;
            }
            case 9:
            {
               lcd_clear(GRAY);
                break;
            }
            case 10:
            {
               lcd_clear(LGRAY);
                break;
            }
            case 11:
            {
               lcd_clear(BROWN);
                break;
            }
        }
       lcd_show_string(10, 40, 240, 32, 32, "ESP32", RED);
       lcd_show_string(10, 80, 240, 24, 24, "SPILCDTEST", RED);
       lcd_show_string(10, 110, 240, 16, 16, "ATOM@ALIENTEK", RED);
        x++;
        if (x == 12)
        {
            x = 0;
        }
        LED_TOGGLE();
        vTaskDelay(500);
    }
}

從上面的代碼中可以看出,在初始化完LCD後,便在LCD上顯示一些本實驗的相關信息,隨後便每間隔500毫秒就更換一次LCD屏幕顯示的背景色。

22.4 下載驗證

在完成編譯和燒錄操作後,可以看到SPI LCD上不斷變換着不同的顏色,LED燈閃爍。

圖22.4.1 SPI LCD顯示效果圖

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.