- merge with original master from jorgen

- minimize RAM usage of all components
- use both IRAM and DRAM in player component so we can buffer up to 1s on modules without SPI RAM
- support fragemented pcm chunks so we can use all available RAM if there isn't a big enough block available but still enough HEAP
- reinclude all components from jorgen's master branch
- add custom i2s driver to get a precise timing of initial sync
- change wrong usage of esp_timer for latency measurement of snapcast protocol
- add player component
This commit is contained in:
Carlos
2021-08-19 21:57:16 +02:00
Unverified
parent 2bd066ca09
commit 15b4baba28
340 changed files with 35616 additions and 3624 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,3 @@
build
libopus
components/lopus
components/opus

3
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "components/opus/opus"]
path = components/opus/opus
url = https://github.com/xiph/opus
[submodule "components/esp-dsp"]
path = components/esp-dsp
url = https://github.com/espressif/esp-dsp

168
README.md
View File

@@ -1,100 +1,162 @@
# Snapcast client for ESP32
# Snapcast client for ESP32
### Synchronous Multiroom audio streaming client for [Snapcast](https://github.com/badaix/snapcast) ported to ESP32
## Feature list
- Opus decoding currently supported
- Wifi connection hardcoded in app
- Auto connect to snapcast server on network
- Buffers up to 150 ms on Wroom modules
- Buffers more then enough on Wrover modules
- Multiroom sync delay controlled from Snapcast server
## Feature list
- Opus and PCM decoding currently supported
- Wifi setup from menuconfig
- Auto connect to snapcast server on network
- Buffers up to 150 ms on Wroom modules
- Buffers more then enough on Wrover modules
- Multiroom sync delay controlled from Snapcast server 400ms - 2000ms
## Description
I have continued the work from @badaix and @bridadan towards a ESP32 Snapcast client. Currently it support basic features like multirum sync, network controlled volume and mute. For now it only support Opus 16bit/48Khz audio streams and the synchornization part is still being worked on.
## Description
I have continued the work from @badaix and @bridadan towards a ESP32 Snapcast
client. Currently it support basic features like multirum sync, network
controlled volume and mute. For now it only support Opus and PCM 16bit/48Khz
audio streams and the synchornization part is still being worked on.
Please check out the task list and feel free to fill in.
I have used the Infineon MA12070P Multi level Class D combined coded/amp due to its superior power effecienty on a high supply rail. It allows battery power system with good playback time at normal listen level and stil have the power to start the party.
I have used the Infineon MA12070P Multi level Class D combined coded/amp due to
its superior power effecienty on a high supply rail. It allows battery power
system with good playback time at normal listen level and still have the power
to start the party.
### Codebase
The codebase is split into components and build on vanilla ESP-IDF. I stil have some refactoring on the todo list as the concept has started to settle and allow for new features can be added in a stuctured manner. In the code you will find parts that are only partly related features and still not on the task list.
Components
- MerusAudio : Low level communication interface MA12070P
- opus : Opus audio coder/decoder full submodule
- rtprx : Alternative RTP audio client UDP low latency also opus based
- lightsnapcast : Port of @bridadan scapcast packages decode library
- libbuffer : Generic buffer abstraction
- esp-dsp : Port of ESP-DSP library - stripped version - submodule considered
- dsp_processor : Audio Processor and I2S low level interface including sync buffer
The codebase is split into components and build on vanilla ESP-IDF. I still
have some refactoring on the todo list as the concept has started to settle and
allow for new features can be added in a structured manner. In the code you
will find parts that are only partly related features and still not on the task
list.
### Hardware
- ESP pinout MA12070P
Components
- MerusAudio : Low level communication interface MA12070P
- opus : Opus audio coder/decoder full submodule
- rtprx : Alternative RTP audio client UDP low latency also opus based
- lightsnapcast : Port of @bridadan scapcast packages decode library
- libbuffer : Generic buffer abstraction
- esp-dsp : Submodule to the ESP-ADF done by David Douard
- dsp_processor : Audio Processor and I2S low level interface including sync
buffer
The snapclient functionanlity are implemented in a task included in main - but
will be refactored to a component in near future.
Sync concept has been changed start 2021 on this implementation and differ a
bit from the way original snap clints handle this.
The snapclient frontend handles communiction with the server and after
successfully hello hand shake it dispatches packages from the server.
- CODEC_HEADER : Setup client audio codec (FLAC, OPUS, OGG or PCM) bitrate, n
channels and bits pr sample
- WIRE_CHUNK : Coded audio data
- SERVER_SETTING : Channel volume, mute state, playback delay etc
- TIME : Ping pong time keeping packages to keep track of time dif from server
to client
Each wire_chunk of audio data comes with a timestamp and client has agreed play
that sample playback-delay after the timestamp. One way to handle that is to
pass on audio data to a buffer with a length that compensate for for
playback-delay, network jitter and DAC to speaker.
In this implementation I have separated the sync task to a backend on the other
end of a large ring buffer. Now the front end just need to pass on the audio
data to the ring buffer with the server timestamp and chunk size. The backen
read timestamps and waits until the audio chunk has the correct playback-delay
to be written to the DAC amplifer speaker pipeline. When the backend pipeline
is in sync, any offset get rolled in by micro tuning the APLL on the ESP. No
sample manipulation needed.
### Hardware
- ESP pinout MA12070P
------------------------------------------------------
-> I2S_BCK Audio Clock 3.072 MHz
-> I2S_WS Frame Word Select or L/R
-> GND Ground
-> I2S_DI Audio data 24bits LSB first
-> I2S_BCK Audio Clock 3.072 MHz
-> I2S_WS Frame Word Select or L/R
-> GND Ground
-> I2S_DI Audio data 24bits LSB first
-> MCLK Master clk connect to I2S_BCK
-> I2C_SCL I2C clock
-> I2C_SDA I2C Data
-> GND Ground
-> NENABLE Amplifier Enable active low
-> NENABLE Amplifier Enable active low
-> NMUTE Amplifier Mute active low
## Build
Clone this repo:
git clone https://github.com/jorgenkraghjakobsen/snapclint
## Build
Update third party code:
Clone this repo:
git clone https://github.com/jorgenkraghjakobsen/snapclint
Update third party code (opus and esp-dsp):
git submodule update --init
Configure to match your setup:
Configure to match your setup
- Wifi network name and password
- Audio coded setup
idf.py menuconfig
Build, compile and flash:
idf.py build flash monitor
idf.py build flash monitor
## Test
Setup a snapcast server on your network
## Test
Setup a snapcast server on your network
On a linux box:
On a linux box:
Clone snapcast build and start the server
./snapserver
./snapserver
Pipe some audio to the snapcast server fifo
Pipe some audio to the snapcast server fifo
mplayer http://ice1.somafm.com/secretagent-128-aac -ao pcm:file=/tmp/snapfifo -af format=s16LE -srate 48000
Test the server config on other knowen platform
Test the server config on other knowen platform
./snapclient from the snapcast repo
Android : snapclient from the app play store
Android : snapclient from the app play store
## Contribute
You are very welcome to help and provide [Pull
Requests](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests)
to the project.
We strongly suggest you activate [pre-commit](https://pre-commit.com) hooks in
this git repository before starting to hack and make commits.
Assuming you have `pre-commit` installed on your machine (using `pip install
pre-commit` or, on a debian-like system, `sudo apt install pre-commit`), type:
```
:~/snapclient$ pre-commit install
pre-commit installed at .git/hooks/pre-commit
```
Then on every `git commit`, a few sanity/formatting checks will be performed.
## Task list
- [ok] Fix to alinge with above
- [ok] Fix to alinge with above
* kconfig
* add codec description
- [ ] Integrate ESP wifi provision
* add codec description
- [ ] Integrate ESP wifi provision
- [ok] Find and connect to Avahi broadcasted Snapcast server name
- [ ] Add a client command interface layer like volume/mute control
- [ ] Build a ESP-ADF branch
- [ ] Add a client command interface layer like volume/mute control
- [ ] Build a ESP-ADF branch
## Minor task
- [ ] Propergate mute/unute from server message to DSP backend mute control.
- [ ] soft mute - play sample in buffer with decresing volume
- [ok] hard mute - pass on zero at the DSP hackend
- [ ] Startup: do not start parsing on samples to codec before sample ring buffer hits requested buffer size.
## Minor task
- [ ] Propergate mute/unute from server message to DSP backend mute control.
- [ ] soft mute - play sample in buffer with decreasing volume
- [ok] hard mute - pass on zero at the DSP hackend
- [ ] Startup: do not start parsing on samples to codec before sample ring buffer hits requested buffer size.
- [ok] Start from empty buffer

View File

@@ -0,0 +1,42 @@
# Edit following two lines to set component requirements (see docs)
set(COMPONENT_REQUIRES)
set(COMPONENT_PRIV_REQUIRES audio_hal esp_dispatcher esp_peripherals display_service)
if(CONFIG_AUDIO_BOARD_CUSTOM)
message(STATUS "Current board name is " CONFIG_AUDIO_BOARD_CUSTOM)
set(COMPONENT_ADD_INCLUDEDIRS ./generic_board/include)
set(COMPONENT_SRCS
./generic_board/board.c
./generic_board/board_pins_config.c
)
if(CONFIG_DAC_PCM51XX)
message(STATUS "Selected DAC is " CONFIG_DAC_PCM15XX)
list(APPEND COMPONENT_ADD_INCLUDEDIRS ./pcm51xx/include)
list(APPEND COMPONENT_SRCS ./pcm51xx/pcm51xx.c)
endif()
if(CONFIG_DAC_MA120X0)
message(STATUS "Selected DAC is " CONFIG_DAC_MA120X0)
list(APPEND COMPONENT_ADD_INCLUDEDIRS ./ma120x0/include)
list(APPEND COMPONENT_SRCS ./ma120x0/MerusAudio.c)
endif()
if(CONFIG_DAC_MA120)
message(STATUS "Selected DAC is " CONFIG_DAC_MA120)
list(APPEND COMPONENT_ADD_INCLUDEDIRS ./ma120/include)
list(APPEND COMPONENT_SRCS ./ma120/ma120.c)
endif()
endif()
register_component()
IF (IDF_VER MATCHES "v4.")
idf_component_get_property(audio_board_lib audio_board COMPONENT_LIB)
set_property(TARGET ${audio_board_lib} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${COMPONENT_LIB})
ELSEIF (IDF_VER MATCHES "v3.")
set_property(TARGET idf_component_audio_board APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:${COMPONENT_TARGET},INTERFACE_INCLUDE_DIRECTORIES>)
ENDIF (IDF_VER MATCHES "v4.")

View File

@@ -0,0 +1,137 @@
menu "Custom Audio Board"
depends on AUDIO_BOARD_CUSTOM
choice GENERIC_BOARD_DAC
prompt "DAC chip"
default DAC_MA120
help
Select a DAC connected to the generic ESP32 board
config DAC_PCM51XX
bool "TI PCM51XX/TAS57XX based DAC"
config DAC_MA120
bool "Infineon MA120 ClassD AMP"
config DAC_MA120X0
bool "Infineon MA120X0 ClassD AMP"
endchoice
menu "DAC I2C control interface"
config DAC_I2C_SDA
int "SDA pin"
default 21
help
I2C SDA pin of the DAC control interface
config DAC_I2C_SCL
int "SCL pin"
default 22
help
I2C SCL pin of the DAC control interface
config DAC_I2C_ADDR
hex "I2C address"
default 0x20
help
I2C Address of the DAC control interface
endmenu
menu "I2S master interface"
config MASTER_I2S_BCK_PIN
int "Master i2s bck"
default 23
help
Master audio interface bit clock.
config MASTER_I2S_LRCK_PIN
int "Master i2s lrck"
default 13
help
Master audio interface left/right sync clock.
config MASTER_I2S_DATAOUT_PIN
int "Master i2s data out"
default 14
help
Master audio interface data out.
endmenu
menu "I2S slave interface"
config SLAVE_I2S_BCK_PIN
int "Slave i2s bck"
default 26
help
Slave audio interface bit clock.
config SLAVE_I2S_LRCK_PIN
int "Slave i2s lrck"
default 12
help
Slave audio interface left/right sync clock.
config SLAVE_I2S_DATAOUT_PIN
int "Slave i2s data out"
default 5
help
Slave audio interface data out.
endmenu
menu "Merus MA120x0 interface Configuration"
depends on DAC_MA120X0
config MA120X0_NENABLE_PIN
int "Master enable/disable for ma120x0"
default 16
help
GPIO number to control enable/disable.
config MA120X0_NMUTE_PIN
int "Master mute/unmute for ma120x0"
default 2
help
GPIO number to controm mute/unmute.
config MERUS_NERR_PIN
int "NERR monitor pin"
default 21
help
GPIO number to monitor NERROR.
config MERUS_NCLIP_PIN
int "Clip indication pin"
default 22
help
GPIO number low if clip observed
endmenu
menu "Merus MA120 interface Configuration"
depends on DAC_MA120
config MA120_ENABLE_PIN
int "Master enable/disable for ma120x0"
default 16
help
GPIO number to control enable/disable.
config MA120_NMUTE_PIN
int "Master mute/unmute for ma120x0"
default 2
help
GPIO number to controm mute/unmute.
config MERUS_NERR_PIN
int "NERR monitor pin"
default 21
help
GPIO number to monitor NERROR.
config MERUS_NCLIP_PIN
int "Clip indication pin"
default 22
help
GPIO number low if clip observed
endmenu
endmenu

View File

@@ -0,0 +1,20 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
ifdef CONFIG_AUDIO_BOARD_CUSTOM
COMPONENT_ADD_INCLUDEDIRS += ./generic_board/include
COMPONENT_SRCDIRS += ./generic_board
ifdef CONFIG_DAC_PCM51XX
COMPONENT_ADD_INCLUDEDIRS += ./pcm51xx/include
COMPONENT_SRCDIRS += ./pcm51xx
endif
ifdef CONFIG_DAC_MA120X0
COMPONENT_ADD_INCLUDEDIRS += ./ma120x0/include
COMPONENT_SRCDIRS += ./ma120x0
endif
endif

View File

@@ -0,0 +1,112 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2020 <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 "board.h"
#include "audio_mem.h"
#include "esp_log.h"
#include "periph_adc_button.h"
#include "periph_sdcard.h"
#if CONFIG_DAC_PCM51XX
extern audio_hal_func_t AUDIO_CODEC_PCM51XX_DEFAULT_HANDLE;
#define AUDIO_CODEC_DEFAULT_HANDLE AUDIO_CODEC_PCM51XX_DEFAULT_HANDLE
#elif CONFIG_DAC_MA120X0
extern audio_hal_func_t AUDIO_CODEC_MA120X0_DEFAULT_HANDLE;
#define AUDIO_CODEC_DEFAULT_HANDLE AUDIO_CODEC_MA120X0_DEFAULT_HANDLE
#elif CONFIG_DAC_MA120
extern audio_hal_func_t AUDIO_CODEC_MA120_DEFAULT_HANDLE;
#define AUDIO_CODEC_DEFAULT_HANDLE AUDIO_CODEC_MA120_DEFAULT_HANDLE
#endif
static const char *TAG = "AUDIO_BOARD";
static audio_board_handle_t board_handle = 0;
audio_board_handle_t audio_board_init(void) {
if (board_handle) {
ESP_LOGW(TAG, "The board has already been initialized!");
return board_handle;
}
board_handle =
(audio_board_handle_t)audio_calloc(1, sizeof(struct audio_board_handle));
AUDIO_MEM_CHECK(TAG, board_handle, return NULL);
board_handle->audio_hal = audio_board_codec_init();
ESP_LOGI(TAG,"board-handle done") ;
return board_handle;
}
audio_hal_handle_t audio_board_codec_init(void) {
ESP_LOGI("HAL", "INIT" );
audio_hal_codec_config_t audio_codec_cfg = AUDIO_CODEC_DEFAULT_CONFIG();
audio_hal_handle_t codec_hal =
audio_hal_init(&audio_codec_cfg, &AUDIO_CODEC_DEFAULT_HANDLE);
ESP_LOGI("HAL", "codec_hal done" );
AUDIO_NULL_CHECK(TAG, codec_hal, return NULL);
return codec_hal;
}
esp_err_t audio_board_key_init(esp_periph_set_handle_t set) {
esp_err_t ret = ESP_OK;
periph_adc_button_cfg_t adc_btn_cfg = PERIPH_ADC_BUTTON_DEFAULT_CONFIG();
adc_arr_t adc_btn_tag = ADC_DEFAULT_ARR();
adc_btn_tag.adc_ch = ADC1_CHANNEL_0; // GPIO36
adc_btn_tag.total_steps = 4;
int btn_array[5] = {200, 1355, 1820, 2280, 2930};
adc_btn_tag.adc_level_step = btn_array;
adc_btn_cfg.arr = &adc_btn_tag;
adc_btn_cfg.arr_size = 1;
esp_periph_handle_t adc_btn_handle = periph_adc_button_init(&adc_btn_cfg);
AUDIO_NULL_CHECK(TAG, adc_btn_handle, return ESP_ERR_ADF_MEMORY_LACK);
ret = esp_periph_start(set, adc_btn_handle);
return ret;
}
esp_err_t audio_board_sdcard_init(esp_periph_set_handle_t set,
periph_sdcard_mode_t mode) {
periph_sdcard_cfg_t sdcard_cfg = {
.root = "/sdcard",
.card_detect_pin = get_sdcard_intr_gpio(), // GPIO_NUM_34
};
esp_periph_handle_t sdcard_handle = periph_sdcard_init(&sdcard_cfg);
esp_err_t ret = esp_periph_start(set, sdcard_handle);
while (!periph_sdcard_is_mounted(sdcard_handle)) {
vTaskDelay(500 / portTICK_PERIOD_MS);
}
return ret;
}
audio_board_handle_t audio_board_get_handle(void) { return board_handle; }
esp_err_t audio_board_deinit(audio_board_handle_t audio_board) {
esp_err_t ret = ESP_OK;
ret |= audio_hal_deinit(audio_board->audio_hal);
free(audio_board);
board_handle = NULL;
return ret;
}

View File

@@ -0,0 +1,126 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2020 <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 "audio_error.h"
#include "audio_mem.h"
#include "board.h"
#include "driver/gpio.h"
#include "esp_log.h"
static const char *TAG = "GENERIC_BOARD";
esp_err_t get_i2c_pins(i2c_port_t port, i2c_config_t *i2c_config) {
AUDIO_NULL_CHECK(TAG, i2c_config, return ESP_FAIL);
if (port == I2C_NUM_0 || port == I2C_NUM_1) {
i2c_config->sda_io_num = CONFIG_DAC_I2C_SDA;
i2c_config->scl_io_num = CONFIG_DAC_I2C_SCL;
} else {
i2c_config->sda_io_num = -1;
i2c_config->scl_io_num = -1;
ESP_LOGE(TAG, "i2c port %d is not supported", port);
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t get_i2s_pins(i2s_port_t port, i2s_pin_config_t *i2s_config) {
AUDIO_NULL_CHECK(TAG, i2s_config, return ESP_FAIL);
if (port == I2S_NUM_0) {
i2s_config->bck_io_num = CONFIG_MASTER_I2S_BCK_PIN;
i2s_config->ws_io_num = CONFIG_MASTER_I2S_LRCK_PIN;
i2s_config->data_out_num = CONFIG_MASTER_I2S_DATAOUT_PIN;
i2s_config->data_in_num = -1;
} else if (port == I2S_NUM_1) {
i2s_config->bck_io_num = CONFIG_SLAVE_I2S_BCK_PIN;
i2s_config->ws_io_num = CONFIG_SLAVE_I2S_LRCK_PIN;
i2s_config->data_out_num = CONFIG_SLAVE_I2S_DATAOUT_PIN;
i2s_config->data_in_num = -1;
} else {
memset(i2s_config, -1, sizeof(i2s_pin_config_t));
ESP_LOGE(TAG, "i2s port %d is not supported", port);
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t i2s_mclk_gpio_select(i2s_port_t i2s_num, gpio_num_t gpio_num) {
if (i2s_num >= I2S_NUM_MAX) {
ESP_LOGE(TAG, "Does not support i2s number(%d)", i2s_num);
return ESP_ERR_INVALID_ARG;
}
if (gpio_num != GPIO_NUM_0 && gpio_num != GPIO_NUM_1 &&
gpio_num != GPIO_NUM_3) {
ESP_LOGE(TAG, "Only support GPIO0/GPIO1/GPIO3, gpio_num:%d", gpio_num);
return ESP_ERR_INVALID_ARG;
}
ESP_LOGI(TAG, "I2S%d, MCLK output by GPIO%d", i2s_num, gpio_num);
if (i2s_num == I2S_NUM_0) {
if (gpio_num == GPIO_NUM_0) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
WRITE_PERI_REG(PIN_CTRL, 0xFFF0);
} else if (gpio_num == GPIO_NUM_1) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_CLK_OUT3);
WRITE_PERI_REG(PIN_CTRL, 0xF0F0);
} else {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_CLK_OUT2);
WRITE_PERI_REG(PIN_CTRL, 0xFF00);
}
}
return ESP_OK;
}
esp_err_t get_spi_pins(
spi_bus_config_t *spi_config,
spi_device_interface_config_t *spi_device_interface_config) {
AUDIO_NULL_CHECK(TAG, spi_config, return ESP_FAIL);
AUDIO_NULL_CHECK(TAG, spi_device_interface_config, return ESP_FAIL);
spi_config->mosi_io_num = -1;
spi_config->miso_io_num = -1;
spi_config->sclk_io_num = -1;
spi_config->quadwp_io_num = -1;
spi_config->quadhd_io_num = -1;
spi_device_interface_config->spics_io_num = -1;
ESP_LOGW(TAG, "SPI interface is not supported");
return ESP_OK;
}
// sdcard
int8_t get_sdcard_intr_gpio(void) { return SDCARD_INTR_GPIO; }
int8_t get_sdcard_open_file_num_max(void) { return SDCARD_OPEN_FILE_NUM_MAX; }
int8_t get_input_volup_id(void) { return BUTTON_VOLUP_ID; }
int8_t get_input_voldown_id(void) { return BUTTON_VOLDOWN_ID; }
int8_t get_pa_enable_gpio(void) { return PA_ENABLE_GPIO; }

View File

@@ -0,0 +1,115 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2020 <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 _AUDIO_BOARD_H_
#define _AUDIO_BOARD_H_
#include "audio_hal.h"
#include "board_def.h"
#include "board_pins_config.h"
#include "esp_peripherals.h"
#include "periph_sdcard.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Audio board handle
*/
struct audio_board_handle {
audio_hal_handle_t audio_hal; /*!< pa hardware abstract layer handle */
audio_hal_handle_t adc_hal; /*!< adc hardware abstract layer handle */
};
typedef struct audio_board_handle *audio_board_handle_t;
/**
* @brief Initialize audio board
*
* @return The audio board handle
*/
audio_board_handle_t audio_board_init(void);
/**
* @brief Initialize codec
*
* @return The audio hal handle
*/
audio_hal_handle_t audio_board_codec_init(void);
/**
* @brief Initialize adc
*
* @return The adc hal handle
*/
audio_hal_handle_t audio_board_adc_init(void);
/**
* @brief Initialize key peripheral
*
* @param set The handle of esp_periph_set_handle_t
*
* @return
* - ESP_OK, success
* - Others, fail
*/
esp_err_t audio_board_key_init(esp_periph_set_handle_t set);
/**
* @brief Initialize sdcard peripheral
*
* @param set The handle of esp_periph_set_handle_t
*
* @return
* - ESP_OK, success
* - Others, fail
*/
esp_err_t audio_board_sdcard_init(esp_periph_set_handle_t set,
periph_sdcard_mode_t mode);
/**
* @brief Query audio_board_handle
*
* @return The audio board handle
*/
audio_board_handle_t audio_board_get_handle(void);
/**
* @brief Uninitialize the audio board
*
* @param audio_board The handle of audio board
*
* @return 0 success,
* others fail
*/
esp_err_t audio_board_deinit(audio_board_handle_t audio_board);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,82 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2020 <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 _AUDIO_BOARD_DEFINITION_H_
#define _AUDIO_BOARD_DEFINITION_H_
#define BUTTON_VOLUP_ID 0
#define BUTTON_VOLDOWN_ID 1
#define BUTTON_MUTE_ID 2
#define BUTTON_SET_ID 3
#define PA_ENABLE_GPIO GPIO_NUM_12
#define ADC_DETECT_GPIO GPIO_NUM_36
#define BATTERY_DETECT_GPIO GPIO_NUM_37
#define SDCARD_OPEN_FILE_NUM_MAX 5
#define SDCARD_INTR_GPIO GPIO_NUM_34
#define AUDIO_CODEC_DEFAULT_CONFIG() \
{ \
.adc_input = AUDIO_HAL_ADC_INPUT_LINE1, \
.dac_output = AUDIO_HAL_DAC_OUTPUT_ALL, \
.codec_mode = AUDIO_HAL_CODEC_MODE_BOTH, \
.i2s_iface = \
{ \
.mode = AUDIO_HAL_MODE_SLAVE, \
.fmt = AUDIO_HAL_I2S_NORMAL, \
.samples = AUDIO_HAL_48K_SAMPLES, \
.bits = AUDIO_HAL_BIT_LENGTH_16BITS, \
}, \
};
#define INPUT_KEY_NUM 4
#define INPUT_KEY_DEFAULT_INFO() \
{ \
{ \
.type = PERIPH_ID_ADC_BTN, \
.user_id = INPUT_KEY_USER_ID_VOLUP, \
.act_id = BUTTON_VOLUP_ID, \
}, \
{ \
.type = PERIPH_ID_ADC_BTN, \
.user_id = INPUT_KEY_USER_ID_VOLDOWN, \
.act_id = BUTTON_VOLDOWN_ID, \
}, \
{ \
.type = PERIPH_ID_ADC_BTN, \
.user_id = INPUT_KEY_USER_ID_MUTE, \
.act_id = BUTTON_MUTE_ID, \
}, \
{ \
.type = PERIPH_ID_ADC_BTN, \
.user_id = INPUT_KEY_USER_ID_SET, \
.act_id = BUTTON_SET_ID, \
}, \
}
#endif

View File

@@ -0,0 +1,31 @@
#ifndef _MA120_H_
#define _MA120_H_
#include "esp_system.h"
#include "board.h"
esp_err_t ma120_init(audio_hal_codec_config_t *codec_cfg);
esp_err_t ma120_deinit(void);
esp_err_t ma120_set_volume(int vol);
esp_err_t ma120_get_volume(int *value);
esp_err_t ma120_set_mute(bool enable);
esp_err_t ma120_get_mute(bool *enabled);
esp_err_t ma120_ctrl(audio_hal_codec_mode_t, audio_hal_ctrl_t);
esp_err_t ma120_config_iface(audio_hal_codec_mode_t , audio_hal_codec_i2s_iface_t *);
void setup_ma120(void);
void ma120_read_error(uint8_t i2c_addr);
void ma120_setup_audio(uint8_t i2c_addr);
void i2c_master_init(void);
esp_err_t ma_write_byte(uint8_t i2c_addr, uint8_t prot, uint16_t address,
uint8_t value);
esp_err_t ma_write(uint8_t i2c_addr, uint8_t prot, uint16_t address,
uint8_t *wbuf, uint8_t n);
uint8_t ma_read_byte(uint8_t i2c_addr, uint8_t prot, uint16_t address);
esp_err_t ma_read(uint8_t i2c_addr, uint8_t prot, uint16_t address,
uint8_t *rbuf, uint8_t n);
#endif /* _MA120_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,364 @@
//
// MA120 ESP32 Driver
//
// Merus Audio - September 2018
// Written by Joergen Kragh Jakobsen, jkj@myrun.dk
//
// Register interface thrugh I2C for MA120
// Support a single amplifier/i2c address
//
//
#include <stdint.h>
#include <stdio.h>
#include "ma120.h"
#include "board.h"
#include "driver/i2c.h"
#include "esp_log.h"
static const char *TAG = "MA120";
#define MA_ENABLE_IO CONFIG_MA120_ENABLE_PIN
#define MA_NMUTE_IO CONFIG_MA120_NMUTE_PIN
#define MA_NERR_IO CONFIG_MERUS_NERR_PIN
#define MA_NCLIP_IO CONFIG_MERUS_NCLIP_PIN
static const char *I2C_TAG = "i2c";
#define I2C_CHECK(a, str, ret) \
if (!(a)) { \
ESP_LOGE(I2C_TAG, "%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
return (ret); \
}
#define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C port number for master dev */
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
#define MA120_ADDR \
CONFIG_DAC_I2C_ADDR /*!< slave address for MA120 amplifier \
*/
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */
audio_hal_func_t AUDIO_CODEC_MA120_DEFAULT_HANDLE = {
.audio_codec_initialize = ma120_init,
.audio_codec_deinitialize = ma120_deinit,
.audio_codec_ctrl = ma120_ctrl,
.audio_codec_config_iface = ma120_config_iface,
.audio_codec_set_mute = ma120_set_mute,
.audio_codec_set_volume = ma120_set_volume,
.audio_codec_get_volume = ma120_get_volume,
.audio_hal_lock = NULL,
.handle = NULL,
};
esp_err_t ma120_deinit(void) {
// TODO
return ESP_OK;
}
esp_err_t ma120_ctrl(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state) {
ESP_LOGI("MA120 Driver", "ctrl w. mode and ctrl_state");
return ESP_OK;
}
esp_err_t ma120_config_iface(audio_hal_codec_mode_t mode,
audio_hal_codec_i2s_iface_t *iface) {
ESP_LOGI("MA120 Driver", "config_iface w. mode and interface");
return ESP_OK;
}
esp_err_t ma120_set_volume(int vol) {
esp_err_t ret = ESP_OK;
uint8_t cmd[2];
cmd[0] = 128 - vol;
cmd[1] = cmd[0];
ma_write(MA120_ADDR, 2, 0x0003, cmd, 2);
return ret;
}
esp_err_t ma120_get_volume(int *vol) {
esp_err_t ret = ESP_OK;
uint8_t rxbuf;
rxbuf = ma_read_byte(MA120_ADDR, 2, 3);
*vol = 128 - rxbuf;
return ret;
}
esp_err_t ma120_set_mute(bool enable) {
esp_err_t ret = ESP_OK;
uint8_t nmute;
nmute = (enable) ? 0 : 1;
gpio_set_level(MA_NMUTE_IO, nmute);
return ret;
}
esp_err_t ma120_get_mute(bool *enabled) {
esp_err_t ret = ESP_OK;
*enabled = false; // TODO read from register
return ret;
}
esp_err_t ma120_init(audio_hal_codec_config_t *codec_cfg) {
esp_err_t ret = ESP_OK;
setup_ma120();
return ret;
}
void setup_ma120(void){
gpio_config_t io_conf;
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << MA_ENABLE_IO | 1ULL << MA_NMUTE_IO);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
printf("setup output %d %d \n", MA_ENABLE_IO, MA_NMUTE_IO);
gpio_config(&io_conf);
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = (1ULL << MA_NCLIP_IO | 1ULL << MA_NERR_IO);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
printf("Setup input NCLIP:%d NERR:%d \n", MA_NCLIP_IO, MA_NERR_IO);
gpio_config(&io_conf);
gpio_set_level(MA_NMUTE_IO, 0);
gpio_set_level(MA_ENABLE_IO, 0);
i2c_master_init();
gpio_set_level(MA_ENABLE_IO, 1);
uint8_t res = ma_write_byte(MA120_ADDR, 2, 0x060c, 0);
res = ma_read_byte(MA120_ADDR, 2, 0x060c);
printf("Hardware version: 0x%02x\n", res);
printf("Scan I2C bus: ");
for (uint8_t addr = 0x20; addr <= 0x23; addr++) {
res = ma_read_byte(addr, 2, 0);
printf(" 0x%02x => GEN2 ,", addr);
// printf("Scan i2c address 0x%02x read address 0 : 0x%02x \n", addr ,res);
}
printf("\n");
uint8_t rxbuf[32];
uint8_t otp[1024];
for (uint8_t i = 0; i < 16; i++) {
ma_read(MA120_ADDR, 2, 0x8000 + i * 32, rxbuf, 32);
// printf("%04x : ",0x8000+i*32 );
for (uint8_t j = 0; j < 32; j++) {
otp[i * 32 + j] = rxbuf[j];
}
}
for (uint16_t i = 0; i < 16 * 32; i++) {
if (i % 32 == 0) {
printf("\n0x%04x : ", 0x8000 + i);
}
printf("%02x ", otp[i]);
}
res = ma_read(MA120_ADDR, 2, 0x0000, rxbuf, 2);
printf("\nAddress 0 : 0x%02x\n", rxbuf[0]);
ma_write_byte(MA120_ADDR, 2, 0x0003, 0x50);
ma_write_byte(MA120_ADDR, 2, 0x0004, 0x50);
ma_write_byte(MA120_ADDR, 2, 0x0005, 0x02);
// ma_write_byte(MA120_ADDR,2,0x0246,0x00) ; //
ESP_LOGI(TAG, "ma120_setup done [ok]");
}
#define CRED "\x1b[31m"
#define CGRE "\x1b[32m"
#define CYEL "\x1b[33m"
#define CBLU "\x1b[34m"
#define CMAG "\x1b[35m"
#define CYAN "\x1b[36m"
#define CWHI "\x1b[0m"
const char *cherr_str[] = {"Clip_stuck", "DC", "VCF", "OCP_SEV", "OCP"};
const char *syserr1_str[] = {" X ", " X ", "DSP3 ", " DSP2",
" DSP1 ", "DSP0 ", "ERR", "PVT_low"};
const char *syserr0_str[] = {"OTW", "OTE", "PV_uv", "PV_low",
"OV_ov", " CLK ", "AUD", " TW "};
void ma120_read_error(uint8_t i2c_addr) { // 0x0118 error now ch0 [clip_stuck
// dc vcf_err ocp_severe ocp]
// 0x0119 error now ch1 [clip_stuck dc vcf_err ocp_severe ocp]
// 0x011a error now system [ AE CE ... ]
// 0x011b error now system [DSP3 DSP2 DSP1 DSP0 OC OE]
// 0x011c error acc ch0 [clip_stuck dc vcf_err ocp_severe ocp]
// 0x011d error acc ch1 [clip_stuck dc vcf_err ocp_severe ocp]
// 0x011e error acc system [7..0]
// 0x011f error acc system [13..8]
uint8_t errbuf[10] = {0};
ma_read(i2c_addr, 2, 0x0118, errbuf, 8);
// Error flag now : RED
// Error flag acc : WHITE
// No flag set : GREEN
for (int i = 0; i <= 7; i++) { // Error now
printf(" %s%s ",
((errbuf[3] & (1 << i)) == (1 << i)) ? CRED
: ((errbuf[7] & (1 << i)) == (1 << i)) ? CWHI
: CGRE,
syserr1_str[i]);
}
printf(" [0x%02x 0x%02x]\n", errbuf[3], errbuf[7]);
for (int i = 0; i <= 7; i++) {
printf(" %s%s ",
((errbuf[2] & (1 << i)) == (1 << i)) ? CRED
: ((errbuf[6] & (1 << i)) == (1 << i)) ? CWHI
: CGRE,
syserr0_str[i]);
}
printf(" [0x%02x 0x%02x]\n", errbuf[2], errbuf[6]);
// printf("0x011b : 0x%02x %s", rxbuf[2], l1 );
// printf("\nError vectors :");
// for (int i = 0; i<8; i++)
//{ printf("%02x ", errbuf[i]);
// }
// printf("\n");
}
static i2c_config_t i2c_cfg = {
.mode = I2C_MODE_MASTER,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
void i2c_master_init() {
int i2c_master_port = I2C_MASTER_NUM;
get_i2c_pins(I2C_NUM_0, &i2c_cfg);
esp_err_t res = i2c_param_config(i2c_master_port, &i2c_cfg);
printf("Driver param setup : %d\n", res);
res = i2c_driver_install(i2c_master_port, i2c_cfg.mode,
I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE,
0);
printf("Driver installed : %d\n", res);
}
esp_err_t ma_write(uint8_t i2c_addr, uint8_t prot, uint16_t address,
uint8_t *wbuf, uint8_t n) {
bool ack = ACK_VAL;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, i2c_addr << 1 | WRITE_BIT, ACK_CHECK_EN);
if (prot == 2) {
i2c_master_write_byte(cmd, (uint8_t)((address & 0xff00) >> 8), ACK_VAL);
i2c_master_write_byte(cmd, (uint8_t)(address & 0x00ff), ACK_VAL);
} else {
i2c_master_write_byte(cmd, (uint8_t)address, ACK_VAL);
}
for (int i = 0; i < n; i++) {
if (i == n - 1) ack = NACK_VAL;
i2c_master_write_byte(cmd, wbuf[i], ack);
}
i2c_master_stop(cmd);
int ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_FAIL) {
return ret;
}
return ESP_OK;
}
esp_err_t ma_write_byte(uint8_t i2c_addr, uint8_t prot, uint16_t address,
uint8_t value) {
printf("%04x %02x\n", address, value);
esp_err_t ret = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | WRITE_BIT, ACK_CHECK_EN);
if (prot == 2) {
i2c_master_write_byte(cmd, (uint8_t)((address & 0xff00) >> 8), ACK_VAL);
i2c_master_write_byte(cmd, (uint8_t)(address & 0x00ff), ACK_VAL);
} else {
i2c_master_write_byte(cmd, (uint8_t)address, ACK_VAL);
}
i2c_master_write_byte(cmd, value, ACK_VAL);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_FAIL) {
printf("ESP_I2C_WRITE ERROR : %d\n", ret);
return ret;
}
return ESP_OK;
}
esp_err_t ma_read(uint8_t i2c_addr, uint8_t prot, uint16_t address,
uint8_t *rbuf, uint8_t n) {
esp_err_t ret;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
if (cmd == NULL) {
printf("ERROR handle null\n");
}
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | WRITE_BIT, ACK_CHECK_EN);
if (prot == 2) {
i2c_master_write_byte(cmd, (uint8_t)((address & 0xff00) >> 8), ACK_VAL);
i2c_master_write_byte(cmd, (uint8_t)(address & 0x00ff), ACK_VAL);
} else {
i2c_master_write_byte(cmd, (uint8_t)address, ACK_VAL);
}
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | READ_BIT, ACK_CHECK_EN);
// if (n == 1 )
i2c_master_read(cmd, rbuf, n - 1, ACK_VAL);
// for (uint8_t i = 0;i<n;i++)
// { i2c_master_read_byte(cmd, rbuf++, ACK_VAL); }
i2c_master_read_byte(cmd, rbuf + n - 1, NACK_VAL);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 100 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_FAIL) {
printf("i2c Error read - readback\n");
return ESP_FAIL;
}
return ret;
}
uint8_t ma_read_byte(uint8_t i2c_addr, uint8_t prot, uint16_t address) {
uint8_t value = 0;
esp_err_t ret;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd); // Send i2c start on bus
i2c_master_write_byte(cmd, (i2c_addr << 1) | WRITE_BIT, ACK_CHECK_EN);
if (prot == 2) {
i2c_master_write_byte(cmd, (uint8_t)((address & 0xff00) >> 8), ACK_VAL);
i2c_master_write_byte(cmd, (uint8_t)(address & 0x00ff), ACK_VAL);
} else {
i2c_master_write_byte(cmd, (uint8_t)address, ACK_VAL);
}
i2c_master_start(cmd); // Repeated start
i2c_master_write_byte(cmd, (i2c_addr << 1) | READ_BIT, ACK_CHECK_EN);
i2c_master_read_byte(cmd, &value, NACK_VAL);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_FAIL) {
printf("i2c Error read - readback\n");
return ESP_FAIL;
}
return value;
}

