搜索
bottom↓
回复: 0
打印 上一主题 下一主题

《DNESP32S3使用指南-IDF版_V1.6》第二十二章 SPI_LCD实验

[复制链接]

出0入234汤圆

跳转到指定楼层
1
本帖最后由 正点原子 于 2024-7-22 16:30 编辑


1)实验平台:正点原子ESP32S3开发板
2)购买链接:https://detail.tmall.com/item.htm?id=768499342659
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-347618-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子手把手教你学ESP32S3快速入门视频教程:https://www.bilibili.com/video/BV1sH4y1W7Tc
6)正点原子FPGA交流群:132780729



第二十二章 SPI_LCD实验


       本章,我们将学习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,Serial Peripheral interface,顾名思义,就是串行外围设备接口,是由原摩托罗拉公司在其MC68HCXX系列处理器上定义的。SPI是一种高速的全双工、同步、串行的通信总线,已经广泛应用在众多MCU、存储芯片、AD转换器和LCD之间。
       SPI通信跟IIC通信一样,通信总线上允许挂载一个主设备和一个或者多个从设备。为了跟从设备进行通信,一个主设备至少需要4跟数据线,分别为:

       MOSI(Master Out / Slave In):主数据输出,从数据输入,用于主机向从机发送数据。

       MISO(Master In / Slave Out):主数据输入,从数据输出,用于从机向主机发送数据。

       SCLK(Serial Clock):时钟信号,由主设备产生,决定通信的速率。

       CS(Chip Select):从设备片选信号,由主设备产生,低电平时选中从设备。

       多从机SPI通信网络连接如下图所示。


图22.1.1.1 多从机SPI通信网络图

       从上图可以知道,MOSI、MISO、SCLK引脚连接SPI总线上每一个设备,如果CS引脚为低电平,则从设备只侦听主机并与主机通信。SPI主设备一次只能和一个从设备进行通信。如果主设备要和另外一个从设备通信,必须先终止和当前从设备通信,否则不能通信。
       SPI通信有4种不同的模式,不同的从机可能在出厂时就配置为某种模式,这是不能改变的。通信双方必须工作在同一模式下,才能正常进行通信,所以可以对主机的SPI模式进行配置。SPI通信模式是通过配置CPOL(时钟极性)和CPHA(时钟相位)来选择的。
       CPOL,详称Clock Polarity,就是时钟极性,当主从机没有数据传输的时候即空闲状态,SCL线的电平状态,假如空闲状态是高电平,CPOL=1;若空闲状态时低电平,那么CPOL = 0。
       CPHA,详称Clock Phase,就是时钟相位,实质指的是数据的采样时刻。CPHA = 0表示数据的采样是从第1个边沿信号上即奇数边沿,具体是上升沿还是下降沿的问题,是由CPOL决定的。CPHA=1表示数据采样是从第2个边沿即偶数边沿。
       SPI的4种模式对比图,如下图所示。

图22.1.1.2 SPI的4种模式对比图

       模式0,CPOL=0,CPHA=0;空闲时,SCL处于低电平,数据采样在第1个边沿,即SCL由低电平到高电平的跳变,数据采样在上升沿,数据发送在下降沿。

       模式1,CPOL=0,CPHA=1;空闲时,SCL处于低电平,数据采样在第2个边沿,即SCL由高电平到低电平的跳变,数据采样在下升沿,数据发送在上降沿。

       模式2,CPOL=1,CPHA=0;空闲时,SCL处于高电平,数据采样在第1个边沿,即SCL由高电平到低电平的跳变,数据采样在下升沿,数据发送在上降沿。

       模式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,支持多种数据模式:
       SPI2:1-bit SPI模式、2-bit Dual SPI模式、4-bit Quad SPI模式、QPI模式、8-bit Octal模式、OPI模式
       SPI3:1-bit SPI模式、2-bit Dual SPI模式、4-bit Quad SPI模式、QPI模式

        4,时钟频率可配置:
       在主机模式下:时钟频率可达80MHz
       在从机模式下:时钟频率可达60MHz

       5,数据位的读写顺序可配置

       6,时钟极性和相位可配置

       7,四种SPI时钟模式:模式0 ~ 模式3

       8,在主机模式下,提供多条CS线
       SPI2:CS0 ~ CS5
       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。要使用此功能,需要导入必要的头文件:
  1. #include "driver/spi_master.h"
