nodemcu 使用 Lua 编写/移植 ST7735S 0.96 寸彩色 LCD 屏幕驱动(SPI 通信)
本站提供了很多现成的屏幕驱动供大家使用,也写了很多文章教大家如何绘图、显示中文、动画制作等。现在,让我们来自己编写/移植一款彩色屏幕的驱动,这样大家以后也可以把驱动移植到其它平台,或者举一反三,自行学会编写其它型号的屏幕驱动。当然,你也可以移植到其它语言比如 nodejs,Lua,C 来驱动此屏幕。本站已经实现了 C, NodeJS, Lua, Python 各种语言的版本,可以运行在单片机,树莓派,ESP8266/32等各种平台。
先介绍一下 ST7735S LCD 屏幕,这个屏幕的主控型号是使用的 ST7735S,分辨率是 160*80,接口是 SPI。它是 LCD 屏幕,色彩和可视角度都比 TFT 好多了,尤其是色彩鲜艳度和细腻度。此款屏幕可以接 3.3V 或者 5V。注意,不接背光接口它也能亮,只是会发生闪烁现象和屏幕灰暗,所以看到背光闪烁多半是因为电压不够或者没有接背光导致的。它支持 262K 颜色,即 565 格式,使用 2 个字节表示 RGB 颜色。
上面的图片是本店中出售的 ST7735S SPI 接口的屏幕,分辨率是 160*80。
驱动例程
我们是按照厂家给的驱动例程来移植的,所以大家在阅读此文章时,请先查看这篇文章 树莓派使用 Python 编写/移植 ST7735S 0.96 寸彩色 LCD 屏幕驱动(SPI 通信) 里面的 驱动移植部分。
硬件接线
下面是 nodemcu 的引脚图。
程序的开头已经注释了如何接线,请大家自己对照 nodemcu 的引脚图接线。屏幕的 VCC 和 GND 是电源,记得也要接,可以接 3.3V。因为搞电子模块的大家都知道电源是必备的,之前的文章就省略了,然后很多人问我屏幕为什么不亮,我也是很无奈啊。
注意:请一定以屏幕的实际引脚编号为准,如你不清楚如何接,请本店咨询客服,电源反接必烧毁,本店概不负责,屏幕会产生大量热量,注意烫伤手。
在本店购买的屏幕除非特殊说明,默认接 3.3V 电压,防止烧坏。其它地方购买的屏幕,请一定注意电源,烧坏本人不负责。
移植到 nodemcu
前篇文章我们讲解了如何在树莓派上使用 Python 移植驱动。下面我们把它移植到 nodemcu 上。ESP8266 自带了硬件 SPI,所以我们移植起来非常简单,只需要把 SPI 通信部分替换成硬件 SPI,然后将 C 代码改写成 Lua 就行了。上完整代码:
-- Hardware SPI CLK = GPIO14
-- Hardware SPI MOSI = GPIO13
-- Hardware SPI MISO = GPIO12 (not used)
cs = 8 -- GPIO15, pull-down 10k to GND
dc = 4 -- GPIO2
res = 0 -- GPIO16
bus = 1
node.setcpufreq(node.CPU160MHZ)
function spi_begin (id)
spi.setup(id, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, 8, 2)
-- we won't be using the HSPI /CS line, so disable it again
gpio.mode(cs, gpio.INPUT, gpio.PULLUP)
-- setup command and data pin.
gpio.mode(dc, gpio.OUTPUT)
gpio.mode(res, gpio.OUTPUT)
end
function spi_write (id, data)
spi.send(id, data)
end
function delay_ms (ms)
tmr.delay(ms * 1000)
end
function lcd_writeCmd(cmd)
gpio.write(dc, 0)
spi_write(bus, cmd)
end
function lcd_writeData (data)
gpio.write(dc, 1)
spi_write(bus, data)
end
function lcd_reset ()
gpio.write(res, 0)
delay_ms(100)
gpio.write(res, 1)
end
function lcd_init ()
lcd_reset()
lcd_writeCmd(0x11)
delay_ms(120)
lcd_writeCmd(0x21)
lcd_writeCmd(0xB1)
lcd_writeData(0x05)
lcd_writeData(0x3A)
lcd_writeData(0x3A)
lcd_writeCmd(0xB2)
lcd_writeData(0x05)
lcd_writeData(0x3A)
lcd_writeData(0x3A)
lcd_writeCmd(0xB3)
lcd_writeData(0x05)
lcd_writeData(0x3A)
lcd_writeData(0x3A)
lcd_writeData(0x05)
lcd_writeData(0x3A)
lcd_writeData(0x3A)
lcd_writeCmd(0xB4)
lcd_writeData(0x03)
lcd_writeCmd(0xC0)
lcd_writeData(0x62)
lcd_writeData(0x02)
lcd_writeData(0x04)
lcd_writeCmd(0xC1)
lcd_writeData(0xC0)
lcd_writeCmd(0xC2)
lcd_writeData(0x0D)
lcd_writeData(0x00)
lcd_writeCmd(0xC3)
lcd_writeData(0x8D)
lcd_writeData(0x6A)
lcd_writeCmd(0xC4)
lcd_writeData(0x8D)
lcd_writeData(0xEE)
lcd_writeCmd(0xC5)
lcd_writeData(0x0E)
lcd_writeCmd(0xE0)
lcd_writeData(0x10)
lcd_writeData(0x0E)
lcd_writeData(0x02)
lcd_writeData(0x03)
lcd_writeData(0x0E)
lcd_writeData(0x07)
lcd_writeData(0x02)
lcd_writeData(0x07)
lcd_writeData(0x0A)
lcd_writeData(0x12)
lcd_writeData(0x27)
lcd_writeData(0x37)
lcd_writeData(0x00)
lcd_writeData(0x0D)
lcd_writeData(0x0E)
lcd_writeData(0x10)
lcd_writeCmd(0xE1)
lcd_writeData(0x10)
lcd_writeData(0x0E)
lcd_writeData(0x03)
lcd_writeData(0x03)
lcd_writeData(0x0F)
lcd_writeData(0x06)
lcd_writeData(0x02)
lcd_writeData(0x08)
lcd_writeData(0x0A)
lcd_writeData(0x13)
lcd_writeData(0x26)
lcd_writeData(0x36)
lcd_writeData(0x00)
lcd_writeData(0x0D)
lcd_writeData(0x0E)
lcd_writeData(0x10)
lcd_writeCmd(0x3A)
lcd_writeData(0x05)
lcd_writeCmd(0x36)
lcd_writeData(0xC8)
lcd_writeCmd(0x29)
end
function lcd_setRegion(x1, y1, x2, y2)
lcd_writeCmd(0x2a)
lcd_writeData(0x00)
lcd_writeData(x1 + 0x1A)
lcd_writeData(0x00)
lcd_writeData(x2 + 0x1A)
lcd_writeCmd(0x2b)
lcd_writeData(0x00)
lcd_writeData(y1 + 1)
lcd_writeData(0x00)
lcd_writeData(y2 + 1)
lcd_writeCmd(0x2c)
end
function lcd_fill (color1, color2)
lcd_setRegion(0, 0, 79, 159)
for i=1,160 do
for j=1,80 do
lcd_writeData(color1)
lcd_writeData(color2)
end
tmr.wdclr()
end
end
function main()
spi_begin(bus)
lcd_init()
lcd_fill(0xf8, 0x00) -- red
lcd_fill(0x07, 0xe0) -- green
lcd_fill(0x00, 0x1f) -- blue
lcd_fill(0xff, 0xe0) -- yellow
lcd_fill(0xff, 0xff) -- white
end
main()
可以看到,代码基本和 C 是差不多的,只有 2 处不同,第一个是我们使用 ESP8266 的硬件 SPI 替换了模拟 SPI,延时函数用的是定时器模块。ESP8266 的硬件 SPI 使用方法请查看官方文档,没什么好说的。使用定时器是因为我们没法在应用层准确的知道执行时间,因为这是 Lua 解释器,指令的执行时间是不确定的。
第二处不同是我们在 lcd_fill
函数内增加了一个 tmr.wdclr()
函数调用。这个函数去查看 api 就知道是手动重置看门狗计时器。因为在 nodemcu 上默认开启了看门狗,而我们的清屏函数需要发送整屏的像素数据,并且是同步阻塞的。SPI 的速率没那么快,这就导致还没等我们发送整屏的数据后,看门狗被触发了,导致芯片被硬重启。然后你就会发现你的 ESP8266 一直在无限重启,屏幕总是在刷新到一半的时候就停止。所以我们在每次刷新完一行数据后,就手动重置看门狗,这样就不会无限重启了。虽然对刷新速度有一点影响,不过总算比不能用好。
将上面的代码保存为 init.lua
文件,上传到 nodemcu,屏幕的接线在程序开头就写了,大家自己对照 nodemcu 的引脚图接。然后复位就能看到屏幕在刷屏了。
到这里,最基本的驱动移植已经做完了,能点亮就成功了一大半了,剩下的就是如何封装更友好的图形 api,比如点、线、面、图片、文字之类的。在树莓派上我们有 pillow 这么好用的图形库,但是在 nodemcu 上没有啊,点、线、面这些简单的图形 api 还好说,可以使用现成的算法函数,代码也不多,但是文字和图片就没办法了。如果要显示文字,必须要有字库,英文的字库比较小,实现起来也不是很难,并且网上已经有很多点阵的英文字库了,nodemcu 的 u8g 已经集成了很多的英文字库,可以直接拿来用。中文的话,大家老老实实去取点阵。图片建议在 PC 上先预处理好,保存成像素数据文件后直接刷屏。