未经私信同意禁止转载!

一、简介

显示技术发展很快,高中的时候大家还在学习阴极射线管显示,天天在算电子的轨迹。而如今随处可见LED显示液晶显示。
今天给大家介绍一下电子墨水屏。目前生活中比较常见的是在kindle上使用的,显示效果很像纸,质感很好,不上眼睛。这篇文章就是介绍电子墨水屏,并给大家提供在 ESP32 上使用 MicroPython 驱动墨水屏的demo。

电子墨水屏,又称电子纸,采用“微胶囊电泳显示”技术进行图像显示, 其基本原理是悬浮在液体中的带电纳米粒子受到电场作用而产生迁移。电子纸显示屏是靠反射环境光来显示图案的,不需要背光,即使是在阳光底下,电子纸显示屏依然清晰可视,可视角度几乎达到了 180°。因此,电子纸显示屏非常适合阅读。具有耗低、视角宽、阳光直射下仍可清晰显示等优点,常用于货架标签、工业仪表等显示
应用。电子纸不同于我们常见的显示技术,它的的特点是在掉电状态下可以保持显示状态,这点特性使得它在低刷新低功耗领域有很大的应用潜力,比如电子书、电子广告牌、电子标签等等。当然目前电子墨水屏由以下三个缺点:
1.造价很高,严重限制其广泛应用。
2.支持的显示颜色很少,目前主流的是黑白两色,市场上能看到支持黑白红或者背白黄三色的,但是价格高多了。
3.刷新速率低,无法支持动态显示。

我这次试用的是微雪的1.54in黑白显示模块。在这里放一个带彩色显示的官方图片(微雪应该付我广告费)。

微雪1.54inc黑白红三色电子墨水屏

电子墨水屏使用的SPI接口,其引脚定义以及和ESP32的连接关系如下

接口定义

二、驱动编写

电子墨水屏的驱动官方提供C语言版本和python版本,我写的是Micropyon版本,主要是在pyhon版本的基础拿掉Micropython中无法使用的图片库,并且做了底层驱动数据格式上的转换,驱动分为两个文件epdif.py和epd1in54.py。

epdif.py中定义了硬件接口和数据写入函数,源码如下

from machine import Pin, SPI
import time# Pin definition
reset_pin         = Pin(2,Pin.OUT)
dc_pin          = Pin(4,Pin.OUT)
cs_pin          = Pin(5,Pin.OUT)
busy_pin        = Pin(0,Pin.IN)gpio_sck = Pin(18)
gpio_mosi = Pin(23)
gpio_miso = Pin(19)spi = SPI(-1, baudrate=100000, polarity=1, phase=0, sck=gpio_sck, mosi=gpio_mosi, miso=gpio_miso)def epd_digital_write(pin, val):pin.value(val)def epd_digital_read(pin):return pin.value()def epd_delay_ms(delaytime):time.sleep_ms(delaytime)def spi_transfer(data):spi.write(data)def epd_init():epd_digital_write(cs_pin, 0)spi.init(baudrate=200000)return 0

epd1in54.py中定义了复位设置刷新区域等函数,用于对显示信息进行设置。源码如下:

