5 Commits
1.2.0 ... 1.3.3

Author SHA1 Message Date
fsender
3ae6156886 1.3.3: fix 2.66BWR refresh & pre-add new driver 2023-11-12 04:08:29 +08:00
fsender
2aca106448 feat: new button gesture 2023-11-08 16:44:42 +08:00
fsender
d24639a962 fix memory bug 2023-11-07 21:26:51 +08:00
fsender
85a785eabd update to 1.3.1 ver 2023-11-07 16:41:37 +08:00
fsender
a8b2540468 update to 1.3.0 version 2023-11-06 15:39:13 +08:00
58 changed files with 2784 additions and 862 deletions

View File

@@ -1,3 +1,87 @@
## Release 1.3.3 - 2023/11/12
1. 添加了2.66寸三色和黑白的不同驱动程序.
2. guy_epaper 文件夹内添加了一些文件. 为添加新版本驱动做准备.
3. 现在SD卡功能是可移除的. 可以自定义文件系统来初始化并使用. 但是对应的, SD卡引脚设定保留.
4. 优化按键手势的按动手感.
5. 重构了"固定引脚"(DYNAMIC_PIN_SETTINGS被注释)的宏定义逻辑代码. 现在如果在运行过"固定引脚"的程序之后, 再运行常规固件不需要二次配置.
6. 增大了S3的按钮任务栈空间, 避免因为stack overflow导致的任务终止.
7. 修复若干其他方面的bug, 比如配置引脚时, 屏幕没转向.
## Release 1.3.2 - 2023/11/8
1. 按键驱动 (guy_button) 获得新操作更新: (New button gesture)
- 单按键操作更新: 现在双击效果为选择/确定, 三击效果为返回, 长按效果为向上翻页;
- 单按键新增点按后长按操作;
- 双按键新增按住按键1点按按键2操作;
- 三按键新增按住按键1点按按键3操作.
2. 新增获取系统内存的API函数 getFreeMem().
3. 修复若干驱动层bug.
## Release 1.3.1 - 2023/11/7
1. 增加了分步绘制的支持. 可以使用 READGUY_FAST_START 等绘制选项来控制绘制过程:
- READGUY_SLOW 慢刷, 完整的进行一次慢刷, 等于连续执行慢刷开始和慢刷结束.
- READGUY_FAST 快刷, 完整的进行一次快刷, 需要等待刷完才能继续执行代码.
- READGUY_SLOW_START 慢刷开始, 此过程不会进行等待, 发送完要刷新的缓存之后立刻返回
- READGUY_FAST_START 快刷开始.
- READGUY_SLOW_END 慢刷结束, 调用慢刷开始之后才能执行. 会等待到屏幕刷完再执行后续操作.
- READGUY_FAST_END 快刷结束, 如果两次调用间隔时间屏幕已经刷完, 那么该函数会完成刷屏后续操作之后立刻返回.
2. 修复了首次配置驱动时, 墨水屏显示页面错位的bug (现在对于新设备, 配置起来会像以前一样丝滑)
3. 修复若干其他bug
## Release 1.3.0 - 2023/11/6
1. 增加了真.保姆级的教程 (详细到注释比代码多很多倍)
2. 目前所有支持的屏幕设备都可以在主控复位之后, 不需要调用init函数即可直接快刷(也不会变成慢刷). 为此, 调用init函数之后, 会让屏幕的下一次刷新变为慢刷.
3. 优化了所有的驱动程序. 包括灰度效果, 内存占用等.
4. 增加了更多的图片显示示例程序. 现在支持多种图片显示方式:
4.1. 在内存中开辟新画布(sprite). 支持颜色格式有1,2,4,8bit灰度(greyscale), 1,2,4,8bit调色板(palette), 8bit-RGB332, 16bit-RGB565, 24bit-RGB888.
4.2. 画布可以用抖动算法在屏幕上显示为二值图.
4.3. 画布可以在屏幕上显示为16级灰度图.
4.4. 画布可以显示在屏幕缓存中, 此过程不刷新屏幕. 下次刷新屏幕时, 画布内容将呈现在屏幕缓存中.
4.5. 将画布缩放到指定的宽度和高度后显示.
4.6. 显示BMP, JPG, PNG(仅限ESP32系列)图片文件到画布上.
4.7. 直接将图片文件刷新到屏幕上, 无需画布(sprite).
4.8. 将图片文件显示到屏幕缓存内, (不显示).
4.9. 将图片文件以16级灰度显示到屏幕上, 无需画布(sprite).
4.10. 缩放显示图片文件, 需要指定缩放比例. >1.0为放大, <1.0为缩小. (float scale_x, float scale_y).
5. 修复若干bug.
## Release 1.2.0 - 2023/11/3
1. 添加了图片demo, 和 可选关闭的WiFi 的示例程序。其中图片相关功能相当节省内存, 还请大胆使用。

View File

