正点原子 发表于 4 天前

《ESP32-S3使用指南—MicroPython版 V1.0》第十六章 SPILCD实验


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


第十六章 SPILCD实验

       在本章实验中,我们将通过编写MicroPython驱动程序来实现SPILCD显示。在开发板上,我们已经预留了SPILCS模块接口,因此需要准备一个SPILCD显示模块。我们将一起点亮SPILCS,并实现字符的显示。
       16.1 SPILCD模块简介
       16.2 SPILCD C模块解析
       16.3 硬件设计
       16.4 软件设计
       16.5 下载验证

       16.1 SPILCD模块简介
       本例程仅支持两种屏幕,一种是ATK-MD0130的1.3寸显示模块,另一种是ATK-MD0240的2.4寸显示模块。这两种显示模块是由正点原子推出的高性能LCD显示模块。这两个显示模块的LCD分辨率为240*240和320*240像素,支持16位真彩色显示。模块采用ST7789V作为LCD的驱动芯片,该芯片自带RAM,无需外加驱动器或存储器。使用外接的主控芯片时,仅需使用SPI接口就可以轻松地驱动这两个显示模块。
       ATK-MD0130和ATK-MD0240 模块的各项基本参数,如下表所示。

表16.1.1 ATK-MD0130和ATK-MD0240 模块基本参数
       ATK-MD0130和ATK-MD0240模块通过2*4的排针(2.54mm 间距)同外部相连接,该模块可直接与正点原子DNESP32-S3开发板和正点原子MiniSTM32H750开发板等开发板的WIRELESS接口(SPI 接口)连接,而对于没有板载WIRELESS接口的开发板,可以通过杜邦线连接。正点原子大部分的STM32开发板,我们都提供了本模块相应的例程,用户可以直接在这些开发板上,对模块进行测试。
       ATK-MD0130模块的外观,如下图所示。

图16.1.1 ATK-MD0130模块实物图
       ATK-MD0130和ATK-MD0240模块的原理图,如下图所示。

图16.1.2 ATK-MD0130模块原理图
       ATK-MD0130和ATK-MD0240模块通过一个2*4的排针(2.54mm间距)同外部电路连接,各引脚的详细描述,如下表所示。

表16.1.2 ATK-MD0130和ATK-MD0240模块引脚说明
       16.1.1 模块SPI时序介绍
       ATK-MD0130和ATK-MD0240模块在四线SPI通讯模式下,最少仅需四根信号线(CS、SCK、SDA、WR(DC))就能够完成与这两个显示模块的通讯,四线SPI接口时序如下图所示。

图16.1.1.1 四线SPI接口时序图
       上图中各个时间参数,如下图所示。

图16.1.1.2 四线SPI接口时序时间参数
       从上图中可以看出,ATK-MD0130和ATK-MD0240模块四线SPI的写周期是非常快的(TSCYCW = 66ns),而读周期就相对慢了很多(TSCYCR = 150ns)。
       更详细的时序介绍,可以参考ST7789V的数据手册《ST7789V_SPEC_V1.4.pdf》。

       16.1.2 模块驱动说明
       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格式传输、存储颜色数据,如下图所示。

图16.1.2.1 16位色深模式(RGB565)传输颜色数据
       如上图所示,一个像素的颜色数据需要使用16比特来传输,这16比特数据中,高5比特用于表示红色,低5比特用于表示蓝色,中间的6比特用于表示绿色。数据的数值越大,对应表示的颜色就越深。
       ST7789V支持连续读写RAM中存放的LCD上颜色对应的数据,并且连续读写的方向(LCD的扫描方向)是可以通过命令0x36进行配置的,如下图所示。

图16.1.2.2 命令0x36
       从上图中可以看出,命令0x36可以配置6个参数,但对于配置LCD的扫描方向,仅需关心MY、MX和MV这三个参数,如下表所示。

表16.1.2.1 命令0x36配置LCD扫描方向
       这样一来,就能够大大地提高ATK-MD0130和ATK-MD0240模块在刷屏时的效率,仅需设置一次坐标,然后连续地往ATK-MD0130和ATK-MD0240模块传输颜色数据即可。
       在往ATK-MD0130和ATK-MD0240模块写入颜色数据前,还需要设置地址,以确定随后写入的颜色数据对应LCD上的哪一个像素,通过命令0x2A和命令0x2B可以分别设置ATK-MD0130和ATK-MD0240模块显示颜色数据的列地址和行地址,命令0x2A的描述,如下图所示。

