simplify epd driver api

This commit is contained in:
Shuanglei Tao
2025-03-08 22:23:04 +08:00
parent 27406c577b
commit cc79fcd764
10 changed files with 330 additions and 307 deletions

View File

@@ -34,7 +34,7 @@ void epd_config_load(epd_config_t *cfg)
fds_record_desc_t record_desc;
fds_find_token_t ftok;
memset(cfg, 0xFF, sizeof(epd_config_t));
memset(cfg, EPD_CONFIG_EMPTY, sizeof(epd_config_t));
memset(&ftok, 0x00, sizeof(fds_find_token_t));
if (fds_record_find(CONFIG_FILE_ID, CONFIG_REC_KEY, &record_desc, &ftok) != NRF_SUCCESS) {
@@ -99,3 +99,12 @@ void epd_config_save(epd_config_t *cfg)
NRF_LOG_ERROR("epd_config_save: record write/update failed!\n");
}
}
bool epd_config_empty(epd_config_t *cfg)
{
for (uint8_t i = 0; i < EPD_CONFIG_SIZE; i++) {
if (((uint8_t *)cfg)[i] != EPD_CONFIG_EMPTY)
return false;
}
return true;
}

View File

@@ -1,3 +1,6 @@
#ifndef EPD_CONFIG_H__
#define EPD_CONFIG_H__
#include <stdbool.h>
#include <stdint.h>
typedef struct
@@ -9,13 +12,19 @@ typedef struct
uint8_t rst_pin;
uint8_t busy_pin;
uint8_t bs_pin;
uint8_t driver_id;
uint8_t model_id;
uint8_t wakeup_pin;
uint8_t led_pin;
uint8_t en_pin;
} epd_config_t;
#define EPD_CONFIG_SIZE (sizeof(epd_config_t) / sizeof(uint8_t))
#define EPD_CONFIG_EMPTY 0xFF
void epd_config_init(epd_config_t *cfg);
void epd_config_load(epd_config_t *cfg);
void epd_config_clear(epd_config_t *cfg);
void epd_config_save(epd_config_t *cfg);
bool epd_config_empty(epd_config_t *cfg);
#endif

View File