@@ -4,7 +4,11 @@
<img src="extra/artset/readguy_theme3.png" width="30%" height="auto">
**版本1.1.1正式发布欢迎分享、star和fork~** 上面的图是项目看板娘, 盖. 可爱的盖姐在等你哟~
**版本1.3.3正式发布欢迎分享、star和fork~** 上面的图是项目看板娘, 盖. 可爱的盖姐在等你哟~
**即将发布7个全新的屏幕驱动: 欢迎支持! (详见后面的驱动表格)**
**发布好的全新驱动程序版本号将会是2.0.0!**
欢迎克隆, 项目交流QQ群: 926824162 (萌新可以进来问问题的哟), 项目的 Bilibili 主页: [BV1f94y187wz](https://www.bilibili.com/video/BV1f94y187wz/) 记得三连+关注我这个宝藏up主哦~
@@ -53,14 +57,23 @@ Supported displays: 1.54-inch, 2.13-inch, 2.66-inch, 2.9-inch, 4.2-inch.
| 1 |1.54寸标准 |原创自研 |200*200| 16阶 | 不支持 | 1.3s | 0.35s |
| 2 |1.54寸Lilygo |原创自研 |200*200| 16阶 | 支持 | 2.3s | 0.6s |
| 3 |2.13寸汉朔价签 |原创自研 |122*250| 16阶 | 不支持 | 1.6s | 0.31s |
| 4 |2.13寸三色 |原创自研 |104*212| 16阶 | 支持 | 2s | 0.2s |
| 5 |2.66寸Vusion价签 |和4号相同|152*296| 16阶 | 支持 | 2s | 0.2s |
| 4 |2.13寸低分辨率版 |原创自研 |104*212| 16阶 | 支持 | 1.12s | 0.17s |
| 5 |2.66寸Vusion价签 |和4号相同|152*296| 16阶 | 支持 | 1.12s | 0.17s |
| 6 |2.7寸佳显触摸屏 |和2号相同|176*264| 16阶 | 支持 | 2.3s | 0.6s |
| 7 |2.9寸A01 |和1号相同|128*296| 16阶 | 不支持 | 1.8s | 0.55s |
| 8 |2.9寸T94背光 |和2号相同|128*296| 16阶 | 支持 | 2.3s | 0.6s |
| 9 |3.7寸高dpi墨水屏 |原创自研 |280*480| 16阶 | 不支持 | 1.3s | 0.5s |
|10 |4.2寸HINK黑白价签 |原创自研 |400*300| 16阶 | 支持 | 1.72s | 0.38s |
|11 |4.2寸WF丝印 |原创自研 |400*300| 16阶 | 不支持 | 3.6s | 0.8s |
|12 |2.13寸三色 |和4号相同|122*250| 16阶 | 支持 | 未知 | 未知 |
|13 |2.66寸三色价签 |和4号相同|152*296| 16阶 | 支持 | 2.48s | 0.65s |
|14 |M5 Core.Ink 1.54寸 |即将支持|200*200|/|/|/|/|
|15 |3.7寸低DPI版墨水屏 |即将支持|416*240|/|/|/|/|
|16 |4.26寸高分辨率墨水屏|即将支持|800*480|/|/|/|/|
|17 |5.83寸墨水屏幕 |即将支持|600*448|/|/|/|/|
|18 |5.83寸GDEQ0583T31 |即将支持|640*480|/|/|/|/|
|19 |7.5寸三色墨水屏幕 |即将支持|800*480|/|/|/|/|
|20 |10.2寸GDEQ102T90 |即将支持|960*640|/|/|/|/|
可以自己在代码中加入你想要的屏幕型号

View File

@@ -6,8 +6,9 @@
*
* @file ex01_helloWorld.ino
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @version 1.0
* @date 2023-09-19
* @version 1.1
* @date create: 2023-09-19
* last modify: 2023-11-06
* @brief ReadGuy最基础的HelloWorld显示.
*
* @note 食用方法:
@@ -27,6 +28,79 @@
* 你需要知道你的哪个引脚对应哪个GPIO, 才能使用这个库 (带来的不便请谅解nia~)
* 配置好了, 代码就开始正常运行了.
*
* 以下是看不懂代码的解决方法. 仔细看就行了
* {0} 代码食用注意事项:
* 这一部分的代码很难读, 或者按维莫斯小姐的说法, 很 "抽象" .
* 如果你要开始一行一行读取下去, 不妨先在这里停顿一下, 看一下一些需要你提前知道的东西.
*
* {1} 画布, 或者叫sprite. 是一块专门存储图像的数据结构.
* 画布需要大量的内存来存放图像的像素, 但是你可以对画布进行任何图像操作, 就像是在墨水屏上能进行的操作一样多.
* 画布类型的数据结构至少需要包含3个要素: 宽度, 高度, 每像素需要的字节数.
* 画布需要的内存为 宽度*高度*每像素需要的字节数. (1bit图像需要8分之一字节)
* 在画布上可以进行几乎任何图像操作, 就像是在墨水屏上能进行的操作一样多.
* 而且你还可以获取像素的颜色 (这和你创建的画布的颜色格式有关).
* 画布可以随时被快速的显示到墨水屏上, 显示完了之后再使用display方法, 你刚才创建的画布就显示出来了.
*
* {2} 屏幕缓存. 你就从来没好奇过, 墨水屏上怎么呈现图像的? 我好奇过.
* 我翻看了里面那些错综复杂又是深奥的C++骚操作又是类继承又是虚函数指针...反正就是抽象而且难懂的的源代码,
* 简而言之, 墨水屏显示的原理就是墨水屏驱动里面也有个画布, 不过这个画布是1bit的(每8像素消耗1字节内存).
* 所有对墨水屏进行的操作其实都是对那个内部的画布进行的操作.
* 在你使用墨水屏的display方法时, 呃, 墨水屏的驱动程序可以读取到这个屏幕缓存的所有字节.
* 并且根据驱动手册的方法将这个画布上的字节发送到屏幕上.
* 发送完成之后再通过一些代码, 墨水屏就会刷屏, 将这个屏幕缓存里的内容显示出来了.
* 维莫斯小姐: 苏滴嘶内~
* 下面的库其实用了一些更加骚气的方法, 直接读取了这块画布, 从而能允许你在ESP8266上也可以...显示不小的图片
*
* {3} 灰度显示. ReadGuy这个库的一大特色就是支持灰度显示.
* 欸, 可是屏幕缓存画布只有1bit啊, 怎么显示灰度啊?
* 不用急, 原理也不难.
* 16级灰度颜色的刷屏分为15个步骤. 因为白色不需要刷.
* 在开始刷屏之前, 务必进行一次慢刷, 确保所有的白色像素都足够白.
* 第一次刷屏, 通常会刷最浅的灰色.
* 此时, 首先将屏幕缓存内所有需要显示为这个颜色的像素全写入为黑色(只是名义上的黑色, 实际上是设定的颜色).
* 再初始化墨水屏的刷屏功能.
* - 此处需要额外设定刷屏的颜色的参数. 此参数设定是可以由驱动程序自动完成的, 不过也可以通过你调用函数完成.
* - 用户只需要用简单的setDepth函数设置颜色即可. 用户设定的颜色深度通常用于显示字符串或者形状等GUI控件.
* - 刷图程序的灰度图片刷新功能会自动控制刷屏颜色, 不需要手动调用.
* 最后刷屏. 因为之前刷图程序已经设定过刷的颜色了, 所以此时刷屏呈现的颜色就是之前设定的颜色.
* 第2-15次刷屏原理类似.
* 每次追加黑色像素, 追加的像素就是来显示灰度的像素. 而原本的黑色像素就是之前那些灰度颜色的像素.
*
* \*(^_^)*/ /** 我相信你应该还不懂. 我就举个例子吧.
* 比如要显示一个渐变色, 从左到右为白色→黑色的渐变图.
* 首先进行一次慢刷, 确保要刷图的地方都是纯洁的白色.
* ≡=-(1)-=≡
* 筛选出最浅的灰色设为灰1, (灰X代表灰度颜色, 灰15代表黑色)
* 再把图片内所有颜色和灰1最接近的颜色写入到屏幕缓存内.
* 此时屏幕缓存内的"黑色"部分都是与灰1最接近的颜色, 其他颜色均为"白色"部分.
* 刷好了之后, 进行一次刷屏. (系统自动调用setDepth(1)来保证接下来图片的黑色部分其实是灰1).
* ≡=-(2)-=≡
* 筛选出比最浅的灰色深一点的颜色设为灰2, 把图片内和灰2最接近的颜色写入屏幕缓存, 但保持原本代表灰1的"黑色"不变.
* (也就是说, 把灰2的像素写入之后, 屏幕缓存的黑色像素只多不少. 写入过程中, 只能是白→黑, 不存在黑→白)
* 此时屏幕缓存内的"黑色"部分包含了与灰2最接近的颜色, 和刚才刷完的与灰1最接近的颜色. 其他颜色均为"白色"部分.
* 刷好了之后, 进行一次刷屏. (系统自动调用setDepth(2)来保证接下来图片的黑色部分其实是灰2)
* 注意屏幕显示, 原本的灰1并没有颜色变化, 变化的都是本次追加的"灰2"代表的"黑色".
* 追加的像素会显示为新设定的颜色.
* ≡=-(3)-=≡
* 对于灰3, 和灰2一样, 追加显示到屏幕缓存内.
* (也就是说, 屏幕缓存的黑色像素还是只多不少. 写入过程中, 只能是白→黑, 不存在黑→白)
* 此时屏幕缓存内的"黑色"部分包含了与灰3, 灰2和灰1最接近的颜色. 其他颜色均为"白色"部分.
* 设置颜色深度, 刷屏.
* 对于灰4~灰15, 都这么做
* ≡=-(5)-=≡
* 刷完灰15之后, 灰度图就刷完了.
*
* {4} 自定义刷屏函数. 到底是什么决定了刷屏时的每一像素是什么颜色? 是黑是白?
* 其实刷屏的本质就是通过调用一个外部函数.
* 该函数可以根据输入的像素坐标位置来决定输出的像素颜色.
* 为了简化程序调用过程并提高调用速度, 此处的像素坐标位置参数为一个整数(而不是两个)
* 至于该怎么调用这个函数, 并不是你需要了解的事情.
*
* 关于图形函数:
* 用过 LovyanGFX / Adafruit GFX / GxEPD2 / TFT_eSPI 库的用户, 可以直接套用他们的接口函数
* 用过 U8G2 的用户, 接口函数和U8G2不一样的,
* 请参考上述库的示例程序.
*
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
@@ -46,14 +120,17 @@
*/
//在这里包含程序需要用到的库函数
//就像是你在C语言里面总是用的 #include <stdio.h> 一样,
//为了能在这里使用readguy库, 你需要在这里调用 #include "readguy.h" 来包含此库
//在platformio中, 还要额外 #include <Arduino.h> 来确保你用的是Arduino环境.
#include <Arduino.h> //arduino功能基础库. 在platformIO平台上此语句不可或缺
#include "readguy.h" //包含readguy_driver 基础驱动库
#include "readguy.h" //包含readguy_driver 基础驱动库
ReadguyDriver guy;//新建一个readguy对象, 用于显示驱动.
//所有对墨水屏的操作都是对guy这个对象进行的操作.
void drawLines(); //声明一个函数, 用于显示一些线条. 此函数在后面的程序中会用到的
void setup(){
void setup(){ //Arduino的setup函数. 这个函数在上电之后仅执行一次.
// --------------------- 1 - 初始化和启动ReadGuy -------<
Serial.begin(115200); //初始化串口
@@ -64,19 +141,24 @@ void setup(){
guy.init(); //初始化readguy_driver 基础驱动库.
//首次初始化完成之后, 以后再初始化就不需要配网了, 除非你抹除了芯片的flash
//完成之后会全屏刷新一次
//完成之后会让下一次刷屏 全屏慢速刷新一次, 之后的刷屏即可自由定义是全刷还是局刷.
guy.setFont(&FreeMonoBold9pt7b); //设置显示的字体
//字体可以参考LovyanGFX的示例程序.
guy.setTextColor(0,1); //设置显示的颜色. 0代表黑色, 1代表白色
//此函数的作用是设置显示颜色. 左边的0代表前景色(文字颜色: 黑色), 右边的1代表背景色(白色)
//类似于 guy.setTextColor(0) 的用法说明此颜色为透明背景.
guy.drawString("Hello Readguy!",10,10); //用此函数将字符串显示到屏幕缓存内
//调用此函数并不会立即刷屏, 而是会将文本字符串写入到屏幕缓存, 在下一次调用display()函数之后就会显示出来.
//也可以用print函数来显示.
//guy.setCursor(10,10); //设置显示的坐标
//guy.print("Hello Readguy!"); //使用这个函数也能显示出字符串, 但是需要提前使用setCursor确定显示坐标
guy.display(true); // 快速刷新. 将屏幕缓存内的内容显示到墨水屏幕上
//guy.display(false); // 慢速刷新.
guy.display(READGUY_FAST); // 快速刷新. 将屏幕缓存内的内容显示到墨水屏幕上. 可简写为 guy.display(), 效果一样.
//guy.display(READGUY_SLOW); // 慢速刷新.
//想知道更多内容, 欢迎移步到其他示例.
}
@@ -85,6 +167,7 @@ void loop(){
//什么也不做, 毕竟刷新墨水屏要消耗墨水屏的阳寿.
//盖姐说它们也是有阳寿的. 刷多了会老化.
delay(1);
}/* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -11,6 +11,25 @@
* @brief ReadGuy功能演示.
* 将根目录下的data文件夹 上传到LittleFS之后运行效果更佳
* 或者可以准备一张SD卡,并准备在卡的根目录下放置data文件夹内的文件.
* 就是SD卡内放data文件夹内的所有文件, 不能额外套文件夹.
* 如果你的SD卡插入电脑上显示为可移动磁盘, 那么双击SD卡目录就要看到这个文件夹里的文件
*
* 默认的文件系统为SD卡. 当没有插入SD卡时, 会读取LittleFS文件系统.
* 没有条件准备SD卡的, 可以烧录LittleFS文件系统.
*
* 对于ESP8266 Arduino 用户, 在项目草图文件夹内新建一个data文件夹, 并放入文件 (示例已提供data文件夹)
* 再在 arduinoIDE 的工具选项里选择 ESP8266 LittleFS Data Upload.
* 没有这个选项的需要参考以下文档安装ESP8266 Sketch upload tool
* https://randomnerdtutorials.com/install-esp8266-nodemcu-littlefs-arduino/
*
* 对于ESP32 Arduino 用户, 也要在项目草图文件夹内放一个data文件夹, 并把文件放入其中 (示例已提供data文件夹)
* 再在 arduinoIDE 的工具选项里选择 ESP32 Sketch data upload, 最后选择LittleFS.
* 没有这个选项的需要参考以下文档安装ESP32 LittleFS upload tool
* https://randomnerdtutorials.com/esp32-littlefs-arduino-ide/
*
* 对于PlatformIO 用户, 需要进入platformIO扩展界面, 选择Upload Filesystem Image, 上传项目文件.
* ESP8266和ESP32都要用这种方法.
*
* 用于演示BMP格式图片灰度显示.
*
* @note 食用方法:
@@ -66,7 +85,6 @@ void setup(){
guy.init(); //初始化readguy_driver 基础驱动库.
//首次初始化完成之后, 以后再初始化就不需要配网了, 除非你抹除了芯片的flash
//完成之后会全屏刷新一次
// ------------------- 2 - 使用ReadGuy来显示字符串 ------<<
@@ -77,14 +95,15 @@ void setup(){
guy.drawString("Hello Readguy!",10,10); //用此函数将字符串显示到屏幕缓存内
//guy.print("Hello Readguy!"); //使用这个函数也能显示出字符串, 但是需要提前使用setCursor确定显示坐标
guy.display(true); // 快速刷新. 将屏幕缓存内的内容显示到墨水屏幕上
//guy.display(false); // 慢速刷新.
guy.display(READGUY_FAST); // 快速刷新. 将屏幕缓存内的内容显示到墨水屏幕上
// 但是, 上电初始化之后的首次刷新必为慢速刷新
//guy.display(READGUY_SLOW); // 慢速刷新.
guy.setCursor(10,30); //设置显示的坐标
guy.print("Hello~"); //或者用print函数在屏幕上打印字符串, 数值, 字符等等... 两种函数都行
guy.display(false); // 慢速刷新. 慢刷的对比度显著高于快速刷新, 而且可以消除残影
guy.display(READGUY_SLOW); // 慢速刷新. 慢刷的对比度显著高于快速刷新, 而且可以消除残影
guy.drawString(guy.SDinside()?"SD card OK.":"No SD card!",10,50); //检查readguy是否插入了SD卡
@@ -135,7 +154,7 @@ void setup(){
guy.fillScreen(1);
guy.display(false); // 慢速刷新. 慢刷的对比度显著高于快速刷新, 而且可以消除残影
guy.display(READGUY_SLOW); // 慢速刷新. 慢刷的对比度显著高于快速刷新, 而且可以消除残影
for(int i=1;i<16;i++){ //灰度测试, 循环设置不同灰度
@@ -204,7 +223,7 @@ void setup(){
guy.setFont(&FreeMonoBold9pt7b); //设置文本字体
guy.setTextColor(0); //设置文本颜色
guy.fillScreen(1); //用白色清屏.
guy.display(false); //慢刷. 注意, 进行慢刷操作之后, 所有之前显示的灰度内容均会被重新刷成纯黑色
guy.display(READGUY_SLOW); //慢刷. 注意, 进行慢刷操作之后, 所有之前显示的灰度内容均会被重新刷成纯黑色
//不管是浅灰色还是深灰色, 进行慢刷之后只有黑白色. 原来的非白色像素(浅灰色,深灰色和黑色等) 会全刷成白色.
guy.drawString("Rotation 0",10,12); //默认旋转方向为0. 实际的默认方向取决于屏幕IC. 大多数屏幕IC是竖屏.
@@ -240,13 +259,13 @@ void setup(){
guy.setTextColor(1); //设置文本颜色为白色,因为被反色的屏幕的当前像素颜色以黑色像素为主
guy.drawString("Wake Up! ~\\(^_^)/~",10,50); //退出睡眠状态
guy.display(false); //使用慢刷 来唤醒处于低功耗状态下的屏幕.
guy.display(READGUY_SLOW); //使用慢刷 来唤醒处于低功耗状态下的屏幕.
// ------------------ 6 - 可以利用灰度来达到的一些显示效果 --<<<<<<
guy.fillScreen(1); //清屏
guy.display(FILL_WHITE,false); //慢刷清屏. 左侧的FILL_WHITE表示 不写入屏幕缓存, 直接刷全白
guy.display(FILL_WHITE,READGUY_SLOW); //慢刷清屏. 左侧的FILL_WHITE表示 不写入屏幕缓存, 直接刷全白
//可以改为FILL_BLACK来设置写入缓存全黑.
//以上的方式均不会修改屏幕缓存中的内容. 右侧的false表示全屏慢刷.

View File

@@ -0,0 +1,85 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
*
* @file 3_build_without_wifi.ino
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @version 1.0
* @date 2023-11-06
* @brief ReadGuy最基础的HelloWorld显示, 但不编译WiFi库.
*
* @note 打开方式:
* 1. 深入库文件夹, 找到guy_driver_config.h文件
* 2. 找到宏定义 READGUY_ENABLE_WIFI
* 3. 注释掉这一行, 就像这样
* ```C++
* /// 启用WIFI配网功能.必须先启用 #define DYNAMIC_PIN_SETTINGS. 此选项对 ESP32xx 会减少大量flash.
* //#define READGUY_ENABLE_WIFI
* ```
* 4. 回到这里编译运行.
* 5. 其实那里的很多功能都能被禁用掉, 直接注释对应的功能定义即可
* 但我懒得测试哪些注释掉之后会导致编译不过去, 或是, 鬼找上门.
* 6. 运行完本示例之后想要跑其他示例, 记得注释回去, 免得有些功能用不了
*
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//在这里包含程序需要用到的库函数
#include <Arduino.h> //arduino功能基础库. 在platformIO平台上此语句不可或缺
#include "readguy.h" //包含readguy_driver 基础驱动库
ReadguyDriver guy;//新建一个readguy对象, 用于显示驱动.
void setup(){
// --------------------- 1 - 初始化和启动ReadGuy -------<
Serial.begin(115200); //初始化串口
//注意: 首次烧录此程序时, 这一步需要你打开手机联esp8266/32的 WiFi, 用于配网.
//名称是 readguy 密码 12345678. 连接后访问 192.168.4.1 再在网页中完成后续步骤
//后续启动就可以不用这个配置了
guy.init(); //初始化readguy_driver 基础驱动库.
//首次初始化完成之后, 以后再初始化就不需要配网了, 除非你抹除了芯片的flash
guy.setFont(&FreeMonoBold9pt7b); //设置显示的字体
guy.setTextColor(0,1); //设置显示的颜色. 0代表黑色, 1代表白色
guy.drawString("Hello Readguy!",10,10); //用此函数将字符串显示到屏幕缓存内
guy.drawString("Without WiFi!",10,30); //用此函数将字符串显示到屏幕缓存内
//guy.setCursor(10,10); //设置显示的坐标
//guy.print("Hello Readguy!"); //使用这个函数也能显示出字符串, 但是需要提前使用setCursor确定显示坐标
guy.display(READGUY_FAST); // 快速刷新. 将屏幕缓存内的内容显示到墨水屏幕上
//guy.display(READGUY_SLOW); // 慢速刷新.
//想知道更多内容, 欢迎移步到其他示例.
}
void loop(){
//什么也不做, 毕竟刷新墨水屏要消耗墨水屏的阳寿.
//盖姐说它们也是有阳寿的. 刷多了会老化.
}/* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 980 KiB

After

Width:  |  Height:  |  Size: 221 KiB

View File

@@ -0,0 +1,411 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
*
* @file ex06_Image.ino
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @version 1.0
* @date create: 2023-11-01
* last modify: 2023-11-06
* @brief ReadGuy 图片显示功能演示.
*
* - 演示如何将比较大的图片通过多种方法显示到屏幕上.
* - 运行的会很缓慢, 因为示例的图片文件比较大.
* 1. 在运行过ex01或者ex02的开发板上 编译烧录本程序.
* 2. 将该项目data文件夹内的所有文件放置于SD卡的根目录上.
* 就是SD卡内放data文件夹内的所有文件, 不能额外套文件夹.
* 如果你的SD卡插入电脑上显示为可移动磁盘, 那么双击SD卡目录就要看到这个文件夹里的文件
*
* 将根目录下的data文件夹 上传到LittleFS之后运行效果更佳
* 或者可以准备一张SD卡,并准备在卡的根目录下放置data文件夹内的文件.
* 就是SD卡内放data文件夹内的所有文件, 不能额外套文件夹.
* 如果你的SD卡插入电脑上显示为可移动磁盘, 那么双击SD卡目录就要看到这个文件夹里的文件
*
* 默认的文件系统为SD卡. 当没有插入SD卡时, 会读取LittleFS文件系统.
* 没有条件准备SD卡的, 可以烧录LittleFS文件系统.
*
* 对于ESP8266 Arduino 用户, 在项目草图文件夹内新建一个data文件夹, 并放入文件 (示例已提供data文件夹)
* 再在 arduinoIDE 的工具选项里选择 ESP8266 LittleFS Data Upload.
* 没有这个选项的需要参考以下文档安装ESP8266 Sketch upload tool
* https://randomnerdtutorials.com/install-esp8266-nodemcu-littlefs-arduino/
*
* 对于ESP32 Arduino 用户, 也要在项目草图文件夹内放一个data文件夹, 并把文件放入其中 (示例已提供data文件夹)
* 再在 arduinoIDE 的工具选项里选择 ESP32 Sketch data upload, 最后选择LittleFS.
* 没有这个选项的需要参考以下文档安装ESP32 LittleFS upload tool
* https://randomnerdtutorials.com/esp32-littlefs-arduino-ide/
*
* 对于PlatformIO 用户, 需要进入platformIO扩展界面, 选择Upload Filesystem Image, 上传项目文件.
* ESP8266和ESP32都要用这种方法.
*
* {0} 代码食用注意事项:
* 这一部分的代码很难读, 或者按维莫斯小姐的说法, 很 "抽象" .
* 如果你要开始一行一行读取下去, 不妨先在这里停顿一下, 看一下一些需要你提前知道的东西.
*
* {1} 画布, 或者叫sprite. 是一块专门存储图像的数据结构.
* 画布需要大量的内存来存放图像的像素, 但是你可以对画布进行任何图像操作, 就像是在墨水屏上能进行的操作一样多.
* 画布类型的数据结构至少需要包含3个要素: 宽度, 高度, 每像素需要的字节数.
* 画布需要的内存为 宽度*高度*每像素需要的字节数. (1bit图像需要8分之一字节)
* 在画布上可以进行几乎任何图像操作, 就像是在墨水屏上能进行的操作一样多.
* 而且你还可以获取像素的颜色 (这和你创建的画布的颜色格式有关).
* 画布可以随时被快速的显示到墨水屏上, 显示完了之后再使用display方法, 你刚才创建的画布就显示出来了.
*
* {2} 屏幕缓存. 你就从来没好奇过, 墨水屏上怎么呈现图像的? 我好奇过.
* 我翻看了里面那些错综复杂又是深奥的C++骚操作又是类继承又是虚函数指针...反正就是抽象而且难懂的的源代码,
* 简而言之, 墨水屏显示的原理就是墨水屏驱动里面也有个画布, 不过这个画布是1bit的(每8像素消耗1字节内存).
* 所有对墨水屏进行的操作其实都是对那个内部的画布进行的操作.
* 在你使用墨水屏的display方法时, 呃, 墨水屏的驱动程序可以读取到这个屏幕缓存的所有字节.
* 并且根据驱动手册的方法将这个画布上的字节发送到屏幕上.
* 发送完成之后再通过一些代码, 墨水屏就会刷屏, 将这个屏幕缓存里的内容显示出来了.
* 维莫斯小姐: 苏滴嘶内~
* 下面的库其实用了一些更加骚气的方法, 直接读取了这块画布, 从而能允许你在ESP8266上也可以...显示不小的图片
*
* {3} 灰度显示. ReadGuy这个库的一大特色就是支持灰度显示.
* 欸, 可是屏幕缓存画布只有1bit啊, 怎么显示灰度啊?
* 不用急, 原理也不难.
* 16级灰度颜色的刷屏分为15个步骤. 因为白色不需要刷.
* 在开始刷屏之前, 务必进行一次慢刷, 确保所有的白色像素都足够白.
* 第一次刷屏, 通常会刷最浅的灰色.
* 此时, 首先将屏幕缓存内所有需要显示为这个颜色的像素全写入为黑色(只是名义上的黑色, 实际上是设定的颜色).
* 再初始化墨水屏的刷屏功能.
* - 此处需要额外设定刷屏的颜色的参数. 此参数设定是可以由驱动程序自动完成的, 不过也可以通过你调用函数完成.
* - 用户只需要用简单的setDepth函数设置颜色即可. 用户设定的颜色深度通常用于显示字符串或者形状等GUI控件.
* - 刷图程序的灰度图片刷新功能会自动控制刷屏颜色, 不需要手动调用.
* 最后刷屏. 因为之前刷图程序已经设定过刷的颜色了, 所以此时刷屏呈现的颜色就是之前设定的颜色.
* 第2-15次刷屏原理类似.
* 每次追加黑色像素, 追加的像素就是来显示灰度的像素. 而原本的黑色像素就是之前那些灰度颜色的像素.
*
* \*(^_^)*/ /** 我相信你应该还不懂. 我就举个例子吧.
* 比如要显示一个渐变色, 从左到右为白色→黑色的渐变图.
* 首先进行一次慢刷, 确保要刷图的地方都是纯洁的白色.
* ≡=-(1)-=≡
* 筛选出最浅的灰色设为灰1, (灰X代表灰度颜色, 灰15代表黑色)
* 再把图片内所有颜色和灰1最接近的颜色写入到屏幕缓存内.
* 此时屏幕缓存内的"黑色"部分都是与灰1最接近的颜色, 其他颜色均为"白色"部分.
* 刷好了之后, 进行一次刷屏. (系统自动调用setDepth(1)来保证接下来图片的黑色部分其实是灰1).
* ≡=-(2)-=≡
* 筛选出比最浅的灰色深一点的颜色设为灰2, 把图片内和灰2最接近的颜色写入屏幕缓存, 但保持原本代表灰1的"黑色"不变.
* (也就是说, 把灰2的像素写入之后, 屏幕缓存的黑色像素只多不少. 写入过程中, 只能是白→黑, 不存在黑→白)
* 此时屏幕缓存内的"黑色"部分包含了与灰2最接近的颜色, 和刚才刷完的与灰1最接近的颜色. 其他颜色均为"白色"部分.
* 刷好了之后, 进行一次刷屏. (系统自动调用setDepth(2)来保证接下来图片的黑色部分其实是灰2)
* 注意屏幕显示, 原本的灰1并没有颜色变化, 变化的都是本次追加的"灰2"代表的"黑色".
* 追加的像素会显示为新设定的颜色.
* ≡=-(3)-=≡
* 对于灰3, 和灰2一样, 追加显示到屏幕缓存内.
* (也就是说, 屏幕缓存的黑色像素还是只多不少. 写入过程中, 只能是白→黑, 不存在黑→白)
* 此时屏幕缓存内的"黑色"部分包含了与灰3, 灰2和灰1最接近的颜色. 其他颜色均为"白色"部分.
* 设置颜色深度, 刷屏.
* 对于灰4~灰15, 都这么做
* ≡=-(5)-=≡
* 刷完灰15之后, 灰度图就刷完了.
*
* {4} 自定义刷屏函数. 到底是什么决定了刷屏时的每一像素是什么颜色? 是黑是白?
* 其实刷屏的本质就是通过调用一个外部函数.
* 该函数可以根据输入的像素坐标位置来决定输出的像素颜色.
* 为了简化程序调用过程并提高调用速度, 此处的像素坐标位置参数为一个整数(而不是两个)
* 至于该怎么调用这个函数, 并不是你需要了解的事情.
*
* @note 重要消息: 这是一个实验性功能. 可能你所使用的LGFX库版本较旧而无法通过编译.
* 如果你的项目中无法成功编译源码中的setBuffer, 请更改LovyanGFX库的函数!
* 位于文件 LovyanGFX/src/lgfx/v1/LGFX_Sprite.hpp
* 第155行 void setBuffer 函数:
* 修改为如下内容并保存
*
``` C++
void setBuffer(void* buffer, int32_t w, int32_t h, color_depth_t bpp = rgb565_2Byte)
{
deleteSprite();
if (bpp != 0) {
_write_conv.setColorDepth(bpp);
_read_conv = _write_conv;
_panel_sprite.setColorDepth(bpp);
}
_panel_sprite.setBuffer(buffer, w, h, &_write_conv);
_img = _panel_sprite.getBuffer();
_sw = w;
_clip_r = w - 1;
_xpivot = w >> 1;
_sh = h;
_clip_b = h - 1;
_ypivot = h >> 1;
}
```
* 完成后请再次尝试编译
* [已经向lovyan03/LovyanGFX发布issue, 等待解决]
*
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//在这里包含程序需要用到的库函数
#include <Arduino.h> //arduino功能基础库. 在platformIO平台上此语句不可或缺
#include "readguy.h" //包含readguy_driver 基础驱动库
#include "guy_image.h" //包含 readguy_image 图片功能库. 此库文件放在项目文件夹内.
constexpr const char * BMP_FILE = "/bmp.bmp"; //在此更改你的图片文件路径名
constexpr const char * JPG_FILE = "/jpg.jpg"; //在此更改你的图片文件路径名
constexpr const char * PNG_FILE = "/png.png"; //在此更改你的图片文件路径名
ReadguyDriver guy;//新建一个readguy对象, 用于显示驱动.
void setup(){
// 1 - 初始化和启动ReadGuy --------------------- 1 - 初始化和启动ReadGuy -------<
Serial.begin(115200); //初始化串口
guy.init(); //初始化readguy_driver 基础驱动库.
guy.setRotation(3); //设置旋转方向. 因为此库的功能不随旋转而发生变化.
// 2 - 显示图片 方式1 --------------------- 2 - 显示图片 方式1:使用readguy提供的drawImage方法. ------<<
//此功能可以在 画布{1}(Sprite) 绘制图片, 再使用内置的drawImage方法来显示出这个sprite.
//此方法消耗的内存颇多, 需要为整个sprite分配所有内存才能成功绘制.
//由于目前版本LovyanGFX的限制, 不能分配4bit的画布, 只能分配大小至少为8bit的画布.
guy.fillScreen(1); //清屏
//guy.display(); //显示白屏,用于将来显示图片.
LGFX_Sprite sp(&guy); //创建一个 画布(Sprite) (可以存储一些像素, 快速读写)
sp.setColorDepth(lgfx::v1::color_depth_t::grayscale_8bit); //设置画布sprite的色彩深度
sp.createSprite(80,80); //为刚刚创建的画布对象分配内存 (像素宽度*像素高度). 此处为80(宽)*80(高)像素
sp.drawBmpFile(guy.guyFS(),BMP_FILE,0,0); //从文件创建BMP图像信息. 文件系统内必须要有这个BMP图像.
//默认的文件系统为SD卡. 当没有插入SD卡时, 会读取LittleFS文件系统.
//没有条件准备文件系统的, 可以烧录文件系统.
Serial.printf("[%lu] sp.w: %d, h: %d, res: %im.\n",millis(),sp.width(),sp.height(),ESP.getFreeHeap());
guy.drawImage(sp,9,11); //使用抖动像素的方式显示图片(不是灰度, 只有黑点和白点的那种显示效果)
//sp为要显示的画布, 后面的9 11都是要显示的目标坐标位置. 此处代表将画布显示在 屏幕缓存{2} 的第9列, 第11行.
Serial.printf("[%lu] drawn dithering bmp.\n",millis()); //显示信息
guy.display(); //1.2.0版本之后, drawImage不再刷屏, 需要额外调用display函数刷屏.
delay(2000);
guy.display(FILL_WHITE,READGUY_FAST); //在保持屏幕缓存不变的时候快速刷新白屏.
guy.drawImage(sp,0,0,guy.width(),guy.height()); //绘画的画布可以被放大或者缩小到任意宽度和高度.
//此处的参数调用表示将会在屏幕坐标(0,0)开始显示, 显示的画布宽度缩放到屏幕宽度, 画布高度缩放到屏幕高度.
guy.display(); //调用display函数刷屏.
delay(2000);
guy.display(FILL_WHITE,READGUY_FAST); //在保持屏幕缓存不变的时候快速刷新白屏
guy.fillScreen(1); //白屏清屏(清屏幕缓存)
guy.drawImage(sp,10,10,65,50); //缩放: 缩小到65X50
guy.display(); //调用display函数刷屏.
delay(2000);
//以下函数可以允许你用灰度显示画布.
//在刷灰度之前, 该函数可以对刷屏区域自动慢刷白屏.
//0-关闭连续刷屏 开启16阶灰度抖动. 连续刷屏并非所有的屏幕都支持. 所以此处使用了一个函数检验.
if(guy.supportGreyscaling()==-16){ //此函数返回-16代表支持连续刷屏. 返回16代表支持灰度, 返回0代表仅黑白.
guy.fillScreen(1); //白屏清屏(清屏幕缓存)
guy.setGreyQuality(0); //设置刷屏方式:0-关闭连续刷屏, 开启16阶灰度抖动.
guy.draw16grey(sp,9,9); //使用16级灰度的方式显示图片 需要的时间比较长
delay(2000);
}
//1-开启连续刷屏 开启16阶灰度抖动 (默认值)
guy.fillScreen(1); //白屏清屏(清屏幕缓存)
guy.setGreyQuality(1); //设置刷屏方式:1-开启连续刷屏, 开启16阶灰度抖动. 在初始化时, 这是默认值.
guy.draw16grey(sp,12,12,120,120); //使用16级灰度的方式显示图片. 后两个参数120代表缩放之后的宽度高度
delay(2000);
//2-关闭连续刷屏 关闭16阶灰度抖动. 连续刷屏并非所有的屏幕都支持. 所以此处使用了一个函数检验.
if(guy.supportGreyscaling()==-16){ //此函数返回-16代表支持连续刷屏. 返回16代表支持灰度, 返回0代表仅黑白.
guy.fillScreen(1); //白屏清屏(清屏幕缓存)
guy.setGreyQuality(2); //2-关闭连续刷屏, 关闭16阶灰度抖动
guy.draw16grey(sp,9,9,50,100); //使用16级灰度的方式显示图片. 参数50和100分别代表宽度缩放和高度缩放
delay(2000);
}
//3-开启连续刷屏 关闭16阶灰度抖动
guy.fillScreen(1); //白屏清屏(清屏幕缓存)
guy.setGreyQuality(3); //3-打开连续刷屏, 关闭16阶灰度抖动
guy.draw16grey(sp,3,3,100,50); //使用16级灰度的方式显示图片 参数100和50分别代表宽度缩放和高度缩放
delay(2000);
//灰度显示画布到此显示完了
sp.deleteSprite(); //关闭画布, 释放图片占用的大量内存
Serial.printf("[%lu] drawn 16-layer greyscale bmp.\n",millis()); //显示信息
delay(2000);
// 3 - 显示图片 方式2 --------------------- 3 - 显示图片 方式2:使用readguyImage类. -----<<<
//readguy驱动程序的内部有一个屏幕缓存, 可以用于存储显示引擎显示的内容, 在刷屏时 直接发送此缓存的内容.
//显示引擎会通过显示函数, 来改写屏幕缓存的内容. (显示就是这样显示的. 类似于print, drawPixel, fillRect...)
//此绘制方式可以做到几乎不消耗额外的内存, 仅需要屏幕缓存即可显示图片.
//适合用在esp8266或者消耗的内存较多的项目上.
//显示原理比较复杂, 因为显示函数的限制, 图片不能直接显示到屏幕缓存中.
//大体上说就是用屏幕缓存作为显示图片的中间内存, 再分阶段解码图片, 最后通过自定义刷屏函数, 完成刷新图片
//使用此方法, 因为屏幕缓存被复用作图片解码中间内存, 屏幕缓存上原本的内容会因为图片解码而发生改变.
//不需要指定额外的外部内存.
//显示BMP格式
readguyImage im(guy); //定义一个绘制器, 此类中的函数用于绘制图片.
//所有的绘制图片的参数均需要放入此结构内
//直接更改im内的数据即可设置绘制参数. (就像结构体一样用它)
im.baseFs=&guy.guyFS(); //在此处就是设置文件系统.
im.filename=BMP_FILE; //在此直接设置文件路径和文件名.
guy.display(FILL_WHITE,READGUY_SLOW); //将屏幕全刷成白屏. 为了即将的图片刷新.
//建议在使用drawImageFile函数之前, 使用慢刷刷白屏, 可以保证显示效果清晰可见.
im.drawImageFile(); //显示BMP格式.图片. im会自动识别文件扩展名并绘制.
//此处不再需要调用 guy.display() 函数即可显示.
//此时显示缓存已经清空(因为显示缓存内原本是用来解码图片用的)
//此时直接使用guy.display()将刷白屏.
delay(2000);
//显示JPG格式
im.filename=JPG_FILE; //在此直接设置文件路径和文件名.
im.scalex=0.3333333f; //设置横坐标图片缩放
im.scaley=0.3333333f; //设置纵坐标图片缩放.
im.offsetx=70; //设置显示偏移. 此处的70说明 JPG图像应该从图片文件的(70,40)坐标处开始解码
im.offsety=40; //设置显示偏移Y轴方向.
im.background=0; //设置背景颜色, 0黑1白, 此处设为背景色为黑色.
guy.display(FILL_WHITE,READGUY_SLOW); //将屏幕全刷成白屏. 为了即将的图片刷新.
im.drawImageFile(); //显示JPG格式.图片. im会自动识别文件扩展名并绘制.
delay(2000);
#ifndef ESP8266 //显示PNG格式. ESP8266不支持PNG格式.
im.filename=PNG_FILE; //PNG图片. ESP8266的内存不够, 所以不能显示PNG格式~(T_T)~
im.x=10; //设置在哪里绘制图片. 在此编辑图片绘制起始点的 X, Y 坐标
im.y=5 ; //
im.scalex=400.0f/1280.0f;
im.scaley=300.0f/576.0f;
guy.display(FILL_WHITE, READGUY_SLOW);//显示. 此处的功能就是将显示缓存输出到屏幕上
im.drawImageFile(); //显示PNG格式.图片. ESP8266可能不会绘制.
delay(2000);
#endif
//开启灰度显示, 然后显示BMP格式
im.filename=BMP_FILE; //在此直接设置文件路径和文件名.
im.scalex=0.75f; //设置横坐标图片缩放
im.scaley=0.5625f; //设置纵坐标图片缩放.
im.offsetx=0; //设置显示偏移. 此处的70说明 JPG图像应该从图片文件的(70,40)坐标处开始解码
im.offsety=0; //设置显示偏移Y轴方向.
im.background=1; //设置背景颜色, 0黑1白, 此处设为背景色为黑色.
//预分配内存, 可以用作显示缓存.
#ifdef ESP8266
#define MEM_POOL 10000
#else
#define MEM_POOL 60000
#endif
uint8_t *buf = new uint8_t [MEM_POOL]; //分配缓存
im.exPool=buf; //设置外部缓存内存地址
im.exPoolSize=MEM_POOL; //设置外部缓存内存大小
guy.setGreyQuality(1); //设置灰度模式为默认灰度显示模式
guy.display(FILL_WHITE,READGUY_SLOW); //将屏幕全刷成白屏. 为了即将显示灰度图.
im.enableFloyd=0; //禁用掉抖动算法.
im.drawImageFile(1); //显示JPG格式.图片.
delay(2000);
guy.display(FILL_WHITE,READGUY_SLOW); //将屏幕全刷成白屏. 为了即将显示灰度图.
im.enableFloyd=1; // 重新启用抖动算法.
im.drawImageFile(1); //显示JPG格式.图片.
delay(5000);
delete [] buf; //缓存用完了, 释放内存.
im.exPool=nullptr; //用完之后将这个内存地址置为null, 避免重复使用导致内存不可访问.
im.exPoolSize=0;
// --------------------- 4 - 显示图片 使用readguyImage类的externalBuffer绘制到主屏幕缓存. -----<<<
//实现原理: 通过一个额外的缓存, 显示引擎可以分多次将图片显示在这个额外的内存中, 再将这块内存写入到缓存内.
//由于用了外部内存, 所以可以分多次显示一张大图片, 相比于分配整张大图片的内存, 要更节省内存.代价是速度更慢
//readguy驱动程序的内部有一个屏幕缓存, 如果使用这个缓存来显示图片, 屏幕上原本的东西也会变得混乱.(上个示例)
//因此, readguyImage类提供了使用外部内存的显示功能.
//分为多次, 每次将图片的一小部分显示到外部内存中, 再写入到屏幕缓存内.
//需要提供额外分配好的内存, 但此函数不会更改显示区域之外的显示缓存.
guy.fillScreen(1); //将屏幕全刷成白屏. 为了即将的图片刷新.
for(int i=1;i<10;i++){ //在屏幕上绘画线段. 本质是更改显示缓存.
guy.drawLine(i*guy.width()/10,0,0,i*guy.height()/10,0);
guy.drawLine(i*guy.width()/10,guy.height()-1,guy.width()-1,i*guy.height()/10,0);
}
guy.drawLine(guy.width(),0,0,guy.height(),0);
guy.display(READGUY_SLOW); //刷新屏幕, 显示绘画的线段
//im.baseFs=&guy.guyFS(); //直接更改im内的数据即可设置绘制参数. 在此处就是设置文件系统.
im.filename=BMP_FILE; //在此直接设置文件路径和文件名.
im.background=0; //设置背景颜色, 0黑1白, 此处设为背景色为黑色.
im.x=15; //设置将要显示的横坐标
im.y=15; //设置将要显示的纵坐标
im.w=180; //设置将要显示区域的宽度
im.h=270; //设置将要显示区域的高度
im.offsetx=0; //如果不从左上角开始解码图片, 从哪个坐标开始显示/解码图片?
im.offsety=0;
im.scalex=0.5625f; //设置横坐标缩放.
im.scaley=0.5625f; //设置纵坐标缩放. 为0代表与横坐标缩放相同.
buf = new uint8_t [ 60*270 ]; //分配缓存
im.exPool=buf; //设置外部缓存内存地址
im.exPoolSize=60*270; //设置外部缓存内存大小
im.drawImageToBuffer(); //将图片写入到屏幕缓存. im会自动识别文件扩展名并绘制.
guy.display(); //显示缓存.此时应当能看到图片.
delete [] buf; //缓存用完了, 释放内存.
im.exPool=nullptr; //用完之后将这个内存地址置为null, 避免重复使用导致内存不可访问.
im.exPoolSize=0;
}
void loop(){
delay(1);
}/* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -61,16 +61,16 @@ uint8_t readguyImage::drawImgHandler(int r, LGFX_Sprite *spr){
//_x=y-r/stage*_h; yd=0;
//_y=x;//(widthDiv8<<3)-x-1;
_x=x-r/stage*_h;
_y=y; yd=0;
_y=y+w-guy->drvWidth(); yd=0;
if(_x<0){ xd=-_x; _x=0; }
break;
case 2:
_x=x; xd=0;
_y=y-(GUY_STAGES-r/stage-1)*_h;
_x=x+w-guy->drvWidth(); xd=0;
_y=y+(r/stage+1)*_h-h;
if(_y<0){ yd=-_y; _y=0; }
break;
case 3:
_x=x-(GUY_STAGES-r/stage-1)*_h;
_x=x+(r/stage+1)*_h-h;
_y=y; yd=0;
if(_x<0){ xd=-_x; _x=0; }
break;
@@ -80,9 +80,11 @@ uint8_t readguyImage::drawImgHandler(int r, LGFX_Sprite *spr){
case 1:
spr->drawBmpFile(*baseFs,filename,_x,_y,0,0,offsetx+xd,offsety+yd,scalex,scaley,datum);
break;
#ifndef ESP8266
case 2:
spr->drawPngFile(*baseFs,filename,_x,_y,0,0,offsetx+xd,offsety+yd,scalex,scaley,datum);
break;
#endif
case 3:
spr->drawJpgFile(*baseFs,filename,_x,_y,0,0,offsetx+xd,offsety+yd,scalex,scaley,datum);
break;
@@ -224,15 +226,15 @@ void readguyImage::drawImageFile(bool use16grey){
//Serial.printf("filename: %s, exname: %s\n",filename,ex);
//图片将会分割成8个部分, 分块绘制, 节省内存.
w=(guy->memWidth()+7)&0x7ffffff8; //guy->guyMemoryWidth() 返回不随旋转参数而改变的显示内存宽度
w=(guy->drvWidth()+7)&0x7ffffff8; //guy->guyMemoryWidth() 返回不随旋转参数而改变的显示内存宽度
if(!w) return; //保证宽度>0
h=guy->memHeight();
h=guy->drvHeight();
if(exPoolSize>guy->bufferLength()){ //当外部缓存的像素超过屏幕缓存时,使用外部缓存作为主缓冲区
_h=exPoolSize/w;
_pool=exPool;
}
if(_pool==nullptr) {
_h=(h+7)>>3; //设置缓存区的高度. 更多内存将可以更快显示
_h=h>>3; //设置缓存区的高度. 更多内存将可以更快显示
_pool=(uint8_t *)guy->getBuffer();
}
//(guy->guyMemoryHeight()+7)>>3 返回高度,并补齐后右移三位 (等效于除以2³, 分成8份)
@@ -250,8 +252,10 @@ void readguyImage::drawImageFile(bool use16grey){
//随后打开图片进行解码. 可选显示的位置和宽度高度参数, 屏幕上的其他部分则会变成白色.
if(strcmp(ex,"bmp") == 0 || strcmp(ex,"BMP") == 0) //BMP格式, 绘制BMP图片
format|=1; //BMP格式
#ifndef ESP8266
else if(strcmp(ex,"png") == 0 || strcmp(ex,"PNG") == 0) //PNG格式, 绘制PNG图片
format|=2; //PNG格式
#endif
else if(strcmp(ex,"jpg") == 0 || strcmp(ex,"JPG") == 0 || strcmp(ex,"jpeg") == 0 || strcmp(ex,"JPEG") == 0)
format|=3; //JPG格式
else return; //未知格式
@@ -271,7 +275,7 @@ void readguyImage::drawImageFile(bool use16grey){
}
else{
// ************* 提示: 编写此示例时的最新版本LovyanGFX库不提供此函数. 请看ex06_Image.ino文件开头的解决方法!
guy->display(std::bind(&readguyImage::drawImgHandler,this,std::placeholders::_1,&bmpspr));
guy->display(std::bind(&readguyImage::drawImgHandler,this,std::placeholders::_1,&bmpspr),READGUY_FAST);
// 此函数过不了编译 需要改库.
}
@@ -291,8 +295,10 @@ uint8_t readguyImage::drawImageToBuffer(){
if(strcmp(ex,"bmp") == 0 || strcmp(ex,"BMP") == 0) //BMP格式, 绘制BMP图片
format|=1; //BMP格式
#ifndef ESP8266
else if(strcmp(ex,"png") == 0 || strcmp(ex,"PNG") == 0) //PNG格式, 绘制PNG图片
format|=2; //PNG格式
#endif
else if(strcmp(ex,"jpg") == 0 || strcmp(ex,"JPG") == 0 || strcmp(ex,"jpeg") == 0 || strcmp(ex,"JPEG") == 0)
format|=3; //JPG格式
else return 2; //未知格式
@@ -310,9 +316,11 @@ uint8_t readguyImage::drawImageToBuffer(){
case 1:
spr.drawBmpFile(*baseFs,filename,0,0,0,0,offsetx,offsety+_h*i,scalex,scaley);
break;
#ifndef ESP8266
case 2:
spr.drawPngFile(*baseFs,filename,0,0,0,0,offsetx,offsety+_h*i,scalex,scaley);
break;
#endif
case 3:
spr.drawJpgFile(*baseFs,filename,0,0,0,0,offsetx,offsety+_h*i,scalex,scaley);
break;

View File

@@ -1,341 +0,0 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
*
* @file ex06_Image.ino
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @version 1.0
* @date 2023-11-01
* @brief ReadGuy 图片显示功能演示.
* 1. 在运行过ex01或者ex02的开发板上 编译烧录本程序.
* 2. 将该项目data文件夹内的所有文件放置于SD卡的根目录上.
*
* @note 重要消息: 这是一个实验性功能. 可能你所使用的LGFX库版本较旧而无法通过编译.
* 如果你的项目中无法成功编译源码中的setBuffer, 请更改LovyanGFX库的函数!
* 位于文件 LovyanGFX/src/lgfx/v1/LGFX_Sprite.hpp
* 第155行 void setBuffer 函数:
* 修改为如下内容并保存
*
``` C++
void setBuffer(void* buffer, int32_t w, int32_t h, color_depth_t bpp = rgb565_2Byte)
{
deleteSprite();
if (bpp != 0) {
_write_conv.setColorDepth(bpp);
_read_conv = _write_conv;
_panel_sprite.setColorDepth(bpp);
}
_panel_sprite.setBuffer(buffer, w, h, &_write_conv);
_img = _panel_sprite.getBuffer();
_sw = w;
_clip_r = w - 1;
_xpivot = w >> 1;
_sh = h;
_clip_b = h - 1;
_ypivot = h >> 1;
}
```
* 完成后请再次尝试编译
* [已经向lovyan03/LovyanGFX发布issue, 等待解决]
*
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//在这里包含程序需要用到的库函数
#include <Arduino.h> //arduino功能基础库. 在platformIO平台上此语句不可或缺
#include "readguy.h" //包含readguy_driver 基础驱动库
#include "guy_image.h" //包含 readguy_image 图片功能库. 此库文件放在项目文件夹内.
constexpr const char * BMP_FILE = "/bmp.bmp"; //在此更改你的图片文件路径名
constexpr const char * JPG_FILE = "/jpg.jpg"; //在此更改你的图片文件路径名
constexpr const char * PNG_FILE = "/png.png"; //在此更改你的图片文件路径名
ReadguyDriver guy;//新建一个readguy对象, 用于显示驱动.
void setup(){
// 1 - 初始化和启动ReadGuy --------------------- 1 - 初始化和启动ReadGuy -------<
Serial.begin(115200); //初始化串口
guy.init(); //初始化readguy_driver 基础驱动库.
guy.setRotation(3); //设置旋转. 旋转属性不会影响图片显示的所有功能
// 2 - 显示图片 方式1 --------------------- 2 - 显示图片 方式1:使用readguy提供的drawImage方法. ------<<
//此功能可以在Sprite绘制图片, 再使用内置的drawImage方法来显示出这个sprite.
//此方法消耗的内存颇多, 需要整个sprite才能成功绘制.
guy.fillScreen(1); //清屏
guy.display(); //显示白屏,用于将来显示图片.
LGFX_Sprite sp(&guy); //创建一个Sprite (可以存储一些像素, 快速读写)
sp.setColorDepth(lgfx::v1::color_depth_t::grayscale_8bit); //设置sprite的色彩深度
sp.createSprite(80,80); //从文件创建BMP图像信息.
sp.drawBmpFile(guy.guyFS(),BMP_FILE,0,0); //显示JPG文件
Serial.printf("[%lu] sp.w: %d, h: %d, res: %im.\n",millis(),sp.width(),sp.height(),ESP.getFreeHeap());
guy.drawImage(sp,9,9); //使用抖动像素的方式显示图片(不是灰度, 只有黑点和白点的那种显示效果)
Serial.printf("[%lu] drawn dithering bmp.\n",millis()); //显示信息
guy.display(); //1.2.0版本之后, drawImage不再刷屏, 需要额外调用display函数刷屏.
delay(2000);
//0-关闭连续刷屏 开启16阶灰度抖动
guy.setGreyQuality(0); //部分屏幕允许使用不同的灰度显示模式.
guy.draw16grey(sp,9,9); //使用16级灰度的方式显示图片 需要的时间比较长
delay(2000);
//1-开启连续刷屏 开启16阶灰度抖动 (默认值)
guy.setGreyQuality(1); //部分屏幕允许使用不同的灰度显示模式.
guy.draw16grey(sp,9,9); //使用16级灰度的方式显示图片 需要的时间比较长
delay(2000);
//2-关闭连续刷屏 关闭16阶灰度抖动
guy.setGreyQuality(2); //部分屏幕允许使用不同的灰度显示模式.
guy.draw16grey(sp,9,9); //使用16级灰度的方式显示图片 需要的时间比较长
delay(2000);
//3-开启连续刷屏 关闭16阶灰度抖动
guy.setGreyQuality(3); //部分屏幕允许使用不同的灰度显示模式.
guy.draw16grey(sp,9,9); //使用16级灰度的方式显示图片 需要的时间比较长
delay(2000);
sp.deleteSprite(); //关闭图片文件, 释放图片占用的大量内存
Serial.printf("[%lu] drawn 16-layer greyscale bmp.\n",millis()); //显示信息
delay(2000);
// 3 - 显示图片 方式2 --------------------- 3 - 显示图片 方式2:使用readguyImage类. -----<<<
//readguy驱动程序的内部有一个屏幕缓存, 可以用于存储显示引擎显示的内容, 在刷屏时 直接发送此缓存的内容.
//显示引擎会通过显示函数, 来改写屏幕缓存的内容. (显示就是这样显示的. 类似于print, drawPixel, fillRect...)
//此绘制方式可以做到几乎不消耗额外的内存, 仅需要屏幕缓存即可显示图片.
//适合用在esp8266或者消耗的内存较多的项目上.
//显示原理比较复杂, 因为显示函数的限制, 图片不能直接显示到屏幕缓存中.
//大体上说就是用屏幕缓存作为显示图片的中间内存, 再分阶段解码图片, 最后通过自定义刷屏函数, 完成刷新图片
//使用此方法, 因为屏幕缓存被复用作图片解码中间内存, 屏幕缓存上原本的内容会因为图片解码而发生改变.
//不需要指定额外的外部内存.
//显示BMP格式
readguyImage im(guy); //定义一个绘制器, 此类中的函数用于绘制图片.
//所有的绘制图片的参数均需要放入此结构内
//直接更改im内的数据即可设置绘制参数. (就像结构体一样用它)
im.baseFs=&guy.guyFS(); //在此处就是设置文件系统.
im.filename=BMP_FILE; //在此直接设置文件路径和文件名.
guy.display(FILL_WHITE,false); //将屏幕全刷成白屏. 为了即将的图片刷新.
//建议在使用drawImageFile函数之前, 使用慢刷刷白屏, 可以保证显示效果清晰可见.
im.drawImageFile(); //显示BMP格式.图片. im会自动识别文件扩展名并绘制.
//此处不再需要调用 guy.display() 函数即可显示.
//此时显示缓存已经清空(因为显示缓存内原本是用来解码图片用的)
//此时直接使用guy.display()将刷白屏.
delay(2000);
//显示JPG格式
im.filename=JPG_FILE; //在此直接设置文件路径和文件名.
im.scalex=0.3333333f; //设置横坐标图片缩放
im.scaley=0.3333333f; //设置纵坐标图片缩放.
im.offsetx=70; //设置显示偏移. 此处的70说明 JPG图像应该从图片文件的(70,40)坐标处开始解码
im.offsety=40; //设置显示偏移Y轴方向.
im.background=0; //设置背景颜色, 0黑1白, 此处设为背景色为黑色.
guy.display(FILL_WHITE,false); //将屏幕全刷成白屏. 为了即将的图片刷新.
im.drawImageFile(); //显示JPG格式.图片. im会自动识别文件扩展名并绘制.
delay(2000);
#ifndef ESP8266 //显示PNG格式. ESP8266不支持PNG格式
im.filename=PNG_FILE; //PNG图片. ESP8266的内存不够, 所以不能显示PNG格式~(T_T)~
im.x=10; //设置在哪里绘制图片. 在此编辑图片绘制起始点的 X, Y 坐标
im.y=5 ; //
im.scalex=400.0f/1280.0f;
im.scaley=300.0f/576.0f;
guy.display(FILL_WHITE, false); //显示. 此处的功能就是将显示缓存输出到屏幕上
im.drawImageFile(); //显示JPG格式.图片. ESP8266可能不会绘制.
delay(2000);
#endif
//开启灰度显示, 然后显示BMP格式
im.filename=BMP_FILE; //在此直接设置文件路径和文件名.
im.scalex=0.75f; //设置横坐标图片缩放
im.scaley=0.5625f; //设置纵坐标图片缩放.
im.offsetx=0; //设置显示偏移. 此处的70说明 JPG图像应该从图片文件的(70,40)坐标处开始解码
im.offsety=0; //设置显示偏移Y轴方向.
im.background=1; //设置背景颜色, 0黑1白, 此处设为背景色为黑色.
#ifdef ESP8266
#define MEM_POOL 10000
#else
#define MEM_POOL 60000
#endif
uint8_t *buffer = new uint8_t [ MEM_POOL ]; //分配缓存
im.exPool=buffer; //设置外部缓存内存地址
im.exPoolSize=MEM_POOL; //设置外部缓存内存大小
guy.setGreyQuality(1); //设置灰度模式为默认灰度显示模式
guy.display(FILL_WHITE,false); //将屏幕全刷成白屏. 为了即将显示灰度图.
im.enableFloyd=0; //禁用掉抖动算法.
im.drawImageFile(1); //显示JPG格式.图片.
delay(2000);
guy.display(FILL_WHITE,false); //将屏幕全刷成白屏. 为了即将显示灰度图.
im.enableFloyd=1; // 重新启用抖动算法.
im.drawImageFile(1); //显示JPG格式.图片.
delay(5000);
delete []buffer;
im.exPool=nullptr; //用完之后将这个内存地址置为null, 避免重复使用导致内存不可访问.
im.exPoolSize=0;
/*
// 灰度测试代码 -------------------------------------------------------------------------- 灰度测试代码
im.filename=BMP_FILE; //在此直接设置文件路径和文件名.
guy.setGreyQuality(0);
guy.display(FILL_WHITE, false);
#ifdef ESP8266
#define MEM_POOL 10000
#else
#define MEM_POOL 60000
#endif
uint8_t *buffer = new uint8_t [ MEM_POOL ]; //分配缓存
im.exPool=buffer; //设置外部缓存内存地址
im.exPoolSize=MEM_POOL; //设置外部缓存内存大小
im.enableFloyd = 0;
im.drawImageFile(true); //显示BMP格式.图片. im会自动识别文件扩展名并绘制.
//此处不再需要调用 guy.display() 函数即可显示.
//此时显示缓存已经清空(因为显示缓存内原本是用来解码图片用的)
//此时直接使用guy.display()将刷白屏.
delay(3000);
guy.display(FILL_WHITE,false);
im.enableFloyd = 1;
guy.setGreyQuality(1);
im.drawImageFile(true); //显示BMP格式.图片. im会自动识别文件扩展名并绘制.
delay(3000);
//显示JPG格式
im.filename=JPG_FILE; //在此直接设置文件路径和文件名.
im.scalex=1.3333333f;
im.scaley=1.3333333f;
//im.scalex=0.3333333f; //设置横坐标图片缩放
//im.scaley=0.3333333f; //设置纵坐标图片缩放.
//im.offsetx=70; //设置显示偏移. 此处的70说明 JPG图像应该从图片文件的(70,40)坐标处开始解码
//im.offsety=40; //设置显示偏移Y轴方向.
im.background=0; //设置背景颜色, 0黑1白, 此处设为背景色为黑色.
guy.display(FILL_WHITE, false);
im.drawImageFile(); //显示JPG格式.图片. im会自动识别文件扩展名并绘制.
delete []buffer;
// 灰度测试代码 -------------------------------------------------------------------------- 灰度测试代码
*/
// --------------------- 4 - 显示图片 使用readguyImage类的externalBuffer绘制到主屏幕缓存. -----<<<
//实现原理: 通过一个额外的缓存, 显示引擎可以分多次将图片显示在这个额外的内存中, 再将这块内存写入到缓存内.
//由于用了外部内存, 所以可以分多次显示一张大图片, 相比于分配整张大图片的内存, 要更节省内存.代价是速度更慢
//readguy驱动程序的内部有一个屏幕缓存, 如果使用这个缓存来显示图片, 屏幕上原本的东西也会变得混乱.(上个示例)
//因此, readguyImage类提供了使用外部内存的显示功能.
//分为多次, 每次将图片的一小部分显示到外部内存中, 再写入到屏幕缓存内.
//需要提供额外分配好的内存, 但此函数不会更改显示区域之外的显示缓存.
guy.fillScreen(1); //将屏幕全刷成白屏. 为了即将的图片刷新.
for(int i=1;i<10;i++){ //在屏幕上绘画线段. 本质是更改显示缓存.
guy.drawLine(i*guy.width()/10,0,0,i*guy.height()/10,0);
guy.drawLine(i*guy.width()/10,guy.height()-1,guy.width()-1,i*guy.height()/10,0);
}
guy.drawLine(guy.width(),0,0,guy.height(),0);
guy.display(false); //刷新屏幕, 显示绘画的线段
//im.baseFs=&guy.guyFS(); //直接更改im内的数据即可设置绘制参数. 在此处就是设置文件系统.
im.filename=BMP_FILE; //在此直接设置文件路径和文件名.
im.background=0; //设置背景颜色, 0黑1白, 此处设为背景色为黑色.
im.x=15; //设置将要显示的横坐标
im.y=15; //设置将要显示的纵坐标
im.w=180; //设置将要显示区域的宽度
im.h=270; //设置将要显示区域的高度
im.offsetx=0; //如果不从左上角开始解码图片, 从哪个坐标开始显示/解码图片?
im.offsety=0;
im.scalex=0.5625f; //设置横坐标缩放.
im.scaley=0.5625f; //设置纵坐标缩放. 为0代表与横坐标缩放相同.
buffer = new uint8_t [ 60*270 ]; //分配缓存
im.exPool=buffer; //设置外部缓存内存地址
im.exPoolSize=60*270; //设置外部缓存内存大小
im.drawImageToBuffer(); //将图片写入到屏幕缓存. im会自动识别文件扩展名并绘制.
guy.display(); //显示缓存.此时应当能看到图片.
delete []buffer;
}
void loop(){
delay(1);
}/* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,74 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_template.cpp
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 源代码模板
* 使用前, 先查找替换 template 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 template 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include "guy_template.h"
#ifdef READGUY_DEV_template
namespace guydev_template{
//以下代码均为我 FriendshipEnder 原创, 呵呵哒~~
void drv::drv_init(){ //初始化屏幕
//add driver code...
}
void drv::drv_fullpart(bool part){ //初始化慢刷功能
if(lastRefresh) return;
//add driver code...
}
void drv::drv_setDepth(uint8_t i){
epdFull=0; iLut = i?(i>15?15:i):15; //如果需要, 改成自己的代码
}
void drv::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
//add driver code...
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
uint32_t u=epdFull?slowRefTime:fastRefTime; //全刷:局刷 busy时间
if(ms<u) guy_epdBusy(u-ms); //对于busy电平为低电平忙碌,高电平正常的屏幕则改为ms-u
lastRefresh=0;
//add driver code...
}
}
void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0){ //RST_PIN<0 无法唤醒
//add driver code...
}
epd_PowerOn = 0;
epdFull = 1; //强制设置为慢刷新模式
}
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,63 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_template.h
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 头文件模板
* 使用前, 先查找替换 template 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 template 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../guy_epdbase.h"
#if (!defined(_GUY_EPDtemplate_H_FILE) && (defined(READGUY_DEV_template)))
#define _GUY_EPDtemplate_H_FILE
namespace guydev_template{
constexpr int GUY_D_WIDTH =200; //驱动屏幕宽度
constexpr int GUY_D_HEIGHT =200; //驱动屏幕高度
constexpr int slowRefTime =2000; //驱动屏幕慢刷时间, 单位毫秒
constexpr int fastRefTime =500; //驱动屏幕快刷时间, 单位毫秒
class drv : public readguyEpdBase {
public:
int drv_ID() const { return READGUY_DEV_template; }
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return GUY_D_WIDTH; }; //返回显示区域宽度
//int drv_panelwidth() const { return GUY_D_WIDTH; }; //返回缓存的数据宽度
int drv_height() const{ return GUY_D_HEIGHT; }; //返回显示区域高度
void drv_setDepth(uint8_t i); //设置显示颜色深度
int drv_supportGreyscaling() const { return 16; }
private:
uint8_t epd_PowerOn=1; //是否上电. 睡眠则设为0
uint8_t epdFull=0; //是partical模式/快速刷新模式 0快刷, 1慢刷
uint8_t iLut=15; //颜色深度
};
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,23 @@
# 添加新驱动程序
1. 将文件夹 guy_template 整个拷贝到 guy_epaper 文件夹下
2. 根据里面的模板文件, 添加自定义驱动.
3. 使用查找工具搜索 src 下的以下文本.
```
//添加新屏幕型号 add displays here
```
然后把新加入的屏幕驱动 define 给加上
4. 更新 `EPD_DRIVERS_NUM_MAX` 变量, 表明一共有多少个驱动可用.
5. 加好了之后, 应该就可以使用这个驱动了.
6. 记得更改项目根目录的 readme 文件
# 禁用驱动程序
前往 guy_epaper_config.h 文件, 注释掉不需要编译的驱动文件即可

View File

@@ -971,7 +971,11 @@ CONFIG_MBEDTLS_TLS_ENABLED=y
#
# TLS Key Exchange Methods
#
# CONFIG_MBEDTLS_PSK_MODES is not set
CONFIG_MBEDTLS_PSK_MODES=y
CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y
CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_PSK=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_PSK=y
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_PSK=y
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y
CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y

View File

@@ -1012,7 +1012,11 @@ CONFIG_MBEDTLS_TLS_ENABLED=y
#
# TLS Key Exchange Methods
#
# CONFIG_MBEDTLS_PSK_MODES is not set
CONFIG_MBEDTLS_PSK_MODES=y
CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y
CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_PSK=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_PSK=y
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_PSK=y
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y
CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y

View File

@@ -978,7 +978,11 @@ CONFIG_MBEDTLS_TLS_ENABLED=y
#
# TLS Key Exchange Methods
#
# CONFIG_MBEDTLS_PSK_MODES is not set
CONFIG_MBEDTLS_PSK_MODES=y
CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y
CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_PSK=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_PSK=y
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_PSK=y
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y
CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y

View File

@@ -11,7 +11,7 @@
"type": "git",
"url": "https://github.com/fsender/readguy"
},
"version": "1.1.1",
"version": "1.3.3",
"frameworks": "arduino",
"platforms": ["espressif32", "espressif8266"],
"headers": "readguy.h",

View File

@@ -1,5 +1,5 @@
name=readguy
version=1.1.1
version=1.3.3
author=fsender <f_ender@163.com>
maintainer=fsender <f_ender@163.com>
sentence=A free E-paper display driver library supports 16-level greyscale.

View File

@@ -78,11 +78,18 @@ void guy_button::begin(uint8_t _pin, std_U8_function_U8 f, bool activeLow /* = t
state = get_state_cb(pin);
min_debounce =25; //去抖时间
long_press_ms =300; //长按持续时间+双击识别间隔最大时间
long_repeat_ms =150; //长按连按间隔时间
long_repeat_ms =200; //长按连按间隔时间
multibtn =0;
lk=0;
}
bool guy_button::isPressedRaw() {
int mi=millis();
while(lk) if(millis()-mi>GUYBTN_READ_TIMEOUT) return 0; //等待数据读完
lk=3;
bool willreturn = (get_state_cb(pin) == _pressedState);
lk=0;
return willreturn;
}
uint8_t guy_button::read() { //注意ticker不能在此触发
int mi=millis();
while(lk) if(millis()-mi>GUYBTN_READ_TIMEOUT) return 0; //等待数据读完
@@ -91,13 +98,14 @@ uint8_t guy_button::read() { //注意ticker不能在此触发
if(state == _pressedState && n - down_ms>= long_press_ms && long_clicked < n){
long_clicked = trig_mode?(n+long_repeat_ms):0xfffffffful;
lk=0;
return GUYBUTTON_long_click;
return (click_count>=3)?GUYBUTTON_xxlong_click:\
((click_count==2)?GUYBUTTON_xlong_click:GUYBUTTON_long_click);
}
uint8_t res = last_click_type;
last_click_type = GUYBUTTON_empty;
was_pressed = false;
lk=0;
return (res==GUYBUTTON_long_click)?GUYBUTTON_empty:res;
return (res>=GUYBUTTON_long_click)?GUYBUTTON_empty:res;
}
void guy_button::loop() {
@@ -140,8 +148,9 @@ void guy_button::loop() {
// was there a longclick?
if (longclick_detected) {
// was it part of a combination?
if (click_count == 1) {
last_click_type = GUYBUTTON_long_click;
if (click_count) {
last_click_type = (click_count>=3)?GUYBUTTON_xxlong_click:\
((click_count==2)?GUYBUTTON_xlong_click:GUYBUTTON_long_click);
was_pressed = true;
}
longclick_detected = false;

View File

@@ -76,14 +76,17 @@ SOFTWARE.
#define GUYBUTTON_double_click 2
#define GUYBUTTON_triple_click 3
#define GUYBUTTON_long_click 4
#define GUYBUTTON_xlong_click 5
#define GUYBUTTON_xxlong_click 6
#define GUYBTN_READ_TIMEOUT 100
#define GUYBTN_LOOP_TIMEOUT 10
class guy_button{
protected:
public:
uint16_t min_debounce ; //去抖时间
uint16_t long_press_ms ; //长按持续时间+双击识别间隔最大时间
uint16_t long_repeat_ms ; //长按连按间隔时间
protected:
uint8_t pin = 255; //未定义引脚
uint8_t state;
uint8_t prev_state;
@@ -114,16 +117,19 @@ class guy_button{
void setLongRepeatMode(bool trigMode) { trig_mode = trigMode; }
unsigned int wasPressedFor() const { return down_time_ms; }
bool isPressed() const { return (state == _pressedState); }
bool isPressedRaw() { return (get_state_cb(pin) == _pressedState); }
bool isPressedRaw(); // { return (get_state_cb(pin) == _pressedState); }
bool wasPressed(){ if(was_pressed){ was_pressed = false; return true; } return false; }
uint8_t getNumberOfClicks() const{ return click_count;}
uint8_t getType() const { return last_click_type; }
uint8_t read();
void loop();
void setMultiBtn(uint8_t btns) { multibtn = btns; }
void setMinDebounce(short n) { min_debounce =n;} //去抖时间
/* void setMinDebounce(short n) { min_debounce =n;} //去抖时间
void setLongPressMs(short n) { long_press_ms =n;} //长按持续时间+双击识别间隔最大时间
void setLongRepeat(short n) { long_repeat_ms =n;} //长按连按间隔时间
void getMinDebounce(short n) { min_debounce =n;} //去抖时间
void getLongPressMs(short n) { long_press_ms =n;} //长按持续时间+双击识别间隔最大时间
void getLongRepeat(short n) { long_repeat_ms =n;} //长按连按间隔时间 */
};
#endif /* END OF FILE. ReadGuy project. */

View File

@@ -6,11 +6,16 @@
* @file guy_driver_config.h
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief readguy 基础配置文件. 用户可以根据自己对库的编译需求来修改此文件.
*
* 如果你希望在程序代码内包含引脚定义, 请参考此文件下方的注释来确定并编译
* !!!但是这样编译得到的程序不具备跨平台特性.!!!
* 可以禁用WiFi功能来减少ESP32的flash消耗
* @version 1.0
* @date 2023-09-21
* 这样就可以只针对你设计的这一个硬件来设定引脚功能.
* 关于屏幕配置, 请参考 guy_epaper/guy_epaper_config.h 文件来配置到底哪些屏幕型号的屏幕会被包含进来
* 可以禁用WiFi功能来减少程序的flash消耗
* @version 1.1
* @date create: 2023-09-21
* last modify: 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
@@ -61,6 +66,7 @@
/// @brief 使用静态的数据 !!!注意:注释此选项编写的程序是不支持跨平台运行的!!!
/// @note 相比于禁用WiFi配网功能, 禁用此功能减少的flash并不多, 为保证程序可在不同屏幕上运行, 请不要注释此选项
// 关闭此选项自动禁用wifi功能. 如需wifi功能需要自己在程序里加.
#define DYNAMIC_PIN_SETTINGS
/// @brief 启用WIFI配网功能.必须先启用 #define DYNAMIC_PIN_SETTINGS. 此选项对 ESP32xx 会减少大量flash.
@@ -69,11 +75,20 @@
/// @brief 启用I2C功能. 可用于联网时钟, 温度计, 陀螺仪等外设. 目前暂不支持库内使用类似函数. 仅可以提供引脚定义
#define READGUY_ENABLE_I2C
/** @brief (即将推出) 启用SD卡功能. 开启此功能将会使用内置SD卡管理功能. 关闭后仅可保存SD卡用到的引脚.
@note 会破坏兼容性. 若没有启用通用的SD卡驱动程序, 那么那些跨屏台编译的程序将无法用guyFS读取到SD卡.
若用户程序希望能从外部加载SD卡, 可以使用getSdMiso()等函数获取SD卡的Miso等引脚, 再由用户程序初始化SD卡. */
#define READGUY_ENABLE_SD
/// @brief 使用LittleFS作为片上文件系统, 注释此行则用SPIFFS(功能少, 不好用)
#define READGUY_USE_LITTLEFS 1
/// @brief ESP32按键服务任务的栈空间大小, 不建议普通用户更改. 默认值1024字节. 小于此大小会使程序栈溢出.
#ifdef CONFIG_IDF_TARGET_ESP32S3
#define BTN_LOOPTASK_STACK 1280
#else
#define BTN_LOOPTASK_STACK 1024
#endif
/// @brief ESP32按键服务任务运行在哪个核心. 通常运行在核心0上.
#define BTN_LOOPTASK_CORE_ID 0

View File

@@ -0,0 +1,74 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_1020A.cpp
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 源代码模板
* 使用前, 先查找替换 1020A 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 1020A 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include "guy_1020A.h"
#ifdef READGUY_DEV_1020A
namespace guydev_1020A{
//以下代码均为我 FriendshipEnder 原创, 呵呵哒~~
void drv::drv_init(){ //初始化屏幕
//add driver code...
}
void drv::drv_fullpart(bool part){ //初始化慢刷功能
if(lastRefresh) return;
//add driver code...
}
void drv::drv_setDepth(uint8_t i){
epdFull=0; iLut = i?(i>15?15:i):15; //如果需要, 改成自己的代码
}
void drv::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
//add driver code...
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
uint32_t u=epdFull?slowRefTime:fastRefTime; //全刷:局刷 busy时间
if(ms<u) guy_epdBusy(u-ms); //对于busy电平为低电平忙碌,高电平正常的屏幕则改为ms-u
lastRefresh=0;
//add driver code...
}
}
void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0){ //RST_PIN<0 无法唤醒
//add driver code...
}
epd_PowerOn = 0;
epdFull = 1; //强制设置为慢刷新模式
}
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,63 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_1020A.h
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 头文件模板
* 使用前, 先查找替换 1020A 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 1020A 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../guy_epdbase.h"
#if (!defined(_GUY_EPD1020A_H_FILE) && (defined(READGUY_DEV_1020A)))
#define _GUY_EPD1020A_H_FILE
namespace guydev_1020A{
constexpr int GUY_D_WIDTH =200; //驱动屏幕宽度
constexpr int GUY_D_HEIGHT =200; //驱动屏幕高度
constexpr int slowRefTime =2000; //驱动屏幕慢刷时间, 单位毫秒
constexpr int fastRefTime =500; //驱动屏幕快刷时间, 单位毫秒
class drv : public readguyEpdBase {
public:
int drv_ID() const { return READGUY_DEV_1020A; }
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return GUY_D_WIDTH; }; //返回显示区域宽度
//int drv_panelwidth() const { return GUY_D_WIDTH; }; //返回缓存的数据宽度
int drv_height() const{ return GUY_D_HEIGHT; }; //返回显示区域高度
void drv_setDepth(uint8_t i); //设置显示颜色深度
int drv_supportGreyscaling() const { return 16; }
private:
uint8_t epd_PowerOn=1; //是否上电. 睡眠则设为0
uint8_t epdFull=0; //是partical模式/快速刷新模式 0快刷, 1慢刷
uint8_t iLut=15; //颜色深度
};
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,69 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_154c.cpp
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 1.54英寸墨水屏 驱动库函数
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include "guy_154C.h"
#ifdef READGUY_DEV_154C
namespace guydev_154C{
//以下代码均为我 FriendshipEnder 原创, 呵呵哒~~
void drv::drv_init(){ //初始化屏幕
//add driver code...
}
void drv::drv_fullpart(bool part){ //初始化慢刷功能
if(lastRefresh) return;
//add driver code...
}
void drv::drv_setDepth(uint8_t i){
epdFull=0; iLut = i?(i>15?15:i):15; //如果需要, 改成自己的代码
}
void drv::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
//add driver code...
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
uint32_t u=epdFull?slowRefTime:fastRefTime; //全刷:局刷 busy时间
if(ms<u) guy_epdBusy(u-ms); //对于busy电平为低电平忙碌,高电平正常的屏幕则改为ms-u
lastRefresh=0;
//add driver code...
}
}
void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0){ //RST_PIN<0 无法唤醒
//add driver code...
}
epd_PowerOn = 0;
epdFull = 1; //强制设置为慢刷新模式
}
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,58 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_154c.h
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 1.54英寸墨水屏 驱动库函数 头文件
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../guy_epdbase.h"
#if (!defined(_GUY_EPD154C_H_FILE) && (defined(READGUY_DEV_154C)))
#define _GUY_EPD154C_H_FILE
namespace guydev_154C{
constexpr int GUY_D_WIDTH =200; //驱动屏幕宽度
constexpr int GUY_D_HEIGHT =200; //驱动屏幕高度
constexpr int slowRefTime =2000; //驱动屏幕慢刷时间, 单位毫秒
constexpr int fastRefTime =500; //驱动屏幕快刷时间, 单位毫秒
class drv : public readguyEpdBase {
public:
int drv_ID() const { return READGUY_DEV_154C; }
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return GUY_D_WIDTH; }; //返回显示区域宽度
//int drv_panelwidth() const { return GUY_D_WIDTH; }; //返回缓存的数据宽度
int drv_height() const{ return GUY_D_HEIGHT; }; //返回显示区域高度
void drv_setDepth(uint8_t i); //设置显示颜色深度
int drv_supportGreyscaling() const { return 16; }
private:
uint8_t epd_PowerOn=1; //是否上电. 睡眠则设为0
uint8_t epdFull=0; //是partical模式/快速刷新模式 0快刷, 1慢刷
uint8_t iLut=15; //颜色深度
};
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -56,16 +56,7 @@ int drvBase::Init(const unsigned char* lut) {
guy_epdParam((iLut==1)?0x02:((iLut==3 || iLut==5)?0x05:0x08)); // 2us per line
guy_epdCmd(0x11);
guy_epdParam(0x03); // X increment; Y increment
SetLut(this->lut);
// * EPD hardware init end /
EndTransfer();
return 0;
}
void drvBase::SetLut(const unsigned char* lut) {
this->lut = lut;
guy_epdCmd(0x32);
// * the length of look-up table is 30 bytes /
for (int i = 0; i < 30; i++) {
if(iLut>0 && iLut<15 && i>19 && i<23){
guy_epdParam(pgm_read_byte(lut_grey_update+iLut*2+i-(i==20?22:23)));
@@ -73,6 +64,8 @@ void drvBase::SetLut(const unsigned char* lut) {
else
guy_epdParam(pgm_read_byte(this->lut+i));
}
EndTransfer();
return 0;
}
const PROGMEM unsigned char lut_slow[] =
@@ -99,15 +92,28 @@ const PROGMEM unsigned char lut_grey_update[]={ //从上到下是依次加深
0x12, 0x44, 0x13, 0x44
};
void drvBase::drv_init(){
void drvBase::drv_init(){ //new init method can init without freshing.
iLut = 15; //新的初始化方式可以允许不初始化直接显示
sleeping=1;
//drv_color(0xffu); //睡眠模式下始终需要慢刷
}
void drvBase::drv_fullpart(bool part){ //切换慢刷/快刷功能
if(lastRefresh) return;
if(!part) iLut=15;
if(sleeping) iLut=15;
else Init(part?lut_fast:lut_slow);
}
/*void drvBase::drv_init(){
Init(lut_slow);
drv_color(0xffu); //睡眠模式下始终需要慢刷
}
void drvBase::drv_fullpart(bool part){ //切换慢刷/快刷功能
if(!part) iLut=15; //恢复默认的灰度模式
Init(part?lut_fast:lut_slow);
}
void drvBase::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新等功能
}*/
void drvBase::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新等功能
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
if(sleeping) Init(lut_slow);
BeginTransfer();
SetMemory(); // bit set = white, bit reset = black
@@ -120,14 +126,22 @@ void drvBase::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新等
guy_epdCmd(0x20);
guy_epdCmd(0xff);
EndTransfer();
guy_epdBusy((this->lut == (const uint8_t*)lut_fast)?idleFastRf:idleSlowRf);
BeginTransfer();
SetMemory(); // bit set = white, bit reset = black
guy_epdBusy(90);
guy_epdCmd(0x26); /* will send the color data */
for (int i = 0; i < epdHeight*epdWidth / 8; i++)
SpiTransfer(f(i));
EndTransfer();
lastRefresh=millis();
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
uint32_t u=(this->lut == (const uint8_t*)lut_fast)?idleFastRf:idleSlowRf;
if(ms<u) guy_epdBusy(u-ms);
lastRefresh=0;
BeginTransfer();
SetMemory(); // bit set = white, bit reset = black
guy_epdBusy(90);
guy_epdCmd(0x26); /* will send the color data */
for (int i = 0; i < epdHeight*epdWidth / 8; i++)
SpiTransfer(f(i));
EndTransfer();
}
}
void drvBase::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0) { //未定义RST_PIN时无法唤醒
@@ -136,8 +150,8 @@ void drvBase::drv_sleep() { //开始屏幕睡眠
EndTransfer();
guy_epdBusy(150);
DigitalWrite(RST_PIN, LOW);
sleeping=1;
}
sleeping=1;
}
void drvBase::drv_setDepth(uint8_t i){
iLut = i?(i>15?15:i):15;

View File

@@ -42,7 +42,7 @@ public:
virtual int drv_ID() const=0;
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f); //按照函数刷新
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return epdWidth; }; //返回显示区域宽度
int drv_height() const{ return epdHeight; }; //返回显示区域高度
@@ -57,9 +57,7 @@ private:
int Init(const unsigned char* lut);
const unsigned char* lut;
uint8_t iLut = 15;
uint8_t sleeping=1;
void SetLut(const unsigned char* lut);
uint8_t sleeping=0;
};
#ifdef READGUY_DEV_154A