View File

@@ -0,0 +1,365 @@
//
// MA120x0P ESP32 Driver
//
// Merus Audio - September 2018
// Written by Joergen Kragh Jakobsen, jkj@myrun.dk
//
// Register interface thrugh I2C for MA12070P and MA12040P
// Support a single amplifier/i2c address
//
//
#include "MerusAudio.h"
#include <stdint.h>
#include <stdio.h>
#include "driver/i2c.h"
#include "esp_log.h"
#include "ma120x0.h"
//#include "ma120_rev1_all.h"
static const char *TAG = "MA120X0";
#define MA_NENABLE_IO CONFIG_MA120X0_NENABLE_PIN
#define MA_ENABLE_IO CONFIG_MA120X0_ENABLE_PIN
#define MA_NMUTE_IO CONFIG_MA120X0_NMUTE_PIN
#define MA_NERR_IO CONFIG_MA120X0_NERR_PIN
#define MA_NCLIP_IO CONFIG_MA120X0_NCLIP_PIN
static const char *I2C_TAG = "i2c";
#define I2C_CHECK(a, str, ret) \
if (!(a)) { \
ESP_LOGE(I2C_TAG, "%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
return (ret); \
}
#define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C port number for master dev */
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
#define MA120X0_ADDR \
CONFIG_DAC_I2C_ADDR /*!< slave address for MA120X0 amplifier */
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */
static i2c_config_t i2c_cfg;
audio_hal_func_t AUDIO_CODEC_MA120X0_DEFAULT_HANDLE = {
.audio_codec_initialize = ma120x0_init,
.audio_codec_deinitialize = ma120x0_deinit,
.audio_codec_ctrl = ma120x0_ctrl,
.audio_codec_config_iface = ma120x0_config_iface,
.audio_codec_set_mute = ma120x0_set_mute,
.audio_codec_set_volume = ma120x0_set_volume,
.audio_codec_get_volume = ma120x0_get_volume,
.audio_hal_lock = NULL,
.handle = NULL,
};
esp_err_t ma120x0_deinit(void) {
// TODO
return ESP_OK;
}
esp_err_t ma120x0_ctrl(audio_hal_codec_mode_t mode,
audio_hal_ctrl_t ctrl_state) {
// TODO
return ESP_OK;
}
esp_err_t ma120x0_config_iface(audio_hal_codec_mode_t mode,
audio_hal_codec_i2s_iface_t *iface) {
// TODO
return ESP_OK;
}
esp_err_t ma120x0_set_volume(int vol) {
esp_err_t ret = ESP_OK;
uint8_t cmd;
cmd = 128 - vol;
ma_write_byte(0x20, 1, 64, cmd);
return ret;
}
esp_err_t ma120x0_get_volume(int *vol) {
esp_err_t ret = ESP_OK;
uint8_t rxbuf;
rxbuf = ma_read_byte(0x20, 1, 64, rxbuf);
*vol = 128 - rxbuf;
return ret;
}
esp_err_t ma120x0_set_mute(bool enable) {
esp_err_t ret = ESP_OK;
uint8_t nmute = (enable) ? 0 : 1 gpio_set_level(MA_NMUTE_IO, nmute);
return ret;
}
esp_err_t ma120x0_get_mute(bool *enabled) {
esp_err_t ret = ESP_OK;
*enabled = false; // TODO read from register
return ret;
}
esp_err_t ma120x0_init(audio_hal_codec_config_t *codec_cfg) {
esp_err_t ret = ESP_OK;
gpio_config_t io_conf;
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << MA_ENABLE_IO | 1ULL << MA_NMUTE_IO);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
printf("setup output %d %d \n", MA_ENABLE_IO, MA_NMUTE_IO);
gpio_config(&io_conf);
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = (1ULL << MA_NCLIP_IO | 1ULL << MA_NERR_IO);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
printf("setup input %d %d \n", MA_NCLIP_IO, MA_NERR_IO);
gpio_config(&io_conf);
gpio_set_level(MA_NMUTE_IO, 0);
gpio_set_level(MA_ENABLE_IO, 0);
// required?
// gpio_set_drive_capability(I2C_MASTER_SCL_IO,2);
// gpio_set_drive_capability(I2C_MASTER_SDA_IO,2);
i2c_master_init();
gpio_set_level(MA_ENABLE_IO, 1);
uint8_t res = ma_write_byte(0x20, 2, 1544, 0);
res = ma_read_byte(0x20, 2, 1544);
printf("Hardware version: 0x%02x\n", res);
printf("Scan I2C bus: ");
for (uint8_t addr = 0x20; addr <= 0x23; addr++) {
res = ma_read_byte(addr, 2, 0);
printf(" 0x%02x => GEN2 ,", addr);
// printf("Scan i2c address 0x%02x read address 0 : 0x%02x \n", addr ,res);
}
printf("\n");
uint8_t rxbuf[32];
uint8_t otp[1024];
for (uint8_t i = 0; i < 16; i++) {
ma_read(0x20, 2, 0x8000 + i * 32, rxbuf, 32);
// printf("%04x : ",0x8000+i*32 );
for (uint8_t j = 0; j < 32; j++) {
otp[i * 32 + j] = rxbuf[j];
}
}
for (uint16_t i = 0; i < 16 * 32; i++) {
if (i % 32 == 0) {
printf("\n0x%04x : ", 0x8000 + i);
}
printf("%02x ", otp[i]);
}
res = ma_write_byte(0x20, 2, 0x060c, 0);
res = ma_read(0x20, 2, 0x060c, rxbuf, 2);
printf("\nHardware version: 0x%02x\n", rxbuf[0]);
res = ma_read(0x20, 2, 0x0000, rxbuf, 2);
printf("\nAddress 0 : 0x%02x\n", rxbuf[0]);
ma_write_byte(0x20, 2, 0x0003, 0x50);
ma_write_byte(0x20, 2, 0x0004, 0x50);
ma_write_byte(0x20, 2, 0x0005, 0x02);
// ma_write_byte(0x20,2,0x0246,0x00) ; //
printf("\n");
return ret;
}
#define CRED "\x1b[31m"
#define CGRE "\x1b[32m"
#define CYEL "\x1b[33m"
#define CBLU "\x1b[34m"
#define CMAG "\x1b[35m"
#define CYAN "\x1b[36m"
#define CWHI "\x1b[0m"
const char *cherr_str[] = {"Clip_stuck", "DC", "VCF", "OCP_SEV", "OCP"};
const char *syserr1_str[] = {" X ", " X ", "DSP3 ", " DSP2",
" DSP1 ", "DSP0 ", "ERR", "PVT_low"};
const char *syserr0_str[] = {"OTW", "OTE", "PV_uv", "PV_low",
"OV_ov", " CLK ", "AUD", " TW "};
// static uint8_t terr = 0;
void ma120_read_error(uint8_t i2c_addr) { // 0x0118 error now ch0 [clip_stuck
// dc vcf_err ocp_severe ocp]
// 0x0119 error now ch1 [clip_stuck dc vcf_err ocp_severe ocp]
// 0x011a error now system [ AE CE ... ]
// 0x011b error now system [DSP3 DSP2 DSP1 DSP0 OC OE]
// 0x011c error acc ch0 [clip_stuck dc vcf_err ocp_severe ocp]
// 0x011d error acc ch1 [clip_stuck dc vcf_err ocp_severe ocp]
// 0x011e error acc system [7..0]
// 0x011f error acc system [13..8]
uint8_t errbuf[10] = {0};
uint8_t res = ma_read(i2c_addr, 2, 0x0118, errbuf, 8);
// Error flag now : RED
// Error flag acc : WHITE
// No flag set : GREEN
for (int i = 0; i <= 7; i++) { // Error now
printf(" %s%s ",
((errbuf[3] & (1 << i)) == (1 << i)) ? CRED
: ((errbuf[7] & (1 << i)) == (1 << i)) ? CWHI
: CGRE,
syserr1_str[i]);
}
printf(" [0x%02x 0x%02x]\n", errbuf[3], errbuf[7]);
for (int i = 0; i <= 7; i++) {
printf(" %s%s ",
((errbuf[2] & (1 << i)) == (1 << i)) ? CRED
: ((errbuf[6] & (1 << i)) == (1 << i)) ? CWHI
: CGRE,
syserr0_str[i]);
}
printf(" [0x%02x 0x%02x]\n", errbuf[2], errbuf[6]);
// printf("0x011b : 0x%02x %s", rxbuf[2], l1 );
// printf("\nError vectors :");
// for (int i = 0; i<8; i++)
//{ printf("%02x ", errbuf[i]);
// }
// printf("\n");
}
void i2c_master_init() {
int i2c_master_port = I2C_MASTER_NUM;
i2c_cfg = {
.mode = I2C_MODE_MASTER,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
get_i2c_pins(I2C_NUM_0, &i2c_cfg);
esp_err_t res = i2c_param_config(i2c_master_port, &i2c_cfg);
printf("Driver param setup : %d\n", res);
res = i2c_driver_install(i2c_master_port, i2c_cfg.mode,
I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE,
0);
printf("Driver installed : %d\n", res);
}
esp_err_t ma_write(uint8_t i2c_addr, uint8_t prot, uint16_t address,
uint8_t *wbuf, uint8_t n) {
bool ack = ACK_VAL;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, i2c_addr << 1 | WRITE_BIT, ACK_CHECK_EN);
if (prot == 2) {
i2c_master_write_byte(cmd, (uint8_t)((address & 0xff00) >> 8), ACK_VAL);
i2c_master_write_byte(cmd, (uint8_t)(address & 0x00ff), ACK_VAL);
} else {
i2c_master_write_byte(cmd, (uint8_t)address, ACK_VAL);
}
for (int i = 0; i < n; i++) {
if (i == n - 1) ack = NACK_VAL;
i2c_master_write_byte(cmd, wbuf[i], ack);
}
i2c_master_stop(cmd);
int ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_FAIL) {
return ret;
}
return ESP_OK;
}
esp_err_t ma_write_byte(uint8_t i2c_addr, uint8_t prot, uint16_t address,
uint8_t value) {
printf("%04x %02x\n", address, value);
esp_err_t ret = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | WRITE_BIT, ACK_CHECK_EN);
if (prot == 2) {
i2c_master_write_byte(cmd, (uint8_t)((address & 0xff00) >> 8), ACK_VAL);
i2c_master_write_byte(cmd, (uint8_t)(address & 0x00ff), ACK_VAL);
} else {
i2c_master_write_byte(cmd, (uint8_t)address, ACK_VAL);
}
i2c_master_write_byte(cmd, value, ACK_VAL);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_FAIL) {
printf("ESP_I2C_WRITE ERROR : %d\n", ret);
return ret;
}
return ESP_OK;
}
esp_err_t ma_read(uint8_t i2c_addr, uint8_t prot, uint16_t address,
uint8_t *rbuf, uint8_t n) {
esp_err_t ret;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
if (cmd == NULL) {
printf("ERROR handle null\n");
}
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | WRITE_BIT, ACK_CHECK_EN);
if (prot == 2) {
i2c_master_write_byte(cmd, (uint8_t)((address & 0xff00) >> 8), ACK_VAL);
i2c_master_write_byte(cmd, (uint8_t)(address & 0x00ff), ACK_VAL);
} else {
i2c_master_write_byte(cmd, (uint8_t)address, ACK_VAL);
}
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | READ_BIT, ACK_CHECK_EN);
// if (n == 1 )
i2c_master_read(cmd, rbuf, n - 1, ACK_VAL);
// for (uint8_t i = 0;i<n;i++)
// { i2c_master_read_byte(cmd, rbuf++, ACK_VAL); }
i2c_master_read_byte(cmd, rbuf + n - 1, NACK_VAL);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 100 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_FAIL) {
printf("i2c Error read - readback\n");
return ESP_FAIL;
}
return ret;
}
uint8_t ma_read_byte(uint8_t i2c_addr, uint8_t prot, uint16_t address) {
uint8_t value = 0;
esp_err_t ret;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd); // Send i2c start on bus
i2c_master_write_byte(cmd, (i2c_addr << 1) | WRITE_BIT, ACK_CHECK_EN);
if (prot == 2) {
i2c_master_write_byte(cmd, (uint8_t)((address & 0xff00) >> 8), ACK_VAL);
i2c_master_write_byte(cmd, (uint8_t)(address & 0x00ff), ACK_VAL);
} else {
i2c_master_write_byte(cmd, (uint8_t)address, ACK_VAL);
}
i2c_master_start(cmd); // Repeated start
i2c_master_write_byte(cmd, (i2c_addr << 1) | READ_BIT, ACK_CHECK_EN);
i2c_master_read_byte(cmd, &value, NACK_VAL);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_FAIL) {
printf("i2c Error read - readback\n");
return ESP_FAIL;
}
return value;
}

