上一家中,我们学习了如何在屏幕上显示一张图片,那怎me显示动态图片呢?其实原理很简单,我们把一个GIF动态图片,通过工具分成若干张图片,然后快速的切换显示,就可以看到动起来的效果。让我们一起来试试吧。

一、目的

在我们的240x240的oled屏幕上显示动态图片

二、环境

        ESP32 + 240x240的oled彩色屏幕+ Thonny IDE + 几根杜邦线

接线方式请看上一节,此处不再重复赘述。

三、准备GIF图片

        我们在百度上直接搜索你想要的动态图片即可,找到后下载到本地。

四、格式转换

我们下载一个图片格式转换工具,叫做:优速图片格式转换器

下载地址1:优速图片格式转换器免费版_优速图片格式转换器免费版下载_优速图片格式转换器V2.0.3-华军软件园

下载地址2:

优速图片格式转换器-优速图片格式转换器免费下载安装_优速办公软件

下载后,安装,然后打开,按如下步骤操作:

然后我们就得到若干张图片:

接下来我们需要对图片进行裁剪,变为240x240的大小。

五、图片裁剪

        我们先下载一个图片批量裁剪的工具,叫做:优速图片格式转换器

哈哈。。。还是上面的那个工具,功能强大哈。。。就是免费的有水印,囧。。。。

然后按照如下方法,进行操作:

处理后的240x240图片:

然后我们需要将图片转化为二进制文件。

我们用上一节给大家那个代码就可以,忘记了没,我们一起再来复习下哈:

上代码喽,客观慢用,哈。。。

# img_to_binary.py
import struct
import numpy as np
from PIL import Image  # PIL就是pillow库def color565(r, g, b):return (r & 0xf8) << 8 | (g & 0xfc) << 3 | b >> 3def main():for i in range(1, 9):img = Image.open("./images/PuBu{}.jpg".format(i))# print(img.format, img.size, img.mode)img_data = np.array(img)  # 240行240列有3个 240x240x3with open("./images/PuBu{}.dat".format(i), "wb") as f:for line in img_data:for dot in line:f.write(struct.pack("H", color565(dot[0], dot[1], dot[2]))[::-1])if __name__ == '__main__':main()

我们在电脑上打开pycharm,运行上面代码,就会得到.dat文件

然后我们需要将这些dat文件,转送到开发板上

七、dat文件传送

        还记得怎么从电脑上将文件传到开发板中吗?看一下上一节内容,想一想哦。。。

忘了没关系我们一起看一下,首先我们需要早开发板上开启一个TCP的服务端,用来接收数据。在Thonny IDE中运行以下代码:

# recv_img_dat.py 在MicroPython上启动TCP服务器,接收数据
import time
import network
import machine
import socketdef do_connect():wlan = network.WLAN(network.STA_IF)wlan.active(True)if not wlan.isconnected():print('connecting to network...')wlan.connect('WIFI名字', 'WIFI密码')  # WIFI名称和密码i = 1while not wlan.isconnected():print("正在链接...{}".format(i))i += 1time.sleep(1)print('network config:', wlan.ifconfig())# 0. 链接wifi
do_connect()# 1. 创建TCP套接字
server_s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 2. 绑定本地信息
server_s.bind(("", 8080))# 3. 设置为被动的
server_s.listen(128)print("等待对方链接...")# 4. 等待客户端链接
new_s, client_info = server_s.accept()print("等待对方发送图片数据...")# 3. 创建文件,接收数据
for i in range(1, 9):with open("PuBu{}.dat".format(i), "wb") as f:for j in range(240):# 3.1 接收数据data = new_s.recv(480)  # 240*2=480 一行有240个点,每个点有2个字节# 3.2 写到文件f.write(data)print("接收完毕{}".format(i))
print("全部数据接收完毕")# 7. 关闭套接字
new_s.close()
server_s.close()

然后我们在电脑上的pycharm上运行以下代码,给开发板发送dat文件:

