update to 1.3.0 version

This commit is contained in:
fsender
2023-11-06 15:39:13 +08:00
parent b5d77bc054
commit a8b2540468
31 changed files with 903 additions and 635 deletions

View File

@@ -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 ## Release 1.2.0 - 2023/11/3
1. 添加了图片demo, 和 可选关闭的WiFi 的示例程序。其中图片相关功能相当节省内存, 还请大胆使用。 1. 添加了图片demo, 和 可选关闭的WiFi 的示例程序。其中图片相关功能相当节省内存, 还请大胆使用。

View File

@@ -4,7 +4,7 @@
<img src="extra/artset/readguy_theme3.png" width="30%" height="auto"> <img src="extra/artset/readguy_theme3.png" width="30%" height="auto">
**版本1.1.1正式发布欢迎分享、star和fork~** 上面的图是项目看板娘, 盖. 可爱的盖姐在等你哟~ **版本1.3.0正式发布欢迎分享、star和fork~** 上面的图是项目看板娘, 盖. 可爱的盖姐在等你哟~
欢迎克隆, 项目交流QQ群: 926824162 (萌新可以进来问问题的哟), 项目的 Bilibili 主页: [BV1f94y187wz](https://www.bilibili.com/video/BV1f94y187wz/) 记得三连+关注我这个宝藏up主哦~ 欢迎克隆, 项目交流QQ群: 926824162 (萌新可以进来问问题的哟), 项目的 Bilibili 主页: [BV1f94y187wz](https://www.bilibili.com/video/BV1f94y187wz/) 记得三连+关注我这个宝藏up主哦~

View File

@@ -6,8 +6,9 @@
* *
* @file ex01_helloWorld.ino * @file ex01_helloWorld.ino
* @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder * @author FriendshipEnder (f_ender@163.com), Bilibili: FriendshipEnder
* @version 1.0 * @version 1.1
* @date 2023-09-19 * @date create: 2023-09-19
* last modify: 2023-11-06
* @brief ReadGuy最基础的HelloWorld显示. * @brief ReadGuy最基础的HelloWorld显示.
* *
* @note 食用方法: * @note 食用方法:
@@ -27,6 +28,79 @@
* 你需要知道你的哪个引脚对应哪个GPIO, 才能使用这个库 (带来的不便请谅解nia~) * 你需要知道你的哪个引脚对应哪个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 * @attention
* Copyright (c) 2022-2023 FriendshipEnder * 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 <Arduino.h> //arduino功能基础库. 在platformIO平台上此语句不可或缺
#include "readguy.h" //包含readguy_driver 基础驱动库 #include "readguy.h" //包含readguy_driver 基础驱动库
ReadguyDriver guy;//新建一个readguy对象, 用于显示驱动. ReadguyDriver guy;//新建一个readguy对象, 用于显示驱动.
//所有对墨水屏的操作都是对guy这个对象进行的操作.
void drawLines(); //声明一个函数, 用于显示一些线条. 此函数在后面的程序中会用到的 void setup(){ //Arduino的setup函数. 这个函数在上电之后仅执行一次.
void setup(){
// --------------------- 1 - 初始化和启动ReadGuy -------< // --------------------- 1 - 初始化和启动ReadGuy -------<
Serial.begin(115200); //初始化串口 Serial.begin(115200); //初始化串口
@@ -64,18 +141,23 @@ void setup(){
guy.init(); //初始化readguy_driver 基础驱动库. guy.init(); //初始化readguy_driver 基础驱动库.
//首次初始化完成之后, 以后再初始化就不需要配网了, 除非你抹除了芯片的flash //首次初始化完成之后, 以后再初始化就不需要配网了, 除非你抹除了芯片的flash
//完成之后会全屏刷新一次 //完成之后会让下一次刷屏 全屏慢速刷新一次, 之后的刷屏即可自由定义是全刷还是局刷.
guy.setFont(&FreeMonoBold9pt7b); //设置显示的字体 guy.setFont(&FreeMonoBold9pt7b); //设置显示的字体
//字体可以参考LovyanGFX的示例程序.
guy.setTextColor(0,1); //设置显示的颜色. 0代表黑色, 1代表白色 guy.setTextColor(0,1); //设置显示的颜色. 0代表黑色, 1代表白色
//此函数的作用是设置显示颜色. 左边的0代表前景色(文字颜色: 黑色), 右边的1代表背景色(白色)
//类似于 guy.setTextColor(0) 的用法说明此颜色为透明背景.
guy.drawString("Hello Readguy!",10,10); //用此函数将字符串显示到屏幕缓存内 guy.drawString("Hello Readguy!",10,10); //用此函数将字符串显示到屏幕缓存内
//调用此函数并不会立即刷屏, 而是会将文本字符串写入到屏幕缓存, 在下一次调用display()函数之后就会显示出来.
//也可以用print函数来显示.
//guy.setCursor(10,10); //设置显示的坐标 //guy.setCursor(10,10); //设置显示的坐标
//guy.print("Hello Readguy!"); //使用这个函数也能显示出字符串, 但是需要提前使用setCursor确定显示坐标 //guy.print("Hello Readguy!"); //使用这个函数也能显示出字符串, 但是需要提前使用setCursor确定显示坐标
guy.display(true); // 快速刷新. 将屏幕缓存内的内容显示到墨水屏幕上 guy.display(true); // 快速刷新. 将屏幕缓存内的内容显示到墨水屏幕上. 可简写为 guy.display(), 效果一样.
//guy.display(false); // 慢速刷新. //guy.display(false); // 慢速刷新.
//想知道更多内容, 欢迎移步到其他示例. //想知道更多内容, 欢迎移步到其他示例.
@@ -85,6 +167,7 @@ void loop(){
//什么也不做, 毕竟刷新墨水屏要消耗墨水屏的阳寿. //什么也不做, 毕竟刷新墨水屏要消耗墨水屏的阳寿.
//盖姐说它们也是有阳寿的. 刷多了会老化. //盖姐说它们也是有阳寿的. 刷多了会老化.
delay(1);
}/* END OF FILE. ReadGuy project. }/* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */ Copyright (C) 2023 FriendshipEnder. */

View File

@@ -66,7 +66,6 @@ void setup(){
guy.init(); //初始化readguy_driver 基础驱动库. guy.init(); //初始化readguy_driver 基础驱动库.
//首次初始化完成之后, 以后再初始化就不需要配网了, 除非你抹除了芯片的flash //首次初始化完成之后, 以后再初始化就不需要配网了, 除非你抹除了芯片的flash
//完成之后会全屏刷新一次
// ------------------- 2 - 使用ReadGuy来显示字符串 ------<< // ------------------- 2 - 使用ReadGuy来显示字符串 ------<<

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(true); // 快速刷新. 将屏幕缓存内的内容显示到墨水屏幕上
//guy.display(false); // 慢速刷新.
//想知道更多内容, 欢迎移步到其他示例.
}
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,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.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,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. */

View File

@@ -80,9 +80,11 @@ uint8_t readguyImage::drawImgHandler(int r, LGFX_Sprite *spr){
case 1: case 1:
spr->drawBmpFile(*baseFs,filename,_x,_y,0,0,offsetx+xd,offsety+yd,scalex,scaley,datum); spr->drawBmpFile(*baseFs,filename,_x,_y,0,0,offsetx+xd,offsety+yd,scalex,scaley,datum);
break; break;
#ifndef ESP8266
case 2: case 2:
spr->drawPngFile(*baseFs,filename,_x,_y,0,0,offsetx+xd,offsety+yd,scalex,scaley,datum); spr->drawPngFile(*baseFs,filename,_x,_y,0,0,offsetx+xd,offsety+yd,scalex,scaley,datum);
break; break;
#endif
case 3: case 3:
spr->drawJpgFile(*baseFs,filename,_x,_y,0,0,offsetx+xd,offsety+yd,scalex,scaley,datum); spr->drawJpgFile(*baseFs,filename,_x,_y,0,0,offsetx+xd,offsety+yd,scalex,scaley,datum);
break; break;
@@ -224,9 +226,9 @@ void readguyImage::drawImageFile(bool use16grey){
//Serial.printf("filename: %s, exname: %s\n",filename,ex); //Serial.printf("filename: %s, exname: %s\n",filename,ex);
//图片将会分割成8个部分, 分块绘制, 节省内存. //图片将会分割成8个部分, 分块绘制, 节省内存.
w=(guy->memWidth()+7)&0x7ffffff8; //guy->guyMemoryWidth() 返回不随旋转参数而改变的显示内存宽度 w=(guy->drvWidth()+7)&0x7ffffff8; //guy->guyMemoryWidth() 返回不随旋转参数而改变的显示内存宽度
if(!w) return; //保证宽度>0 if(!w) return; //保证宽度>0
h=guy->memHeight(); h=guy->drvHeight();
if(exPoolSize>guy->bufferLength()){ //当外部缓存的像素超过屏幕缓存时,使用外部缓存作为主缓冲区 if(exPoolSize>guy->bufferLength()){ //当外部缓存的像素超过屏幕缓存时,使用外部缓存作为主缓冲区
_h=exPoolSize/w; _h=exPoolSize/w;
_pool=exPool; _pool=exPool;
@@ -250,8 +252,10 @@ void readguyImage::drawImageFile(bool use16grey){
//随后打开图片进行解码. 可选显示的位置和宽度高度参数, 屏幕上的其他部分则会变成白色. //随后打开图片进行解码. 可选显示的位置和宽度高度参数, 屏幕上的其他部分则会变成白色.
if(strcmp(ex,"bmp") == 0 || strcmp(ex,"BMP") == 0) //BMP格式, 绘制BMP图片 if(strcmp(ex,"bmp") == 0 || strcmp(ex,"BMP") == 0) //BMP格式, 绘制BMP图片
format|=1; //BMP格式 format|=1; //BMP格式
#ifndef ESP8266
else if(strcmp(ex,"png") == 0 || strcmp(ex,"PNG") == 0) //PNG格式, 绘制PNG图片 else if(strcmp(ex,"png") == 0 || strcmp(ex,"PNG") == 0) //PNG格式, 绘制PNG图片
format|=2; //PNG格式 format|=2; //PNG格式
#endif
else if(strcmp(ex,"jpg") == 0 || strcmp(ex,"JPG") == 0 || strcmp(ex,"jpeg") == 0 || strcmp(ex,"JPEG") == 0) else if(strcmp(ex,"jpg") == 0 || strcmp(ex,"JPG") == 0 || strcmp(ex,"jpeg") == 0 || strcmp(ex,"JPEG") == 0)
format|=3; //JPG格式 format|=3; //JPG格式
else return; //未知格式 else return; //未知格式
@@ -291,8 +295,10 @@ uint8_t readguyImage::drawImageToBuffer(){
if(strcmp(ex,"bmp") == 0 || strcmp(ex,"BMP") == 0) //BMP格式, 绘制BMP图片 if(strcmp(ex,"bmp") == 0 || strcmp(ex,"BMP") == 0) //BMP格式, 绘制BMP图片
format|=1; //BMP格式 format|=1; //BMP格式
#ifndef ESP8266
else if(strcmp(ex,"png") == 0 || strcmp(ex,"PNG") == 0) //PNG格式, 绘制PNG图片 else if(strcmp(ex,"png") == 0 || strcmp(ex,"PNG") == 0) //PNG格式, 绘制PNG图片
format|=2; //PNG格式 format|=2; //PNG格式
#endif
else if(strcmp(ex,"jpg") == 0 || strcmp(ex,"JPG") == 0 || strcmp(ex,"jpeg") == 0 || strcmp(ex,"JPEG") == 0) else if(strcmp(ex,"jpg") == 0 || strcmp(ex,"JPG") == 0 || strcmp(ex,"jpeg") == 0 || strcmp(ex,"JPEG") == 0)
format|=3; //JPG格式 format|=3; //JPG格式
else return 2; //未知格式 else return 2; //未知格式
@@ -310,9 +316,11 @@ uint8_t readguyImage::drawImageToBuffer(){
case 1: case 1:
spr.drawBmpFile(*baseFs,filename,0,0,0,0,offsetx,offsety+_h*i,scalex,scaley); spr.drawBmpFile(*baseFs,filename,0,0,0,0,offsetx,offsety+_h*i,scalex,scaley);
break; break;
#ifndef ESP8266
case 2: case 2:
spr.drawPngFile(*baseFs,filename,0,0,0,0,offsetx,offsety+_h*i,scalex,scaley); spr.drawPngFile(*baseFs,filename,0,0,0,0,offsetx,offsety+_h*i,scalex,scaley);
break; break;
#endif
case 3: case 3:
spr.drawJpgFile(*baseFs,filename,0,0,0,0,offsetx,offsety+_h*i,scalex,scaley); spr.drawJpgFile(*baseFs,filename,0,0,0,0,offsetx,offsety+_h*i,scalex,scaley);
break; 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

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

View File

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

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_epdParam((iLut==1)?0x02:((iLut==3 || iLut==5)?0x05:0x08)); // 2us per line
guy_epdCmd(0x11); guy_epdCmd(0x11);
guy_epdParam(0x03); // X increment; Y increment 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); guy_epdCmd(0x32);
// * the length of look-up table is 30 bytes /
for (int i = 0; i < 30; i++) { for (int i = 0; i < 30; i++) {
if(iLut>0 && iLut<15 && i>19 && i<23){ if(iLut>0 && iLut<15 && i>19 && i<23){
guy_epdParam(pgm_read_byte(lut_grey_update+iLut*2+i-(i==20?22: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 else
guy_epdParam(pgm_read_byte(this->lut+i)); guy_epdParam(pgm_read_byte(this->lut+i));
} }
EndTransfer();
return 0;
} }
const PROGMEM unsigned char lut_slow[] = const PROGMEM unsigned char lut_slow[] =
@@ -99,14 +92,24 @@ const PROGMEM unsigned char lut_grey_update[]={ //从上到下是依次加深
0x12, 0x44, 0x13, 0x44 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); Init(lut_slow);
drv_color(0xffu); //睡眠模式下始终需要慢刷 drv_color(0xffu); //睡眠模式下始终需要慢刷
} }
void drvBase::drv_fullpart(bool part){ //切换慢刷/快刷功能 void drvBase::drv_fullpart(bool part){ //切换慢刷/快刷功能
if(!part) iLut=15; //恢复默认的灰度模式 if(!part) iLut=15; //恢复默认的灰度模式
Init(part?lut_fast:lut_slow); 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){ //单色刷新等功能
if(sleeping) Init(lut_slow); if(sleeping) Init(lut_slow);
BeginTransfer(); BeginTransfer();
@@ -136,8 +139,8 @@ void drvBase::drv_sleep() { //开始屏幕睡眠
EndTransfer(); EndTransfer();
guy_epdBusy(150); guy_epdBusy(150);
DigitalWrite(RST_PIN, LOW); DigitalWrite(RST_PIN, LOW);
sleeping=1;
} }
sleeping=1;
} }
void drvBase::drv_setDepth(uint8_t i){ void drvBase::drv_setDepth(uint8_t i){
iLut = i?(i>15?15:i):15; iLut = i?(i>15?15:i):15;

View File

@@ -57,9 +57,7 @@ private:
int Init(const unsigned char* lut); int Init(const unsigned char* lut);
const unsigned char* lut; const unsigned char* lut;
uint8_t iLut = 15; uint8_t iLut = 15;
uint8_t sleeping=1; uint8_t sleeping=0;
void SetLut(const unsigned char* lut);
}; };
#ifdef READGUY_DEV_154A #ifdef READGUY_DEV_154A

View File

@@ -53,7 +53,7 @@ namespace guydev_154B_270B_290B{
0x22,0x17,0x41,0xB0,0x32,0x28 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, 0x80, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0,
0x10, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 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,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, */
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 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]= const unsigned char drvSSD168x::VSH_table[32]=
{0x00,0x24,0x24,0x24,0x25,0x26,0x27,0x28,0x2a,0x2c,0x2e,0x32,0x35,0x39,0x3d,0x41, {/*0x00,0xbc,0xc1,0xc6,0xcb,0x23,0x26,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,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 原创, 呵呵哒~~ //以下代码均为我 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() { 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; unsigned char i;
guy_epdCmd(0x32); guy_epdCmd(0x32);
if(_part){ if(_part){
@@ -134,10 +119,10 @@ void drvSSD168x::SetLut() {
guy_epdParam(i==1?0x80:(i==(greyScaling?0:2)?0x40:0x00)); guy_epdParam(i==1?0x80:(i==(greyScaling?0:2)?0x40:0x00));
for(int j=0;j<11;j++) guy_epdParam(0); 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<83;i++) guy_epdParam(0);
for(i=0;i<6;i++) guy_epdParam(0x22); guy_epdParam(0x22);
for(i=0;i<3;i++) guy_epdParam(0); for(i=0;i<8;i++) guy_epdParam(0);
} }
else{ else{
//for(i=0; i<153; i++) guy_epdParam(lut[i]==0xff?iLut:lut[i]); //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<66;i++) guy_epdParam(0);
for(i=0;i<9;i++) guy_epdParam(pgm_read_byte(WS_20_30+i+33)); 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(){ void drvSSD168x::drv_init(){
_part=0; _part=0;
drv_color(0xffu); epd_PowerOn=0;
//drv_color(0xffu);
} }
void drvSSD168x::drv_fullpart(bool part){ //切换慢刷/快刷功能 void drvSSD168x::drv_fullpart(bool part){ //切换慢刷/快刷功能
if(!epd_PowerOn) part=0; //未上电 无法局刷
if(!part) { iLut=15; greyScaling=0; } if(!part) { iLut=15; greyScaling=0; }
_part=part; _part=part;
} }
void drvSSD168x::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新 void drvSSD168x::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
BeginTransfer(); BeginTransfer();
if(_part && epd_PowerOn){ if(_part){
//Reset(); //Reset();
SetLut(); SetLut();
guy_epdCmd(0x37); guy_epdCmd(0x37);
@@ -186,7 +163,26 @@ void drvSSD168x::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
guy_epdCmd(0x20); guy_epdCmd(0x20);
guy_epdBusy(140); 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(); SetMemory();
guy_epdCmd(0x24); guy_epdCmd(0x24);
for (int i = 0; i < epdHeight*epdWidth / 8; i++) for (int i = 0; i < epdHeight*epdWidth / 8; i++)
@@ -196,7 +192,9 @@ void drvSSD168x::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
for (int i = 0; i < epdHeight*epdWidth / 8; i++) for (int i = 0; i < epdHeight*epdWidth / 8; i++)
SpiTransfer(f(i)); SpiTransfer(f(i));
} }
DisplayFrame(); guy_epdCmd(0x22);
guy_epdParam(_part?0x0f:0xc7);
guy_epdCmd(0x20);
EndTransfer(); EndTransfer();
guy_epdBusy(_part?600:2300); guy_epdBusy(_part?600:2300);
} }
@@ -206,8 +204,8 @@ void drvSSD168x::drv_sleep() { //开始屏幕睡眠
guy_epdCmd(0x10); guy_epdCmd(0x10);
guy_epdParam(0x01); guy_epdParam(0x01);
EndTransfer(); EndTransfer();
epd_PowerOn=0;
} }
epd_PowerOn=0;
} }
void drvSSD168x::drv_setDepth(uint8_t i){ //设置显示颜色深度, 不支持的话什么都不做 void drvSSD168x::drv_setDepth(uint8_t i){ //设置显示颜色深度, 不支持的话什么都不做
if(i>0 && i<16) { if(i>0 && i<16) {

View File

@@ -52,16 +52,13 @@ protected:
int epdWidth; int epdWidth;
int epdHeight; int epdHeight;
private: private:
uint8_t _part; uint8_t _part=1;
uint8_t epd_PowerOn = 0; uint8_t epd_PowerOn = 1;
uint8_t iLut=15; uint8_t iLut=15;
uint8_t greyScaling=0; uint8_t greyScaling=0;
//static const unsigned char _WF_PARTIAL_2IN9[48] ; //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]; static const unsigned char VSH_table[32];
void epd_init();
void DisplayFrame(void);
void SetLut(); void SetLut();
}; };

View File

@@ -34,7 +34,7 @@ namespace guydev_213A{
const PROGMEM uint8_t drv::_ed_lut_full[] = { // command //慢刷lut 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, 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 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, 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(){ //初始化屏幕 void drv::drv_init(){ //初始化屏幕
epdFull = 1; epdFull = 1;
BeginTransfer(); epd_PowerOn = 0;
/*BeginTransfer();
epd_Init(); epd_Init();
EndTransfer(); EndTransfer();*/
drv_color(0xff); //drv_color(0xff);
} }
void drv::drv_fullpart(bool part){ //初始化慢刷功能 void drv::drv_fullpart(bool part){ //初始化慢刷功能
if(!epd_PowerOn) part=0; //未上电 无法局刷
//if(part==epdFull) return; //if(part==epdFull) return;
if(!part) iLut=15; //恢复默认的灰度模式 if(!part) iLut=15; //恢复默认的灰度模式
epdFull = !part; epdFull = !part;
@@ -144,21 +146,22 @@ void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
guy_epdBusy(epdFull?1600:310); guy_epdBusy(epdFull?1600:310);
} }
void drv::drv_sleep() { //开始屏幕睡眠 void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN<0) return; //无法唤醒 if(RST_PIN>=0){ //RST_PIN<0 无法唤醒
BeginTransfer(); BeginTransfer();
if(epd_PowerOn){ if(epd_PowerOn){
guy_epdCmd(0x22); guy_epdCmd(0x22);
guy_epdParam(0xc3); guy_epdParam(0xc3);
guy_epdCmd(0x20); guy_epdCmd(0x20);
guy_epdBusy(200); guy_epdBusy(200);
epd_PowerOn = 0; }
epdFull = 1; //强制设置为慢刷新模式 guy_epdCmd(0x10); //enter deep sleep
guy_epdParam(0x01);
EndTransfer();
DelayMs(200);
DigitalWrite(RST_PIN, LOW);
} }
guy_epdCmd(0x10); //enter deep sleep epd_PowerOn = 0;
guy_epdParam(0x01); epdFull = 1; //强制设置为慢刷新模式
EndTransfer();
DelayMs(200);
DigitalWrite(RST_PIN, LOW);
} }
} }
#endif /* END OF FILE. ReadGuy project. #endif /* END OF FILE. ReadGuy project.

View File

@@ -56,8 +56,8 @@ public:
int drv_supportGreyscaling() const { return 16; } int drv_supportGreyscaling() const { return 16; }
private: private:
void epd_Init(void); void epd_Init(void);
uint8_t epdFull; //是partical模式/快速刷新模式 0快刷, 1慢刷 uint8_t epdFull=0; //是partical模式/快速刷新模式 0快刷, 1慢刷
uint8_t epd_PowerOn = 0; //是否上电 uint8_t epd_PowerOn = 1; //是否上电
uint8_t iLut = 15; //颜色深度(灰度模式用) uint8_t iLut = 15; //颜色深度(灰度模式用)
// uint8_t dc_d = 0; //dc引脚状态 0 command, 1 data // uint8_t dc_d = 0; //dc引脚状态 0 command, 1 data
static const PROGMEM uint8_t _ed_lut_full[]; static const PROGMEM uint8_t _ed_lut_full[];

View File

@@ -76,13 +76,8 @@ drv_base::drv_base(){
guy_lutArray[4] = lutSlow_b_b; guy_lutArray[4] = lutSlow_b_b;
guy_lutArray[5] = lutFast_; guy_lutArray[5] = lutFast_;
} }
void drv_base::pre(){
guy_epdCmd(0x91);
send_zoneInfo();
guy_epdCmd(0x13);
}
void drv_base::epd_init(){ void drv_base::epd_init(){
if(!Power_is_on) Reset(); //if(!Power_is_on) Reset();
guy_epdCmd(0x01); guy_epdCmd(0x01);
guy_epdParam(0x03); guy_epdParam(0x03);
guy_epdParam(0x00); guy_epdParam(0x00);
@@ -110,11 +105,11 @@ void drv_base::epd_init(){
void drv_base::send_zoneInfo(){ void drv_base::send_zoneInfo(){
guy_epdCmd(0x90); guy_epdCmd(0x90);
guy_epdParam(0x00); guy_epdParam(0x00);
guy_epdParam(0x97); guy_epdParam(epdWidth-1);
guy_epdParam(0x00); guy_epdParam(0x00);
guy_epdParam(0x00); guy_epdParam(0x00);
guy_epdParam(0x01); guy_epdParam((epdHeight-1)>>8);
guy_epdParam(0x27); guy_epdParam((epdHeight-1)&0xff);
guy_epdParam(0x00); guy_epdParam(0x00);
} }
void drv_base::SendLuts(bool part_lut){ 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_epdCmd(0x04);
guy_epdBusy(-60); guy_epdBusy(-60);
Power_is_on = 1; Power_is_on = 1;
} }
} }
void drv_base::drv_init(){ void drv_base::drv_init(){
part_mode=0; part_mode = 0;
drv_color(0xff); Power_is_on = 0; //初始为未上电
//drv_color(0xff);
} }
void drv_base::drv_fullpart(bool part){ //切换慢刷/快刷功能 void drv_base::drv_fullpart(bool part){ //切换慢刷/快刷功能
if(!Power_is_on) part=0;
if(!part) greyLut=15; //恢复默认的灰度模式 if(!part) greyLut=15; //恢复默认的灰度模式
part_mode = part; part_mode = part;
} }
@@ -157,11 +154,11 @@ void drv_base::drv_setDepth(uint8_t i){
} }
void drv_base::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新 void drv_base::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
BeginTransfer(); BeginTransfer();
pre(); epd_init();
//send pixel data -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ SendLuts(part_mode);
//Total 5624 data written. guy_epdCmd(0x91);
//for(int i=0;i<GUY_D_WIDTH*GUY_D_HEIGHT/8;i++) send_zoneInfo();
// guy_epdParam(c); guy_epdCmd(0x13);
for (int i = 0; i < epdHeight*epdWidth/8; i++) for (int i = 0; i < epdHeight*epdWidth/8; i++)
SpiTransfer(f(i)); //按照给定的RAM写入数据 SpiTransfer(f(i)); //按照给定的RAM写入数据
@@ -169,58 +166,33 @@ void drv_base::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
if(part_mode){ if(part_mode){
guy_epdCmd(0x30); guy_epdCmd(0x30);
guy_epdParam(0x3a); //0x3a:100Hz, 0x29:150Hz guy_epdParam(0x3a); //0x3a:100Hz, 0x29:150Hz
//[EPDrg_BW<>] refresh fx
send_zoneInfo(); send_zoneInfo();
guy_epdCmd(0x12); guy_epdCmd(0x12);
EndTransfer(); EndTransfer();
//[EPDrg_EPD] wait_until_idle fx: 1300
guy_epdBusy(-200); 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{ else{
epd_init();
SendLuts(0);
guy_epdCmd(0x12); guy_epdCmd(0x12);
//[EPDrg_EPD] wait_until_idle fx: 1600
EndTransfer(); EndTransfer();
guy_epdBusy(-2000); guy_epdBusy(-2000);
//[EPDrg_BW<>] writeImageAgain fx
BeginTransfer(); BeginTransfer();
epd_init(); epd_init();
SendLuts(1); 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); guy_epdCmd(0x92);
EndTransfer(); EndTransfer();
//[EPDrg_BW<>] powerOff fx
//guy_epdCmd(0x02);
//[EPDrg_EPD] wait_until_idle fx: 20
//guy_epdBusy(-20);
} }
} }
void drv_base::drv_sleep() { //开始屏幕睡眠 void drv_base::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0) { //未定义RST_PIN时无法唤醒 //if(RST_PIN>=0) { //未定义RST_PIN时无法唤醒
part_mode = 0; BeginTransfer();
BeginTransfer(); guy_epdCmd(0x02); // power off
guy_epdCmd(0x02); // power off guy_epdBusy(-20);
guy_epdBusy(-20); guy_epdCmd(0X10);
guy_epdCmd(0X10); guy_epdParam(0x01);
guy_epdParam(0x01); EndTransfer();
EndTransfer(); //}
Power_is_on = 0; part_mode = 0;
} Power_is_on = 0;
} }
void drv_base::drv_draw16grey_step(std::function<uint8_t(int)> f, int step){ void drv_base::drv_draw16grey_step(std::function<uint8_t(int)> f, int step){

View File

@@ -50,12 +50,11 @@ protected:
int epdWidth; int epdWidth;
int epdHeight; int epdHeight;
private: private:
void pre();
void epd_init(); void epd_init();
void send_zoneInfo(); void send_zoneInfo();
void SendLuts(bool part_lut); void SendLuts(bool part_lut);
uint8_t part_mode = 0; uint8_t part_mode = 1;
uint8_t Power_is_on = 0; //初始为未上电 uint8_t Power_is_on = 2; //初始为未上电
uint8_t greyLut=15; uint8_t greyLut=15;
uint8_t greyHQ=4; // greyHQ==3 时 为高品质刷新灰度 否则为常规方式刷新灰度 uint8_t greyHQ=4; // greyHQ==3 时 为高品质刷新灰度 否则为常规方式刷新灰度
const uint8_t *guy_lutArray[6]; const uint8_t *guy_lutArray[6];

View File

@@ -103,10 +103,14 @@ void drv::Load_LUT(unsigned char mode) {
} }
void drv::drv_init(){ //初始化屏幕 void drv::drv_init(){ //初始化屏幕
sleeping = 1;
Init(); Init();
drv_color(0xff); sleeping = 1;
part_mode=0;
//drv_color(0xff);
} }
void drv::drv_fullpart(bool part){ //切换慢刷/快刷功能 void drv::drv_fullpart(bool part){ //切换慢刷/快刷功能
if(sleeping) return;
if(!part) { if(!part) {
greyScaling=15; //恢复默认的灰度模式 greyScaling=15; //恢复默认的灰度模式
BeginTransfer(); BeginTransfer();
@@ -142,8 +146,9 @@ void drv::drv_sleep() { //开始屏幕睡眠
guy_epdCmd(0X10); //deep sleep guy_epdCmd(0X10); //deep sleep
guy_epdParam(0x03); guy_epdParam(0x03);
EndTransfer(); EndTransfer();
sleeping = true;
} }
sleeping = true;
part_mode=0;
} }
void drv::drv_setDepth(uint8_t i){ //设置显示颜色深度, 不支持的话什么都不做 void drv::drv_setDepth(uint8_t i){ //设置显示颜色深度, 不支持的话什么都不做

View File

@@ -47,9 +47,9 @@ public:
int drv_supportGreyscaling() const { return 16; } int drv_supportGreyscaling() const { return 16; }
void drv_setDepth(uint8_t i); //设置显示颜色深度, 不支持的话什么都不做 void drv_setDepth(uint8_t i); //设置显示颜色深度, 不支持的话什么都不做
private: private:
uint8_t part_mode; uint8_t part_mode=1;
uint8_t greyScaling = 15; uint8_t greyScaling = 15;
uint8_t sleeping = true; uint8_t sleeping = false;
int Init(void); int Init(void);
void Load_LUT(unsigned char mode); void Load_LUT(unsigned char mode);
}; };