View File

@@ -53,7 +53,7 @@ namespace guydev_154B_270B_290B{
0x22,0x17,0x41,0xB0,0x32,0x28
};*/
const unsigned char drvSSD168x::WS_20_30[48] =
const unsigned char drvSSD168x::WS_20_30[42] =
{ /*
0x80, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0,
0x10, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0,
@@ -93,40 +93,25 @@ const unsigned char drvSSD168x::WS_20_30[48] =
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, */
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0,
0x22, 0x17, 0x41, 0xA8, 0x32, 0x28
};
const unsigned char drvSSD168x::VSH_table[32]=
{0x00,0x24,0x24,0x24,0x25,0x26,0x27,0x28,0x2a,0x2c,0x2e,0x32,0x35,0x39,0x3d,0x41,
0x46,0x45,0x44,0x42,0x41,0x40,0x3f,0x3f,0x3c,0x39,0x36,0x34,0x32,0x31,0x30,0x2f};
{/*0x00,0xbc,0xc1,0xc6,0xcb,0x23,0x26,0x28,0x2a,0x2c,0x2e,0x32,0x35,0x39,0x3d,0x41,*/
0x00,0x24,0x28,0x2c,0x30,0x33,0x36,0x38,0x3a,0x3c,0x3e,0x40,0x42,0x44,0x45,0x46,
0x00,0x41,0x3c,0x37,0x32,0x2d,0x28,0x23,0xcb,0xc6,0xc1,0xbc,0xb7,0xb2,0xb0,0xae};
//以下代码均为我 FriendshipEnder 原创, 呵呵哒~~
void drvSSD168x::epd_init() {
if(epd_PowerOn==0) {
Reset();
epd_PowerOn=1;
_part=0;
iLut=15;
}
guy_epdCmd(0x12); //SWRESET
guy_epdBusy(10);
guy_epdCmd(0x01); //Driver output control
guy_epdParam((epdHeight-1)&0xff);
guy_epdParam(((epdHeight-1)>>8)&0xff);
guy_epdParam(0x00);
guy_epdCmd(0x11); //data entry mode
guy_epdParam(0x03);
SetMemory();
SetLut();
}
void drvSSD168x::DisplayFrame(void) {
guy_epdCmd(0x22);
guy_epdParam(_part?0x0f:0xc7);
guy_epdCmd(0x20);
}
void drvSSD168x::SetLut() {
guy_epdCmd(0x3f);
guy_epdParam(greyScaling?0x07:0x22);
guy_epdCmd(0x03); // gate voltage
guy_epdParam(0x17);
guy_epdCmd(0x04); // source voltage
guy_epdParam(greyScaling?pgm_read_byte(VSH_table+16+iLut):pgm_read_byte(VSH_table+iLut));
guy_epdParam(0xA8); // VSH2
guy_epdParam(0x32); // VSL
guy_epdCmd(0x2c); // VCOM
guy_epdParam(0x28);
unsigned char i;
guy_epdCmd(0x32);
if(_part){
@@ -134,10 +119,10 @@ void drvSSD168x::SetLut() {
guy_epdParam(i==1?0x80:(i==(greyScaling?0:2)?0x40:0x00));
for(int j=0;j<11;j++) guy_epdParam(0);
}
guy_epdParam(greyScaling?(iLut<5?4-((iLut+1)>>1):1):iLut);
guy_epdParam(greyScaling?1:iLut);
for(i=0;i<83;i++) guy_epdParam(0);
for(i=0;i<6;i++) guy_epdParam(0x22);
for(i=0;i<3;i++) guy_epdParam(0);
guy_epdParam(0x22);
for(i=0;i<8;i++) guy_epdParam(0);
}
else{
//for(i=0; i<153; i++) guy_epdParam(lut[i]==0xff?iLut:lut[i]);
@@ -151,29 +136,24 @@ void drvSSD168x::SetLut() {
for(i=0;i<66;i++) guy_epdParam(0);
for(i=0;i<9;i++) guy_epdParam(pgm_read_byte(WS_20_30+i+33));
}
guy_epdCmd(0x3f);
guy_epdParam(pgm_read_byte(WS_20_30+42));
guy_epdCmd(0x03); // gate voltage
guy_epdParam(pgm_read_byte(WS_20_30+43));
guy_epdCmd(0x04); // source voltage
guy_epdParam(greyScaling?pgm_read_byte(VSH_table+16+iLut):pgm_read_byte(VSH_table+iLut));
guy_epdParam(pgm_read_byte(WS_20_30+45)); // VSH2
guy_epdParam(pgm_read_byte(WS_20_30+46)); // VSL
guy_epdCmd(0x2c); // VCOM
guy_epdParam(pgm_read_byte(WS_20_30+47));
}
void drvSSD168x::drv_init(){
_part=0;
drv_color(0xffu);
epd_PowerOn=0;
//drv_color(0xffu);
}
void drvSSD168x::drv_fullpart(bool part){ //切换慢刷/快刷功能
if(lastRefresh) return;
if(!epd_PowerOn) part=0; //未上电 无法局刷
if(!part) { iLut=15; greyScaling=0; }
_part=part;
}
void drvSSD168x::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
void drvSSD168x::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
BeginTransfer();
if(_part && epd_PowerOn){
if(_part){
//Reset();
SetLut();
guy_epdCmd(0x37);
@@ -186,7 +166,26 @@ void drvSSD168x::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
guy_epdCmd(0x20);
guy_epdBusy(140);
}
else epd_init();
else{
if(epd_PowerOn==0) {
Reset();
epd_PowerOn=1;
_part=0;
iLut=15;
}
guy_epdCmd(0x12); //SWRESET
guy_epdBusy(10);
guy_epdCmd(0x01); //Driver output control
guy_epdParam((epdHeight-1)&0xff);
guy_epdParam(((epdHeight-1)>>8)&0xff);
guy_epdParam(0x00);
guy_epdCmd(0x11); //data entry mode
guy_epdParam(0x03);
SetMemory();
SetLut();
}
SetMemory();
guy_epdCmd(0x24);
for (int i = 0; i < epdHeight*epdWidth / 8; i++)
@@ -196,9 +195,19 @@ void drvSSD168x::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
for (int i = 0; i < epdHeight*epdWidth / 8; i++)
SpiTransfer(f(i));
}
DisplayFrame();
guy_epdCmd(0x22);
guy_epdParam(_part?0x0f:0xc7);
guy_epdCmd(0x20);
EndTransfer();
guy_epdBusy(_part?600:2300);
lastRefresh=millis();
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
uint32_t u=_part?600:2300;
if(ms<u) guy_epdBusy(u-ms);
lastRefresh=0;
}
//guy_epdBusy(_part?600:2300);
}
void drvSSD168x::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0){ //无法唤醒
@@ -206,8 +215,8 @@ void drvSSD168x::drv_sleep() { //开始屏幕睡眠
guy_epdCmd(0x10);
guy_epdParam(0x01);
EndTransfer();
epd_PowerOn=0;
}
epd_PowerOn=0;
}
void drvSSD168x::drv_setDepth(uint8_t i){ //设置显示颜色深度, 不支持的话什么都不做
if(i>0 && i<16) {

View File

@@ -40,7 +40,7 @@ public:
virtual int drv_ID() const =0;
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f); //按照函数刷新
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return epdWidth; }; //返回显示区域宽度
int drv_height() const{ return epdHeight; }; //返回显示区域高度
@@ -52,16 +52,13 @@ protected:
int epdWidth;
int epdHeight;
private:
uint8_t _part;
uint8_t epd_PowerOn = 0;
uint8_t _part=1;
uint8_t epd_PowerOn = 1;
uint8_t iLut=15;
uint8_t greyScaling=0;
//static const unsigned char _WF_PARTIAL_2IN9[48] ;
static const unsigned char WS_20_30[48] ;
static const unsigned char WS_20_30[42] ;
static const unsigned char VSH_table[32];
void epd_init();
void DisplayFrame(void);
void SetLut();
};

