Browse Source

1st cut, not complete, not ready yet

v0.9.4
Guy Turcotte 2 years ago
parent
commit
ea4d20b770
  1. 2
      .gitignore
  2. 3
      CMakeLists.txt
  3. 67
      README.md
  4. 47
      doc/arduino-version.pu
  5. 115
      doc/esp-idf-version.pu
  6. 37
      doc/test.pu
  7. 39
      include/README
  8. 46
      lib/README
  9. 17
      lib/drivers/library.json
  10. 35
      lib/drivers/src/battery.cpp
  11. 30
      lib/drivers/src/battery.hpp
  12. 93
      lib/drivers/src/eink.hpp
  13. 738
      lib/drivers/src/eink_10.cpp
  14. 198
      lib/drivers/src/eink_10.hpp
  15. 738
      lib/drivers/src/eink_6.cpp
  16. 198
      lib/drivers/src/eink_6.hpp
  17. 77
      lib/drivers/src/inkplate_platform.cpp
  18. 78
      lib/drivers/src/inkplate_platform.hpp
  19. 249
      lib/drivers/src/mcp.cpp
  20. 152
      lib/drivers/src/mcp.hpp
  21. 50
      lib/drivers/src/touch_keys.cpp
  22. 50
      lib/drivers/src/touch_keys.hpp
  23. 17
      lib/graphical/library.json
  24. 2624
      lib/graphical/src/adafruit_gfx.cpp
  25. 394
      lib/graphical/src/adafruit_gfx.hpp
  26. 31
      lib/graphical/src/gfx_font.hpp
  27. 281
      lib/graphical/src/glcd_font.hpp
  28. 162
      lib/graphical/src/graphics.cpp
  29. 90
      lib/graphical/src/graphics.hpp
  30. 136
      lib/graphical/src/image.cpp
  31. 146
      lib/graphical/src/image.hpp
  32. 280
      lib/graphical/src/image_bmp.cpp
  33. 94
      lib/graphical/src/image_dither.cpp
  34. 160
      lib/graphical/src/image_jpeg.cpp
  35. 159
      lib/graphical/src/image_png.cpp
  36. 36
      lib/graphical/src/image_utils.cpp
  37. 874
      lib/graphical/src/pngle.cpp
  38. 86
      lib/graphical/src/pngle.hpp
  39. 329
      lib/graphical/src/print.cpp
  40. 104
      lib/graphical/src/print.hpp
  41. 40
      lib/graphical/src/printable.hpp
  42. 159
      lib/graphical/src/shapes.cpp
  43. 75
      lib/graphical/src/shapes.hpp
  44. 161
      lib/graphical/src/stdio_noniso.cpp
  45. 49
      lib/graphical/src/stdio_noniso.hpp
  46. 187
      lib/graphical/src/tjpg_decoder.cpp
  47. 77
      lib/graphical/src/tjpg_decoder.hpp
  48. 1963
      lib/graphical/src/tjpgd.cpp
  49. 94
      lib/graphical/src/tjpgd.hpp
  50. 863
      lib/graphical/src/wstring.cpp
  51. 334
      lib/graphical/src/wstring.hpp
  52. 19
      lib/inkplate/library.json
  53. 213
      lib/inkplate/src/inkplate.cpp
  54. 49
      lib/inkplate/src/inkplate.hpp
  55. 12
      lib/services/library.json
  56. 79
      lib/services/src/esp.hpp
  57. 39
      lib/services/src/network_client.cpp
  58. 48
      lib/services/src/network_client.hpp
  59. 49
      lib/services/src/sd_card.cpp
  60. 22
      lib/services/src/sd_card.hpp
  61. 123
      lib/services/src/wire.cpp
  62. 54
      lib/services/src/wire.hpp
  63. 12
      lib/tools/library.json
  64. 15
      lib/tools/src/logging.hpp
  65. 4931
      lib/tools/src/miniz.cpp
  66. 2
      lib/tools/src/miniz.hpp
  67. 27
      lib/tools/src/non_copyable.hpp
  68. 10
      licenses.txt
  69. 8
      partitions.csv
  70. 38
      platformio.ini
  71. 1145
      sdkconfig
  72. 6
      src/CMakeLists.txt
  73. 53
      src/main.cpp
  74. 11
      test/README

2
.gitignore

@ -0,0 +1,2 @@
.pio
.vscode

3
CMakeLists.txt

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.16.0)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ESP-IDF-InkPlate)

67
README.md