import epdif
from machine import Pin, SPI
import ujson
import struct
# Display resolution
EPD_WIDTH       = 200
EPD_HEIGHT      = 200# EPD1IN54 commands
DRIVER_OUTPUT_CONTROL                       = b'x01'
BOOSTER_SOFT_START_CONTROL                  = b'x0C'
GATE_SCAN_START_POSITION                    = b'x0F'
DEEP_SLEEP_MODE                             = b'x10'
DATA_ENTRY_MODE_SETTING                     = b'x11'
SW_RESET                                    = b'x12'
TEMPERATURE_SENSOR_CONTROL                  = b'x1A'
MASTER_ACTIVATION                           = b'x20'
DISPLAY_UPDATE_CONTROL_1                    = b'x21'
DISPLAY_UPDATE_CONTROL_2                    = b'x22'
WRITE_RAM                                   = b'x24'
WRITE_VCOM_REGISTER                         = b'x2C'
WRITE_LUT_REGISTER                          = b'x32'
SET_DUMMY_LINE_PERIOD                       = b'x3A'
SET_GATE_TIME                               = b'x3B'
BORDER_WAVEFORM_CONTROL                     = b' '
SET_RAM_X_ADDRESS_START_END_POSITION        = b'x44'
SET_RAM_Y_ADDRESS_START_END_POSITION        = b'x45'
SET_RAM_X_ADDRESS_COUNTER                   = b'x4E'
SET_RAM_Y_ADDRESS_COUNTER                   = b'x4F'
TERMINATE_FRAME_READ_WRITE                  = b'xFF'class EPD :def __init__(self):self.reset_pin = epdif.reset_pinself.dc_pin = epdif.dc_pinself.busy_pin = epdif.busy_pinself.width = EPD_WIDTHself.height = EPD_HEIGHTself.lut = self.lut_full_updatelut_full_update = [0x02, 0x02, 0x01, 0x11, 0x12, 0x12, 0x22, 0x22, 0x66, 0x69, 0x69, 0x59, 0x58, 0x99, 0x99, 0x88, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xB4, 0x13, 0x51, 0x35, 0x51, 0x51, 0x19, 0x01, 0x00]lut_partial_update  = [0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]def digital_write(self, pin, value):epdif.epd_digital_write(pin, value)def digital_read(self, pin):return epdif.epd_digital_read(pin)def delay_ms(self, delaytime):epdif.epd_delay_ms(delaytime)def send_command(self, command):self.digital_write(self.dc_pin, 0)# the parameter type is list but not int# so use [command] instead of commandepdif.spi_transfer(command)def send_data(self, data):self.digital_write(self.dc_pin, 1)# the parameter type is list but not int# so use [data] instead of dataepdif.spi_transfer(data)def init(self, lut):if (epdif.epd_init() != 0):return -1# EPD hardware init startself.lut = lutself.reset()self.send_command(DRIVER_OUTPUT_CONTROL)self.send_data(struct.pack("B", (EPD_HEIGHT - 1) & 0xFF))self.send_data(struct.pack("B", ((EPD_HEIGHT - 1) >> 8) & 0xFF))self.send_data(b'x00')                     # GD = 0 SM = 0 TB = 0self.send_command(BOOSTER_SOFT_START_CONTROL)self.send_data(b'xD7')self.send_data(b'xD6')self.send_data(b'x9D')self.send_command(WRITE_VCOM_REGISTER)self.send_data(b'xA8')                     # VCOM 7Cself.send_command(SET_DUMMY_LINE_PERIOD)self.send_data(b'x1A')                     # 4 dummy lines per gateself.send_command(SET_GATE_TIME)self.send_data(b'x08')                     # 2us per lineself.send_command(DATA_ENTRY_MODE_SETTING)self.send_data(b'x03')                     # X increment Y incrementself.set_lut(self.lut)      # EPD hardware init endreturn 0def wait_until_idle(self):while(self.digital_read(self.busy_pin) == 1):      # 0: idle, 1: busyself.delay_ms(100)
###  @brief: module reset.#          often used to awaken the module in deep sleep,##def reset(self):self.digital_write(self.reset_pin, 0)         # module resetself.delay_ms(300)self.digital_write(self.reset_pin, 1)self.delay_ms(300)    ###  @brief: set the look-up table register##def set_lut(self, lut):self.lut = lutself.send_command(WRITE_LUT_REGISTER)# the length of look-up table is 30 bytesfor i in range(0, len(lut)):self.send_data(struct.pack("B", self.lut[i]))###  @brief: put an image to the frame memory.#          this won't update the display.##def set_frame_memory(self, image, image_width,image_height,x, y):#if (image == None or x < 0 or y < 0):#    return# x point must be the multiple of 8 or the last 3 bits will be ignoredx = x & 0xF8image_width = image_width & 0xF8if (x + image_width >= self.width):x_end = self.width - 1else:x_end = x + image_width - 1if (y + image_height >= self.height):y_end = self.height - 1else:y_end = y + image_height - 1        self.set_memory_area(x, y, x_end, y_end)self.set_memory_pointer(x, y)self.send_command(WRITE_RAM)# send the image data#pixels = image_monocolor.load()byte_to_send = 0x00for j in range(0, y_end - y + 1):# 1 byte = 8 pixels, steps of i = 8for i in range(0, (x_end - x + 1)/8):self.send_data(struct.pack("B", image[i+j*(image_width >>3)]))# Set the bits for the column of pixels at the current position.#if pixels[i, j] != 0:#    byte_to_send |= 0x80 >> (i % 8)#if (i % 8 == 7):#    self.send_data(struct.pack("B", byte_to_send))#    byte_to_send = 0x00
###  @brief: clear the frame memory with the specified color.#          this won't update the display.##def clear_frame_memory(self, color):self.set_memory_area(0, 0, self.width - 1, self.height - 1)self.set_memory_pointer(0, 0)self.send_command(WRITE_RAM)# send the color datafor i in range(0, self.width / 8 * self.height):self.send_data(struct.pack("B", color))###  @brief: update the display#          there are 2 memory areas embedded in the e-paper display#          but once this function is called,#          the the next action of SetFrameMemory or ClearFrame will #          set the other memory area.##def display_frame(self):self.send_command(DISPLAY_UPDATE_CONTROL_2)self.send_data(b'xC4')self.send_command(MASTER_ACTIVATION)self.send_command(TERMINATE_FRAME_READ_WRITE)self.wait_until_idle()###  @brief: specify the memory area for data R/W##def set_memory_area(self, x_start, y_start, x_end, y_end):self.send_command(SET_RAM_X_ADDRESS_START_END_POSITION)# x point must be the multiple of 8 or the last 3 bits will be ignoredself.send_data(struct.pack("B", (x_start >> 3) & 0xFF))self.send_data(struct.pack("B", (x_end >> 3) & 0xFF))self.send_command(SET_RAM_Y_ADDRESS_START_END_POSITION)self.send_data(struct.pack("B", y_start & 0xFF))self.send_data(struct.pack("B", (y_start >> 8) & 0xFF))self.send_data(struct.pack("B", y_end & 0xFF))self.send_data(struct.pack("B", (y_end >> 8) & 0xFF))###  @brief: specify the start point for data R/W##def set_memory_pointer(self, x, y):self.send_command(SET_RAM_X_ADDRESS_COUNTER)# x point must be the multiple of 8 or the last 3 bits will be ignoredself.send_data(struct.pack("B", (x >> 3) & 0xFF))self.send_command(SET_RAM_Y_ADDRESS_COUNTER)self.send_data(struct.pack("B", y & 0xFF))self.send_data(struct.pack("B", (y >> 8) & 0xFF))self.wait_until_idle()###  @brief: After this command is transmitted, the chip would enter the#          deep-sleep mode to save power.#          The deep sleep mode would return to standby by hardware reset.#          You can use reset() to awaken or init() to initialize##def sleep(self):self.send_command(DEEP_SLEEP_MODE)self.wait_until_idle()