View File

@@ -34,7 +34,7 @@ namespace guydev_213A{
const PROGMEM uint8_t drv::_ed_lut_full[] = { // command //慢刷lut
0x22, 0x55, 0xaa, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x13, 0x13, 0x13, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
0x00, 0x13, 0x16, 0x16, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const PROGMEM uint8_t drv::_ed_lut_part[] = { // command //快刷lut
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -106,18 +106,23 @@ void drv::epd_Init(void){
}
void drv::drv_init(){ //初始化屏幕
epdFull = 1;
BeginTransfer();
epd_PowerOn = 0;
/*BeginTransfer();
epd_Init();
EndTransfer();
drv_color(0xff);
EndTransfer();*/
//drv_color(0xff);
}
void drv::drv_fullpart(bool part){ //初始化慢刷功能
if(lastRefresh) return;
if(!epd_PowerOn) part=0; //未上电 无法局刷
//if(part==epdFull) return;
if(!part) iLut=15; //恢复默认的灰度模式
epdFull = !part;
//epd_Init();
}
void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
void drv::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
BeginTransfer();
if(epdFull) { //当刷新模式从快刷切换为慢刷时, 需要发送一次init
epdFull=0;
@@ -141,24 +146,33 @@ void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
guy_epdParam(epdFull?0xc4:0x04);
guy_epdCmd(0x20);
EndTransfer();
guy_epdBusy(epdFull?1600:310);
lastRefresh=millis();
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
uint32_t u=epdFull?1600:310;
if(ms<u) guy_epdBusy(u-ms);
lastRefresh=0;
}
//guy_epdBusy(epdFull?1600:310);
}
void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN<0) return; //无法唤醒
BeginTransfer();
if(epd_PowerOn){
guy_epdCmd(0x22);
guy_epdParam(0xc3);
guy_epdCmd(0x20);
guy_epdBusy(200);
epd_PowerOn = 0;
epdFull = 1; //强制设置为慢刷新模式
if(RST_PIN>=0){ //RST_PIN<0 无法唤醒
BeginTransfer();
if(epd_PowerOn){
guy_epdCmd(0x22);
guy_epdParam(0xc3);
guy_epdCmd(0x20);
guy_epdBusy(200);
}
guy_epdCmd(0x10); //enter deep sleep
guy_epdParam(0x01);
EndTransfer();
DelayMs(200);
DigitalWrite(RST_PIN, LOW);
}
guy_epdCmd(0x10); //enter deep sleep
guy_epdParam(0x01);
EndTransfer();
DelayMs(200);
DigitalWrite(RST_PIN, LOW);
epd_PowerOn = 0;
epdFull = 1; //强制设置为慢刷新模式
}
}
#endif /* END OF FILE. ReadGuy project.

View File

@@ -47,7 +47,7 @@ public:
int drv_ID() const { return READGUY_DEV_213A; }
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f); //按照函数刷新
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return EPD_REAL_WIDTH; }; //返回显示区域宽度
//int drv_panelwidth() const { return GUY_D_WIDTH; }; //返回缓存的数据宽度
@@ -56,8 +56,8 @@ public:
int drv_supportGreyscaling() const { return 16; }
private:
void epd_Init(void);
uint8_t epdFull; //是partical模式/快速刷新模式 0快刷, 1慢刷
uint8_t epd_PowerOn = 0; //是否上电
uint8_t epdFull=0; //是partical模式/快速刷新模式 0快刷, 1慢刷
uint8_t epd_PowerOn = 1; //是否上电
uint8_t iLut = 15; //颜色深度(灰度模式用)
// uint8_t dc_d = 0; //dc引脚状态 0 command, 1 data
static const PROGMEM uint8_t _ed_lut_full[];

View File

@@ -30,7 +30,8 @@
#include <stdlib.h>
#include "guy_213b_266a.h"
// #define MEPD_DEBUG_WAVE
#ifdef READGUY_DEV_266A
#if (defined(READGUY_DEV_213B) || defined(READGUY_DEV_213B3C) \
|| defined(READGUY_DEV_266A) || defined(READGUY_DEV_266A3C))
namespace guydev_213B_266A{
static const PROGMEM unsigned char lutSlow_vcom0[] ={
0x00, 0x08, 0x00, 0x00, 0x00, 0x02,
@@ -68,26 +69,30 @@ static const PROGMEM unsigned char lutSlow_b_b[] ={
static const PROGMEM unsigned char lutFast_[]={
0x00,0x18,0x5a,0xa5,0x24
};
static const PROGMEM unsigned char greyLutLevel[]={
0, 3, 6, 9,12,15,18,21,24,28,31,34,37,42,48,54, //for BWR displays
0, 3, 5, 7,10,13,16,19,22,25,29,32,35,38,43,49,
0, 3, 5, 6, 8,11,14,17,20,23,26,29,32,35,39,44,
0, 2, 4, 5, 6, 8,10,13,15,18,21,24,27,31,35,39,
0, 2, 3, 5, 6, 7, 8,10,12,15,17,19,22,24,27,30,
0, 2, 3, 4, 5, 6, 7, 8,10,12,14,16,18,20,22,24,
0, 3, 3, 3, 4, 4, 5, 6, 7, 9,12,16,21,28,36,48,
};
drv_base::drv_base(){
guy_lutArray[0] = lutSlow_vcom0;
guy_lutArray[1] = lutSlow_w_w;
guy_lutArray[2] = lutSlow_b_w;
guy_lutArray[3] = lutSlow_w_b;
guy_lutArray[4] = lutSlow_b_b;
guy_lutArray[5] = lutFast_;
}
void drv_base::pre(){
guy_epdCmd(0x91);
send_zoneInfo();
guy_epdCmd(0x13);
}
void drv_base::epd_init(){
if(!Power_is_on) Reset();
//if(!Power_is_on) Reset();
guy_epdCmd(0x01);
guy_epdParam(0x03);
guy_epdParam(0x00);
guy_epdParam(0x2b);
guy_epdParam(0x2b);
guy_epdParam(0x10);
guy_epdParam(isBWR?0x3f:0x2b);
guy_epdParam(isBWR?0x3f:0x2b);
guy_epdParam(0x03);
guy_epdCmd(0x06);
guy_epdParam(0x17);
@@ -97,7 +102,7 @@ void drv_base::epd_init(){
guy_epdParam(0xbf);
guy_epdParam(0x0d);
guy_epdCmd(0x30);
guy_epdParam(0x29); //0x3a:100Hz, 0x29:150Hz
guy_epdParam(isBWR?0x3b:0x29); //0x3a:100Hz, 0x29:150Hz
guy_epdCmd(0x61);
guy_epdParam(0x98);
guy_epdParam(0x01);
@@ -110,11 +115,11 @@ void drv_base::epd_init(){
void drv_base::send_zoneInfo(){
guy_epdCmd(0x90);
guy_epdParam(0x00);
guy_epdParam(0x97);
guy_epdParam(epdWidth-1);
guy_epdParam(0x00);
guy_epdParam(0x00);
guy_epdParam(0x01);
guy_epdParam(0x27);
guy_epdParam((epdHeight-1)>>8);
guy_epdParam((epdHeight-1)&0xff);
guy_epdParam(0x00);
}
void drv_base::SendLuts(bool part_lut){
@@ -123,9 +128,9 @@ void drv_base::SendLuts(bool part_lut){
guy_epdCmd(i+0x20);
for(int j=0;j<(i==0?44:42);j++){
if(part_lut){
if(j==4 && ((i==2) || (greyHQ==3 && i==4))) guy_epdParam(15);
else if(j==greyHQ) guy_epdParam(greyLut);
else if(j==0) guy_epdParam(pgm_read_byte(guy_lutArray[5]+(i)));
if(j==4 && ((i==2) || (greyHQ==3 && i==4))) guy_epdParam(isBWR?0x2f:0x0f); //刷黑->白
else if(j==greyHQ) guy_epdParam(isBWR?pgm_read_byte(greyLutLevel+(greyLut+refTime*16)):greyLut);
else if(j==0) guy_epdParam(pgm_read_byte(lutFast_+(i)));
else if(j==5) guy_epdParam(1);
else guy_epdParam(0x0);
}
@@ -134,17 +139,20 @@ void drv_base::SendLuts(bool part_lut){
}
}
}
if(!Power_is_on){
if(!Power_is_on || Power_is_on==2){
guy_epdCmd(0x04);
guy_epdBusy(-60);
Power_is_on = 1;
}
}
void drv_base::drv_init(){
part_mode=0;
drv_color(0xff);
part_mode = 0;
Power_is_on = 0; //初始为未上电
//drv_color(0xff);
}
void drv_base::drv_fullpart(bool part){ //切换慢刷/快刷功能
if(lastRefresh) return;
if(!Power_is_on) part=0;
if(!part) greyLut=15; //恢复默认的灰度模式
part_mode = part;
}
@@ -155,78 +163,65 @@ void drv_base::drv_setDepth(uint8_t i){
SendLuts(1);
EndTransfer();
}
void drv_base::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
void drv_base::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
BeginTransfer();
pre();
//send pixel data -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
//Total 5624 data written.
//for(int i=0;i<GUY_D_WIDTH*GUY_D_HEIGHT/8;i++)
// guy_epdParam(c);
epd_init();
SendLuts(part_mode);
guy_epdCmd(0x91);
send_zoneInfo();
guy_epdCmd(0x13);
for (int i = 0; i < epdHeight*epdWidth/8; i++)
SpiTransfer(f(i)); //按照给定的RAM写入数据
guy_epdCmd(0x92);
if(part_mode){
guy_epdCmd(0x30);
guy_epdParam(0x3a); //0x3a:100Hz, 0x29:150Hz
//[EPDrg_BW<>] refresh fx
//guy_epdParam(0x3a); //黑白色用此行, 三色用下一行
guy_epdParam(isBWR?0x19:(refTime?0x21:0x3a)); //0x3a:100Hz, 0x29:150Hz
send_zoneInfo();
guy_epdCmd(0x12);
EndTransfer();
//[EPDrg_EPD] wait_until_idle fx: 1300
guy_epdBusy(-200);
//[EPDrg_BW<>] writeImageAgain fx
//guy_epdCmd(0x91);
//send_zoneInfo();
//guy_epdCmd(0x13);
//send image data -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
//Total 5624 data written.
//guy_epdCmd(0x92);
}
else{
epd_init();
SendLuts(0);
guy_epdCmd(0x12);
//[EPDrg_EPD] wait_until_idle fx: 1600
EndTransfer();
guy_epdBusy(-2000);
//[EPDrg_BW<>] writeImageAgain fx
BeginTransfer();
epd_init();
SendLuts(1);
pre();
//send image data -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
//Total 5624 data written.
for (int i = 0; i < epdHeight*epdWidth/8; i++)
SpiTransfer(f(i)); //按照给定的RAM写入数据
guy_epdCmd(0x92);
EndTransfer();
//[EPDrg_BW<>] powerOff fx
//guy_epdCmd(0x02);
//[EPDrg_EPD] wait_until_idle fx: 20
//guy_epdBusy(-20);
guy_epdCmd(0x12);
EndTransfer();
lastRefresh=millis();
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
if(part_mode){
if((int32_t)ms<fastRefTime) guy_epdBusy((int32_t)ms-fastRefTime);
refTime+=(refTime<5);
}
else{
if((int32_t)ms<slowRefTime) guy_epdBusy((int32_t)ms-slowRefTime);
BeginTransfer();
epd_init();
SendLuts(1);
guy_epdCmd(0x92);
EndTransfer();
refTime=0;
}
lastRefresh=0;
}
}
void drv_base::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0) { //未定义RST_PIN时无法唤醒
part_mode = 0;
BeginTransfer();
guy_epdCmd(0x02); // power off
guy_epdBusy(-20);
guy_epdCmd(0X10);
guy_epdParam(0x01);
EndTransfer();
Power_is_on = 0;
}
//if(RST_PIN>=0) { //未定义RST_PIN时无法唤醒
BeginTransfer();
guy_epdCmd(0x02); // power off
guy_epdBusy(-20);
guy_epdCmd(0X10);
guy_epdParam(0x01);
EndTransfer();
//}
part_mode = 0;
Power_is_on = 0;
}
void drv_base::drv_draw16grey_step(std::function<uint8_t(int)> f, int step){
if(_quality&1) return readguyEpdBase::drv_draw16grey_step(f,step);
if(step==1){
greyHQ=3;
refTime=6;
drv_setDepth(3);
drv_fullpart(1); //开始快刷
}
@@ -235,6 +230,7 @@ void drv_base::drv_draw16grey_step(std::function<uint8_t(int)> f, int step){
drv_dispWriter(f);
if(step==15){
greyHQ=4;
refTime=5;
drv_setDepth(15);
}
} //据说可以设置灰度渲染方式. 好像是调用setGreyQuality函数就行来着

View File

@@ -6,8 +6,10 @@
* @file guy_213b_266a.h
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 2.13英寸B型墨水屏和 2.66英寸墨水屏 驱动库函数 头文件
* @version 1.0
* @date 2023-09-21
* 增加了对三色屏幕的支持
* @version 1.1
* @date create: 2023-09-21
* last modify: 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
@@ -29,7 +31,8 @@
#include "../guy_epdbase.h"
#if (!defined(_GUY_EPD213B_266A_H_FILE) && \
(defined(READGUY_DEV_266A) || defined(READGUY_DEV_213B)))
(defined(READGUY_DEV_213B) || defined(READGUY_DEV_213B3C) \
|| defined(READGUY_DEV_266A) || defined(READGUY_DEV_266A3C)))
#define _GUY_EPD213B_266A_H_FILE
namespace guydev_213B_266A{
@@ -39,7 +42,7 @@ public:
virtual int drv_ID() const=0;
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f); //按照函数刷新
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return epdWidth; }; //返回显示区域宽度
int drv_height() const{ return epdHeight; }; //返回显示区域高度
@@ -49,31 +52,47 @@ public:
protected:
int epdWidth;
int epdHeight;
private:
void pre();
void epd_init();
void send_zoneInfo();
void SendLuts(bool part_lut);
uint8_t part_mode = 0;
uint8_t Power_is_on = 0; //初始为未上电
uint8_t part_mode = 1;
uint8_t Power_is_on = 2; //初始为未上电
uint8_t greyLut=15;
uint8_t greyHQ=4; // greyHQ==3 时 为高品质刷新灰度 否则为常规方式刷新灰度
const uint8_t *guy_lutArray[6];
uint8_t refTime=0;
uint8_t isBWR=0; //三色2.66将此屏幕数值改为1.
int16_t slowRefTime;
int16_t fastRefTime;
const uint8_t *guy_lutArray[5];
};
#ifdef READGUY_DEV_213B
class dev213B : public drv_base {
public:
dev213B(){ epdWidth=104; epdHeight=212; }
dev213B(){ epdWidth=104; epdHeight=212; slowRefTime=1300; fastRefTime=200; }
int drv_ID() const { return READGUY_DEV_213B; }
};
#endif
#ifdef READGUY_DEV_213B3C
class dev213B3C : public drv_base {
public:
dev213B3C(){ epdWidth=104; epdHeight=212; isBWR=1; slowRefTime=2800; fastRefTime=800; }
int drv_ID() const { return READGUY_DEV_213B3C; }
};
#endif
#ifdef READGUY_DEV_266A
class dev266A : public drv_base {
public:
dev266A(){ epdWidth=152; epdHeight=296; }
dev266A(){ epdWidth=152; epdHeight=296; slowRefTime=1300; fastRefTime=200; }
int drv_ID() const { return READGUY_DEV_266A; }
};
#endif
#ifdef READGUY_DEV_266A3C
class dev266A3C : public drv_base {
public:
dev266A3C(){ epdWidth=152; epdHeight=296; isBWR=1; slowRefTime=2800; fastRefTime=800; }
int drv_ID() const { return READGUY_DEV_266A3C; }
};
#endif
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,74 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_370B.cpp
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 源代码模板
* 使用前, 先查找替换 370B 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 370B 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include "guy_370B.h"
#ifdef READGUY_DEV_370B
namespace guydev_370B{
//以下代码均为我 FriendshipEnder 原创, 呵呵哒~~
void drv::drv_init(){ //初始化屏幕
//add driver code...
}
void drv::drv_fullpart(bool part){ //初始化慢刷功能
if(lastRefresh) return;
//add driver code...
}
void drv::drv_setDepth(uint8_t i){
epdFull=0; iLut = i?(i>15?15:i):15; //如果需要, 改成自己的代码
}
void drv::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
//add driver code...
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
uint32_t u=epdFull?slowRefTime:fastRefTime; //全刷:局刷 busy时间
if(ms<u) guy_epdBusy(u-ms); //对于busy电平为低电平忙碌,高电平正常的屏幕则改为ms-u
lastRefresh=0;
//add driver code...
}
}
void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0){ //RST_PIN<0 无法唤醒
//add driver code...
}
epd_PowerOn = 0;
epdFull = 1; //强制设置为慢刷新模式
}
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,63 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_370B.h
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 头文件模板
* 使用前, 先查找替换 370B 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 370B 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../guy_epdbase.h"
#if (!defined(_GUY_EPD370B_H_FILE) && (defined(READGUY_DEV_370B)))
#define _GUY_EPD370B_H_FILE
namespace guydev_370B{
constexpr int GUY_D_WIDTH =200; //驱动屏幕宽度
constexpr int GUY_D_HEIGHT =200; //驱动屏幕高度
constexpr int slowRefTime =2000; //驱动屏幕慢刷时间, 单位毫秒
constexpr int fastRefTime =500; //驱动屏幕快刷时间, 单位毫秒
class drv : public readguyEpdBase {
public:
int drv_ID() const { return READGUY_DEV_370B; }
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return GUY_D_WIDTH; }; //返回显示区域宽度
//int drv_panelwidth() const { return GUY_D_WIDTH; }; //返回缓存的数据宽度
int drv_height() const{ return GUY_D_HEIGHT; }; //返回显示区域高度
void drv_setDepth(uint8_t i); //设置显示颜色深度
int drv_supportGreyscaling() const { return 16; }
private:
uint8_t epd_PowerOn=1; //是否上电. 睡眠则设为0
uint8_t epdFull=0; //是partical模式/快速刷新模式 0快刷, 1慢刷
uint8_t iLut=15; //颜色深度
};
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -103,10 +103,14 @@ void drv::Load_LUT(unsigned char mode) {
}
void drv::drv_init(){ //初始化屏幕
sleeping = 1;
Init();
drv_color(0xff);
sleeping = 1;
part_mode=0;
//drv_color(0xff);
}
void drv::drv_fullpart(bool part){ //切换慢刷/快刷功能
if(lastRefresh || sleeping) return;
if(!part) {
greyScaling=15; //恢复默认的灰度模式
BeginTransfer();
@@ -116,7 +120,9 @@ void drv::drv_fullpart(bool part){ //切换慢刷/快刷功能
}
part_mode=part;
}
void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
void drv::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
if(sleeping) Init();
BeginTransfer();
guy_epdCmd(0x4E); guy_epdParam(0x00); guy_epdParam(0x00);
@@ -134,7 +140,15 @@ void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
Load_LUT(!part_mode);
guy_epdCmd(0x20);
EndTransfer();
guy_epdBusy(part_mode?500:1300);
lastRefresh=millis();
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
uint32_t u=part_mode?500:1300;
if(ms<u) guy_epdBusy(u-ms);
lastRefresh=0;
}
//guy_epdBusy(part_mode?500:1300);
}
void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0){
@@ -142,8 +156,9 @@ void drv::drv_sleep() { //开始屏幕睡眠
guy_epdCmd(0X10); //deep sleep
guy_epdParam(0x03);
EndTransfer();
sleeping = true;
}
sleeping = true;
part_mode=0;
}
void drv::drv_setDepth(uint8_t i){ //设置显示颜色深度, 不支持的话什么都不做

View File

@@ -40,16 +40,16 @@ public:
int drv_ID() const { return READGUY_DEV_370A; }
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f); //按照函数刷新
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return GUY_D_WIDTH; }; //返回显示区域宽度
int drv_height() const{ return GUY_D_HEIGHT; }; //返回显示区域高度
int drv_supportGreyscaling() const { return 16; }
void drv_setDepth(uint8_t i); //设置显示颜色深度, 不支持的话什么都不做
private:
uint8_t part_mode;
uint8_t part_mode=1;
uint8_t greyScaling = 15;
uint8_t sleeping = true;
uint8_t sleeping = false;
int Init(void);
void Load_LUT(unsigned char mode);
};