@ -1 +1,68 @@
# ESP-IDF-InkPlate
A porting effort for the e-Radionica InkPlate software that can be find [here](https://github.com/e-radionicacom/Inkplate-Arduino-library) to the ESP-IDF framework.
--> Work in progress. Not ready yet. <--
Beware that these classes are **not** re-entrant. That means that it is not possible to use them in a multi-thread context without proper mutual exclusion access control.
This project is a working example. You can build it using PlatformIO. It will draw a square and a straight line on the device display.
## Modifications done to the Arduino Inkplate Source code
As you can expect, the ESP-IDF framework is quit different than the Arduino framework. The porting effort of the Inkplate source code done here is not just a transformation to make it runs, but also getting it inline with the available packages available with the ESP-IDF. Many aspects must be transformed in this regard. Here is a list of the changes being done:
### Global changes
- Give the overall source code a transformation to use what is called *modern C++*, alligned with C++17
- All .h files are renamed .hpp
- macro definitions (`#define`) are reduced to a minimum
- `enum class` are used everywhere possible
- source code filenames are all in lowercase
### SdCard
- The module is used only to initialize the ESP-IDF drivers (SPI and FAT filesystem are used). The card can then be accessed through the standard C++/C capabilities. All filenames located on the card must be prefixed with `/sdcard`.
### Image (.hpp, .cpp)
- the `drawXXXXFromWeb(WiFiClient *s, ...)` methods are no longer available.
- the `bool drawXXXXFromSd(SdFile *p, ...)` methods are renamed `bool drawXXXXFromFile(File *p, ...)`.
## ESP-IDF configuration specifics
An InkPlate application requires some functionalities to be properly set up within the ESP-IDF. The following elements have been done for this project and can be used as a reference for other projects:
- **PSRAM memory management**: The PSRAM is an extension to the ESP32 memory that offers 4MB+4MB of additional RAM. The first 4MB is readily available to integrate into the dynamic memory allocation of the ESP-IDF SDK. To do so, some parameters located in the `sdkconfig` file must be set accordingly. This must be done using the menuconfig application that is part of the ESP-IDF. The following command will launch the application (the current folder must be the main folder of EPub-InkPlate):
```
$ idf.py menuconfig
```
The application will show a list of configuration aspects. To configure PSRAM:
- Select `Component Config` > `ESP32-Specific` > `Support for external, SPI-Connected RAM`
- Select `SPI RAM config` > `Initialize SPI RAM during startup`
- Select `Run memory test on SPI RAM Initialization`
- Select `Enable workaround for bug in SPI RAM cache for Rev 1 ESP32s`
- Select `SPI RAM access method` > `Make RAM allocatable using malloc() as well`
Leave the other options as they are.
- **ESP32 processor speed**: The processor must be run at 240MHz. The following line in `platformio.ini` request this speed:
```
board_build.f_cpu = 240000000L
```
You can also select the speed in the sdkconfig file:
- Select `Component config` > `ESP32-Specific` > `CPU frequency` > `240 Mhz`
- **FAT Filesystem Support**: If the application requires the usage of the micro SD card. This card must be formatted on a computer (Linux or Windows) with a FAT 32 partition. The InkPlate6Ctrl class requires this in its current configuration. The following parameters must be adjusted in `sdkconfig`:
- Select `Component config` > `FAT Filesystem support` > `Max Long filename length` > `255`
- Select `Number of simultaneously open files protected by lock function` > `5`
- Select `Prefer external RAM when allocating FATFS buffer`
- **Flash memory partitioning (optional)**: the file `partitions.csv` contains the table of partitions definition for the 4MB flash memory, required to support the largest applications size in an OTA context. The partitions OTA_0 and OTA_1 have been set to be 1.3MB in size. In the `platformio.ini` file, the line `board_build.partitions=...` is directing the use of these partitions configuration.

47
doc/arduino-version.pu

@ -0,0 +1,47 @@
@startuml "Inkplate 6 Arduino Library"
package "Inkplate6-Arduino" <<rectangle>> {
package "Arduino" <<frame>> {
class Print
class Wire
}
package "Adafruit-GFX" <<folder>> {
abstract class Adafruit_GFX
Print <|-- Adafruit_GFX
}
package "Include" <<folder>> {
together group2 {
abstract class Graphics
abstract class Image
abstract class Shapes
}
together group1 {
class NetworkClient
class MCP
class System
}
group1 -[hidden]- group2
Adafruit_GFX <|-- Image
NetworkClient <|-- Image
Image <|-- Graphics
Shapes <|-- Graphics
MCP <|-- System
Wire <.. MCP
NetworkClient <|-- System
}
package "Src" <<folder>> {
class Inkplate
Graphics <|-- Inkplate
System <|-- Inkplate
}
group1 -[hidden]-> Inkplate
}
@enduml

115
doc/esp-idf-version.pu

@ -0,0 +1,115 @@
@startuml "Inkplate ESP-IDF Library"
package "Inkplate-ESP-IDF" <<rectangle>> {
package "ESP-IDF Framework" <<rectangle>> {
}
package "Graphical" <<folder>> {
class Print
abstract class Adafruit_GFX
together group2 {
abstract class Graphics
abstract class Image
class Shapes
}
Print <|-- Adafruit_GFX
}
package "ESP-IDF Services" <<folder>> {
class NetworkClient
class SDCard << (S,#FF7700) Singleton >>
class Wire << (S,#FF7700) Singleton >>
}
"ESP-IDF Framework" <.. "ESP-IDF Services"
package "Drivers" <<folder>> {
together {
class MCP23017 << (S,#FF7700) Singleton >>
class TPS65186 << (S,#FF7700) Singleton >>
}
together {
class TouchScreen << (S,#FF7700) Singleton >>
class TouchKeys << (S,#FF7700) Singleton >>
}
together {
abstract class EInk
package "EInkKind" <<folder>> {
class EInk6 << (S,#FF7700) Singleton >>
class EInk6Plus << (S,#FF7700) Singleton >>
class EInk10 << (S,#FF7700) Singleton >>
EInk6 -[hidden]-> EInk10
EInk10 -[hidden]-> EInk6Plus
}
}
' MCP23017 -[hidden]-> TouchKeys
EInk -[hidden]> TouchKeys
TouchKeys -[hidden]- TouchScreen
Wire <.. MCP23017
Wire <.. TPS65186
TPS65186 <.. EInk
MCP23017 <.. EInk
MCP23017 <.. TouchKeys
MCP23017 <.. TouchScreen
EInk <|-- EInkKind
}
"EInkKind" -right[hidden] EInk
package "InkPlate" <<folder>> {
together group1 {
class InkPlatePlatform << (S,#FF7700) Singleton >>
class Inkplate << (S,#FF7700) Singleton >>
}
Graphics <|-- Inkplate
InkPlatePlatform <.. Inkplate
Adafruit_GFX <|-- Image
Image <|-- Graphics
Shapes <|-- Graphics
Drivers <.. InkPlatePlatform
"ESP-IDF Services" <.. InkPlatePlatform
' EInkKind <.. InkPlatePlatform
' EInk <.. Inkplate
}
"Drivers" -left[hidden]- "Inkplate"
package "User App" <<folder>> {
class MainApp
}
Inkplate --o MainApp
package "Bitmaps" <<folder>> {
abstract class Bitmap
abstract class Bitmap1Bit
abstract class Bitmap3Bit
class Bitmap1BitX
class Bitmap3BitX
Bitmap <|-- Bitmap1Bit
Bitmap <|-- Bitmap3Bit
Bitmap1Bit <|-- Bitmap1BitX
Bitmap3Bit <|-- Bitmap3BitX
}
' Bitmaps <|-- EInkKind
' Bitmaps -right[hidden]- EInkKind
Bitmap1Bit --o Graphics
Graphical -left[hidden]- Bitmaps
' Inkplate -left[hidden]- Graphical
' Inkplate ..> EInk
}
@enduml

37
doc/test.pu

@ -0,0 +1,37 @@
@startuml "ESP-IDF-InkPlate-Port"
package "InkPlate Low Level" <<folder>> {
abstract class NonCopyable
class Wire << (S,#FF7700) Singleton >>
Wire : setup()
Wire : begin_transmission(addr)
Wire : read()
Wire : write(val)
Wire : end_transmission()
Wire : request_from(addr, size)
class MCP << (S,#FF7700) Singleton >>
class EInk << (S,#FF7700) Singleton >>
EInk : WIDTH
EInk : HEIGHT
EInk : typedef Bitmap1Bit
EInk : typedef Bitmap3Bit
EInk : setup()
EInk : update(Bitmap1bit &)
EInk : update(Bitmap3bit &)
EInk : partial_update(Bitmap1bit &)
EInk : partial_update(Bitmap3bit &)
EInk : clear_bitmap(Bitmap1bit &)
EInk : clear_bitmap(Bitmap3bit &)
EInk : clean()
EInk : read_power_good()
EInk : read_temperature()
NonCopyable <|-- Wire
NonCopyable <|-- MCP
NonCopyable <|-- EInk
Wire o-- MCP
MCP o-- EInk
}
@enduml

39
include/README

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

46
lib/README

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

17
lib/drivers/library.json

@ -0,0 +1,17 @@
{
"name": "inkplate_drivers",
"frameworks": "espidf",
"platforms": "espressif32",
"export": {
"include": [
"src/*.hpp",
"src/*.h"
]
},
"dependencies": [
{ "name": "services" },
{ "name": "tools" }
]
}

35
lib/drivers/src/battery.cpp

@ -0,0 +1,35 @@
#define __BATTERY__ 1
#include "battery.hpp"
#include "wire.hpp"
Battery Battery::singleton;
bool
Battery::setup()
{
mcp.set_direction(BATTERY_SWITCH, MCP::PinMode::OUTPUT);
return true;
}
double
Battery::read_level()
{
Wire::enter();
mcp.digital_write(BATTERY_SWITCH, MCP::SignalLevel::HIGH);
Wire::leave();
ESP::delay(1);
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(ADC1_CHANNEL_7, ADC_ATTEN_DB_11);
int16_t adc = ESP::analog_read(ADC1_CHANNEL_7); // ADC 1 Channel 7 is GPIO port 35
Wire::enter();
mcp.digital_write(BATTERY_SWITCH, MCP::SignalLevel::LOW);
Wire::leave();
return (double(adc) * 3.3 * 2) / 4095.0;
}

30
lib/drivers/src/battery.hpp

@ -0,0 +1,30 @@
#ifndef __BATTERY_HPP__
#define __BATTERY_HPP__
#include "non_copyable.hpp"
#include "mcp.hpp"
class Battery : NonCopyable
{
public:
static inline Battery & get_singleton() noexcept { return singleton; }
bool setup();
double read_level();
private:
static constexpr char const * TAG = "Battery";
static Battery singleton;
Battery() {}
const MCP::Pin BATTERY_SWITCH = MCP::Pin::IOPIN_9;
};
#ifdef __BATTERY__
Battery & battery = Battery::get_singleton();
#else
extern Battery & battery;
#endif
#endif

93
lib/drivers/src/eink.hpp

@ -0,0 +1,93 @@
#ifndef __EINK_HPP__
#define __EINK_HPP__
#include "logging.hpp"
class Bitmap
{
protected:
static constexpr char const * TAG = "Bitmap";
const int32_t data_size;
const int16_t width, height, line_size;
const uint8_t init_value;
public:
Bitmap(int16_t w, int16_t h, int32_t s, uint8_t i) :
data_size(s), width(w), height(h), line_size(s / h), init_value(i) {}
inline int16_t get_width() { return width; }
inline int16_t get_height() { return height; }
inline int32_t get_data_size() { return data_size; }
inline int16_t get_line_size() { return line_size; }
inline uint8_t get_init_value() { return init_value; }
void clear() {
// ESP_LOGD(TAG, "Clear: %08x, with: %02x, size: %d", (int)get_data(), (int)get_init_value(), (int)get_data_size());
memset(get_data(), get_init_value(), get_data_size());
}
virtual uint8_t * get_data() = 0;
};
class Bitmap1Bit : public Bitmap
{
public:
Bitmap1Bit(int16_t w, int16_t h, int32_t s) : Bitmap(w, h, s, 0) {}
};
class Bitmap3Bit : public Bitmap
{
public:
Bitmap3Bit(int16_t w, int16_t h, int32_t s) : Bitmap(w, h, s, (uint8_t) 0x77) {}
};
class EInk
{
public:
enum class PanelState { OFF, ON };
inline PanelState get_panel_state() { return panel_state; }
inline bool is_initialized() { return initialized; }
virtual inline Bitmap1Bit * new_bitmap1bit() = 0;
virtual inline Bitmap3Bit * new_bitmap3bit() = 0;
virtual inline int16_t get_width() = 0;
virtual inline int16_t get_height() = 0;
// All the following methods are protecting the I2C device interface trough
// the Wire::enter() and Wire::leave() methods. These are implementing a
// Mutex semaphore access control.
//
// If you ever add public methods, you *MUST* consider adding calls to Wire::enter()
// and Wire::leave() and insure no deadlock will happen... or modify the mutex to use
// a recursive mutex.
virtual bool setup() = 0;
inline void update(Bitmap1Bit & bitmap) { update_1bit(bitmap); }
inline void update(Bitmap3Bit & bitmap) { update_3bit(bitmap); }
virtual void partial_update(Bitmap1Bit & bitmap) = 0;
virtual void clean() = 0;
int8_t read_temperature();
protected:
EInk() :
panel_state(PanelState::OFF),
initialized(false),
partial_allowed(false) {}
PanelState panel_state;
bool initialized;
bool partial_allowed;
virtual void update_1bit(Bitmap1Bit & bitmap) = 0;
virtual void update_3bit(Bitmap3Bit & bitmap) = 0;
};
#endif

738
lib/drivers/src/eink_10.cpp

@ -0,0 +1,738 @@
/*
eink.cpp
Inkplate 10 ESP-IDF
Modified by Guy Turcotte
December 23, 2020
from the Arduino Library:
David Zovko, Borna Biro, Denis Vajak, Zvonimir Haramustek @ e-radionica.com
September 24, 2020
https://github.com/e-radionicacom/Inkplate-6-Arduino-library
For support, please reach over forums: forum.e-radionica.com/en
For more info about the product, please check: www.inkplate.io
This code is released under the GNU Lesser General Public License v3.0: https://www.gnu.org/licenses/lgpl-3.0.en.html
Please review the LICENSE file included with this example.
If you have any questions about licensing, please contact techsupport@e-radionica.com
Distributed as-is; no warranty is given.
*/
#ifdef INKPLATE_10
#define __EINK10__ 1
#include "eink10.hpp"
#include "logging.hpp"
#include "wire.hpp"
#include "mcp.hpp"
#include "esp.hpp"
#include <iostream>
EInk10 EInk10::singleton;
const uint8_t EInk10::WAVEFORM_3BIT[8][8] = {
{0, 0, 0, 0, 1, 1, 1, 0}, {1, 2, 2, 2, 1, 1, 1, 0}, {0, 1, 2, 1, 1, 2, 1, 0},
{0, 2, 1, 2, 1, 2, 1, 0}, {0, 0, 0, 1, 1, 1, 2, 0}, {2, 1, 1, 1, 2, 1, 2, 0},
{1, 1, 1, 2, 1, 2, 2, 0}, {0, 0, 0, 0, 0, 0, 2, 0} };
const uint32_t EInk10::WAVEFORM[50] = {
0x00000008, 0x00000008, 0x00200408, 0x80281888, 0x60A81898, 0x60A8A8A8, 0x60A8A8A8, 0x6068A868, 0x6868A868,
0x6868A868, 0x68686868, 0x6A686868, 0x5A686868, 0x5A686868, 0x5A586A68, 0x5A5A6A68, 0x5A5A6A68, 0x55566A68,
0x55565A64, 0x55555654, 0x55555556, 0x55555556, 0x55555556, 0x55555516, 0x55555596, 0x15555595, 0x95955595,
0x95959595, 0x95949495, 0x94949495, 0x94949495, 0xA4949494, 0x9494A4A4, 0x84A49494, 0x84948484, 0x84848484,
0x84848484, 0x84848484, 0xA5A48484, 0xA9A4A4A8, 0xA9A8A8A8, 0xA5A9A9A4, 0xA5A5A5A4, 0xA1A5A5A1, 0xA9A9A9A9,
0xA9A9A9A9, 0xA9A9A9A9, 0xA9A9A9A9, 0x15151515, 0x11111111 };
const uint8_t EInk10::LUT2[16] = {
0xAA, 0xA9, 0xA6, 0xA5, 0x9A, 0x99, 0x96, 0x95,
0x6A, 0x69, 0x66, 0x65, 0x5A, 0x59, 0x56, 0x55 };
const uint8_t EInk10::LUTW[16] = {
0xFF, 0xFE, 0xFB, 0xFA, 0xEF, 0xEE, 0xEB, 0xEA,
0xBF, 0xBE, 0xBB, 0xBA, 0xAF, 0xAE, 0xAB, 0xAA };
const uint8_t EInk10::LUTB[16] = {
0xFF, 0xFD, 0xF7, 0xF5, 0xDF, 0xDD, 0xD7, 0xD5,
0x7F, 0x7D, 0x77, 0x75, 0x5F, 0x5D, 0x57, 0x55 };
// PIN_LUT built from the following:
//
// for (uint32_t i = 0; i < 256; i++) {
// PIN_LUT[i] = ((i & 0b00000011) << 4) |
// (((i & 0b00001100) >> 2) << 18) |
// (((i & 0b00010000) >> 4) << 23) |
// (((i & 0b11100000) >> 5) << 25);
// }
const uint32_t EInk10::PIN_LUT[256] = {
0x00000000, 0x00000010, 0x00000020, 0x00000030, 0x00040000, 0x00040010, 0x00040020, 0x00040030,
0x00080000, 0x00080010, 0x00080020, 0x00080030, 0x000c0000, 0x000c0010, 0x000c0020, 0x000c0030,
0x00800000, 0x00800010, 0x00800020, 0x00800030, 0x00840000, 0x00840010, 0x00840020, 0x00840030,
0x00880000, 0x00880010, 0x00880020, 0x00880030, 0x008c0000, 0x008c0010, 0x008c0020, 0x008c0030,
0x02000000, 0x02000010, 0x02000020, 0x02000030, 0x02040000, 0x02040010, 0x02040020, 0x02040030,
0x02080000, 0x02080010, 0x02080020, 0x02080030, 0x020c0000, 0x020c0010, 0x020c0020, 0x020c0030,
0x02800000, 0x02800010, 0x02800020, 0x02800030, 0x02840000, 0x02840010, 0x02840020, 0x02840030,
0x02880000, 0x02880010, 0x02880020, 0x02880030, 0x028c0000, 0x028c0010, 0x028c0020, 0x028c0030,
0x04000000, 0x04000010, 0x04000020, 0x04000030, 0x04040000, 0x04040010, 0x04040020, 0x04040030,
0x04080000, 0x04080010, 0x04080020, 0x04080030, 0x040c0000, 0x040c0010, 0x040c0020, 0x040c0030,
0x04800000, 0x04800010, 0x04800020, 0x04800030, 0x04840000, 0x04840010, 0x04840020, 0x04840030,
0x04880000, 0x04880010, 0x04880020, 0x04880030, 0x048c0000, 0x048c0010, 0x048c0020, 0x048c0030,
0x06000000, 0x06000010, 0x06000020, 0x06000030, 0x06040000, 0x06040010, 0x06040020, 0x06040030,
0x06080000, 0x06080010, 0x06080020, 0x06080030, 0x060c0000, 0x060c0010, 0x060c0020, 0x060c0030,
0x06800000, 0x06800010, 0x06800020, 0x06800030, 0x06840000, 0x06840010, 0x06840020, 0x06840030,
0x06880000, 0x06880010, 0x06880020, 0x06880030, 0x068c0000, 0x068c0010, 0x068c0020, 0x068c0030,
0x08000000, 0x08000010, 0x08000020, 0x08000030, 0x08040000, 0x08040010, 0x08040020, 0x08040030,
0x08080000, 0x08080010, 0x08080020, 0x08080030, 0x080c0000, 0x080c0010, 0x080c0020, 0x080c0030,
0x08800000, 0x08800010, 0x08800020, 0x08800030, 0x08840000, 0x08840010, 0x08840020, 0x08840030,
0x08880000, 0x08880010, 0x08880020, 0x08880030, 0x088c0000, 0x088c0010, 0x088c0020, 0x088c0030,
0x0a000000, 0x0a000010, 0x0a000020, 0x0a000030, 0x0a040000, 0x0a040010, 0x0a040020, 0x0a040030,
0x0a080000, 0x0a080010, 0x0a080020, 0x0a080030, 0x0a0c0000, 0x0a0c0010, 0x0a0c0020, 0x0a0c0030,
0x0a800000, 0x0a800010, 0x0a800020, 0x0a800030, 0x0a840000, 0x0a840010, 0x0a840020, 0x0a840030,
0x0a880000, 0x0a880010, 0x0a880020, 0x0a880030, 0x0a8c0000, 0x0a8c0010, 0x0a8c0020, 0x0a8c0030,
0x0c000000, 0x0c000010, 0x0c000020, 0x0c000030, 0x0c040000, 0x0c040010, 0x0c040020, 0x0c040030,
0x0c080000, 0x0c080010, 0x0c080020, 0x0c080030, 0x0c0c0000, 0x0c0c0010, 0x0c0c0020, 0x0c0c0030,
0x0c800000, 0x0c800010, 0x0c800020, 0x0c800030, 0x0c840000, 0x0c840010, 0x0c840020, 0x0c840030,
0x0c880000, 0x0c880010, 0x0c880020, 0x0c880030, 0x0c8c0000, 0x0c8c0010, 0x0c8c0020, 0x0c8c0030,
0x0e000000, 0x0e000010, 0x0e000020, 0x0e000030, 0x0e040000, 0x0e040010, 0x0e040020, 0x0e040030,
0x0e080000, 0x0e080010, 0x0e080020, 0x0e080030, 0x0e0c0000, 0x0e0c0010, 0x0e0c0020, 0x0e0c0030,
0x0e800000, 0x0e800010, 0x0e800020, 0x0e800030, 0x0e840000, 0x0e840010, 0x0e840020, 0x0e840030,
0x0e880000, 0x0e880010, 0x0e880020, 0x0e880030, 0x0e8c0000, 0x0e8c0010, 0x0e8c0020, 0x0e8c0030
};
bool
EInk10::setup()
{
ESP_LOGD(TAG, "Initializing...");
if (initialized) return true;
wire.setup();
if (!mcp.setup()) {
ESP_LOGE(TAG, "Initialization not completed (MCP Issue).");
return false;
}
else {
ESP_LOGD(TAG, "MCP initialized.");
}
Wire::enter();
mcp.set_direction(VCOM, MCP::PinMode::OUTPUT);
mcp.set_direction(PWRUP, MCP::PinMode::OUTPUT);
mcp.set_direction(WAKEUP, MCP::PinMode::OUTPUT);
mcp.set_direction(GPIO0_ENABLE, MCP::PinMode::OUTPUT);
mcp.digital_write(GPIO0_ENABLE, MCP::SignalLevel::HIGH);
wakeup_set();
//ESP_LOGD(TAG, "Power Mgr Init..."); fflush(stdout);
ESP::delay_microseconds(1800);
wire.begin_transmission(PWRMGR_ADDRESS);
wire.write(0x09);
wire.write(0b00011011); // Power up seq.
wire.write(0b00000000); // Power up delay (3mS per rail)
wire.write(0b00011011); // Power down seq.
wire.write(0b00000000); // Power down delay (6mS per rail)
wire.end_transmission();
ESP::delay(1);
//ESP_LOGD(TAG, "Power init completed");
wakeup_clear();
// CONTROL PINS
gpio_set_direction(GPIO_NUM_0, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_32, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_33, GPIO_MODE_OUTPUT);
mcp.set_direction(OE, MCP::PinMode::OUTPUT);
mcp.set_direction(GMOD, MCP::PinMode::OUTPUT);
mcp.set_direction(SPV, MCP::PinMode::OUTPUT);
// DATA PINS
gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT); // D0
gpio_set_direction(GPIO_NUM_5, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_18, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_19, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_23, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_25, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_26, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_27, GPIO_MODE_OUTPUT); // D7
d_memory_new = new_bitmap1bit();
p_buffer = (uint8_t *) ESP::ps_malloc(120000);
ESP_LOGD(TAG, "Memory allocation for bitmap buffers.");
ESP_LOGD(TAG, "d_memory_new: %08x p_buffer: %08x.", (unsigned int)d_memory_new, (unsigned int)p_buffer);
if ((d_memory_new == nullptr) ||
(p_buffer == nullptr)) {
do {
ESP_LOGE(TAG, "Unable to complete buffers allocation");
ESP::delay(10000);
} while (true);
}
Wire::leave();
d_memory_new->clear();
memset(p_buffer, 0, 120000);
initialized = true;
return true;
}
void
EInk10::update_1bit(Bitmap1Bit & bitmap)
{
ESP_LOGD(TAG, "update_1bit...");
const uint8_t * ptr;
uint32_t send;
uint8_t dram;
Wire::enter();
turn_on();
clean_fast(0, 1);
clean_fast(1, 21);
clean_fast(2, 1);
clean_fast(0, 12);
clean_fast(2, 1);
clean_fast(1, 21);
clean_fast(2, 1);
clean_fast(0, 12);
uint8_t * data = bitmap.get_data();
for (int8_t k = 0; k < 4; k++) {
ptr = &data[BITMAP_SIZE_1BIT - 1];
vscan_start();
for (uint16_t i = 0; i < HEIGHT; i++) {
dram = *ptr--;
send = PIN_LUT[LUTB[dram >> 4]];
hscan_start(send);
send = PIN_LUT[LUTB[dram & 0x0F]];
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
for (uint16_t j = 0; j < LINE_SIZE_1BIT - 1; j++) {
dram = *ptr--;
send = PIN_LUT[LUTB[dram >> 4]];
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
send = PIN_LUT[LUTB[dram & 0x0F]];
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
}
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
vscan_end();
}
ESP::delay_microseconds(230);
}
ptr = &data[BITMAP_SIZE_1BIT - 1];
vscan_start();
for (uint16_t i = 0; i < HEIGHT; i++) {
dram = *ptr--;
send = PIN_LUT[LUT2[dram >> 4]];
hscan_start(send);
send = PIN_LUT[LUT2[dram & 0x0F]];
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
for (uint16_t j = 0; j < LINE_SIZE_1BIT - 1; j++) {
dram = *ptr--;
send = PIN_LUT[LUT2[dram >> 4]];
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
send = PIN_LUT[LUT2[dram & 0x0F]];
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
}
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
vscan_end();
}
ESP::delay_microseconds(230);
vscan_start();
send = PIN_LUT[0];
for (uint16_t i = 0; i < HEIGHT; i++) {
hscan_start(send);
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
for (int j = 0; j < LINE_SIZE_1BIT - 1; j++) {
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
}
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
vscan_end();
}
ESP::delay_microseconds(230);
vscan_start();
turn_off();
Wire::leave();
memcpy(d_memory_new->get_data(), bitmap.get_data(), BITMAP_SIZE_1BIT);
partial_allowed = true;
}
void
EInk10::update_3bit(Bitmap3Bit & bitmap)
{
ESP_LOGD(TAG, "Update_3bit...");
Wire::enter();
turn_on();
clean_fast(0, 1);
clean_fast(1, 21);
clean_fast(2, 1);
clean_fast(0, 12);
clean_fast(2, 1);
clean_fast(1, 21);
clean_fast(2, 1);
clean_fast(0, 12);
uint8_t * data = bitmap.get_data();
for (int k = 0; k < 8; k++) {
const uint8_t * dp = &data[BITMAP_SIZE_3BIT - 1];
uint32_t send;
uint8_t pix1;
uint8_t pix2;
uint8_t pix3;
uint8_t pix4;
uint8_t pixel;
uint8_t pixel2;
vscan_start();
for (int i = 0; i < HEIGHT; i++) {
pixel = 0;
pixel2 = 0;
pix1 = *(dp--);
pix2 = *(dp--);
pix3 = *(dp--);
pix4 = *(dp--);
pixel |= (WAVEFORM_3BIT[ pix1 & 0x07][k] << 6) |
(WAVEFORM_3BIT[(pix1 >> 4) & 0x07][k] << 4) |
(WAVEFORM_3BIT[ pix2 & 0x07][k] << 2) |
(WAVEFORM_3BIT[(pix2 >> 4) & 0x07][k] << 0);
pixel2 |= (WAVEFORM_3BIT[ pix3 & 0x07][k] << 6) |
(WAVEFORM_3BIT[(pix3 >> 4) & 0x07][k] << 4) |
(WAVEFORM_3BIT[ pix4 & 0x07][k] << 2) |
(WAVEFORM_3BIT[(pix4 >> 4) & 0x07][k] << 0);
send = PIN_LUT[pixel];
hscan_start(send);
send = PIN_LUT[pixel2];
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
for (int j = 0; j < (LINE_SIZE_3BIT >> 2) - 1; j++) {
pixel = 0;
pixel2 = 0;
pix1 = *(dp--);
pix2 = *(dp--);
pix3 = *(dp--);
pix4 = *(dp--);
pixel |= (WAVEFORM_3BIT[ pix1 & 0x07][k] << 6) |
(WAVEFORM_3BIT[(pix1 >> 4) & 0x07][k] << 4) |
(WAVEFORM_3BIT[ pix2 & 0x07][k] << 2) |
(WAVEFORM_3BIT[(pix2 >> 4) & 0x07][k] << 0);
pixel2 |= (WAVEFORM_3BIT[ pix3 & 0x07][k] << 6) |
(WAVEFORM_3BIT[(pix3 >> 4) & 0x07][k] << 4) |
(WAVEFORM_3BIT[ pix4 & 0x07][k] << 2) |
(WAVEFORM_3BIT[(pix4 >> 4) & 0x07][k] << 0);
send = PIN_LUT[pixel];
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
send = PIN_LUT[pixel2];
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
}
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL | DATA;
vscan_end();
}
ESP::delay_microseconds(230);
}
clean_fast(3, 1);
vscan_start();
turn_off();
Wire::leave();
}
void
EInk10::partial_update(Bitmap1Bit & bitmap)
{
if (!partial_allowed) {
update_1bit(bitmap);
return;
}
Wire::enter();
ESP_LOGD(TAG, "Partial update...");
uint32_t send;
uint32_t n = 119999;
uint16_t pos = BITMAP_SIZE_1BIT - 1;
uint8_t diffw, diffb;
uint8_t * idata = bitmap.get_data();
uint8_t * odata = d_memory_new->get_data();
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < LINE_SIZE_1BIT; j++) {
diffw = odata[pos] & ~idata[pos];
diffb = ~odata[pos] & idata[pos];
pos--;
p_buffer[n--] = LUTW[diffw >> 4] & (LUTB[diffb >> 4]);
p_buffer[n--] = LUTW[diffw & 0x0F] & (LUTB[diffb & 0x0F]);
}
}
turn_on();
for (int k = 0; k < 5; k++) {
vscan_start();
n = 119999;
for (int i = 0; i < HEIGHT; i++) {
send = PIN_LUT[p_buffer[n--]];
hscan_start(send);
for (int j = 0; j < 199; j++) {
send = PIN_LUT[p_buffer[n--]];
GPIO.out_w1ts = send | CL;
GPIO.out_w1tc = DATA | CL;
}
GPIO.out_w1ts = send | CL;
GPIO.out_w1tc = DATA | CL;
vscan_end();
}
ESP::delay_microseconds(230);
}
clean_fast(2, 2);
clean_fast(3, 1);
vscan_start();
turn_off();
Wire::leave();
memcpy(d_memory_new->get_data(), bitmap.get_data(), BITMAP_SIZE_1BIT);
}
void
EInk10::clean()
{
ESP_LOGD(TAG, "Clean...");
Wire::enter();
turn_on();
int m = 0;
clean_fast(0, 1);
m++; clean_fast((WAVEFORM[m] >> 30) & 3, 8);
m++; clean_fast((WAVEFORM[m] >> 24) & 3, 1);
m++; clean_fast( WAVEFORM[m] & 3, 8);
m++; clean_fast((WAVEFORM[m] >> 6) & 3, 1);
m++; clean_fast((WAVEFORM[m] >> 30) & 3, 10);
Wire::leave();
}
void
EInk10::clean_fast(uint8_t c, uint8_t rep)
{
static uint8_t byte[4] = { 0b10101010, 0b01010101, 0b00000000, 0b11111111 };
turn_on();
uint32_t send = PIN_LUT[byte[c]];
for (int8_t k = 0; k < rep; k++) {
vscan_start();
for (uint16_t i = 0; i < HEIGHT; i++) {
hscan_start(send);
GPIO.out_w1ts = CL | send;
GPIO.out_w1tc = CL;
for (uint16_t j = 0; j < LINE_SIZE_1BIT - 1; j++) {
GPIO.out_w1ts = CL;
GPIO.out_w1tc = CL;
GPIO.out_w1ts = CL;
GPIO.out_w1tc = CL;
}
GPIO.out_w1ts = CL;
GPIO.out_w1tc = CL;
vscan_end();
}
ESP::delay_microseconds(230);
}
}
// Turn off epaper power supply and put all digital IO pins in high Z state
void
EInk10::turn_off()
{
if (get_panel_state() == PanelState::OFF) return;
oe_clear();
gmod_clear();
GPIO.out &= ~(DATA | LE | CL);
ckv_clear();
sph_clear();
spv_clear();
vcom_clear();
ESP::delay(6);
pwrup_clear();
wakeup_clear();
unsigned long timer = ESP::millis();
do {
ESP::delay(1);
} while ((read_power_good() != 0) && (ESP::millis() - timer) < 250);
pins_z_state();
set_panel_state(PanelState::OFF);
}
// Turn on supply for epaper display (TPS65186)
// [+15 VDC, -15VDC, +22VDC, -20VDC, +3.3VDC, VCOM]
void
EInk10::turn_on()
{
if (get_panel_state() == PanelState::ON) return;
wakeup_set();
ESP::delay_microseconds(1800);
pwrup_set();
// Enable all rails
wire.begin_transmission(PWRMGR_ADDRESS);
wire.write(0x01);
wire.write(0b00111111);
wire.end_transmission();
pins_as_outputs();
le_clear();
oe_clear();
cl_clear();
sph_set();
gmod_set();
spv_set();
ckv_clear();
oe_clear();
vcom_set();
unsigned long timer = ESP::millis();
do {
ESP::delay(1);
} while ((read_power_good() != PWR_GOOD_OK) && (ESP::millis() - timer) < 250);
if ((ESP::millis() - timer) >= 250) {
wakeup_clear();
vcom_clear();
pwrup_clear();
return;
}
oe_set();
set_panel_state(PanelState::ON);
}
uint8_t
EInk10::read_power_good()
{
wire.begin_transmission(PWRMGR_ADDRESS);
wire.write(0x0F);
wire.end_transmission();
wire.request_from(PWRMGR_ADDRESS, 1);
return wire.read();
}
// LOW LEVEL FUNCTIONS
void
EInk10::vscan_start()
{
ckv_set(); ESP::delay_microseconds( 7);
spv_clear(); ESP::delay_microseconds(10);
ckv_clear(); ESP::delay_microseconds( 0);
ckv_set(); ESP::delay_microseconds( 8);
spv_set(); ESP::delay_microseconds(10);
ckv_clear(); ESP::delay_microseconds( 0);
ckv_set(); ESP::delay_microseconds(18);
ckv_clear(); ESP::delay_microseconds( 0);
ckv_set(); ESP::delay_microseconds(18);
ckv_clear(); ESP::delay_microseconds( 0);
ckv_set();
}
void
EInk10::hscan_start(uint32_t d)
{
sph_clear();
GPIO.out_w1ts = CL | d ;
GPIO.out_w1tc = CL | DATA;
sph_set();
ckv_set();
}
void
EInk10::vscan_end()
{
ckv_clear();
le_set();
le_clear();
ESP::delay_microseconds(0);
}
void
EInk10::pins_z_state()
{
gpio_set_direction(GPIO_NUM_0, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_NUM_2, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_NUM_32, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_NUM_33, GPIO_MODE_INPUT);
mcp.set_direction(OE, MCP::PinMode::INPUT);
mcp.set_direction(GMOD, MCP::PinMode::INPUT);
mcp.set_direction(SPV, MCP::PinMode::INPUT);
gpio_set_direction(GPIO_NUM_4, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_NUM_5, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_NUM_18, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_NUM_19, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_NUM_23, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_NUM_25, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_NUM_26, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_NUM_27, GPIO_MODE_INPUT);
}
void
EInk10::pins_as_outputs()
{
gpio_set_direction(GPIO_NUM_0, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_32, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_33, GPIO_MODE_OUTPUT);
mcp.set_direction(OE, MCP::PinMode::OUTPUT);
mcp.set_direction(GMOD, MCP::PinMode::OUTPUT);
mcp.set_direction(SPV, MCP::PinMode::OUTPUT);
gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_5, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_18, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_19, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_23, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_25, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_26, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_27, GPIO_MODE_OUTPUT);
}
int8_t
EInk10::read_temperature()
{
int8_t temp;
if (get_panel_state() == PanelState::OFF) {
Wire::enter();
wakeup_set();
ESP::delay_microseconds(1800);
pwrup_set();
Wire::leave();
ESP::delay(5);
}
Wire::enter();
wire.begin_transmission(PWRMGR_ADDRESS);
wire.write(0x0D);
wire.write(0b10000000);
wire.end_transmission();
Wire::leave();
ESP::delay(5);
Wire::enter();
wire.begin_transmission(PWRMGR_ADDRESS);
wire.write(0x00);
wire.end_transmission();
wire.request_from(PWRMGR_ADDRESS, 1);
temp = wire.read();
if (get_panel_state() == PanelState::OFF) {
pwrup_clear();
wakeup_clear();
Wire::leave();
ESP::delay(5);
}
else {
Wire::leave();
}
return temp;
}
#endif

198
lib/drivers/src/eink_10.hpp

@ -0,0 +1,198 @@
/*
eink.h
Inkplate 10 ESP-IDF
Modified by Guy Turcotte
December 23, 2020
from the Arduino Library:
David Zovko, Borna Biro, Denis Vajak, Zvonimir Haramustek @ e-radionica.com
September 24, 2020
https://github.com/e-radionicacom/Inkplate-6-Arduino-library
For support, please reach over forums: forum.e-radionica.com/en
For more info about the product, please check: www.inkplate.io
This code is released under the GNU Lesser General Public License v3.0: https://www.gnu.org/licenses/lgpl-3.0.en.html
Please review the LICENSE file included with this example.
If you have any questions about licensing, please contact techsupport@e-radionica.com
Distributed as-is; no warranty is given.
*/
#ifdef INKPLATE_10
#ifndef __EINK10_HPP__
#define __EINK10_HPP__
#include <cinttypes>
#include <cstring>
#include "non_copyable.hpp"
#include "driver/gpio.h"
#include "mcp.hpp"
#include "eink.hpp"
/**
* @brief Low level e-Ink display
*
* This class implements the low level methods required to control
* and access the e-ink display of the InkPlate-6 device.
*
* This is a singleton. It cannot be instanciated elsewhere. It is not
* instanciated in the heap. This is reinforced by the C++ construction
* below. It also cannot be copied through the NonCopyable derivation.
*/
class EInk10 : public EInk, NonCopyable
{
public:
static const uint16_t WIDTH = 800; // In pixels
static const uint16_t HEIGHT = 600; // In pixels
static const uint16_t BITMAP_SIZE_1BIT = (WIDTH * HEIGHT) >> 3; // In bytes
static const uint32_t BITMAP_SIZE_3BIT = ((uint32_t) WIDTH * HEIGHT) >> 1; // In bytes
static const uint16_t LINE_SIZE_1BIT = WIDTH >> 3; // In bytes
static const uint16_t LINE_SIZE_3BIT = WIDTH >> 1; // In bytes
static inline EInk6 & get_singleton() noexcept { return singleton; }
inline int16_t get_width() { return WIDTH; }
inline int16_t get_height() { return HEIGHT; }
private:
static constexpr char const * TAG = "EInk10";
static EInk6 singleton;
static const uint8_t PWRMGR_ADDRESS = 0x48;
static const uint8_t PWR_GOOD_OK = 0b11111010;
class Bitmap1Bit6 : public Bitmap1Bit {
private:
uint8_t data[BITMAP_SIZE_1BIT];
public:
Bitmap1Bit6() : Bitmap1Bit(WIDTH, HEIGHT, BITMAP_SIZE_1BIT) {}
uint8_t * get_data() { return data; }
};
class Bitmap3Bit6 : public Bitmap3Bit {
private:
uint8_t data[BITMAP_SIZE_3BIT];
public:
Bitmap3Bit6() : Bitmap3Bit(WIDTH, HEIGHT, BITMAP_SIZE_3BIT) {}
uint8_t * get_data() { return data; }
};
EInk10() : EInk()
{ } // Private constructor
void update_1bit(Bitmap1Bit & bitmap);
void update_3bit(Bitmap3Bit & bitmap);
void vscan_start();
void vscan_end();
void hscan_start(uint32_t d = 0);
void pins_z_state();
void pins_as_outputs();
void turn_on();
void turn_off();
void clean_fast(uint8_t c, uint8_t rep);
uint8_t read_power_good();
inline void set_panel_state(PanelState s) { panel_state = s; }
inline void allow_partial() { partial_allowed = true; }
inline void block_partial() { partial_allowed = false; }
inline bool is_partial_allowed() { return partial_allowed; }
static const uint32_t PIN_LUT[256];
static const uint8_t WAVEFORM_3BIT[8][8];
static const uint32_t WAVEFORM[50];
static const uint8_t LUT2[16];
static const uint8_t LUTW[16];
static const uint8_t LUTB[16];
static const uint32_t CL = 0x01;
static const uint32_t CKV = 0x01;
static const uint32_t SPH = 0x02;
static const uint32_t LE = 0x04;
static const uint32_t DATA = 0x0E8C0030;
uint8_t * p_buffer;
Bitmap1Bit * d_memory_new;
const MCP::Pin OE = MCP::Pin::IOPIN_0;
const MCP::Pin GMOD = MCP::Pin::IOPIN_1;
const MCP::Pin SPV = MCP::Pin::IOPIN_2;
const MCP::Pin WAKEUP = MCP::Pin::IOPIN_3;
const MCP::Pin PWRUP = MCP::Pin::IOPIN_4;
const MCP::Pin VCOM = MCP::Pin::IOPIN_5;
const MCP::Pin GPIO0_ENABLE = MCP::Pin::IOPIN_8;
inline void cl_set() { GPIO.out_w1ts = CL; }
inline void cl_clear() { GPIO.out_w1tc = CL; }
inline void ckv_set() { GPIO.out1_w1ts.val = CKV; }
inline void ckv_clear() { GPIO.out1_w1tc.val = CKV; }
inline void sph_set() { GPIO.out1_w1ts.val = SPH; }
inline void sph_clear() { GPIO.out1_w1tc.val = SPH; }
inline void le_set() { GPIO.out_w1ts = LE; }
inline void le_clear() { GPIO.out_w1tc = LE; }
inline void oe_set() { mcp.digital_write(OE, MCP::SignalLevel::HIGH); }
inline void oe_clear() { mcp.digital_write(OE, MCP::SignalLevel::LOW ); }
inline void gmod_set() { mcp.digital_write(GMOD, MCP::SignalLevel::HIGH); }
inline void gmod_clear() { mcp.digital_write(GMOD, MCP::SignalLevel::LOW ); }
inline void spv_set() { mcp.digital_write(SPV, MCP::SignalLevel::HIGH); }
inline void spv_clear() { mcp.digital_write(SPV, MCP::SignalLevel::LOW ); }
inline void wakeup_set() { mcp.digital_write(WAKEUP, MCP::SignalLevel::HIGH); }
inline void wakeup_clear() { mcp.digital_write(WAKEUP, MCP::SignalLevel::LOW ); }
inline void pwrup_set() { mcp.digital_write(PWRUP, MCP::SignalLevel::HIGH); }
inline void pwrup_clear() { mcp.digital_write(PWRUP, MCP::SignalLevel::LOW ); }
inline void vcom_set() { mcp.digital_write(VCOM, MCP::SignalLevel::HIGH); }
inline void vcom_clear() { mcp.digital_write(VCOM, MCP::SignalLevel::LOW ); }
public:
inline PanelState get_panel_state() { return panel_state; }
inline bool is_initialized() { return initialized; }
virtual inline Bitmap1Bit * new_bitmap1bit() { return new Bitmap1Bit6; }
virtual inline Bitmap3Bit * new_bitmap3bit() { return new Bitmap3Bit6; }
// All the following methods are protecting the I2C device trough
// the Wire::enter() and Wire::leave() methods. These are implementing a
// Mutex semaphore access control.
//
// If you ever add public methods, you *MUST* consider adding calls to Wire::enter()
// and Wire::leave() and insure no deadlock will happen... or modifu the mutex to use
// a recursive mutex.
bool setup();
inline void update(Bitmap1Bit & bitmap) { update_1bit(bitmap); }
inline void update(Bitmap3Bit & bitmap) { update_3bit(bitmap); }
void partial_update(Bitmap1Bit & bitmap);
void clean();
int8_t read_temperature();
};
#endif
#endif

738
lib/drivers/src/eink_6.cpp

@ -0,0 +1,738 @@
/*
eink.cpp
Inkplate 6 ESP-IDF
Modified by Guy Turcotte
December 23, 2020
from the Arduino Library:
David Zovko, Borna Biro, Denis Vajak, Zvonimir Haramustek @ e-radionica.com
September 24, 2020
https://github.com/e-radionicacom/Inkplate-6-Arduino-library
For support, please reach over forums: forum.e-radionica.com/en
For more info about the product, please check: www.inkplate.io
This code is released under the GNU Lesser General Public License v3.0: https://www.gnu.org/licenses/lgpl-3.0.en.html
Please review the LICENSE file included with this example.
If you have any questions about licensing, please contact techsupport@e-radionica.com
Distributed as-is; no warranty is given.
*/
#ifdef INKPLATE_6
#define __EINK6__ 1
#include "eink6.hpp"
#include "logging.hpp"
#include "wire.hpp"
#include "mcp.hpp"
#include "esp.hpp"
#include <iostream>
EInk6 EInk6::singleton;
const uint8_t EInk6::WAVEFORM_3BIT[8][8] = {
{0, 0, 0, 0, 1, 1, 1, 0}, {1, 2, 2, 2, 1, 1, 1, 0}, {0, 1, 2, 1, 1, 2, 1, 0},
{0, 2, 1, 2, 1, 2, 1, 0}, {0, 0, 0, 1, 1, 1, 2, 0}, {2, 1, 1, 1, 2, 1, 2, 0},
{1, 1, 1, 2, 1, 2, 2, 0}, {0, 0, 0, 0, 0, 0, 2, 0} };
const uint32_t EInk6::WAVEFORM[50] = {
0x00000008, 0x00000008, 0x00200408, 0x80281888, 0x60A81898, 0x60A8A8A8, 0x60A8A8A8, 0x6068A868, 0x6868A868,
0x6868A868, 0x68686868, 0x6A686868, 0x5A686868, 0x5A686868, 0x5A586A68, 0x5A5A6A68, 0x5A5A6A68, 0x55566A68,
0x55565A64, 0x55555654, 0x55555556, 0x55555556, 0x55555556, 0x55555516, 0x55555596, 0x15555595, 0x95955595,
0x95959595, 0x95949495, 0x94949495, 0x94949495, 0xA4949494, 0x9494A4A4, 0x84A49494, 0x84948484, 0x84848484,
0x84848484, 0x84848484, 0xA5A48484, 0xA9A4A4A8, 0xA9A8A8A8, 0xA5A9A9A4, 0xA5A5A5A4, 0xA1A5A5A1, 0xA9A9A9A9,
0xA9A9A9A9, 0xA9A9A9A9, 0xA9A9A9A9, 0x15151515, 0x11111111 };
const uint8_t EInk6::LUT2[16] = {
0xAA, 0xA9, 0xA6, 0xA5, 0x9A, 0x99, 0x96, 0x95,
0x6A, 0x69, 0x66, 0x65, 0x5A, 0x59, 0x56, 0x55 };
const uint8_t EInk6::LUTW[16] = {
0xFF, 0xFE, 0xFB, 0xFA, 0xEF, 0xEE, 0xEB, 0xEA,
0xBF, 0xBE, 0xBB, 0xBA, 0xAF, 0xAE, 0xAB, 0xAA };
const uint8_t EInk6::LUTB[16] = {
0xFF, 0xFD, 0xF7, 0xF5, 0xDF, 0xDD, 0xD7, 0xD5,
0x7F, 0x7D, 0x77, 0x75, 0x5F, 0x5D, 0x57, 0x55 };
// PIN_LUT built from the following:
//
// for (uint32_t i = 0; i < 256; i++) {
// PIN_LUT[i] = ((i & 0b00000011) << 4) |
// (((i & 0b00001100) >> 2) << 18) |
// (((i & 0b00010000) >> 4) << 23) |
// (((i & 0b11100000) >> 5) << 25);
// }
const uint32_t EInk6::PIN_LUT[256] = {
0x00000000, 0x00000010, 0x00000020, 0x00000030, 0x00040000, 0x00040010, 0x00040020, 0x00040030,
0x00080000, 0x00080010, 0x00080020, 0x00080030, 0x000c0000, 0x000c0010, 0x000c0020, 0x000c0030,
0x00800000, 0x00800010, 0x00800020, 0x00800030, 0x00840000, 0x00840010, 0x00840020, 0x00840030,
0x00880000, 0x00880010, 0x00880020, 0x00880030, 0x008c0000, 0x008c0010, 0x008c0020, 0x008c0030,
0x02000000, 0x02000010, 0x02000020, 0x02000030,