View File

@@ -159,16 +159,6 @@ void drv::epd_Init(void){
guy_epdCmd(0x11); //Data Entry mode guy_epdCmd(0x11); //Data Entry mode
guy_epdParam(0x03); //0x03 or 0x01 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(){ void drv::power_down(){
Power_is_on=0; Power_is_on=0;
BeginTransfer(); BeginTransfer();
@@ -187,18 +177,25 @@ void drv::SetLut(const unsigned char* lut){
} }
} }
void drv::drv_init(){ //初始化屏幕 void drv::drv_init(){ //初始化屏幕
epdFull = 2; Power_is_on = 0; //初始为未上电
drv_color(0xff); epdFull = 2; //初始设为正在休眠
//drv_color(0xff);
} }
void drv::drv_fullpart(bool part){ //初始化慢刷功能 void drv::drv_fullpart(bool part){ //初始化慢刷功能
if(!part) GreyScaling=0;
if(epdFull<=1) epdFull = !part; //epdFull==2代表睡眠中, 不能快刷 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){ //单色刷新
BeginTransfer(); BeginTransfer();
epd_Init(); epd_Init();
SetMemory(); 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(); SetMemory();
if(epdFull){ //慢刷 if(epdFull){ //慢刷
guy_epdCmd(0x26); guy_epdCmd(0x26);
@@ -260,13 +257,14 @@ void drv::drv_draw16grey_step(std::function<uint8_t(int)> f, int step){
}*/ }*/
void drv::drv_sleep() { //开始屏幕睡眠 void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0) { //未定义RST_PIN时无法唤醒 if(RST_PIN>=0) { //未定义RST_PIN时无法唤醒
epdFull=2; //睡眠
power_down(); power_down();
BeginTransfer(); BeginTransfer();
guy_epdCmd(0x10); // deep sleep mode guy_epdCmd(0x10); // deep sleep mode
guy_epdParam(0x01); // enter deep sleep guy_epdParam(0x01); // enter deep sleep
EndTransfer(); EndTransfer();
} }
epdFull=2; //睡眠
Power_is_on=0;
} }
} }
#endif /* END OF FILE. ReadGuy project. #endif /* END OF FILE. ReadGuy project.

