Audio cuts out when updating I2C OLED display

Status
Not open for further replies.

garbanzo

New member
Hello Teensys

I am working on project that involves the audio and the wire library. I have connected a generic OLED I2C display to my teensy audio project in order to display some information about what the teensy is doing. When I update the display the audio cuts out. If I put the display.display() function in the main loop I get almost no audio. If I make the display update every few hundred milliseconds I can hear the audio cut out every time the display updates. I have done some goggling on this subject and it looks like the teensy can not do anything else while its sending data out to the I2C bus. It looks like the same issue is described in this post

https://forum.pjrc.com/threads/49187-OLED-updating-slowing-down-motors

Bellow is my troubleshooting code. I am using a Teensy 4.1 with a Adafruit I2S Stereo Decoder UDA1334A and a SSD1306 128x64 OLED display.
I have tied using different I2C bus on the Teensy pins 18, 19 and pins 16, 17 and it did not help. I tried using the wire library and the i2c_t3 library and the end result is the same.
I have watched the audio library demo video on youtube and the last part they have a display showing the audio level in real time with no audio interruption at all using a SPI display. I understand i2c is a slower protocol but is there not no way to use i2c without pausing all other code?



Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

#define OLED_RESET 4
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire1, OLED_RESET);


// GUItool: begin automatically generated code
AudioSynthWaveform       waveform1;      //xy=285,305
AudioEffectEnvelope      envelope1;      //xy=487,307
AudioEffectEnvelope      envelope2;      //xy=491,371
AudioFilterStateVariable filter1;        //xy=743,450
AudioAmplifier           amp1;           //xy=903,444
AudioOutputI2S           i2s1;           //xy=1094,439
AudioConnection          patchCord1(waveform1, envelope1);
AudioConnection          patchCord2(waveform1, envelope2);
AudioConnection          patchCord3(envelope1, 0, filter1, 0);
AudioConnection          patchCord4(envelope2, 0, filter1, 1);
AudioConnection          patchCord5(filter1, 0, amp1, 0);
AudioConnection          patchCord6(amp1, 0, i2s1, 0);
AudioConnection          patchCord7(amp1, 0, i2s1, 1);
// GUItool: end automatically generated code

//Global Variables
const float noteFreqs[128] = {8.176, 8.662, 9.177, 9.723, 10.301, 10.913, 11.562, 12.25, 12.978, 13.75, 14.568, 15.434, 16.352, 17.324, 18.354, 19.445, 20.602, 21.827, 23.125, 24.5, 25.957, 27.5, 29.135, 30.868, 32.703, 34.648, 36.708, 38.891, 41.203, 43.654, 46.249, 48.999, 51.913, 55, 58.27, 61.735, 65.406, 69.296, 73.416, 77.782, 82.407, 87.307, 92.499, 97.999, 103.826, 110, 116.541, 123.471, 130.813, 138.591, 146.832, 155.563, 164.814, 174.614, 184.997, 195.998, 207.652, 220, 233.082, 246.942, 261.626, 277.183, 293.665, 311.127, 329.628, 349.228, 369.994, 391.995, 415.305, 440, 466.164, 493.883, 523.251, 554.365, 587.33, 622.254, 659.255, 698.456, 739.989, 783.991, 830.609, 880, 932.328, 987.767, 1046.502, 1108.731, 1174.659, 1244.508, 1318.51, 1396.913, 1479.978, 1567.982, 1661.219, 1760, 1864.655, 1975.533, 2093.005, 2217.461, 2349.318, 2489.016, 2637.02, 2793.826, 2959.955, 3135.963, 3322.438, 3520, 3729.31, 3951.066, 4186.009, 4434.922, 4698.636, 4978.032, 5274.041, 5587.652, 5919.911, 6271.927, 6644.875, 7040, 7458.62, 7902.133, 8372.018, 8869.844, 9397.273, 9956.063, 10548.08, 11175.3, 11839.82, 12543.85};
byte globalNote = 0;
byte globalVelocity = 0;
long previousMillis = 0;
bool envelope_state = true;
int PlayingNote = 60;
long DisplayRefresh = 0;

void setup() {
  Wire1.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  Wire1.setClock(400000);
  display.clearDisplay();
  display.display();
  
  AudioMemory(300);
  waveform1.begin(WAVEFORM_SQUARE);
  waveform1.amplitude(0.75);
  waveform1.frequency(440);
  waveform1.phase(0);

  envelope1.attack(50);
  envelope1.decay(5);
  envelope1.sustain(1.0);
  envelope1.release(250);
  envelope2.delay(0);
  envelope2.hold(0);
  filter1.octaveControl(7.0);
  filter1.resonance(2.0);
  envelope1.noteOn();
    envelope2.noteOn();
}

void loop() {
waveform1.frequency(noteFreqs[PlayingNote]);

unsigned long currentMillis = millis();
 if (currentMillis - DisplayRefresh > 750) {
  DisplayRefresh = currentMillis;
  UserDisplay();
 }
 
} // end of main loop

void UserDisplay(){
  display.clearDisplay();
 display.setTextColor(WHITE);
 display.setTextSize(1);
 display.setCursor(0,0);
   display.print("Teensy Bass");
   display.setTextSize(2);
   display.setCursor(0,24);
   display.print("Note ");
   display.print(PlayingNote);
   display.display();
}
 