还编写了一个简单的测试demo:

def test():print('startn')epd = epd1in54.EPD()epd.init(epd.lut_full_update)print('epd init finish n')epd.clear_frame_memory(0xff)epd.set_frame_memory(gImage_imag,200,200, 0, 0)epd.display_frame()epd.delay_ms(2000)

我将测试源码和相关参考资料打包放在百度云盘中。
链接:https://pan.baidu.com/s/1RrKF14Nc21VC-coSittHQQ 密码:jp6t

三、构想

有了墨水屏和wifi功能,如果添加上简单的按键控制和电池,就具备一个kindle的基本结构了,做一个小号的kindle应该不难的哈。

最后,如果有大佬打赏,让我买个彩色的墨水屏玩玩就好了。

欢迎关注我的专栏《电子工程师有多无聊》,你可以看到更多关于使用Python进行硬件编程的文章。如果你有兴趣,也欢迎投稿。

yota3墨水屏设置_使用ESP32驱动电子墨水屏相关推荐

  1. 左右滑屏设置_王者荣耀怎么滑屏操作 滑屏手法详解[多图]

    王者荣耀有一个比较高端的操作手法滑屏,能让玩家们更加快速的点出要施放的技能,对战斗有很大的帮助,下面安族小编给大家介绍一下滑屏手法详解. 如何滑屏: 首先我们要在设置里面把镜头移动设置成滑动,灵敏度调 ...

  2. yota3墨水屏设置_【YotaPhone2 手机使用总结】墨水屏|操作|桌面投射|第三方_摘要频道_什么值得买...

    YotaPhone2 手机使用总结(墨水屏|操作|桌面投射|第三方) 电子墨水屏,这个是值得来认真说一说的.这个屏有三个模式,一个是基本待机模式YotaCover,只显示四个图标,可以更换背景图片,有 ...

  3. 使用ESP32能驱动电子墨水屏吗?

    ESP32是可以支持墨水屏的,电子墨水屏一般都是12C或者SPI接口,硬件方面都可以支持,只需要玩家根据所搭配的墨水屏移植相关的初始化代码和驱动就好. 电子墨水屏的驱动官方提供C语言版本和python ...

  4. 手机端主图在哪里设置_原来华为手机灭屏也能看时间,操作方法非常简单,1分钟学会...

    手机已经成为大家出门必带的一项物品,无论走到哪里我们都会带着它,以至于现在的朋友都是机不离手,随身携带手机还可以让我们随时知道时间,只要按下电源键就可以查看.不过现在很多手机可以设置成灭屏显示时间,下 ...

  5. android系统息屏设置_不用羡慕了!安卓8.0手机都能用上息屏显示

    原标题:不用羡慕了!安卓8.0手机都能用上息屏显示 说起息屏显示,近些年来很多采用AMOLED屏幕的手机都纷纷加入这一功能.比如三星S7 edge.小米Note 2等,即便是采用了LCD屏幕的LG,也 ...

  6. win10设置锁屏密码_如何取消Win10锁屏密码?2招教你迅速关闭

    对于追求高效率的电脑用户来说,Win10每次开机或锁屏后需要输入密码,显然是太浪费时间.那么,Win10锁屏密码如何取消?今天小编为大家带来2招关闭Win10锁屏密码方法,希望对电脑爱好者小伙伴们有所 ...

  7. 玩玩带ESP32的电子墨水屏模块

    之前一直对电子墨水屏很感兴趣,所以最近入手了一个墨水屏模块来玩玩,主控是ESP32,但是在上手的过程中因为有些商家资料有问题踩了很多坑. 这里记录下来希望对同样入手了的同学有所帮助吧. 电子墨水具有超 ...

  8. vscode win10笔记本 蓝屏_遇到win10电脑蓝屏怎么办_简单解决win10蓝屏的方法

    Win10系统出现蓝屏现象属于比较常见问题之一,想必大家也不陌生了.如果电脑时不时出现蓝屏,会影响正常使用体验,引发win10电脑蓝屏的原因有很多,可能是系统问题,还可能是驱动问题,也有可能是硬件问题 ...

  9. ie退出全屏快捷键_讲解win7电脑全屏快捷键介绍

    快捷键可以代替鼠标完成一些指令,免去了键盘鼠标来回切换的麻烦,提高了工作效率,有些朋友就询问win7电脑全屏快捷键.我给大家整理了不停同软件的全屏快捷键,赶紧来瞧瞧吧 win7系统与Vista一脉相承 ...