复制代码
       接下来,作者将介绍一些常用的ESP32-S3中的SPI函数,以及IO扩展芯片中用到的函数,这些函数的描述及其作用如下:

       1,初始化和配置
       该函数用于初始化SPI总线,并配置其GPIO引脚和主模式下的时钟等参数,该函数原型如下所示:
  1. esp_err_t spi_bus_initialize(spi_host_device_t host_id,
  2.                                                           const spi_bus_config_t *bus_config,
  3. spi_dma_chan_t dma_chan);
复制代码
       该函数的形参描述如下表所示:

表22.3.2.1 spi_bus_initialize()函数形参描述

       返回值:ESP_OK配置成功。其他配置失败。
       该函数使用spi_bus_config_t类型的结构体变量传入,笔者此处列举了我们需要用到的结构体,该结构体的定义如下所示:
  1. typedef struct {
  2.     int miso_io_num;        /* MISO引脚号 */     
  3.     int mosi_io_num;        /* MOSI引脚号 */     
  4.     int sclk_io_num;        /* 时钟引脚号 */   
  5.     int quadwp_io_num;      /* 用于Quad模式的WP引脚号,未使用时设置为-1 */  
  6.     int quadhd_io_num;      /* 用于Quad模式的HD引脚号,未使用时设置为-1 */  
  7. int max_transfer_sz;    /* 最大传输大小 */
  8. …                                                 /* 其他特定的配置参数 */
  9. } spi_bus_config_t;