View File

@@ -159,16 +159,6 @@ void drv::epd_Init(void){
guy_epdCmd(0x11); //Data Entry mode
guy_epdParam(0x03); //0x03 or 0x01
}
void drv::power_up(){
if(!Power_is_on){
//Power is not On
guy_epdCmd(0x22);
guy_epdParam(0xc0);
guy_epdCmd(0x20);
guy_epdBusy(120);
Power_is_on=1;
}
}
void drv::power_down(){
Power_is_on=0;
BeginTransfer();
@@ -187,18 +177,28 @@ void drv::SetLut(const unsigned char* lut){
}
}
void drv::drv_init(){ //初始化屏幕
epdFull = 2;
drv_color(0xff);
Power_is_on = 0; //初始为未上电
epdFull = 2; //初始设为正在休眠
//drv_color(0xff);
}
void drv::drv_fullpart(bool part){ //初始化慢刷功能
if(!part) GreyScaling=0;
if(lastRefresh) return;
if(epdFull<=1) epdFull = !part; //epdFull==2代表睡眠中, 不能快刷
if(epdFull) GreyScaling=0;
}
void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
void drv::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
BeginTransfer();
epd_Init();
SetMemory();
power_up();
if(!Power_is_on){ //Power is not On, Power up.
guy_epdCmd(0x22);
guy_epdParam(0xc0);
guy_epdCmd(0x20);
guy_epdBusy(120);
Power_is_on=1;
}
SetMemory();
if(epdFull){ //慢刷
guy_epdCmd(0x26);
@@ -214,9 +214,7 @@ void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
SetLut(lut_213_B72_Full);
guy_epdCmd(0x22);
guy_epdParam(0xc4);
guy_epdCmd(0x20);
EndTransfer();
guy_epdBusy(1600); //等待刷完
//guy_epdBusy(1600); //等待刷完
}
else{ //快刷
guy_epdCmd(0x2c); //may a mistake? 此处不需要设置vcom
@@ -224,17 +222,24 @@ void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
SetLut(GreyScalingHighQuality?lut_213_B72_16grey:lut_213_B72);
guy_epdCmd(0x22);
guy_epdParam(0x04);
guy_epdCmd(0x20);
EndTransfer();
guy_epdBusy(260); //等待屏幕刷新完成
}
BeginTransfer(); //write again
SetMemory();
guy_epdCmd(0x26);
for (int i = 0; i < GUY_D_HEIGHT*GUY_D_WIDTH/8; i++)
SpiTransfer(f(i)); //按照给定的RAM写入数据
guy_epdCmd(0x20);
EndTransfer();
if(epdFull) power_down();
lastRefresh=millis();
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
uint32_t u=epdFull?1600:260;
if(ms<u) guy_epdBusy(u-ms); //等待屏幕刷新完成
lastRefresh=0;
BeginTransfer(); //write again
SetMemory();
guy_epdCmd(0x26);
for (int i = 0; i < GUY_D_HEIGHT*GUY_D_WIDTH/8; i++)
SpiTransfer(f(i)); //按照给定的RAM写入数据
EndTransfer();
if(epdFull) power_down();
}
}
void drv::drv_draw16grey_step(std::function<uint8_t(int)> f, int step){
if(_quality&1) return readguyEpdBase::drv_draw16grey_step(f,step);
@@ -260,13 +265,14 @@ void drv::drv_draw16grey_step(std::function<uint8_t(int)> f, int step){
}*/
void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0) { //未定义RST_PIN时无法唤醒
epdFull=2; //睡眠
power_down();
BeginTransfer();
guy_epdCmd(0x10); // deep sleep mode
guy_epdParam(0x01); // enter deep sleep
EndTransfer();
}
epdFull=2; //睡眠
Power_is_on=0;
}
}
#endif /* END OF FILE. ReadGuy project.

View File

@@ -41,7 +41,7 @@ public:
int drv_ID() const { return READGUY_DEV_420A; }
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f); //按照函数刷新
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return GUY_D_WIDTH; }; //返回显示区域宽度
int drv_height() const{ return GUY_D_HEIGHT; }; //返回显示区域高度
@@ -53,14 +53,13 @@ private:
void epd_Init(void);
void SetLut(const unsigned char* lut);
uint8_t Power_is_on = 0; //初始为未上电
uint8_t epdFull = 2; //是partical模式/快速刷新模式 0快刷, 1慢刷
uint8_t epdFull = 1; //是partical模式/快速刷新模式 0快刷, 1慢刷
uint8_t GreyScaling = 0; //是否正在灰度显示
uint8_t GreyScalingHighQuality = 0; //是否正在16阶高品质灰度显示
static const PROGMEM unsigned char epd42_lut_full[];
static const PROGMEM unsigned char lut_213_B72[];
static const PROGMEM unsigned char lut_213_B72_Full[];
static const PROGMEM unsigned char lut_213_B72_16grey[];
void power_up();
void power_down();
//std::function<void(int)> send; //此处不能用 void (*send)(int); 是因为lambda函数是std的
};

View File

@@ -49,35 +49,55 @@ void drv::Init(uint8_t pt) {
Reset();
guy_epdCmd(0x00); //Software reset
guy_epdParam(0x00);
delayMicroseconds(40);
guy_epdCmd(0x00);
guy_epdParam(0x1f);
guy_epdParam(0x0d);
delayMicroseconds(40);
guy_epdCmd(0x50);
guy_epdParam(0x97);
delayMicroseconds(40);
guy_epdCmd(0x01);
guy_epdParam(0x03);
guy_epdParam(0x00);
guy_epdParam(0x2b);
guy_epdParam(0x2b);
delayMicroseconds(40);
guy_epdCmd(0x06);
guy_epdParam(0x17);
guy_epdParam(0x17);
guy_epdParam(0x17);
delayMicroseconds(40);
guy_epdCmd(0x00);
guy_epdParam(0x3f);
delayMicroseconds(40);
guy_epdCmd(0x30);
guy_epdParam(0x3a);
delayMicroseconds(40);
guy_epdCmd(0x61);
guy_epdParam(0x01);
guy_epdParam(0x90);
guy_epdParam(0x01);
guy_epdParam(0x2c);
delayMicroseconds(40);
guy_epdCmd(0x82); //vcom setting: 0x00:-0.1V, 0x3a: -3.0V
guy_epdParam(0x1a);
delayMicroseconds(40);
guy_epdCmd(0x50);
guy_epdParam(0xd7);
SendLuts(pt?1:0); //不知为何, 此处需要快刷lut
delayMicroseconds(40);
//不知为何, 此处需要快刷lut
for(uint8_t i=0;i<=4;i++){ //for(uint8_t i=0;i<=(lutOption==2?5:4);i++){
guy_epdCmd(i+0x20);
for(int j=0;j<(pt?6:(i+pt==0?44:42));j++){
if(j==2 && customGreyscale) guy_epdParam(63);
else if(j==3 && i!=2) guy_epdParam(customLut);
else guy_epdParam(j<18?pgm_read_byte(guy_lutArray[i+(pt?1:0)*5]+j):0x00);
delayMicroseconds(40);
}
}
if(pt!=2) guy_epdCmd(0x04);
delayMicroseconds(40);
}
void drv::sendArea(){
@@ -96,18 +116,6 @@ void drv::sendAreaRaw(){
guy_epdParam(0x2b);
guy_epdParam(0x01);
}
void drv::SendLuts(uint8_t lutOption){ // -------- 在此发送你的Lut数据 --------<<<<
//Serial.printf("SendLuts fx: %d\n",lutOption);
for(uint8_t i=0;i<=4;i++){ //for(uint8_t i=0;i<=(lutOption==2?5:4);i++){
guy_epdCmd(i+0x20);
//Serial.printf("%d th lut loaded.\n",i+lutOption*5);
for(int j=0;j<(lutOption==1?6:(i+lutOption==0?44:42));j++){
if(j==2 && customGreyscale) guy_epdParam(63);
else if(j==3 && i!=2) guy_epdParam(customLut);
else guy_epdParam(j<18?pgm_read_byte(guy_lutArray[i+lutOption*5]+j):0x00);
}
}
}
const PROGMEM unsigned char drv::lut_vcom0[] ={
0x00, 0x08, 0x08, 0x00, 0x00, 0x02,
0x00, 0x0F, 0x0F, 0x00, 0x00, 0x01,
@@ -139,7 +147,9 @@ const PROGMEM unsigned char drv::lutFast_b_w[] ={ 0x5a,2,0,63,0,1 };
const PROGMEM unsigned char drv::lutFast_w_b[] ={ 0x84,2,0,48,0,1 };
const PROGMEM unsigned char drv::lutFast_b_b[] ={ 0x01,2,0,48,0,1 };
//void drv::epd_display(){
void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
void drv::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
BeginTransfer();
Init(part_mode);
if(part_mode){
@@ -153,6 +163,7 @@ void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
//[EPDrg_BW<>] refresh fx
}
else{
Power_is_on=1;
guy_epdCmd(0x10);
//刷新数据
for(int i=0;i<GUY_D_WIDTH*GUY_D_HEIGHT/8;i++)
@@ -172,7 +183,13 @@ void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
}
guy_epdCmd(0x12);
EndTransfer();
guy_epdBusy(part_mode?-800:-3600);
lastRefresh=millis();
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
int32_t u=part_mode?800:3600;
if((int32_t)ms<u) guy_epdBusy((int32_t)ms-u);
lastRefresh=0;
BeginTransfer();
if(part_mode){
sendArea();
@@ -180,7 +197,6 @@ void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
for(int i=0;i<GUY_D_WIDTH*GUY_D_HEIGHT/8;i++)
SpiTransfer(f(i));
guy_epdCmd(0x92);
EndTransfer();
}
else{
Init(2);
@@ -190,6 +206,7 @@ void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
SpiTransfer(f(i));
guy_epdCmd(0x92);
guy_epdCmd(0x02);
}
EndTransfer();
guy_epdBusy(-20);
}
@@ -198,15 +215,21 @@ const PROGMEM uint8_t drv::greyTable[16]={
32,23,18,15,12,10,9,8,
7,6,5,5,5,5,5,0
};
void drv::drv_init(){
//Init(0);
drv_color(0xffu);
part_mode=0;
Power_is_on=0;
BeginTransfer();
Init(0);
EndTransfer();
//drv_color(0xffu);
}
void drv::drv_fullpart(bool part){ //切换慢刷/快刷功能
if(!part) customLut = CUSTOM_LUT_DISABLE;
part_mode = part;
//Init(part);
if(lastRefresh) return;
if(Power_is_on) {
if(!part) customLut = CUSTOM_LUT_DISABLE;
part_mode = part;
//Init(part);
}
}
void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0){ //未定义RST_PIN时无法唤醒
@@ -219,6 +242,8 @@ void drv::drv_sleep() { //开始屏幕睡眠
guy_epdParam(0xA5);
EndTransfer();
}
Power_is_on=0;
part_mode=0;
}
}
#endif /* END OF FILE. ReadGuy project.

View File

@@ -44,7 +44,7 @@ public:
void drv_init(); //初始化屏幕
//void drv_draw16grey(uint8_t *d16bit);
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f); //按照函数刷新
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return GUY_D_WIDTH; }; //返回显示区域宽度
int drv_height() const{ return GUY_D_HEIGHT; }; //返回显示区域高度
@@ -56,8 +56,8 @@ public:
int drv_supportGreyscaling() const { return 16; }
private:
uint8_t part_mode = 0;
//uint8_t Power_is_on = 0; //初始为未上电
uint8_t part_mode = 1;
uint8_t Power_is_on = 1; //初始为未上电
static const PROGMEM unsigned char lut_vcom0[];
static const PROGMEM unsigned char lut_ww[];
@@ -76,8 +76,6 @@ private:
void Init(uint8_t pt);
void sendArea();
void sendAreaRaw();
void SendLuts(uint8_t lutOption); //0:慢刷, 1:快刷, 2:四阶灰度
uint8_t customLut, customGreyscale; //customLut 是灰度刷新时的自定义lut
static const PROGMEM uint8_t greyTable[16];
};

View File

