-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #449 from shorepine/amyboard
amychip v0
- Loading branch information
Showing
4 changed files
with
1,347 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,341 @@ | ||
#include <stdio.h> | ||
#include <inttypes.h> | ||
#include "sdkconfig.h" | ||
#include "freertos/FreeRTOS.h" | ||
#include "freertos/task.h" | ||
#include "esp_chip_info.h" | ||
#include "esp_flash.h" | ||
#include "esp_system.h" | ||
#include "esp_task.h" | ||
#include "driver/i2c_master.h" | ||
|
||
#include "amy.h" | ||
#include "examples.h" | ||
#include "freertos/queue.h" | ||
#include "freertos/event_groups.h" | ||
#include "esp_log.h" | ||
#include "esp_err.h" | ||
|
||
#include "driver/i2s_std.h" | ||
|
||
static const char *TAG = "amy-chip"; | ||
|
||
i2s_chan_handle_t tx_handle; | ||
i2s_chan_handle_t rx_handle; | ||
|
||
#ifdef AMYBOARD_DIY | ||
#define I2S_MCLK 10 | ||
#define I2S_BCLK 13 | ||
#define I2S_LRCLK 12 | ||
#define AMYOUT 11 // data going to the codec, eg DAC data | ||
#define I2C_SLAVE_SCL 5 | ||
#define I2C_SLAVE_SDA 4 | ||
#define I2C_MASTER_SCL 17 | ||
#define I2C_MASTER_SDA 18 | ||
#define AMYIN 16 // data coming from the codec, eg ADC data | ||
#elif defined AMYBOARD_R3 | ||
// stuff in the eagle | ||
#define I2S_MCLK 3 | ||
#define I2S_BCLK 8 | ||
#define I2S_LRCLK 2 | ||
#define AMYOUT 6 // data going to the codec, eg DAC data | ||
#define I2C_SLAVE_SCL 5 | ||
#define I2C_SLAVE_SDA 4 | ||
#define I2C_MASTER_SCL 17 | ||
#define I2C_MASTER_SDA 18 | ||
#define AMYIN 9 // data coming from the codec, eg ADC data | ||
#endif | ||
|
||
#define I2S_SAMPLE_TYPE I2S_BITS_PER_SAMPLE_32BIT | ||
typedef int32_t i2s_sample_type; | ||
|
||
|
||
|
||
// mutex that locks writes to the delta queue | ||
SemaphoreHandle_t xQueueSemaphore; | ||
|
||
void delay_ms(uint32_t ms) { | ||
vTaskDelay(ms / portTICK_PERIOD_MS); | ||
} | ||
|
||
// Task handles for the renderers, multicast listener and main | ||
TaskHandle_t amy_render_handle; | ||
TaskHandle_t alles_fill_buffer_handle; | ||
|
||
|
||
#define ALLES_TASK_COREID (1) | ||
#define ALLES_RENDER_TASK_COREID (0) | ||
#define ALLES_FILL_BUFFER_TASK_COREID (1) | ||
#define ALLES_RENDER_TASK_PRIORITY (ESP_TASK_PRIO_MAX-1 ) | ||
#define ALLES_FILL_BUFFER_TASK_PRIORITY (ESP_TASK_PRIO_MAX-1) | ||
#define ALLES_TASK_NAME "alles_task" | ||
#define ALLES_RENDER_TASK_NAME "alles_r_task" | ||
#define ALLES_FILL_BUFFER_TASK_NAME "alles_fb_task" | ||
#define ALLES_TASK_STACK_SIZE (8 * 1024) | ||
#define ALLES_RENDER_TASK_STACK_SIZE (8 * 1024) | ||
#define ALLES_FILL_BUFFER_TASK_STACK_SIZE (8 * 1024) | ||
|
||
|
||
// i2c stuff | ||
#define I2C_CLK_FREQ 400000 | ||
#include "esp32-hal-i2c-slave.h" | ||
#define DATA_LENGTH 255 | ||
#define _I2C_NUMBER(num) I2C_NUM_##num | ||
#define I2C_NUMBER(num) _I2C_NUMBER(num) | ||
#define I2C_SLAVE_NUM I2C_NUMBER(1) /*!< I2C port number for slave dev */ | ||
#define I2C_SLAVE_TX_BUF_LEN (2 * DATA_LENGTH) /*!< I2C slave tx buffer size */ | ||
#define I2C_SLAVE_RX_BUF_LEN (2 * DATA_LENGTH) /*!< I2C slave rx buffer size */ | ||
#define AMYCHIP_ADDR 0x58 | ||
#define PCM9211_ADDR 0x40 | ||
#define I2C_MASTER_NUM I2C_NUMBER(0) /*!< I2C port number for master dev */ | ||
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */ | ||
#define I2C_MASTER_RX_BUF_DISABLE 0 | ||
|
||
i2c_master_bus_handle_t tool_bus_handle; | ||
i2c_master_dev_handle_t pcm9211_handle; | ||
#define I2C_TOOL_TIMEOUT_VALUE_MS (50) | ||
|
||
esp_err_t i2c_master_write(uint8_t device_addr, uint8_t *data_wr, size_t size_wr) { | ||
esp_err_t ret = i2c_master_transmit(pcm9211_handle, data_wr, size_wr, I2C_TOOL_TIMEOUT_VALUE_MS); | ||
if (ret == ESP_OK) { | ||
//ESP_LOGI(TAG, "Write OK"); | ||
} else if (ret == ESP_ERR_TIMEOUT) { | ||
ESP_LOGW(TAG, "Bus is busy"); | ||
} else { | ||
ESP_LOGW(TAG, "Write Failed"); | ||
} | ||
return 0; | ||
} | ||
|
||
uint8_t pcm9211_readRegister(uint8_t reg) { | ||
uint8_t buf[1] = {reg}; | ||
uint8_t buffer[1] = { 0}; | ||
ESP_ERROR_CHECK(i2c_master_transmit_receive(pcm9211_handle, buf, 1, buffer, 1, I2C_TOOL_TIMEOUT_VALUE_MS)); | ||
return(buffer[0]); | ||
} | ||
|
||
void pcm9211_writeRegister(uint8_t reg, uint16_t value) { | ||
uint8_t data[2]; | ||
data[0] = reg; | ||
data[1] = value; | ||
if (i2c_master_write(PCM9211_ADDR, data, 2)) | ||
fprintf(stderr, "bad write to pcm9211\n"); | ||
} | ||
|
||
esp_err_t setup_pcm9211(void) { | ||
// #System RST Control | ||
fprintf(stderr, "setting up pcm9211\n"); | ||
pcm9211_writeRegister(0x40, 0x33); // Power down ADC, power down DIR, power down DIT, power down OSC | ||
pcm9211_writeRegister(0x40, 0xc0); // Normal operation for all | ||
|
||
// Initialize DIR - both biphase amps on, input from RXIN0 | ||
pcm9211_writeRegister(0x34, 0x00); | ||
// Main Out is DIR/ADC if no DIR sync (these match power-on default, repeated for clarity). | ||
pcm9211_writeRegister(0x26, 0x01); // AUTO selects based on PLL lock error. | ||
pcm9211_writeRegister(0x6B, 0x00); // Main output pins are DIR/ADC AUTO | ||
|
||
// PLL sends 512fs as SCK | ||
pcm9211_writeRegister(0x30, 0x04); | ||
// XTI SCK as 512fs too | ||
pcm9211_writeRegister(0x31, 0x0A); | ||
|
||
// Initialize PCM9211 DIT to send SPDIF from AUXIN1 through MPO0 (pin15). MPO1 (pin16) is VOUT (Valid) | ||
pcm9211_writeRegister(0x60, 0x44); // 0x44 = AUXIN1 | ||
pcm9211_writeRegister(0x78, 0x3d); // MPO0 = 0b1101 = TXOUT, MPO1 = 0b0011 = VOUT | ||
|
||
// Initialize MPIO_C as I2S input to AUXIN1 | ||
pcm9211_writeRegister(0x6F, 0x40); // MPIO_A = CLKST etc / MPIO_B = AUXIN2 / MPIO_C = AUXIN1 | ||
return ESP_OK; | ||
} | ||
|
||
static esp_err_t i2c_master_init(void) { | ||
i2c_master_bus_config_t i2c_bus_config = { | ||
.clk_source = I2C_CLK_SRC_DEFAULT, | ||
.i2c_port = I2C_MASTER_NUM, | ||
.scl_io_num = I2C_MASTER_SCL, | ||
.sda_io_num = I2C_MASTER_SDA, | ||
.flags.enable_internal_pullup = true, | ||
}; | ||
|
||
if (i2c_new_master_bus(&i2c_bus_config, &tool_bus_handle) != ESP_OK) { | ||
return 1; | ||
} | ||
i2c_device_config_t i2c_dev_conf = { | ||
.scl_speed_hz = I2C_CLK_FREQ, | ||
.device_address = PCM9211_ADDR, | ||
.flags.disable_ack_check = true, | ||
}; | ||
if (i2c_master_bus_add_device(tool_bus_handle, &i2c_dev_conf, &pcm9211_handle) != ESP_OK) { | ||
return 1; | ||
} | ||
return ESP_OK; | ||
} | ||
|
||
|
||
|
||
static void i2c_slave_request_cb(uint8_t num, uint8_t *cmd, uint8_t cmd_len, void * arg) { | ||
if (cmd_len > 0) { | ||
// first write to master | ||
i2cSlaveWrite(I2C_SLAVE_NUM, cmd, cmd_len, 0); | ||
} else { | ||
// cmd_len == 0 means master want more data from slave | ||
// we just send one byte 0 each time to master here | ||
uint8_t extra_data = 0x00; | ||
i2cSlaveWrite(I2C_SLAVE_NUM, &extra_data, 1, 0); | ||
} | ||
} | ||
|
||
static void i2c_slave_receive_cb(uint8_t num, uint8_t * data, size_t len, bool stop, void * arg) { | ||
if (len > 0) { | ||
data[len]= 0; | ||
amy_play_message((char*)data); | ||
} | ||
} | ||
|
||
static esp_err_t i2c_slave_init(void) { | ||
i2cSlaveAttachCallbacks(I2C_SLAVE_NUM, i2c_slave_request_cb, i2c_slave_receive_cb, NULL); | ||
return i2cSlaveInit(I2C_SLAVE_NUM, I2C_SLAVE_SDA, I2C_SLAVE_SCL, AMYCHIP_ADDR, I2C_CLK_FREQ, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN); | ||
} | ||
|
||
|
||
|
||
// AMY synth states | ||
extern struct state amy_global; | ||
extern uint32_t event_counter; | ||
extern uint32_t message_counter; | ||
|
||
void esp_show_debug(uint8_t t) { | ||
|
||
} | ||
|
||
// Render the second core | ||
void esp_render_task( void * pvParameters) { | ||
while(1) { | ||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); | ||
amy_render(0, AMY_OSCS/2, 1); | ||
xTaskNotifyGive(alles_fill_buffer_handle); | ||
} | ||
} | ||
|
||
extern int16_t amy_in_block[AMY_BLOCK_SIZE * AMY_NCHANS]; | ||
i2s_sample_type my_int32_block[AMY_BLOCK_SIZE * AMY_NCHANS]; | ||
|
||
// Make AMY's FABT run forever , as a FreeRTOS task | ||
void esp_fill_audio_buffer_task() { | ||
size_t read = 0; | ||
size_t written = 0; | ||
while(1) { | ||
AMY_PROFILE_START(AMY_ESP_FILL_BUFFER) | ||
i2s_channel_read(rx_handle, my_int32_block, AMY_BLOCK_SIZE * sizeof(i2s_sample_type) * AMY_NCHANS, &read, portMAX_DELAY); | ||
|
||
for (int i = 0; i < AMY_BLOCK_SIZE * AMY_NCHANS; ++i) | ||
amy_in_block[i] = (i2s_sample_type)(my_int32_block[i] >> 16); | ||
|
||
|
||
// Get ready to render | ||
amy_prepare_buffer(); | ||
|
||
// Tell the other core to start rendering | ||
xTaskNotifyGive(amy_render_handle); | ||
// Render me | ||
|
||
amy_render(AMY_OSCS/2, AMY_OSCS, 0); | ||
|
||
// Wait for the other core to finish | ||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); | ||
|
||
// Write to i2s | ||
int16_t *block = amy_fill_buffer(); | ||
for (int i = 0; i < AMY_BLOCK_SIZE * AMY_NCHANS; ++i) | ||
my_int32_block[i] = ((i2s_sample_type)block[i]) << 16; | ||
|
||
|
||
AMY_PROFILE_STOP(AMY_ESP_FILL_BUFFER) | ||
|
||
i2s_channel_write(tx_handle, my_int32_block, AMY_BLOCK_SIZE * sizeof(i2s_sample_type) * AMY_NCHANS, &written, portMAX_DELAY); | ||
|
||
if(written != AMY_BLOCK_SIZE * sizeof(i2s_sample_type) * AMY_NCHANS || read != AMY_BLOCK_SIZE * sizeof(i2s_sample_type) * AMY_NCHANS) { | ||
fprintf(stderr,"i2s underrun: [w %d,r %d] vs %d\n", written, read, AMY_BLOCK_SIZE * sizeof(i2s_sample_type) * AMY_NCHANS); | ||
} | ||
|
||
} | ||
} | ||
|
||
|
||
|
||
// init AMY from the esp. wraps some amy funcs in a task to do multicore rendering on the ESP32 | ||
amy_err_t esp_amy_init() { | ||
amy_start(2, 1, 1, 1); | ||
// We create a mutex for changing the event queue and pointers as two tasks do it at once | ||
xQueueSemaphore = xSemaphoreCreateMutex(); | ||
|
||
// Create the second core rendering task | ||
xTaskCreatePinnedToCore(&esp_render_task, ALLES_RENDER_TASK_NAME, ALLES_RENDER_TASK_STACK_SIZE, NULL, ALLES_RENDER_TASK_PRIORITY, &amy_render_handle, ALLES_RENDER_TASK_COREID); | ||
|
||
// Wait for the render tasks to get going before starting the i2s task | ||
delay_ms(100); | ||
|
||
// And the fill audio buffer thread, combines, does volume & filters | ||
xTaskCreatePinnedToCore(&esp_fill_audio_buffer_task, ALLES_FILL_BUFFER_TASK_NAME, ALLES_FILL_BUFFER_TASK_STACK_SIZE, NULL, ALLES_FILL_BUFFER_TASK_PRIORITY, &alles_fill_buffer_handle, ALLES_FILL_BUFFER_TASK_COREID); | ||
|
||
return AMY_OK; | ||
} | ||
|
||
|
||
// Setup I2S | ||
amy_err_t setup_i2s(void) { | ||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_SLAVE); // ************* I2S_ROLE_SLAVE - needs external I2S clock input. | ||
i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle); | ||
|
||
i2s_std_config_t std_cfg = { | ||
.clk_cfg = { | ||
.sample_rate_hz = 48000, | ||
.clk_src = I2S_CLK_SRC_EXTERNAL, | ||
.ext_clk_freq_hz = 48000 * 512, | ||
.mclk_multiple = 512, | ||
}, | ||
.slot_cfg = { | ||
.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT, | ||
.slot_bit_width = I2S_SLOT_BIT_WIDTH_32BIT, | ||
.slot_mode = I2S_SLOT_MODE_STEREO, | ||
.slot_mask = I2S_STD_SLOT_BOTH, | ||
.ws_width = 32, | ||
.ws_pol = false, // false in STD_PHILIPS macro | ||
.bit_shift = false, // true for STD_PHILIPS macro, but that results in *2* bits delay of dout vs lrclk in Follower mode. false gives 1 bit delay, as expected for i2s. | ||
.left_align = false, | ||
.big_endian = false, | ||
.bit_order_lsb = false, | ||
}, | ||
.gpio_cfg = { | ||
.mclk = I2S_MCLK, | ||
.bclk = I2S_BCLK, | ||
.ws = I2S_LRCLK, | ||
.dout = AMYOUT, | ||
.din = AMYIN, | ||
.invert_flags = { | ||
.mclk_inv = false, | ||
.bclk_inv = true, // invert bclk for pcm9211 | ||
.ws_inv = false, | ||
}, | ||
}, | ||
}; | ||
|
||
/* Initialize the channel */ | ||
i2s_channel_init_std_mode(tx_handle, &std_cfg); | ||
i2s_channel_init_std_mode(rx_handle, &std_cfg); | ||
|
||
/* Before writing data, start the TX channel first */ | ||
i2s_channel_enable(tx_handle); | ||
i2s_channel_enable(rx_handle); | ||
return AMY_OK; | ||
} | ||
|
||
void setup_amychip(void) { | ||
check_init(&i2c_master_init, "i2c_master"); | ||
check_init(&i2c_slave_init, "i2c_slave"); | ||
check_init(&setup_pcm9211, "pcm9211"); | ||
check_init(&setup_i2s, "i2s"); | ||
|
||
esp_amy_init(); | ||
amy_reset_oscs(); | ||
} |
Oops, something went wrong.