full implementation

This commit is contained in:
2025-10-18 13:35:36 +02:00
commit 303d59076f
21 changed files with 62065 additions and 0 deletions

557
AXS15231B.cpp Normal file
View File

@@ -0,0 +1,557 @@
#include "AXS15231B.h"
#include "SPI.h"
#include "Arduino.h"
#include "driver/spi_master.h"
static volatile bool lcd_spi_dma_write = false;
extern void my_print(const char *buf);
uint32_t transfer_num = 0;
size_t lcd_PushColors_len = 0;
const static lcd_cmd_t axs15231b_qspi_init[] = {
{0x28, {0x00}, 0x40},
{0x10, {0x00}, 0x20},
{0x11, {0x00}, 0x80},
{0x29, {0x00}, 0x00},
};
const static lcd_cmd_t axs15231b_qspi_init_new[] = {
{0x28, {0x00}, 0x40},
{0x10, {0x00}, 0x80},
{0xbb, {0x00,0x00,0x00,0x00,0x00,0x00,0x5a,0xa5}, 0x08},
{0xa0, {0x00,0x30,0x00,0x02,0x00,0x00,0x05,0x3f,0x30,0x05,0x3f,0x3f,0x00,0x00,0x00,0x00,0x00}, 0x11},
{0xa2, {0x30,0x04,0x14,0x50,0x80,0x30,0x85,0x80,0xb4,0x28,0xff,0xff,0xff,0x20,0x50,0x10,0x02,0x06,0x20,0xd0,0xc0,0x01,0x12,0xa0,0x91,0xc0,0x20,0x7f,0xff,0x00,0x06}, 0x1F},
{0xd0, {0x80,0xb4,0x21,0x24,0x08,0x05,0x10,0x01,0xf2,0x02,0xc2,0x02,0x22,0x22,0xaa,0x03,0x10,0x12,0xc0,0x10,0x10,0x40,0x04,0x00,0x30,0x10,0x00,0x03,0x0d,0x12}, 0x1E},
{0xa3, {0xa0,0x06,0xaa,0x00,0x08,0x02,0x0a,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,0x55,0x55}, 0x16},
{0xc1, {0x33,0x04,0x02,0x02,0x71,0x05,0x24,0x55,0x02,0x00,0x01,0x01,0x53,0xff,0xff,0xff,0x4f,0x52,0x00,0x4f,0x52,0x00,0x45,0x3b,0x0b,0x04,0x0d,0x00,0xff,0x42}, 0x1E},
{0xc4, {0x00,0x24,0x33,0x80,0x66,0xea,0x64,0x32,0xc8,0x64,0xc8,0x32,0x90,0x90,0x11,0x06,0xdc,0xfa,0x00,0x00,0x80,0xfe,0x10,0x10,0x00,0x0a,0x0a,0x44,0x50}, 0x1D},
{0xc5, {0x18,0x00,0x00,0x03,0xfe,0xe8,0x3b,0x20,0x30,0x10,0x88,0xde,0x0d,0x08,0x0f,0x0f,0x01,0xe8,0x3b,0x20,0x10,0x10,0x00}, 0x17},
{0xc6, {0x05,0x0a,0x05,0x0a,0x00,0xe0,0x2e,0x0b,0x12,0x22,0x12,0x22,0x01,0x03,0x00,0x02,0x6a,0x18,0xc8,0x22}, 0x14},
{0xc7, {0x50,0x36,0x28,0x00,0xa2,0x80,0x8f,0x00,0x80,0xff,0x07,0x11,0x9c,0x6f,0xff,0x24,0x0c,0x0d,0x0e,0x0f,0x01,0x01,0x01,0x01,0x3f,0x07,0x00}, 0x1B},
{0xc9, {0x33,0x44,0x44,0x01}, 0x04},
{0xcf, {0x2c,0x1e,0x88,0x58,0x13,0x18,0x56,0x18,0x1e,0x68,0xf7,0x00,0x66,0x0d,0x22,0xc4,0x0c,0x77,0x22,0x44,0xaa,0x55,0x04,0x04,0x12,0xa0,0x08}, 0x1B},
{0xd5, {0x30,0x30,0x8a,0x00,0x44,0x04,0x4a,0xe5,0x02,0x4a,0xe5,0x02,0x04,0xd9,0x02,0x47,0x03,0x03,0x03,0x03,0x83,0x00,0x00,0x00,0x80,0x52,0x53,0x50,0x50,0x00}, 0x1E},
{0xd6, {0x10,0x32,0x54,0x76,0x98,0xba,0xdc,0xfe,0x34,0x02,0x01,0x83,0xff,0x00,0x20,0x50,0x00,0x30,0x03,0x03,0x50,0x13,0x00,0x00,0x00,0x04,0x50,0x20,0x01,0x00}, 0x1E},
{0xd7, {0x03,0x01,0x09,0x0b,0x0d,0x0f,0x1e,0x1f,0x18,0x1d,0x1f,0x19,0x30,0x30,0x04,0x00,0x20,0x20,0x1f}, 0x13},
{0xd8, {0x02,0x00,0x08,0x0a,0x0c,0x0e,0x1e,0x1f,0x18,0x1d,0x1f,0x19}, 0x0C},
{0xdf, {0x44,0x33,0x4b,0x69,0x00,0x0a,0x02,0x90}, 0x06},
{0xe0, {0x1f,0x20,0x10,0x17,0x0d,0x09,0x12,0x2a,0x44,0x25,0x0c,0x15,0x13,0x31,0x36,0x2f,0x02}, 0x11},
{0xe1, {0x3f,0x20,0x10,0x16,0x0c,0x08,0x12,0x29,0x43,0x25,0x0c,0x15,0x13,0x32,0x36,0x2f,0x27}, 0x11},
{0xe2, {0x3b,0x07,0x12,0x18,0x0e,0x0d,0x17,0x35,0x44,0x32,0x0c,0x14,0x14,0x36,0x3a,0x2f,0x0d}, 0x11},
{0xe3, {0x37,0x07,0x12,0x18,0x0e,0x0d,0x17,0x35,0x44,0x32,0x0c,0x14,0x14,0x36,0x32,0x2f,0x0f}, 0x11},
{0xe4, {0x3b,0x07,0x12,0x18,0x0e,0x0d,0x17,0x39,0x44,0x2e,0x0c,0x14,0x14,0x36,0x3a,0x2f,0x0d}, 0x11},
{0xe5, {0x37,0x07,0x12,0x18,0x0e,0x0d,0x17,0x39,0x44,0x2e,0x0c,0x14,0x14,0x36,0x3a,0x2f,0x0f}, 0x11},
{0xbb, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, 0x06},
{0x28, {0x00}, 0x40},
{0x10, {0x00}, 0x80},
{0x11, {0x00}, 0x80},
{0x29, {0x00}, 0x00},
};
bool get_lcd_spi_dma_write(void)
{
return lcd_spi_dma_write;
}
static spi_device_handle_t spi;
static void WriteComm(uint8_t data)
{
TFT_CS_L;
SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE));
SPI.write(0x00);
SPI.write(data);
SPI.write(0x00);
SPI.endTransaction();
TFT_CS_H;
}
static void WriteData(uint8_t data)
{
TFT_CS_L;
SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE));
SPI.write(data);
SPI.endTransaction();
TFT_CS_H;
}
static void lcd_send_cmd(uint32_t cmd, uint8_t *dat, uint32_t len)
{
#if LCD_USB_QSPI_DREVER == 1
TFT_CS_L;
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.flags = (SPI_TRANS_MULTILINE_CMD | SPI_TRANS_MULTILINE_ADDR);
#ifdef LCD_SPI_DMA
if(cmd == 0xff && len == 0x1f)
{
t.cmd = 0x02;
t.addr = 0xffff;
len = 0;
}
else if(cmd == 0x00)
{
t.cmd = 0X00;
t.addr = 0X0000;
len = 4;
}
else
{
t.cmd = 0x02;
t.addr = cmd << 8;
}
#else
t.cmd = 0x02;
t.addr = cmd << 8;
#endif
if (len != 0) {
t.tx_buffer = dat;
t.length = 8 * len;
} else {
t.tx_buffer = NULL;
t.length = 0;
}
spi_device_polling_transmit(spi, &t);
TFT_CS_H;
if(0)
{
WriteComm(cmd);
if (len != 0) {
for (int i = 0; i < len; i++)
WriteData(dat[i]);
}
}
#else
WriteComm(cmd);
if (len != 0) {
for (int i = 0; i < len; i++)
WriteData(dat[i]);
}
#endif
}
static void IRAM_ATTR spi_dma_cd(spi_transaction_t *trans)
{
if(transfer_num > 0)
{
transfer_num--;
}
if(lcd_PushColors_len <= 0 && transfer_num <= 0)
{
if(lcd_spi_dma_write) {
lcd_spi_dma_write = false;
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
if(disp != NULL)
lv_disp_flush_ready(disp->driver);
TFT_CS_H;
}
}
}
void lcd_send_data8(uint8_t dat) {
unsigned char i;
for (i = 0; i < 8; i++) {
if (dat & 0x80) {
digitalWrite(TFT_QSPI_D0, 1);
} else {
digitalWrite(TFT_QSPI_D0, 0);
}
dat <<= 1;
digitalWrite(TFT_QSPI_SCK, 0);
digitalWrite(TFT_QSPI_SCK, HIGH);
}
}
void axs15231_init(void)
{
pinMode(TFT_QSPI_CS, OUTPUT);
pinMode(TFT_QSPI_RST, OUTPUT);
TFT_RES_H;
delay(130);
TFT_RES_L;
delay(130);
TFT_RES_H;
delay(300);
#if LCD_USB_QSPI_DREVER == 1
esp_err_t ret;
spi_bus_config_t buscfg = {
.data0_io_num = TFT_QSPI_D0,
.data1_io_num = TFT_QSPI_D1,
.sclk_io_num = TFT_QSPI_SCK,
.data2_io_num = TFT_QSPI_D2,
.data3_io_num = TFT_QSPI_D3,
.max_transfer_sz = (SEND_BUF_SIZE * 16) + 8,
.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_GPIO_PINS /* |
SPICOMMON_BUSFLAG_QUAD */
,
};
spi_device_interface_config_t devcfg = {
.command_bits = 8,
.address_bits = 24,
.mode = TFT_SPI_MODE,
.clock_speed_hz = SPI_FREQUENCY,
.spics_io_num = -1,
// .spics_io_num = TFT_QSPI_CS,
.flags = SPI_DEVICE_HALFDUPLEX,
.queue_size = 17,
.post_cb = spi_dma_cd,
};
ret = spi_bus_initialize(TFT_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
ESP_ERROR_CHECK(ret);
ret = spi_bus_add_device(TFT_SPI_HOST, &devcfg, &spi);
ESP_ERROR_CHECK(ret);
#else
SPI.begin(TFT_SCK, -1, TFT_MOSI, TFT_CS);
SPI.setFrequency(SPI_FREQUENCY);
pinMode(TFT_DC, OUTPUT);
#endif
// Initialize the screen multiple times to prevent initialization failure
int i = 1;
while (i--) {
#if LCD_USB_QSPI_DREVER == 1
const lcd_cmd_t *lcd_init = axs15231b_qspi_init;
for (int i = 0; i < sizeof(axs15231b_qspi_init) / sizeof(lcd_cmd_t); i++)
#else
const lcd_cmd_t *lcd_init = axs15231_spi_init;
for (int i = 0; i < sizeof(axs15231_spi_init) / sizeof(lcd_cmd_t); i++)
#endif
{
lcd_send_cmd(lcd_init[i].cmd,
(uint8_t *)lcd_init[i].data,
lcd_init[i].len & 0x3f);
if (lcd_init[i].len & 0x80)
delay(200);
if (lcd_init[i].len & 0x40)
delay(20);
}
}
}
void lcd_setRotation(uint8_t r)
{
uint8_t gbr = TFT_MAD_RGB;
switch (r) {
case 0: // Portrait
// WriteData(gbr);
break;
case 1: // Landscape (Portrait + 90)
gbr = TFT_MAD_MX | TFT_MAD_MV | gbr;
break;
case 2: // Inverter portrait
gbr = TFT_MAD_MX | TFT_MAD_MY | gbr;
break;
case 3: // Inverted landscape
gbr = TFT_MAD_MV | TFT_MAD_MY | gbr;
break;
}
lcd_send_cmd(TFT_MADCTL, &gbr, 1);
}
void lcd_address_set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
lcd_cmd_t t[3] = {
{0x2a, {(uint8_t)(x1 >> 8), (uint8_t)x1, uint8_t(x2 >> 8), (uint8_t)(x2)}, 0x04},
{0x2b, {(uint8_t)(y1 >> 8), (uint8_t)(y1), (uint8_t)(y2 >> 8), (uint8_t)(y2)}, 0x04},
};
for (uint32_t i = 0; i < 2; i++) {
lcd_send_cmd(t[i].cmd, t[i].data, t[i].len);
}
}
void lcd_fill(uint16_t xsta,
uint16_t ysta,
uint16_t xend,
uint16_t yend,
uint16_t color)
{
uint16_t w = xend - xsta;
uint16_t h = yend - ysta;
uint16_t *color_p = (uint16_t *)heap_caps_malloc(w * h * 2, MALLOC_CAP_INTERNAL);
int i = 0;
for(i = 0; i < w * h ; i+=1)
{
color_p[i] = color;
}
lcd_PushColors(xsta, ysta, w, h, color_p);
free(color_p);
}
void lcd_DrawPoint(uint16_t x, uint16_t y, uint16_t color)
{
lcd_address_set(x, y, x + 1, y + 1);
lcd_PushColors(&color, 1);
}
void spi_device_queue_trans_fun(spi_device_handle_t handle, spi_transaction_t *trans_desc, TickType_t ticks_to_wait)
{
ESP_ERROR_CHECK(spi_device_queue_trans(spi, (spi_transaction_t *)trans_desc, portMAX_DELAY));
}
#ifdef LCD_SPI_DMA
spi_transaction_ext_t t = {0};
void lcd_PushColors(uint16_t x,
uint16_t y,
uint16_t width,
uint16_t high,
uint16_t *data)
{
static bool first_send = 1;
static uint16_t *p = (uint16_t *)data;
static uint32_t transfer_num_old = 0;
if(data != NULL && (width != 0) && (high != 0))
{
lcd_PushColors_len = width * high;
p = (uint16_t *)data;
first_send = 1;
transfer_num = 0;
lcd_address_set(x, y, x + width - 1, y + high - 1);
TFT_CS_L;
}
for (int x = 0; x < (transfer_num_old - (transfer_num_old-(transfer_num_old-transfer_num))); x++) {
spi_transaction_t *rtrans;
esp_err_t ret = spi_device_get_trans_result(spi, &rtrans, portMAX_DELAY);
if (ret != ESP_OK) {
// ESP_LOGW(TAG, "1. transfer_num = %d", transfer_num_old);
}
assert(ret == ESP_OK);
}
transfer_num_old -= (transfer_num_old - (transfer_num_old-(transfer_num_old-transfer_num)));
do {
if(transfer_num >= 3 || ESP.getFreeHeap() <= 70000)
{
break;
}
size_t chunk_size = lcd_PushColors_len;
memset(&t, 0, sizeof(t));
if (first_send) {
t.base.flags =
SPI_TRANS_MODE_QIO ;// | SPI_TRANS_MODE_DIOQIO_ADDR
t.base.cmd = 0x32 ;// 0x12
t.base.addr = 0x002C00;
first_send = 0;
} else {
t.base.flags = SPI_TRANS_MODE_QIO | SPI_TRANS_VARIABLE_CMD |
SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_DUMMY;
t.command_bits = 0;
t.address_bits = 0;
t.dummy_bits = 0;
}
if (chunk_size > SEND_BUF_SIZE) {
chunk_size = SEND_BUF_SIZE;
}
t.base.tx_buffer = p;
t.base.length = chunk_size * 16;
lcd_spi_dma_write = true;
transfer_num++;
transfer_num_old++;
lcd_PushColors_len -= chunk_size;
esp_err_t ret;
ESP_ERROR_CHECK(spi_device_queue_trans(spi, (spi_transaction_t *)&t, portMAX_DELAY));
assert(ret == ESP_OK);
p += chunk_size;
} while (lcd_PushColors_len > 0);
}
#if 0
void lcd_PushColors(uint16_t x,
uint16_t y,
uint16_t width,
uint16_t high,
uint16_t *data)
{
bool first_send = 1;
lcd_PushColors_len = width * high;
uint16_t *p = (uint16_t *)data;
spi_transaction_t *rtrans;
for (int x = 0; x < transfer_num; x++) {
esp_err_t ret = spi_device_get_trans_result(spi, &rtrans, portMAX_DELAY);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "1. transfer_num = %d", transfer_num);
}
assert(ret == ESP_OK);
}
transfer_num = 0;
lcd_address_set(x, y, x + width - 1, y + high - 1);
TFT_CS_L;
do {
size_t chunk_size = lcd_PushColors_len;
spi_transaction_ext_t t = {0};
memset(&t, 0, sizeof(t));
if (first_send) {
t.base.flags =
SPI_TRANS_MODE_QIO /* | SPI_TRANS_MODE_DIOQIO_ADDR */;
t.base.cmd = 0x32 /* 0x12 */;
t.base.addr = 0x002C00;
first_send = 0;
} else {
t.base.flags = SPI_TRANS_MODE_QIO | SPI_TRANS_VARIABLE_CMD |
SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_DUMMY;
t.command_bits = 0;
t.address_bits = 0;
t.dummy_bits = 0;
}
if (chunk_size > SEND_BUF_SIZE) {
chunk_size = SEND_BUF_SIZE;
}
t.base.tx_buffer = p;
t.base.length = chunk_size * 16;
lcd_spi_dma_write = true;
transfer_num++;
lcd_PushColors_len -= chunk_size;
spi_device_queue_trans_fun(spi, (spi_transaction_t *)&t, portMAX_DELAY);
p += chunk_size;
} while (lcd_PushColors_len > 0);
}
#endif
#else
void lcd_PushColors(uint16_t x,
uint16_t y,
uint16_t width,
uint16_t high,
uint16_t *data)
{
#if LCD_USB_QSPI_DREVER == 1
bool first_send = 1;
size_t len = width * high;
uint16_t *p = (uint16_t *)data;
lcd_address_set(x, y, x + width - 1, y + high - 1);
do {
TFT_CS_L;
size_t chunk_size = len;
spi_transaction_ext_t t = {0};
memset(&t, 0, sizeof(t));
if (1) {
t.base.flags =
SPI_TRANS_MODE_QIO /* | SPI_TRANS_MODE_DIOQIO_ADDR */;
t.base.cmd = 0x32 /* 0x12 */;
if(first_send)
{
t.base.addr = 0x002C00;
}
else
t.base.addr = 0x003C00;
first_send = 0;
} else {
t.base.flags = SPI_TRANS_MODE_QIO | SPI_TRANS_VARIABLE_CMD |
SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_DUMMY;
t.command_bits = 0;
t.address_bits = 0;
t.dummy_bits = 0;
}
if (chunk_size > SEND_BUF_SIZE) {
chunk_size = SEND_BUF_SIZE;
}
t.base.tx_buffer = p;
t.base.length = chunk_size * 16;
int aaa = 0;
aaa = aaa>>1;
aaa = aaa>>1;
aaa = aaa>>1;
if(!first_send)
TFT_CS_H;
aaa = aaa>>1;
aaa = aaa>>1;
aaa = aaa>>1;
aaa = aaa>>1;
aaa = aaa>>1;
TFT_CS_L;
aaa = aaa>>1;
aaa = aaa>>1;
aaa = aaa>>1;
spi_device_polling_transmit(spi, (spi_transaction_t *)&t);
len -= chunk_size;
p += chunk_size;
} while (len > 0);
TFT_CS_H;
#else
lcd_address_set(x, y, x + width - 1, y + high - 1);
TFT_CS_L;
SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE));
SPI.writeBytes((uint8_t *)data, width * high * 2);
SPI.endTransaction();
TFT_CS_H;
#endif
}
#endif
void lcd_PushColors(uint16_t *data, uint32_t len)
{
#if LCD_USB_QSPI_DREVER == 1
bool first_send = 1;
uint16_t *p = (uint16_t *)data;
TFT_CS_L;
do {
size_t chunk_size = len;
spi_transaction_ext_t t = {0};
memset(&t, 0, sizeof(t));
if (first_send) {
t.base.flags =
SPI_TRANS_MODE_QIO /* | SPI_TRANS_MODE_DIOQIO_ADDR */;
t.base.cmd = 0x32 /* 0x12 */;
t.base.addr = 0x002C00;
first_send = 0;
} else {
t.base.flags = SPI_TRANS_MODE_QIO | SPI_TRANS_VARIABLE_CMD |
SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_DUMMY;
t.command_bits = 0;
t.address_bits = 0;
t.dummy_bits = 0;
}
if (chunk_size > SEND_BUF_SIZE) {
chunk_size = SEND_BUF_SIZE;
}
t.base.tx_buffer = p;
t.base.length = chunk_size * 16;
spi_device_polling_transmit(spi, (spi_transaction_t *)&t);
len -= chunk_size;
p += chunk_size;
} while (len > 0);
TFT_CS_H;
#else
TFT_CS_L;
SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, TFT_SPI_MODE));
SPI.writeBytes((uint8_t *)data, len * 2);
SPI.endTransaction();
TFT_CS_H;
#endif
}
void lcd_sleep()
{
lcd_send_cmd(0x10, NULL, 0);
}

