EPD Working

This commit is contained in:
Paul Warren 2023-11-18 22:58:02 +11:00
parent e1eae34dba
commit 0c2549d1df
7 changed files with 1203 additions and 0 deletions

View File

@ -0,0 +1,108 @@
#ifndef LIB_ADAFRUIT_SSD1675BX
#define LIB_ADAFRUIT_SSD1675BX
#include <Adafruit_EPD.h>
#include <Arduino.h>
#define EPD_RAM_BW 0x10
#define EPD_RAM_RED 0x13
#define SSD1675BX_DRIVER_CONTROL 0x01
#define SSD1675BX_GATE_VOLTAGE 0x03
#define SSD1675BX_SOURCE_VOLTAGE 0x04
#define SSD1675BX_DEEP_SLEEP 0x10
#define SSD1675BX_DATA_MODE 0x11
#define SSD1675BX_SW_RESET 0x12
#define SSD1675BX_HV_READY 0x14
#define SSD1675BX_VCI_READY 0x15
#define SSD1675BX_TEMP_SELECT 0x18
#define SSD1675BX_TEMP_WRITE 0x1A
#define SSD1675BX_MASTER_ACTIVATE 0x20
#define SSD1675BX_DISP_CTRL1 0x21
#define SSD1675BX_DISP_CTRL2 0x22
#define SSD1675BX_WRITE_RAM1 0x24
#define SSD1675BX_WRITE_RAM2 0x26
#define SSD1675BX_WRITE_VCOM 0x2C
#define SSD1675BX_READ_OTP 0x2D
#define SSD1675BX_READ_STATUS 0x2F
#define SSD1675BX_WRITE_LUT 0x32
#define SSD1675BX_WRITE_DUMMY 0x3A
#define SSD1675BX_WRITE_GATELINE 0x3B
#define SSD1675BX_WRITE_BORDER 0x3C
#define SSD1675BX_SET_RAMXPOS 0x44
#define SSD1675BX_SET_RAMYPOS 0x45
#define SSD1675BX_SET_RAMXCOUNT 0x4E
#define SSD1675BX_SET_RAMYCOUNT 0x4F
#define SSD1675BX_SET_ANALOGBLOCK 0x74
#define SSD1675BX_SET_DIGITALBLOCK 0x7E
/**************************************************************************/
/*!
@brief Class for interfacing with SSD1675 (2020 BSides Badge EINK) EPD drivers
*/
/**************************************************************************/
class Adafruit_SSD1675BX : public Adafruit_EPD {
public:
Adafruit_SSD1675BX(int width, int height, int8_t SID, int8_t SCLK, int8_t DC,
int8_t RST, int8_t CS, int8_t SRCS, int8_t MISO,
int8_t BUSY = -1);
Adafruit_SSD1675BX(int width, int height, int8_t DC, int8_t RST, int8_t CS,
int8_t SRCS, int8_t BUSY = -1, SPIClass *spi = &SPI);
void begin(bool reset = true);
void powerUp();
void update();
void powerDown();
// faster drawing
void fillScreen(uint16_t color);
void drawPixel(int16_t x, int16_t y, uint16_t color);
void display();
// added so we can write white pixels
void invertBuffers();
void swapBuffers();
const static uint8_t kModeBlackWhiteRed = 0;
const static uint8_t kModeFullBlackWhite = 1;
const static uint8_t kModeNormalBlackWhite = 2;
const static uint8_t kModeFasterBlackWhite = 3;
const static uint8_t kModeFastestBlackWhite = 4;
void setMode(uint8_t mode);
uint8_t getMode() const { return mode_; };
inline bool fastMode() { return ( mode_ == kModeFasterBlackWhite) || ( mode_ == kModeFastestBlackWhite); }
void thread();
protected:
uint8_t writeRAMCommand(uint8_t index);
void setRAMAddress(uint16_t x, uint16_t y);
void busy_wait();
private:
uint16_t einkColor(uint16_t color);
uint8_t mode_;
uint16_t bw_red_color_;
// threading for speed (wait in background for hardware update to complete).
void threadInit();
void lock();
void unlock();
void renderBegin();
void renderEnd();
void uploadBegin();
void uploadEnd();
void displayBegin();
void displayEnd();
void displayQueuePush();
bool displayQueuePop();
bool displayQueueFull();
QueueDefinition *lock_;
bool render_in_progress_;
bool upload_in_progress_;
bool display_in_progress_;
uint8_t refreshes_needed_;
};
#endif

