Initial commit.

This commit is contained in:
imi415 2021-12-29 14:27:33 +08:00
commit 7f183e2928
Signed by: imi415
GPG Key ID: 17F01E106F9F5E0A
16 changed files with 578 additions and 0 deletions

11
.clang-format Normal file
View File

@ -0,0 +1,11 @@
BasedOnStyle: Google
IndentWidth: 4
AlignConsecutiveMacros: AcrossEmptyLines
AlignConsecutiveDeclarations: true
AlignConsecutiveAssignments: AcrossEmptyLinesAndComments
BreakBeforeBraces: Custom
BraceWrapping:
AfterEnum: false
AfterStruct: false
SplitEmptyFunction: false
ColumnLimit: 120

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/build
/sdkconfig
/sdkconfig.old

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "components/lvgl"]
path = components/lvgl
url = https://github.com/lvgl/lvgl.git
[submodule "main/lib/epd-spi"]
path = main/lib/epd-spi
url = https://github.com/imi415/epd-spi.git

6
CMakeLists.txt Normal file
View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp32_s2_cal)

8
Makefile Normal file
View File

@ -0,0 +1,8 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := esp32_s2_cal
include $(IDF_PATH)/make/project.mk

52
README.md Normal file
View File

@ -0,0 +1,52 @@
# Hello World Example
Starts a FreeRTOS task to print "Hello World".
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## How to use example
Follow detailed instructions provided specifically for this example.
Select the instructions depending on Espressif chip installed on your development board:
- [ESP32 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/stable/get-started/index.html)
- [ESP32-S2 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html)
## Example folder contents
The project **hello_world** contains one source file in C language [hello_world_main.c](main/hello_world_main.c). The file is located in folder [main](main).
ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt` files that provide set of directives and instructions describing the project's source files and targets (executable, library, or both).
Below is short explanation of remaining files in the project folder.
```
├── CMakeLists.txt
├── example_test.py Python script used for automated example testing
├── main
│   ├── CMakeLists.txt
│   ├── component.mk Component make file
│   └── hello_world_main.c
├── Makefile Makefile used by legacy GNU Make
└── README.md This is the file you are currently reading
```
For more information on structure and contents of ESP-IDF projects, please refer to Section [Build System](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html) of the ESP-IDF Programming Guide.
## Troubleshooting
* Program upload failure
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
## Technical support and feedback
Please use the following feedback channels:
* For technical queries, go to the [esp32.com](https://esp32.com/) forum
* For a feature request or bug report, create a [GitHub issue](https://github.com/espressif/esp-idf/issues)
We will get back to you as soon as possible.

1
components/lvgl Submodule

@ -0,0 +1 @@
Subproject commit d38eb1e689fa5a64c25e677275172d9c8a4ab2f0

12
main/CMakeLists.txt Normal file
View File

@ -0,0 +1,12 @@
idf_component_register(SRCS
"main.c"
"helper/helper_wifi.c"
"impl/impl_epd.c"
"impl/impl_lvgl.c"
"lib/epd-spi/src/epd_common.c"
"lib/epd-spi/src/epd_panel_gdew042t2.c"
INCLUDE_DIRS
"lib/epd-spi/include"
"impl"
)

4
main/component.mk Normal file
View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

17
main/helper/helper_wifi.c Normal file
View File

@ -0,0 +1,17 @@
#include "esp_log.h"
#include "esp_spi_flash.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
esp_err_t helper_wifi_init(void) {
// Check stored credentials from NVS,
// if no credentials stored or button pressed, force initiate easy connect
return ESP_OK;
}
void helper_wifi_task(void *pvParameters) {
//
}

179
main/impl/impl_epd.c Normal file
View File

@ -0,0 +1,179 @@
#include "impl_epd.h"
#define EPD_GPIO_MOSI GPIO_NUM_15
#define EPD_GPIO_SCLK GPIO_NUM_14
#define EPD_GPIO_BUSY GPIO_NUM_11
#define EPD_GPIO_CS GPIO_NUM_13
#define EPD_GPIO_RES GPIO_NUM_10
#define EPD_GPIO_DC GPIO_NUM_12
#define EPD_SPI_HOST SPI2_HOST
#define EPD_SPI_SPEED (16 * 1000 * 1000) /* 16MHz */
#define EPD_SPI_MAX_XFER_SIZE (1024)
/* Pre-transfer callback for SPI driver. */
static void impl_epd_spi_pre_transfer_cb(spi_transaction_t *txn) {
uint32_t dc_value = (uint32_t)txn->user;
gpio_set_level(EPD_GPIO_DC, dc_value);
}
static void IRAM_ATTR impl_epd_busy_irq_handler(void *arg) {
impl_epd_handle_t *epd = arg;
BaseType_t xHigherPriorityTaskWoken;
xSemaphoreGiveFromISR(epd->busy_semaphore, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
esp_err_t impl_epd_init(void *handle) {
esp_err_t ret;
spi_bus_config_t bus_config = {
.mosi_io_num = EPD_GPIO_MOSI,
.sclk_io_num = EPD_GPIO_SCLK,
.miso_io_num = -1,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = EPD_SPI_MAX_XFER_SIZE,
};
spi_device_interface_config_t device_config = {
.clock_speed_hz = EPD_SPI_SPEED,
.mode = 0,
.spics_io_num = EPD_GPIO_CS,
.queue_size = 7,
.pre_cb = impl_epd_spi_pre_transfer_cb,
};
impl_epd_handle_t *epd = handle;
/* Initialize SPI bus */
ret = spi_bus_initialize(EPD_SPI_HOST, &bus_config, SPI_DMA_CH_AUTO);
ESP_ERROR_CHECK(ret);
ret = spi_bus_add_device(EPD_SPI_HOST, &device_config, &epd->spi);
ESP_ERROR_CHECK(ret);
/* Initialize RTOS components */
epd->busy_semaphore = xSemaphoreCreateBinary();
if (epd->busy_semaphore == NULL) {
return ESP_FAIL;
}
/* Initialize auxillary IOs */
gpio_set_direction(EPD_GPIO_BUSY, GPIO_MODE_INPUT);
gpio_set_direction(EPD_GPIO_DC, GPIO_MODE_OUTPUT);
gpio_set_direction(EPD_GPIO_RES, GPIO_MODE_OUTPUT);
// Set up EPD busy interrupt
gpio_pullup_en(EPD_GPIO_BUSY);
gpio_set_intr_type(EPD_GPIO_BUSY, GPIO_INTR_POSEDGE);
gpio_isr_handler_add(EPD_GPIO_BUSY, impl_epd_busy_irq_handler, epd);
/* Release RESET pin */
gpio_set_level(EPD_GPIO_RES, 1);
return ret;
}
epd_ret_t impl_epd_write_command(void *handle, uint8_t *command, uint32_t len) {
esp_err_t ret = EPD_OK;
spi_transaction_t txn;
impl_epd_handle_t *epd = handle;
memset(&txn, 0x0, sizeof(txn));
txn.length = 8;
txn.tx_buffer = command;
txn.user = (void *)0;
ret = spi_device_polling_transmit(epd->spi, &txn);
if (ret != ESP_OK) ret = EPD_FAIL;
if (len > 1) {
txn.length = 8 * (len - 1);
txn.tx_buffer = &command[1];
txn.user = (void *)1;
ret = spi_device_polling_transmit(epd->spi, &txn);
if (ret != ESP_OK) ret = EPD_FAIL;
}
return ret;
}
epd_ret_t impl_epd_write_data(void *handle, uint8_t *data, uint32_t len) {
esp_err_t ret = EPD_OK;
spi_transaction_t txn;
impl_epd_handle_t *epd = handle;
uint8_t *dma_buffer = heap_caps_malloc(EPD_SPI_MAX_XFER_SIZE, MALLOC_CAP_DMA);
if (dma_buffer == NULL) return EPD_FAIL;
memset(&txn, 0x0, sizeof(txn));
uint8_t has_partial = (len % EPD_SPI_MAX_XFER_SIZE ? 1 : 0);
uint32_t txn_count = len / EPD_SPI_MAX_XFER_SIZE + has_partial;
txn.user = (void *)1;
for (uint32_t i = 0; i < txn_count; i++) {
uint16_t txn_bytes = EPD_SPI_MAX_XFER_SIZE;
if ((i == txn_count - 1) && has_partial) {
txn_bytes = len % EPD_SPI_MAX_XFER_SIZE;
}
memcpy(dma_buffer, &data[i * EPD_SPI_MAX_XFER_SIZE], txn_bytes);
txn.length = 8 * txn_bytes;
txn.rxlength = 0;
txn.tx_buffer = dma_buffer;
ret = spi_device_polling_transmit(epd->spi, &txn);
if (ret != ESP_OK) ret = EPD_FAIL;
}
free(dma_buffer);
return ret;
}
epd_ret_t impl_epd_reset(void *handle) {
for (uint8_t i = 0; i < 4; i++) {
gpio_set_level(EPD_GPIO_RES, 0);
vTaskDelay(pdMS_TO_TICKS(10));
gpio_set_level(EPD_GPIO_RES, 1);
vTaskDelay(pdMS_TO_TICKS(10));
}
vTaskDelay(pdMS_TO_TICKS(50));
return EPD_OK;
}
epd_ret_t impl_epd_poll_busy(void *handle) {
impl_epd_handle_t *epd = handle;
if (gpio_get_level(EPD_GPIO_BUSY) == 1) {
return EPD_OK;
}
gpio_intr_enable(EPD_GPIO_BUSY);
if (xSemaphoreTake(epd->busy_semaphore, pdMS_TO_TICKS(10000)) != pdTRUE) {
gpio_intr_disable(EPD_GPIO_BUSY);
return EPD_FAIL;
}
gpio_intr_disable(EPD_GPIO_BUSY);
return EPD_OK;
}
epd_ret_t impl_epd_delay(void *handle, uint32_t msec) {
vTaskDelay(pdMS_TO_TICKS(msec));
return EPD_OK;
}

25
main/impl/impl_epd.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef IMPL_EPD_H
#define IMPL_EPD_H
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
//
#include "epd_panel_gdew042t2.h"
typedef struct {
spi_device_handle_t spi;
SemaphoreHandle_t busy_semaphore;
} impl_epd_handle_t;
esp_err_t impl_epd_init(void *handle);
epd_ret_t impl_epd_write_command(void *handle, uint8_t *command, uint32_t len);
epd_ret_t impl_epd_write_data(void *handle, uint8_t *data, uint32_t len);
epd_ret_t impl_epd_reset(void *handle);
epd_ret_t impl_epd_poll_busy(void *handle);
epd_ret_t impl_epd_delay(void *handle, uint32_t msec);
#endif

186
main/impl/impl_lvgl.c Normal file
View File

@ -0,0 +1,186 @@
#include "impl_lvgl.h"
#include "esp_log.h"
#include "impl_epd.h"
#define EPD_DISPLAY_PIXEL_COUNT 400 * 300
#define EPD_DISPLAY_FRAME_SIZE (EPD_DISPLAY_PIXEL_COUNT * 2 / 8)
#define EPD_DISPLAY_GS 0
#define EPD_DISPLAY_MAX_PARTIAL 6
static impl_epd_handle_t s_epd_impl;
static epd_gdew042t2_t s_gd_epd = {
.cb =
{
.reset_cb = impl_epd_reset,
.write_command_cb = impl_epd_write_command,
.write_data_cb = impl_epd_write_data,
.poll_busy_cb = impl_epd_poll_busy,
.delay_cb = impl_epd_delay,
},
#if EPD_DISPLAY_GS
.mode = EPD_GDEW042T2_MODE_GS,
#else
.mode = EPD_GDEW042T2_MODE_BW,
#endif
.user_data = &s_epd_impl,
};
#if !EPD_DISPLAY_GS
static uint8_t s_epd_partial_counter = EPD_DISPLAY_MAX_PARTIAL - 1;
#endif
static lv_disp_draw_buf_t s_disp_buf;
static lv_color_t s_disp_store[EPD_DISPLAY_FRAME_SIZE];
static lv_disp_drv_t s_disp_drv;
static TaskHandle_t s_lv_tick_handle;
static TaskHandle_t s_lv_task_handle;
static SemaphoreHandle_t s_lv_semphr_handle;
static void impl_lvgl_epd_set_px_cb(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
lv_color_t color, lv_opa_t opa) {
uint32_t byte_index = (buf_w * y + x) / 8;
uint8_t bit_index = 7 - (x % 8);
uint8_t brightness = lv_color_brightness(color);
#if EPD_DISPLAY_GS
if (brightness < 64) {
buf[byte_index] &= ~(1 << bit_index);
buf[15000 + byte_index] &= ~(1 << bit_index);
} else if (brightness < 128) {
buf[byte_index] &= ~(1 << bit_index);
buf[15000 + byte_index] |= (1 << bit_index);
} else if (brightness < 192) {
buf[byte_index] |= (1 << bit_index);
buf[15000 + byte_index] &= ~(1 << bit_index);
} else {
buf[byte_index] |= (1 << bit_index);
buf[15000 + byte_index] |= (1 << bit_index);
}
#else
uint8_t current_frame = 0;
if ((s_epd_partial_counter % 2) == 1) {
current_frame = 1;
}
if (brightness > 128) {
buf[current_frame * 15000 + byte_index] |= (1 << bit_index);
} else {
buf[current_frame * 15000 + byte_index] &= ~(1 << bit_index);
}
#endif
}
static void impl_lvgl_epd_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
epd_coord_t coord = {
.x_start = 0,
.x_end = 399,
.y_start = 0,
.y_end = 299,
};
#if EPD_DISPLAY_GS
epd_gdew042t2_upload(&s_gd_epd, &coord, (uint8_t *)color_p, (uint8_t *)&color_p[15000]);
#else
if (s_epd_partial_counter == 0) {
s_epd_partial_counter = EPD_DISPLAY_MAX_PARTIAL - 1;
s_gd_epd.mode = EPD_GDEW042T2_MODE_BW;
// Flush to full
epd_gdew042t2_upload(&s_gd_epd, &coord, (uint8_t *)color_p, (uint8_t *)color_p);
} else {
s_gd_epd.mode = EPD_GDEW042T2_MODE_BW_PART;
// Even: buffer 0, odd: buffer 1
if ((s_epd_partial_counter % 2) == 1) {
epd_gdew042t2_upload(&s_gd_epd, &coord, (uint8_t *)color_p, (uint8_t *)&color_p[15000]);
} else {
epd_gdew042t2_upload(&s_gd_epd, &coord, (uint8_t *)&color_p[15000], (uint8_t *)color_p);
}
s_epd_partial_counter--;
}
#endif
lv_disp_flush_ready(disp_drv);
}
static void impl_lvgl_epd_rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) {
area->x1 = 0;
area->x2 = 399;
}
static void impl_lvgl_log_cb(const char *buf) { ESP_LOGI("LVGL", "%s", buf); }
void impl_lvgl_timer_task(void *pvParameters) {
for (;;) {
impl_lvgl_lock();
lv_timer_handler();
impl_lvgl_unlock();
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void impl_lvgl_tick_task(void *pvParameters) {
for (;;) {
impl_lvgl_lock();
lv_tick_inc(50);
impl_lvgl_unlock();
vTaskDelay(pdMS_TO_TICKS(50));
}
}
esp_err_t impl_lvgl_lock(void) {
if (xSemaphoreTake(s_lv_semphr_handle, portMAX_DELAY) != pdTRUE) {
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t impl_lvgl_unlock(void) {
xSemaphoreGive(s_lv_semphr_handle);
return ESP_OK;
}
esp_err_t impl_lvgl_init(void) {
ESP_ERROR_CHECK(impl_epd_init(&s_epd_impl));
lv_init();
lv_disp_draw_buf_init(&s_disp_buf, s_disp_store, NULL, EPD_DISPLAY_PIXEL_COUNT);
lv_disp_drv_init(&s_disp_drv);
s_disp_drv.set_px_cb = impl_lvgl_epd_set_px_cb;
s_disp_drv.flush_cb = impl_lvgl_epd_flush_cb;
s_disp_drv.rounder_cb = impl_lvgl_epd_rounder_cb;
s_disp_drv.hor_res = 400;
s_disp_drv.ver_res = 300;
s_disp_drv.draw_buf = &s_disp_buf;
s_disp_drv.full_refresh = 1;
printf("Buffer start: %p\r\n", s_disp_store);
lv_disp_t *disp = lv_disp_drv_register(&s_disp_drv);
if (disp == NULL) {
return ESP_FAIL;
}
s_lv_semphr_handle = xSemaphoreCreateBinary();
if (s_lv_semphr_handle == NULL) {
return ESP_FAIL;
}
xSemaphoreGive(s_lv_semphr_handle);
xTaskCreate(impl_lvgl_tick_task, "LV_TICK", 1024, NULL, 5, &s_lv_tick_handle);
xTaskCreate(impl_lvgl_timer_task, "LV_TMR", 4096, NULL, 4, &s_lv_task_handle);
// TODO: Launch LVGL tasks below.
return ESP_OK;
}

12
main/impl/impl_lvgl.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef IMPL_LVGL_H
#define IMPL_LVGL_H
#include "esp_system.h"
#include "lvgl.h"
esp_err_t impl_lvgl_lock(void);
esp_err_t impl_lvgl_unlock(void);
esp_err_t impl_lvgl_init(void);
#endif

1
main/lib/epd-spi Submodule

@ -0,0 +1 @@
Subproject commit e08d953fcb0f8483f72c7b232494d421c22f2447

55
main/main.c Normal file
View File

@ -0,0 +1,55 @@
#include <stdio.h>
#include "driver/gpio.h"
#include "esp_spi_flash.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "sdkconfig.h"
//
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
//
#include "impl_lvgl.h"
static esp_err_t app_init_nvs(void) {
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
return ret;
}
static esp_err_t app_init_gpio(void) { return gpio_install_isr_service(0); }
esp_err_t impl_lvgl_init(void);
void app_main(void) {
ESP_ERROR_CHECK(app_init_nvs());
ESP_ERROR_CHECK(app_init_gpio());
ESP_ERROR_CHECK(impl_lvgl_init());
/*Create a style for the shadow*/
impl_lvgl_lock();
lv_obj_t *test_label = lv_label_create(lv_scr_act());
lv_label_set_recolor(test_label, true);
lv_label_set_text(test_label, "#000000 Test Label with# #808080 ReColor #");
lv_obj_set_width(test_label, 400);
lv_obj_set_style_text_align(test_label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_align(test_label, LV_ALIGN_CENTER, 0, -25);
impl_lvgl_unlock();
char text_buf[32];
/* Dead loop */
for (;;) {
vTaskDelay(pdMS_TO_TICKS(2000));
snprintf(text_buf, 32, "C=%d", xTaskGetTickCount());
impl_lvgl_lock();
lv_label_set_text(test_label, text_buf);
lv_obj_set_style_text_align(test_label, LV_TEXT_ALIGN_CENTER, 0);
impl_lvgl_unlock();
}
}