复制代码
       完成上述结构体参数配置之后,可以将结构传递给 spi_bus_initialize () 函数,用以实例化SPI。

       2,设备配置
       该函数用于在SPI总线上分配设备,函数原型如下所示:
  1. esp_err_t spi_bus_add_device(spi_host_device_t host_id,
  2.                                                           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外围设备的配置参数,该结构体的定义如下所示:
  1. /**
  2. * @brief 带有三个SPI外围设备的枚举,这些外围设备可通过软件访问
  3. */
  4. typedef enum {
  5.     /* SPI1只能在ESP32上用作GPSPI */
  6.     SPI1_HOST = 0,    /* SPI1 */
  7.     SPI2_HOST = 1,    /* SPI2 */
  8. #if SOC_SPI_PERIPH_NUM > 2
  9.     SPI3_HOST = 2,    /* SPI3 */
  10. #endif
  11. SPI_HOST_MAX,   /* 无效的主机值 */
  12. }spi_host_device_t

  13. typedef struct {
  14.     uint32_t command_bits;      /* 命令阶段的位数 */
  15.     uint32_t address_bits;      /* 地址阶段的位数 */
  16.     uint32_t dummy_bits;        /* 虚拟阶段的位数 */
  17.     int clock_speed_hz;         /* 时钟速率 */
  18.     uint32_t mode;              /* SPI模式(0-3) */
  19.     int spics_io_num;           /* CS引脚号 */
  20.     ...                         /* 其他设备特定的配置参数 */
  21. } 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文件
  1. /* 引脚定义 */
  2. #define SPI_MOSI_GPIO_PIN   GPIO_NUM_11         /* SPI2_MOSI */
  3. #define SPI_CLK_GPIO_PIN    GPIO_NUM_12         /* SPI2_CLK */
  4. #define SPI_MISO_GPIO_PIN   GPIO_NUM_13         /* SPI2_MISO */
复制代码
       该文件下定义了SPI的时钟引脚与通讯引脚。

       2,spi.c文件
  1. /**
  2. * @brief       初始化SPI
  3. * @param       无
  4. * @retval      无
  5. */
  6. void spi2_init(void)
  7. {
  8.     esp_err_t ret = 0;
  9.     spi_bus_config_t spi_bus_conf = {0};

  10.     /* SPI总线配置 */
  11.     spi_bus_conf.miso_io_num = SPI_MISO_GPIO_PIN;  /* SPI_MISO引脚 */                             
  12.     spi_bus_conf.mosi_io_num = SPI_MOSI_GPIO_PIN;  /* SPI_MOSI引脚 */                             
  13.     spi_bus_conf.sclk_io_num = SPI_CLK_GPIO_PIN;   /* SPI_SCLK引脚 */                             
  14.     spi_bus_conf.quadwp_io_num = -1;   /* SPI写保护信号引脚,该引脚未使能 */                                         
  15.     spi_bus_conf.quadhd_io_num = -1;          /* SPI保持信号引脚,该引脚未使能 */                                          
  16.     spi_bus_conf.max_transfer_sz = 320 * 240 * 2;/* 配置最大传输大小,以字节为单位 */                             
  17.    
  18.     /* 初始化SPI总线 */
  19.     ret = spi_bus_initialize(SPI2_HOST, &spi_bus_conf, SPI_DMA_CH_AUTO);        
  20.     ESP_ERROR_CHECK(ret);                                 /* 校验参数值 */                                                      
  21. }

  22. /**
  23. * @brief       SPI发送命令
  24. * @param       handle : SPI句柄
  25. * @param       cmd    : 要发送命令
  26. * @retval      无
  27. */
  28. void spi2_write_cmd(spi_device_handle_t handle, uint8_t cmd)
  29. {
  30.     esp_err_t ret;
  31.     spi_transaction_t t = {0};

  32.     t.length = 8;                                   /* 要传输的位数 一个字节 8位 */
  33.     t.tx_buffer = &cmd;                             /* 将命令填充进去 */
  34.     ret = spi_device_polling_transmit(handle, &t);  /* 开始传输 */
  35.     ESP_ERROR_CHECK(ret);                           /* 一般不会有问题 */
  36. }

  37. /**
  38. * @brief       SPI发送数据
  39. * @param       handle : SPI句柄
  40. * @param       data   : 要发送的数据
  41. * @param       len    : 要发送的数据长度
  42. * @retval      无
  43. */
  44. void spi2_write_data(spi_device_handle_t handle, const uint8_t *data, int len)
  45. {
  46.     esp_err_t ret;
  47.     spi_transaction_t t = {0};

  48.     if (len == 0)
  49.     {
  50.         return;                                     /* 长度为0 没有数据要传输 */
  51.     }
  52.     t.length = len * 8;                             /* 要传输的位数 一个字节 8位 */
  53.     t.tx_buffer = data;                             /* 将命令填充进去 */
  54.     ret = spi_device_polling_transmit(handle, &t);  /* 开始传输 */
  55.     ESP_ERROR_CHECK(ret);                           /* 一般不会有问题 */
  56. }

  57. /**
  58. * @brief       SPI处理数据
  59. * @param       handle       : SPI句柄
  60. * @param       data         : 要发送的数据
  61. * @retval      t.rx_data[0] : 接收到的数据
  62. */
  63. uint8_t spi2_transfer_byte(spi_device_handle_t handle, uint8_t data)
  64. {
  65.     spi_transaction_t t;

  66.     memset(&t, 0, sizeof(t));

  67.     t.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA;
  68.     t.length = 8;
  69.     t.tx_data[0] = data;
  70.     spi_device_transmit(handle, &t);

  71.     return t.rx_data[0];
  72. }
复制代码
       在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的引脚定义:
  1. /* 引脚定义 */
  2. #define LCD_NUM_WR      GPIO_NUM_40
  3. #define LCD_NUM_CS      GPIO_NUM_21

  4. /* IO操作 */
  5. #define LCD_WR(x)       do{ x ? \
  6.                             (gpio_set_level(LCD_NUM_WR, 1)):    \
  7.                             (gpio_set_level(LCD_NUM_WR, 0));    \
  8.                         }while(0)

  9. #define LCD_CS(x)       do{ x ? \
  10.                             (gpio_set_level(LCD_NUM_CS, 1)):    \
  11.                             (gpio_set_level(LCD_NUM_CS, 0));    \
  12.                         }while(0)

  13. #define LCD_PWR(x)       do{ x ? \
  14.                             (xl9555_pin_write(SLCD_PWR_IO, 1)): \
  15.                             (xl9555_pin_write(SLCD_PWR_IO, 0)); \
  16.                         }while(0)

  17. #define LCD_RST(x)       do{ x ? \
  18.                             (xl9555_pin_write(SLCD_RST_IO, 1)): \
  19.                             (xl9555_pin_write(SLCD_RST_IO, 0)); \
  20.                         }while(0)

  21. /* 常用颜色值 */
  22. #define WHITE           0xFFFF      /* 白色 */
  23. #define BLACK           0x0000      /* 黑色 */
  24. #define RED             0xF800      /* 红色 */
  25. #define GREEN           0x07E0      /* 绿色 */
  26. #define BLUE            0x001F      /* 蓝色 */
  27. #define MAGENTA         0XF81F      /* 品红色/紫红色 = BLUE + RED */
  28. #define YELLOW          0XFFE0      /* 黄色 = GREEN + RED */
  29. #define CYAN            0X07FF      /* 青色 = GREEN + BLUE */  

  30. /* 非常用颜色 */
  31. #define BROWN           0XBC40      /* 棕色 */
  32. #define BRRED           0XFC07      /* 棕红色 */
  33. #define GRAY            0X8430      /* 灰色 */
  34. #define DARKBLUE        0X01CF      /* 深蓝色 */
  35. #define LIGHTBLUE       0X7D7C      /* 浅蓝色 */
  36. #define GRAYBLUE        0X5458      /* 灰蓝色 */
  37. #define LIGHTGREEN      0X841F      /* 浅绿色 */  
  38. #define LGRAY           0XC618      /* 浅灰色(PANNEL),窗体背景色 */
  39. #define LGRAYBLUE       0XA651      /* 浅灰蓝色(中间层颜色) */
  40. #define LBBLUE          0X2B12      /* 浅棕蓝色(选择条目的反色) */

  41. /* 扫描方向定义 */
  42. #define L2R_U2D         0           /* 从左到右,从上到下 */
  43. #define L2R_D2U         1           /* 从左到右,从下到上 */
  44. #define R2L_U2D         2           /* 从右到左,从上到下 */
  45. #define R2L_D2U         3           /* 从右到左,从下到上 */
  46. #define U2D_L2R         4           /* 从上到下,从左到右 */
  47. #define U2D_R2L         5           /* 从上到下,从右到左 */
  48. #define D2U_L2R         6           /* 从下到上,从左到右 */
  49. #define D2U_R2L         7           /* 从下到上,从右到左 */

  50. #define DFT_SCAN_DIR    L2R_U2D     /* 默认的扫描方向 */

  51. /* 屏幕选择 */
  52. #define LCD_320X240     0
  53. #define LCD_240X240     1



  54. /* LCD信息结构体 */
  55. typedef struct _lcd_obj_t
  56. {
  57.     uint16_t        width;          /* 宽度 */
  58.     uint16_t        height;         /* 高度 */
  59.     uint8_t         dir;            /* 横屏还是竖屏控制:0,竖屏;1,横屏。 */
  60.     uint16_t        wramcmd;        /* 开始写gram指令 */
  61.     uint16_t        setxcmd;        /* 设置x坐标指令 */
  62.     uint16_t        setycmd;        /* 设置y坐标指令 */
  63.     uint16_t        wr;             /* 命令/数据IO */
  64.     uint16_t        cs;             /* 片选IO */
  65. } lcd_obj_t;

  66. /* LCD缓存大小设置,修改此值时请注意!!!!修改这两个值时可能会影响以下函数 lcd_clear/lcd_fill/lcd_draw_line */
  67. #define LCD_TOTAL_BUF_SIZE      (320 * 240 * 2)
  68. #define LCD_BUF_SIZE            15360

  69. /* 导出相关变量 */
  70. extern lcd_obj_t lcd_self;
  71. extern uint8_t lcd_buf[LCD_TOTAL_BUF_SIZE];