@@ -0,0 +1,74 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_426A.cpp
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 源代码模板
* 使用前, 先查找替换 426A 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 426A 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include "guy_426A.h"
#ifdef READGUY_DEV_426A
namespace guydev_426A{
//以下代码均为我 FriendshipEnder 原创, 呵呵哒~~
void drv::drv_init(){ //初始化屏幕
//add driver code...
}
void drv::drv_fullpart(bool part){ //初始化慢刷功能
if(lastRefresh) return;
//add driver code...
}
void drv::drv_setDepth(uint8_t i){
epdFull=0; iLut = i?(i>15?15:i):15; //如果需要, 改成自己的代码
}
void drv::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
//add driver code...
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
uint32_t u=epdFull?slowRefTime:fastRefTime; //全刷:局刷 busy时间
if(ms<u) guy_epdBusy(u-ms); //对于busy电平为低电平忙碌,高电平正常的屏幕则改为ms-u
lastRefresh=0;
//add driver code...
}
}
void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0){ //RST_PIN<0 无法唤醒
//add driver code...
}
epd_PowerOn = 0;
epdFull = 1; //强制设置为慢刷新模式
}
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,63 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_426A.h
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 头文件模板
* 使用前, 先查找替换 426A 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 426A 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../guy_epdbase.h"
#if (!defined(_GUY_EPD426A_H_FILE) && (defined(READGUY_DEV_426A)))
#define _GUY_EPD426A_H_FILE
namespace guydev_426A{
constexpr int GUY_D_WIDTH =200; //驱动屏幕宽度
constexpr int GUY_D_HEIGHT =200; //驱动屏幕高度
constexpr int slowRefTime =2000; //驱动屏幕慢刷时间, 单位毫秒
constexpr int fastRefTime =500; //驱动屏幕快刷时间, 单位毫秒
class drv : public readguyEpdBase {
public:
int drv_ID() const { return READGUY_DEV_426A; }
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return GUY_D_WIDTH; }; //返回显示区域宽度
//int drv_panelwidth() const { return GUY_D_WIDTH; }; //返回缓存的数据宽度
int drv_height() const{ return GUY_D_HEIGHT; }; //返回显示区域高度
void drv_setDepth(uint8_t i); //设置显示颜色深度
int drv_supportGreyscaling() const { return 16; }
private:
uint8_t epd_PowerOn=1; //是否上电. 睡眠则设为0
uint8_t epdFull=0; //是partical模式/快速刷新模式 0快刷, 1慢刷
uint8_t iLut=15; //颜色深度
};
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,74 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_583A.cpp
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 源代码模板
* 使用前, 先查找替换 583A 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 583A 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include "guy_583A.h"
#ifdef READGUY_DEV_583A
namespace guydev_583A{
//以下代码均为我 FriendshipEnder 原创, 呵呵哒~~
void drv::drv_init(){ //初始化屏幕
//add driver code...
}
void drv::drv_fullpart(bool part){ //初始化慢刷功能
if(lastRefresh) return;
//add driver code...
}
void drv::drv_setDepth(uint8_t i){
epdFull=0; iLut = i?(i>15?15:i):15; //如果需要, 改成自己的代码
}
void drv::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
//add driver code...
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
uint32_t u=epdFull?slowRefTime:fastRefTime; //全刷:局刷 busy时间
if(ms<u) guy_epdBusy(u-ms); //对于busy电平为低电平忙碌,高电平正常的屏幕则改为ms-u
lastRefresh=0;
//add driver code...
}
}
void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0){ //RST_PIN<0 无法唤醒
//add driver code...
}
epd_PowerOn = 0;
epdFull = 1; //强制设置为慢刷新模式
}
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,63 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_583A.h
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 头文件模板
* 使用前, 先查找替换 583A 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 583A 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../guy_epdbase.h"
#if (!defined(_GUY_EPD583A_H_FILE) && (defined(READGUY_DEV_583A)))
#define _GUY_EPD583A_H_FILE
namespace guydev_583A{
constexpr int GUY_D_WIDTH =200; //驱动屏幕宽度
constexpr int GUY_D_HEIGHT =200; //驱动屏幕高度
constexpr int slowRefTime =2000; //驱动屏幕慢刷时间, 单位毫秒
constexpr int fastRefTime =500; //驱动屏幕快刷时间, 单位毫秒
class drv : public readguyEpdBase {
public:
int drv_ID() const { return READGUY_DEV_583A; }
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return GUY_D_WIDTH; }; //返回显示区域宽度
//int drv_panelwidth() const { return GUY_D_WIDTH; }; //返回缓存的数据宽度
int drv_height() const{ return GUY_D_HEIGHT; }; //返回显示区域高度
void drv_setDepth(uint8_t i); //设置显示颜色深度
int drv_supportGreyscaling() const { return 16; }
private:
uint8_t epd_PowerOn=1; //是否上电. 睡眠则设为0
uint8_t epdFull=0; //是partical模式/快速刷新模式 0快刷, 1慢刷
uint8_t iLut=15; //颜色深度
};
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,74 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_583B.cpp
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 源代码模板
* 使用前, 先查找替换 583B 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 583B 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include "guy_583B.h"
#ifdef READGUY_DEV_583B
namespace guydev_583B{
//以下代码均为我 FriendshipEnder 原创, 呵呵哒~~
void drv::drv_init(){ //初始化屏幕
//add driver code...
}
void drv::drv_fullpart(bool part){ //初始化慢刷功能
if(lastRefresh) return;
//add driver code...
}
void drv::drv_setDepth(uint8_t i){
epdFull=0; iLut = i?(i>15?15:i):15; //如果需要, 改成自己的代码
}
void drv::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
//add driver code...
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
uint32_t u=epdFull?slowRefTime:fastRefTime; //全刷:局刷 busy时间
if(ms<u) guy_epdBusy(u-ms); //对于busy电平为低电平忙碌,高电平正常的屏幕则改为ms-u
lastRefresh=0;
//add driver code...
}
}
void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0){ //RST_PIN<0 无法唤醒
//add driver code...
}
epd_PowerOn = 0;
epdFull = 1; //强制设置为慢刷新模式
}
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,63 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_583B.h
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 头文件模板
* 使用前, 先查找替换 583B 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 583B 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../guy_epdbase.h"
#if (!defined(_GUY_EPD583B_H_FILE) && (defined(READGUY_DEV_583B)))
#define _GUY_EPD583B_H_FILE
namespace guydev_583B{
constexpr int GUY_D_WIDTH =200; //驱动屏幕宽度
constexpr int GUY_D_HEIGHT =200; //驱动屏幕高度
constexpr int slowRefTime =2000; //驱动屏幕慢刷时间, 单位毫秒
constexpr int fastRefTime =500; //驱动屏幕快刷时间, 单位毫秒
class drv : public readguyEpdBase {
public:
int drv_ID() const { return READGUY_DEV_583B; }
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return GUY_D_WIDTH; }; //返回显示区域宽度
//int drv_panelwidth() const { return GUY_D_WIDTH; }; //返回缓存的数据宽度
int drv_height() const{ return GUY_D_HEIGHT; }; //返回显示区域高度
void drv_setDepth(uint8_t i); //设置显示颜色深度
int drv_supportGreyscaling() const { return 16; }
private:
uint8_t epd_PowerOn=1; //是否上电. 睡眠则设为0
uint8_t epdFull=0; //是partical模式/快速刷新模式 0快刷, 1慢刷
uint8_t iLut=15; //颜色深度
};
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,74 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_750A.cpp
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 源代码模板
* 使用前, 先查找替换 750A 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 750A 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include "guy_750A.h"
#ifdef READGUY_DEV_750A
namespace guydev_750A{
//以下代码均为我 FriendshipEnder 原创, 呵呵哒~~
void drv::drv_init(){ //初始化屏幕
//add driver code...
}
void drv::drv_fullpart(bool part){ //初始化慢刷功能
if(lastRefresh) return;
//add driver code...
}
void drv::drv_setDepth(uint8_t i){
epdFull=0; iLut = i?(i>15?15:i):15; //如果需要, 改成自己的代码
}
void drv::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(m&1){//stage 1
if(lastRefresh) drv_dispWriter(f,2);
//add driver code...
}
if(m&2){//stage 2
uint32_t ms=millis()-lastRefresh;
uint32_t u=epdFull?slowRefTime:fastRefTime; //全刷:局刷 busy时间
if(ms<u) guy_epdBusy(u-ms); //对于busy电平为低电平忙碌,高电平正常的屏幕则改为ms-u
lastRefresh=0;
//add driver code...
}
}
void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0){ //RST_PIN<0 无法唤醒
//add driver code...
}
epd_PowerOn = 0;
epdFull = 1; //强制设置为慢刷新模式
}
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -0,0 +1,63 @@
/******************** F r i e n d s h i p E n d e r ********************
* 本程序隶属于 Readguy 开源项目, 请尊重开源开发者, 也就是我FriendshipEnder.
* 如果有条件请到 extra/artset/reward 中扫描打赏,否则请在 Bilibili 上支持我.
* 项目交流QQ群: 926824162 (萌新可以进来问问题的哟)
* 郑重声明: 未经授权还请不要商用本开源项目编译出的程序.
* @file guy_750A.h
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief 墨水屏 驱动库函数 头文件模板
* 使用前, 先查找替换 750A 改为你需要的驱动名称
* 比如3英寸的屏幕驱动程序应该叫 guy_dev_300a
* 此时需要将 750A 替换为300A.
* 替换好之后, 改文件名, 大小写也要注意改 (文件名最好不要出现大写)
* 最后把这个brief替换掉
* @version 1.0
* @date 2023-11-11
* @attention
* Copyright (c) 2022-2023 FriendshipEnder
*
* Apache License, Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../guy_epdbase.h"
#if (!defined(_GUY_EPD750A_H_FILE) && (defined(READGUY_DEV_750A)))
#define _GUY_EPD750A_H_FILE
namespace guydev_750A{
constexpr int GUY_D_WIDTH =200; //驱动屏幕宽度
constexpr int GUY_D_HEIGHT =200; //驱动屏幕高度
constexpr int slowRefTime =2000; //驱动屏幕慢刷时间, 单位毫秒
constexpr int fastRefTime =500; //驱动屏幕快刷时间, 单位毫秒
class drv : public readguyEpdBase {
public:
int drv_ID() const { return READGUY_DEV_750A; }
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //按照函数刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return GUY_D_WIDTH; }; //返回显示区域宽度
//int drv_panelwidth() const { return GUY_D_WIDTH; }; //返回缓存的数据宽度
int drv_height() const{ return GUY_D_HEIGHT; }; //返回显示区域高度
void drv_setDepth(uint8_t i); //设置显示颜色深度
int drv_supportGreyscaling() const { return 16; }
private:
uint8_t epd_PowerOn=1; //是否上电. 睡眠则设为0
uint8_t epdFull=0; //是partical模式/快速刷新模式 0快刷, 1慢刷
uint8_t iLut=15; //颜色深度
};
}
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -44,7 +44,7 @@
#define READGUY_DEV_154A 0 //1.54寸标准, 甘草酸不酸使用的1.54默认屏幕型号. 1.54汉朔价签也选这个
#define READGUY_DEV_154B 1 //1.54寸Lilygo, lilygo的1.54触摸和1.54背光墨水屏. GDEH0154D67
#define READGUY_DEV_213A 2 //2.13寸汉朔价签, 价格便宜, 兼容性好. 夏襄居士单词卡用 GDE0213B1
#define READGUY_DEV_213B 3 //2.13寸三色, 分辨率低212x104, 部分价签也用这个屏 易老化
#define READGUY_DEV_213B 3 //2.13寸, 分辨率低212x104, 部分2.13驱动不了的情况下用这个屏驱动
#define READGUY_DEV_266A 4 //2.66寸Vusion价签, 黑白双色.
#define READGUY_DEV_270B 5 //2.7寸佳显触摸墨水屏, 佳显官方店有售: 2.7寸触摸墨水屏.
#define READGUY_DEV_290A 6 //2.9寸A01, 甘草酸不酸2.9寸A01型号. 显示性能一般
@@ -53,6 +53,20 @@
#define READGUY_DEV_420A 9 //4.2寸HINK黑白价签, 汉朔价签, 汉朔三色价签通用. 速度快, 但效果一般
#define READGUY_DEV_420B 10 //4.2寸WF丝印, 部分价签也用此型号屏, 排线有WF开头的丝印
#define MEPD_DEBUG_DISPLAY 11 //使用LCD显示屏幕来debug. 前往ctg_stack_c_defines.h修改兼容的屏幕.
#define READGUY_DEV_213B3C 12 //2.13寸三色, 分辨率低212x104, 部分价签也用这个屏 易老化
#define READGUY_DEV_266A3C 13 //2.66寸三色. 驱动只能支持黑白色, 分辨率 296x152,
//#define READGUY_DEV_154C 14 //(即将推出) 1.54寸M09墨水屏 (M5Stack Core-Ink 同款; GDEW0154M09)
//#define READGUY_DEV_370B 15 //(即将推出) 3.7寸低DPI墨水屏, 分辨率416*240, b站 @叫我武哒哒 的项目用
//#define READGUY_DEV_426A 16 //(即将推出) 4.26寸高分辨率墨水屏, 800*480. GDEQ0426T82 支持硬件四灰
//#define READGUY_DEV_583A 17 //(即将推出) 5.83寸墨水屏幕, 分辨率为600*448. 有黑白有三色
//#define READGUY_DEV_583B 18 //(即将推出) 5.83寸高分辨率, 640*480. GDEQ0583T31 只有黑白
//#define READGUY_DEV_750A 19 //(即将推出) 7.5 寸墨水屏幕, 800*480. 只有三色(买不到黑白)
//#define READGUY_DEV_1020A 20 //(即将推出) 10.2寸墨水屏GDEQ102T90, 芯片SSD1677. 黑白色分辨率960*640
//添加新屏幕型号 add displays here
#define EPD_DRIVERS_NUM_MAX 21 //此选项请不要取消注释掉, 有几个屏幕就写多少.
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -70,11 +70,14 @@ int readguyEpdBase::IfInit(SPIClass &c,int8_t cs,int8_t dc,int8_t rst,int8_t bus
RST_PIN = rst;
BUSY_PIN = busy;
if(CS_PIN>=0) pinMode(CS_PIN , OUTPUT);
DigitalWrite(CS_PIN,HIGH);
if(RST_PIN>=0) pinMode(RST_PIN , OUTPUT);
DigitalWrite(RST_PIN,HIGH);
if(DC_PIN>=0) pinMode(DC_PIN , OUTPUT);
DigitalWrite(DC_PIN,HIGH);
if(BUSY_PIN>=0) pinMode(BUSY_PIN, INPUT);
_spi = &c;
lastRefresh=0;
//_spi->begin();
//_spi->beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));
@@ -157,9 +160,9 @@ void readguyEpdBase::Reset(uint32_t minTime)
}
//void readguyEpdBase::drv_draw16grey(const uint8_t *d16bit){ //不支持的话什么都不做
void readguyEpdBase::drv_drawImage(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_t x,uint16_t y,int o){
#ifndef FLOYD_STEINBERG_DITHERING
static const uint8_t bayer_tab [64]={
void readguyEpdBase::drv_drawImage(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_t x,uint16_t y,int o,
uint16_t fw, uint16_t fh){
static const PROGMEM uint8_t bayer_tab [64]={
0, 32, 8, 40, 2, 34, 10, 42,
48, 16, 56, 24, 50, 18, 58, 26,
12, 44, 4, 36, 14, 46, 6, 38,
@@ -169,31 +172,30 @@ void readguyEpdBase::drv_drawImage(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_
15, 47, 7, 39, 13, 45, 5, 37,
63, 31, 55, 23, 61, 29, 53, 21
};
#endif
const uint32_t w = (spr.width()+7)>>3;
if((!w) || (!spr.height())) return;
if(!fw) fw=spr.width();
if(!fh) fh=spr.height();
if((!fw) || (!fh)) return;
if(o==0 || o==1){
readBuff = new uint16_t[spr.width()];
#ifdef FLOYD_STEINBERG_DITHERING
floyd_tab[0] = new int16_t [spr.width()];
floyd_tab[1] = new int16_t [spr.width()];
for(int j=0;j<spr.width();j++){ floyd_tab[0][j] = 0; floyd_tab[1][j] = 0; }
#endif
writeBuff = new uint8_t[w];
floyd_tab[0] = new int16_t [fw];
floyd_tab[1] = new int16_t [fw];
for(int j=0;j<fw;j++){ floyd_tab[0][j] = 0; floyd_tab[1][j] = 0; }
writeBuff = new uint8_t[(fw+7)>>3];
}
sprbase.fillRect(x,y,spr.width(),spr.height(),1);
for(int32_t i=y;i<(int32_t)spr.height()+y;i++){
spr.readRect(0,i-y,spr.width(),1,readBuff);
#ifdef FLOYD_STEINBERG_DITHERING
sprbase.fillRect(x,y,fw,fh,1);
for(int32_t i=y;i<(int32_t)fh+y;i++){
uint_fast8_t buff8bit=0;
for(int32_t j=0;j<spr.width();j++){
int32_t flodelta = floyd_tab[i&1][j]+(int32_t)((greysc(readBuff[j])<<8)|greysc(readBuff[j]));
spr.readRect(0,(i-y)*spr.height()/fh,spr.width(),1,readBuff);
if(_quality&2){
for(int32_t j=0;j<fw;j++){
int gv=greysc(readBuff[j*spr.width()/fw]);
int32_t flodelta = floyd_tab[i&1][j]+(int32_t)((gv<<8)|gv);
if(flodelta>=0x8000) {
//spr.drawPixel(j,i,1);
buff8bit |= 1<<((~j)&7);
flodelta -= 0xffff;
}
if((j&7)==7 || j==((int32_t)spr.width()-1)){
if((j&7)==7 || j==((int32_t)fw-1)){
writeBuff[j>>3]=buff8bit;
buff8bit=0;
}
@@ -205,110 +207,99 @@ void readguyEpdBase::drv_drawImage(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_
}
if(j) { floyd_tab[!(i&1)][j-1] += (flodelta*3)>>4; }
{ floyd_tab[!(i&1)][j ] += (flodelta*5)>>4; }*/
if(j!=spr.width()-1)
if(j!=fw-1)
{ floyd_tab[i&1] [j+1] += (flodelta*7)>>4; }
if(j) { floyd_tab[!(i&1)][j-1] += (flodelta*3)>>4; }
{ floyd_tab[!(i&1)][j ] += (flodelta*5)>>4; }
if(j!=spr.width()-1)
if(j!=fw-1)
{ floyd_tab[!(i&1)][j+1] += (flodelta )>>4; }
}
for(int floi=0;floi<spr.width();floi++) floyd_tab[i&1][floi]=0;
#else
for(int32_t j=0;j<w;j++){
uint_fast8_t buff8bit=0;
for(uint_fast8_t b=0;b<8;b++)
buff8bit |= (bayer_tab[(b<<3)|(i&7)]<(greysc(readBuff[(j<<3)+b])>>2))<<(7-b);
writeBuff[j]=buff8bit;
for(int floi=0;floi<fw;floi++) floyd_tab[i&1][floi]=0;
}
#endif
sprbase.drawBitmap(x,i,writeBuff,spr.width(),1,1,0);
else{
for(int32_t j=0;j<((fw+7)>>3);j++){
buff8bit=0;
for(uint_fast8_t b=0;b<8;b++)
buff8bit |= ((pgm_read_byte(bayer_tab+((b<<3)|(i&7)))<<2)+2
<greysc(readBuff[((j<<3)+b)*spr.width()/fw]))<<(7-b);
writeBuff[j]=buff8bit;
}
}
sprbase.drawBitmap(x,i,writeBuff,fw,1,1,0);
}
//_display((const uint8_t*)sprbase.getBuffer()); //显示
if(o==0 || o==3){
delete []readBuff;
delete []writeBuff;
#ifdef FLOYD_STEINBERG_DITHERING
delete [] floyd_tab[0] ;
delete [] floyd_tab[1] ;
#endif
}
}
//不支持的话使用单色抖动刷屏
void readguyEpdBase::drv_draw16grey(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_t x,uint16_t y){
void readguyEpdBase::drv_draw16grey(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_t x,uint16_t y,
uint16_t fw, uint16_t fh){
//Serial.println("drv_draw16grey fx");
const uint32_t w = (spr.width()+7)>>3;
if((!w) || (!spr.height())) return;
if(!fw) fw=spr.width();
if(!fh) fh=spr.height();
if((!fw) || (!fh)) return;
readBuff = new uint16_t[spr.width()];
if(_quality&2){
#ifdef FLOYD_DITHERING_16GREY
floyd_tab[0] = new int16_t [spr.width()];
floyd_tab[1] = new int16_t [spr.width()];
#endif
floyd_tab[0] = new int16_t [fw];
floyd_tab[1] = new int16_t [fw];
}
writeBuff = new uint8_t[w];
sprbase.fillRect(x,y,spr.width(),spr.height(),1);
writeBuff = new uint8_t[(fw+7)>>3];
sprbase.fillRect(x,y,fw,fh,1);
bool negativeOrder=(drv_supportGreyscaling()==-16);
drv_fullpart(0);
//_display((const uint8_t*)sprbase.getBuffer()); //先对区域慢刷白屏确保颜色正确
drv_dispWriter(FILL_WHITE);
drv_fullpart(1);
for(uint_fast8_t k=1;k<16;k++){ //亮度为15的不用绘制,因为本来就是白色
#ifdef FLOYD_DITHERING_16GREY
if(_quality&2) for(int j=0;j<spr.width();j++){ floyd_tab[0][j] = 0; floyd_tab[1][j] = 0; }
#endif
for(int i=y;i<spr.height()+y;i++){
if(_quality&2) for(int j=0;j<fw;j++){ floyd_tab[0][j] = 0; floyd_tab[1][j] = 0; }
for(int i=y;i<(int32_t)fh+y;i++){
uint_fast8_t buff8bit=0;
spr.readRect(0,i-y,spr.width(),1,readBuff);
for(int32_t j=0;j<spr.width();j++){
spr.readRect(0,(i-y)*spr.height()/fh,spr.width(),1,readBuff);
for(int32_t j=0;j<fw;j++){
//for(uint_fast8_t b=0;b<8;b++){
#ifdef FLOYD_DITHERING_16GREY
uint_fast8_t cg=0;
if(_quality&2){
int gv=greysc(readBuff[j]);
int gv=greysc(readBuff[j*spr.width()/fw]);
int32_t fd = floyd_tab[i&1][j]+((gv<<8)|gv);
while(fd>=0x800) {
cg++;
if(fd>=0) fd -= 0x1000;
}
if(fd<0) fd++;
if(j!=spr.width()-1)
{ floyd_tab[i&1] [j+1] += (fd*7)>>4; }
if(j) { floyd_tab[!(i&1)][j-1] += (fd*3)>>4; }
{ floyd_tab[!(i&1)][j ] += (fd*5)>>4; }
if(j!=spr.width()-1)
{ floyd_tab[!(i&1)][j+1] += (fd )>>4; }
if(j!=fw-1) { floyd_tab[i&1] [j+1] += (fd*7)>>4; }
if(j) { floyd_tab[!(i&1)][j-1] += (fd*3)>>4; }
{ floyd_tab[!(i&1)][j ] += (fd*5)>>4; }
if(j!=fw-1) { floyd_tab[!(i&1)][j+1] += (fd )>>4; }
}
else{ cg=greysc(readBuff[j])>>4; }
#else
uint_fast8_t cg=greysc(readBuff[j])>>4;
#endif
else{ cg=greysc(readBuff[j*spr.width()/fw])>>4; }
//uint_fast8_t cg=greysc(readBuff[j*spr.width()/fw])>>4;
if(negativeOrder)
buff8bit |= (cg<k)<<((~j)&7);
else{
if(cg<15) //白色不考虑
buff8bit |= (cg>=((~k)&15))<<((~j)&7);
}
if((j&7)==7 || j==((int32_t)spr.width()-1)){
if((j&7)==7 || j==((int32_t)fw-1)){
writeBuff[j>>3]=buff8bit^0xff;
buff8bit=0;
}
//}
//sprbase.drawPixel(x+j,i,(greysc(readBuff[j])/16)==(15-k));
//sprbase.drawPixel(x+j,i,(greysc(readBuff[j*spr.width()/fw])/16)==(15-k));
}
#ifdef FLOYD_DITHERING_16GREY
if(_quality&2) for(int floi=0;floi<spr.width();floi++) floyd_tab[i&1][floi]=0;
#endif
sprbase.drawBitmap(x,i,writeBuff,spr.width(),1,1,0);
if(_quality&2) for(int floi=0;floi<fw;floi++) floyd_tab[i&1][floi]=0;
sprbase.drawBitmap(x,i,writeBuff,fw,1,1,0);
}
drv_draw16grey_step((const uint8_t*)sprbase.getBuffer(),k); //使用灰度显示函数
}
delete []readBuff;
delete []writeBuff;
if(_quality&2){
#ifdef FLOYD_DITHERING_16GREY
delete [] floyd_tab[0] ;
delete [] floyd_tab[1] ;
#endif
}
} /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */

View File

@@ -36,7 +36,6 @@
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include "guy_epaper_config.h"
#define EPD_DRIVERS_NUM_MAX 12 //此选项请不要取消注释掉
#define FILL_WHITE ([](int)->uint8_t{return 0xff;})
#define FILL_BLACK ([](int)->uint8_t{return 0x00;})
@@ -55,9 +54,8 @@ protected:
#endif
uint16_t *readBuff;// = new uint16_t[spr.width()];
uint8_t *writeBuff;// = new uint8_t[w];
#if (defined(FLOYD_DITHERING_16GREY) || defined(FLOYD_STEINBERG_DITHERING))
int16_t *floyd_tab[2];
#endif
uint32_t lastRefresh;
public:
readguyEpdBase(void);
@@ -82,9 +80,9 @@ public:
virtual int drv_ID() const =0; //返回驱动代号
virtual void drv_init()=0; //初始化屏幕
virtual void drv_fullpart(bool part)=0; //初始化慢刷功能
void _display(const uint8_t *d){ drv_dispWriter([&](int n)->uint8_t{return d[n];}); }
virtual void drv_dispWriter(std::function<uint8_t(int)>)=0; //按照显示函数刷新
void drv_color(uint8_t c){ drv_dispWriter([=](int)->uint8_t{return c;}); } //单色刷新
void _display(const uint8_t *d,uint8_t m=3){ drv_dispWriter([&](int n)->uint8_t{return d[n];},m); }
virtual void drv_dispWriter(std::function<uint8_t(int)>,uint8_t m=3)=0; //按照显示函数刷新
void drv_color(uint8_t c,uint8_t m=3){ drv_dispWriter([=](int)->uint8_t{return c;},m); } //单色刷新
virtual void drv_sleep() =0; //开始屏幕睡眠
virtual int drv_width() const=0; //返回显示区域宽度, 即使旋转了也不能影响此函数输出
virtual int drv_height()const=0; //返回显示区域高度, 即使旋转了也不能影响此函数输出
@@ -97,8 +95,10 @@ public:
* @return uint32_t 颜色的灰度值
*/
IRAM_ATTR static int greysc(int c){return(((c>>3)&0x1F)*79+(((c<<3)+(c>>13))&0x3F)*76+((c>>8)&0x1F)*30)>>5;}
void drv_drawImage(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_t x,uint16_t y,int o=0); //分步完成灰度刷新
void drv_draw16grey(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_t x,uint16_t y);//省内存方式
void drv_drawImage(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_t x,uint16_t y,int o=0,
uint16_t fw=0, uint16_t fh=0); //分步完成灰度刷新
void drv_draw16grey(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_t x,uint16_t y,
uint16_t fw=0, uint16_t fh=0);//省内存方式
void drv_draw16grey_step(const uint8_t *buf, int step){ //分步完成灰度刷新
drv_draw16grey_step([&](int n)->uint8_t{return buf[n];},step);
}

View File

@@ -73,7 +73,7 @@
//#define _DEFINA_SD_CS_PIN 0
// * for NodeMcu ctg stack LCF board
#define WHITE_GAP 8
#define WHITE_GAP 2
#ifdef ESP8266
#define DISPLAY_TYPE_ST7789_240320 //2.0寸的ST7789 IPS TFT模块

View File

@@ -38,9 +38,11 @@ void drv::drv_init(){
ips.fillScreen(0xffff);
}
void drv::drv_fullpart(bool part){
if(lastRefresh) return;
partMode=part;
}
void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
void drv::drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m){ //单色刷新
if(!(m&1)) return; //stage 1
uint16_t dat[8];
unsigned short xbits=(drv_width()+7)/8;
if(partMode==0){
@@ -79,7 +81,6 @@ void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
}
}
}
yield();
}
}
void drv::drv_sleep() {}

View File

@@ -41,7 +41,7 @@ public:
int drv_ID() const { return MEPD_DEBUG_DISPLAY; }
void drv_init(); //初始化屏幕
void drv_fullpart(bool part); //切换慢刷/快刷功能
void drv_dispWriter(std::function<uint8_t(int)> f); //单色刷新
void drv_dispWriter(std::function<uint8_t(int)> f,uint8_t m=3); //单色刷新
void drv_sleep() ; //开始屏幕睡眠
int drv_width() const { return ips.width()-2*WHITE_GAP; } //返回显示区域宽度
int drv_height() const{ return ips.height()-2*WHITE_GAP; } //返回显示区域高度

View File

@@ -6,7 +6,7 @@
* @file guy_version.h
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @brief readguy 版本控制文件.
* @version 1.0
* @version 1.3.2
* @date 2023-09-21
* @attention
@@ -30,11 +30,20 @@
#ifndef _READGUY_VERSION_H_FILE
#define _READGUY_VERSION_H_FILE
//在进行版本更迭之前, 需要检查以下文件的版本号是否为最新
//1. ChangeLog.md
//2. library.json
//3. library.properities
//4. README.md
//5. git commit 信息
//6. 下面的三行 以及下面的这个字符串
//务必保证这些版本号是一致的.
//另外, 在提交新版本之前, 不要忘记在github上创建release, 否则Arduino IDE会读不到
#define READGUY_V_MAJOR 1
#define READGUY_V_MINOR 1
#define READGUY_V_PATCH 1
#define READGUY_V_MINOR 3
#define READGUY_V_PATCH 3
#define READGUY_VERSION_VAL (READGUY_V_MAJOR*1000+READGUY_V_MINOR*100+READGUY_V_PATCH*10)
#define READGUY_VERSION "1.1.1"
#define READGUY_VERSION "1.3.3"
#ifdef ESP8266
#define _READGUY_PLATFORM "ESP8266"

View File