@@ -21,6 +21,7 @@
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
// GPIO Pins
uint32_t EPD_MOSI_PIN = 5;
uint32_t EPD_SCLK_PIN = 8;
uint32_t EPD_CS_PIN = 9;
@@ -31,6 +32,13 @@ uint32_t EPD_BS_PIN = 13;
uint32_t EPD_EN_PIN = 0xFF;
uint32_t EPD_LED_PIN = 0xFF;
// Display resolution
uint16_t EPD_WIDTH = 400;
uint16_t EPD_HEIGHT = 300;
// BWR mode
bool EPD_BWR_MODE = true;
// Arduino like function wrappers
void pinMode(uint32_t pin, uint32_t mode)
@@ -82,50 +90,13 @@ void delay(uint32_t ms)
nrf_delay_ms(ms);
}
// GPIO
void DEV_Module_Init(void)
{
pinMode(EPD_CS_PIN, OUTPUT);
pinMode(EPD_DC_PIN, OUTPUT);
pinMode(EPD_RST_PIN, OUTPUT);
pinMode(EPD_BUSY_PIN, INPUT);
if (EPD_EN_PIN != 0xFF) {
pinMode(EPD_EN_PIN, OUTPUT);
digitalWrite(EPD_EN_PIN, HIGH);
}
pinMode(EPD_BS_PIN, OUTPUT);
digitalWrite(EPD_BS_PIN, LOW);
digitalWrite(EPD_DC_PIN, LOW);
digitalWrite(EPD_CS_PIN, LOW);
digitalWrite(EPD_RST_PIN, HIGH);
if (EPD_LED_PIN != 0xFF) {
pinMode(EPD_LED_PIN, OUTPUT);
EPD_LED_ON();
}
}
void DEV_Module_Exit(void)
{
digitalWrite(EPD_DC_PIN, LOW);
digitalWrite(EPD_CS_PIN, LOW);
digitalWrite(EPD_RST_PIN, LOW);
DEV_SPI_Exit();
EPD_LED_OFF();
}
// Hardware SPI (write only)
#define SPI_INSTANCE 0 /**< SPI instance index. */
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE); /**< SPI instance. */
static bool spi_initialized = false;
void DEV_SPI_Init(void)
static void EPD_SPI_Init(void)
{
if (spi_initialized) return;
nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
@@ -140,30 +111,30 @@ void DEV_SPI_Init(void)
spi_initialized = true;
}
void DEV_SPI_Exit(void)
static void EPD_SPI_Uninit(void)
{
if (!spi_initialized) return;
nrf_drv_spi_uninit(&spi);
spi_initialized = false;
}
void DEV_SPI_WriteByte(uint8_t value)
void EPD_SPI_WriteByte(uint8_t value)
{
DEV_SPI_Init();
EPD_SPI_Init();
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, &value, 1, NULL, 0));
}
void DEV_SPI_WriteBytes(uint8_t *value, uint8_t len)
void EPD_SPI_WriteBytes(uint8_t *value, uint8_t len)
{
DEV_SPI_Init();
EPD_SPI_Init();
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, value, len, NULL, 0));
}
// Software SPI (read / write)
void DEV_SPI_WriteByte_SW(uint8_t data)
void EPD_SPI_WriteByte_SW(uint8_t data)
{
DEV_SPI_Exit();
EPD_SPI_Uninit();
pinMode(EPD_MOSI_PIN, OUTPUT);
digitalWrite(EPD_CS_PIN, LOW);
for (int i = 0; i < 8; i++)
@@ -178,9 +149,9 @@ void DEV_SPI_WriteByte_SW(uint8_t data)
digitalWrite(EPD_CS_PIN, HIGH);
}
uint8_t DEV_SPI_ReadByte_SW(void)
uint8_t EPD_SPI_ReadByte_SW(void)
{
DEV_SPI_Exit();
EPD_SPI_Uninit();
uint8_t j = 0xff;
pinMode(EPD_MOSI_PIN, INPUT);
digitalWrite(EPD_CS_PIN, LOW);
@@ -202,7 +173,7 @@ void EPD_WriteCommand_SW(uint8_t Reg)
{
digitalWrite(EPD_DC_PIN, LOW);
digitalWrite(EPD_CS_PIN, LOW);
DEV_SPI_WriteByte_SW(Reg);
EPD_SPI_WriteByte_SW(Reg);
digitalWrite(EPD_CS_PIN, HIGH);
}
@@ -210,14 +181,14 @@ void EPD_WriteByte_SW(uint8_t Data)
{
digitalWrite(EPD_DC_PIN, HIGH);
digitalWrite(EPD_CS_PIN, LOW);
DEV_SPI_WriteByte_SW(Data);
EPD_SPI_WriteByte_SW(Data);
digitalWrite(EPD_CS_PIN, HIGH);
}
uint8_t EPD_ReadByte_SW(void)
{
digitalWrite(EPD_DC_PIN, HIGH);
return DEV_SPI_ReadByte_SW();
return EPD_SPI_ReadByte_SW();
}
@@ -225,19 +196,19 @@ uint8_t EPD_ReadByte_SW(void)
void EPD_WriteCommand(uint8_t Reg)
{
digitalWrite(EPD_DC_PIN, LOW);
DEV_SPI_WriteByte(Reg);
EPD_SPI_WriteByte(Reg);
}
void EPD_WriteByte(uint8_t Data)
{
digitalWrite(EPD_DC_PIN, HIGH);
DEV_SPI_WriteByte(Data);
EPD_SPI_WriteByte(Data);
}
void EPD_WriteData(uint8_t *Data, uint8_t Len)
{
digitalWrite(EPD_DC_PIN, HIGH);
DEV_SPI_WriteBytes(Data, Len);
EPD_SPI_WriteBytes(Data, Len);
}
void EPD_Reset(uint32_t value, uint16_t duration)
@@ -266,6 +237,44 @@ void EPD_WaitBusy(uint32_t value, uint16_t timeout)
NRF_LOG_DEBUG("[EPD]: busy release\n");
}
// GPIO
void EPD_GPIO_Init(void)
{
pinMode(EPD_CS_PIN, OUTPUT);
pinMode(EPD_DC_PIN, OUTPUT);
pinMode(EPD_RST_PIN, OUTPUT);
pinMode(EPD_BUSY_PIN, INPUT);
if (EPD_EN_PIN != 0xFF) {
pinMode(EPD_EN_PIN, OUTPUT);
digitalWrite(EPD_EN_PIN, HIGH);
}
pinMode(EPD_BS_PIN, OUTPUT);
digitalWrite(EPD_BS_PIN, LOW);
EPD_SPI_Init();
digitalWrite(EPD_DC_PIN, LOW);
digitalWrite(EPD_CS_PIN, LOW);
digitalWrite(EPD_RST_PIN, HIGH);
if (EPD_LED_PIN != 0xFF) {
pinMode(EPD_LED_PIN, OUTPUT);
EPD_LED_ON();
}
}
void EPD_GPIO_Uninit(void)
{
digitalWrite(EPD_DC_PIN, LOW);
digitalWrite(EPD_CS_PIN, LOW);
digitalWrite(EPD_RST_PIN, LOW);
EPD_SPI_Uninit();
EPD_LED_OFF();
}
// lED
void EPD_LED_ON(void)
@@ -286,41 +295,28 @@ void EPD_LED_TOGGLE(void)
nrf_gpio_pin_toggle(EPD_LED_PIN);
}
// EPD models
extern epd_model_t epd_4in2;
extern epd_model_t epd_4in2bv2;
extern epd_driver_t epd_driver_4in2;
extern epd_driver_t epd_driver_4in2bv2;
/** EPD drivers */
static epd_driver_t *epd_drivers[] = {
&epd_driver_4in2, // UC8176: 4.2 inch, BW
&epd_driver_4in2bv2, // UC8176: 4.2 inch, BWR
static epd_model_t *epd_models[] = {
&epd_4in2,
&epd_4in2bv2,
};
/**< current EPD driver */
static epd_driver_t *m_driver = NULL;
epd_driver_t *epd_driver_get(void)
static epd_model_t *epd_model_get(epd_model_id_t id)
{
if (m_driver == NULL)
m_driver = epd_drivers[0];
return m_driver;
}
epd_driver_t *epd_driver_by_id(uint8_t id)
{
for (uint8_t i = 0; i < ARRAY_SIZE(epd_drivers); i++) {
if (epd_drivers[i]->id == id)
return epd_drivers[i];
for (uint8_t i = 0; i < ARRAY_SIZE(epd_models); i++) {
if (epd_models[i]->id == id) {
return epd_models[i];
}
}
return NULL;
return epd_models[0];
}
bool epd_driver_set(uint8_t id)
epd_driver_t *epd_model_init(epd_model_id_t id)
{
epd_driver_t *driver = epd_driver_by_id(id);
if (driver ) {
m_driver = driver;
return true;
}
return false;
epd_model_t *model = epd_model_get(id);
model->drv->init(model->res, model->bwr);
return model->drv;
}

View File

@@ -19,12 +19,16 @@
#include <stdint.h>
#include <stdlib.h>
/**< EPD driver IDs. */
enum EPD_DRIVER_IDS
#define BIT(n) (1UL << (n))
// Display resolution
typedef enum
{
EPD_DRIVER_4IN2 = 1,
EPD_DRIVER_4IN2B_V2 = 3,
};
EPD_RES_400x300,
EPD_RES_320x300,
EPD_RES_320x240,
EPD_RES_200x300,
} epd_res_t;
/**@brief EPD driver structure.
*
@@ -32,14 +36,8 @@ enum EPD_DRIVER_IDS
*/
typedef struct
{
uint8_t id; /**< driver ID. */
uint16_t width;
uint16_t height;
void (*init)(void); /**< Initialize the e-Paper register */
void (*init)(epd_res_t res, bool bwr); /**< Initialize the e-Paper register */
void (*clear)(void); /**< Clear screen */
void (*send_command)(uint8_t Reg); /**< send command */
void (*send_byte)(uint8_t Reg); /**< send byte */
void (*send_data)(uint8_t *Data, uint8_t Len); /**< send data */
void (*write_image)(uint8_t *black, uint8_t *color, uint16_t x, uint16_t y, uint16_t w, uint16_t h); /**< write image */
void (*refresh)(void); /**< Sends the image buffer in RAM to e-Paper and displays */
void (*sleep)(void); /**< Enter sleep mode */
@@ -47,6 +45,21 @@ typedef struct
void (*force_temp)(int8_t value); /**< Force temperature (will trigger OTP LUT switch) */
} epd_driver_t;
typedef enum
{
EPD_4IN2 = 1,
EPD_4IN2_V2 = 2,
EPD_4IN2B_V2 = 3,
} epd_model_id_t;
typedef struct
{
epd_model_id_t id;
epd_driver_t *drv;
epd_res_t res;
bool bwr;
} epd_model_t;
extern uint32_t EPD_MOSI_PIN;
extern uint32_t EPD_SCLK_PIN;
extern uint32_t EPD_CS_PIN;
@@ -57,6 +70,11 @@ extern uint32_t EPD_BS_PIN;
extern uint32_t EPD_EN_PIN;
extern uint32_t EPD_LED_PIN;
extern uint16_t EPD_WIDTH;
extern uint16_t EPD_HEIGHT;
extern bool EPD_BWR_MODE;
#define LOW (0x0)
#define HIGH (0x1)
@@ -72,21 +90,19 @@ uint32_t digitalRead(uint32_t pin);
void delay(uint32_t ms);
// GPIO
void DEV_Module_Init(void);
void DEV_Module_Exit(void);
void EPD_GPIO_Init(void);
void EPD_GPIO_Uninit(void);
// Software SPI (read / write)
void DEV_SPI_WriteByte_SW(uint8_t data);
uint8_t DEV_SPI_ReadByte_SW(void);
void EPD_SPI_WriteByte_SW(uint8_t data);
uint8_t EPD_SPI_ReadByte_SW(void);
void EPD_WriteCommand_SW(uint8_t Reg);
void EPD_WriteByte_SW(uint8_t Data);
uint8_t EPD_ReadByte_SW(void);
// Hardware SPI (write only)
void DEV_SPI_Init(void);
void DEV_SPI_Exit(void);
void DEV_SPI_WriteByte(uint8_t value);
void DEV_SPI_WriteBytes(uint8_t *value, uint8_t len);
void EPD_SPI_WriteByte(uint8_t value);
void EPD_SPI_WriteBytes(uint8_t *value, uint8_t len);
void EPD_WriteCommand(uint8_t Reg);
void EPD_WriteByte(uint8_t Data);
void EPD_WriteData(uint8_t *Data, uint8_t Len);
@@ -99,8 +115,6 @@ void EPD_LED_ON(void);
void EPD_LED_OFF(void);
void EPD_LED_TOGGLE(void);
epd_driver_t *epd_driver_get(void);
epd_driver_t *epd_driver_by_id(uint8_t id);
bool epd_driver_set(uint8_t id);
epd_driver_t *epd_model_init(epd_model_id_t id);
#endif

View File

@@ -15,12 +15,14 @@
#include "ble_srv_common.h"
#include "nrf_delay.h"
#include "nrf_gpio.h"
#include "app_scheduler.h"
#include "EPD_service.h"
#include "Calendar.h"
#include "nrf_log.h"
#if defined(S112)
#define EPD_CFG_DEFAULT {0x14, 0x13, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0xFF, 0x12, 0x07} // 52811
//#define EPD_CFG_DEFAULT {0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x03, 0xFF, 0x0D, 0x02} // 52810
//#define EPD_CFG_DEFAULT {0x14, 0x13, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0xFF, 0x12, 0x07} // 52811
#define EPD_CFG_DEFAULT {0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x03, 0xFF, 0x0D, 0x02} // 52810
#else
#define EPD_CFG_DEFAULT {0x05, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x01, 0x07}
#endif
@@ -33,8 +35,40 @@
0X8D, 0X91, 0X28, 0XD8, 0X22, 0X36, 0X75, 0X62}}
#define BLE_UUID_EPD_CHARACTERISTIC 0x0002
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define EPD_CONFIG_SIZE (sizeof(epd_config_t) / sizeof(uint8_t))
extern uint32_t timestamp(void); // defined in main.c
static uint16_t m_driver_refs = 0;
static void epd_gpio_init()
{
if (m_driver_refs == 0) {
NRF_LOG_DEBUG("[EPD]: driver init\n");
EPD_GPIO_Init();
}
m_driver_refs++;
}
static void epd_gpio_uninit()
{
m_driver_refs--;
if (m_driver_refs == 0) {
NRF_LOG_DEBUG("[EPD]: driver exit\n");
EPD_GPIO_Uninit();
}
}
static void calendar_update(void * p_event_data, uint16_t event_size)
{
epd_calendar_update_event_t *event = (epd_calendar_update_event_t *)p_event_data;
ble_epd_t *p_epd = event->p_epd;
p_epd->calendar_mode = true;
epd_gpio_init();
epd_driver_t *drv = epd_model_init((epd_model_id_t)p_epd->config.model_id);
DrawCalendar(drv, event->timestamp);
epd_gpio_uninit();
}
/**@brief Function for handling the @ref BLE_GAP_EVT_CONNECTED event from the S110 SoftDevice.
*
@@ -44,9 +78,9 @@
static void on_connect(ble_epd_t * p_epd, ble_evt_t * p_ble_evt)
{
p_epd->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
epd_gpio_init();
}
/**@brief Function for handling the @ref BLE_GAP_EVT_DISCONNECTED event from the S110 SoftDevice.
*
* @param[in] p_epd EPD Service structure.
@@ -56,6 +90,7 @@ static void on_disconnect(ble_epd_t * p_epd, ble_evt_t * p_ble_evt)
{
UNUSED_PARAMETER(p_ble_evt);
p_epd->conn_handle = BLE_CONN_HANDLE_INVALID;
epd_gpio_uninit();
}
static void epd_service_process(ble_epd_t * p_epd, uint8_t * p_data, uint16_t length)
@@ -73,7 +108,7 @@ static void epd_service_process(ble_epd_t * p_epd, uint8_t * p_data, uint16_t le
case EPD_CMD_SET_PINS:
if (length < 8) return;
DEV_Module_Exit();
EPD_GPIO_Uninit();
EPD_MOSI_PIN = p_epd->config.mosi_pin = p_data[1];
EPD_SCLK_PIN = p_epd->config.sclk_pin = p_data[2];
@@ -86,38 +121,34 @@ static void epd_service_process(ble_epd_t * p_epd, uint8_t * p_data, uint16_t le
EPD_EN_PIN = p_epd->config.en_pin = p_data[8];
epd_config_save(&p_epd->config);
DEV_Module_Init();
EPD_GPIO_Init();
break;
case EPD_CMD_INIT:
if (length > 1)
{
if (epd_driver_set(p_data[1]))
{
p_epd->driver = epd_driver_get();
p_epd->config.driver_id = p_epd->driver->id;
epd_config_save(&p_epd->config);
}
case EPD_CMD_INIT: {
uint8_t id = length > 1 ? p_data[1] : p_epd->config.model_id;
if (id != p_epd->config.model_id) {
p_epd->config.model_id = id;
epd_config_save(&p_epd->config);
}
NRF_LOG_INFO("[EPD]: DRIVER=%d\n", p_epd->driver->id);
p_epd->driver->init();
break;
p_epd->driver = epd_model_init((epd_model_id_t)id);
} break;
case EPD_CMD_CLEAR:
p_epd->calendar_mode = false;
p_epd->driver->clear();
break;
case EPD_CMD_SEND_COMMAND:
if (length < 2) return;
p_epd->driver->send_command(p_data[1]);
EPD_WriteCommand(p_data[1]);
break;
case EPD_CMD_SEND_DATA:
p_epd->driver->send_data(&p_data[1], length - 1);
EPD_WriteData(&p_data[1], length - 1);
break;
case EPD_CMD_DISPLAY:
p_epd->calendar_mode = false;
p_epd->driver->refresh();
break;
@@ -253,21 +284,11 @@ static uint32_t epd_service_init(ble_epd_t * p_epd)
static void ble_epd_config_load(ble_epd_t * p_epd)
{
bool is_empty_config = true;
for (uint8_t i = 0; i < EPD_CONFIG_SIZE; i++)
{
if (((uint8_t *)&p_epd->config)[i] != 0xFF)
{
is_empty_config = false;
}
}
NRF_LOG_DEBUG("is_empty_config: %d\n", is_empty_config);
// write default config
if (is_empty_config)
if (epd_config_empty(&p_epd->config))
{
uint8_t cfg[] = EPD_CFG_DEFAULT;
memcpy(&p_epd->config, cfg, ARRAY_SIZE(cfg));
memcpy(&p_epd->config, cfg, sizeof(cfg));
epd_config_save(&p_epd->config);
}
@@ -282,11 +303,8 @@ static void ble_epd_config_load(ble_epd_t * p_epd)
EPD_EN_PIN = p_epd->config.en_pin;
EPD_LED_PIN = p_epd->config.led_pin;
epd_driver_set(p_epd->config.driver_id);
p_epd->driver = epd_driver_get();
// blink LED on start
if (EPD_LED_PIN != 0xFF)
if (EPD_LED_PIN != EPD_CONFIG_EMPTY)
{
pinMode(EPD_LED_PIN, OUTPUT);
EPD_LED_ON();
@@ -352,3 +370,12 @@ uint32_t ble_epd_string_send(ble_epd_t * p_epd, uint8_t * p_string, uint16_t len
return sd_ble_gatts_hvx(p_epd->conn_handle, &hvx_params);
}
void ble_epd_on_timer(ble_epd_t * p_epd, uint32_t timestamp, bool force_update)
{
// Update calendar on 00:00:00
if (force_update || (p_epd->calendar_mode && timestamp % 86400 == 0)) {
epd_calendar_update_event_t event = { p_epd, timestamp };
app_sched_event_put(&event, sizeof(epd_calendar_update_event_t), calendar_update);
}
}

View File

@@ -10,8 +10,8 @@
*
*/
#ifndef EPD_BLE_H__
#define EPD_BLE_H__
#ifndef EPD_SERVICE_H__
#define EPD_SERVICE_H__
#include <stdint.h>
#include <stdbool.h>
@@ -85,8 +85,17 @@ typedef struct
epd_driver_t *driver; /**< current EPD driver */
epd_config_t config; /**< EPD config */
epd_callback_t epd_cmd_cb; /**< EPD callback */
bool calendar_mode; /**< Calendar mode flag */
} ble_epd_t;
typedef struct
{
ble_epd_t *p_epd;
uint32_t timestamp;
} epd_calendar_update_event_t;
#define EPD_CALENDAR_SCHD_EVENT_DATA_SIZE sizeof(epd_calendar_update_event_t)
/**@brief Function for preparing sleep mode.
*
* @param[in] p_epd EPD Service structure.
@@ -130,6 +139,8 @@ void ble_epd_on_ble_evt(ble_epd_t * p_epd, ble_evt_t * p_ble_evt);
*/
uint32_t ble_epd_string_send(ble_epd_t * p_epd, uint8_t * p_string, uint16_t length);
void ble_epd_on_timer(ble_epd_t * p_epd, uint32_t timestamp, bool force_update);
#endif // EPD_BLE_H__
/** @} */

