Initial commit.

This commit is contained in:
imi415 2022-07-23 23:04:21 +08:00
commit e680528d73
Signed by: imi415
GPG Key ID: 17F01E106F9F5E0A
11 changed files with 594 additions and 0 deletions

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(hello_world)

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 := hello_world
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.

20
example_test.py Normal file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env python
from __future__ import division, print_function, unicode_literals
import ttfw_idf
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC', target=['esp32', 'esp32s2', 'esp32c3'], ci_target=['esp32'])
def test_examples_hello_world(env, extra_data):
app_name = 'hello_world'
dut = env.get_dut(app_name, 'examples/get-started/hello_world')
dut.start_app()
res = dut.expect(ttfw_idf.MINIMUM_FREE_HEAP_SIZE_RE)
if not res:
raise ValueError('Maximum heap size info not found')
ttfw_idf.print_heap_size(app_name, dut.app.config_name, dut.TARGET, res[0])
if __name__ == '__main__':
test_examples_hello_world()

9
main/CMakeLists.txt Normal file
View File

@ -0,0 +1,9 @@
idf_component_register(
SRCS
"app_main.c"
"app_lcd_impl.c"
"lib/st7789_lcd.c"
INCLUDE_DIRS
"lib"
)

172
main/app_lcd_impl.c Normal file
View File

@ -0,0 +1,172 @@
/* IDF */
#include "esp_log.h"
#include "esp_system.h"
/* FreeRTOS */
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
/* IDF drivers */
#include "driver/gpio.h"
#include "driver/spi_master.h"
/* Private */
#include "st7789_lcd.h"
#define LCD_GPIO_RST 4
#define LCD_GPIO_DC 2
#define LCD_GPIO_MOSI 23
#define LCD_GPIO_SCK 18
#define LCD_GPIO_BL 5
#define LCD_SPI_HOST SPI3_HOST
#define LCD_SPI_MAX_SIZE 4096
#define LCD_SPI_MHZ 10
#define APP_LOG_TAG "LCD_IMPL"
static void lcd_impl_spi_pre_cb(spi_transaction_t *txn) {
int dc = (int)txn->user;
gpio_set_level(LCD_GPIO_DC, dc);
}
spi_device_handle_t lcd_impl_init(void) {
gpio_set_direction(LCD_GPIO_BL, GPIO_MODE_OUTPUT);
gpio_set_direction(LCD_GPIO_DC, GPIO_MODE_OUTPUT);
gpio_set_direction(LCD_GPIO_RST, GPIO_MODE_OUTPUT);
gpio_set_level(LCD_GPIO_BL, 0);
gpio_set_level(LCD_GPIO_RST, 1);
gpio_set_level(LCD_GPIO_DC, 0);
gpio_set_pull_mode(LCD_GPIO_MOSI, GPIO_PULLUP_ONLY);
gpio_set_pull_mode(LCD_GPIO_SCK, GPIO_PULLUP_ONLY);
esp_err_t err;
spi_device_handle_t spi_handle;
spi_bus_config_t bus_cfg = {
.mosi_io_num = LCD_GPIO_MOSI,
.sclk_io_num = LCD_GPIO_SCK,
.miso_io_num = -1,
.quadhd_io_num = -1,
.quadwp_io_num = -1,
.max_transfer_sz = LCD_SPI_MAX_SIZE,
};
spi_device_interface_config_t if_cfg = {
.clock_speed_hz = LCD_SPI_MHZ * 1000 * 1000,
.mode = 3,
.spics_io_num = -1,
.queue_size = 1,
.pre_cb = lcd_impl_spi_pre_cb,
};
err = spi_bus_initialize(LCD_SPI_HOST, &bus_cfg, SPI_DMA_CH_AUTO);
if (err != ESP_OK) {
ESP_LOGE(APP_LOG_TAG, "SPI bus initialization failed");
return NULL;
}
err = spi_bus_add_device(LCD_SPI_HOST, &if_cfg, &spi_handle);
if (err != ESP_OK) {
ESP_LOGE(APP_LOG_TAG, "SPI add device failed");
return NULL;
}
return spi_handle;
}
st7789_lcd_ret_t lcd_impl_reset(void *pdev) {
/* Hardware reset is necessary if CS not used !! */
ESP_LOGI(APP_LOG_TAG, "LCD reset!");
gpio_set_level(LCD_GPIO_RST, 0U);
vTaskDelay(pdMS_TO_TICKS(10));
gpio_set_level(LCD_GPIO_RST, 1U);
vTaskDelay(pdMS_TO_TICKS(100));
return ST7789_SUCCESS;
}
st7789_lcd_ret_t lcd_impl_delay(void *pdev, uint32_t msec) {
vTaskDelay(pdMS_TO_TICKS(msec));
return ST7789_SUCCESS;
}
st7789_lcd_ret_t lcd_impl_write_command(void *pdev, uint8_t *command, uint32_t len) {
ESP_LOGD(APP_LOG_TAG, "Write CMD: %d bytes, 0x%02x...", len, command[0]);
esp_err_t ret;
spi_device_handle_t handle = pdev;
spi_transaction_t txn = {
.length = 0x08,
.tx_buffer = command,
.user = (void *)0U,
};
ret = spi_device_polling_transmit(handle, &txn);
if (ret != ESP_OK) {
ESP_LOGE(APP_LOG_TAG, "CMD transmit failed phase 1");
return ST7789_FAIL;
}
/* This command has parameters */
if (len > 1) {
txn.length = (len - 1) * 8;
txn.tx_buffer = &command[1];
txn.user = (void *)1U;
gpio_set_level(LCD_GPIO_DC, 1U);
ret = spi_device_polling_transmit(handle, &txn);
if (ret != ESP_OK) {
ESP_LOGE(APP_LOG_TAG, "CMD transmit failed phase 2");
return ST7789_FAIL;
}
}
return ST7789_SUCCESS;
}
st7789_lcd_ret_t lcd_impl_write_data(void *pdev, uint8_t *data, uint32_t len) {
ESP_LOGD(APP_LOG_TAG, "Write DATA: %d bytes", len);
esp_err_t ret;
spi_device_handle_t handle = pdev;
spi_transaction_t txn;
uint32_t d_ptr = 0U;
txn.user = (void *)1U;
while (len > LCD_SPI_MAX_SIZE) {
txn.tx_buffer = &data[d_ptr];
txn.length = LCD_SPI_MAX_SIZE * 8;
ret = spi_device_polling_transmit(handle, &txn);
if (ret != ESP_OK) {
ESP_LOGE(APP_LOG_TAG, "DATA transmit failed phase 1");
return ST7789_FAIL;
}
len -= LCD_SPI_MAX_SIZE;
d_ptr += LCD_SPI_MAX_SIZE;
}
if (len > 0) {
txn.tx_buffer = &data[d_ptr];
txn.length = len * 8;
ret = spi_device_polling_transmit(handle, &txn);
if (ret != ESP_OK) {
ESP_LOGE(APP_LOG_TAG, "DATA transmit failed phase 2");
return ST7789_FAIL;
}
}
return ST7789_SUCCESS;
}