Try using the U8g2lib which is non-blocking. At least here using teensy 3.2 with a SD1306 OLED on I2C Pins 18+19 and a PT8211. Yeah, i know but I like weird performing D/As, at least for this project....
 
I think the T4 I2C does not yet support DMA which means, when you are sending large blocks of pixel data, you are blocking the entire CPU while those bytes are very slowly pumped out using the standard Wire library. Teensy 3.X had I2C DMA, so you could try a Teensy 3.X and see if you have the same problem.

There is also this thread which looks at async transfers instead of relying on DMA to avoid blocking the processor.
https://forum.pjrc.com/threads/61681-Teensy-4-Master-i2c-DMA-possible
 
Thank you for the reply.

I tried the code with the U8g2lib as fdaniels suggested, but the same thing happens. The audio cuts out every time the display updates. The issue seams to be with the Wire library like Blackaddr suggested.

I downloaded and installed the teensy4_i2c library. I managed to get the code to compile after having replaced all the #include "Wire.h" with #include <i2c_driver_wire.h> in all the files that reference the wire library (this is all the adafruit graphics library files, and the teensy audio library codec control files). Unfortunately the end result is the same. The audio cuts out when the display updates.

Is there anything else I can do to try and get an I2C display to work on a Teensy 4.1 smoothly? Unfortunately I do not have a teensy 3.x to play with.
 
Hi garbanzo!

This is not a straight solution to your problem, but I hope you or anyone coming through this thread will find this piece of information helpful.

I currently have a setup similar to yours, but with different chips, and I'm experiencing no audio issues at all!

Specifically I'm using:
Teensy 4.1, Teensyduino 1.53 on Arduino IDE 1.8.13
i2c 128x64 monochrome OLED display, which uses the SH1106 chip, purchased from Aliexpress
audio i2s dac, PCM5102A breakout board, also purchased from Aliexpress.

For running the display, I'm using the U8g2 library and the "standard" Wire library (pin 18 and 19). I am able to update the screen at more or less 20 Hz with no audio glitches.

Here's a video in which you can see my setup https://www.youtube.com/watch?v=PSEETCdL-Rs

I know nothing about DMA, but as you can see I'm using the 4.1 without problems.

Have a nice day,
Gionata
 
Hello everyone

Thank you Gionata for sharing that. I would also be interested in looking at your code to see what I am doing wrong.

I have some developments on this issue. I have ordered and received a SPI display module with the ST7735 driver. I have hooked it up and its doing the same thing, the audio cuts out when the display updates. I went further and replicated the tutorial (Part_3_03_TFT_display) but modified it for the ST7735 display and the same thing happens. The audio playing back is glitchy and distorted... because of the display updating.

Now I am thinking maybe its the ST7735 library so I compiled the Part_3_03_TFT_display stretch (found on in the audio examples) only changing the SPI pins to match the Teensy 4.1 and the SD card pins. I left the original ILI9341_t3 configuration as if it were connected to the teensy and uploaded it. The play back is still distorted(even tho there is no display as I do not have a ILI9341_t3 display).
I did some research and found the TSynth project. I looked at the source code posted on git hub and found that its using the same type ST7735 display (but different size). I connected the display to the same pins as in the TSynth but that did not help.

Now I am thinking it must be something with the way the code compiles. I notices that there has been updates to the arduino IDE and teensyduino so I uninstalled and reinstalled the latest versions and same thing happens.

I dont know what I am doing wrong but I feel like the answer is simple and I am overlooking something. Does any one have any ideas what my issue might be?

Thanks
Garbanzo
 
Hi there.

I too am having delay troubles because of an I2C display, but in my case, it's related to RF24 communication (losing some incoming packets because display refresh takes 50ms, which is too long) and not audio.

Having some programming experience with Microsoft DirectSound and dealing with audio buffers, one theoretical solution that comes to my mind is buffering.

I have no knowledge of UDA1334A and its supporting library but theoretically, you must ensure that UDA1334A has enough data to play back while the display is being updated in the loop(). Of course, this idea totally breaks down if Teensy + UDA1334A is not capable of delivering 50ms of audio data through some CPU-free way while running some heavy task inside the loop().

This also makes me wonder if there is any other way to prepare a longer buffer of data and feed it to UDA1334A through some intermediary device... if there was such a thing as a "streaming memory" chip :D

Another solution that came to mind is to try moving the display updates to a separate thread. There is TeensyThreads library. However, it's just concurrency, not parallelism. The SD1306 library commands cannot be effectively split into smaller schedulable tasks, thus the LCD update will steal all the CPU time from audio streaming anyway.

If the ideas above are not feasible, I have the last one which should work for sure. Add another microcontroller (can use something other than Teensy) dedicated solely for non-realtime work (LCD updates, sensors that are ok to use with 50ms latency etc.) and push your data (not pixels, but values that affect the image) from your audio Teensy to the LCD controller though some convenient protocol, depending on free pins (SPI, I2C, serial). I myself also am leaning this way - to split the system into highest-priority real-time part and lower-priority part.
 
Last edited:
Status
Not open for further replies.
Back
Top