60
AXS15231B.h Normal file
View File

@@ -0,0 +1,60 @@
#pragma once
#include "stdint.h"
#include "pins_config.h"
#include "lvgl.h"/* https://github.com/lvgl/lvgl.git */
#define LCD_SPI_DMA
#define AX15231B
#define TFT_MADCTL 0x36
#define TFT_MAD_MY 0x80
#define TFT_MAD_MX 0x40
#define TFT_MAD_MV 0x20
#define TFT_MAD_ML 0x10
#define TFT_MAD_BGR 0x08
#define TFT_MAD_MH 0x04
#define TFT_MAD_RGB 0x00
#define TFT_INVOFF 0x20
#define TFT_INVON 0x21
#define TFT_SCK_H digitalWrite(TFT_SCK, 1);
#define TFT_SCK_L digitalWrite(TFT_SCK, 0);
#define TFT_SDA_H digitalWrite(TFT_MOSI, 1);
#define TFT_SDA_L digitalWrite(TFT_MOSI, 0);
#define TFT_RES_H digitalWrite(TFT_QSPI_RST, 1);
#define TFT_RES_L digitalWrite(TFT_QSPI_RST, 0);
#define TFT_DC_H digitalWrite(TFT_DC, 1);
#define TFT_DC_L digitalWrite(TFT_DC, 0);
#define TFT_CS_H digitalWrite(TFT_QSPI_CS, 1);
#define TFT_CS_L digitalWrite(TFT_QSPI_CS, 0);
typedef struct
{
uint8_t cmd;
uint8_t data[36];
uint8_t len;
} lcd_cmd_t;
void axs15231_init(void);
// Set the display window size
void lcd_address_set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void lcd_setRotation(uint8_t r);
void lcd_DrawPoint(uint16_t x, uint16_t y, uint16_t color);
void lcd_fill(uint16_t xsta,
uint16_t ysta,
uint16_t xend,
uint16_t yend,
uint16_t color);
void lcd_PushColors(uint16_t x,
uint16_t y,
uint16_t width,
uint16_t high,
uint16_t *data);
void lcd_PushColors(uint16_t *data, uint32_t len);
void lcd_sleep();
bool get_lcd_spi_dma_write(void);