View File

@@ -0,0 +1,20 @@
#ifndef _MERUSAUDIO_H_
#define _MERUSAUDIO_H_
void setup_ma120x0(void);
void setup_ma120(void);
void ma120_read_error(uint8_t i2c_addr);
void ma120_setup_audio(uint8_t i2c_addr);
void i2c_master_init(void);
esp_err_t ma_write_byte(uint8_t i2c_addr, uint8_t prot, uint16_t address,
uint8_t value);
esp_err_t ma_write(uint8_t i2c_addr, uint8_t prot, uint16_t address,
uint8_t *wbuf, uint8_t n);
uint8_t ma_read_byte(uint8_t i2c_addr, uint8_t prot, uint16_t address);
esp_err_t ma_read(uint8_t i2c_addr, uint8_t prot, uint16_t address,
uint8_t *rbuf, uint8_t n);
#endif /* _MERUSAUDIO_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
/*
* MIT License
*
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
* Copyright (c) 2021 David Douard <david.douard@sdfa3.org>
*
* 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 _PCM51XX_H_
#define _PCM51XX_H_
#include "audio_hal.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#define PCM51XX_REG_00 0x00
#define PCM51XX_REG_02 0x02
#define PCM51XX_REG_03 0x03
#define PCM51XX_REG_24 0x24
#define PCM51XX_REG_25 0x25
#define PCM51XX_REG_26 0x26
#define PCM51XX_REG_27 0x27
#define PCM51XX_REG_28 0x28
#define PCM51XX_REG_29 0x29
#define PCM51XX_REG_2A 0x2a
#define PCM51XX_REG_2B 0x2b
#define PCM51XX_REG_35 0x35
#define PCM51XX_REG_7E 0x7e
#define PCM51XX_REG_7F 0x7f
#define PCM51XX_PAGE_00 0x00
#define PCM51XX_PAGE_2A 0x2a
#define PCM51XX_BOOK_00 0x00
#define PCM51XX_BOOK_8C 0x8c
#define PCM51XX_REG_VOL_L 0X3D
#define PCM51XX_REG_VOL_R 0X3E
#define PCM51XX_REG_MUTE 0X03
#define PCM51XX_DAMP_MODE_BTL 0x0
#define PCM51XX_DAMP_MODE_PBTL 0x04
/**
* @brief Initialize TAS5805 codec chip
*
* @param cfg configuration of TAS5805
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t pcm51xx_init(audio_hal_codec_config_t *codec_cfg);
/**
* @brief Deinitialize TAS5805 codec chip
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t pcm51xx_deinit(void);
/**
* @brief Set voice volume
*
* @param volume: voice volume (0~100)
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t pcm51xx_set_volume(int vol);
/**
* @brief Get voice volume
*
* @param[out] *volume: voice volume (0~100)
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t pcm51xx_get_volume(int *value);
/**
* @brief Set TAS5805 mute or not
* Continuously call should have an interval time determined by
* pcm51xx_set_mute_fade()
*
* @param enable enable(1) or disable(0)
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success
*/
esp_err_t pcm51xx_set_mute(bool enable);
/**
* @brief Get TAS5805 mute status
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success
*/
esp_err_t pcm51xx_get_mute(bool *enabled);
/**
* @brief Set DAMP mode
*
* @param value PCM51XX_DAMP_MODE_BTL or PCM51XX_DAMP_MODE_PBTL
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success
*
*/
esp_err_t pcm51xx_set_damp_mode(int value);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,56 @@
/*
* MIT License
*
* Copyright (c) 2021 David Douard <david.douard@sdfa3.org>
*
* 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 _PCM51XX_REG_CFG_
#define _PCM51XX_REG_CFG_
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
uint8_t offset;
uint8_t value;
} pcm51xx_cfg_reg_t;
static const pcm51xx_cfg_reg_t pcm51xx_init_seq[] = {
// EXIT SHUTDOWN STATE
{0x00, 0x00}, // SELECT PAGE 0
{0x03, 0x00}, // UNMUTE
{0x2a, 0x11}, // DAC DATA PATH L->ch1, R->ch2
{0x02, 0x00}, // DISABLE STBY
{0x0d, 0x10}, // BCK as SRC for PLL
{0x25, 0x08}, // IGNORE MISSING MCLK
{0x3d, 0x55}, // DIGITAL VOLUME L
{0x3e, 0x55}, // DIGITAL VOLUME R
};
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,219 @@
/*
* MIT License
*
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
* Copyright (c) 2021 David Douard <david.douard@sdfa3.org>
*
* 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 "pcm51xx.h"
#include "board.h"
#include "esp_log.h"
#include "i2c_bus.h"
#include "pcm51xx_reg_cfg.h"
static const char *TAG = "PCM51XX";
#define PCM51XX_BASE_ADDR 0x98
#define PCM51XX_RST_GPIO get_pa_enable_gpio()
#define PCM51XX_VOLUME_MAX 255
#define PCM51XX_VOLUME_MIN 0
#define PCM51XX_ASSERT(a, format, b, ...) \
if ((a) != 0) { \
ESP_LOGE(TAG, format, ##__VA_ARGS__); \
return b; \
}
esp_err_t pcm51xx_ctrl(audio_hal_codec_mode_t mode,
audio_hal_ctrl_t ctrl_state);
esp_err_t pcm51xx_config_iface(audio_hal_codec_mode_t mode,
audio_hal_codec_i2s_iface_t *iface);
static i2c_bus_handle_t i2c_handler;
static int pcm51xx_addr;
/*
* i2c default configuration
*/
static 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,
};
/*
* Operate function
*/
audio_hal_func_t AUDIO_CODEC_PCM51XX_DEFAULT_HANDLE = {
.audio_codec_initialize = pcm51xx_init,
.audio_codec_deinitialize = pcm51xx_deinit,
.audio_codec_ctrl = pcm51xx_ctrl,
.audio_codec_config_iface = pcm51xx_config_iface,
.audio_codec_set_mute = pcm51xx_set_mute,
.audio_codec_set_volume = pcm51xx_set_volume,
.audio_codec_get_volume = pcm51xx_get_volume,
.audio_hal_lock = NULL,
.handle = NULL,
};
static esp_err_t pcm51xx_transmit_registers(const pcm51xx_cfg_reg_t *conf_buf,
int size) {
int i = 0;
esp_err_t ret = ESP_OK;
while (i < size) {
ret = i2c_bus_write_bytes(i2c_handler, pcm51xx_addr,
(unsigned char *)(&conf_buf[i].offset), 1,
(unsigned char *)(&conf_buf[i].value), 1);
i++;
}
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Fail to load configuration to pcm51xx");
return ESP_FAIL;
}
ESP_LOGI(TAG, "%s: write %d reg done", __FUNCTION__, i);
return ret;
}
esp_err_t pcm51xx_init(audio_hal_codec_config_t *codec_cfg) {
esp_err_t ret = ESP_OK;
ESP_LOGI(TAG, "Power ON CODEC with GPIO %d", PCM51XX_RST_GPIO);
// probably unnecessary...
/*
gpio_config_t io_conf;
io_conf.pin_bit_mask = BIT64(PCM51XX_RST_GPIO);
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&io_conf);
gpio_set_level(PCM51XX_RST_GPIO, 0);
vTaskDelay(20 / portTICK_RATE_MS);
gpio_set_level(PCM51XX_RST_GPIO, 1);
vTaskDelay(200 / portTICK_RATE_MS);
*/
ret = get_i2c_pins(I2C_NUM_0, &i2c_cfg);
i2c_handler = i2c_bus_create(I2C_NUM_0, &i2c_cfg);
if (i2c_handler == NULL) {
ESP_LOGW(TAG, "failed to create i2c bus handler\n");
return ESP_FAIL;
}
uint8_t data[] = {0, 0};
for (int i = 0; i < 4; i++) {
pcm51xx_addr = PCM51XX_BASE_ADDR + 2 * i;
ESP_LOGI(TAG, "Looking for a pcm51xx chip at address 0x%x", pcm51xx_addr);
ret = i2c_bus_write_data(i2c_handler, pcm51xx_addr, data, 0);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "Found a pcm51xx chip at address 0x%x", pcm51xx_addr);
break;
}
}
PCM51XX_ASSERT(ret, "Fail to detect pcm51xx PA", ESP_FAIL);
ret |= pcm51xx_transmit_registers(
pcm51xx_init_seq, sizeof(pcm51xx_init_seq) / sizeof(pcm51xx_init_seq[0]));
PCM51XX_ASSERT(ret, "Fail to iniitialize pcm51xx PA", ESP_FAIL);
return ret;
}
esp_err_t pcm51xx_set_volume(int vol) {
// vol is given as 1/2dB step with
// 255: -inf (mute)
// 254: -103dB
// 48: 0dB
// 0 (max): +24dB
if (vol < PCM51XX_VOLUME_MIN) {
vol = PCM51XX_VOLUME_MIN;
}
if (vol > PCM51XX_VOLUME_MAX) {
vol = PCM51XX_VOLUME_MAX;
}
uint8_t cmd[2] = {0, 0};
esp_err_t ret = ESP_OK;
cmd[1] = vol;
cmd[0] = PCM51XX_REG_VOL_L;
ret = i2c_bus_write_bytes(i2c_handler, pcm51xx_addr, &cmd[0], 1, &cmd[1], 1);
cmd[0] = PCM51XX_REG_VOL_R;
ret |= i2c_bus_write_bytes(i2c_handler, pcm51xx_addr, &cmd[0], 1, &cmd[1], 1);
ESP_LOGW(TAG, "Volume set to 0x%x", cmd[1]);
return ret;
}
esp_err_t pcm51xx_get_volume(int *value) {
/// FIXME: Got the digit volume is not right.
uint8_t cmd[2] = {PCM51XX_REG_VOL_L, 0x00};
esp_err_t ret =
i2c_bus_read_bytes(i2c_handler, pcm51xx_addr, &cmd[0], 1, &cmd[1], 1);
PCM51XX_ASSERT(ret, "Fail to get volume", ESP_FAIL);
ESP_LOGI(TAG, "Volume is %d", cmd[1]);
*value = cmd[1];
return ret;
}
esp_err_t pcm51xx_set_mute(bool enable) {
esp_err_t ret = ESP_OK;
uint8_t cmd[2] = {PCM51XX_REG_MUTE, 0x00};
ret |= i2c_bus_read_bytes(i2c_handler, pcm51xx_addr, &cmd[0], 1, &cmd[1], 1);
if (enable) {
cmd[1] |= 0x11;
} else {
cmd[1] &= (~0x11);
}
ret |= i2c_bus_write_bytes(i2c_handler, pcm51xx_addr, &cmd[0], 1, &cmd[1], 1);
PCM51XX_ASSERT(ret, "Fail to set mute", ESP_FAIL);
return ret;
}
esp_err_t pcm51xx_get_mute(bool *enabled) {
esp_err_t ret = ESP_OK;
uint8_t cmd[2] = {PCM51XX_REG_MUTE, 0x00};
ret |= i2c_bus_read_bytes(i2c_handler, pcm51xx_addr, &cmd[0], 1, &cmd[1], 1);
PCM51XX_ASSERT(ret, "Fail to get mute", ESP_FAIL);
*enabled = (bool)(cmd[1] & 0x11);
ESP_LOGI(TAG, "Get mute value: %s", *enabled ? "muted" : "unmuted");
return ret;
}
esp_err_t pcm51xx_deinit(void) {
// TODO
return ESP_OK;
}
esp_err_t pcm51xx_ctrl(audio_hal_codec_mode_t mode,
audio_hal_ctrl_t ctrl_state) {
// TODO
return ESP_OK;
}
esp_err_t pcm51xx_config_iface(audio_hal_codec_mode_t mode,
audio_hal_codec_i2s_iface_t *iface) {
// TODO
return ESP_OK;
}

View File

@@ -0,0 +1,2 @@
idf_component_register( SRCS "i2s.c"
INCLUDE_DIRS "include")

View File

@@ -0,0 +1,2 @@
COMPONENT_SRCDIRS := .
# CFLAGS +=

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,302 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "esp_types.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "soc/i2s_periph.h"
#include "soc/rtc_periph.h"
#include "soc/soc_caps.h"
#include "hal/i2s_hal.h"
#include "hal/i2s_types.h"
#include "driver/periph_ctrl.h"
#include "esp_intr_alloc.h"
#ifdef __cplusplus
extern "C" {
#endif
#define I2S_PIN_NO_CHANGE (-1) /*!< Use in i2s_pin_config_t for pins which should not be changed */
typedef intr_handle_t i2s_isr_handle_t;
/**
* @brief Set I2S pin number
*
* @note
* The I2S peripheral output signals can be connected to multiple GPIO pads.
* However, the I2S peripheral input signal can only be connected to one GPIO pad.
*
* @param i2s_num I2S_NUM_0 or I2S_NUM_1
*
* @param pin I2S Pin structure, or NULL to set 2-channel 8-bit internal DAC pin configuration (GPIO25 & GPIO26)
*
* Inside the pin configuration structure, set I2S_PIN_NO_CHANGE for any pin where
* the current configuration should not be changed.
*
* @note if *pin is set as NULL, this function will initialize both of the built-in DAC channels by default.
* if you don't want this to happen and you want to initialize only one of the DAC channels, you can call i2s_set_dac_mode instead.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL IO error
*/
esp_err_t i2s_custom_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin);
#if SOC_I2S_SUPPORTS_PDM
/**
* @brief Set PDM mode down-sample rate
* In PDM RX mode, there would be 2 rounds of downsample process in hardware.
* In the first downsample process, the sampling number can be 16 or 8.
* In the second downsample process, the sampling number is fixed as 8.
* So the clock frequency in PDM RX mode would be (fpcm * 64) or (fpcm * 128) accordingly.
* @param i2s_num I2S_NUM_0, I2S_NUM_1
* @param dsr i2s RX down sample rate for PDM mode.
*
* @note After calling this function, it would call i2s_set_clk inside to update the clock frequency.
* Please call this function after I2S driver has been initialized.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM Out of memory
*/
esp_err_t i2s_custom_set_pdm_rx_down_sample(i2s_port_t i2s_num, i2s_pdm_dsr_t dsr);
#endif
/**
* @brief Set I2S dac mode, I2S built-in DAC is disabled by default
*
* @param dac_mode DAC mode configurations - see i2s_dac_mode_t
*
* @note Built-in DAC functions are only supported on I2S0 for current ESP32 chip.
* If either of the built-in DAC channel are enabled, the other one can not
* be used as RTC DAC function at the same time.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2s_custom_set_dac_mode(i2s_dac_mode_t dac_mode);
/**
* @brief Install and start I2S driver.
*
* @param i2s_num I2S_NUM_0, I2S_NUM_1
*
* @param i2s_config I2S configurations - see i2s_config_t struct
*
* @param queue_size I2S event queue size/depth.
*
* @param i2s_queue I2S event queue handle, if set NULL, driver will not use an event queue.
*
* This function must be called before any I2S driver read/write operations.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM Out of memory
*/
esp_err_t i2s_custom_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void* i2s_queue);
/**
* @brief Uninstall I2S driver.
*
* @param i2s_num I2S_NUM_0, I2S_NUM_1
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2s_custom_driver_uninstall(i2s_port_t i2s_num);
/**
* @brief Write data to I2S DMA transmit buffer.
*
* @param i2s_num I2S_NUM_0, I2S_NUM_1
*
* @param src Source address to write from
*
* @param size Size of data in bytes
*
* @param[out] bytes_written Number of bytes written, if timeout, the result will be less than the size passed in.
*
* @param ticks_to_wait TX buffer wait timeout in RTOS ticks. If this
* many ticks pass without space becoming available in the DMA
* transmit buffer, then the function will return (note that if the
* data is written to the DMA buffer in pieces, the overall operation
* may still take longer than this timeout.) Pass portMAX_DELAY for no
* timeout.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2s_custom_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *bytes_written, TickType_t ticks_to_wait);
/**
* @brief Write data to I2S DMA transmit buffer while expanding the number of bits per sample. For example, expanding 16-bit PCM to 32-bit PCM.
*
* @param i2s_num I2S_NUM_0, I2S_NUM_1
*
* @param src Source address to write from
*
* @param size Size of data in bytes
*
* @param src_bits Source audio bit
*
* @param aim_bits Bit wanted, no more than 32, and must be greater than src_bits
*
* @param[out] bytes_written Number of bytes written, if timeout, the result will be less than the size passed in.
*
* @param ticks_to_wait TX buffer wait timeout in RTOS ticks. If this
* many ticks pass without space becoming available in the DMA
* transmit buffer, then the function will return (note that if the
* data is written to the DMA buffer in pieces, the overall operation
* may still take longer than this timeout.) Pass portMAX_DELAY for no
* timeout.
*
* Format of the data in source buffer is determined by the I2S
* configuration (see i2s_config_t).
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2s_custom_write_expand(i2s_port_t i2s_num, const void *src, size_t size, size_t src_bits, size_t aim_bits, size_t *bytes_written, TickType_t ticks_to_wait);
/**
* @brief Read data from I2S DMA receive buffer
*
* @param i2s_num I2S_NUM_0, I2S_NUM_1
*
* @param dest Destination address to read into
*
* @param size Size of data in bytes
*
* @param[out] bytes_read Number of bytes read, if timeout, bytes read will be less than the size passed in.
*
* @param ticks_to_wait RX buffer wait timeout in RTOS ticks. If this many ticks pass without bytes becoming available in the DMA receive buffer, then the function will return (note that if data is read from the DMA buffer in pieces, the overall operation may still take longer than this timeout.) Pass portMAX_DELAY for no timeout.
*
* @note If the built-in ADC mode is enabled, we should call i2s_adc_enable and i2s_adc_disable around the whole reading process,
* to prevent the data getting corrupted.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2s_custom_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_read, TickType_t ticks_to_wait);
/**
* @brief Set sample rate used for I2S RX and TX.
*
* The bit clock rate is determined by the sample rate and i2s_config_t configuration parameters (number of channels, bits_per_sample).
*
* `bit_clock = rate * (number of channels) * bits_per_sample`
*
* @param i2s_num I2S_NUM_0, I2S_NUM_1
*
* @param rate I2S sample rate (ex: 8000, 44100...)
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM Out of memory
*/
esp_err_t i2s_custom_set_sample_rates(i2s_port_t i2s_num, uint32_t rate);
/**
* @brief Stop I2S driver
*
* There is no need to call i2s_stop() before calling i2s_driver_uninstall().
*
* Disables I2S TX/RX, until i2s_start() is called.
*
* @param i2s_num I2S_NUM_0, I2S_NUM_1
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2s_custom_stop(i2s_port_t i2s_num);
/**
* @brief Start I2S driver
*
* It is not necessary to call this function after i2s_driver_install() (it is started automatically), however it is necessary to call it after i2s_stop().
*
*
* @param i2s_num I2S_NUM_0, I2S_NUM_1
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2s_custom_start(i2s_port_t i2s_num);
/**
* @brief Zero the contents of the TX DMA buffer.
*
* Pushes zero-byte samples into the TX DMA buffer, until it is full.
*
* @param i2s_num I2S_NUM_0, I2S_NUM_1
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2s_custom_zero_dma_buffer(i2s_port_t i2s_num);
/**
* @brief send all dma buffers, so they are available to i2s_custom_write() immediatly
*/
esp_err_t i2s_custom_init_dma_tx_queues(i2s_port_t i2s_num, uint8_t *data, size_t size, size_t *written);
/**
* @brief Set clock & bit width used for I2S RX and TX.
*
* Similar to i2s_set_sample_rates(), but also sets bit width.
*
* @param i2s_num I2S_NUM_0, I2S_NUM_1
*
* @param rate I2S sample rate (ex: 8000, 44100...)
*
* @param bits I2S bit width (I2S_BITS_PER_SAMPLE_16BIT, I2S_BITS_PER_SAMPLE_24BIT, I2S_BITS_PER_SAMPLE_32BIT)
*
* @param ch I2S channel, (I2S_CHANNEL_MONO, I2S_CHANNEL_STEREO)
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM Out of memory
*/
esp_err_t i2s_custom_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t bits, i2s_channel_t ch);
/**
* @brief get clock set on particular port number.
*
* @param i2s_num I2S_NUM_0, I2S_NUM_1
*
* @return
* - actual clock set by i2s driver
*/
float i2s_custom_get_clk(i2s_port_t i2s_num);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,10 @@
set(COMPONENT_REQUIRES)
set(COMPONENT_PRIV_REQUIRES audio_board audio_sal audio_hal esp-dsp)
list(APPEND COMPONENT_ADD_INCLUDEDIRS ./include)
set(COMPONENT_SRCS ./dsp_processor.c)
register_component()
# IDF >=4
idf_component_get_property(audio_board_lib audio_board COMPONENT_LIB)
set_property(TARGET ${audio_board_lib} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${COMPONENT_LIB})