复制代码
       第一部分的宏定义是对WR/CS引脚的定义,第二部分宏定义是LCD_WR/CS/PWR/RST 引脚操作的定义,接下来的部分是对一些常用颜色的RGB数值以及LCD信息结构体的定义。

       4,lcd.c文件
  1. /**
  2. * @brief       LCD初始化
  3. * @param       无
  4. * @retval      无
  5. */
  6. void lcd_init(void)
  7. {
  8.     int cmd = 0;
  9.     esp_err_t ret = 0;
  10.    
  11.     lcd_self.dir = 0;
  12.     lcd_self.wr = LCD_NUM_WR;                                                                        /* 配置WR引脚 */
  13.     lcd_self.cs = LCD_NUM_CS;                                                                        /* 配置CS引脚 */
  14.    
  15.     gpio_config_t gpio_init_struct;

  16.     /* SPI驱动接口配置 */
  17.     spi_device_interface_config_t devcfg = {
  18.         .clock_speed_hz = 60 * 1000 * 1000,                                        /* SPI时钟 */
  19.         .mode = 0,                                                   /* SPI模式0 */
  20.         .spics_io_num = lcd_self.cs,                              /* SPI设备引脚 */
  21.         .queue_size = 7,                                          /* 事务队列尺寸 7个 */
  22.     };
  23.    
  24.     /* 添加SPI总线设备 */
  25.     ret = spi_bus_add_device(SPI2_HOST, &devcfg, &MY_LCD_Handle);
  26.     ESP_ERROR_CHECK(ret);

  27.     gpio_init_struct.intr_type = GPIO_INTR_DISABLE;                /* 失能引脚中断 */
  28.     gpio_init_struct.mode = GPIO_MODE_OUTPUT;                                /* 配置输出模式 */
  29.     gpio_init_struct.pin_bit_mask = 1ull << lcd_self.wr;        /* 配置引脚位掩码 */
  30.     gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE;        /* 失能下拉 */
  31.     gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE;                /* 使能上拉 */
  32.     gpio_config(&gpio_init_struct);                                                /* 引脚配置 */

  33.     lcd_hard_reset();                                                                                /* LCD硬件复位 */

  34.     /* 初始化代码,对2.4寸LCD寄存器进行设置 */
  35. #if SPI_LCD_TYPE
  36.     lcd_init_cmd_t ili_init_cmds[] =
  37.     {
  38.         (……此处省略初始化代码……)
  39.     };

  40. #else        /* 不为0则视为使用1.3寸SPILCD屏,那么屏幕将不会反显 */
  41.     lcd_init_cmd_t ili_init_cmds[] =
  42.     {
  43.        (……此处省略初始化代码……)    };
  44. #endif

  45.     /* 循环发送设置所有寄存器 */
  46.     while (ili_init_cmds[cmd].databytes != 0xff)
  47.     {
  48.         lcd_write_cmd(ili_init_cmds[cmd].cmd);
  49.         lcd_write_data(ili_init_cmds[cmd].data,
  50.                                            ili_init_cmds[cmd].databytes & 0x1F);
  51.         
  52.         if (ili_init_cmds[cmd].databytes & 0x80)
  53.         {
  54.             vTaskDelay(120);
  55.         }
  56.         
  57.         cmd++;
  58.     }

  59.     lcd_display_dir(1);                                                                                /* 设置屏幕方向 */
  60.     LCD_PWR(1);
  61.     lcd_clear(WHITE);                                                                                /* 清屏 */
  62. }