0
README.md Normal file
View File

309
lilygo-sensor-clock.ino Normal file
View File

@@ -0,0 +1,309 @@
#include "lvgl.h"
#include "pins_config.h"
#include "AXS15231B.h"
#include "ui-util.h"
#include "WiFi.h"
#include "sntp.h"
#include "time.h"
#include <Arduino.h>
#include <XPowersLib.h>
#include <HTTPClient.h>
#include "freertos/semphr.h"
// Serial
#define BAUD 115200
// Sensor pins
#define RX2_PIN TOUCH_IICSCL
#define TX2_PIN TOUCH_IICSDA
// Sensor configuration
#define COMMAND "FDFCFBFA0800120000006400000004030201" //Normal mode command, see documentation waveshare sensor
#define PRESENCE_DISTANCE 200 // in cm
#define DELAY_STATUS_CHECK 1000 // delay (in ms) between two checks of the presence
#define SLEEP_DELAY 10000 // delay (in ms) until sleep mode
#define POWER_DELAY 30000 // delay (in ms) until turning off the screen
SemaphoreHandle_t xSemaphore = NULL;
PowersSY6970 PMU;
// Sensor variables
uint16_t state = 1; // 0 = presence detected, 1 = no presence detected for the past SLEEP_DELAY ms, 2 = no presence detected for the past POWER_DELAY ms
unsigned long lastMotionCheckTime = millis();
unsigned long lastMotionDetected = 0;
// Time variables
static uint32_t last_tick;
struct tm timeinfo;
uint32_t cycleInterval = 0;
// Display variables
static lv_disp_draw_buf_t draw_buf;
static lv_color_t *buf;
static lv_color_t *buf1;
// REST API Home assistant request
static String BASE_URL_HA = "http://192.168.1.2:8123/api/services";
static String HA_TOKEN = "abc";
void setup() {
xSemaphore = xSemaphoreCreateBinary();
xSemaphoreGive(xSemaphore);
initSerial();
initializeWifi();
initializeTime();
sendCommandAsHex(COMMAND);
initializeUI();
}
void loop() {
delay(1);
if (transfer_num <= 0 && lcd_PushColors_len <= 0)
lv_timer_handler();
if (transfer_num <= 1 && lcd_PushColors_len > 0) {
lcd_PushColors(0, 0, 0, 0, NULL);
}
static int flag_bl = 0;
static unsigned long cnt = 0;
printLocalTime();
readAndProcessSensorLines();
cnt++;
if (cnt >= 100) {
if (flag_bl == 0) {
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
flag_bl = 1;
lv_delay_ms(500);
ui_begin();
}
}
}
void initSerial() {
// Start the primary serial communication (USB Monitor)
Serial.begin(BAUD);
unsigned long startAttemptTime = millis();
while (!Serial && millis() - startAttemptTime < 2000) {
delay(100);
}
Serial.println("Serial Monitor Initialized.");
pinMode(PIN_BAT_VOLT, ANALOG);
// Screen
pinMode(TOUCH_RES, OUTPUT);
digitalWrite(TOUCH_RES, HIGH);
delay(2);
digitalWrite(TOUCH_RES, LOW);
delay(10);
digitalWrite(TOUCH_RES, HIGH);
delay(2);
// Start Serial2 for the HMMD Sensor
Serial2.begin(BAUD, SERIAL_8N1, RX2_PIN, TX2_PIN);
Serial.println("Serial2 Initialized on RX:" + String(RX2_PIN) + ", TX:" + String(TX2_PIN));
}
void sendCommandAsHex(String hexString) {
int hexStringLength = hexString.length();
if (hexStringLength % 2 != 0) {
Serial.println("Error: Hex string must have an even number of characters.");
return;
}
int byteCount = hexStringLength / 2;
byte hexBytes[byteCount];
for (int i = 0; i < hexStringLength; i += 2) {
String byteString = hexString.substring(i, i + 2);
byte hexByte = (byte)strtoul(byteString.c_str(), NULL, 16);
hexBytes[i / 2] = hexByte;
}
// Print confirmation of what's being sent
Serial.print("Sending ");
Serial.print(byteCount);
Serial.print(" bytes: ");
for(int i=0; i<byteCount; i++) {
if (hexBytes[i] < 16) Serial.print("0");;
}
Serial.println();
// Send the data
Serial2.write(hexBytes, byteCount);
Serial.println("Initial command sent to Serial2.");
}
void initializeWifi() {
WiFi.persistent(true);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.println("Connecting to WiFi...");
int connectTime = millis();
while(WiFi.status() != WL_CONNECTED) {
delay(200);
Serial.print(".");
if (millis() - connectTime >= WIFI_CONNECT_WAIT_MAX) {
ESP.restart();
}
}
Serial.println();
Serial.println("Connection succesful!");
Serial.printf("SSID: %s\r\n", WiFi.SSID().c_str());
String IP = WiFi.localIP().toString();
Serial.printf("IP address: %s\r\n", IP.c_str());
}
void initializeTime() {
Serial.println("Initializing time...");
configTime(GMT_OFFSET_SEC, DAY_LIGHT_OFFSET_SEC, NTP_SERVER1, NTP_SERVER2);
}
void initializeUI() {
Serial.println("Initializing UI....");
axs15231_init();
lv_init();
size_t buffer_size =
sizeof(lv_color_t) * EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES;
buf = (lv_color_t *)ps_malloc(buffer_size);
if (buf == NULL) {
while (1) {
Serial.println("buf NULL");
delay(500);
}
}
buf1 = (lv_color_t *)ps_malloc(buffer_size);
if (buf1 == NULL) {
while (1) {
Serial.println("buf NULL");
delay(500);
}
}
lv_disp_draw_buf_init(&draw_buf, buf, buf1, buffer_size);
/*Initialize the display*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
/*Change the following line to your display resolution*/
disp_drv.hor_res = EXAMPLE_LCD_H_RES;
disp_drv.ver_res = EXAMPLE_LCD_V_RES;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
disp_drv.sw_rotate = 1; // If you turn on software rotation, Do not update or replace LVGL
disp_drv.rotated = LV_DISP_ROT_90;
disp_drv.full_refresh = 1; // full_refresh must be 1
lv_disp_drv_register(&disp_drv);
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
lv_indev_drv_register(&indev_drv);
Serial.println("UI initialized");
}
void printLocalTime() {
if (millis() - last_tick > 100) {
struct tm timeInfo;
if(!getLocalTime(&timeInfo)){
Serial.println("Failed to obtain time");
} else {
lv_msg_send(MSG_NEW_HOUR, &timeInfo.tm_hour);
lv_msg_send(MSG_NEW_MIN, &timeInfo.tm_min);
lv_msg_send(MSG_NEW_SEC, &timeInfo.tm_sec);
}
last_tick = millis();
}
}
void readAndProcessSensorLines() {
if (millis() - lastMotionCheckTime >= DELAY_STATUS_CHECK) {
lastMotionCheckTime = millis();
// Check if data is available on Serial2
while (Serial2.available() > 0) {
// Read a line of text until a newline character (\n) is received
// The timeout helps prevent blocking forever if a line ending is missed
String line = Serial2.readStringUntil('\n');
// Clean up the line: remove potential carriage return (\r) and leading/trailing whitespace
line.trim();
// Check if the line contains the "Range" information
if (line.startsWith("Range ")) {
// Extract the substring after "Range "
String distanceStr = line.substring(6);
int distance = distanceStr.toInt();
bool currentStatus = distance <= PRESENCE_DISTANCE;
if (currentStatus) {
lastMotionDetected = millis();
}
if (currentStatus && state != 0) {
state = 0;
Serial.println("Motion detected, turning screen on.");
//rest_api_action(0);
} else if (!currentStatus && state == 1 && (millis() - lastMotionDetected >= POWER_DELAY)) {
state = 2;
Serial.printf("No motion detected for %d seconds, turning screen off.\n", POWER_DELAY);
//rest_api_action(2);
} else if (!currentStatus && state == 0 && (millis() - lastMotionDetected >= SLEEP_DELAY)) {
state = 1;
Serial.printf("No motion detected for %d seconds, turning screen screensaver on.\n", SLEEP_DELAY);
//rest_api_action(1);
}
}
}
}
}
// action: 0 => Screen on, => 1 Screen off, => 2 Power off
void rest_api_action(int action) {
WiFiClient client;
HTTPClient http;
int httpResponseCode;
String url;
if(WiFi.status() != WL_CONNECTED){
Serial.println("WiFi Disconnected. No request sent");
}
switch (action) {
case 0: // Screen on
Serial.println("Sending Screen ON Request");
url = BASE_URL_HA + "/webostv/command";
http.begin(client, url);
http.addHeader("Authorization", "Bearer " + HA_TOKEN);
http.addHeader("Content-Type", "application/json");
httpResponseCode = http.POST("{\"entity_id\":\"media_player.lg_webos_smart_tv\",\"command\":\"com.webos.service.tvpower/power/turnOnScreen\"}");
break;
case 1: // Screen off
Serial.println("Sending Screen OFF Request");
url = BASE_URL_HA + "/webostv/command";
http.begin(client, url);
http.addHeader("Authorization", "Bearer " + HA_TOKEN);
http.addHeader("Content-Type", "application/json");
httpResponseCode = http.POST("{\"entity_id\":\"media_player.lg_webos_smart_tv\",\"command\":\"com.webos.service.tvpower/power/turnOffScreen\"}");
break;
case 2: // Power off
Serial.println("Sending Power Off Request");
url = BASE_URL_HA + "/webostv/command";
http.begin(client, url);
http.addHeader("Authorization", "Bearer " + HA_TOKEN);
http.addHeader("Content-Type", "application/json");
httpResponseCode = http.POST("{\"entity_id\":\"media_player.lg_webos_smart_tv\",\"command\":\"com.webos.service.tvpower/power/powerOffScreen\"}");
break;
}
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
http.end();
}