View File

@@ -0,0 +1,47 @@
# Config file for ESP32 DSP Processor
menu "ESP32 audio buffer and I2S pin config"
config USE_PSRAM
bool "Use PSRAM"
default true
depends on ESP32_SPIRAM_SUPPORT
help
Need wrover class modules with large SPRAM to have required buffers for Snapcast network delay
config USE_DSP_PROCESSOR
bool "enable signal processing on audio data"
default false
help
enable audio filtering before queueing it to player component
config BITS_PER_SAMPLE
int "bits per sample output to i2s driver"
default 16
help
Select number of bits per sample for codec configured and connected to esp32 i2s dma hw
config CHANNELS
int "number of channels per sample output to i2s driver"
default 2
help
Select number of channels per sample for codec configured and connected to esp32 i2s dma hw
config PCM_SAMPLE_RATE
int "sample rate of audio pcm data"
default 48000
help
sample rate of audio data, currently only 48kHz is heavily tested and used during development
config WIRE_CHUNK_DURATION_MS
int "wire chunk duration [ms]"
default 20
help
pcm data is encoded in chunks of x ms, this value has to match snapserver configuration
config USE_BIQUAD_ASM
bool "Use uptimized asm version of Biquad_f32"
default true
help
Asm version 2 x speed on ESP32 - not working on ESP32-S2
endmenu

View File

@@ -0,0 +1,11 @@
#
# Main Makefile. This is basically the same as a component makefile.
#
# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default,
# this will take the sources in the src/ directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#
COMPONENT_ADD_INLUCDEDIRS += ./include
COMPONENT_SRCDIRS += ./dsp_processor.c

View File

@@ -0,0 +1,361 @@
#include <stdint.h>
#include <string.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#if CONFIG_USE_DSP_PROCESSOR
#include "freertos/ringbuf.h"
#include "freertos/task.h"
#include "driver/i2s.h"
#include "dsps_biquad.h"
#include "dsps_biquad_gen.h"
#include "esp_log.h"
//#include "websocket_if.h"
#include "driver/dac.h"
#include "driver/i2s.h"
#include "dsp_processor.h"
#include "hal/i2s_hal.h"
//#include "adc1_i2s_private.h"
#include "board_pins_config.h"
#ifdef CONFIG_USE_BIQUAD_ASM
#define BIQUAD dsps_biquad_f32_ae32
#else
#define BIQUAD dsps_biquad_f32
#endif
static const char *TAG = "dspProc";
static const uint8_t chunkDurationMs = CONFIG_WIRE_CHUNK_DURATION_MS;
static const uint32_t sampleRate = CONFIG_PCM_SAMPLE_RATE;
//static const uint8_t channels = CONFIG_CHANNELS;
//static const uint8_t bitsPerSample = CONFIG_BITS_PER_SAMPLE;
// TODO: allocate these buffers dynamically from heap
static float *sbuffer0 = NULL;//[1024];
//static float sbuffer1[1024];
//static float sbuffer2[1024];
static float *sbufout0 = NULL;//[1024];
//static float sbufout1[1024];
//static float sbufout2[1024];
static float *sbuftmp0 = NULL;//[1024];
//static uint8_t dsp_audio[4 * 1024];
//static uint8_t dsp_audio1[4 * 1024];
extern uint8_t muteCH[4];
ptype_t bq[8];
int dsp_processor(char *audio, size_t chunk_size, dspFlows_t dspFlow) {
double dynamic_vol = 1.0;
int16_t len = chunk_size / 4;
int16_t valint;
uint16_t i;
// ESP_LOGI(TAG,
// "got data %p, %d, %u", audio, chunk_size, dspFlow);
if ((sbuffer0 == NULL) || (sbufout0 == NULL) || (sbuftmp0 == NULL)) {
ESP_LOGE(
TAG,
"No Memory allocated for dsp_processor %p %p %p", sbuffer0, sbufout0, sbuftmp0);
return -1;
}
/*
for (uint16_t i = 0; i < len; i++) {
sbuffer0[i] =
dynamic_vol * 0.5 *
((float)((int16_t)(audio[i * 4 + 1] << 8) + audio[i * 4 + 0])) / 32768;
sbuffer1[i] =
dynamic_vol * 0.5 *
((float)((int16_t)(audio[i * 4 + 3] << 8) + audio[i * 4 + 2])) / 32768;
sbuffer2[i] = ((sbuffer0[i] / 2) + (sbuffer1[i] / 2));
}
*/
switch (dspFlow) {
case dspfStereo: {
// for (i = 0; i < len; i++) {
// audio[i * 4 + 0] = (muteCH[0] == 1) ? 0 : audio[i * 4 + 0];
// audio[i * 4 + 1] = (muteCH[0] == 1) ? 0 : audio[i * 4 + 1];
// audio[i * 4 + 2] = (muteCH[1] == 1) ? 0 : audio[i * 4 + 2];
// audio[i * 4 + 3] = (muteCH[1] == 1) ? 0 : audio[i * 4 + 3];
// }
// mute is done through audio_hal_set_mute()
} break;
case dspfBassBoost: { // CH0 low shelf 6dB @ 400Hz
// channel 0
for (i = 0; i < len; i++) {
sbuffer0[i] =
dynamic_vol * 0.5 *
((float)((int16_t)(audio[i * 4 + 1] << 8) + audio[i * 4 + 0])) / 32768;
}
BIQUAD(sbuffer0, sbufout0, len, bq[6].coeffs, bq[6].w);
for (i = 0; i < len; i++) {
valint = (int16_t)(sbufout0[i] * 32768);
audio[i * 4 + 0] = (valint & 0x00ff);
audio[i * 4 + 1] = ((valint & 0xff00) >> 8);
}
// channel 1
for (i = 0; i < len; i++) {
sbuffer0[i] =
dynamic_vol * 0.5 *
((float)((int16_t)(audio[i * 4 + 3] << 8) + audio[i * 4 + 2])) / 32768;
}
BIQUAD(sbuffer0, sbufout0, len, bq[7].coeffs, bq[7].w);
for (i = 0; i < len; i++) {
valint = (int16_t)(sbufout0[i] * 32768);
audio[i * 4 + 2] = (valint & 0x00ff);
audio[i * 4 + 3] = ((valint & 0xff00) >> 8);
}
} break;
case dspfBiamp: {
// Process audio ch0 LOW PASS FILTER
for (i = 0; i < len; i++) {
sbuffer0[i] =
dynamic_vol * 0.5 *
((float)((int16_t)(audio[i * 4 + 1] << 8) + audio[i * 4 + 0])) / 32768;
}
BIQUAD(sbuffer0, sbuftmp0, len, bq[0].coeffs, bq[0].w);
BIQUAD(sbuftmp0, sbufout0, len, bq[1].coeffs, bq[1].w);
for (i = 0; i < len; i++) {
valint = (int16_t)(sbufout0[i] * 32768);
audio[i * 4 + 0] = (valint & 0x00ff);
audio[i * 4 + 1] = ((valint & 0xff00) >> 8);
}
// Process audio ch1 HIGH PASS FILTER
for (i = 0; i < len; i++) {
sbuffer0[i] =
dynamic_vol * 0.5 *
((float)((int16_t)(audio[i * 4 + 3] << 8) + audio[i * 4 + 2])) / 32768;
}
BIQUAD(sbuffer0, sbuftmp0, len, bq[2].coeffs, bq[2].w);
BIQUAD(sbuftmp0, sbufout0, len, bq[3].coeffs, bq[3].w);
for (i = 0; i < len; i++) {
valint = (int16_t)(sbufout0[i] * 32768);
audio[i * 4 + 2] = (valint & 0x00ff);
audio[i * 4 + 3] = ((valint & 0xff00) >> 8);
}
} break;
case dspf2DOT1: { // Process audio L + R LOW PASS FILTER
/*
BIQUAD(sbuffer2, sbuftmp0, len, bq[0].coeffs, bq[0].w);
BIQUAD(sbuftmp0, sbufout2, len, bq[1].coeffs, bq[1].w);
// Process audio L HIGH PASS FILTER
BIQUAD(sbuffer0, sbuftmp0, len, bq[2].coeffs, bq[2].w);
BIQUAD(sbuftmp0, sbufout0, len, bq[3].coeffs, bq[3].w);
// Process audio R HIGH PASS FILTER
BIQUAD(sbuffer1, sbuftmp0, len, bq[4].coeffs, bq[4].w);
BIQUAD(sbuftmp0, sbufout1, len, bq[5].coeffs, bq[5].w);
int16_t valint[5];
for (uint16_t i = 0; i < len; i++) {
valint[0] =
(muteCH[0] == 1) ? (int16_t)0 : (int16_t)(sbufout0[i] * 32768);
valint[1] =
(muteCH[1] == 1) ? (int16_t)0 : (int16_t)(sbufout1[i] * 32768);
valint[2] =
(muteCH[2] == 1) ? (int16_t)0 : (int16_t)(sbufout2[i] * 32768);
dsp_audio[i * 4 + 0] = (valint[2] & 0xff);
dsp_audio[i * 4 + 1] = ((valint[2] & 0xff00) >> 8);
dsp_audio[i * 4 + 2] = 0;
dsp_audio[i * 4 + 3] = 0;
dsp_audio1[i * 4 + 0] = (valint[0] & 0xff);
dsp_audio1[i * 4 + 1] = ((valint[0] & 0xff00) >> 8);
dsp_audio1[i * 4 + 2] = (valint[1] & 0xff);
dsp_audio1[i * 4 + 3] = ((valint[1] & 0xff00) >> 8);
}
// TODO: this copy could be avoided if dsp_audio buffers are
// allocated dynamically and pointers are exchanged after
// audio was freed
memcpy(audio, dsp_audio, chunk_size);
ESP_LOGW(TAG, "Don't know what to do with dsp_audio1");
*/
ESP_LOGW(TAG, "dspf2DOT1, not implemented yet, using stereo instead");
} break;
case dspfFunkyHonda: { // Process audio L + R LOW PASS FILTER
/*
BIQUAD(sbuffer2, sbuftmp0, len, bq[0].coeffs, bq[0].w);
BIQUAD(sbuftmp0, sbufout2, len, bq[1].coeffs, bq[1].w);
// Process audio L HIGH PASS FILTER
BIQUAD(sbuffer0, sbuftmp0, len, bq[2].coeffs, bq[2].w);
BIQUAD(sbuftmp0, sbufout0, len, bq[3].coeffs, bq[3].w);
// Process audio R HIGH PASS FILTER
BIQUAD(sbuffer1, sbuftmp0, len, bq[4].coeffs, bq[4].w);
BIQUAD(sbuftmp0, sbufout1, len, bq[5].coeffs, bq[5].w);
uint16_t scale = 16384; // 32768
int16_t valint[5];
for (uint16_t i = 0; i < len; i++) {
valint[0] =
(muteCH[0] == 1) ? (int16_t)0 : (int16_t)(sbufout0[i] * scale);
valint[1] =
(muteCH[1] == 1) ? (int16_t)0 : (int16_t)(sbufout1[i] * scale);
valint[2] =
(muteCH[2] == 1) ? (int16_t)0 : (int16_t)(sbufout2[i] * scale);
valint[3] = valint[0] + valint[2];
valint[4] = -valint[2];
valint[5] = -valint[1] - valint[2];
dsp_audio[i * 4 + 0] = (valint[3] & 0xff);
dsp_audio[i * 4 + 1] = ((valint[3] & 0xff00) >> 8);
dsp_audio[i * 4 + 2] = (valint[2] & 0xff);
dsp_audio[i * 4 + 3] = ((valint[2] & 0xff00) >> 8);
dsp_audio1[i * 4 + 0] = (valint[4] & 0xff);
dsp_audio1[i * 4 + 1] = ((valint[4] & 0xff00) >> 8);
dsp_audio1[i * 4 + 2] = (valint[5] & 0xff);
dsp_audio1[i * 4 + 3] = ((valint[5] & 0xff00) >> 8);
}
// TODO: this copy could be avoided if dsp_audio buffers are
// allocated dynamically and pointers are exchanged after
// audio was freed
memcpy(audio, dsp_audio, chunk_size);
ESP_LOGW(TAG, "Don't know what to do with dsp_audio1");
*/
ESP_LOGW(TAG, "dspfFunkyHonda, not implemented yet, using stereo instead");
} break;
default: { } break; }
return 0;
}
// ESP32 DSP processor
//======================================================
// Each time a buffer of audio is passed to the DSP - samples are
// processed according to a dynamic list of audio processing nodes.
// Each audio processor node consist of a data struct holding the
// required weights and states for processing an automomous processing
// function. The high level parameters is maintained in the structure
// as well
// Release - Prove off concept
// ----------------------------------------
// Fixed 2x2 biquad flow Xover for biAmp systems
// Interface for cross over frequency and level
void dsp_setup_flow(double freq, uint32_t samplerate) {
float f = freq / samplerate / 2.0;
uint16_t len = (sampleRate * chunkDurationMs / 1000);
bq[0] = (ptype_t){LPF, f, 0, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}};
bq[1] = (ptype_t){LPF, f, 0, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}};
bq[2] = (ptype_t){HPF, f, 0, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}};
bq[3] = (ptype_t){HPF, f, 0, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}};
bq[4] = (ptype_t){HPF, f, 0, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}};
bq[5] = (ptype_t){HPF, f, 0, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}};
bq[6] = (ptype_t){LOWSHELF, f, 6, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}};
bq[7] = (ptype_t){LOWSHELF, f, 6, 0.707, NULL, NULL, {0, 0, 0, 0, 0}, {0, 0}};
pnode_t *aflow = NULL;
aflow = malloc(sizeof(pnode_t));
if (aflow == NULL) {
printf("Could not create node");
}
for (uint8_t n = 0; n <= 7; n++) {
switch (bq[n].filtertype) {
case LOWSHELF:
dsps_biquad_gen_lowShelf_f32(bq[n].coeffs, bq[n].freq, bq[n].gain,
bq[n].q);
break;
case LPF:
dsps_biquad_gen_lpf_f32(bq[n].coeffs, bq[n].freq, bq[n].q);
break;
case HPF:
dsps_biquad_gen_hpf_f32(bq[n].coeffs, bq[n].freq, bq[n].q);
break;
default:
break;
}
// for (uint8_t i = 0; i <= 4; i++) {
// printf("%.6f ", bq[n].coeffs[i]);
// }
// printf("\n");
}
sbuffer0 = (float *)heap_caps_malloc(sizeof(float) * len, MALLOC_CAP_8BIT);
sbufout0 = (float *)heap_caps_malloc(sizeof(float) * len, MALLOC_CAP_8BIT);
sbuftmp0 = (float *)heap_caps_malloc(sizeof(float) * len, MALLOC_CAP_8BIT);
if ((sbuffer0 == NULL) || (sbufout0 == NULL) || (sbuftmp0 == NULL)) {
ESP_LOGE(
TAG,
"Failed to allocate initial memory for dsp_processor %p %p %p", sbuffer0, sbufout0, sbuftmp0);
if (sbuffer0) {
free(sbuffer0);
}
if (sbufout0) {
free(sbufout0);
}
if (sbuftmp0) {
free(sbuftmp0);
}
}
else {
ESP_LOGI(
TAG,
"GOT memory for dsp_processor %p %p", sbuffer0, sbufout0);
}
}
void dsp_set_xoverfreq(uint8_t freqh, uint8_t freql, uint32_t samplerate) {
float freq = freqh * 256 + freql;
// printf("%f\n", freq);
float f = freq / samplerate / 2.;
for (int8_t n = 0; n <= 5; n++) {
bq[n].freq = f;
switch (bq[n].filtertype) {
case LPF:
// for (uint8_t i = 0; i <= 4; i++) {
// printf("%.6f ", bq[n].coeffs[i]);
// }
// printf("\n");
dsps_biquad_gen_lpf_f32(bq[n].coeffs, bq[n].freq, bq[n].q);
// for (uint8_t i = 0; i <= 4; i++) {
// printf("%.6f ", bq[n].coeffs[i]);
// }
// printf("%f \n", bq[n].freq);
break;
case HPF:
dsps_biquad_gen_hpf_f32(bq[n].coeffs, bq[n].freq, bq[n].q);
break;
default:
break;
}
}
}
#endif

View File

@@ -0,0 +1,46 @@
#ifndef _DSP_PROCESSOR_H_
#define _DSP_PROCESSOR_H_
typedef enum dspFlows {
dspfStereo,
dspfBiamp,
dspf2DOT1,
dspfFunkyHonda,
dspfBassBoost
} dspFlows_t;
enum filtertypes {
LPF,
HPF,
BPF,
BPF0DB,
NOTCH,
ALLPASS360,
ALLPASS180,
PEAKINGEQ,
LOWSHELF,
HIGHSHELF
};
// Process node
typedef struct ptype {
int filtertype;
float freq;
float gain;
float q;
float *in, *out;
float coeffs[5];
float w[2];
} ptype_t;
// Process flow
typedef struct pnode {
ptype_t process;
struct pnode *next;
} pnode_t;
void dsp_setup_flow(double freq, uint32_t samplerate);
int dsp_processor(char *audio, size_t chunk_size, dspFlows_t dspFlow);
void dsp_set_xoverfreq(uint8_t, uint8_t, uint32_t);
#endif /* _DSP_PROCESSOR_H_ */

54
components/esp-dsp/.gitignore vendored Normal file
View File

@@ -0,0 +1,54 @@
.config
*.o
*.pyc
# gtags
GTAGS
GRTAGS
GPATH
# emacs
.dir-locals.el
# emacs temp file suffixes
*~
.#*
\#*#
# eclipse setting
.settings
# MacOS directory files
.DS_Store
# Example project files
examples/**/sdkconfig
examples/**/sdkconfig.old
examples/**/build
# Test app files
test_app/build
test_app/sdkconfig
test_app/sdkconfig.old
# Doc build artifacts
docs/_build/
docs/doxygen-warning-log.txt
docs/sphinx-warning-log.txt
docs/sphinx-warning-log-sanitized.txt
docs/xml/
docs/xml_in/
docs/man/
docs/doxygen_sqlite3.db
TEST_LOGS
# gcov coverage reports
*.gcda
*.gcno
coverage.info
coverage_report/
# VS Code Settings
.vscode/

View File

@@ -0,0 +1,98 @@
stages:
- build
- deploy
image: $CI_DOCKER_REGISTRY/esp32-ci-env:latest
variables:
# System environment
# Common parameters for the 'make' during CI tests
MAKEFLAGS: "-j5 --no-keep-going"
# GitLab-CI environment
GET_SOURCES_ATTEMPTS: "10"
ARTIFACT_DOWNLOAD_ATTEMPTS: "10"
GIT_SUBMODULE_STRATEGY: none
ESP_IDF_GIT: "https://gitlab-ci-token:${CI_JOB_TOKEN}@${GITLAB_HTTPS_SERVER}/espressif/esp-idf.git"
.setup_idf_tools: &setup_idf_tools |
tools/idf_tools.py --non-interactive install && eval "$(tools/idf_tools.py --non-interactive export)" || exit 1
.add_gh_key_remote: &add_gh_key_remote |
command -v ssh-agent >/dev/null || exit 1
eval $(ssh-agent -s)
printf '%s\n' "${GH_PUSH_KEY}" | tr -d '\r' | ssh-add - > /dev/null
mkdir -p ~/.ssh && chmod 700 ~/.ssh
[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config || ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts
git remote remove github || true
git remote add github ${GH_PUSH_REPO}
after_script:
# Just for cleaning space, no other causes
- git clean -ffdx
build:
stage: build
tags:
- build
script:
# Get ESP-IDF
- git clone ${ESP_IDF_GIT} esp-idf
- pushd esp-idf
# Non-recursive getting its submodules
- git submodule update --init
- export IDF_PATH=$PWD
- *setup_idf_tools
- popd
# Create a copy of the project in "esp-dsp" directory.
# This is needed because CMake build system can not build a component
# when ESP-IDF directory is inside the component.
# After cloning, we will have two directories at the same level: "esp-idf" and "esp-dsp"
- git clone $PWD esp-dsp
- cd esp-dsp
# Build test app by both Makefiles and CMake ways
- pushd test_app
- make defconfig && make
- rm -rf build
- idf.py build
- popd
# Build examples
- ./build_examples.sh
build_docs:
stage: build
tags:
- build_docs
artifacts:
when: always
paths:
- docs/doxygen-warning-log.txt
- docs/sphinx-warning-log.txt
- docs/_build/html
expire_in: 1 day
# No cleaning when the artifacts
after_script: []
script:
- cd docs
- make html
- ./check_doc_warnings.sh
push_master_to_github:
stage: deploy
tags:
- deploy
only:
- master
- /^release\/v/
- /^v\d+\.\d+(\.\d+)?($|-)/
when: on_success
script:
# Just for a helper script
- git clone --depth 1 ${ESP_IDF_GIT} esp-idf
- *add_gh_key_remote
- esp-idf/tools/ci/push_to_github.sh

View File

@@ -0,0 +1,108 @@
set(COMPONENT_SRCS "modules/common/misc/dsps_pwroftwo.cpp"
"modules/dotprod/float/dsps_dotprod_f32_ae32.S"
"modules/dotprod/float/dsps_dotprod_f32_m_ae32.S"
"modules/dotprod/float/dsps_dotprode_f32_ae32.S"
"modules/dotprod/float/dsps_dotprode_f32_m_ae32.S"
"modules/dotprod/float/dsps_dotprod_f32_ansi.c"
"modules/dotprod/float/dsps_dotprode_f32_ansi.c"
"modules/dotprod/fixed/dsps_dotprod_s16_ae32.S"
"modules/dotprod/fixed/dsps_dotprod_s16_m_ae32.S"
"modules/dotprod/fixed/dsps_dotprod_s16_ansi.c"
"modules/matrix/float/dspm_mult_3x3x1_f32_ae32.S"
"modules/matrix/float/dspm_mult_3x3x3_f32_ae32.S"
"modules/matrix/float/dspm_mult_4x4x1_f32_ae32.S"
"modules/matrix/float/dspm_mult_4x4x4_f32_ae32.S"
"modules/matrix/float/dspm_mult_f32_ae32.S"
"modules/matrix/float/dspm_mult_f32_ansi.c"
"modules/matrix/fixed/dspm_mult_s16_ae32.S"
"modules/matrix/fixed/dspm_mult_s16_m_ae32_vector.S"
"modules/matrix/fixed/dspm_mult_s16_m_ae32.S"
"modules/matrix/fixed/dspm_mult_s16_ansi.c"
"modules/matrix/mat/mat.cpp"
"modules/math/mulc/float/dsps_mulc_f32_ansi.c"
"modules/math/addc/float/dsps_addc_f32_ansi.c"
"modules/math/mulc/fixed/dsps_mulc_s16_ansi.c"
"modules/math/mulc/fixed/dsps_mulc_s16_ae32.S"
"modules/math/add/float/dsps_add_f32_ansi.c"
"modules/math/add/fixed/dsps_add_s16_ansi.c"
"modules/math/add/fixed/dsps_add_s16_ae32.S"
"modules/math/sub/float/dsps_sub_f32_ansi.c"
"modules/math/mul/float/dsps_mul_f32_ansi.c"
"modules/math/mul/fixed/dsps_mul_s16_ansi.c"
"modules/math/mulc/float/dsps_mulc_f32_ae32.S"
"modules/math/addc/float/dsps_addc_f32_ae32.S"
"modules/math/add/float/dsps_add_f32_ae32.S"
"modules/math/sub/float/dsps_sub_f32_ae32.S"
"modules/math/mul/float/dsps_mul_f32_ae32.S"
"modules/math/sqrt/float/dsps_sqrt_f32_ansi.c"
"modules/fft/float/dsps_fft2r_fc32_ae32_.S"
"modules/fft/float/dsps_fft2r_fc32_ansi.c"
"modules/fft/float/dsps_fft2r_fc32_ae32.c"
"modules/fft/float/dsps_fft4r_fc32_ansi.c"
"modules/fft/float/dsps_fft4r_fc32_ae32.c"
"modules/fft/float/dsps_fft2r_bitrev_tables_fc32.c"
"modules/fft/float/dsps_fft4r_bitrev_tables_fc32.c"
"modules/fft/fixed/dsps_fft2r_sc16_ae32.S"
"modules/fft/fixed/dsps_fft2r_sc16_ansi.c"
"modules/dct/float/dsps_dct_f32.c"
"modules/support/snr/float/dsps_snr_f32.cpp"
"modules/support/sfdr/float/dsps_sfdr_f32.cpp"
"modules/support/misc/dsps_d_gen.c"
"modules/support/misc/dsps_h_gen.c"
"modules/support/misc/dsps_tone_gen.c"
"modules/support/view/dsps_view.cpp"
"modules/windows/hann/float/dsps_wind_hann_f32.c"
"modules/windows/blackman/float/dsps_wind_blackman_f32.c"
"modules/windows/blackman_harris/float/dsps_wind_blackman_harris_f32.c"
"modules/windows/blackman_nuttall/float/dsps_wind_blackman_nuttall_f32.c"
"modules/windows/nuttall/float/dsps_wind_nuttall_f32.c"
"modules/windows/flat_top/float/dsps_wind_flat_top_f32.c"
"modules/conv/float/dsps_conv_f32_ansi.c"
"modules/conv/float/dsps_conv_f32_ae32.S"
"modules/conv/float/dsps_corr_f32_ansi.c"
"modules/conv/float/dsps_corr_f32_ae32.S"
"modules/conv/float/dsps_ccorr_f32_ansi.c"
"modules/conv/float/dsps_ccorr_f32_ae32.S"
"modules/iir/biquad/dsps_biquad_f32_ae32.S"
"modules/iir/biquad/dsps_biquad_f32_ansi.c"
"modules/iir/biquad/dsps_biquad_gen_f32.c"
"modules/fir/float/dsps_fir_f32_ae32.S"
"modules/fir/float/dsps_fird_f32_ae32.S"
"modules/fir/float/dsps_fir_f32_ansi.c"
"modules/fir/float/dsps_fir_init_f32.c"
"modules/fir/float/dsps_fird_f32_ansi.c"
"modules/fir/float/dsps_fird_init_f32.c")
set(COMPONENT_ADD_INCLUDEDIRS "modules/dotprod/include"
"modules/support/include"
"modules/windows/include"
"modules/windows/hann/include"
"modules/windows/blackman/include"
"modules/windows/blackman_harris/include"
"modules/windows/blackman_nuttall/include"
"modules/windows/nuttall/include"
"modules/windows/flat_top/include"
"modules/iir/include"
"modules/fir/include"
"modules/math/include"
"modules/math/add/include"
"modules/math/sub/include"
"modules/math/mul/include"
"modules/math/addc/include"
"modules/math/mulc/include"
"modules/math/sqrt/include"
"modules/matrix/include"
"modules/fft/include"
"modules/dct/include"
"modules/conv/include"
"modules/common/include")
set(COMPONENT_PRIV_INCLUDEDIRS "modules/dotprod/float"
"modules/dotprod/fixed"
"modules/common/private_include")
register_component()
#component_compile_options(-ffast-math -O2 -Wno-error=maybe-uninitialized)

View File

@@ -0,0 +1,53 @@
menu "DSP Library"
choice DSP_OPTIMIZATION
bool "DSP Optimization for ESP32"
default DSP_OPTIMIZED
help
An Ansi C version could be used for verification and debug purpose.
config DSP_ANSI
bool "ANSI C"
config DSP_OPTIMIZED
bool "ESP32 Optimized"
endchoice
config DSP_OPTIMIZATION
int
default 0 if DSP_ANSI
default 1 if DSP_OPTIMIZED
choice DSP_MAX_FFT_SIZE
bool "Maximum FFT length"
default DSP_MAX_FFT_SIZE_4096
help
This is default FFT size for internal usage.
config DSP_MAX_FFT_SIZE_512
bool "512"
config DSP_MAX_FFT_SIZE_1024
bool "1024"
config DSP_MAX_FFT_SIZE_2048
bool "2048"
config DSP_MAX_FFT_SIZE_4096
bool "4096"
config DSP_MAX_FFT_SIZE_8192
bool "8192"
config DSP_MAX_FFT_SIZE_16384
bool "16384"
config DSP_MAX_FFT_SIZE_32768
bool "32768"
endchoice
config DSP_MAX_FFT_SIZE
int
default 512 if DSP_MAX_FFT_SIZE_512
default 1024 if DSP_MAX_FFT_SIZE_1024
default 2048 if DSP_MAX_FFT_SIZE_2048
default 4096 if DSP_MAX_FFT_SIZE_4096
default 8192 if DSP_MAX_FFT_SIZE_8192
default 16384 if DSP_MAX_FFT_SIZE_16384
default 32768 if DSP_MAX_FFT_SIZE_32768
endmenu

202
components/esp-dsp/LICENSE Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,71 @@
# Espressif DSP Library
ESP-DSP is the official DSP library for the [ESP32](https://espressif.com/en/products/hardware/esp32/overview) chip.
## Overview
ESP-DSP is intended to be used as an [ESP-IDF](https://github.com/espressif/esp-idf) component. For the introduction to ESP-IDF, refer to the [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/).
The ESP-DSP library includes implementations of the following functions:
- Matrix multiplication: [reference](https://docs.espressif.com/projects/esp-dsp/en/latest/esp-dsp-apis.html#matrix-operations-apis)
- Dot product: [reference](https://docs.espressif.com/projects/esp-dsp/en/latest/esp-dsp-apis.html#dot-product), [example](https://github.com/espressif/esp-dsp/tree/master/examples/dotprod)
- FFT: [reference](https://docs.espressif.com/projects/esp-dsp/en/latest/esp-dsp-apis.html#fft), [example](https://github.com/espressif/esp-dsp/tree/master/examples/fft)
- IIR: [reference](https://docs.espressif.com/projects/esp-dsp/en/latest/esp-dsp-apis.html#iir), [example](https://github.com/espressif/esp-dsp/tree/master/examples/iir)
- FIR: [reference](https://docs.espressif.com/projects/esp-dsp/en/latest/esp-dsp-apis.html#fir)
- Vector math operations: [reference](https://docs.espressif.com/projects/esp-dsp/en/latest/esp-dsp-apis.html#math)
Many of the library functions are written in assembly and are optimized for the CPU configuration used in the ESP32. In addition to the optimized implementations, reference implementations written in ANSI C are provided.
Function implementations are provided for single precision floating point (32-bit float), and 16-bit signed integers.
## Documentation
- [ESP-DSP Overview](https://docs.espressif.com/projects/esp-dsp/en/latest/esp-dsp-library.html)
- [ESP-DSP API Reference](https://docs.espressif.com/projects/esp-dsp/en/latest/esp-dsp-apis.html)
- [ESP-DSP Benchmarks](https://docs.espressif.com/projects/esp-dsp/en/latest/esp-dsp-benchmarks.html)
Documentation found in the above links is automatically generated from the contents of this repository. If you find that some information is missing or incomplete, please report an issue.
## Installation and Usage
The ESP-DSP library is a component for the [ESP-IDF build system](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html). It also works with the [new CMake-based build system](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system-cmake.html).
### Trying out ESP-DSP examples
If you haven't created an ESP-IDF project yet, and wish to try the examples provided with ESP-DSP, you can clone ESP-DSP repository into any directory, and then run examples from there:
cd ~/esp
git clone https://github.com/espressif/esp-dsp.git
cd esp-dsp/examples/dotprod
make -j4 flash monitor ESPPORT=PORT
or, if you are using CMake based build system,
idf.py -p PORT flash monitor
where `PORT` is the UART port name of your development board, such as `/dev/ttyUSB0` or `COM1`.
Note that you need to set up environment variables (`IDF_PATH`, `PATH`) before building the project. Refer to the [ESP-IDF Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) if you don't have the environment set up yet.
For the list of the examples, please see [README.md](examples/README.md) in the examples directory.
### Including ESP-DSP into your own project
To include ESP-DSP into your ESP-IDF project, clone ESP-DSP repository (or add it as a submodule) into the components directory of the project:
cd your-project-directory
mkdir -p components
cd components
git clone https://github.com/espressif/esp-dsp.git
ESP-IDF build system also allows including components which are not located in the project components directory, using `EXTRA_COMPONENT_DIRS` project variable. Please refer to the [ESP-IDF build system documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html) for details.
## Reporting Issues
If you have found an issue in ESP-DSP, or wish to submit an enhancement request, please use the [Issues](https://github.com/espressif/esp-dsp/issues) section on Github.
For general questions related to this library, please use the [esp32.com forum](https://esp32.com/).
## Copyrights and License
All original source code in this repository is Copyright (C) 2018-2019 Espressif Systems. This source code is licensed under the Apache License 2.0 as described in the file LICENSE.

View File

@@ -0,0 +1,52 @@
#!/bin/bash
#
# Build all examples from the examples directory.
#
# -----------------------------------------------------------------------------
# Safety settings (see https://gist.github.com/ilg-ul/383869cbb01f61a51c4d).
if [[ ! -z ${DEBUG_SHELL} ]]
then
set -x # Activate the expand mode if DEBUG is anything but empty.
fi
set -o errexit # Exit if command failed.
set -o pipefail # Exit if pipe failed.
set -o nounset # Exit if variable not set.
# Remove the initial space and instead use '\n'.
IFS=$'\n\t'
STARS='***************************************************'
# -----------------------------------------------------------------------------
die() {
echo "${1:-"Unknown Error"}" 1>&2
exit 1
}
pushd examples
EXAMPLES=$(find . -maxdepth 1 -mindepth 1 -type d | cut -d '/' -f 2)
for NAME in ${EXAMPLES}
do
echo "$STARS"
echo "Building example $NAME with Make"
pushd $NAME
# -j option will be set via MAKEFLAGS in .gitlab-ci.yml
make defconfig && make || die "Make build for ${NAME} has failed"
rm -rf build
echo "$STARS"
echo "Building example $NAME with CMake for Esp32"
idf.py set-target esp32
idf.py build || die "CMake build for ${NAME} has failed for Esp32"
echo "Building example $NAME with CMake for Esp32-s2"
idf.py clean
idf.py set-target esp32s2
idf.py build || die "CMake build for ${NAME} has failed for Esp32-s2"
popd
done
popd

View File

@@ -0,0 +1,87 @@
COMPONENT_ADD_INCLUDEDIRS := modules/dotprod/include \
modules/support/include \
modules/windows/include \
modules/windows/hann/include \
modules/windows/blackman/include \
modules/windows/blackman_harris/include \
modules/windows/blackman_nuttall/include \
modules/windows/nuttall/include \
modules/windows/flat_top/include \
modules/iir/include \
modules/fir/include \
modules/math/include \
modules/math/add/include \
modules/math/sub/include \
modules/math/mul/include \
modules/math/addc/include \
modules/math/mulc/include \
modules/math/sqrt/include \
modules/matrix/include \
modules/fft/include \
modules/dct/include \
modules/conv/include \
modules/common/include
COMPONENT_SRCDIRS :=. \
modules/common \
modules/common/misc \
modules/dotprod \
modules/dotprod/float \
modules/dotprod/fixed \
modules/matrix \
modules/matrix/float \
modules/matrix/fixed \
modules/matrix/mat \
modules/math \
modules/math/mulc \
modules/math/mulc/float \
modules/math/mulc/fixed \
modules/math/addc \
modules/math/addc/float \
modules/math/add \
modules/math/add/float \
modules/math/add/fixed \
modules/math/mul \
modules/math/mul/float \
modules/math/mul/fixed \
modules/math/sub \
modules/math/sub/float \
modules/math/sqrt/float \
modules/fft/float \
modules/fft/fixed \
modules/support \
modules/support/snr/float \
modules/support/sfdr/float \
modules/support/misc \
modules/support/view \
modules/windows/hann \
modules/windows/hann/float \
modules/windows/hann/fixed \
modules/windows/blackman \
modules/windows/blackman/float \
modules/windows/blackman/fixed \
modules/windows/blackman_harris \
modules/windows/blackman_harris/float \
modules/windows/blackman_harris/fixed \
modules/windows/blackman_nuttall \
modules/windows/blackman_nuttall/float \
modules/windows/blackman_nuttall/fixed \
modules/windows/nuttall \
modules/windows/nuttall/float \
modules/windows/nuttall/fixed \
modules/windows/flat_top \
modules/windows/flat_top/float \
modules/windows/flat_top/fixed \
modules/conv \
modules/conv/float \
modules/dct \
modules/dct/float \
modules/iir \
modules/iir/biquad \
modules/fir \
modules/fir/float
COMPONENT_PRIV_INCLUDEDIRS := modules/dotprod/float \
modules/dotprod/fixed \
modules/common/private_include

View File

@@ -0,0 +1,109 @@
# This is Doxygen configuration file
#
# Doxygen provides over 260 configuration statements
# To make this file easier to follow,
# it contains only statements that are non-default
#
# NOTE:
# It is recommended not to change defaults unless specifically required
# Test any changes how they affect generated documentation
# Make sure that correct warnings are generated to flag issues with documented code
#
# For the complete list of configuration statements see:
# https://www.stack.nl/~dimitri/doxygen/manual/config.html
PROJECT_NAME = "Espressif DSP Library"
## The 'INPUT' statement below is used as input by script 'gen-df-input.py'
## to automatically generate API reference list files heder_file.inc
## These files are placed in '_inc' directory
## and used to include in API reference documentation
INPUT = \
##
## Common - API Reference
##
../modules/common/include/dsp_common.h \
## Dot Product - API Reference
../modules/dotprod/include/dsps_dotprod.h \
## FFT - API Reference
../modules/fft/include/dsps_fft2r.h \
## DCT - API Reference
../modules/dct/include/dsps_dct.h \
## FIR Filter - API Reference
../modules/fir/include/dsps_fir.h \
## IIR Filter - API Reference
../modules/iir/include/dsps_biquad_gen.h \
../modules/iir/include/dsps_biquad.h \
## Math - API Reference
../modules/math/mulc/include/dsps_mulc.h \
../modules/math/addc/include/dsps_addc.h \
../modules/math/add/include/dsps_add.h \
../modules/math/sub/include/dsps_sub.h \
../modules/math/mul/include/dsps_mul.h \
## Matrix - API Reference
../modules/matrix/include/dspm_mult.h \
../modules/matrix/include/mat.h \
## Convolution/Cprrelation - API Reference
../modules/conv/include/dsps_conv.h \
../modules/conv/include/dsps_corr.h \
## Support - API Reference
../modules/support/include/dsps_view.h \
../modules/support/include/dsps_tone_gen.h \
../modules/support/include/dsps_snr.h \
../modules/support/include/dsps_sfdr.h \
../modules/support/include/dsps_d_gen.h \
../modules/support/include/dsps_h_gen.h \
## Windows - API Reference
../modules/windows/hann/include/dsps_wind_hann.h \
../modules/windows/blackman/include/dsps_wind_blackman.h \
../modules/windows/blackman_harris/include/dsps_wind_blackman_harris.h \
../modules/windows/blackman_nuttall/include/dsps_wind_blackman_nuttall.h \
../modules/windows/nuttall/include/dsps_wind_nuttall.h \
../modules/windows/flat_top/include/dsps_wind_flat_top.h
## Get warnings for functions that have no documentation for their parameters or return value
##
WARN_NO_PARAMDOC = YES
## Enable preprocessing and remove __attribute__(...) expressions from the INPUT files
##
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
PREDEFINED = \
__attribute__(x)= \
IRAM_ATTR= \
configSUPPORT_DYNAMIC_ALLOCATION=1 \
configSUPPORT_STATIC_ALLOCATION=1 \
configQUEUE_REGISTRY_SIZE=1 \
configUSE_RECURSIVE_MUTEXES=1 \
configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS=1 \
configNUM_THREAD_LOCAL_STORAGE_POINTERS=1 \
configUSE_APPLICATION_TASK_TAG=1 \
configTASKLIST_INCLUDE_COREID=1
## Do not complain about not having dot
##
HAVE_DOT = NO
## Generate XML that is required for Breathe
##
GENERATE_XML = YES
XML_OUTPUT = xml
GENERATE_HTML = NO
HAVE_DOT = NO
GENERATE_LATEX = NO
GENERATE_MAN = YES
GENERATE_RTF = NO
## Skip distracting progress messages
##
QUIET = YES
## Log warnings in a file for further review
##
WARN_LOGFILE = "doxygen-warning-log.txt"

View File

@@ -0,0 +1,2 @@
LANGUAGE=en
include docs_common.mk

View File

@@ -0,0 +1,46 @@
#!/bin/bash
#
# Check for Documentation warnings:
# doxygen-warning-log.txt should be an empty file
# sphinx-warning-log.txt should only contain (fuzzy) matches to ../sphinx-known-warnings.txt
RESULT=0
STARS='***************************************************'
if [ -s doxygen-warning-log.txt ]; then
echo "$STARS"
echo "Build failed due to doxygen warnings:"
cat doxygen-warning-log.txt
echo "$STARS"
RESULT=1
fi
SED=sed
os_name=`uname -s`
if [[ "${os_name}" == "Darwin" ]]; then
SED=gsed
fi
# Remove escape characters, file paths, line numbers from
# the Sphinx warning log
# (escape char removal from https://www.commandlinefu.com/commands/view/6141/remove-color-codes-special-characters-with-sed
${SED} -r 's:\x1B\[[0-9;]*[mK]::g' sphinx-warning-log.txt | \
${SED} -E "s~${IDF_PATH}~\${IDF_PATH}~" | \
${SED} -E "s/:[0-9]+:/:line:/" > sphinx-warning-log-sanitized.txt
# diff sanitized warnings, ignoring lines which only appear in ../sphinx-known-warnings.txt
# format is to display only lines new or changed in second argument
DIFF_FORMAT="--unchanged-line-format= --old-line-format= --new-line-format=%L"
SPHINX_WARNINGS=$(diff $DIFF_FORMAT sphinx-known-warnings.txt sphinx-warning-log-sanitized.txt)
if ! [ -z "$SPHINX_WARNINGS" ]; then
echo "$STARS"
echo "Build failed due to new/different Sphinx warnings:"
echo "$SPHINX_WARNINGS"
echo "$STARS"
RESULT=1
echo "(Check files sphinx-known-warnings.txt and sphinx-warning-log.txt for full details.)"
fi
exit $RESULT

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
#
# English Language RTD & Sphinx config file
#
# Uses IDF_PATH/conf_common.py for most non-language-specific settings.
# Importing conf_common adds all the non-language-specific
# parts to this conf module
import sys, os
sys.path.insert(0, os.path.abspath('.'))
from conf_common import *
# General information about the project.
project = u'Espressif DSP Library'
copyright = u'2016 - 2018, Espressif Systems (Shanghai) PTE LTD'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
language = 'en'

View File

@@ -0,0 +1,317 @@
# -*- coding: utf-8 -*-
#
# Common (non-language-specific) configuration for Read The Docs & Sphinx
#
# Based on a Read the Docs Template documentation build configuration file,
# created by sphinx-quickstart on Tue Aug 26 14:19:49 2014.
#
# This file is imported from a language-specific conf.py (ie en/conf.py or
# zh_CN/conf.py)
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
from __future__ import print_function
from __future__ import unicode_literals
import sys, os
import re
import subprocess
import shlex
# Note: If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute
sys.path.insert(0, os.path.abspath('.'))
from local_util import run_cmd_get_output, copy_if_modified
# build_docs on the CI server sometimes fails under Python3. This is a workaround:
sys.setrecursionlimit(3500)
try:
builddir = os.environ['BUILDDIR']
except KeyError:
builddir = '_build'
def call_with_python(cmd):
# using sys.executable ensures that the scripts are called with the same Python interpreter
if os.system('{} {}'.format(sys.executable, cmd)) != 0:
raise RuntimeError('{} failed'.format(cmd))
# Call Doxygen to get XML files from the header files
print("Calling Doxygen to generate latest XML files")
if os.system("doxygen Doxyfile") != 0:
raise RuntimeError('Doxygen call failed')
# Doxygen has generated XML files in 'xml' directory.
# Copy them to 'xml_in', only touching the files which have changed.
copy_if_modified('xml/', 'xml_in/')
# Generate 'api_name.inc' files using the XML files by Doxygen
call_with_python('gen-dxd.py')
# http://stackoverflow.com/questions/12772927/specifying-an-online-image-in-sphinx-restructuredtext-format
#
suppress_warnings = ['image.nonlocal_uri']
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['breathe', 'link-roles' ]
# Enabling this fixes cropping of blockdiag edge labels
seqdiag_antialias = True
# Breathe extension variables
# Doxygen regenerates files in 'xml/' directory every time,
# but we copy files to 'xml_in/' only when they change, to speed up
# incremental builds.
breathe_projects = { "esp32-idf": "xml_in/" }
breathe_default_project = "esp32-idf"
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = ['.rst', '.md']
source_parsers = {
'.md': 'recommonmark.parser.CommonMarkParser',
}
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# Readthedocs largely ignores 'version' and 'release', and displays one of
# 'latest', tag name, or branch name, depending on the build type.
# Still, this is useful for non-RTD builds.
# This is supposed to be "the short X.Y version", but it's the only version
# visible when you open index.html.
# Display full version to make things less confusing.
version = run_cmd_get_output('git describe')
# The full version, including alpha/beta/rc tags.
# If needed, nearest tag is returned by 'git describe --abbrev=0'.
release = version
print('Version: {0} Release: {1}'.format(version, release))
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build', 'issue_template.md']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = idf_path + "/docs/_static/espressif-logo.svg"
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
#html_static_path = [idf_path + '/docs/_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'ReadtheDocsTemplatedoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'ReadtheDocsTemplate.tex', u'Read the Docs Template Documentation',
u'Read the Docs', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'readthedocstemplate', u'Read the Docs Template Documentation',
[u'Read the Docs'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'ReadtheDocsTemplate', u'Read the Docs Template Documentation',
u'Read the Docs', 'ReadtheDocsTemplate', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
# Override RTD CSS theme to introduce the theme corrections
# https://github.com/rtfd/sphinx_rtd_theme/pull/432
# def setup(app):
# app.add_stylesheet('theme_overrides.css')
# generate_version_specific_includes(app)

View File

@@ -0,0 +1,214 @@
# "Common" Makefile for Sphinx documentation
#
# (included from en/Makefile & zh_CN/Makefile
#
# NOTE: This makefile runs with cwd=either en or zh_CN subfolder, so this
# (docs/) directory is '..' relative to it.
# ************ IMPORTANT *****************
#
# ReadTheDocs DOES NOT USE THIS MAKEFILE,
# so any behaviour additions must be
# done via Sphinx Config not here
#
# ****************************************
# You can set these variables from the command line.
SPHINXOPTS =
# note: this is changed from sphinx-build so it depends on default python interpreter, not on /bin/sphinx-build
# (which will be the most recently installed version of sphinx and may not match)
SPHINXBUILD = python -m sphinx
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) -w sphinx-warning-log.txt .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext dependencies version-specific-includes check_python_packages
help:
@echo "Please use \`make <target>\' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled) "
clean:
rm -rf $(BUILDDIR)/*
check_python_packages:
true
html: | check_python_packages
@echo "Build the HTML pages are in $(BUILDDIR)/html."
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml: | check_python_packages
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml: | check_python_packages
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle: | check_python_packages
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json: | check_python_packages
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp: | check_python_packages
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp: | check_python_packages
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ReadtheDocsTemplate.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ReadtheDocsTemplate.qhc"
devhelp: | check_python_packages
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/ReadtheDocsTemplate"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ReadtheDocsTemplate"
@echo "# devhelp"
epub: | check_python_packages
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex: | check_python_packages
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf: | check_python_packages
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja: | check_python_packages
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text: | check_python_packages
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man: | check_python_packages
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo: | check_python_packages
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info: | check_python_packages
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext: | check_python_packages
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes: | check_python_packages
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck: | check_python_packages
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
gh-linkcheck: | check_python_packages
@echo "Checking for hardcoded GitHub links"
@if (find ../ -name '*.rst' | xargs grep \
'https://github.com/espressif/esp-idf/tree\|https://github.com/espressif/esp-idf/blob\|https://github.com/espressif/esp-idf/raw'\
); \
then \
echo "WARNINIG: Some .rst files contain hardcoded Github links."; \
echo "Please check above output and replace links with one of the following:"; \
echo "- :idf:\`dir\` - points to directory inside ESP-IDF"; \
echo "- :idf_file:\`file\` - points to file inside ESP-IDF"; \
echo "- :idf_raw:\`file\` - points to raw view of the file inside ESP-IDF"; \
echo "- :component:\`dir\` - points to directory inside ESP-IDF components dir"; \
echo "- :component_file:\`file\` - points to file inside ESP-IDF components dir"; \
echo "- :component_raw:\`file\` - points to raw view of the file inside ESP-IDF"; \
echo " components dir"; \
echo "- :example:\`dir\` - points to directory inside ESP-IDF examples dir"; \
echo "- :example_file:\`file\` - points to file inside ESP-IDF examples dir"; \
echo "- :example_raw:\`file\` - points to raw view of the file inside ESP-IDF"; \
echo " examples dir"; \
echo "These link types will point to the correct GitHub version automatically"; \
exit 1; \
fi
@echo "No hardcoded links found"
doctest: | check_python_packages
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
xml: | check_python_packages
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml: | check_python_packages
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."

View File

@@ -0,0 +1,108 @@
Espressif DSP Library API Reference
===================================
Header Files
------------
To use the library, include ``esp_dsp.h`` header file into the source code.
* :repo_file:`modules/common/include/esp_dsp.h`
Signal (1D) Processing APIs
----------------------------
Signal processing APIs use ``dsps`` prefix. The following modules are available:
- Dot-product_ - Calculates dot-product of two vectors
- FFT_ - Fast Fourier Transform functionality
- DCT_ - Discrete Cosine Transform functionality
- IIR_ - IIR filter functionality
- FIR_ - FIR filter functionality
- Math_ - Basic vector operations
- Conv_ - Convolution/correlation functionality
- Support_ - Support functions
- `Window functions`_ - FFT window generation functions
Dot-product
+++++++++++
.. include:: /_build/inc/dsps_dotprod.inc
FFT
+++
.. include:: /_build/inc/dsps_fft2r.inc
DCT
+++
.. include:: /_build/inc/dsps_dct.inc
FIR
+++
.. include:: /_build/inc/dsps_fir.inc
IIR
+++
.. include:: /_build/inc/dsps_biquad_gen.inc
.. include:: /_build/inc/dsps_biquad.inc
Math
++++
.. include:: /_build/inc/dsps_add.inc
.. include:: /_build/inc/dsps_sub.inc
.. include:: /_build/inc/dsps_mul.inc
.. include:: /_build/inc/dsps_addc.inc
.. include:: /_build/inc/dsps_mulc.inc
Conv
++++
.. include:: /_build/inc/dsps_conv.inc
.. include:: /_build/inc/dsps_corr.inc
Support
+++++++
.. include:: /_build/inc/dsps_d_gen.inc
.. include:: /_build/inc/dsps_h_gen.inc
.. include:: /_build/inc/dsps_tone_gen.inc
.. include:: /_build/inc/dsps_view.inc
.. include:: /_build/inc/dsps_snr.inc
.. include:: /_build/inc/dsps_sfdr.inc
Window Functions
++++++++++++++++
.. include:: /_build/inc/dsps_wind_hann.inc
Matrix Operations APIs
----------------------
Matrix operations APIs use ``dspm`` prefix. The following modules are available:
- Multiplication - basic matrix multiplication operations
Matrix Multiplication
+++++++++++++++++++++
.. include:: /_build/inc/dspm_mult.inc
Matrix Operations
+++++++++++++++++++++
.. include:: /_build/inc/mat.inc
Miscellaneous
-------------
Various common functions used by other modules are included in this module.
Common APIs
+++++++++++
.. include:: /_build/inc/dsp_common.inc

View File

@@ -0,0 +1,66 @@
Espressif DSP Library Benchmarks
================================
The table bellow contains benchmarks of functions provided by ESP-DSP library. The values are CPU cycle counts taken to execute each of the functions. Values in "ESP32" column are for the optimized (assembly) implementation, values in "ANSI C" column are for the non-optimized implementation.
+----------------------------------------------------------+----------+----------+
| Function name and arguments | CPU cycles |
+----------------------------------------------------------+----------+----------+
| | ESP32 | ANSI C |
+==========================================================+==========+==========+
| | | |
+----------------------------------------------------------+----------+----------+
| **Dot Product** | | |
+----------------------------------------------------------+----------+----------+
| dsps_dotprod_f32 for N=256 points | 1057 | 2597 |
+----------------------------------------------------------+----------+----------+
| dsps_dotprode_f32 for N=256 points, with step 1 | 1318 | 2601 |
+----------------------------------------------------------+----------+----------+
| dsps_dotprod_s16 for N=256 points | 448 | 5185 |
+----------------------------------------------------------+----------+----------+
| | | |
+----------------------------------------------------------+----------+----------+
| **FIR Filters** | | |
+----------------------------------------------------------+----------+----------+
| dsps_fir_f32 1024 input samples and 256 coefficients | 1338418 | 3583556 |
+----------------------------------------------------------+----------+----------+
| dsps_fird_f32 1024 samples, 256 coeffs and decimation 4 | 37582 | 82535 |
+----------------------------------------------------------+----------+----------+
| | | |
+----------------------------------------------------------+----------+----------+
| **FFTs** | | |
+----------------------------------------------------------+----------+----------+
| dsps_fft2r_fc32 for 64 complex points | 5451 | 8187 |
+----------------------------------------------------------+----------+----------+
| dsps_fft2r_fc32 for 128 complex points | 12400 | 18756 |
+----------------------------------------------------------+----------+----------+
| dsps_fft2r_fc32 for 256 complex points | 27829 | 42381 |
+----------------------------------------------------------+----------+----------+
| dsps_fft2r_fc32 for 512 complex points | 61755 | 94616 |
+----------------------------------------------------------+----------+----------+
| dsps_fft2r_fc32 for 1024 complex points | 135745 | 209058 |
+----------------------------------------------------------+----------+----------+
| | | |
+----------------------------------------------------------+----------+----------+
| **IIR Filters** | | |
+----------------------------------------------------------+----------+----------+
| dsps_biquad_f32 - biquad filter for 1024 input samples | 17451 | 31778 |
+----------------------------------------------------------+----------+----------+
| | | |
+----------------------------------------------------------+----------+----------+
| **Matrix Multiplication** | | |
+----------------------------------------------------------+----------+----------+
| dspm_mult_f32 - C[16,16] = A[16,16]*B[16,16]; | 24669 | 59690 |
+----------------------------------------------------------+----------+----------+
| dspm_mult_s16 - C[16,16] = A[16,16]*B[16,16]; | 24964 | 114150 |
+----------------------------------------------------------+----------+----------+
| dspm_mult_3x3x1_f32 - C[3,1] = A[3,3]*B[3,1]; | 80 | 242 |
+----------------------------------------------------------+----------+----------+
| dspm_mult_3x3x3_f32 - C[3,3] = A[3,3]*B[3,3]; | 212 | 541 |
+----------------------------------------------------------+----------+----------+
| dspm_mult_4x4x1_f32 - C[4,1] = A[4,4]*B[4,1]; | 112 | 362 |
+----------------------------------------------------------+----------+----------+
| dspm_mult_4x4x4_f32 - C[4,4] = A[4,4]*B[4,4]; | 404 | 1130 |
+----------------------------------------------------------+----------+----------+
The benchmark test could be reproduced by executing test cases found in :repo_file:`test/test_dsp.c`.

View File

@@ -0,0 +1,78 @@
Espressif DSP Library
=====================
Overview
--------
An Espressif DSP Library (esp-dsp) it's library of functions, modules and components that provides possibility
to use Espressif's CPUs as DSPs in efficient way.
Function Naming
---------------
Naming conventions for the Library functions are similar for all covered domains. You can distinguish signal processing functions by the dsps prefix, while image and video processing functions have dspi prefix,
and functions that are specific for operations on small matrices have dspm prefix in their names. Function names in Library have the following general format:
dsp<data-domain>_<name>_<datatype1><datatype_ext>_<datatype2><datatype_ext>[_<descriptor>]<_impl>(<parameters>);
The elements of this format are explained in the sections that follow.
Data-Domain
^^^^^^^^^^^
The data-domain is a single character that expresses the subset of functionality to which a given function belongs. The Library designed to supports the following data-domains:
- s - for signals (expected data type is a 1D signal)
- i - for images and video (expected data type is a 2D image)
- m - for matrices (expected data type is a matrix)
- r - for realistic rendering functionality and 3D data processing (expected data type depends on supported rendering techniques)
- q - for signals of fixed length
For example, function names that begin with dspi signify that respective functions are used for image or video processing.
Name
^^^^
The name is an abbreviation for the core operation that the function really does, for example Add, Sqrt,
followed in some cases by a function-specific modifier: = [_modifier]
This modifier, if present, denotes a slight modification or variation of the given function.
Data Types
^^^^^^^^^^
The library supports two main data types int16 for fixed point arithmetic and float for floating point arithmetic. The datatype described as:
Data type suffices:
^^^^^^^^^^^^^^^^^^^
- s - signed
- u - unsigned
- f - float
Data type extensions:
^^^^^^^^^^^^^^^^^^^^^
- c - complex
Data type Bits resolution:
^^^^^^^^^^^^^^^^^^^^^^^^^^
- 16
- 32
For example: dsps_mac_sc16 defines that mac operation with 1d array will be made with 16 bit signed complex data.
Implementation Type
^^^^^^^^^^^^^^^^^^^
Each function could be implemented different for different platform and could use different style and resources.
That's why every implemented function will have name extension <_impl> that will define which kind of implementation it is.
User can use universal function without extension.
Implementation extensions:
^^^^^^^^^^^^^^^^^^^^^^^^^^
By default all functions could be used without extensions. The option that select optimized/ansi can be chosen in menuconfig.
Inside library the extensions means:
- _ansi - a universal function where body of function implemented on ANSI C. This implementation not includes any hardware optimization
- _ae32 - written on ESP32 assembler and optimized for ESP32
- _platform - header file with definitions of available CPUs instructions for different functions
- others- depends on amount of supported CPUs. This list will be extended in future

View File

@@ -0,0 +1,318 @@
#!/usr/bin/env python
#
# gen-dxd.py - Generate Doxygen Directives
#
# This code is in the Public Domain (or CC0 licensed, at your option.)
# Unless required by applicable law or agreed to in writing, this
# software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied.
#
from __future__ import print_function
from __future__ import unicode_literals
from builtins import range
from io import open
import sys
import os
import re
# Determime build directory
builddir = '_build'
if 'BUILDDIR' in os.environ:
builddir = os.environ['BUILDDIR']
# Script configuration
header_file_path_prefix = "../modules/"
"""string: path prefix for header files.
"""
doxyfile_path = "./Doxyfile"
"""string: path to a file containing header files to processs.
"""
xml_directory_path = "xml"
"""string: path to directory with XML files by Doxygen.
"""
inc_directory_path = os.path.join(builddir, 'inc')
"""string: path prefix for header files.
"""
all_kinds = [
("function", "Functions"),
("union", "Unions"),
("struct", "Structures"),
("define", "Macros"),
("typedef", "Type Definitions"),
("enum", "Enumerations")
]
"""list of items that will be generated for a single API file
"""
def get_doxyfile_input():
"""Get contents of Doxyfile's INPUT statement.
Returns:
Contents of Doxyfile's INPUT.
"""
if not os.path.isfile(doxyfile_path):
print("Doxyfile '%s' does not exist!" % doxyfile_path)
sys.exit()
print("Getting Doxyfile's INPUT")
input_file = open(doxyfile_path, "r", encoding='utf-8')
line = input_file.readline()
# read contents of Doxyfile until 'INPUT' statement
while line:
if line.find("INPUT") == 0:
break
line = input_file.readline()
doxyfile_INPUT = ""
line = input_file.readline()
# skip input_file contents until end of 'INPUT' statement
while line:
if line.isspace():
# we have reached the end of 'INPUT' statement
break
# process only lines that are not comments
if line.find("#") == -1:
# extract header file path inside components folder
m = re.search(header_file_path_prefix + "(.*\.h)", line)
header_file_path = m.group(1)
doxyfile_INPUT += "modules/" + header_file_path + "\n"
# proceed reading next line
line = input_file.readline()
input_file.close()
return doxyfile_INPUT
def get_api_name(header_file_path):
"""Get name of API from header file path.
Args:
header_file_path: path to the header file.
Returns:
The name of API.
"""
api_name = ""
regex = r".*/(.*)\.h"
m = re.search(regex, header_file_path)
if m:
api_name = m.group(1)
return api_name
def get_rst_header(header_name):
"""Get rst formatted code with a header.
Args:
header_name: name of header.
Returns:
Formatted rst code with the header.
"""
rst_output = ""
rst_output += header_name + "\n"
rst_output += "^" * len(header_name) + "\n"
rst_output += "\n"
return rst_output
def select_unions(innerclass_list):
"""Select unions from innerclass list.
Args:
innerclass_list: raw list with unions and structures
extracted from Dogygen's xml file.
Returns:
Doxygen directives with unions selected from the list.
"""
rst_output = ""
for line in innerclass_list.splitlines():
# union is denoted by "union" at the beginning of line
if line.find("union") == 0:
union_id, union_name = re.split(r"\t+", line)
rst_output += ".. doxygenunion:: "
rst_output += union_name
rst_output += "\n"
return rst_output
def select_structs(innerclass_list):
"""Select structures from innerclass list.
Args:
innerclass_list: raw list with unions and structures
extracted from Dogygen's xml file.
Returns:
Doxygen directives with structures selected from the list.
Note: some structures are excluded as described on code below.
"""
rst_output = ""
for line in innerclass_list.splitlines():
# structure is denoted by "struct" at the beginning of line
if line.find("struct") == 0:
# skip structures that are part of union
# they are documented by 'doxygenunion' directive
if line.find("::") > 0:
continue
struct_id, struct_name = re.split(r"\t+", line)
rst_output += ".. doxygenstruct:: "
rst_output += struct_name
rst_output += "\n"
rst_output += " :members:\n"
rst_output += "\n"
return rst_output
def get_directives(tree, kind):
"""Get directives for specific 'kind'.
Args:
tree: the ElementTree 'tree' of XML by Doxygen
kind: name of API "kind" to be generated
Returns:
Doxygen directives for selected 'kind'.
Note: the header with "kind" name is included.
"""
rst_output = ""
if kind in ["union", "struct"]:
innerclass_list = ""
for elem in tree.iterfind('compounddef/innerclass'):
innerclass_list += elem.attrib["refid"] + "\t" + elem.text + "\n"
if kind == "union":
rst_output += select_unions(innerclass_list)
else:
rst_output += select_structs(innerclass_list)
else:
for elem in tree.iterfind(
'compounddef/sectiondef/memberdef[@kind="%s"]' % kind):
name = elem.find('name')
rst_output += ".. doxygen%s:: " % kind
rst_output += name.text + "\n"
if rst_output:
all_kinds_dict = dict(all_kinds)
rst_output = get_rst_header(all_kinds_dict[kind]) + rst_output + "\n"
return rst_output
def generate_directives(header_file_path):
"""Generate API reference with Doxygen directives for a header file.
Args:
header_file_path: a path to the header file with API.
Returns:
Doxygen directives for the header file.
"""
api_name = get_api_name(header_file_path)
# in XLT file name each "_" in the api name is expanded by Doxygen to "__"
xlt_api_name = api_name.replace("_", "__")
xml_file_path = "%s/%s_8h.xml" % (xml_directory_path, xlt_api_name)
rst_output = ""
rst_output = ".. File automatically generated by 'gen-dxd.py'\n"
rst_output += "\n"
rst_output += get_rst_header("Header File")
rst_output += "* :repo_file:`" + header_file_path + "`\n"
rst_output += "\n"
try:
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET
tree = ET.ElementTree(file=xml_file_path)
for i in range(len(all_kinds)):
kind = all_kinds[i][0]
rst_output += get_directives(tree, kind)
return rst_output
def generate_api_inc_files():
"""Generate header_file.inc files
with API reference made of doxygen directives
for each header file
specified in the 'INPUT' statement of Doxyfile.
"""
if not os.path.isdir(xml_directory_path):
print("Directory %s does not exist!" % xml_directory_path)
sys.exit()
if not os.path.exists(inc_directory_path):
os.makedirs(inc_directory_path)
list_to_generate = get_doxyfile_input()
print("Generating 'api_name.inc' files with Doxygen directives")
for header_file_path in list_to_generate.splitlines():
api_name = get_api_name(header_file_path)
inc_file_path = inc_directory_path + "/" + api_name + ".inc"
rst_output = generate_directives(header_file_path)
previous_rst_output = ''
if os.path.isfile(inc_file_path):
with open(inc_file_path, "r", encoding='utf-8') as inc_file_old:
previous_rst_output = inc_file_old.read()
if previous_rst_output != rst_output:
with open(inc_file_path, "w", encoding='utf-8') as inc_file:
inc_file.write(rst_output)
if __name__ == "__main__":
"""The main script that generates
Doxygen directives.
"""
# Process command line arguments, if any
if len(sys.argv) > 1:
if not os.path.isdir(xml_directory_path):
print("Directory %s does not exist!" % xml_directory_path)
sys.exit()
header_file_path = sys.argv[1]
api_name = get_api_name(header_file_path)
if api_name:
rst_output = generate_directives(header_file_path)
print("Doxygen directives for '%s'" % header_file_path)
print()
print(rst_output)
else:
print("Options to execute 'gen-dxd.py' application:")
print("1: $ python gen-dxd.py")
print(" Generate API 'header_file.inc' files for headers defined in '%s'" % doxyfile_path)
print("2: $ python gen-dxd.py header_file_path")
print(" Print out Doxygen directives for a single header file")
print(" example: $ python gen-dxd.py mdns/include/mdns.h")
print(" NOTE: Run Doxygen first to get XML files for the header file")
sys.exit()
# No command line arguments given
generate_api_inc_files()

View File

@@ -0,0 +1,9 @@
ESP-DSP Library
***************
.. toctree::
:maxdepth: 1
Introduction <esp-dsp-library>
Benchmarks <esp-dsp-benchmarks>
API Reference <esp-dsp-apis>

View File

@@ -0,0 +1,80 @@
----------------------------- Delete below -----------------------------
If your issue is a general question, starts similar to "How do I..", or is related to 3rd party development kits/libs, please discuss this on our community forum at esp32.com instead.
INSTRUCTIONS
============
Before submitting a new issue, please follow the checklist and try to find the answer.
- [ ] I have read the documentation [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/) and the issue is not addressed there.
- [ ] I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
- [ ] I have searched the issue tracker for a similar issue and not found a similar issue.
If the issue cannot be solved after the steps before, please follow these instructions so we can get the needed information to help you in a quick and effective fashion.
1. Fill in all the fields under **Environment** marked with [ ] by picking the correct option for you in each case and deleting the others.
2. Describe your problem.
3. Include [debug logs on the monitor](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/idf-monitor.html#automatically-decoding-addresses) or the [coredump](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/core_dump.html).
4. Provide more items under **Other items if possible** can help us better locate your problem.
5. Use markup (buttons above) and the Preview tab to check what the issue will look like.
6. Delete these instructions from the above to the below marker lines before submitting this issue.
----------------------------- Delete above -----------------------------
## Environment
- Development Kit: [ESP32-Wrover-Kit|ESP32-DevKitC|ESP32-PICO-Kit|ESP32-LyraT|ESP32-LyraTD-MSC|none]
- Kit version (for WroverKit/PicoKit/DevKitC): [v1|v2|v3|v4]
- Module or chip used: [ESP32-WROOM-32|ESP32-WROOM-32D|ESP32-WROOM-32U|ESP32-WROVER|ESP32-WROVER-I|ESP32-WROVER-B|ESP32-WROVER-IB|ESP32-SOLO-1|ESP32-PICO-D4|ESP32]
- IDF version (run ``git describe --tags`` to find it):
// v3.2-dev-1148-g96cd3b75c
- Build System: [Make|CMake]
- Compiler version (run ``xtensa-esp32-elf-gcc --version`` to find it):
// 1.22.0-80-g6c4433a
- Operating System: [Windows|Linux|macOS]
- Power Supply: [USB|external 5V|external 3.3V|Battery]
## Problem Description
//Detailed problem description goes here.
### Expected Behavior
### Actual Behavior
### Steps to repropduce
1. step1
2. ...
// It helps if you attach a picture of your setup/wiring here.
### Code to reproduce this issue
```cpp
// the code should be wrapped in the ```cpp tag so that it will be displayed better.
#include "esp_log.h"
void app_main()
{
}
```
// If your code is longer than 30 lines, [GIST](https://gist.github.com) is preferred.
## Debug Logs
```
Debug log goes here, should contain the backtrace, as well as the reset source if it is a crash.
Please copy the plain text here for us to search the error log. Or attach the complete logs but leave the main part here if the log is *too* long.
```
## Other items if possible
- [ ] sdkconfig file (attach the sdkconfig file from your project folder)
- [ ] elf file in the ``build`` folder (**note this may contain all the code details and symbols of your project.**)
- [ ] coredump (This provides stacks of tasks.)

View File

@@ -0,0 +1,46 @@
# based on http://protips.readthedocs.io/link-roles.html
from __future__ import print_function
from __future__ import unicode_literals
import re
import os
from docutils import nodes
from local_util import run_cmd_get_output
def get_github_rev():
path = run_cmd_get_output('git rev-parse --short HEAD')
tag = run_cmd_get_output('git describe --exact-match')
print('Git commit ID: ', path)
if len(tag):
print('Git tag: ', tag)
path = tag
return path
def setup(app):
rev = get_github_rev()
# links to files or folders on the GitHub
baseurl = 'https://github.com/espressif/esp-dsp'
app.add_role('repo', autolink('{}/tree/{}/%s'.format(baseurl, rev)))
app.add_role('repo_file', autolink('{}/blob/{}/%s'.format(baseurl, rev)))
app.add_role('repo_raw', autolink('{}/raw/{}/%s'.format(baseurl, rev)))
app.add_role('example', autolink('{}/tree/{}/examples/%s'.format(baseurl, rev)))
app.add_role('example_file', autolink('{}/blob/{}/examples/%s'.format(baseurl, rev)))
app.add_role('example_raw', autolink('{}/raw/{}/examples/%s'.format(baseurl, rev)))
def autolink(pattern):
def role(name, rawtext, text, lineno, inliner, options={}, content=[]):
m = re.search('(.*)\s*<(.*)>', text) # noqa: W605 - regular expression
if m:
link_text = m.group(1)
link = m.group(2)
else:
link_text = text
link = text
url = pattern % (link,)
node = nodes.reference(rawtext, link_text, refuri=url, **options)
return [node], []
return role

View File

@@ -0,0 +1,57 @@
# Utility functions used in conf.py
#
# Copyright 2017 Espressif Systems (Shanghai) PTE LTD
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http:#www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import unicode_literals
from io import open
import os
import shutil
def run_cmd_get_output(cmd):
return os.popen(cmd).read().strip()
def files_equal(path_1, path_2):
if not os.path.exists(path_1) or not os.path.exists(path_2):
return False
file_1_contents = ''
with open(path_1, "r", encoding='utf-8') as f_1:
file_1_contents = f_1.read()
file_2_contents = ''
with open(path_2, "r", encoding='utf-8') as f_2:
file_2_contents = f_2.read()
return file_1_contents == file_2_contents
def copy_file_if_modified(src_file_path, dst_file_path):
if not files_equal(src_file_path, dst_file_path):
dst_dir_name = os.path.dirname(dst_file_path)
if not os.path.isdir(dst_dir_name):
os.makedirs(dst_dir_name)
shutil.copy(src_file_path, dst_file_path)
def copy_if_modified(src_path, dst_path):
if os.path.isfile(src_path):
copy_file_if_modified(src_path, dst_path)
return
src_path_len = len(src_path)
for root, dirs, files in os.walk(src_path):
for src_file_name in files:
src_file_path = os.path.join(root, src_file_name)
dst_file_path = os.path.join(dst_path + root[src_path_len:], src_file_name)
copy_file_if_modified(src_file_path, dst_file_path)

View File

@@ -0,0 +1,8 @@
# This is a list of python packages used to generate documentation. This file is used with pip:
# pip install --user -r requirements.txt
#
future
sphinx==1.6.5
sphinx-rtd-theme
breathe==4.7.3
recommonmark

View File

@@ -0,0 +1,18 @@
# ESP-DSP Examples
This directory contains a range of examples for ESP-DSP library.
These examples are intended to demonstrate part of ESP-DSP functionality (e.g. initialization, execution) and to provide code that you can copy and adapt into your own projects.
See the [README.md](../README.md) file in the upper level directory for more information about ESP-DSP.
# Example Layout
The examples are grouped into subdirectories by category. Each category directory contains one or more example projects:
* [Dot Product Calculation](./dotprod/README.md) Example
* [Basic Math Operations](./basic_math/README.md) Example
* [FFT](./fft/README.md) Example
* [Matrix](./matrix/README.md) example
* [FFT Window](./fft_window/README.md) Example
* [IIR Filter](./iir/README.md) Example

View File

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

View File

@@ -0,0 +1,16 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := basic_math
# This line has to be included into the make file
# to include components that are located somewhere
# but not in "component" directory
EXTRA_COMPONENT_DIRS := $(realpath ../..)
EXCLUDE_COMPONENTS := test
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,83 @@
# Basic Math Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates how to use basic math functions from esp-dsp library. Example does the following steps:
1. Initialize the library
2. Initialize input signals with 1024 samples
3. Apply window to input signal by standard C loop.
4. Calculate FFT for 1024 complex samples and show the result
5. Show results on the plots
6. Apply window to input signal by basic math functions dsps_mul_f32 and dsps_mulc_f32.
7. Calculate FFT for 1024 complex samples
8. Show results on the plots
## How to use example
### Hardware required
This example does not require any special hardware, and can be run on any common development board.
### Configure the project
If using Make based build system, run `make menuconfig` and set serial port under Serial Flasher Options.
If using CMake based build system, no configuration is required.
Also, under Component Config ---> DSP Library ---> Optimization for ESP32, it's possible to select optimized or ansi implementation to compare.
### Build and flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
Or, for CMake based build system (replace PORT with serial port name):
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example output
Here is an typical example console output.
```bash
I (132) main: *** Start Example. ***
I (132) main: *** Multiply tone signal with Hann window by standard C loop. ***
I (152) view: Data min[432] = -173.749878, Data max[205] = 23.849705
________________________________________________________________
0 | |
1 | |
2 | |
3 || |
4 | | |
5 || | |
6 ||| || |
7 ||||| |||| |
8||||||||||||||| |||||| |
9 |||||||||||||||||||||||||
0123456789012345678901234567890123456789012345678901234567890123
I (162) view: Plot: Length=512, min=-120.000000, max=40.000000
I (162) main: *** Multiply tone signal with Hann window by esp-dsp basic math functions. ***
I (162) view: Data min[432] = -173.749878, Data max[205] = 23.849705
________________________________________________________________
0 | |
1 | |
2 | |
3 || |
4 | | |
5 || | |
6 ||| || |
7 ||||| |||| |
8||||||||||||||| |||||| |
9 |||||||||||||||||||||||||
0123456789012345678901234567890123456789012345678901234567890123
I (172) view: Plot: Length=512, min=-120.000000, max=40.000000
I (172) main: *** End Example. ***
```

View File

@@ -0,0 +1,4 @@
set(COMPONENT_SRCS "dsps_math_main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()

View File

@@ -0,0 +1,8 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#

View File

@@ -0,0 +1,93 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "soc/gpio_struct.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "soc/uart_struct.h"
#include <math.h>
#include "esp_dsp.h"
static const char *TAG = "main";
// This example shows how to use FFT from esp-dsp library
#define N_SAMPLES 1024
int N = N_SAMPLES;
// Input test array
float x1[N_SAMPLES];
// Window coefficients
float wind[N_SAMPLES];
// working complex array
float y_cf[N_SAMPLES*2];
// Pointers to result arrays
float* y1_cf = &y_cf[0];
static void process_and_show(float* data, int length)
{
dsps_fft2r_fc32(data, length);
// Bit reverse
dsps_bit_rev_fc32(data, length);
// Convert one complex vector to two complex vectors
dsps_cplx2reC_fc32(data, length);
for (int i = 0 ; i < length/2 ; i++) {
data[i] = 10 * log10f((data[i * 2 + 0] * data[i * 2 + 0] + data[i * 2 + 1] * data[i * 2 + 1])/N);
}
// Show power spectrum in 64x10 window from -100 to 0 dB from 0..N/4 samples
dsps_view(data, length/2, 64, 10, -120, 40, '|');
}
void app_main()
{
esp_err_t ret;
ESP_LOGI(TAG, "*** Start Example. ***");
ret = dsps_fft2r_init_fc32(NULL, CONFIG_DSP_MAX_FFT_SIZE);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Not possible to initialize FFT. Error = %i", ret);
return;
}
// Generate Hann window
dsps_wind_hann_f32(wind, N);
ESP_LOGI(TAG, "*** Multiply tone signal with Hann window by standard C loop. ***");
// Generate input signal
dsps_tone_gen_f32(x1, N, 1., 0.2, 0);
// Convert two input vectors to one complex vector
for (int i=0 ; i< N ; i++)
{
y_cf[i*2 + 0] = x1[i]*wind[i];
y_cf[i*2 + 1] = 0;
}
process_and_show(y_cf, N);
ESP_LOGI(TAG, "*** Multiply tone signal with Hann window by esp-dsp basic math functions. ***");
// Convert two input vectors to one complex vector with basic functions
dsps_mul_f32(x1, wind, y_cf, N, 1, 1, 2); // Multiply input array with window and store as real part
dsps_mulc_f32(&y_cf[1], &y_cf[1], N, 0, 2, 2); // Clear imaginary part of the complex signal
process_and_show(y_cf, N);
ESP_LOGI(TAG, "*** End Example. ***");
}

View File

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

View File

@@ -0,0 +1,16 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := dotprod
# This line has to be included into the make file
# to include components that are located somewhere
# but not in "component" directory
EXTRA_COMPONENT_DIRS := $(realpath ../..)
EXCLUDE_COMPONENTS := test
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,50 @@
# Dot Product Calculation Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates how to use dotprod dsps_dotprod_f32 from esp-dsp library. Example does the following steps:
1. Initialize the input arrays
2. Calculate dot product of two arrays
3. Compare results and calculate execution time in cycles.
## How to use example
### Hardware required
This example does not require any special hardware, and can be run on any common development board.
### Configure the project
If using Make based build system, run `make menuconfig` and set serial port under Serial Flasher Options.
If using CMake based build system, no configuration is required.
Also, under Component Config/DSP Library/Optimization for ESP32, it's possible to select optimized or ansi implementation to compare.
### Build and flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
Or, for CMake based build system (replace PORT with serial port name):
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example output
Here is an typical example console output.
```
I (55) main: Start Example.
I (55) main: The sum of 101 elements from 0..100 = 5050.000000
I (55) main: Operation for 101 samples took 1381 cycles
I (65) main: End Example.
```

View File

@@ -0,0 +1,4 @@
set(COMPONENT_SRCS "dsps_dotproduct_main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()

View File

@@ -0,0 +1,8 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#

View File

@@ -0,0 +1,63 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "soc/gpio_struct.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "soc/uart_struct.h"
#include "esp_dsp.h"
static const char *TAG = "main";
// This example shows how to use dsps_dotprod_f32 and dsps_dotprode_f32 functions
#define N_SAMPLES 256
int N = N_SAMPLES;
float input1[N_SAMPLES];
float input2[N_SAMPLES];
void app_main()
{
esp_err_t ret;
ESP_LOGI(TAG, "Start Example.");
// The example will calculate n!
//Initialize an input arrays
for (int i=0 ; i< N ; i++)
{
input1[i] = 1;
input2[i] = i;
}
float result1 = 0;
unsigned int start_b = xthal_get_ccount();
ret = dsps_dotprod_f32(input1, input2, &result1, 101);
unsigned int end_b = xthal_get_ccount();
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Operation error = %i", ret);
}
ESP_LOGI(TAG, "The sum of 101 elements from 0..100 = %f", result1);
ESP_LOGI(TAG, "Operation for 101 samples take %i cycles", end_b - start_b);
ESP_LOGI(TAG, "End Example.");
}

View File

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

View File

@@ -0,0 +1,16 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := fft2r
# This line has to be included into the make file
# to include components that are located somewhere
# but not in "component" directory
EXTRA_COMPONENT_DIRS := $(realpath ../..)
EXCLUDE_COMPONENTS := test
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,99 @@
# FFT Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates how to use FFT functionality from esp-dsp library. Example does the following steps:
1. Initialize the library
2. Initialize input signals with 1024 samples: one 0 dB, second with -20 dB
3. Combine two signals as one complex input signal and apply window to input signals paar.
4. Calculate FFT for 1024 complex samples
5. Apply bit reverse operation for output complex vector
6. Split one complex FFT output spectrum to two real signal spectrums
7. Show results on the plots
8. Show execution time of FFT
## How to use example
### Hardware required
This example does not require any special hardware, and can be run on any common development board.
### Configure the project
If using Make based build system, run `make menuconfig` and set serial port under Serial Flasher Options.
If using CMake based build system, no configuration is required.
Also, under Component Config/DSP Library/Optimization for ESP32, it's possible to select optimized or ansi implementation to compare.
### Build and flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
Or, for CMake based build system (replace PORT with serial port name):
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example output
Here is an typical example console output.
```
I (59) main: Start Example.
W (89) main: Signal x1
I (89) view: Data min[495] = -162.760925, Data max[164] = 23.938747
________________________________________________________________
0 |
1 | |
2 | |
3 | |
4 | |
5 | |
6 | | |
7 | | |
8 || || |
9|||||||||||||||||| ||||||||||||||||||||||||||||||||||||||||||
0123456789012345678901234567890123456789012345678901234567890123
I (159) view: Plot: Length=512, min=-60.000000, max=40.000000
W (169) main: Signal x2
I (169) view: Data min[502] = -164.545135, Data max[205] = 3.857752
________________________________________________________________
0 |
1 |
2 |
3 | |
4 | |
5 | |
6 | |
7 || |
8 | | |
9|||||||||||||||||||||||| ||||||||||||||||||||||||||||||||||||||
0123456789012345678901234567890123456789012345678901234567890123
I (249) view: Plot: Length=512, min=-60.000000, max=40.000000
W (249) main: Signals x1 and x2 on one plot
I (259) view: Data min[505] = -159.215271, Data max[164] = 23.938747
________________________________________________________________
0 |
1 | |
2 | |
3 | | |
4 | | |
5 | | |
6 | | | |
7 | | || |
8 || || | | |
9|||||||||||||||||| | ||||||||||||||||||||||||||||||||||||||
0123456789012345678901234567890123456789012345678901234567890123
I (339) view: Plot: Length=512, min=-60.000000, max=40.000000
I (339) main: FFT for 1024 complex points take 140472 cycles
I (349) main: End Example.
```

View File

@@ -0,0 +1,4 @@
set(COMPONENT_SRCS "dsps_fft_main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()

View File

@@ -0,0 +1,8 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#

View File

@@ -0,0 +1,101 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "soc/gpio_struct.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "soc/uart_struct.h"
#include <math.h>
#include "esp_dsp.h"
static const char *TAG = "main";
// This example shows how to use FFT from esp-dsp library
#define N_SAMPLES 1024
int N = N_SAMPLES;
// Input test array
float x1[N_SAMPLES];
float x2[N_SAMPLES];
// Window coefficients
float wind[N_SAMPLES];
// working complex array
float y_cf[N_SAMPLES*2];
// Pointers to result arrays
float* y1_cf = &y_cf[0];
float* y2_cf = &y_cf[N_SAMPLES];
// Sum of y1 and y2
float sum_y[N_SAMPLES/2];
void app_main()
{
esp_err_t ret;
ESP_LOGI(TAG, "Start Example.");
ret = dsps_fft2r_init_fc32(NULL, CONFIG_DSP_MAX_FFT_SIZE);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Not possible to initialize FFT. Error = %i", ret);
return;
}
// Generate hann window
dsps_wind_hann_f32(wind, N);
// Generate input signal for x1 A=1 , F=0.1
dsps_tone_gen_f32(x1, N, 1.0, 0.16, 0);
// Generate input signal for x2 A=0.1,F=0.2
dsps_tone_gen_f32(x2, N, 0.1, 0.2, 0);
// Convert two input vectors to one complex vector
for (int i=0 ; i< N ; i++)
{
y_cf[i*2 + 0] = x1[i] * wind[i];
y_cf[i*2 + 1] = x2[i] * wind[i];
}
// FFT
unsigned int start_b = xthal_get_ccount();
dsps_fft2r_fc32(y_cf, N);
unsigned int end_b = xthal_get_ccount();
// Bit reverse
dsps_bit_rev_fc32(y_cf, N);
// Convert one complex vector to two complex vectors
dsps_cplx2reC_fc32(y_cf, N);
for (int i = 0 ; i < N/2 ; i++) {
y1_cf[i] = 10 * log10f((y1_cf[i * 2 + 0] * y1_cf[i * 2 + 0] + y1_cf[i * 2 + 1] * y1_cf[i * 2 + 1])/N);
y2_cf[i] = 10 * log10f((y2_cf[i * 2 + 0] * y2_cf[i * 2 + 0] + y2_cf[i * 2 + 1] * y2_cf[i * 2 + 1])/N);
// Simple way to show two power spectrums as one plot
sum_y[i] = fmax(y1_cf[i], y2_cf[i]);
}
// Show power spectrum in 64x10 window from -100 to 0 dB from 0..N/4 samples
ESP_LOGW(TAG, "Signal x1");
dsps_view(y1_cf, N/2, 64, 10, -60, 40, '|');
ESP_LOGW(TAG, "Signal x2");
dsps_view(y2_cf, N/2, 64, 10, -60, 40, '|');
ESP_LOGW(TAG, "Signals x1 and x2 on one plot");
dsps_view(sum_y, N/2, 64, 10, -60, 40, '|');
ESP_LOGI(TAG, "FFT for %i complex points take %i cycles", N, end_b - start_b);
ESP_LOGI(TAG, "End Example.");
}

View File

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

View File

@@ -0,0 +1,16 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := fft4real
# This line has to be included into the make file
# to include components that are located somewhere
# but not in "component" directory
EXTRA_COMPONENT_DIRS := $(realpath ../..)
EXCLUDE_COMPONENTS := test
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,99 @@
# FFT 4 Real Input Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates how to use FFT functionality from esp-dsp library. Example does the following steps:
1. Initialize the library
2. Initialize input signals with 1024 samples: one 0 dB, second with -20 dB
4. Calculate FFT Radix-2 for 1024 complex samples
4. Calculate FFT Radix-4 for 1024 complex samples
5. Apply bit reverse operation for output complex vectors
7. Show results on the plots
8. Show execution time of FFTs
## How to use example
### Hardware required
This example does not require any special hardware, and can be run on any common development board.
### Configure the project
If using Make based build system, run `make menuconfig` and set serial port under Serial Flasher Options.
If using CMake based build system, no configuration is required.
Also, under Component Config/DSP Library/Optimization for ESP32, it's possible to select optimized or ansi implementation to compare.
### Build and flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
Or, for CMake based build system (replace PORT with serial port name):
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example output
Here is an typical example console output.
```
I (344) main: Start Example.
W (424) main: Signal x1
I (424) view: Data min[673] = -103.113297, Data max[328] = 20.490950
________________________________________________________________
0 |
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | | |
8 | | |
9||||||||||||||||||| |||||||||||||||||||||||||||||||||||||||||||
0123456789012345678901234567890123456789012345678901234567890123
I (494) view: Plot: Length=1024, min=-60.000000, max=40.000000
W (504) main: Signal x2
I (504) view: Data min[582] = -103.113297, Data max[328] = 20.490950
________________________________________________________________
0 |
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | | |
8 | | |
9||||||||||||||||||| |||||||||||||||||||||||||||||||||||||||||||
0123456789012345678901234567890123456789012345678901234567890123
I (584) view: Plot: Length=1024, min=-60.000000, max=40.000000
W (593) main: Difference between signals x1 and x2 on one plot
I (594) view: Data min[0] = 0.000000, Data max[392] = 0.313019
________________________________________________________________
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8----------------------------------------------------------------|
9 |
0123456789012345678901234567890123456789012345678901234567890123
I (674) view: Plot: Length=1024, min=0.000000, max=40.000000
I (674) main: FFT Radix 2 for 1024 complex points take 168652 cycles
I (684) main: FFT Radix 4 for 1024 complex points take 104665 cycles
I (694) main: End Example.
```

View File

@@ -0,0 +1,4 @@
set(COMPONENT_SRCS "dsps_fft4real_main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()

View File

@@ -0,0 +1,8 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#

View File

@@ -0,0 +1,114 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "soc/gpio_struct.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "soc/uart_struct.h"
#include <math.h>
#include "esp_dsp.h"
static const char *TAG = "main";
// This example shows how to use FFT from esp-dsp library
#define N_SAMPLES 2048 // Amount of real input samples
int N = N_SAMPLES;
// Input test array
float x1[N_SAMPLES];
float x2[N_SAMPLES];
// Window coefficients
float wind[N_SAMPLES];
// Pointers to result arrays
float* y1_cf = &x1[0];
float* y2_cf = &x2[0];
// diff of y1 and y2
float diff_y[N_SAMPLES/2];
void app_main()
{
esp_err_t ret;
ESP_LOGI(TAG, "Start Example.");
ret = dsps_fft2r_init_fc32(NULL, N>>1);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Not possible to initialize FFT2R. Error = %i", ret);
return;
}
ret = dsps_fft4r_init_fc32(NULL, N >> 1);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Not possible to initialize FFT4R. Error = %i", ret);
return;
}
// Generate hann window
dsps_wind_hann_f32(wind, N);
// Generate input signal for x1 A=1 , F=0.1
dsps_tone_gen_f32(x1, N, 1.0, 0.16, 0);
// Convert two input vectors to one complex vector
for (int i=0 ; i< N ; i++)
{
x1[i] = x1[i] * wind[i];
x2[i] = x1[i];
}
// FFT Radix-2
unsigned int start_r2 = xthal_get_ccount();
dsps_fft2r_fc32(x1, N>>1);
// Bit reverse
dsps_bit_rev2r_fc32(x1, N>>1);
// Convert one complex vector with length N/2 to one real spectrum vector with length N/2
dsps_cplx2real_fc32(x1, N>>1);
unsigned int end_r2 = xthal_get_ccount();
// FFT Radix-4
unsigned int start_r4 = xthal_get_ccount();
dsps_fft4r_fc32(x2, N>>1);
// Bit reverse
dsps_bit_rev4r_fc32(x2, N>>1);
// Convert one complex vector with length N/2 to one real spectrum vector with length N/2
dsps_cplx2real_fc32(x2, N>>1);
unsigned int end_r4 = xthal_get_ccount();
for (int i = 0 ; i < N/2 ; i++) {
x1[i] = 10 * log10f((x1[i * 2 + 0] * x1[i * 2 + 0] + x1[i * 2 + 1] * x1[i * 2 + 1] + 0.0000001)/N);
x2[i] = 10 * log10f((x2[i * 2 + 0] * x2[i * 2 + 0] + x2[i * 2 + 1] * x2[i * 2 + 1] + 0.0000001)/N);
// Simple way to show two power spectrums as one plot
diff_y[i] = fabs(x1[i] - x2[i]);
}
// Show power spectrum in 64x10 window from -100 to 0 dB from 0..N/4 samples
ESP_LOGW(TAG, "Signal x1");
dsps_view(x1, N/2, 64, 10, -60, 40, '|');
ESP_LOGW(TAG, "Signal x2");
dsps_view(x2, N/2, 64, 10, -60, 40, '|');
ESP_LOGW(TAG, "Difference between signals x1 and x2 on one plot");
dsps_view(diff_y, N/2, 64, 10, 0, 40, '-');
ESP_LOGI(TAG, "FFT Radix 2 for %i complex points take %i cycles", N/2, end_r2 - start_r2);
ESP_LOGI(TAG, "FFT Radix 4 for %i complex points take %i cycles", N/2, end_r4 - start_r4);
ESP_LOGI(TAG, "End Example.");
}

View File

@@ -0,0 +1,9 @@
[mapping:dsp]
archive: libdsp.a
entries:
* (noflash)
[mapping:esp-dsp]
archive: libesp-dsp.a
entries:
* (noflash)

View File

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

View File

@@ -0,0 +1,16 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := fft_window
# This line has to be included into the make file
# to include components that are located somewhere
# but not in "component" directory
EXTRA_COMPONENT_DIRS := $(realpath ../..)
EXCLUDE_COMPONENTS := test
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,142 @@
# FFT Window Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates how to use Window and FFT functionality from esp-dsp library. Example does the following steps:
1. Initialize the library
2. Initialize input signals with 1024 samples
3. Apply window to input signal.
4. Calculate FFT for 1024 complex samples
5. Apply bit reverse operation for output complex vector
6. Split one complex FFT output spectrum to two real signal spectrums
7. Show results on the plots
## How to use example
### Hardware required
This example does not require any special hardware, and can be run on any common development board.
### Configure the project
If using Make based build system, run `make menuconfig` and set serial port under Serial Flasher Options.
If using CMake based build system, no configuration is required.
Also, under Component Config/DSP Library/Optimization for ESP32, it's possible to select optimized or ansi implementation to compare.
### Build and flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
Or, for CMake based build system (replace PORT with serial port name):
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example output
Here is an typical example console output.
```
I (128) main: Start Example.
W (128) main: Hann Window
I (128) view: Data min[256] = -inf, Data max[1] = 24.086628
________________________________________________________________
0| |
1| |
2| |
3| |
4| |
5 | |
6 | |
7 ||||| |
8 ||||||||||||||| |
9 ||||||||||||||||||||||||||||||||||||||||||
0123456789012345678901234567890123456789012345678901234567890123
I (138) view: Plot: Length=512, min=-120.000000, max=40.000000
W (138) main: Blackman Window
I (148) view: Data min[355] = -165.295654, Data max[1] = 24.083012
________________________________________________________________
0| |
1| |
2| |
3| |
4| |
5| |
6 | |
7 ||| |
8 ||||||||| |
9 |||||||||||||||||||||||||||||||||||||||||||||||||||
0123456789012345678901234567890123456789012345678901234567890123
I (158) view: Plot: Length=512, min=-120.000000, max=40.000000
W (158) main: Blackman-Harris Window
I (168) view: Data min[128] = -inf, Data max[1] = 23.874702
________________________________________________________________
0| |
1| |
2| |
3| |
4| |
5| |
6| |
7|| |
8| |||| |
9 |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0123456789012345678901234567890123456789012345678901234567890123
I (178) view: Plot: Length=512, min=-120.000000, max=40.000000
W (178) main: Blackman-Nuttall Window
I (188) view: Data min[128] = -inf, Data max[1] = 23.890663
________________________________________________________________
0| |
1| |
2| |
3| |
4| |
5| |
6| |
7 || |
8 |||| | |
9 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0123456789012345678901234567890123456789012345678901234567890123
I (198) view: Plot: Length=512, min=-120.000000, max=40.000000
W (198) main: Nuttall Window
I (208) view: Data min[203] = -175.147400, Data max[1] = 23.858671
________________________________________________________________
0| |
1| |
2| |
3| |
4| |
5| |
6| |
7|| |
8 ||| |
9 ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0123456789012345678901234567890123456789012345678901234567890123
I (218) view: Plot: Length=512, min=-120.000000, max=40.000000
W (218) main: Flat-Top Window
I (228) view: Data min[256] = -inf, Data max[1] = 22.490753
________________________________________________________________
0| |
1| |
2| |
3| |
4| |
5| |
6| |
7 || |
8 ||||| |
9 |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0123456789012345678901234567890123456789012345678901234567890123
I (238) view: Plot: Length=512, min=-120.000000, max=40.000000
I (238) main: End Example.
```

View File

@@ -0,0 +1,4 @@
set(COMPONENT_SRCS "dsps_window_main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()

View File

@@ -0,0 +1,8 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#

View File

@@ -0,0 +1,141 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "soc/gpio_struct.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "soc/uart_struct.h"
#include <math.h>
#include "esp_dsp.h"
static const char *TAG = "main";
// This example shows how to use FFT from esp-dsp library
#define N_SAMPLES 1024
int N = N_SAMPLES;
// Input test array
float x1[N_SAMPLES];
// Window coefficients
float wind[N_SAMPLES];
// working complex array
float y_cf[N_SAMPLES*2];
// Pointers to result arrays
float* y1_cf = &y_cf[0];
void process_and_show(float* data, int length)
{
dsps_fft2r_fc32(data, length);
// Bit reverse
dsps_bit_rev_fc32(data, length);
// Convert one complex vector to two complex vectors
dsps_cplx2reC_fc32(data, length);
for (int i = 0 ; i < length/2 ; i++) {
data[i] = 10 * log10f((data[i * 2 + 0] * data[i * 2 + 0] + data[i * 2 + 1] * data[i * 2 + 1])/N);
}
// Show power spectrum in 64x10 window from -100 to 0 dB from 0..N/4 samples
dsps_view(data, length/2, 64, 10, -120, 40, '|');
}
void app_main()
{
esp_err_t ret;
ESP_LOGI(TAG, "Start Example.");
ret = dsps_fft2r_init_fc32(NULL, CONFIG_DSP_MAX_FFT_SIZE);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Not possible to initialize FFT. Error = %i", ret);
return;
}
ESP_LOGW(TAG, "Hann Window");
// Generate Hann window
dsps_wind_hann_f32(wind, N);
// Convert two input vectors to one complex vector
for (int i=0 ; i< N ; i++)
{
y_cf[i*2 + 0] = wind[i];
y_cf[i*2 + 1] = 0;
}
process_and_show(y_cf, N);
ESP_LOGW(TAG, "Blackman Window");
// Generate Blackman window
dsps_wind_blackman_f32(wind, N);
// Convert two input vectors to one complex vector
for (int i=0 ; i< N ; i++)
{
y_cf[i*2 + 0] = wind[i];
y_cf[i*2 + 1] = 0;
}
process_and_show(y_cf, N);
ESP_LOGW(TAG, "Blackman-Harris Window");
// Generate Blackman-Harris window
dsps_wind_blackman_harris_f32(wind, N);
// Convert two input vectors to one complex vector
for (int i=0 ; i< N ; i++)
{
y_cf[i*2 + 0] = wind[i];
y_cf[i*2 + 1] = 0;
}
process_and_show(y_cf, N);
ESP_LOGW(TAG, "Blackman-Nuttall Window");
// Generate Blackman-Nuttall window
dsps_wind_blackman_nuttall_f32(wind, N);
// Convert two input vectors to one complex vector
for (int i=0 ; i< N ; i++)
{
y_cf[i*2 + 0] = wind[i];
y_cf[i*2 + 1] = 0;
}
process_and_show(y_cf, N);
ESP_LOGW(TAG, "Nuttall Window");
// Generate Nuttall window
dsps_wind_nuttall_f32(wind, N);
// Convert two input vectors to one complex vector
for (int i=0 ; i< N ; i++)
{
y_cf[i*2 + 0] = wind[i];
y_cf[i*2 + 1] = 0;
}
process_and_show(y_cf, N);
ESP_LOGW(TAG, "Flat-Top Window");
// Generate Flat-Top window
dsps_wind_flat_top_f32(wind, N);
// Convert two input vectors to one complex vector
for (int i=0 ; i< N ; i++)
{
y_cf[i*2 + 0] = wind[i];
y_cf[i*2 + 1] = 0;
}
process_and_show(y_cf, N);
ESP_LOGI(TAG, "End Example.");
}

View File

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

View File

@@ -0,0 +1,16 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := iir
# This line has to be included into the make file
# to include components that are located somewhere
# but not in "component" directory
EXTRA_COMPONENT_DIRS := $(realpath ../..)
EXCLUDE_COMPONENTS := test
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,115 @@
# IIR Filter Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates how to use IIR filters functionality from esp-dsp library. Example does the following steps:
1. Initialize the library
2. Initialize input signal
3. Show LPF filter with Q factor 1
* Calculate iir filter coefficients
* Filter the input test signal (delta function)
* Shows impulse response on the plot
* Shows frequency response on the plot
* Calculate execution performance
4. The same for LPF filter with Q factor 10
## How to use example
### Hardware required
This example does not require any special hardware, and can be run on any common development board.
### Configure the project
If using Make based build system, run `make menuconfig` and set serial port under Serial Flasher Options.
If using CMake based build system, no configuration is required.
Also, under Component Config/DSP Library/Optimization for ESP32, it's possible to select optimized or ansi implementation to compare.
### Build and flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
Or, for CMake based build system (replace PORT with serial port name):
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example output
Here is an typical example console output.
```
I (58) main: Start Example.
I (58) main: Impulse response of IIR filter with F=0.100000, qFactor=1.000000
I (68) view: Data min[8] = -0.060052, Data max[2] = 0.333517
________________________________________________________________
0 |
1 |
2 - |
3- - |
4 -------------------------------------------------------------|
5 |
6 |
7 |
8 |
9 |
0123456789012345678901234567890123456789012345678901234567890123
I (138) view: Plot: Length=128, min=-1.000000, max=1.000000
I (148) view: Data min[511] = -149.983795, Data max[0] = 0.000000
________________________________________________________________
0 |
1 |
2----------------- |
3 ---------- |
4 ------------- |
5 ---------- |
6 ------- |
7 --- |
8 -- |
9 --|
0123456789012345678901234567890123456789012345678901234567890123
I (228) view: Plot: Length=512, min=-100.000000, max=0.000000
I (228) main: IIR for 1024 samples take 20276 cycles
I (238) main: Impulse response of IIR filter with F=0.100000, qFactor=10.000000
I (248) view: Data min[7] = -0.453739, Data max[2] = 0.526114
________________________________________________________________
0 |
1 |
2 - - |
3- - - - --- --- - - |
4- - - - - ---- -------------------------------------|
5 -- -- -- -- |
6 |
7 |
8 |
9 |
0123456789012345678901234567890123456789012345678901234567890123
I (318) view: Plot: Length=128, min=-1.000000, max=1.000000
I (328) view: Data min[511] = -149.480377, Data max[0] = 0.000000
________________________________________________________________
0 -- |
1 -- - |
2---------- ----- |
3 -------- |
4 ------------ |
5 ---------- |
6 ------- |
7 --- |
8 -- |
9 --|
0123456789012345678901234567890123456789012345678901234567890123
I (408) view: Plot: Length=512, min=-100.000000, max=0.000000
I (408) main: IIR for 1024 samples take 17456 cycles
I (418) main: End Example.
```

View File

@@ -0,0 +1,4 @@
set(COMPONENT_SRCS "dsps_iir_main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()

View File

@@ -0,0 +1,8 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#

View File

@@ -0,0 +1,112 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "soc/gpio_struct.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "soc/uart_struct.h"
#include <math.h>
#include "esp_dsp.h"
static const char *TAG = "main";
// This example shows how to use iir filters from esp-dsp library
#define N_SAMPLES 1024
int N = N_SAMPLES;
// Input test array
float d[N_SAMPLES];
// output array
float y[N_SAMPLES];
float y_cf[N_SAMPLES*2];
// Function shows result of IIR filter
void ShowIIRfilter(float freq, float qFactor)
{
esp_err_t ret = ESP_OK;
float coeffs_lpf[5];
float w_lpf[5] = {0,0};
// Calculate iir filter coefficients
ret = dsps_biquad_gen_lpf_f32(coeffs_lpf, freq, qFactor);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Operation error = %i", ret);
return;
}
// Process input signal
unsigned int start_b = xthal_get_ccount();
ret = dsps_biquad_f32(d, y, N, coeffs_lpf, w_lpf);
unsigned int end_b = xthal_get_ccount();
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Operation error = %i", ret);
return;
}
// Show result as a plot
ESP_LOGI(TAG, "Impulse response of IIR filter with F=%f, qFactor=%f", freq, qFactor);
dsps_view(y, 128, 64, 10, -1, 1, '-');
// Show result as frequency responce on the plot
for (int i=0 ; i< N ; i++)
{
y_cf[i*2 + 0] = y[i];
y_cf[i*2 + 1] = 0;
}
// We making FFT transform
dsps_fft2r_fc32_ansi(y_cf, N);
// Bit reverse
dsps_bit_rev_fc32_ansi(y_cf, N);
// Calculating power of spectrum in dB
for (int i = 0 ; i < N/2 ; i++) {
y_cf[i] = 10 * log10f((y_cf[i * 2 + 0] * y_cf[i * 2 + 0] + y_cf[i * 2 + 1] * y_cf[i * 2 + 1])/N);
}
// Show power spectrum in 64x10 window from -100 to 0 dB from 0..N/2 samples
dsps_view(y_cf, N/2, 64, 10, -100, 0, '-');
ESP_LOGI(TAG, "IIR for %i samples take %i cycles", N, end_b - start_b);
}
void app_main()
{
esp_err_t ret;
ESP_LOGI(TAG, "Start Example.");
// If user don't care about buffer allocation, the defalt
// initialization could be used as shown here:
ret = dsps_fft2r_init_fc32(NULL, CONFIG_DSP_MAX_FFT_SIZE);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Not possible to initialize FFT. Error = %i", ret);
return;
}
// Initialize input signal
// Generate d function as input signal
dsps_d_gen_f32(d, N, 0);
// Show filter with Q factor 1
ShowIIRfilter(0.1, 1);
// Show filter with Q factor 10
ShowIIRfilter(0.1, 10);
ESP_LOGI(TAG, "End Example.");
}

View File

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

View File

@@ -0,0 +1,16 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := matrix
# This line has to be included into the make file
# to include components that are located somewhere
# but not in "component" directory
EXTRA_COMPONENT_DIRS := $(realpath ../..)
EXCLUDE_COMPONENTS := test
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,62 @@
# Matrix Operations Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates how to use Mat class functionality from esp-dsp library. Example does the following steps:
1. Initialize a matrix A and matirx x
2. Calculate matrix b: b = A*x
3. Find roots x1_: A*x1_ = b, with different methods
4. Print result
## How to use example
### Hardware required
This example does not require any special hardware, and can be run on any common development board.
### Configure the project
If using Make based build system, run `make menuconfig` and set serial port under Serial Flasher Options.
If using CMake based build system, no configuration is required.
Also, under Component Config/DSP Library/Optimization for ESP32, it's possible to select optimized or ansi implementation to compare.
### Build and flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
Or, for CMake based build system (replace PORT with serial port name):
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example output
Here is an typical example console output.
```
I (215) main: Start Example.
I (215) main: Original vector x:
0
1
2
I (215) main: Solve result:
0
1
2
I (215) main: Roots result:
0
1
2
I (215) main: End Example.
```

View File

@@ -0,0 +1,4 @@
set(COMPONENT_SRCS "dspm_matrix_main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()

View File

@@ -0,0 +1,8 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#

View File

@@ -0,0 +1,62 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dsp_platform.h"
#include "esp_log.h"
#include "esp_dsp.h"
static const char *TAG = "main";
// This example shows how to use Mat class from esp-dsp library.
//
// First we create matix A and x, and then calculating matrix b as result
// A*x = b
// Then we can find x as roots of matrices X and b
//
extern "C" void app_main();
void app_main()
{
ESP_LOGI(TAG, "Start Example.");
int M = 3;
int N = 3;
dspm::Mat A(M, N);
dspm::Mat x(N, 1);
for (int m = 0 ; m < M ; m++) {
for (int n = 0 ; n < N ; n++) {
A(m, n) = N * m + n;
}
x(m, 0) = m;
}
A(0, 0) = 10;
A(0, 1) = 11;
dspm::Mat b = A * x;
// Gaussian method
dspm::Mat x1_ = dspm::Mat::solve(A, b);
// Non Gaussian method
dspm::Mat x2_ = dspm::Mat::roots(A, b);
ESP_LOGI(TAG, "Original vector x:");
std::cout << x;
ESP_LOGI(TAG, "Solve result:");
std::cout << x1_;
ESP_LOGI(TAG, "Roots result:");
std::cout << x2_;
ESP_LOGI(TAG, "End Example.");
}

View File

@@ -0,0 +1,53 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _dsp_common_H_
#define _dsp_common_H_
#include <stdint.h>
#include <stdbool.h>
#include "dsp_err.h"
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief check power of two
* The function check if the argument is power of 2.
* The implementation use ANSI C and could be compiled and run on any platform
*
* @return
* - true if x is power of two
* - false if no
*/
bool dsp_is_power_of_two(int x);
/**
* @brief Power of two
* The function return power of 2 for values 2^N.
* The implementation use ANSI C and could be compiled and run on any platform
*
* @return
* - power of two
*/
int dsp_power_of_two(int x);
#ifdef __cplusplus
}
#endif
#endif // _dsp_common_H_

View File

@@ -0,0 +1,23 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _DSP_ERR_H_
#define _DSP_ERR_H_
#include "stdint.h"
#include "esp_err.h"
#include "dsp_err_codes.h"
#endif // _DSP_ERR_H_

View File

@@ -0,0 +1,27 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _dsp_error_codes_H_
#define _dsp_error_codes_H_
#define DSP_OK 0 // For internal use only. Please use ESP_OK instead
#define ESP_ERR_DSP_BASE 0x70000
#define ESP_ERR_DSP_INVALID_LENGTH (ESP_ERR_DSP_BASE + 1)
#define ESP_ERR_DSP_INVALID_PARAM (ESP_ERR_DSP_BASE + 2)
#define ESP_ERR_DSP_PARAM_OUTOFRANGE (ESP_ERR_DSP_BASE + 3)
#define ESP_ERR_DSP_UNINITIALIZED (ESP_ERR_DSP_BASE + 4)
#define ESP_ERR_DSP_REINITIALIZED (ESP_ERR_DSP_BASE + 5)
#endif // _dsp_error_codes_H_

View File

@@ -0,0 +1,25 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dsp_platform_h_
#define dsp_platform_h_
#include "soc/cpu.h"
#include "freertos/FreeRTOS.h"
#include "freertos/portable.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#endif // dsp_platform_h_

Some files were not shown because too many files have changed in this diff Show More