39
include/README Normal file
View File

@ -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 Normal file
View File

@ -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
platformio.ini Normal file
View File

@ -0,0 +1,17 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:pico32]
platform = espressif32
board = pico32
framework = arduino
upload_port = /dev/ttyUSB0
lib_deps =
adafruit/Adafruit EPD@^4.5.3

870
src/Adafruit_SSD1675BX.cpp Normal file
View File

@ -0,0 +1,870 @@
#include "Adafruit_SSD1675BX.h"
// We use a thread for display updates when built for ESP32
#ifdef ESP32
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
#endif
/** specification of display update instruction */
struct badge_eink_lut_entry {
/** the number of cycles the voltages are held; 0 = end of list */
uint8_t length;
/** bitmapped value containing voltages for every (old-bit, new-bit) pair:
* - bits 0,1: from 0 to 0
* - bits 2,3: from 0 to 1
* - bits 4,5: from 1 to 0
* - bits 6,7: from 1 to 1
*
* allowed values:
* - 0: VSS
* - 1: VSH
* - 2: VSL
*/
uint8_t voltages;
};
/** filters to use on a badge_eink_lut_entry structure */
enum badge_eink_lut_flags {
LUT_FLAG_NONE = 0,
LUT_FLAG_FIRST = 1, // do not depend on previous image
LUT_FLAG_PARTIAL = 2, // do not touch already correct pixels
LUT_FLAG_WHITE = 4, // white only
LUT_FLAG_BLACK = 8, // black only
LUT_FLAG_RED = 16, // red only
};
// full, includes inverting
const struct badge_eink_lut_entry badge_eink_lut_full[] = {
{ .length = 23, .voltages = 0x02, },
{ .length = 4, .voltages = 0x01, },
{ .length = 11, .voltages = 0x11, },
{ .length = 4, .voltages = 0x12, },
{ .length = 6, .voltages = 0x22, },
{ .length = 5, .voltages = 0x66, },
{ .length = 4, .voltages = 0x69, },
{ .length = 5, .voltages = 0x59, },
{ .length = 1, .voltages = 0x58, },
{ .length = 14, .voltages = 0x99, },
{ .length = 1, .voltages = 0x88, },
{ .length = 0 }
};
// full, no inversion
const struct badge_eink_lut_entry badge_eink_lut_normal[] = {
{ .length = 3, .voltages = 0x10, },
{ .length = 5, .voltages = 0x18, },
{ .length = 1, .voltages = 0x08, },
{ .length = 8, .voltages = 0x18, },
{ .length = 2, .voltages = 0x08, },
{ .length = 0 }
};
// full, no inversion, needs 2 updates for full update
const struct badge_eink_lut_entry badge_eink_lut_faster[] = {
{ .length = 1, .voltages = 0x10, },
{ .length = 8, .voltages = 0x18, },
{ .length = 1, .voltages = 0x08, },
{ .length = 0 }
};
// full, no inversion, needs 4 updates for full update
const struct badge_eink_lut_entry badge_eink_lut_fastest[] = {
{ .length = 1, .voltages = 0x10, },
{ .length = 3, .voltages = 0x18, },
{ .length = 1, .voltages = 0x08, },
{ .length = 0 }
};
static uint8_t
badge_eink_lut_conv(uint8_t voltages, enum badge_eink_lut_flags flags)
{
if (flags & LUT_FLAG_FIRST)
{
voltages |= voltages >> 4;
voltages &= 15;
if ((voltages & 3) == 3) // reserved
voltages ^= 2; // set to '1': VSH (black)
if ((voltages & 12) == 12) // reserved
voltages ^= 4; // set to '2': VSL (white)
voltages |= voltages << 4;
}
if (flags & LUT_FLAG_PARTIAL)
voltages &= 0x3c; // only keep 0->1 and 1->0
if (flags & LUT_FLAG_WHITE)
voltages &= 0xcc; // only keep 0->1 and 1->1
if (flags & LUT_FLAG_BLACK)
voltages &= 0x33; // only keep 0->0 and 1->0
return voltages;
}
// DEPG0290B01
int
badge_eink_lut_generate_depg0290rws(const struct badge_eink_lut_entry *list, enum badge_eink_lut_flags flags, uint8_t *lut)
{
//ESP_LOGD(TAG, "flags = %d.", flags);
memset(lut, 0, 70);
int pos = 0;
int spos = 0;
while (list->length != 0)
{
int len = list->length;
if (pos == 7)
{
//ESP_LOGE(TAG, "lut overflow.");
return -1; // full
}
uint8_t voltages = badge_eink_lut_conv(list->voltages, flags);
lut[0*10 + pos] |= ((voltages >> 0) & 3) << ((3-spos)*2);
lut[1*10 + pos] |= ((voltages >> 2) & 3) << ((3-spos)*2);
lut[2*10 + pos] |= ((voltages >> 4) & 3) << ((3-spos)*2);
lut[3*10 + pos] |= ((voltages >> 6) & 3) << ((3-spos)*2);
lut[5*10 + pos*5 + spos] = len;
lut[5*10 + pos*5 + spos] = len;
spos++;
if (spos == 2)
{
spos = 0;
pos++;
}
list = &list[1];
}
return 70;
}
#define BUSY_WAIT 500
#define BUSY_WAIT_FAST_MODE 120
#define FAST_MODE_REFRESHES_NEEDED 8
const unsigned char LUT_DATA[] = {
0xA0, 0x90, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x50, 0x90, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xA0, 0x90, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x50, 0x90, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x03,
0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x17, 0x41, 0x00, 0x32, 0x50, 0x2C, 0x0B,
};
/**************************************************************************/
/*!
@brief constructor if using external SRAM chip and software SPI
@param width the width of the display in pixels
@param height the height of the display in pixels
@param SID the SID pin to use
@param SCLK the SCLK pin to use
@param DC the data/command pin to use
@param RST the reset pin to use
@param CS the chip select pin to use
@param SRCS the SRAM chip select pin to use
@param MISO the MISO pin to use
@param BUSY the busy pin to use
*/
/**************************************************************************/
Adafruit_SSD1675BX::Adafruit_SSD1675BX(int width, int height, int8_t SID,
int8_t SCLK, int8_t DC, int8_t RST,
int8_t CS, int8_t SRCS, int8_t MISO,
int8_t BUSY)
: Adafruit_EPD(width, height, SID, SCLK, DC, RST, CS, SRCS, MISO, BUSY) {
mode_ = kModeBlackWhiteRed;
bw_red_color_ = EPD_BLACK;
if ((height % 8) != 0) {
height += 8 - (height % 8);
}
buffer1_size = width * height / 8;
buffer2_size = buffer1_size;
if (SRCS >= 0) {
use_sram = true;
buffer1_addr = 0;
buffer2_addr = buffer1_size;
buffer1 = buffer2 = NULL;
} else {
buffer1 = (uint8_t *)malloc(buffer1_size);
buffer2 = (uint8_t *)malloc(buffer2_size);
}
threadInit();
}
// constructor for hardware SPI - we indicate DataCommand, ChipSelect, Reset
/**************************************************************************/
/*!
@brief constructor if using on-chip RAM and hardware SPI
@param width the width of the display in pixels
@param height the height of the display in pixels
@param DC the data/command pin to use
@param RST the reset pin to use
@param CS the chip select pin to use
@param SRCS the SRAM chip select pin to use
@param BUSY the busy pin to use
*/
/**************************************************************************/
Adafruit_SSD1675BX::Adafruit_SSD1675BX(int width, int height, int8_t DC,
int8_t RST, int8_t CS, int8_t SRCS,
int8_t BUSY, SPIClass *spi)
: Adafruit_EPD(width, height, DC, RST, CS, SRCS, BUSY, spi) {
mode_ = kModeBlackWhiteRed;
bw_red_color_ = EPD_BLACK;
if ((height % 8) != 0) {
height += 8 - (height % 8);
}
buffer1_size = width * height / 8;
buffer2_size = buffer1_size;
if (SRCS >= 0) {
use_sram = true;
buffer1_addr = 0;
buffer2_addr = buffer1_size;
buffer1 = buffer2 = NULL;
} else {
buffer1 = (uint8_t *)malloc(buffer1_size);
buffer2 = (uint8_t *)malloc(buffer2_size);
}
threadInit();
}
/**************************************************************************/
/*!
@brief set display mode (sharp & colors VS fast black & white)
*/
/**************************************************************************/
void Adafruit_SSD1675BX::setMode(uint8_t mode)
{
lock();
refreshes_needed_ = 0;
while ( upload_in_progress_ || display_in_progress_ )
{
unlock();
delay(1);
lock();
}
mode_ = mode;
if ( fastMode() )
{
buffer2_size = 0;
}
else
{
buffer2_size = buffer1_size;
}
unlock();
}
/**************************************************************************/
/*!
@brief wait for busy signal to end
*/
/**************************************************************************/
void Adafruit_SSD1675BX::busy_wait(void) {
if (_busy_pin >= 0) {
// start of busy wait
uint32_t start = (uint32_t)millis();
// use 1ms delays for the first 20ms delays
for ( uint8_t i = 0; i < 20; i++ )
{
if ( !digitalRead(_busy_pin) )
{
return;
}
delay(1);
}
// wait for busy low
while (digitalRead(_busy_pin)) {
delay(10);
if ( fastMode() && displayQueueFull() )
{
uint32_t tick = (uint32_t)millis();
if ( (tick - start) > BUSY_WAIT_FAST_MODE )
{
// fast mode - stop this wait and start with "newer" data
return;
}
}
}
} else {
delay(BUSY_WAIT);
}
}
/**************************************************************************/
/*!
@brief begin communication with and set up the display.
@param reset if true the reset pin will be toggled.
*/
/**************************************************************************/
void Adafruit_SSD1675BX::begin(bool reset) {
Adafruit_EPD::begin(reset);
setBlackBuffer(0, true); // black defaults to inverted
setColorBuffer(1, false);
powerDown();
}
void Adafruit_SSD1675BX::invertBuffers() {
setBlackBuffer(0, !blackInverted);
setColorBuffer(1, !colorInverted);
}
void Adafruit_SSD1675BX::swapBuffers() {
uint8_t *tmp = buffer1;
buffer1 = buffer2;
buffer2 = tmp;
uint16_t tmp_addr = buffer1_addr;
buffer1_addr = buffer2_addr;
buffer2_addr = tmp_addr;
setBlackBuffer(0, blackInverted);
setColorBuffer(1, colorInverted);
}
/**************************************************************************/
/*!
@brief faster drawing methods
*/
/**************************************************************************/
uint16_t Adafruit_SSD1675BX::einkColor(uint16_t color)
{
if ( ( color & (0x8000 | 0x0400 | 0x0010) ) == 0 )
{
// high bits of RGB are all 0s, treat it as black
color = EPD_BLACK;
}
else if ( ( color & (0x8000 | 0x0400 | 0x0010) ) == (0x8000 | 0x0400 | 0x0010) )
{
// high bits of RGB are all 1s, treat it as white
color = EPD_WHITE;
}
else
{
// everything else is red
color = EPD_RED;
}
return color;
}
void Adafruit_SSD1675BX::fillScreen(uint16_t color)
{
renderBegin();
color = einkColor(color);
if ( mode_ != kModeBlackWhiteRed ) {
if ( color == EPD_BLACK ) {
bw_red_color_ = EPD_WHITE;
} else if ( color == EPD_WHITE ) {
bw_red_color_ = EPD_BLACK;
} else {
bw_red_color_ = EPD_BLACK;
color = EPD_WHITE;
}
}
uint8_t black_byte = ( (color == EPD_BLACK) ? 0x00 : 0xff );
if ( !fastMode() ) {
// slow mode - memset both BLACK and RED buffers.
uint8_t color_byte = ( (color == EPD_RED) ? 0xff : 0x00 );
if (use_sram) {
if (buffer1_size != 0) {
sram.erase(buffer1_addr, buffer1_size, black_byte);
}
if (buffer2_size != 0) {
sram.erase(buffer2_addr, buffer2_size, color_byte);
}
} else {
if (buffer1 && (buffer1_size != 0)) {
memset(buffer1, black_byte, buffer1_size);
}
if (buffer2 && (buffer2_size != 0)) {
memset(buffer2, color_byte, buffer2_size);
}
}
} else {
// fast mode - render into buffer 2, display buffer 1, then swap etc.
if (use_sram) {
sram.erase(buffer2_addr, buffer1_size, black_byte);
} else {
memset(buffer2, black_byte, buffer1_size);
}
}
}
void Adafruit_SSD1675BX::drawPixel(int16_t x, int16_t y, uint16_t color)
{
if ( !fastMode() )
{
// fix color
color = einkColor(color);
// what to do with
if ( mode_ != kModeBlackWhiteRed ) {
color = ( color == EPD_RED ) ? bw_red_color_ : color;
}
// hack
y = height() - 1 - y;
// slow mode - use Adafruit_EPD to draw the pixel.
if ( color == EPD_WHITE ) {
invertBuffers();
Adafruit_EPD::drawPixel(x, y, EPD_BLACK);
Adafruit_EPD::drawPixel(x, y, EPD_RED);
invertBuffers();
return;
}
Adafruit_EPD::drawPixel(x, y, color);
} else {
// fast mode - almost a 1-to-1 copy of drawPixel from Adafruit_EPD - with some optimisations
// check bounds.
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height()))
return;
if ( ( color & (0x8000 | 0x0400 | 0x0010) ) == 0 )
{
// high bits of RGB are all 0s, treat it as black
color = EPD_BLACK;
}
else if ( ( color & (0x8000 | 0x0400 | 0x0010) ) == (0x8000 | 0x0400 | 0x0010) )
{
// high bits of RGB are all 1s, treat it as white
color = EPD_WHITE;
}
else
{
// everything else is red
color = bw_red_color_;
}
uint8_t *pBuf;
// check rotation, move pixel around if necessary
switch (getRotation()) {
case 0:
y = height() - 1 - y;
break;
case 1:
EPD_swap(x, y);
break;
case 2:
y = height() - 1 - y;
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
break;
case 3:
EPD_swap(x, y);
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
break;
}
uint16_t addr = ((uint32_t)(WIDTH - 1 - x) * (uint32_t)HEIGHT + y) >> 3;
uint8_t c;
if (use_sram) {
addr = buffer2_addr + addr;
c = sram.read8(addr);
pBuf = &c;
if (color == EPD_BLACK) {
*pBuf &= ~(1 << (7 - y & 0x7));
} else {
*pBuf |= (1 << (7 - y & 0x7));
}
sram.write8(addr, *pBuf);
} else {
pBuf = buffer2 + addr;
if (color == EPD_BLACK) {
*pBuf &= ~(1 << (7 - y & 0x7));
} else {
*pBuf |= (1 << (7 - y & 0x7));
}
}
}
}
/**************************************************************************/
/*!
@brief signal the display to update
*/
/**************************************************************************/
void Adafruit_SSD1675BX::update() {
uint8_t mode_old = mode_;
uploadEnd();
if ( mode_old != mode_ )
{
// mode has changed, stop display.
return;
}
displayBegin();
uint8_t buf[1];
uint8_t lut[70];
memset(lut, 0, sizeof(lut));
if ( mode_ != kModeBlackWhiteRed )
{
switch ( mode_ )
{
case kModeFullBlackWhite:
badge_eink_lut_generate_depg0290rws(badge_eink_lut_full, LUT_FLAG_FIRST, lut);
EPD_command(SSD1675BX_WRITE_LUT, lut, sizeof(lut));
break;
case kModeNormalBlackWhite:
badge_eink_lut_generate_depg0290rws(badge_eink_lut_normal, LUT_FLAG_FIRST, lut);
EPD_command(SSD1675BX_WRITE_LUT, lut, sizeof(lut));
break;
case kModeFasterBlackWhite:
badge_eink_lut_generate_depg0290rws(badge_eink_lut_faster, LUT_FLAG_FIRST, lut);
EPD_command(SSD1675BX_WRITE_LUT, lut, sizeof(lut));
break;
case kModeFastestBlackWhite:
badge_eink_lut_generate_depg0290rws(badge_eink_lut_fastest, LUT_FLAG_FIRST, lut);
EPD_command(SSD1675BX_WRITE_LUT, lut, sizeof(lut));
break;
default:
break;
}
}
// display update sequence
buf[0] = 0xC7;
EPD_command(SSD1675BX_DISP_CTRL2, buf, 1);
EPD_command(SSD1675BX_MASTER_ACTIVATE);
busy_wait();
if (_busy_pin <= -1) {
delay(3000);
}
displayEnd();
}
/**************************************************************************/
/*!
@brief start up the display
*/
/**************************************************************************/
void Adafruit_SSD1675BX::powerUp() {
uint8_t buf[5];
hardwareReset();
delay(1);
busy_wait();
// soft reset
EPD_command(SSD1675BX_SW_RESET);
busy_wait();
// set analog block control
buf[0] = 0x54;
EPD_command(SSD1675BX_SET_ANALOGBLOCK, buf, 1);
// set digital block control
buf[0] = 0x3B;
EPD_command(SSD1675BX_SET_DIGITALBLOCK, buf, 1);
// driver output control
buf[0] = WIDTH - 1;
buf[1] = (WIDTH - 1) >> 8;
buf[2] = 0x00;
EPD_command(SSD1675BX_DRIVER_CONTROL, buf, 3);
// Data entry sequence
buf[0] = 0x01;
EPD_command(SSD1675BX_DATA_MODE, buf, 1);
// Set ram X start/end postion
buf[0] = 0x00;
buf[1] = (HEIGHT / 8) - 1;
EPD_command(SSD1675BX_SET_RAMXPOS, buf, 2);
// Set ram Y start/end postion
buf[0] = WIDTH - 1;
buf[1] = (WIDTH - 1) >> 8;
buf[2] = 0x00;
buf[3] = 0x00;
EPD_command(SSD1675BX_SET_RAMYPOS, buf, 4);
// border color
buf[0] = 0x01;
EPD_command(SSD1675BX_WRITE_BORDER, buf, 1);
// Vcom Voltage
buf[0] = 0x36;
EPD_command(SSD1675BX_WRITE_VCOM, buf, 1);
// Set gate voltage
buf[0] = LUT_DATA[100];
EPD_command(SSD1675BX_GATE_VOLTAGE, buf, 1);
// Set source voltage
buf[0] = LUT_DATA[101];
buf[1] = LUT_DATA[102];
buf[2] = LUT_DATA[103];
EPD_command(SSD1675BX_SOURCE_VOLTAGE, buf, 3);
// Set dummy line period
buf[0] = LUT_DATA[105];
EPD_command(SSD1675BX_WRITE_DUMMY, buf, 1);
// Set gate line width
buf[0] = LUT_DATA[106];
EPD_command(SSD1675BX_WRITE_GATELINE, buf, 1);
EPD_command(SSD1675BX_WRITE_LUT, LUT_DATA, 99);
// Set internal temperature sensor
buf[0] = 0x80;
EPD_command(SSD1675BX_TEMP_SELECT, buf, 1);
if ( !fastMode() ) {
// slow mode - needed steps (but can be skipped - with artifacts - in fast mode).
buf[0] = 0xB1;
EPD_command(SSD1675BX_DISP_CTRL2, buf, 1);
EPD_command(SSD1675BX_MASTER_ACTIVATE);
}
busy_wait();
uploadBegin();
}
/**************************************************************************/
/*!
@brief wind down the display
*/
/**************************************************************************/
void Adafruit_SSD1675BX::powerDown() {
uint8_t buf[1];
// Only deep sleep if we can get out of it
if (_reset_pin >= 0) {
// deep sleep
buf[0] = 0x01;
EPD_command(SSD1675BX_DEEP_SLEEP, buf, 1);
delay(1);
} else {
EPD_command(SSD1675BX_SW_RESET);
busy_wait();
}
}
/**************************************************************************/
/*!
@brief Send the specific command to start writing to EPD display RAM
@param index The index for which buffer to write (0 or 1 or tri-color
displays) Ignored for monochrome displays.
@returns The byte that is read from SPI at the same time as sending the
command
*/
/**************************************************************************/
uint8_t Adafruit_SSD1675BX::writeRAMCommand(uint8_t index) {
if (index == 0) {
return EPD_command(SSD1675_WRITE_RAM1, false);
}
if (index == 1) {
return EPD_command(SSD1675_WRITE_RAM2, false);
}
return 0;
}
/**************************************************************************/
/*!
@brief Some displays require setting the RAM address pointer
@param x X address counter value
@param y Y address counter value
*/
/**************************************************************************/
void Adafruit_SSD1675BX::setRAMAddress(uint16_t x, uint16_t y) {
uint8_t buf[2];
// Set RAM X address counter
buf[0] = 0x00;
EPD_command(SSD1675_SET_RAMXCOUNT, buf, 1);
// Set RAM Y address counter
buf[0] = WIDTH - 1;
buf[1] = (WIDTH - 1) >> 8;
EPD_command(SSD1675_SET_RAMYCOUNT, buf, 2);
}
#ifdef ESP32
void display_thread(void *arg) {
reinterpret_cast<Adafruit_SSD1675BX*>(arg)->thread();
};
#endif
void Adafruit_SSD1675BX::threadInit() {
#ifdef ESP32
lock_ = xSemaphoreCreateMutex();
xTaskCreate(&display_thread, "display_thread", 8192, (void*)this, 5, NULL);
#endif
render_in_progress_ = false;
upload_in_progress_ = false;
display_in_progress_ = false;
refreshes_needed_ = 0;
}
void Adafruit_SSD1675BX::lock() {
#ifdef ESP32
xSemaphoreTake(lock_, portMAX_DELAY);
#endif
}
void Adafruit_SSD1675BX::unlock() {
#ifdef ESP32
xSemaphoreGive(lock_);
#endif
}
void Adafruit_SSD1675BX::thread() {
while(1) {
delay(10);
if ( fastMode() && displayQueuePop() )
{
Adafruit_EPD::display();
}
}
}
void Adafruit_SSD1675BX::displayQueuePush() {
lock();
refreshes_needed_ = FAST_MODE_REFRESHES_NEEDED;
unlock();
}
bool Adafruit_SSD1675BX::displayQueuePop() {
uint8_t refreshes_needed = 0;
lock();
refreshes_needed = refreshes_needed_;
refreshes_needed_ = refreshes_needed_ > 0 ? refreshes_needed_ - 1 : 0;
unlock();
return refreshes_needed > 0;
}
bool Adafruit_SSD1675BX::displayQueueFull() {
uint8_t refreshes_needed = 0;
lock();
refreshes_needed = refreshes_needed_;
unlock();
return refreshes_needed == FAST_MODE_REFRESHES_NEEDED;
}
static uint32_t main_tick_ = 0;
void Adafruit_SSD1675BX::display(void) {
renderEnd();
#ifdef ESP32
if ( !fastMode() )
{
// slow mode - just display
Adafruit_EPD::display();
}
else
{
// fast mode - queue display refresh
displayQueuePush();
}
#else
// non-esp32, just display (on esp32, this is handled in a thread)
Adafruit_EPD::display();
#endif
}
void Adafruit_SSD1675BX::renderBegin() {
lock();
if ( render_in_progress_ ) {
unlock();
return;
}
render_in_progress_ = true;
unlock();
}
void Adafruit_SSD1675BX::renderEnd() {
lock();
if ( !render_in_progress_ ) {
unlock();
return;
}
render_in_progress_ = false;
if ( fastMode() )
{
// fast mode - wait for upload to complete before swapping buffers.
while ( upload_in_progress_ )
{
unlock();
delay(1);
lock();
}
swapBuffers();
}
unlock();
}
void Adafruit_SSD1675BX::uploadBegin() {
lock();
if ( upload_in_progress_ ) {
unlock();
return;
}
upload_in_progress_ = true;
unlock();
}
void Adafruit_SSD1675BX::uploadEnd() {
lock();
if ( !upload_in_progress_ ) {
unlock();
return;
}
upload_in_progress_ = false;
unlock();
}
void Adafruit_SSD1675BX::displayBegin() {
lock();
if ( display_in_progress_ ) {
unlock();
return;
}
display_in_progress_ = true;
unlock();
}
void Adafruit_SSD1675BX::displayEnd() {
lock();
if ( !display_in_progress_ ) {
unlock();
return;
}
display_in_progress_ = false;
unlock();
}

112
src/main.cpp Normal file
View File

@ -0,0 +1,112 @@
/***************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
MIT license, all text above must be included in any redistribution
****************************************************/
#include "Adafruit_EPD.h"
#include "Adafruit_SSD1675BX.h"
#include <SPI.h>
//#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
//#define VSPI FSPI
//#endif
//static const int spiClk = 10000000;
SPIClass SPI3(HSPI);
#define HSPI_MISO 12
#define HSPI_MOSI 13
#define HSPI_SCLK 14
#define HSPI_SS 15
// DISPLAY
#define DISPLAY_WIDTH ((int16_t)(296))
#define DISPLAY_HEIGHT ((int16_t)(128))
#define DISPLAY_COLOR_BLACK ((uint16_t)(0x0000))
#define DISPLAY_COLOR_WHITE ((uint16_t)(0xffff))
#define DISPLAY_COLOR_RED ((uint16_t)(0xf800))
#define EPD_DC 25
#define EPD_CS 27
#define EPD_BUSY 35 // can set to -1 to not use a pin (will wait a fixed delay)
#define SRAM_CS -1
#define EPD_RESET 26 // can set to -1 and share with microcontroller Reset!
#define EPD_SPI &SPI3
Adafruit_SSD1675BX display(DISPLAY_WIDTH, DISPLAY_HEIGHT, EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY, EPD_SPI);
#define COLOR1 EPD_BLACK
#define COLOR2 EPD_RED
void testdrawtext(const char *text, uint16_t color) {
display.setCursor(0, 0);
display.setTextColor(color);
display.setTextWrap(true);
display.print(text);
}
void setup() {
Serial.begin(115200);
// while (!Serial) { delay(10); }
Serial.println("Adafruit EPD test");
// Serial.println("Setting up extra SPI bits");
// vspi = new SPIClass(VSPI);
// hspi = new SPIClass(HSPI);
// vspi->begin();
// hspi->begin(HSPI_SCLK, HSPI_MISO, HSPI_MOSI, HSPI_SS);
// pinMode(hspi->pinSS(), OUTPUT); //HSPI SS);
display.setRotation(1);
display.clearBuffer();
display.begin();
// large block of text
display.clearBuffer();
testdrawtext(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur "
"adipiscing ante sed nibh tincidunt feugiat. Maecenas enim massa, "
"fringilla sed malesuada et, malesuada sit amet turpis. Sed porttitor "
"neque ut ante pretium vitae malesuada nunc bibendum. Nullam aliquet "
"ultrices massa eu hendrerit. Ut sed nisi lorem. In vestibulum purus a "
"tortor imperdiet posuere. ",
COLOR1);
display.display();
delay(5000);
display.clearBuffer();
for (int16_t i = 0; i < display.width(); i += 4) {
display.drawLine(0, 0, i, display.height() - 1, COLOR1);
}
for (int16_t i = 0; i < display.height(); i += 4) {
display.drawLine(display.width() - 1, 0, 0, i,
COLOR2); // on grayscale this will be mid-gray
}
display.display();
Serial.println("End of EPD Test");
display.setMode(Adafruit_SSD1675BX::kModeBlackWhiteRed);
display.fillScreen(DISPLAY_COLOR_BLACK);
display.display();
display.fillScreen(DISPLAY_COLOR_RED);
display.display();
display.fillScreen(DISPLAY_COLOR_WHITE);
display.display();
}
void loop() {
// don't do anything!
}

11
test/README Normal file
View File

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html