first commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
# 请使用arduino_esp32_v3.2.1版本
|
||||||
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#include "WiFi.h"
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
// Set WiFi to station mode and disconnect from an AP if it was previously connected.
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.disconnect();
|
||||||
|
delay(100);
|
||||||
|
|
||||||
|
Serial.println("Setup done");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
Serial.println("Scan start");
|
||||||
|
|
||||||
|
// WiFi.scanNetworks will return the number of networks found.
|
||||||
|
int n = WiFi.scanNetworks();
|
||||||
|
Serial.println("Scan done");
|
||||||
|
if (n == 0) {
|
||||||
|
Serial.println("no networks found");
|
||||||
|
} else {
|
||||||
|
Serial.print(n);
|
||||||
|
Serial.println(" networks found");
|
||||||
|
Serial.println("Nr | SSID | RSSI | CH | Encryption");
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
// Print SSID and RSSI for each network found
|
||||||
|
Serial.printf("%2d", i + 1);
|
||||||
|
Serial.print(" | ");
|
||||||
|
Serial.printf("%-32.32s", WiFi.SSID(i).c_str());
|
||||||
|
Serial.print(" | ");
|
||||||
|
Serial.printf("%4ld", WiFi.RSSI(i));
|
||||||
|
Serial.print(" | ");
|
||||||
|
Serial.printf("%2ld", WiFi.channel(i));
|
||||||
|
Serial.print(" | ");
|
||||||
|
switch (WiFi.encryptionType(i)) {
|
||||||
|
case WIFI_AUTH_OPEN: Serial.print("open"); break;
|
||||||
|
case WIFI_AUTH_WEP: Serial.print("WEP"); break;
|
||||||
|
case WIFI_AUTH_WPA_PSK: Serial.print("WPA"); break;
|
||||||
|
case WIFI_AUTH_WPA2_PSK: Serial.print("WPA2"); break;
|
||||||
|
case WIFI_AUTH_WPA_WPA2_PSK: Serial.print("WPA+WPA2"); break;
|
||||||
|
case WIFI_AUTH_WPA2_ENTERPRISE: Serial.print("WPA2-EAP"); break;
|
||||||
|
case WIFI_AUTH_WPA3_PSK: Serial.print("WPA3"); break;
|
||||||
|
case WIFI_AUTH_WPA2_WPA3_PSK: Serial.print("WPA2+WPA3"); break;
|
||||||
|
case WIFI_AUTH_WAPI_PSK: Serial.print("WAPI"); break;
|
||||||
|
default: Serial.print("unknown");
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println("");
|
||||||
|
|
||||||
|
// Delete the scan result to free memory for code below.
|
||||||
|
WiFi.scanDelete();
|
||||||
|
|
||||||
|
// Wait a bit before scanning again.
|
||||||
|
delay(5000);
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
# 请使用arduino_esp32_v3.2.1版本
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_adc/adc_oneshot.h"
|
||||||
|
#include "esp_adc/adc_cali.h"
|
||||||
|
#include "esp_adc/adc_cali_scheme.h"
|
||||||
|
|
||||||
|
#define EXAMPLE_ADC2_CHAN0 ADC_CHANNEL_4
|
||||||
|
#define EXAMPLE_ADC_ATTEN ADC_ATTEN_DB_12
|
||||||
|
|
||||||
|
#define V_C_MAX (2450) //电池满电时检测的值
|
||||||
|
#define V_C_MIN (2250) //电池没电时检测的值
|
||||||
|
|
||||||
|
adc_oneshot_unit_handle_t adc2_handle;
|
||||||
|
adc_cali_handle_t adc2_cali_handle = NULL;
|
||||||
|
bool do_calibration2;
|
||||||
|
|
||||||
|
static int adc_raw;
|
||||||
|
static int adc_raw_;
|
||||||
|
static int voltage_;
|
||||||
|
static int voltage;
|
||||||
|
static int voltage_per;
|
||||||
|
static int voltage_per_;
|
||||||
|
static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle);
|
||||||
|
static void example_adc_calibration_deinit(adc_cali_handle_t handle);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200); // 初始化串口通信
|
||||||
|
adc_oneshot_chan_cfg_t config = {
|
||||||
|
.atten = EXAMPLE_ADC_ATTEN,
|
||||||
|
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||||
|
};
|
||||||
|
//-------------ADC2 Init---------------//
|
||||||
|
adc_oneshot_unit_init_cfg_t init_config2 = {
|
||||||
|
.unit_id = ADC_UNIT_2,
|
||||||
|
.ulp_mode = ADC_ULP_MODE_DISABLE,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config2, &adc2_handle));
|
||||||
|
|
||||||
|
//-------------ADC2 Calibration Init---------------//
|
||||||
|
do_calibration2 = example_adc_calibration_init(ADC_UNIT_2, EXAMPLE_ADC2_CHAN0, EXAMPLE_ADC_ATTEN, &adc2_cali_handle);
|
||||||
|
|
||||||
|
//-------------ADC2 Config---------------//
|
||||||
|
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc2_handle, EXAMPLE_ADC2_CHAN0, &config));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
for(int i=0;i<500;i++)
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(adc_oneshot_read(adc2_handle, EXAMPLE_ADC2_CHAN0,&adc_raw));
|
||||||
|
adc_raw_ += adc_raw;
|
||||||
|
}
|
||||||
|
adc_raw_ = adc_raw_ / 500;
|
||||||
|
Serial.printf("ADC%d Channel[%d] Raw Data: %d \r\n", ADC_UNIT_2 + 1, EXAMPLE_ADC2_CHAN0, adc_raw_);
|
||||||
|
|
||||||
|
if (do_calibration2) {
|
||||||
|
ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc2_cali_handle, adc_raw_, &voltage));
|
||||||
|
Serial.printf("ADC%d Channel[%d] Cali Voltage: %d mV \r\n", ADC_UNIT_2 + 1, EXAMPLE_ADC2_CHAN0, voltage);
|
||||||
|
}
|
||||||
|
|
||||||
|
voltage_ = voltage - V_C_MIN;
|
||||||
|
if(voltage_ < 0)
|
||||||
|
voltage_ = 0;
|
||||||
|
voltage_per_ = voltage_per;
|
||||||
|
voltage_per = voltage_ * 10000 / (V_C_MAX - V_C_MIN) / 100 ;
|
||||||
|
|
||||||
|
voltage_per = (voltage_per_ + voltage_per) / 2;
|
||||||
|
if(voltage_per > 100)
|
||||||
|
voltage_per = 100;
|
||||||
|
|
||||||
|
Serial.printf("Battery charge: %d %% \r\n",voltage_per);
|
||||||
|
|
||||||
|
delay(1000); // 延迟1秒
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------
|
||||||
|
ADC Calibration
|
||||||
|
---------------------------------------------------------------*/
|
||||||
|
static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)
|
||||||
|
{
|
||||||
|
adc_cali_handle_t handle = NULL;
|
||||||
|
esp_err_t ret = ESP_FAIL;
|
||||||
|
bool calibrated = false;
|
||||||
|
|
||||||
|
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||||
|
if (!calibrated) {
|
||||||
|
ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
|
||||||
|
adc_cali_curve_fitting_config_t cali_config = {
|
||||||
|
.unit_id = unit,
|
||||||
|
.chan = channel,
|
||||||
|
.atten = atten,
|
||||||
|
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||||
|
};
|
||||||
|
ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
calibrated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||||
|
if (!calibrated) {
|
||||||
|
ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting");
|
||||||
|
adc_cali_line_fitting_config_t cali_config = {
|
||||||
|
.unit_id = unit,
|
||||||
|
.atten = atten,
|
||||||
|
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||||
|
};
|
||||||
|
ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
calibrated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*out_handle = handle;
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "Calibration Success");
|
||||||
|
} else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
|
||||||
|
ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Invalid arg or no memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
return calibrated;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void example_adc_calibration_deinit(adc_cali_handle_t handle)
|
||||||
|
{
|
||||||
|
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||||
|
ESP_LOGI(TAG, "deregister %s calibration scheme", "Curve Fitting");
|
||||||
|
ESP_ERROR_CHECK(adc_cali_delete_scheme_curve_fitting(handle));
|
||||||
|
|
||||||
|
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||||
|
ESP_LOGI(TAG, "deregister %s calibration scheme", "Line Fitting");
|
||||||
|
ESP_ERROR_CHECK(adc_cali_delete_scheme_line_fitting(handle));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
# 请使用arduino_esp32_v3.2版本
|
||||||
|
#此样例使用的屏幕是微雪的10.1寸屏幕,具体型号位:10.1-DSI-TOUCH-A
|
||||||
|
|
||||||
|
# lvgl V8.4.0
|
||||||
|
|
||||||
|
# lv_conf.h文件必要设置(lvgl_v8)
|
||||||
|
# 要将lvgl文件夹中的demos文件夹移动到同目录下的src文件夹中
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define LV_COLOR_DEPTH 16
|
||||||
|
|
||||||
|
#define LV_COLOR_16_SWAP 0
|
||||||
|
|
||||||
|
#define LV_MEM_CUSTOM 1
|
||||||
|
|
||||||
|
#define LV_TICK_CUSTOM 1
|
||||||
|
|
||||||
|
#define LV_MEMCPY_MEMSET_STD 1
|
||||||
|
|
||||||
|
#define LV_ATTRIBUTE_FAST_MEM IRAM_ATTR
|
||||||
|
```
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
#pragma GCC push_options
|
||||||
|
#pragma GCC optimize("O3")
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "lvgl.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
#include "demos/lv_demos.h"
|
||||||
|
#include "pins_config.h"
|
||||||
|
#include "src/lcd/jd9365_lcd.h"
|
||||||
|
#include "src/touch/gt911_touch.h"
|
||||||
|
|
||||||
|
bsp_lcd_handles_t lcd_panels;
|
||||||
|
|
||||||
|
jd9365_lcd lcd = jd9365_lcd(LCD_RST);
|
||||||
|
gt911_touch touch = gt911_touch(TP_I2C_SDA, TP_I2C_SCL, TP_RST, TP_INT);
|
||||||
|
|
||||||
|
static lv_disp_draw_buf_t draw_buf;
|
||||||
|
static lv_color_t *buf;
|
||||||
|
static lv_color_t *buf1;
|
||||||
|
|
||||||
|
|
||||||
|
static bool lvgl_port_flush_dpi_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
|
||||||
|
{
|
||||||
|
lv_disp_drv_t *disp_drv = (lv_disp_drv_t *)user_ctx;
|
||||||
|
assert(disp_drv != NULL);
|
||||||
|
// lv_disp_flush_ready(disp_drv);
|
||||||
|
lv_disp_flush_ready(disp_drv);
|
||||||
|
|
||||||
|
// if (disp_ctx->trans_size && disp_ctx->trans_sem) {
|
||||||
|
// xSemaphoreGiveFromISR(disp_ctx->trans_sem, &taskAwake);
|
||||||
|
// }
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示刷新
|
||||||
|
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
|
||||||
|
{
|
||||||
|
const int offsetx1 = area->x1;
|
||||||
|
const int offsetx2 = area->x2;
|
||||||
|
const int offsety1 = area->y1;
|
||||||
|
const int offsety2 = area->y2;
|
||||||
|
lcd.lcd_draw_bitmap(offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, &color_p->full);
|
||||||
|
// lv_disp_flush_ready(disp); // 告诉lvgl刷新完成
|
||||||
|
}
|
||||||
|
|
||||||
|
void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
|
||||||
|
{
|
||||||
|
bool touched;
|
||||||
|
uint16_t touchX, touchY;
|
||||||
|
|
||||||
|
touched = touch.getTouch(&touchX, &touchY);
|
||||||
|
// touchX = 800 - touchX;
|
||||||
|
|
||||||
|
if (!touched)
|
||||||
|
{
|
||||||
|
data->state = LV_INDEV_STATE_REL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data->state = LV_INDEV_STATE_PR;
|
||||||
|
|
||||||
|
// 设置坐标
|
||||||
|
data->point.x = touchX;
|
||||||
|
data->point.y = touchY;
|
||||||
|
Serial.printf("x=%d,y=%d \r\n",touchX,touchY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lvgl_port_update_callback(lv_disp_drv_t *drv)
|
||||||
|
{
|
||||||
|
switch (drv->rotated) {
|
||||||
|
case LV_DISP_ROT_NONE:
|
||||||
|
touch.set_rotation(0);
|
||||||
|
break;
|
||||||
|
case LV_DISP_ROT_90:
|
||||||
|
touch.set_rotation(1);
|
||||||
|
break;
|
||||||
|
case LV_DISP_ROT_180:
|
||||||
|
touch.set_rotation(2);
|
||||||
|
break;
|
||||||
|
case LV_DISP_ROT_270:
|
||||||
|
touch.set_rotation(3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("ESP32P4 MIPI DSI LVGL");
|
||||||
|
|
||||||
|
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||||
|
|
||||||
|
i2c_master_bus_config_t i2c_bus_conf = {
|
||||||
|
.i2c_port = I2C_NUM_1,
|
||||||
|
.sda_io_num = (gpio_num_t)TP_I2C_SDA,
|
||||||
|
.scl_io_num = (gpio_num_t)TP_I2C_SCL,
|
||||||
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||||
|
.glitch_ignore_cnt = 7,
|
||||||
|
.intr_priority = 0,
|
||||||
|
.trans_queue_depth = 0,
|
||||||
|
.flags = {
|
||||||
|
.enable_internal_pullup = 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
i2c_new_master_bus(&i2c_bus_conf, &i2c_handle);
|
||||||
|
|
||||||
|
lcd.begin();
|
||||||
|
touch.begin();
|
||||||
|
|
||||||
|
lcd.get_handle(&lcd_panels);
|
||||||
|
|
||||||
|
|
||||||
|
lv_init();
|
||||||
|
size_t buffer_size = sizeof(int16_t) * LCD_H_RES * LCD_V_RES;
|
||||||
|
// buf = (int32_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM);
|
||||||
|
// buf1 = (int32_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM);
|
||||||
|
buf = (lv_color_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM);
|
||||||
|
buf1 = (lv_color_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM);
|
||||||
|
assert(buf);
|
||||||
|
assert(buf1);
|
||||||
|
|
||||||
|
lv_disp_draw_buf_init(&draw_buf, buf, buf1, LCD_H_RES * LCD_V_RES);
|
||||||
|
|
||||||
|
static lv_disp_drv_t disp_drv;
|
||||||
|
/*Initialize the display*/
|
||||||
|
lv_disp_drv_init(&disp_drv);
|
||||||
|
disp_drv.hor_res = LCD_H_RES;
|
||||||
|
disp_drv.ver_res = LCD_V_RES;
|
||||||
|
disp_drv.flush_cb = my_disp_flush;
|
||||||
|
disp_drv.draw_buf = &draw_buf;
|
||||||
|
disp_drv.full_refresh = false;
|
||||||
|
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;
|
||||||
|
indev_drv.read_cb = my_touchpad_read;
|
||||||
|
lv_indev_drv_register(&indev_drv);
|
||||||
|
|
||||||
|
esp_lcd_dpi_panel_event_callbacks_t cbs = {0};
|
||||||
|
cbs.on_color_trans_done = lvgl_port_flush_dpi_panel_ready_callback;
|
||||||
|
/* Register done callback */
|
||||||
|
esp_lcd_dpi_panel_register_event_callbacks(lcd_panels.panel, &cbs, &disp_drv);
|
||||||
|
|
||||||
|
// lv_disp_set_rotation(NULL, 0);
|
||||||
|
Serial.println("start demo");
|
||||||
|
|
||||||
|
lv_demo_widgets(); /* 小部件示例 */
|
||||||
|
// lv_demo_music(); /* 类似智能手机的现代音乐播放器演示 */
|
||||||
|
// lv_demo_stress(); /* LVGL 压力测试 */
|
||||||
|
// lv_demo_benchmark(); /* 用于测量 LVGL 性能或比较不同设置的演示 */
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
lv_timer_handler();
|
||||||
|
delay(5);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define LCD_H_RES 800
|
||||||
|
#define LCD_V_RES 1280
|
||||||
|
|
||||||
|
#define LCD_RST -1
|
||||||
|
#define LCD_LED -1
|
||||||
|
|
||||||
|
#define TP_I2C_SDA 7
|
||||||
|
#define TP_I2C_SCL 8
|
||||||
|
#define TP_RST -1
|
||||||
|
#define TP_INT -1
|
||||||
@@ -0,0 +1,676 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
|
#if SOC_MIPI_DSI_SUPPORTED
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_lcd_panel_commands.h"
|
||||||
|
#include "esp_lcd_panel_interface.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
#include "esp_lcd_panel_vendor.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_lcd_jd9365_10_1.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define JD9365_CMD_PAGE (0xE0)
|
||||||
|
#define JD9365_PAGE_USER (0x00)
|
||||||
|
|
||||||
|
#define JD9365_CMD_DSI_INT0 (0x80)
|
||||||
|
#define JD9365_DSI_1_LANE (0x00)
|
||||||
|
#define JD9365_DSI_2_LANE (0x01)
|
||||||
|
#define JD9365_DSI_3_LANE (0x10)
|
||||||
|
#define JD9365_DSI_4_LANE (0x11)
|
||||||
|
|
||||||
|
#define JD9365_CMD_GS_BIT (1 << 0)
|
||||||
|
#define JD9365_CMD_SS_BIT (1 << 1)
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
esp_lcd_panel_io_handle_t io;
|
||||||
|
int reset_gpio_num;
|
||||||
|
uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
|
||||||
|
uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
|
||||||
|
const jd9365_lcd_init_cmd_t *init_cmds;
|
||||||
|
uint16_t init_cmds_size;
|
||||||
|
uint8_t lane_num;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned int reset_level : 1;
|
||||||
|
} flags;
|
||||||
|
// To save the original functions of MIPI DPI panel
|
||||||
|
esp_err_t (*del)(esp_lcd_panel_t *panel);
|
||||||
|
esp_err_t (*init)(esp_lcd_panel_t *panel);
|
||||||
|
} jd9365_panel_t;
|
||||||
|
|
||||||
|
static const char *TAG = "jd9365";
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel);
|
||||||
|
static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel);
|
||||||
|
static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel);
|
||||||
|
static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
|
||||||
|
static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
|
||||||
|
static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
|
||||||
|
static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
|
||||||
|
static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off);
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_new_panel_jd9365(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||||
|
esp_lcd_panel_handle_t *ret_panel)
|
||||||
|
{
|
||||||
|
//ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_JD9365_10_1_VER_MAJOR, ESP_LCD_JD9365_10_1_VER_MINOR,
|
||||||
|
// ESP_LCD_JD9365_10_1_VER_PATCH);
|
||||||
|
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
|
||||||
|
jd9365_vendor_config_t *vendor_config = (jd9365_vendor_config_t *)panel_dev_config->vendor_config;
|
||||||
|
ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG,
|
||||||
|
"invalid vendor config");
|
||||||
|
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)calloc(1, sizeof(jd9365_panel_t));
|
||||||
|
ESP_RETURN_ON_FALSE(jd9365, ESP_ERR_NO_MEM, TAG, "no mem for jd9365 panel");
|
||||||
|
|
||||||
|
if (panel_dev_config->reset_gpio_num >= 0)
|
||||||
|
{
|
||||||
|
gpio_config_t io_conf = {
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (panel_dev_config->color_space)
|
||||||
|
{
|
||||||
|
case LCD_RGB_ELEMENT_ORDER_RGB:
|
||||||
|
jd9365->madctl_val = 0;
|
||||||
|
break;
|
||||||
|
case LCD_RGB_ELEMENT_ORDER_BGR:
|
||||||
|
jd9365->madctl_val |= LCD_CMD_BGR_BIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (panel_dev_config->bits_per_pixel)
|
||||||
|
{
|
||||||
|
case 16: // RGB565
|
||||||
|
jd9365->colmod_val = 0x55;
|
||||||
|
break;
|
||||||
|
case 18: // RGB666
|
||||||
|
jd9365->colmod_val = 0x66;
|
||||||
|
break;
|
||||||
|
case 24: // RGB888
|
||||||
|
jd9365->colmod_val = 0x77;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
jd9365->io = io;
|
||||||
|
jd9365->init_cmds = vendor_config->init_cmds;
|
||||||
|
jd9365->init_cmds_size = vendor_config->init_cmds_size;
|
||||||
|
jd9365->lane_num = vendor_config->mipi_config.lane_num;
|
||||||
|
jd9365->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
||||||
|
jd9365->flags.reset_level = panel_dev_config->flags.reset_active_high;
|
||||||
|
|
||||||
|
// i2c_config_t conf = {
|
||||||
|
// .mode = I2C_MODE_MASTER,
|
||||||
|
// .sda_io_num = 7,
|
||||||
|
// .sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
// .scl_io_num = 8,
|
||||||
|
// .scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
// .master.clk_speed = 100000,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// i2c_bus_handle_t i2c0_bus = i2c_bus_create(I2C_NUM_1, &conf);
|
||||||
|
// i2c_bus_device_handle_t i2c0_device1 = i2c_bus_device_create(i2c0_bus, 0x45, 0);
|
||||||
|
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||||
|
i2c_master_get_bus_handle(1,&i2c_handle);
|
||||||
|
|
||||||
|
uint8_t chip_addr = 0x45;
|
||||||
|
|
||||||
|
i2c_device_config_t i2c_dev_conf = {
|
||||||
|
.scl_speed_hz = 100 * 1000,
|
||||||
|
.device_address = chip_addr,
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c_master_dev_handle_t dev_handle = NULL;
|
||||||
|
i2c_master_bus_add_device(i2c_handle, &i2c_dev_conf, &dev_handle);
|
||||||
|
|
||||||
|
uint8_t data_to_send[2] = {0x95,0x11};
|
||||||
|
i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
|
||||||
|
|
||||||
|
// data_to_send[0] ={0x95,0x17};
|
||||||
|
data_to_send[0] = 0x95;
|
||||||
|
data_to_send[1] = 0x17;
|
||||||
|
i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
|
||||||
|
|
||||||
|
// data_to_send[2] ={0x96,0x00};
|
||||||
|
data_to_send[0] = 0x96;
|
||||||
|
data_to_send[1] = 0x00;
|
||||||
|
i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
|
||||||
|
|
||||||
|
// data_to_send[2] ={0x96,0xFF};
|
||||||
|
data_to_send[0] = 0x96;
|
||||||
|
data_to_send[1] = 0xFF;
|
||||||
|
i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
|
||||||
|
|
||||||
|
i2c_master_bus_rm_device(dev_handle);
|
||||||
|
// uint8_t data = 0x11;
|
||||||
|
// i2c_bus_write_bytes(i2c0_device1, 0x95, 1, &data);
|
||||||
|
// data = 0x17;
|
||||||
|
// i2c_bus_write_bytes(i2c0_device1, 0x95, 1, &data);
|
||||||
|
// data = 0x00;
|
||||||
|
// i2c_bus_write_bytes(i2c0_device1, 0x96, 1, &data);
|
||||||
|
// vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
// data = 0xFF;
|
||||||
|
// i2c_bus_write_bytes(i2c0_device1, 0x96, 1, &data);
|
||||||
|
|
||||||
|
// i2c_bus_device_delete(&i2c0_device1);
|
||||||
|
// i2c_bus_delete(&i2c0_bus);
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
|
||||||
|
// Create MIPI DPI panel
|
||||||
|
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG,
|
||||||
|
"create MIPI DPI panel failed");
|
||||||
|
ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle);
|
||||||
|
|
||||||
|
// Save the original functions of MIPI DPI panel
|
||||||
|
jd9365->del = panel_handle->del;
|
||||||
|
jd9365->init = panel_handle->init;
|
||||||
|
// Overwrite the functions of MIPI DPI panel
|
||||||
|
panel_handle->del = panel_jd9365_del;
|
||||||
|
panel_handle->init = panel_jd9365_init;
|
||||||
|
panel_handle->reset = panel_jd9365_reset;
|
||||||
|
panel_handle->mirror = panel_jd9365_mirror;
|
||||||
|
panel_handle->swap_xy = panel_jd9365_swap_xy;
|
||||||
|
panel_handle->set_gap = panel_jd9365_set_gap;
|
||||||
|
panel_handle->invert_color = panel_jd9365_invert_color;
|
||||||
|
panel_handle->disp_on_off = panel_jd9365_disp_on_off;
|
||||||
|
panel_handle->user_data = jd9365;
|
||||||
|
*ret_panel = panel_handle;
|
||||||
|
ESP_LOGD(TAG, "new jd9365 panel @%p", jd9365);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (jd9365)
|
||||||
|
{
|
||||||
|
if (panel_dev_config->reset_gpio_num >= 0)
|
||||||
|
{
|
||||||
|
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
||||||
|
}
|
||||||
|
free(jd9365);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const jd9365_lcd_init_cmd_t vendor_specific_init_default[] = {
|
||||||
|
// {cmd, { data }, data_size, delay_ms}
|
||||||
|
// {0xE0, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0xE0, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0xE1, (uint8_t[]){0x93}, 1, 0},
|
||||||
|
{0xE2, (uint8_t[]){0x65}, 1, 0},
|
||||||
|
{0xE3, (uint8_t[]){0xF8}, 1, 0},
|
||||||
|
{0x80, (uint8_t[]){0x01}, 1, 0},
|
||||||
|
|
||||||
|
{0xE0, (uint8_t[]){0x01}, 1, 0},
|
||||||
|
{0x00, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x01, (uint8_t[]){0x38}, 1, 0},
|
||||||
|
{0x03, (uint8_t[]){0x10}, 1, 0},
|
||||||
|
{0x04, (uint8_t[]){0x38}, 1, 0},
|
||||||
|
|
||||||
|
{0x0C, (uint8_t[]){0x74}, 1, 0},
|
||||||
|
|
||||||
|
{0x17, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x18, (uint8_t[]){0xAF}, 1, 0},
|
||||||
|
{0x19, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x1A, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x1B, (uint8_t[]){0xAF}, 1, 0},
|
||||||
|
{0x1C, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
|
||||||
|
{0x35, (uint8_t[]){0x26}, 1, 0},
|
||||||
|
|
||||||
|
{0x37, (uint8_t[]){0x09}, 1, 0},
|
||||||
|
|
||||||
|
{0x38, (uint8_t[]){0x04}, 1, 0},
|
||||||
|
{0x39, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x3A, (uint8_t[]){0x01}, 1, 0},
|
||||||
|
{0x3C, (uint8_t[]){0x78}, 1, 0},
|
||||||
|
{0x3D, (uint8_t[]){0xFF}, 1, 0},
|
||||||
|
{0x3E, (uint8_t[]){0xFF}, 1, 0},
|
||||||
|
{0x3F, (uint8_t[]){0x7F}, 1, 0},
|
||||||
|
|
||||||
|
{0x40, (uint8_t[]){0x06}, 1, 0},
|
||||||
|
{0x41, (uint8_t[]){0xA0}, 1, 0},
|
||||||
|
{0x42, (uint8_t[]){0x81}, 1, 0},
|
||||||
|
{0x43, (uint8_t[]){0x1E}, 1, 0},
|
||||||
|
{0x44, (uint8_t[]){0x0D}, 1, 0},
|
||||||
|
{0x45, (uint8_t[]){0x28}, 1, 0},
|
||||||
|
//{0x4A, (uint8_t[]){0x35}, 1, 0},//bist
|
||||||
|
|
||||||
|
{0x55, (uint8_t[]){0x02}, 1, 0},
|
||||||
|
{0x57, (uint8_t[]){0x69}, 1, 0},
|
||||||
|
{0x59, (uint8_t[]){0x0A}, 1, 0},
|
||||||
|
{0x5A, (uint8_t[]){0x2A}, 1, 0},
|
||||||
|
{0x5B, (uint8_t[]){0x17}, 1, 0},
|
||||||
|
|
||||||
|
{0x5D, (uint8_t[]){0x7F}, 1, 0},
|
||||||
|
{0x5E, (uint8_t[]){0x6A}, 1, 0},
|
||||||
|
{0x5F, (uint8_t[]){0x5B}, 1, 0},
|
||||||
|
{0x60, (uint8_t[]){0x4F}, 1, 0},
|
||||||
|
{0x61, (uint8_t[]){0x4A}, 1, 0},
|
||||||
|
{0x62, (uint8_t[]){0x3D}, 1, 0},
|
||||||
|
{0x63, (uint8_t[]){0x41}, 1, 0},
|
||||||
|
{0x64, (uint8_t[]){0x2A}, 1, 0},
|
||||||
|
{0x65, (uint8_t[]){0x44}, 1, 0},
|
||||||
|
{0x66, (uint8_t[]){0x43}, 1, 0},
|
||||||
|
{0x67, (uint8_t[]){0x44}, 1, 0},
|
||||||
|
{0x68, (uint8_t[]){0x62}, 1, 0},
|
||||||
|
{0x69, (uint8_t[]){0x52}, 1, 0},
|
||||||
|
{0x6A, (uint8_t[]){0x59}, 1, 0},
|
||||||
|
{0x6B, (uint8_t[]){0x4C}, 1, 0},
|
||||||
|
{0x6C, (uint8_t[]){0x48}, 1, 0},
|
||||||
|
{0x6D, (uint8_t[]){0x3A}, 1, 0},
|
||||||
|
{0x6E, (uint8_t[]){0x26}, 1, 0},
|
||||||
|
{0x6F, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x70, (uint8_t[]){0x7F}, 1, 0},
|
||||||
|
{0x71, (uint8_t[]){0x6A}, 1, 0},
|
||||||
|
{0x72, (uint8_t[]){0x5B}, 1, 0},
|
||||||
|
{0x73, (uint8_t[]){0x4F}, 1, 0},
|
||||||
|
{0x74, (uint8_t[]){0x4A}, 1, 0},
|
||||||
|
{0x75, (uint8_t[]){0x3D}, 1, 0},
|
||||||
|
{0x76, (uint8_t[]){0x41}, 1, 0},
|
||||||
|
{0x77, (uint8_t[]){0x2A}, 1, 0},
|
||||||
|
{0x78, (uint8_t[]){0x44}, 1, 0},
|
||||||
|
{0x79, (uint8_t[]){0x43}, 1, 0},
|
||||||
|
{0x7A, (uint8_t[]){0x44}, 1, 0},
|
||||||
|
{0x7B, (uint8_t[]){0x62}, 1, 0},
|
||||||
|
{0x7C, (uint8_t[]){0x52}, 1, 0},
|
||||||
|
{0x7D, (uint8_t[]){0x59}, 1, 0},
|
||||||
|
{0x7E, (uint8_t[]){0x4C}, 1, 0},
|
||||||
|
{0x7F, (uint8_t[]){0x48}, 1, 0},
|
||||||
|
{0x80, (uint8_t[]){0x3A}, 1, 0},
|
||||||
|
{0x81, (uint8_t[]){0x26}, 1, 0},
|
||||||
|
{0x82, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
|
||||||
|
{0xE0, (uint8_t[]){0x02}, 1, 0},
|
||||||
|
{0x00, (uint8_t[]){0x42}, 1, 0},
|
||||||
|
{0x01, (uint8_t[]){0x42}, 1, 0},
|
||||||
|
{0x02, (uint8_t[]){0x40}, 1, 0},
|
||||||
|
{0x03, (uint8_t[]){0x40}, 1, 0},
|
||||||
|
{0x04, (uint8_t[]){0x5E}, 1, 0},
|
||||||
|
{0x05, (uint8_t[]){0x5E}, 1, 0},
|
||||||
|
{0x06, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
{0x07, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
{0x08, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
{0x09, (uint8_t[]){0x57}, 1, 0},
|
||||||
|
{0x0A, (uint8_t[]){0x57}, 1, 0},
|
||||||
|
{0x0B, (uint8_t[]){0x77}, 1, 0},
|
||||||
|
{0x0C, (uint8_t[]){0x77}, 1, 0},
|
||||||
|
{0x0D, (uint8_t[]){0x47}, 1, 0},
|
||||||
|
{0x0E, (uint8_t[]){0x47}, 1, 0},
|
||||||
|
{0x0F, (uint8_t[]){0x45}, 1, 0},
|
||||||
|
{0x10, (uint8_t[]){0x45}, 1, 0},
|
||||||
|
{0x11, (uint8_t[]){0x4B}, 1, 0},
|
||||||
|
{0x12, (uint8_t[]){0x4B}, 1, 0},
|
||||||
|
{0x13, (uint8_t[]){0x49}, 1, 0},
|
||||||
|
{0x14, (uint8_t[]){0x49}, 1, 0},
|
||||||
|
{0x15, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
|
||||||
|
{0x16, (uint8_t[]){0x41}, 1, 0},
|
||||||
|
{0x17, (uint8_t[]){0x41}, 1, 0},
|
||||||
|
{0x18, (uint8_t[]){0x40}, 1, 0},
|
||||||
|
{0x19, (uint8_t[]){0x40}, 1, 0},
|
||||||
|
{0x1A, (uint8_t[]){0x5E}, 1, 0},
|
||||||
|
{0x1B, (uint8_t[]){0x5E}, 1, 0},
|
||||||
|
{0x1C, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
{0x1D, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
{0x1E, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
{0x1F, (uint8_t[]){0x57}, 1, 0},
|
||||||
|
{0x20, (uint8_t[]){0x57}, 1, 0},
|
||||||
|
{0x21, (uint8_t[]){0x77}, 1, 0},
|
||||||
|
{0x22, (uint8_t[]){0x77}, 1, 0},
|
||||||
|
{0x23, (uint8_t[]){0x46}, 1, 0},
|
||||||
|
{0x24, (uint8_t[]){0x46}, 1, 0},
|
||||||
|
{0x25, (uint8_t[]){0x44}, 1, 0},
|
||||||
|
{0x26, (uint8_t[]){0x44}, 1, 0},
|
||||||
|
{0x27, (uint8_t[]){0x4A}, 1, 0},
|
||||||
|
{0x28, (uint8_t[]){0x4A}, 1, 0},
|
||||||
|
{0x29, (uint8_t[]){0x48}, 1, 0},
|
||||||
|
{0x2A, (uint8_t[]){0x48}, 1, 0},
|
||||||
|
{0x2B, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
|
||||||
|
{0x2C, (uint8_t[]){0x01}, 1, 0},
|
||||||
|
{0x2D, (uint8_t[]){0x01}, 1, 0},
|
||||||
|
{0x2E, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x2F, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x30, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
{0x31, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
{0x32, (uint8_t[]){0x1E}, 1, 0},
|
||||||
|
{0x33, (uint8_t[]){0x1E}, 1, 0},
|
||||||
|
{0x34, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
{0x35, (uint8_t[]){0x17}, 1, 0},
|
||||||
|
{0x36, (uint8_t[]){0x17}, 1, 0},
|
||||||
|
{0x37, (uint8_t[]){0x37}, 1, 0},
|
||||||
|
{0x38, (uint8_t[]){0x37}, 1, 0},
|
||||||
|
{0x39, (uint8_t[]){0x08}, 1, 0},
|
||||||
|
{0x3A, (uint8_t[]){0x08}, 1, 0},
|
||||||
|
{0x3B, (uint8_t[]){0x0A}, 1, 0},
|
||||||
|
{0x3C, (uint8_t[]){0x0A}, 1, 0},
|
||||||
|
{0x3D, (uint8_t[]){0x04}, 1, 0},
|
||||||
|
{0x3E, (uint8_t[]){0x04}, 1, 0},
|
||||||
|
{0x3F, (uint8_t[]){0x06}, 1, 0},
|
||||||
|
{0x40, (uint8_t[]){0x06}, 1, 0},
|
||||||
|
{0x41, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
|
||||||
|
{0x42, (uint8_t[]){0x02}, 1, 0},
|
||||||
|
{0x43, (uint8_t[]){0x02}, 1, 0},
|
||||||
|
{0x44, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x45, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x46, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
{0x47, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
{0x48, (uint8_t[]){0x1E}, 1, 0},
|
||||||
|
{0x49, (uint8_t[]){0x1E}, 1, 0},
|
||||||
|
{0x4A, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
{0x4B, (uint8_t[]){0x17}, 1, 0},
|
||||||
|
{0x4C, (uint8_t[]){0x17}, 1, 0},
|
||||||
|
{0x4D, (uint8_t[]){0x37}, 1, 0},
|
||||||
|
{0x4E, (uint8_t[]){0x37}, 1, 0},
|
||||||
|
{0x4F, (uint8_t[]){0x09}, 1, 0},
|
||||||
|
{0x50, (uint8_t[]){0x09}, 1, 0},
|
||||||
|
{0x51, (uint8_t[]){0x0B}, 1, 0},
|
||||||
|
{0x52, (uint8_t[]){0x0B}, 1, 0},
|
||||||
|
{0x53, (uint8_t[]){0x05}, 1, 0},
|
||||||
|
{0x54, (uint8_t[]){0x05}, 1, 0},
|
||||||
|
{0x55, (uint8_t[]){0x07}, 1, 0},
|
||||||
|
{0x56, (uint8_t[]){0x07}, 1, 0},
|
||||||
|
{0x57, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
|
||||||
|
{0x58, (uint8_t[]){0x40}, 1, 0},
|
||||||
|
{0x5B, (uint8_t[]){0x30}, 1, 0},
|
||||||
|
{0x5C, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x5D, (uint8_t[]){0x34}, 1, 0},
|
||||||
|
{0x5E, (uint8_t[]){0x05}, 1, 0},
|
||||||
|
{0x5F, (uint8_t[]){0x02}, 1, 0},
|
||||||
|
{0x63, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x64, (uint8_t[]){0x6A}, 1, 0},
|
||||||
|
{0x67, (uint8_t[]){0x73}, 1, 0},
|
||||||
|
{0x68, (uint8_t[]){0x07}, 1, 0},
|
||||||
|
{0x69, (uint8_t[]){0x08}, 1, 0},
|
||||||
|
{0x6A, (uint8_t[]){0x6A}, 1, 0},
|
||||||
|
{0x6B, (uint8_t[]){0x08}, 1, 0},
|
||||||
|
|
||||||
|
{0x6C, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x6D, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x6E, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x6F, (uint8_t[]){0x88}, 1, 0},
|
||||||
|
|
||||||
|
{0x75, (uint8_t[]){0xFF}, 1, 0},
|
||||||
|
{0x77, (uint8_t[]){0xDD}, 1, 0},
|
||||||
|
{0x78, (uint8_t[]){0x2C}, 1, 0},
|
||||||
|
{0x79, (uint8_t[]){0x15}, 1, 0},
|
||||||
|
{0x7A, (uint8_t[]){0x17}, 1, 0},
|
||||||
|
{0x7D, (uint8_t[]){0x14}, 1, 0},
|
||||||
|
{0x7E, (uint8_t[]){0x82}, 1, 0},
|
||||||
|
|
||||||
|
{0xE0, (uint8_t[]){0x04}, 1, 0},
|
||||||
|
{0x00, (uint8_t[]){0x0E}, 1, 0},
|
||||||
|
{0x02, (uint8_t[]){0xB3}, 1, 0},
|
||||||
|
{0x09, (uint8_t[]){0x61}, 1, 0},
|
||||||
|
{0x0E, (uint8_t[]){0x48}, 1, 0},
|
||||||
|
{0x37, (uint8_t[]){0x58}, 1, 0}, // 全志
|
||||||
|
{0x2B, (uint8_t[]){0x0F}, 1, 0}, // 全志
|
||||||
|
|
||||||
|
{0xE0, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
|
||||||
|
{0xE6, (uint8_t[]){0x02}, 1, 0},
|
||||||
|
{0xE7, (uint8_t[]){0x0C}, 1, 0},
|
||||||
|
|
||||||
|
{0x11, (uint8_t[]){0x00}, 1, 120},
|
||||||
|
|
||||||
|
{0x29, (uint8_t[]){0x00}, 1, 20},
|
||||||
|
};
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel)
|
||||||
|
{
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
|
||||||
|
|
||||||
|
if (jd9365->reset_gpio_num >= 0)
|
||||||
|
{
|
||||||
|
gpio_reset_pin(jd9365->reset_gpio_num);
|
||||||
|
}
|
||||||
|
// Delete MIPI DPI panel
|
||||||
|
jd9365->del(panel);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "del jd9365 panel @%p", jd9365);
|
||||||
|
free(jd9365);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel)
|
||||||
|
{
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
|
||||||
|
esp_lcd_panel_io_handle_t io = jd9365->io;
|
||||||
|
const jd9365_lcd_init_cmd_t *init_cmds = NULL;
|
||||||
|
uint16_t init_cmds_size = 0;
|
||||||
|
uint8_t lane_command = JD9365_DSI_2_LANE;
|
||||||
|
bool is_user_set = true;
|
||||||
|
bool is_cmd_overwritten = false;
|
||||||
|
|
||||||
|
switch (jd9365->lane_num)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
lane_command = JD9365_DSI_1_LANE;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
lane_command = JD9365_DSI_2_LANE;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
lane_command = JD9365_DSI_3_LANE;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
lane_command = JD9365_DSI_4_LANE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGE(TAG, "Invalid lane number %d", jd9365->lane_num);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ID[3];
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed");
|
||||||
|
ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]);
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_PAGE, (uint8_t[]){JD9365_PAGE_USER}, 1), TAG, "send command failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){
|
||||||
|
jd9365->madctl_val,
|
||||||
|
},
|
||||||
|
1),
|
||||||
|
TAG, "send command failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]){
|
||||||
|
jd9365->colmod_val,
|
||||||
|
},
|
||||||
|
1),
|
||||||
|
TAG, "send command failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_DSI_INT0, (uint8_t[]){
|
||||||
|
lane_command,
|
||||||
|
},
|
||||||
|
1),
|
||||||
|
TAG, "send command failed");
|
||||||
|
|
||||||
|
// vendor specific initialization, it can be different between manufacturers
|
||||||
|
// should consult the LCD supplier for initialization sequence code
|
||||||
|
if (jd9365->init_cmds)
|
||||||
|
{
|
||||||
|
init_cmds = jd9365->init_cmds;
|
||||||
|
init_cmds_size = jd9365->init_cmds_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
init_cmds = vendor_specific_init_default;
|
||||||
|
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(jd9365_lcd_init_cmd_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < init_cmds_size; i++)
|
||||||
|
{
|
||||||
|
// Check if the command has been used or conflicts with the internal
|
||||||
|
if (is_user_set && (init_cmds[i].data_bytes > 0))
|
||||||
|
{
|
||||||
|
switch (init_cmds[i].cmd)
|
||||||
|
{
|
||||||
|
case LCD_CMD_MADCTL:
|
||||||
|
is_cmd_overwritten = true;
|
||||||
|
jd9365->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||||
|
break;
|
||||||
|
case LCD_CMD_COLMOD:
|
||||||
|
is_cmd_overwritten = true;
|
||||||
|
jd9365->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
is_cmd_overwritten = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_cmd_overwritten)
|
||||||
|
{
|
||||||
|
is_cmd_overwritten = false;
|
||||||
|
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
|
||||||
|
init_cmds[i].cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send command
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
|
||||||
|
|
||||||
|
// Check if the current cmd is the "page set" cmd
|
||||||
|
if ((init_cmds[i].cmd == JD9365_CMD_PAGE) && (init_cmds[i].data_bytes > 0))
|
||||||
|
{
|
||||||
|
is_user_set = (((uint8_t *)init_cmds[i].data)[0] == JD9365_PAGE_USER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "send init commands success");
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(jd9365->init(panel), TAG, "init MIPI DPI panel failed");
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel)
|
||||||
|
{
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
|
||||||
|
esp_lcd_panel_io_handle_t io = jd9365->io;
|
||||||
|
|
||||||
|
// Perform hardware reset
|
||||||
|
if (jd9365->reset_gpio_num >= 0)
|
||||||
|
{
|
||||||
|
gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(5));
|
||||||
|
gpio_set_level(jd9365->reset_gpio_num, jd9365->flags.reset_level);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(120));
|
||||||
|
}
|
||||||
|
else if (io)
|
||||||
|
{ // Perform software reset
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(120));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
|
||||||
|
{
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
|
||||||
|
esp_lcd_panel_io_handle_t io = jd9365->io;
|
||||||
|
uint8_t command = 0;
|
||||||
|
|
||||||
|
ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
|
||||||
|
|
||||||
|
if (invert_color_data)
|
||||||
|
{
|
||||||
|
command = LCD_CMD_INVON;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
command = LCD_CMD_INVOFF;
|
||||||
|
}
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||||
|
{
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
|
||||||
|
esp_lcd_panel_io_handle_t io = jd9365->io;
|
||||||
|
uint8_t madctl_val = jd9365->madctl_val;
|
||||||
|
|
||||||
|
ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
|
||||||
|
|
||||||
|
// Control mirror through LCD command
|
||||||
|
if (mirror_x)
|
||||||
|
{
|
||||||
|
madctl_val |= JD9365_CMD_GS_BIT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
madctl_val &= ~JD9365_CMD_GS_BIT;
|
||||||
|
}
|
||||||
|
if (mirror_y)
|
||||||
|
{
|
||||||
|
madctl_val |= JD9365_CMD_SS_BIT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
madctl_val &= ~JD9365_CMD_SS_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){madctl_val}, 1), TAG, "send command failed");
|
||||||
|
jd9365->madctl_val = madctl_val;
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "swap_xy is not supported by this panel");
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "set_gap is not supported by this panel");
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
||||||
|
{
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
|
||||||
|
esp_lcd_panel_io_handle_t io = jd9365->io;
|
||||||
|
int command = 0;
|
||||||
|
|
||||||
|
if (on_off)
|
||||||
|
{
|
||||||
|
command = LCD_CMD_DISPON;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
command = LCD_CMD_DISPOFF;
|
||||||
|
}
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
|
#if SOC_MIPI_DSI_SUPPORTED
|
||||||
|
#include "esp_lcd_panel_vendor.h"
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LCD panel initialization commands.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int cmd; /*<! The specific LCD command */
|
||||||
|
const void *data; /*<! Buffer that holds the command specific data */
|
||||||
|
size_t data_bytes; /*<! Size of `data` in memory, in bytes */
|
||||||
|
unsigned int delay_ms; /*<! Delay in milliseconds after this command */
|
||||||
|
} jd9365_lcd_init_cmd_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LCD panel vendor configuration.
|
||||||
|
*
|
||||||
|
* @note This structure needs to be passed to the `vendor_config` field in `esp_lcd_panel_dev_config_t`.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
const jd9365_lcd_init_cmd_t *init_cmds; /*!< Pointer to initialization commands array. Set to NULL if using default commands.
|
||||||
|
* The array should be declared as `static const` and positioned outside the function.
|
||||||
|
* Please refer to `vendor_specific_init_default` in source file.
|
||||||
|
*/
|
||||||
|
uint16_t init_cmds_size; /*<! Number of commands in above array */
|
||||||
|
struct {
|
||||||
|
esp_lcd_dsi_bus_handle_t dsi_bus; /*!< MIPI-DSI bus configuration */
|
||||||
|
const esp_lcd_dpi_panel_config_t *dpi_config; /*!< MIPI-DPI panel configuration */
|
||||||
|
uint8_t lane_num; /*!< Number of MIPI-DSI lanes */
|
||||||
|
} mipi_config;
|
||||||
|
struct {
|
||||||
|
unsigned int use_mipi_interface: 1; /*<! Set to 1 if using MIPI interface, default is RGB interface */
|
||||||
|
unsigned int mirror_by_cmd: 1; /*<! The `mirror()` function will be implemented by LCD command if set to 1. This flag is only valid for the RGB interface.
|
||||||
|
* Otherwise, the function will be implemented by software.
|
||||||
|
*/
|
||||||
|
|
||||||
|
union {
|
||||||
|
unsigned int auto_del_panel_io: 1;
|
||||||
|
unsigned int enable_io_multiplex: 1;
|
||||||
|
}; /*<! Delete the panel IO instance automatically if set to 1. All `*_by_cmd` flags will be invalid.
|
||||||
|
* If the panel IO pins are sharing other pins of the RGB interface to save GPIOs,
|
||||||
|
* Please set it to 1 to release the panel IO and its pins (except CS signal).
|
||||||
|
* This flag is only valid for the RGB interface.
|
||||||
|
*/
|
||||||
|
} flags;
|
||||||
|
} jd9365_vendor_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create LCD panel for model JD9365
|
||||||
|
*
|
||||||
|
* @note Vendor specific initialization can be different between manufacturers, should consult the LCD supplier for initialization sequence code.
|
||||||
|
*
|
||||||
|
* @param[in] io LCD panel IO handle
|
||||||
|
* @param[in] panel_dev_config General panel device configuration
|
||||||
|
* @param[out] ret_panel Returned LCD panel handle
|
||||||
|
* @return
|
||||||
|
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - Otherwise on fail
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_new_panel_jd9365(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||||
|
esp_lcd_panel_handle_t *ret_panel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MIPI-DSI bus configuration structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define JD9365_PANEL_BUS_DSI_2CH_CONFIG() \
|
||||||
|
{ \
|
||||||
|
.bus_id = 0, \
|
||||||
|
.num_data_lanes = 2, \
|
||||||
|
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, \
|
||||||
|
.lane_bit_rate_mbps = 1500, \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MIPI-DBI panel IO configuration structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define JD9365_PANEL_IO_DBI_CONFIG() \
|
||||||
|
{ \
|
||||||
|
.virtual_channel = 0, \
|
||||||
|
.lcd_cmd_bits = 8, \
|
||||||
|
.lcd_param_bits = 8, \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MIPI DPI configuration structure
|
||||||
|
*
|
||||||
|
* @note refresh_rate = (dpi_clock_freq_mhz * 1000000) / (h_res + hsync_pulse_width + hsync_back_porch + hsync_front_porch)
|
||||||
|
* / (v_res + vsync_pulse_width + vsync_back_porch + vsync_front_porch)
|
||||||
|
*
|
||||||
|
* @param[in] px_format Pixel format of the panel
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(px_format) \
|
||||||
|
{ \
|
||||||
|
.virtual_channel = 0, \
|
||||||
|
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, \
|
||||||
|
.dpi_clock_freq_mhz = 80, \
|
||||||
|
.pixel_format = px_format, \
|
||||||
|
.num_fbs = 1, \
|
||||||
|
.video_timing = { \
|
||||||
|
.h_size = 800, \
|
||||||
|
.v_size = 1280, \
|
||||||
|
.hsync_pulse_width = 20, \
|
||||||
|
.hsync_back_porch = 20, \
|
||||||
|
.hsync_front_porch = 40, \
|
||||||
|
.vsync_pulse_width = 4, \
|
||||||
|
.vsync_back_porch = 10, \
|
||||||
|
.vsync_front_porch = 30, \
|
||||||
|
}, \
|
||||||
|
.flags = { \
|
||||||
|
.use_dma2d = true, \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "esp_lcd_panel_ops.h"
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "esp_ldo_regulator.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#include "esp_lcd_jd9365_10_1.h"
|
||||||
|
#include "jd9365_lcd.h"
|
||||||
|
|
||||||
|
#define LCD_H_RES 800
|
||||||
|
#define LCD_V_RES 1280
|
||||||
|
|
||||||
|
#define MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565)
|
||||||
|
#define LCD_BIT_PER_PIXEL (16)
|
||||||
|
|
||||||
|
// “VDD_MIPI_DPHY”应供电 2.5V,可从内部 LDO 稳压器或外部 LDO 芯片获取电源
|
||||||
|
#define EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN 3 // LDO_VO3 连接至 VDD_MIPI_DPHY
|
||||||
|
#define EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV 2500
|
||||||
|
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 100
|
||||||
|
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL (0)
|
||||||
|
#define EXAMPLE_PIN_NUM_BK_LIGHT GPIO_NUM_NC
|
||||||
|
|
||||||
|
static const char *TAG = "example";
|
||||||
|
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||||
|
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||||
|
|
||||||
|
jd9365_lcd::jd9365_lcd(int8_t lcd_rst)
|
||||||
|
{
|
||||||
|
_lcd_rst = lcd_rst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::example_bsp_enable_dsi_phy_power()
|
||||||
|
{
|
||||||
|
// 打开 MIPI DSI PHY 的电源,使其从“无电”状态进入“关机”状态
|
||||||
|
esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
|
||||||
|
#ifdef EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN
|
||||||
|
esp_ldo_channel_config_t ldo_mipi_phy_config = {
|
||||||
|
.chan_id = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN,
|
||||||
|
.voltage_mv = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
|
||||||
|
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::example_bsp_init_lcd_backlight()
|
||||||
|
{
|
||||||
|
// #if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
|
||||||
|
// gpio_config_t bk_gpio_config = {
|
||||||
|
// .pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT,
|
||||||
|
// .mode = GPIO_MODE_OUTPUT
|
||||||
|
// };
|
||||||
|
// ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::example_bsp_set_lcd_backlight(uint32_t level)
|
||||||
|
{
|
||||||
|
// #if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
|
||||||
|
// gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, level);
|
||||||
|
// #else
|
||||||
|
if (level > 100) {
|
||||||
|
level = 100;
|
||||||
|
}
|
||||||
|
if (level < 0) {
|
||||||
|
level = 0;
|
||||||
|
}
|
||||||
|
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||||
|
i2c_master_get_bus_handle(1,&i2c_handle);
|
||||||
|
|
||||||
|
uint8_t data = (uint8_t)(255 * level * 0.01);
|
||||||
|
uint8_t chip_addr = 0x45;
|
||||||
|
|
||||||
|
uint8_t data_addr = 0x96;
|
||||||
|
uint8_t data_to_send[2] = {data_addr, data};
|
||||||
|
|
||||||
|
|
||||||
|
i2c_device_config_t i2c_dev_conf = {
|
||||||
|
.device_address = chip_addr,
|
||||||
|
.scl_speed_hz = 100 * 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c_master_dev_handle_t dev_handle = NULL;
|
||||||
|
if (i2c_master_bus_add_device(i2c_handle, &i2c_dev_conf, &dev_handle) != ESP_OK)
|
||||||
|
{
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t ret = i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
i2c_master_bus_rm_device(dev_handle);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_master_bus_rm_device(dev_handle);
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::begin()
|
||||||
|
{
|
||||||
|
example_bsp_enable_dsi_phy_power();
|
||||||
|
example_bsp_init_lcd_backlight();
|
||||||
|
// example_bsp_set_lcd_backlight(EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);
|
||||||
|
|
||||||
|
// 首先创建 MIPI DSI 总线,它还将初始化 DSI PHY
|
||||||
|
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
|
||||||
|
esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG();
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
|
||||||
|
// 我们使用DBI接口发送LCD命令和参数
|
||||||
|
esp_lcd_dbi_io_config_t dbi_config = JD9365_PANEL_IO_DBI_CONFIG();
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io_handle));
|
||||||
|
|
||||||
|
// 创建JD9365控制面板
|
||||||
|
esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(MIPI_DPI_PX_FORMAT);
|
||||||
|
|
||||||
|
jd9365_vendor_config_t vendor_config = {
|
||||||
|
.mipi_config = {
|
||||||
|
.dsi_bus = mipi_dsi_bus,
|
||||||
|
.dpi_config = &dpi_config,
|
||||||
|
.lane_num = 2,
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.use_mipi_interface = 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const esp_lcd_panel_dev_config_t panel_config = {
|
||||||
|
.reset_gpio_num = _lcd_rst,
|
||||||
|
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
|
||||||
|
.bits_per_pixel = LCD_BIT_PER_PIXEL,
|
||||||
|
.vendor_config = &vendor_config,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_new_panel_jd9365(io_handle, &panel_config, &panel_handle));
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
|
||||||
|
|
||||||
|
// esp_lcd_dpi_panel_event_callbacks_t cbs = {0};
|
||||||
|
// if (dsi_cfg->flags.avoid_tearing) {
|
||||||
|
// cbs.on_refresh_done = lvgl_port_flush_dpi_vsync_ready_callback;
|
||||||
|
// } else {
|
||||||
|
// cbs.on_color_trans_done = lvgl_port_flush_dpi_panel_ready_callback;
|
||||||
|
// }
|
||||||
|
// /* Register done callback */
|
||||||
|
// esp_lcd_dpi_panel_register_event_callbacks(disp_ctx->panel_handle, &cbs, &disp_ctx->disp_drv);
|
||||||
|
|
||||||
|
// 打开背光
|
||||||
|
example_bsp_set_lcd_backlight(EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::lcd_draw_bitmap(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint16_t *color_data)
|
||||||
|
{
|
||||||
|
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, color_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::draw16bitbergbbitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *color_data)
|
||||||
|
{
|
||||||
|
uint16_t x_start = x;
|
||||||
|
uint16_t y_start = y;
|
||||||
|
uint16_t x_end = w + x;
|
||||||
|
uint16_t y_end = h + y;
|
||||||
|
|
||||||
|
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, color_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::fillScreen(uint16_t color)
|
||||||
|
{
|
||||||
|
uint16_t *color_data = (uint16_t *)heap_caps_malloc(480 * 272 * 2, MALLOC_CAP_INTERNAL);
|
||||||
|
memset(color_data, color, 480 * 272 * 2);
|
||||||
|
draw16bitbergbbitmap(0, 0, 480, 272, color_data);
|
||||||
|
free(color_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::te_on()
|
||||||
|
{
|
||||||
|
esp_lcd_panel_io_tx_param(io_handle, 0x35,new (uint8_t[]){0x00}, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::te_off()
|
||||||
|
{
|
||||||
|
esp_lcd_panel_io_tx_param(io_handle, 0x34,new (uint8_t[]){0x00}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t jd9365_lcd::width()
|
||||||
|
{
|
||||||
|
return LCD_H_RES;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t jd9365_lcd::height()
|
||||||
|
{
|
||||||
|
return LCD_V_RES;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::get_handle(bsp_lcd_handles_t *ret_handles)
|
||||||
|
{
|
||||||
|
ret_handles->io = io_handle;
|
||||||
|
ret_handles->mipi_dsi_bus = NULL;
|
||||||
|
ret_handles->panel = panel_handle;
|
||||||
|
ret_handles->control = NULL;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef _JD9165_LCD_H
|
||||||
|
#define _JD9165_LCD_H
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "esp_lcd_types.h"
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_lcd_dsi_bus_handle_t mipi_dsi_bus; /*!< MIPI DSI bus handle */
|
||||||
|
esp_lcd_panel_io_handle_t io; /*!< ESP LCD IO handle */
|
||||||
|
esp_lcd_panel_handle_t panel; /*!< ESP LCD panel (color) handle */
|
||||||
|
esp_lcd_panel_handle_t control; /*!< ESP LCD panel (control) handle */
|
||||||
|
} bsp_lcd_handles_t;
|
||||||
|
|
||||||
|
class jd9365_lcd
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
jd9365_lcd(int8_t lcd_rst);
|
||||||
|
|
||||||
|
void begin();
|
||||||
|
void example_bsp_enable_dsi_phy_power();
|
||||||
|
void example_bsp_init_lcd_backlight();
|
||||||
|
void example_bsp_set_lcd_backlight(uint32_t level);
|
||||||
|
void lcd_draw_bitmap(uint16_t x_start, uint16_t y_start,
|
||||||
|
uint16_t x_end, uint16_t y_end, uint16_t *color_data);
|
||||||
|
void draw16bitbergbbitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *color_data);
|
||||||
|
void fillScreen(uint16_t color);
|
||||||
|
void te_on();
|
||||||
|
void te_off();
|
||||||
|
uint16_t width();
|
||||||
|
uint16_t height();
|
||||||
|
void get_handle(bsp_lcd_handles_t *ret_handles);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int8_t _lcd_rst;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_lcd_touch.h"
|
||||||
|
|
||||||
|
static const char *TAG = "TP";
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Function definitions
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Local variables
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Public API functions
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_read_data(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(tp->read_data != NULL);
|
||||||
|
|
||||||
|
return tp->read_data(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool esp_lcd_touch_get_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
|
||||||
|
{
|
||||||
|
bool touched = false;
|
||||||
|
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(x != NULL);
|
||||||
|
assert(y != NULL);
|
||||||
|
assert(tp->get_xy != NULL);
|
||||||
|
|
||||||
|
touched = tp->get_xy(tp, x, y, strength, point_num, max_point_num);
|
||||||
|
if (!touched) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process coordinates by user */
|
||||||
|
if (tp->config.process_coordinates != NULL) {
|
||||||
|
tp->config.process_coordinates(tp, x, y, strength, point_num, max_point_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Software coordinates adjustment needed */
|
||||||
|
bool sw_adj_needed = ((tp->config.flags.mirror_x && (tp->set_mirror_x == NULL)) ||
|
||||||
|
(tp->config.flags.mirror_y && (tp->set_mirror_y == NULL)) ||
|
||||||
|
(tp->config.flags.swap_xy && (tp->set_swap_xy == NULL)));
|
||||||
|
|
||||||
|
/* Adjust all coordinates */
|
||||||
|
for (int i = 0; (sw_adj_needed && i < *point_num); i++) {
|
||||||
|
|
||||||
|
/* Mirror X coordinates (if not supported by HW) */
|
||||||
|
if (tp->config.flags.mirror_x && tp->set_mirror_x == NULL) {
|
||||||
|
x[i] = tp->config.x_max - x[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mirror Y coordinates (if not supported by HW) */
|
||||||
|
if (tp->config.flags.mirror_y && tp->set_mirror_y == NULL) {
|
||||||
|
y[i] = tp->config.y_max - y[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Swap X and Y coordinates (if not supported by HW) */
|
||||||
|
if (tp->config.flags.swap_xy && tp->set_swap_xy == NULL) {
|
||||||
|
uint16_t tmp = x[i];
|
||||||
|
x[i] = y[i];
|
||||||
|
y[i] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return touched;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||||
|
esp_err_t esp_lcd_touch_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(state != NULL);
|
||||||
|
|
||||||
|
*state = 0;
|
||||||
|
|
||||||
|
if (tp->get_button_state) {
|
||||||
|
return tp->get_button_state(tp, n, state);
|
||||||
|
} else {
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_set_swap_xy(esp_lcd_touch_handle_t tp, bool swap)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
tp->config.flags.swap_xy = swap;
|
||||||
|
|
||||||
|
/* Is swap supported by HW? */
|
||||||
|
if (tp->set_swap_xy) {
|
||||||
|
return tp->set_swap_xy(tp, swap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_get_swap_xy(esp_lcd_touch_handle_t tp, bool *swap)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(swap != NULL);
|
||||||
|
|
||||||
|
/* Is swap supported by HW? */
|
||||||
|
if (tp->get_swap_xy) {
|
||||||
|
return tp->get_swap_xy(tp, swap);
|
||||||
|
} else {
|
||||||
|
*swap = tp->config.flags.swap_xy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_set_mirror_x(esp_lcd_touch_handle_t tp, bool mirror)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
tp->config.flags.mirror_x = mirror;
|
||||||
|
|
||||||
|
/* Is mirror supported by HW? */
|
||||||
|
if (tp->set_mirror_x) {
|
||||||
|
return tp->set_mirror_x(tp, mirror);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_get_mirror_x(esp_lcd_touch_handle_t tp, bool *mirror)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(mirror != NULL);
|
||||||
|
|
||||||
|
/* Is swap supported by HW? */
|
||||||
|
if (tp->get_mirror_x) {
|
||||||
|
return tp->get_mirror_x(tp, mirror);
|
||||||
|
} else {
|
||||||
|
*mirror = tp->config.flags.mirror_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_set_mirror_y(esp_lcd_touch_handle_t tp, bool mirror)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
tp->config.flags.mirror_y = mirror;
|
||||||
|
|
||||||
|
/* Is mirror supported by HW? */
|
||||||
|
if (tp->set_mirror_y) {
|
||||||
|
return tp->set_mirror_y(tp, mirror);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_get_mirror_y(esp_lcd_touch_handle_t tp, bool *mirror)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(mirror != NULL);
|
||||||
|
|
||||||
|
/* Is swap supported by HW? */
|
||||||
|
if (tp->get_mirror_y) {
|
||||||
|
return tp->get_mirror_y(tp, mirror);
|
||||||
|
} else {
|
||||||
|
*mirror = tp->config.flags.mirror_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_del(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
if (tp->del != NULL) {
|
||||||
|
return tp->del(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_handle_t tp, esp_lcd_touch_interrupt_callback_t callback)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
/* Interrupt pin is not selected */
|
||||||
|
if (tp->config.int_gpio_num == GPIO_NUM_NC) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
tp->config.interrupt_callback = callback;
|
||||||
|
|
||||||
|
if (callback != NULL) {
|
||||||
|
ret = gpio_install_isr_service(0);
|
||||||
|
/* ISR service can be installed from user before, then it returns invalid state */
|
||||||
|
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
|
||||||
|
ESP_LOGE(TAG, "GPIO ISR install failed");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/* Add GPIO ISR handler */
|
||||||
|
ret = gpio_intr_enable(tp->config.int_gpio_num);
|
||||||
|
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR install failed");
|
||||||
|
ret = gpio_isr_handler_add(tp->config.int_gpio_num, (gpio_isr_t)tp->config.interrupt_callback, tp);
|
||||||
|
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR install failed");
|
||||||
|
} else {
|
||||||
|
/* Remove GPIO ISR handler */
|
||||||
|
ret = gpio_isr_handler_remove(tp->config.int_gpio_num);
|
||||||
|
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR remove handler failed");
|
||||||
|
ret = gpio_intr_disable(tp->config.int_gpio_num);
|
||||||
|
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR disable failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,370 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief ESP LCD touch
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS (1)
|
||||||
|
#define CONFIG_ESP_LCD_TOUCH_MAX_POINTS (5)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Touch controller type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct esp_lcd_touch_s esp_lcd_touch_t;
|
||||||
|
typedef esp_lcd_touch_t *esp_lcd_touch_handle_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Touch controller interrupt callback type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef void (*esp_lcd_touch_interrupt_callback_t)(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Touch Configuration Type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint16_t x_max; /*!< X coordinates max (for mirroring) */
|
||||||
|
uint16_t y_max; /*!< Y coordinates max (for mirroring) */
|
||||||
|
|
||||||
|
gpio_num_t rst_gpio_num; /*!< GPIO number of reset pin */
|
||||||
|
gpio_num_t int_gpio_num; /*!< GPIO number of interrupt pin */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
unsigned int reset: 1; /*!< Level of reset pin in reset */
|
||||||
|
unsigned int interrupt: 1;/*!< Active Level of interrupt pin */
|
||||||
|
} levels;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
unsigned int swap_xy: 1; /*!< Swap X and Y after read coordinates */
|
||||||
|
unsigned int mirror_x: 1; /*!< Mirror X after read coordinates */
|
||||||
|
unsigned int mirror_y: 1; /*!< Mirror Y after read coordinates */
|
||||||
|
} flags;
|
||||||
|
|
||||||
|
/*!< User callback called after get coordinates from touch controller for apply user adjusting */
|
||||||
|
void (*process_coordinates)(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||||
|
/*!< User callback called after the touch interrupt occured */
|
||||||
|
esp_lcd_touch_interrupt_callback_t interrupt_callback;
|
||||||
|
} esp_lcd_touch_config_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t points; /*!< Count of touch points saved */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint16_t x; /*!< X coordinate */
|
||||||
|
uint16_t y; /*!< Y coordinate */
|
||||||
|
uint16_t strength; /*!< Strength */
|
||||||
|
} coords[CONFIG_ESP_LCD_TOUCH_MAX_POINTS];
|
||||||
|
|
||||||
|
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||||
|
uint8_t buttons; /*!< Count of buttons states saved */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint8_t status; /*!< Status of button */
|
||||||
|
} button[CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
portMUX_TYPE lock; /*!< Lock for read/write */
|
||||||
|
} esp_lcd_touch_data_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Declare of Touch Type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct esp_lcd_touch_s {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read data from touch controller (mandatory)
|
||||||
|
*
|
||||||
|
* @note This function is usually blocking.
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*read_data)(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get coordinates from touch controller (mandatory)
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param x: Array of X coordinates
|
||||||
|
* @param y: Array of Y coordinates
|
||||||
|
* @param strength: Array of strengths
|
||||||
|
* @param point_num: Count of points touched (equals with count of items in x and y array)
|
||||||
|
* @param max_point_num: Maximum count of touched points to return (equals with max size of x and y array)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||||
|
*/
|
||||||
|
bool (*get_xy)(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||||
|
|
||||||
|
|
||||||
|
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||||
|
/**
|
||||||
|
* @brief Get button state (optional)
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param n: Button index
|
||||||
|
* @param state: Button state
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||||
|
*/
|
||||||
|
esp_err_t (*get_button_state)(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Swap X and Y after read coordinates (optional)
|
||||||
|
* If set, then not used SW swapping.
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param swap: Set swap value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*set_swap_xy)(esp_lcd_touch_handle_t tp, bool swap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Are X and Y coordinates swapped (optional)
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param swap: Get swap value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*get_swap_xy)(esp_lcd_touch_handle_t tp, bool *swap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mirror X after read coordinates
|
||||||
|
* If set, then not used SW mirroring.
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Set X mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*set_mirror_x)(esp_lcd_touch_handle_t tp, bool mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is mirrored X (optional)
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Get X mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*get_mirror_x)(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mirror Y after read coordinates
|
||||||
|
* If set, then not used SW mirroring.
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Set Y mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*set_mirror_y)(esp_lcd_touch_handle_t tp, bool mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is mirrored Y (optional)
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Get Y mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*get_mirror_y)(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delete Touch
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*del)(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configuration structure
|
||||||
|
*/
|
||||||
|
esp_lcd_touch_config_t config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Communication interface
|
||||||
|
*/
|
||||||
|
esp_lcd_panel_io_handle_t io;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Data structure
|
||||||
|
*/
|
||||||
|
esp_lcd_touch_data_t data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read data from touch controller
|
||||||
|
*
|
||||||
|
* @note This function is usually blocking.
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_ARG parameter error
|
||||||
|
* - ESP_FAIL sending command error, slave hasn't ACK the transfer
|
||||||
|
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode
|
||||||
|
* - ESP_ERR_TIMEOUT operation timeout because the bus is busy
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_read_data(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read coordinates from touch controller
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param x: Array of X coordinates
|
||||||
|
* @param y: Array of Y coordinates
|
||||||
|
* @param strength: Array of the strengths (can be NULL)
|
||||||
|
* @param point_num: Count of points touched (equals with count of items in x and y array)
|
||||||
|
* @param max_point_num: Maximum count of touched points to return (equals with max size of x and y array)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||||
|
*/
|
||||||
|
bool esp_lcd_touch_get_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||||
|
|
||||||
|
|
||||||
|
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||||
|
/**
|
||||||
|
* @brief Get button state
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param n: Button index
|
||||||
|
* @param state: Button state
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_NOT_SUPPORTED if this function is not supported by controller
|
||||||
|
* - ESP_ERR_INVALID_ARG if bad button index
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Swap X and Y after read coordinates
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param swap: Set swap value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_set_swap_xy(esp_lcd_touch_handle_t tp, bool swap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Are X and Y coordinates swapped
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param swap: Get swap value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_get_swap_xy(esp_lcd_touch_handle_t tp, bool *swap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mirror X after read coordinates
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Set X mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_set_mirror_x(esp_lcd_touch_handle_t tp, bool mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is mirrored X
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Get X mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_get_mirror_x(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mirror Y after read coordinates
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Set Y mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_set_mirror_y(esp_lcd_touch_handle_t tp, bool mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is mirrored Y
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Get Y mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_get_mirror_y(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delete touch (free all allocated memory and restart HW)
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_del(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register user callback called after the touch interrupt occured
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param callback: Interrupt callback
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_handle_t tp, esp_lcd_touch_interrupt_callback_t callback);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,270 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/i2c.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "esp_lcd_touch.h"
|
||||||
|
|
||||||
|
static const char *TAG = "GT911";
|
||||||
|
|
||||||
|
/* GT911 registers */
|
||||||
|
#define ESP_LCD_TOUCH_GT911_READ_XY_REG (0x814E)
|
||||||
|
#define ESP_LCD_TOUCH_GT911_CONFIG_REG (0x8047)
|
||||||
|
#define ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG (0x8140)
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Function definitions
|
||||||
|
*******************************************************************************/
|
||||||
|
static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp);
|
||||||
|
static bool esp_lcd_touch_gt911_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||||
|
static esp_err_t esp_lcd_touch_gt911_del(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/* I2C read/write */
|
||||||
|
static esp_err_t touch_gt911_i2c_read(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len);
|
||||||
|
static esp_err_t touch_gt911_i2c_write(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t data);
|
||||||
|
|
||||||
|
/* GT911 reset */
|
||||||
|
static esp_err_t touch_gt911_reset(esp_lcd_touch_handle_t tp);
|
||||||
|
/* Read status and config register */
|
||||||
|
static esp_err_t touch_gt911_read_cfg(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Public API functions
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_new_i2c_gt911(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
assert(io != NULL);
|
||||||
|
assert(config != NULL);
|
||||||
|
assert(out_touch != NULL);
|
||||||
|
|
||||||
|
/* Prepare main structure */
|
||||||
|
esp_lcd_touch_handle_t esp_lcd_touch_gt911 = heap_caps_calloc(1, sizeof(esp_lcd_touch_t), MALLOC_CAP_DEFAULT);
|
||||||
|
ESP_GOTO_ON_FALSE(esp_lcd_touch_gt911, ESP_ERR_NO_MEM, err, TAG, "no mem for GT911 controller");
|
||||||
|
|
||||||
|
/* Communication interface */
|
||||||
|
esp_lcd_touch_gt911->io = io;
|
||||||
|
|
||||||
|
/* Only supported callbacks are set */
|
||||||
|
esp_lcd_touch_gt911->read_data = esp_lcd_touch_gt911_read_data;
|
||||||
|
esp_lcd_touch_gt911->get_xy = esp_lcd_touch_gt911_get_xy;
|
||||||
|
esp_lcd_touch_gt911->del = esp_lcd_touch_gt911_del;
|
||||||
|
|
||||||
|
/* Mutex */
|
||||||
|
esp_lcd_touch_gt911->data.lock.owner = portMUX_FREE_VAL;
|
||||||
|
|
||||||
|
/* Save config */
|
||||||
|
memcpy(&esp_lcd_touch_gt911->config, config, sizeof(esp_lcd_touch_config_t));
|
||||||
|
|
||||||
|
/* Prepare pin for touch interrupt */
|
||||||
|
if (esp_lcd_touch_gt911->config.int_gpio_num != GPIO_NUM_NC) {
|
||||||
|
const gpio_config_t int_gpio_config = {
|
||||||
|
.mode = GPIO_MODE_INPUT,
|
||||||
|
.intr_type = GPIO_INTR_NEGEDGE,
|
||||||
|
.pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.int_gpio_num)
|
||||||
|
};
|
||||||
|
ret = gpio_config(&int_gpio_config);
|
||||||
|
ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed");
|
||||||
|
|
||||||
|
/* Register interrupt callback */
|
||||||
|
if (esp_lcd_touch_gt911->config.interrupt_callback) {
|
||||||
|
esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_gt911, esp_lcd_touch_gt911->config.interrupt_callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare pin for touch controller reset */
|
||||||
|
if (esp_lcd_touch_gt911->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||||
|
const gpio_config_t rst_gpio_config = {
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.rst_gpio_num)
|
||||||
|
};
|
||||||
|
ret = gpio_config(&rst_gpio_config);
|
||||||
|
ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset controller */
|
||||||
|
ret = touch_gt911_reset(esp_lcd_touch_gt911);
|
||||||
|
ESP_GOTO_ON_ERROR(ret, err, TAG, "GT911 reset failed");
|
||||||
|
|
||||||
|
/* Read status and config info */
|
||||||
|
ret = touch_gt911_read_cfg(esp_lcd_touch_gt911);
|
||||||
|
ESP_GOTO_ON_ERROR(ret, err, TAG, "GT911 init failed");
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Error (0x%x)! Touch controller GT911 initialization failed!", ret);
|
||||||
|
if (esp_lcd_touch_gt911) {
|
||||||
|
esp_lcd_touch_gt911_del(esp_lcd_touch_gt911);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_touch = esp_lcd_touch_gt911;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
esp_err_t err;
|
||||||
|
uint8_t buf[41];
|
||||||
|
uint8_t touch_cnt = 0;
|
||||||
|
uint8_t clear = 0;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, buf, 1);
|
||||||
|
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||||
|
|
||||||
|
/* Any touch data? */
|
||||||
|
if ((buf[0] & 0x80) == 0x00) {
|
||||||
|
touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||||
|
} else {
|
||||||
|
/* Count of touched points */
|
||||||
|
touch_cnt = buf[0] & 0x0f;
|
||||||
|
if (touch_cnt > 5 || touch_cnt == 0) {
|
||||||
|
touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read all points */
|
||||||
|
err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG + 1, &buf[1], touch_cnt * 8);
|
||||||
|
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||||
|
|
||||||
|
/* Clear all */
|
||||||
|
err = touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||||
|
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&tp->data.lock);
|
||||||
|
|
||||||
|
/* Number of touched points */
|
||||||
|
touch_cnt = (touch_cnt > CONFIG_ESP_LCD_TOUCH_MAX_POINTS ? CONFIG_ESP_LCD_TOUCH_MAX_POINTS : touch_cnt);
|
||||||
|
tp->data.points = touch_cnt;
|
||||||
|
|
||||||
|
/* Fill all coordinates */
|
||||||
|
for (i = 0; i < touch_cnt; i++) {
|
||||||
|
tp->data.coords[i].x = ((uint16_t)buf[(i * 8) + 3] << 8) + buf[(i * 8) + 2];
|
||||||
|
tp->data.coords[i].y = (((uint16_t)buf[(i * 8) + 5] << 8) + buf[(i * 8) + 4]);
|
||||||
|
tp->data.coords[i].strength = (((uint16_t)buf[(i * 8) + 7] << 8) + buf[(i * 8) + 6]);
|
||||||
|
}
|
||||||
|
|
||||||
|
portEXIT_CRITICAL(&tp->data.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool esp_lcd_touch_gt911_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(x != NULL);
|
||||||
|
assert(y != NULL);
|
||||||
|
assert(point_num != NULL);
|
||||||
|
assert(max_point_num > 0);
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&tp->data.lock);
|
||||||
|
|
||||||
|
/* Count of points */
|
||||||
|
*point_num = (tp->data.points > max_point_num ? max_point_num : tp->data.points);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < *point_num; i++) {
|
||||||
|
x[i] = tp->data.coords[i].x;
|
||||||
|
y[i] = tp->data.coords[i].y;
|
||||||
|
if (strength) {
|
||||||
|
strength[i] = tp->data.coords[i].strength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invalidate */
|
||||||
|
tp->data.points = 0;
|
||||||
|
|
||||||
|
portEXIT_CRITICAL(&tp->data.lock);
|
||||||
|
|
||||||
|
return (*point_num > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_lcd_touch_gt911_del(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
/* Reset GPIO pin settings */
|
||||||
|
if (tp->config.int_gpio_num != GPIO_NUM_NC) {
|
||||||
|
gpio_reset_pin(tp->config.int_gpio_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset GPIO pin settings */
|
||||||
|
if (tp->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||||
|
gpio_reset_pin(tp->config.rst_gpio_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(tp);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Private API function
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
/* Reset controller */
|
||||||
|
static esp_err_t touch_gt911_reset(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
if (tp->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||||
|
ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, tp->config.levels.reset), TAG, "GPIO set level error!");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, !tp->config.levels.reset), TAG, "GPIO set level error!");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t touch_gt911_read_cfg(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
uint8_t buf[4];
|
||||||
|
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG, (uint8_t *)&buf[0], 3), TAG, "GT911 read error!");
|
||||||
|
ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_CONFIG_REG, (uint8_t *)&buf[3], 1), TAG, "GT911 read error!");
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "TouchPad_ID:0x%02x,0x%02x,0x%02x", buf[0], buf[1], buf[2]);
|
||||||
|
ESP_LOGI(TAG, "TouchPad_Config_Version:%d", buf[3]);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t touch_gt911_i2c_read(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(data != NULL);
|
||||||
|
|
||||||
|
/* Read data */
|
||||||
|
return esp_lcd_panel_io_rx_param(tp->io, reg, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t touch_gt911_i2c_write(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t data)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
// *INDENT-OFF*
|
||||||
|
/* Write data */
|
||||||
|
return esp_lcd_panel_io_tx_param(tp->io, reg, (uint8_t[]){data}, 1);
|
||||||
|
// *INDENT-ON*
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief ESP LCD touch: GT911
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_lcd_touch.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a new GT911 touch driver
|
||||||
|
*
|
||||||
|
* @note The I2C communication should be initialized before use this function.
|
||||||
|
*
|
||||||
|
* @param io LCD/Touch panel IO handle
|
||||||
|
* @param config: Touch configuration
|
||||||
|
* @param out_touch: Touch instance handle
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_NO_MEM if there is no memory for allocating main structure
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_new_i2c_gt911(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief I2C address of the GT911 controller
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS (0x5D)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Touch IO configuration structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG() \
|
||||||
|
{ \
|
||||||
|
.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, \
|
||||||
|
.control_phase_bytes = 1, \
|
||||||
|
.dc_bit_offset = 0, \
|
||||||
|
.lcd_cmd_bits = 16, \
|
||||||
|
.flags = \
|
||||||
|
{ \
|
||||||
|
.disable_control_phase = 1, \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
#include "esp_lcd_touch_gt911.h"
|
||||||
|
#include "gt911_touch.h"
|
||||||
|
|
||||||
|
#define CONFIG_LCD_HRES 800
|
||||||
|
#define CONFIG_LCD_VRES 1280
|
||||||
|
|
||||||
|
static const char *TAG = "example";
|
||||||
|
|
||||||
|
esp_lcd_touch_handle_t tp;
|
||||||
|
esp_lcd_panel_io_handle_t tp_io_handle;
|
||||||
|
|
||||||
|
uint16_t touch_strength[1];
|
||||||
|
uint8_t touch_cnt = 0;
|
||||||
|
|
||||||
|
gt911_touch::gt911_touch(int8_t sda_pin, int8_t scl_pin, int8_t rst_pin, int8_t int_pin)
|
||||||
|
{
|
||||||
|
_sda = sda_pin;
|
||||||
|
_scl = scl_pin;
|
||||||
|
_rst = rst_pin;
|
||||||
|
_int = int_pin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gt911_touch::begin()
|
||||||
|
{
|
||||||
|
// i2c_config_t i2c_conf = {
|
||||||
|
// .mode = I2C_MODE_MASTER,
|
||||||
|
// .sda_io_num = (gpio_num_t)_sda,
|
||||||
|
// .scl_io_num = (gpio_num_t)_scl,
|
||||||
|
// .sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
// .scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
// };
|
||||||
|
// i2c_conf.master.clk_speed = 400000; // 400kHz
|
||||||
|
|
||||||
|
|
||||||
|
// ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &i2c_conf));
|
||||||
|
// ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, i2c_conf.mode, 0, 0, 0));
|
||||||
|
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||||
|
i2c_master_get_bus_handle(1,&i2c_handle);
|
||||||
|
|
||||||
|
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||||
|
tp_io_config.scl_speed_hz = 100000;
|
||||||
|
ESP_LOGI(TAG, "Initialize touch IO (I2C)");
|
||||||
|
esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle);
|
||||||
|
|
||||||
|
esp_lcd_touch_config_t tp_cfg = {
|
||||||
|
.x_max = CONFIG_LCD_HRES,
|
||||||
|
.y_max = CONFIG_LCD_VRES,
|
||||||
|
.rst_gpio_num = (gpio_num_t)_rst,
|
||||||
|
.int_gpio_num = (gpio_num_t)_int,
|
||||||
|
.levels = {
|
||||||
|
.reset = 0,
|
||||||
|
.interrupt = 0,
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.swap_xy = 0,
|
||||||
|
.mirror_x = 0,
|
||||||
|
.mirror_y = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Initialize touch controller gt911");
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gt911_touch::getTouch(uint16_t *x, uint16_t *y)
|
||||||
|
{
|
||||||
|
esp_lcd_touch_read_data(tp);
|
||||||
|
bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, x, y, touch_strength, &touch_cnt, 1);
|
||||||
|
|
||||||
|
return touchpad_pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gt911_touch::set_rotation(uint8_t r){
|
||||||
|
switch(r){
|
||||||
|
case 0:
|
||||||
|
esp_lcd_touch_set_swap_xy(tp, false);
|
||||||
|
esp_lcd_touch_set_mirror_x(tp, false);
|
||||||
|
esp_lcd_touch_set_mirror_y(tp, false);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
esp_lcd_touch_set_swap_xy(tp, false);
|
||||||
|
esp_lcd_touch_set_mirror_x(tp, true);
|
||||||
|
esp_lcd_touch_set_mirror_y(tp, true);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
esp_lcd_touch_set_swap_xy(tp, false);
|
||||||
|
esp_lcd_touch_set_mirror_x(tp, false);
|
||||||
|
esp_lcd_touch_set_mirror_y(tp, false);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
esp_lcd_touch_set_swap_xy(tp, false);
|
||||||
|
esp_lcd_touch_set_mirror_x(tp, true);
|
||||||
|
esp_lcd_touch_set_mirror_y(tp, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef _GT911_TOUCH_H
|
||||||
|
#define _GT911_TOUCH_H
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
class gt911_touch
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
gt911_touch(int8_t sda_pin, int8_t scl_pin, int8_t rst_pin = -1, int8_t int_pin = -1);
|
||||||
|
|
||||||
|
void begin();
|
||||||
|
bool getTouch(uint16_t *x, uint16_t *y);
|
||||||
|
void set_rotation(uint8_t r);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int8_t _sda, _scl, _rst, _int;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
@@ -0,0 +1,8 @@
|
|||||||
|
#此样例使用的屏幕是微雪的10.1寸屏幕,具体型号位:10.1-DSI-TOUCH-A
|
||||||
|
# 请使用arduino_esp32_v3.2.1版本
|
||||||
|
|
||||||
|
#lvgl v9.2.2
|
||||||
|
|
||||||
|
|
||||||
|
# 要将lvgl文件夹中的demos文件夹移动到同目录下的src文件夹中
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,708 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
#include "esp_lcd_panel_ops.h"
|
||||||
|
#if SOC_LCDCAM_RGB_LCD_SUPPORTED
|
||||||
|
#include "esp_lcd_panel_rgb.h"
|
||||||
|
#endif
|
||||||
|
#if SOC_MIPI_DSI_SUPPORTED
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
#endif
|
||||||
|
#include "src/touch/esp_lcd_touch.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32P4
|
||||||
|
#include "esp_private/esp_cache_private.h"
|
||||||
|
#include "driver/ppa.h"
|
||||||
|
#endif
|
||||||
|
#include "lvgl.h"
|
||||||
|
#include "lvgl_private.h"
|
||||||
|
#include "lvgl_port_v9.h"
|
||||||
|
|
||||||
|
#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||||
|
#define BLOCK_SIZE_SMALL (32)
|
||||||
|
#define BLOCK_SIZE_LARGE (256)
|
||||||
|
|
||||||
|
static const char *TAG = "lv_port";
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_lcd_panel_handle_t lcd_handle;
|
||||||
|
esp_lcd_touch_handle_t tp_handle;
|
||||||
|
bool is_init;
|
||||||
|
} lvgl_port_task_param_t;
|
||||||
|
|
||||||
|
typedef esp_err_t (*get_lcd_frame_buffer_cb_t)(esp_lcd_panel_handle_t panel, uint32_t fb_num, void **fb0, ...);
|
||||||
|
|
||||||
|
#if LVGL_PORT_PPA_ROTATION_ENABLE
|
||||||
|
static ppa_client_handle_t ppa_srm_handle = NULL;
|
||||||
|
static size_t data_cache_line_size = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static SemaphoreHandle_t lvgl_mux; // LVGL mutex
|
||||||
|
static TaskHandle_t lvgl_task_handle = NULL;
|
||||||
|
static lvgl_port_interface_t lvgl_port_interface = LVGL_PORT_INTERFACE_RGB;
|
||||||
|
|
||||||
|
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||||
|
static get_lcd_frame_buffer_cb_t lvgl_get_lcd_frame_buffer = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
|
||||||
|
static void *get_next_frame_buffer(esp_lcd_panel_handle_t panel_handle)
|
||||||
|
{
|
||||||
|
static void *next_fb = NULL;
|
||||||
|
static void *fb[2] = { NULL };
|
||||||
|
if (next_fb == NULL) {
|
||||||
|
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 2, &fb[0], &fb[1]));
|
||||||
|
next_fb = fb[1];
|
||||||
|
} else {
|
||||||
|
next_fb = (next_fb == fb[0]) ? fb[1] : fb[0];
|
||||||
|
}
|
||||||
|
return next_fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !LVGL_PORT_PPA_ROTATION_ENABLE
|
||||||
|
static void rotate_image(const void *src, void *dst, int width, int height, int rotation, int bpp)
|
||||||
|
{
|
||||||
|
int bytes_per_pixel = bpp / 8;
|
||||||
|
int block_w = rotation == 90 || rotation == 270 ? BLOCK_SIZE_SMALL : BLOCK_SIZE_LARGE;
|
||||||
|
int block_h = rotation == 90 || rotation == 270 ? BLOCK_SIZE_LARGE : BLOCK_SIZE_SMALL;
|
||||||
|
|
||||||
|
for (int i = 0; i < height; i += block_h) {
|
||||||
|
int max_height = i + block_h > height ? height : i + block_h;
|
||||||
|
|
||||||
|
for (int j = 0; j < width; j += block_w) {
|
||||||
|
int max_width = j + block_w > width ? width : j + block_w;
|
||||||
|
|
||||||
|
for (int x = i; x < max_height; x++) {
|
||||||
|
for (int y = j; y < max_width; y++) {
|
||||||
|
void *src_pixel = (uint8_t *)src + (x * width + y) * bytes_per_pixel;
|
||||||
|
void *dst_pixel;
|
||||||
|
|
||||||
|
switch (rotation) {
|
||||||
|
case 270:
|
||||||
|
dst_pixel = (uint8_t *)dst + ((width - 1 - y) * height + x) * bytes_per_pixel;
|
||||||
|
break;
|
||||||
|
case 180:
|
||||||
|
dst_pixel = (uint8_t *)dst + ((height - 1 - x) * width + (width - 1 - y)) * bytes_per_pixel;
|
||||||
|
break;
|
||||||
|
case 90:
|
||||||
|
dst_pixel = (uint8_t *)dst + (y * height + (height - 1 - x)) * bytes_per_pixel;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bpp == 16) {
|
||||||
|
*(uint16_t *)dst_pixel = *(uint16_t *)src_pixel;
|
||||||
|
} else if (bpp == 24) {
|
||||||
|
((uint8_t *)dst_pixel)[0] = ((uint8_t *)src_pixel)[0];
|
||||||
|
((uint8_t *)dst_pixel)[1] = ((uint8_t *)src_pixel)[1];
|
||||||
|
((uint8_t *)dst_pixel)[2] = ((uint8_t *)src_pixel)[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IRAM_ATTR static void rotate_copy_pixel(const uint16_t *from, uint16_t *to, uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint16_t w, uint16_t h, uint16_t rotation)
|
||||||
|
{
|
||||||
|
#if LVGL_PORT_PPA_ROTATION_ENABLE
|
||||||
|
ppa_srm_rotation_angle_t ppa_rotation;
|
||||||
|
int x_offset = 0, y_offset = 0;
|
||||||
|
|
||||||
|
// Determine rotation settings once and reuse
|
||||||
|
switch (rotation) {
|
||||||
|
case 90:
|
||||||
|
ppa_rotation = PPA_SRM_ROTATION_ANGLE_270;
|
||||||
|
x_offset = h - y_end - 1;
|
||||||
|
y_offset = x_start;
|
||||||
|
break;
|
||||||
|
case 180:
|
||||||
|
ppa_rotation = PPA_SRM_ROTATION_ANGLE_180;
|
||||||
|
x_offset = w - x_end - 1;
|
||||||
|
y_offset = h - y_end - 1;
|
||||||
|
break;
|
||||||
|
case 270:
|
||||||
|
ppa_rotation = PPA_SRM_ROTATION_ANGLE_90;
|
||||||
|
x_offset = y_start;
|
||||||
|
y_offset = w - x_end - 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ppa_rotation = PPA_SRM_ROTATION_ANGLE_0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill operation config for PPA rotation, without recalculating each time
|
||||||
|
ppa_srm_oper_config_t oper_config = {
|
||||||
|
.in.buffer = from,
|
||||||
|
.in.pic_w = w,
|
||||||
|
.in.pic_h = h,
|
||||||
|
.in.block_w = x_end - x_start + 1,
|
||||||
|
.in.block_h = y_end - y_start + 1,
|
||||||
|
.in.block_offset_x = x_start,
|
||||||
|
.in.block_offset_y = y_start,
|
||||||
|
.in.srm_cm = (LV_COLOR_DEPTH == 24) ? PPA_SRM_COLOR_MODE_RGB888 : PPA_SRM_COLOR_MODE_RGB565,
|
||||||
|
|
||||||
|
.out.buffer = to,
|
||||||
|
.out.buffer_size = ALIGN_UP_BY(sizeof(lv_color_t) * w * h, data_cache_line_size),
|
||||||
|
.out.pic_w = (ppa_rotation == PPA_SRM_ROTATION_ANGLE_90 || ppa_rotation == PPA_SRM_ROTATION_ANGLE_270) ? h : w,
|
||||||
|
.out.pic_h = (ppa_rotation == PPA_SRM_ROTATION_ANGLE_90 || ppa_rotation == PPA_SRM_ROTATION_ANGLE_270) ? w : h,
|
||||||
|
.out.block_offset_x = x_offset,
|
||||||
|
.out.block_offset_y = y_offset,
|
||||||
|
.out.srm_cm = (LV_COLOR_DEPTH == 24) ? PPA_SRM_COLOR_MODE_RGB888 : PPA_SRM_COLOR_MODE_RGB565,
|
||||||
|
|
||||||
|
.rotation_angle = ppa_rotation,
|
||||||
|
.scale_x = 1.0,
|
||||||
|
.scale_y = 1.0,
|
||||||
|
.rgb_swap = 0,
|
||||||
|
.byte_swap = 0,
|
||||||
|
.mode = PPA_TRANS_MODE_BLOCKING,
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(ppa_do_scale_rotate_mirror(ppa_srm_handle, &oper_config));
|
||||||
|
|
||||||
|
#else
|
||||||
|
// Fallback: optimized transpose for non-PPA systems
|
||||||
|
rotate_image(from, to, w, h, rotation, LV_COLOR_DEPTH);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* EXAMPLE_LVGL_PORT_ROTATION_DEGREE */
|
||||||
|
|
||||||
|
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||||
|
|
||||||
|
static void switch_lcd_frame_buffer_to(esp_lcd_panel_handle_t panel_handle, void *fb)
|
||||||
|
{
|
||||||
|
esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, LVGL_PORT_H_RES, LVGL_PORT_V_RES, fb);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LVGL_PORT_DIRECT_MODE
|
||||||
|
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
|
||||||
|
typedef struct {
|
||||||
|
uint16_t inv_p;
|
||||||
|
uint8_t inv_area_joined[LV_INV_BUF_SIZE];
|
||||||
|
lv_area_t inv_areas[LV_INV_BUF_SIZE];
|
||||||
|
} lv_port_dirty_area_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FLUSH_STATUS_PART,
|
||||||
|
FLUSH_STATUS_FULL
|
||||||
|
} lv_port_flush_status_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FLUSH_PROBE_PART_COPY,
|
||||||
|
FLUSH_PROBE_SKIP_COPY,
|
||||||
|
FLUSH_PROBE_FULL_COPY,
|
||||||
|
} lv_port_flush_probe_t;
|
||||||
|
|
||||||
|
static lv_port_dirty_area_t dirty_area;
|
||||||
|
|
||||||
|
static void flush_dirty_save(lv_port_dirty_area_t *dirty_area)
|
||||||
|
{
|
||||||
|
lv_disp_t *disp = lv_refr_get_disp_refreshing();
|
||||||
|
dirty_area->inv_p = disp->inv_p;
|
||||||
|
for (int i = 0; i < disp->inv_p; i++) {
|
||||||
|
dirty_area->inv_area_joined[i] = disp->inv_area_joined[i];
|
||||||
|
dirty_area->inv_areas[i] = disp->inv_areas[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Probe dirty area to copy
|
||||||
|
*
|
||||||
|
* @note This function is used to avoid tearing effect, and only work with LVGL direct-mode.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static lv_port_flush_probe_t flush_copy_probe(lv_display_t *disp)
|
||||||
|
{
|
||||||
|
static lv_port_flush_status_t prev_status = FLUSH_STATUS_PART;
|
||||||
|
lv_port_flush_status_t cur_status;
|
||||||
|
lv_port_flush_probe_t probe_result;
|
||||||
|
lv_disp_t *disp_refr = lv_refr_get_disp_refreshing();
|
||||||
|
|
||||||
|
uint32_t flush_ver = 0;
|
||||||
|
uint32_t flush_hor = 0;
|
||||||
|
for (int i = 0; i < disp_refr->inv_p; i++) {
|
||||||
|
if (disp_refr->inv_area_joined[i] == 0) {
|
||||||
|
flush_ver = (disp_refr->inv_areas[i].y2 + 1 - disp_refr->inv_areas[i].y1);
|
||||||
|
flush_hor = (disp_refr->inv_areas[i].x2 + 1 - disp_refr->inv_areas[i].x1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Check if the current full screen refreshes */
|
||||||
|
cur_status = ((flush_ver == disp->ver_res) && (flush_hor == disp->hor_res)) ? (FLUSH_STATUS_FULL) : (FLUSH_STATUS_PART);
|
||||||
|
|
||||||
|
if (prev_status == FLUSH_STATUS_FULL) {
|
||||||
|
if ((cur_status == FLUSH_STATUS_PART)) {
|
||||||
|
probe_result = FLUSH_PROBE_FULL_COPY;
|
||||||
|
} else {
|
||||||
|
probe_result = FLUSH_PROBE_SKIP_COPY;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
probe_result = FLUSH_PROBE_PART_COPY;
|
||||||
|
}
|
||||||
|
prev_status = cur_status;
|
||||||
|
|
||||||
|
return probe_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *flush_get_next_buf(void *panel_handle)
|
||||||
|
{
|
||||||
|
return get_next_frame_buffer(panel_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy dirty area
|
||||||
|
*
|
||||||
|
* @note This function is used to avoid tearing effect, and only work with LVGL direct-mode.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void flush_dirty_copy(void *dst, void *src, lv_port_dirty_area_t *dirty_area)
|
||||||
|
{
|
||||||
|
lv_coord_t x_start, x_end, y_start, y_end;
|
||||||
|
for (int i = 0; i < dirty_area->inv_p; i++) {
|
||||||
|
/* Refresh the unjoined areas*/
|
||||||
|
if (dirty_area->inv_area_joined[i] == 0) {
|
||||||
|
x_start = dirty_area->inv_areas[i].x1;
|
||||||
|
x_end = dirty_area->inv_areas[i].x2;
|
||||||
|
y_start = dirty_area->inv_areas[i].y1;
|
||||||
|
y_end = dirty_area->inv_areas[i].y2;
|
||||||
|
|
||||||
|
rotate_copy_pixel(src, dst, x_start, y_start, x_end, y_end, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
|
||||||
|
{
|
||||||
|
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
|
||||||
|
const int offsetx1 = area->x1;
|
||||||
|
const int offsetx2 = area->x2;
|
||||||
|
const int offsety1 = area->y1;
|
||||||
|
const int offsety2 = area->y2;
|
||||||
|
void *next_fb = NULL;
|
||||||
|
lv_port_flush_probe_t probe_result = FLUSH_PROBE_PART_COPY;
|
||||||
|
|
||||||
|
/* Action after last area refresh */
|
||||||
|
if (lv_disp_flush_is_last(disp)) {
|
||||||
|
/* Check if the `full_refresh` flag has been triggered */
|
||||||
|
if (disp->render_mode == LV_DISPLAY_RENDER_MODE_FULL) {
|
||||||
|
/* Reset flag */
|
||||||
|
disp->render_mode = LV_DISPLAY_RENDER_MODE_DIRECT;
|
||||||
|
|
||||||
|
// Rotate and copy data from the whole screen LVGL's buffer to the next frame buffer
|
||||||
|
next_fb = flush_get_next_buf(panel_handle);
|
||||||
|
rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
|
||||||
|
|
||||||
|
/* Switch the current LCD frame buffer to `next_fb` */
|
||||||
|
switch_lcd_frame_buffer_to(panel_handle, next_fb);
|
||||||
|
|
||||||
|
/* Waiting for the current frame buffer to complete transmission */
|
||||||
|
ulTaskNotifyValueClear(NULL, ULONG_MAX);
|
||||||
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
|
|
||||||
|
/* Synchronously update the dirty area for another frame buffer */
|
||||||
|
flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area);
|
||||||
|
flush_get_next_buf(panel_handle);
|
||||||
|
} else {
|
||||||
|
/* Probe the copy method for the current dirty area */
|
||||||
|
probe_result = flush_copy_probe(disp);
|
||||||
|
|
||||||
|
if (probe_result == FLUSH_PROBE_FULL_COPY) {
|
||||||
|
/* Save current dirty area for next frame buffer */
|
||||||
|
flush_dirty_save(&dirty_area);
|
||||||
|
|
||||||
|
/* Set LVGL full-refresh flag and set flush ready in advance */
|
||||||
|
disp->render_mode = LV_DISPLAY_RENDER_MODE_FULL;
|
||||||
|
disp->rendering_in_progress = false;
|
||||||
|
lv_disp_flush_ready(disp);
|
||||||
|
|
||||||
|
/* Force to refresh whole screen, and will invoke `flush_callback` recursively */
|
||||||
|
lv_refr_now(lv_refr_get_disp_refreshing());
|
||||||
|
} else {
|
||||||
|
/* Update current dirty area for next frame buffer */
|
||||||
|
next_fb = flush_get_next_buf(panel_handle);
|
||||||
|
flush_dirty_save(&dirty_area);
|
||||||
|
flush_dirty_copy(next_fb, color_map, &dirty_area);
|
||||||
|
|
||||||
|
/* Switch the current LCD frame buffer to `next_fb` */
|
||||||
|
switch_lcd_frame_buffer_to(panel_handle, next_fb);
|
||||||
|
|
||||||
|
/* Waiting for the current frame buffer to complete transmission */
|
||||||
|
ulTaskNotifyValueClear(NULL, ULONG_MAX);
|
||||||
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
|
|
||||||
|
if (probe_result == FLUSH_PROBE_PART_COPY) {
|
||||||
|
/* Synchronously update the dirty area for another frame buffer */
|
||||||
|
flush_dirty_save(&dirty_area);
|
||||||
|
flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area);
|
||||||
|
flush_get_next_buf(panel_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_disp_flush_ready(disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
|
||||||
|
{
|
||||||
|
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
|
||||||
|
|
||||||
|
/* Action after last area refresh */
|
||||||
|
if (lv_disp_flush_is_last(disp)) {
|
||||||
|
/* Switch the current LCD frame buffer to `color_map` */
|
||||||
|
switch_lcd_frame_buffer_to(panel_handle, color_map);
|
||||||
|
|
||||||
|
/* Waiting for the last frame buffer to complete transmission */
|
||||||
|
ulTaskNotifyValueClear(NULL, ULONG_MAX);
|
||||||
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_disp_flush_ready(disp);
|
||||||
|
}
|
||||||
|
#endif /* EXAMPLE_LVGL_PORT_ROTATION_DEGREE */
|
||||||
|
|
||||||
|
#elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_LCD_BUFFER_NUMS == 2
|
||||||
|
|
||||||
|
static void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
|
||||||
|
{
|
||||||
|
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
|
||||||
|
|
||||||
|
/* Switch the current LCD frame buffer to `color_map` */
|
||||||
|
switch_lcd_frame_buffer_to(panel_handle, color_map);
|
||||||
|
|
||||||
|
/* Waiting for the last frame buffer to complete transmission */
|
||||||
|
ulTaskNotifyValueClear(NULL, ULONG_MAX);
|
||||||
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
|
|
||||||
|
lv_disp_flush_ready(disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_LCD_BUFFER_NUMS == 3
|
||||||
|
|
||||||
|
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0
|
||||||
|
static void *lvgl_port_rgb_last_buf = NULL;
|
||||||
|
static void *lvgl_port_rgb_next_buf = NULL;
|
||||||
|
static void *lvgl_port_flush_next_buf = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
|
||||||
|
{
|
||||||
|
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
|
||||||
|
|
||||||
|
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
|
||||||
|
const int offsetx1 = area->x1;
|
||||||
|
const int offsetx2 = area->x2;
|
||||||
|
const int offsety1 = area->y1;
|
||||||
|
const int offsety2 = area->y2;
|
||||||
|
void *next_fb = get_next_frame_buffer(panel_handle);
|
||||||
|
|
||||||
|
/* Rotate and copy dirty area from the current LVGL's buffer to the next LCD frame buffer */
|
||||||
|
rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
|
||||||
|
|
||||||
|
/* Switch the current LCD frame buffer to `next_fb` */
|
||||||
|
switch_lcd_frame_buffer_to(panel_handle, next_fb);
|
||||||
|
#else
|
||||||
|
if (disp->buf_act == disp->buf_1) {
|
||||||
|
disp->buf_2->data = lvgl_port_flush_next_buf;
|
||||||
|
} else {
|
||||||
|
disp->buf_1->data = lvgl_port_flush_next_buf;
|
||||||
|
}
|
||||||
|
lvgl_port_flush_next_buf = color_map;
|
||||||
|
|
||||||
|
/* Switch the current LCD frame buffer to `color_map` */
|
||||||
|
switch_lcd_frame_buffer_to(panel_handle, color_map);
|
||||||
|
|
||||||
|
lvgl_port_rgb_next_buf = color_map;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
lv_disp_flush_ready(disp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
|
||||||
|
{
|
||||||
|
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
|
||||||
|
const int offsetx1 = area->x1;
|
||||||
|
const int offsetx2 = area->x2;
|
||||||
|
const int offsety1 = area->y1;
|
||||||
|
const int offsety2 = area->y2;
|
||||||
|
|
||||||
|
/* Just copy data from the color map to the LCD frame buffer */
|
||||||
|
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
|
||||||
|
|
||||||
|
if (lvgl_port_interface != LVGL_PORT_INTERFACE_MIPI_DSI_DMA) {
|
||||||
|
lv_disp_flush_ready(disp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* LVGL_PORT_AVOID_TEAR_ENABLE */
|
||||||
|
|
||||||
|
static lv_display_t *display_init(esp_lcd_panel_handle_t panel_handle)
|
||||||
|
{
|
||||||
|
#if LVGL_PORT_PPA_ROTATION_ENABLE
|
||||||
|
// Initialize the PPA
|
||||||
|
ppa_client_config_t ppa_srm_config = {
|
||||||
|
.oper_type = PPA_OPERATION_SRM,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(ppa_register_client(&ppa_srm_config, &ppa_srm_handle));
|
||||||
|
ESP_ERROR_CHECK(esp_cache_get_alignment(MALLOC_CAP_DMA|MALLOC_CAP_SPIRAM, &data_cache_line_size));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
assert(panel_handle);
|
||||||
|
|
||||||
|
// alloc draw buffers used by LVGL
|
||||||
|
void *buf1 = NULL;
|
||||||
|
void *buf2 = NULL;
|
||||||
|
int buffer_size = 0;
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Malloc memory for LVGL buffer");
|
||||||
|
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||||
|
// To avoid the tearing effect, we should use at least two frame buffers: one for LVGL rendering and another for RGB output
|
||||||
|
buffer_size = LVGL_PORT_H_RES * LVGL_PORT_V_RES;
|
||||||
|
#if (LVGL_PORT_LCD_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0) && LVGL_PORT_FULL_REFRESH
|
||||||
|
// With the usage of three buffers and full-refresh, we always have one buffer available for rendering, eliminating the need to wait for the RGB's sync signal
|
||||||
|
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 3, &lvgl_port_rgb_last_buf, &buf1, &buf2));
|
||||||
|
lvgl_port_rgb_next_buf = lvgl_port_rgb_last_buf;
|
||||||
|
lvgl_port_flush_next_buf = buf2;
|
||||||
|
#elif (LVGL_PORT_LCD_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0)
|
||||||
|
// Here we are using three frame buffers, one for LVGL rendering, and the other two for RGB driver (one of them is used for rotation)
|
||||||
|
void *fbs[3];
|
||||||
|
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 3, &fbs[0], &fbs[1], &fbs[2]));
|
||||||
|
buf1 = fbs[2];
|
||||||
|
#else
|
||||||
|
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 2, &buf1, &buf2));
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
// Normmaly, for RGB LCD, we just use one buffer for LVGL rendering
|
||||||
|
buffer_size = LVGL_PORT_H_RES * LVGL_PORT_BUFFER_HEIGHT;
|
||||||
|
buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), LVGL_PORT_BUFFER_MALLOC_CAPS);
|
||||||
|
// buffer_size = LVGL_PORT_H_RES * LVGL_PORT_BUFFER_HEIGHT;
|
||||||
|
// buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||||
|
assert(buf1);
|
||||||
|
ESP_LOGI(TAG, "LVGL buffer size: %dKB", buffer_size * sizeof(lv_color_t) / 1024);
|
||||||
|
#endif /* LVGL_PORT_AVOID_TEAR_ENABLE */
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Register display driver to LVGL");
|
||||||
|
lv_display_t *display = lv_display_create(
|
||||||
|
#if (EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 90) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 270)
|
||||||
|
LVGL_PORT_H_RES, LVGL_PORT_V_RES
|
||||||
|
#else
|
||||||
|
LVGL_PORT_V_RES, LVGL_PORT_H_RES
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
|
lv_display_set_buffers(
|
||||||
|
display, buf1, buf2, buffer_size * sizeof(lv_color_t),
|
||||||
|
#if LVGL_PORT_FULL_REFRESH
|
||||||
|
LV_DISPLAY_RENDER_MODE_FULL
|
||||||
|
#elif LVGL_PORT_DIRECT_MODE
|
||||||
|
LV_DISPLAY_RENDER_MODE_DIRECT
|
||||||
|
#else
|
||||||
|
LV_DISPLAY_RENDER_MODE_PARTIAL
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
lv_display_set_flush_cb(display, flush_callback);
|
||||||
|
lv_display_set_user_data(display, panel_handle);
|
||||||
|
|
||||||
|
return display;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void touchpad_read(lv_indev_t *indev_drv, lv_indev_data_t *data)
|
||||||
|
{
|
||||||
|
esp_lcd_touch_handle_t tp = (esp_lcd_touch_handle_t)lv_indev_get_user_data(indev_drv);
|
||||||
|
assert(tp);
|
||||||
|
|
||||||
|
uint16_t touchpad_x;
|
||||||
|
uint16_t touchpad_y;
|
||||||
|
uint8_t touchpad_cnt = 0;
|
||||||
|
/* Read data from touch controller into memory */
|
||||||
|
esp_lcd_touch_read_data(tp);
|
||||||
|
|
||||||
|
/* Read data from touch controller */
|
||||||
|
bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, &touchpad_x, &touchpad_y, NULL, &touchpad_cnt, 1);
|
||||||
|
if (touchpad_pressed && touchpad_cnt > 0) {
|
||||||
|
data->point.x = touchpad_x;
|
||||||
|
data->point.y = touchpad_y;
|
||||||
|
data->state = LV_INDEV_STATE_PRESSED;
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Touch position: %d,%d", touchpad_x, touchpad_y);
|
||||||
|
} else {
|
||||||
|
data->state = LV_INDEV_STATE_RELEASED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_indev_t *indev_init(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
assert(tp);
|
||||||
|
|
||||||
|
lv_indev_t *indev = lv_indev_create();
|
||||||
|
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); /*See below.*/
|
||||||
|
lv_indev_set_user_data(indev, tp);
|
||||||
|
lv_indev_set_read_cb(indev, touchpad_read); /*See below.*/
|
||||||
|
|
||||||
|
return indev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tick_increment(void *arg)
|
||||||
|
{
|
||||||
|
/* Tell LVGL how many milliseconds have elapsed */
|
||||||
|
lv_tick_inc(LVGL_PORT_TICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t tick_init(void)
|
||||||
|
{
|
||||||
|
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
|
||||||
|
const esp_timer_create_args_t lvgl_tick_timer_args = {
|
||||||
|
.callback = &tick_increment,
|
||||||
|
.name = "LVGL tick"
|
||||||
|
};
|
||||||
|
esp_timer_handle_t lvgl_tick_timer = NULL;
|
||||||
|
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
|
||||||
|
return esp_timer_start_periodic(lvgl_tick_timer, LVGL_PORT_TICK_PERIOD_MS * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lvgl_port_task(void *arg)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Starting LVGL task");
|
||||||
|
|
||||||
|
lvgl_port_task_param_t *param = (lvgl_port_task_param_t *)arg;
|
||||||
|
|
||||||
|
lv_init();
|
||||||
|
ESP_ERROR_CHECK(tick_init());
|
||||||
|
|
||||||
|
lv_display_t *disp = display_init(param->lcd_handle);
|
||||||
|
assert(disp);
|
||||||
|
|
||||||
|
if (param->tp_handle) {
|
||||||
|
lv_indev_t *indev = indev_init(param->tp_handle);
|
||||||
|
assert(indev);
|
||||||
|
|
||||||
|
#if EXAMPLE_LVGL_PORT_ROTATION_90
|
||||||
|
esp_lcd_touch_set_swap_xy(param->tp_handle, true);
|
||||||
|
esp_lcd_touch_set_mirror_x(param->tp_handle, true);
|
||||||
|
#elif EXAMPLE_LVGL_PORT_ROTATION_180
|
||||||
|
esp_lcd_touch_set_mirror_x(param->tp_handle, false);
|
||||||
|
esp_lcd_touch_set_mirror_y(param->tp_handle, false);
|
||||||
|
#elif EXAMPLE_LVGL_PORT_ROTATION_270
|
||||||
|
esp_lcd_touch_set_swap_xy(param->tp_handle, true);
|
||||||
|
esp_lcd_touch_set_mirror_y(param->tp_handle, false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
param->is_init = true;
|
||||||
|
|
||||||
|
uint32_t task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
|
||||||
|
while (1) {
|
||||||
|
if (lvgl_port_lock(-1)) {
|
||||||
|
task_delay_ms = lv_timer_handler();
|
||||||
|
lvgl_port_unlock();
|
||||||
|
}
|
||||||
|
if (task_delay_ms > LVGL_PORT_TASK_MAX_DELAY_MS) {
|
||||||
|
task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
|
||||||
|
} else if (task_delay_ms < LVGL_PORT_TASK_MIN_DELAY_MS) {
|
||||||
|
task_delay_ms = LVGL_PORT_TASK_MIN_DELAY_MS;
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t lvgl_port_init(esp_lcd_panel_handle_t lcd_handle, esp_lcd_touch_handle_t tp_handle, lvgl_port_interface_t interface)
|
||||||
|
{
|
||||||
|
lvgl_port_task_param_t lvgl_task_param = {
|
||||||
|
.lcd_handle = lcd_handle,
|
||||||
|
.tp_handle = tp_handle,
|
||||||
|
.is_init = false
|
||||||
|
};
|
||||||
|
|
||||||
|
lvgl_port_interface = interface;
|
||||||
|
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||||
|
switch (interface) {
|
||||||
|
#if SOC_LCDCAM_RGB_LCD_SUPPORTED
|
||||||
|
case LVGL_PORT_INTERFACE_RGB:
|
||||||
|
lvgl_get_lcd_frame_buffer = esp_lcd_rgb_panel_get_frame_buffer;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SOC_MIPI_DSI_SUPPORTED
|
||||||
|
case LVGL_PORT_INTERFACE_MIPI_DSI_DMA:
|
||||||
|
case LVGL_PORT_INTERFACE_MIPI_DSI_NO_DMA:
|
||||||
|
lvgl_get_lcd_frame_buffer = esp_lcd_dpi_panel_get_frame_buffer;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
default:
|
||||||
|
ESP_LOGE(TAG, "Invalid interface type");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
lvgl_mux = xSemaphoreCreateRecursiveMutex();
|
||||||
|
assert(lvgl_mux);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Create LVGL task");
|
||||||
|
BaseType_t core_id = (LVGL_PORT_TASK_CORE < 0) ? tskNO_AFFINITY : LVGL_PORT_TASK_CORE;
|
||||||
|
BaseType_t ret = xTaskCreatePinnedToCore(lvgl_port_task, "lvgl", LVGL_PORT_TASK_STACK_SIZE, &lvgl_task_param,
|
||||||
|
LVGL_PORT_TASK_PRIORITY, &lvgl_task_handle, core_id);
|
||||||
|
if (ret != pdPASS) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create LVGL task");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!lvgl_task_param.is_init) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lvgl_port_lock(int timeout_ms)
|
||||||
|
{
|
||||||
|
assert(lvgl_mux && "lvgl_port_init must be called first");
|
||||||
|
|
||||||
|
const TickType_t timeout_ticks = (timeout_ms < 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||||
|
return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lvgl_port_unlock(void)
|
||||||
|
{
|
||||||
|
assert(lvgl_mux && "lvgl_port_init must be called first");
|
||||||
|
xSemaphoreGiveRecursive(lvgl_mux);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lvgl_port_notify_lcd_vsync(void)
|
||||||
|
{
|
||||||
|
BaseType_t need_yield = pdFALSE;
|
||||||
|
#if LVGL_PORT_FULL_REFRESH && (LVGL_PORT_LCD_RGB_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0)
|
||||||
|
if (lvgl_port_rgb_next_buf != lvgl_port_rgb_last_buf) {
|
||||||
|
lvgl_port_flush_next_buf = lvgl_port_rgb_last_buf;
|
||||||
|
lvgl_port_rgb_last_buf = lvgl_port_rgb_next_buf;
|
||||||
|
}
|
||||||
|
#elif LVGL_PORT_AVOID_TEAR_ENABLE
|
||||||
|
// Notify that the current LCD frame buffer has been transmitted
|
||||||
|
if (lvgl_task_handle) {
|
||||||
|
xTaskNotifyFromISR(lvgl_task_handle, ULONG_MAX, eNoAction, &need_yield);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (lvgl_port_interface == LVGL_PORT_INTERFACE_MIPI_DSI_DMA) {
|
||||||
|
lv_display_t *disp = lv_disp_get_default();
|
||||||
|
lv_disp_flush_ready(disp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return (need_yield == pdTRUE);
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_lcd_types.h"
|
||||||
|
#include "src/touch/esp_lcd_touch.h"
|
||||||
|
#include "lvgl.h"
|
||||||
|
#include "pins_config.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LVGL port interface type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LVGL_PORT_INTERFACE_RGB,
|
||||||
|
LVGL_PORT_INTERFACE_MIPI_DSI_DMA,
|
||||||
|
LVGL_PORT_INTERFACE_MIPI_DSI_NO_DMA,
|
||||||
|
LVGL_PORT_INTERFACE_MAX,
|
||||||
|
} lvgl_port_interface_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LVGL related parameters, can be adjusted by users
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define LVGL_PORT_H_RES (800)
|
||||||
|
#define LVGL_PORT_V_RES (1280)
|
||||||
|
#define LVGL_PORT_TICK_PERIOD_MS (EXAMPLE_LVGL_PORT_TICK)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LVGL timer handle task related parameters, can be adjusted by users
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define LVGL_PORT_TASK_MAX_DELAY_MS (EXAMPLE_LVGL_PORT_TASK_MAX_DELAY_MS) // The maximum delay of the LVGL timer task, in milliseconds
|
||||||
|
#define LVGL_PORT_TASK_MIN_DELAY_MS (EXAMPLE_LVGL_PORT_TASK_MIN_DELAY_MS) // The minimum delay of the LVGL timer task, in milliseconds
|
||||||
|
#define LVGL_PORT_TASK_STACK_SIZE (EXAMPLE_LVGL_PORT_TASK_STACK_SIZE_KB * 1024) // The stack size of the LVGL timer task, in bytes
|
||||||
|
#define LVGL_PORT_TASK_PRIORITY (EXAMPLE_LVGL_PORT_TASK_PRIORITY) // The priority of the LVGL timer task
|
||||||
|
#define LVGL_PORT_TASK_CORE (EXAMPLE_LVGL_PORT_TASK_CORE) // The core of the LVGL timer task,
|
||||||
|
// `-1` means the don't specify the core
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* LVGL buffer related parameters, can be adjusted by users:
|
||||||
|
* (These parameters will be useless if the avoid tearing function is enabled)
|
||||||
|
*
|
||||||
|
* - Memory type for buffer allocation:
|
||||||
|
* - MALLOC_CAP_SPIRAM: Allocate LVGL buffer in PSRAM
|
||||||
|
* - MALLOC_CAP_INTERNAL: Allocate LVGL buffer in SRAM
|
||||||
|
* (The SRAM is faster than PSRAM, but the PSRAM has a larger capacity)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if CONFIG_EXAMPLE_LVGL_PORT_BUF_PSRAM
|
||||||
|
#define LVGL_PORT_BUFFER_MALLOC_CAPS (MALLOC_CAP_SPIRAM)
|
||||||
|
#elif CONFIG_EXAMPLE_LVGL_PORT_BUF_INTERNAL
|
||||||
|
#define LVGL_PORT_BUFFER_MALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||||
|
#endif
|
||||||
|
#define LVGL_PORT_BUFFER_HEIGHT (CONFIG_EXAMPLE_LVGL_PORT_BUF_HEIGHT)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Avoid tering related configurations, can be adjusted by users.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define LVGL_PORT_AVOID_TEAR_ENABLE (EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE) // Set to 1 to enable
|
||||||
|
#if LVGL_PORT_AVOID_TEAR_ENABLE
|
||||||
|
/**
|
||||||
|
* Set the avoid tearing mode:
|
||||||
|
* - 0: Disable avoid tearing function
|
||||||
|
* - 1: LCD double-buffer & LVGL full-refresh
|
||||||
|
* - 2: LCD triple-buffer & LVGL full-refresh
|
||||||
|
* - 3: LCD double-buffer & LVGL direct-mode (recommended)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define LVGL_PORT_AVOID_TEAR_MODE (EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the PPA rotation enable:
|
||||||
|
* - 0: Disable PPA rotation
|
||||||
|
* - 1: Enable PPA rotation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define LVGL_PORT_PPA_ROTATION_ENABLE (EXAMPLE_LVGL_PORT_PPA_ROTATION_ENABLE)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the rotation degree of the LCD panel when the avoid tearing function is enabled:
|
||||||
|
* - 0: 0 degree
|
||||||
|
* - 90: 90 degree
|
||||||
|
* - 180: 180 degree
|
||||||
|
* - 270: 270 degree
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define EXAMPLE_LVGL_PORT_ROTATION_DEGREE (EXAMPLE_LVGL_PORT_ROTATION_DEGREE_)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Below configurations are automatically set according to the above configurations, users do not need to modify them.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if LVGL_PORT_AVOID_TEAR_MODE == 1
|
||||||
|
#define LVGL_PORT_LCD_BUFFER_NUMS (2)
|
||||||
|
#define LVGL_PORT_FULL_REFRESH (1)
|
||||||
|
#elif LVGL_PORT_AVOID_TEAR_MODE == 2
|
||||||
|
#define LVGL_PORT_LCD_BUFFER_NUMS (3)
|
||||||
|
#define LVGL_PORT_FULL_REFRESH (1)
|
||||||
|
#elif LVGL_PORT_AVOID_TEAR_MODE == 3
|
||||||
|
#define LVGL_PORT_LCD_BUFFER_NUMS (2)
|
||||||
|
#define LVGL_PORT_DIRECT_MODE (1)
|
||||||
|
#endif /* LVGL_PORT_AVOID_TEAR_MODE */
|
||||||
|
|
||||||
|
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0
|
||||||
|
#define EXAMPLE_LVGL_PORT_ROTATION_0 (1)
|
||||||
|
#else
|
||||||
|
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 90
|
||||||
|
#define EXAMPLE_LVGL_PORT_ROTATION_90 (1)
|
||||||
|
#elif EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 180
|
||||||
|
#define EXAMPLE_LVGL_PORT_ROTATION_180 (1)
|
||||||
|
#elif EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 270
|
||||||
|
#define EXAMPLE_LVGL_PORT_ROTATION_270 (1)
|
||||||
|
#endif
|
||||||
|
#ifdef LVGL_PORT_LCD_BUFFER_NUMS
|
||||||
|
#undef LVGL_PORT_LCD_BUFFER_NUMS
|
||||||
|
#define LVGL_PORT_LCD_BUFFER_NUMS (3)
|
||||||
|
#endif
|
||||||
|
#endif /* EXAMPLE_LVGL_PORT_ROTATION_DEGREE */
|
||||||
|
#else
|
||||||
|
#define LVGL_PORT_LCD_BUFFER_NUMS (1)
|
||||||
|
#define LVGL_PORT_FULL_REFRESH (0)
|
||||||
|
#define LVGL_PORT_DIRECT_MODE (0)
|
||||||
|
#endif /* LVGL_PORT_AVOID_TEAR_ENABLE */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize LVGL port
|
||||||
|
*
|
||||||
|
* @param[in] lcd_handle: LCD panel handle
|
||||||
|
* @param[in] tp_handle: Touch panel handle
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Success
|
||||||
|
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||||
|
* - Others: Fail
|
||||||
|
*/
|
||||||
|
esp_err_t lvgl_port_init(esp_lcd_panel_handle_t lcd_handle, esp_lcd_touch_handle_t tp_handle, lvgl_port_interface_t interface);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Take LVGL mutex
|
||||||
|
*
|
||||||
|
* @param[in] timeout_ms: Timeout in [ms]. 0 will block indefinitely.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - true: Mutex was taken
|
||||||
|
* - false: Mutex was NOT taken
|
||||||
|
*/
|
||||||
|
bool lvgl_port_lock(int timeout_ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Give LVGL mutex
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void lvgl_port_unlock(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Notifies the LVGL task when the transmission of the RGB frame buffer is completed.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - true: The tasks need to be re-scheduled
|
||||||
|
* - false: The tasks don't need to be re-scheduled
|
||||||
|
*/
|
||||||
|
bool lvgl_port_notify_lcd_vsync(void);
|
||||||
|
|
||||||
|
void lvgl_sw_rotation_main(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
#include "driver/spi_master.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
#include "esp_ldo_regulator.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "esp_lcd_panel_ops.h"
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
#include "esp_cache.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
#include "esp_private/esp_cache_private.h"
|
||||||
|
#include "src/touch/esp_lcd_touch_gt911.h"
|
||||||
|
#include "src/lcd/esp_lcd_jd9365_10_1.h"
|
||||||
|
#include "lvgl_port_v9.h"
|
||||||
|
#include "demos/lv_demos.h"
|
||||||
|
#include "driver/ppa.h"
|
||||||
|
|
||||||
|
#define TAG "main"
|
||||||
|
|
||||||
|
|
||||||
|
#define BSP_MIPI_DSI_PHY_PWR_LDO_CHAN (3) // LDO_VO3 is connected to VDD_MIPI_DPHY
|
||||||
|
#define BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500)
|
||||||
|
|
||||||
|
#define BSP_LCD_DPI_BUFFER_NUMS (1)
|
||||||
|
|
||||||
|
#define BSP_LCD_H_RES (800)
|
||||||
|
#define BSP_LCD_V_RES (1280)
|
||||||
|
|
||||||
|
#define BSP_I2C_NUM (I2C_NUM_1)
|
||||||
|
#define BSP_I2C_SDA (GPIO_NUM_7)
|
||||||
|
#define BSP_I2C_SCL (GPIO_NUM_8)
|
||||||
|
|
||||||
|
#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC)
|
||||||
|
#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC)
|
||||||
|
|
||||||
|
#define BSP_LCD_RST (GPIO_NUM_NC)
|
||||||
|
#define BSP_LCD_BACKLIGHT (GPIO_NUM_NC)
|
||||||
|
|
||||||
|
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||||
|
|
||||||
|
IRAM_ATTR static bool mipi_dsi_lcd_on_vsync_event(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
|
||||||
|
{
|
||||||
|
return lvgl_port_notify_lcd_vsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static esp_err_t bsp_display_brightness_set(int brightness_percent)
|
||||||
|
{
|
||||||
|
if (brightness_percent > 100) {
|
||||||
|
brightness_percent = 100;
|
||||||
|
}
|
||||||
|
if (brightness_percent < 0) {
|
||||||
|
brightness_percent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t data = (uint8_t)(255 * brightness_percent * 0.01);
|
||||||
|
uint8_t chip_addr = 0x45;
|
||||||
|
|
||||||
|
uint8_t data_addr = 0x96;
|
||||||
|
uint8_t data_to_send[2] = {data_addr, data};
|
||||||
|
|
||||||
|
i2c_device_config_t i2c_dev_conf = {
|
||||||
|
.scl_speed_hz = 100 * 1000,
|
||||||
|
.device_address = chip_addr,
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c_master_dev_handle_t dev_handle = NULL;
|
||||||
|
if (i2c_master_bus_add_device(i2c_handle, &i2c_dev_conf, &dev_handle) != ESP_OK)
|
||||||
|
{
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t ret = i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
i2c_master_bus_rm_device(dev_handle);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_master_bus_rm_device(dev_handle);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void lvgl_sw_rotation_main(void)
|
||||||
|
{
|
||||||
|
i2c_master_bus_config_t i2c_bus_conf = {
|
||||||
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||||
|
.sda_io_num = BSP_I2C_SDA,
|
||||||
|
.scl_io_num = BSP_I2C_SCL,
|
||||||
|
.i2c_port = BSP_I2C_NUM,
|
||||||
|
};
|
||||||
|
i2c_new_master_bus(&i2c_bus_conf, &i2c_handle);
|
||||||
|
|
||||||
|
static esp_ldo_channel_handle_t phy_pwr_chan = NULL;
|
||||||
|
esp_ldo_channel_config_t ldo_cfg = {
|
||||||
|
.chan_id = BSP_MIPI_DSI_PHY_PWR_LDO_CHAN,
|
||||||
|
.voltage_mv = BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
|
||||||
|
};
|
||||||
|
esp_ldo_acquire_channel(&ldo_cfg, &phy_pwr_chan);
|
||||||
|
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
|
||||||
|
|
||||||
|
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
|
||||||
|
esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG();
|
||||||
|
|
||||||
|
esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
|
||||||
|
// we use DBI interface to send LCD commands and parameters
|
||||||
|
esp_lcd_panel_io_handle_t io = NULL;
|
||||||
|
esp_lcd_dbi_io_config_t dbi_config =JD9365_PANEL_IO_DBI_CONFIG();
|
||||||
|
|
||||||
|
esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io);
|
||||||
|
|
||||||
|
esp_lcd_panel_handle_t disp_panel = NULL;
|
||||||
|
esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
|
||||||
|
|
||||||
|
dpi_config.num_fbs = LVGL_PORT_LCD_BUFFER_NUMS;
|
||||||
|
|
||||||
|
jd9365_vendor_config_t vendor_config = {
|
||||||
|
.flags = {
|
||||||
|
.use_mipi_interface = 1,
|
||||||
|
},
|
||||||
|
.mipi_config = {
|
||||||
|
.dsi_bus = mipi_dsi_bus,
|
||||||
|
.dpi_config = &dpi_config,
|
||||||
|
.lane_num = 2,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
esp_lcd_panel_dev_config_t lcd_dev_config = {
|
||||||
|
.bits_per_pixel = 16,
|
||||||
|
.rgb_ele_order = ESP_LCD_COLOR_SPACE_RGB,
|
||||||
|
.reset_gpio_num = BSP_LCD_RST,
|
||||||
|
.vendor_config = &vendor_config,
|
||||||
|
};
|
||||||
|
esp_lcd_new_panel_jd9365(io, &lcd_dev_config, &disp_panel);
|
||||||
|
esp_lcd_panel_reset(disp_panel);
|
||||||
|
esp_lcd_panel_init(disp_panel);
|
||||||
|
|
||||||
|
esp_lcd_dpi_panel_event_callbacks_t cbs = {
|
||||||
|
#if LVGL_PORT_AVOID_TEAR_MODE
|
||||||
|
.on_refresh_done = mipi_dsi_lcd_on_vsync_event,
|
||||||
|
#else
|
||||||
|
.on_color_trans_done = mipi_dsi_lcd_on_vsync_event,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
esp_lcd_dpi_panel_register_event_callbacks(disp_panel, &cbs, NULL);
|
||||||
|
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
|
||||||
|
esp_lcd_touch_handle_t tp_handle;
|
||||||
|
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||||
|
tp_io_config.scl_speed_hz = 100000;
|
||||||
|
esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle);
|
||||||
|
const esp_lcd_touch_config_t tp_cfg = {
|
||||||
|
.x_max = BSP_LCD_H_RES,
|
||||||
|
.y_max = BSP_LCD_V_RES,
|
||||||
|
.rst_gpio_num = BSP_LCD_TOUCH_RST, // Shared with LCD reset
|
||||||
|
.int_gpio_num = BSP_LCD_TOUCH_INT,
|
||||||
|
.levels = {
|
||||||
|
.reset = 0,
|
||||||
|
.interrupt = 0,
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.swap_xy = 0,
|
||||||
|
.mirror_x = 0,
|
||||||
|
.mirror_y = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp_handle);
|
||||||
|
|
||||||
|
lvgl_port_interface_t interface = (dpi_config.flags.use_dma2d) ? LVGL_PORT_INTERFACE_MIPI_DSI_DMA : LVGL_PORT_INTERFACE_MIPI_DSI_NO_DMA;
|
||||||
|
ESP_LOGI(TAG,"interface is %d",interface);
|
||||||
|
ESP_ERROR_CHECK(lvgl_port_init(disp_panel, tp_handle, interface));
|
||||||
|
|
||||||
|
bsp_display_brightness_set(100);
|
||||||
|
|
||||||
|
if(lvgl_port_lock(-1))
|
||||||
|
{
|
||||||
|
// lv_demo_music();
|
||||||
|
// lv_demo_benchmark();
|
||||||
|
lv_demo_widgets();
|
||||||
|
|
||||||
|
lvgl_port_unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma GCC push_options
|
||||||
|
#pragma GCC optimize("O3")
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "lvgl.h"
|
||||||
|
#include "demos/lv_demos.h"
|
||||||
|
#include "pins_config.h"
|
||||||
|
#include "lvgl_port_v9.h"
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("ESP32P4 MIPI DSI LVGL");
|
||||||
|
|
||||||
|
lvgl_sw_rotation_main();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define EXAMPLE_LVGL_PORT_TASK_MAX_DELAY_MS 500 //range 2 to 2000
|
||||||
|
#define EXAMPLE_LVGL_PORT_TASK_MIN_DELAY_MS 5 //range 1 to 100
|
||||||
|
#define EXAMPLE_LVGL_PORT_TASK_PRIORITY 4
|
||||||
|
#define EXAMPLE_LVGL_PORT_TASK_STACK_SIZE_KB 6 //KB
|
||||||
|
#define EXAMPLE_LVGL_PORT_TASK_CORE -1 //range -1 to 1
|
||||||
|
#define EXAMPLE_LVGL_PORT_TICK 2 //ragne 1 to 100
|
||||||
|
|
||||||
|
#define EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE 1
|
||||||
|
|
||||||
|
#ifdef EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
|
||||||
|
#define EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE 3 //range 1 to 3
|
||||||
|
|
||||||
|
#define EXAMPLE_LVGL_PORT_ROTATION_DEGREE_ 90 // 0,90,180 or 270
|
||||||
|
#define EXAMPLE_LVGL_PORT_PPA_ROTATION_ENABLE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define LCD_H_RES 800
|
||||||
|
#define LCD_V_RES 1280
|
||||||
|
|
||||||
|
#define LCD_RST -1
|
||||||
|
#define LCD_LED -1
|
||||||
|
|
||||||
|
#define TP_I2C_SDA 7
|
||||||
|
#define TP_I2C_SCL 8
|
||||||
|
#define TP_RST -1
|
||||||
|
#define TP_INT -1
|
||||||
@@ -0,0 +1,676 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
|
#if SOC_MIPI_DSI_SUPPORTED
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_lcd_panel_commands.h"
|
||||||
|
#include "esp_lcd_panel_interface.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
#include "esp_lcd_panel_vendor.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_lcd_jd9365_10_1.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define JD9365_CMD_PAGE (0xE0)
|
||||||
|
#define JD9365_PAGE_USER (0x00)
|
||||||
|
|
||||||
|
#define JD9365_CMD_DSI_INT0 (0x80)
|
||||||
|
#define JD9365_DSI_1_LANE (0x00)
|
||||||
|
#define JD9365_DSI_2_LANE (0x01)
|
||||||
|
#define JD9365_DSI_3_LANE (0x10)
|
||||||
|
#define JD9365_DSI_4_LANE (0x11)
|
||||||
|
|
||||||
|
#define JD9365_CMD_GS_BIT (1 << 0)
|
||||||
|
#define JD9365_CMD_SS_BIT (1 << 1)
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
esp_lcd_panel_io_handle_t io;
|
||||||
|
int reset_gpio_num;
|
||||||
|
uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
|
||||||
|
uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
|
||||||
|
const jd9365_lcd_init_cmd_t *init_cmds;
|
||||||
|
uint16_t init_cmds_size;
|
||||||
|
uint8_t lane_num;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned int reset_level : 1;
|
||||||
|
} flags;
|
||||||
|
// To save the original functions of MIPI DPI panel
|
||||||
|
esp_err_t (*del)(esp_lcd_panel_t *panel);
|
||||||
|
esp_err_t (*init)(esp_lcd_panel_t *panel);
|
||||||
|
} jd9365_panel_t;
|
||||||
|
|
||||||
|
static const char *TAG = "jd9365";
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel);
|
||||||
|
static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel);
|
||||||
|
static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel);
|
||||||
|
static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
|
||||||
|
static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
|
||||||
|
static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
|
||||||
|
static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
|
||||||
|
static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off);
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_new_panel_jd9365(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||||
|
esp_lcd_panel_handle_t *ret_panel)
|
||||||
|
{
|
||||||
|
//ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_JD9365_10_1_VER_MAJOR, ESP_LCD_JD9365_10_1_VER_MINOR,
|
||||||
|
// ESP_LCD_JD9365_10_1_VER_PATCH);
|
||||||
|
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
|
||||||
|
jd9365_vendor_config_t *vendor_config = (jd9365_vendor_config_t *)panel_dev_config->vendor_config;
|
||||||
|
ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG,
|
||||||
|
"invalid vendor config");
|
||||||
|
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)calloc(1, sizeof(jd9365_panel_t));
|
||||||
|
ESP_RETURN_ON_FALSE(jd9365, ESP_ERR_NO_MEM, TAG, "no mem for jd9365 panel");
|
||||||
|
|
||||||
|
if (panel_dev_config->reset_gpio_num >= 0)
|
||||||
|
{
|
||||||
|
gpio_config_t io_conf = {
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (panel_dev_config->color_space)
|
||||||
|
{
|
||||||
|
case LCD_RGB_ELEMENT_ORDER_RGB:
|
||||||
|
jd9365->madctl_val = 0;
|
||||||
|
break;
|
||||||
|
case LCD_RGB_ELEMENT_ORDER_BGR:
|
||||||
|
jd9365->madctl_val |= LCD_CMD_BGR_BIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (panel_dev_config->bits_per_pixel)
|
||||||
|
{
|
||||||
|
case 16: // RGB565
|
||||||
|
jd9365->colmod_val = 0x55;
|
||||||
|
break;
|
||||||
|
case 18: // RGB666
|
||||||
|
jd9365->colmod_val = 0x66;
|
||||||
|
break;
|
||||||
|
case 24: // RGB888
|
||||||
|
jd9365->colmod_val = 0x77;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
jd9365->io = io;
|
||||||
|
jd9365->init_cmds = vendor_config->init_cmds;
|
||||||
|
jd9365->init_cmds_size = vendor_config->init_cmds_size;
|
||||||
|
jd9365->lane_num = vendor_config->mipi_config.lane_num;
|
||||||
|
jd9365->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
||||||
|
jd9365->flags.reset_level = panel_dev_config->flags.reset_active_high;
|
||||||
|
|
||||||
|
// i2c_config_t conf = {
|
||||||
|
// .mode = I2C_MODE_MASTER,
|
||||||
|
// .sda_io_num = 7,
|
||||||
|
// .sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
// .scl_io_num = 8,
|
||||||
|
// .scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
// .master.clk_speed = 100000,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// i2c_bus_handle_t i2c0_bus = i2c_bus_create(I2C_NUM_1, &conf);
|
||||||
|
// i2c_bus_device_handle_t i2c0_device1 = i2c_bus_device_create(i2c0_bus, 0x45, 0);
|
||||||
|
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||||
|
i2c_master_get_bus_handle(1,&i2c_handle);
|
||||||
|
|
||||||
|
uint8_t chip_addr = 0x45;
|
||||||
|
|
||||||
|
i2c_device_config_t i2c_dev_conf = {
|
||||||
|
.scl_speed_hz = 100 * 1000,
|
||||||
|
.device_address = chip_addr,
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c_master_dev_handle_t dev_handle = NULL;
|
||||||
|
i2c_master_bus_add_device(i2c_handle, &i2c_dev_conf, &dev_handle);
|
||||||
|
|
||||||
|
uint8_t data_to_send[2] = {0x95,0x11};
|
||||||
|
i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
|
||||||
|
|
||||||
|
// data_to_send[0] ={0x95,0x17};
|
||||||
|
data_to_send[0] = 0x95;
|
||||||
|
data_to_send[1] = 0x17;
|
||||||
|
i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
|
||||||
|
|
||||||
|
// data_to_send[2] ={0x96,0x00};
|
||||||
|
data_to_send[0] = 0x96;
|
||||||
|
data_to_send[1] = 0x00;
|
||||||
|
i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
|
||||||
|
|
||||||
|
// data_to_send[2] ={0x96,0xFF};
|
||||||
|
data_to_send[0] = 0x96;
|
||||||
|
data_to_send[1] = 0xFF;
|
||||||
|
i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
|
||||||
|
|
||||||
|
i2c_master_bus_rm_device(dev_handle);
|
||||||
|
// uint8_t data = 0x11;
|
||||||
|
// i2c_bus_write_bytes(i2c0_device1, 0x95, 1, &data);
|
||||||
|
// data = 0x17;
|
||||||
|
// i2c_bus_write_bytes(i2c0_device1, 0x95, 1, &data);
|
||||||
|
// data = 0x00;
|
||||||
|
// i2c_bus_write_bytes(i2c0_device1, 0x96, 1, &data);
|
||||||
|
// vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
// data = 0xFF;
|
||||||
|
// i2c_bus_write_bytes(i2c0_device1, 0x96, 1, &data);
|
||||||
|
|
||||||
|
// i2c_bus_device_delete(&i2c0_device1);
|
||||||
|
// i2c_bus_delete(&i2c0_bus);
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
|
||||||
|
// Create MIPI DPI panel
|
||||||
|
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG,
|
||||||
|
"create MIPI DPI panel failed");
|
||||||
|
ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle);
|
||||||
|
|
||||||
|
// Save the original functions of MIPI DPI panel
|
||||||
|
jd9365->del = panel_handle->del;
|
||||||
|
jd9365->init = panel_handle->init;
|
||||||
|
// Overwrite the functions of MIPI DPI panel
|
||||||
|
panel_handle->del = panel_jd9365_del;
|
||||||
|
panel_handle->init = panel_jd9365_init;
|
||||||
|
panel_handle->reset = panel_jd9365_reset;
|
||||||
|
panel_handle->mirror = panel_jd9365_mirror;
|
||||||
|
panel_handle->swap_xy = panel_jd9365_swap_xy;
|
||||||
|
panel_handle->set_gap = panel_jd9365_set_gap;
|
||||||
|
panel_handle->invert_color = panel_jd9365_invert_color;
|
||||||
|
panel_handle->disp_on_off = panel_jd9365_disp_on_off;
|
||||||
|
panel_handle->user_data = jd9365;
|
||||||
|
*ret_panel = panel_handle;
|
||||||
|
ESP_LOGD(TAG, "new jd9365 panel @%p", jd9365);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (jd9365)
|
||||||
|
{
|
||||||
|
if (panel_dev_config->reset_gpio_num >= 0)
|
||||||
|
{
|
||||||
|
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
||||||
|
}
|
||||||
|
free(jd9365);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const jd9365_lcd_init_cmd_t vendor_specific_init_default[] = {
|
||||||
|
// {cmd, { data }, data_size, delay_ms}
|
||||||
|
// {0xE0, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0xE0, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0xE1, (uint8_t[]){0x93}, 1, 0},
|
||||||
|
{0xE2, (uint8_t[]){0x65}, 1, 0},
|
||||||
|
{0xE3, (uint8_t[]){0xF8}, 1, 0},
|
||||||
|
{0x80, (uint8_t[]){0x01}, 1, 0},
|
||||||
|
|
||||||
|
{0xE0, (uint8_t[]){0x01}, 1, 0},
|
||||||
|
{0x00, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x01, (uint8_t[]){0x38}, 1, 0},
|
||||||
|
{0x03, (uint8_t[]){0x10}, 1, 0},
|
||||||
|
{0x04, (uint8_t[]){0x38}, 1, 0},
|
||||||
|
|
||||||
|
{0x0C, (uint8_t[]){0x74}, 1, 0},
|
||||||
|
|
||||||
|
{0x17, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x18, (uint8_t[]){0xAF}, 1, 0},
|
||||||
|
{0x19, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x1A, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x1B, (uint8_t[]){0xAF}, 1, 0},
|
||||||
|
{0x1C, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
|
||||||
|
{0x35, (uint8_t[]){0x26}, 1, 0},
|
||||||
|
|
||||||
|
{0x37, (uint8_t[]){0x09}, 1, 0},
|
||||||
|
|
||||||
|
{0x38, (uint8_t[]){0x04}, 1, 0},
|
||||||
|
{0x39, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x3A, (uint8_t[]){0x01}, 1, 0},
|
||||||
|
{0x3C, (uint8_t[]){0x78}, 1, 0},
|
||||||
|
{0x3D, (uint8_t[]){0xFF}, 1, 0},
|
||||||
|
{0x3E, (uint8_t[]){0xFF}, 1, 0},
|
||||||
|
{0x3F, (uint8_t[]){0x7F}, 1, 0},
|
||||||
|
|
||||||
|
{0x40, (uint8_t[]){0x06}, 1, 0},
|
||||||
|
{0x41, (uint8_t[]){0xA0}, 1, 0},
|
||||||
|
{0x42, (uint8_t[]){0x81}, 1, 0},
|
||||||
|
{0x43, (uint8_t[]){0x1E}, 1, 0},
|
||||||
|
{0x44, (uint8_t[]){0x0D}, 1, 0},
|
||||||
|
{0x45, (uint8_t[]){0x28}, 1, 0},
|
||||||
|
//{0x4A, (uint8_t[]){0x35}, 1, 0},//bist
|
||||||
|
|
||||||
|
{0x55, (uint8_t[]){0x02}, 1, 0},
|
||||||
|
{0x57, (uint8_t[]){0x69}, 1, 0},
|
||||||
|
{0x59, (uint8_t[]){0x0A}, 1, 0},
|
||||||
|
{0x5A, (uint8_t[]){0x2A}, 1, 0},
|
||||||
|
{0x5B, (uint8_t[]){0x17}, 1, 0},
|
||||||
|
|
||||||
|
{0x5D, (uint8_t[]){0x7F}, 1, 0},
|
||||||
|
{0x5E, (uint8_t[]){0x6A}, 1, 0},
|
||||||
|
{0x5F, (uint8_t[]){0x5B}, 1, 0},
|
||||||
|
{0x60, (uint8_t[]){0x4F}, 1, 0},
|
||||||
|
{0x61, (uint8_t[]){0x4A}, 1, 0},
|
||||||
|
{0x62, (uint8_t[]){0x3D}, 1, 0},
|
||||||
|
{0x63, (uint8_t[]){0x41}, 1, 0},
|
||||||
|
{0x64, (uint8_t[]){0x2A}, 1, 0},
|
||||||
|
{0x65, (uint8_t[]){0x44}, 1, 0},
|
||||||
|
{0x66, (uint8_t[]){0x43}, 1, 0},
|
||||||
|
{0x67, (uint8_t[]){0x44}, 1, 0},
|
||||||
|
{0x68, (uint8_t[]){0x62}, 1, 0},
|
||||||
|
{0x69, (uint8_t[]){0x52}, 1, 0},
|
||||||
|
{0x6A, (uint8_t[]){0x59}, 1, 0},
|
||||||
|
{0x6B, (uint8_t[]){0x4C}, 1, 0},
|
||||||
|
{0x6C, (uint8_t[]){0x48}, 1, 0},
|
||||||
|
{0x6D, (uint8_t[]){0x3A}, 1, 0},
|
||||||
|
{0x6E, (uint8_t[]){0x26}, 1, 0},
|
||||||
|
{0x6F, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x70, (uint8_t[]){0x7F}, 1, 0},
|
||||||
|
{0x71, (uint8_t[]){0x6A}, 1, 0},
|
||||||
|
{0x72, (uint8_t[]){0x5B}, 1, 0},
|
||||||
|
{0x73, (uint8_t[]){0x4F}, 1, 0},
|
||||||
|
{0x74, (uint8_t[]){0x4A}, 1, 0},
|
||||||
|
{0x75, (uint8_t[]){0x3D}, 1, 0},
|
||||||
|
{0x76, (uint8_t[]){0x41}, 1, 0},
|
||||||
|
{0x77, (uint8_t[]){0x2A}, 1, 0},
|
||||||
|
{0x78, (uint8_t[]){0x44}, 1, 0},
|
||||||
|
{0x79, (uint8_t[]){0x43}, 1, 0},
|
||||||
|
{0x7A, (uint8_t[]){0x44}, 1, 0},
|
||||||
|
{0x7B, (uint8_t[]){0x62}, 1, 0},
|
||||||
|
{0x7C, (uint8_t[]){0x52}, 1, 0},
|
||||||
|
{0x7D, (uint8_t[]){0x59}, 1, 0},
|
||||||
|
{0x7E, (uint8_t[]){0x4C}, 1, 0},
|
||||||
|
{0x7F, (uint8_t[]){0x48}, 1, 0},
|
||||||
|
{0x80, (uint8_t[]){0x3A}, 1, 0},
|
||||||
|
{0x81, (uint8_t[]){0x26}, 1, 0},
|
||||||
|
{0x82, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
|
||||||
|
{0xE0, (uint8_t[]){0x02}, 1, 0},
|
||||||
|
{0x00, (uint8_t[]){0x42}, 1, 0},
|
||||||
|
{0x01, (uint8_t[]){0x42}, 1, 0},
|
||||||
|
{0x02, (uint8_t[]){0x40}, 1, 0},
|
||||||
|
{0x03, (uint8_t[]){0x40}, 1, 0},
|
||||||
|
{0x04, (uint8_t[]){0x5E}, 1, 0},
|
||||||
|
{0x05, (uint8_t[]){0x5E}, 1, 0},
|
||||||
|
{0x06, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
{0x07, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
{0x08, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
{0x09, (uint8_t[]){0x57}, 1, 0},
|
||||||
|
{0x0A, (uint8_t[]){0x57}, 1, 0},
|
||||||
|
{0x0B, (uint8_t[]){0x77}, 1, 0},
|
||||||
|
{0x0C, (uint8_t[]){0x77}, 1, 0},
|
||||||
|
{0x0D, (uint8_t[]){0x47}, 1, 0},
|
||||||
|
{0x0E, (uint8_t[]){0x47}, 1, 0},
|
||||||
|
{0x0F, (uint8_t[]){0x45}, 1, 0},
|
||||||
|
{0x10, (uint8_t[]){0x45}, 1, 0},
|
||||||
|
{0x11, (uint8_t[]){0x4B}, 1, 0},
|
||||||
|
{0x12, (uint8_t[]){0x4B}, 1, 0},
|
||||||
|
{0x13, (uint8_t[]){0x49}, 1, 0},
|
||||||
|
{0x14, (uint8_t[]){0x49}, 1, 0},
|
||||||
|
{0x15, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
|
||||||
|
{0x16, (uint8_t[]){0x41}, 1, 0},
|
||||||
|
{0x17, (uint8_t[]){0x41}, 1, 0},
|
||||||
|
{0x18, (uint8_t[]){0x40}, 1, 0},
|
||||||
|
{0x19, (uint8_t[]){0x40}, 1, 0},
|
||||||
|
{0x1A, (uint8_t[]){0x5E}, 1, 0},
|
||||||
|
{0x1B, (uint8_t[]){0x5E}, 1, 0},
|
||||||
|
{0x1C, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
{0x1D, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
{0x1E, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
{0x1F, (uint8_t[]){0x57}, 1, 0},
|
||||||
|
{0x20, (uint8_t[]){0x57}, 1, 0},
|
||||||
|
{0x21, (uint8_t[]){0x77}, 1, 0},
|
||||||
|
{0x22, (uint8_t[]){0x77}, 1, 0},
|
||||||
|
{0x23, (uint8_t[]){0x46}, 1, 0},
|
||||||
|
{0x24, (uint8_t[]){0x46}, 1, 0},
|
||||||
|
{0x25, (uint8_t[]){0x44}, 1, 0},
|
||||||
|
{0x26, (uint8_t[]){0x44}, 1, 0},
|
||||||
|
{0x27, (uint8_t[]){0x4A}, 1, 0},
|
||||||
|
{0x28, (uint8_t[]){0x4A}, 1, 0},
|
||||||
|
{0x29, (uint8_t[]){0x48}, 1, 0},
|
||||||
|
{0x2A, (uint8_t[]){0x48}, 1, 0},
|
||||||
|
{0x2B, (uint8_t[]){0x5F}, 1, 0},
|
||||||
|
|
||||||
|
{0x2C, (uint8_t[]){0x01}, 1, 0},
|
||||||
|
{0x2D, (uint8_t[]){0x01}, 1, 0},
|
||||||
|
{0x2E, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x2F, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x30, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
{0x31, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
{0x32, (uint8_t[]){0x1E}, 1, 0},
|
||||||
|
{0x33, (uint8_t[]){0x1E}, 1, 0},
|
||||||
|
{0x34, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
{0x35, (uint8_t[]){0x17}, 1, 0},
|
||||||
|
{0x36, (uint8_t[]){0x17}, 1, 0},
|
||||||
|
{0x37, (uint8_t[]){0x37}, 1, 0},
|
||||||
|
{0x38, (uint8_t[]){0x37}, 1, 0},
|
||||||
|
{0x39, (uint8_t[]){0x08}, 1, 0},
|
||||||
|
{0x3A, (uint8_t[]){0x08}, 1, 0},
|
||||||
|
{0x3B, (uint8_t[]){0x0A}, 1, 0},
|
||||||
|
{0x3C, (uint8_t[]){0x0A}, 1, 0},
|
||||||
|
{0x3D, (uint8_t[]){0x04}, 1, 0},
|
||||||
|
{0x3E, (uint8_t[]){0x04}, 1, 0},
|
||||||
|
{0x3F, (uint8_t[]){0x06}, 1, 0},
|
||||||
|
{0x40, (uint8_t[]){0x06}, 1, 0},
|
||||||
|
{0x41, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
|
||||||
|
{0x42, (uint8_t[]){0x02}, 1, 0},
|
||||||
|
{0x43, (uint8_t[]){0x02}, 1, 0},
|
||||||
|
{0x44, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x45, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x46, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
{0x47, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
{0x48, (uint8_t[]){0x1E}, 1, 0},
|
||||||
|
{0x49, (uint8_t[]){0x1E}, 1, 0},
|
||||||
|
{0x4A, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
{0x4B, (uint8_t[]){0x17}, 1, 0},
|
||||||
|
{0x4C, (uint8_t[]){0x17}, 1, 0},
|
||||||
|
{0x4D, (uint8_t[]){0x37}, 1, 0},
|
||||||
|
{0x4E, (uint8_t[]){0x37}, 1, 0},
|
||||||
|
{0x4F, (uint8_t[]){0x09}, 1, 0},
|
||||||
|
{0x50, (uint8_t[]){0x09}, 1, 0},
|
||||||
|
{0x51, (uint8_t[]){0x0B}, 1, 0},
|
||||||
|
{0x52, (uint8_t[]){0x0B}, 1, 0},
|
||||||
|
{0x53, (uint8_t[]){0x05}, 1, 0},
|
||||||
|
{0x54, (uint8_t[]){0x05}, 1, 0},
|
||||||
|
{0x55, (uint8_t[]){0x07}, 1, 0},
|
||||||
|
{0x56, (uint8_t[]){0x07}, 1, 0},
|
||||||
|
{0x57, (uint8_t[]){0x1F}, 1, 0},
|
||||||
|
|
||||||
|
{0x58, (uint8_t[]){0x40}, 1, 0},
|
||||||
|
{0x5B, (uint8_t[]){0x30}, 1, 0},
|
||||||
|
{0x5C, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x5D, (uint8_t[]){0x34}, 1, 0},
|
||||||
|
{0x5E, (uint8_t[]){0x05}, 1, 0},
|
||||||
|
{0x5F, (uint8_t[]){0x02}, 1, 0},
|
||||||
|
{0x63, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x64, (uint8_t[]){0x6A}, 1, 0},
|
||||||
|
{0x67, (uint8_t[]){0x73}, 1, 0},
|
||||||
|
{0x68, (uint8_t[]){0x07}, 1, 0},
|
||||||
|
{0x69, (uint8_t[]){0x08}, 1, 0},
|
||||||
|
{0x6A, (uint8_t[]){0x6A}, 1, 0},
|
||||||
|
{0x6B, (uint8_t[]){0x08}, 1, 0},
|
||||||
|
|
||||||
|
{0x6C, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x6D, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x6E, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
{0x6F, (uint8_t[]){0x88}, 1, 0},
|
||||||
|
|
||||||
|
{0x75, (uint8_t[]){0xFF}, 1, 0},
|
||||||
|
{0x77, (uint8_t[]){0xDD}, 1, 0},
|
||||||
|
{0x78, (uint8_t[]){0x2C}, 1, 0},
|
||||||
|
{0x79, (uint8_t[]){0x15}, 1, 0},
|
||||||
|
{0x7A, (uint8_t[]){0x17}, 1, 0},
|
||||||
|
{0x7D, (uint8_t[]){0x14}, 1, 0},
|
||||||
|
{0x7E, (uint8_t[]){0x82}, 1, 0},
|
||||||
|
|
||||||
|
{0xE0, (uint8_t[]){0x04}, 1, 0},
|
||||||
|
{0x00, (uint8_t[]){0x0E}, 1, 0},
|
||||||
|
{0x02, (uint8_t[]){0xB3}, 1, 0},
|
||||||
|
{0x09, (uint8_t[]){0x61}, 1, 0},
|
||||||
|
{0x0E, (uint8_t[]){0x48}, 1, 0},
|
||||||
|
{0x37, (uint8_t[]){0x58}, 1, 0}, // 全志
|
||||||
|
{0x2B, (uint8_t[]){0x0F}, 1, 0}, // 全志
|
||||||
|
|
||||||
|
{0xE0, (uint8_t[]){0x00}, 1, 0},
|
||||||
|
|
||||||
|
{0xE6, (uint8_t[]){0x02}, 1, 0},
|
||||||
|
{0xE7, (uint8_t[]){0x0C}, 1, 0},
|
||||||
|
|
||||||
|
{0x11, (uint8_t[]){0x00}, 1, 120},
|
||||||
|
|
||||||
|
{0x29, (uint8_t[]){0x00}, 1, 20},
|
||||||
|
};
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel)
|
||||||
|
{
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
|
||||||
|
|
||||||
|
if (jd9365->reset_gpio_num >= 0)
|
||||||
|
{
|
||||||
|
gpio_reset_pin(jd9365->reset_gpio_num);
|
||||||
|
}
|
||||||
|
// Delete MIPI DPI panel
|
||||||
|
jd9365->del(panel);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "del jd9365 panel @%p", jd9365);
|
||||||
|
free(jd9365);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel)
|
||||||
|
{
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
|
||||||
|
esp_lcd_panel_io_handle_t io = jd9365->io;
|
||||||
|
const jd9365_lcd_init_cmd_t *init_cmds = NULL;
|
||||||
|
uint16_t init_cmds_size = 0;
|
||||||
|
uint8_t lane_command = JD9365_DSI_2_LANE;
|
||||||
|
bool is_user_set = true;
|
||||||
|
bool is_cmd_overwritten = false;
|
||||||
|
|
||||||
|
switch (jd9365->lane_num)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
lane_command = JD9365_DSI_1_LANE;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
lane_command = JD9365_DSI_2_LANE;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
lane_command = JD9365_DSI_3_LANE;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
lane_command = JD9365_DSI_4_LANE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGE(TAG, "Invalid lane number %d", jd9365->lane_num);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ID[3];
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed");
|
||||||
|
ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]);
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_PAGE, (uint8_t[]){JD9365_PAGE_USER}, 1), TAG, "send command failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){
|
||||||
|
jd9365->madctl_val,
|
||||||
|
},
|
||||||
|
1),
|
||||||
|
TAG, "send command failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]){
|
||||||
|
jd9365->colmod_val,
|
||||||
|
},
|
||||||
|
1),
|
||||||
|
TAG, "send command failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_DSI_INT0, (uint8_t[]){
|
||||||
|
lane_command,
|
||||||
|
},
|
||||||
|
1),
|
||||||
|
TAG, "send command failed");
|
||||||
|
|
||||||
|
// vendor specific initialization, it can be different between manufacturers
|
||||||
|
// should consult the LCD supplier for initialization sequence code
|
||||||
|
if (jd9365->init_cmds)
|
||||||
|
{
|
||||||
|
init_cmds = jd9365->init_cmds;
|
||||||
|
init_cmds_size = jd9365->init_cmds_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
init_cmds = vendor_specific_init_default;
|
||||||
|
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(jd9365_lcd_init_cmd_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < init_cmds_size; i++)
|
||||||
|
{
|
||||||
|
// Check if the command has been used or conflicts with the internal
|
||||||
|
if (is_user_set && (init_cmds[i].data_bytes > 0))
|
||||||
|
{
|
||||||
|
switch (init_cmds[i].cmd)
|
||||||
|
{
|
||||||
|
case LCD_CMD_MADCTL:
|
||||||
|
is_cmd_overwritten = true;
|
||||||
|
jd9365->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||||
|
break;
|
||||||
|
case LCD_CMD_COLMOD:
|
||||||
|
is_cmd_overwritten = true;
|
||||||
|
jd9365->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
is_cmd_overwritten = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_cmd_overwritten)
|
||||||
|
{
|
||||||
|
is_cmd_overwritten = false;
|
||||||
|
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
|
||||||
|
init_cmds[i].cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send command
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
|
||||||
|
|
||||||
|
// Check if the current cmd is the "page set" cmd
|
||||||
|
if ((init_cmds[i].cmd == JD9365_CMD_PAGE) && (init_cmds[i].data_bytes > 0))
|
||||||
|
{
|
||||||
|
is_user_set = (((uint8_t *)init_cmds[i].data)[0] == JD9365_PAGE_USER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "send init commands success");
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(jd9365->init(panel), TAG, "init MIPI DPI panel failed");
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel)
|
||||||
|
{
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
|
||||||
|
esp_lcd_panel_io_handle_t io = jd9365->io;
|
||||||
|
|
||||||
|
// Perform hardware reset
|
||||||
|
if (jd9365->reset_gpio_num >= 0)
|
||||||
|
{
|
||||||
|
gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(5));
|
||||||
|
gpio_set_level(jd9365->reset_gpio_num, jd9365->flags.reset_level);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(120));
|
||||||
|
}
|
||||||
|
else if (io)
|
||||||
|
{ // Perform software reset
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(120));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
|
||||||
|
{
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
|
||||||
|
esp_lcd_panel_io_handle_t io = jd9365->io;
|
||||||
|
uint8_t command = 0;
|
||||||
|
|
||||||
|
ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
|
||||||
|
|
||||||
|
if (invert_color_data)
|
||||||
|
{
|
||||||
|
command = LCD_CMD_INVON;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
command = LCD_CMD_INVOFF;
|
||||||
|
}
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||||
|
{
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
|
||||||
|
esp_lcd_panel_io_handle_t io = jd9365->io;
|
||||||
|
uint8_t madctl_val = jd9365->madctl_val;
|
||||||
|
|
||||||
|
ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
|
||||||
|
|
||||||
|
// Control mirror through LCD command
|
||||||
|
if (mirror_x)
|
||||||
|
{
|
||||||
|
madctl_val |= JD9365_CMD_GS_BIT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
madctl_val &= ~JD9365_CMD_GS_BIT;
|
||||||
|
}
|
||||||
|
if (mirror_y)
|
||||||
|
{
|
||||||
|
madctl_val |= JD9365_CMD_SS_BIT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
madctl_val &= ~JD9365_CMD_SS_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){madctl_val}, 1), TAG, "send command failed");
|
||||||
|
jd9365->madctl_val = madctl_val;
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "swap_xy is not supported by this panel");
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "set_gap is not supported by this panel");
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
||||||
|
{
|
||||||
|
jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data;
|
||||||
|
esp_lcd_panel_io_handle_t io = jd9365->io;
|
||||||
|
int command = 0;
|
||||||
|
|
||||||
|
if (on_off)
|
||||||
|
{
|
||||||
|
command = LCD_CMD_DISPON;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
command = LCD_CMD_DISPOFF;
|
||||||
|
}
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
|
#if SOC_MIPI_DSI_SUPPORTED
|
||||||
|
#include "esp_lcd_panel_vendor.h"
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LCD panel initialization commands.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int cmd; /*<! The specific LCD command */
|
||||||
|
const void *data; /*<! Buffer that holds the command specific data */
|
||||||
|
size_t data_bytes; /*<! Size of `data` in memory, in bytes */
|
||||||
|
unsigned int delay_ms; /*<! Delay in milliseconds after this command */
|
||||||
|
} jd9365_lcd_init_cmd_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LCD panel vendor configuration.
|
||||||
|
*
|
||||||
|
* @note This structure needs to be passed to the `vendor_config` field in `esp_lcd_panel_dev_config_t`.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
const jd9365_lcd_init_cmd_t *init_cmds; /*!< Pointer to initialization commands array. Set to NULL if using default commands.
|
||||||
|
* The array should be declared as `static const` and positioned outside the function.
|
||||||
|
* Please refer to `vendor_specific_init_default` in source file.
|
||||||
|
*/
|
||||||
|
uint16_t init_cmds_size; /*<! Number of commands in above array */
|
||||||
|
struct {
|
||||||
|
esp_lcd_dsi_bus_handle_t dsi_bus; /*!< MIPI-DSI bus configuration */
|
||||||
|
const esp_lcd_dpi_panel_config_t *dpi_config; /*!< MIPI-DPI panel configuration */
|
||||||
|
uint8_t lane_num; /*!< Number of MIPI-DSI lanes */
|
||||||
|
} mipi_config;
|
||||||
|
struct {
|
||||||
|
unsigned int use_mipi_interface: 1; /*<! Set to 1 if using MIPI interface, default is RGB interface */
|
||||||
|
unsigned int mirror_by_cmd: 1; /*<! The `mirror()` function will be implemented by LCD command if set to 1. This flag is only valid for the RGB interface.
|
||||||
|
* Otherwise, the function will be implemented by software.
|
||||||
|
*/
|
||||||
|
|
||||||
|
union {
|
||||||
|
unsigned int auto_del_panel_io: 1;
|
||||||
|
unsigned int enable_io_multiplex: 1;
|
||||||
|
}; /*<! Delete the panel IO instance automatically if set to 1. All `*_by_cmd` flags will be invalid.
|
||||||
|
* If the panel IO pins are sharing other pins of the RGB interface to save GPIOs,
|
||||||
|
* Please set it to 1 to release the panel IO and its pins (except CS signal).
|
||||||
|
* This flag is only valid for the RGB interface.
|
||||||
|
*/
|
||||||
|
} flags;
|
||||||
|
} jd9365_vendor_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create LCD panel for model JD9365
|
||||||
|
*
|
||||||
|
* @note Vendor specific initialization can be different between manufacturers, should consult the LCD supplier for initialization sequence code.
|
||||||
|
*
|
||||||
|
* @param[in] io LCD panel IO handle
|
||||||
|
* @param[in] panel_dev_config General panel device configuration
|
||||||
|
* @param[out] ret_panel Returned LCD panel handle
|
||||||
|
* @return
|
||||||
|
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - Otherwise on fail
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_new_panel_jd9365(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||||
|
esp_lcd_panel_handle_t *ret_panel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MIPI-DSI bus configuration structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define JD9365_PANEL_BUS_DSI_2CH_CONFIG() \
|
||||||
|
{ \
|
||||||
|
.bus_id = 0, \
|
||||||
|
.num_data_lanes = 2, \
|
||||||
|
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, \
|
||||||
|
.lane_bit_rate_mbps = 1500, \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MIPI-DBI panel IO configuration structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define JD9365_PANEL_IO_DBI_CONFIG() \
|
||||||
|
{ \
|
||||||
|
.virtual_channel = 0, \
|
||||||
|
.lcd_cmd_bits = 8, \
|
||||||
|
.lcd_param_bits = 8, \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MIPI DPI configuration structure
|
||||||
|
*
|
||||||
|
* @note refresh_rate = (dpi_clock_freq_mhz * 1000000) / (h_res + hsync_pulse_width + hsync_back_porch + hsync_front_porch)
|
||||||
|
* / (v_res + vsync_pulse_width + vsync_back_porch + vsync_front_porch)
|
||||||
|
*
|
||||||
|
* @param[in] px_format Pixel format of the panel
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(px_format) \
|
||||||
|
{ \
|
||||||
|
.virtual_channel = 0, \
|
||||||
|
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, \
|
||||||
|
.dpi_clock_freq_mhz = 80, \
|
||||||
|
.pixel_format = px_format, \
|
||||||
|
.num_fbs = 1, \
|
||||||
|
.video_timing = { \
|
||||||
|
.h_size = 800, \
|
||||||
|
.v_size = 1280, \
|
||||||
|
.hsync_pulse_width = 20, \
|
||||||
|
.hsync_back_porch = 20, \
|
||||||
|
.hsync_front_porch = 40, \
|
||||||
|
.vsync_pulse_width = 4, \
|
||||||
|
.vsync_back_porch = 10, \
|
||||||
|
.vsync_front_porch = 30, \
|
||||||
|
}, \
|
||||||
|
.flags = { \
|
||||||
|
.use_dma2d = true, \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "esp_lcd_panel_ops.h"
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "esp_ldo_regulator.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#include "esp_lcd_jd9365_10_1.h"
|
||||||
|
#include "jd9365_lcd.h"
|
||||||
|
|
||||||
|
#define LCD_H_RES 800
|
||||||
|
#define LCD_V_RES 1280
|
||||||
|
|
||||||
|
#define MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565)
|
||||||
|
#define LCD_BIT_PER_PIXEL (16)
|
||||||
|
|
||||||
|
// “VDD_MIPI_DPHY”应供电 2.5V,可从内部 LDO 稳压器或外部 LDO 芯片获取电源
|
||||||
|
#define EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN 3 // LDO_VO3 连接至 VDD_MIPI_DPHY
|
||||||
|
#define EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV 2500
|
||||||
|
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 100
|
||||||
|
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL (0)
|
||||||
|
#define EXAMPLE_PIN_NUM_BK_LIGHT GPIO_NUM_NC
|
||||||
|
|
||||||
|
static const char *TAG = "example";
|
||||||
|
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||||
|
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||||
|
|
||||||
|
jd9365_lcd::jd9365_lcd(int8_t lcd_rst)
|
||||||
|
{
|
||||||
|
_lcd_rst = lcd_rst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::example_bsp_enable_dsi_phy_power()
|
||||||
|
{
|
||||||
|
// 打开 MIPI DSI PHY 的电源,使其从“无电”状态进入“关机”状态
|
||||||
|
esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
|
||||||
|
#ifdef EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN
|
||||||
|
esp_ldo_channel_config_t ldo_mipi_phy_config = {
|
||||||
|
.chan_id = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN,
|
||||||
|
.voltage_mv = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
|
||||||
|
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::example_bsp_init_lcd_backlight()
|
||||||
|
{
|
||||||
|
// #if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
|
||||||
|
// gpio_config_t bk_gpio_config = {
|
||||||
|
// .pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT,
|
||||||
|
// .mode = GPIO_MODE_OUTPUT
|
||||||
|
// };
|
||||||
|
// ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::example_bsp_set_lcd_backlight(uint32_t level)
|
||||||
|
{
|
||||||
|
// #if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
|
||||||
|
// gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, level);
|
||||||
|
// #else
|
||||||
|
if (level > 100) {
|
||||||
|
level = 100;
|
||||||
|
}
|
||||||
|
if (level < 0) {
|
||||||
|
level = 0;
|
||||||
|
}
|
||||||
|
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||||
|
i2c_master_get_bus_handle(1,&i2c_handle);
|
||||||
|
|
||||||
|
uint8_t data = (uint8_t)(255 * level * 0.01);
|
||||||
|
uint8_t chip_addr = 0x45;
|
||||||
|
|
||||||
|
uint8_t data_addr = 0x96;
|
||||||
|
uint8_t data_to_send[2] = {data_addr, data};
|
||||||
|
|
||||||
|
|
||||||
|
i2c_device_config_t i2c_dev_conf = {
|
||||||
|
.device_address = chip_addr,
|
||||||
|
.scl_speed_hz = 100 * 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c_master_dev_handle_t dev_handle = NULL;
|
||||||
|
if (i2c_master_bus_add_device(i2c_handle, &i2c_dev_conf, &dev_handle) != ESP_OK)
|
||||||
|
{
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t ret = i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
i2c_master_bus_rm_device(dev_handle);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_master_bus_rm_device(dev_handle);
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::begin()
|
||||||
|
{
|
||||||
|
example_bsp_enable_dsi_phy_power();
|
||||||
|
example_bsp_init_lcd_backlight();
|
||||||
|
// example_bsp_set_lcd_backlight(EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);
|
||||||
|
|
||||||
|
// 首先创建 MIPI DSI 总线,它还将初始化 DSI PHY
|
||||||
|
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
|
||||||
|
esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG();
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
|
||||||
|
// 我们使用DBI接口发送LCD命令和参数
|
||||||
|
esp_lcd_dbi_io_config_t dbi_config = JD9365_PANEL_IO_DBI_CONFIG();
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io_handle));
|
||||||
|
|
||||||
|
// 创建JD9365控制面板
|
||||||
|
esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(MIPI_DPI_PX_FORMAT);
|
||||||
|
|
||||||
|
jd9365_vendor_config_t vendor_config = {
|
||||||
|
.mipi_config = {
|
||||||
|
.dsi_bus = mipi_dsi_bus,
|
||||||
|
.dpi_config = &dpi_config,
|
||||||
|
.lane_num = 2,
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.use_mipi_interface = 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const esp_lcd_panel_dev_config_t panel_config = {
|
||||||
|
.reset_gpio_num = _lcd_rst,
|
||||||
|
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
|
||||||
|
.bits_per_pixel = LCD_BIT_PER_PIXEL,
|
||||||
|
.vendor_config = &vendor_config,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_new_panel_jd9365(io_handle, &panel_config, &panel_handle));
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
|
||||||
|
|
||||||
|
// esp_lcd_dpi_panel_event_callbacks_t cbs = {0};
|
||||||
|
// if (dsi_cfg->flags.avoid_tearing) {
|
||||||
|
// cbs.on_refresh_done = lvgl_port_flush_dpi_vsync_ready_callback;
|
||||||
|
// } else {
|
||||||
|
// cbs.on_color_trans_done = lvgl_port_flush_dpi_panel_ready_callback;
|
||||||
|
// }
|
||||||
|
// /* Register done callback */
|
||||||
|
// esp_lcd_dpi_panel_register_event_callbacks(disp_ctx->panel_handle, &cbs, &disp_ctx->disp_drv);
|
||||||
|
|
||||||
|
// 打开背光
|
||||||
|
example_bsp_set_lcd_backlight(EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::lcd_draw_bitmap(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint16_t *color_data)
|
||||||
|
{
|
||||||
|
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, color_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::draw16bitbergbbitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *color_data)
|
||||||
|
{
|
||||||
|
uint16_t x_start = x;
|
||||||
|
uint16_t y_start = y;
|
||||||
|
uint16_t x_end = w + x;
|
||||||
|
uint16_t y_end = h + y;
|
||||||
|
|
||||||
|
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, color_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::fillScreen(uint16_t color)
|
||||||
|
{
|
||||||
|
uint16_t *color_data = (uint16_t *)heap_caps_malloc(480 * 272 * 2, MALLOC_CAP_INTERNAL);
|
||||||
|
memset(color_data, color, 480 * 272 * 2);
|
||||||
|
draw16bitbergbbitmap(0, 0, 480, 272, color_data);
|
||||||
|
free(color_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::te_on()
|
||||||
|
{
|
||||||
|
esp_lcd_panel_io_tx_param(io_handle, 0x35,new (uint8_t[]){0x00}, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::te_off()
|
||||||
|
{
|
||||||
|
esp_lcd_panel_io_tx_param(io_handle, 0x34,new (uint8_t[]){0x00}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t jd9365_lcd::width()
|
||||||
|
{
|
||||||
|
return LCD_H_RES;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t jd9365_lcd::height()
|
||||||
|
{
|
||||||
|
return LCD_V_RES;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jd9365_lcd::get_handle(bsp_lcd_handles_t *ret_handles)
|
||||||
|
{
|
||||||
|
ret_handles->io = io_handle;
|
||||||
|
ret_handles->mipi_dsi_bus = NULL;
|
||||||
|
ret_handles->panel = panel_handle;
|
||||||
|
ret_handles->control = NULL;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef _JD9165_LCD_H
|
||||||
|
#define _JD9165_LCD_H
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "esp_lcd_types.h"
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_lcd_dsi_bus_handle_t mipi_dsi_bus; /*!< MIPI DSI bus handle */
|
||||||
|
esp_lcd_panel_io_handle_t io; /*!< ESP LCD IO handle */
|
||||||
|
esp_lcd_panel_handle_t panel; /*!< ESP LCD panel (color) handle */
|
||||||
|
esp_lcd_panel_handle_t control; /*!< ESP LCD panel (control) handle */
|
||||||
|
} bsp_lcd_handles_t;
|
||||||
|
|
||||||
|
class jd9365_lcd
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
jd9365_lcd(int8_t lcd_rst);
|
||||||
|
|
||||||
|
void begin();
|
||||||
|
void example_bsp_enable_dsi_phy_power();
|
||||||
|
void example_bsp_init_lcd_backlight();
|
||||||
|
void example_bsp_set_lcd_backlight(uint32_t level);
|
||||||
|
void lcd_draw_bitmap(uint16_t x_start, uint16_t y_start,
|
||||||
|
uint16_t x_end, uint16_t y_end, uint16_t *color_data);
|
||||||
|
void draw16bitbergbbitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *color_data);
|
||||||
|
void fillScreen(uint16_t color);
|
||||||
|
void te_on();
|
||||||
|
void te_off();
|
||||||
|
uint16_t width();
|
||||||
|
uint16_t height();
|
||||||
|
void get_handle(bsp_lcd_handles_t *ret_handles);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int8_t _lcd_rst;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_lcd_touch.h"
|
||||||
|
|
||||||
|
static const char *TAG = "TP";
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Function definitions
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Local variables
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Public API functions
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_read_data(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(tp->read_data != NULL);
|
||||||
|
|
||||||
|
return tp->read_data(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool esp_lcd_touch_get_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
|
||||||
|
{
|
||||||
|
bool touched = false;
|
||||||
|
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(x != NULL);
|
||||||
|
assert(y != NULL);
|
||||||
|
assert(tp->get_xy != NULL);
|
||||||
|
|
||||||
|
touched = tp->get_xy(tp, x, y, strength, point_num, max_point_num);
|
||||||
|
if (!touched) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process coordinates by user */
|
||||||
|
if (tp->config.process_coordinates != NULL) {
|
||||||
|
tp->config.process_coordinates(tp, x, y, strength, point_num, max_point_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Software coordinates adjustment needed */
|
||||||
|
bool sw_adj_needed = ((tp->config.flags.mirror_x && (tp->set_mirror_x == NULL)) ||
|
||||||
|
(tp->config.flags.mirror_y && (tp->set_mirror_y == NULL)) ||
|
||||||
|
(tp->config.flags.swap_xy && (tp->set_swap_xy == NULL)));
|
||||||
|
|
||||||
|
/* Adjust all coordinates */
|
||||||
|
for (int i = 0; (sw_adj_needed && i < *point_num); i++) {
|
||||||
|
|
||||||
|
/* Mirror X coordinates (if not supported by HW) */
|
||||||
|
if (tp->config.flags.mirror_x && tp->set_mirror_x == NULL) {
|
||||||
|
x[i] = tp->config.x_max - x[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mirror Y coordinates (if not supported by HW) */
|
||||||
|
if (tp->config.flags.mirror_y && tp->set_mirror_y == NULL) {
|
||||||
|
y[i] = tp->config.y_max - y[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Swap X and Y coordinates (if not supported by HW) */
|
||||||
|
if (tp->config.flags.swap_xy && tp->set_swap_xy == NULL) {
|
||||||
|
uint16_t tmp = x[i];
|
||||||
|
x[i] = y[i];
|
||||||
|
y[i] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return touched;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||||
|
esp_err_t esp_lcd_touch_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(state != NULL);
|
||||||
|
|
||||||
|
*state = 0;
|
||||||
|
|
||||||
|
if (tp->get_button_state) {
|
||||||
|
return tp->get_button_state(tp, n, state);
|
||||||
|
} else {
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_set_swap_xy(esp_lcd_touch_handle_t tp, bool swap)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
tp->config.flags.swap_xy = swap;
|
||||||
|
|
||||||
|
/* Is swap supported by HW? */
|
||||||
|
if (tp->set_swap_xy) {
|
||||||
|
return tp->set_swap_xy(tp, swap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_get_swap_xy(esp_lcd_touch_handle_t tp, bool *swap)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(swap != NULL);
|
||||||
|
|
||||||
|
/* Is swap supported by HW? */
|
||||||
|
if (tp->get_swap_xy) {
|
||||||
|
return tp->get_swap_xy(tp, swap);
|
||||||
|
} else {
|
||||||
|
*swap = tp->config.flags.swap_xy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_set_mirror_x(esp_lcd_touch_handle_t tp, bool mirror)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
tp->config.flags.mirror_x = mirror;
|
||||||
|
|
||||||
|
/* Is mirror supported by HW? */
|
||||||
|
if (tp->set_mirror_x) {
|
||||||
|
return tp->set_mirror_x(tp, mirror);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_get_mirror_x(esp_lcd_touch_handle_t tp, bool *mirror)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(mirror != NULL);
|
||||||
|
|
||||||
|
/* Is swap supported by HW? */
|
||||||
|
if (tp->get_mirror_x) {
|
||||||
|
return tp->get_mirror_x(tp, mirror);
|
||||||
|
} else {
|
||||||
|
*mirror = tp->config.flags.mirror_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_set_mirror_y(esp_lcd_touch_handle_t tp, bool mirror)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
tp->config.flags.mirror_y = mirror;
|
||||||
|
|
||||||
|
/* Is mirror supported by HW? */
|
||||||
|
if (tp->set_mirror_y) {
|
||||||
|
return tp->set_mirror_y(tp, mirror);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_get_mirror_y(esp_lcd_touch_handle_t tp, bool *mirror)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(mirror != NULL);
|
||||||
|
|
||||||
|
/* Is swap supported by HW? */
|
||||||
|
if (tp->get_mirror_y) {
|
||||||
|
return tp->get_mirror_y(tp, mirror);
|
||||||
|
} else {
|
||||||
|
*mirror = tp->config.flags.mirror_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_del(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
if (tp->del != NULL) {
|
||||||
|
return tp->del(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_handle_t tp, esp_lcd_touch_interrupt_callback_t callback)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
/* Interrupt pin is not selected */
|
||||||
|
if (tp->config.int_gpio_num == GPIO_NUM_NC) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
tp->config.interrupt_callback = callback;
|
||||||
|
|
||||||
|
if (callback != NULL) {
|
||||||
|
ret = gpio_install_isr_service(0);
|
||||||
|
/* ISR service can be installed from user before, then it returns invalid state */
|
||||||
|
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
|
||||||
|
ESP_LOGE(TAG, "GPIO ISR install failed");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/* Add GPIO ISR handler */
|
||||||
|
ret = gpio_intr_enable(tp->config.int_gpio_num);
|
||||||
|
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR install failed");
|
||||||
|
ret = gpio_isr_handler_add(tp->config.int_gpio_num, (gpio_isr_t)tp->config.interrupt_callback, tp);
|
||||||
|
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR install failed");
|
||||||
|
} else {
|
||||||
|
/* Remove GPIO ISR handler */
|
||||||
|
ret = gpio_isr_handler_remove(tp->config.int_gpio_num);
|
||||||
|
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR remove handler failed");
|
||||||
|
ret = gpio_intr_disable(tp->config.int_gpio_num);
|
||||||
|
ESP_RETURN_ON_ERROR(ret, TAG, "GPIO ISR disable failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,370 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief ESP LCD touch
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS (1)
|
||||||
|
#define CONFIG_ESP_LCD_TOUCH_MAX_POINTS (5)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Touch controller type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct esp_lcd_touch_s esp_lcd_touch_t;
|
||||||
|
typedef esp_lcd_touch_t *esp_lcd_touch_handle_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Touch controller interrupt callback type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef void (*esp_lcd_touch_interrupt_callback_t)(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Touch Configuration Type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint16_t x_max; /*!< X coordinates max (for mirroring) */
|
||||||
|
uint16_t y_max; /*!< Y coordinates max (for mirroring) */
|
||||||
|
|
||||||
|
gpio_num_t rst_gpio_num; /*!< GPIO number of reset pin */
|
||||||
|
gpio_num_t int_gpio_num; /*!< GPIO number of interrupt pin */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
unsigned int reset: 1; /*!< Level of reset pin in reset */
|
||||||
|
unsigned int interrupt: 1;/*!< Active Level of interrupt pin */
|
||||||
|
} levels;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
unsigned int swap_xy: 1; /*!< Swap X and Y after read coordinates */
|
||||||
|
unsigned int mirror_x: 1; /*!< Mirror X after read coordinates */
|
||||||
|
unsigned int mirror_y: 1; /*!< Mirror Y after read coordinates */
|
||||||
|
} flags;
|
||||||
|
|
||||||
|
/*!< User callback called after get coordinates from touch controller for apply user adjusting */
|
||||||
|
void (*process_coordinates)(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||||
|
/*!< User callback called after the touch interrupt occured */
|
||||||
|
esp_lcd_touch_interrupt_callback_t interrupt_callback;
|
||||||
|
} esp_lcd_touch_config_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t points; /*!< Count of touch points saved */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint16_t x; /*!< X coordinate */
|
||||||
|
uint16_t y; /*!< Y coordinate */
|
||||||
|
uint16_t strength; /*!< Strength */
|
||||||
|
} coords[CONFIG_ESP_LCD_TOUCH_MAX_POINTS];
|
||||||
|
|
||||||
|
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||||
|
uint8_t buttons; /*!< Count of buttons states saved */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint8_t status; /*!< Status of button */
|
||||||
|
} button[CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
portMUX_TYPE lock; /*!< Lock for read/write */
|
||||||
|
} esp_lcd_touch_data_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Declare of Touch Type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct esp_lcd_touch_s {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read data from touch controller (mandatory)
|
||||||
|
*
|
||||||
|
* @note This function is usually blocking.
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*read_data)(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get coordinates from touch controller (mandatory)
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param x: Array of X coordinates
|
||||||
|
* @param y: Array of Y coordinates
|
||||||
|
* @param strength: Array of strengths
|
||||||
|
* @param point_num: Count of points touched (equals with count of items in x and y array)
|
||||||
|
* @param max_point_num: Maximum count of touched points to return (equals with max size of x and y array)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||||
|
*/
|
||||||
|
bool (*get_xy)(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||||
|
|
||||||
|
|
||||||
|
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||||
|
/**
|
||||||
|
* @brief Get button state (optional)
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param n: Button index
|
||||||
|
* @param state: Button state
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||||
|
*/
|
||||||
|
esp_err_t (*get_button_state)(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Swap X and Y after read coordinates (optional)
|
||||||
|
* If set, then not used SW swapping.
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param swap: Set swap value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*set_swap_xy)(esp_lcd_touch_handle_t tp, bool swap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Are X and Y coordinates swapped (optional)
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param swap: Get swap value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*get_swap_xy)(esp_lcd_touch_handle_t tp, bool *swap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mirror X after read coordinates
|
||||||
|
* If set, then not used SW mirroring.
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Set X mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*set_mirror_x)(esp_lcd_touch_handle_t tp, bool mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is mirrored X (optional)
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Get X mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*get_mirror_x)(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mirror Y after read coordinates
|
||||||
|
* If set, then not used SW mirroring.
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Set Y mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*set_mirror_y)(esp_lcd_touch_handle_t tp, bool mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is mirrored Y (optional)
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Get Y mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*get_mirror_y)(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delete Touch
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success, otherwise returns ESP_ERR_xxx
|
||||||
|
*/
|
||||||
|
esp_err_t (*del)(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configuration structure
|
||||||
|
*/
|
||||||
|
esp_lcd_touch_config_t config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Communication interface
|
||||||
|
*/
|
||||||
|
esp_lcd_panel_io_handle_t io;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Data structure
|
||||||
|
*/
|
||||||
|
esp_lcd_touch_data_t data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read data from touch controller
|
||||||
|
*
|
||||||
|
* @note This function is usually blocking.
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_ARG parameter error
|
||||||
|
* - ESP_FAIL sending command error, slave hasn't ACK the transfer
|
||||||
|
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode
|
||||||
|
* - ESP_ERR_TIMEOUT operation timeout because the bus is busy
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_read_data(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read coordinates from touch controller
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param x: Array of X coordinates
|
||||||
|
* @param y: Array of Y coordinates
|
||||||
|
* @param strength: Array of the strengths (can be NULL)
|
||||||
|
* @param point_num: Count of points touched (equals with count of items in x and y array)
|
||||||
|
* @param max_point_num: Maximum count of touched points to return (equals with max size of x and y array)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - Returns true, when touched and coordinates readed. Otherwise returns false.
|
||||||
|
*/
|
||||||
|
bool esp_lcd_touch_get_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||||
|
|
||||||
|
|
||||||
|
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
|
||||||
|
/**
|
||||||
|
* @brief Get button state
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param n: Button index
|
||||||
|
* @param state: Button state
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_NOT_SUPPORTED if this function is not supported by controller
|
||||||
|
* - ESP_ERR_INVALID_ARG if bad button index
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Swap X and Y after read coordinates
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param swap: Set swap value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_set_swap_xy(esp_lcd_touch_handle_t tp, bool swap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Are X and Y coordinates swapped
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param swap: Get swap value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_get_swap_xy(esp_lcd_touch_handle_t tp, bool *swap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mirror X after read coordinates
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Set X mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_set_mirror_x(esp_lcd_touch_handle_t tp, bool mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is mirrored X
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Get X mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_get_mirror_x(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mirror Y after read coordinates
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Set Y mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_set_mirror_y(esp_lcd_touch_handle_t tp, bool mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is mirrored Y
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param mirror: Get Y mirror value
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_get_mirror_y(esp_lcd_touch_handle_t tp, bool *mirror);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delete touch (free all allocated memory and restart HW)
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_del(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register user callback called after the touch interrupt occured
|
||||||
|
*
|
||||||
|
* @param tp: Touch handler
|
||||||
|
* @param callback: Interrupt callback
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_handle_t tp, esp_lcd_touch_interrupt_callback_t callback);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,270 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/i2c.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "esp_lcd_touch.h"
|
||||||
|
|
||||||
|
static const char *TAG = "GT911";
|
||||||
|
|
||||||
|
/* GT911 registers */
|
||||||
|
#define ESP_LCD_TOUCH_GT911_READ_XY_REG (0x814E)
|
||||||
|
#define ESP_LCD_TOUCH_GT911_CONFIG_REG (0x8047)
|
||||||
|
#define ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG (0x8140)
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Function definitions
|
||||||
|
*******************************************************************************/
|
||||||
|
static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp);
|
||||||
|
static bool esp_lcd_touch_gt911_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
|
||||||
|
static esp_err_t esp_lcd_touch_gt911_del(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/* I2C read/write */
|
||||||
|
static esp_err_t touch_gt911_i2c_read(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len);
|
||||||
|
static esp_err_t touch_gt911_i2c_write(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t data);
|
||||||
|
|
||||||
|
/* GT911 reset */
|
||||||
|
static esp_err_t touch_gt911_reset(esp_lcd_touch_handle_t tp);
|
||||||
|
/* Read status and config register */
|
||||||
|
static esp_err_t touch_gt911_read_cfg(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Public API functions
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_touch_new_i2c_gt911(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
assert(io != NULL);
|
||||||
|
assert(config != NULL);
|
||||||
|
assert(out_touch != NULL);
|
||||||
|
|
||||||
|
/* Prepare main structure */
|
||||||
|
esp_lcd_touch_handle_t esp_lcd_touch_gt911 = heap_caps_calloc(1, sizeof(esp_lcd_touch_t), MALLOC_CAP_DEFAULT);
|
||||||
|
ESP_GOTO_ON_FALSE(esp_lcd_touch_gt911, ESP_ERR_NO_MEM, err, TAG, "no mem for GT911 controller");
|
||||||
|
|
||||||
|
/* Communication interface */
|
||||||
|
esp_lcd_touch_gt911->io = io;
|
||||||
|
|
||||||
|
/* Only supported callbacks are set */
|
||||||
|
esp_lcd_touch_gt911->read_data = esp_lcd_touch_gt911_read_data;
|
||||||
|
esp_lcd_touch_gt911->get_xy = esp_lcd_touch_gt911_get_xy;
|
||||||
|
esp_lcd_touch_gt911->del = esp_lcd_touch_gt911_del;
|
||||||
|
|
||||||
|
/* Mutex */
|
||||||
|
esp_lcd_touch_gt911->data.lock.owner = portMUX_FREE_VAL;
|
||||||
|
|
||||||
|
/* Save config */
|
||||||
|
memcpy(&esp_lcd_touch_gt911->config, config, sizeof(esp_lcd_touch_config_t));
|
||||||
|
|
||||||
|
/* Prepare pin for touch interrupt */
|
||||||
|
if (esp_lcd_touch_gt911->config.int_gpio_num != GPIO_NUM_NC) {
|
||||||
|
const gpio_config_t int_gpio_config = {
|
||||||
|
.mode = GPIO_MODE_INPUT,
|
||||||
|
.intr_type = GPIO_INTR_NEGEDGE,
|
||||||
|
.pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.int_gpio_num)
|
||||||
|
};
|
||||||
|
ret = gpio_config(&int_gpio_config);
|
||||||
|
ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed");
|
||||||
|
|
||||||
|
/* Register interrupt callback */
|
||||||
|
if (esp_lcd_touch_gt911->config.interrupt_callback) {
|
||||||
|
esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_gt911, esp_lcd_touch_gt911->config.interrupt_callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare pin for touch controller reset */
|
||||||
|
if (esp_lcd_touch_gt911->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||||
|
const gpio_config_t rst_gpio_config = {
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.rst_gpio_num)
|
||||||
|
};
|
||||||
|
ret = gpio_config(&rst_gpio_config);
|
||||||
|
ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset controller */
|
||||||
|
ret = touch_gt911_reset(esp_lcd_touch_gt911);
|
||||||
|
ESP_GOTO_ON_ERROR(ret, err, TAG, "GT911 reset failed");
|
||||||
|
|
||||||
|
/* Read status and config info */
|
||||||
|
ret = touch_gt911_read_cfg(esp_lcd_touch_gt911);
|
||||||
|
ESP_GOTO_ON_ERROR(ret, err, TAG, "GT911 init failed");
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Error (0x%x)! Touch controller GT911 initialization failed!", ret);
|
||||||
|
if (esp_lcd_touch_gt911) {
|
||||||
|
esp_lcd_touch_gt911_del(esp_lcd_touch_gt911);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_touch = esp_lcd_touch_gt911;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
esp_err_t err;
|
||||||
|
uint8_t buf[41];
|
||||||
|
uint8_t touch_cnt = 0;
|
||||||
|
uint8_t clear = 0;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, buf, 1);
|
||||||
|
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||||
|
|
||||||
|
/* Any touch data? */
|
||||||
|
if ((buf[0] & 0x80) == 0x00) {
|
||||||
|
touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||||
|
} else {
|
||||||
|
/* Count of touched points */
|
||||||
|
touch_cnt = buf[0] & 0x0f;
|
||||||
|
if (touch_cnt > 5 || touch_cnt == 0) {
|
||||||
|
touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read all points */
|
||||||
|
err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG + 1, &buf[1], touch_cnt * 8);
|
||||||
|
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||||
|
|
||||||
|
/* Clear all */
|
||||||
|
err = touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
|
||||||
|
ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&tp->data.lock);
|
||||||
|
|
||||||
|
/* Number of touched points */
|
||||||
|
touch_cnt = (touch_cnt > CONFIG_ESP_LCD_TOUCH_MAX_POINTS ? CONFIG_ESP_LCD_TOUCH_MAX_POINTS : touch_cnt);
|
||||||
|
tp->data.points = touch_cnt;
|
||||||
|
|
||||||
|
/* Fill all coordinates */
|
||||||
|
for (i = 0; i < touch_cnt; i++) {
|
||||||
|
tp->data.coords[i].x = ((uint16_t)buf[(i * 8) + 3] << 8) + buf[(i * 8) + 2];
|
||||||
|
tp->data.coords[i].y = (((uint16_t)buf[(i * 8) + 5] << 8) + buf[(i * 8) + 4]);
|
||||||
|
tp->data.coords[i].strength = (((uint16_t)buf[(i * 8) + 7] << 8) + buf[(i * 8) + 6]);
|
||||||
|
}
|
||||||
|
|
||||||
|
portEXIT_CRITICAL(&tp->data.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool esp_lcd_touch_gt911_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(x != NULL);
|
||||||
|
assert(y != NULL);
|
||||||
|
assert(point_num != NULL);
|
||||||
|
assert(max_point_num > 0);
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&tp->data.lock);
|
||||||
|
|
||||||
|
/* Count of points */
|
||||||
|
*point_num = (tp->data.points > max_point_num ? max_point_num : tp->data.points);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < *point_num; i++) {
|
||||||
|
x[i] = tp->data.coords[i].x;
|
||||||
|
y[i] = tp->data.coords[i].y;
|
||||||
|
if (strength) {
|
||||||
|
strength[i] = tp->data.coords[i].strength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invalidate */
|
||||||
|
tp->data.points = 0;
|
||||||
|
|
||||||
|
portEXIT_CRITICAL(&tp->data.lock);
|
||||||
|
|
||||||
|
return (*point_num > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_lcd_touch_gt911_del(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
/* Reset GPIO pin settings */
|
||||||
|
if (tp->config.int_gpio_num != GPIO_NUM_NC) {
|
||||||
|
gpio_reset_pin(tp->config.int_gpio_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset GPIO pin settings */
|
||||||
|
if (tp->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||||
|
gpio_reset_pin(tp->config.rst_gpio_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(tp);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Private API function
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
/* Reset controller */
|
||||||
|
static esp_err_t touch_gt911_reset(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
if (tp->config.rst_gpio_num != GPIO_NUM_NC) {
|
||||||
|
ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, tp->config.levels.reset), TAG, "GPIO set level error!");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, !tp->config.levels.reset), TAG, "GPIO set level error!");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t touch_gt911_read_cfg(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
uint8_t buf[4];
|
||||||
|
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG, (uint8_t *)&buf[0], 3), TAG, "GT911 read error!");
|
||||||
|
ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_CONFIG_REG, (uint8_t *)&buf[3], 1), TAG, "GT911 read error!");
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "TouchPad_ID:0x%02x,0x%02x,0x%02x", buf[0], buf[1], buf[2]);
|
||||||
|
ESP_LOGI(TAG, "TouchPad_Config_Version:%d", buf[3]);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t touch_gt911_i2c_read(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
assert(data != NULL);
|
||||||
|
|
||||||
|
/* Read data */
|
||||||
|
return esp_lcd_panel_io_rx_param(tp->io, reg, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t touch_gt911_i2c_write(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t data)
|
||||||
|
{
|
||||||
|
assert(tp != NULL);
|
||||||
|
|
||||||
|
// *INDENT-OFF*
|
||||||
|
/* Write data */
|
||||||
|
return esp_lcd_panel_io_tx_param(tp->io, reg, (uint8_t[]){data}, 1);
|
||||||
|
// *INDENT-ON*
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief ESP LCD touch: GT911
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_lcd_touch.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a new GT911 touch driver
|
||||||
|
*
|
||||||
|
* @note The I2C communication should be initialized before use this function.
|
||||||
|
*
|
||||||
|
* @param io LCD/Touch panel IO handle
|
||||||
|
* @param config: Touch configuration
|
||||||
|
* @param out_touch: Touch instance handle
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_NO_MEM if there is no memory for allocating main structure
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_touch_new_i2c_gt911(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief I2C address of the GT911 controller
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS (0x5D)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Touch IO configuration structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG() \
|
||||||
|
{ \
|
||||||
|
.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, \
|
||||||
|
.control_phase_bytes = 1, \
|
||||||
|
.dc_bit_offset = 0, \
|
||||||
|
.lcd_cmd_bits = 16, \
|
||||||
|
.flags = \
|
||||||
|
{ \
|
||||||
|
.disable_control_phase = 1, \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
#include "esp_lcd_touch_gt911.h"
|
||||||
|
#include "gt911_touch.h"
|
||||||
|
|
||||||
|
#define CONFIG_LCD_HRES 800
|
||||||
|
#define CONFIG_LCD_VRES 1280
|
||||||
|
|
||||||
|
static const char *TAG = "example";
|
||||||
|
|
||||||
|
esp_lcd_touch_handle_t tp;
|
||||||
|
esp_lcd_panel_io_handle_t tp_io_handle;
|
||||||
|
|
||||||
|
uint16_t touch_strength[1];
|
||||||
|
uint8_t touch_cnt = 0;
|
||||||
|
|
||||||
|
gt911_touch::gt911_touch(int8_t sda_pin, int8_t scl_pin, int8_t rst_pin, int8_t int_pin)
|
||||||
|
{
|
||||||
|
_sda = sda_pin;
|
||||||
|
_scl = scl_pin;
|
||||||
|
_rst = rst_pin;
|
||||||
|
_int = int_pin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gt911_touch::begin()
|
||||||
|
{
|
||||||
|
// i2c_config_t i2c_conf = {
|
||||||
|
// .mode = I2C_MODE_MASTER,
|
||||||
|
// .sda_io_num = (gpio_num_t)_sda,
|
||||||
|
// .scl_io_num = (gpio_num_t)_scl,
|
||||||
|
// .sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
// .scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
|
// };
|
||||||
|
// i2c_conf.master.clk_speed = 400000; // 400kHz
|
||||||
|
|
||||||
|
|
||||||
|
// ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &i2c_conf));
|
||||||
|
// ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, i2c_conf.mode, 0, 0, 0));
|
||||||
|
i2c_master_bus_handle_t i2c_handle = NULL;
|
||||||
|
i2c_master_get_bus_handle(1,&i2c_handle);
|
||||||
|
|
||||||
|
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||||
|
tp_io_config.scl_speed_hz = 100000;
|
||||||
|
ESP_LOGI(TAG, "Initialize touch IO (I2C)");
|
||||||
|
esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle);
|
||||||
|
|
||||||
|
esp_lcd_touch_config_t tp_cfg = {
|
||||||
|
.x_max = CONFIG_LCD_HRES,
|
||||||
|
.y_max = CONFIG_LCD_VRES,
|
||||||
|
.rst_gpio_num = (gpio_num_t)_rst,
|
||||||
|
.int_gpio_num = (gpio_num_t)_int,
|
||||||
|
.levels = {
|
||||||
|
.reset = 0,
|
||||||
|
.interrupt = 0,
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.swap_xy = 0,
|
||||||
|
.mirror_x = 0,
|
||||||
|
.mirror_y = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Initialize touch controller gt911");
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gt911_touch::getTouch(uint16_t *x, uint16_t *y)
|
||||||
|
{
|
||||||
|
esp_lcd_touch_read_data(tp);
|
||||||
|
bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, x, y, touch_strength, &touch_cnt, 1);
|
||||||
|
|
||||||
|
return touchpad_pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gt911_touch::set_rotation(uint8_t r){
|
||||||
|
switch(r){
|
||||||
|
case 0:
|
||||||
|
esp_lcd_touch_set_swap_xy(tp, false);
|
||||||
|
esp_lcd_touch_set_mirror_x(tp, false);
|
||||||
|
esp_lcd_touch_set_mirror_y(tp, false);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
esp_lcd_touch_set_swap_xy(tp, false);
|
||||||
|
esp_lcd_touch_set_mirror_x(tp, true);
|
||||||
|
esp_lcd_touch_set_mirror_y(tp, true);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
esp_lcd_touch_set_swap_xy(tp, false);
|
||||||
|
esp_lcd_touch_set_mirror_x(tp, false);
|
||||||
|
esp_lcd_touch_set_mirror_y(tp, false);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
esp_lcd_touch_set_swap_xy(tp, false);
|
||||||
|
esp_lcd_touch_set_mirror_x(tp, true);
|
||||||
|
esp_lcd_touch_set_mirror_y(tp, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef _GT911_TOUCH_H
|
||||||
|
#define _GT911_TOUCH_H
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
class gt911_touch
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
gt911_touch(int8_t sda_pin, int8_t scl_pin, int8_t rst_pin = -1, int8_t int_pin = -1);
|
||||||
|
|
||||||
|
void begin();
|
||||||
|
bool getTouch(uint16_t *x, uint16_t *y);
|
||||||
|
void set_rotation(uint8_t r);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int8_t _sda, _scl, _rst, _int;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
# 请使用arduino_esp32_v3.2.1版本
|
||||||
|
# ESP32-audioI2S V3.3.0
|
||||||
|
# arduino-audio-driver https://github.com/pschatzmann/arduino-audio-driver
|
||||||
|
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
#include "string.h"
|
||||||
|
#include "AudioBoard.h" //https://github.com/pschatzmann/arduino-audio-driver
|
||||||
|
#include "Audio.h" //https://github.com/schreibfaul1/ESP32-audioI2S
|
||||||
|
#include "SD_MMC.h"
|
||||||
|
|
||||||
|
Audio audio;
|
||||||
|
DriverPins my_pins;
|
||||||
|
AudioBoard board(AudioDriverES8311, my_pins);
|
||||||
|
|
||||||
|
//SD_MMC
|
||||||
|
#define SD_D0 39
|
||||||
|
#define SD_D1 40
|
||||||
|
#define SD_D2 41
|
||||||
|
#define SD_D3 42
|
||||||
|
#define SD_CMD 44
|
||||||
|
#define SD_CLK 43
|
||||||
|
|
||||||
|
#define I2S_MCK_IO 13
|
||||||
|
#define I2S_BCK_IO 12
|
||||||
|
#define I2S_DI_IO 48
|
||||||
|
#define I2S_WS_IO 10
|
||||||
|
#define I2S_DO_IO 9
|
||||||
|
#define ES8311_PA 11
|
||||||
|
|
||||||
|
#define I2C_SDA 7
|
||||||
|
#define I2C_SCL 8
|
||||||
|
#define ES8311_ADDRESS 0x18
|
||||||
|
|
||||||
|
static char file_list[20][256];
|
||||||
|
static int file_cnt = 0;
|
||||||
|
static int cnt = 0;
|
||||||
|
|
||||||
|
|
||||||
|
void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
|
||||||
|
Serial.printf("Listing directory: %s\n", dirname);
|
||||||
|
|
||||||
|
File root = fs.open(dirname);
|
||||||
|
if (!root) {
|
||||||
|
Serial.println("Failed to open directory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!root.isDirectory()) {
|
||||||
|
Serial.println("Not a directory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File file = root.openNextFile();
|
||||||
|
while (file) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
Serial.print(" DIR : ");
|
||||||
|
Serial.println(file.name());
|
||||||
|
if (levels) {
|
||||||
|
listDir(fs, file.path(), levels - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Serial.print(" FILE: ");
|
||||||
|
Serial.print(file.name());
|
||||||
|
snprintf(&file_list[file_cnt][0],256,"/music/%s",file.name());
|
||||||
|
file_cnt++;
|
||||||
|
Serial.print(" SIZE: ");
|
||||||
|
Serial.println(file.size());
|
||||||
|
}
|
||||||
|
file = root.openNextFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("start");
|
||||||
|
if(! SD_MMC.setPins(SD_CLK, SD_CMD, SD_D0, SD_D1, SD_D2, SD_D3)){
|
||||||
|
Serial.println("Pin change failed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SD_MMC.begin()) {
|
||||||
|
Serial.println("Card Mount Failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listDir(SD_MMC,"/music/",0);
|
||||||
|
|
||||||
|
// pinMode(ES8311_PA, OUTPUT);
|
||||||
|
// digitalWrite(ES8311_PA, HIGH);
|
||||||
|
|
||||||
|
my_pins.addI2C(PinFunction::CODEC,I2C_SCL,I2C_SDA,ES8311_ADDRESS);
|
||||||
|
my_pins.addPin(PinFunction::PA,ES8311_PA,PinLogic::Output);
|
||||||
|
|
||||||
|
CodecConfig cfg;
|
||||||
|
cfg.input_device = ADC_INPUT_LINE1;
|
||||||
|
cfg.output_device = DAC_OUTPUT_ALL;
|
||||||
|
cfg.i2s.bits = BIT_LENGTH_16BITS;
|
||||||
|
cfg.i2s.rate = RATE_44K;
|
||||||
|
// cfg.i2s.fmt = I2S_NORMAL;
|
||||||
|
// cfg.i2s.mode = MODE_SLAVE;
|
||||||
|
board.begin(cfg);
|
||||||
|
|
||||||
|
audio.setPinout(I2S_BCK_IO, I2S_WS_IO, I2S_DO_IO,I2S_MCK_IO);
|
||||||
|
audio.setVolume(4); // 0...21
|
||||||
|
audio.connecttoFS(SD_MMC, file_list[cnt]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
audio.loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional
|
||||||
|
void audio_info(const char *info){
|
||||||
|
Serial.print("info "); Serial.println(info);
|
||||||
|
}
|
||||||
|
void audio_id3data(const char *info){ //id3 metadata
|
||||||
|
Serial.print("id3data ");Serial.println(info);
|
||||||
|
}
|
||||||
|
void audio_eof_mp3(const char *info){ //end of file
|
||||||
|
Serial.print("eof_mp3 ");Serial.println(info);
|
||||||
|
cnt++;
|
||||||
|
if(cnt > file_cnt - 1)
|
||||||
|
cnt = 0;
|
||||||
|
|
||||||
|
audio.connecttoFS(SD_MMC, file_list[cnt]);
|
||||||
|
|
||||||
|
}
|
||||||
|
void audio_showstation(const char *info){
|
||||||
|
Serial.print("station ");Serial.println(info);
|
||||||
|
}
|
||||||
|
void audio_showstreamtitle(const char *info){
|
||||||
|
Serial.print("streamtitle ");Serial.println(info);
|
||||||
|
}
|
||||||
|
void audio_bitrate(const char *info){
|
||||||
|
Serial.print("bitrate ");Serial.println(info);
|
||||||
|
}
|
||||||
|
void audio_commercial(const char *info){ //duration in sec
|
||||||
|
Serial.print("commercial ");Serial.println(info);
|
||||||
|
}
|
||||||
|
void audio_icyurl(const char *info){ //homepage
|
||||||
|
Serial.print("icyurl ");Serial.println(info);
|
||||||
|
}
|
||||||
|
void audio_lasthost(const char *info){ //stream URL played
|
||||||
|
Serial.print("lasthost ");Serial.println(info);
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,6 @@
|
|||||||
|
# The following five 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.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(battery_adc_test)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "battery_adc_test.c"
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_adc/adc_oneshot.h"
|
||||||
|
#include "esp_adc/adc_cali.h"
|
||||||
|
#include "esp_adc/adc_cali_scheme.h"
|
||||||
|
|
||||||
|
const static char *TAG = "EXAMPLE";
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------
|
||||||
|
ADC General Macros
|
||||||
|
---------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
#define EXAMPLE_ADC2_CHAN0 ADC_CHANNEL_4
|
||||||
|
#define EXAMPLE_ADC_ATTEN ADC_ATTEN_DB_12
|
||||||
|
|
||||||
|
#define V_C_MAX (2500)
|
||||||
|
#define V_C_MIN (2250)
|
||||||
|
|
||||||
|
static int adc_raw;
|
||||||
|
static int adc_raw_;
|
||||||
|
static int voltage_;
|
||||||
|
static int voltage;
|
||||||
|
static int voltage_per;
|
||||||
|
static int voltage_per_;
|
||||||
|
static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle);
|
||||||
|
static void example_adc_calibration_deinit(adc_cali_handle_t handle);
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
adc_oneshot_chan_cfg_t config = {
|
||||||
|
.atten = EXAMPLE_ADC_ATTEN,
|
||||||
|
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||||
|
};
|
||||||
|
//-------------ADC2 Init---------------//
|
||||||
|
adc_oneshot_unit_handle_t adc2_handle;
|
||||||
|
adc_oneshot_unit_init_cfg_t init_config2 = {
|
||||||
|
.unit_id = ADC_UNIT_2,
|
||||||
|
.ulp_mode = ADC_ULP_MODE_DISABLE,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config2, &adc2_handle));
|
||||||
|
|
||||||
|
//-------------ADC2 Calibration Init---------------//
|
||||||
|
adc_cali_handle_t adc2_cali_handle = NULL;
|
||||||
|
bool do_calibration2 = example_adc_calibration_init(ADC_UNIT_2, EXAMPLE_ADC2_CHAN0, EXAMPLE_ADC_ATTEN, &adc2_cali_handle);
|
||||||
|
|
||||||
|
//-------------ADC2 Config---------------//
|
||||||
|
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc2_handle, EXAMPLE_ADC2_CHAN0, &config));
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
for(int i=0;i<500;i++)
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(adc_oneshot_read(adc2_handle, EXAMPLE_ADC2_CHAN0,&adc_raw));
|
||||||
|
adc_raw_ += adc_raw;
|
||||||
|
}
|
||||||
|
adc_raw_ = adc_raw_ / 500;
|
||||||
|
ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_2 + 1, EXAMPLE_ADC2_CHAN0, adc_raw_);
|
||||||
|
|
||||||
|
if (do_calibration2) {
|
||||||
|
ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc2_cali_handle, adc_raw_, &voltage));
|
||||||
|
ESP_LOGI(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", ADC_UNIT_2 + 1, EXAMPLE_ADC2_CHAN0, voltage);
|
||||||
|
}
|
||||||
|
|
||||||
|
voltage_ = voltage - V_C_MIN;
|
||||||
|
if(voltage_ < 0)
|
||||||
|
voltage_ = 0;
|
||||||
|
voltage_per_ = voltage_per;
|
||||||
|
voltage_per = voltage_ * 10000 / (V_C_MAX - V_C_MIN) / 100 ;
|
||||||
|
|
||||||
|
voltage_per = (voltage_per_ + voltage_per) / 2;
|
||||||
|
if(voltage_per > 100)
|
||||||
|
voltage_per = 100;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG,"Battery charge: %d %%",voltage_per);
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Tear Down
|
||||||
|
ESP_ERROR_CHECK(adc_oneshot_del_unit(adc2_handle));
|
||||||
|
if (do_calibration2) {
|
||||||
|
example_adc_calibration_deinit(adc2_cali_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------
|
||||||
|
ADC Calibration
|
||||||
|
---------------------------------------------------------------*/
|
||||||
|
static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)
|
||||||
|
{
|
||||||
|
adc_cali_handle_t handle = NULL;
|
||||||
|
esp_err_t ret = ESP_FAIL;
|
||||||
|
bool calibrated = false;
|
||||||
|
|
||||||
|
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||||
|
if (!calibrated) {
|
||||||
|
ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
|
||||||
|
adc_cali_curve_fitting_config_t cali_config = {
|
||||||
|
.unit_id = unit,
|
||||||
|
.chan = channel,
|
||||||
|
.atten = atten,
|
||||||
|
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||||
|
};
|
||||||
|
ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
calibrated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||||
|
if (!calibrated) {
|
||||||
|
ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting");
|
||||||
|
adc_cali_line_fitting_config_t cali_config = {
|
||||||
|
.unit_id = unit,
|
||||||
|
.atten = atten,
|
||||||
|
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||||
|
};
|
||||||
|
ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
calibrated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*out_handle = handle;
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "Calibration Success");
|
||||||
|
} else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
|
||||||
|
ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Invalid arg or no memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
return calibrated;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void example_adc_calibration_deinit(adc_cali_handle_t handle)
|
||||||
|
{
|
||||||
|
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||||
|
ESP_LOGI(TAG, "deregister %s calibration scheme", "Curve Fitting");
|
||||||
|
ESP_ERROR_CHECK(adc_cali_delete_scheme_curve_fitting(handle));
|
||||||
|
|
||||||
|
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||||
|
ESP_LOGI(TAG, "deregister %s calibration scheme", "Line Fitting");
|
||||||
|
ESP_ERROR_CHECK(adc_cali_delete_scheme_line_fitting(handle));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||||
|
# Espressif IoT Development Framework (ESP-IDF) 6.0.0 Project Minimal Configuration
|
||||||
|
#
|
||||||
|
CONFIG_IDF_TARGET="esp32p4"
|
||||||
|
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||||
|
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
|
||||||
|
CONFIG_SPIRAM=y
|
||||||
|
CONFIG_SPIRAM_SPEED_200M=y
|
||||||
|
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
|
||||||
|
CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096
|
||||||
|
CONFIG_FREERTOS_HZ=1000
|
||||||
|
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
|||||||
|
set(SRCS "")
|
||||||
|
list(APPEND SRCS
|
||||||
|
"src/bsp_board_extra.c"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(INCLUDE_DIRS "")
|
||||||
|
list(APPEND INCLUDE_DIRS "include")
|
||||||
|
|
||||||
|
foreach(SRC_DIR IN LISTS SRC_DIRS)
|
||||||
|
file(GLOB_RECURSE SRC ${SRC_DIR}/*.c)
|
||||||
|
list(APPEND SRCS ${SRC})
|
||||||
|
list(APPEND INCLUDE_DIRS ${SRC_DIR}/include)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
idf_component_register(
|
||||||
|
SRCS ${SRCS}
|
||||||
|
INCLUDE_DIRS ${INCLUDE_DIRS}
|
||||||
|
REQUIRES driver
|
||||||
|
PRIV_REQUIRES esp_timer fatfs esp_psram esp_mm
|
||||||
|
)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
menu "Board Support Package"
|
||||||
|
config BSP_I2S_NUM
|
||||||
|
int "I2S peripheral index"
|
||||||
|
default 1
|
||||||
|
range 0 1
|
||||||
|
help
|
||||||
|
ESP32S3 has two I2S peripherals, pick the one you want to use.
|
||||||
|
endmenu
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
dependencies:
|
||||||
|
idf:
|
||||||
|
version: '>=5.3.0'
|
||||||
|
|
||||||
|
espressif/esp32_p4_function_ev_board:
|
||||||
|
path: ../espressif__esp32_p4_function_ev_board
|
||||||
|
|
||||||
|
chmorgan/esp-audio-player:
|
||||||
|
version: "1.0.*"
|
||||||
|
public: true
|
||||||
|
|
||||||
|
chmorgan/esp-file-iterator:
|
||||||
|
version: "1.0.0"
|
||||||
|
public: true
|
||||||
|
|
||||||
|
description: Board Support Package for ESP32-P4-Function-ev-board
|
||||||
|
targets:
|
||||||
|
- esp32p4
|
||||||
|
version: 0.0.2
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "esp_codec_dev.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/i2s_std.h"
|
||||||
|
#include "audio_player.h"
|
||||||
|
#include "file_iterator.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CODEC_DEFAULT_SAMPLE_RATE (16000)
|
||||||
|
#define CODEC_DEFAULT_BIT_WIDTH (16)
|
||||||
|
#define CODEC_DEFAULT_ADC_VOLUME (24.0)
|
||||||
|
#define CODEC_DEFAULT_CHANNEL (2)
|
||||||
|
#define CODEC_DEFAULT_VOLUME (60)
|
||||||
|
|
||||||
|
#define BSP_LCD_BACKLIGHT_BRIGHTNESS_MAX (95)
|
||||||
|
#define BSP_LCD_BACKLIGHT_BRIGHTNESS_MIN (0)
|
||||||
|
#define LCD_LEDC_CH (CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH)
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* BSP Extra interface
|
||||||
|
* Mainly provided some I2S Codec interfaces.
|
||||||
|
**************************************************************************************************/
|
||||||
|
/**
|
||||||
|
* @brief Player set mute.
|
||||||
|
*
|
||||||
|
* @param enable: true or false
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Success
|
||||||
|
* - Others: Fail
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_extra_codec_mute_set(bool enable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Player set volume.
|
||||||
|
*
|
||||||
|
* @param volume: volume set
|
||||||
|
* @param volume_set: volume set response
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Success
|
||||||
|
* - Others: Fail
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_extra_codec_volume_set(int volume, int *volume_set);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Player get volume.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - volume: volume get
|
||||||
|
*/
|
||||||
|
int bsp_extra_codec_volume_get(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stop I2S function.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Success
|
||||||
|
* - Others: Fail
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_extra_codec_dev_stop(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resume I2S function.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Success
|
||||||
|
* - Others: Fail
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_extra_codec_dev_resume(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set I2S format to codec.
|
||||||
|
*
|
||||||
|
* @param rate: Sample rate of sample
|
||||||
|
* @param bits_cfg: Bit lengths of one channel data
|
||||||
|
* @param ch: Channels of sample
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Success
|
||||||
|
* - Others: Fail
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_extra_codec_set_fs(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read data from recoder.
|
||||||
|
*
|
||||||
|
* @param audio_buffer: The pointer of receiving data buffer
|
||||||
|
* @param len: Max data buffer length
|
||||||
|
* @param bytes_read: Byte number that actually be read, can be NULL if not needed
|
||||||
|
* @param timeout_ms: Max block time
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Success
|
||||||
|
* - Others: Fail
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_extra_i2s_read(void *audio_buffer, size_t len, size_t *bytes_read, uint32_t timeout_ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write data to player.
|
||||||
|
*
|
||||||
|
* @param audio_buffer: The pointer of sent data buffer
|
||||||
|
* @param len: Max data buffer length
|
||||||
|
* @param bytes_written: Byte number that actually be sent, can be NULL if not needed
|
||||||
|
* @param timeout_ms: Max block time
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Success
|
||||||
|
* - Others: Fail
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_extra_i2s_write(void *audio_buffer, size_t len, size_t *bytes_written, uint32_t timeout_ms);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize codec play and record handle.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Success
|
||||||
|
* - Others: Fail
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_extra_codec_init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize audio player task.
|
||||||
|
*
|
||||||
|
* @param path file path
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Success
|
||||||
|
* - Others: Fail
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_extra_player_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delete audio player task.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Success
|
||||||
|
* - Others: Fail
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_extra_player_del(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize a file iterator instance
|
||||||
|
*
|
||||||
|
* @param path The file path for the iterator.
|
||||||
|
* @param ret_instance A pointer to the file iterator instance to be returned.
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Successfully initialized the file iterator instance.
|
||||||
|
* - ESP_FAIL: Failed to initialize the file iterator instance due to invalid parameters or memory allocation failure.
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_extra_file_instance_init(const char *path, file_iterator_instance_t **ret_instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Play the audio file at the specified index in the file iterator
|
||||||
|
*
|
||||||
|
* @param instance The file iterator instance.
|
||||||
|
* @param index The index of the file to play within the iterator.
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Successfully started playing the audio file.
|
||||||
|
* - ESP_FAIL: Failed to play the audio file due to invalid parameters or file access issues.
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_extra_player_play_index(file_iterator_instance_t *instance, int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Play the audio file specified by the file path
|
||||||
|
*
|
||||||
|
* @param file_path The path to the audio file to be played.
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Successfully started playing the audio file.
|
||||||
|
* - ESP_FAIL: Failed to play the audio file due to file access issues.
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_extra_player_play_file(const char *file_path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register a callback function for the audio player
|
||||||
|
*
|
||||||
|
* @param cb The callback function to be registered.
|
||||||
|
* @param user_data User data to be passed to the callback function.
|
||||||
|
*/
|
||||||
|
void bsp_extra_player_register_callback(audio_player_cb_t cb, void *user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the specified audio file is currently playing
|
||||||
|
*
|
||||||
|
* @param file_path The path to the audio file to check.
|
||||||
|
* @return
|
||||||
|
* - true: The specified audio file is currently playing.
|
||||||
|
* - false: The specified audio file is not currently playing.
|
||||||
|
*/
|
||||||
|
bool bsp_extra_player_is_playing_by_path(const char *file_path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the audio file at the specified index is currently playing
|
||||||
|
*
|
||||||
|
* @param instance The file iterator instance.
|
||||||
|
* @param index The index of the file to check.
|
||||||
|
* @return
|
||||||
|
* - true: The audio file at the specified index is currently playing.
|
||||||
|
* - false: The audio file at the specified index is not currently playing.
|
||||||
|
*/
|
||||||
|
bool bsp_extra_player_is_playing_by_index(file_iterator_instance_t *instance, int index);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_codec_dev_defaults.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_vfs_fat.h"
|
||||||
|
#include "driver/i2c.h"
|
||||||
|
#include "driver/i2s_std.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/ledc.h"
|
||||||
|
|
||||||
|
#include "bsp/esp-bsp.h"
|
||||||
|
#include "bsp_board_extra.h"
|
||||||
|
|
||||||
|
static const char *TAG = "bsp_extra_board";
|
||||||
|
|
||||||
|
static esp_codec_dev_handle_t play_dev_handle;
|
||||||
|
static esp_codec_dev_handle_t record_dev_handle;
|
||||||
|
|
||||||
|
static bool _is_audio_init = false;
|
||||||
|
static bool _is_player_init = false;
|
||||||
|
static int _vloume_intensity = CODEC_DEFAULT_VOLUME;
|
||||||
|
|
||||||
|
static audio_player_cb_t audio_idle_callback = NULL;
|
||||||
|
static void *audio_idle_cb_user_data = NULL;
|
||||||
|
static char audio_file_path[128];
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
*
|
||||||
|
* Extra Board Function
|
||||||
|
*
|
||||||
|
**************************************************************************************************/
|
||||||
|
|
||||||
|
static esp_err_t audio_mute_function(AUDIO_PLAYER_MUTE_SETTING setting)
|
||||||
|
{
|
||||||
|
// Volume saved when muting and restored when unmuting. Restoring volume is necessary
|
||||||
|
// as es8311_set_voice_mute(true) results in voice volume (REG32) being set to zero.
|
||||||
|
|
||||||
|
bsp_extra_codec_mute_set(setting == AUDIO_PLAYER_MUTE ? true : false);
|
||||||
|
|
||||||
|
// restore the voice volume upon unmuting
|
||||||
|
if (setting == AUDIO_PLAYER_UNMUTE) {
|
||||||
|
ESP_RETURN_ON_ERROR(esp_codec_dev_set_out_vol(play_dev_handle, _vloume_intensity), TAG, "Set Codec volume failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audio_callback(audio_player_cb_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
if (audio_idle_callback) {
|
||||||
|
ctx->user_ctx = audio_idle_cb_user_data;
|
||||||
|
audio_idle_callback(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_extra_i2s_read(void *audio_buffer, size_t len, size_t *bytes_read, uint32_t timeout_ms)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
ret = esp_codec_dev_read(record_dev_handle, audio_buffer, len);
|
||||||
|
*bytes_read = len;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_extra_i2s_write(void *audio_buffer, size_t len, size_t *bytes_written, uint32_t timeout_ms)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
ret = esp_codec_dev_write(play_dev_handle, audio_buffer, len);
|
||||||
|
*bytes_written = len;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_extra_codec_set_fs(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
esp_codec_dev_sample_info_t fs = {
|
||||||
|
.sample_rate = rate,
|
||||||
|
.channel = ch,
|
||||||
|
.bits_per_sample = bits_cfg,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (play_dev_handle) {
|
||||||
|
ret = esp_codec_dev_close(play_dev_handle);
|
||||||
|
}
|
||||||
|
if (record_dev_handle) {
|
||||||
|
ret |= esp_codec_dev_close(record_dev_handle);
|
||||||
|
ret |= esp_codec_dev_set_in_gain(record_dev_handle, CODEC_DEFAULT_ADC_VOLUME);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (play_dev_handle) {
|
||||||
|
ret |= esp_codec_dev_open(play_dev_handle, &fs);
|
||||||
|
}
|
||||||
|
if (record_dev_handle) {
|
||||||
|
ret |= esp_codec_dev_open(record_dev_handle, &fs);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_extra_codec_volume_set(int volume, int *volume_set)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_ERROR(esp_codec_dev_set_out_vol(play_dev_handle, volume), TAG, "Set Codec volume failed");
|
||||||
|
_vloume_intensity = volume;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Setting volume: %d", volume);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bsp_extra_codec_volume_get(void)
|
||||||
|
{
|
||||||
|
return _vloume_intensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_extra_codec_mute_set(bool enable)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
ret = esp_codec_dev_set_out_mute(play_dev_handle, enable);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_extra_codec_dev_stop(void)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
if (play_dev_handle) {
|
||||||
|
ret = esp_codec_dev_close(play_dev_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record_dev_handle) {
|
||||||
|
ret = esp_codec_dev_close(record_dev_handle);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_extra_codec_dev_resume(void)
|
||||||
|
{
|
||||||
|
return bsp_extra_codec_set_fs(CODEC_DEFAULT_SAMPLE_RATE, CODEC_DEFAULT_BIT_WIDTH, CODEC_DEFAULT_CHANNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_extra_codec_init()
|
||||||
|
{
|
||||||
|
if (_is_audio_init) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
play_dev_handle = bsp_audio_codec_speaker_init();
|
||||||
|
assert((play_dev_handle) && "play_dev_handle not initialized");
|
||||||
|
|
||||||
|
record_dev_handle = bsp_audio_codec_microphone_init();
|
||||||
|
assert((record_dev_handle) && "record_dev_handle not initialized");
|
||||||
|
|
||||||
|
bsp_extra_codec_set_fs(CODEC_DEFAULT_SAMPLE_RATE, CODEC_DEFAULT_BIT_WIDTH, CODEC_DEFAULT_CHANNEL);
|
||||||
|
|
||||||
|
_is_audio_init = true;
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_extra_player_init(void)
|
||||||
|
{
|
||||||
|
if (_is_player_init) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_player_config_t config = { .mute_fn = audio_mute_function,
|
||||||
|
.write_fn = bsp_extra_i2s_write,
|
||||||
|
.clk_set_fn = bsp_extra_codec_set_fs,
|
||||||
|
.priority = 5
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(audio_player_new(config), TAG, "audio_player_init failed");
|
||||||
|
audio_player_callback_register(audio_callback, NULL);
|
||||||
|
|
||||||
|
_is_player_init = true;
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_extra_player_del(void)
|
||||||
|
{
|
||||||
|
_is_player_init = false;
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(audio_player_delete(), TAG, "audio_player_delete failed");
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_extra_file_instance_init(const char *path, file_iterator_instance_t **ret_instance)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(path, ESP_FAIL, TAG, "path is NULL");
|
||||||
|
ESP_RETURN_ON_FALSE(ret_instance, ESP_FAIL, TAG, "ret_instance is NULL");
|
||||||
|
|
||||||
|
file_iterator_instance_t *file_iterator = file_iterator_new(path);
|
||||||
|
ESP_RETURN_ON_FALSE(file_iterator, ESP_FAIL, TAG, "file_iterator_new failed, %s", path);
|
||||||
|
|
||||||
|
*ret_instance = file_iterator;
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_extra_player_play_index(file_iterator_instance_t *instance, int index)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(instance, ESP_FAIL, TAG, "instance is NULL");
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "play_index(%d)", index);
|
||||||
|
char filename[128];
|
||||||
|
int retval = file_iterator_get_full_path_from_index(instance, index, filename, sizeof(filename));
|
||||||
|
ESP_RETURN_ON_FALSE(retval != 0, ESP_FAIL, TAG, "file_iterator_get_full_path_from_index failed");
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "opening file '%s'", filename);
|
||||||
|
FILE *fp = fopen(filename, "rb");
|
||||||
|
ESP_RETURN_ON_FALSE(fp, ESP_FAIL, TAG, "unable to open file");
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Playing '%s'", filename);
|
||||||
|
ESP_RETURN_ON_ERROR(audio_player_play(fp), TAG, "audio_player_play failed");
|
||||||
|
|
||||||
|
memcpy(audio_file_path, filename, sizeof(audio_file_path));
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_extra_player_play_file(const char *file_path)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "opening file '%s'", file_path);
|
||||||
|
FILE *fp = fopen(file_path, "rb");
|
||||||
|
ESP_RETURN_ON_FALSE(fp, ESP_FAIL, TAG, "unable to open file");
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Playing '%s'", file_path);
|
||||||
|
ESP_RETURN_ON_ERROR(audio_player_play(fp), TAG, "audio_player_play failed");
|
||||||
|
|
||||||
|
memcpy(audio_file_path, file_path, sizeof(audio_file_path));
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsp_extra_player_register_callback(audio_player_cb_t cb, void *user_data)
|
||||||
|
{
|
||||||
|
audio_idle_callback = cb;
|
||||||
|
audio_idle_cb_user_data = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bsp_extra_player_is_playing_by_path(const char *file_path)
|
||||||
|
{
|
||||||
|
return (strcmp(audio_file_path, file_path) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bsp_extra_player_is_playing_by_index(file_iterator_instance_t *instance, int index)
|
||||||
|
{
|
||||||
|
return (index == file_iterator_get_index(instance));
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
1e0436b3d220275d6b7930330b1a9b828fedf0dbc81006531e592b059842641e
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
idf_component_register(
|
||||||
|
SRCS "esp32_p4_function_ev_board.c"
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
PRIV_INCLUDE_DIRS "priv_include"
|
||||||
|
REQUIRES driver
|
||||||
|
PRIV_REQUIRES esp_lcd usb spiffs fatfs
|
||||||
|
)
|
||||||
+138
@@ -0,0 +1,138 @@
|
|||||||
|
menu "Board Support Package(ESP32-P4)"
|
||||||
|
|
||||||
|
config BSP_ERROR_CHECK
|
||||||
|
bool "Enable error check in BSP"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Error check assert the application before returning the error code.
|
||||||
|
|
||||||
|
menu "I2C"
|
||||||
|
config BSP_I2C_NUM
|
||||||
|
int "I2C peripheral index"
|
||||||
|
default 1
|
||||||
|
range 0 1
|
||||||
|
help
|
||||||
|
ESP32P4 has two I2C peripherals, pick the one you want to use.
|
||||||
|
|
||||||
|
config BSP_I2C_FAST_MODE
|
||||||
|
bool "Enable I2C fast mode"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
I2C has two speed modes: normal (100kHz) and fast (400kHz).
|
||||||
|
|
||||||
|
config BSP_I2C_CLK_SPEED_HZ
|
||||||
|
int
|
||||||
|
default 400000 if BSP_I2C_FAST_MODE
|
||||||
|
default 100000
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
menu "I2S"
|
||||||
|
config BSP_I2S_NUM
|
||||||
|
int "I2S peripheral index"
|
||||||
|
default 1
|
||||||
|
range 0 2
|
||||||
|
help
|
||||||
|
ESP32P4 has three I2S peripherals, pick the one you want to use.
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
menu "uSD card - Virtual File System"
|
||||||
|
config BSP_SD_FORMAT_ON_MOUNT_FAIL
|
||||||
|
bool "Format uSD card if mounting fails"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
The SDMMC host will format (FAT) the uSD card if it fails to mount the filesystem.
|
||||||
|
|
||||||
|
config BSP_SD_MOUNT_POINT
|
||||||
|
string "uSD card mount point"
|
||||||
|
default "/sdcard"
|
||||||
|
help
|
||||||
|
Mount point of the uSD card in the Virtual File System
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
menu "SPIFFS - Virtual File System"
|
||||||
|
config BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL
|
||||||
|
bool "Format SPIFFS if mounting fails"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Format SPIFFS if it fails to mount the filesystem.
|
||||||
|
|
||||||
|
config BSP_SPIFFS_MOUNT_POINT
|
||||||
|
string "SPIFFS mount point"
|
||||||
|
default "/spiffs"
|
||||||
|
help
|
||||||
|
Mount point of SPIFFS in the Virtual File System.
|
||||||
|
|
||||||
|
config BSP_SPIFFS_PARTITION_LABEL
|
||||||
|
string "Partition label of SPIFFS"
|
||||||
|
default "storage"
|
||||||
|
help
|
||||||
|
Partition label which stores SPIFFS.
|
||||||
|
|
||||||
|
config BSP_SPIFFS_MAX_FILES
|
||||||
|
int "Max files supported for SPIFFS VFS"
|
||||||
|
default 5
|
||||||
|
help
|
||||||
|
Supported max files for SPIFFS in the Virtual File System.
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
menu "Display"
|
||||||
|
config BSP_LCD_DPI_BUFFER_NUMS
|
||||||
|
int "Set number of frame buffers"
|
||||||
|
default 1
|
||||||
|
range 1 3
|
||||||
|
help
|
||||||
|
Let DPI LCD driver create a specified number of frame-size buffers. Only when it is set to multiple can the avoiding tearing be turned on.
|
||||||
|
|
||||||
|
config BSP_DISPLAY_LVGL_AVOID_TEAR
|
||||||
|
bool "Avoid tearing effect"
|
||||||
|
depends on BSP_LCD_DPI_BUFFER_NUMS > 1
|
||||||
|
default "n"
|
||||||
|
help
|
||||||
|
Avoid tearing effect through LVGL buffer mode and double frame buffers of RGB LCD. This feature is only available for RGB LCD.
|
||||||
|
|
||||||
|
choice BSP_DISPLAY_LVGL_MODE
|
||||||
|
depends on BSP_DISPLAY_LVGL_AVOID_TEAR
|
||||||
|
prompt "Select LVGL buffer mode"
|
||||||
|
default BSP_DISPLAY_LVGL_FULL_REFRESH
|
||||||
|
config BSP_DISPLAY_LVGL_FULL_REFRESH
|
||||||
|
bool "Full refresh"
|
||||||
|
config BSP_DISPLAY_LVGL_DIRECT_MODE
|
||||||
|
bool "Direct mode"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config BSP_DISPLAY_BRIGHTNESS_LEDC_CH
|
||||||
|
int "LEDC channel index"
|
||||||
|
default 1
|
||||||
|
range 0 7
|
||||||
|
help
|
||||||
|
LEDC channel is used to generate PWM signal that controls display brightness.
|
||||||
|
Set LEDC index that should be used.
|
||||||
|
|
||||||
|
choice BSP_LCD_COLOR_FORMAT
|
||||||
|
prompt "Select LCD color format"
|
||||||
|
default BSP_LCD_COLOR_FORMAT_RGB565
|
||||||
|
help
|
||||||
|
Select the LCD color format RGB565/RGB888.
|
||||||
|
|
||||||
|
config BSP_LCD_COLOR_FORMAT_RGB565
|
||||||
|
bool "RGB565"
|
||||||
|
config BSP_LCD_COLOR_FORMAT_RGB888
|
||||||
|
bool "RGB888"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
choice BSP_LCD_TYPE
|
||||||
|
prompt "Select LCD type"
|
||||||
|
default BSP_LCD_TYPE_1024_600
|
||||||
|
help
|
||||||
|
Select the LCD.
|
||||||
|
|
||||||
|
config BSP_LCD_TYPE_1024_600
|
||||||
|
bool "LCD 7-inch 1024x600 - ek79007"
|
||||||
|
config BSP_LCD_TYPE_1280_800
|
||||||
|
bool "LCD 1280x800 - ili9881c"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
endmenu
|
||||||
+202
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
# BSP: ESP32-P4 Function EV Board
|
||||||
|
|
||||||
|
[](https://components.espressif.com/components/espressif/esp32_p4_function_ev_board)
|
||||||
|
|
||||||
|
ESP32-P4 Function EV Board is internal Espressif board for testing features on ESP32P4 chip.
|
||||||
|
|
||||||
|
| HW version | BSP Version |
|
||||||
|
| :--------: | :---------: |
|
||||||
|
| V1.0 | ^1 |
|
||||||
|
| V1.2 | ^2 |
|
||||||
|
| [V1.4](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/user_guide.html) | ^3 |
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Configuration in `menuconfig`.
|
||||||
|
|
||||||
|
Selection LCD display `Board Support Package(ESP32-P4) --> Display --> Select LCD type`
|
||||||
|
- LCD 7-inch 1280x800 - ili9881c (default)
|
||||||
|
- LCD 1024x600 - ek79007
|
||||||
|
|
||||||
|
Selection color format `Board Support Package(ESP32-P4) --> Display --> Select LCD color format`
|
||||||
|
- RGB565 (default)
|
||||||
|
- RGB888
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Autogenerated start: Dependencies -->
|
||||||
|
### Capabilities and dependencies
|
||||||
|
| Capability | Available | Component |Version|
|
||||||
|
|-------------|------------------|----------------------------------------------------------------------------------------------------------|-------|
|
||||||
|
| DISPLAY |:heavy_check_mark:| [espressif/esp_lcd_ek79007](https://components.espressif.com/components/espressif/esp_lcd_ek79007) | 1.* |
|
||||||
|
| LVGL_PORT |:heavy_check_mark:| [espressif/esp_lvgl_port](https://components.espressif.com/components/espressif/esp_lvgl_port) | ^2 |
|
||||||
|
| TOUCH |:heavy_check_mark:|[espressif/esp_lcd_touch_gt911](https://components.espressif.com/components/espressif/esp_lcd_touch_gt911)| ^1 |
|
||||||
|
| BUTTONS | :x: | | |
|
||||||
|
| AUDIO |:heavy_check_mark:| [espressif/esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev) | 1.2.* |
|
||||||
|
|AUDIO_SPEAKER|:heavy_check_mark:| | |
|
||||||
|
| AUDIO_MIC |:heavy_check_mark:| | |
|
||||||
|
| SDCARD |:heavy_check_mark:| idf | >=5.3 |
|
||||||
|
| IMU | :x: | | |
|
||||||
|
<!-- Autogenerated end: Dependencies -->
|
||||||
+736
@@ -0,0 +1,736 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/ledc.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_spiffs.h"
|
||||||
|
#include "esp_lcd_panel_ops.h"
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
#include "esp_ldo_regulator.h"
|
||||||
|
#include "esp_vfs_fat.h"
|
||||||
|
#include "usb/usb_host.h"
|
||||||
|
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
|
||||||
|
|
||||||
|
#include "esp_lcd_jd9365_10_1.h"
|
||||||
|
#if CONFIG_BSP_LCD_TYPE_1024_600
|
||||||
|
#include "esp_lcd_ek79007.h"
|
||||||
|
#else
|
||||||
|
#include "esp_lcd_ili9881c.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "bsp/esp32_p4_function_ev_board.h"
|
||||||
|
#include "bsp/display.h"
|
||||||
|
#include "bsp/touch.h"
|
||||||
|
#include "esp_lcd_touch_gt911.h"
|
||||||
|
#include "bsp_err_check.h"
|
||||||
|
#include "esp_codec_dev_defaults.h"
|
||||||
|
#include "lvgl.h"
|
||||||
|
|
||||||
|
static const char *TAG = "ESP32_P4_EV";
|
||||||
|
|
||||||
|
#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
|
||||||
|
static lv_indev_t *disp_indev = NULL;
|
||||||
|
#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
|
||||||
|
|
||||||
|
sdmmc_card_t *bsp_sdcard = NULL; // Global uSD card handler
|
||||||
|
static bool i2c_initialized = false;
|
||||||
|
static TaskHandle_t usb_host_task; // USB Host Library task
|
||||||
|
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0))
|
||||||
|
static i2c_master_bus_handle_t i2c_handle = NULL; // I2C Handle
|
||||||
|
#endif
|
||||||
|
static i2s_chan_handle_t i2s_tx_chan = NULL;
|
||||||
|
static i2s_chan_handle_t i2s_rx_chan = NULL;
|
||||||
|
static const audio_codec_data_if_t *i2s_data_if = NULL; /* Codec data interface */
|
||||||
|
|
||||||
|
/* Can be used for `i2s_std_gpio_config_t` and/or `i2s_std_config_t` initialization */
|
||||||
|
#define BSP_I2S_GPIO_CFG \
|
||||||
|
{ \
|
||||||
|
.mclk = BSP_I2S_MCLK, \
|
||||||
|
.bclk = BSP_I2S_SCLK, \
|
||||||
|
.ws = BSP_I2S_LCLK, \
|
||||||
|
.dout = BSP_I2S_DOUT, \
|
||||||
|
.din = BSP_I2S_DSIN, \
|
||||||
|
.invert_flags = { \
|
||||||
|
.mclk_inv = false, \
|
||||||
|
.bclk_inv = false, \
|
||||||
|
.ws_inv = false, \
|
||||||
|
}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This configuration is used by default in `bsp_extra_audio_init()` */
|
||||||
|
#define BSP_I2S_DUPLEX_MONO_CFG(_sample_rate) \
|
||||||
|
{ \
|
||||||
|
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_sample_rate), \
|
||||||
|
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO), \
|
||||||
|
.gpio_cfg = BSP_I2S_GPIO_CFG, \
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_i2c_init(void)
|
||||||
|
{
|
||||||
|
/* I2C was initialized before */
|
||||||
|
if (i2c_initialized) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_master_bus_config_t i2c_bus_conf = {
|
||||||
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||||
|
.sda_io_num = BSP_I2C_SDA,
|
||||||
|
.scl_io_num = BSP_I2C_SCL,
|
||||||
|
.i2c_port = BSP_I2C_NUM,
|
||||||
|
};
|
||||||
|
BSP_ERROR_CHECK_RETURN_ERR(i2c_new_master_bus(&i2c_bus_conf, &i2c_handle));
|
||||||
|
|
||||||
|
i2c_initialized = true;
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_i2c_deinit(void)
|
||||||
|
{
|
||||||
|
BSP_ERROR_CHECK_RETURN_ERR(i2c_del_master_bus(i2c_handle));
|
||||||
|
i2c_initialized = false;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_master_bus_handle_t bsp_i2c_get_handle(void)
|
||||||
|
{
|
||||||
|
return i2c_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_sdcard_mount(void)
|
||||||
|
{
|
||||||
|
const esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||||
|
#ifdef CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL
|
||||||
|
.format_if_mount_failed = true,
|
||||||
|
#else
|
||||||
|
.format_if_mount_failed = false,
|
||||||
|
#endif
|
||||||
|
.max_files = 5,
|
||||||
|
.allocation_unit_size = 64 * 1024
|
||||||
|
};
|
||||||
|
|
||||||
|
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||||
|
host.slot = SDMMC_HOST_SLOT_0;
|
||||||
|
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||||
|
|
||||||
|
sd_pwr_ctrl_ldo_config_t ldo_config = {
|
||||||
|
.ldo_chan_id = 4,
|
||||||
|
};
|
||||||
|
sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL;
|
||||||
|
esp_err_t ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create a new on-chip LDO power control driver");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
host.pwr_ctrl_handle = pwr_ctrl_handle;
|
||||||
|
|
||||||
|
const sdmmc_slot_config_t slot_config = {
|
||||||
|
/* SD card is connected to Slot 0 pins. Slot 0 uses IO MUX, so not specifying the pins here */
|
||||||
|
.cd = SDMMC_SLOT_NO_CD,
|
||||||
|
.wp = SDMMC_SLOT_NO_WP,
|
||||||
|
.width = 4,
|
||||||
|
.flags = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
return esp_vfs_fat_sdmmc_mount(BSP_SD_MOUNT_POINT, &host, &slot_config, &mount_config, &bsp_sdcard);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_sdcard_unmount(void)
|
||||||
|
{
|
||||||
|
return esp_vfs_fat_sdcard_unmount(BSP_SD_MOUNT_POINT, bsp_sdcard);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_spiffs_mount(void)
|
||||||
|
{
|
||||||
|
esp_vfs_spiffs_conf_t conf = {
|
||||||
|
.base_path = CONFIG_BSP_SPIFFS_MOUNT_POINT,
|
||||||
|
.partition_label = CONFIG_BSP_SPIFFS_PARTITION_LABEL,
|
||||||
|
.max_files = CONFIG_BSP_SPIFFS_MAX_FILES,
|
||||||
|
#ifdef CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL
|
||||||
|
.format_if_mount_failed = true,
|
||||||
|
#else
|
||||||
|
.format_if_mount_failed = false,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t ret_val = esp_vfs_spiffs_register(&conf);
|
||||||
|
|
||||||
|
BSP_ERROR_CHECK_RETURN_ERR(ret_val);
|
||||||
|
|
||||||
|
size_t total = 0, used = 0;
|
||||||
|
ret_val = esp_spiffs_info(conf.partition_label, &total, &used);
|
||||||
|
if (ret_val != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret_val));
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_spiffs_unmount(void)
|
||||||
|
{
|
||||||
|
return esp_vfs_spiffs_unregister(CONFIG_BSP_SPIFFS_PARTITION_LABEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config)
|
||||||
|
{
|
||||||
|
if (i2s_tx_chan && i2s_rx_chan) {
|
||||||
|
/* Audio was initialized before */
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup I2S peripheral */
|
||||||
|
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(CONFIG_BSP_I2S_NUM, I2S_ROLE_MASTER);
|
||||||
|
chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer
|
||||||
|
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &i2s_tx_chan, &i2s_rx_chan));
|
||||||
|
|
||||||
|
/* Setup I2S channels */
|
||||||
|
const i2s_std_config_t std_cfg_default = BSP_I2S_DUPLEX_MONO_CFG(22050);
|
||||||
|
const i2s_std_config_t *p_i2s_cfg = &std_cfg_default;
|
||||||
|
if (i2s_config != NULL) {
|
||||||
|
p_i2s_cfg = i2s_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2s_tx_chan != NULL) {
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_tx_chan, p_i2s_cfg));
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_enable(i2s_tx_chan));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2s_rx_chan != NULL) {
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_rx_chan, p_i2s_cfg));
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_enable(i2s_rx_chan));
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||||
|
.port = CONFIG_BSP_I2S_NUM,
|
||||||
|
.tx_handle = i2s_tx_chan,
|
||||||
|
.rx_handle = i2s_rx_chan,
|
||||||
|
};
|
||||||
|
i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void)
|
||||||
|
{
|
||||||
|
if (i2s_data_if == NULL) {
|
||||||
|
/* Initilize I2C */
|
||||||
|
ESP_ERROR_CHECK(bsp_i2c_init());
|
||||||
|
/* Configure I2S peripheral and Power Amplifier */
|
||||||
|
ESP_ERROR_CHECK(bsp_audio_init(NULL));
|
||||||
|
}
|
||||||
|
assert(i2s_data_if);
|
||||||
|
|
||||||
|
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
|
||||||
|
|
||||||
|
audio_codec_i2c_cfg_t i2c_cfg = {
|
||||||
|
.port = BSP_I2C_NUM,
|
||||||
|
.addr = ES8311_CODEC_DEFAULT_ADDR,
|
||||||
|
.bus_handle = i2c_handle,
|
||||||
|
};
|
||||||
|
const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||||
|
assert(i2c_ctrl_if);
|
||||||
|
|
||||||
|
esp_codec_dev_hw_gain_t gain = {
|
||||||
|
.pa_voltage = 5.0,
|
||||||
|
.codec_dac_voltage = 3.3,
|
||||||
|
};
|
||||||
|
|
||||||
|
es8311_codec_cfg_t es8311_cfg = {
|
||||||
|
.ctrl_if = i2c_ctrl_if,
|
||||||
|
.gpio_if = gpio_if,
|
||||||
|
.codec_mode = ESP_CODEC_DEV_TYPE_OUT,
|
||||||
|
.pa_pin = BSP_POWER_AMP_IO,
|
||||||
|
.pa_reverted = false,
|
||||||
|
.master_mode = false,
|
||||||
|
.use_mclk = true,
|
||||||
|
.digital_mic = false,
|
||||||
|
.invert_mclk = false,
|
||||||
|
.invert_sclk = false,
|
||||||
|
.hw_gain = gain,
|
||||||
|
};
|
||||||
|
const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg);
|
||||||
|
assert(es8311_dev);
|
||||||
|
|
||||||
|
esp_codec_dev_cfg_t codec_dev_cfg = {
|
||||||
|
.dev_type = ESP_CODEC_DEV_TYPE_IN_OUT,
|
||||||
|
.codec_if = es8311_dev,
|
||||||
|
.data_if = i2s_data_if,
|
||||||
|
};
|
||||||
|
return esp_codec_dev_new(&codec_dev_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void)
|
||||||
|
{
|
||||||
|
if (i2s_data_if == NULL) {
|
||||||
|
/* Initilize I2C */
|
||||||
|
ESP_ERROR_CHECK(bsp_i2c_init());
|
||||||
|
/* Configure I2S peripheral and Power Amplifier */
|
||||||
|
ESP_ERROR_CHECK(bsp_audio_init(NULL));
|
||||||
|
}
|
||||||
|
assert(i2s_data_if);
|
||||||
|
|
||||||
|
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
|
||||||
|
|
||||||
|
audio_codec_i2c_cfg_t i2c_cfg = {
|
||||||
|
.port = BSP_I2C_NUM,
|
||||||
|
.addr = ES8311_CODEC_DEFAULT_ADDR,
|
||||||
|
.bus_handle = i2c_handle,
|
||||||
|
};
|
||||||
|
const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||||
|
assert(i2c_ctrl_if);
|
||||||
|
|
||||||
|
esp_codec_dev_hw_gain_t gain = {
|
||||||
|
.pa_voltage = 5.0,
|
||||||
|
.codec_dac_voltage = 3.3,
|
||||||
|
};
|
||||||
|
|
||||||
|
es8311_codec_cfg_t es8311_cfg = {
|
||||||
|
.ctrl_if = i2c_ctrl_if,
|
||||||
|
.gpio_if = gpio_if,
|
||||||
|
.codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH,
|
||||||
|
.pa_pin = BSP_POWER_AMP_IO,
|
||||||
|
.pa_reverted = false,
|
||||||
|
.master_mode = false,
|
||||||
|
.use_mclk = true,
|
||||||
|
.digital_mic = false,
|
||||||
|
.invert_mclk = false,
|
||||||
|
.invert_sclk = false,
|
||||||
|
.hw_gain = gain,
|
||||||
|
};
|
||||||
|
|
||||||
|
const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg);
|
||||||
|
assert(es8311_dev);
|
||||||
|
|
||||||
|
esp_codec_dev_cfg_t codec_es8311_dev_cfg = {
|
||||||
|
.dev_type = ESP_CODEC_DEV_TYPE_IN,
|
||||||
|
.codec_if = es8311_dev,
|
||||||
|
.data_if = i2s_data_if,
|
||||||
|
};
|
||||||
|
return esp_codec_dev_new(&codec_es8311_dev_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bit number used to represent command and parameter
|
||||||
|
#define LCD_LEDC_CH CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH
|
||||||
|
|
||||||
|
esp_err_t bsp_display_brightness_init(void)
|
||||||
|
{
|
||||||
|
bsp_i2c_init();
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_display_brightness_set(int brightness_percent)
|
||||||
|
{
|
||||||
|
if (brightness_percent > 100) {
|
||||||
|
brightness_percent = 100;
|
||||||
|
}
|
||||||
|
if (brightness_percent < 0) {
|
||||||
|
brightness_percent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t data = (uint8_t)(255 * brightness_percent * 0.01);
|
||||||
|
uint8_t chip_addr = 0x45;
|
||||||
|
|
||||||
|
uint8_t data_addr = 0x96;
|
||||||
|
uint8_t data_to_send[2] = {data_addr, data};
|
||||||
|
|
||||||
|
i2c_device_config_t i2c_dev_conf = {
|
||||||
|
.scl_speed_hz = 100 * 1000,
|
||||||
|
.device_address = chip_addr,
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c_master_dev_handle_t dev_handle = NULL;
|
||||||
|
if (i2c_master_bus_add_device(i2c_handle, &i2c_dev_conf, &dev_handle) != ESP_OK)
|
||||||
|
{
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t ret = i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
i2c_master_bus_rm_device(dev_handle);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_master_bus_rm_device(dev_handle);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_display_backlight_off(void)
|
||||||
|
{
|
||||||
|
return bsp_display_brightness_set(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_display_backlight_on(void)
|
||||||
|
{
|
||||||
|
return bsp_display_brightness_set(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t bsp_enable_dsi_phy_power(void)
|
||||||
|
{
|
||||||
|
#if BSP_MIPI_DSI_PHY_PWR_LDO_CHAN > 0
|
||||||
|
// Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state
|
||||||
|
static esp_ldo_channel_handle_t phy_pwr_chan = NULL;
|
||||||
|
esp_ldo_channel_config_t ldo_cfg = {
|
||||||
|
.chan_id = BSP_MIPI_DSI_PHY_PWR_LDO_CHAN,
|
||||||
|
.voltage_mv = BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(esp_ldo_acquire_channel(&ldo_cfg, &phy_pwr_chan), TAG, "Acquire LDO channel for DPHY failed");
|
||||||
|
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
|
||||||
|
#endif // BSP_MIPI_DSI_PHY_PWR_LDO_CHAN > 0
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
bsp_lcd_handles_t handles;
|
||||||
|
ret = bsp_display_new_with_handles(config, &handles);
|
||||||
|
|
||||||
|
*ret_panel = handles.panel;
|
||||||
|
*ret_io = handles.io;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_display_new_with_handles(const bsp_display_config_t *config, bsp_lcd_handles_t *ret_handles)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed");
|
||||||
|
ESP_RETURN_ON_ERROR(bsp_enable_dsi_phy_power(), TAG, "DSI PHY power failed");
|
||||||
|
|
||||||
|
/* create MIPI DSI bus first, it will initialize the DSI PHY as well */
|
||||||
|
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
|
||||||
|
esp_lcd_dsi_bus_config_t bus_config = {
|
||||||
|
.bus_id = 0,
|
||||||
|
.num_data_lanes = BSP_LCD_MIPI_DSI_LANE_NUM,
|
||||||
|
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
|
||||||
|
.lane_bit_rate_mbps = BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS,
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus), TAG, "New DSI bus init failed");
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
|
||||||
|
// we use DBI interface to send LCD commands and parameters
|
||||||
|
esp_lcd_panel_io_handle_t io;
|
||||||
|
esp_lcd_dbi_io_config_t dbi_config = {
|
||||||
|
.virtual_channel = 0,
|
||||||
|
.lcd_cmd_bits = 8, // according to the LCD ILI9881C spec
|
||||||
|
.lcd_param_bits = 8, // according to the LCD ILI9881C spec
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io), err, TAG, "New panel IO failed");
|
||||||
|
|
||||||
|
esp_lcd_panel_handle_t disp_panel = NULL;
|
||||||
|
#if CONFIG_BSP_LCD_TYPE_1024_600
|
||||||
|
// create EK79007 control panel
|
||||||
|
ESP_LOGI(TAG, "Install JD9365 LCD control panel");
|
||||||
|
|
||||||
|
#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
|
||||||
|
esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888);
|
||||||
|
#else
|
||||||
|
esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
|
||||||
|
#endif
|
||||||
|
dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS;
|
||||||
|
|
||||||
|
jd9365_vendor_config_t vendor_config = {
|
||||||
|
.flags = {
|
||||||
|
.use_mipi_interface = 1,
|
||||||
|
},
|
||||||
|
.mipi_config = {
|
||||||
|
.dsi_bus = mipi_dsi_bus,
|
||||||
|
.dpi_config = &dpi_config,
|
||||||
|
.lane_num = 2
|
||||||
|
},
|
||||||
|
};
|
||||||
|
esp_lcd_panel_dev_config_t lcd_dev_config = {
|
||||||
|
#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
|
||||||
|
.bits_per_pixel = 24,
|
||||||
|
#else
|
||||||
|
.bits_per_pixel = 16,
|
||||||
|
#endif
|
||||||
|
.rgb_ele_order = BSP_LCD_COLOR_SPACE,
|
||||||
|
.reset_gpio_num = BSP_LCD_RST,
|
||||||
|
.vendor_config = &vendor_config,
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_jd9365(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel JD9365 failed");
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed");
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed");
|
||||||
|
// ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(disp_panel, true), err, TAG, "LCD panel ON failed");
|
||||||
|
// ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_sleep(disp_panel, false), err, TAG, "LCD Sleep ON failed");
|
||||||
|
|
||||||
|
#else
|
||||||
|
// create ILI9881C control panel
|
||||||
|
ESP_LOGI(TAG, "Install ILI9881C LCD control panel");
|
||||||
|
#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
|
||||||
|
esp_lcd_dpi_panel_config_t dpi_config = ILI9881C_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888);
|
||||||
|
#else
|
||||||
|
esp_lcd_dpi_panel_config_t dpi_config = ILI9881C_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
|
||||||
|
#endif
|
||||||
|
dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS;
|
||||||
|
|
||||||
|
ili9881c_vendor_config_t vendor_config = {
|
||||||
|
.mipi_config = {
|
||||||
|
.dsi_bus = mipi_dsi_bus,
|
||||||
|
.dpi_config = &dpi_config,
|
||||||
|
.lane_num = BSP_LCD_MIPI_DSI_LANE_NUM,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const esp_lcd_panel_dev_config_t lcd_dev_config = {
|
||||||
|
.reset_gpio_num = BSP_LCD_RST,
|
||||||
|
.rgb_ele_order = BSP_LCD_COLOR_SPACE,
|
||||||
|
.bits_per_pixel = 16,
|
||||||
|
.vendor_config = &vendor_config,
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_ili9881c(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel ILI9881C failed");
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed");
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed");
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(disp_panel, true), err, TAG, "LCD panel ON failed");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Return all handles */
|
||||||
|
ret_handles->io = io;
|
||||||
|
ret_handles->mipi_dsi_bus = mipi_dsi_bus;
|
||||||
|
ret_handles->panel = disp_panel;
|
||||||
|
ret_handles->control = NULL;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Display initialized");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (disp_panel) {
|
||||||
|
esp_lcd_panel_del(disp_panel);
|
||||||
|
}
|
||||||
|
if (io) {
|
||||||
|
esp_lcd_panel_io_del(io);
|
||||||
|
}
|
||||||
|
if (mipi_dsi_bus) {
|
||||||
|
esp_lcd_del_dsi_bus(mipi_dsi_bus);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch)
|
||||||
|
{
|
||||||
|
/* Initilize I2C */
|
||||||
|
BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init());
|
||||||
|
|
||||||
|
/* Initialize touch */
|
||||||
|
const esp_lcd_touch_config_t tp_cfg = {
|
||||||
|
.x_max = BSP_LCD_H_RES,
|
||||||
|
.y_max = BSP_LCD_V_RES,
|
||||||
|
.rst_gpio_num = BSP_LCD_TOUCH_RST, // Shared with LCD reset
|
||||||
|
.int_gpio_num = BSP_LCD_TOUCH_INT,
|
||||||
|
.levels = {
|
||||||
|
.reset = 0,
|
||||||
|
.interrupt = 0,
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.swap_xy = 0,
|
||||||
|
#if CONFIG_BSP_LCD_TYPE_1024_600
|
||||||
|
.mirror_x = 0,
|
||||||
|
.mirror_y = 0,
|
||||||
|
#else
|
||||||
|
.mirror_x = 0,
|
||||||
|
.mirror_y = 0,
|
||||||
|
#endif
|
||||||
|
},
|
||||||
|
};
|
||||||
|
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
|
||||||
|
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||||
|
tp_io_config.scl_speed_hz = 100000;
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle), TAG, "");
|
||||||
|
return esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, ret_touch);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
|
||||||
|
static lv_display_t *bsp_display_lcd_init(const bsp_display_cfg_t *cfg)
|
||||||
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
bsp_lcd_handles_t lcd_panels;
|
||||||
|
BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new_with_handles(NULL, &lcd_panels));
|
||||||
|
|
||||||
|
/* Add LCD screen */
|
||||||
|
ESP_LOGD(TAG, "Add LCD screen");
|
||||||
|
const lvgl_port_display_cfg_t disp_cfg = {
|
||||||
|
.io_handle = lcd_panels.io,
|
||||||
|
.panel_handle = lcd_panels.panel,
|
||||||
|
.control_handle = lcd_panels.control,
|
||||||
|
.buffer_size = cfg->buffer_size,
|
||||||
|
.double_buffer = cfg->double_buffer,
|
||||||
|
.hres = BSP_LCD_H_RES,
|
||||||
|
.vres = BSP_LCD_V_RES,
|
||||||
|
.monochrome = false,
|
||||||
|
/* Rotation values must be same as used in esp_lcd for initial settings of the screen */
|
||||||
|
.rotation = {
|
||||||
|
.swap_xy = true,
|
||||||
|
.mirror_x = false,
|
||||||
|
.mirror_y = false,
|
||||||
|
},
|
||||||
|
#if LVGL_VERSION_MAJOR >= 9
|
||||||
|
#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
|
||||||
|
.color_format = LV_COLOR_FORMAT_RGB888,
|
||||||
|
#else
|
||||||
|
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
.flags = {
|
||||||
|
.buff_dma = cfg->flags.buff_dma,
|
||||||
|
.buff_spiram = cfg->flags.buff_spiram,
|
||||||
|
#if LVGL_VERSION_MAJOR >= 9
|
||||||
|
.swap_bytes = (BSP_LCD_BIGENDIAN ? true : false),
|
||||||
|
#endif
|
||||||
|
#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR
|
||||||
|
.sw_rotate = false, /* Avoid tearing is not supported for SW rotation */
|
||||||
|
#else
|
||||||
|
.sw_rotate = cfg->flags.sw_rotate, /* Only SW rotation is supported for 90° and 270° */
|
||||||
|
#endif
|
||||||
|
#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH
|
||||||
|
.full_refresh = true,
|
||||||
|
#elif CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE
|
||||||
|
.direct_mode = true,
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const lvgl_port_display_dsi_cfg_t dpi_cfg = {
|
||||||
|
.flags = {
|
||||||
|
#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR
|
||||||
|
.avoid_tearing = true,
|
||||||
|
#else
|
||||||
|
.avoid_tearing = false,
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return lvgl_port_add_disp_dsi(&disp_cfg, &dpi_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_indev_t *bsp_display_indev_init(lv_display_t *disp)
|
||||||
|
{
|
||||||
|
esp_lcd_touch_handle_t tp;
|
||||||
|
BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp));
|
||||||
|
assert(tp);
|
||||||
|
|
||||||
|
/* Add touch input (for selected screen) */
|
||||||
|
const lvgl_port_touch_cfg_t touch_cfg = {
|
||||||
|
.disp = disp,
|
||||||
|
.handle = tp,
|
||||||
|
};
|
||||||
|
|
||||||
|
return lvgl_port_add_touch(&touch_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_display_t *bsp_display_start(void)
|
||||||
|
{
|
||||||
|
bsp_display_cfg_t cfg = {
|
||||||
|
.lvgl_port_cfg = ESP_LVGL_PORT_INIT_CONFIG(),
|
||||||
|
.buffer_size = BSP_LCD_DRAW_BUFF_SIZE,
|
||||||
|
.double_buffer = BSP_LCD_DRAW_BUFF_DOUBLE,
|
||||||
|
.flags = {
|
||||||
|
#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
|
||||||
|
.buff_dma = false,
|
||||||
|
#else
|
||||||
|
.buff_dma = true,
|
||||||
|
#endif
|
||||||
|
.buff_spiram = false,
|
||||||
|
.sw_rotate = true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return bsp_display_start_with_config(&cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg)
|
||||||
|
{
|
||||||
|
lv_display_t *disp;
|
||||||
|
|
||||||
|
assert(cfg != NULL);
|
||||||
|
BSP_ERROR_CHECK_RETURN_NULL(lvgl_port_init(&cfg->lvgl_port_cfg));
|
||||||
|
|
||||||
|
BSP_ERROR_CHECK_RETURN_NULL(bsp_display_brightness_init());
|
||||||
|
|
||||||
|
BSP_NULL_CHECK(disp = bsp_display_lcd_init(cfg), NULL);
|
||||||
|
|
||||||
|
BSP_NULL_CHECK(disp_indev = bsp_display_indev_init(disp), NULL);
|
||||||
|
|
||||||
|
return disp;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_indev_t *bsp_display_get_input_dev(void)
|
||||||
|
{
|
||||||
|
return disp_indev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation)
|
||||||
|
{
|
||||||
|
lv_disp_set_rotation(disp, rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bsp_display_lock(uint32_t timeout_ms)
|
||||||
|
{
|
||||||
|
return lvgl_port_lock(timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bsp_display_unlock(void)
|
||||||
|
{
|
||||||
|
lvgl_port_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
|
||||||
|
|
||||||
|
static void usb_lib_task(void *arg)
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
// Start handling system events
|
||||||
|
uint32_t event_flags;
|
||||||
|
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
|
||||||
|
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
|
||||||
|
ESP_ERROR_CHECK(usb_host_device_free_all());
|
||||||
|
}
|
||||||
|
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
|
||||||
|
ESP_LOGI(TAG, "USB: All devices freed");
|
||||||
|
// Continue handling USB events to allow device reconnection
|
||||||
|
// The only way this task can be stopped is by calling bsp_usb_host_stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_usb_host_start(bsp_usb_host_power_mode_t mode, bool limit_500mA)
|
||||||
|
{
|
||||||
|
//Install USB Host driver. Should only be called once in entire application
|
||||||
|
ESP_LOGI(TAG, "Installing USB Host");
|
||||||
|
const usb_host_config_t host_config = {
|
||||||
|
.skip_phy_setup = false,
|
||||||
|
.intr_flags = ESP_INTR_FLAG_LEVEL1,
|
||||||
|
};
|
||||||
|
BSP_ERROR_CHECK_RETURN_ERR(usb_host_install(&host_config));
|
||||||
|
|
||||||
|
// Create a task that will handle USB library events
|
||||||
|
if (xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, 10, &usb_host_task) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Creating USB host lib task failed");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bsp_usb_host_stop(void)
|
||||||
|
{
|
||||||
|
usb_host_uninstall();
|
||||||
|
if (usb_host_task) {
|
||||||
|
vTaskSuspend(usb_host_task);
|
||||||
|
vTaskDelete(usb_host_task);
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
dependencies:
|
||||||
|
esp_codec_dev:
|
||||||
|
public: true
|
||||||
|
version: 1.2.*
|
||||||
|
esp_lcd_ek79007: 1.*
|
||||||
|
esp_lcd_ili9881c: 1.*
|
||||||
|
esp_lcd_touch_gt911: ^1
|
||||||
|
waveshare/esp_lcd_jd9365_10_1: ^1.0.3
|
||||||
|
espressif/esp_lvgl_port:
|
||||||
|
public: true
|
||||||
|
version: ^2
|
||||||
|
idf: '>=5.3'
|
||||||
|
lvgl/lvgl: '>=8,<10'
|
||||||
|
description: Board Support Package (BSP) for ESP32-P4 Function EV Board (preview)
|
||||||
|
repository: git://github.com/espressif/esp-bsp.git
|
||||||
|
repository_info:
|
||||||
|
commit_sha: 0064ab931c38699fc993ee161713ac1d1c0943a5
|
||||||
|
path: bsp/esp32_p4_function_ev_board
|
||||||
|
tags:
|
||||||
|
- bsp
|
||||||
|
targets:
|
||||||
|
- esp32p4
|
||||||
|
url: https://github.com/espressif/esp-bsp/tree/master/bsp/esp32_p4_function_ev_board
|
||||||
|
version: 4.1.1
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* BSP configuration
|
||||||
|
**************************************************************************************************/
|
||||||
|
// By default, this BSP is shipped with LVGL graphical library. Enabling this option will exclude it.
|
||||||
|
// If you want to use BSP without LVGL, select BSP version with 'noglib' suffix.
|
||||||
|
#if !defined(BSP_CONFIG_NO_GRAPHIC_LIB) // Check if the symbol is not coming from compiler definitions (-D...)
|
||||||
|
#define BSP_CONFIG_NO_GRAPHIC_LIB (0)
|
||||||
|
#endif
|
||||||
+192
@@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BSP LCD
|
||||||
|
*
|
||||||
|
* This file offers API for basic LCD control.
|
||||||
|
* It is useful for users who want to use the LCD without the default Graphical Library LVGL.
|
||||||
|
*
|
||||||
|
* For standard LCD initialization with LVGL graphical library, you can call all-in-one function bsp_display_start().
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "esp_lcd_types.h"
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
/* LCD color formats */
|
||||||
|
#define ESP_LCD_COLOR_FORMAT_RGB565 (1)
|
||||||
|
#define ESP_LCD_COLOR_FORMAT_RGB888 (2)
|
||||||
|
|
||||||
|
/* LCD display color format */
|
||||||
|
#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
|
||||||
|
#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB888)
|
||||||
|
#else
|
||||||
|
#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB565)
|
||||||
|
#endif
|
||||||
|
/* LCD display color bytes endianess */
|
||||||
|
#define BSP_LCD_BIGENDIAN (0)
|
||||||
|
/* LCD display color bits */
|
||||||
|
#define BSP_LCD_BITS_PER_PIXEL (16)
|
||||||
|
/* LCD display color space */
|
||||||
|
#define BSP_LCD_COLOR_SPACE (ESP_LCD_COLOR_SPACE_RGB)
|
||||||
|
|
||||||
|
#if CONFIG_BSP_LCD_TYPE_1024_600
|
||||||
|
/* LCD display definition 1024x600 */
|
||||||
|
#define BSP_LCD_H_RES (800)
|
||||||
|
#define BSP_LCD_V_RES (1280)
|
||||||
|
|
||||||
|
#define BSP_LCD_MIPI_DSI_LCD_HSYNC (40)
|
||||||
|
#define BSP_LCD_MIPI_DSI_LCD_HBP (140)
|
||||||
|
#define BSP_LCD_MIPI_DSI_LCD_HFP (40)
|
||||||
|
#define BSP_LCD_MIPI_DSI_LCD_VSYNC (4)
|
||||||
|
#define BSP_LCD_MIPI_DSI_LCD_VBP (16)
|
||||||
|
#define BSP_LCD_MIPI_DSI_LCD_VFP (16)
|
||||||
|
#else
|
||||||
|
/* LCD display definition 1280x800 */
|
||||||
|
#define BSP_LCD_H_RES (800)
|
||||||
|
#define BSP_LCD_V_RES (1280)
|
||||||
|
|
||||||
|
#define BSP_LCD_MIPI_DSI_LCD_HSYNC (40)
|
||||||
|
#define BSP_LCD_MIPI_DSI_LCD_HBP (140)
|
||||||
|
#define BSP_LCD_MIPI_DSI_LCD_HFP (40)
|
||||||
|
#define BSP_LCD_MIPI_DSI_LCD_VSYNC (4)
|
||||||
|
#define BSP_LCD_MIPI_DSI_LCD_VBP (16)
|
||||||
|
#define BSP_LCD_MIPI_DSI_LCD_VFP (16)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BSP_LCD_MIPI_DSI_LANE_NUM (2) // 2 data lanes
|
||||||
|
#define BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS (1500) // 1Gbps
|
||||||
|
|
||||||
|
#define BSP_MIPI_DSI_PHY_PWR_LDO_CHAN (3) // LDO_VO3 is connected to VDD_MIPI_DPHY
|
||||||
|
#define BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief BSP display configuration structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int dummy;
|
||||||
|
} bsp_display_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief BSP display return handles
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
esp_lcd_dsi_bus_handle_t mipi_dsi_bus; /*!< MIPI DSI bus handle */
|
||||||
|
esp_lcd_panel_io_handle_t io; /*!< ESP LCD IO handle */
|
||||||
|
esp_lcd_panel_handle_t panel; /*!< ESP LCD panel (color) handle */
|
||||||
|
esp_lcd_panel_handle_t control; /*!< ESP LCD panel (control) handle */
|
||||||
|
} bsp_lcd_handles_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create new display panel
|
||||||
|
*
|
||||||
|
* For maximum flexibility, this function performs only reset and initialization of the display.
|
||||||
|
* You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off().
|
||||||
|
* The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(),
|
||||||
|
* bsp_display_brightness_set() (on supported boards) or implement your own backlight control.
|
||||||
|
*
|
||||||
|
* If you want to free resources allocated by this function, you can use esp_lcd API, ie.:
|
||||||
|
*
|
||||||
|
* \code{.c}
|
||||||
|
* esp_lcd_panel_del(panel);
|
||||||
|
* esp_lcd_panel_io_del(io);
|
||||||
|
* esp_lcd_del_dsi_bus(mipi_dsi_bus);
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* @param[in] config display configuration
|
||||||
|
* @param[out] ret_panel esp_lcd panel handle
|
||||||
|
* @param[out] ret_io esp_lcd IO handle
|
||||||
|
* @return
|
||||||
|
* - ESP_OK On success
|
||||||
|
* - Else esp_lcd failure
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create new display panel
|
||||||
|
*
|
||||||
|
* For maximum flexibility, this function performs only reset and initialization of the display.
|
||||||
|
* You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off().
|
||||||
|
* The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(),
|
||||||
|
* bsp_display_brightness_set() (on supported boards) or implement your own backlight control.
|
||||||
|
*
|
||||||
|
* If you want to free resources allocated by this function, you can use esp_lcd API, ie.:
|
||||||
|
*
|
||||||
|
* \code{.c}
|
||||||
|
* esp_lcd_panel_del(panel);
|
||||||
|
* esp_lcd_panel_del(control);
|
||||||
|
* esp_lcd_panel_io_del(io);
|
||||||
|
* esp_lcd_del_dsi_bus(mipi_dsi_bus);
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* @param[in] config display configuration
|
||||||
|
* @param[out] ret_handles all esp_lcd handles in one structure
|
||||||
|
* @return
|
||||||
|
* - ESP_OK On success
|
||||||
|
* - Else esp_lcd failure
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_display_new_with_handles(const bsp_display_config_t *config, bsp_lcd_handles_t *ret_handles);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize display's brightness
|
||||||
|
*
|
||||||
|
* Brightness is controlled with PWM signal to a pin controlling backlight.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK On success
|
||||||
|
* - ESP_ERR_INVALID_ARG Parameter error
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_display_brightness_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set display's brightness
|
||||||
|
*
|
||||||
|
* Brightness is controlled with PWM signal to a pin controlling backlight.
|
||||||
|
* Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new()
|
||||||
|
*
|
||||||
|
* @param[in] brightness_percent Brightness in [%]
|
||||||
|
* @return
|
||||||
|
* - ESP_OK On success
|
||||||
|
* - ESP_ERR_INVALID_ARG Parameter error
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_display_brightness_set(int brightness_percent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Turn on display backlight
|
||||||
|
*
|
||||||
|
* Brightness is controlled with PWM signal to a pin controlling backlight.
|
||||||
|
* Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new()
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK On success
|
||||||
|
* - ESP_ERR_INVALID_ARG Parameter error
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_display_backlight_on(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Turn off display backlight
|
||||||
|
*
|
||||||
|
* Brightness is controlled with PWM signal to a pin controlling backlight.
|
||||||
|
* Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new()
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK On success
|
||||||
|
* - ESP_ERR_INVALID_ARG Parameter error
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_display_backlight_off(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "bsp/esp32_p4_function_ev_board.h"
|
||||||
+382
@@ -0,0 +1,382 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief ESP BSP: ESP32-P4 Function EV Board
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
#include "driver/sdmmc_host.h"
|
||||||
|
#include "driver/i2s_std.h"
|
||||||
|
#include "bsp/config.h"
|
||||||
|
#include "bsp/display.h"
|
||||||
|
#include "esp_codec_dev.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
|
||||||
|
#include "lvgl.h"
|
||||||
|
#include "esp_lvgl_port.h"
|
||||||
|
#endif // BSP_CONFIG_NO_GRAPHIC_LIB == 0
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* BSP Capabilities
|
||||||
|
**************************************************************************************************/
|
||||||
|
|
||||||
|
#define BSP_CAPS_DISPLAY 1
|
||||||
|
#define BSP_CAPS_TOUCH 1
|
||||||
|
#define BSP_CAPS_BUTTONS 0
|
||||||
|
#define BSP_CAPS_AUDIO 1
|
||||||
|
#define BSP_CAPS_AUDIO_SPEAKER 1
|
||||||
|
#define BSP_CAPS_AUDIO_MIC 1
|
||||||
|
#define BSP_CAPS_SDCARD 1
|
||||||
|
#define BSP_CAPS_IMU 0
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
* ESP-BOX pinout
|
||||||
|
**************************************************************************************************/
|
||||||
|
/* I2C */
|
||||||
|
#define BSP_I2C_SCL (GPIO_NUM_8)
|
||||||
|
#define BSP_I2C_SDA (GPIO_NUM_7)
|
||||||
|
|
||||||
|
/* Audio */
|
||||||
|
#define BSP_I2S_SCLK (GPIO_NUM_12)
|
||||||
|
#define BSP_I2S_MCLK (GPIO_NUM_13)
|
||||||
|
#define BSP_I2S_LCLK (GPIO_NUM_10)
|
||||||
|
#define BSP_I2S_DOUT (GPIO_NUM_9)
|
||||||
|
#define BSP_I2S_DSIN (GPIO_NUM_48)
|
||||||
|
#define BSP_POWER_AMP_IO (GPIO_NUM_11)
|
||||||
|
|
||||||
|
/* Display */
|
||||||
|
#if CONFIG_BSP_LCD_TYPE_1024_600
|
||||||
|
#define BSP_LCD_BACKLIGHT (GPIO_NUM_NC)
|
||||||
|
#define BSP_LCD_RST (GPIO_NUM_NC)
|
||||||
|
#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC)
|
||||||
|
#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC)
|
||||||
|
#else
|
||||||
|
#define BSP_LCD_BACKLIGHT (GPIO_NUM_23)
|
||||||
|
#define BSP_LCD_RST (GPIO_NUM_NC)
|
||||||
|
#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC)
|
||||||
|
#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* uSD card */
|
||||||
|
#define BSP_SD_D0 (GPIO_NUM_39)
|
||||||
|
#define BSP_SD_D1 (GPIO_NUM_40)
|
||||||
|
#define BSP_SD_D2 (GPIO_NUM_41)
|
||||||
|
#define BSP_SD_D3 (GPIO_NUM_42)
|
||||||
|
#define BSP_SD_CMD (GPIO_NUM_44)
|
||||||
|
#define BSP_SD_CLK (GPIO_NUM_43)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
*
|
||||||
|
* I2C interface
|
||||||
|
*
|
||||||
|
* There are multiple devices connected to I2C peripheral:
|
||||||
|
* - Codec ES8311 (configuration only)
|
||||||
|
* - LCD Touch controller
|
||||||
|
**************************************************************************************************/
|
||||||
|
#define BSP_I2C_NUM CONFIG_BSP_I2C_NUM
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Init I2C driver
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK On success
|
||||||
|
* - ESP_ERR_INVALID_ARG I2C parameter error
|
||||||
|
* - ESP_FAIL I2C driver installation error
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_i2c_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deinit I2C driver and free its resources
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK On success
|
||||||
|
* - ESP_ERR_INVALID_ARG I2C parameter error
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_i2c_deinit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get I2C driver handle
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - I2C handle
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
i2c_master_bus_handle_t bsp_i2c_get_handle(void);
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
*
|
||||||
|
* I2S audio interface
|
||||||
|
*
|
||||||
|
* There are two devices connected to the I2S peripheral:
|
||||||
|
* - Codec ES8311 for output(playback) and input(recording) path
|
||||||
|
*
|
||||||
|
* For speaker initialization use bsp_audio_codec_speaker_init() which is inside initialize I2S with bsp_audio_init().
|
||||||
|
* For microphone initialization use bsp_audio_codec_microphone_init() which is inside initialize I2S with bsp_audio_init().
|
||||||
|
* After speaker or microphone initialization, use functions from esp_codec_dev for play/record audio.
|
||||||
|
* Example audio play:
|
||||||
|
* \code{.c}
|
||||||
|
* esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME);
|
||||||
|
* esp_codec_dev_open(spk_codec_dev, &fs);
|
||||||
|
* esp_codec_dev_write(spk_codec_dev, wav_bytes, bytes_read_from_spiffs);
|
||||||
|
* esp_codec_dev_close(spk_codec_dev);
|
||||||
|
* \endcode
|
||||||
|
**************************************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Init audio
|
||||||
|
*
|
||||||
|
* @note There is no deinit audio function. Users can free audio resources by calling i2s_del_channel()
|
||||||
|
* @warning The type of i2s_config param is depending on IDF version.
|
||||||
|
* @param[in] i2s_config I2S configuration. Pass NULL to use default values (Mono, duplex, 16bit, 22050 Hz)
|
||||||
|
* @return
|
||||||
|
* - ESP_OK On success
|
||||||
|
* - ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip
|
||||||
|
* - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration
|
||||||
|
* - ESP_ERR_NOT_FOUND No available I2S channel found
|
||||||
|
* - ESP_ERR_NO_MEM No memory for storing the channel information
|
||||||
|
* - ESP_ERR_INVALID_STATE This channel has not initialized or already started
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize speaker codec device
|
||||||
|
*
|
||||||
|
* @return Pointer to codec device handle or NULL when error occurred
|
||||||
|
*/
|
||||||
|
esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize microphone codec device
|
||||||
|
*
|
||||||
|
* @return Pointer to codec device handle or NULL when error occurred
|
||||||
|
*/
|
||||||
|
esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void);
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
*
|
||||||
|
* SPIFFS
|
||||||
|
*
|
||||||
|
* After mounting the SPIFFS, it can be accessed with stdio functions ie.:
|
||||||
|
* \code{.c}
|
||||||
|
* FILE* f = fopen(BSP_SPIFFS_MOUNT_POINT"/hello.txt", "w");
|
||||||
|
* fprintf(f, "Hello World!\n");
|
||||||
|
* fclose(f);
|
||||||
|
* \endcode
|
||||||
|
**************************************************************************************************/
|
||||||
|
#define BSP_SPIFFS_MOUNT_POINT CONFIG_BSP_SPIFFS_MOUNT_POINT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mount SPIFFS to virtual file system
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_STATE if esp_vfs_spiffs_register was already called
|
||||||
|
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||||
|
* - ESP_FAIL if partition can not be mounted
|
||||||
|
* - other error codes
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_spiffs_mount(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unmount SPIFFS from virtual file system
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_NOT_FOUND if the partition table does not contain SPIFFS partition with given label
|
||||||
|
* - ESP_ERR_INVALID_STATE if esp_vfs_spiffs_unregister was already called
|
||||||
|
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||||
|
* - ESP_FAIL if partition can not be mounted
|
||||||
|
* - other error codes
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_spiffs_unmount(void);
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
*
|
||||||
|
* uSD card
|
||||||
|
*
|
||||||
|
* After mounting the uSD card, it can be accessed with stdio functions ie.:
|
||||||
|
* \code{.c}
|
||||||
|
* FILE* f = fopen(BSP_MOUNT_POINT"/hello.txt", "w");
|
||||||
|
* fprintf(f, "Hello %s!\n", bsp_sdcard->cid.name);
|
||||||
|
* fclose(f);
|
||||||
|
* \endcode
|
||||||
|
**************************************************************************************************/
|
||||||
|
#define BSP_SD_MOUNT_POINT CONFIG_BSP_SD_MOUNT_POINT
|
||||||
|
extern sdmmc_card_t *bsp_sdcard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mount microSD card to virtual file system
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||||
|
* - ESP_ERR_NO_MEM if memory cannot be allocated
|
||||||
|
* - ESP_FAIL if partition cannot be mounted
|
||||||
|
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_sdcard_mount(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unmount microSD card from virtual file system
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label
|
||||||
|
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount was already called
|
||||||
|
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||||
|
* - ESP_FAIL if partition can not be mounted
|
||||||
|
* - other error codes from wear levelling library, SPI flash driver, or FATFS drivers
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_sdcard_unmount(void);
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
*
|
||||||
|
* LCD interface
|
||||||
|
*
|
||||||
|
* ESP-BOX is shipped with 2.4inch ST7789 display controller.
|
||||||
|
* It features 16-bit colors, 320x240 resolution and capacitive touch controller.
|
||||||
|
*
|
||||||
|
* LVGL is used as graphics library. LVGL is NOT thread safe, therefore the user must take LVGL mutex
|
||||||
|
* by calling bsp_display_lock() before calling and LVGL API (lv_...) and then give the mutex with
|
||||||
|
* bsp_display_unlock().
|
||||||
|
*
|
||||||
|
* Display's backlight must be enabled explicitly by calling bsp_display_backlight_on()
|
||||||
|
**************************************************************************************************/
|
||||||
|
#define BSP_LCD_PIXEL_CLOCK_MHZ (80)
|
||||||
|
|
||||||
|
#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
|
||||||
|
|
||||||
|
#define BSP_LCD_DRAW_BUFF_SIZE (BSP_LCD_H_RES * 50) // Frame buffer size in pixels
|
||||||
|
#define BSP_LCD_DRAW_BUFF_DOUBLE (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief BSP display configuration structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
lvgl_port_cfg_t lvgl_port_cfg; /*!< LVGL port configuration */
|
||||||
|
uint32_t buffer_size; /*!< Size of the buffer for the screen in pixels */
|
||||||
|
bool double_buffer; /*!< True, if should be allocated two buffers */
|
||||||
|
struct {
|
||||||
|
unsigned int buff_dma: 1; /*!< Allocated LVGL buffer will be DMA capable */
|
||||||
|
unsigned int buff_spiram: 1; /*!< Allocated LVGL buffer will be in PSRAM */
|
||||||
|
unsigned int sw_rotate: 1; /*!< Use software rotation (slower), The feature is unavailable under avoid-tear mode */
|
||||||
|
} flags;
|
||||||
|
} bsp_display_cfg_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize display
|
||||||
|
*
|
||||||
|
* This function initializes SPI, display controller and starts LVGL handling task.
|
||||||
|
* LCD backlight must be enabled separately by calling bsp_display_brightness_set()
|
||||||
|
*
|
||||||
|
* @return Pointer to LVGL display or NULL when error occured
|
||||||
|
*/
|
||||||
|
lv_display_t *bsp_display_start(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize display
|
||||||
|
*
|
||||||
|
* This function initializes SPI, display controller and starts LVGL handling task.
|
||||||
|
* LCD backlight must be enabled separately by calling bsp_display_brightness_set()
|
||||||
|
*
|
||||||
|
* @param cfg display configuration
|
||||||
|
*
|
||||||
|
* @return Pointer to LVGL display or NULL when error occured
|
||||||
|
*/
|
||||||
|
lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get pointer to input device (touch, buttons, ...)
|
||||||
|
*
|
||||||
|
* @note The LVGL input device is initialized in bsp_display_start() function.
|
||||||
|
*
|
||||||
|
* @return Pointer to LVGL input device or NULL when not initialized
|
||||||
|
*/
|
||||||
|
lv_indev_t *bsp_display_get_input_dev(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Take LVGL mutex
|
||||||
|
*
|
||||||
|
* @param timeout_ms Timeout in [ms]. 0 will block indefinitely.
|
||||||
|
* @return true Mutex was taken
|
||||||
|
* @return false Mutex was NOT taken
|
||||||
|
*/
|
||||||
|
bool bsp_display_lock(uint32_t timeout_ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Give LVGL mutex
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void bsp_display_unlock(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate screen
|
||||||
|
*
|
||||||
|
* Display must be already initialized by calling bsp_display_start()
|
||||||
|
*
|
||||||
|
* @param[in] disp Pointer to LVGL display
|
||||||
|
* @param[in] rotation Angle of the display rotation
|
||||||
|
*/
|
||||||
|
void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation);
|
||||||
|
#endif // BSP_CONFIG_NO_GRAPHIC_LIB == 0
|
||||||
|
|
||||||
|
/**************************************************************************************************
|
||||||
|
*
|
||||||
|
* USB
|
||||||
|
*
|
||||||
|
**************************************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Power modes of USB Host connector
|
||||||
|
*/
|
||||||
|
typedef enum bsp_usb_host_power_mode_t {
|
||||||
|
BSP_USB_HOST_POWER_MODE_USB_DEV, //!< Power from USB DEV port
|
||||||
|
} bsp_usb_host_power_mode_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start USB host
|
||||||
|
*
|
||||||
|
* This is a one-stop-shop function that will configure the board for USB Host mode
|
||||||
|
* and start USB Host library
|
||||||
|
*
|
||||||
|
* @param[in] mode USB Host connector power mode (Not used on this board)
|
||||||
|
* @param[in] limit_500mA Limit output current to 500mA (Not used on this board)
|
||||||
|
* @return
|
||||||
|
* - ESP_OK On success
|
||||||
|
* - ESP_ERR_INVALID_ARG Parameter error
|
||||||
|
* - ESP_ERR_NO_MEM Memory cannot be allocated
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_usb_host_start(bsp_usb_host_power_mode_t mode, bool limit_500mA);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stop USB host
|
||||||
|
*
|
||||||
|
* USB Host lib will be uninstalled and power from connector removed.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK On success
|
||||||
|
* - ESP_ERR_INVALID_ARG Parameter error
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_usb_host_stop(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief BSP Touchscreen
|
||||||
|
*
|
||||||
|
* This file offers API for basic touchscreen initialization.
|
||||||
|
* It is useful for users who want to use the touchscreen without the default Graphical Library LVGL.
|
||||||
|
*
|
||||||
|
* For standard LCD initialization with LVGL graphical library, you can call all-in-one function bsp_display_start().
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "esp_lcd_touch.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief BSP touch configuration structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
void *dummy; /*!< Prepared for future use. */
|
||||||
|
} bsp_touch_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create new touchscreen
|
||||||
|
*
|
||||||
|
* If you want to free resources allocated by this function, you can use esp_lcd_touch API, ie.:
|
||||||
|
*
|
||||||
|
* \code{.c}
|
||||||
|
* esp_lcd_touch_del(tp);
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* @param[in] config touch configuration
|
||||||
|
* @param[out] ret_touch esp_lcd_touch touchscreen handle
|
||||||
|
* @return
|
||||||
|
* - ESP_OK On success
|
||||||
|
* - Else esp_lcd_touch failure
|
||||||
|
*/
|
||||||
|
esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Assert on error, if selected in menuconfig. Otherwise return error code. */
|
||||||
|
#if CONFIG_BSP_ERROR_CHECK
|
||||||
|
#define BSP_ERROR_CHECK_RETURN_ERR(x) ESP_ERROR_CHECK(x)
|
||||||
|
#define BSP_ERROR_CHECK_RETURN_NULL(x) ESP_ERROR_CHECK(x)
|
||||||
|
#define BSP_ERROR_CHECK(x, ret) ESP_ERROR_CHECK(x)
|
||||||
|
#define BSP_NULL_CHECK(x, ret) assert(x)
|
||||||
|
#define BSP_NULL_CHECK_GOTO(x, goto_tag) assert(x)
|
||||||
|
#else
|
||||||
|
#define BSP_ERROR_CHECK_RETURN_ERR(x) do { \
|
||||||
|
esp_err_t err_rc_ = (x); \
|
||||||
|
if (unlikely(err_rc_ != ESP_OK)) { \
|
||||||
|
return err_rc_; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define BSP_ERROR_CHECK_RETURN_NULL(x) do { \
|
||||||
|
if (unlikely((x) != ESP_OK)) { \
|
||||||
|
return NULL; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define BSP_NULL_CHECK(x, ret) do { \
|
||||||
|
if ((x) == NULL) { \
|
||||||
|
return ret; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define BSP_ERROR_CHECK(x, ret) do { \
|
||||||
|
if (unlikely((x) != ESP_OK)) { \
|
||||||
|
return ret; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define BSP_NULL_CHECK_GOTO(x, goto_tag) do { \
|
||||||
|
if ((x) == NULL) { \
|
||||||
|
goto goto_tag; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# 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.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||||
|
idf_build_set_property(MINIMAL_BUILD ON)
|
||||||
|
project(ethernet_basic)
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
# Ethernet Example
|
||||||
|
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This example demonstrates basic usage of `Ethernet driver` together with `esp_netif`. Initialization of the `Ethernet driver` is wrapped in separate [sub-component](./components/ethernet_init/ethernet_init.c) of this project to clearly distinguish between the driver's and `esp_netif` initializations. The work flow of the example could be as follows:
|
||||||
|
|
||||||
|
1. Install Ethernet driver
|
||||||
|
2. Attach the driver to `esp_netif`
|
||||||
|
3. Send DHCP requests and wait for a DHCP lease
|
||||||
|
4. If get IP address successfully, then you will be able to ping the device
|
||||||
|
|
||||||
|
If you have a new Ethernet application to go (for example, connect to IoT cloud via Ethernet), try this as a basic template, then add your own code.
|
||||||
|
|
||||||
|
## How to use example
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
|
||||||
|
To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). This example should also work for 3rd party ESP32 board as long as it's integrated with a supported Ethernet PHY chip. Up until now, ESP-IDF supports up to four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves.
|
||||||
|
|
||||||
|
Besides that, `esp_eth` component can drive third-party Ethernet module which integrates MAC and PHY and provides common communication interface (e.g. SPI, USB, etc). This example will take the `DM9051`, `W5500` or `KSZ8851SNL` SPI modules as an example, illustrating how to install the Ethernet driver in the same manner.
|
||||||
|
|
||||||
|
The ESP-IDF supports the usage of multiple Ethernet interfaces at a time when external modules are utilized which is also demonstrated by this example. There are several options you can combine:
|
||||||
|
* Internal EMAC and one SPI Ethernet module.
|
||||||
|
* Two SPI Ethernet modules of the same type connected to single SPI interface and accessed by switching appropriate CS.
|
||||||
|
* Internal EMAC and two SPI Ethernet modules of the same type.
|
||||||
|
|
||||||
|
#### Pin Assignment
|
||||||
|
|
||||||
|
See common pin assignments for Ethernet examples from [upper level](../README.md#common-pin-assignments).
|
||||||
|
|
||||||
|
When using two Ethernet SPI modules at a time, they are to be connected to single SPI interface. Both modules then share data (MOSI/MISO) and CLK signals. However, the CS, interrupt and reset pins need to be connected to separate GPIO for each Ethernet SPI module.
|
||||||
|
|
||||||
|
### Configure the project
|
||||||
|
|
||||||
|
```
|
||||||
|
idf.py menuconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
See common configurations for Ethernet examples from [upper level](../README.md#common-configurations).
|
||||||
|
|
||||||
|
### Build, Flash, and Run
|
||||||
|
|
||||||
|
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||||
|
|
||||||
|
```
|
||||||
|
idf.py -p PORT build flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
(Replace PORT with the name of the serial port to use.)
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
```bash
|
||||||
|
I (394) eth_example: Ethernet Started
|
||||||
|
I (3934) eth_example: Ethernet Link Up
|
||||||
|
I (3934) eth_example: Ethernet HW Addr 30:ae:a4:c6:87:5b
|
||||||
|
I (5864) esp_netif_handlers: eth ip: 192.168.2.151, mask: 255.255.255.0, gw: 192.168.2.2
|
||||||
|
I (5864) eth_example: Ethernet Got IP Address
|
||||||
|
I (5864) eth_example: ~~~~~~~~~~~
|
||||||
|
I (5864) eth_example: ETHIP:192.168.2.151
|
||||||
|
I (5874) eth_example: ETHMASK:255.255.255.0
|
||||||
|
I (5874) eth_example: ETHGW:192.168.2.2
|
||||||
|
I (5884) eth_example: ~~~~~~~~~~~
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can ping your ESP32 in the terminal by entering `ping 192.168.2.151` (it depends on the actual IP address you get).
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
See common troubleshooting for Ethernet examples from [upper level](../README.md#common-troubleshooting).
|
||||||
|
|
||||||
|
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
idf_component_register(SRCS "ethernet_init.c"
|
||||||
|
PRIV_REQUIRES esp_driver_gpio esp_eth
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
@@ -0,0 +1,308 @@
|
|||||||
|
menu "Example Ethernet Configuration"
|
||||||
|
|
||||||
|
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
|
||||||
|
|
||||||
|
config EXAMPLE_USE_INTERNAL_ETHERNET
|
||||||
|
depends on SOC_EMAC_SUPPORTED
|
||||||
|
select ETH_USE_ESP32_EMAC
|
||||||
|
default y
|
||||||
|
bool "Internal EMAC"
|
||||||
|
help
|
||||||
|
Use internal Ethernet MAC controller.
|
||||||
|
|
||||||
|
if EXAMPLE_USE_INTERNAL_ETHERNET
|
||||||
|
choice EXAMPLE_ETH_PHY_MODEL
|
||||||
|
prompt "Ethernet PHY Device"
|
||||||
|
default EXAMPLE_ETH_PHY_IP101
|
||||||
|
help
|
||||||
|
Select the Ethernet PHY device to use in the example.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_PHY_GENERIC
|
||||||
|
bool "Generic 802.3 PHY"
|
||||||
|
help
|
||||||
|
Any Ethernet PHY chip compliant with IEEE 802.3 can be used. However, while
|
||||||
|
basic functionality should always work, some specific features might be limited,
|
||||||
|
even if the PHY meets IEEE 802.3 standard. A typical example is loopback
|
||||||
|
functionality, where certain PHYs may require setting a specific speed mode to
|
||||||
|
operate correctly.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_PHY_IP101
|
||||||
|
bool "IP101"
|
||||||
|
help
|
||||||
|
IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver.
|
||||||
|
Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_PHY_RTL8201
|
||||||
|
bool "RTL8201/SR8201"
|
||||||
|
help
|
||||||
|
RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX.
|
||||||
|
Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_PHY_LAN87XX
|
||||||
|
bool "LAN87xx"
|
||||||
|
help
|
||||||
|
Below chips are supported:
|
||||||
|
LAN8710A is a small footprint MII/RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and
|
||||||
|
flexPWR® Technology.
|
||||||
|
LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support.
|
||||||
|
LAN8740A/LAN8741A is a small footprint MII/RMII 10/100 Energy Efficient Ethernet Transceiver
|
||||||
|
with HP Auto-MDIX and flexPWR® Technology.
|
||||||
|
LAN8742A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and
|
||||||
|
flexPWR® Technology.
|
||||||
|
Goto https://www.microchip.com for more information about them.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_PHY_DP83848
|
||||||
|
bool "DP83848"
|
||||||
|
help
|
||||||
|
DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver.
|
||||||
|
Goto http://www.ti.com/product/DP83848J for more information about it.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_PHY_KSZ80XX
|
||||||
|
bool "KSZ80xx"
|
||||||
|
help
|
||||||
|
With the KSZ80xx series, Microchip offers single-chip 10BASE-T/100BASE-TX
|
||||||
|
Ethernet Physical Layer Transceivers (PHY).
|
||||||
|
The following chips are supported: KSZ8001, KSZ8021, KSZ8031, KSZ8041,
|
||||||
|
KSZ8051, KSZ8061, KSZ8081, KSZ8091
|
||||||
|
Goto https://www.microchip.com for more information about them.
|
||||||
|
endchoice # EXAMPLE_ETH_PHY_MODEL
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_MDC_GPIO
|
||||||
|
int "SMI MDC GPIO number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default 23 if IDF_TARGET_ESP32
|
||||||
|
default 31 if IDF_TARGET_ESP32P4
|
||||||
|
help
|
||||||
|
Set the GPIO number used by SMI MDC.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_MDIO_GPIO
|
||||||
|
int "SMI MDIO GPIO number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default 18 if IDF_TARGET_ESP32
|
||||||
|
default 52 if IDF_TARGET_ESP32P4
|
||||||
|
help
|
||||||
|
Set the GPIO number used by SMI MDIO.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_PHY_RST_GPIO
|
||||||
|
int "PHY Reset GPIO number"
|
||||||
|
range -1 ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default 5 if IDF_TARGET_ESP32
|
||||||
|
default 51 if IDF_TARGET_ESP32P4
|
||||||
|
help
|
||||||
|
Set the GPIO number used to reset PHY chip.
|
||||||
|
Set to -1 to disable PHY chip hardware reset.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_PHY_ADDR
|
||||||
|
int "PHY Address"
|
||||||
|
range -1 31
|
||||||
|
default 1
|
||||||
|
help
|
||||||
|
Set PHY address according your board schematic.
|
||||||
|
Set to -1 to driver find the PHY address automatically.
|
||||||
|
endif # EXAMPLE_USE_INTERNAL_ETHERNET
|
||||||
|
|
||||||
|
config EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
bool "SPI Ethernet"
|
||||||
|
default n
|
||||||
|
select ETH_USE_SPI_ETHERNET
|
||||||
|
help
|
||||||
|
Use external SPI-Ethernet module(s).
|
||||||
|
|
||||||
|
if EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
config EXAMPLE_SPI_ETHERNETS_NUM
|
||||||
|
int "Number of SPI Ethernet modules to use at a time"
|
||||||
|
range 1 2
|
||||||
|
default 1
|
||||||
|
help
|
||||||
|
Set the number of SPI Ethernet modules you want to use at a time. Multiple SPI modules can be connected
|
||||||
|
to one SPI interface and can be separately accessed based on state of associated Chip Select (CS).
|
||||||
|
|
||||||
|
choice EXAMPLE_ETHERNET_TYPE_SPI
|
||||||
|
prompt "Ethernet SPI"
|
||||||
|
default EXAMPLE_USE_W5500
|
||||||
|
help
|
||||||
|
Select which kind of Ethernet will be used in the example.
|
||||||
|
|
||||||
|
config EXAMPLE_USE_DM9051
|
||||||
|
bool "DM9051 Module"
|
||||||
|
select ETH_SPI_ETHERNET_DM9051
|
||||||
|
help
|
||||||
|
Select external SPI-Ethernet module (DM9051).
|
||||||
|
|
||||||
|
config EXAMPLE_USE_KSZ8851SNL
|
||||||
|
bool "KSZ8851SNL Module"
|
||||||
|
select ETH_SPI_ETHERNET_KSZ8851SNL
|
||||||
|
help
|
||||||
|
Select external SPI-Ethernet module (KSZ8851SNL).
|
||||||
|
|
||||||
|
config EXAMPLE_USE_W5500
|
||||||
|
bool "W5500 Module"
|
||||||
|
select ETH_SPI_ETHERNET_W5500
|
||||||
|
help
|
||||||
|
Select external SPI-Ethernet module (W5500).
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_HOST
|
||||||
|
int "SPI Host Number"
|
||||||
|
range 0 2
|
||||||
|
default 1
|
||||||
|
help
|
||||||
|
Set the SPI host used to communicate with the SPI Ethernet Controller.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_SCLK_GPIO
|
||||||
|
int "SPI SCLK GPIO number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default 14 if IDF_TARGET_ESP32
|
||||||
|
default 12 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||||
|
default 6 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C61
|
||||||
|
default 4 if IDF_TARGET_ESP32H2
|
||||||
|
default 33 if IDF_TARGET_ESP32P4
|
||||||
|
default 8 if IDF_TARGET_ESP32C5
|
||||||
|
help
|
||||||
|
Set the GPIO number used by SPI SCLK.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_MOSI_GPIO
|
||||||
|
int "SPI MOSI GPIO number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default 13 if IDF_TARGET_ESP32
|
||||||
|
default 11 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||||
|
default 7 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C61
|
||||||
|
default 5 if IDF_TARGET_ESP32H2
|
||||||
|
default 32 if IDF_TARGET_ESP32P4
|
||||||
|
default 10 if IDF_TARGET_ESP32C5
|
||||||
|
help
|
||||||
|
Set the GPIO number used by SPI MOSI.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_MISO_GPIO
|
||||||
|
int "SPI MISO GPIO number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
|
||||||
|
default 12 if IDF_TARGET_ESP32
|
||||||
|
default 13 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||||
|
default 2 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C61
|
||||||
|
default 0 if IDF_TARGET_ESP32H2
|
||||||
|
default 24 if IDF_TARGET_ESP32P4
|
||||||
|
default 9 if IDF_TARGET_ESP32C5
|
||||||
|
help
|
||||||
|
Set the GPIO number used by SPI MISO.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_CLOCK_MHZ
|
||||||
|
int "SPI clock speed (MHz)"
|
||||||
|
range 5 80
|
||||||
|
default 16
|
||||||
|
help
|
||||||
|
Set the clock speed (MHz) of SPI interface.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_CS0_GPIO
|
||||||
|
int "SPI CS0 GPIO number for SPI Ethernet module #1"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default 15 if IDF_TARGET_ESP32
|
||||||
|
default 10 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C2
|
||||||
|
default 3 if IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C5 || IDF_TARGET_ESP32C61
|
||||||
|
default 1 if IDF_TARGET_ESP32H2
|
||||||
|
default 21 if IDF_TARGET_ESP32P4
|
||||||
|
help
|
||||||
|
Set the GPIO number used by SPI CS0, i.e. Chip Select associated with the first SPI Eth module).
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_CS1_GPIO
|
||||||
|
depends on EXAMPLE_SPI_ETHERNETS_NUM > 1
|
||||||
|
int "SPI CS1 GPIO number for SPI Ethernet module #2"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default 32 if IDF_TARGET_ESP32
|
||||||
|
default 7 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||||
|
default 8 if IDF_TARGET_ESP32C3
|
||||||
|
default 21 if IDF_TARGET_ESP32C6
|
||||||
|
default 3 if IDF_TARGET_ESP32C2
|
||||||
|
default 11 if IDF_TARGET_ESP32H2
|
||||||
|
default 23 if IDF_TARGET_ESP32P4 || IDF_TARGET_ESP32C61
|
||||||
|
default 1 if IDF_TARGET_ESP32C5
|
||||||
|
help
|
||||||
|
Set the GPIO number used by SPI CS1, i.e. Chip Select associated with the second SPI Eth module.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_INT0_GPIO
|
||||||
|
int "Interrupt GPIO number SPI Ethernet module #1"
|
||||||
|
range -1 ENV_GPIO_IN_RANGE_MAX
|
||||||
|
default 4 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3
|
||||||
|
default 4 if IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C5
|
||||||
|
default 10 if IDF_TARGET_ESP32H2
|
||||||
|
default 48 if IDF_TARGET_ESP32P4
|
||||||
|
default 0 if IDF_TARGET_ESP32C61
|
||||||
|
help
|
||||||
|
Set the GPIO number used by the first SPI Ethernet module interrupt line.
|
||||||
|
Set -1 to use SPI Ethernet module in polling mode.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_INT1_GPIO
|
||||||
|
depends on EXAMPLE_SPI_ETHERNETS_NUM > 1
|
||||||
|
int "Interrupt GPIO number SPI Ethernet module #2"
|
||||||
|
range -1 ENV_GPIO_IN_RANGE_MAX
|
||||||
|
default 33 if IDF_TARGET_ESP32
|
||||||
|
default 5 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C2
|
||||||
|
default 5 if IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C5
|
||||||
|
default 9 if IDF_TARGET_ESP32H2
|
||||||
|
default 47 if IDF_TARGET_ESP32P4
|
||||||
|
default 1 if IDF_TARGET_ESP32C61
|
||||||
|
help
|
||||||
|
Set the GPIO number used by the second SPI Ethernet module interrupt line.
|
||||||
|
Set -1 to use SPI Ethernet module in polling mode.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_POLLING0_MS_VAL
|
||||||
|
depends on EXAMPLE_ETH_SPI_INT0_GPIO < 0
|
||||||
|
int "Polling period in msec of SPI Ethernet Module #1"
|
||||||
|
default 10
|
||||||
|
help
|
||||||
|
Set SPI Ethernet module polling period.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_POLLING1_MS_VAL
|
||||||
|
depends on EXAMPLE_SPI_ETHERNETS_NUM > 1 && EXAMPLE_ETH_SPI_INT1_GPIO < 0
|
||||||
|
int "Polling period in msec of SPI Ethernet Module #2"
|
||||||
|
default 10
|
||||||
|
help
|
||||||
|
Set SPI Ethernet module polling period.
|
||||||
|
|
||||||
|
# Hidden variable to ensure that polling period option is visible only when interrupt is set disabled and
|
||||||
|
# it is set to known value (0) when interrupt is enabled at the same time.
|
||||||
|
config EXAMPLE_ETH_SPI_POLLING0_MS
|
||||||
|
int
|
||||||
|
default EXAMPLE_ETH_SPI_POLLING0_MS_VAL if EXAMPLE_ETH_SPI_POLLING0_MS_VAL > 0
|
||||||
|
default 0
|
||||||
|
|
||||||
|
# Hidden variable to ensure that polling period option is visible only when interrupt is set disabled and
|
||||||
|
# it is set to known value (0) when interrupt is enabled at the same time.
|
||||||
|
config EXAMPLE_ETH_SPI_POLLING1_MS
|
||||||
|
depends on EXAMPLE_SPI_ETHERNETS_NUM > 1
|
||||||
|
int
|
||||||
|
default EXAMPLE_ETH_SPI_POLLING1_MS_VAL if EXAMPLE_ETH_SPI_POLLING1_MS_VAL > 0
|
||||||
|
default 0
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_PHY_RST0_GPIO
|
||||||
|
int "PHY Reset GPIO number of SPI Ethernet Module #1"
|
||||||
|
range -1 ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default -1
|
||||||
|
help
|
||||||
|
Set the GPIO number used to reset PHY chip on the first SPI Ethernet module.
|
||||||
|
Set to -1 to disable PHY chip hardware reset.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_PHY_RST1_GPIO
|
||||||
|
depends on EXAMPLE_SPI_ETHERNETS_NUM > 1
|
||||||
|
int "PHY Reset GPIO number of SPI Ethernet Module #2"
|
||||||
|
range -1 ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default -1
|
||||||
|
help
|
||||||
|
Set the GPIO number used to reset PHY chip on the second SPI Ethernet module.
|
||||||
|
Set to -1 to disable PHY chip hardware reset.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_PHY_ADDR0
|
||||||
|
int "PHY Address of SPI Ethernet Module #1"
|
||||||
|
range 0 31
|
||||||
|
default 1
|
||||||
|
help
|
||||||
|
Set the first SPI Ethernet module PHY address according your board schematic.
|
||||||
|
|
||||||
|
config EXAMPLE_ETH_SPI_PHY_ADDR1
|
||||||
|
depends on EXAMPLE_SPI_ETHERNETS_NUM > 1
|
||||||
|
int "PHY Address of SPI Ethernet Module #2"
|
||||||
|
range 0 31
|
||||||
|
default 1
|
||||||
|
help
|
||||||
|
Set the second SPI Ethernet module PHY address according your board schematic.
|
||||||
|
endif # EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
endmenu
|
||||||
@@ -0,0 +1,340 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include "ethernet_init.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_mac.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#if CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
#include "driver/spi_master.h"
|
||||||
|
#endif // CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_SPI_ETHERNETS_NUM
|
||||||
|
#define SPI_ETHERNETS_NUM CONFIG_EXAMPLE_SPI_ETHERNETS_NUM
|
||||||
|
#else
|
||||||
|
#define SPI_ETHERNETS_NUM 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
|
||||||
|
#define INTERNAL_ETHERNETS_NUM 1
|
||||||
|
#else
|
||||||
|
#define INTERNAL_ETHERNETS_NUM 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define INIT_SPI_ETH_MODULE_CONFIG(eth_module_config, num) \
|
||||||
|
do { \
|
||||||
|
eth_module_config[num].spi_cs_gpio = CONFIG_EXAMPLE_ETH_SPI_CS ##num## _GPIO; \
|
||||||
|
eth_module_config[num].int_gpio = CONFIG_EXAMPLE_ETH_SPI_INT ##num## _GPIO; \
|
||||||
|
eth_module_config[num].polling_ms = CONFIG_EXAMPLE_ETH_SPI_POLLING ##num## _MS; \
|
||||||
|
eth_module_config[num].phy_reset_gpio = CONFIG_EXAMPLE_ETH_SPI_PHY_RST ##num## _GPIO; \
|
||||||
|
eth_module_config[num].phy_addr = CONFIG_EXAMPLE_ETH_SPI_PHY_ADDR ##num; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t spi_cs_gpio;
|
||||||
|
int8_t int_gpio;
|
||||||
|
uint32_t polling_ms;
|
||||||
|
int8_t phy_reset_gpio;
|
||||||
|
uint8_t phy_addr;
|
||||||
|
uint8_t *mac_addr;
|
||||||
|
}spi_eth_module_config_t;
|
||||||
|
|
||||||
|
static const char *TAG = "example_eth_init";
|
||||||
|
#if CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
static bool gpio_isr_svc_init_by_eth = false; // indicates that we initialized the GPIO ISR service
|
||||||
|
#endif // CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
|
||||||
|
/**
|
||||||
|
* @brief Internal ESP32 Ethernet initialization
|
||||||
|
*
|
||||||
|
* @param[out] mac_out optionally returns Ethernet MAC object
|
||||||
|
* @param[out] phy_out optionally returns Ethernet PHY object
|
||||||
|
* @return
|
||||||
|
* - esp_eth_handle_t if init succeeded
|
||||||
|
* - NULL if init failed
|
||||||
|
*/
|
||||||
|
static esp_eth_handle_t eth_init_internal(esp_eth_mac_t **mac_out, esp_eth_phy_t **phy_out)
|
||||||
|
{
|
||||||
|
esp_eth_handle_t ret = NULL;
|
||||||
|
|
||||||
|
// Init common MAC and PHY configs to default
|
||||||
|
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||||
|
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
||||||
|
|
||||||
|
// Update PHY config based on board specific configuration
|
||||||
|
phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
|
||||||
|
phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
|
||||||
|
// Init vendor specific MAC config to default
|
||||||
|
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
|
||||||
|
// Update vendor specific MAC config based on board configuration
|
||||||
|
esp32_emac_config.smi_gpio.mdc_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;
|
||||||
|
esp32_emac_config.smi_gpio.mdio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
|
||||||
|
#if CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
// The DMA is shared resource between EMAC and the SPI. Therefore, adjust
|
||||||
|
// EMAC DMA burst length when SPI Ethernet is used along with EMAC.
|
||||||
|
esp32_emac_config.dma_burst_len = ETH_DMA_BURST_LEN_4;
|
||||||
|
#endif // CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
// Create new ESP32 Ethernet MAC instance
|
||||||
|
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
|
||||||
|
// Create new PHY instance based on board configuration
|
||||||
|
#if CONFIG_EXAMPLE_ETH_PHY_GENERIC
|
||||||
|
esp_eth_phy_t *phy = esp_eth_phy_new_generic(&phy_config);
|
||||||
|
#elif CONFIG_EXAMPLE_ETH_PHY_IP101
|
||||||
|
esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
|
||||||
|
#elif CONFIG_EXAMPLE_ETH_PHY_RTL8201
|
||||||
|
esp_eth_phy_t *phy = esp_eth_phy_new_rtl8201(&phy_config);
|
||||||
|
#elif CONFIG_EXAMPLE_ETH_PHY_LAN87XX
|
||||||
|
esp_eth_phy_t *phy = esp_eth_phy_new_lan87xx(&phy_config);
|
||||||
|
#elif CONFIG_EXAMPLE_ETH_PHY_DP83848
|
||||||
|
esp_eth_phy_t *phy = esp_eth_phy_new_dp83848(&phy_config);
|
||||||
|
#elif CONFIG_EXAMPLE_ETH_PHY_KSZ80XX
|
||||||
|
esp_eth_phy_t *phy = esp_eth_phy_new_ksz80xx(&phy_config);
|
||||||
|
#endif
|
||||||
|
// Init Ethernet driver to default and install it
|
||||||
|
esp_eth_handle_t eth_handle = NULL;
|
||||||
|
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);
|
||||||
|
ESP_GOTO_ON_FALSE(esp_eth_driver_install(&config, ð_handle) == ESP_OK, NULL,
|
||||||
|
err, TAG, "Ethernet driver install failed");
|
||||||
|
|
||||||
|
if (mac_out != NULL) {
|
||||||
|
*mac_out = mac;
|
||||||
|
}
|
||||||
|
if (phy_out != NULL) {
|
||||||
|
*phy_out = phy;
|
||||||
|
}
|
||||||
|
return eth_handle;
|
||||||
|
err:
|
||||||
|
if (eth_handle != NULL) {
|
||||||
|
esp_eth_driver_uninstall(eth_handle);
|
||||||
|
}
|
||||||
|
if (mac != NULL) {
|
||||||
|
mac->del(mac);
|
||||||
|
}
|
||||||
|
if (phy != NULL) {
|
||||||
|
phy->del(phy);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif // CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
/**
|
||||||
|
* @brief SPI bus initialization (to be used by Ethernet SPI modules)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
static esp_err_t spi_bus_init(void)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
#if (CONFIG_EXAMPLE_ETH_SPI_INT0_GPIO >= 0) || (CONFIG_EXAMPLE_ETH_SPI_INT1_GPIO > 0)
|
||||||
|
// Install GPIO ISR handler to be able to service SPI Eth modules interrupts
|
||||||
|
ret = gpio_install_isr_service(0);
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
gpio_isr_svc_init_by_eth = true;
|
||||||
|
} else if (ret == ESP_ERR_INVALID_STATE) {
|
||||||
|
ESP_LOGW(TAG, "GPIO ISR handler has been already installed");
|
||||||
|
ret = ESP_OK; // ISR handler has been already installed so no issues
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "GPIO ISR handler install failed");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Init SPI bus
|
||||||
|
spi_bus_config_t buscfg = {
|
||||||
|
.miso_io_num = CONFIG_EXAMPLE_ETH_SPI_MISO_GPIO,
|
||||||
|
.mosi_io_num = CONFIG_EXAMPLE_ETH_SPI_MOSI_GPIO,
|
||||||
|
.sclk_io_num = CONFIG_EXAMPLE_ETH_SPI_SCLK_GPIO,
|
||||||
|
.quadwp_io_num = -1,
|
||||||
|
.quadhd_io_num = -1,
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO),
|
||||||
|
err, TAG, "SPI host #%d init failed", CONFIG_EXAMPLE_ETH_SPI_HOST);
|
||||||
|
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ethernet SPI modules initialization
|
||||||
|
*
|
||||||
|
* @param[in] spi_eth_module_config specific SPI Ethernet module configuration
|
||||||
|
* @param[out] mac_out optionally returns Ethernet MAC object
|
||||||
|
* @param[out] phy_out optionally returns Ethernet PHY object
|
||||||
|
* @return
|
||||||
|
* - esp_eth_handle_t if init succeeded
|
||||||
|
* - NULL if init failed
|
||||||
|
*/
|
||||||
|
static esp_eth_handle_t eth_init_spi(spi_eth_module_config_t *spi_eth_module_config, esp_eth_mac_t **mac_out, esp_eth_phy_t **phy_out)
|
||||||
|
{
|
||||||
|
esp_eth_handle_t ret = NULL;
|
||||||
|
|
||||||
|
// Init common MAC and PHY configs to default
|
||||||
|
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||||
|
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
||||||
|
|
||||||
|
// Update PHY config based on board specific configuration
|
||||||
|
phy_config.phy_addr = spi_eth_module_config->phy_addr;
|
||||||
|
phy_config.reset_gpio_num = spi_eth_module_config->phy_reset_gpio;
|
||||||
|
|
||||||
|
// Configure SPI interface for specific SPI module
|
||||||
|
spi_device_interface_config_t spi_devcfg = {
|
||||||
|
.mode = 0,
|
||||||
|
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
|
||||||
|
.queue_size = 20,
|
||||||
|
.spics_io_num = spi_eth_module_config->spi_cs_gpio
|
||||||
|
};
|
||||||
|
// Init vendor specific MAC config to default, and create new SPI Ethernet MAC instance
|
||||||
|
// and new PHY instance based on board configuration
|
||||||
|
#if CONFIG_EXAMPLE_USE_KSZ8851SNL
|
||||||
|
eth_ksz8851snl_config_t ksz8851snl_config = ETH_KSZ8851SNL_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg);
|
||||||
|
ksz8851snl_config.int_gpio_num = spi_eth_module_config->int_gpio;
|
||||||
|
ksz8851snl_config.poll_period_ms = spi_eth_module_config->polling_ms;
|
||||||
|
esp_eth_mac_t *mac = esp_eth_mac_new_ksz8851snl(&ksz8851snl_config, &mac_config);
|
||||||
|
esp_eth_phy_t *phy = esp_eth_phy_new_ksz8851snl(&phy_config);
|
||||||
|
#elif CONFIG_EXAMPLE_USE_DM9051
|
||||||
|
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg);
|
||||||
|
dm9051_config.int_gpio_num = spi_eth_module_config->int_gpio;
|
||||||
|
dm9051_config.poll_period_ms = spi_eth_module_config->polling_ms;
|
||||||
|
esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
|
||||||
|
esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config);
|
||||||
|
#elif CONFIG_EXAMPLE_USE_W5500
|
||||||
|
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(CONFIG_EXAMPLE_ETH_SPI_HOST, &spi_devcfg);
|
||||||
|
w5500_config.int_gpio_num = spi_eth_module_config->int_gpio;
|
||||||
|
w5500_config.poll_period_ms = spi_eth_module_config->polling_ms;
|
||||||
|
esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
|
||||||
|
esp_eth_phy_t *phy = esp_eth_phy_new_w5500(&phy_config);
|
||||||
|
#endif //CONFIG_EXAMPLE_USE_W5500
|
||||||
|
// Init Ethernet driver to default and install it
|
||||||
|
esp_eth_handle_t eth_handle = NULL;
|
||||||
|
esp_eth_config_t eth_config_spi = ETH_DEFAULT_CONFIG(mac, phy);
|
||||||
|
ESP_GOTO_ON_FALSE(esp_eth_driver_install(ð_config_spi, ð_handle) == ESP_OK, NULL, err, TAG, "SPI Ethernet driver install failed");
|
||||||
|
|
||||||
|
// The SPI Ethernet module might not have a burned factory MAC address, we can set it manually.
|
||||||
|
if (spi_eth_module_config->mac_addr != NULL) {
|
||||||
|
ESP_GOTO_ON_FALSE(esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, spi_eth_module_config->mac_addr) == ESP_OK,
|
||||||
|
NULL, err, TAG, "SPI Ethernet MAC address config failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mac_out != NULL) {
|
||||||
|
*mac_out = mac;
|
||||||
|
}
|
||||||
|
if (phy_out != NULL) {
|
||||||
|
*phy_out = phy;
|
||||||
|
}
|
||||||
|
return eth_handle;
|
||||||
|
err:
|
||||||
|
if (eth_handle != NULL) {
|
||||||
|
esp_eth_driver_uninstall(eth_handle);
|
||||||
|
}
|
||||||
|
if (mac != NULL) {
|
||||||
|
mac->del(mac);
|
||||||
|
}
|
||||||
|
if (phy != NULL) {
|
||||||
|
phy->del(phy);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif // CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
|
||||||
|
esp_err_t example_eth_init(esp_eth_handle_t *eth_handles_out[], uint8_t *eth_cnt_out)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
esp_eth_handle_t *eth_handles = NULL;
|
||||||
|
uint8_t eth_cnt = 0;
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET || CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
ESP_GOTO_ON_FALSE(eth_handles_out != NULL && eth_cnt_out != NULL, ESP_ERR_INVALID_ARG,
|
||||||
|
err, TAG, "invalid arguments: initialized handles array or number of interfaces");
|
||||||
|
eth_handles = calloc(SPI_ETHERNETS_NUM + INTERNAL_ETHERNETS_NUM, sizeof(esp_eth_handle_t));
|
||||||
|
ESP_GOTO_ON_FALSE(eth_handles != NULL, ESP_ERR_NO_MEM, err, TAG, "no memory");
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
|
||||||
|
eth_handles[eth_cnt] = eth_init_internal(NULL, NULL);
|
||||||
|
ESP_GOTO_ON_FALSE(eth_handles[eth_cnt], ESP_FAIL, err, TAG, "internal Ethernet init failed");
|
||||||
|
eth_cnt++;
|
||||||
|
#endif //CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
ESP_GOTO_ON_ERROR(spi_bus_init(), err, TAG, "SPI bus init failed");
|
||||||
|
// Init specific SPI Ethernet module configuration from Kconfig (CS GPIO, Interrupt GPIO, etc.)
|
||||||
|
spi_eth_module_config_t spi_eth_module_config[CONFIG_EXAMPLE_SPI_ETHERNETS_NUM] = { 0 };
|
||||||
|
INIT_SPI_ETH_MODULE_CONFIG(spi_eth_module_config, 0);
|
||||||
|
// The SPI Ethernet module(s) might not have a burned factory MAC address, hence use manually configured address(es).
|
||||||
|
// In this example, Locally Administered MAC address derived from ESP32x base MAC address is used.
|
||||||
|
// Note that Locally Administered OUI range should be used only when testing on a LAN under your control!
|
||||||
|
uint8_t base_mac_addr[ETH_ADDR_LEN];
|
||||||
|
ESP_GOTO_ON_ERROR(esp_efuse_mac_get_default(base_mac_addr), err, TAG, "get EFUSE MAC failed");
|
||||||
|
uint8_t local_mac_1[ETH_ADDR_LEN];
|
||||||
|
esp_derive_local_mac(local_mac_1, base_mac_addr);
|
||||||
|
spi_eth_module_config[0].mac_addr = local_mac_1;
|
||||||
|
#if CONFIG_EXAMPLE_SPI_ETHERNETS_NUM > 1
|
||||||
|
INIT_SPI_ETH_MODULE_CONFIG(spi_eth_module_config, 1);
|
||||||
|
uint8_t local_mac_2[ETH_ADDR_LEN];
|
||||||
|
base_mac_addr[ETH_ADDR_LEN - 1] += 1;
|
||||||
|
esp_derive_local_mac(local_mac_2, base_mac_addr);
|
||||||
|
spi_eth_module_config[1].mac_addr = local_mac_2;
|
||||||
|
#endif
|
||||||
|
#if CONFIG_EXAMPLE_SPI_ETHERNETS_NUM > 2
|
||||||
|
#error Maximum number of supported SPI Ethernet devices is currently limited to 2 by this example.
|
||||||
|
#endif
|
||||||
|
for (int i = 0; i < CONFIG_EXAMPLE_SPI_ETHERNETS_NUM; i++) {
|
||||||
|
eth_handles[eth_cnt] = eth_init_spi(&spi_eth_module_config[i], NULL, NULL);
|
||||||
|
ESP_GOTO_ON_FALSE(eth_handles[eth_cnt], ESP_FAIL, err, TAG, "SPI Ethernet init failed");
|
||||||
|
eth_cnt++;
|
||||||
|
}
|
||||||
|
#endif // CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
#else
|
||||||
|
ESP_LOGD(TAG, "no Ethernet device selected to init");
|
||||||
|
#endif // CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET || CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
*eth_handles_out = eth_handles;
|
||||||
|
*eth_cnt_out = eth_cnt;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET || CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
err:
|
||||||
|
free(eth_handles);
|
||||||
|
return ret;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t example_eth_deinit(esp_eth_handle_t *eth_handles, uint8_t eth_cnt)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(eth_handles != NULL, ESP_ERR_INVALID_ARG, TAG, "array of Ethernet handles cannot be NULL");
|
||||||
|
for (int i = 0; i < eth_cnt; i++) {
|
||||||
|
esp_eth_mac_t *mac = NULL;
|
||||||
|
esp_eth_phy_t *phy = NULL;
|
||||||
|
if (eth_handles[i] != NULL) {
|
||||||
|
esp_eth_get_mac_instance(eth_handles[i], &mac);
|
||||||
|
esp_eth_get_phy_instance(eth_handles[i], &phy);
|
||||||
|
ESP_RETURN_ON_ERROR(esp_eth_driver_uninstall(eth_handles[i]), TAG, "Ethernet %p uninstall failed", eth_handles[i]);
|
||||||
|
}
|
||||||
|
if (mac != NULL) {
|
||||||
|
mac->del(mac);
|
||||||
|
}
|
||||||
|
if (phy != NULL) {
|
||||||
|
phy->del(phy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
spi_bus_free(CONFIG_EXAMPLE_ETH_SPI_HOST);
|
||||||
|
#if (CONFIG_EXAMPLE_ETH_SPI_INT0_GPIO >= 0) || (CONFIG_EXAMPLE_ETH_SPI_INT1_GPIO > 0)
|
||||||
|
// We installed the GPIO ISR service so let's uninstall it too.
|
||||||
|
// BE CAREFUL HERE though since the service might be used by other functionality!
|
||||||
|
if (gpio_isr_svc_init_by_eth) {
|
||||||
|
ESP_LOGW(TAG, "uninstalling GPIO ISR service!");
|
||||||
|
gpio_uninstall_isr_service();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif //CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||||
|
free(eth_handles);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_eth_driver.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize Ethernet driver based on Espressif IoT Development Framework Configuration
|
||||||
|
*
|
||||||
|
* @param[out] eth_handles_out array of initialized Ethernet driver handles
|
||||||
|
* @param[out] eth_cnt_out number of initialized Ethernets
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_ARG when passed invalid pointers
|
||||||
|
* - ESP_ERR_NO_MEM when there is no memory to allocate for Ethernet driver handles array
|
||||||
|
* - ESP_FAIL on any other failure
|
||||||
|
*/
|
||||||
|
esp_err_t example_eth_init(esp_eth_handle_t *eth_handles_out[], uint8_t *eth_cnt_out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief De-initialize array of Ethernet drivers
|
||||||
|
* @note All Ethernet drivers in the array must be stopped prior calling this function.
|
||||||
|
*
|
||||||
|
* @param[in] eth_handles array of Ethernet drivers to be de-initialized
|
||||||
|
* @param[in] eth_cnt number of Ethernets drivers to be de-initialized
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_ARG when passed invalid pointers
|
||||||
|
*/
|
||||||
|
esp_err_t example_eth_deinit(esp_eth_handle_t *eth_handles, uint8_t eth_cnt);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
idf_component_register(SRCS "ethernet_example_main.c"
|
||||||
|
PRIV_REQUIRES esp_netif esp_eth ethernet_init
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
menu "Example Configuration"
|
||||||
|
config EXAMPLE_ETH_DEINIT_AFTER_S
|
||||||
|
int "Stop and deinit Ethernet after elapsing number of secs"
|
||||||
|
range -1 300
|
||||||
|
default -1
|
||||||
|
help
|
||||||
|
This option is for demonstration purposes only to demonstrate deinitialization of the Ethernet driver.
|
||||||
|
Set to -1 to not deinitialize.
|
||||||
|
endmenu
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
/* Ethernet Basic 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>
|
||||||
|
#include <string.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_netif.h"
|
||||||
|
#include "esp_eth.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "ethernet_init.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
static const char *TAG = "eth_example";
|
||||||
|
|
||||||
|
/** Event handler for Ethernet events */
|
||||||
|
static void eth_event_handler(void *arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void *event_data)
|
||||||
|
{
|
||||||
|
uint8_t mac_addr[6] = {0};
|
||||||
|
/* we can get the ethernet driver handle from event data */
|
||||||
|
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
|
||||||
|
|
||||||
|
switch (event_id) {
|
||||||
|
case ETHERNET_EVENT_CONNECTED:
|
||||||
|
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
|
||||||
|
ESP_LOGI(TAG, "Ethernet Link Up");
|
||||||
|
ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||||
|
break;
|
||||||
|
case ETHERNET_EVENT_DISCONNECTED:
|
||||||
|
ESP_LOGI(TAG, "Ethernet Link Down");
|
||||||
|
break;
|
||||||
|
case ETHERNET_EVENT_START:
|
||||||
|
ESP_LOGI(TAG, "Ethernet Started");
|
||||||
|
break;
|
||||||
|
case ETHERNET_EVENT_STOP:
|
||||||
|
ESP_LOGI(TAG, "Ethernet Stopped");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Event handler for IP_EVENT_ETH_GOT_IP */
|
||||||
|
static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void *event_data)
|
||||||
|
{
|
||||||
|
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
|
||||||
|
const esp_netif_ip_info_t *ip_info = &event->ip_info;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Ethernet Got IP Address");
|
||||||
|
ESP_LOGI(TAG, "~~~~~~~~~~~");
|
||||||
|
ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
|
||||||
|
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
|
||||||
|
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
|
||||||
|
ESP_LOGI(TAG, "~~~~~~~~~~~");
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
// Initialize Ethernet driver
|
||||||
|
uint8_t eth_port_cnt = 0;
|
||||||
|
esp_eth_handle_t *eth_handles;
|
||||||
|
ESP_ERROR_CHECK(example_eth_init(ð_handles, ð_port_cnt));
|
||||||
|
|
||||||
|
// Initialize TCP/IP network interface aka the esp-netif (should be called only once in application)
|
||||||
|
ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
// Create default event loop that running in background
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
|
||||||
|
esp_netif_t *eth_netifs[eth_port_cnt];
|
||||||
|
esp_eth_netif_glue_handle_t eth_netif_glues[eth_port_cnt];
|
||||||
|
|
||||||
|
// Create instance(s) of esp-netif for Ethernet(s)
|
||||||
|
if (eth_port_cnt == 1) {
|
||||||
|
// Use ESP_NETIF_DEFAULT_ETH when just one Ethernet interface is used and you don't need to modify
|
||||||
|
// default esp-netif configuration parameters.
|
||||||
|
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();
|
||||||
|
eth_netifs[0] = esp_netif_new(&cfg);
|
||||||
|
eth_netif_glues[0] = esp_eth_new_netif_glue(eth_handles[0]);
|
||||||
|
// Attach Ethernet driver to TCP/IP stack
|
||||||
|
ESP_ERROR_CHECK(esp_netif_attach(eth_netifs[0], eth_netif_glues[0]));
|
||||||
|
} else {
|
||||||
|
// Use ESP_NETIF_INHERENT_DEFAULT_ETH when multiple Ethernet interfaces are used and so you need to modify
|
||||||
|
// esp-netif configuration parameters for each interface (name, priority, etc.).
|
||||||
|
esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH();
|
||||||
|
esp_netif_config_t cfg_spi = {
|
||||||
|
.base = &esp_netif_config,
|
||||||
|
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH
|
||||||
|
};
|
||||||
|
char if_key_str[10];
|
||||||
|
char if_desc_str[10];
|
||||||
|
char num_str[3];
|
||||||
|
for (int i = 0; i < eth_port_cnt; i++) {
|
||||||
|
itoa(i, num_str, 10);
|
||||||
|
strcat(strcpy(if_key_str, "ETH_"), num_str);
|
||||||
|
strcat(strcpy(if_desc_str, "eth"), num_str);
|
||||||
|
esp_netif_config.if_key = if_key_str;
|
||||||
|
esp_netif_config.if_desc = if_desc_str;
|
||||||
|
esp_netif_config.route_prio -= i*5;
|
||||||
|
eth_netifs[i] = esp_netif_new(&cfg_spi);
|
||||||
|
eth_netif_glues[i] = esp_eth_new_netif_glue(eth_handles[i]);
|
||||||
|
// Attach Ethernet driver to TCP/IP stack
|
||||||
|
ESP_ERROR_CHECK(esp_netif_attach(eth_netifs[i], eth_netif_glues[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register user defined event handlers
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));
|
||||||
|
|
||||||
|
// Start Ethernet driver state machine
|
||||||
|
for (int i = 0; i < eth_port_cnt; i++) {
|
||||||
|
ESP_ERROR_CHECK(esp_eth_start(eth_handles[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_ETH_DEINIT_AFTER_S >= 0
|
||||||
|
// For demonstration purposes, wait and then deinit Ethernet network
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(CONFIG_EXAMPLE_ETH_DEINIT_AFTER_S * 1000));
|
||||||
|
ESP_LOGI(TAG, "stop and deinitialize Ethernet network...");
|
||||||
|
// Stop Ethernet driver state machine and destroy netif
|
||||||
|
for (int i = 0; i < eth_port_cnt; i++) {
|
||||||
|
ESP_ERROR_CHECK(esp_eth_stop(eth_handles[i]));
|
||||||
|
ESP_ERROR_CHECK(esp_eth_del_netif_glue(eth_netif_glues[i]));
|
||||||
|
esp_netif_destroy(eth_netifs[i]);
|
||||||
|
}
|
||||||
|
esp_netif_deinit();
|
||||||
|
ESP_ERROR_CHECK(example_eth_deinit(eth_handles, eth_port_cnt));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, got_ip_event_handler));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_delete_default());
|
||||||
|
#endif // EXAMPLE_ETH_DEINIT_AFTER_S > 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pytest_embedded import Dut
|
||||||
|
from pytest_embedded_idf.utils import idf_parametrize
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'config',
|
||||||
|
[
|
||||||
|
pytest.param('default_ip101', marks=[pytest.mark.ethernet_router]),
|
||||||
|
pytest.param('default_generic', marks=[pytest.mark.ethernet_router]),
|
||||||
|
pytest.param('default_dm9051', marks=[pytest.mark.eth_dm9051]),
|
||||||
|
],
|
||||||
|
indirect=True,
|
||||||
|
)
|
||||||
|
@idf_parametrize('target', ['esp32'], indirect=['target'])
|
||||||
|
def test_esp_eth_basic(dut: Dut) -> None:
|
||||||
|
# wait for ip received
|
||||||
|
dut_ip = dut.expect(r'esp_netif_handlers: .+ ip: (\d+\.\d+\.\d+\.\d+),').group(1)
|
||||||
|
# ping it once
|
||||||
|
param = '-n' if platform.system().lower() == 'windows' else '-c'
|
||||||
|
command = ['ping', param, '1', dut_ip]
|
||||||
|
output = subprocess.run(command, capture_output=True)
|
||||||
|
assert 'unreachable' not in str(output.stdout)
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
|||||||
|
CONFIG_EXAMPLE_USE_SPI_ETHERNET=y
|
||||||
|
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=n
|
||||||
|
CONFIG_EXAMPLE_SPI_ETHERNETS_NUM=1
|
||||||
|
CONFIG_EXAMPLE_USE_DM9051=y
|
||||||
|
CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ=20
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
|
||||||
|
CONFIG_EXAMPLE_ETH_PHY_GENERIC=y
|
||||||
|
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
|
||||||
|
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||||
|
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||||
|
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
||||||
|
|
||||||
|
CONFIG_ETH_ENABLED=y
|
||||||
|
CONFIG_ETH_USE_ESP32_EMAC=y
|
||||||
|
CONFIG_ETH_PHY_INTERFACE_RMII=y
|
||||||
|
CONFIG_ETH_RMII_CLK_INPUT=y
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
|
||||||
|
CONFIG_EXAMPLE_ETH_PHY_IP101=y
|
||||||
|
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
|
||||||
|
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||||
|
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||||
|
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
||||||
|
|
||||||
|
CONFIG_ETH_ENABLED=y
|
||||||
|
CONFIG_ETH_USE_ESP32_EMAC=y
|
||||||
|
CONFIG_ETH_PHY_INTERFACE_RMII=y
|
||||||
|
CONFIG_ETH_RMII_CLK_INPUT=y
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# The following five 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.16)
|
||||||
|
|
||||||
|
set(EXTRA_COMPONENT_DIRS
|
||||||
|
../common_components
|
||||||
|
)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(mp3_player)
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
dependencies:
|
||||||
|
chmorgan/esp-audio-player:
|
||||||
|
component_hash: c8ac1998e9af863bc41b57e592f88d1a5791a0f891485122336ddabbf7a65033
|
||||||
|
dependencies:
|
||||||
|
- name: chmorgan/esp-libhelix-mp3
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: private
|
||||||
|
version: '>=1.0.0,<2.0.0'
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=5.0'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 1.0.7
|
||||||
|
chmorgan/esp-file-iterator:
|
||||||
|
component_hash: 327091394b9ef5c2cd395a960ab70ae64479e0a8831cbd9925e38895fad93719
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.1.0'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 1.0.0
|
||||||
|
chmorgan/esp-libhelix-mp3:
|
||||||
|
component_hash: cbb76089dc2c5749f7b470e2e70aedc44c9da519e04eb9a67d4c7ec275229e53
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.1.0'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
type: service
|
||||||
|
version: 1.0.3
|
||||||
|
espressif/button:
|
||||||
|
component_hash: f53face2ab21fa0ffaf4cf0f6e513d393f56df6586bb2ad1146120f03f19ee05
|
||||||
|
dependencies:
|
||||||
|
- name: espressif/cmake_utilities
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: private
|
||||||
|
version: '*'
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.0'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 4.1.3
|
||||||
|
espressif/cmake_utilities:
|
||||||
|
component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.1'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
type: service
|
||||||
|
version: 0.5.3
|
||||||
|
espressif/esp32_p4_function_ev_board:
|
||||||
|
dependencies:
|
||||||
|
- name: espressif/esp_codec_dev
|
||||||
|
public: true
|
||||||
|
version: 1.2.*
|
||||||
|
- name: espressif/esp_lcd_ek79007
|
||||||
|
version: 1.*
|
||||||
|
- name: espressif/esp_lcd_ili9881c
|
||||||
|
version: 1.*
|
||||||
|
- name: espressif/esp_lcd_touch_gt911
|
||||||
|
version: ^1
|
||||||
|
- name: waveshare/esp_lcd_jd9365_10_1
|
||||||
|
version: ^1.0.3
|
||||||
|
- name: espressif/esp_lvgl_port
|
||||||
|
public: true
|
||||||
|
version: ^2
|
||||||
|
- name: idf
|
||||||
|
version: '>=5.3'
|
||||||
|
- name: lvgl/lvgl
|
||||||
|
version: '>=8,<10'
|
||||||
|
source:
|
||||||
|
path: /home/jczn/esp/JC-ESP32P4-M3-DEV/common_components/espressif__esp32_p4_function_ev_board
|
||||||
|
type: local
|
||||||
|
targets:
|
||||||
|
- esp32p4
|
||||||
|
version: 4.1.1
|
||||||
|
espressif/esp_codec_dev:
|
||||||
|
component_hash: 014948481bda426cd46714f297fe1891711246c62bea288863a8cc8cf13ef1f0
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.0'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 1.2.0
|
||||||
|
espressif/esp_lcd_ek79007:
|
||||||
|
component_hash: 07c1afab7e9fd4dd2fd06ff9245e65327c5bbd5485efec199496e19a9304d47b
|
||||||
|
dependencies:
|
||||||
|
- name: espressif/cmake_utilities
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: private
|
||||||
|
version: 0.*
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=5.3'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
targets:
|
||||||
|
- esp32p4
|
||||||
|
version: 1.0.2
|
||||||
|
espressif/esp_lcd_ili9881c:
|
||||||
|
component_hash: f4f374226b62baf13f735864e8fae58e17c537df34d598e059f6caad4761ef65
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=5.3'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
targets:
|
||||||
|
- esp32p4
|
||||||
|
version: 1.0.1
|
||||||
|
espressif/esp_lcd_touch:
|
||||||
|
component_hash: 779b4ba2464a3ae85681e4b860caa5fdc35801458c23f3039ee761bae7f442a4
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.4.2'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
type: service
|
||||||
|
version: 1.1.2
|
||||||
|
espressif/esp_lcd_touch_gt911:
|
||||||
|
component_hash: acc1c184358aa29ef72506f618c9c76a8cc2bf12af38a2bff3d44d84f3a08857
|
||||||
|
dependencies:
|
||||||
|
- name: espressif/esp_lcd_touch
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: public
|
||||||
|
version: ^1.1.0
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.4.2'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 1.1.3
|
||||||
|
espressif/esp_lvgl_port:
|
||||||
|
component_hash: e720c95cf0667554a204591bb5fade4655fb2990465557041200fa44b5bc7556
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.4'
|
||||||
|
- name: lvgl/lvgl
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: public
|
||||||
|
version: '>=8,<10'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 2.6.0
|
||||||
|
espressif/i2c_bus:
|
||||||
|
component_hash: e3dddc78baa172f4768f3973fbecbd6c6c1f2cb23cc6a36cf3132758be092482
|
||||||
|
dependencies:
|
||||||
|
- name: espressif/cmake_utilities
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: private
|
||||||
|
version: 0.*
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.0'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
type: service
|
||||||
|
version: 1.4.0
|
||||||
|
idf:
|
||||||
|
source:
|
||||||
|
type: idf
|
||||||
|
version: 5.5.0
|
||||||
|
lvgl/lvgl:
|
||||||
|
component_hash: b702d642e03e95928046d5c6726558e6444e112420c77efa5fdb6650b0a13c5d
|
||||||
|
dependencies: []
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 9.3.0
|
||||||
|
waveshare/esp_lcd_jd9365_10_1:
|
||||||
|
component_hash: 148d03ade699a7fa0f08c98e86c1df3d32e92580fac421c04e0ee5b779e86267
|
||||||
|
dependencies:
|
||||||
|
- name: espressif/cmake_utilities
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: private
|
||||||
|
version: 0.*
|
||||||
|
- name: espressif/i2c_bus
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: private
|
||||||
|
version: ^1.3.0
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=5.3'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
targets:
|
||||||
|
- esp32p4
|
||||||
|
version: 1.0.3
|
||||||
|
direct_dependencies:
|
||||||
|
- chmorgan/esp-audio-player
|
||||||
|
- chmorgan/esp-file-iterator
|
||||||
|
- espressif/button
|
||||||
|
- espressif/esp32_p4_function_ev_board
|
||||||
|
- espressif/esp_codec_dev
|
||||||
|
- espressif/esp_lcd_ek79007
|
||||||
|
- espressif/esp_lcd_ili9881c
|
||||||
|
- espressif/esp_lcd_touch_gt911
|
||||||
|
- espressif/esp_lvgl_port
|
||||||
|
- idf
|
||||||
|
- lvgl/lvgl
|
||||||
|
- waveshare/esp_lcd_jd9365_10_1
|
||||||
|
manifest_hash: 229ed32ff9200019cd1a12c5bf4b55456c8c469acd0d6599875ec89a02c14973
|
||||||
|
target: esp32p4
|
||||||
|
version: 2.0.0
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "mp3_player.c"
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
## IDF Component Manager Manifest File
|
||||||
|
dependencies:
|
||||||
|
## Required IDF version
|
||||||
|
idf:
|
||||||
|
version: '>=4.1.0'
|
||||||
|
# # Put list of dependencies here
|
||||||
|
# # For components maintained by Espressif:
|
||||||
|
# component: "~1.0.0"
|
||||||
|
# # For 3rd party components:
|
||||||
|
# username/component: ">=1.0.0,<2.0.0"
|
||||||
|
# username2/component2:
|
||||||
|
# version: "~1.0.0"
|
||||||
|
# # For transient dependencies `public` flag can be set.
|
||||||
|
# # `public` flag doesn't have an effect dependencies of the `main` component.
|
||||||
|
# # All dependencies of `main` are public by default.
|
||||||
|
# public: true
|
||||||
|
espressif/button: ^4.1.3
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/ledc.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_spiffs.h"
|
||||||
|
#include "esp_vfs_fat.h"
|
||||||
|
#include "bsp/esp-bsp.h"
|
||||||
|
#include "bsp/display.h"
|
||||||
|
#include "bsp_board_extra.h"
|
||||||
|
#include "audio_player.h"
|
||||||
|
|
||||||
|
#include "file_iterator.h"
|
||||||
|
#include "iot_button.h"
|
||||||
|
#include "button_gpio.h"
|
||||||
|
|
||||||
|
#define TAG "mp3_player"
|
||||||
|
#define MUSIC_DIR "/sdcard/music"
|
||||||
|
#define BUTTON_IO_NUM 35
|
||||||
|
#define BUTTON_ACTIVE_LEVEL 0
|
||||||
|
|
||||||
|
file_iterator_instance_t *_file_iterator;
|
||||||
|
static audio_player_cb_t audio_idle_callback = NULL;
|
||||||
|
static QueueHandle_t event_queue;
|
||||||
|
static SemaphoreHandle_t semph_event;
|
||||||
|
int music_cnt = 0;
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
static void audio_player_callback(audio_player_cb_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG,"audio_player_callback %d",ctx->audio_event);
|
||||||
|
if(ctx->audio_event == AUDIO_PLAYER_CALLBACK_EVENT_SHUTDOWN || ctx->audio_event == AUDIO_PLAYER_CALLBACK_EVENT_IDLE)
|
||||||
|
xSemaphoreGive(semph_event);
|
||||||
|
// xQueueSend(event_queue, &(ctx->audio_event), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mp3_player_task(void *arg)
|
||||||
|
{
|
||||||
|
audio_player_callback_event_t event;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
bsp_extra_player_play_index(_file_iterator,cnt);
|
||||||
|
cnt++;
|
||||||
|
if(cnt > music_cnt)
|
||||||
|
cnt = 0;
|
||||||
|
xSemaphoreTake(semph_event, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
bsp_extra_player_del();
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
esp_err_t ret = bsp_sdcard_mount();
|
||||||
|
if(ret == ESP_OK)
|
||||||
|
ESP_LOGI(TAG, "SD card mount successfully");
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(bsp_extra_codec_init());
|
||||||
|
bsp_extra_codec_volume_set(40,NULL);
|
||||||
|
bsp_extra_player_init();
|
||||||
|
|
||||||
|
_file_iterator = file_iterator_new(MUSIC_DIR);
|
||||||
|
music_cnt = file_iterator_get_count(_file_iterator);
|
||||||
|
|
||||||
|
event_queue = xQueueCreate(1, sizeof(audio_player_callback_event_t));
|
||||||
|
semph_event = xSemaphoreCreateBinary();
|
||||||
|
|
||||||
|
bsp_extra_player_register_callback(audio_player_callback,NULL);
|
||||||
|
|
||||||
|
xTaskCreatePinnedToCore(mp3_player_task,"mp3_player",4096,NULL,4,NULL,1);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
|
||||||
|
nvs, data, nvs, 0x9000, 0x6000,
|
||||||
|
phy_init, data, phy, 0xf000, 0x1000,
|
||||||
|
factory, app, factory, , 9M,
|
||||||
|
storage, data, spiffs, , 5M,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,45 @@
|
|||||||
|
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||||
|
# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration
|
||||||
|
#
|
||||||
|
CONFIG_IDF_TARGET="esp32p4"
|
||||||
|
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||||
|
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||||
|
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||||
|
CONFIG_SPIRAM=y
|
||||||
|
CONFIG_SPIRAM_SPEED_200M=y
|
||||||
|
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
|
||||||
|
CONFIG_CACHE_L2_CACHE_256KB=y
|
||||||
|
CONFIG_CACHE_L2_CACHE_LINE_128B=y
|
||||||
|
CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096
|
||||||
|
CONFIG_FATFS_LFN_HEAP=y
|
||||||
|
CONFIG_FATFS_API_ENCODING_UTF_8=y
|
||||||
|
CONFIG_FREERTOS_HZ=1000
|
||||||
|
CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y
|
||||||
|
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
|
||||||
|
CONFIG_LV_USE_CLIB_MALLOC=y
|
||||||
|
CONFIG_LV_USE_CLIB_STRING=y
|
||||||
|
CONFIG_LV_USE_CLIB_SPRINTF=y
|
||||||
|
CONFIG_LV_DEF_REFR_PERIOD=15
|
||||||
|
CONFIG_LV_OS_FREERTOS=y
|
||||||
|
CONFIG_LV_DRAW_SW_DRAW_UNIT_CNT=2
|
||||||
|
CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y
|
||||||
|
CONFIG_LV_FONT_MONTSERRAT_12=y
|
||||||
|
CONFIG_LV_FONT_MONTSERRAT_16=y
|
||||||
|
CONFIG_LV_FONT_MONTSERRAT_18=y
|
||||||
|
CONFIG_LV_FONT_MONTSERRAT_22=y
|
||||||
|
CONFIG_LV_USE_FONT_COMPRESSED=y
|
||||||
|
CONFIG_LV_TXT_BREAK_CHARS=" ,.;:-_"
|
||||||
|
CONFIG_LV_USE_SYSMON=y
|
||||||
|
CONFIG_LV_USE_PERF_MONITOR=y
|
||||||
|
CONFIG_LV_USE_IMGFONT=y
|
||||||
|
CONFIG_LV_USE_DEMO_BENCHMARK=y
|
||||||
|
CONFIG_LV_USE_DEMO_RENDER=y
|
||||||
|
CONFIG_LV_USE_DEMO_SCROLL=y
|
||||||
|
CONFIG_LV_USE_DEMO_STRESS=y
|
||||||
|
CONFIG_LV_USE_DEMO_TRANSFORM=y
|
||||||
|
CONFIG_LV_USE_DEMO_MUSIC=y
|
||||||
|
CONFIG_LV_DEMO_MUSIC_AUTO_PLAY=y
|
||||||
|
CONFIG_LV_USE_DEMO_FLEX_LAYOUT=y
|
||||||
|
CONFIG_LV_USE_DEMO_MULTILANG=y
|
||||||
|
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
|||||||
|
# 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.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||||
|
idf_build_set_property(MINIMAL_BUILD ON)
|
||||||
|
project(echo_rs485)
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
# UART RS485 Echo Example
|
||||||
|
|
||||||
|
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||||
|
|
||||||
|
This is an example which echoes any data it receives on UART port back to the sender in the RS485 network.
|
||||||
|
It uses ESP-IDF UART software driver in RS485 half duplex transmission mode and requires external connection of bus drivers.
|
||||||
|
The approach demonstrated in this example can be used in user application to transmit/receive data in RS485 networks.
|
||||||
|
|
||||||
|
## How to use example
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
PC + USB Serial adapter connected to USB port + RS485 line drivers + Espressif development board.
|
||||||
|
The MAX483 line driver is used for example below but other similar chips can be used as well.
|
||||||
|
|
||||||
|
#### RS485 example connection circuit schematic:
|
||||||
|
```
|
||||||
|
VCC ---------------+ +--------------- VCC
|
||||||
|
| |
|
||||||
|
+-------x-------+ +-------x-------+
|
||||||
|
RXD <------| RO | | RO|-----> RXD
|
||||||
|
| B|---------------|B |
|
||||||
|
TXD ------>| DI MAX483 | \ / | MAX483 DI|<----- TXD
|
||||||
|
ESP32 BOARD | | RS-485 side | | SERIAL ADAPTER SIDE
|
||||||
|
RTS --+--->| DE | / \ | DE|---+
|
||||||
|
| | A|---------------|A | |
|
||||||
|
+----| /RE | | /RE|---+-- RTS
|
||||||
|
+-------x-------+ +-------x-------+
|
||||||
|
| |
|
||||||
|
--- ---
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Connect an external RS485 serial interface to an ESP32 board
|
||||||
|
Connect a USB-to-RS485 adapter to a computer, then connect the adapter's A/B output lines with the corresponding A/B output lines of the RS485 line driver connected to the ESP32 chip (see figure above).
|
||||||
|
```
|
||||||
|
------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
| UART Interface | #define | Default pin for ESP32 | Default pins for others | External RS485 Driver Pin |
|
||||||
|
| ----------------------|--------------------|-----------------------|---------------------------|---------------------------|
|
||||||
|
| Transmit Data (TxD) | CONFIG_MB_UART_TXD | GPIO23 | GPIO9 | DI |
|
||||||
|
| Receive Data (RxD) | CONFIG_MB_UART_RXD | GPIO22 | GPIO8 | RO |
|
||||||
|
| Request To Send (RTS) | CONFIG_MB_UART_RTS | GPIO18 | GPIO10 | ~RE/DE |
|
||||||
|
| Ground | n/a | GND | GND | GND |
|
||||||
|
------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
```
|
||||||
|
Note: Each target chip has different GPIO pins available for UART connection. Please refer to UART documentation for selected target for more information.
|
||||||
|
|
||||||
|
### Configure the project
|
||||||
|
```
|
||||||
|
idf.py menuconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||||
|
```
|
||||||
|
idf.py -p PORT flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
#### Setup external terminal software
|
||||||
|
Refer to the example and set up a serial terminal program to the same settings as of UART in ESP32-WROVER-KIT board.
|
||||||
|
Open the external serial interface in the terminal. By default if no any symbols are received, the application sends character `.` to check transmission side.
|
||||||
|
When typing message and push send button in the terminal you should see the message `RS485 Received: [ your message ]`, where "your message" is the message you sent from terminal.
|
||||||
|
Verify if echo indeed comes from your board by disconnecting either `TxD` or `RxD` pin. Once done there should be no any `.` displayed.
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
Example output of the application:
|
||||||
|
```
|
||||||
|
I (655020) RS485_ECHO_APP: Received 12 bytes:
|
||||||
|
[ 0x79 0x6F 0x75 0x72 0x20 0x6D 0x65 0x73 0x73 0x61 0x67 0x65 ]
|
||||||
|
```
|
||||||
|
The received message is showed in hexadecimal form in the brackets.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
When example software does not show the `.` symbol, the issue is most likely related to connection errors of the external RS485 interface.
|
||||||
|
Check the RS485 interface connection with the environment according to schematic above and restart the application.
|
||||||
|
Then start terminal software and open the appropriate serial port.
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
idf_component_register(SRCS "rs485_example.c"
|
||||||
|
REQUIRES nvs_flash esp_driver_uart
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
menu "Echo RS485 Example Configuration"
|
||||||
|
|
||||||
|
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
|
||||||
|
|
||||||
|
config ECHO_UART_PORT_NUM
|
||||||
|
int "UART port number"
|
||||||
|
range 0 2 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3
|
||||||
|
default 2 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3
|
||||||
|
range 0 1
|
||||||
|
default 1
|
||||||
|
help
|
||||||
|
UART communication port number for the example.
|
||||||
|
See UART documentation for available port numbers.
|
||||||
|
|
||||||
|
config ECHO_UART_BAUD_RATE
|
||||||
|
int "UART communication speed"
|
||||||
|
range 1200 115200
|
||||||
|
default 115200
|
||||||
|
help
|
||||||
|
UART communication speed for Modbus example.
|
||||||
|
|
||||||
|
config ECHO_UART_RXD
|
||||||
|
int "UART RXD pin number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
|
||||||
|
default 27 if IDF_TARGET_ESP32P4
|
||||||
|
default 22 if IDF_TARGET_ESP32
|
||||||
|
default 8 if !IDF_TARGET_ESP32
|
||||||
|
help
|
||||||
|
GPIO number for UART RX pin. See UART documentation for more information
|
||||||
|
about available pin numbers for UART.
|
||||||
|
|
||||||
|
config ECHO_UART_TXD
|
||||||
|
int "UART TXD pin number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default 26 if IDF_TARGET_ESP32P4
|
||||||
|
default 23 if IDF_TARGET_ESP32
|
||||||
|
default 9 if !IDF_TARGET_ESP32
|
||||||
|
help
|
||||||
|
GPIO number for UART TX pin. See UART documentation for more information
|
||||||
|
about available pin numbers for UART.
|
||||||
|
|
||||||
|
# config ECHO_UART_RTS
|
||||||
|
# int "UART RTS pin number"
|
||||||
|
# range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
# default -1 if IDF_TARGET_ESP32P4
|
||||||
|
# default 18 if IDF_TARGET_ESP32
|
||||||
|
# default 10 if !IDF_TARGET_ESP32
|
||||||
|
# help
|
||||||
|
# GPIO number for UART RTS pin. This pin is connected to
|
||||||
|
# ~RE/DE pin of RS485 transceiver to switch direction.
|
||||||
|
# See UART documentation for more information about available pin
|
||||||
|
# numbers for UART.
|
||||||
|
|
||||||
|
config ECHO_TASK_STACK_SIZE
|
||||||
|
int "UART echo RS485 example task stack size"
|
||||||
|
range 1024 16384
|
||||||
|
default 3072
|
||||||
|
help
|
||||||
|
Defines stack size for UART echo RS485 example. Insufficient stack size can cause crash.
|
||||||
|
|
||||||
|
endmenu
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
/* Uart Events 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>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "driver/uart.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a example which echos any data it receives on UART back to the sender using RS485 interface in half duplex mode.
|
||||||
|
*/
|
||||||
|
#define TAG "RS485_ECHO_APP"
|
||||||
|
|
||||||
|
// Note: Some pins on target chip cannot be assigned for UART communication.
|
||||||
|
// Please refer to documentation for selected board and target to configure pins using Kconfig.
|
||||||
|
#define ECHO_TEST_TXD (CONFIG_ECHO_UART_TXD)
|
||||||
|
#define ECHO_TEST_RXD (CONFIG_ECHO_UART_RXD)
|
||||||
|
|
||||||
|
// RTS for RS485 Half-Duplex Mode manages DE/~RE
|
||||||
|
#define ECHO_TEST_RTS (UART_PIN_NO_CHANGE)
|
||||||
|
|
||||||
|
// CTS is not used in RS485 Half-Duplex Mode
|
||||||
|
#define ECHO_TEST_CTS (UART_PIN_NO_CHANGE)
|
||||||
|
|
||||||
|
#define BUF_SIZE (127)
|
||||||
|
#define BAUD_RATE (CONFIG_ECHO_UART_BAUD_RATE)
|
||||||
|
|
||||||
|
// Read packet timeout
|
||||||
|
#define PACKET_READ_TICS (100 / portTICK_PERIOD_MS)
|
||||||
|
#define ECHO_TASK_STACK_SIZE (CONFIG_ECHO_TASK_STACK_SIZE)
|
||||||
|
#define ECHO_TASK_PRIO (10)
|
||||||
|
#define ECHO_UART_PORT (CONFIG_ECHO_UART_PORT_NUM)
|
||||||
|
|
||||||
|
// Timeout threshold for UART = number of symbols (~10 tics) with unchanged state on receive pin
|
||||||
|
#define ECHO_READ_TOUT (3) // 3.5T * 8 = 28 ticks, TOUT=3 -> ~24..33 ticks
|
||||||
|
|
||||||
|
static void echo_send(const int port, const char* str, uint8_t length)
|
||||||
|
{
|
||||||
|
if (uart_write_bytes(port, str, length) != length) {
|
||||||
|
ESP_LOGE(TAG, "Send data critical failure.");
|
||||||
|
// add your code to handle sending failure here
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An example of echo test with hardware flow control on UART
|
||||||
|
static void echo_task(void *arg)
|
||||||
|
{
|
||||||
|
const int uart_num = ECHO_UART_PORT;
|
||||||
|
uart_config_t uart_config = {
|
||||||
|
.baud_rate = BAUD_RATE,
|
||||||
|
.data_bits = UART_DATA_8_BITS,
|
||||||
|
.parity = UART_PARITY_DISABLE,
|
||||||
|
.stop_bits = UART_STOP_BITS_1,
|
||||||
|
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||||
|
.rx_flow_ctrl_thresh = 122,
|
||||||
|
.source_clk = UART_SCLK_DEFAULT,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set UART log level
|
||||||
|
esp_log_level_set(TAG, ESP_LOG_INFO);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Start RS485 application test and configure UART.");
|
||||||
|
|
||||||
|
// Install UART driver (we don't need an event queue here)
|
||||||
|
// In this example we don't even use a buffer for sending data.
|
||||||
|
ESP_ERROR_CHECK(uart_driver_install(uart_num, BUF_SIZE * 2, 0, 0, NULL, 0));
|
||||||
|
|
||||||
|
// Configure UART parameters
|
||||||
|
ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "UART set pins, mode and install driver.");
|
||||||
|
|
||||||
|
// Set UART pins as per KConfig settings
|
||||||
|
ESP_ERROR_CHECK(uart_set_pin(uart_num, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS));
|
||||||
|
|
||||||
|
// Set RS485 half duplex mode
|
||||||
|
ESP_ERROR_CHECK(uart_set_mode(uart_num, UART_MODE_RS485_HALF_DUPLEX));
|
||||||
|
|
||||||
|
// Set read timeout of UART TOUT feature
|
||||||
|
ESP_ERROR_CHECK(uart_set_rx_timeout(uart_num, ECHO_READ_TOUT));
|
||||||
|
|
||||||
|
// Allocate buffers for UART
|
||||||
|
uint8_t* data = (uint8_t*) malloc(BUF_SIZE);
|
||||||
|
assert(data);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "UART start receive loop.\r");
|
||||||
|
echo_send(uart_num, "Start RS485 UART test.\r\n", 24);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
//Read data from UART
|
||||||
|
int len = uart_read_bytes(uart_num, data, BUF_SIZE, PACKET_READ_TICS);
|
||||||
|
|
||||||
|
//Write data back to UART
|
||||||
|
if (len > 0) {
|
||||||
|
echo_send(uart_num, "\r\n", 2);
|
||||||
|
char prefix[] = "RS485 Received: [";
|
||||||
|
echo_send(uart_num, prefix, (sizeof(prefix) - 1));
|
||||||
|
ESP_LOGI(TAG, "Received %u bytes:", len);
|
||||||
|
printf("[ ");
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
printf("0x%.2X ", (uint8_t)data[i]);
|
||||||
|
echo_send(uart_num, (const char*)&data[i], 1);
|
||||||
|
// Add a Newline character if you get a return character from paste (Paste tests multibyte receipt/buffer)
|
||||||
|
if (data[i] == '\r') {
|
||||||
|
echo_send(uart_num, "\n", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("] \n");
|
||||||
|
echo_send(uart_num, "]\r\n", 3);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Echo a "." to show we are alive while we wait for input
|
||||||
|
echo_send(uart_num, ".", 1);
|
||||||
|
ESP_ERROR_CHECK(uart_wait_tx_done(uart_num, 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
//A uart read/write example without event queue;
|
||||||
|
xTaskCreate(echo_task, "uart_echo_task", ECHO_TASK_STACK_SIZE, NULL, ECHO_TASK_PRIO, NULL);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
|||||||
|
# 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.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||||
|
idf_build_set_property(MINIMAL_BUILD ON)
|
||||||
|
project(echo_rs485)
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
# UART RS485 Echo Example
|
||||||
|
|
||||||
|
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||||
|
|
||||||
|
This is an example which echoes any data it receives on UART port back to the sender in the RS485 network.
|
||||||
|
It uses ESP-IDF UART software driver in RS485 half duplex transmission mode and requires external connection of bus drivers.
|
||||||
|
The approach demonstrated in this example can be used in user application to transmit/receive data in RS485 networks.
|
||||||
|
|
||||||
|
## How to use example
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
PC + USB Serial adapter connected to USB port + RS485 line drivers + Espressif development board.
|
||||||
|
The MAX483 line driver is used for example below but other similar chips can be used as well.
|
||||||
|
|
||||||
|
#### RS485 example connection circuit schematic:
|
||||||
|
```
|
||||||
|
VCC ---------------+ +--------------- VCC
|
||||||
|
| |
|
||||||
|
+-------x-------+ +-------x-------+
|
||||||
|
RXD <------| RO | | RO|-----> RXD
|
||||||
|
| B|---------------|B |
|
||||||
|
TXD ------>| DI MAX483 | \ / | MAX483 DI|<----- TXD
|
||||||
|
ESP32 BOARD | | RS-485 side | | SERIAL ADAPTER SIDE
|
||||||
|
RTS --+--->| DE | / \ | DE|---+
|
||||||
|
| | A|---------------|A | |
|
||||||
|
+----| /RE | | /RE|---+-- RTS
|
||||||
|
+-------x-------+ +-------x-------+
|
||||||
|
| |
|
||||||
|
--- ---
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Connect an external RS485 serial interface to an ESP32 board
|
||||||
|
Connect a USB-to-RS485 adapter to a computer, then connect the adapter's A/B output lines with the corresponding A/B output lines of the RS485 line driver connected to the ESP32 chip (see figure above).
|
||||||
|
```
|
||||||
|
------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
| UART Interface | #define | Default pin for ESP32 | Default pins for others | External RS485 Driver Pin |
|
||||||
|
| ----------------------|--------------------|-----------------------|---------------------------|---------------------------|
|
||||||
|
| Transmit Data (TxD) | CONFIG_MB_UART_TXD | GPIO23 | GPIO9 | DI |
|
||||||
|
| Receive Data (RxD) | CONFIG_MB_UART_RXD | GPIO22 | GPIO8 | RO |
|
||||||
|
| Request To Send (RTS) | CONFIG_MB_UART_RTS | GPIO18 | GPIO10 | ~RE/DE |
|
||||||
|
| Ground | n/a | GND | GND | GND |
|
||||||
|
------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
```
|
||||||
|
Note: Each target chip has different GPIO pins available for UART connection. Please refer to UART documentation for selected target for more information.
|
||||||
|
|
||||||
|
### Configure the project
|
||||||
|
```
|
||||||
|
idf.py menuconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||||
|
```
|
||||||
|
idf.py -p PORT flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
#### Setup external terminal software
|
||||||
|
Refer to the example and set up a serial terminal program to the same settings as of UART in ESP32-WROVER-KIT board.
|
||||||
|
Open the external serial interface in the terminal. By default if no any symbols are received, the application sends character `.` to check transmission side.
|
||||||
|
When typing message and push send button in the terminal you should see the message `RS485 Received: [ your message ]`, where "your message" is the message you sent from terminal.
|
||||||
|
Verify if echo indeed comes from your board by disconnecting either `TxD` or `RxD` pin. Once done there should be no any `.` displayed.
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
Example output of the application:
|
||||||
|
```
|
||||||
|
I (655020) RS485_ECHO_APP: Received 12 bytes:
|
||||||
|
[ 0x79 0x6F 0x75 0x72 0x20 0x6D 0x65 0x73 0x73 0x61 0x67 0x65 ]
|
||||||
|
```
|
||||||
|
The received message is showed in hexadecimal form in the brackets.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
When example software does not show the `.` symbol, the issue is most likely related to connection errors of the external RS485 interface.
|
||||||
|
Check the RS485 interface connection with the environment according to schematic above and restart the application.
|
||||||
|
Then start terminal software and open the appropriate serial port.
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
idf_component_register(SRCS "rs485_example.c"
|
||||||
|
REQUIRES nvs_flash esp_driver_uart
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
menu "Echo RS485 Example Configuration"
|
||||||
|
|
||||||
|
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
|
||||||
|
|
||||||
|
config ECHO_UART_PORT_NUM
|
||||||
|
int "UART port number"
|
||||||
|
range 0 2 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3
|
||||||
|
default 2 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3
|
||||||
|
range 0 1
|
||||||
|
default 1
|
||||||
|
help
|
||||||
|
UART communication port number for the example.
|
||||||
|
See UART documentation for available port numbers.
|
||||||
|
|
||||||
|
config ECHO_UART_BAUD_RATE
|
||||||
|
int "UART communication speed"
|
||||||
|
range 1200 115200
|
||||||
|
default 115200
|
||||||
|
help
|
||||||
|
UART communication speed for Modbus example.
|
||||||
|
|
||||||
|
config ECHO_UART_RXD
|
||||||
|
int "UART RXD pin number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
|
||||||
|
default 27 if IDF_TARGET_ESP32P4
|
||||||
|
default 22 if IDF_TARGET_ESP32
|
||||||
|
default 8 if !IDF_TARGET_ESP32
|
||||||
|
help
|
||||||
|
GPIO number for UART RX pin. See UART documentation for more information
|
||||||
|
about available pin numbers for UART.
|
||||||
|
|
||||||
|
config ECHO_UART_TXD
|
||||||
|
int "UART TXD pin number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default 26 if IDF_TARGET_ESP32P4
|
||||||
|
default 23 if IDF_TARGET_ESP32
|
||||||
|
default 9 if !IDF_TARGET_ESP32
|
||||||
|
help
|
||||||
|
GPIO number for UART TX pin. See UART documentation for more information
|
||||||
|
about available pin numbers for UART.
|
||||||
|
|
||||||
|
# config ECHO_UART_RTS
|
||||||
|
# int "UART RTS pin number"
|
||||||
|
# range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
# default -1 if IDF_TARGET_ESP32P4
|
||||||
|
# default 18 if IDF_TARGET_ESP32
|
||||||
|
# default 10 if !IDF_TARGET_ESP32
|
||||||
|
# help
|
||||||
|
# GPIO number for UART RTS pin. This pin is connected to
|
||||||
|
# ~RE/DE pin of RS485 transceiver to switch direction.
|
||||||
|
# See UART documentation for more information about available pin
|
||||||
|
# numbers for UART.
|
||||||
|
|
||||||
|
config ECHO_TASK_STACK_SIZE
|
||||||
|
int "UART echo RS485 example task stack size"
|
||||||
|
range 1024 16384
|
||||||
|
default 3072
|
||||||
|
help
|
||||||
|
Defines stack size for UART echo RS485 example. Insufficient stack size can cause crash.
|
||||||
|
|
||||||
|
endmenu
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user