60
pins_config.h Normal file
View File

@@ -0,0 +1,60 @@
#pragma once
/***********************config*************************/
#define LCD_USB_QSPI_DREVER 1
#define SPI_FREQUENCY 32000000
#define TFT_SPI_MODE SPI_MODE0
#define TFT_SPI_HOST SPI2_HOST
#define WIFI_SSID "WIFI SSID"
#define WIFI_PASSWORD "WIFI Password"
#define WIFI_CONNECT_WAIT_MAX (30 * 1000)
#define NTP_SERVER1 "pool.ntp.org"
#define NTP_SERVER2 "time.nist.gov"
#define GMT_OFFSET_SEC 1*60*60
#define DAY_LIGHT_OFFSET_SEC 3600
/* Automatically update local time */
#define GET_TIMEZONE_API "https://ipapi.co/timezone/"
/***********************config*************************/
#define TFT_WIDTH 180
#define TFT_HEIGHT 640
#ifdef TFT_WIDTH
#define EXAMPLE_LCD_H_RES TFT_WIDTH
#else
#define EXAMPLE_LCD_H_RES 180
#endif
#ifdef TFT_HEIGHT
#define EXAMPLE_LCD_V_RES TFT_HEIGHT
#else
#define EXAMPLE_LCD_V_RES 640
#endif
#define LVGL_LCD_BUF_SIZE (EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES)
#define SEND_BUF_SIZE (28800/2) //16bit(RGB565)
#define TFT_QSPI_CS 12
#define TFT_QSPI_SCK 17
#define TFT_QSPI_D0 13
#define TFT_QSPI_D1 18
#define TFT_QSPI_D2 21
#define TFT_QSPI_D3 14
#define TFT_QSPI_RST 16
#define TFT_BL 1
#define PIN_BAT_VOLT 2
#define PIN_BUTTON_1 0
#define PIN_BUTTON_2 21
#define TOUCH_IICSCL 10
#define TOUCH_IICSDA 15
#define TOUCH_INT 11
#define TOUCH_RES 16

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