复制代码
       从上的代码中可以看出,本章实验的SPILCD驱动是兼容了正点原子的1.3寸与2.4寸SPILCD模块的,因此在加载完SPI设备后,会与SPILCD进行通讯,确定SPILCD的型号,然后根据型号针对性地对SPILCD模块进行配置。
       SPILCD驱动中与SPILCD模块通讯的函数,如下所示:
  1. /**
  2. * @brief       发送命令到LCD,使用轮询方式阻塞等待传输完成(由于数据传输量很少,因此在轮询方                            式处理可提高速度。使用中断方式的开销要超过轮询方式)
  3. * @param       cmd 传输的8位命令数据
  4. * @retval      无
  5. */
  6. void lcd_write_cmd(const uint8_t cmd)
  7. {
  8.     LCD_WR(0);
  9.     spi2_write_cmd(MY_LCD_Handle, cmd);
  10. }

  11. /**
  12. * @brief       发送数据到LCD,使用轮询方式阻塞等待传输完成(由于数据传输量很少,因此在轮询方                            式处理可提高速度。使用中断方式的开销要超过轮询方式)
  13. * @param       data 传输的8位数据
  14. * @retval      无
  15. */
  16. void lcd_write_data(const uint8_t *data, int len)
  17. {
  18.     LCD_WR(1);
  19.     spi2_write_data(MY_LCD_Handle, data, len);
  20. }

  21. /**
  22. * @brief        发送数据到LCD,使用轮询方式阻塞等待传输完成(由于数据传输量很少,因此在轮询方式处理可提高速度。使用中断方式的开销要超过轮询方式)
  23. * @param       data 传输的16位数据
  24. * @retval      无
  25. */
  26. void lcd_write_data16(uint16_t data)
  27. {
  28.     uint8_t dataBuf[2] = {0,0};
  29.     dataBuf[0] = data >> 8;
  30.     dataBuf[1] = data & 0xFF;
  31.     LCD_WR(1);
  32.     spi2_write_data(MY_LCD_Handle, dataBuf,2);
  33. }