70
main/app_main.c Normal file
View File

@ -0,0 +1,70 @@
/* Hello World Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
/* IDF headers */
#include "esp_log.h"
#include "esp_spi_flash.h"
#include "esp_system.h"
/* Drivers */
#include "driver/spi_master.h"
/* FreeRTOS */
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
/* Config */
#include "sdkconfig.h"
/* LCD */
#include "st7789_lcd.h"
#define APP_LOG_TAG "MAIN"
spi_device_handle_t lcd_impl_init(void);
st7789_lcd_ret_t lcd_impl_reset(void *pdev);
st7789_lcd_ret_t lcd_impl_delay(void *pdev, uint32_t msec);
st7789_lcd_ret_t lcd_impl_write_command(void *pdev, uint8_t *command, uint32_t len);
st7789_lcd_ret_t lcd_impl_write_data(void *pdev, uint8_t *data, uint32_t len);
void app_main(void) {
ESP_LOGI(APP_LOG_TAG, "MAIN");
st7789_lcd_t lcd = {
.cb =
{
.write_command_cb = lcd_impl_write_command,
.write_data_cb = lcd_impl_write_data,
.reset_cb = lcd_impl_reset,
.delay_cb = lcd_impl_delay,
},
.config =
{
.dir = ST7789_LCD_DIR_0,
.inverted = false,
.bgr_mode = false,
.pix_fmt = ST7789_LCD_PIXFMT_RGB565,
},
.user_data = lcd_impl_init(),
};
if (lcd.user_data == NULL) {
ESP_LOGE(APP_LOG_TAG, "IMPL initialization failed");
vTaskSuspend(NULL);
}
if (st7789_lcd_init(&lcd) != ST7789_SUCCESS) {
ESP_LOGE(APP_LOG_TAG, "LCD init failed");
}
for (;;) {
vTaskSuspend(NULL);
}
}

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.)

195
main/lib/st7789_lcd.c Normal file
View File

