diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a7d2b2..2af05d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,35 @@ +## 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 的示例程序。其中图片相关功能相当节省内存, 还请大胆使用。 diff --git a/README.md b/README.md index 1d24d80..81b5cf4 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ -**版本1.1.1正式发布!欢迎分享、star和fork~** 上面的图是项目看板娘, 盖. 可爱的盖姐在等你哟~ +**版本1.3.0正式发布!欢迎分享、star和fork~** 上面的图是项目看板娘, 盖. 可爱的盖姐在等你哟~ 欢迎克隆, 项目交流QQ群: 926824162 (萌新可以进来问问题的哟), 项目的 Bilibili 主页: [BV1f94y187wz](https://www.bilibili.com/video/BV1f94y187wz/) 记得三连+关注我这个宝藏up主哦~ diff --git a/examples/ex01_helloWorld/ex01_helloWorld.ino b/examples/ex01_helloWorld/ex01_helloWorld.ino index 169d197..ab70f40 100644 --- a/examples/ex01_helloWorld/ex01_helloWorld.ino +++ b/examples/ex01_helloWorld/ex01_helloWorld.ino @@ -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 一样, +//为了能在这里使用readguy库, 你需要在这里调用 #include "readguy.h" 来包含此库 +//在platformio中, 还要额外 #include 来确保你用的是Arduino环境. + #include //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,18 +141,23 @@ 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(true); // 快速刷新. 将屏幕缓存内的内容显示到墨水屏幕上. 可简写为 guy.display(), 效果一样. //guy.display(false); // 慢速刷新. //想知道更多内容, 欢迎移步到其他示例. @@ -85,6 +167,7 @@ void loop(){ //什么也不做, 毕竟刷新墨水屏要消耗墨水屏的阳寿. //盖姐说它们也是有阳寿的. 刷多了会老化. + delay(1); }/* END OF FILE. ReadGuy project. Copyright (C) 2023 FriendshipEnder. */ \ No newline at end of file diff --git a/examples/ex02_demo/ex02_demo.ino b/examples/ex02_demo/ex02_demo.ino index 1675932..42321bf 100644 --- a/examples/ex02_demo/ex02_demo.ino +++ b/examples/ex02_demo/ex02_demo.ino @@ -66,7 +66,6 @@ void setup(){ guy.init(); //初始化readguy_driver 基础驱动库. //首次初始化完成之后, 以后再初始化就不需要配网了, 除非你抹除了芯片的flash - //完成之后会全屏刷新一次 // ------------------- 2 - 使用ReadGuy来显示字符串 ------<< diff --git a/examples/ex04_wifi/3_build_without_wifi/3_build_without_wifi.ino b/examples/ex04_wifi/3_build_without_wifi/3_build_without_wifi.ino new file mode 100644 index 0000000..b340ad7 --- /dev/null +++ b/examples/ex04_wifi/3_build_without_wifi/3_build_without_wifi.ino @@ -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功能基础库. 在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(true); // 快速刷新. 将屏幕缓存内的内容显示到墨水屏幕上 + //guy.display(false); // 慢速刷新. + + //想知道更多内容, 欢迎移步到其他示例. +} + +void loop(){ + + //什么也不做, 毕竟刷新墨水屏要消耗墨水屏的阳寿. + //盖姐说它们也是有阳寿的. 刷多了会老化. + +}/* END OF FILE. ReadGuy project. +Copyright (C) 2023 FriendshipEnder. */ \ No newline at end of file diff --git a/examples/ex06_Image/data/png.png b/examples/ex06_Image/data/png.png index 3655264..bb6fea1 100644 Binary files a/examples/ex06_Image/data/png.png and b/examples/ex06_Image/data/png.png differ diff --git a/examples/ex06_Image/ex06_Image.ino b/examples/ex06_Image/ex06_Image.ino new file mode 100644 index 0000000..ea28162 --- /dev/null +++ b/examples/ex06_Image/ex06_Image.ino @@ -0,0 +1,388 @@ +/******************** 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卡的根目录上. + * + * {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功能基础库. 在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,true); //在保持屏幕缓存不变的时候快速刷新白屏. + guy.drawImage(sp,0,0,guy.width(),guy.height()); //绘画的画布可以被放大或者缩小到任意宽度和高度. + //此处的参数调用表示将会在屏幕坐标(0,0)开始显示, 显示的画布宽度缩放到屏幕宽度, 画布高度缩放到屏幕高度. + guy.display(); //调用display函数刷屏. + delay(2000); + + guy.display(FILL_WHITE,true); //在保持屏幕缓存不变的时候快速刷新白屏 + 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,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(); //显示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,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 [] 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(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代表与横坐标缩放相同. + + 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. */ + + diff --git a/examples/ex06_Image/guy_image.cpp b/examples/ex06_Image/guy_image.cpp index b678f5e..600c3ff 100644 --- a/examples/ex06_Image/guy_image.cpp +++ b/examples/ex06_Image/guy_image.cpp @@ -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,9 +226,9 @@ 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; @@ -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; //未知格式 @@ -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; diff --git a/examples/ex06_Image/main.cpp b/examples/ex06_Image/main.cpp deleted file mode 100644 index 97dcb8e..0000000 --- a/examples/ex06_Image/main.cpp +++ /dev/null @@ -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功能基础库. 在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. */ - - diff --git a/library.json b/library.json index d7ff2ff..48567fd 100644 --- a/library.json +++ b/library.json @@ -11,7 +11,7 @@ "type": "git", "url": "https://github.com/fsender/readguy" }, - "version": "1.1.1", + "version": "1.3.0", "frameworks": "arduino", "platforms": ["espressif32", "espressif8266"], "headers": "readguy.h", diff --git a/library.properties b/library.properties index 79ac62a..c9dc35d 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=readguy -version=1.1.1 +version=1.3.0 author=fsender maintainer=fsender sentence=A free E-paper display driver library supports 16-level greyscale. diff --git a/src/guy_epaper/guy_154a_290a/guy_154a_290a.cpp b/src/guy_epaper/guy_154a_290a/guy_154a_290a.cpp index 4c39139..815c9d1 100644 --- a/src/guy_epaper/guy_154a_290a/guy_154a_290a.cpp +++ b/src/guy_epaper/guy_154a_290a/guy_154a_290a.cpp @@ -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,14 +92,24 @@ 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(!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 f){ //单色刷新等功能 if(sleeping) Init(lut_slow); BeginTransfer(); @@ -136,8 +139,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; diff --git a/src/guy_epaper/guy_154a_290a/guy_154a_290a.h b/src/guy_epaper/guy_154a_290a/guy_154a_290a.h index bc292df..5a6d653 100644 --- a/src/guy_epaper/guy_154a_290a/guy_154a_290a.h +++ b/src/guy_epaper/guy_154a_290a/guy_154a_290a.h @@ -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 diff --git a/src/guy_epaper/guy_154b_270b_290b/guy_154b_270b_290b.cpp b/src/guy_epaper/guy_154b_270b_290b/guy_154b_270b_290b.cpp index ea8b396..7fbbc5c 100644 --- a/src/guy_epaper/guy_154b_270b_290b/guy_154b_270b_290b.cpp +++ b/src/guy_epaper/guy_154b_270b_290b/guy_154b_270b_290b.cpp @@ -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,21 @@ 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(!epd_PowerOn) part=0; //未上电 无法局刷 if(!part) { iLut=15; greyScaling=0; } _part=part; } void drvSSD168x::drv_dispWriter(std::function f){ //单色刷新 BeginTransfer(); - if(_part && epd_PowerOn){ + if(_part){ //Reset(); SetLut(); guy_epdCmd(0x37); @@ -186,7 +163,26 @@ void drvSSD168x::drv_dispWriter(std::function 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,7 +192,9 @@ void drvSSD168x::drv_dispWriter(std::function 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); } @@ -206,8 +204,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) { diff --git a/src/guy_epaper/guy_154b_270b_290b/guy_154b_270b_290b.h b/src/guy_epaper/guy_154b_270b_290b/guy_154b_270b_290b.h index f736c40..e65790b 100644 --- a/src/guy_epaper/guy_154b_270b_290b/guy_154b_270b_290b.h +++ b/src/guy_epaper/guy_154b_270b_290b/guy_154b_270b_290b.h @@ -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(); }; diff --git a/src/guy_epaper/guy_213a/guy_213a.cpp b/src/guy_epaper/guy_213a/guy_213a.cpp index 2c68a97..3f4f526 100644 --- a/src/guy_epaper/guy_213a/guy_213a.cpp +++ b/src/guy_epaper/guy_213a/guy_213a.cpp @@ -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,12 +106,14 @@ 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(!epd_PowerOn) part=0; //未上电 无法局刷 //if(part==epdFull) return; if(!part) iLut=15; //恢复默认的灰度模式 epdFull = !part; @@ -144,21 +146,22 @@ void drv::drv_dispWriter(std::function f){ //单色刷新 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. diff --git a/src/guy_epaper/guy_213a/guy_213a.h b/src/guy_epaper/guy_213a/guy_213a.h index 4cfc1d8..368f086 100644 --- a/src/guy_epaper/guy_213a/guy_213a.h +++ b/src/guy_epaper/guy_213a/guy_213a.h @@ -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[]; diff --git a/src/guy_epaper/guy_213b_266a/guy_213b_266a.cpp b/src/guy_epaper/guy_213b_266a/guy_213b_266a.cpp index c9a627d..4323966 100644 --- a/src/guy_epaper/guy_213b_266a/guy_213b_266a.cpp +++ b/src/guy_epaper/guy_213b_266a/guy_213b_266a.cpp @@ -76,13 +76,8 @@ drv_base::drv_base(){ 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); @@ -110,11 +105,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){ @@ -134,17 +129,19 @@ 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(!Power_is_on) part=0; if(!part) greyLut=15; //恢复默认的灰度模式 part_mode = part; } @@ -157,11 +154,11 @@ void drv_base::drv_setDepth(uint8_t i){ } void drv_base::drv_dispWriter(std::function f){ //单色刷新 BeginTransfer(); - pre(); - //send pixel data -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ - //Total 5624 data written. - //for(int i=0;i f){ //单色刷新 if(part_mode){ guy_epdCmd(0x30); guy_epdParam(0x3a); //0x3a:100Hz, 0x29:150Hz - //[EPDrg_BW<>] refresh fx 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); } } 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 f, int step){ diff --git a/src/guy_epaper/guy_213b_266a/guy_213b_266a.h b/src/guy_epaper/guy_213b_266a/guy_213b_266a.h index 2d87631..dece496 100644 --- a/src/guy_epaper/guy_213b_266a/guy_213b_266a.h +++ b/src/guy_epaper/guy_213b_266a/guy_213b_266a.h @@ -50,12 +50,11 @@ 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]; diff --git a/src/guy_epaper/guy_370a/guy_370a.cpp b/src/guy_epaper/guy_370a/guy_370a.cpp index 5b63904..d793866 100644 --- a/src/guy_epaper/guy_370a/guy_370a.cpp +++ b/src/guy_epaper/guy_370a/guy_370a.cpp @@ -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(sleeping) return; if(!part) { greyScaling=15; //恢复默认的灰度模式 BeginTransfer(); @@ -142,8 +146,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){ //设置显示颜色深度, 不支持的话什么都不做 diff --git a/src/guy_epaper/guy_370a/guy_370a.h b/src/guy_epaper/guy_370a/guy_370a.h index 9851a46..ee17a05 100644 --- a/src/guy_epaper/guy_370a/guy_370a.h +++ b/src/guy_epaper/guy_370a/guy_370a.h @@ -47,9 +47,9 @@ public: 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); }; diff --git a/src/guy_epaper/guy_420a/guy_420a.cpp b/src/guy_epaper/guy_420a/guy_420a.cpp index be57896..b2cc5af 100644 --- a/src/guy_epaper/guy_420a/guy_420a.cpp +++ b/src/guy_epaper/guy_420a/guy_420a.cpp @@ -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,25 @@ 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(epdFull<=1) epdFull = !part; //epdFull==2代表睡眠中, 不能快刷 + if(epdFull) GreyScaling=0; } void drv::drv_dispWriter(std::function f){ //单色刷新 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); @@ -260,13 +257,14 @@ void drv::drv_draw16grey_step(std::function 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. diff --git a/src/guy_epaper/guy_420a/guy_420a.h b/src/guy_epaper/guy_420a/guy_420a.h index 666f289..1bc95c8 100644 --- a/src/guy_epaper/guy_420a/guy_420a.h +++ b/src/guy_epaper/guy_420a/guy_420a.h @@ -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 send; //此处不能用 void (*send)(int); 是因为lambda函数是std的 }; diff --git a/src/guy_epaper/guy_420b/guy_420b.cpp b/src/guy_epaper/guy_420b/guy_420b.cpp index 76a972d..f309958 100644 --- a/src/guy_epaper/guy_420b/guy_420b.cpp +++ b/src/guy_epaper/guy_420b/guy_420b.cpp @@ -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, @@ -153,6 +161,7 @@ void drv::drv_dispWriter(std::function f){ //单色刷新 //[EPDrg_BW<>] refresh fx } else{ + Power_is_on=1; guy_epdCmd(0x10); //刷新数据 for(int i=0;i=0){ //未定义RST_PIN时无法唤醒 @@ -219,6 +233,8 @@ void drv::drv_sleep() { //开始屏幕睡眠 guy_epdParam(0xA5); EndTransfer(); } + Power_is_on=0; + part_mode=0; } } #endif /* END OF FILE. ReadGuy project. diff --git a/src/guy_epaper/guy_420b/guy_420b.h b/src/guy_epaper/guy_420b/guy_420b.h index 76ad10a..69409da 100644 --- a/src/guy_epaper/guy_420b/guy_420b.h +++ b/src/guy_epaper/guy_420b/guy_420b.h @@ -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]; }; diff --git a/src/guy_epaper/guy_epdbase.cpp b/src/guy_epaper/guy_epdbase.cpp index 2440f74..af81083 100644 --- a/src/guy_epaper/guy_epdbase.cpp +++ b/src/guy_epaper/guy_epdbase.cpp @@ -70,9 +70,11 @@ 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; @@ -157,7 +159,8 @@ 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){ +void readguyEpdBase::drv_drawImage(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_t x,uint16_t y,int o, + uint16_t fw, uint16_t fh){ #ifndef FLOYD_STEINBERG_DITHERING static const uint8_t bayer_tab [64]={ 0, 32, 8, 40, 2, 34, 10, 42, @@ -170,30 +173,32 @@ void readguyEpdBase::drv_drawImage(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_ 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>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); + sprbase.fillRect(x,y,fw,fh,1); + for(int32_t i=y;i<(int32_t)fh+y;i++){ + spr.readRect(0,(i-y)*spr.height()/fh,spr.width(),1,readBuff); #ifdef FLOYD_STEINBERG_DITHERING uint_fast8_t buff8bit=0; - for(int32_t j=0;j=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,23 +210,23 @@ 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>2))<<(7-b); + buff8bit |= (bayer_tab[(b<<3)|(i&7)]<(greysc(readBuff[j*spr.width()/fw])>>2))<<(7-b); writeBuff[j]=buff8bit; } #endif - sprbase.drawBitmap(x,i,writeBuff,spr.width(),1,1,0); + sprbase.drawBitmap(x,i,writeBuff,fw,1,1,0); } //_display((const uint8_t*)sprbase.getBuffer()); //显示 if(o==0 || o==3){ @@ -234,19 +239,21 @@ void readguyEpdBase::drv_drawImage(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16_ } } //不支持的话使用单色抖动刷屏 -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()]; + floyd_tab[0] = new int16_t [fw]; + floyd_tab[1] = new int16_t [fw]; #endif } - 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()); //先对区域慢刷白屏确保颜色正确 @@ -254,33 +261,31 @@ void readguyEpdBase::drv_draw16grey(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16 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=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{ cg=greysc(readBuff[j*spr.width()/fw])>>4; } #else - uint_fast8_t cg=greysc(readBuff[j])>>4; + uint_fast8_t cg=greysc(readBuff[j*spr.width()/fw])>>4; #endif if(negativeOrder) 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>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); } @@ -113,6 +115,8 @@ public: #ifdef MEPD_DEBUG_DISPLAY friend class LGFX; #endif + private: + int16_t guy_width=0,guy_height=0; }; #endif /* END OF FILE. ReadGuy project. diff --git a/src/guy_version.h b/src/guy_version.h index 3bd7a87..c211f68 100644 --- a/src/guy_version.h +++ b/src/guy_version.h @@ -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 0 #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.0" #ifdef ESP8266 #define _READGUY_PLATFORM "ESP8266" diff --git a/src/guy_wireless.cpp b/src/guy_wireless.cpp index faca519..a2fcfef 100644 --- a/src/guy_wireless.cpp +++ b/src/guy_wireless.cpp @@ -126,7 +126,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 ¬ify, const serveFunc *serveFuncs, int funcs){ //启动WiFi服务器端, 这样就可以进行配网工作 @@ -163,7 +163,7 @@ void ReadguyDriver::server_setup(const String ¬ify, 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()); } @@ -218,7 +218,7 @@ 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) @@ -280,7 +280,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,12 +304,13 @@ 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...")); + Serial.println(F("[Guy] Init details...")); + setTextSize(1); drawCenterString(setSDcardDriver()?"SD Init OK!":"SD Init failed!",width()>>1,(height()>>1)+20); setButtonDriver(); //初始化按钮.. //} //尝试初始化按键, 调用后, 若SD卡初始化成功, READGUY_sd_ok的值会变成1 @@ -321,7 +322,7 @@ void ReadguyDriver::handleInitPost(){ 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(){ @@ -592,7 +593,7 @@ void ReadguyDriver::handleFinal(){ //s+=F("
"); //换行 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(); diff --git a/src/readguy.cpp b/src/readguy.cpp index 5a92240..2a88e84 100644 --- a/src/readguy.cpp +++ b/src/readguy.cpp @@ -48,8 +48,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/* ,int g_width,int g_height */){ if(READGUY_cali==127) //已经初始化过了一次了, 为了防止里面一些volatile的东西出现问题....还是退出吧 return 0; #ifdef DYNAMIC_PIN_SETTINGS @@ -77,7 +77,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 +85,12 @@ uint8_t ReadguyDriver::init(uint8_t WiFiSet){ //WiFiSet: 是否保持AP服务器 #endif nvs_deinit(); #else - if(checkEpdDriver()!=127) setEpdDriver(); //初始化屏幕 + if(checkEpdDriver()!=127) setEpdDriver(initepd/* ,g_width,g_height */); //初始化屏幕 else for(;;); //此处可能添加程序rollback等功能操作(比如返回加载上一个程序) setSDcardDriver(); setButtonDriver(); #endif - Serial.println(F("init done.")); + Serial.println(F("[Guy init] init done.")); READGUY_cali=127; return READGUY_sd_ok; } @@ -101,7 +101,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++) @@ -149,7 +149,7 @@ uint8_t ReadguyDriver::checkEpdDriver(){ case READGUY_DEV_270B: guy_dev = new guydev_154B_270B_290B ::dev270B; break; #endif default: - Serial.println(F("[ERR] EPD DRIVER IC NOT SUPPORTED!\n")); + Serial.println(F("[GUY ERR] EPD DRIVER IC NOT SUPPORTED!\n")); return 127; } #if (defined(ESP8266)) @@ -169,26 +169,27 @@ 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/* ,int g_width,int g_height */){ 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(); + Serial.println(F("[Guy EPD] EPD init OK")); //以下依赖于你的图形驱动 setColorDepth(1); //单色模式 createPalette(); //初始化颜色系统 - Serial.printf_P(PSTR("mono set: w: %d, h: %d\n"),guy_width,guy_height); + Serial.printf_P(PSTR("[Guy EPD] mono set: w: %d, h: %d\n"),guy_dev->drv_width(),guy_dev->drv_height()); //创建画布. 根据LovyanGFX的特性, 如果以前有画布会自动重新生成新画布 //此外, 即使画布宽度不是8的倍数(如2.13寸单色),也支持自动补全8的倍数 ( 250x122 => 250x128 ) //为了保证图片显示功能的正常使用, 高度也必须是8的倍数. - createSprite(guy_width,(guy_height+7)&0x7ffffff8); + createSprite(guy_dev->drv_width(),(guy_dev->drv_height()+7)&0x7ffffff8); + //这里发现如果用自定义的内存分配方式会更好一些. 不会导致返回的height不对. 但是因为LovyanGFX库未更新 暂时不能这么用. //setRotation(1); //旋转之后操作更方便 setRotation(0); setFont(&fonts::Font0); @@ -226,6 +227,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(); @@ -387,21 +389,22 @@ void ReadguyDriver::display(std::function f, bool part){ //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::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 &spr,uint16_t x,uint16_t y,uint8_t stage,uint8_t totalstage) { +void ReadguyDriver::drawImageStage(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))); + guy_dev->drv_drawImage(*this, 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 +420,7 @@ void ReadguyDriver::draw16greyStep(std::function 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=8) config_data[i-8] = rd; else s[i]=(char)rd; } - Serial.printf("Get NVS...%d\n", config_data[0]); + Serial.printf("[Guy NVS] Get NVS...%d\n", config_data[0]); return !(strcmp_P(s,projname)); } void ReadguyDriver::nvs_write(){ diff --git a/src/readguy.h b/src/readguy.h index 4dfe26b..f7e374a 100644 --- a/src/readguy.h +++ b/src/readguy.h @@ -149,7 +149,7 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类 * 2:自动连接到已存的WiFi, 但不等待连接成功 * @return SD卡是否就绪 */ - uint8_t init(uint8_t WiFiSet = 0); + uint8_t init(uint8_t WiFiSet = 0,bool initepd = 1/* ,int g_width = 0,int g_height = 0 */); /// @brief 设置显示亮度 void setBright(int d); /// @brief 返回显示亮度 @@ -171,8 +171,12 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类 * 该函数会将参数从0开始,每次逐渐增加1的顺序来被调用. 即先调用f(0),再f(1),f(2),f(3)... 以此类推. */ void display(std::function f, bool part = true); - /// @brief 显示图片, 使用抖动算法. 可以用省内存的方法显示 - void drawImage(LGFX_Sprite &spr,uint16_t x,uint16_t y); + /// @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 +188,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) 的注释. @@ -225,7 +229,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/* ,int g_width = 0,int g_height = 0 */); /** @brief 初始化SD卡, 设置驱动代号, 引脚排列顺序. 过程会检验引脚可用性. * @return SD卡初始化成功与否 */ bool setSDcardDriver(); @@ -261,7 +265,6 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类 #endif int epd_OK=0; //墨水屏可用 int currentBright = -3; //初始亮度 - int16_t guy_width=0,guy_height=0; //LGFX_Sprite gfx; // 图形引擎类指针, 可以用这个指针去操作屏幕缓冲区 readguyEpdBase *guy_dev = nullptr; @@ -361,15 +364,18 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类 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 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 READGUY_cali==127?((getRotation()&1)?drvHeight():drvWidth()):0; } + int height() const { return READGUY_cali==127?((getRotation()&1)?drvWidth():drvHeight()):0; } // 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); }; #endif /* END OF FILE. ReadGuy project. Copyright (C) 2023 FriendshipEnder. */ \ No newline at end of file