复制代码
       在上述代码中,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文件,其内容如下所示:
  1. set(src_dirs
  2.             IIC
  3.             LCD
  4.             LED
  5.             SPI
  6.             XL9555)

  7. set(include_dirs
  8.             IIC
  9.             LCD
  10.             LED
  11.             SPI
  12.             XL9555)

  13. set(requires
  14.             driver)

  15. idf_component_register(SRC_DIRS ${src_dirs}
  16. INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})

  17. 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。该函数代码如下。
  1. i2c_obj_t i2c0_master;

  2. /**
  3. * @brief       程序入口
  4. * @param       无
  5. * @retval      无
  6. */
  7. void app_main(void)
  8. {
  9.     uint8_t x = 0;
  10.     esp_err_t ret;
  11.     ret = nvs_flash_init();                    /* 初始化NVS */
  12. if (ret == ESP_ERR_NVS_NO_FREE_PAGES
  13. || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  14.     {
  15.         ESP_ERROR_CHECK(nvs_flash_erase());
  16.         ret = nvs_flash_init();
  17.     }
  18.     led_init();                                 /* 初始化LED */
  19.     i2c0_master = iic_init(I2C_NUM_0);          /* 初始化IIC0 */
  20.     spi2_init();                                /* 初始化SPI2 */
  21.     xl9555_init(i2c0_master);                   /* IO扩展芯片初始化 */
  22.     lcd_init();                                 /* 初始化LCD */
  23.     while (1)
  24.     {
  25.         switch (x)
  26.         {
  27.             case 0:
  28.             {
  29.                 lcd_clear(WHITE);
  30.                 break;
  31.             }
  32.             case 1:
  33.             {
  34.                 lcd_clear(BLACK);
  35.                 break;
  36.             }
  37.             case 2:
  38.             {
  39.                 lcd_clear(BLUE);
  40.                 break;
  41.             }
  42.             case 3:
  43.             {
  44.                 lcd_clear(RED);
  45.                 break;
  46.             }
  47.             case 4:
  48.             {
  49.                 lcd_clear(MAGENTA);
  50.                 break;
  51.             }
  52.             case 5:
  53.             {
  54.                 lcd_clear(GREEN);
  55.                 break;
  56.             }
  57.             case 6:
  58.             {
  59.                 lcd_clear(CYAN);
  60.                 break;
  61.             }
  62.             case 7:
  63.             {
  64.                 lcd_clear(YELLOW);
  65.                 break;
  66.             }
  67.             case 8:
  68.             {
  69.                 lcd_clear(BRRED);
  70.                 break;
  71.             }
  72.             case 9:
  73.             {
  74.                 lcd_clear(GRAY);
  75.                 break;
  76.             }
  77.             case 10:
  78.             {
  79.                 lcd_clear(LGRAY);
  80.                 break;
  81.             }
  82.             case 11:
  83.             {
  84.                 lcd_clear(BROWN);
  85.                 break;
  86.             }
  87.         }
  88.         lcd_show_string(10, 40, 240, 32, 32, "ESP32", RED);
  89.         lcd_show_string(10, 80, 240, 24, 24, "SPILCD TEST", RED);
  90.         lcd_show_string(10, 110, 240, 16, 16, "ATOM@ALIENTEK", RED);
  91.         x++;
  92.         if (x == 12)
  93.         {
  94.             x = 0;
  95.         }
  96.         LED_TOGGLE();
  97.         vTaskDelay(500);
  98.     }
  99. }
复制代码
       从上面的代码中可以看出,在初始化完LCD后,便在LCD上显示一些本实验的相关信息,随后便每间隔500毫秒就更换一次LCD屏幕显示的背景色。

       22.4 下载验证
       在完成编译和烧录操作后,可以看到SPI LCD上不断变换着不同的颜色,LED灯闪烁。

图22.4.1 SPI LCD显示效果图

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-22 20:11

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表