Hello,
So I finally got my hands on a teensy 3.2. I am having some difficulty with the I2S implementation.
After using a logic analyzer, I found that the radio has a LRCLK and a BCLK that are always active where I am trying to send an audio signal. I can turn off the LRCLK by writing to the I2C bus, but I haven't figured out how to turn off the BCLK. This was determined using a cheap 24mhz 8 channel logic analyzer.
So, I put my prototype ESP32 board into I2S Slave mode by modifying one of the examples to send sine waves in slave mode. Code for ESP32 looks like this:
Code:
/* I2S Example
This example code will output 100Hz sine wave and triangle wave to 2-channel of I2S driver
Every 5 seconds, it will change bits_per_sample [16, 24, 32] for i2s data
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s.h"
#include "esp_system.h"
#include <math.h>
#define SAMPLE_RATE (36000)
#define I2S_NUM (0)
#define WAVE_FREQ_HZ (100)
#define PI (3.14159265)
#define I2S_BCK_IO (GPIO_NUM_26)
#define I2S_WS_IO (GPIO_NUM_25)
#define I2S_DO_IO (GPIO_NUM_22)
#define I2S_DI_IO (-1)
#define SAMPLE_PER_CYCLE (SAMPLE_RATE/WAVE_FREQ_HZ)
static void setup_triangle_sine_waves(int bits)
{
int *samples_data = malloc(((bits+8)/16)*SAMPLE_PER_CYCLE*4);
unsigned int i, sample_val;
double sin_float, triangle_float, triangle_step = (double) pow(2, bits) / SAMPLE_PER_CYCLE;
size_t i2s_bytes_write = 0;
printf("\r\nTest bits=%d free mem=%d, written data=%d\n", bits, esp_get_free_heap_size(), ((bits+8)/16)*SAMPLE_PER_CYCLE*4);
triangle_float = -(pow(2, bits)/2 - 1);
for(i = 0; i < SAMPLE_PER_CYCLE; i++) {
sin_float = sin(i * PI / 180.0);
if(sin_float >= 0)
triangle_float += triangle_step;
else
triangle_float -= triangle_step;
sin_float *= (pow(2, bits)/2 - 1);
if (bits == 16) {
sample_val = 0;
sample_val += (short)triangle_float;
sample_val = sample_val << 16;
sample_val += (short) sin_float;
samples_data[i] = sample_val;
} else if (bits == 24) { //1-bytes unused
samples_data[i*2] = ((int) triangle_float) << 8;
samples_data[i*2 + 1] = ((int) sin_float) << 8;
} else {
samples_data[i*2] = ((int) triangle_float);
samples_data[i*2 + 1] = ((int) sin_float);
}
}
i2s_set_clk(I2S_NUM, SAMPLE_RATE, bits, 2);
//Using push
// for(i = 0; i < SAMPLE_PER_CYCLE; i++) {
// if (bits == 16)
// i2s_push_sample(0, &samples_data[i], 100);
// else
// i2s_push_sample(0, &samples_data[i*2], 100);
// }
// or write
i2s_write(I2S_NUM, samples_data, ((bits+8)/16)*SAMPLE_PER_CYCLE*4, &i2s_bytes_write, 100);
free(samples_data);
}
void app_main(void)
{
//for 36Khz sample rates, we create 100Hz sine wave, every cycle need 36000/100 = 360 samples (4-bytes or 8-bytes each sample)
//depend on bits_per_sample
//using 6 buffers, we need 60-samples per buffer
//if 2-channels, 16-bit each channel, total buffer is 360*4 = 1440 bytes
//if 2-channels, 24/32-bit each channel, total buffer is 360*8 = 2880 bytes
i2s_config_t i2s_config = {
.mode = I2S_MODE_SLAVE | I2S_MODE_TX, // Only TX
.sample_rate = SAMPLE_RATE,
.bits_per_sample = 16,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
.communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
.dma_buf_count = 6,
.dma_buf_len = 60,
.use_apll = true,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1
};
i2s_pin_config_t pin_config = {
.bck_io_num = I2S_BCK_IO,
.ws_io_num = I2S_WS_IO,
.data_out_num = I2S_DO_IO,
.data_in_num = I2S_DI_IO //Not used
};
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM, &pin_config);
int test_bits = 16;
while (1) {
setup_triangle_sine_waves(test_bits);
vTaskDelay(5000/portTICK_RATE_MS);
test_bits += 8;
if(test_bits > 32)
test_bits = 16;
}
}
This code generates sine waves and they sound very good in slave mode. In master mode, it sounds just like a bunch of static.
When I try to put the teensy 3.2 in I2S Slave mode, and send audio from my PC via USB, it sounds like there is a lot of static - just like when the ESP32 is in I2S master mode. I verified with the logic analyzer that the BCLK and LRCLK are not generating a signal on the teensy. I am not sure if this is an issue with the configuration of the I2S data on the teensy or an issue with the driver, or if there is more testing that needs to be done on my end.
This is the code I used to send audio over usb on the teensy 3.2 in I2S Slave mode.
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
AudioInputUSB usb1; //xy=200,69 (must set Tools > USB Type to Audio)
AudioOutputI2Sslave i2sslave1; //xy=443,232
AudioConnection patchCord1(usb1, 0, i2sslave1, 0);
AudioConnection patchCord2(usb1, 1, i2sslave1, 1);
void setup() {
AudioMemory(80);
}
void loop() {
}
I am currently digging through this and troubleshooting, but if anyone has any suggestions for me I would really appreciate them!
1) I am going to mess with the frequency, sample rates, etc of the ESP32 to see if it sounds good at all types of different settings. Maybe just this one particular sine wave sounds good.
2) I am gong to double check how the teensy sends data in I2S slave mode with the logic analyzer. Maybe it is sending data in master mode despite the code specifying slave?
3) I do not know how to modify the I2S settings for the teensy, I am thinking they are in Audio.h. I am not great at C, so I am learning as I go. My plan is to mess with Audio.h, rename it, and load it up separately to see if it helps. This sounds like a pain though. Is there a way to just re-define the configuration in the main script?
Thanks for looking this over, and I appreciate your help!
Alex