View File

@@ -53,14 +53,13 @@ private:
void epd_Init(void); void epd_Init(void);
void SetLut(const unsigned char* lut); void SetLut(const unsigned char* lut);
uint8_t Power_is_on = 0; //初始为未上电 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 GreyScaling = 0; //是否正在灰度显示
uint8_t GreyScalingHighQuality = 0; //是否正在16阶高品质灰度显示 uint8_t GreyScalingHighQuality = 0; //是否正在16阶高品质灰度显示
static const PROGMEM unsigned char epd42_lut_full[]; static const PROGMEM unsigned char epd42_lut_full[];
static const PROGMEM unsigned char lut_213_B72[]; 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_Full[];
static const PROGMEM unsigned char lut_213_B72_16grey[]; static const PROGMEM unsigned char lut_213_B72_16grey[];
void power_up();
void power_down(); void power_down();
//std::function<void(int)> send; //此处不能用 void (*send)(int); 是因为lambda函数是std的 //std::function<void(int)> send; //此处不能用 void (*send)(int); 是因为lambda函数是std的
}; };

View File

@@ -49,35 +49,55 @@ void drv::Init(uint8_t pt) {
Reset(); Reset();
guy_epdCmd(0x00); //Software reset guy_epdCmd(0x00); //Software reset
guy_epdParam(0x00); guy_epdParam(0x00);
delayMicroseconds(40);
guy_epdCmd(0x00); guy_epdCmd(0x00);
guy_epdParam(0x1f); guy_epdParam(0x1f);
guy_epdParam(0x0d); guy_epdParam(0x0d);
delayMicroseconds(40);
guy_epdCmd(0x50); guy_epdCmd(0x50);
guy_epdParam(0x97); guy_epdParam(0x97);
delayMicroseconds(40);
guy_epdCmd(0x01); guy_epdCmd(0x01);
guy_epdParam(0x03); guy_epdParam(0x03);
guy_epdParam(0x00); guy_epdParam(0x00);
guy_epdParam(0x2b); guy_epdParam(0x2b);
guy_epdParam(0x2b); guy_epdParam(0x2b);
delayMicroseconds(40);
guy_epdCmd(0x06); guy_epdCmd(0x06);
guy_epdParam(0x17); guy_epdParam(0x17);
guy_epdParam(0x17); guy_epdParam(0x17);
guy_epdParam(0x17); guy_epdParam(0x17);
delayMicroseconds(40);
guy_epdCmd(0x00); guy_epdCmd(0x00);
guy_epdParam(0x3f); guy_epdParam(0x3f);
delayMicroseconds(40);
guy_epdCmd(0x30); guy_epdCmd(0x30);
guy_epdParam(0x3a); guy_epdParam(0x3a);
delayMicroseconds(40);
guy_epdCmd(0x61); guy_epdCmd(0x61);
guy_epdParam(0x01); guy_epdParam(0x01);
guy_epdParam(0x90); guy_epdParam(0x90);
guy_epdParam(0x01); guy_epdParam(0x01);
guy_epdParam(0x2c); guy_epdParam(0x2c);
delayMicroseconds(40);
guy_epdCmd(0x82); //vcom setting: 0x00:-0.1V, 0x3a: -3.0V guy_epdCmd(0x82); //vcom setting: 0x00:-0.1V, 0x3a: -3.0V
guy_epdParam(0x1a); guy_epdParam(0x1a);
delayMicroseconds(40);
guy_epdCmd(0x50); guy_epdCmd(0x50);
guy_epdParam(0xd7); 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); if(pt!=2) guy_epdCmd(0x04);
delayMicroseconds(40);
} }
void drv::sendArea(){ void drv::sendArea(){
@@ -96,18 +116,6 @@ void drv::sendAreaRaw(){
guy_epdParam(0x2b); guy_epdParam(0x2b);
guy_epdParam(0x01); 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[] ={ const PROGMEM unsigned char drv::lut_vcom0[] ={
0x00, 0x08, 0x08, 0x00, 0x00, 0x02, 0x00, 0x08, 0x08, 0x00, 0x00, 0x02,
0x00, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x01,
@@ -153,6 +161,7 @@ void drv::drv_dispWriter(std::function<uint8_t(int)> f){ //单色刷新
//[EPDrg_BW<>] refresh fx //[EPDrg_BW<>] refresh fx
} }
else{ else{
Power_is_on=1;
guy_epdCmd(0x10); guy_epdCmd(0x10);
//刷新数据 //刷新数据
for(int i=0;i<GUY_D_WIDTH*GUY_D_HEIGHT/8;i++) for(int i=0;i<GUY_D_WIDTH*GUY_D_HEIGHT/8;i++)
@@ -198,15 +207,20 @@ const PROGMEM uint8_t drv::greyTable[16]={
32,23,18,15,12,10,9,8, 32,23,18,15,12,10,9,8,
7,6,5,5,5,5,5,0 7,6,5,5,5,5,5,0
}; };
void drv::drv_init(){ void drv::drv_init(){
//Init(0); part_mode=0;
drv_color(0xffu); Power_is_on=0;
BeginTransfer();
Init(0);
EndTransfer();
//drv_color(0xffu);
} }
void drv::drv_fullpart(bool part){ //切换慢刷/快刷功能 void drv::drv_fullpart(bool part){ //切换慢刷/快刷功能
if(!part) customLut = CUSTOM_LUT_DISABLE; if(Power_is_on) {
part_mode = part; if(!part) customLut = CUSTOM_LUT_DISABLE;
//Init(part); part_mode = part;
//Init(part);
}
} }
void drv::drv_sleep() { //开始屏幕睡眠 void drv::drv_sleep() { //开始屏幕睡眠
if(RST_PIN>=0){ //未定义RST_PIN时无法唤醒 if(RST_PIN>=0){ //未定义RST_PIN时无法唤醒
@@ -219,6 +233,8 @@ void drv::drv_sleep() { //开始屏幕睡眠
guy_epdParam(0xA5); guy_epdParam(0xA5);
EndTransfer(); EndTransfer();
} }
Power_is_on=0;
part_mode=0;
} }
} }
#endif /* END OF FILE. ReadGuy project. #endif /* END OF FILE. ReadGuy project.

View File

@@ -56,8 +56,8 @@ public:
int drv_supportGreyscaling() const { return 16; } int drv_supportGreyscaling() const { return 16; }
private: private:
uint8_t part_mode = 0; uint8_t part_mode = 1;
//uint8_t Power_is_on = 0; //初始为未上电 uint8_t Power_is_on = 1; //初始为未上电
static const PROGMEM unsigned char lut_vcom0[]; static const PROGMEM unsigned char lut_vcom0[];
static const PROGMEM unsigned char lut_ww[]; static const PROGMEM unsigned char lut_ww[];
@@ -76,8 +76,6 @@ private:
void Init(uint8_t pt); void Init(uint8_t pt);
void sendArea(); void sendArea();
void sendAreaRaw(); void sendAreaRaw();
void SendLuts(uint8_t lutOption); //0:慢刷, 1:快刷, 2:四阶灰度
uint8_t customLut, customGreyscale; //customLut 是灰度刷新时的自定义lut uint8_t customLut, customGreyscale; //customLut 是灰度刷新时的自定义lut
static const PROGMEM uint8_t greyTable[16]; static const PROGMEM uint8_t greyTable[16];
}; };