@ -0,0 +1,195 @@
#include "st7789_lcd.h"
#include <stddef.h>
#define LCD_ASSERT(x) \
if (!(x)) \
for (;;) { /* ABORT. */ \
}
#define LCD_ERROR_CHECK(x) \
if (x != ST7789_SUCCESS) return ST7789_FAIL
static uint8_t st7789_lcd_init_sequence[] = {
0x05, 0xB2, 0x0C, 0x0C, 0x00, 0x33, 0x33, // Porch Setting
0x01, 0xB7, 0x35, // Gate Control
0x01, 0xBB, 0x19, // VCOM Setting // Factory 0x19 -> 0.725V
0x01, 0xC0, 0x2C, // LCM Control
0x01, 0xC2, 0x01, // VDV and VRH Command Enable
0x01, 0xC3, 0x12, // VRH Set // Factory 12H
0x01, 0xC4, 0x20, // VDV Set
0x01, 0xC6, 0x0F, // Frame Rate Control in Normal Mode
0x02, 0xD0, 0xA4, 0xA1, // Power Control 1
0x0E, 0xE0, 0xD0, 0x04, 0x0D, 0x11, 0x13, 0x2B, 0x3F, 0x54, 0x4C, 0x18, 0x0D, 0x0B, 0x1F,
0x23, // Positive Voltage Gamma Control
0x0E, 0xE1, 0xD0, 0x04, 0x0C, 0x11, 0x13, 0x2C, 0x3F, 0x44, 0x51, 0x2F, 0x1F, 0x1F, 0x20,
0x23, // Negative Voltage Gamma Control
0x00, 0x21, // Inversion On
};
static st7789_lcd_ret_t st7789_lcd_execute_sequence(st7789_lcd_cb_t *cb, void *user_data, uint8_t *seq,
uint32_t seq_len) {
LCD_ASSERT(cb->write_command_cb != NULL);
uint32_t i = 0;
while (i < seq_len) {
LCD_ERROR_CHECK(cb->write_command_cb(user_data, &seq[i + 1], seq[i] + 1));
i += seq[i] + 2;
}
return ST7789_SUCCESS;
}
static st7789_lcd_ret_t st7789_lcd_reset(st7789_lcd_t *lcd) {
return lcd->cb.reset_cb(lcd->user_data);
}
static st7789_lcd_ret_t st7789_lcd_sleep(st7789_lcd_t *lcd, bool sleep) {
uint8_t tx_buf[1] = {0x11U};
if (sleep) {
tx_buf[0] = 0x10U;
}
return lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x01);
}
static st7789_lcd_ret_t st7789_lcd_config(st7789_lcd_t *lcd) {
/* PIXFMT */
uint8_t tx_buf[2] = {0x3AU, lcd->config.pix_fmt};
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x02));
/* INVERSION */
if (lcd->config.inverted) {
tx_buf[0] = 0x20U;
} else {
tx_buf[0] = 0x21U;
}
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x01));
tx_buf[0] = 0x36;
switch (lcd->config.dir) {
case ST7789_LCD_DIR_270:
tx_buf[1] = 0xA8;
break;
case ST7789_LCD_DIR_180:
tx_buf[1] = 0xC8;
break;
case ST7789_LCD_DIR_90:
tx_buf[1] = 0x68;
break;
case ST7789_LCD_DIR_0:
default:
tx_buf[1] = 0x08;
break;
}
if (lcd->config.bgr_mode) {
tx_buf[1] &= ~(1 << 3U); /* RGB <-> BGR */
}
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x02));
return ST7789_SUCCESS;
}
static st7789_lcd_ret_t st7789_lcd_window(st7789_lcd_t *lcd, st7789_lcd_coord_t *coord) {
uint16_t real_x_start, real_x_end, real_y_start, real_y_end;
uint16_t x_offset, y_offset;
switch (lcd->config.dir) {
case ST7789_LCD_DIR_0:
case ST7789_LCD_DIR_90:
x_offset = 0;
y_offset = 0;
break;
case ST7789_LCD_DIR_180:
x_offset = 0;
y_offset = 80;
break;
case ST7789_LCD_DIR_270:
x_offset = 80;
y_offset = 0;
break;
default:
x_offset = 0;
y_offset = 0;
}
real_x_start = coord->x_start + x_offset;
real_x_end = coord->x_end + x_offset;
real_y_start = coord->y_start + y_offset;
real_y_end = coord->y_end + y_offset;
uint8_t tx_buf[5] = {
0x2A,
((uint8_t)(real_x_start >> 0x08U) & 0xFFU),
(real_x_start & 0xFFU),
((uint8_t)(real_x_end >> 0x08U) & 0xFFU),
(real_x_end & 0xFFU),
};
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x05));
tx_buf[0] = 0x2BU;
tx_buf[1] = ((uint8_t)(real_y_start >> 0x08U) & 0xFFU);
tx_buf[2] = (real_y_start & 0xFFU);
tx_buf[3] = ((uint8_t)(real_y_end >> 0x08U) & 0xFFU);
tx_buf[4] = (real_y_end & 0xFFU);
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x05));
return ST7789_SUCCESS;
}
st7789_lcd_ret_t st7789_lcd_init(st7789_lcd_t *lcd) {
LCD_ERROR_CHECK(st7789_lcd_reset(lcd));
LCD_ERROR_CHECK(st7789_lcd_sleep(lcd, false));
LCD_ERROR_CHECK(lcd->cb.delay_cb(lcd->user_data, 120));
LCD_ERROR_CHECK(st7789_lcd_execute_sequence(&lcd->cb, lcd->user_data, st7789_lcd_init_sequence,
sizeof(st7789_lcd_init_sequence)));
LCD_ERROR_CHECK(st7789_lcd_config(lcd));
LCD_ERROR_CHECK(st7789_lcd_power(lcd, true));
return ST7789_SUCCESS;
}
st7789_lcd_ret_t st7789_lcd_power(st7789_lcd_t *lcd, bool on) {
uint8_t tx_buf[1] = {0x28U};
if (on) {
tx_buf[0] = 0x29U;
}
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 1));
return ST7789_SUCCESS;
}
st7789_lcd_ret_t st7789_lcd_upload(st7789_lcd_t *lcd, st7789_lcd_coord_t *coord, uint8_t *data) {
LCD_ERROR_CHECK(st7789_lcd_window(lcd, coord));
uint32_t data_size = (coord->y_end - coord->y_start + 1) * (coord->x_end - coord->x_start + 1);
switch (lcd->config.pix_fmt) {
case ST7789_LCD_PIXFMT_RGB444:
data_size = data_size * 3 / 2;
break;
case ST7789_LCD_PIXFMT_RGB565:
data_size = data_size * 2;
break;
case ST7789_LCD_PIXFMT_RGB666:
case ST7789_LCD_PIXFMT_RGB888:
default:
data_size = data_size * 3;
break;
}
uint8_t tx_buf[1] = {0x2C};
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x01));
LCD_ERROR_CHECK(lcd->cb.write_data_cb(lcd->user_data, data, data_size));
return ST7789_SUCCESS;
}