View File

@@ -31,48 +31,52 @@
#include "nrf_log.h"
// commands used by this driver
enum {
CMD_PSR = 0x00, // Panel Setting
CMD_POF = 0x02, // Power OFF
CMD_PON = 0x04, // Power ON
CMD_DSLP = 0x07, // Deep sleep
CMD_DTM1 = 0x10, // Display Start Transmission 1
CMD_DRF = 0x12, // Display Refresh
CMD_DTM2 = 0x13, // Display Start transmission 2
CMD_TSC = 0x40, // Temperature Sensor Calibration
CMD_CDI = 0x50, // Vcom and data interval setting
CMD_PTL = 0x90, // Partial Window
CMD_PTIN = 0x91, // Partial In
CMD_PTOUT = 0x92, // Partial Out
CMD_CCSET = 0xE0, // Cascade Setting
CMD_TSSET = 0xE5, // Force Temperauture
} UC8176_CMD;
#define CMD_PSR 0x00 // Panel Setting
#define CMD_POF 0x02 // Power OFF
#define CMD_PON 0x04 // Power ON
#define CMD_DSLP 0x07 // Deep sleep
#define CMD_DTM1 0x10 // Display Start Transmission 1
#define CMD_DRF 0x12 // Display Refresh
#define CMD_DTM2 0x13 // Display Start transmission 2
#define CMD_TSC 0x40 // Temperature Sensor Calibration
#define CMD_CDI 0x50 // Vcom and data interval setting
#define CMD_PTL 0x90 // Partial Window
#define CMD_PTIN 0x91 // Partial In
#define CMD_PTOUT 0x92 // Partial Out
#define CMD_CCSET 0xE0 // Cascade Setting
#define CMD_TSSET 0xE5 // Force Temperauture
// Display resolution
#define EPD_4IN2_WIDTH 400
#define EPD_4IN2_HEIGHT 300
// PSR registers
#define PSR_RES1 BIT(7)
#define PSR_RES0 BIT(6)
#define PSR_REG BIT(5)
#define PSR_BWR BIT(4)
#define PSR_UD BIT(3)
#define PSR_SHL BIT(2)
#define PSR_SHD BIT(1)
#define PSR_RST BIT(0)
static void EPD_4IN2_PowerOn(void)
static void UC8176_PowerOn(void)
{
EPD_WriteCommand(CMD_PON);
EPD_WaitBusy(LOW, 100);
}
static void EPD_4IN2_PowerOff(void)
static void UC8176_PowerOff(void)
{
EPD_WriteCommand(CMD_POF);
EPD_WaitBusy(LOW, 100);
}
// Read temperature from driver chip
int8_t EPD_4IN2_Read_Temp(void)
int8_t UC8176_Read_Temp(void)
{
EPD_WriteCommand_SW(CMD_TSC);
return (int8_t) EPD_ReadByte_SW();
}
// Force temperature (will trigger OTP LUT switch)
void EPD_4IN2_Force_Temp(int8_t value)
void UC8176_Force_Temp(int8_t value)
{
EPD_WriteCommand_SW(CMD_CCSET);
EPD_WriteByte_SW(0x02);
@@ -84,15 +88,15 @@ void EPD_4IN2_Force_Temp(int8_t value)
function : Turn On Display
parameter:
******************************************************************************/
void EPD_4IN2_Refresh(void)
void UC8176_Refresh(void)
{
NRF_LOG_DEBUG("[EPD]: refresh begin\n");
EPD_4IN2_PowerOn();
NRF_LOG_DEBUG("[EPD]: temperature: %d\n", EPD_4IN2_Read_Temp());
UC8176_PowerOn();
NRF_LOG_DEBUG("[EPD]: temperature: %d\n", UC8176_Read_Temp());
EPD_WriteCommand(CMD_DRF);
delay(100);
EPD_WaitBusy(LOW, 20000);
EPD_4IN2_PowerOff();
EPD_WaitBusy(LOW, 30000);
UC8176_PowerOff();
NRF_LOG_DEBUG("[EPD]: refresh end\n");
}
@@ -100,34 +104,53 @@ void EPD_4IN2_Refresh(void)
function : Initialize the e-Paper register
parameter:
******************************************************************************/
void EPD_4IN2_Init(void)
void UC8176_Init(epd_res_t res, bool bwr)
{
EPD_BWR_MODE = bwr;
EPD_Reset(HIGH, 10);
EPD_WriteCommand(CMD_PSR); // panel setting
EPD_WriteByte(0x1f); // 400x300 B/W mode, LUT from OTP
EPD_WriteCommand(CMD_CDI); // VCOM AND DATA INTERVAL SETTING
EPD_WriteByte(0x97); // LUTB=0 LUTW=1 interval=10
}
void EPD_4IN2B_V2_Init(void)
{
EPD_Reset(HIGH, 200);
uint8_t psr = PSR_UD | PSR_SHL | PSR_SHD | PSR_RST;
if (!EPD_BWR_MODE) psr |= PSR_BWR;
switch (res) {
case EPD_RES_320x300:
EPD_WIDTH = 320;
EPD_HEIGHT = 300;
psr |= PSR_RES0;
break;
case EPD_RES_320x240:
EPD_WIDTH = 320;
EPD_HEIGHT = 240;
psr |= PSR_RES1;
break;
case EPD_RES_200x300:
EPD_WIDTH = 200;
EPD_HEIGHT = 300;
psr |= PSR_RES1 | PSR_RES0;
break;
case EPD_RES_400x300:
default:
EPD_WIDTH = 400;
EPD_HEIGHT = 300;
break;
}
NRF_LOG_DEBUG("[EPD]: PSR=%02x\n", psr);
EPD_WriteCommand(CMD_PSR);
EPD_WriteByte(0x0f); // 400x300 B/W/R mode, LUT from OTP
EPD_WriteByte(psr);
if (!EPD_BWR_MODE) {
EPD_WriteCommand(CMD_CDI);
EPD_WriteByte(0x97);
}
}
/******************************************************************************
function : Clear screen
parameter:
******************************************************************************/
void EPD_4IN2_Clear(void)
void UC8176_Clear(void)
{
uint16_t Width, Height;
Width = (EPD_4IN2_WIDTH % 8 == 0)? (EPD_4IN2_WIDTH / 8 ): (EPD_4IN2_WIDTH / 8 + 1);
Height = EPD_4IN2_HEIGHT;
uint16_t Width = (EPD_WIDTH % 8 == 0)? (EPD_WIDTH / 8 ): (EPD_WIDTH / 8 + 1);
uint16_t Height = EPD_HEIGHT;
EPD_WriteCommand(CMD_DTM1);
for (uint16_t j = 0; j < Height; j++) {
@@ -143,7 +166,7 @@ void EPD_4IN2_Clear(void)
}
}
EPD_4IN2_Refresh();
UC8176_Refresh();
}
static void _setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
@@ -164,41 +187,29 @@ static void _setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
EPD_WriteByte(0x01);
}
void EPD_4IN2_Write_Image(uint8_t *black, uint8_t *color, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
void UC8176_Write_Image(uint8_t *black, uint8_t *color, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
uint16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
x -= x % 8; // byte boundary
w = wb * 8; // byte boundary
if (x + w > EPD_4IN2_WIDTH || y + h > EPD_4IN2_HEIGHT) return;
if (x + w > EPD_WIDTH || y + h > EPD_HEIGHT) return;
EPD_WriteCommand(CMD_PTIN); // partial in
_setPartialRamArea(x, y, w, h);
EPD_WriteCommand(CMD_DTM2);
for (uint16_t i = 0; i < h; i++) {
for (uint16_t j = 0; j < w / 8; j++) {
EPD_WriteByte(black[j + i * wb]);
}
}
EPD_WriteCommand(CMD_PTOUT); // partial out
}
void EPD_4IN2B_V2_Write_Image(uint8_t *black, uint8_t *color, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
uint16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
x -= x % 8; // byte boundary
w = wb * 8; // byte boundary
if (x + w > EPD_4IN2_WIDTH || y + h > EPD_4IN2_HEIGHT) return;
EPD_WriteCommand(CMD_PTIN); // partial in
_setPartialRamArea(x, y, w, h);
EPD_WriteCommand(CMD_DTM1);
for (uint16_t i = 0; i < h; i++) {
for (uint16_t j = 0; j < w / 8; j++) {
EPD_WriteByte(black ? black[j + i * wb] : 0xFF);
if (EPD_BWR_MODE) {
EPD_WriteCommand(CMD_DTM1);
for (uint16_t i = 0; i < h; i++) {
for (uint16_t j = 0; j < w / 8; j++) {
EPD_WriteByte(black ? black[j + i * wb] : 0xFF);
}
}
}
EPD_WriteCommand(CMD_DTM2);
for (uint16_t i = 0; i < h; i++) {
for (uint16_t j = 0; j < w / 8; j++) {
EPD_WriteByte(color ? color[j + i * wb] : 0xFF);
if (EPD_BWR_MODE)
EPD_WriteByte(color ? color[j + i * wb] : 0xFF);
else
EPD_WriteByte(black[j + i * wb]);
}
}
EPD_WriteCommand(CMD_PTOUT); // partial out
@@ -208,42 +219,35 @@ void EPD_4IN2B_V2_Write_Image(uint8_t *black, uint8_t *color, uint16_t x, uint16
function : Enter sleep mode
parameter:
******************************************************************************/
void EPD_4IN2_Sleep(void)
void UC8176_Sleep(void)
{
EPD_4IN2_PowerOff();
UC8176_PowerOff();
EPD_WriteCommand(CMD_DSLP);
EPD_WriteByte(0XA5);
}
const epd_driver_t epd_driver_4in2 = {
.id = EPD_DRIVER_4IN2,
.width = EPD_4IN2_WIDTH,
.height = EPD_4IN2_HEIGHT,
.init = EPD_4IN2_Init,
.clear = EPD_4IN2_Clear,
.send_command = EPD_WriteCommand,
.send_byte = EPD_WriteByte,
.send_data = EPD_WriteData,
.write_image = EPD_4IN2_Write_Image,
.refresh = EPD_4IN2_Refresh,
.sleep = EPD_4IN2_Sleep,
.read_temp = EPD_4IN2_Read_Temp,
.force_temp = EPD_4IN2_Force_Temp,
// Declare driver and models
static epd_driver_t epd_drv_uc8176 = {
.init = UC8176_Init,
.clear = UC8176_Clear,
.write_image = UC8176_Write_Image,
.refresh = UC8176_Refresh,
.sleep = UC8176_Sleep,
.read_temp = UC8176_Read_Temp,
.force_temp = UC8176_Force_Temp,
};
const epd_driver_t epd_driver_4in2bv2 = {
.id = EPD_DRIVER_4IN2B_V2,
.width = EPD_4IN2_WIDTH,
.height = EPD_4IN2_HEIGHT,
.init = EPD_4IN2B_V2_Init,
.clear = EPD_4IN2_Clear,
.send_command = EPD_WriteCommand,
.send_byte = EPD_WriteByte,
.send_data = EPD_WriteData,
.write_image = EPD_4IN2B_V2_Write_Image,
.refresh = EPD_4IN2_Refresh,
.sleep = EPD_4IN2_Sleep,
.read_temp = EPD_4IN2_Read_Temp,
.force_temp = EPD_4IN2_Force_Temp,
const epd_model_t epd_4in2 = {
.id = EPD_4IN2,
.drv = &epd_drv_uc8176,
.res = EPD_RES_400x300,
.bwr = false,
};
const epd_model_t epd_4in2bv2 = {
.id = EPD_4IN2B_V2,
.drv = &epd_drv_uc8176,
.res = EPD_RES_400x300,
.bwr = true,
};

View File

@@ -1,6 +1,5 @@
#include "Adafruit_GFX.h"
#include "fonts.h"
#include "EPD_driver.h"
#include "Lunar.h"
#include "Calendar.h"
#include "nrf_log.h"
@@ -73,11 +72,10 @@ static void DrawMonthDay(Adafruit_GFX *gfx, int16_t x, int16_t y, tm_t *tm, stru
}
}
void DrawCalendar(uint32_t timestamp)
void DrawCalendar(epd_driver_t *driver, uint32_t timestamp)
{
tm_t tm = {0};
struct Lunar_Date Lunar;
epd_driver_t *driver = epd_driver_get();
transformTime(timestamp, &tm);
@@ -87,10 +85,10 @@ void DrawCalendar(uint32_t timestamp)
Adafruit_GFX gfx;
if (driver->id == EPD_DRIVER_4IN2B_V2)
GFX_begin_3c(&gfx, driver->width, driver->height, PAGE_HEIGHT);
if (EPD_BWR_MODE)
GFX_begin_3c(&gfx, EPD_WIDTH, EPD_HEIGHT, PAGE_HEIGHT);
else
GFX_begin(&gfx, driver->width, driver->height, PAGE_HEIGHT);
GFX_begin(&gfx, EPD_WIDTH, EPD_HEIGHT, PAGE_HEIGHT);
GFX_firstPage(&gfx);
do {

View File

@@ -2,7 +2,8 @@
#define __CALENDAR_H
#include <stdint.h>
#include "EPD_driver.h"
void DrawCalendar(uint32_t timestamp);
void DrawCalendar(epd_driver_t *driver, uint32_t timestamp);
#endif

60
main.c
View File

@@ -36,7 +36,6 @@
#include "nrf_drv_gpiote.h"
#include "nrf_pwr_mgmt.h"
#include "EPD_service.h"
#include "Calendar.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
@@ -74,7 +73,7 @@
#define NEXT_CONN_PARAMS_UPDATE_DELAY TIMER_TICKS(30000) /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
#define MAX_CONN_PARAMS_UPDATE_COUNT 3 /**< Number of attempts before giving up the connection parameter negotiation. */
#define SCHED_MAX_EVENT_DATA_SIZE 0 /**< Maximum size of scheduler events. */
#define SCHED_MAX_EVENT_DATA_SIZE EPD_CALENDAR_SCHD_EVENT_DATA_SIZE /**< Maximum size of scheduler events. */
#define SCHED_QUEUE_SIZE 10 /**< Maximum number of events in the scheduler queue. */
#define CLOCK_TIMER_INTERVAL TIMER_TICKS(1000) /**< Clock timer interval (ticks). */
@@ -85,49 +84,14 @@
NRF_BLE_GATT_DEF(m_gatt); /**< GATT module instance. */
BLE_ADVERTISING_DEF(m_advertising); /**< Advertising module instance. */
#endif
static uint16_t m_driver_refs = 0;
static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */
static ble_uuid_t m_adv_uuids[] = {{BLE_UUID_EPD_SERVICE, \
EPD_SERVICE_UUID_TYPE}}; /**< Universally unique service identifier. */
BLE_EPD_DEF(m_epd); /**< Structure to identify the EPD Service. */
static uint32_t m_timestamp = 1735689600; /**< Current timestamp. */
static bool m_calendar_mode = false; /**< Whether we are in calendar mode */
APP_TIMER_DEF(m_clock_timer_id); /**< Clock timer. */
static void epd_driver_init()
{
if (m_driver_refs == 0) {
NRF_LOG_DEBUG("[EPD]: driver init\n");
DEV_Module_Init();
}
m_driver_refs++;
}
static void epd_driver_exit()
{
m_driver_refs--;
if (m_driver_refs == 0) {
NRF_LOG_DEBUG("[EPD]: driver exit\n");
DEV_Module_Exit();
}
}
static void calendar_update(void * p_event_data, uint16_t event_size)
{
m_calendar_mode = true;
epd_driver_init();
m_epd.driver->init();
DrawCalendar(m_timestamp);
epd_driver_exit();
}
static uint32_t calendar_update_schedule(void)
{
return app_sched_event_put(NULL, 0, calendar_update);
}
/**@brief Callback function for asserts in the SoftDevice.
*
* @details This function will be called in case of an assert in the SoftDevice.
@@ -150,9 +114,7 @@ static void clock_timer_timeout_handler(void * p_context)
m_timestamp++;
// Update calendar on 00:00:00
if (m_calendar_mode && m_timestamp % 86400 == 0)
calendar_update_schedule();
ble_epd_on_timer(&m_epd, m_timestamp, false);
}
/**@brief Function for the Event Scheduler initialization.
@@ -217,14 +179,9 @@ bool epd_cmd_callback(uint8_t cmd, uint8_t *data, uint16_t len)
m_timestamp = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
m_timestamp += (len > 4 ? (int8_t)data[4] : 8) * 60 * 60; // timezone
app_timer_start(m_clock_timer_id, CLOCK_TIMER_INTERVAL, NULL);
calendar_update_schedule();
ble_epd_on_timer(&m_epd, m_timestamp, true);
return true;
case EPD_CMD_CLEAR:
case EPD_CMD_DISPLAY:
m_calendar_mode = false;
break;
case EPD_CMD_SYS_SLEEP:
sleep_mode_enter();
return true;
@@ -385,7 +342,7 @@ static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
case BLE_ADV_EVT_IDLE:
NRF_LOG_INFO("advertising timeout\n");
if (m_epd.config.wakeup_pin != 0xFF) {
if (m_calendar_mode)
if (m_epd.calendar_mode)
setup_wakeup_pin(m_epd.config.wakeup_pin);
else
sleep_mode_enter();
@@ -409,13 +366,11 @@ static void on_ble_evt(ble_evt_t * p_ble_evt)
case BLE_GAP_EVT_CONNECTED:
NRF_LOG_INFO("CONNECTED\n");
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
epd_driver_init();
break;
case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("DISCONNECTED\n");
m_conn_handle = BLE_CONN_HANDLE_INVALID;
epd_driver_exit();
#if !defined(S112)
advertising_start();
#endif
@@ -642,18 +597,17 @@ static void advertising_init(void)
#endif
}
#if NRF_MODULE_ENABLED(NRF_LOG)
static uint32_t timestamp_func(void)
// return current timestamp
uint32_t timestamp(void)
{
return m_timestamp;
}
#endif
/**@brief Function for initializing the nrf log module.
*/
static void log_init(void)
{
APP_ERROR_CHECK(NRF_LOG_INIT(timestamp_func));
APP_ERROR_CHECK(NRF_LOG_INIT(timestamp));
#if defined(S112)
NRF_LOG_DEFAULT_BACKENDS_INIT();
#endif