最新文章

  1. 修改mysql数据库默认编码为utf8
  2. Docker初学1:初识Docker
  3. 1.段描述符与段选择子
  4. xtext_使用Xtext为Eclipse和IntelliJ开发DSL
  5. Error: Cannot find module 'webpack-cli'--解决方案
  6. 途牛windows转linux,在 Windows 中通过 VirtualBox 启动物理硬盘上的 Linux 操作系统...
  7. Python 利用*args和**kwargs解决函数遇到不确定数量参数问题
  8. javascript中encodeURI和decodeURI方法
  9. Python04,变量与赋值
  10. 如何使用Python玩转PDF各种骚操作?你看了就知道。
  11. 计算机txt公式,完整word版本积分公式
  12. 高校舆情分析python_微博的高校舆情监控系统设计
  13. firefox主页被360篡改_IE浏览器主页被劫持,如何解决主页被篡改问题?
  14. 中科院软件所的毕业去向(硕+博)
  15. Bear + Reminders 是完美的Thing 3 的替代品
  16. 小数在计算机中的存储形式
  17. Java练习(十九):编写代码,实现AOP的@Around操作 (两种写法)
  18. 计算机键盘一般分四个区域 其中,四个键盘区域分别在哪里
  19. 注册表修改3389端口号
  20. LKT6830C安全MCU(一):资源介绍

热门文章

  1. #1081 : 最短路径·一(Dijkstra)
  2. 自定义 View 功能上线,你的小程序可以更多变
  3. 揭秘下一代云数据库引擎MyBasefor PostgreSQL
  4. Dubbo 版 Swagger 来啦!
  5. 闲鱼把各种玩法做成了一个平台:哆啦A梦
  6. 如何实现一个跨库连表SQL生成器?
  7. 自研开源框架 Midway Serverless ,让前端提效 50% 背后的故事
  8. 雨中冒险:难度随时间增高的设计
  9. 玩英雄联盟手游,谁才是新手最喜欢的英雄?无极剑圣还是吗?
  10. ORA-20000: ORU-10027: 执行存储过程的错误