58
main/lib/st7789_lcd.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef ST7789_LCD_H
#define ST7789_LCD_H
#include <stdbool.h>
#include <stdint.h>
typedef enum {
ST7789_SUCCESS,
ST7789_FAIL,
} st7789_lcd_ret_t;
typedef enum {
ST7789_LCD_DIR_0,
ST7789_LCD_DIR_90,
ST7789_LCD_DIR_180,
ST7789_LCD_DIR_270,
} st7789_lcd_dir_t;
typedef enum {
ST7789_LCD_PIXFMT_RGB444 = 3U,
ST7789_LCD_PIXFMT_RGB565 = 5U,
ST7789_LCD_PIXFMT_RGB666 = 6U,
ST7789_LCD_PIXFMT_RGB888 = 7U,
} st7789_lcd_pixfmt_t;
typedef struct {
st7789_lcd_ret_t (*write_command_cb)(void *handle, uint8_t *command, uint32_t len);
st7789_lcd_ret_t (*write_data_cb)(void *handle, uint8_t *data, uint32_t len);
st7789_lcd_ret_t (*reset_cb)(void *handle);
st7789_lcd_ret_t (*poll_busy_cb)(void *handle);
st7789_lcd_ret_t (*delay_cb)(void *handle, uint32_t msec);
} st7789_lcd_cb_t;
typedef struct {
uint32_t x_start;
uint32_t x_end;
uint32_t y_start;
uint32_t y_end;
} st7789_lcd_coord_t;
typedef struct {
st7789_lcd_dir_t dir;
st7789_lcd_pixfmt_t pix_fmt;
bool inverted;
bool bgr_mode;
} st7789_lcd_config_t;
typedef struct {
st7789_lcd_cb_t cb;
st7789_lcd_config_t config;
void *user_data;
} st7789_lcd_t;
st7789_lcd_ret_t st7789_lcd_init(st7789_lcd_t *lcd);
st7789_lcd_ret_t st7789_lcd_power(st7789_lcd_t *lcd, bool on);
st7789_lcd_ret_t st7789_lcd_upload(st7789_lcd_t *lcd, st7789_lcd_coord_t *coord, uint8_t *data);
#endif

0
sdkconfig.ci Normal file
View File