initial commit
This commit is contained in:
1
components/ExternLib/codec_board/.component_hash
Normal file
1
components/ExternLib/codec_board/.component_hash
Normal file
@@ -0,0 +1 @@
|
||||
f72797539ecd2b14776c1eec0c39b9b9a48e587c5155297e248dcffdb3bcaaac
|
||||
21
components/ExternLib/codec_board/CHANGELOG.md
Normal file
21
components/ExternLib/codec_board/CHANGELOG.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Changelog
|
||||
|
||||
## v0.5.5
|
||||
|
||||
- Add board support for `ESP32_S3_KORVO_2L`, `ESP32_S3_EchoEar`, `ATOMS3_ECHO_BASE`
|
||||
- Mount SDcard use high speed for better performance on esp32s3 and esp32p4
|
||||
|
||||
## v0.5.4
|
||||
|
||||
- Add PDM support
|
||||
- Add play and record support for `XD_AIOT_C3` and `ESP_SPOT`
|
||||
|
||||
## v0.5.3
|
||||
|
||||
- Add support for mount SDCard on esp32
|
||||
- Add support for I2C manually control use `init_i2c` and `deinit_i2c`
|
||||
- Fixed I2S driver failed to initialized if more than one
|
||||
|
||||
## v0.5.0
|
||||
|
||||
- Initial version of `codec_board`
|
||||
17
components/ExternLib/codec_board/CMakeLists.txt
Normal file
17
components/ExternLib/codec_board/CMakeLists.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
set(COMPONENT_PRIV_REQUIRE driver esp_codec_dev fatfs esp_lcd)
|
||||
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3")
|
||||
list(APPEND COMPONENT_PRIV_REQUIRE esp_driver_i2c)
|
||||
endif()
|
||||
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3")
|
||||
list(APPEND COMPONENT_PRIV_REQUIRE esp_driver_i2c)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "cfg_parse.c" "codec_board.c" "codec_init.c" "dummy_codec.c"
|
||||
"lcd_init.c" "drv/tca9554.c"
|
||||
INCLUDE_DIRS ./include
|
||||
PRIV_INCLUDE_DIRS drv
|
||||
REQUIRES "${COMPONENT_PRIV_REQUIRE}"
|
||||
EMBED_TXTFILES "board_cfg.txt")
|
||||
22
components/ExternLib/codec_board/LICENSE
Normal file
22
components/ExternLib/codec_board/LICENSE
Normal file
@@ -0,0 +1,22 @@
|
||||
Espressif Modified MIT License
|
||||
|
||||
Copyright (c) 2025 Espressif Systems (Shanghai) CO., LTD
|
||||
|
||||
Permission is hereby granted for use EXCLUSIVELY with Espressif Systems products.
|
||||
This includes the right to use, copy, modify, merge, publish, distribute, and sublicense
|
||||
the Software, subject to the following conditions:
|
||||
|
||||
1. This Software MUST BE USED IN CONJUNCTION WITH ESPRESSIF SYSTEMS PRODUCTS.
|
||||
2. The above copyright notice and this permission notice shall be included in all copies
|
||||
or substantial portions of the Software.
|
||||
3. Redistribution of the Software in source or binary form FOR USE WITH NON-ESPRESSIF PRODUCTS
|
||||
is strictly prohibited.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
|
||||
48
components/ExternLib/codec_board/README.md
Normal file
48
components/ExternLib/codec_board/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Codec_board
|
||||
|
||||
## Overview
|
||||
`codec_board` is a simple implementation of board support for a variety of ESP32 series boards.
|
||||
Its main purpose is to:
|
||||
- Gather board-specific peripheral settings from user.
|
||||
- Add drivers for the supported peripherals.
|
||||
- Provide high-level APIs to interact with the peripherals or abstract the peripherals into manageable handles.
|
||||
|
||||
## Supported Peripherals
|
||||
|
||||
- **Codec**
|
||||
Currently support `ES8311`, `ES8388`, `ES7210`, `ES7243`, user can add other codec also.
|
||||
After `init_codec` is called, codec is abstracted into `esp_codec_dev` handle.
|
||||
Then use can use `esp_codec_dev` API to do playback and record.
|
||||
Simple configuration of codec as:
|
||||
```
|
||||
out: {codec: ES8311, pa: 38, use_mclk: 0, pa_gain:6}
|
||||
in: {codec: ES7210, i2s_port: 1}
|
||||
```
|
||||
|
||||
- **SDcard**
|
||||
Users can configure SDCard related GPIO then use API `mount_sdcard` to mount sdcard to use it.
|
||||
SDcard configuration as:
|
||||
```
|
||||
sdcard: {clk: 43, cmd: 44, d0: 39, d1: 40, d2: 41, d3: 42}
|
||||
```
|
||||
|
||||
- **Camera**
|
||||
Currently support DVP and MIPI camera, use can get camera settings use API `get_camera_cfg`
|
||||
|
||||
- **LCD**
|
||||
Currently only support LCD on `S3_Korvo_V2` and `ESP32_P4_FUNCTION_EV`.
|
||||
User can get LCD handle through API `board_get_lcd_handle` after `board_lcd_init`
|
||||
|
||||
## Customized a New Board
|
||||
To reuse the pre-defined devices, follow these steps:
|
||||
1. Add a new section to the [board_cfg.txt](board_cfg.txt) file and modify corresponding GPIO settings as needed.
|
||||
2. Call `set_codec_board_type` with the name of the newly added section.
|
||||
For new devices, you will need to manually modify the code.
|
||||
|
||||
## Comparison with esp-bsp
|
||||
|
||||
[esp-bsp](https://github.com/espressif/esp-bsp) supports almost all devices carried on board while this module only support media related devices. It provides a quick verification option if reusing the same device with different GPIO settings. Users can easily replace this module with `esp-bsp` by making the following substitutions:
|
||||
|
||||
- `bsp_audio_codec_speaker_init`: Replaces `get_playback_handle`
|
||||
- `bsp_audio_codec_microphone_init`: Replaces `get_record_handle`
|
||||
- `bsp_display_new`: Replaces `board_get_lcd_handle`
|
||||
27
components/ExternLib/codec_board/board_cfg.txt
Normal file
27
components/ExternLib/codec_board/board_cfg.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
# support in, out, in_out type
|
||||
# support i2c_port, i2s_port settings
|
||||
# support pa_gain, i2c_addr setting
|
||||
|
||||
Board: C6_AMOLED_1_43
|
||||
i2c: {sda: 18, scl: 8}
|
||||
i2s: {bclk: 21, ws: 22, dout: 23, din: 20, mclk: 19}
|
||||
out: {codec: ES8311, pa: -1, use_mclk: 1, pa_gain:6}
|
||||
in: {codec: ES7210}
|
||||
|
||||
Board: S3_Korvo_V2
|
||||
i2c: {sda: 17, scl: 18}
|
||||
i2s: {mclk: 16, bclk: 9, ws: 45, din: 10, dout: 8}
|
||||
out: {codec: ES8311, pa: 48, pa_gain: 6, use_mclk: 1, pa_gain:6}
|
||||
in: {codec: ES7210}
|
||||
|
||||
Board: S3_LCD_3_49
|
||||
i2c: {sda: 47, scl: 48}
|
||||
i2s: {mclk: 7, bclk: 15, ws: 46, din: 6, dout: 45}
|
||||
out: {codec: ES8311, pa: -1, pa_gain: 6, use_mclk: 1, pa_gain:6}
|
||||
in: {codec: ES7210}
|
||||
|
||||
Board: S3_RLCD_4_2
|
||||
i2c: {sda: 13, scl: 14}
|
||||
i2s: {mclk: 16, bclk: 9, ws: 45, din: 10, dout: 8}
|
||||
out: {codec: ES8311, pa: 46, pa_gain: 6, use_mclk: 1, pa_gain:6}
|
||||
in: {codec: ES7210}
|
||||
596
components/ExternLib/codec_board/cfg_parse.c
Normal file
596
components/ExternLib/codec_board/cfg_parse.c
Normal file
@@ -0,0 +1,596 @@
|
||||
#include "codec_board.h"
|
||||
|
||||
#define BOARD_SECTION "Board:"
|
||||
|
||||
#define IN_STR(a, const_b, len) in_str(a, const_b, sizeof(const_b) - 1, len)
|
||||
|
||||
typedef struct _board_cfg_attr {
|
||||
const char *attr;
|
||||
const char *value;
|
||||
struct _board_cfg_attr *next;
|
||||
} board_cfg_attr_t;
|
||||
|
||||
typedef struct {
|
||||
const char *type;
|
||||
board_cfg_attr_t *attr;
|
||||
} board_cfg_line_t;
|
||||
|
||||
static board_section_t *codec_section;
|
||||
|
||||
extern const char board_cfg_start[] asm("_binary_board_cfg_txt_start");
|
||||
extern const char board_cfg_end[] asm("_binary_board_cfg_txt_end");
|
||||
|
||||
static bool is_word(char s)
|
||||
{
|
||||
return ((s >= 'A' && s <= 'Z') || (s >= 'a' && s <= 'z') || (s >= '0' && s <= '9') || s == '_' || s == '-' ||
|
||||
s == '.');
|
||||
}
|
||||
|
||||
bool str_same(const char *a, const char *b)
|
||||
{
|
||||
while (*b && *a == *b) {
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
if (*b == 0) {
|
||||
if (is_word(*a) == false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char *in_str(const char *s, const char *org, int org_len, int len)
|
||||
{
|
||||
while (len > org_len) {
|
||||
if (*s == *org && memcmp(s, org, org_len) == 0 && is_word(s[org_len]) == false) {
|
||||
return s;
|
||||
}
|
||||
s++;
|
||||
len--;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *get_section_data(const char *data, int size, const char *section_name)
|
||||
{
|
||||
const char *s = data;
|
||||
while (1) {
|
||||
int left = size - (s - data);
|
||||
s = IN_STR(s, BOARD_SECTION, left);
|
||||
if (s == NULL) {
|
||||
break;
|
||||
}
|
||||
s += sizeof(BOARD_SECTION) - 1;
|
||||
while (!is_word(*s)) {
|
||||
s++;
|
||||
}
|
||||
if (str_same(s, section_name)) {
|
||||
while (*(s++) != '\n')
|
||||
;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int str_len(const char *s)
|
||||
{
|
||||
int len = 0;
|
||||
while (is_word(*s)) {
|
||||
len++;
|
||||
s++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static void print_cfg_line(board_cfg_line_t *cfg_line)
|
||||
{
|
||||
printf("%.*s: {", str_len(cfg_line->type), cfg_line->type);
|
||||
board_cfg_attr_t *attr = cfg_line->attr;
|
||||
while (attr) {
|
||||
printf("%.*s: %.*s", str_len(attr->attr), attr->attr, str_len(attr->value), attr->value);
|
||||
if (attr->next) {
|
||||
printf(", ");
|
||||
}
|
||||
attr = attr->next;
|
||||
}
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
static void free_cfg_line(board_cfg_line_t *cfg_line)
|
||||
{
|
||||
if (cfg_line) {
|
||||
board_cfg_attr_t *attr = cfg_line->attr;
|
||||
while (attr) {
|
||||
board_cfg_attr_t *nxt = attr->next;
|
||||
free(attr);
|
||||
attr = nxt;
|
||||
}
|
||||
free(cfg_line);
|
||||
}
|
||||
}
|
||||
|
||||
static board_cfg_line_t *parse_section(const char *s, int size, int *consume)
|
||||
{
|
||||
board_cfg_line_t *cfg_line;
|
||||
const char *start = s;
|
||||
const char *end = s + size;
|
||||
const char *name = s;
|
||||
bool is_comment = false;
|
||||
while (size) {
|
||||
if (*name == '#') {
|
||||
is_comment = true;
|
||||
}
|
||||
if (is_comment) {
|
||||
if (*name == '\n') {
|
||||
is_comment = false;
|
||||
}
|
||||
} else {
|
||||
if (is_word(*name)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
size--;
|
||||
name++;
|
||||
}
|
||||
if (size == 0 || str_same(name, BOARD_SECTION)) {
|
||||
return NULL;
|
||||
}
|
||||
cfg_line = calloc(1, sizeof(board_cfg_line_t));
|
||||
if (cfg_line == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
board_cfg_attr_t *tail = NULL;
|
||||
cfg_line->type = name;
|
||||
s = name;
|
||||
while (*(s++) != '{')
|
||||
;
|
||||
int word_len = 0;
|
||||
const char *attr = NULL;
|
||||
|
||||
while (s < end) {
|
||||
if (*s == '}') {
|
||||
*consume = (s + 1 - start);
|
||||
break;
|
||||
}
|
||||
if (is_word(*s)) {
|
||||
if (word_len == 0) {
|
||||
if (attr == NULL) {
|
||||
attr = s;
|
||||
} else {
|
||||
board_cfg_attr_t *cfg_attr = calloc(1, sizeof(board_cfg_attr_t));
|
||||
if (cfg_attr) {
|
||||
cfg_attr->attr = attr;
|
||||
cfg_attr->value = s;
|
||||
if (tail == NULL) {
|
||||
cfg_line->attr = cfg_attr;
|
||||
} else {
|
||||
tail->next = cfg_attr;
|
||||
}
|
||||
tail = cfg_attr;
|
||||
}
|
||||
attr = NULL;
|
||||
}
|
||||
}
|
||||
word_len++;
|
||||
} else {
|
||||
word_len = 0;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
return cfg_line;
|
||||
}
|
||||
|
||||
static int fill_i2c_cfg(board_cfg_attr_t *attr)
|
||||
{
|
||||
if (codec_section->i2c_num >= MAX_I2C_NUM) {
|
||||
return -1;
|
||||
}
|
||||
codec_i2c_pin_t *i2c_cfg = &codec_section->i2c_pin[codec_section->i2c_num++];
|
||||
while (attr) {
|
||||
if (str_same(attr->attr, "sda")) {
|
||||
i2c_cfg->sda = atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "scl")) {
|
||||
i2c_cfg->scl = atoi(attr->value);
|
||||
}
|
||||
attr = attr->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fill_i2s_cfg(board_cfg_attr_t *attr)
|
||||
{
|
||||
if (codec_section->i2s_num >= MAX_I2C_NUM) {
|
||||
return -1;
|
||||
}
|
||||
codec_i2s_pin_t *i2s_cfg = &codec_section->i2s_pin[codec_section->i2s_num++];
|
||||
i2s_cfg->din = i2s_cfg->dout = -1;
|
||||
while (attr) {
|
||||
if (str_same(attr->attr, "bclk")) {
|
||||
i2s_cfg->bclk = atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "din")) {
|
||||
i2s_cfg->din = atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "dout")) {
|
||||
i2s_cfg->dout = atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "ws")) {
|
||||
i2s_cfg->ws = atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "mclk")) {
|
||||
i2s_cfg->mclk = atoi(attr->value);
|
||||
}
|
||||
attr = attr->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static extend_io_type_t lcd_get_io_type(const char* s)
|
||||
{
|
||||
if (str_same(s, "tca9554")) {
|
||||
return EXTENT_IO_TYPE_TCA9554;
|
||||
}
|
||||
return EXTENT_IO_TYPE_NONE;
|
||||
}
|
||||
|
||||
static lcd_controller_type_t lcd_get_controller(const char* s)
|
||||
{
|
||||
if (str_same(s, "st7789")) {
|
||||
return LCD_CONTROLLER_TYPE_ST7789;
|
||||
}
|
||||
return LCD_CONTROLLER_TYPE_NONE;
|
||||
}
|
||||
|
||||
static lcd_bus_type_t lcd_get_bus(const char* s)
|
||||
{
|
||||
if (str_same(s, "spi")) {
|
||||
return LCD_BUS_TYPE_SPI;
|
||||
}
|
||||
if (str_same(s, "rgb")) {
|
||||
return LCD_BUS_TYPE_RGB;
|
||||
}
|
||||
if (str_same(s, "i80")) {
|
||||
return LCD_BUS_TYPE_I80;
|
||||
}
|
||||
if (str_same(s, "mipi")) {
|
||||
return LCD_BUS_TYPE_MIPI;
|
||||
}
|
||||
return LCD_BUS_TYPE_NONE;
|
||||
}
|
||||
|
||||
static int16_t get_pin(const char* s)
|
||||
{
|
||||
char* ext = strstr(s, "ext");
|
||||
if (ext) {
|
||||
return 0x1000 + atoi(ext + 3);
|
||||
}
|
||||
return (int16_t)atoi(s);
|
||||
}
|
||||
|
||||
static int fill_lcd_cfg(board_cfg_attr_t *attr)
|
||||
{
|
||||
if (codec_section->lcd_num >= 1) {
|
||||
return -1;
|
||||
}
|
||||
lcd_cfg_t *lcd_cfg = &codec_section->lcd;
|
||||
lcd_cfg->reset_pin = lcd_cfg->ctrl_pin = -1;
|
||||
codec_section->lcd_num++;
|
||||
while (attr) {
|
||||
if (str_same(attr->attr, "bus")) {
|
||||
lcd_cfg->bus_type = lcd_get_bus(attr->value);
|
||||
if (lcd_cfg->bus_type == LCD_BUS_TYPE_SPI) {
|
||||
for (int i = 0; i < sizeof(lcd_cfg->spi_cfg.d)/sizeof(lcd_cfg->spi_cfg.d[0]); i++) {
|
||||
lcd_cfg->spi_cfg.d[i] = -1;
|
||||
}
|
||||
}
|
||||
} else if (str_same(attr->attr, "controller")) {
|
||||
lcd_cfg->controller = lcd_get_controller(attr->value);
|
||||
}
|
||||
else if (str_same(attr->attr, "extend_io")) {
|
||||
lcd_cfg->io_type = lcd_get_io_type(attr->value);
|
||||
} else if (str_same(attr->attr, "width")) {
|
||||
lcd_cfg->width = atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "height")) {
|
||||
lcd_cfg->height = atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "mirror_x")) {
|
||||
lcd_cfg->mirror_x = atoi(attr->value) ? 1: 0;
|
||||
} else if (str_same(attr->attr, "mirror_y")) {
|
||||
lcd_cfg->mirror_y = atoi(attr->value) ? 1: 0;
|
||||
} else if (str_same(attr->attr, "swap_xy")) {
|
||||
lcd_cfg->swap_xy = atoi(attr->value) ? 1: 0;
|
||||
} else if (str_same(attr->attr, "color_inv")) {
|
||||
lcd_cfg->color_inv = atoi(attr->value) ? 1: 0;
|
||||
} else if (str_same(attr->attr, "ctrl")) {
|
||||
lcd_cfg->ctrl_pin = get_pin(attr->value);
|
||||
} else if (str_same(attr->attr, "rst")) {
|
||||
lcd_cfg->reset_pin = get_pin(attr->value);
|
||||
}
|
||||
else if (lcd_cfg->bus_type == LCD_BUS_TYPE_SPI) {
|
||||
// Bus configuration
|
||||
if (str_same(attr->attr, "spi_bus")) {
|
||||
lcd_cfg->spi_cfg.spi_bus = atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "cs")) {
|
||||
lcd_cfg->spi_cfg.cs = get_pin(attr->value);
|
||||
} else if (str_same(attr->attr, "dc")) {
|
||||
lcd_cfg->spi_cfg.dc = get_pin(attr->value);
|
||||
} else if (str_same(attr->attr, "clk")) {
|
||||
lcd_cfg->spi_cfg.clk = get_pin(attr->value);
|
||||
} else if (str_same(attr->attr, "mosi")) {
|
||||
lcd_cfg->spi_cfg.mosi = get_pin(attr->value);
|
||||
} else if (str_same(attr->attr, "cmd_bits")) {
|
||||
lcd_cfg->spi_cfg.cmd_bits = (uint8_t)atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "param_bits")) {
|
||||
lcd_cfg->spi_cfg.param_bits = (uint8_t)atoi(attr->value);
|
||||
}
|
||||
} else if (lcd_cfg->bus_type == LCD_BUS_TYPE_MIPI) {
|
||||
if (str_same(attr->attr, "ldo_chan")) {
|
||||
lcd_cfg->mipi_cfg.ldo_chan = (uint8_t)atoi(attr->value);
|
||||
}
|
||||
else if (str_same(attr->attr, "ldo_voltage")) {
|
||||
lcd_cfg->mipi_cfg.ldo_voltage = (uint16_t)atoi(attr->value);
|
||||
}
|
||||
else if (str_same(attr->attr, "lane_num")) {
|
||||
lcd_cfg->mipi_cfg.lane_num = (uint8_t)atoi(attr->value);
|
||||
}
|
||||
else if (str_same(attr->attr, "fb_num")) {
|
||||
lcd_cfg->mipi_cfg.fb_num = (uint8_t)atoi(attr->value);
|
||||
}
|
||||
else if (str_same(attr->attr, "bit_depth")) {
|
||||
lcd_cfg->mipi_cfg.bit_depth = (uint16_t)atoi(attr->value);
|
||||
}
|
||||
else if (str_same(attr->attr, "lane_bitrate")) {
|
||||
lcd_cfg->mipi_cfg.lane_bitrate = (uint32_t)atoi(attr->value);
|
||||
}
|
||||
else if (str_same(attr->attr, "dpi_clk")) {
|
||||
lcd_cfg->mipi_cfg.dpi_clk = (uint32_t)atoi(attr->value);
|
||||
}
|
||||
else if (str_same(attr->attr, "dsi_hsync")) {
|
||||
lcd_cfg->mipi_cfg.dsi_hsync = (uint8_t)atoi(attr->value);
|
||||
}
|
||||
else if (str_same(attr->attr, "dsi_vsync")) {
|
||||
lcd_cfg->mipi_cfg.dsi_vsync = (uint8_t)atoi(attr->value);
|
||||
}
|
||||
else if (str_same(attr->attr, "dsi_hbp")) {
|
||||
lcd_cfg->mipi_cfg.dsi_hbp = (uint8_t)atoi(attr->value);
|
||||
}
|
||||
else if (str_same(attr->attr, "dsi_hfp")) {
|
||||
lcd_cfg->mipi_cfg.dsi_hfp = (uint8_t)atoi(attr->value);
|
||||
}
|
||||
else if (str_same(attr->attr, "dsi_vbp")) {
|
||||
lcd_cfg->mipi_cfg.dsi_vbp = (uint8_t)atoi(attr->value);
|
||||
}
|
||||
else if (str_same(attr->attr, "dsi_vfp")) {
|
||||
lcd_cfg->mipi_cfg.dsi_vfp = (uint8_t)atoi(attr->value);
|
||||
}
|
||||
}
|
||||
attr = attr->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO add other codec
|
||||
static codec_type_t get_codec_type(const char *type)
|
||||
{
|
||||
if (str_same(type, "ES8311")) {
|
||||
return CODEC_TYPE_ES8311;
|
||||
}
|
||||
if (str_same(type, "ES8388")) {
|
||||
return CODEC_TYPE_ES8388;
|
||||
}
|
||||
if (str_same(type, "ES7243")) {
|
||||
return CODEC_TYPE_ES7243;
|
||||
}
|
||||
if (str_same(type, "ES7210")) {
|
||||
return CODEC_TYPE_ES7210;
|
||||
}
|
||||
if (str_same(type, "DUMMY")) {
|
||||
return CODEC_TYPE_DUMMY;
|
||||
}
|
||||
return CODEC_TYPE_NONE;
|
||||
}
|
||||
|
||||
static int fill_codec_cfg(board_cfg_attr_t *attr, uint8_t codec_dir)
|
||||
{
|
||||
if (codec_section->codec_num >= MAX_CODEC_NUM) {
|
||||
return -1;
|
||||
}
|
||||
codec_section->codec[codec_section->codec_num].codec_dir = codec_dir;
|
||||
codec_cfg_t *codec_cfg = &codec_section->codec[codec_section->codec_num].codec_cfg;
|
||||
while (attr) {
|
||||
if (str_same(attr->attr, "i2c_port")) {
|
||||
codec_cfg->i2c_port = atoi(attr->value);
|
||||
if (codec_cfg->i2c_port >= codec_section->i2c_num) {
|
||||
return -1;
|
||||
}
|
||||
} else if (str_same(attr->attr, "i2s_port")) {
|
||||
codec_cfg->i2s_port = atoi(attr->value);
|
||||
if (codec_cfg->i2s_port >= codec_section->i2s_num) {
|
||||
return -1;
|
||||
}
|
||||
} else if (str_same(attr->attr, "pa")) {
|
||||
codec_cfg->pa_pin = atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "pa_gain")) {
|
||||
codec_cfg->pa_gain = atof(attr->value);
|
||||
} else if (str_same(attr->attr, "use_mclk")) {
|
||||
codec_cfg->use_mclk = (bool) atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "codec")) {
|
||||
codec_cfg->codec_type = get_codec_type(attr->value);
|
||||
if (codec_cfg->codec_type == CODEC_TYPE_NONE) {
|
||||
return -1;
|
||||
}
|
||||
} else if (str_same(attr->attr, "i2c_addr")) {
|
||||
codec_cfg->i2c_addr = (uint8_t) atoi(attr->value);
|
||||
}
|
||||
attr = attr->next;
|
||||
}
|
||||
printf("Codec %d dir %d type:%d\n", codec_section->codec_num, codec_dir, codec_cfg->codec_type);
|
||||
codec_section->codec_num++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fill_sdcard_cfg(board_cfg_attr_t *attr)
|
||||
{
|
||||
if (codec_section->sdcard_num >= 1) {
|
||||
return -1;
|
||||
}
|
||||
sdcard_cfg_t *sdcard_cfg = &codec_section->sdcard;
|
||||
sdcard_cfg->power = -1;
|
||||
while (attr) {
|
||||
if (str_same(attr->attr, "clk")) {
|
||||
sdcard_cfg->clk = (int16_t) atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "cmd")) {
|
||||
sdcard_cfg->cmd = (int16_t) atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "d0")) {
|
||||
sdcard_cfg->d0 = (int16_t) atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "d1")) {
|
||||
sdcard_cfg->d1 = (int16_t) atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "d2")) {
|
||||
sdcard_cfg->d2 = (int16_t) atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "d3")) {
|
||||
sdcard_cfg->d3 = (int16_t) atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "power")) {
|
||||
sdcard_cfg->power = (int16_t) atoi(attr->value);
|
||||
}
|
||||
attr = attr->next;
|
||||
}
|
||||
printf("Sdcard clk:%d cmd:%d d0:%d\n", sdcard_cfg->clk, sdcard_cfg->cmd, sdcard_cfg->d0);
|
||||
codec_section->sdcard_num++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static camera_type_t get_camera_type(const char* s) {
|
||||
if (str_same(s, "dvp")) {
|
||||
return CAMERA_TYPE_DVP;
|
||||
}
|
||||
if (str_same(s, "usb")) {
|
||||
return CAMERA_TYPE_USB;
|
||||
}
|
||||
if (str_same(s, "mipi")) {
|
||||
return CAMERA_TYPE_MIPI;
|
||||
}
|
||||
return CAMERA_TYPE_NONE;
|
||||
}
|
||||
|
||||
static int fill_camera_cfg(board_cfg_attr_t *attr)
|
||||
{
|
||||
if (codec_section->camera_num >= 1) {
|
||||
return -1;
|
||||
}
|
||||
camera_cfg_t *camera_cfg = &codec_section->camera;
|
||||
for (int i = 0; i < sizeof(camera_cfg->data)/sizeof(camera_cfg->data[0]); i++) {
|
||||
camera_cfg->data[i] = -1;
|
||||
}
|
||||
camera_cfg->pwr = -1;
|
||||
camera_cfg->reset = -1;
|
||||
while (attr) {
|
||||
if (str_same(attr->attr, "type")) {
|
||||
camera_cfg->type = get_camera_type(attr->value);
|
||||
}
|
||||
if (str_same(attr->attr, "xclk")) {
|
||||
camera_cfg->xclk = (int16_t) atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "pclk")) {
|
||||
camera_cfg->pclk = (int16_t) atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "pwr")) {
|
||||
camera_cfg->pwr = (int16_t) atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "reset")) {
|
||||
camera_cfg->reset = (int16_t) atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "vsync")) {
|
||||
camera_cfg->vsync = (int16_t) atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "href")) {
|
||||
camera_cfg->href = (int16_t) atoi(attr->value);
|
||||
} else if (str_same(attr->attr, "de")) {
|
||||
camera_cfg->de = (int16_t) atoi(attr->value);
|
||||
} else if (attr->attr[0] == 'd' && attr->attr[1] >= '0' && attr->attr[1] <= '9') {
|
||||
int n = atoi(attr->attr + 1);
|
||||
if (n < sizeof(camera_cfg->data)/sizeof(camera_cfg->data[0])) {
|
||||
camera_cfg->data[n] = (int16_t) atoi(attr->value);
|
||||
}
|
||||
}
|
||||
attr = attr->next;
|
||||
}
|
||||
codec_section->camera_num++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fill_cfg(board_cfg_line_t *cfg_line)
|
||||
{
|
||||
board_cfg_attr_t *attr = cfg_line->attr;
|
||||
if (str_same(cfg_line->type, "i2c")) {
|
||||
if (fill_i2c_cfg(attr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if (str_same(cfg_line->type, "i2s")) {
|
||||
if (fill_i2s_cfg(attr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if (str_same(cfg_line->type, "sdcard")) {
|
||||
if (fill_sdcard_cfg(attr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if (str_same(cfg_line->type, "in_out")) {
|
||||
if (fill_codec_cfg(attr, CODEC_DIR_IN_OUT) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if (str_same(cfg_line->type, "out")) {
|
||||
if (fill_codec_cfg(attr, CODEC_DIR_OUT) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if (str_same(cfg_line->type, "in")) {
|
||||
if (fill_codec_cfg(attr, CODEC_DIR_IN) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if (str_same(cfg_line->type, "lcd")) {
|
||||
if (fill_lcd_cfg(attr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else if (str_same(cfg_line->type, "camera")) {
|
||||
if (fill_camera_cfg(attr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_cfg(const char *section, int size)
|
||||
{
|
||||
int consume = 0;
|
||||
while (1) {
|
||||
board_cfg_line_t *cfg_line = parse_section(section, size, &consume);
|
||||
if (cfg_line == NULL) {
|
||||
break;
|
||||
}
|
||||
size -= consume;
|
||||
section += consume;
|
||||
print_cfg_line(cfg_line);
|
||||
int ret = fill_cfg(cfg_line);
|
||||
free_cfg_line(cfg_line);
|
||||
if (ret != 0) {
|
||||
printf("Fail to parse cfg line\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
board_section_t *get_codec_section(const char *codec_type)
|
||||
{
|
||||
if (codec_section) {
|
||||
free(codec_section);
|
||||
}
|
||||
codec_section = calloc(1, sizeof(board_section_t));
|
||||
if (codec_type == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
int cfg_size = board_cfg_end - board_cfg_start;
|
||||
do {
|
||||
const char *section = get_section_data(board_cfg_start, cfg_size, codec_type);
|
||||
if (section == NULL) {
|
||||
break;
|
||||
}
|
||||
int left_size = cfg_size - (section - board_cfg_start);
|
||||
if (parse_cfg(section, left_size) != 0) {
|
||||
break;
|
||||
}
|
||||
return codec_section;
|
||||
} while (0);
|
||||
free(codec_section);
|
||||
return NULL;
|
||||
}
|
||||
101
components/ExternLib/codec_board/codec_board.c
Normal file
101
components/ExternLib/codec_board/codec_board.c
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "codec_board.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define TAG "BOARD"
|
||||
|
||||
board_section_t *get_codec_section(const char *codec_type);
|
||||
|
||||
static board_section_t *codec;
|
||||
|
||||
#define RET_ON_NOT_INIT() if (codec == NULL) { \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
void set_codec_board_type(const char *codec_type)
|
||||
{
|
||||
if (codec) {
|
||||
return;
|
||||
}
|
||||
codec = get_codec_section(codec_type);
|
||||
}
|
||||
|
||||
int get_sdcard_config(sdcard_cfg_t *card_cfg)
|
||||
{
|
||||
RET_ON_NOT_INIT();
|
||||
if (codec->sdcard_num == 0) {
|
||||
ESP_LOGE(TAG, "Sdcard not exits on board");
|
||||
return -1;
|
||||
}
|
||||
memcpy(card_cfg, &codec->sdcard, sizeof(sdcard_cfg_t));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_i2c_pin(uint8_t port, codec_i2c_pin_t *i2c_pin)
|
||||
{
|
||||
RET_ON_NOT_INIT();
|
||||
if (port > codec->i2c_num) {
|
||||
ESP_LOGE(TAG, "I2C %d not exits on board", port);
|
||||
return -1;
|
||||
}
|
||||
memcpy(i2c_pin, &codec->i2c_pin[port], sizeof(codec_i2c_pin_t));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_i2s_pin(uint8_t port, codec_i2s_pin_t *i2s_pin)
|
||||
{
|
||||
RET_ON_NOT_INIT();
|
||||
if (port > codec->i2s_num) {
|
||||
ESP_LOGE(TAG, "I2S %d not exits on board", port);
|
||||
return -1;
|
||||
}
|
||||
memcpy(i2s_pin, &codec->i2s_pin[port], sizeof(codec_i2s_pin_t));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_out_codec_cfg(codec_cfg_t *out_cfg)
|
||||
{
|
||||
RET_ON_NOT_INIT();
|
||||
for (int i = 0; i < codec->codec_num; i++) {
|
||||
if (codec->codec[i].codec_dir & CODEC_DIR_OUT) {
|
||||
memcpy(out_cfg, &codec->codec[i].codec_cfg, sizeof(codec_cfg_t));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ESP_LOGE(TAG, "Output codec not exits on board");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int get_in_codec_cfg(codec_cfg_t *in_cfg)
|
||||
{
|
||||
RET_ON_NOT_INIT();
|
||||
for (int i = 0; i < codec->codec_num; i++) {
|
||||
if (codec->codec[i].codec_dir & CODEC_DIR_IN) {
|
||||
memcpy(in_cfg, &codec->codec[i].codec_cfg, sizeof(codec_cfg_t));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ESP_LOGE(TAG, "Input codec not exits on board");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int get_camera_cfg(camera_cfg_t *cam_cfg)
|
||||
{
|
||||
RET_ON_NOT_INIT();
|
||||
if (codec->camera_num == 0) {
|
||||
ESP_LOGE(TAG, "Camera not exits on board");
|
||||
return -1;
|
||||
}
|
||||
memcpy(cam_cfg, &codec->camera, sizeof(camera_cfg_t));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_lcd_cfg(lcd_cfg_t *lcd_cfg)
|
||||
{
|
||||
RET_ON_NOT_INIT();
|
||||
if (codec->lcd_num) {
|
||||
memcpy(lcd_cfg, &codec->lcd, sizeof(lcd_cfg_t));
|
||||
return 0;
|
||||
}
|
||||
ESP_LOGE(TAG, "LCD not exits on board");
|
||||
return -1;
|
||||
}
|
||||
710
components/ExternLib/codec_board/codec_init.c
Normal file
710
components/ExternLib/codec_board/codec_init.c
Normal file
@@ -0,0 +1,710 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include "driver/i2s_std.h"
|
||||
#include "driver/i2s_tdm.h"
|
||||
#include "driver/i2s_pdm.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#else
|
||||
#include "driver/i2s.h"
|
||||
#endif
|
||||
#include "esp_codec_dev.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
#include "esp_codec_dev_os.h"
|
||||
#include "codec_board.h"
|
||||
#include "codec_init.h"
|
||||
#include "esp_log.h"
|
||||
#include "dummy_codec.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "driver/sdmmc_defs.h"
|
||||
|
||||
#define TAG "CODEC_INIT"
|
||||
|
||||
typedef struct {
|
||||
bool inited;
|
||||
const audio_codec_data_if_t *data_if;
|
||||
const audio_codec_data_if_t *data_in_if;
|
||||
const audio_codec_gpio_if_t *gpio_if;
|
||||
const audio_codec_ctrl_if_t *in_ctrl_if;
|
||||
const audio_codec_ctrl_if_t *out_ctrl_if;
|
||||
const audio_codec_if_t *out_codec_if;
|
||||
const audio_codec_if_t *in_codec_if;
|
||||
esp_codec_dev_handle_t play_dev;
|
||||
esp_codec_dev_handle_t record_dev;
|
||||
} codec_res_t;
|
||||
|
||||
#define USE_I2C_MASTER
|
||||
typedef struct {
|
||||
bool inited;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
#endif
|
||||
} i2s_keep_t;
|
||||
|
||||
static bool i2c_inited[MAX_I2C_NUM];
|
||||
static i2c_master_bus_handle_t i2c_bus_handle[MAX_I2C_NUM];
|
||||
static i2s_keep_t *i2s_keep[MAX_I2S_NUM];
|
||||
static codec_res_t codec_res;
|
||||
|
||||
static int _i2c_init(uint8_t port)
|
||||
{
|
||||
if (port >= MAX_I2C_NUM) {
|
||||
return -1;
|
||||
}
|
||||
if (i2c_inited[port]) {
|
||||
return 0;
|
||||
}
|
||||
codec_i2c_pin_t i2c_pin;
|
||||
if (get_i2c_pin(port, &i2c_pin)) {
|
||||
ESP_LOGE(TAG, "Fail to get i2c pin");
|
||||
return -1;
|
||||
}
|
||||
// workaround to check i2c initialized already
|
||||
int ret = 0;
|
||||
#ifdef USE_I2C_MASTER
|
||||
i2c_master_bus_config_t i2c_bus_config = { 0 };
|
||||
i2c_bus_config.clk_source = I2C_CLK_SRC_DEFAULT;
|
||||
i2c_bus_config.i2c_port = port;
|
||||
i2c_bus_config.scl_io_num = i2c_pin.scl;
|
||||
i2c_bus_config.sda_io_num = i2c_pin.sda;
|
||||
i2c_bus_config.glitch_ignore_cnt = 7;
|
||||
i2c_bus_config.flags.enable_internal_pullup = true;
|
||||
ret = i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle[port]);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to initialize I2C master bus port %d", port);
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGI(TAG, "Set mater handle %d %p", port, i2c_bus_handle[port]);
|
||||
#else
|
||||
i2c_config_t i2c_cfg = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = 100000,
|
||||
};
|
||||
i2c_cfg.sda_io_num = i2c_pin.sda;
|
||||
i2c_cfg.scl_io_num = i2c_pin.scl;
|
||||
ret = i2c_param_config(port, &i2c_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return -1;
|
||||
}
|
||||
ret = i2c_driver_install(port, i2c_cfg.mode, 0, 0, 0);
|
||||
#endif
|
||||
if (ret == 0) {
|
||||
i2c_inited[port] = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *get_i2c_bus_handle(uint8_t port)
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
|
||||
// Try to get port from I2C driver directly
|
||||
i2c_master_bus_handle_t bus_handle = NULL;
|
||||
i2c_master_get_bus_handle(port, &bus_handle);
|
||||
return bus_handle;
|
||||
#endif
|
||||
return i2c_bus_handle[port];
|
||||
}
|
||||
|
||||
static int _i2c_deinit(uint8_t port)
|
||||
{
|
||||
if (port >= MAX_I2C_NUM || i2c_inited[port] == false) {
|
||||
return -1;
|
||||
}
|
||||
#ifdef USE_I2C_MASTER
|
||||
i2c_del_master_bus(i2c_bus_handle[port]);
|
||||
#else
|
||||
i2c_driver_delete(port);
|
||||
#endif
|
||||
i2c_bus_handle[port] = NULL;
|
||||
i2c_inited[port] = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _i2s_init(uint8_t port, esp_codec_dev_type_t dev_type, codec_init_cfg_t *init_cfg)
|
||||
{
|
||||
if (port >= MAX_I2S_NUM) {
|
||||
return -1;
|
||||
}
|
||||
if (i2s_keep[port]) {
|
||||
return 0;
|
||||
}
|
||||
codec_i2s_pin_t i2s_cfg;
|
||||
if (get_i2s_pin(port, &i2s_cfg)) {
|
||||
ESP_LOGE(TAG, "Fail to get i2s pin");
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGI(TAG, "Init i2s %d type: %d mclk:%d bclk:%d ws:%d din:%d dout:%d",
|
||||
port, dev_type, i2s_cfg.mclk,
|
||||
i2s_cfg.bclk, i2s_cfg.ws, i2s_cfg.din, i2s_cfg.dout);
|
||||
i2s_keep[port] = (i2s_keep_t *)calloc(1, sizeof(i2s_keep_t));
|
||||
if (i2s_keep[port] == NULL) {
|
||||
return -1;
|
||||
}
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
chan_cfg.auto_clear = true;
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(32, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = i2s_cfg.mclk,
|
||||
.bclk = i2s_cfg.bclk,
|
||||
.ws = i2s_cfg.ws,
|
||||
.dout = i2s_cfg.dout,
|
||||
.din = i2s_cfg.din,
|
||||
},
|
||||
};
|
||||
bool output = (dev_type & ESP_CODEC_DEV_TYPE_OUT) > 0;
|
||||
bool input = (dev_type & ESP_CODEC_DEV_TYPE_IN) > 0;
|
||||
// Same I2S have tx and rx but use rx or tx only
|
||||
if (output && init_cfg->out_mode == CODEC_I2S_MODE_NONE) {
|
||||
output = false;
|
||||
}
|
||||
if (input && init_cfg->in_mode == CODEC_I2S_MODE_NONE) {
|
||||
input = false;
|
||||
}
|
||||
if (input == false && output == false) {
|
||||
return 0;
|
||||
}
|
||||
#ifdef SOC_I2S_SUPPORTS_TDM
|
||||
i2s_tdm_slot_mask_t slot_mask = I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3;
|
||||
i2s_tdm_config_t tdm_cfg = {
|
||||
.slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(32, I2S_SLOT_MODE_STEREO, slot_mask),
|
||||
.clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(16000),
|
||||
.gpio_cfg = {
|
||||
.mclk = i2s_cfg.mclk,
|
||||
.bclk = i2s_cfg.bclk,
|
||||
.ws = i2s_cfg.ws,
|
||||
.dout = i2s_cfg.dout,
|
||||
.din = i2s_cfg.din,
|
||||
},
|
||||
};
|
||||
tdm_cfg.slot_cfg.total_slot = 4;
|
||||
#endif
|
||||
chan_cfg.id = I2S_NUM_AUTO; // Use auto ID
|
||||
int ret = i2s_new_channel(&chan_cfg, output == false ? NULL : &i2s_keep[port]->tx_handle,
|
||||
input == false ? NULL : &i2s_keep[port]->rx_handle);
|
||||
ESP_LOGI(TAG, "tx:%p rx:%p", i2s_keep[port]->tx_handle, i2s_keep[port]->rx_handle);
|
||||
if (i2s_keep[port]->tx_handle) {
|
||||
if (init_cfg->out_mode == CODEC_I2S_MODE_STD) {
|
||||
ret = i2s_channel_init_std_mode(i2s_keep[port]->tx_handle, &std_cfg);
|
||||
ESP_LOGI(TAG, "output init std ret %d", ret);
|
||||
}
|
||||
#ifdef SOC_I2S_SUPPORTS_TDM
|
||||
else if (init_cfg->out_mode == CODEC_I2S_MODE_TDM) {
|
||||
ret = i2s_channel_init_tdm_mode(i2s_keep[port]->tx_handle, &tdm_cfg);
|
||||
ESP_LOGI(TAG, "output init tdm ret %d", ret);
|
||||
}
|
||||
#endif
|
||||
#ifdef SOC_I2S_SUPPORTS_PDM_TX
|
||||
else if (init_cfg->out_mode == CODEC_I2S_MODE_PDM) {
|
||||
i2s_pdm_tx_config_t pdm_cfg = {
|
||||
.clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(16000),
|
||||
.slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(16, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.dout = i2s_cfg.dout,
|
||||
.clk = i2s_cfg.bclk,
|
||||
.invert_flags = {
|
||||
.clk_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
ret = i2s_channel_init_pdm_tx_mode(i2s_keep[port]->tx_handle, &pdm_cfg);
|
||||
ESP_LOGI(TAG, "output init pdm ret %d", ret);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (i2s_keep[port]->rx_handle) {
|
||||
if (init_cfg->in_mode == CODEC_I2S_MODE_STD) {
|
||||
ret = i2s_channel_init_std_mode(i2s_keep[port]->rx_handle, &std_cfg);
|
||||
ESP_LOGI(TAG, "Input init std ret %d", ret);
|
||||
}
|
||||
#ifdef SOC_I2S_SUPPORTS_TDM
|
||||
else if (init_cfg->in_mode == CODEC_I2S_MODE_TDM) {
|
||||
ret = i2s_channel_init_tdm_mode(i2s_keep[port]->rx_handle, &tdm_cfg);
|
||||
ESP_LOGI(TAG, "Input init tdm ret %d", ret);
|
||||
}
|
||||
#endif
|
||||
#ifdef SOC_I2S_SUPPORTS_PDM_RX
|
||||
else if (init_cfg->in_mode == CODEC_I2S_MODE_PDM) {
|
||||
i2s_pdm_rx_config_t pdm_cfg = {
|
||||
.clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(16000),
|
||||
.slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(16, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.din = i2s_cfg.din,
|
||||
.clk = i2s_cfg.bclk,
|
||||
.invert_flags = {
|
||||
.clk_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
ret = i2s_channel_init_pdm_rx_mode(i2s_keep[port]->rx_handle, &pdm_cfg);
|
||||
ESP_LOGI(TAG, "Input init pdm ret %d", ret);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// Enable I2S here for maybe some codec need I2S clock to set register correctly
|
||||
if (i2s_keep[port]->tx_handle) {
|
||||
i2s_channel_enable(i2s_keep[port]->tx_handle);
|
||||
}
|
||||
if (i2s_keep[port]->rx_handle) {
|
||||
i2s_channel_enable(i2s_keep[port]->rx_handle);
|
||||
}
|
||||
#else
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX),
|
||||
.sample_rate = 44100,
|
||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_IRAM,
|
||||
.dma_buf_count = 3,
|
||||
.dma_buf_len = 300,
|
||||
.use_apll = true,
|
||||
.tx_desc_auto_clear = true,
|
||||
.fixed_mclk = 0
|
||||
//.mclk_multiple = 256,
|
||||
};
|
||||
int ret = i2s_driver_install(port, &i2s_config, 0, NULL);
|
||||
i2s_pin_config_t i2s_pin_cfg = {
|
||||
.mck_io_num = i2s_cfg.mclk,
|
||||
.bck_io_num = i2s_cfg.bclk,
|
||||
.ws_io_num = i2s_cfg.ws,
|
||||
.data_out_num = i2s_cfg.dout,
|
||||
.data_in_num = i2s_cfg.din,
|
||||
};
|
||||
i2s_set_pin(port, &i2s_pin_cfg);
|
||||
#endif
|
||||
ESP_LOGI(TAG, "Init i2s %d ok", port);
|
||||
i2s_keep[port]->inited = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _i2s_deinit(uint8_t port)
|
||||
{
|
||||
if (port >= MAX_I2S_NUM) {
|
||||
return -1;
|
||||
}
|
||||
if (i2s_keep[port] == NULL) {
|
||||
return 0;
|
||||
}
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
if (i2s_keep[port]->tx_handle) {
|
||||
i2s_channel_disable(i2s_keep[port]->tx_handle);
|
||||
}
|
||||
if (i2s_keep[port]->rx_handle) {
|
||||
i2s_channel_disable(i2s_keep[port]->rx_handle);
|
||||
}
|
||||
if (i2s_keep[port]->tx_handle) {
|
||||
i2s_del_channel(i2s_keep[port]->tx_handle);
|
||||
}
|
||||
if (i2s_keep[port]->rx_handle) {
|
||||
i2s_del_channel(i2s_keep[port]->rx_handle);
|
||||
}
|
||||
#else
|
||||
i2s_driver_uninstall(port);
|
||||
#endif
|
||||
free(i2s_keep[port]);
|
||||
i2s_keep[port] = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_i2c(uint8_t port)
|
||||
{
|
||||
return _i2c_init(port);
|
||||
}
|
||||
|
||||
int deinit_i2c(uint8_t port)
|
||||
{
|
||||
return _i2c_deinit(port);
|
||||
}
|
||||
|
||||
static int check_i2c_inited(int8_t port)
|
||||
{
|
||||
if (port < 0) {
|
||||
return 0;
|
||||
}
|
||||
// Already installed
|
||||
if (get_i2c_bus_handle(port)) {
|
||||
return 0;
|
||||
}
|
||||
return _i2c_init(port);
|
||||
}
|
||||
|
||||
int init_codec(codec_init_cfg_t *cfg)
|
||||
{
|
||||
if (cfg == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (codec_res.inited) {
|
||||
ESP_LOGI(TAG, "Already initialized");
|
||||
return 0;
|
||||
}
|
||||
codec_cfg_t out_cfg = { 0 };
|
||||
codec_cfg_t in_cfg = { 0 };
|
||||
bool has_out = false;
|
||||
bool has_in = false;
|
||||
// Get codec configuration
|
||||
if (get_out_codec_cfg(&out_cfg) == 0) {
|
||||
has_out = true;
|
||||
}
|
||||
if (get_in_codec_cfg(&in_cfg) == 0) {
|
||||
has_in = true;
|
||||
}
|
||||
if (has_out == false && has_in == false) {
|
||||
ESP_LOGE(TAG, "No codec device found");
|
||||
return -1;
|
||||
}
|
||||
// Try to get I2C handle
|
||||
check_i2c_inited(0);
|
||||
// Init i2c and i2s
|
||||
bool same_i2s = (has_in && has_out && out_cfg.i2s_port == in_cfg.i2s_port);
|
||||
ESP_LOGI(TAG, "in:%d out:%d port: %d", has_in, has_out, out_cfg.i2s_port == in_cfg.i2s_port);
|
||||
if (has_out) {
|
||||
if (check_i2c_inited(out_cfg.i2c_port) < 0) {
|
||||
ESP_LOGE(TAG, "Fail to int i2c: %d", out_cfg.i2c_port);
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGI(TAG, "Success to int i2c: %d", in_cfg.i2c_port);
|
||||
if (_i2s_init(out_cfg.i2s_port, same_i2s ? ESP_CODEC_DEV_TYPE_IN_OUT : ESP_CODEC_DEV_TYPE_OUT, cfg)) {
|
||||
ESP_LOGE(TAG, "Fail to init i2s: %d", out_cfg.i2s_port);
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGI(TAG, "Success to init i2s: %d", in_cfg.i2s_port);
|
||||
}
|
||||
if (has_in) {
|
||||
if (check_i2c_inited(in_cfg.i2c_port) < 0) {
|
||||
ESP_LOGE(TAG, "Fail to int i2c: %d", in_cfg.i2c_port);
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGI(TAG, "Success to int i2c: %d", in_cfg.i2c_port);
|
||||
if (_i2s_init(in_cfg.i2s_port, same_i2s ? ESP_CODEC_DEV_TYPE_IN_OUT : ESP_CODEC_DEV_TYPE_IN, cfg)) {
|
||||
ESP_LOGE(TAG, "Fail to init i2s: %d", in_cfg.i2s_port);
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGI(TAG, "Success to init i2s: %d", in_cfg.i2s_port);
|
||||
}
|
||||
// Create gpio interface
|
||||
codec_res.gpio_if = audio_codec_new_gpio();
|
||||
|
||||
bool same_codec = same_i2s && (in_cfg.codec_type == out_cfg.codec_type);
|
||||
if (has_out) {
|
||||
audio_codec_i2s_cfg_t i2s_out_cfg = {
|
||||
.port = out_cfg.i2s_port,
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
.tx_handle = i2s_keep[out_cfg.i2s_port]->tx_handle,
|
||||
.rx_handle = i2s_keep[out_cfg.i2s_port]->rx_handle,
|
||||
#endif
|
||||
};
|
||||
ESP_LOGI(TAG, "Get out handle %p port %d", i2s_out_cfg.tx_handle, out_cfg.i2s_port);
|
||||
codec_res.data_if = audio_codec_new_i2s_data(&i2s_out_cfg);
|
||||
|
||||
audio_codec_i2c_cfg_t i2c_cfg = {
|
||||
.port = out_cfg.i2c_port,
|
||||
#ifdef USE_I2C_MASTER
|
||||
.bus_handle = get_i2c_bus_handle(out_cfg.i2c_port),
|
||||
#endif
|
||||
};
|
||||
// TODO add other codec support
|
||||
switch (out_cfg.codec_type) {
|
||||
case CODEC_TYPE_ES8311: {
|
||||
i2c_cfg.addr = out_cfg.i2c_addr ? out_cfg.i2c_addr : ES8311_CODEC_DEFAULT_ADDR;
|
||||
codec_res.out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
es8311_codec_cfg_t es8311_cfg = {
|
||||
.codec_mode = same_codec ? ESP_CODEC_DEV_WORK_MODE_BOTH : ESP_CODEC_DEV_WORK_MODE_DAC,
|
||||
.ctrl_if = codec_res.out_ctrl_if,
|
||||
.gpio_if = codec_res.gpio_if,
|
||||
.pa_pin = out_cfg.pa_pin,
|
||||
.use_mclk = out_cfg.use_mclk,
|
||||
.hw_gain.pa_gain = out_cfg.pa_gain,
|
||||
};
|
||||
codec_res.out_codec_if = es8311_codec_new(&es8311_cfg);
|
||||
} break;
|
||||
case CODEC_TYPE_ES8388: {
|
||||
i2c_cfg.addr = out_cfg.i2c_addr ? out_cfg.i2c_addr : ES8388_CODEC_DEFAULT_ADDR;
|
||||
codec_res.out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
es8388_codec_cfg_t es8388_cfg = {
|
||||
.codec_mode = same_codec ? ESP_CODEC_DEV_WORK_MODE_BOTH : ESP_CODEC_DEV_WORK_MODE_DAC,
|
||||
.ctrl_if = codec_res.out_ctrl_if,
|
||||
.gpio_if = codec_res.gpio_if,
|
||||
.pa_pin = out_cfg.pa_pin,
|
||||
.hw_gain.pa_gain = out_cfg.pa_gain,
|
||||
};
|
||||
codec_res.out_codec_if = es8388_codec_new(&es8388_cfg);
|
||||
} break;
|
||||
case CODEC_TYPE_DUMMY: {
|
||||
dummy_codec_cfg_t dummy_cfg = {
|
||||
.gpio_if = codec_res.gpio_if,
|
||||
.enable_gpio = out_cfg.pa_pin,
|
||||
};
|
||||
codec_res.out_codec_if = dummy_codec_new(&dummy_cfg);
|
||||
} break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "TODO not supported output codec type %d", out_cfg.codec_type);
|
||||
break;
|
||||
}
|
||||
esp_codec_dev_cfg_t dev_cfg = {
|
||||
.codec_if = codec_res.out_codec_if,
|
||||
.data_if = codec_res.data_if,
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_OUT,
|
||||
};
|
||||
if (same_codec && cfg->reuse_dev) {
|
||||
dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_IN_OUT;
|
||||
}
|
||||
codec_res.play_dev = esp_codec_dev_new(&dev_cfg);
|
||||
if (same_codec) {
|
||||
if (cfg->reuse_dev) {
|
||||
codec_res.record_dev = codec_res.play_dev;
|
||||
} else {
|
||||
// separate record from playback so that can set different fs
|
||||
dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_IN;
|
||||
codec_res.record_dev = esp_codec_dev_new(&dev_cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (same_codec == false && has_in) {
|
||||
if (same_i2s == false) {
|
||||
audio_codec_i2s_cfg_t i2s_in_cfg = {
|
||||
.port = in_cfg.i2s_port,
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
.rx_handle = i2s_keep[in_cfg.i2s_port]->rx_handle,
|
||||
#endif
|
||||
};
|
||||
ESP_LOGI(TAG, "Get in handle %p port %d", i2s_in_cfg.rx_handle, i2s_in_cfg.port);
|
||||
codec_res.data_in_if = audio_codec_new_i2s_data(&i2s_in_cfg);
|
||||
}
|
||||
audio_codec_i2c_cfg_t i2c_cfg = {
|
||||
.port = in_cfg.i2c_port,
|
||||
#ifdef USE_I2C_MASTER
|
||||
.bus_handle = get_i2c_bus_handle(in_cfg.i2c_port),
|
||||
#endif
|
||||
};
|
||||
// TODO add other codec support
|
||||
switch (in_cfg.codec_type) {
|
||||
case CODEC_TYPE_ES7210: {
|
||||
i2c_cfg.addr = in_cfg.i2c_addr ? in_cfg.i2c_addr : ES7210_CODEC_DEFAULT_ADDR;
|
||||
codec_res.in_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
es7210_codec_cfg_t es7210_cfg = {
|
||||
.ctrl_if = codec_res.in_ctrl_if,
|
||||
.mic_selected = ES7210_SEL_MIC1 | ES7210_SEL_MIC3,
|
||||
};
|
||||
if (cfg->in_use_tdm || (cfg->in_mode == CODEC_I2S_MODE_TDM)) {
|
||||
es7210_cfg.mic_selected |= ES7210_SEL_MIC2 | ES7210_SEL_MIC4;
|
||||
}
|
||||
codec_res.in_codec_if = es7210_codec_new(&es7210_cfg);
|
||||
} break;
|
||||
|
||||
case CODEC_TYPE_ES7243: {
|
||||
i2c_cfg.addr = in_cfg.i2c_addr ? in_cfg.i2c_addr : ES7243_CODEC_DEFAULT_ADDR;
|
||||
codec_res.in_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
es7243_codec_cfg_t es7243_cfg = {
|
||||
.ctrl_if = codec_res.in_ctrl_if,
|
||||
};
|
||||
codec_res.in_codec_if = es7243_codec_new(&es7243_cfg);
|
||||
} break;
|
||||
|
||||
case CODEC_TYPE_DUMMY: {
|
||||
dummy_codec_cfg_t dummy_cfg = {
|
||||
.gpio_if = codec_res.gpio_if,
|
||||
.enable_gpio = out_cfg.pa_pin,
|
||||
};
|
||||
codec_res.in_codec_if = dummy_codec_new(&dummy_cfg);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
esp_codec_dev_cfg_t dev_cfg = {
|
||||
.codec_if = codec_res.in_codec_if,
|
||||
.data_if = same_i2s ? codec_res.data_if : codec_res.data_in_if,
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_IN,
|
||||
};
|
||||
codec_res.record_dev = esp_codec_dev_new(&dev_cfg);
|
||||
}
|
||||
// Set default volume and gain for play and record
|
||||
if (codec_res.play_dev) {
|
||||
esp_codec_dev_set_out_vol(codec_res.play_dev, 60.0);
|
||||
}
|
||||
if (codec_res.record_dev) {
|
||||
esp_codec_dev_set_in_gain(codec_res.record_dev, 30.0);
|
||||
}
|
||||
if ((codec_res.play_dev != NULL) || (codec_res.record_dev != NULL)) {
|
||||
codec_res.inited = true;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
esp_codec_dev_handle_t get_playback_handle(void)
|
||||
{
|
||||
return codec_res.play_dev;
|
||||
}
|
||||
|
||||
esp_codec_dev_handle_t get_record_handle(void)
|
||||
{
|
||||
return codec_res.record_dev;
|
||||
}
|
||||
|
||||
void deinit_codec(void)
|
||||
{
|
||||
if (codec_res.play_dev) {
|
||||
esp_codec_dev_delete(codec_res.play_dev);
|
||||
if (codec_res.record_dev == codec_res.play_dev) {
|
||||
codec_res.record_dev = NULL;
|
||||
}
|
||||
codec_res.play_dev = NULL;
|
||||
}
|
||||
if (codec_res.record_dev) {
|
||||
esp_codec_dev_delete(codec_res.record_dev);
|
||||
codec_res.record_dev = NULL;
|
||||
}
|
||||
// Delete codec interface
|
||||
if (codec_res.in_codec_if) {
|
||||
audio_codec_delete_codec_if(codec_res.in_codec_if);
|
||||
codec_res.in_codec_if = NULL;
|
||||
}
|
||||
if (codec_res.out_codec_if) {
|
||||
audio_codec_delete_codec_if(codec_res.out_codec_if);
|
||||
codec_res.out_codec_if = NULL;
|
||||
}
|
||||
// Delete codec control interface
|
||||
if (codec_res.in_ctrl_if) {
|
||||
audio_codec_delete_ctrl_if(codec_res.in_ctrl_if);
|
||||
codec_res.in_ctrl_if = NULL;
|
||||
}
|
||||
if (codec_res.out_ctrl_if) {
|
||||
audio_codec_delete_ctrl_if(codec_res.out_ctrl_if);
|
||||
codec_res.out_ctrl_if = NULL;
|
||||
}
|
||||
if (codec_res.data_if) {
|
||||
audio_codec_delete_data_if(codec_res.data_if);
|
||||
codec_res.data_if = NULL;
|
||||
}
|
||||
if (codec_res.data_in_if) {
|
||||
audio_codec_delete_data_if(codec_res.data_in_if);
|
||||
codec_res.data_in_if = NULL;
|
||||
}
|
||||
if (codec_res.gpio_if) {
|
||||
audio_codec_delete_gpio_if(codec_res.gpio_if);
|
||||
codec_res.gpio_if = NULL;
|
||||
}
|
||||
for (int i = 0; i < MAX_I2C_NUM; i++) {
|
||||
_i2c_deinit(i);
|
||||
_i2s_deinit(i);
|
||||
}
|
||||
codec_res.inited = false;
|
||||
}
|
||||
|
||||
static sdmmc_card_t *card = NULL;
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
#include "esp_ldo_regulator.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void enable_mmc_phy_power(void)
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
esp_ldo_channel_config_t ldo_cfg = {
|
||||
.chan_id = 4,
|
||||
.voltage_mv = 3300,
|
||||
};
|
||||
esp_ldo_channel_handle_t ldo_phy_chan;
|
||||
esp_ldo_acquire_channel(&ldo_cfg, &ldo_phy_chan);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
static void sdmmc_get_slot(const int slot, sdmmc_slot_config_t *config)
|
||||
{
|
||||
memset(config, 0, sizeof(sdmmc_slot_config_t));
|
||||
config->cd = SDMMC_SLOT_NO_CD;
|
||||
config->wp = SDMMC_SLOT_NO_WP;
|
||||
config->width = 4;
|
||||
config->flags = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int mount_sdcard(void)
|
||||
{
|
||||
sdcard_cfg_t cfg = { 0 };
|
||||
enable_mmc_phy_power();
|
||||
int ret = get_sdcard_config(&cfg);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined CONFIG_IDF_TARGET_ESP32
|
||||
gpio_config_t sdcard_pwr_pin_cfg = {
|
||||
.pin_bit_mask = 1UL << GPIO_NUM_13,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
gpio_config(&sdcard_pwr_pin_cfg);
|
||||
gpio_set_level(GPIO_NUM_13, 0);
|
||||
#endif
|
||||
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = false,
|
||||
.max_files = 5,
|
||||
};
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
host.slot = 0;
|
||||
#endif
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4
|
||||
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
#endif
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
slot_config.width = cfg.d3 ? 4 : 1;
|
||||
#if SOC_SDMMC_USE_GPIO_MATRIX
|
||||
slot_config.width = cfg.d3 ? 4 : 1;
|
||||
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
|
||||
slot_config.d4 = -1;
|
||||
slot_config.d5 = -1;
|
||||
slot_config.d6 = -1;
|
||||
slot_config.d7 = -1;
|
||||
slot_config.cd = -1;
|
||||
slot_config.wp = -1;
|
||||
slot_config.clk = cfg.clk;
|
||||
slot_config.cmd = cfg.cmd;
|
||||
slot_config.d0 = cfg.d0;
|
||||
slot_config.d1 = cfg.d1 ? cfg.d1 : -1;
|
||||
slot_config.d2 = cfg.d2 ? cfg.d2 : -1;
|
||||
slot_config.d3 = cfg.d3 ? cfg.d3 : -1;
|
||||
#endif /* SOC_SDMMC_USE_GPIO_MATRIX */
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
sdmmc_get_slot(0, &slot_config);
|
||||
#endif
|
||||
|
||||
printf("use %d %d %d %d\n", cfg.d0, cfg.d1, cfg.d2, cfg.d3);
|
||||
return esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card);
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *get_sdcard_handle(void)
|
||||
{
|
||||
return card;
|
||||
}
|
||||
|
||||
void unmount_sdcard(void)
|
||||
{
|
||||
if (card) {
|
||||
esp_vfs_fat_sdcard_unmount("/sdcard", card);
|
||||
card = NULL;
|
||||
}
|
||||
}
|
||||
178
components/ExternLib/codec_board/drv/tca9554.c
Normal file
178
components/ExternLib/codec_board/drv/tca9554.c
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2021 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "tca9554.h"
|
||||
#include "codec_init.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
|
||||
static char *TAG = "TCA9554";
|
||||
|
||||
#define SET_BITS(_m, _s, _v) ((_v) ? (_m)|((_s)) : (_m)&~((_s)))
|
||||
#define GET_BITS(_m, _s) (((_m) & (_s)) ? true : false)
|
||||
|
||||
#define TCA9554_INPUT_PORT 0x00
|
||||
#define TCA9554_OUTPUT_PORT 0x01
|
||||
#define TCA9554_POLARITY_INVERSION_PORT 0x02
|
||||
#define TCA9554_CONFIGURATION_PORT 0x03
|
||||
|
||||
typedef struct {
|
||||
uint8_t addr;
|
||||
char *name;
|
||||
} tca9554_dev_t;
|
||||
|
||||
static tca9554_dev_t dev_list[] = {
|
||||
{ 0x70, "TCA9554A"},
|
||||
{ 0x40, "TCA9554"},
|
||||
};
|
||||
|
||||
const static audio_codec_ctrl_if_t *i2c_ctrl;
|
||||
|
||||
static esp_err_t expander_dev_prob(uint8_t port)
|
||||
{
|
||||
audio_codec_i2c_cfg_t i2c_cfg = {
|
||||
.port = port,
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(dev_list) / sizeof(dev_list[0]); i++) {
|
||||
i2c_cfg.addr = dev_list[i].addr;
|
||||
i2c_cfg.bus_handle = get_i2c_bus_handle(port);
|
||||
i2c_ctrl = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
uint8_t data = 0;
|
||||
if (i2c_ctrl->read_reg(i2c_ctrl, TCA9554_OUTPUT_PORT, 1, &data, 1) == 0) {
|
||||
ESP_LOGI(TAG, "Detected IO expander device at 0x%02X, name is: %s",
|
||||
dev_list[i].addr, dev_list[i].name);
|
||||
return ESP_OK;
|
||||
}
|
||||
audio_codec_delete_ctrl_if(i2c_ctrl);
|
||||
i2c_ctrl = NULL;
|
||||
}
|
||||
ESP_LOGE(TAG, "IO expander device has not detected");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
static esp_err_t tca9554_write_reg(uint8_t reg_addr, uint8_t data)
|
||||
{
|
||||
return i2c_ctrl->write_reg(i2c_ctrl, reg_addr, 1, &data, 1);
|
||||
}
|
||||
|
||||
static char tca9554_read_reg(uint8_t reg_addr)
|
||||
{
|
||||
uint8_t data;
|
||||
i2c_ctrl->read_reg(i2c_ctrl, reg_addr, 1, &data, 1);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
esp_tca9554_io_level_t tca9554_get_input_state(esp_tca9554_gpio_num_t gpio_num)
|
||||
{
|
||||
char data = 0;
|
||||
if (gpio_num < TCA9554_GPIO_NUM_MAX) {
|
||||
data = tca9554_read_reg(TCA9554_INPUT_PORT);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
|
||||
return TCA9554_LEVEL_ERROR;
|
||||
}
|
||||
return GET_BITS(data, gpio_num);
|
||||
}
|
||||
|
||||
esp_tca9554_io_level_t tca9554_get_output_state(esp_tca9554_gpio_num_t gpio_num)
|
||||
{
|
||||
char data = 0;
|
||||
if (gpio_num < TCA9554_GPIO_NUM_MAX) {
|
||||
data = tca9554_read_reg(TCA9554_OUTPUT_PORT);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
|
||||
return TCA9554_LEVEL_ERROR;
|
||||
}
|
||||
|
||||
return GET_BITS(data, gpio_num);
|
||||
}
|
||||
|
||||
esp_err_t tca9554_set_output_state(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_level_t level)
|
||||
{
|
||||
char data;
|
||||
esp_err_t res = ESP_FAIL;
|
||||
if (gpio_num < TCA9554_GPIO_NUM_MAX) {
|
||||
data = tca9554_read_reg(TCA9554_OUTPUT_PORT);
|
||||
res = tca9554_write_reg(TCA9554_OUTPUT_PORT, SET_BITS(data, gpio_num, level));
|
||||
} else {
|
||||
ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
esp_err_t tca9554_set_polarity_inversion(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_polarity_t polarity)
|
||||
{
|
||||
char data;
|
||||
esp_err_t res = ESP_FAIL;
|
||||
if (gpio_num < TCA9554_GPIO_NUM_MAX) {
|
||||
data = tca9554_read_reg(TCA9554_POLARITY_INVERSION_PORT);
|
||||
res = tca9554_write_reg(TCA9554_POLARITY_INVERSION_PORT, SET_BITS(data, gpio_num, polarity));
|
||||
} else {
|
||||
ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
esp_tca9554_io_config_t tca9554_get_io_config(esp_tca9554_gpio_num_t gpio_num)
|
||||
{
|
||||
char data = 0;
|
||||
if (gpio_num < TCA9554_GPIO_NUM_MAX) {
|
||||
data = tca9554_read_reg(TCA9554_CONFIGURATION_PORT);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
|
||||
return TCA9554_LEVEL_ERROR;
|
||||
}
|
||||
|
||||
return GET_BITS(data, gpio_num);
|
||||
}
|
||||
|
||||
esp_err_t tca9554_set_io_config(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_config_t io_config)
|
||||
{
|
||||
char data;
|
||||
esp_err_t res = ESP_FAIL;
|
||||
if (gpio_num < TCA9554_GPIO_NUM_MAX) {
|
||||
data = tca9554_read_reg(TCA9554_CONFIGURATION_PORT);
|
||||
res = tca9554_write_reg(TCA9554_CONFIGURATION_PORT, SET_BITS(data, gpio_num, io_config));
|
||||
} else {
|
||||
ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
esp_err_t tca9554_init(uint8_t port)
|
||||
{
|
||||
return expander_dev_prob(port);
|
||||
}
|
||||
|
||||
esp_err_t tca9554_deinit()
|
||||
{
|
||||
if (i2c_ctrl) {
|
||||
audio_codec_delete_ctrl_if(i2c_ctrl);
|
||||
i2c_ctrl = NULL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
158
components/ExternLib/codec_board/drv/tca9554.h
Normal file
158
components/ExternLib/codec_board/drv/tca9554.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2021 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _TCA9554_H
|
||||
#define _TCA9554_H
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
TCA9554_GPIO_NUM_0 = 1 << 0,
|
||||
TCA9554_GPIO_NUM_1 = 1 << 1,
|
||||
TCA9554_GPIO_NUM_2 = 1 << 2,
|
||||
TCA9554_GPIO_NUM_3 = 1 << 3,
|
||||
TCA9554_GPIO_NUM_4 = 1 << 4,
|
||||
TCA9554_GPIO_NUM_5 = 1 << 5,
|
||||
TCA9554_GPIO_NUM_6 = 1 << 6,
|
||||
TCA9554_GPIO_NUM_7 = 1 << 7,
|
||||
TCA9554_GPIO_NUM_MAX
|
||||
} esp_tca9554_gpio_num_t;
|
||||
|
||||
typedef enum {
|
||||
TCA9554_IO_LOW,
|
||||
TCA9554_IO_HIGH,
|
||||
TCA9554_LEVEL_ERROR
|
||||
} esp_tca9554_io_level_t;
|
||||
|
||||
typedef enum {
|
||||
TCA9554_IO_RETAINED,
|
||||
TCA9554_IO_INVERTED
|
||||
} esp_tca9554_io_polarity_t;
|
||||
|
||||
typedef enum {
|
||||
TCA9554_IO_OUTPUT,
|
||||
TCA9554_IO_INPUT
|
||||
} esp_tca9554_io_config_t;
|
||||
|
||||
/*
|
||||
* @brief Initialize TCA9554 chip
|
||||
*
|
||||
* @param codec_cfg configuration of TCA9554
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t tca9554_init(uint8_t i2c_port);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize TCA9554 chip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t tca9554_deinit(void);
|
||||
|
||||
/*
|
||||
* @brief Get TCA9554 input level
|
||||
*
|
||||
* @param gpio_num GPIO of TCA9554
|
||||
*
|
||||
* @return
|
||||
* - esp_tca9554_io_level_t
|
||||
*/
|
||||
esp_tca9554_io_level_t tca9554_get_input_state(esp_tca9554_gpio_num_t gpio_num);
|
||||
|
||||
/*
|
||||
* @brief Get PCA95xian39 output level
|
||||
*
|
||||
* @param gpio_num GPIO of TCA9554
|
||||
*
|
||||
* @return
|
||||
* - esp_tca9554_io_level_t
|
||||
*/
|
||||
esp_tca9554_io_level_t tca9554_get_output_state(esp_tca9554_gpio_num_t gpio_num);
|
||||
|
||||
/*
|
||||
* @brief Get TCA9554 output state
|
||||
*
|
||||
* @param gpio_num GPIO of TCA9554
|
||||
*
|
||||
* @return
|
||||
* - esp_tca9554_io_level_t
|
||||
*/
|
||||
esp_err_t tca9554_set_output_state(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_level_t level);
|
||||
|
||||
/*
|
||||
* @brief Set TCA9554 polarity
|
||||
*
|
||||
* @param gpio_num GPIO of TCA9554
|
||||
* polarity polarity
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t tca9554_set_polarity_inversion(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_polarity_t polarity);
|
||||
|
||||
/*
|
||||
* @brief Get TCA9554 output level
|
||||
*
|
||||
* @param gpio_num GPIO of TCA9554
|
||||
*
|
||||
* @return
|
||||
* - esp_tca9554_io_level_t
|
||||
*/
|
||||
esp_tca9554_io_config_t tca9554_get_io_config(esp_tca9554_gpio_num_t gpio_num);
|
||||
|
||||
/*
|
||||
* @brief Set TCA9554 io config
|
||||
*
|
||||
* @param gpio_num GPIO of TCA9554
|
||||
* io_config io config
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*/
|
||||
esp_err_t tca9554_set_io_config(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_config_t io_config);
|
||||
|
||||
/**
|
||||
* @brief Print all TCA9554 registers
|
||||
*
|
||||
* @return
|
||||
* - void
|
||||
*/
|
||||
void tca9554_read_all();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
72
components/ExternLib/codec_board/dummy_codec.c
Normal file
72
components/ExternLib/codec_board/dummy_codec.c
Normal file
@@ -0,0 +1,72 @@
|
||||
|
||||
#include "dummy_codec.h"
|
||||
|
||||
typedef struct {
|
||||
audio_codec_if_t base;
|
||||
const audio_codec_gpio_if_t *gpio_if;
|
||||
bool is_open;
|
||||
bool enable;
|
||||
} dummy_codec_t;
|
||||
|
||||
typedef struct {
|
||||
audio_codec_ctrl_if_t base;
|
||||
bool is_open;
|
||||
} dummy_codec_ctrl_t;
|
||||
|
||||
static int dummy_codec_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
|
||||
{
|
||||
dummy_codec_cfg_t *codec_cfg = (dummy_codec_cfg_t *)cfg;
|
||||
if (cfg_size != sizeof(dummy_codec_cfg_t) || codec_cfg->gpio_if == NULL) {
|
||||
return -1;
|
||||
}
|
||||
dummy_codec_t *codec = (dummy_codec_t *)h;
|
||||
codec->gpio_if = codec_cfg->gpio_if;
|
||||
codec->gpio_if->setup(codec_cfg->enable_gpio, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
|
||||
codec->gpio_if->set(codec_cfg->enable_gpio, true);
|
||||
codec->is_open = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool dummy_codec_is_open(const audio_codec_if_t *h)
|
||||
{
|
||||
dummy_codec_t *codec = (dummy_codec_t *)h;
|
||||
return codec->is_open;
|
||||
}
|
||||
|
||||
static int dummy_codec_enable(const audio_codec_if_t *h, bool enable)
|
||||
{
|
||||
dummy_codec_t *codec = (dummy_codec_t *)h;
|
||||
codec->enable = enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dummy_codec_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dummy_codec_close(const audio_codec_if_t *h)
|
||||
{
|
||||
dummy_codec_t *codec = (dummy_codec_t *)h;
|
||||
// Auto disable when codec closed
|
||||
if (codec->enable) {
|
||||
dummy_codec_enable(h, false);
|
||||
}
|
||||
codec->is_open = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const audio_codec_if_t *dummy_codec_new(dummy_codec_cfg_t *codec_cfg)
|
||||
{
|
||||
dummy_codec_t *codec = (dummy_codec_t *)calloc(1, sizeof(dummy_codec_t));
|
||||
if (codec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
codec->base.open = dummy_codec_open;
|
||||
codec->base.is_open = dummy_codec_is_open;
|
||||
codec->base.enable = dummy_codec_enable;
|
||||
codec->base.set_fs = dummy_codec_set_fs;
|
||||
codec->base.close = dummy_codec_close;
|
||||
codec->base.open(&codec->base, codec_cfg, sizeof(dummy_codec_cfg_t));
|
||||
return &codec->base;
|
||||
}
|
||||
33
components/ExternLib/codec_board/dummy_codec.h
Normal file
33
components/ExternLib/codec_board/dummy_codec.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "esp_codec_dev.h"
|
||||
#include "audio_codec_ctrl_if.h"
|
||||
#include "audio_codec_gpio_if.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Dummy codec configuration
|
||||
*/
|
||||
typedef struct {
|
||||
int16_t enable_gpio; /*!< Enable GPIO setting */
|
||||
const audio_codec_gpio_if_t *gpio_if; /*!< GPIO interface to control gpio */
|
||||
} dummy_codec_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Create a dummy codec
|
||||
*
|
||||
* @note Dummy codec means use I2S to transfer audio data with out I2C control interface
|
||||
*
|
||||
* @param[in] codec_cfg Dummy codec configuration
|
||||
*
|
||||
* @return
|
||||
* - NULL No memory for dummy codec
|
||||
* - Others Dummy codec instance
|
||||
*
|
||||
*/
|
||||
const audio_codec_if_t *dummy_codec_new(dummy_codec_cfg_t *codec_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
12
components/ExternLib/codec_board/idf_component.yml
Normal file
12
components/ExternLib/codec_board/idf_component.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
dependencies:
|
||||
esp_lcd_ek79007:
|
||||
rules:
|
||||
- if: target in [esp32p4]
|
||||
version: ~1.0.2
|
||||
espressif/esp_codec_dev: ~1.4.0
|
||||
espressif/esp_lcd_ili9881c:
|
||||
rules:
|
||||
- if: target in [esp32p4]
|
||||
version: ~1.0.1
|
||||
description: Simple ESP32 series codec board realization
|
||||
version: 0.5.5
|
||||
346
components/ExternLib/codec_board/include/codec_board.h
Normal file
346
components/ExternLib/codec_board/include/codec_board.h
Normal file
@@ -0,0 +1,346 @@
|
||||
/**
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2025 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum of supported I2C number
|
||||
*/
|
||||
#define MAX_I2C_NUM (2)
|
||||
|
||||
/**
|
||||
* @brief Maximum of supported I2S number
|
||||
*/
|
||||
#define MAX_I2S_NUM (2)
|
||||
|
||||
/**
|
||||
* @brief Maximum of supported codec number
|
||||
*/
|
||||
#define MAX_CODEC_NUM (2)
|
||||
|
||||
/**
|
||||
* @brief Codec input and output direction
|
||||
*/
|
||||
#define CODEC_DIR_IN (1 << 0)
|
||||
#define CODEC_DIR_OUT (1 << 1)
|
||||
#define CODEC_DIR_IN_OUT (CODEC_DIR_IN | CODEC_DIR_OUT)
|
||||
|
||||
/**
|
||||
* @brief Start of extended GPIO start
|
||||
*/
|
||||
#define BOARD_EXTEND_IO_START (0x1000)
|
||||
|
||||
/**
|
||||
* @brief Codec type
|
||||
*/
|
||||
typedef enum {
|
||||
CODEC_TYPE_NONE, /*!< Codec type none */
|
||||
CODEC_TYPE_ES8311, /*!< ES8311 codec type */
|
||||
CODEC_TYPE_ES7210, /*!< ES7210 codec type */
|
||||
CODEC_TYPE_ES7243, /*!< ES7243 codec type */
|
||||
CODEC_TYPE_ES8388, /*!< ES8388 codec type */
|
||||
CODEC_TYPE_DUMMY, /*!< Dummy codec type (which have I2S interface only) */
|
||||
} codec_type_t;
|
||||
|
||||
/**
|
||||
* @brief Camera type
|
||||
*/
|
||||
typedef enum {
|
||||
CAMERA_TYPE_NONE, /*!< Camera type none */
|
||||
CAMERA_TYPE_DVP, /*!< DVP camera type */
|
||||
CAMERA_TYPE_USB, /*!< USB camera type */
|
||||
CAMERA_TYPE_MIPI /*!< MIPI camera type */
|
||||
} camera_type_t;
|
||||
|
||||
/**
|
||||
* @brief I2C pin setting
|
||||
*/
|
||||
typedef struct {
|
||||
int16_t sda; /*!< GPIO for SDA */
|
||||
int16_t scl; /*!< GPIO for SCL */
|
||||
} codec_i2c_pin_t;
|
||||
|
||||
/**
|
||||
* @brief I2S pin setting
|
||||
*/
|
||||
typedef struct {
|
||||
int16_t mclk; /*!< GPIO for MCLK */
|
||||
int16_t bclk; /*!< GPIO for BCLK */
|
||||
int16_t ws; /*!< GPIO for Word Selction */
|
||||
int16_t dout; /*!< GPIO for digital output */
|
||||
int16_t din; /*!< GPIO for digital input */
|
||||
} codec_i2s_pin_t;
|
||||
|
||||
/**
|
||||
* @brief Codec configuration
|
||||
*/
|
||||
typedef struct {
|
||||
codec_type_t codec_type; /*!< Codec type */
|
||||
int16_t pa_pin; /*!< GPIO for PA control */
|
||||
float pa_gain; /*!< PA gain */
|
||||
uint8_t i2c_addr; /*!< I2C address */
|
||||
int8_t i2c_port; /*!< I2C port */
|
||||
int8_t i2s_port; /*!< I2S port */
|
||||
bool use_mclk; /*!< Whether codec need MCLK clock */
|
||||
} codec_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Sdcard configuration
|
||||
*/
|
||||
typedef struct {
|
||||
int16_t clk; /*!< GPIO for clock */
|
||||
int16_t cmd; /*!< GPIO for command */
|
||||
int16_t d0; /*!< GPIO for d0 */
|
||||
int16_t d1; /*!< GPIO for d1 (if only one line need set to -1) */
|
||||
int16_t d2; /*!< GPIO for d2 (if only one line need set to -1) */
|
||||
int16_t d3; /*!< GPIO for d3 (if only one line need set to -1) */
|
||||
int16_t power; /*!< GPIO for power */
|
||||
} sdcard_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Camera configuration
|
||||
*/
|
||||
typedef struct {
|
||||
camera_type_t type; /*!< Camera type */
|
||||
int16_t pwr; /*!< GPIO for power */
|
||||
int16_t reset; /*!< GPIO for reset */
|
||||
int16_t xclk; /*!< GPIO for XCLK */
|
||||
int16_t pclk; /*!< GPIO for PCLK */
|
||||
int16_t vsync; /*!< GPIO for VSYNC */
|
||||
int16_t de; /*!< GPIO for DE */
|
||||
int16_t href; /*!< GPIO for HREF */
|
||||
int16_t data[16]; /*!< GPIO for DATA */
|
||||
} camera_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Codec setting
|
||||
*/
|
||||
typedef struct {
|
||||
codec_cfg_t codec_cfg; /*!< Codec configuration */
|
||||
uint8_t codec_dir; /*!< Codec direction */
|
||||
} codec_setting_t;
|
||||
|
||||
/**
|
||||
* @brief Extent IO board type
|
||||
*/
|
||||
typedef enum {
|
||||
EXTENT_IO_TYPE_NONE, /*!< NOne extent IO */
|
||||
EXTENT_IO_TYPE_TCA9554, /*!< TCA9554 extent IO type */
|
||||
} extend_io_type_t;
|
||||
|
||||
/**
|
||||
* @brief LCD controller type
|
||||
*/
|
||||
typedef enum {
|
||||
LCD_CONTROLLER_TYPE_NONE, /*!< None controller type */
|
||||
LCD_CONTROLLER_TYPE_ST7789, /*!< ST7789 controller type */
|
||||
} lcd_controller_type_t;
|
||||
|
||||
/**
|
||||
* @brief LCD bus type
|
||||
*/
|
||||
typedef enum {
|
||||
LCD_BUS_TYPE_NONE, /*!< None LCD bus type */
|
||||
LCD_BUS_TYPE_SPI, /*!< SPI LCD bus type */
|
||||
LCD_BUS_TYPE_RGB, /*!< RGB LCD bus type */
|
||||
LCD_BUS_TYPE_I80, /*!< I80 LCD bus type */
|
||||
LCD_BUS_TYPE_MIPI, /*!< MIPI LCD bus type */
|
||||
} lcd_bus_type_t;
|
||||
|
||||
/**
|
||||
* @brief SPI LCD configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t spi_bus; /*!< SPI bus number */
|
||||
int pclk_clk; /*!< PCLK clock */
|
||||
uint8_t cmd_bits; /*!< Command bit width */
|
||||
uint8_t param_bits; /*!< Parameter bit width */
|
||||
int16_t cs; /*!< CS GPIO */
|
||||
int16_t dc; /*!< DC GPIO */
|
||||
int16_t clk; /*!< Clock GPIO */
|
||||
int16_t mosi; /*!< MOSI GPIO */
|
||||
int16_t d[7]; /*!< Data GPIOs */
|
||||
} lcd_spi_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief MIPI LCD configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t ldo_chan;
|
||||
uint16_t ldo_voltage;
|
||||
uint8_t lane_num;
|
||||
uint32_t lane_bitrate; // Mbps
|
||||
uint32_t dpi_clk; // MHz
|
||||
uint8_t bit_depth;
|
||||
uint8_t fb_num;
|
||||
uint8_t dsi_hsync;
|
||||
uint8_t dsi_vsync;
|
||||
uint8_t dsi_hbp;
|
||||
uint8_t dsi_hfp;
|
||||
uint8_t dsi_vbp;
|
||||
uint8_t dsi_vfp;
|
||||
} lcd_mipi_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief LCD configuration
|
||||
*/
|
||||
typedef struct {
|
||||
extend_io_type_t io_type;
|
||||
uint8_t io_i2c_port;
|
||||
lcd_bus_type_t bus_type;
|
||||
lcd_controller_type_t controller;
|
||||
int width;
|
||||
int height;
|
||||
uint8_t i2c_port;
|
||||
uint8_t mirror_x : 1;
|
||||
uint8_t mirror_y : 1;
|
||||
uint8_t swap_xy : 1;
|
||||
uint8_t color_inv : 1;
|
||||
int16_t ctrl_pin;
|
||||
int16_t reset_pin;
|
||||
union {
|
||||
lcd_spi_cfg_t spi_cfg;
|
||||
lcd_mipi_cfg_t mipi_cfg;
|
||||
};
|
||||
} lcd_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Board section
|
||||
*/
|
||||
typedef struct {
|
||||
codec_i2c_pin_t i2c_pin[MAX_I2C_NUM];
|
||||
codec_i2s_pin_t i2s_pin[MAX_I2S_NUM];
|
||||
codec_setting_t codec[MAX_CODEC_NUM];
|
||||
lcd_cfg_t lcd;
|
||||
sdcard_cfg_t sdcard;
|
||||
camera_cfg_t camera;
|
||||
uint8_t i2c_num;
|
||||
uint8_t i2s_num;
|
||||
uint8_t codec_num;
|
||||
uint8_t sdcard_num;
|
||||
uint8_t lcd_num;
|
||||
uint8_t camera_num;
|
||||
} board_section_t;
|
||||
|
||||
/**
|
||||
* @brief Set codec board type
|
||||
*
|
||||
* @param[in] board_type Board name selection
|
||||
*/
|
||||
void set_codec_board_type(const char *board_type);
|
||||
|
||||
/**
|
||||
* @brief Get SDCard configuration
|
||||
*
|
||||
* @param[out] card_cfg SDCard configuration to store
|
||||
*
|
||||
* @return
|
||||
* - 0 On success
|
||||
* - -1 Not exists
|
||||
*
|
||||
*/
|
||||
int get_sdcard_config(sdcard_cfg_t *card_cfg);
|
||||
|
||||
/**
|
||||
* @brief Get I2C pin setting by port
|
||||
*
|
||||
* @param[in] port I2C port
|
||||
* @param[out] i2c_pin I2C pin setting
|
||||
*
|
||||
* @return
|
||||
* - 0 On success
|
||||
* - -1 Not exists
|
||||
*
|
||||
*/
|
||||
int get_i2c_pin(uint8_t port, codec_i2c_pin_t *i2c_pin);
|
||||
|
||||
/**
|
||||
* @brief Get I2S pin setting by port
|
||||
*
|
||||
* @param[in] port I2S port
|
||||
* @param[out] i2s_pin I2S pin setting
|
||||
*
|
||||
* @return
|
||||
* - 0 On success
|
||||
* - -1 Not exists
|
||||
*/
|
||||
int get_i2s_pin(uint8_t port, codec_i2s_pin_t *i2s_pin);
|
||||
|
||||
/**
|
||||
* @brief Get output codec configuration
|
||||
*
|
||||
* @param[out] out_cfg Output codec configuration to store
|
||||
*
|
||||
* @return
|
||||
* - 0 On success
|
||||
* - -1 Not exists
|
||||
*/
|
||||
int get_out_codec_cfg(codec_cfg_t *out_cfg);
|
||||
|
||||
/**
|
||||
* @brief Get input codec configuration
|
||||
*
|
||||
* @param[out] in_cfg Input codec configuration to store
|
||||
*
|
||||
* @return
|
||||
* - 0 On success
|
||||
* - -1 Not exists
|
||||
*/
|
||||
int get_in_codec_cfg(codec_cfg_t *in_cfg);
|
||||
|
||||
/**
|
||||
* @brief Get LCD configuration
|
||||
*
|
||||
* @param[out] lcd_cfg LCD configuration to store
|
||||
*
|
||||
* @return
|
||||
* - 0 On success
|
||||
* - -1 Not exists
|
||||
*/
|
||||
int get_lcd_cfg(lcd_cfg_t *lcd_cfg);
|
||||
|
||||
/**
|
||||
* @brief Get camera configuration
|
||||
*
|
||||
* @param[out] cam_cfg Camera configuration to store
|
||||
*
|
||||
* @return
|
||||
* - 0 On success
|
||||
* - -1 Not exists
|
||||
*/
|
||||
int get_camera_cfg(camera_cfg_t *cam_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
168
components/ExternLib/codec_board/include/codec_init.h
Normal file
168
components/ExternLib/codec_board/include/codec_init.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2025 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_codec_dev.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2S mode
|
||||
*/
|
||||
typedef enum {
|
||||
CODEC_I2S_MODE_STD = 0, /*!< STD mode */
|
||||
CODEC_I2S_MODE_TDM, /*!< TDM mode */
|
||||
CODEC_I2S_MODE_PDM, /*!< PDM mode */
|
||||
CODEC_I2S_MODE_NONE, /*!< NONE means not support */
|
||||
} codec_i2s_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Codec initialize configuration
|
||||
*/
|
||||
typedef struct {
|
||||
codec_i2s_mode_t in_mode; /*!< I2S input mode */
|
||||
codec_i2s_mode_t out_mode; /*!< I2S output mode */
|
||||
bool in_use_tdm; /*!< Whether codec use TDM mode for input (codec use TDM, I2S can use STD mode) */
|
||||
bool reuse_dev; /*!< Use same handle for both input and output */
|
||||
} codec_init_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize I2C driver
|
||||
*
|
||||
* @note When user want to get I2C bus handle either use `get_i2c_bus_handle`
|
||||
* Or use IDF API `i2c_master_get_bus_handle` on IDFv5.4 or high
|
||||
*
|
||||
* @param[in] port I2C port
|
||||
*
|
||||
* @return
|
||||
* - 0 On success
|
||||
* - Others Fail to initialize
|
||||
*/
|
||||
int init_i2c(uint8_t port);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize I2C driver
|
||||
*
|
||||
* @note Attention make sure no driver use I2C bus anymore before do this action
|
||||
*
|
||||
* @param[in] port I2C port
|
||||
*
|
||||
* @return
|
||||
* - 0 On success
|
||||
* - Others Fail to initialize
|
||||
*/
|
||||
int deinit_i2c(uint8_t port);
|
||||
|
||||
/**
|
||||
* @brief Initialize codec
|
||||
*
|
||||
* @param[in] cfg Codec initialize configuration
|
||||
*
|
||||
* @return
|
||||
* - 0 On success
|
||||
* - Others Fail to initialize
|
||||
*/
|
||||
int init_codec(codec_init_cfg_t *cfg);
|
||||
|
||||
/**
|
||||
* @brief Get `esp_codec_dev` handle for playback
|
||||
*
|
||||
* @return
|
||||
* - NULL Fail to get playback handle
|
||||
* - Others Playback `esp_codec_dev` handle
|
||||
*/
|
||||
esp_codec_dev_handle_t get_playback_handle(void);
|
||||
|
||||
/**
|
||||
* @brief Get `esp_codec_dev` handle for record
|
||||
*
|
||||
* @return
|
||||
* - NULL Fail to get record handle
|
||||
* - Others Record `esp_codec_dev` handle
|
||||
*/
|
||||
esp_codec_dev_handle_t get_record_handle(void);
|
||||
|
||||
/**
|
||||
* @brief Get I2C master bus handle by port
|
||||
*
|
||||
* @param[in] port I2C bus port
|
||||
*
|
||||
* @return
|
||||
* - NULL Fail to get I2C bus handle
|
||||
* - Others I2C bus handle
|
||||
*/
|
||||
void *get_i2c_bus_handle(uint8_t port);
|
||||
|
||||
/**
|
||||
* @brief Mount SDCard
|
||||
*
|
||||
* @return
|
||||
* - 0 On success
|
||||
* - Others Fail to mount
|
||||
*/
|
||||
int mount_sdcard(void);
|
||||
|
||||
/**
|
||||
* @brief Get mounted SDCard handle
|
||||
*
|
||||
* @return
|
||||
* - 0 On success
|
||||
* - Others Fail to mount
|
||||
*/
|
||||
void *get_sdcard_handle(void);
|
||||
|
||||
/**
|
||||
* @brief Unmount SDCard
|
||||
*/
|
||||
void unmount_sdcard(void);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize codec
|
||||
*/
|
||||
void deinit_codec(void);
|
||||
|
||||
/**
|
||||
* @brief Initialized for LCD
|
||||
*
|
||||
* @return
|
||||
* - 0 On success
|
||||
* - Others Fail to init LCD
|
||||
*/
|
||||
int board_lcd_init(void);
|
||||
|
||||
/**
|
||||
* @brief Get LCD handle
|
||||
*
|
||||
* @return
|
||||
* - NULL Fail to get LCD handle
|
||||
* - Others LCD handle
|
||||
*/
|
||||
void *board_get_lcd_handle(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
378
components/ExternLib/codec_board/lcd_init.c
Normal file
378
components/ExternLib/codec_board/lcd_init.c
Normal file
@@ -0,0 +1,378 @@
|
||||
#include "codec_board.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "tca9554.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_common.h"
|
||||
#include "esp_idf_version.h"
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
#include "esp_lcd_panel_dev.h"
|
||||
#include "esp_lcd_panel_st7789.h"
|
||||
#else
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_mipi_dsi.h"
|
||||
#include "esp_lcd_ili9881c.h"
|
||||
#include "esp_ldo_regulator.h"
|
||||
#include "soc/mipi_dsi_bridge_struct.h"
|
||||
#include "esp_lcd_ek79007.h"
|
||||
#endif
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
#define TAG "LCD_INIT"
|
||||
|
||||
#define RETURN_ON_ERR(ret) if (ret != 0) { \
|
||||
ESP_LOGE(TAG, "Fail to run on %d ret %d", __LINE__, ret); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int (*init)(lcd_cfg_t *cfg);
|
||||
int (*set_dir)(int16_t gpio, bool output);
|
||||
int (*set_gpio)(int16_t gpio, bool high);
|
||||
} extend_io_ops_t;
|
||||
|
||||
static extend_io_ops_t extend_io_ops;
|
||||
static esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
|
||||
static int tca9554_io_init(lcd_cfg_t *cfg)
|
||||
{
|
||||
return tca9554_init(cfg->io_i2c_port);
|
||||
}
|
||||
|
||||
static int tca9554_io_set_dir(int16_t gpio, bool output)
|
||||
{
|
||||
gpio = (1 << gpio);
|
||||
tca9554_set_io_config(gpio, output ? TCA9554_IO_OUTPUT : TCA9554_IO_INPUT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tca9554_io_set(int16_t gpio, bool high)
|
||||
{
|
||||
gpio = (1 << gpio);
|
||||
return tca9554_set_output_state(gpio, high ? TCA9554_IO_HIGH : TCA9554_IO_LOW);
|
||||
}
|
||||
|
||||
static void register_tca9554(void)
|
||||
{
|
||||
extend_io_ops.init = tca9554_io_init;
|
||||
extend_io_ops.set_dir = tca9554_io_set_dir;
|
||||
extend_io_ops.set_gpio = tca9554_io_set;
|
||||
}
|
||||
|
||||
static int init_extend_io(lcd_cfg_t *cfg)
|
||||
{
|
||||
if (cfg->io_type == EXTENT_IO_TYPE_NONE) {
|
||||
return 0;
|
||||
}
|
||||
switch (cfg->io_type) {
|
||||
case EXTENT_IO_TYPE_TCA9554:
|
||||
register_tca9554();
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return extend_io_ops.init(cfg);
|
||||
}
|
||||
|
||||
static int set_pin_dir(int16_t pin, bool output)
|
||||
{
|
||||
if (pin & BOARD_EXTEND_IO_START) {
|
||||
pin &= ~BOARD_EXTEND_IO_START;
|
||||
extend_io_ops.set_dir(pin, output);
|
||||
} else {
|
||||
gpio_config_t bk_gpio_config = {
|
||||
.mode = output ? GPIO_MODE_OUTPUT : GPIO_MODE_INPUT,
|
||||
.pin_bit_mask = pin > 0 ? 1ULL << pin : 0ULL,
|
||||
};
|
||||
gpio_config(&bk_gpio_config);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_pin_state(int16_t pin, bool high)
|
||||
{
|
||||
if (pin & BOARD_EXTEND_IO_START) {
|
||||
extend_io_ops.set_gpio(pin, high);
|
||||
} else {
|
||||
gpio_set_level(pin, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int16_t get_hw_gpio(int16_t pin)
|
||||
{
|
||||
if (pin == -1) {
|
||||
return pin;
|
||||
}
|
||||
if (pin & BOARD_EXTEND_IO_START) {
|
||||
return -1;
|
||||
}
|
||||
return pin;
|
||||
}
|
||||
|
||||
static void sleep_ms(int ms)
|
||||
{
|
||||
vTaskDelay(ms / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static int _lcd_rest(lcd_cfg_t *cfg)
|
||||
{
|
||||
if (cfg->reset_pin >= 0) {
|
||||
set_pin_state(cfg->reset_pin, false);
|
||||
sleep_ms(100);
|
||||
set_pin_state(cfg->reset_pin, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _init_spi_lcd(lcd_cfg_t *cfg)
|
||||
{
|
||||
int ret = 0;
|
||||
if (cfg->spi_cfg.cs & BOARD_EXTEND_IO_START) {
|
||||
set_pin_dir(cfg->spi_cfg.cs, true);
|
||||
sleep_ms(10);
|
||||
set_pin_dir(cfg->spi_cfg.cs, false);
|
||||
sleep_ms(10);
|
||||
}
|
||||
spi_bus_config_t buscfg = {
|
||||
.sclk_io_num = cfg->spi_cfg.clk,
|
||||
.mosi_io_num = cfg->spi_cfg.mosi,
|
||||
.miso_io_num = -1,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = cfg->width * cfg->height * 2,
|
||||
};
|
||||
#if SOC_SPI_SUPPORT_OCT
|
||||
if (cfg->spi_cfg.d[6] >= 0) {
|
||||
buscfg.data1_io_num = cfg->spi_cfg.d[0];
|
||||
buscfg.data2_io_num = cfg->spi_cfg.d[1];
|
||||
;
|
||||
buscfg.data3_io_num = cfg->spi_cfg.d[2];
|
||||
;
|
||||
buscfg.data4_io_num = cfg->spi_cfg.d[3];
|
||||
;
|
||||
buscfg.data5_io_num = cfg->spi_cfg.d[4];
|
||||
;
|
||||
buscfg.data6_io_num = cfg->spi_cfg.d[5];
|
||||
;
|
||||
buscfg.data7_io_num = cfg->spi_cfg.d[6];
|
||||
;
|
||||
buscfg.flags = SPICOMMON_BUSFLAG_OCTAL;
|
||||
}
|
||||
#endif
|
||||
int bus_id = SPI1_HOST + (cfg->spi_cfg.spi_bus - 1);
|
||||
ret = spi_bus_initialize(bus_id, &buscfg, SPI_DMA_CH_AUTO);
|
||||
ESP_LOGI(TAG, "CLK %d MOSI %d CS:%d DC: %d Bus:%d",
|
||||
cfg->spi_cfg.clk, cfg->spi_cfg.mosi,
|
||||
get_hw_gpio(cfg->spi_cfg.cs), cfg->spi_cfg.dc,
|
||||
bus_id);
|
||||
RETURN_ON_ERR(ret);
|
||||
esp_lcd_panel_io_spi_config_t io_config = {
|
||||
.dc_gpio_num = cfg->spi_cfg.dc,
|
||||
.cs_gpio_num = get_hw_gpio(cfg->spi_cfg.cs),
|
||||
.pclk_hz = cfg->spi_cfg.pclk_clk ? cfg->spi_cfg.pclk_clk : 60 * 1000 * 1000,
|
||||
.spi_mode = 0,
|
||||
.trans_queue_depth = 10,
|
||||
.lcd_cmd_bits = cfg->spi_cfg.cmd_bits ? cfg->spi_cfg.cmd_bits : 8,
|
||||
.lcd_param_bits = cfg->spi_cfg.param_bits ? cfg->spi_cfg.param_bits : 8,
|
||||
.on_color_trans_done = NULL,
|
||||
.user_ctx = NULL,
|
||||
};
|
||||
#if SOC_SPI_SUPPORT_OCT
|
||||
if (cfg->spi_cfg.d[6] >= 0) {
|
||||
io_config.flags.octal_mode = 1;
|
||||
io_config.spi_mode = 3;
|
||||
}
|
||||
#endif
|
||||
esp_lcd_panel_io_handle_t io_handle;
|
||||
ret = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)bus_id, &io_config, &io_handle);
|
||||
RETURN_ON_ERR(ret);
|
||||
esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = get_hw_gpio(cfg->reset_pin),
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
.rgb_ele_order = ESP_LCD_COLOR_SPACE_BGR,
|
||||
#else
|
||||
.rgb_endian = LCD_RGB_ENDIAN_BGR,
|
||||
#endif
|
||||
.bits_per_pixel = 16,
|
||||
};
|
||||
switch (cfg->controller) {
|
||||
default:
|
||||
return -1;
|
||||
case LCD_CONTROLLER_TYPE_ST7789:
|
||||
ret = esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle);
|
||||
RETURN_ON_ERR(ret);
|
||||
ESP_LOGI(TAG, "Init driver ST7789 finished");
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
static int power_on_dsi(lcd_mipi_cfg_t *mipi_cfg)
|
||||
{
|
||||
esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
|
||||
esp_ldo_channel_config_t ldo_mipi_phy_config = {
|
||||
.chan_id = mipi_cfg->ldo_chan,
|
||||
.voltage_mv = mipi_cfg->ldo_voltage,
|
||||
};
|
||||
return esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy);
|
||||
}
|
||||
|
||||
static int _init_mipi_lcd(lcd_cfg_t *cfg)
|
||||
{
|
||||
int ret = 0;
|
||||
lcd_mipi_cfg_t *mipi_cfg = &cfg->mipi_cfg;
|
||||
power_on_dsi(mipi_cfg);
|
||||
// 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 = mipi_cfg->lane_num,
|
||||
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
|
||||
.lane_bit_rate_mbps = mipi_cfg->lane_bitrate,
|
||||
};
|
||||
ret = esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus);
|
||||
RETURN_ON_ERR(ret);
|
||||
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
|
||||
esp_lcd_panel_io_handle_t mipi_dbi_io;
|
||||
// we use DBI interface to send LCD commands and parameters
|
||||
esp_lcd_dbi_io_config_t dbi_config = {
|
||||
.virtual_channel = 0,
|
||||
.lcd_cmd_bits = 8,
|
||||
.lcd_param_bits = 8,
|
||||
};
|
||||
ret = esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io);
|
||||
RETURN_ON_ERR(ret);
|
||||
esp_lcd_dpi_panel_config_t dpi_config = {
|
||||
.num_fbs = mipi_cfg->fb_num,
|
||||
.virtual_channel = 0,
|
||||
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
|
||||
.dpi_clock_freq_mhz = mipi_cfg->dpi_clk,
|
||||
.pixel_format = mipi_cfg->bit_depth == 24 ? LCD_COLOR_PIXEL_FORMAT_RGB888 : LCD_COLOR_PIXEL_FORMAT_RGB565,
|
||||
.video_timing = {
|
||||
.h_size = cfg->width,
|
||||
.v_size = cfg->height,
|
||||
.hsync_back_porch = mipi_cfg->dsi_hbp,
|
||||
.hsync_pulse_width = mipi_cfg->dsi_hsync,
|
||||
.hsync_front_porch = mipi_cfg->dsi_hfp,
|
||||
.vsync_back_porch = mipi_cfg->dsi_vbp,
|
||||
.vsync_pulse_width = mipi_cfg->dsi_vsync,
|
||||
.vsync_front_porch = mipi_cfg->dsi_vfp,
|
||||
},
|
||||
.flags.use_dma2d = true,
|
||||
};
|
||||
esp_lcd_panel_dev_config_t panel_config = {
|
||||
.bits_per_pixel = mipi_cfg->bit_depth,
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
|
||||
.reset_gpio_num = cfg->reset_pin,
|
||||
};
|
||||
if (cfg->width == 1024 && cfg->height == 600) {
|
||||
ESP_LOGI(TAG, "Install EK79007 LCD control panel");
|
||||
esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
|
||||
ek79007_vendor_config_t vendor_config = {
|
||||
.mipi_config = {
|
||||
.dsi_bus = mipi_dsi_bus,
|
||||
.dpi_config = &dpi_config,
|
||||
},
|
||||
};
|
||||
panel_config.vendor_config = &vendor_config;
|
||||
ret = esp_lcd_new_panel_ek79007(mipi_dbi_io, &panel_config, &panel_handle);
|
||||
RETURN_ON_ERR(ret);
|
||||
} else {
|
||||
ili9881c_vendor_config_t vendor_config = {
|
||||
.mipi_config = {
|
||||
.dsi_bus = mipi_dsi_bus,
|
||||
.dpi_config = &dpi_config,
|
||||
.lane_num = mipi_cfg->lane_num,
|
||||
},
|
||||
};
|
||||
panel_config.vendor_config = &vendor_config;
|
||||
ret = esp_lcd_new_panel_ili9881c(mipi_dbi_io, &panel_config, &panel_handle);
|
||||
}
|
||||
RETURN_ON_ERR(ret);
|
||||
esp_lcd_panel_reset(panel_handle);
|
||||
ESP_LOGI(TAG, "Install MIPI DSI LCD data panel");
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
|
||||
static int _init_mipi_lcd(lcd_cfg_t *cfg)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int _init_lcd(lcd_cfg_t *cfg)
|
||||
{
|
||||
int ret = 0;
|
||||
if (cfg->io_type != EXTENT_IO_TYPE_NONE) {
|
||||
ret = init_extend_io(cfg);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
// Config reset and ctrl gpio dir
|
||||
if (cfg->reset_pin >= 0) {
|
||||
set_pin_dir(cfg->reset_pin, true);
|
||||
}
|
||||
if (cfg->ctrl_pin >= 0) {
|
||||
set_pin_dir(cfg->ctrl_pin, true);
|
||||
}
|
||||
if (cfg->bus_type == LCD_BUS_TYPE_SPI) {
|
||||
if (cfg->spi_cfg.cs >= 0) {
|
||||
set_pin_dir(cfg->spi_cfg.cs, true);
|
||||
}
|
||||
}
|
||||
_lcd_rest(cfg);
|
||||
if (cfg->ctrl_pin >= 0) {
|
||||
set_pin_dir(cfg->ctrl_pin, true);
|
||||
}
|
||||
if (cfg->bus_type == LCD_BUS_TYPE_SPI) {
|
||||
ret = _init_spi_lcd(cfg);
|
||||
} else if (cfg->bus_type == LCD_BUS_TYPE_MIPI) {
|
||||
ret = _init_mipi_lcd(cfg);
|
||||
}
|
||||
if (panel_handle) {
|
||||
ret = esp_lcd_panel_init(panel_handle);
|
||||
RETURN_ON_ERR(ret);
|
||||
if (cfg->color_inv) {
|
||||
ret = esp_lcd_panel_invert_color(panel_handle, cfg->color_inv);
|
||||
}
|
||||
// ret = esp_lcd_panel_set_gap(panel_handle, 0, 0);
|
||||
if (cfg->swap_xy) {
|
||||
ret = esp_lcd_panel_swap_xy(panel_handle, cfg->swap_xy);
|
||||
}
|
||||
if (cfg->mirror_x || cfg->mirror_y) {
|
||||
ret = esp_lcd_panel_mirror(panel_handle, cfg->mirror_x, cfg->mirror_y);
|
||||
}
|
||||
ret = esp_lcd_panel_disp_on_off(panel_handle, true);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int board_lcd_init(void)
|
||||
{
|
||||
lcd_cfg_t cfg = { 0 };
|
||||
int ret = get_lcd_cfg(&cfg);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
return _init_lcd(&cfg);
|
||||
}
|
||||
|
||||
void *board_get_lcd_handle(void)
|
||||
{
|
||||
if (panel_handle) {
|
||||
return panel_handle;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
Reference in New Issue
Block a user