图2.3.3 命令0x2A
       命令0x2B的描述,如下图所示。

图2.3.4 命令0x2B
       以默认的LCD扫描方式(从左到右,从上到下)为例,命令0x2A的参数XS和XE和命令0x2B的参数YS和YE就在LCD上确定了一个区域,在连读读写颜色数据时,ST7789V就会按照从左到右,从上到下的扫描方式读写设个区域的颜色数据。

       16.2 SPILCD C模块解析

       16.2.1 C模块解析
       作者将简要介绍正点原子SPILCD C模块驱动。这个讲解内容会分为几个部分:SPILCD构造函数、写数据、写命令。SPILCD C模块驱动可在A盘6,软件资料1,软件2,MicroPython开发工具01-Windows2,正点原子MicroPython驱动CModules_LibLCD路径下找到。

       1,SPILCD构造函数
mp_obj_t lcd_make_new(const mp_obj_type_t *type,size_t n_args,size_t n_kw
,const mp_obj_t *all_args )
{
    /* 创建对象的参数 */
    enum
    {
      ARG_spi, ARG_reset, ARG_dc, ARG_cs,
      ARG_backlight, ARG_dir,ARG_lcd,
    };
    /* 创建对象参数的默认值 */
    static const mp_arg_t allowed_args[] = {
      { MP_QSTR_spi, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} },
      { MP_QSTR_reset, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
      { MP_QSTR_dc, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
      { MP_QSTR_cs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
      { MP_QSTR_backlight, MP_ARG_KW_ONLY|MP_ARG_OBJ,{.u_obj = MP_OBJ_NULL} },
      { MP_QSTR_dir, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
      { MP_QSTR_lcd, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
    };
      
    mp_arg_val_t args;
mp_arg_parse_all_kw_array(n_args, n_kw, all_args,
                        MP_ARRAY_SIZE(allowed_args), allowed_args, args);

    /* 创建对象 */
    self = m_new_obj(lcd_obj_t);
    self->base.type = &lcd_type;

    /* 设置对象参数 */
mp_obj_base_t *spi_obj= (mp_obj_base_t*)
MP_OBJ_TO_PTR(args.u_obj);
    self->spi_obj         = spi_obj;
    self->dir               = args.u_int;
    self->lcd               = args.u_int;
   
    /* SPI控制块是否为空 */
    if (spi_obj == MP_OBJ_NULL)
    {
      mp_raise_ValueError(MP_ERROR_TEXT("SPI init ???"));
    }
    /* 屏幕方向只能横屏和竖屏 */
    if (self->dir != 1 && self->dir != 0)
    {
      mp_raise_ValueError(MP_ERROR_TEXT("Not horizontal and
vertical screens"));
    }
    /* LCD类型选择 */
    if (self->lcd != 1 && self->lcd != 0)
    {
      mp_raise_ValueError(MP_ERROR_TEXT("Only supports 320x240(0)
and 240x240(1)"));
    }
    /* DC命令/数据选择管脚 */
    if (args.u_obj == MP_OBJ_NULL) {
      mp_raise_ValueError(MP_ERROR_TEXT("must specify dc pin"));
    }
    /* 背光管脚 */
    if (args.u_obj != MP_OBJ_NULL) {
      self->backlight = mp_hal_get_pin_obj(args.u_obj);
    }
    /* 复位管脚 */
    if (args.u_obj != MP_OBJ_NULL) {
      self->reset = mp_hal_get_pin_obj(args.u_obj);
    }
    /* 获取DC管脚号 */
    self->dc = mp_hal_get_pin_obj(args.u_obj);
    /* 片选管脚 */
    if (args.u_obj != MP_OBJ_NULL) {
      self->cs = mp_hal_get_pin_obj(args.u_obj);
    }
    /* 执行LCD初始化序列 */
    lcd_dv_init();
   
    return MP_OBJ_FROM_PTR(self);
}       根据上述源代码可以看出,作者首先创建了一个LCD对象控制块,然后设置了LCD对象的属性。接着,系统判断SPI控制块、LCD类型、LCD扫描方向和各个管脚是否满足相关条件。如果不满足条件,系统将不再执行下去,并在Shell交互窗口下提示错误信息。最后,系统调用了lcd_dv_init函数来初始化LCD序列,并返回LCD对象。

       2,SPILCD写命令函数
/**
* @brief       发送命令到LCD,使用轮询方式阻塞等待传输完成(由于数据传输量很少,
因此在轮询方式处理可提高速度。使用中断方式的开销要超过轮询方式)
* @param       cmd 传输的8位命令数据
* @retval      无
*/
STATIC void lcd_write_cmd(const uint8_t cmd)
{
    CS_LOW();
    DC_LOW();
    mp_obj_base_t *s = (mp_obj_base_t *)MP_OBJ_TO_PTR(lcd_self->spi_obj);
mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t *)
MP_OBJ_TYPE_GET_SLOT(s->type, protocol);
    spi_p->transfer(s, 1, &cmd, NULL);
    CS_HIGH();
}       上述源码中,作者首先拉低DC数据/命令和CS片选管脚的电平,接着根据传入的SPI控制块,调用了SPI收发函数来发送SPILCD命令。发送完成后,拉高CS片选管脚的电平。

       3,SPILCD写数据函数
/**
* @brief       发送数据到LCD,使用轮询方式阻塞等待传输完成(由于数据传输量很少,
因此在轮询方式处理可提高速度。使用中断方式的开销要超过轮询方式)
* @param       data 传输的8位数据
* @retval      无
*/
STATIC void lcd_write_data(const uint8_t *data, int len){
    CS_LOW();
    DC_HIGH();
    mp_obj_base_t *s = (mp_obj_base_t *)MP_OBJ_TO_PTR(lcd_self->spi_obj);
mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t *)
MP_OBJ_TYPE_GET_SLOT(s->type, protocol);
    spi_p->transfer(s, len, data, NULL);
    CS_HIGH();
}       上述源码中,作者首先拉高DC数据/命令和拉低CS片选管脚的电平,接着,调用了SPI收发函数来发送SPILCD数据。发送完成后,拉高CS片选管脚的电平。其他函数,如画线、画点等,请参考lcd.c/.h文件。

       16.2.2 C模块构造与类的方法

       1,atk_lcd类与SPI构造函数
       Lcd和SPI的构造对象方法如下:
class atk_lcd.init(spi,reset,dc,cs,backlight,dir,lcd)
使用示例:
spi=SPI(2,baudrate=80000000, sck = Pin(12), mosi = Pin(11), miso = Pin(13))
lcd=atk_lcd.init(spi,dc=Pin(40,Pin.OUT),cs=Pin(21,Pin.OUT,value=1),dir=1,lcd=0)       该构造方法的参数描述,如下表所示。

表16.2.2.1 atk_lcd.init构造函数参数描述
       返回值:LCD对象。

       2,lcd类的方法

       ①:打开LCD背光。
       其函数原型如下:
lcd.on()
       ②:关闭LCD背光。
       其函数原型如下:
lcd.off()
       ③:LCD清屏。
       其函数原型如下:
lcd.clear(color)       该函数的参数描述,如下表所示。

表16.2.2.2 lcd.clear函数参数描述
       ④:LCD画点。
       其函数原型如下:
lcd.pixel(x,y,colot)       该函数的参数描述,如下表所示。

表16.2.2.3 lcd.clear函数参数描述
       ⑤:LCD画线。
       其函数原型如下:
lcd.line(x1,y1,x2,y2,color)       该函数的参数描述,如下表所示。

表16.2.2.4 lcd.line函数参数描述
       ⑥:LCD画一个矩形。
       其函数原型如下:
lcd.rectangle (x0,y0,x1,y1,color)       该函数的参数描述,如下表所示。

表16.2.2.5 lcd.rectangle函数参数描述
       ⑦:LCD画一个圆。
       其函数原型如下:
lcd.circle (x0,y0,x1,y1,color)       该函数的参数描述,如下表所示。

表16.2.2.6 lcd.circle函数参数描述
       ⑧:LCD显示字符。
       其函数原型如下:
lcd.char(x,y,chr,size,mode,color)       该函数的参数描述,如下表所示。

表16.2.2.7 lcd.char函数参数描述
       ⑨:LCD显示len个数字。
       其函数原型如下:
lcd.num(x,y,num,len,size,color)       该函数的参数描述,如下表所示。

表16.2.2.7 lcd.num函数参数描述
       ⑩:LCD显示字符串。
       其函数原型如下:
lcd.string(x,y,width,heught,size,p,color)       该函数的参数描述,如下表所示。

表16.2.2.8 lcd.string函数参数描述
       11:LCD填充区域。
       其函数原型如下:
lcd.fill(x0,y0,x1,y1,color)       该函数的参数描述,如下表所示。

表16.2.2.9 lcd.fill函数参数描述
       12,RGB888转RGB565,返回RGB565颜色数值。
       其函数原型如下:
atk_lcd.color565(r,g,b)       该函数的参数描述,如下表所示。

表16.2.2.10 atk_lcd.color565函数参数描述
       16.3 硬件设计

       1. 例程功能
       本章实验功能简介:使用开发板的SPI接口连接正点原子 SPILCD模块(仅限SPI显示模块),实现SPILCD模块的显示。通过把LCD模块插入底板上的WIRELESS接口(SPI 接口),按下复位之后,就可以看到SPILCD模块不停的显示一些信息并不断切换底色。LED闪烁用于提示程序正在运行。

       2. 硬件资源
       1)LED灯
              LED-IO1

       2)XL9555
              IIC_INT-IO0(需在P5连接IO0)
              IIC_SDA-IO41
              IIC_SCL-IO42

       3)SPILCD
              CS-IO21
              SCK-IO12
              SDA-IO11
              DC-IO40(在P5端口,使用跳线帽将IO_SET和LCD_DC相连)
              PWR- IO1_3(XL9555)
              RST- IO1_2(XL9555)

       3. 原理图
       SPILCD硬件部分的原理图,如下图所示。

图16.3.1 SPILCD接口原理图
       16.4 软件设计

       16.4.1 程序流程图
       程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图。


图16.3.1.1 程序流程图
       16.4.2 程序解析
       本书籍的代码都在main.py脚本下编写的,读者可在光盘资料下找到对应的源码。SPILCD实验main.py源码如下:
from machine import Pin,SPI,I2C
import atk_xl9555 as io_ex
import atk_lcd as lcd
import time
"""
* @brief       程序入口
* @param       无
* @retval      无
"""
if __name__ == '__main__':
    x = 0
    # IIC初始化
    i2c0 = I2C(0, scl = Pin(42), sda = Pin(41), freq = 400000)
    # XL9555初始化
    xl9555 = io_ex.init(i2c0)
    # 复位LCD
    xl9555.write_bit(io_ex.SLCD_RST,0)
    time.sleep_ms(100)
    xl9555.write_bit(io_ex.SLCD_RST,1)
    time.sleep_ms(100)
    # 初始化SPI
    spi=SPI(2,baudrate=80000000, sck = Pin(12), mosi = Pin(11), miso = Pin(13))
    # 初始化LCD,lcd = 0为正点原子2.4寸屏幕;lcd = 1为正点原子1.3寸SPILCD屏幕;
    display=lcd.init(spi,dc=Pin(40,Pin.OUT),cs=Pin(21, Pin.OUT),dir = 1,lcd = 0)
    # 打开背光
    xl9555.write_bit(io_ex.SLCD_PWR,1)
    time.sleep_ms(100)
    while True:
      #创建字典
      seasondict = {
      0: lcd.BLACK,
      1: lcd.BLUE,
      2: lcd.RED,
      3: lcd.GREEN,
      4: lcd.CYAN,
      5: lcd.MAGENTA,
      6: lcd.YELLOW}
      #刷新颜色
      display.clear(seasondict)
      #显示字体
      display.string(0, 5, 240, 32, 32, "ESP32-S3",lcd.RED)
      display.string(0, 34, 240, 16, 16, "SPI LCD Test",lcd.RED)
      display.string(0, 50, 240, 16, 16, "ATOM@ALIENTEK",lcd.RED)
      x += 1
      if x == 7:
            x = 0
      time.sleep(1)       这示例代码使用SPI和I2C接口与外部设备进行通信,并通过特定的库函数控制2.4寸LCD显示屏的显示内容。具体来说,代码中初始化了I2C和SPI接口,然后使用这些接口与XL9555芯片通信,以控制LCD显示屏的复位和背光。接着,它通过调用特定库(atk_lcd)提供的函数在LCD屏幕上显示文本和颜色。在循环中,它使用一个变量x来循环显示不同的颜色,并在屏幕上显示固定的文本。当变量x的值达到7时,它会被重置为0,从而循环显示。

       16.5 下载验证
       下载代码后,LED不停的闪烁,提示程序已经在运行了。同时可以看到SPILCD模块的显示实验信息,并且背景色不停切换,如下图所示。

图16.5.1 SPILCD显示效果图
页: [1]
查看完整版本: 《ESP32-S3使用指南—MicroPython版 V1.0》第十六章 SPILCD实验