3316
src/font_Alibaba.c Normal file

File diff suppressed because it is too large Load Diff

2216
src/gif_01.c Normal file

File diff suppressed because it is too large Load Diff

2404
src/gif_12.c Normal file

File diff suppressed because it is too large Load Diff

2286
src/gif_23.c Normal file

File diff suppressed because it is too large Load Diff

2106
src/gif_34.c Normal file

File diff suppressed because it is too large Load Diff

2526
src/gif_45.c Normal file

File diff suppressed because it is too large Load Diff

2176
src/gif_56.c Normal file

File diff suppressed because it is too large Load Diff

2217
src/gif_67.c Normal file

File diff suppressed because it is too large Load Diff

2481
src/gif_78.c Normal file

File diff suppressed because it is too large Load Diff

2506
src/gif_89.c Normal file

File diff suppressed because it is too large Load Diff

2676
src/gif_90.c Normal file

File diff suppressed because it is too large Load Diff

1415
src/lilygo1_gif.c Normal file

File diff suppressed because it is too large Load Diff

32565
src/lilygo2_gif.c Normal file

File diff suppressed because it is too large Load Diff

156
ui-util.cpp Normal file
View File

@@ -0,0 +1,156 @@
#include "lvgl.h"
#include "pins_config.h"
#include "AXS15231B.h"
#include "ui-util.h"
#include "time.h"
#include <Arduino.h>
#include <SPI.h>
#define set_anim_src(x) ((const void *)trans_ainm_buf[(x)])
#define buf_limit(idx) ((idx) % 10)
#define timer_h(v, n, obj) \
do { \
if (v != n / 10) { \
v = buf_limit(n / 10); \
if (v != 0) \
lv_gif_set_src(obj, (const void *)trans_ainm_buf[v - 1]); \
else \
lv_gif_set_src(obj, (const void *)trans_ainm_buf[buf_limit(9)]); \
} \
} while (0);
#define timer_l(v, n, obj) \
do { \
if (v != n % 10) { \
v = buf_limit(n % 10); \
if (v != 0) \
lv_gif_set_src(obj, (const void *)trans_ainm_buf[v - 1]); \
else \
lv_gif_set_src(obj, (const void *)trans_ainm_buf[buf_limit(9)]); \
} \
} while (0);
static lv_obj_t *dis;
static lv_obj_t *img1;
static lv_obj_t *img2;
static lv_obj_t *img3;
static lv_obj_t *img4;
static lv_obj_t *img5;
static lv_obj_t *img6;
// Transition animation
static const void *trans_ainm_buf[] = {
&gif_01, &gif_12, &gif_23, &gif_34, &gif_45,
&gif_56, &gif_67, &gif_78, &gif_89, &gif_90
};
static void update_text_subscriber_cb_demo1(void *s, lv_msg_t *msg);
void set_flip_time_anim(int hour, int minute, int second);
void ui_begin() {
dis = lv_tileview_create(lv_scr_act());
lv_obj_align(dis, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_obj_set_size(dis, LV_PCT(100), LV_PCT(100));
// lv_obj_remove_style(dis, 0, LV_PART_SCROLLBAR);
lv_obj_set_style_bg_color(dis, lv_color_black(), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_t *tv1 = lv_tileview_add_tile(dis, 0, 0, LV_DIR_VER);
lv_obj_t *tv2 = lv_tileview_add_tile(dis, 0, 1, LV_DIR_VER);
lv_obj_t *tv3 = lv_tileview_add_tile(dis, 0, 2, LV_DIR_VER);
lv_obj_t *tv4 = lv_tileview_add_tile(dis, 0, 3, LV_DIR_VER);
img1 = lv_gif_create(tv1);
img2 = lv_gif_create(tv1);
img3 = lv_gif_create(tv1);
img4 = lv_gif_create(tv1);
img5 = lv_gif_create(tv1);
img6 = lv_gif_create(tv1);
lv_gif_set_src(img1, set_anim_src(9));
lv_gif_set_src(img2, set_anim_src(9));
lv_gif_set_src(img3, set_anim_src(9));
lv_gif_set_src(img4, set_anim_src(9));
lv_gif_set_src(img5, set_anim_src(9));
lv_gif_set_src(img6, set_anim_src(9));
lv_obj_align(img1, LV_ALIGN_LEFT_MID, 20, 0);
lv_obj_align_to(img2, img1, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
lv_obj_align(img3, LV_ALIGN_LEFT_MID, 225, 0);
lv_obj_align_to(img4, img3, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
lv_obj_align(img5, LV_ALIGN_LEFT_MID, 430, 0);
lv_obj_align_to(img6, img5, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
lv_msg_subsribe(MSG_NEW_HOUR, update_text_subscriber_cb_demo1, NULL);
lv_msg_subsribe(MSG_NEW_MIN, update_text_subscriber_cb_demo1, NULL);
lv_msg_subsribe(MSG_NEW_SEC, update_text_subscriber_cb_demo1, NULL);
}
static void update_text_subscriber_cb_demo1(void *s, lv_msg_t *msg) {
static int hour = 0;
static int minute = 0;
static int second = 0;
static uint32_t start_tick = lv_tick_get();
const int32_t *v = (const int32_t *)lv_msg_get_payload(msg);
/* clang-format off */
switch (lv_msg_get_id(msg)) {
case MSG_NEW_HOUR: hour = *v; break;
case MSG_NEW_MIN: minute = *v; break;
case MSG_NEW_SEC: second = *v; break;
default:
break;
} /* clang-format off */
if (lv_tick_elaps(start_tick) >= 1000) {
start_tick = lv_tick_get();
set_flip_time_anim(hour, minute, second);
}
}
void set_flip_time_anim(int hour, int minute, int second) {
static int sec_h, sec_l, min_h, min_l, hou_h, hou_l;
timer_l(sec_l, second, img6);
timer_h(sec_h, second, img5);
timer_l(min_l, minute, img4);
timer_h(min_h, minute, img3);
timer_l(hou_l, hour, img2);
timer_h(hou_h, hour, img1);
// printf("clock=%02d:%02d:%02d\n", hour, minute, second);
}
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
#ifdef LCD_SPI_DMA
char i = 0;
while (get_lcd_spi_dma_write()) {
i = i >> 1;
lcd_PushColors(0, 0, 0, 0, NULL);
}
#endif
lcd_PushColors(area->x1, area->y1, w, h, (uint16_t *)&color_p->full);
#ifdef LCD_SPI_DMA
#else
lv_disp_flush_ready(disp);
#endif
}
void lv_delay_ms(int x) {
do {
uint32_t t = x;
while (t--) {
delay(1);
if (transfer_num <= 0 && lcd_PushColors_len <= 0)
lv_timer_handler();
if (transfer_num <= 1 && lcd_PushColors_len > 0) {
lcd_PushColors(0, 0, 0, 0, NULL);
}
}
} while (0);
}

33
ui-util.h Normal file
View File

@@ -0,0 +1,33 @@
#define UI_BG_COLOR lv_color_black()
#define UI_FRAME_COLOR lv_color_hex(0x282828)
#define UI_FONT_COLOR lv_color_white()
#define UI_PAGE_COUNT 3
#define MSG_NEW_HOUR 1
#define MSG_NEW_MIN 2
#define MSG_NEW_VOLT 3
#define MSG_NEW_TOUCH_POINT 4
#define MSG_NEW_SEC 5
#define MSG_NEW_USB 6
#define MSG_NEW_OTG_BTN 7
LV_FONT_DECLARE(font_Alibaba);
LV_IMG_DECLARE(lilygo1_gif);
LV_IMG_DECLARE(lilygo2_gif);
LV_IMG_DECLARE(gif_01);
LV_IMG_DECLARE(gif_12);
LV_IMG_DECLARE(gif_23);
LV_IMG_DECLARE(gif_34);
LV_IMG_DECLARE(gif_45);
LV_IMG_DECLARE(gif_56);
LV_IMG_DECLARE(gif_67);
LV_IMG_DECLARE(gif_78);
LV_IMG_DECLARE(gif_89);
LV_IMG_DECLARE(gif_90);
extern uint32_t transfer_num;
extern size_t lcd_PushColors_len;
void ui_begin();
void lv_delay_ms(int x);
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);