# send_img_dat_to_esp.py 发送二进制文件到开发板中
from socket import *# 1. 创建socket
tcp_client_socket = socket(AF_INET, SOCK_STREAM)# 2. 链接服务器
tcp_client_socket.connect(("192.168.0.100", 8080))  # ESP32开发板的IP地址和端口号# 2. 打开文件,发送数据
for i in range(1, 9):with open("./images/PuBu{}.dat".format(i), "rb") as f:for j in range(240):# 3.1 写到文件data = f.read(480)# 3.2 接收数据tcp_client_socket.send(data)  # 240*2=480 一行有240个点,每个点有2个字节print("发送完毕{}".format(i))
print("所有数据发送完毕")# 7. 关闭套接字
tcp_client_socket.close()

电脑发送完成:

开发板接收完成:

开发板接收完数据,我们接下来就可以想办法显示啦。。。。
八、动态图片显示

        在Thonny上新建一个文件show_img.py:

# show_img.py 显示图片代码
from machine import Pin, SPI
import st7789_new
import timetft = st7789_new.ST7889_Image(SPI(2, 80000000), dc=Pin(2), cs=Pin(5), rst=Pin(15))
tft.fill(st7789_new.color565(0, 0, 0))  # 背景设置为黑色# 因为用到了14张图片,所以这里创建14个文件对象
f_list = [open("PuBu{}.dat".format(i), "rb") for i in range(1, 9)]def show_img():while True:for f in f_list:  # 遍历14个文件,显示图片f.seek(0)for row in range(0, 240, 24):buffer = f.read(11520)tft.show_img(0, row, 239, row+24, buffer)show_img()

PS:记得导入屏幕驱动哦,没有驱动屏幕无法显示哈,驱动同上一节一样哈,这里在贴一下:

