第二十二章 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 硬件資源
- LED
LED - IO1 -
正點原子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顯示效果圖