View File

@@ -70,9 +70,11 @@ int readguyEpdBase::IfInit(SPIClass &c,int8_t cs,int8_t dc,int8_t rst,int8_t bus
RST_PIN = rst; RST_PIN = rst;
BUSY_PIN = busy; BUSY_PIN = busy;
if(CS_PIN>=0) pinMode(CS_PIN , OUTPUT); if(CS_PIN>=0) pinMode(CS_PIN , OUTPUT);
DigitalWrite(CS_PIN,HIGH);
if(RST_PIN>=0) pinMode(RST_PIN , OUTPUT); if(RST_PIN>=0) pinMode(RST_PIN , OUTPUT);
DigitalWrite(RST_PIN,HIGH); DigitalWrite(RST_PIN,HIGH);
if(DC_PIN>=0) pinMode(DC_PIN , OUTPUT); if(DC_PIN>=0) pinMode(DC_PIN , OUTPUT);
DigitalWrite(DC_PIN,HIGH);
if(BUSY_PIN>=0) pinMode(BUSY_PIN, INPUT); if(BUSY_PIN>=0) pinMode(BUSY_PIN, INPUT);
_spi = &c; _spi = &c;
@@ -157,7 +159,8 @@ void readguyEpdBase::Reset(uint32_t minTime)
} }
//void readguyEpdBase::drv_draw16grey(const uint8_t *d16bit){ //不支持的话什么都不做 //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 #ifndef FLOYD_STEINBERG_DITHERING
static const uint8_t bayer_tab [64]={ static const uint8_t bayer_tab [64]={
0, 32, 8, 40, 2, 34, 10, 42, 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 63, 31, 55, 23, 61, 29, 53, 21
}; };
#endif #endif
const uint32_t w = (spr.width()+7)>>3; if(!fw) fw=spr.width();
if((!w) || (!spr.height())) return; if(!fh) fh=spr.height();
if((!fw) || (!fh)) return;
if(o==0 || o==1){ if(o==0 || o==1){
readBuff = new uint16_t[spr.width()]; readBuff = new uint16_t[spr.width()];
#ifdef FLOYD_STEINBERG_DITHERING #ifdef FLOYD_STEINBERG_DITHERING
floyd_tab[0] = new int16_t [spr.width()]; floyd_tab[0] = new int16_t [fw];
floyd_tab[1] = new int16_t [spr.width()]; floyd_tab[1] = new int16_t [fw];
for(int j=0;j<spr.width();j++){ floyd_tab[0][j] = 0; floyd_tab[1][j] = 0; } for(int j=0;j<fw;j++){ floyd_tab[0][j] = 0; floyd_tab[1][j] = 0; }
#endif #endif
writeBuff = new uint8_t[w]; writeBuff = new uint8_t[(fw+7)>>3];
} }
sprbase.fillRect(x,y,spr.width(),spr.height(),1); sprbase.fillRect(x,y,fw,fh,1);
for(int32_t i=y;i<(int32_t)spr.height()+y;i++){ for(int32_t i=y;i<(int32_t)fh+y;i++){
spr.readRect(0,i-y,spr.width(),1,readBuff); spr.readRect(0,(i-y)*spr.height()/fh,spr.width(),1,readBuff);
#ifdef FLOYD_STEINBERG_DITHERING #ifdef FLOYD_STEINBERG_DITHERING
uint_fast8_t buff8bit=0; uint_fast8_t buff8bit=0;
for(int32_t j=0;j<spr.width();j++){ for(int32_t j=0;j<fw;j++){
int32_t flodelta = floyd_tab[i&1][j]+(int32_t)((greysc(readBuff[j])<<8)|greysc(readBuff[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) { if(flodelta>=0x8000) {
//spr.drawPixel(j,i,1); //spr.drawPixel(j,i,1);
buff8bit |= 1<<((~j)&7); buff8bit |= 1<<((~j)&7);
flodelta -= 0xffff; 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; writeBuff[j>>3]=buff8bit;
buff8bit=0; 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; } if(j) { floyd_tab[!(i&1)][j-1] += (flodelta*3)>>4; }
{ floyd_tab[!(i&1)][j ] += (flodelta*5)>>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; } { floyd_tab[i&1] [j+1] += (flodelta*7)>>4; }
if(j) { floyd_tab[!(i&1)][j-1] += (flodelta*3)>>4; } if(j) { floyd_tab[!(i&1)][j-1] += (flodelta*3)>>4; }
{ floyd_tab[!(i&1)][j ] += (flodelta*5)>>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; } { floyd_tab[!(i&1)][j+1] += (flodelta )>>4; }
} }
for(int floi=0;floi<spr.width();floi++) floyd_tab[i&1][floi]=0; for(int floi=0;floi<fw;floi++) floyd_tab[i&1][floi]=0;
#else #else
for(int32_t j=0;j<w;j++){ for(int32_t j=0;j<w;j++){
uint_fast8_t buff8bit=0; uint_fast8_t buff8bit=0;
for(uint_fast8_t b=0;b<8;b++) for(uint_fast8_t b=0;b<8;b++)
buff8bit |= (bayer_tab[(b<<3)|(i&7)]<(greysc(readBuff[(j<<3)+b])>>2))<<(7-b); buff8bit |= (bayer_tab[(b<<3)|(i&7)]<(greysc(readBuff[j*spr.width()/fw])>>2))<<(7-b);
writeBuff[j]=buff8bit; writeBuff[j]=buff8bit;
} }
#endif #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()); //显示 //_display((const uint8_t*)sprbase.getBuffer()); //显示
if(o==0 || o==3){ 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"); //Serial.println("drv_draw16grey fx");
const uint32_t w = (spr.width()+7)>>3; if(!fw) fw=spr.width();
if((!w) || (!spr.height())) return; if(!fh) fh=spr.height();
if((!fw) || (!fh)) return;
readBuff = new uint16_t[spr.width()]; readBuff = new uint16_t[spr.width()];
if(_quality&2){ if(_quality&2){
#ifdef FLOYD_DITHERING_16GREY #ifdef FLOYD_DITHERING_16GREY
floyd_tab[0] = new int16_t [spr.width()]; floyd_tab[0] = new int16_t [fw];
floyd_tab[1] = new int16_t [spr.width()]; floyd_tab[1] = new int16_t [fw];
#endif #endif
} }
writeBuff = new uint8_t[w]; writeBuff = new uint8_t[(fw+7)>>3];
sprbase.fillRect(x,y,spr.width(),spr.height(),1); sprbase.fillRect(x,y,fw,fh,1);
bool negativeOrder=(drv_supportGreyscaling()==-16); bool negativeOrder=(drv_supportGreyscaling()==-16);
drv_fullpart(0); drv_fullpart(0);
//_display((const uint8_t*)sprbase.getBuffer()); //先对区域慢刷白屏确保颜色正确 //_display((const uint8_t*)sprbase.getBuffer()); //先对区域慢刷白屏确保颜色正确
@@ -254,33 +261,31 @@ void readguyEpdBase::drv_draw16grey(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16
drv_fullpart(1); drv_fullpart(1);
for(uint_fast8_t k=1;k<16;k++){ //亮度为15的不用绘制,因为本来就是白色 for(uint_fast8_t k=1;k<16;k++){ //亮度为15的不用绘制,因为本来就是白色
#ifdef FLOYD_DITHERING_16GREY #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; } if(_quality&2) for(int j=0;j<fw;j++){ floyd_tab[0][j] = 0; floyd_tab[1][j] = 0; }
#endif #endif
for(int i=y;i<spr.height()+y;i++){ for(int i=y;i<(int32_t)fh+y;i++){
uint_fast8_t buff8bit=0; uint_fast8_t buff8bit=0;
spr.readRect(0,i-y,spr.width(),1,readBuff); spr.readRect(0,(i-y)*spr.height()/fh,spr.width(),1,readBuff);
for(int32_t j=0;j<spr.width();j++){ for(int32_t j=0;j<fw;j++){
//for(uint_fast8_t b=0;b<8;b++){ //for(uint_fast8_t b=0;b<8;b++){
#ifdef FLOYD_DITHERING_16GREY #ifdef FLOYD_DITHERING_16GREY
uint_fast8_t cg=0; uint_fast8_t cg=0;
if(_quality&2){ 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); int32_t fd = floyd_tab[i&1][j]+((gv<<8)|gv);
while(fd>=0x800) { while(fd>=0x800) {
cg++; cg++;
if(fd>=0) fd -= 0x1000; if(fd>=0) fd -= 0x1000;
} }
if(fd<0) fd++; if(fd<0) fd++;
if(j!=spr.width()-1) if(j!=fw-1) { floyd_tab[i&1] [j+1] += (fd*7)>>4; }
{ floyd_tab[i&1] [j+1] += (fd*7)>>4; } if(j) { floyd_tab[!(i&1)][j-1] += (fd*3)>>4; }
if(j) { floyd_tab[!(i&1)][j-1] += (fd*3)>>4; } { floyd_tab[!(i&1)][j ] += (fd*5)>>4; }
{ floyd_tab[!(i&1)][j ] += (fd*5)>>4; } if(j!=fw-1) { floyd_tab[!(i&1)][j+1] += (fd )>>4; }
if(j!=spr.width()-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 #else
uint_fast8_t cg=greysc(readBuff[j])>>4; uint_fast8_t cg=greysc(readBuff[j*spr.width()/fw])>>4;
#endif #endif
if(negativeOrder) if(negativeOrder)
buff8bit |= (cg<k)<<((~j)&7); buff8bit |= (cg<k)<<((~j)&7);
@@ -288,17 +293,17 @@ void readguyEpdBase::drv_draw16grey(LGFX_Sprite &sprbase,LGFX_Sprite &spr,uint16
if(cg<15) //白色不考虑 if(cg<15) //白色不考虑
buff8bit |= (cg>=((~k)&15))<<((~j)&7); 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; writeBuff[j>>3]=buff8bit^0xff;
buff8bit=0; 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 #ifdef FLOYD_DITHERING_16GREY
if(_quality&2) for(int floi=0;floi<spr.width();floi++) floyd_tab[i&1][floi]=0; if(_quality&2) for(int floi=0;floi<fw;floi++) floyd_tab[i&1][floi]=0;
#endif #endif
sprbase.drawBitmap(x,i,writeBuff,spr.width(),1,1,0); sprbase.drawBitmap(x,i,writeBuff,fw,1,1,0);
} }
drv_draw16grey_step((const uint8_t*)sprbase.getBuffer(),k); //使用灰度显示函数 drv_draw16grey_step((const uint8_t*)sprbase.getBuffer(),k); //使用灰度显示函数
} }

View File

@@ -97,8 +97,10 @@ public:
* @return uint32_t 颜色的灰度值 * @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;} 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_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);//省内存方式 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){ //分步完成灰度刷新 void drv_draw16grey_step(const uint8_t *buf, int step){ //分步完成灰度刷新
drv_draw16grey_step([&](int n)->uint8_t{return buf[n];},step); drv_draw16grey_step([&](int n)->uint8_t{return buf[n];},step);
} }
@@ -113,6 +115,8 @@ public:
#ifdef MEPD_DEBUG_DISPLAY #ifdef MEPD_DEBUG_DISPLAY
friend class LGFX; friend class LGFX;
#endif #endif
private:
int16_t guy_width=0,guy_height=0;
}; };
#endif /* END OF FILE. ReadGuy project. #endif /* END OF FILE. ReadGuy project.

View File

@@ -30,11 +30,20 @@
#ifndef _READGUY_VERSION_H_FILE #ifndef _READGUY_VERSION_H_FILE
#define _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_MAJOR 1
#define READGUY_V_MINOR 1 #define READGUY_V_MINOR 3
#define READGUY_V_PATCH 1 #define READGUY_V_PATCH 0
#define READGUY_VERSION_VAL (READGUY_V_MAJOR*1000+READGUY_V_MINOR*100+READGUY_V_PATCH*10) #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 #ifdef ESP8266
#define _READGUY_PLATFORM "ESP8266" #define _READGUY_PLATFORM "ESP8266"

View File

@@ -126,7 +126,7 @@ void ReadguyDriver::ap_setup(){
IPAddress subnet(255,255,255,0); IPAddress subnet(255,255,255,0);
WiFi.softAPConfig(local_IP, gateway, subnet); WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.softAP("readguy","12345678"); 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){ void ReadguyDriver::server_setup(const String &notify, const serveFunc *serveFuncs, int funcs){
//启动WiFi服务器端, 这样就可以进行配网工作 //启动WiFi服务器端, 这样就可以进行配网工作
@@ -163,7 +163,7 @@ void ReadguyDriver::server_setup(const String &notify, const serveFunc *serveFun
sv.begin(); sv.begin();
MDNS.begin("readguy"); MDNS.begin("readguy");
//MDNS.addService("http","tcp",80); //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")); if(WiFi.getMode() == WIFI_AP) Serial.println(F("192.168.4.1"));
else Serial.println(WiFi.localIP()); else Serial.println(WiFi.localIP());
} }
@@ -218,7 +218,7 @@ void ReadguyDriver::handleInitPost(){
// 此时返回一个文本输入框, 定位到 handleFinalPost 函数 // 此时返回一个文本输入框, 定位到 handleFinalPost 函数
uint8_t btn_count_=0; uint8_t btn_count_=0;
if(READGUY_cali){ //再次初始化已经初始化的东西, 此时需要关闭一些外设什么的 if(READGUY_cali){ //再次初始化已经初始化的东西, 此时需要关闭一些外设什么的
Serial.println(F("Reconfig pins and hardwares...")); Serial.println(F("[Guy Pin] Reconfig pins and hardwares..."));
READGUY_cali=0; READGUY_cali=0;
READGUY_sd_ok=0; READGUY_sd_ok=0;
#if defined(ESP8266) #if defined(ESP8266)
@@ -280,7 +280,7 @@ void ReadguyDriver::handleInitPost(){
uint8_t ck=checkEpdDriver(); uint8_t ck=checkEpdDriver();
if(btn_count_<2) config_data[16]=0; if(btn_count_<2) config_data[16]=0;
if(btn_count_<3) config_data[17]=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) { if(ck>=125) {
const char *pNotify[3]={ PSTR("Necessary pin NOT connected."),\ const char *pNotify[3]={ PSTR("Necessary pin NOT connected."),\
PSTR("Pin conflicted."),PSTR("Display not supported.") }; 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[1] = 48+((rdm>>12)%10);//R2CHAR((rdm>>12)%10);
randomch[2] = 48+((rdm>> 6)%10);//R2CHAR((rdm>> 6)%10); randomch[2] = 48+((rdm>> 6)%10);//R2CHAR((rdm>> 6)%10);
randomch[3] = 48+((rdm )%10);//R2CHAR((rdm )%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]); for(int i=0;i<4;i++) Serial.write(randomch[i]);
Serial.write('\n'); Serial.write('\n');
Serial.println(F("Init EPD...")); //此时引脚io数据已经录入, 如果没有问题, 此处屏幕应当可以显示 Serial.println(F("[Guy] Init EPD...")); //此时引脚io数据已经录入, 如果没有问题, 此处屏幕应当可以显示
setEpdDriver(); //尝试初始化屏幕 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); drawCenterString(setSDcardDriver()?"SD Init OK!":"SD Init failed!",width()>>1,(height()>>1)+20);
setButtonDriver(); //初始化按钮.. setButtonDriver(); //初始化按钮..
//} //尝试初始化按键, 调用后, 若SD卡初始化成功, READGUY_sd_ok的值会变成1 //} //尝试初始化按键, 调用后, 若SD卡初始化成功, READGUY_sd_ok的值会变成1
@@ -321,7 +322,7 @@ void ReadguyDriver::handleInitPost(){
guy_dev->drv_fullpart(1); guy_dev->drv_fullpart(1);
guy_dev->_display((const uint8_t*)getBuffer()); guy_dev->_display((const uint8_t*)getBuffer());
spibz--; spibz--;
Serial.println(F("Display done!")); Serial.println(F("[Guy] Display done!"));
READGUY_cali=1; //显示初始化完成 READGUY_cali=1; //显示初始化完成
} }
void ReadguyDriver::handlePinSetup(){ void ReadguyDriver::handlePinSetup(){
@@ -592,7 +593,7 @@ void ReadguyDriver::handleFinal(){
//s+=F("<br/>"); //换行 //s+=F("<br/>"); //换行
sv.send_P(200, TEXT_HTML, (s+FPSTR(end_html)).c_str()); sv.send_P(200, TEXT_HTML, (s+FPSTR(end_html)).c_str());
if(READGUY_cali == 63){ if(READGUY_cali == 63){
Serial.println(F("Data saved to NVS.")); Serial.println(F("[Guy NVS] Data saved to NVS."));
READGUY_cali = 127; READGUY_cali = 127;
nvs_init(); nvs_init();
nvs_write(); nvs_write();

View File

@@ -48,8 +48,8 @@ ReadguyDriver::ReadguyDriver(){
READGUY_cali = 0; // config_data[0] 的初始值为0 READGUY_cali = 0; // config_data[0] 的初始值为0
READGUY_sd_ok = 0; //初始默认SD卡未成功初始化 READGUY_sd_ok = 0; //初始默认SD卡未成功初始化
READGUY_buttons = 0; //初始情况下没有按钮 READGUY_buttons = 0; //初始情况下没有按钮
} } //WiFiSet: 是否保持AP服务器一直处于打开状态
uint8_t ReadguyDriver::init(uint8_t WiFiSet){ //WiFiSet: 是否保持AP服务器一直处于打开状态 uint8_t ReadguyDriver::init(uint8_t WiFiSet,bool initepd/* ,int g_width,int g_height */){
if(READGUY_cali==127) //已经初始化过了一次了, 为了防止里面一些volatile的东西出现问题....还是退出吧 if(READGUY_cali==127) //已经初始化过了一次了, 为了防止里面一些volatile的东西出现问题....还是退出吧
return 0; return 0;
#ifdef DYNAMIC_PIN_SETTINGS #ifdef DYNAMIC_PIN_SETTINGS
@@ -77,7 +77,7 @@ uint8_t ReadguyDriver::init(uint8_t WiFiSet){ //WiFiSet: 是否保持AP服务器
else{ //看来NVS有数据, //从NVS加载数据, 哪怕前面的数据刚刚写入, 还没读取 else{ //看来NVS有数据, //从NVS加载数据, 哪怕前面的数据刚刚写入, 还没读取
if(WiFiSet>=2) WiFi.begin(); //连接到上次存储在flash NVS中的WiFi. if(WiFiSet>=2) WiFi.begin(); //连接到上次存储在flash NVS中的WiFi.
else if(WiFiSet==1) ap_setup(); else if(WiFiSet==1) ap_setup();
if(checkEpdDriver()!=127) setEpdDriver(); //初始化屏幕 if(checkEpdDriver()!=127) setEpdDriver(initepd/* ,g_width,g_height */); //初始化屏幕
else for(;;); //此处可能添加程序rollback等功能操作(比如返回加载上一个程序) else for(;;); //此处可能添加程序rollback等功能操作(比如返回加载上一个程序)
setSDcardDriver(); setSDcardDriver();
setButtonDriver(); setButtonDriver();
@@ -85,12 +85,12 @@ uint8_t ReadguyDriver::init(uint8_t WiFiSet){ //WiFiSet: 是否保持AP服务器
#endif #endif
nvs_deinit(); nvs_deinit();
#else #else
if(checkEpdDriver()!=127) setEpdDriver(); //初始化屏幕 if(checkEpdDriver()!=127) setEpdDriver(initepd/* ,g_width,g_height */); //初始化屏幕
else for(;;); //此处可能添加程序rollback等功能操作(比如返回加载上一个程序) else for(;;); //此处可能添加程序rollback等功能操作(比如返回加载上一个程序)
setSDcardDriver(); setSDcardDriver();
setButtonDriver(); setButtonDriver();
#endif #endif
Serial.println(F("init done.")); Serial.println(F("[Guy init] init done."));
READGUY_cali=127; READGUY_cali=127;
return READGUY_sd_ok; return READGUY_sd_ok;
} }
@@ -101,7 +101,7 @@ uint8_t ReadguyDriver::checkEpdDriver(){
#else #else
#define TEST_ONLY_VALUE 3 #define TEST_ONLY_VALUE 3
#endif #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++){ for(int i=TEST_ONLY_VALUE;i<8;i++){
if(i<7 && config_data[i]<0) return 125;//必要的引脚没连接 if(i<7 && config_data[i]<0) return 125;//必要的引脚没连接
for(int j=1;j<=8-i;j++) 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; case READGUY_DEV_270B: guy_dev = new guydev_154B_270B_290B ::dev270B; break;
#endif #endif
default: default:
Serial.println(F("[ERR] EPD DRIVER IC NOT SUPPORTED!\n")); Serial.println(F("[GUY ERR] EPD DRIVER IC NOT SUPPORTED!\n"));
return 127; return 127;
} }
#if (defined(ESP8266)) #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); 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); guy_dev->IfInit(*epd_spi, READGUY_epd_cs, READGUY_epd_dc, READGUY_epd_rst, READGUY_epd_busy);
#endif #endif
Serial.println(F("IfInit OK")); Serial.println(F("[Guy SPI] drvBase Init OK"));
return READGUY_epd_type; 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_release = in_release;
guy_dev->spi_tr_press = in_press; guy_dev->spi_tr_press = in_press;
guy_dev->drv_init(); //初始化epd驱动层 if(initepd) guy_dev->drv_init(); //初始化epd驱动层
if(g_width) guy_width = g_width; //if(g_width) guy_width = g_width;
else guy_width = guy_dev->drv_width(); //宽度必须是8的倍数, 但这个可以由GFX自动计算 //else guy_width = guy_dev->drv_width(); //宽度必须是8的倍数, 但这个可以由GFX自动计算
if(g_height) guy_height = g_height; //if(g_height) guy_height = g_height;
else guy_height = guy_dev->drv_height(); //else guy_height = guy_dev->drv_height();
Serial.println(F("EPD init OK")); Serial.println(F("[Guy EPD] EPD init OK"));
//以下依赖于你的图形驱动 //以下依赖于你的图形驱动
setColorDepth(1); //单色模式 setColorDepth(1); //单色模式
createPalette(); //初始化颜色系统 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的特性, 如果以前有画布会自动重新生成新画布 //创建画布. 根据LovyanGFX的特性, 如果以前有画布会自动重新生成新画布
//此外, 即使画布宽度不是8的倍数(如2.13寸单色),也支持自动补全8的倍数 ( 250x122 => 250x128 ) //此外, 即使画布宽度不是8的倍数(如2.13寸单色),也支持自动补全8的倍数 ( 250x122 => 250x128 )
//为了保证图片显示功能的正常使用, 高度也必须是8的倍数. //为了保证图片显示功能的正常使用, 高度也必须是8的倍数.
createSprite(guy_width,(guy_height+7)&0x7ffffff8); createSprite(guy_dev->drv_width(),(guy_dev->drv_height()+7)&0x7ffffff8);
//这里发现如果用自定义的内存分配方式会更好一些. 不会导致返回的height不对. 但是因为LovyanGFX库未更新 暂时不能这么用.
//setRotation(1); //旋转之后操作更方便 //setRotation(1); //旋转之后操作更方便
setRotation(0); setRotation(0);
setFont(&fonts::Font0); setFont(&fonts::Font0);
@@ -226,6 +227,7 @@ bool ReadguyDriver::setSDcardDriver(){
} }
else READGUY_sd_ok=0; //引脚不符合规则,或冲突或不可用 else READGUY_sd_ok=0; //引脚不符合规则,或冲突或不可用
if(!READGUY_sd_ok){ if(!READGUY_sd_ok){
Serial.println(F("[Guy SD] SD Init Failed!"));
//guyFS().begin(); //初始化内部FS //guyFS().begin(); //初始化内部FS
#ifdef READGUY_USE_LITTLEFS #ifdef READGUY_USE_LITTLEFS
LittleFS.begin(); LittleFS.begin();
@@ -387,21 +389,22 @@ void ReadguyDriver::display(std::function<uint8_t(int)> f, bool part){
//in_release(); //恢复 //in_release(); //恢复
} }
} }
void ReadguyDriver::drawImage(LGFX_Sprite &spr,uint16_t x,uint16_t 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(*this, spr, x, y); 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; 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){ void ReadguyDriver::setDepth(uint8_t d){
if(READGUY_cali==127 && guy_dev->drv_supportGreyscaling()) guy_dev->drv_setDepth(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(READGUY_cali!=127) return;
if(guy_dev->drv_supportGreyscaling() && (spr.getColorDepth()&0xff)>1) if(guy_dev->drv_supportGreyscaling() && (spr.getColorDepth()&0xff)>1)
return guy_dev->drv_draw16grey(*this,spr,x,y); return guy_dev->drv_draw16grey(*this,spr,x,y,zoomw,zoomh);
guy_dev->drv_drawImage(*this, spr, x, y); guy_dev->drv_drawImage(*this, spr, x, y, 0, zoomw, zoomh);
} }
void ReadguyDriver::draw16greyStep(int step){ void ReadguyDriver::draw16greyStep(int step){
if(READGUY_cali==127 && guy_dev->drv_supportGreyscaling() && step>0 && step<16 ){ if(READGUY_cali==127 && guy_dev->drv_supportGreyscaling() && step>0 && step<16 ){
@@ -417,7 +420,7 @@ void ReadguyDriver::draw16greyStep(std::function<uint8_t(int)> f, int step){
} }
void ReadguyDriver::invertDisplay(){ void ReadguyDriver::invertDisplay(){
if(READGUY_cali==127){ 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++) for(int i=0;i<pixels;i++)
((uint8_t*)(getBuffer()))[i]=uint8_t(~(((uint8_t*)(getBuffer()))[i])); ((uint8_t*)(getBuffer()))[i]=uint8_t(~(((uint8_t*)(getBuffer()))[i]));
} }
@@ -452,7 +455,7 @@ bool ReadguyDriver::nvs_read(){
if(i>=8) config_data[i-8] = rd; if(i>=8) config_data[i-8] = rd;
else s[i]=(char)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)); return !(strcmp_P(s,projname));
} }
void ReadguyDriver::nvs_write(){ void ReadguyDriver::nvs_write(){

View File

@@ -149,7 +149,7 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
* 2:自动连接到已存的WiFi, 但不等待连接成功 * 2:自动连接到已存的WiFi, 但不等待连接成功
* @return SD卡是否就绪 * @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 设置显示亮度 /// @brief 设置显示亮度
void setBright(int d); void setBright(int d);
/// @brief 返回显示亮度 /// @brief 返回显示亮度
@@ -171,8 +171,12 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
* 该函数会将参数从0开始,每次逐渐增加1的顺序来被调用. 即先调用f(0),再f(1),f(2),f(3)... 以此类推. * 该函数会将参数从0开始,每次逐渐增加1的顺序来被调用. 即先调用f(0),再f(1),f(2),f(3)... 以此类推.
*/ */
void display(std::function<uint8_t(int)> f, bool part = true); void display(std::function<uint8_t(int)> f, bool part = true);
/// @brief 显示图片, 使用抖动算法. 可以用省内存的方法显示 /// @brief 显示图片, 使用抖动算法. 可以用省内存的方法显示, 可以缩放到指定的宽度和高度
void drawImage(LGFX_Sprite &spr,uint16_t x,uint16_t y); 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 设置显示对比度(灰度) /// @brief 设置显示对比度(灰度)
void setDepth(uint8_t d); void setDepth(uint8_t d);
/** @brief 返回目标屏幕是否支持16级灰度 返回非0代表支持. /** @brief 返回目标屏幕是否支持16级灰度 返回非0代表支持.
@@ -184,12 +188,12 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
* 2-关闭连续刷屏 关闭16阶灰度抖动 3-开启连续刷屏 关闭16阶灰度抖动 */ * 2-关闭连续刷屏 关闭16阶灰度抖动 3-开启连续刷屏 关闭16阶灰度抖动 */
void setGreyQuality(uint8_t q) { if(READGUY_cali==127) guy_dev->setGreyQuality(q); } void setGreyQuality(uint8_t q) { if(READGUY_cali==127) guy_dev->setGreyQuality(q); }
/// @brief 显示灰度图片,如果支持,否则就不显示灰度图片了. 可以用省内存的方法显示 /// @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 按照自定义分步显示灰度图片,如果支持,否则就不显示灰度图片了. 可以用省内存的方法显示 /** @brief 按照自定义分步显示灰度图片,如果支持,否则就不显示灰度图片了. 可以用省内存的方法显示
* @param step 步骤代号. 从1开始到15,依次调用此函数来自定义的灰度显示显存内容. 没有0和16. * @param step 步骤代号. 从1开始到15,依次调用此函数来自定义的灰度显示显存内容. 没有0和16.
* @note 必须按照 "慢刷全屏->绘图->设置参数1->绘图->设置参数2... 调用15次 来完成一次自定义灰度刷屏 * @note 必须按照 "慢刷全屏->绘图->设置参数1->绘图->设置参数2... 调用15次 来完成一次自定义灰度刷屏
* 连续调用多次此函数之间, 可以修改显存内的像素颜色, 但只能从白色改为黑色. * 连续调用多次此函数之间, 可以修改显存内的像素颜色, 但只能从白色改为黑色.
* @attention 需要先调用 supportGreyscaling() 来确定是否支持灰度分步刷新.为负数时需要从深到浅刷新 * @attention 需要先调用 supportGreyscaling() 来确定是否支持灰度分步刷新.为负数时需要从深到浅刷新. 参见示例.
*/ */
void draw16greyStep(int step); void draw16greyStep(int step);
/** @brief 分步刷新显示灰度, 详见 display(f,part) 和 draw16grey(spr,x,y) 的注释. /** @brief 分步刷新显示灰度, 详见 display(f,part) 和 draw16grey(spr,x,y) 的注释.
@@ -225,7 +229,7 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
/** @brief 初始化屏幕, 设置驱动代号, 引脚排列顺序. 过程会检验引脚可用性. /** @brief 初始化屏幕, 设置驱动代号, 引脚排列顺序. 过程会检验引脚可用性.
* @param g_width, g_height 显示区域的宽度和高度. 为0表示直接使用屏幕的宽度和高度 * @param g_width, g_height 显示区域的宽度和高度. 为0表示直接使用屏幕的宽度和高度
* @note 这两个参数转专为指定分辨率的程序画面设计, 其他分辨率的画面会自动拉伸. [1.2新增] */ * @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卡, 设置驱动代号, 引脚排列顺序. 过程会检验引脚可用性. /** @brief 初始化SD卡, 设置驱动代号, 引脚排列顺序. 过程会检验引脚可用性.
* @return SD卡初始化成功与否 */ * @return SD卡初始化成功与否 */
bool setSDcardDriver(); bool setSDcardDriver();
@@ -261,7 +265,6 @@ class ReadguyDriver: public LGFX_Sprite{ // readguy 基础类
#endif #endif
int epd_OK=0; //墨水屏可用 int epd_OK=0; //墨水屏可用
int currentBright = -3; //初始亮度 int currentBright = -3; //初始亮度
int16_t guy_width=0,guy_height=0;
//LGFX_Sprite gfx; // 图形引擎类指针, 可以用这个指针去操作屏幕缓冲区 //LGFX_Sprite gfx; // 图形引擎类指针, 可以用这个指针去操作屏幕缓冲区
readguyEpdBase *guy_dev = nullptr; 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 getBlPin () const { return config_data[18]; } //前置光接口引脚IO
constexpr int getRtcType () const { return config_data[19]; } //使用的RTC型号(待定, 还没用上) constexpr int getRtcType () const { return config_data[19]; } //使用的RTC型号(待定, 还没用上)
constexpr int getButtonsCount() const { return config_data[21]; } //按钮个数, 0-3都有可能 constexpr int getButtonsCount() const { return config_data[21]; } //按钮个数, 0-3都有可能
constexpr int memWidth () const { return guy_width ; } //返回显存宽度(不是画幅宽度),不会随着画布旋转改变 //constexpr int memWidth () const { return guy_width ; } //返回显存宽度(不是画幅宽度),不会随着画布旋转改变
constexpr int memHeight () const { return guy_height ; } //返回显存高度(不是画幅高度),不会随着画布旋转改变 //constexpr int memHeight () const { return guy_height ; } //返回显存高度(不是画幅高度),不会随着画布旋转改变
int drvWidth () const { return READGUY_cali==127?guy_dev->drv_width():0; } //返回显示屏硬件宽度(不是画幅宽度) 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 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: // private:
void implBeginTransfer() { guy_dev->BeginTransfer(); } //此函数用于开启SPI传输, 只能在自定义刷屏函数中使用!! void implBeginTransfer() { guy_dev->BeginTransfer(); } //此函数用于开启SPI传输, 只能在自定义刷屏函数中使用!!
void implEndTransfer() { guy_dev->EndTransfer(); } //此函数用于开启SPI传输, 只能在自定义刷屏函数中使用!! void implEndTransfer() { guy_dev->EndTransfer(); } //此函数用于开启SPI传输, 只能在自定义刷屏函数中使用!!
/// @brief 分阶段显示图片, 使用抖动算法. 更加的省内存.目前函数 /// @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. #endif /* END OF FILE. ReadGuy project.
Copyright (C) 2023 FriendshipEnder. */ Copyright (C) 2023 FriendshipEnder. */