ESP32使用SPI总线驱动LCD
ST7789液晶屏开发
前言
在前言里面我想告诉你一些在对一款新的处理器的学习中,我们会遇到的那些错误,并且我相信这绝对不会是我一个人会出现的问题,出现这种问题首先原因是处理器的教程不够全面,没有像stm32一样一套保姆级别的教程能够一点一点引导着大家去学习,第二个原因就是esp32更新版本很快,导致一些教程会因为大版本的更新而不能使用。这里我就想站在初学者的角度来进行一番探索,也希望大家看完我出错的的过程后能够有所启发。
初次尝试
在学习一款处理器的过程中,如何连接外设,配置外设接口并且成功驱动外设是非常重要的,经过前面的学习,我们结合其他处理器的学习经验,例如stm32,再结合乐鑫官方的教程和示例工程,其实是很容易就能控制一些常规的GPIO口,USART,I2C接口的外设或者通信。
但是在我学习到LCD屏幕时,却遇到了很大的阻碍,和往常一样,在我准备配置这块1.3寸的驱动为st7789的屏幕时,我照例打开乐鑫官方的编程指南ESP-IDF 编程指南 - ESP32 - — ESP-IDF 编程指南 latest 文档 (espressif.com)。
找到API参考,外设API,进而找到LCD这个目录,然后我就发现没有中文,这种情况我已经不是第一次遇到了,所以我很熟练的进行手册的版本切换,从原来的master版切换到v5.1.3版本,切换完成后我就看到它上面提示到最新稳定版是v5.2.1,所以我就又点击它切换到5.2.1版本。但是令人失望的是依旧没有出现我们想要的中文。那么我就明白了这一个说明大概率是没有中文的,但是没关系,英文版本我们一样看得懂,毕竟现在互联网这么发达,找到一个翻译实在是太容易了。
首先映入眼帘的就是功能概述了,我从中提取到三个关键信息
- 乐鑫提供了开箱即用的LCD驱动程序
- 有安装驱动程序的步骤
- 有控制LCD IO的API
我这一看这不是妥了吗,基本上和之前GPIO,USART,I2C一样,按着官方的教程一步一步来,应该很轻松就能把这个屏幕初始化完成。殊不知这才是噩梦的开始。
并没有找到驱动
首先是即使乐鑫官方明说了有st7789的驱动程序,我也没有在乐鑫组件注册表中找到它,上面所提到的两款驱动,st7789和ssd1306,我只能搜索到ssd1306的驱动组件。但是这也无伤大雅,驱动组件中无非是定义着一些有关驱动的参数罢了,相关参数我们完全可以按照stm32开发时的流程,直接在st7789驱动的数据手册里面或者是商家给的驱动说明里面提取即可。
初次尝试安装SPI LCD IO 驱动程序
然后就是去点击SPI Interfaced LCD链接进入安装SPI LCD IO 驱动程序然后获取面板句柄的步骤的页面,实际上这些内容就在下面,直接向下滚动就能够看到。
我这一看,嚯,一二三步骤都标好了,这不就是我最需要的吗,当时我心里还在想,乐鑫官方是真的贴心啊,学习成本大大降低了,之后就是我按照上面的三个步骤,进行我的SPI配置,先创建SPI总线,然后从 SPI 总线分配 LCD IO 设备句柄,最后就是安装液晶屏控制器驱动程序,一切都是那么的水到渠成。
初次使用控制LCD IO的API
依旧是点击LCD 面板 IO 操作 链接,转到下图界面
然后我选择了这三个看起来有用的初始化函数,进行一一调用。但是当我调用完成以后,我迷茫了,是的跟着手册一路配置加初始化到这里我不知道我该干什么了,按理说我们现在已经初始化完成了SPI和LCD的相关配置,接着就是收发数据了,但是我完全不知道该如何向LCD屏中发送数据,而手册中唯一和发送数据相关的就是esp_lcd_panel_draw_bitmap()函数,这个函数是将用户提供的颜色缓冲区绘制到LCD屏幕上。
当时我没有感觉到又哪里不对,心里想的是这不就是个画点函数,有了画点函数我不随便编写其他的绘画函数。所以当时我选择了先编译一下程序看看,一方面是想看初始化完成后到底是个什么情况,另一方面也是要确定我的程序确实没有问题能够运行起来。
很成功,在解决了编译时出现的小问题后,程序编译成功并且也烧录成功了,但是屏幕这个时候并没有点亮。在了解到初始化完成后屏幕也不会点亮,必须手动填充色块的相关情况后,我决定使用esp_lcd_panel_draw_bitmap()这个函数来试着填充一下屏幕区域,但是这时候我发现了另外的问题,虽然这个函数的参数和画点函数比较相似,都是传递起始坐标和结束坐标,另外就是传递一个颜色指针。但是我如果想填满整个屏幕,我就需要定义一个240x240大小的数组,并且我还要给他填充数据,这也太傻鸟了吧,我在心里忍不住吐槽。于是我就想着只填充几行看看效果就行,定义了一个20x20的数组,将数组的地址传递进去之后,按照它这函数的作用,应该是会显示一个小方块,不过经过我重新烧录验证,屏幕依旧是是没有任何反应。20的数组,将数组的地址传递进去之后,按照它这函数的作用,应该是会显示一个小方块,不过经过我重新烧录验证,屏幕依旧是是没有任何反应。
到这里我就开始怀疑我的程序到底是不是有问题了,于是我不再局限于官方的编程手册,转而到网络上搜索相关的教程,事实证明我这一步决策是十分正确的,网上的相关教程确实补充了我之前根据官方编程手册的一些不足之处(实际上也是官方的另外的教程罢了)。
二次尝试
由于大部分人使用esp32做项目都是用Arduino这个开发环境,所以我在搜索教程时也遇到了一定困难,中间具体的搜索过程我就不细说了,直接讲最后的搜索结果,我在bilibili上找到了一个用esp-idf移植驱动一块触摸屏的教程,起初一段我觉得这个教程讲的十分的详细,所以第二次我就完全按照这个教程来进行学习ESP32-S3 开发 SPI 屏【DIY 智能手表】_哔哩哔哩_bilibili。
依照这个教程,我们再次找到了另外的LCD官方教程SPI LCD 详解 - - — ESP-IoT-Solution latest 文档 (espressif.com), 这次的教程相比之前详细了不少,经过阅读这篇教程(视频前面上其实也是参考了这个教程),我发现我在第一次尝试的时候没有初始化SPI总线 ,也没有配置LCD 驱动 IC 的初始化命令及参数。
所以根据视频上的流程,我对之前的代码进行了更改,最后呈现出一下状态,由于我没有使用官方的驱动配置头文件,没有想视频中一样修改一下其他同类型的驱动头文件,所以关于驱动头文件里面声明的结构体我直接放在了我自己的头文件当中:
1 |
|
当我完成上述代码后,由于视频中是触摸屏,并且还要移植LVGL图形界面,但是我现在还不想一步到位,我只想先实现屏幕能够点亮,能够显示静态的文字跟图片,所以视频后面关于配置触屏I2C以及LVGL的移植代码我就并没有添加。
到了这一步应该是配置已经事无巨细了,可实际当我满怀信息的去烧录完成等着屏幕点亮,却让我异常失望,屏幕并没有亮,这不禁让我怀疑我的屏是不是有问题。这个想法一下子就让我闻到了成功的气息,而问题的转折点也就出现在这里。
最终尝试
为了测试我的屏幕是否有问题,我使用了商家给的代码,并且把代码移植到我自己的F103C8T6程序上,结果是能够使用商家的程序成功点亮这块屏幕,看着keil中熟悉的代码,然后我就有了新的想法,我们不要使用乐鑫教程中普遍使用的官方的配置函数,而是自己通过SPI总线向stm32中一样配置发送命令,发送数据的函数,然后所有的流程都严格依照32中的流程进行,这样不就能够可行了吗。
所以我最后抛弃掉了所有的教程,开始了自己对32控制lcd屏幕的流程的完全移植,到了最后我重要是功夫不负有心人,在第一次尝试后的第二天完成了屏幕点亮。
正片
配置SPI控制器
由于屏幕使用了SPI协议进行通信,那么我们点亮屏幕的第一步就是配置SPI,这一段在我们之前尝试的时候都有提到,我把代码放在这里,相信大家看到代码后就能理解这一环节到底在做些什么:
1 |
|
我稍微解释一下,大家可以看到我PIN_NUM_CS和PIN_NUM_MISO都定义为了-1,这是因为我们这块屏幕并没有使用这两根线,所以给-1表示不使用此功能。
编写LCD屏幕初始化函数
LCD_Init
1 | /*st7789*/ |
这一步就是初始化LCD的最关键的步骤,就是发送命令配置屏幕的参数,这一部分基本上就是参考购买屏幕的商家给的示例程序来进行配置
可以看到我们在初始化的时候用到了发送命令和发送8位数据的函数,这需要我们自己编写,我这里的所有步骤都是基于对stm32的完全模仿,而且这也是我们初学esp32的最好方式,不要一上来就想着使用官方所提供好的功能函数,因为很可能你根本就搞不明白这些功能函数的到底有什么用处。
LCD_WR_GER
1 | void LCD_WR_REG(uint8_t dat) |
LCD_WR_DATA8
1 | void LCD_WR_DATA8(uint8_t dat) |
lcd_cmd
1 | void lcd_cmd(const uint8_t cmd) |
下面我们就是要解决最后第一个问题了,可以看到的是,我在LCD_Init中最后使用了一个清屏函数,只要我们想办法编写这个刷白函数,就能点亮屏幕,实现全屏颜色显示,而且在上面我们已经编写过传输8位指令或者数据的一个函数lcd_cmd,我们要使用这个函数的话就要循环调用2402402次lcd_cmd函数,这样做可以是可以但是有点拖拉,所以在这个函数的基础上,我们可以编写一个一次能够传送指定数据大小的函数:
LCD_data_x
1 | void LCD_data_x(uint16_t *dat,uint32_t len) |
有了这个函数,我们在编写清屏函数时就不用调用如此之多的lcd_cmd了,下面就是我对清屏函数的实现:
1 | void LCD_Clear(uint16_t Color) |
可以发现在清屏函数中我一次传输了两行的颜色数据,所以for循环中我只需要循环二分之一的屏幕宽度次就可以了,这样的操作相比于一次传输半个颜色数据在输出效率上会高一些,因为我们使用的16位颜色,但是之前的函数一次才传递8位,意味着我们一个颜色数据就要传输两次才能传输成功,现在这样就显然合理了很多。
上面的函数里面我们用到了许多宏,这些我都把它放在了头文件中,在下面放出供大家参考使用:
LCD_Init.h
1 |
至此,大家可以在主函数中尽情的调用LCD_Init函数来对屏幕进行初始化了,相信初始化完成后,你们也能成功点亮自己LCD屏。
基于 ESP32 的 LCD 模块(ST7789)开发
初始化命令
1 | DRAM_ATTR static const uint8_t LCD_ORIENTATION_VALUES[]={ |
相关链接(侵删)
欢迎到公众号来唠嗑: