Tasmota/lib/esp-epaper-29-ws-20171230-g.../components/epaper-29-ws/epaper-29-ws.c

696 lines
23 KiB
C

// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
//
// 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/xtensa_api.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/ringbuf.h"
#include "esp_log.h"
#include "epaper-29-ws.h"
static const char* TAG = "ePaper Driver";
#define EPAPER_QUE_SIZE_DEFAULT 10
const unsigned char lut_full_update[] =
{
0x02, 0x02, 0x01, 0x11, 0x12, 0x12, 0x22, 0x22,
0x66, 0x69, 0x69, 0x59, 0x58, 0x99, 0x99, 0x88,
0x00, 0x00, 0x00, 0x00, 0xF8, 0xB4, 0x13, 0x51,
0x35, 0x51, 0x51, 0x19, 0x01, 0x00
};
// This LUT is not yet used in the code below
const unsigned char lut_partial_update[] =
{
0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static portMUX_TYPE epaper_spinlock = portMUX_INITIALIZER_UNLOCKED;
#define EPAPER_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux)
#define EPAPER_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux)
// LCD data/command
typedef struct {
uint8_t dc_io;
uint8_t dc_level;
} epaper_dc_t;
typedef struct {
spi_device_handle_t bus;
epaper_conf_t pin; /* EPD properties */
epaper_paint_t paint; /* Paint properties */
epaper_dc_t dc;
xSemaphoreHandle spi_mux;
} epaper_dev_t;
/* This function is called (in irq context!) just before a transmission starts.
* It will set the D/C line to the value indicated in the user field
*/
static void iot_epaper_pre_transfer_callback(spi_transaction_t *t)
{
epaper_dc_t *dc = (epaper_dc_t *) t->user;
gpio_set_level((int)dc->dc_io, (int)dc->dc_level);
}
static esp_err_t _iot_epaper_spi_send(spi_device_handle_t spi, spi_transaction_t* t)
{
return spi_device_transmit(spi, t);
}
void iot_epaper_send(spi_device_handle_t spi, const uint8_t *data, int len, epaper_dc_t *dc)
{
esp_err_t ret;
if (len == 0) {
return; // no need to send anything
}
spi_transaction_t t = {
.length = len * 8, // Len is in bytes, transaction length is in bits.
.tx_buffer = data,
.user = (void *) dc,
};
ret = _iot_epaper_spi_send(spi, &t);
assert(ret == ESP_OK);
}
static void iot_epaper_send_command(epaper_handle_t dev, unsigned char command)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
device->dc.dc_io = device->pin.dc_pin;
device->dc.dc_level = device->pin.dc_lev_cmd;
iot_epaper_send(device->bus, &command, 1, &device->dc);
}
static void iot_epaper_send_byte(epaper_handle_t dev, const uint8_t data)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
device->dc.dc_io = device->pin.dc_pin;
device->dc.dc_level = device->pin.dc_lev_data;
iot_epaper_send(device->bus, &data, 1, &device->dc);
}
static void iot_epaper_send_data(epaper_handle_t dev, const uint8_t *data, int length)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
device->dc.dc_io = device->pin.dc_pin;
device->dc.dc_level = device->pin.dc_lev_data;
// To Do: original driver was sending byte by byte
// Pay attention to possible performance issues
iot_epaper_send(device->bus, data, length, &device->dc);
ESP_LOGI(TAG, "SPI data sent %d", length);
}
static void iot_epaper_paint_init(epaper_handle_t dev, unsigned char* image, int width, int height)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
device->paint.rotate = E_PAPER_ROTATE_0;
device->paint.image = image;
/* 1 byte = 8 pixels, so the width should be the multiple of 8 */
device->paint.width = width % 8 ? width + 8 - (width % 8) : width;
device->paint.height = height;
}
static void iot_epaper_gpio_init(epaper_conf_t * pin)
{
gpio_pad_select_gpio(pin->reset_pin);
gpio_set_direction(pin->reset_pin, GPIO_MODE_OUTPUT);
gpio_set_level(pin->reset_pin, pin->rst_active_level);
gpio_pad_select_gpio(pin->dc_pin);
gpio_set_direction(pin->dc_pin, GPIO_MODE_OUTPUT);
gpio_set_level(pin->dc_pin, 1);
ets_delay_us(10000);
gpio_set_level(pin->dc_pin, 0);
gpio_pad_select_gpio(pin->busy_pin);
gpio_set_direction(pin->busy_pin, GPIO_MODE_INPUT);
gpio_set_pull_mode(pin->busy_pin, GPIO_PULLUP_ONLY);
}
static esp_err_t iot_epaper_spi_init(epaper_handle_t dev, spi_device_handle_t *e_spi, epaper_conf_t *pin)
{
esp_err_t ret;
spi_bus_config_t buscfg = {
.miso_io_num = -1, // MISO not used, we are transferring to the slave only
.mosi_io_num = pin->mosi_pin,
.sclk_io_num = pin->sck_pin,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
// The maximum size sent below covers the case
// when the whole frame buffer is transferred to the slave
.max_transfer_sz = EPD_WIDTH * EPD_HEIGHT / 8,
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = pin->clk_freq_hz,
.mode = 0, // SPI mode 0
.spics_io_num = pin->cs_pin,
// To Do: clarify what does it mean
.queue_size = EPAPER_QUE_SIZE_DEFAULT,
// We are sending only in one direction (to the ePaper slave)
.flags = (SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE),
//Specify pre-transfer callback to handle D/C line
.pre_cb = iot_epaper_pre_transfer_callback,
};
ret = spi_bus_initialize(pin->spi_host, &buscfg, 1);
assert(ret == ESP_OK);
ret = spi_bus_add_device(pin->spi_host, &devcfg, e_spi);
assert(ret == ESP_OK);
return ret;
}
static void iot_epaper_set_lut(epaper_handle_t dev)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
iot_epaper_send_command(dev, E_PAPER_WRITE_LUT_REGISTER);
iot_epaper_send_data(dev, lut_full_update, sizeof(lut_full_update));
xSemaphoreGiveRecursive(device->spi_mux);
}
static void iot_epaper_epd_init(epaper_handle_t dev)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
iot_epaper_reset(dev);
/* This part of code is ePaper module specific
* It has been copied from the instructions as it is
*/
iot_epaper_send_command(dev, E_PAPER_DRIVER_OUTPUT_CONTROL);
iot_epaper_send_byte(dev, (EPD_HEIGHT - 1) & 0xff);
iot_epaper_send_byte(dev, ((EPD_HEIGHT - 1) >> 8) & 0xff);
iot_epaper_send_byte(dev, 0x00); // GD = 0; SM = 0; TB = 0
iot_epaper_send_command(dev, E_PAPER_BOOSTER_SOFT_START_CONTROL);
iot_epaper_send_byte(dev, 0xD7);
iot_epaper_send_byte(dev, 0xD6);
iot_epaper_send_byte(dev, 0x9D);
iot_epaper_send_command(dev, E_PAPER_WRITE_VCOM_REGISTER);
iot_epaper_send_byte(dev, 0xA8); // VCOM 7C
iot_epaper_send_command(dev, E_PAPER_SET_DUMMY_LINE_PERIOD);
iot_epaper_send_byte(dev, 0x1A); // 4 dummy lines per gate
iot_epaper_send_command(dev, E_PAPER_SET_GATE_TIME);
iot_epaper_send_byte(dev, 0x08); // 2us per line
iot_epaper_send_command(dev, E_PAPER_DATA_ENTRY_MODE_SETTING);
iot_epaper_send_byte(dev, 0x03); // X increment; Y increment
iot_epaper_set_lut(dev);
xSemaphoreGiveRecursive(device->spi_mux);
}
epaper_handle_t iot_epaper_create(spi_device_handle_t bus, epaper_conf_t *epconf)
{
epaper_dev_t* dev = (epaper_dev_t*) calloc(1, sizeof(epaper_dev_t));
dev->spi_mux = xSemaphoreCreateRecursiveMutex();
uint8_t* frame_buf = (unsigned char*) heap_caps_malloc(
(epconf->width * epconf->height / 8), MALLOC_CAP_8BIT);
if (frame_buf == NULL) {
ESP_LOGE(TAG, "frame_buffer malloc fail");
return NULL;
}
iot_epaper_gpio_init(epconf);
ESP_LOGD(TAG, "gpio init ok");
if (bus) {
dev->bus = bus;
} else {
iot_epaper_spi_init(dev, &dev->bus, epconf);
ESP_LOGD(TAG, "spi init ok");
}
dev->pin = *epconf;
iot_epaper_epd_init(dev);
iot_epaper_paint_init(dev, frame_buf, epconf->width, epconf->height);
return (epaper_handle_t) dev;
}
esp_err_t iot_epaper_delete(epaper_handle_t dev, bool del_bus)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
iot_epaper_sleep(dev);
spi_bus_remove_device(device->bus);
if (del_bus) {
spi_bus_free(device->pin.spi_host);
}
vSemaphoreDelete(device->spi_mux);
if (device->paint.image) {
free(device->paint.image);
device->paint.image = NULL;
}
free(device);
return ESP_OK;
}
int iot_epaper_get_width(epaper_handle_t dev)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
return device->paint.width;
}
void iot_epaper_set_width(epaper_handle_t dev, int width)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
device->paint.width = width % 8 ? width + 8 - (width % 8) : width;
xSemaphoreGiveRecursive(device->spi_mux);
}
int iot_epaper_get_height(epaper_handle_t dev)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
return device->paint.height;
}
void iot_epaper_set_height(epaper_handle_t dev, int height)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
device->paint.height = height;
xSemaphoreGiveRecursive(device->spi_mux);
}
int iot_epaper_get_rotate(epaper_handle_t dev)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
return device->paint.rotate;
}
void iot_epaper_set_rotate(epaper_handle_t dev, int rotate)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
device->paint.rotate = rotate;
xSemaphoreGiveRecursive(device->spi_mux);
}
/**
* @brief: Getters and Setters
*/
unsigned char* iot_epaper_get_image(epaper_handle_t dev)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
return device->paint.image;
}
/**
* @brief: this draws a pixel by absolute coordinates.
* this function won't be affected by the rotate parameter.
*/
static void iot_epaper_draw_absolute_pixel(epaper_handle_t dev, int x, int y, int colored)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
if (x < 0 || x >= device->paint.width || y < 0 || y >= device->paint.height) {
return;
}
EPAPER_ENTER_CRITICAL(&epaper_spinlock);
if (device->pin.color_inv) {
if (colored) {
device->paint.image[(x + y * device->paint.width) / 8] |= 0x80 >> (x % 8);
} else {
device->paint.image[(x + y * device->paint.width) / 8] &= ~(0x80 >> (x % 8));
}
} else {
if (colored) {
device->paint.image[(x + y * device->paint.width) / 8] &= ~(0x80 >> (x % 8));
} else {
device->paint.image[(x + y * device->paint.width) / 8] |= 0x80 >> (x % 8);
}
}
EPAPER_EXIT_CRITICAL(&epaper_spinlock);
}
void iot_epaper_clean_paint(epaper_handle_t dev, int colored)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
for (int x = 0; x < device->paint.width; x++) {
for (int y = 0; y < device->paint.height; y++) {
iot_epaper_draw_absolute_pixel(dev, x, y, colored);
}
}
xSemaphoreGiveRecursive(device->spi_mux);
}
/**
* @brief: this displays a string on the frame buffer but not refresh
*/
void iot_epaper_draw_string(epaper_handle_t dev, int x, int y, const char* text, epaper_font_t* font, int colored)
{
const char* p_text = text;
unsigned int counter = 0;
int refcolumn = x;
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
/* Send the string character by character on EPD */
while (*p_text != 0) {
/* Display one character on EPD */
iot_epaper_draw_char(dev, refcolumn, y, *p_text, font, colored);
/* Decrement the column position by 16 */
refcolumn += font->width;
/* Point on the next character */
p_text++;
counter++;
}
xSemaphoreGiveRecursive(device->spi_mux);
}
/**
* @brief: this draws a pixel by the coordinates
*/
void iot_epaper_draw_pixel(epaper_handle_t dev, int x, int y, int colored)
{
int point_temp;
epaper_dev_t* device = (epaper_dev_t*) dev;
if (device->paint.rotate == E_PAPER_ROTATE_0) {
if (x < 0 || x >= device->paint.width || y < 0 || y >= device->paint.height) {
return;
}
iot_epaper_draw_absolute_pixel(dev, x, y, colored);
} else if (device->paint.rotate == E_PAPER_ROTATE_90) {
if (x < 0 || x >= device->paint.height || y < 0 || y >= device->paint.width) {
return;
}
point_temp = x;
x = device->paint.width - y;
y = point_temp;
iot_epaper_draw_absolute_pixel(dev, x, y, colored);
} else if (device->paint.rotate == E_PAPER_ROTATE_180) {
if (x < 0 || x >= device->paint.width || y < 0 || y >= device->paint.height) {
return;
}
x = device->paint.width - x;
y = device->paint.height - y;
iot_epaper_draw_absolute_pixel(dev, x, y, colored);
} else if (device->paint.rotate == E_PAPER_ROTATE_270) {
if (x < 0 || x >= device->paint.height || y < 0 || y >= device->paint.width) {
return;
}
point_temp = x;
x = y;
y = device->paint.height - point_temp;
iot_epaper_draw_absolute_pixel(dev, x, y, colored);
}
}
/**
* @brief: this draws a character on the frame buffer but not refresh
*/
void iot_epaper_draw_char(epaper_handle_t dev, int x, int y, char ascii_char, epaper_font_t* font, int colored)
{
int i, j;
unsigned int char_offset = (ascii_char - ' ') * font->height * (font->width / 8 + (font->width % 8 ? 1 : 0));
const unsigned char* ptr = &font->font_table[char_offset];
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
for (j = 0; j < font->height; j++) {
for (i = 0; i < font->width; i++) {
if (*ptr & (0x80 >> (i % 8))) {
iot_epaper_draw_pixel(dev, x + i, y + j, colored);
}
if (i % 8 == 7) {
ptr++;
}
}
if (font->width % 8 != 0) {
ptr++;
}
}
xSemaphoreGiveRecursive(device->spi_mux);
}
/**
* @brief: this draws a line on the frame buffer
*/
void iot_epaper_draw_line(epaper_handle_t dev, int x0, int y0, int x1, int y1,
int colored)
{
/* Bresenham algorithm */
int dx = x1 - x0 >= 0 ? x1 - x0 : x0 - x1;
int sx = x0 < x1 ? 1 : -1;
int dy = y1 - y0 <= 0 ? y1 - y0 : y0 - y1;
int sy = y0 < y1 ? 1 : -1;
int err = dx + dy;
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
while ((x0 != x1) && (y0 != y1)) {
iot_epaper_draw_pixel(dev, x0, y0, colored);
if (2 * err >= dy) {
err += dy;
x0 += sx;
}
if (2 * err <= dx) {
err += dx;
y0 += sy;
}
}
xSemaphoreGiveRecursive(device->spi_mux);
}
/**
* @brief: this draws a horizontal line on the frame buffer
*/
void iot_epaper_draw_horizontal_line(epaper_handle_t dev, int x, int y, int width, int colored)
{
int i;
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
for (i = x; i < x + width; i++) {
iot_epaper_draw_pixel(dev, i, y, colored);
}
xSemaphoreGiveRecursive(device->spi_mux);
}
/**
* @brief: this draws a vertical line on the frame buffer
*/
void iot_epaper_draw_vertical_line(epaper_handle_t dev, int x, int y, int height, int colored)
{
int i;
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
for (i = y; i < y + height; i++) {
iot_epaper_draw_pixel(dev, x, i, colored);
}
xSemaphoreGiveRecursive(device->spi_mux);
}
/**
* @brief: this draws a rectangle
*/
void iot_epaper_draw_rectangle(epaper_handle_t dev, int x0, int y0, int x1, int y1, int colored)
{
int min_x, min_y, max_x, max_y;
min_x = x1 > x0 ? x0 : x1;
max_x = x1 > x0 ? x1 : x0;
min_y = y1 > y0 ? y0 : y1;
max_y = y1 > y0 ? y1 : y0;
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
iot_epaper_draw_horizontal_line(dev, min_x, min_y, max_x - min_x + 1, colored);
iot_epaper_draw_horizontal_line(dev, min_x, max_y, max_x - min_x + 1, colored);
iot_epaper_draw_vertical_line(dev, min_x, min_y, max_y - min_y + 1, colored);
iot_epaper_draw_vertical_line(dev, max_x, min_y, max_y - min_y + 1, colored);
xSemaphoreGiveRecursive(device->spi_mux);
}
/**
* @brief: this draws a filled rectangle
*/
void ior_epaper_draw_filled_rectangle(epaper_handle_t dev, int x0, int y0, int x1, int y1, int colored)
{
int min_x, min_y, max_x, max_y;
int i;
min_x = x1 > x0 ? x0 : x1;
max_x = x1 > x0 ? x1 : x0;
min_y = y1 > y0 ? y0 : y1;
max_y = y1 > y0 ? y1 : y0;
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
for (i = min_x; i <= max_x; i++) {
iot_epaper_draw_vertical_line(dev, i, min_y, max_y - min_y + 1, colored);
}
xSemaphoreGiveRecursive(device->spi_mux);
}
/**
* @brief: this draws a circle
*/
void iot_epaper_draw_circle(epaper_handle_t dev, int x, int y, int radius,
int colored)
{
/* Bresenham algorithm */
int x_pos = -radius;
int y_pos = 0;
int err = 2 - 2 * radius;
int e2;
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
do {
iot_epaper_draw_pixel(dev, x - x_pos, y + y_pos, colored);
iot_epaper_draw_pixel(dev, x + x_pos, y + y_pos, colored);
iot_epaper_draw_pixel(dev, x + x_pos, y - y_pos, colored);
iot_epaper_draw_pixel(dev, x - x_pos, y - y_pos, colored);
e2 = err;
if (e2 <= y_pos) {
err += ++y_pos * 2 + 1;
if (-x_pos == y_pos && e2 <= x_pos) {
e2 = 0;
}
}
if (e2 > x_pos) {
err += ++x_pos * 2 + 1;
}
} while (x_pos <= 0);
xSemaphoreGiveRecursive(device->spi_mux);
}
/**
* @brief: this draws a filled circle
*/
void iot_epaper_draw_filled_circle(epaper_handle_t dev, int x, int y, int radius, int colored)
{
/* Bresenham algorithm */
int x_pos = -radius;
int y_pos = 0;
int err = 2 - 2 * radius;
int e2;
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
do {
iot_epaper_draw_pixel(dev, x - x_pos, y + y_pos, colored);
iot_epaper_draw_pixel(dev, x + x_pos, y + y_pos, colored);
iot_epaper_draw_pixel(dev, x + x_pos, y - y_pos, colored);
iot_epaper_draw_pixel(dev, x - x_pos, y - y_pos, colored);
iot_epaper_draw_horizontal_line(dev, x + x_pos, y + y_pos, 2 * (-x_pos) + 1, colored);
iot_epaper_draw_horizontal_line(dev, x + x_pos, y - y_pos, 2 * (-x_pos) + 1, colored);
e2 = err;
if (e2 <= y_pos) {
err += ++y_pos * 2 + 1;
if (-x_pos == y_pos && e2 <= x_pos) {
e2 = 0;
}
}
if (e2 > x_pos) {
err += ++x_pos * 2 + 1;
}
} while (x_pos <= 0);
xSemaphoreGiveRecursive(device->spi_mux);
}
void iot_epaper_wait_idle(epaper_handle_t dev)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
while (gpio_get_level((gpio_num_t) device->pin.busy_pin) == device->pin.busy_active_level) {
vTaskDelay(10 / portTICK_RATE_MS);
}
}
void iot_epaper_reset(epaper_handle_t dev)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
gpio_set_level((gpio_num_t) device->pin.reset_pin, (~(device->pin.rst_active_level)) & 0x1);
ets_delay_us(200);
gpio_set_level((gpio_num_t) device->pin.reset_pin, (device->pin.rst_active_level) & 0x1); //module reset
ets_delay_us(200);
gpio_set_level((gpio_num_t) device->pin.reset_pin, (~(device->pin.rst_active_level)) & 0x1);
iot_epaper_wait_idle(dev);
xSemaphoreGiveRecursive(device->spi_mux);
}
/* This function has been exposed to implement partial updates of the image
* To Do: implement partial updates of the image
*/
void iot_set_ram_area(epaper_handle_t dev, int x_start, int y_start, int x_end, int y_end)
{
iot_epaper_send_command(dev, E_PAPER_SET_RAM_X_ADDRESS_START_END_POSITION);
iot_epaper_send_byte(dev, x_start >> 3); // 8 pixels per byte
iot_epaper_send_byte(dev, x_end >> 3); // 8 pixels per byte
iot_epaper_send_command(dev, E_PAPER_SET_RAM_Y_ADDRESS_START_END_POSITION);
iot_epaper_send_byte(dev, y_start & 0xff);
iot_epaper_send_byte(dev, y_start >> 8);
iot_epaper_send_byte(dev, y_end & 0xff);
iot_epaper_send_byte(dev, y_end >> 8);
}
/* This function has been exposed to implement partial updates of the image
* To Do: implement partial updates of the image
*/
void iot_set_ram_address_counter(epaper_handle_t dev, int x, int y)
{
iot_epaper_send_command(dev, E_PAPER_SET_RAM_X_ADDRESS_COUNTER);
iot_epaper_send_byte(dev, x >> 3); // 8 pixels per byte
iot_epaper_send_command(dev, E_PAPER_SET_RAM_Y_ADDRESS_COUNTER);
iot_epaper_send_byte(dev, y & 0xff);
iot_epaper_send_byte(dev, y >> 8);
}
/* This transfer to the display the whole image frame
* To Do: implement partial updates of the image
*/
void iot_epaper_display_frame(epaper_handle_t dev, const unsigned char* frame_buffer)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
if (frame_buffer == NULL) {
frame_buffer = device->paint.image;
}
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
if (frame_buffer != NULL) {
// configure ePaper's memory to send data
iot_set_ram_area(dev, 0, 0, EPD_WIDTH-1, EPD_HEIGHT-1);
iot_set_ram_address_counter(dev, 0, 0);
// send image data
iot_epaper_send_command(dev, E_PAPER_WRITE_RAM);
iot_epaper_send_data(dev, frame_buffer, device->paint.width / 8 * device->paint.height);
// update display
iot_epaper_send_command(dev, E_PAPER_DISPLAY_UPDATE_CONTROL_2);
iot_epaper_send_byte(dev, 0xC4);
iot_epaper_send_command(dev, E_PAPER_MASTER_ACTIVATION);
iot_epaper_send_command(dev, E_PAPER_TERMINATE_FRAME_READ_WRITE);
iot_epaper_wait_idle(dev);
}
xSemaphoreGiveRecursive(device->spi_mux);
}
void iot_epaper_sleep(epaper_handle_t dev)
{
epaper_dev_t* device = (epaper_dev_t*) dev;
xSemaphoreTakeRecursive(device->spi_mux, portMAX_DELAY);
iot_epaper_send_command(dev, E_PAPER_DEEP_SLEEP_MODE);
iot_epaper_wait_idle(dev);
xSemaphoreGiveRecursive(device->spi_mux);
}