import ustruct
import utime_NOP = const(0x00)
_SWRESET = const(0x01)
_RDDID = const(0x04)
_RDDST = const(0x09)_SLPIN = const(0x10)
_SLPOUT = const(0x11)
_PTLON = const(0x12)
_NORON = const(0x13)_INVOFF = const(0x20)
_INVON = const(0x21)
_DISPOFF = const(0x28)
_DISPON = const(0x29)
_CASET = const(0x2A)
_RASET = const(0x2B)
_RAMWR = const(0x2C)
_RAMRD = const(0x2E)_PTLAR = const(0x30)
_COLMOD = const(0x3A)
_MADCTL = const(0x36)_FRMCTR1 = const(0xB1)
_FRMCTR2 = const(0xB2)
_FRMCTR3 = const(0xB3)
_INVCTR = const(0xB4)
_DISSET5 = const(0xB6)
_GCTRL = const(0xB7)
_VCOMS  =  const(0xBB)
_FRCTR2 = const(0xC6)
_D6H = const(0xD6)
_PWCTRL1 = const(0xD0)
_GATECTRL = const(0xE4)_PWCTR1 = const(0xC0)
_PWCTR2 = const(0xC1)
_PWCTR3 = const(0xC2)
_PWCTR4 = const(0xC3)
_PWCTR5 = const(0xC4)
_VMCTR1 = const(0xC5)_RDID1 = const(0xDA)
_RDID2 = const(0xDB)
_RDID3 = const(0xDC)
_RDID4 = const(0xDD)_PWCTR6 = const(0xFC)_GMCTRP1 = const(0xE0)
_GMCTRN1 = const(0xE1)def color565(r, g, b):return (r & 0xf8) << 8 | (g & 0xfc) << 3 | b >> 3class DummyPin:"""A fake gpio pin for when you want to skip pins."""OUT = 0IN = 0PULL_UP = 0PULL_DOWN = 0OPEN_DRAIN = 0ALT = 0ALT_OPEN_DRAIN = 0LOW_POWER = 0MED_POWER = 0HIGH_PWER = 0IRQ_FALLING = 0IRQ_RISING = 0IRQ_LOW_LEVEL = 0IRQ_HIGH_LEVEL = 0def __call__(self, *args, **kwargs):return Falseinit = __call__value = __call__out_value = __call__toggle = __call__high = __call__low = __call__on = __call__off = __call__mode = __call__pull = __call__drive = __call__irq = __call__class Display:_PAGE_SET = None_COLUMN_SET = None_RAM_WRITE = None_RAM_READ = None_INIT = ()_ENCODE_PIXEL = ">H"_ENCODE_POS = ">HH"_DECODE_PIXEL = ">BBB"def __init__(self, width, height):self.width = widthself.height = heightself.init()def init(self):"""Run the initialization commands."""for command, data in self._INIT:self._write(command, data)def _block(self, x0, y0, x1, y1, data=None):"""Read or write a block of data."""self._write(self._COLUMN_SET, self._encode_pos(x0, x1))self._write(self._PAGE_SET, self._encode_pos(y0+80, y1+80))if data is None:size = ustruct.calcsize(self._DECODE_PIXEL)return self._read(self._RAM_READ, (x1 - x0 + 1) * (y1 - y0 + 1) * size)self._write(self._RAM_WRITE, data)def _encode_pos(self, a, b):"""Encode a postion into bytes."""return ustruct.pack(self._ENCODE_POS, a, b)def _encode_pixel(self, color):"""Encode a pixel color into bytes."""return ustruct.pack(self._ENCODE_PIXEL, color)def _decode_pixel(self, data):"""Decode bytes into a pixel color."""return color565(*ustruct.unpack(self._DECODE_PIXEL, data))def pixel(self, x, y, color=None):"""Read or write a pixel."""if color is None:return self._decode_pixel(self._block(x, y, x, y))if not 0 <= x < self.width or not 0 <= y < self.height:returnself._block(x, y, x, y, self._encode_pixel(color))def fill_rectangle(self, x, y, width, height, color):"""Draw a filled rectangle."""x = min(self.width - 1, max(0, x))y = min(self.height - 1, max(0, y))w = min(self.width - x, max(1, width))h = min(self.height - y, max(1, height))self._block(x, y, x + w - 1, y + h - 1, b'')chunks, rest = divmod(w * h, 512)print("color:", color)pixel = self._encode_pixel(color)print("decode:", pixel)if chunks:data = pixel * 512for count in range(chunks):self._write(None, data)if rest:self._write(None, pixel * rest)def fill(self, color=0):"""Fill whole screen."""self.fill_rectangle(0, 0, self.width, self.height, color)def hline(self, x, y, width, color):"""Draw a horizontal line."""self.fill_rectangle(x, y, width, 1, color)def vline(self, x, y, height, color):"""Draw a vertical line."""self.fill_rectangle(x, y, 1, height, color)def blit_buffer(self, buffer, x, y, width, height):"""Copy pixels from a buffer."""if (not 0 <= x < self.width ornot 0 <= y < self.height ornot 0 < x + width <= self.width ornot 0 < y + height <= self.height):raise ValueError("out of bounds")self._block(x, y, x + width - 1, y + height - 1, buffer)class DisplaySPI(Display):def __init__(self, spi, dc, cs=None, rst=None, width=1, height=1):self.spi = spiself.cs = csself.dc = dcself.rst = rstif self.rst is None:self.rst = DummyPin()if self.cs is None:self.cs = DummyPin()self.cs.init(self.cs.OUT, value=1)self.dc.init(self.dc.OUT, value=0)self.rst.init(self.rst.OUT, value=1)self.reset()super().__init__(width, height)def reset(self):self.rst(0)utime.sleep_ms(50)self.rst(1)utime.sleep_ms(50)def _write(self, command=None, data=None):if command is not None:self.dc(0)self.cs(0)self.spi.write(bytearray([command]))self.cs(1)if data:self.dc(1)self.cs(0)self.spi.write(data)self.cs(1)def _read(self, command=None, count=0):self.dc(0)self.cs(0)if command is not None:self.spi.write(bytearray([command]))if count:data = self.spi.read(count)self.cs(1)return dataclass ST7789(DisplaySPI):"""A simple driver for the ST7789-based displays.>>> from machine import Pin, SPI>>> import st7789>>> display = st7789.ST7789(SPI(1), dc=Pin(12), cs=Pin(15), rst=Pin(16))>>> display = st7789.ST7789R(SPI(1, baudrate=40000000), dc=Pin(12), cs=Pin(15), rst=Pin(16))>>> display.fill(0x7521)>>> display.pixel(64, 64, 0)"""_COLUMN_SET = _CASET_PAGE_SET = _RASET_RAM_WRITE = _RAMWR_RAM_READ = _RAMRD_INIT = ((_SWRESET, None),(_SLPOUT, None),(_COLMOD, b"\x55"),  # 16bit color(_MADCTL, b"\x08"),)def __init__(self, spi, dc, cs, rst=None, width=240, height=240):super().__init__(spi, dc, cs, rst, width, height)def init(self):super().init()cols = ustruct.pack(">HH", 0, self.width)rows = ustruct.pack(">HH", 0, self.height)# ctr2p= ustruct.pack(">BBBBB", b"\x1F\x1F\x00\x33\x33")ctr2p= b"\x1F\x1F\x00\x33\x33"# ctr1p= ustruct.pack(">BB", b"\xA4\xA1")ctr1p= b"\xA4\xA1"# e0p= ustruct.pack(">BBBBBBBBBBBBBB", b"\xF0\x08\x0E\x09\x08\x04\x2F\x33\x45\x36\x13\x12\x2A\x2D")e0p= b"\xF0\x08\x0E\x09\x08\x04\x2F\x33\x45\x36\x13\x12\x2A\x2D"# e1p= ustruct.pack(">BBBBBBBBBBBBBB", b"\xF0\x0E\x12\x0C\x0A\x15\x2E\x32\x44\x39\x17\x18\x2B\x2F")e1p= b"\xF0\x0E\x12\x0C\x0A\x15\x2E\x32\x44\x39\x17\x18\x2B\x2F"# gatep= ustruct.pack(">BBB", b"\x1d\x00\x00")gatep= b"\x1d\x00\x00"for command, data in ((_CASET, cols),(_RASET, rows),(_FRMCTR2,ctr2p),(_GCTRL, b"\x00"),(_VCOMS, b"\x36"),(_PWCTR3, b"\x01"),(_PWCTR4, b"\x13"),(_PWCTR5, b"\x20"),(_FRCTR2, b"\x13"),(_D6H, b"\xA1"),(_PWCTRL1, ctr1p),(_GMCTRP1, e0p),(_GMCTRN1, e1p),(_GATECTRL, gatep),(_INVON, None),(_NORON, None),(_DISPON, None),(_MADCTL, b"\xc0"),  # Set rotation to 0 and use RGB):self._write(command, data)class ST7889_Image(ST7789):def _set_columns(self, start, end):if start <= end:self._write(_CASET, self._encode_pos(start, end))def _set_rows(self, start, end):if start <= end:self._write(_RASET, self._encode_pos(start, end))def _set_window(self, x0, y0, x1, y1):"""x0: x起始位置y0:  y起始位置x1:  x结束位置y1:  y结束位置"""self._set_columns(x0, x1)self._set_rows(y0, y1)self._write(_RAMWR)def show_img(self, x0, y0, x1, y1, img_data):self._set_window(x0, y0 + 80, x1, y1 + 80)self._write(None, img_data)

九、显示效果

十、小结

        看下文件结构,电脑端文件:

ESP32开发板端文件:

所有文件我给大家打个包,需要吸取哈。。。

链接: https://pan.baidu.com/s/1mEnhsUE2dS_0TAqT-jkMFA 提取码: 8rpy 复制这段内容后打开百度网盘手机App,操作更方便哦。。。

物联网开发笔记(29)- 使用Micropython开发ESP32开发板之控制240x240的oled屏幕显示动态图片GIF(ST7789芯片)相关推荐

  1. 物联网开发笔记(30)- 使用Micropython开发ESP32开发板之控制240x240的oled屏幕显示二维码(ST7789芯片)

    一.目的 在我们的240x240的oled屏幕上显示二维码 二.环境 ESP32 + 240x240的oled彩色屏幕+ Thonny IDE + 几根杜邦线 接线方式请看上前面的章节,此处不再重复赘 ...

  2. 物联网开发笔记(27)- 使用Micropython开发ESP32开发板之控制240x240的oled屏幕(ST7789芯片)

    这一节我们讲解了如何控制240x240的oled屏幕,该oled屏幕驱动芯片是ST7789,我们控制屏幕显示两行字. 一.目的 使用MicroPython开发ESP32开发板控制240x240的ole ...

  3. MLX90640开发笔记(一)概述及开发资料准备

    现在自己在做红外成像仪的越来越多了,两年前有个井下机电设备运行状态的科研项目,当时使用了AMG8833(8*8像素).前段时间因为公司生产电路板测试需要,打算买一台红外成像仪测量电路板发热是否正常,商 ...

  4. 物联网开发笔记(9)- 使用Wokwi仿真MicroPython on ESP32开发板实现温度和湿度检测并使用屏幕显示

    一.测试环境 我们同样使用在Wokwi网站上选择Micropython with ESP32进行仿真,来进行温度和湿度的检测. ESP32官方技术参考手册: https://www.espressif ...

  5. 基于MicroPython的ESP32开发

    很久前入手了一块ESP32 DEVKIT V1,当时基于C_SDK开发.最近想搞下MicroPython,就又把这块板子找出来了. 一.先下载支持MicroPython的ESP32固件 去MicroP ...

  6. Android开发笔记(七十一)区分开发模式和上线模式

    为什么要区分两种模式 许多开发者(包括博主在内)都是闷骚的程序员,为了开发调试方便,常常在代码里加上日志,还经常在页面上各种弹窗提示.这固然有利于发现bug.提高软件质量,但过多的调试信息往往容易泄露 ...

  7. 【Visual C++】游戏开发笔记三十一 回归季 游戏开发资料整理打包下载 专栏行文思路整理

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

  8. 外设驱动库开发笔记29:DS17887实时时钟驱动

    一些时候,在我们的嵌入式产品中需要记录时间,所以我们就需要获取时钟,当然实现的方式多种多样,有的MCU本身就有这一功能,不过精度较低.当我们的应用要求较高时就需要使用专门的实时时钟芯片,如DS1788 ...

  9. Modbus协议栈开发笔记之六:Modbus RTU Master开发

    这一节我们来封装最后一种应用(Modbus RTU Master应用),RTU主站的开发与TCP客户端的开发是一致的.同样的我们也不是做具体的应用,而是实现RTU主站的基本功能.我们将RTU主站的功能 ...

  10. Modbus协议栈开发笔记之五:Modbus RTU Slave开发

    Modbus在串行链路上分为Slave和Master,这一节我们就来开发Slave.对于Modbus RTU从站来说,需要实现的功能其实与Modbus TCP的服务器端是一样的.其操作过程也是一样的. ...

最新文章

  1. 慕课网基于ElasticSearch的找房网实战开发企业级房屋搜索网项目学习心得(一)
  2. MySQL乐观锁、共享锁、排他锁、行锁、表锁区别和使用方法
  3. spring源码:资源管理器Resource
  4. .net知识和学习方法系列(十四)TraceListener的应用
  5. 心脏为什么长在左边?原来是因为这个消失的器官
  6. mysql cmd 删除表_MySQL查看表和清空表的常用命令总结
  7. android modem开发(16)---MTK语音测试
  8. unity算法面试_Unity面试准备
  9. lisp 计算三点的夹角_平面三点计算夹角
  10. H5唤起APP指南(附开源唤端库)
  11. 猕猴桃的红色果肉受到特定的激活-抑制系统的控制
  12. 开发一流Android SDK
  13. 将vtt字幕转成srt
  14. 手机端自适应表格table样式如何写
  15. 网络数据泄露事件频发,个人隐私信息如何保护?
  16. 计算机视觉——全景图像拼接
  17. 60秒的秒表c语言程序,99秒表c程序
  18. js: 动画 筋斗云导航栏 仿淘宝关闭二维码
  19. 动态八卦图html,HTML绘制太极八卦图
  20. UOS 1050e ARM架构(linux)安装TP-LINK TL-WN823N免驱版无线网卡驱动

热门文章

  1. 9篇分布式机器学习系统经典论文;深度学习硬件的黄金十年|AI系统前沿动态...
  2. 微信小程序组件之间传值
  3. 使用Unified Communications Managed API获取Lync在线会议的链接地址
  4. 金山文字和Word中的格式刷更顺手(转)
  5. 软件工程c语言课程设计的作用,C语言课程设计指导书2016版HNUST
  6. 平面变压器大概价格_莆田大型变压器规格,平面变压器价格
  7. win7用html做背景,怎么美化和自定义Win7鼠标右键菜单背景?
  8. 保护您眼睛视力 对Win7/Vista/XP作如下设置
  9. 台式计算机m.2的参数,联想启天M系列
  10. 小管家进销存_管家婆物联宝微订货V2.3发版公告