@@ -59,7 +59,7 @@ static const PROGMEM char args_name[23][8]={
static const char *NAME_guyDev266=NOT_SUPPORTED;
#endif
#ifdef READGUY_DEV_213B
static const PROGMEM char NAME_guyDev213M21[]="2.13寸三色";
static const PROGMEM char NAME_guyDev213M21[]="2.13寸低分辨率版";
#else
static const char *NAME_guyDev213M21=NOT_SUPPORTED;
#endif
@@ -98,6 +98,53 @@ static const PROGMEM char args_name[23][8]={
#else
static const char *NAME_guyDev270_V2=NOT_SUPPORTED;
#endif
#ifdef READGUY_DEV_213B3C
static const PROGMEM char NAME_guyDev213B3C[]="2.13寸三色";
#else
static const char *NAME_guyDev213B3C=NOT_SUPPORTED;
#endif
#ifdef READGUY_DEV_266A3C
static const PROGMEM char NAME_guyDev266A3C[]="2.66寸三色价签";
#else
static const char *NAME_guyDev266A3C=NOT_SUPPORTED;
#endif
#ifdef READGUY_DEV_154C
static const PROGMEM char NAME_guyDev154M5CoreInk[]="1.54寸Core.Ink";
#else
static const char *NAME_guyDev154M5CoreInk=NOT_SUPPORTED;
#endif
#ifdef READGUY_DEV_370B
static const PROGMEM char NAME_guyDev370LoRes[]="3.7寸低分版本416x240";
#else
static const char *NAME_guyDev370LoRes=NOT_SUPPORTED;
#endif
#ifdef READGUY_DEV_426A
static const PROGMEM char NAME_guyDev426HiRes[]="4.26寸GDEQ0426T82高分辨率";
#else
static const char *NAME_guyDev426HiRes=NOT_SUPPORTED;
#endif
#ifdef READGUY_DEV_583A
static const PROGMEM char NAME_guyDev583Normal[]="5.83寸黑白/三色价签";
#else
static const char *NAME_guyDev583Normal=NOT_SUPPORTED;
#endif
#ifdef READGUY_DEV_583B
static const PROGMEM char NAME_guyDev583HiResBW[]="5.83寸高分辨率版本";
#else
static const char *NAME_guyDev583HiResBW=NOT_SUPPORTED;
#endif
#ifdef READGUY_DEV_750A
static const PROGMEM char NAME_guyDev750A3C[]="7.5寸三色价签";
#else
static const char *NAME_guyDev750A3C=NOT_SUPPORTED;
#endif
#ifdef READGUY_DEV_1020A
static const PROGMEM char NAME_guyDev1020BW[]="10.2寸黑白";
#else
static const char *NAME_guyDev1020BW=NOT_SUPPORTED;
#endif
//添加新屏幕型号 add displays here
const char *ReadguyDriver::epd_drivers_list[EPD_DRIVERS_NUM_MAX]={
NAME_guyDev154, //此处的顺序必须和 guy_epaper_config.h 里面的一样
NAME_guyDev154_V2,
@@ -110,7 +157,18 @@ const char *ReadguyDriver::epd_drivers_list[EPD_DRIVERS_NUM_MAX]={
NAME_guyDev370,
NAME_guyDev420Hink,
NAME_guyDev420WF,
NAME_epdLcd
NAME_epdLcd,
NAME_guyDev213B3C,
NAME_guyDev266A3C,
NAME_guyDev154M5CoreInk,
NAME_guyDev370LoRes,
NAME_guyDev426HiRes,
NAME_guyDev583Normal,
NAME_guyDev583HiResBW,
NAME_guyDev750A3C,
NAME_guyDev1020BW
//添加新屏幕型号 add displays here
};
//static const char *index_cn_html;
//static const uint8_t faviconData[1150];
@@ -126,7 +184,7 @@ void ReadguyDriver::ap_setup(){
IPAddress subnet(255,255,255,0);
WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.softAP("readguy","12345678");
Serial.println(F("ap_setup SSID: readguy, Pass: 12345678"));
Serial.println(F("[Guy AP] ap_setup SSID: readguy, Pass: 12345678"));
}
void ReadguyDriver::server_setup(const String &notify, const serveFunc *serveFuncs, int funcs){
//启动WiFi服务器端, 这样就可以进行配网工作
@@ -163,7 +221,7 @@ void ReadguyDriver::server_setup(const String &notify, const serveFunc *serveFun
sv.begin();
MDNS.begin("readguy");
//MDNS.addService("http","tcp",80);
Serial.println(F("server_setup done! visit "));
Serial.print(F("[Guy server] Done! visit "));
if(WiFi.getMode() == WIFI_AP) Serial.println(F("192.168.4.1"));
else Serial.println(WiFi.localIP());
}
@@ -191,7 +249,7 @@ bool ReadguyDriver::server_loop(){ //此时等待网页操作完成响应...
}
if(refFlag!=127) {
Serial.printf("randch: %d %c\n",randomch[refFlag],(char)(randomch[refFlag]));
drawChar((width()>>1)-46+refFlag*24,(height()>>1)-14,randomch[refFlag],true,false,4);
drawChar((guy_dev->drv_width()>>1)-46+refFlag*24,(guy_dev->drv_height()>>1)-14,randomch[refFlag],true,false,4);
guy_dev->drv_fullpart(1);
guy_dev->_display((const uint8_t*)getBuffer());
}
@@ -218,19 +276,23 @@ void ReadguyDriver::handleInitPost(){
// 此时返回一个文本输入框, 定位到 handleFinalPost 函数
uint8_t btn_count_=0;
if(READGUY_cali){ //再次初始化已经初始化的东西, 此时需要关闭一些外设什么的
Serial.println(F("Reconfig pins and hardwares..."));
Serial.println(F("[Guy Pin] Reconfig pins and hardwares..."));
READGUY_cali=0;
READGUY_sd_ok=0;
#if defined(ESP8266)
//Esp8266无视SPI的设定, 固定为唯一的硬件SPI (D5=SCK, D6=MISO, D7=MOSI)
#ifdef READGUY_ENABLE_SD
SDFS.end();//关闭SD卡
#endif
btnTask.detach();
#else
#ifdef READGUY_ENABLE_SD
SD.end();//关闭SD卡
if(sd_spi != epd_spi) { //共线时, 不要删除SD
delete sd_spi;
sd_spi=nullptr;
}
#endif
vTaskDelete(btn_handle);
#endif
}
@@ -280,7 +342,7 @@ void ReadguyDriver::handleInitPost(){
uint8_t ck=checkEpdDriver();
if(btn_count_<2) config_data[16]=0;
if(btn_count_<3) config_data[17]=0;
Serial.println(F("Config OK. Now init devices."));
Serial.println(F("[Guy Pin] Config OK. Now init devices."));
if(ck>=125) {
const char *pNotify[3]={ PSTR("Necessary pin NOT connected."),\
PSTR("Pin conflicted."),PSTR("Display not supported.") };
@@ -304,24 +366,26 @@ void ReadguyDriver::handleInitPost(){
randomch[1] = 48+((rdm>>12)%10);//R2CHAR((rdm>>12)%10);
randomch[2] = 48+((rdm>> 6)%10);//R2CHAR((rdm>> 6)%10);
randomch[3] = 48+((rdm )%10);//R2CHAR((rdm )%10);
Serial.print(F("rand string: "));
Serial.print(F("[Guy] rand string: "));
for(int i=0;i<4;i++) Serial.write(randomch[i]);
Serial.write('\n');
Serial.println(F("Init EPD...")); //此时引脚io数据已经录入, 如果没有问题, 此处屏幕应当可以显示
Serial.println(F("[Guy] Init EPD...")); //此时引脚io数据已经录入, 如果没有问题, 此处屏幕应当可以显示
setEpdDriver(); //尝试初始化屏幕
Serial.println(F("Init details..."));
drawCenterString(setSDcardDriver()?"SD Init OK!":"SD Init failed!",width()>>1,(height()>>1)+20);
Serial.println(F("[Guy] Init details..."));
setTextSize(1);
drawCenterString(setSDcardDriver()?"SD Init OK!":"SD Init failed!",
guy_dev->drv_width()>>1,(guy_dev->drv_height()>>1)+20);
setButtonDriver(); //初始化按钮..
//} //尝试初始化按键, 调用后, 若SD卡初始化成功, READGUY_sd_ok的值会变成1
drawRect((width()>>1)-46 ,(height()>>1)-14,20,28,0);
drawRect((width()>>1)-46+24,(height()>>1)-14,20,28,0);
drawRect((width()>>1)-46+48,(height()>>1)-14,20,28,0);
drawRect((width()>>1)-46+72,(height()>>1)-14,20,28,0);
drawRect((guy_dev->drv_width()>>1)-46 ,(guy_dev->drv_height()>>1)-14,20,28,0);
drawRect((guy_dev->drv_width()>>1)-46+24,(guy_dev->drv_height()>>1)-14,20,28,0);
drawRect((guy_dev->drv_width()>>1)-46+48,(guy_dev->drv_height()>>1)-14,20,28,0);
drawRect((guy_dev->drv_width()>>1)-46+72,(guy_dev->drv_height()>>1)-14,20,28,0);
spibz++;
guy_dev->drv_fullpart(1);
guy_dev->_display((const uint8_t*)getBuffer());
spibz--;
Serial.println(F("Display done!"));
Serial.println(F("[Guy] Display done!"));
READGUY_cali=1; //显示初始化完成
}
void ReadguyDriver::handlePinSetup(){
@@ -514,6 +578,7 @@ void ReadguyDriver::handleFinal(){
}
s+=F("<br/><hr/>"); //换行
}
#ifdef READGUY_ENABLE_SD
if(!READGUY_sd_ok) s+=F("SD卡不可用!!!<br/>");
#if (defined(ESP8266)) //此函数速度太慢了, 因此删掉不用了
/*else{
@@ -543,6 +608,7 @@ void ReadguyDriver::handleFinal(){
s+=(uint32_t)(SD.usedBytes()/1024);
s+=F("KB.<br/>");
}
#endif
#endif
s+=F("当前WiFi模式: ");
s+=(WiFi.getMode()==WIFI_STA)?F("正常联网模式"):F("AP配网模式");
@@ -592,7 +658,7 @@ void ReadguyDriver::handleFinal(){
//s+=F("<br/>"); //换行
sv.send_P(200, TEXT_HTML, (s+FPSTR(end_html)).c_str());
if(READGUY_cali == 63){
Serial.println(F("Data saved to NVS."));
Serial.println(F("[Guy NVS] Data saved to NVS."));
READGUY_cali = 127;
nvs_init();
nvs_write();
@@ -643,7 +709,7 @@ const PROGMEM char ReadguyDriver::verify2_html[] =
"al\" method=\"POST\"><input type=\'text\' name=\'t_verify\' maxlength=\"6";
const PROGMEM char ReadguyDriver::verifybtn_html[3][200] = {
"一个按键, 操作可能比较繁琐, 但功能还都可以的.<br/>"
"点按: 下一个/向下翻页<br/>双击: 上一个/向上翻页<br/>三连击: 返回/切换输入法<br/>长按: 确定/选择",
"点按: 下一个/向下翻页<br/>双击: 确定/选择<br/>三连击: 返回/切换输入法<br/>长按: 上一个/向上翻页",
"两个按键, 操作可以满足需求.<br/>"
"按键1点按: 下一个/向下翻页<br/>按键1长按: 上一个/向上翻页<br/>按键2点按: 确定/选择<br/>按键2长按: 返回/切换输入法",
"三个按键, 操作非常流畅.<br/>"

View File

@@ -38,6 +38,36 @@ int8_t ReadguyDriver::pin_cmx=-1;
const PROGMEM char ReadguyDriver::projname[8] = "readguy";
const PROGMEM char ReadguyDriver::tagname[7] = "hwconf";
volatile uint8_t ReadguyDriver::spibz=0;
#ifndef DYNAMIC_PIN_SETTINGS
const int8_t ReadguyDriver::config_data[22] = {
127 , //READGUY_cali
READGUY_shareSpi ,
READGUY_epd_type ,// 对应的epd驱动程序代号, -1为未指定
//显示驱动部分, 显示默认使用vspi (vspi也是默认SPI库的通道)
READGUY_epd_mosi ,// 目标显示器的 MOSI 引脚
READGUY_epd_sclk ,// 目标显示器的 SCLK 引脚
READGUY_epd_cs ,// 目标显示器的 CS 引脚
READGUY_epd_dc ,// 目标显示器的 DC 引脚
READGUY_epd_rst ,// 目标显示器的 RST 引脚
READGUY_epd_busy ,// 目标显示器的 BUSY 引脚
//sd卡驱动部分, 默认使用hspi (sd卡建议用hspi)
READGUY_sd_miso ,// 目标sd卡的 MISO 引脚, sd_share_spi == 1 时无效
READGUY_sd_mosi ,// 目标sd卡的 MOSI 引脚, sd_share_spi == 1 时无效
READGUY_sd_sclk ,// 目标sd卡的 SCLK 引脚, sd_share_spi == 1 时无效
READGUY_sd_cs ,// 目标sd卡的 CS 引脚.
READGUY_i2c_sda ,// 目标i2c总线的SDA引脚, 当且仅当启用i2c总线时才生效
READGUY_i2c_scl ,// 目标i2c总线的SCL引脚, 当且仅当启用i2c总线时才生效
//按键驱动部分, 为负代表高触发, 否则低触发,
//注意, 这里的io编号是加1的, 即 1或-1 代表 gpio0 的低触发/高触发
READGUY_btn1 ,
READGUY_btn2 ,
READGUY_btn3 ,
READGUY_bl_pin ,//前置光接口引脚IO
READGUY_rtc_type ,//使用的RTC型号(待定, 还没用上)
0 ,//READGUY_sd_ok SD卡已经成功初始化
0 //READGUY_buttons 按钮个数, 0-3都有可能
};
#endif
#ifndef ESP8266
SPIClass *ReadguyDriver::sd_spi =nullptr;
SPIClass *ReadguyDriver::epd_spi=nullptr;
@@ -48,8 +78,8 @@ ReadguyDriver::ReadguyDriver(){
READGUY_cali = 0; // config_data[0] 的初始值为0
READGUY_sd_ok = 0; //初始默认SD卡未成功初始化
READGUY_buttons = 0; //初始情况下没有按钮
}
uint8_t ReadguyDriver::init(uint8_t WiFiSet){ //WiFiSet: 是否保持AP服务器一直处于打开状态
} //WiFiSet: 是否保持AP服务器一直处于打开状态
uint8_t ReadguyDriver::init(uint8_t WiFiSet, bool initepd){
if(READGUY_cali==127) //已经初始化过了一次了, 为了防止里面一些volatile的东西出现问题....还是退出吧
return 0;
#ifdef DYNAMIC_PIN_SETTINGS
@@ -77,7 +107,7 @@ uint8_t ReadguyDriver::init(uint8_t WiFiSet){ //WiFiSet: 是否保持AP服务器
else{ //看来NVS有数据, //从NVS加载数据, 哪怕前面的数据刚刚写入, 还没读取
if(WiFiSet>=2) WiFi.begin(); //连接到上次存储在flash NVS中的WiFi.
else if(WiFiSet==1) ap_setup();
if(checkEpdDriver()!=127) setEpdDriver(); //初始化屏幕
if(checkEpdDriver()!=127) setEpdDriver(initepd/* ,g_width,g_height */); //初始化屏幕
else for(;;); //此处可能添加程序rollback等功能操作(比如返回加载上一个程序)
setSDcardDriver();
setButtonDriver();
@@ -85,12 +115,17 @@ uint8_t ReadguyDriver::init(uint8_t WiFiSet){ //WiFiSet: 是否保持AP服务器
#endif
nvs_deinit();
#else
if(checkEpdDriver()!=127) setEpdDriver(); //初始化屏幕
nvs_init();
if(checkEpdDriver()!=127) setEpdDriver(initepd/* ,g_width,g_height */); //初始化屏幕
else for(;;); //此处可能添加程序rollback等功能操作(比如返回加载上一个程序)
setSDcardDriver();
setButtonDriver();
if(!nvs_read()){
nvs_write(); //全部成功之后, 写入引脚信息到NVS.
}
nvs_deinit();
#endif
Serial.println(F("init done."));
Serial.println(F("[Guy init] init done."));
READGUY_cali=127;
return READGUY_sd_ok;
}
@@ -101,7 +136,7 @@ uint8_t ReadguyDriver::checkEpdDriver(){
#else
#define TEST_ONLY_VALUE 3
#endif
Serial.printf_P(PSTR("READGUY_shareSpi? %d\n"),READGUY_shareSpi);
Serial.printf_P(PSTR("[Guy SPI] shareSpi? %d\n"),READGUY_shareSpi);
for(int i=TEST_ONLY_VALUE;i<8;i++){
if(i<7 && config_data[i]<0) return 125;//必要的引脚没连接
for(int j=1;j<=8-i;j++)
@@ -111,47 +146,124 @@ uint8_t ReadguyDriver::checkEpdDriver(){
#endif
if(guy_dev != nullptr && READGUY_epd_type!=guy_dev->drv_ID()) delete guy_dev; //释放多余的内存
//Serial.printf("initing epd %s...\n",epd_drivers_list[READGUY_epd_type]);
#ifdef DYNAMIC_PIN_SETTINGS
switch (READGUY_epd_type){
#ifdef READGUY_DEV_154A
case READGUY_DEV_154A: guy_dev = new guydev_154A_290A ::dev154A; break; //适用于一般的价签黑白屏
case READGUY_DEV_154A: guy_dev = new guydev_154A_290A ::dev154A; break; //适用于一般的价签黑白屏
#endif
#ifdef READGUY_DEV_154B
case READGUY_DEV_154B: guy_dev = new guydev_154B_270B_290B ::dev154B; break; //适用于
case READGUY_DEV_154B: guy_dev = new guydev_154B_270B_290B ::dev154B; break; //适用于
#endif
#ifdef READGUY_DEV_213A
case READGUY_DEV_213A: guy_dev = new guydev_213A ::drv; break;
case READGUY_DEV_213A: guy_dev = new guydev_213A ::drv; break;
#endif
#ifdef READGUY_DEV_266A
case READGUY_DEV_266A: guy_dev = new guydev_213B_266A ::dev266A; break;
case READGUY_DEV_266A: guy_dev = new guydev_213B_266A ::dev266A; break;
#endif
#ifdef READGUY_DEV_213B
case READGUY_DEV_213B: guy_dev = new guydev_213B_266A ::dev213B; break;
case READGUY_DEV_213B: guy_dev = new guydev_213B_266A ::dev213B; break;
#endif
#ifdef READGUY_DEV_290A
case READGUY_DEV_290A: guy_dev = new guydev_154A_290A ::dev290A; break;
case READGUY_DEV_290A: guy_dev = new guydev_154A_290A ::dev290A; break;
#endif
#ifdef READGUY_DEV_290B
case READGUY_DEV_290B: guy_dev = new guydev_154B_270B_290B ::dev290B; break;
case READGUY_DEV_290B: guy_dev = new guydev_154B_270B_290B ::dev290B; break;
#endif
#ifdef READGUY_DEV_420A
case READGUY_DEV_420A: guy_dev = new guydev_420A ::drv; break;
case READGUY_DEV_420A: guy_dev = new guydev_420A ::drv; break;
#endif
#ifdef READGUY_DEV_420B
case READGUY_DEV_420B: guy_dev = new guydev_420B ::drv; break;
case READGUY_DEV_420B: guy_dev = new guydev_420B ::drv; break;
#endif
#ifdef READGUY_DEV_370A
case READGUY_DEV_370A: guy_dev = new guydev_370A ::drv; break;
case READGUY_DEV_370A: guy_dev = new guydev_370A ::drv; break;
#endif
#ifdef MEPD_DEBUG_DISPLAY
case MEPD_DEBUG_DISPLAY:guy_dev = new EpdLcdDebug ::drv; break;
case MEPD_DEBUG_DISPLAY: guy_dev = new EpdLcdDebug ::drv; break;
#endif
#ifdef READGUY_DEV_270B
case READGUY_DEV_270B: guy_dev = new guydev_154B_270B_290B ::dev270B; break;
case READGUY_DEV_270B: guy_dev = new guydev_154B_270B_290B ::dev270B; break;
#endif
#ifdef READGUY_DEV_213B3C
case READGUY_DEV_213B3C: guy_dev = new guydev_213B_266A ::dev213B3C; break;
#endif
#ifdef READGUY_DEV_266A3C
case READGUY_DEV_266A3C: guy_dev = new guydev_213B_266A ::dev266A3C; break;
#endif
#ifdef READGUY_DEV_154C
case READGUY_DEV_154C: guy_dev = new guydev_154C ::drv; break;
#endif
#ifdef READGUY_DEV_370B
case READGUY_DEV_370B: guy_dev = new guydev_370B ::drv; break;
#endif
#ifdef READGUY_DEV_426A
case READGUY_DEV_426A: guy_dev = new guydev_426A ::drv; break;
#endif
#ifdef READGUY_DEV_583A
case READGUY_DEV_583A: guy_dev = new guydev_583A ::drv; break;
#endif
#ifdef READGUY_DEV_583B
case READGUY_DEV_583B: guy_dev = new guydev_583B ::drv; break;
#endif
#ifdef READGUY_DEV_750A
case READGUY_DEV_750A: guy_dev = new guydev_750A ::drv; break;
#endif
#ifdef READGUY_DEV_1020A
case READGUY_DEV_1020A: guy_dev = new guydev_1020A ::drv; break;
#endif
//添加新屏幕型号 add displays here
default:
Serial.println(F("[ERR] EPD DRIVER IC NOT SUPPORTED!\n"));
Serial.println(F("[GUY ERR] EPD DRIVER IC NOT SUPPORTED!\n"));
return 127;
}
#else
#if (defined(READGUY_DEV_154A) && (READGUY_epd_type==READGUY_DEV_154A))
guy_dev = new guydev_154A_290A ::dev154A; //适用于一般的价签黑白屏
#elif (defined(READGUY_DEV_154B) && (READGUY_epd_type==READGUY_DEV_154B))
guy_dev = new guydev_154B_270B_290B ::dev154B; //适用于
#elif (defined(READGUY_DEV_213A) && (READGUY_epd_type==READGUY_DEV_213A))
guy_dev = new guydev_213A ::drv;
#elif (defined(READGUY_DEV_266A) && (READGUY_epd_type==READGUY_DEV_266A))
guy_dev = new guydev_213B_266A ::dev266A;
#elif (defined(READGUY_DEV_213B) && (READGUY_epd_type==READGUY_DEV_213B))
guy_dev = new guydev_213B_266A ::dev213B;
#elif (defined(READGUY_DEV_290A) && (READGUY_epd_type==READGUY_DEV_290A))
guy_dev = new guydev_154A_290A ::dev290A;
#elif (defined(READGUY_DEV_290B) && (READGUY_epd_type==READGUY_DEV_290B))
guy_dev = new guydev_154B_270B_290B ::dev290B;
#elif (defined(READGUY_DEV_420A) && (READGUY_epd_type==READGUY_DEV_420A))
guy_dev = new guydev_420A ::drv;
#elif (defined(READGUY_DEV_420B) && (READGUY_epd_type==READGUY_DEV_420B))
guy_dev = new guydev_420B ::drv;
#elif (defined(READGUY_DEV_370A) && (READGUY_epd_type==READGUY_DEV_370A))
guy_dev = new guydev_370A ::drv;
#elif (defined(MEPD_DEBUG_DISPLAY) && (READGUY_epd_type==MEPD_DEBUG_DISPLAY))
guy_dev = new EpdLcdDebug ::drv;
#elif (defined(READGUY_DEV_270B) && (READGUY_epd_type==READGUY_DEV_270B))
guy_dev = new guydev_154B_270B_290B ::dev270B;
#elif (defined(READGUY_DEV_213B3C) && (READGUY_epd_type==READGUY_DEV_213B3C))
guy_dev = new guydev_213B_266A ::dev213B3C;
#elif (defined(READGUY_DEV_266A3C) && (READGUY_epd_type==READGUY_DEV_266A3C))
guy_dev = new guydev_213B_266A ::dev266B3C;
#elif (defined(READGUY_DEV_154C) && (READGUY_epd_type==READGUY_DEV_154C))
guy_dev = new guydev_154C ::drv;
#elif (defined(READGUY_DEV_370B) && (READGUY_epd_type==READGUY_DEV_370B))
guy_dev = new guydev_370B ::drv;
#elif (defined(READGUY_DEV_426A) && (READGUY_epd_type==READGUY_DEV_426A))
guy_dev = new guydev_426A ::drv;
#elif (defined(READGUY_DEV_583A) && (READGUY_epd_type==READGUY_DEV_583A))
guy_dev = new guydev_583A ::drv;
#elif (defined(READGUY_DEV_583B) && (READGUY_epd_type==READGUY_DEV_583B))
guy_dev = new guydev_583B ::drv;
#elif (defined(READGUY_DEV_750A) && (READGUY_epd_type==READGUY_DEV_750A))
guy_dev = new guydev_750A ::drv;
#elif (defined(READGUY_DEV_1020A) && (READGUY_epd_type==READGUY_DEV_1020A))
guy_dev = new guydev_1020A ::drv;
#endif
//添加新屏幕型号 add displays here
#endif
#if (defined(ESP8266))
SPI.begin();
SPI.setFrequency(ESP8266_SPI_FREQUENCY); ///< 1MHz
@@ -169,33 +281,35 @@ uint8_t ReadguyDriver::checkEpdDriver(){
epd_spi->begin(READGUY_epd_sclk,READGUY_shareSpi?READGUY_sd_miso:-1,READGUY_epd_mosi);
guy_dev->IfInit(*epd_spi, READGUY_epd_cs, READGUY_epd_dc, READGUY_epd_rst, READGUY_epd_busy);
#endif
Serial.println(F("IfInit OK"));
Serial.println(F("[Guy SPI] drvBase Init OK"));
return READGUY_epd_type;
}
void ReadguyDriver::setEpdDriver(int g_width,int g_height){
void ReadguyDriver::setEpdDriver(bool initepd, bool initGFX){
guy_dev->spi_tr_release = in_release;
guy_dev->spi_tr_press = in_press;
guy_dev->drv_init(); //初始化epd驱动层
if(g_width) guy_width = g_width;
else guy_width = guy_dev->drv_width(); //宽度必须是8的倍数, 但这个可以由GFX自动计算
if(g_height) guy_height = g_height;
else guy_height = guy_dev->drv_height();
Serial.println(F("EPD init OK"));
if(initepd) guy_dev->drv_init(); //初始化epd驱动层
//if(g_width) guy_width = g_width;
//else guy_width = guy_dev->drv_width(); //宽度必须是8的倍数, 但这个可以由GFX自动计算
//if(g_height) guy_height = g_height;
//else guy_height = guy_dev->drv_height();
//以下依赖于你的图形驱动
setColorDepth(1); //单色模式
createPalette(); //初始化颜色系统
Serial.printf_P(PSTR("mono set: w: %d, h: %d\n"),guy_width,guy_height);
//创建画布. 根据LovyanGFX的特性, 如果以前有画布会自动重新生成新画布
//此外, 即使画布宽度不是8的倍数(如2.13寸单色),也支持自动补全8的倍数 ( 250x122 => 250x128 )
//为了保证图片显示功能的正常使用, 高度也必须是8的倍数.
createSprite(guy_width,(guy_height+7)&0x7ffffff8);
//setRotation(1); //旋转之后操作更方便
setRotation(0);
setFont(&fonts::Font0);
setCursor(0,0);
setTextColor(0);
fillScreen(1); //开始先全屏白色
if(initGFX){
setColorDepth(1); //单色模式
createPalette(); //初始化颜色系统
//创建画布. 根据LovyanGFX的特性, 如果以前有画布会自动重新生成新画布
//此外, 即使画布宽度不是8的倍数(如2.13寸单色),也支持自动补全8的倍数 ( 250x122 => 250x128 )
//为了保证图片显示功能的正常使用, 高度也必须是8的倍数.
createSprite(guy_dev->drv_width(),guy_dev->drv_height());
//这里发现如果用自定义的内存分配方式会更好一些. 不会导致返回的height不对. 但是因为LovyanGFX库未更新 暂时不能这么用.
setRotation(0); //默认的旋转方向就是0咯
setFont(&fonts::Font0);
setCursor(0,0);
setTextColor(0);
fillScreen(1); //开始先全屏白色
}
Serial.printf_P(PSTR("[Guy EPD] EPD init OK(%d): w: %d, h: %d\n"),guy_dev->drv_ID(),guy_dev->drv_width(),guy_dev->drv_height());
}
#ifdef READGUY_ENABLE_SD
bool ReadguyDriver::setSDcardDriver(){
/*重要信息: 有些引脚冲突是难以避免的, 比如8266 尤其需要重写这部分代码
对于esp32也要注意这个引脚是否是一个合法的引脚
@@ -226,6 +340,7 @@ bool ReadguyDriver::setSDcardDriver(){
}
else READGUY_sd_ok=0; //引脚不符合规则,或冲突或不可用
if(!READGUY_sd_ok){
Serial.println(F("[Guy SD] SD Init Failed!"));
//guyFS().begin(); //初始化内部FS
#ifdef READGUY_USE_LITTLEFS
LittleFS.begin();
@@ -235,6 +350,17 @@ bool ReadguyDriver::setSDcardDriver(){
}
return READGUY_sd_ok;
}
#else
bool ReadguyDriver::setSDcardDriver(){
READGUY_sd_ok=0;
#ifdef READGUY_USE_LITTLEFS
LittleFS.begin();
#else
SPIFFS.begin();
#endif
return false;
}
#endif
void ReadguyDriver::setButtonDriver(){
if(READGUY_btn1) { //初始化按键. 注意高电平触发的引脚在初始化时要设置为下拉
int8_t btn_pin=abs(READGUY_btn1)-1;
@@ -278,18 +404,21 @@ void ReadguyDriver::setButtonDriver(){
if(READGUY_btn1) btn_rd[0].begin(abs(READGUY_btn1)-1,rd_btn_f,(READGUY_btn1>0));
if(READGUY_btn2) btn_rd[1].begin(abs(READGUY_btn2)-1,rd_btn_f,(READGUY_btn2>0));
if(READGUY_btn3) btn_rd[2].begin(abs(READGUY_btn3)-1,rd_btn_f,(READGUY_btn3>0));
//if(READGUY_buttons==1){
btn_rd[0].setLongRepeatMode(1); //允许连按
//}
if(READGUY_buttons==2){
btn_rd[0].setMultiBtn(1); //设置为多个按钮,不识别双击或三击
btn_rd[0].setLongRepeatMode(1);
//btn_rd[0].setLongRepeatMode(1);
btn_rd[1].setMultiBtn(1); //设置为多个按钮,不识别双击或三击
btn_rd[1].setLongRepeatMode(0);
}
else if(READGUY_buttons==3){
btn_rd[0].setLongPressMs(1); //不识别双击三击, 只有按一下或者长按, 并且开启连按
btn_rd[0].setLongRepeatMode(1);
btn_rd[0].long_press_ms = 50; //不识别双击三击, 只有按一下或者长按, 并且开启连按
//btn_rd[0].setLongRepeatMode(1);
btn_rd[1].setMultiBtn(1); //设置为多个按钮,不识别双击或三击
btn_rd[1].setLongRepeatMode(0);
btn_rd[2].setLongPressMs(1); //不识别双击三击, 只有按一下或者长按, 并且开启连按
btn_rd[2].long_press_ms = 50; //不识别双击三击, 只有按一下或者长按, 并且开启连按
btn_rd[2].setLongRepeatMode(1);
}
#ifdef ESP8266 //对于esp8266, 需要注册到ticker. 这是因为没freertos.
@@ -339,6 +468,7 @@ void ReadguyDriver::setButtonDriver(){
}
} //关于按键策略, 我们在此使用多个Button2的类, 然后在一个task共享变量来确定上一个按键状态
}
#ifdef READGUY_ENABLE_SD
fs::FS &ReadguyDriver::guyFS(uint8_t initSD){
if(initSD==2 || (!READGUY_sd_ok && initSD)) setSDcardDriver();
if(READGUY_sd_ok){
@@ -354,6 +484,16 @@ fs::FS &ReadguyDriver::guyFS(uint8_t initSD){
return SPIFFS;
#endif
}
#else
fs::FS &ReadguyDriver::guyFS(uint8_t initSD){
(void)initSD; //avoid GCC warning
#ifdef READGUY_USE_LITTLEFS
return LittleFS;
#else
return SPIFFS;
#endif
}
#endif
void ReadguyDriver::setBright(int d){
if(currentBright>=0 && d>=0 && d<=255){
currentBright=d;
@@ -368,40 +508,50 @@ void ReadguyDriver::setBright(int d){
digitalWrite(READGUY_bl_pin,d?HIGH:LOW);
}
}
void ReadguyDriver::display(bool part){
void ReadguyDriver::display(uint8_t part){
//真的是我c++的盲区了啊....搜索了半天才找到可以这么玩的
//......可惜'dynamic_cast' not permitted with -fno-rtti
// static bool _part = 0; 记忆上次到底是full还是part, 注意启动时默认为full
if(READGUY_cali==127){
//in_press(); //暂停, 然后读取按键状态 spibz
guy_dev->drv_fullpart(part);
guy_dev->_display((const uint8_t*)getBuffer());
guy_dev->drv_fullpart(part&1);
guy_dev->_display((const uint8_t*)getBuffer(),((part>>1)?part>>1:3));
//in_release(); //恢复
}
}
void ReadguyDriver::display(std::function<uint8_t(int)> f, bool part){
void ReadguyDriver::displayBuffer(const uint8_t *buf, uint8_t part){
if(READGUY_cali==127){
//in_press(); //暂停, 然后读取按键状态 spibz
guy_dev->drv_fullpart(part);
guy_dev->drv_dispWriter(f);
guy_dev->drv_fullpart(part&1);
guy_dev->_display(buf,((part>>1)?part>>1:3));
//in_release(); //恢复
}
}
void ReadguyDriver::drawImage(LGFX_Sprite &spr,uint16_t x,uint16_t y) {
if(READGUY_cali==127) guy_dev->drv_drawImage(*this, spr, x, y);
void ReadguyDriver::display(std::function<uint8_t(int)> f, uint8_t part){
if(READGUY_cali==127){
//in_press(); //暂停, 然后读取按键状态 spibz
guy_dev->drv_fullpart(part&1);
guy_dev->drv_dispWriter(f,((part>>1)?part>>1:3));
//in_release(); //恢复
}
}
void ReadguyDriver::drawImageStage(LGFX_Sprite &spr,uint16_t x,uint16_t y,uint8_t stage,uint8_t totalstage) {
void ReadguyDriver::drawImage(LGFX_Sprite &base, LGFX_Sprite &spr,uint16_t x,uint16_t y,uint16_t zoomw, uint16_t zoomh) {
if(READGUY_cali==127) guy_dev->drv_drawImage(base, spr, x, y, 0, zoomw, zoomh);
}
void ReadguyDriver::drawImageStage(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_t x,uint16_t y,uint8_t stage,
uint8_t totalstage,uint16_t zoomw,uint16_t zoomh) {
if(READGUY_cali!=127 || stage>=totalstage) return;
guy_dev->drv_drawImage(*this, spr, x, y, (totalstage<=1)?0:(stage==0?1:(stage==(totalstage-1)?3:2)));
//Serial.printf("stage: %d/%d\n",stage+1,totalstage);
guy_dev->drv_drawImage(sprbase, spr, x, y, (totalstage<=1)?0:(stage==0?1:(stage==(totalstage-1)?3:2)),zoomw,zoomh);
}
void ReadguyDriver::setDepth(uint8_t d){
if(READGUY_cali==127 && guy_dev->drv_supportGreyscaling()) guy_dev->drv_setDepth(d);
}
void ReadguyDriver::draw16grey(LGFX_Sprite &spr,uint16_t x,uint16_t y){
void ReadguyDriver::draw16grey(LGFX_Sprite &spr,uint16_t x,uint16_t y,uint16_t zoomw,uint16_t zoomh){
if(READGUY_cali!=127) return;
if(guy_dev->drv_supportGreyscaling() && (spr.getColorDepth()&0xff)>1)
return guy_dev->drv_draw16grey(*this,spr,x,y);
guy_dev->drv_drawImage(*this, spr, x, y);
return guy_dev->drv_draw16grey(*this,spr,x,y,zoomw,zoomh);
guy_dev->drv_drawImage(*this, spr, x, y, 0, zoomw, zoomh);
}
void ReadguyDriver::draw16greyStep(int step){
if(READGUY_cali==127 && guy_dev->drv_supportGreyscaling() && step>0 && step<16 ){
@@ -417,7 +567,7 @@ void ReadguyDriver::draw16greyStep(std::function<uint8_t(int)> f, int step){
}
void ReadguyDriver::invertDisplay(){
if(READGUY_cali==127){
const int pixels=((guy_width+7)>>3)*guy_height;
const int pixels=((guy_dev->drv_width()+7)>>3)*guy_dev->drv_height();
for(int i=0;i<pixels;i++)
((uint8_t*)(getBuffer()))[i]=uint8_t(~(((uint8_t*)(getBuffer()))[i]));
}
@@ -426,8 +576,7 @@ void ReadguyDriver::sleepEPD(){
if(READGUY_cali==127) guy_dev->drv_sleep();
}
#if (!defined(DYNAMIC_PIN_SETTINGS)) //do nothing here.
#elif (defined(INDEV_DEBUG))
#if (defined(INDEV_DEBUG))
void ReadguyDriver::nvs_init(){
}
void ReadguyDriver::nvs_deinit(){
@@ -447,12 +596,19 @@ void ReadguyDriver::nvs_deinit(){
}
bool ReadguyDriver::nvs_read(){
char s[8];
for(unsigned int i=0;i<sizeof(config_data)+8;i++){
for(unsigned int i=0;i<
#ifdef DYNAMIC_PIN_SETTINGS
sizeof(config_data)+
#endif
8;i++){
int8_t rd=(int8_t)EEPROM.read(2+i);
#ifdef DYNAMIC_PIN_SETTINGS
if(i>=8) config_data[i-8] = rd;
else s[i]=(char)rd;
else
#endif
s[i]=(char)rd;
}
Serial.printf("Get NVS...%d\n", config_data[0]);
Serial.printf("[Guy NVS] Get EEPROM...%d\n", config_data[0]);
return !(strcmp_P(s,projname));
}
void ReadguyDriver::nvs_write(){
@@ -469,6 +625,7 @@ void ReadguyDriver::nvs_deinit(){
}
bool ReadguyDriver::nvs_read(){ //此处需要处理一些有关I2C的内容
if(!nvsData.isKey(tagname)) return 0; //没有这个键值
#ifdef DYNAMIC_PIN_SETTINGS
size_t len=nvsData.getBytes(tagname,config_data,sizeof(config_data)); //读取的数据长度
/*if(len<sizeof(config_data)){ //旧版本格式无法获取I2C相关数据, 设置为-1.
for(int i=sizeof(config_data)-1;i>=15;i--) //使用新版本格式来存储相关数据
@@ -478,6 +635,9 @@ bool ReadguyDriver::nvs_read(){ //此处需要处理一些有关I2C的内容
nvsData.putBytes(tagname,config_data,sizeof(config_data)); //用新版本格式保存
}*/
return len==sizeof(config_data);
#else
return 1;
#endif
}
void ReadguyDriver::nvs_write(){
if(nvsData.isKey(tagname)) nvsData.remove(tagname);
@@ -486,29 +646,47 @@ void ReadguyDriver::nvs_write(){
#endif
uint8_t ReadguyDriver::getBtn_impl(){ //按钮不可用, 返回0.
static uint32_t last=0;
uint8_t res1,res2,res3,res4=0;
switch(READGUY_buttons){
case 1:
res1=btn_rd[0].read();
if(res1 == 1) res4 |= 1; //点按
else if(res1 == 2) res4 |= 2; //双击
else if(res1 == 4) res4 |= 4; //长按-确定
else if(res1 == 2) res4 |= 4; //双击-确定
else if(res1 == 3) res4 |= 8; //三击-返回
else if(res1 == 4) res4 |= 2; //长按-向上翻页
else if(res1 == 5) res4 |= 3; //单击后长按-新增操作(可以连按)
break;
case 2:
res1=btn_rd[0].read(); //两个按钮引脚都读取
res2=btn_rd[1].read();
if(res1 == 1) res4 |= 1; //左键点按-向下翻页
else if(res1 == 4) res4 |= 2; //左键按-向翻页
if(res2 == 1) res4 |= 4; //右键点按-确定
else if(res2 == 4) res4 |= 8; //键长按-返回
if(millis()-last>500){
if(res1 == 1) res4 |= 1; //左键按-向翻页
else if(res1 == 4) {
res4 |= 2; //键长按-向上翻页
//if(btn_rd[1].isPressedRaw()) res4 |= 1;
}
}
if(btn_rd[0].isPressedRaw() && res2){
res4 |= 3; //右键点按-确定
last=millis();
}
else{
if(res2 == 1) res4 |= 4; //右键点按-确定
else if(res2 == 4) res4 |= 8; //右键长按-返回
}
if(res4==5 || res4==6) res4=3;
break;
case 3:
res1=btn_rd[0].read();
res2=btn_rd[1].read();
res3=btn_rd[2].read();
if(res1 == 4) res4 |= 1;
if(res3 == 4) res4 |= 2;
if(res1 && millis()-last >= btn_rd[1].long_repeat_ms && (!btn_rd[2].isPressedRaw())) res4 |= 2;
if(res3) {
res4 |= ((btn_rd[0].isPressedRaw()<<1)|1);
last=millis();
}
//if(res3 && ((millis()-last)<btn_rd[0].long_repeat_ms)) res4 |=3;
if(res2 == 1) res4 |= 4;
else if(res2 == 4) res4 |= 8;
break;

View File

@@ -49,7 +49,8 @@
#ifdef READGUY_DEV_213A
#include "guy_epaper/guy_213a/guy_213a.h"
#endif
#if (defined(READGUY_DEV_213B) || defined(READGUY_DEV_266A))
#if (defined(READGUY_DEV_213B) || defined(READGUY_DEV_213B3C) \
|| defined(READGUY_DEV_266A) || defined(READGUY_DEV_266A3C))
#include "guy_epaper/guy_213b_266a/guy_213b_266a.h"
#endif
#ifdef READGUY_DEV_370A
@@ -65,6 +66,29 @@
#include "guy_epaper/lcdDebug/lcdDebug.h"
#endif
#ifdef READGUY_DEV_154C
#include "guy_epaper/guy_154C/guy_154C.h"
#endif
#ifdef READGUY_DEV_370B
#include "guy_epaper/guy_370B/guy_370B.h"
#endif
#ifdef READGUY_DEV_426A
#include "guy_epaper/guy_426A/guy_426A.h"
#endif
#ifdef READGUY_DEV_583A
#include "guy_epaper/guy_583A/guy_583A.h"
#endif
#ifdef READGUY_DEV_583B
#include "guy_epaper/guy_583B/guy_583B.h"
#endif
#ifdef READGUY_DEV_750A
#include "guy_epaper/guy_750A/guy_750A.h"
#endif
#ifdef READGUY_DEV_1020A
#include "guy_epaper/guy_1020A/guy_1020A.h"
#endif
//添加新屏幕型号 add displays here
#include "guy_button.h" //改自Button2精简而来
#include "guy_version.h"
#include "guy_driver_config.h" //config
@@ -77,28 +101,28 @@
#endif
#if defined(ESP8266) //for ESP8266
#ifdef DYNAMIC_PIN_SETTINGS
#include <EEPROM.h> //ESP32需要NVS才可以读取引脚信息
#endif
#include <EEPROM.h> //ESP32需要NVS才可以读取引脚信息,
#ifdef READGUY_ESP_ENABLE_WIFI
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include "ESP8266HTTPUpdateServer.h"
#endif
#ifdef READGUY_ENABLE_SD
#include <SDFS.h>
#endif
#include <Ticker.h>
#else //for ESP32
#ifdef DYNAMIC_PIN_SETTINGS
#include <Preferences.h> //ESP32需要NVS才可以读取引脚信息
#endif
#ifdef READGUY_ESP_ENABLE_WIFI
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include "HTTPUpdateServer.h"
#endif
#ifdef READGUY_ENABLE_SD
#include <SD.h>
#endif
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#endif
@@ -132,6 +156,13 @@
#define READGUY_buttons (config_data[21]) //按钮个数, 0-3都有可能
#endif
#define READGUY_SLOW 0
#define READGUY_FAST 1
#define READGUY_SLOW_START 2
#define READGUY_FAST_START 3
#define READGUY_SLOW_END 4
#define READGUY_FAST_END 5
class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
public:
#ifdef READGUY_ESP_ENABLE_WIFI
@@ -147,15 +178,18 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
/** @brief 初始化readguy
* @param WiFiSet 是否保持AP模式关闭. 0:配网完成自动关WiFi, 1:需要手动调用 WiFi.mode(WIFI_OFF) 关闭WiFi.
* 2:自动连接到已存的WiFi, 但不等待连接成功
* @param initepd 是否初始化墨水屏. 初始化后的首次刷屏必为慢刷. 如果是不断电复位, 可以不初始化墨水屏直接刷屏
* @return SD卡是否就绪
*/
uint8_t init(uint8_t WiFiSet = 0);
uint8_t init(uint8_t WiFiSet = 0, bool initepd = 1);
/// @brief 设置显示亮度
void setBright(int d);
/// @brief 返回显示亮度
int getBright() const { return currentBright; }
/// @brief 刷新显示到屏幕上
void display(bool part = true);
void display(uint8_t part = READGUY_FAST);
/// @brief 刷新显示到屏幕上
void displayBuffer(const uint8_t *buf, uint8_t part);
/** @brief 刷新显示到屏幕上, 可以自定义读取指定位置像素的函数
* @param f 自定义的函数. 此函数将在读取像素并输出到墨水屏时被调用.
* 每次调用需要返回 "参数对应位置" 的8个像素的颜色信息(凑成一字节). 其中左侧应在高位,右侧应在低位.
@@ -170,9 +204,13 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
* @endcode
* 该函数会将参数从0开始,每次逐渐增加1的顺序来被调用. 即先调用f(0),再f(1),f(2),f(3)... 以此类推.
*/
void display(std::function<uint8_t(int)> f, bool part = true);
/// @brief 显示图片, 使用抖动算法. 可以用省内存的方法显示
void drawImage(LGFX_Sprite &spr,uint16_t x,uint16_t y);
void display(std::function<uint8_t(int)> f, uint8_t part);
/// @brief 显示图片, 使用抖动算法. 可以用省内存的方法显示, 可以缩放到指定的宽度和高度
void drawImage(LGFX_Sprite &spr,uint16_t x,uint16_t y,uint16_t zoomw=0, uint16_t zoomh=0){
if(READGUY_cali==127) drawImage(*this,spr,x,y,zoomw,zoomh);
}
/// @brief 显示图片, 将图片(任意颜色格式)显示到一个黑白色的sprite(必须是黑白二值型)上 (未经测试)
void drawImage(LGFX_Sprite &base,LGFX_Sprite &spr,uint16_t x,uint16_t y,uint16_t zoomw=0,uint16_t zoomh=0);
/// @brief 设置显示对比度(灰度)
void setDepth(uint8_t d);
/** @brief 返回目标屏幕是否支持16级灰度 返回非0代表支持.
@@ -184,12 +222,12 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
* 2-关闭连续刷屏 关闭16阶灰度抖动 3-开启连续刷屏 关闭16阶灰度抖动 */
void setGreyQuality(uint8_t q) { if(READGUY_cali==127) guy_dev->setGreyQuality(q); }
/// @brief 显示灰度图片,如果支持,否则就不显示灰度图片了. 可以用省内存的方法显示
void draw16grey(LGFX_Sprite &spr,uint16_t x,uint16_t y);
void draw16grey(LGFX_Sprite &spr,uint16_t x,uint16_t y,uint16_t zoomw=0,uint16_t zoomh=0);
/** @brief 按照自定义分步显示灰度图片,如果支持,否则就不显示灰度图片了. 可以用省内存的方法显示
* @param step 步骤代号. 从1开始到15,依次调用此函数来自定义的灰度显示显存内容. 没有0和16.
* @note 必须按照 "慢刷全屏->绘图->设置参数1->绘图->设置参数2... 调用15次 来完成一次自定义灰度刷屏
* 连续调用多次此函数之间, 可以修改显存内的像素颜色, 但只能从白色改为黑色.
* @attention 需要先调用 supportGreyscaling() 来确定是否支持灰度分步刷新.为负数时需要从深到浅刷新
* @attention 需要先调用 supportGreyscaling() 来确定是否支持灰度分步刷新.为负数时需要从深到浅刷新. 参见示例.
*/
void draw16greyStep(int step);
/** @brief 分步刷新显示灰度, 详见 display(f,part) 和 draw16grey(spr,x,y) 的注释.
@@ -199,13 +237,13 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
void invertDisplay();
/// @brief 进入EPD的低功耗模式
void sleepEPD(void);
#ifdef READGUY_ESP_ENABLE_WIFI
/// @brief ap配网设置页面
typedef struct {
String linkname;
String event; //链接名称 事件URI
std::function<void(ReadguyWebServer*)> func; //触发时执行的函数
} serveFunc;
#ifdef READGUY_ESP_ENABLE_WIFI
/// @brief 初始化WiFi AP模式, 用于将来的连接WiFi 处于已连接状态下会断开原本的连接
void ap_setup();
/// @brief 初始化WiFi AP模式, 用于将来的连接WiFi 处于已连接状态下会断开原本的连接
@@ -213,6 +251,12 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
bool server_loop();
void server_end();
#else
/// @brief ap配网设置页面
typedef struct {
String linkname;
String event; //链接名称 事件URI
std::function<void(void*)> func; //触发时执行的函数
} serveFunc;
/// @brief 初始化WiFi AP模式, 用于将来的连接WiFi 处于已连接状态下会断开原本的连接
void ap_setup(){}
/// @brief 初始化服务器模式, 用于将来的连接WiFi 处于已连接状态下会断开原本的连接
@@ -225,7 +269,7 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
/** @brief 初始化屏幕, 设置驱动代号, 引脚排列顺序. 过程会检验引脚可用性.
* @param g_width, g_height 显示区域的宽度和高度. 为0表示直接使用屏幕的宽度和高度
* @note 这两个参数转专为指定分辨率的程序画面设计, 其他分辨率的画面会自动拉伸. [1.2新增] */
void setEpdDriver(int g_width = 0,int g_height = 0);
void setEpdDriver(bool initepd = 1, bool initGFX = 1);
/** @brief 初始化SD卡, 设置驱动代号, 引脚排列顺序. 过程会检验引脚可用性.
* @return SD卡初始化成功与否 */
bool setSDcardDriver();
@@ -236,6 +280,8 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
bool SDinside(bool check=true) { return check?setSDcardDriver():READGUY_sd_ok; };
/// @brief 检查按钮. 当配置未完成时,按钮不可用, 返回0.
uint8_t getBtn() { return (READGUY_cali==127)?getBtn_impl():0; }
/// @brief [此函数已弃用 非常不建议使用] 根据按钮ID来检查按钮. 注意这里如果按下返回0, 没按下或者按钮无效返回1
//uint8_t getBtn(unsigned int btnID){return btnID<getButtonsCount()?(!(btn_rd[0].isPressedRaw())):1;}
/** @brief 返回可用的文件系统. 当SD卡可用时, 返回SD卡. 否则根据情况返回最近的可用文件系统
* @param initSD 2:总是重新初始化SD卡; 1:若SD卡不可用则初始化; 0:SD卡不可用则返回LittleFS. */
fs::FS &guyFS(uint8_t initSD = 0);
@@ -247,21 +293,21 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
static const char projname[8];
static const char tagname[7];
//uint8_t config_wifi=0; //是否强行在初始化期间设置WiFi.
#ifdef DYNAMIC_PIN_SETTINGS//数据是否已经校准
int8_t config_data[22];
char randomch[4]; //校验用字符串
void nvs_init(); //初始化持久存储器.
void nvs_deinit();//保存持久存储器的内容
bool nvs_read(); //从持久存储器读取, 返回是否读取成功
void nvs_write(); //写入到持久存储器
#ifdef DYNAMIC_PIN_SETTINGS//数据是否已经校准
int8_t config_data[22];
char randomch[4]; //校验用字符串
#else
static const int8_t config_data[22];
int8_t READGUY_sd_ok = 0;
int8_t READGUY_cali = 0;
int8_t READGUY_buttons = 0; //按钮个数, 0-3都有可能
#endif
int epd_OK=0; //墨水屏可用
int currentBright = -3; //初始亮度
int16_t guy_width=0,guy_height=0;
//LGFX_Sprite gfx; // 图形引擎类指针, 可以用这个指针去操作屏幕缓冲区
readguyEpdBase *guy_dev = nullptr;
@@ -274,10 +320,8 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
//对于esp8266, 需要注册到ticker
Ticker btnTask;
#else
#ifdef DYNAMIC_PIN_SETTINGS
//NVS数据操作函数, 无NVS的使用EEProm的最后几个字节块
Preferences nvsData;
#endif
static SPIClass *sd_spi;
static SPIClass *epd_spi;
static TaskHandle_t btn_handle;
@@ -325,51 +369,67 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
static uint8_t rd_btn_f(uint8_t btn);
uint8_t getBtn_impl(); //按钮不可用, 返回0.
static void in_press(){ //SPI开始传输屏幕数据
#ifndef ESP8266
#ifdef ESP8266
if(!spibz) SPI.beginTransaction(SPISettings(ESP8266_SPI_FREQUENCY, MSBFIRST, SPI_MODE0));
#else
if(!spibz) epd_spi->beginTransaction(SPISettings(ESP32_DISP_FREQUENCY, MSBFIRST, SPI_MODE0));
#endif
spibz ++;
}
static void in_release(){//SPI结束传输屏幕数据
spibz --;
#ifndef ESP8266
#ifdef ESP8266
if(!spibz) SPI.endTransaction();
#else
if(!spibz) epd_spi->endTransaction();
#endif
}
public: //增加了一些返回系统状态变量的函数, 它们是静态的, 而且不会对程序造成任何影响.
constexpr int getShareSpi() const { return config_data[1]; }
constexpr int getEpdType () const { return config_data[2]; } // 对应的epd驱动程序代号, -1为未指定
constexpr int getShareSpi() const { return READGUY_shareSpi; }
constexpr int getEpdType () const { return READGUY_epd_type; } // 对应的epd驱动程序代号, -1为未指定
//显示驱动部分, 显示默认使用vspi (vspi也是默认SPI库的通道)
constexpr int getEpdMosi () const { return config_data[3]; } // 目标显示器的 MOSI 引脚
constexpr int getEpdSclk () const { return config_data[4]; } // 目标显示器的 SCLK 引脚
constexpr int getEpdCs () const { return config_data[5]; } // 目标显示器的 CS 引脚
constexpr int getEpdDc () const { return config_data[6]; } // 目标显示器的 DC 引脚
constexpr int getEpdRst () const { return config_data[7]; } // 目标显示器的 RST 引脚
constexpr int getEpdBusy () const { return config_data[8]; } // 目标显示器的 BUSY 引脚
constexpr int getEpdMosi () const { return READGUY_epd_mosi; } // 目标显示器的 MOSI 引脚
constexpr int getEpdSclk () const { return READGUY_epd_sclk; } // 目标显示器的 SCLK 引脚
constexpr int getEpdCs () const { return READGUY_epd_cs; } // 目标显示器的 CS 引脚
constexpr int getEpdDc () const { return READGUY_epd_dc; } // 目标显示器的 DC 引脚
constexpr int getEpdRst () const { return READGUY_epd_rst; } // 目标显示器的 RST 引脚
constexpr int getEpdBusy () const { return READGUY_epd_busy; } // 目标显示器的 BUSY 引脚
//sd卡驱动部分, 默认使用hspi (sd卡建议用hspi)
constexpr int getSdMiso () const { return config_data[9]; } // 目标sd卡的 MISO 引脚, sd_share_spi == 1 时无效
constexpr int getSdMosi () const { return config_data[10]; }// 目标sd卡的 MOSI 引脚, sd_share_spi == 1 时无效
constexpr int getSdSclk () const { return config_data[11]; }// 目标sd卡的 SCLK 引脚, sd_share_spi == 1 时无效
constexpr int getSdCs () const { return config_data[12]; }// 目标sd卡的 CS 引脚.
constexpr int getI2cSda () const { return config_data[13]; }// 目标i2c总线的SDA引脚, 当且仅当启用i2c总线时才生效
constexpr int getI2cScl () const { return config_data[14]; }// 目标i2c总线的SCL引脚, 当且仅当启用i2c总线时才生效
constexpr int getSdMiso () const { return READGUY_sd_miso; } // 目标sd卡的 MISO 引脚, sd_share_spi == 1 时无效
constexpr int getSdMosi () const { return READGUY_sd_mosi; }// 目标sd卡的 MOSI 引脚, sd_share_spi == 1 时无效
constexpr int getSdSclk () const { return READGUY_sd_sclk; }// 目标sd卡的 SCLK 引脚, sd_share_spi == 1 时无效
constexpr int getSdCs () const { return READGUY_sd_cs; }// 目标sd卡的CS引脚. 对ESP32S3, 返回127代表使用SDMMC
constexpr int getI2cSda () const { return READGUY_i2c_sda; }// 目标i2c总线的SDA引脚, 当且仅当启用i2c总线时才生效
constexpr int getI2cScl () const { return READGUY_i2c_scl; }// 目标i2c总线的SCL引脚, 当且仅当启用i2c总线时才生效
//按键驱动部分, 为负代表高触发, 否则低触发,
//注意, 这里的io编号是加1的, 即 1或-1 代表 gpio0 的低触发/高触发
constexpr int getBtn1Pin () const { return config_data[15]; }
constexpr int getBtn2Pin () const { return config_data[16]; }
constexpr int getBtn3Pin () const { return config_data[17]; }
constexpr int getBlPin () const { return config_data[18]; } //前置光接口引脚IO
constexpr int getRtcType () const { return config_data[19]; } //使用的RTC型号(待定, 还没用上)
constexpr int getButtonsCount() const { return config_data[21]; } //按钮个数, 0-3都有可能
constexpr int memWidth () const { return guy_width ; } //返回显存宽度(不是画幅宽度),不会随着画布旋转改变
constexpr int memHeight () const { return guy_height ; } //返回显存高度(不是画幅高度),不会随着画布旋转改变
constexpr int getBtn1Pin () const { return READGUY_btn1; }
constexpr int getBtn2Pin () const { return READGUY_btn2; }
constexpr int getBtn3Pin () const { return READGUY_btn3; }
constexpr int getBlPin () const { return READGUY_bl_pin; } //前置光接口引脚IO
constexpr int getRtcType () const { return READGUY_rtc_type; } //使用的RTC型号(待定, 还没用上)
constexpr int getButtonsCount() const { return READGUY_buttons; } //按钮个数, 0-3都有可能
//constexpr int memWidth () const { return guy_width ; } //返回显存宽度(不是画幅宽度),不会随着画布旋转改变
//constexpr int memHeight () const { return guy_height ; } //返回显存高度(不是画幅高度),不会随着画布旋转改变
int drvWidth () const { return READGUY_cali==127?guy_dev->drv_width():0; } //返回显示屏硬件宽度(不是画幅宽度)
int drvHeight() const { return READGUY_cali==127?guy_dev->drv_height():0; } //返回显示屏硬件高度(不是画幅高度)
//int width () const { return (getRotation()&1)?drvHeight():drvWidth(); }
//int height() const { return (getRotation()&1)?drvWidth():drvHeight(); }
size_t getFreeMem() const { return
#ifdef ESP8266
ESP.getFreeHeap();
#else
esp_get_free_heap_size();
#endif
}
// private:
void implBeginTransfer() { guy_dev->BeginTransfer(); } //此函数用于开启SPI传输, 只能在自定义刷屏函数中使用!!
void implEndTransfer() { guy_dev->EndTransfer(); } //此函数用于开启SPI传输, 只能在自定义刷屏函数中使用!!
/// @brief 分阶段显示图片, 使用抖动算法. 更加的省内存.目前函数
void drawImageStage(LGFX_Sprite &spr,uint16_t x,uint16_t y,uint8_t stage,uint8_t totalstage);
void drawImageStage(LGFX_Sprite &spr,uint16_t x,uint16_t y,uint8_t stage,uint8_t totalstage,
uint16_t zoomw=0,uint16_t zoomh=0){ drawImageStage(*this,spr,x,y,stage,totalstage,zoomw,zoomh); }
void drawImageStage(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_t x,uint16_t y,
uint8_t stage,uint8_t totalstage,uint16_t zoomw=0,uint16_t zoomh=0);
};
#endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */