initial commit
This commit is contained in:
commit
485fd809ac
16 changed files with 7641 additions and 0 deletions
116
flake.lock
Normal file
116
flake.lock
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"esp32-idf": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1724640360,
|
||||||
|
"narHash": "sha256-l77E6PA/NGCKw9hMOSZjqLTCb6FNEqh/SDkdds/KF9o=",
|
||||||
|
"owner": "mirrexagon",
|
||||||
|
"repo": "nixpkgs-esp-dev",
|
||||||
|
"rev": "e740e017122636ac1dbce9c0e3d867b9947a1687",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "mirrexagon",
|
||||||
|
"repo": "nixpkgs-esp-dev",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1705309234,
|
||||||
|
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710146030,
|
||||||
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1725432240,
|
||||||
|
"narHash": "sha256-+yj+xgsfZaErbfYM3T+QvEE2hU7UuE+Jf0fJCJ8uPS0=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "ad416d066ca1222956472ab7d0555a6946746a80",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"esp32-idf": "esp32-idf",
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
33
flake.nix
Normal file
33
flake.nix
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
description = "esp32 devshell";
|
||||||
|
inputs = {
|
||||||
|
#nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
|
||||||
|
esp32-idf.url = "github:mirrexagon/nixpkgs-esp-dev";
|
||||||
|
esp32-idf.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils, esp32-idf, ... }@inputs:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
|
||||||
|
esp32-idf = inputs.esp32-idf.packages.${system}.esp-idf-full;
|
||||||
|
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells.default = pkgs.mkShell rec {
|
||||||
|
packages = with pkgs ; [
|
||||||
|
esp32-idf
|
||||||
|
ffmpeg
|
||||||
|
netcat
|
||||||
|
portaudio
|
||||||
|
zlib
|
||||||
|
bashInteractive
|
||||||
|
envsubst
|
||||||
|
];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
2
inout/.gitignore
vendored
Normal file
2
inout/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
build/
|
||||||
|
.cache/
|
6
inout/CMakeLists.txt
Normal file
6
inout/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||||
|
# in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(i2s_adc_dac)
|
94
inout/README.md
Normal file
94
inout/README.md
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
| Supported Targets | ESP32 |
|
||||||
|
| ----------------- | ----- |
|
||||||
|
|
||||||
|
# I2S Built-in ADC/DAC Example
|
||||||
|
|
||||||
|
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||||
|
|
||||||
|
In this example, we configure I2S to work in I2S_ADC and I2S_DAC modes and then:
|
||||||
|
* recording sound from ADC,
|
||||||
|
* playing the recorded sound,
|
||||||
|
* playing an audio file in flash via DAC.
|
||||||
|
|
||||||
|
#### Note:
|
||||||
|
The `tools` directory contains `generate_audio_file.py` script for generating audio files:
|
||||||
|
|
||||||
|
* The script provides an example of generating audio tables from `.wav` files.
|
||||||
|
* In this example, the `wav` file must be in 16k/16bit mono format.
|
||||||
|
* The script will bundle the `wav` files into a single table named `audio_example_file.h`.
|
||||||
|
* Since the ADC can only play 8-bit data, the script will scale each 16-bit value to a 8-bit value.
|
||||||
|
* The script will covert all signed values into unsigned values because only positive values will be output by the ADC.
|
||||||
|
|
||||||
|
## How to Use Example
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
|
||||||
|
* A development board with ESP32 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||||
|
* A USB cable for power supply and programming
|
||||||
|
* A microphone (with amplifier) and one or two speaker(s) for testing.
|
||||||
|
|
||||||
|
The following is the hardware connection:
|
||||||
|
|
||||||
|
|hardware|module|GPIO|
|
||||||
|
|:---:|:---:|:---:|
|
||||||
|
|Microphone|ADC1_CH0|GPIO36|
|
||||||
|
|speaker(R)|DAC1|GPIO25|
|
||||||
|
|speaker(L)|DAC2|GPIO26|
|
||||||
|
|
||||||
|
### Configure the Project
|
||||||
|
|
||||||
|
```
|
||||||
|
idf.py menuconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
* Set the flash size to 4 MB under Serial Flasher Options.
|
||||||
|
* Select "Custom partition table CSV" and rename "Custom partition CSV file" to "partitions_adc_dac_example.csv".
|
||||||
|
|
||||||
|
(Note that you can use `sdkconfig.defaults`)
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||||
|
|
||||||
|
```
|
||||||
|
idf.py -p PORT flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
Reset your development board. The application it will first record the sound through the microphone. Then it will play the recorded sound, and finally a piece of audio stored in the flash. The following is the output log:
|
||||||
|
|
||||||
|
```
|
||||||
|
partiton addr: 0x00110000; size: 2097152; label: storage
|
||||||
|
Erasing flash
|
||||||
|
partiton addr: 0x00110000; size: 2097152; label: storage
|
||||||
|
Erase size: 323584 Bytes
|
||||||
|
I2S: PLL_D2: Req RATE: 16000, real rate: 1004.000, BITS: 16, CLKM: 83, BCK: 60, MCLK: 83.333, SCLK: 32128.000000, diva: 64, divb: 21
|
||||||
|
Sound recording 5%
|
||||||
|
Sound recording 10%
|
||||||
|
...
|
||||||
|
Sound recording 97%
|
||||||
|
Sound recording 102%
|
||||||
|
playing: 0 %
|
||||||
|
playing: 1 %
|
||||||
|
playing: 2 %
|
||||||
|
...
|
||||||
|
playing: 96 %
|
||||||
|
playing: 97 %
|
||||||
|
playing: 98 %
|
||||||
|
Playing file example:
|
||||||
|
I2S: PLL_D2: Req RATE: 16000, real rate: 1004.000, BITS: 16, CLKM: 83, BCK: 60, MCLK: 83.333, SCLK: 32128.000000, diva: 64, divb: 21
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
* Program upload failure
|
||||||
|
|
||||||
|
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
|
||||||
|
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
|
||||||
|
|
||||||
|
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
2
inout/main/CMakeLists.txt
Normal file
2
inout/main/CMakeLists.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
idf_component_register(SRCS "app_main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
300
inout/main/app_main.c
Normal file
300
inout/main/app_main.c
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ADC/DAC are not supported in the new I2S driver, but still available in the legacy I2S driver for backward compatibility
|
||||||
|
* Please turn to the dedicated ADC/DAC driver instead */
|
||||||
|
#pragma message("ADC/DAC on ESP32 will no longer supported via I2S driver")
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "spi_flash_mmap.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_partition.h"
|
||||||
|
#include "driver/i2s.h"
|
||||||
|
#include "audio_example_file.h"
|
||||||
|
#include "esp_rom_sys.h"
|
||||||
|
#include "driver/adc.h"
|
||||||
|
#include "esp_adc_cal.h"
|
||||||
|
|
||||||
|
static const char* TAG = "ad/da";
|
||||||
|
#define V_REF 1100
|
||||||
|
#define ADC1_TEST_CHANNEL (ADC1_CHANNEL_7)
|
||||||
|
|
||||||
|
#define PARTITION_NAME "storage"
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------
|
||||||
|
EXAMPLE CONFIG
|
||||||
|
---------------------------------------------------------------*/
|
||||||
|
//enable record sound and save in flash
|
||||||
|
#define RECORD_IN_FLASH_EN (1)
|
||||||
|
//enable replay recorded sound in flash
|
||||||
|
#define REPLAY_FROM_FLASH_EN (1)
|
||||||
|
|
||||||
|
//i2s number
|
||||||
|
#define EXAMPLE_I2S_NUM (0)
|
||||||
|
//i2s sample rate
|
||||||
|
#define EXAMPLE_I2S_SAMPLE_RATE (16000)
|
||||||
|
//i2s data bits
|
||||||
|
#define EXAMPLE_I2S_SAMPLE_BITS (16)
|
||||||
|
//enable display buffer for debug
|
||||||
|
#define EXAMPLE_I2S_BUF_DEBUG (0)
|
||||||
|
//I2S read buffer length
|
||||||
|
#define EXAMPLE_I2S_READ_LEN (16 * 1024)
|
||||||
|
//I2S data format
|
||||||
|
#define EXAMPLE_I2S_FORMAT (I2S_CHANNEL_FMT_RIGHT_LEFT)
|
||||||
|
//I2S channel number
|
||||||
|
#define EXAMPLE_I2S_CHANNEL_NUM ((EXAMPLE_I2S_FORMAT < I2S_CHANNEL_FMT_ONLY_RIGHT) ? (2) : (1))
|
||||||
|
//I2S built-in ADC unit
|
||||||
|
#define I2S_ADC_UNIT ADC_UNIT_1
|
||||||
|
//I2S built-in ADC channel
|
||||||
|
#define I2S_ADC_CHANNEL ADC1_CHANNEL_0
|
||||||
|
|
||||||
|
//flash record size, for recording 5 seconds' data
|
||||||
|
#define FLASH_RECORD_SIZE (EXAMPLE_I2S_CHANNEL_NUM * EXAMPLE_I2S_SAMPLE_RATE * EXAMPLE_I2S_SAMPLE_BITS / 8 * 5)
|
||||||
|
#define FLASH_ERASE_SIZE (FLASH_RECORD_SIZE % FLASH_SECTOR_SIZE == 0) ? FLASH_RECORD_SIZE : FLASH_RECORD_SIZE + (FLASH_SECTOR_SIZE - FLASH_RECORD_SIZE % FLASH_SECTOR_SIZE)
|
||||||
|
//sector size of flash
|
||||||
|
#define FLASH_SECTOR_SIZE (0x1000)
|
||||||
|
//flash read / write address
|
||||||
|
#define FLASH_ADDR (0x200000)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief I2S ADC/DAC mode init.
|
||||||
|
*/
|
||||||
|
void example_i2s_init(void)
|
||||||
|
{
|
||||||
|
int i2s_num = EXAMPLE_I2S_NUM;
|
||||||
|
i2s_config_t i2s_config = {
|
||||||
|
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN,
|
||||||
|
.sample_rate = EXAMPLE_I2S_SAMPLE_RATE,
|
||||||
|
.bits_per_sample = EXAMPLE_I2S_SAMPLE_BITS,
|
||||||
|
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
|
||||||
|
.channel_format = EXAMPLE_I2S_FORMAT,
|
||||||
|
.intr_alloc_flags = 0,
|
||||||
|
.dma_desc_num = 6,
|
||||||
|
.dma_frame_num = 256,
|
||||||
|
.use_apll = 1,
|
||||||
|
};
|
||||||
|
//install and start i2s driver
|
||||||
|
i2s_driver_install(i2s_num, &i2s_config, 0, NULL);
|
||||||
|
//init DAC pad
|
||||||
|
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
|
||||||
|
//init ADC pad
|
||||||
|
i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief erase flash for recording
|
||||||
|
*/
|
||||||
|
void example_erase_flash(void)
|
||||||
|
{
|
||||||
|
#if RECORD_IN_FLASH_EN
|
||||||
|
printf("Erasing flash \n");
|
||||||
|
const esp_partition_t *data_partition = NULL;
|
||||||
|
data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||||
|
ESP_PARTITION_SUBTYPE_DATA_FAT, PARTITION_NAME);
|
||||||
|
if (data_partition != NULL) {
|
||||||
|
printf("partiton addr: 0x%08"PRIx32"; size: %"PRIu32"; label: %s\n", data_partition->address, data_partition->size, data_partition->label);
|
||||||
|
}
|
||||||
|
printf("Erase size: %d Bytes\n", FLASH_ERASE_SIZE);
|
||||||
|
ESP_ERROR_CHECK(esp_partition_erase_range(data_partition, 0, FLASH_ERASE_SIZE));
|
||||||
|
#else
|
||||||
|
printf("Skip flash erasing...\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief debug buffer data
|
||||||
|
*/
|
||||||
|
void example_disp_buf(uint8_t* buf, int length)
|
||||||
|
{
|
||||||
|
#if EXAMPLE_I2S_BUF_DEBUG
|
||||||
|
printf("======\n");
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
printf("%02x ", buf[i]);
|
||||||
|
if ((i + 1) % 8 == 0) {
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("======\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset i2s clock and mode
|
||||||
|
*/
|
||||||
|
void example_reset_play_mode(void)
|
||||||
|
{
|
||||||
|
i2s_set_clk(EXAMPLE_I2S_NUM, EXAMPLE_I2S_SAMPLE_RATE, EXAMPLE_I2S_SAMPLE_BITS, EXAMPLE_I2S_CHANNEL_NUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set i2s clock for example audio file
|
||||||
|
*/
|
||||||
|
void example_set_file_play_mode(void)
|
||||||
|
{
|
||||||
|
i2s_set_clk(EXAMPLE_I2S_NUM, 16000, EXAMPLE_I2S_SAMPLE_BITS, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scale data to 16bit/32bit for I2S DMA output.
|
||||||
|
* DAC can only output 8bit data value.
|
||||||
|
* I2S DMA will still send 16 bit or 32bit data, the highest 8bit contains DAC data.
|
||||||
|
*/
|
||||||
|
int example_i2s_dac_data_scale(uint8_t* d_buff, uint8_t* s_buff, uint32_t len)
|
||||||
|
{
|
||||||
|
uint32_t j = 0;
|
||||||
|
#if (EXAMPLE_I2S_SAMPLE_BITS == 16)
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
d_buff[j++] = 0;
|
||||||
|
d_buff[j++] = s_buff[i];
|
||||||
|
}
|
||||||
|
return (len * 2);
|
||||||
|
#else
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
d_buff[j++] = 0;
|
||||||
|
d_buff[j++] = 0;
|
||||||
|
d_buff[j++] = 0;
|
||||||
|
d_buff[j++] = s_buff[i];
|
||||||
|
}
|
||||||
|
return (len * 4);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scale data to 8bit for data from ADC.
|
||||||
|
* Data from ADC are 12bit width by default.
|
||||||
|
* DAC can only output 8 bit data.
|
||||||
|
* Scale each 12bit ADC data to 8bit DAC data.
|
||||||
|
*/
|
||||||
|
void example_i2s_adc_data_scale(uint8_t * d_buff, uint8_t* s_buff, uint32_t len)
|
||||||
|
{
|
||||||
|
uint32_t j = 0;
|
||||||
|
uint32_t dac_value = 0;
|
||||||
|
#if (EXAMPLE_I2S_SAMPLE_BITS == 16)
|
||||||
|
for (int i = 0; i < len; i += 2) {
|
||||||
|
dac_value = ((((uint16_t) (s_buff[i + 1] & 0xf) << 8) | ((s_buff[i + 0]))));
|
||||||
|
d_buff[j++] = 0;
|
||||||
|
d_buff[j++] = dac_value * 256 / 4096;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for (int i = 0; i < len; i += 4) {
|
||||||
|
dac_value = ((((uint16_t)(s_buff[i + 3] & 0xf) << 8) | ((s_buff[i + 2]))));
|
||||||
|
d_buff[j++] = 0;
|
||||||
|
d_buff[j++] = 0;
|
||||||
|
d_buff[j++] = 0;
|
||||||
|
d_buff[j++] = dac_value * 256 / 4096;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief I2S ADC/DAC example
|
||||||
|
* 1. Erase flash
|
||||||
|
* 2. Record audio from ADC and save in flash
|
||||||
|
* 3. Read flash and replay the sound via DAC
|
||||||
|
* 4. Play an example audio file(file format: 8bit/8khz/single channel)
|
||||||
|
* 5. Loop back to step 3
|
||||||
|
*/
|
||||||
|
void example_i2s_adc_dac(void*arg)
|
||||||
|
{
|
||||||
|
const esp_partition_t *data_partition = NULL;
|
||||||
|
data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||||
|
ESP_PARTITION_SUBTYPE_DATA_FAT, PARTITION_NAME);
|
||||||
|
if (data_partition != NULL) {
|
||||||
|
printf("partiton addr: 0x%08"PRIx32"; size: %"PRIu32"; label: %s\n", data_partition->address, data_partition->size, data_partition->label);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Partition error: can't find partition name: %s\n", PARTITION_NAME);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
//1. Erase flash
|
||||||
|
example_erase_flash();
|
||||||
|
int i2s_read_len = EXAMPLE_I2S_READ_LEN;
|
||||||
|
int flash_wr_size = 0;
|
||||||
|
size_t bytes_read, bytes_written;
|
||||||
|
|
||||||
|
//2. Record audio from ADC and save in flash
|
||||||
|
#if RECORD_IN_FLASH_EN
|
||||||
|
char* i2s_read_buff = (char*) calloc(i2s_read_len, sizeof(char));
|
||||||
|
uint8_t* flash_write_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char));
|
||||||
|
i2s_adc_enable(EXAMPLE_I2S_NUM);
|
||||||
|
while (flash_wr_size < FLASH_RECORD_SIZE) {
|
||||||
|
//read data from I2S bus, in this case, from ADC.
|
||||||
|
i2s_read(EXAMPLE_I2S_NUM, (void*) i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY);
|
||||||
|
example_disp_buf((uint8_t*) i2s_read_buff, 64);
|
||||||
|
//save original data from I2S(ADC) into flash.
|
||||||
|
esp_partition_write(data_partition, flash_wr_size, i2s_read_buff, i2s_read_len);
|
||||||
|
flash_wr_size += i2s_read_len;
|
||||||
|
esp_rom_printf("Sound recording %u%%\n", flash_wr_size * 100 / FLASH_RECORD_SIZE);
|
||||||
|
}
|
||||||
|
i2s_adc_disable(EXAMPLE_I2S_NUM);
|
||||||
|
free(i2s_read_buff);
|
||||||
|
i2s_read_buff = NULL;
|
||||||
|
free(flash_write_buff);
|
||||||
|
flash_write_buff = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint8_t* flash_read_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char));
|
||||||
|
uint8_t* i2s_write_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char));
|
||||||
|
while (1) {
|
||||||
|
|
||||||
|
//3. Read flash and replay the sound via DAC
|
||||||
|
#if REPLAY_FROM_FLASH_EN
|
||||||
|
for (int rd_offset = 0; rd_offset < flash_wr_size; rd_offset += FLASH_SECTOR_SIZE) {
|
||||||
|
//read I2S(ADC) original data from flash
|
||||||
|
esp_partition_read(data_partition, rd_offset, flash_read_buff, FLASH_SECTOR_SIZE);
|
||||||
|
//process data and scale to 8bit for I2S DAC.
|
||||||
|
example_i2s_adc_data_scale(i2s_write_buff, flash_read_buff, FLASH_SECTOR_SIZE);
|
||||||
|
//send data
|
||||||
|
i2s_write(EXAMPLE_I2S_NUM, i2s_write_buff, FLASH_SECTOR_SIZE, &bytes_written, portMAX_DELAY);
|
||||||
|
printf("playing: %d %%\n", rd_offset * 100 / flash_wr_size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//4. Play an example audio file(file format: 8bit/16khz/single channel)
|
||||||
|
printf("Playing file example: \n");
|
||||||
|
int offset = 0;
|
||||||
|
int tot_size = sizeof(audio_table);
|
||||||
|
example_set_file_play_mode();
|
||||||
|
while (offset < tot_size) {
|
||||||
|
int play_len = ((tot_size - offset) > (4 * 1024)) ? (4 * 1024) : (tot_size - offset);
|
||||||
|
int i2s_wr_len = example_i2s_dac_data_scale(i2s_write_buff, (uint8_t*)(audio_table + offset), play_len);
|
||||||
|
i2s_write(EXAMPLE_I2S_NUM, i2s_write_buff, i2s_wr_len, &bytes_written, portMAX_DELAY);
|
||||||
|
offset += play_len;
|
||||||
|
example_disp_buf((uint8_t*) i2s_write_buff, 32);
|
||||||
|
}
|
||||||
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
|
example_reset_play_mode();
|
||||||
|
}
|
||||||
|
free(flash_read_buff);
|
||||||
|
free(i2s_write_buff);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void adc_read_task(void* arg)
|
||||||
|
{
|
||||||
|
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||||
|
adc1_config_channel_atten(ADC1_TEST_CHANNEL, ADC_ATTEN_DB_11);
|
||||||
|
esp_adc_cal_characteristics_t characteristics;
|
||||||
|
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, V_REF, &characteristics);
|
||||||
|
while(1) {
|
||||||
|
uint32_t voltage;
|
||||||
|
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||||
|
esp_adc_cal_get_voltage(ADC1_TEST_CHANNEL, &characteristics, &voltage);
|
||||||
|
ESP_LOGI(TAG, "%"PRIu32" mV", voltage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
example_i2s_init();
|
||||||
|
esp_log_level_set("I2S", ESP_LOG_INFO);
|
||||||
|
xTaskCreate(example_i2s_adc_dac, "example_i2s_adc_dac", 1024 * 2, NULL, 5, NULL);
|
||||||
|
xTaskCreate(adc_read_task, "ADC read task", 2048, NULL, 5, NULL);
|
||||||
|
}
|
4979
inout/main/audio_example_file.h
Normal file
4979
inout/main/audio_example_file.h
Normal file
File diff suppressed because it is too large
Load diff
6
inout/partitions_adc_dac_example.csv
Normal file
6
inout/partitions_adc_dac_example.csv
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||||
|
nvs, data, nvs, 0x9000, 0x6000,
|
||||||
|
phy_init, data, phy, 0xf000, 0x1000,
|
||||||
|
factory, app, factory, 0x10000, 1M,
|
||||||
|
storage, data, fat, , 2M,
|
|
15
inout/pytest_i2s_adc_dac.py
Normal file
15
inout/pytest_i2s_adc_dac.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pytest_embedded import Dut
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.esp32
|
||||||
|
@pytest.mark.generic
|
||||||
|
def test_i2s_adc_dac_example(dut: Dut) -> None:
|
||||||
|
dut.expect('partiton addr: 0x([0-9a-fA-F]+); size: ([0-9]+); label: storage', timeout=30)
|
||||||
|
dut.expect_exact('Erasing flash', timeout=30)
|
||||||
|
dut.expect('partiton addr: 0x([0-9a-fA-F]+); size: ([0-9]+); label: storage', timeout=30)
|
||||||
|
dut.expect('Erase size: ([0-9]+) Bytes', timeout=30)
|
||||||
|
dut.expect('I \\(([0-9]+)\\) ad/da: ([0-9]+) mV', timeout=30)
|
2030
inout/sdkconfig
Normal file
2030
inout/sdkconfig
Normal file
File diff suppressed because it is too large
Load diff
7
inout/sdkconfig.defaults
Normal file
7
inout/sdkconfig.defaults
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_adc_dac_example.csv"
|
||||||
|
CONFIG_PARTITION_TABLE_FILENAME="partitions_adc_dac_example.csv"
|
||||||
|
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||||
|
# ADC/DAC are only supported in the legacy driver, suppress the warnings while using the legacy driver
|
||||||
|
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
|
||||||
|
CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y
|
51
inout/tools/generate_audio_file.py
Normal file
51
inout/tools/generate_audio_file.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
|
import wave
|
||||||
|
from builtins import range
|
||||||
|
|
||||||
|
try:
|
||||||
|
from typing import List
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def get_wave_array_str(filename, target_bits): # type: (str, int) -> str
|
||||||
|
wave_read = wave.open(filename, 'r')
|
||||||
|
array_str = ''
|
||||||
|
nchannels, sampwidth, framerate, nframes, comptype, compname = wave_read.getparams()
|
||||||
|
sampwidth *= 8
|
||||||
|
for i in range(wave_read.getnframes()):
|
||||||
|
val, = struct.unpack('<H', wave_read.readframes(1))
|
||||||
|
scale_val = (1 << target_bits) - 1
|
||||||
|
cur_lim = (1 << sampwidth) - 1
|
||||||
|
# scale current data to 8-bit data
|
||||||
|
val = val * scale_val / cur_lim
|
||||||
|
val = int(val + ((scale_val + 1) // 2)) & scale_val
|
||||||
|
array_str += '0x%x, ' % (val)
|
||||||
|
if (i + 1) % 16 == 0:
|
||||||
|
array_str += '\n'
|
||||||
|
return array_str
|
||||||
|
|
||||||
|
|
||||||
|
def gen_wave_table(wav_file_list, target_file_name, scale_bits=8): # type: (List[str], str, int) -> None
|
||||||
|
with open(target_file_name, 'w') as audio_table:
|
||||||
|
print('#include <stdio.h>', file=audio_table)
|
||||||
|
print('const unsigned char audio_table[] = {', file=audio_table)
|
||||||
|
for wav in wav_file_list:
|
||||||
|
print('processing: {}'.format(wav))
|
||||||
|
print(get_wave_array_str(filename=wav, target_bits=scale_bits), file=audio_table)
|
||||||
|
print('};\n', file=audio_table)
|
||||||
|
print('Done...')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('Generating audio array...')
|
||||||
|
wav_list = []
|
||||||
|
for wavefile in os.listdir('./'):
|
||||||
|
if wavefile.endswith('.wav'):
|
||||||
|
wav_list.append(wavefile)
|
||||||
|
gen_wave_table(wav_file_list=wav_list, target_file_name='audio_example_file.h')
|
BIN
inout/tools/sample_00.wav
Normal file
BIN
inout/tools/sample_00.wav
Normal file
Binary file not shown.
BIN
inout/tools/sample_01.wav
Normal file
BIN
inout/tools/sample_01.wav
Normal file
Binary file not shown.
BIN
inout/tools/sample_02.wav
Normal file
BIN
inout/tools/sample_02.wav
Normal file
Binary file not shown.
Loading…
Reference in a new issue