Using Serial5 on T4.1 seems to interfere with T4 Audio Adapter Board

kd5rxt-mark

Well-known member
BACKGROUND: I have continued to expand my TeensyMIDIPolySynth project & am very pleased with how it keeps progressing. The current version uses a T4.1 + Audio Adapter Board + RA8875 display. All controls (buttons & sliders) are managed from custom menus & screens generated on the RA8875. Unfortunately, I have come to realize that a high-pitched noise is injected into the audio stream if/when the RA8875 is touched for control modification by the operator, so my latest activity involves limiting the existing T4.1 strictly for driving the display & managing operator inputs. In an attempt to eliminate this noise, I will be adding another T4.1 strictly for generating the audio. My plan is to connect the two T4.1s serially. As a side benefit, by splitting the functionality across a pair of T4.1s, I can also reduce my RAM usage, for which I am constantly bumping against the top end - I've left as many of my code functions in FLASHMEM as I dare & I put as many variables as I could into RAM2 (those that don't require any specific startup initialization), but it is still a continual challenge to keep from exceeding available RAM.

CONDITIONS:
Arduino IDE Configuration (last built with Arduino 1.8.19 + Teensyduino 1.56):
Tools/Board: "Teensy 4.1"
Tools/USB Type: "Serial + MIDI"
Tools/CPU Speed: "600MHz"
Tools/Optimize: "Smallest Code"
Tools/Keyboard Layout: "US English"
Tools/Port: "COMx Serial+MIDI (Teensy 4.1)"


In order to investigate an apparent conflict between using Serial5 on the T4.1 while connected to a T4 Audio Adapter Board, I am running the following code (NOTE: this assumes that you have a T4.1 stacked directly on top of a T4 Audio Adapter Board using properly soldered mating headers, and that you have the volume pot installed on the Audio Adapter Board and/or you have a pot connected between 3.3VDC & GROUND, with the wiper connected to A1) to demonstrate how the basic capability is expected to operate (this code represents a bare-bones extract of my Teensy-MIDI-RA8875-PolySynth . . . just enough to reproduce the apparent problem). Using this simple example, the serial comms from the DISPLAY T4.1 to the AUDIO T4.1 can be observed in the Serial Monitor (showing a very simple example of the configuration data set by the operator using the menus & displays, which is then forwarded serially to the AUDIO T4.1 to configure & drive the Audio Adapter Board capabilities).

Code:
//
//  Demonstration of Teensy Audio + Serial5 Conflict
//
//     Pared down from Teensy 4.1 MIDI (12-note / 3-voice) RA8875-Controlled Polyphonic Synthesizer
//
//  Arduino IDE Configuration (last built with Arduino 1.8.19 + Teensyduino 1.56):
//     Tools/Board:           "Teensy 4.1"
//     Tools/USB Type:        "Serial + MIDI"
//     Tools/CPU Speed:       "600MHz"
//     Tools/Optimize:        "Smallest Code"
//     Tools/Keyboard Layout: "US English"
//     Tools/Port:            "COMx Serial+MIDI (Teensy 4.1)"
//

#include <SPI.h>
#include <Audio.h>

// GUItool: begin automatically generated code
AudioSynthWaveformModulated VFOsquare;      //xy=129.5,120
AudioOutputI2S           i2s1;           //xy=309.5,120
AudioConnection          patchCord1(VFOsquare, 0, i2s1, 0);
AudioConnection          patchCord2(VFOsquare, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=129.5,60
// GUItool: end automatically generated code



#define EAserial Serial


// MIDI note-to-frequency data from: http://tonalsoft.com/pub/news/pitch-bend.aspx

#define FLOAT_NOTE_OFF  0.000
#define FLOAT_NOTE_END  99999.999
#define FLOAT_NOTE_C0   16.352
#define FLOAT_NOTE_CS0  17.324
#define FLOAT_NOTE_D0   18.354
#define FLOAT_NOTE_DS0  19.445
#define FLOAT_NOTE_E0   20.602
#define FLOAT_NOTE_F0   21.827
#define FLOAT_NOTE_FS0  23.125
#define FLOAT_NOTE_G0   24.500
#define FLOAT_NOTE_GS0  25.957
#define FLOAT_NOTE_A0   27.500
#define FLOAT_NOTE_AS0  29.135
#define FLOAT_NOTE_B0   30.868
#define FLOAT_NOTE_C1   32.703
#define FLOAT_NOTE_CS1  34.648
#define FLOAT_NOTE_D1   36.708
#define FLOAT_NOTE_DS1  38.891
#define FLOAT_NOTE_E1   41.203
#define FLOAT_NOTE_F1   43.654
#define FLOAT_NOTE_FS1  46.249
#define FLOAT_NOTE_G1   48.999
#define FLOAT_NOTE_GS1  51.913
#define FLOAT_NOTE_A1   55.000
#define FLOAT_NOTE_AS1  58.270
#define FLOAT_NOTE_B1   61.735
#define FLOAT_NOTE_C2   65.406
#define FLOAT_NOTE_CS2  69.296
#define FLOAT_NOTE_D2   73.416
#define FLOAT_NOTE_DS2  77.782
#define FLOAT_NOTE_E2   82.407
#define FLOAT_NOTE_F2   87.307
#define FLOAT_NOTE_FS2  92.499
#define FLOAT_NOTE_G2   97.999
#define FLOAT_NOTE_GS2  103.826
#define FLOAT_NOTE_A2   110.000
#define FLOAT_NOTE_AS2  116.541
#define FLOAT_NOTE_B2   123.471
#define FLOAT_NOTE_C3   130.813
#define FLOAT_NOTE_CS3  138.591
#define FLOAT_NOTE_D3   146.832
#define FLOAT_NOTE_DS3  155.563
#define FLOAT_NOTE_E3   164.814
#define FLOAT_NOTE_F3   174.614
#define FLOAT_NOTE_FS3  184.997
#define FLOAT_NOTE_G3   195.998
#define FLOAT_NOTE_GS3  207.652
#define FLOAT_NOTE_A3   220.000
#define FLOAT_NOTE_AS3  233.082
#define FLOAT_NOTE_B3   246.942
#define FLOAT_NOTE_C4   261.626
#define FLOAT_NOTE_CS4  277.183
#define FLOAT_NOTE_D4   293.665
#define FLOAT_NOTE_DS4  311.127
#define FLOAT_NOTE_E4   329.628
#define FLOAT_NOTE_F4   349.228
#define FLOAT_NOTE_FS4  369.994
#define FLOAT_NOTE_G4   391.995
#define FLOAT_NOTE_GS4  415.305
#define FLOAT_NOTE_A4   440.000
#define FLOAT_NOTE_AS4  466.164
#define FLOAT_NOTE_B4   493.883
#define FLOAT_NOTE_C5   523.251
#define FLOAT_NOTE_CS5  554.365
#define FLOAT_NOTE_D5   587.330
#define FLOAT_NOTE_DS5  622.254
#define FLOAT_NOTE_E5   659.255
#define FLOAT_NOTE_F5   698.456
#define FLOAT_NOTE_FS5  739.989
#define FLOAT_NOTE_G5   783.991
#define FLOAT_NOTE_GS5  830.609
#define FLOAT_NOTE_A5   880.000
#define FLOAT_NOTE_AS5  932.328
#define FLOAT_NOTE_B5   987.767
#define FLOAT_NOTE_C6   1046.502
#define FLOAT_NOTE_CS6  1108.731
#define FLOAT_NOTE_D6   1174.659
#define FLOAT_NOTE_DS6  1244.508
#define FLOAT_NOTE_E6   1318.510
#define FLOAT_NOTE_F6   1396.913
#define FLOAT_NOTE_FS6  1479.978
#define FLOAT_NOTE_G6   1567.982
#define FLOAT_NOTE_GS6  1661.219
#define FLOAT_NOTE_A6   1760.000
#define FLOAT_NOTE_AS6  1864.655
#define FLOAT_NOTE_B6   1975.533
#define FLOAT_NOTE_C7   2093.005
#define FLOAT_NOTE_CS7  2217.461
#define FLOAT_NOTE_D7   2349.318
#define FLOAT_NOTE_DS7  2489.016
#define FLOAT_NOTE_E7   2637.020
#define FLOAT_NOTE_F7   2793.826
#define FLOAT_NOTE_FS7  2959.955
#define FLOAT_NOTE_G7   3135.963
#define FLOAT_NOTE_GS7  3322.438
#define FLOAT_NOTE_A7   3520.000
#define FLOAT_NOTE_AS7  3729.310
#define FLOAT_NOTE_B7   3951.066
#define FLOAT_NOTE_C8   4186.009
#define FLOAT_NOTE_CS8  4434.922
#define FLOAT_NOTE_D8   4698.636
#define FLOAT_NOTE_DS8  4978.032
#define FLOAT_NOTE_E8   5274.041
#define FLOAT_NOTE_F8   5587.651
#define FLOAT_NOTE_FS8  5919.911
#define FLOAT_NOTE_G8   6271.927
#define FLOAT_NOTE_GS8  6644.875
#define FLOAT_NOTE_A8   7040.000
#define FLOAT_NOTE_AS8  7458.620
#define FLOAT_NOTE_B8   7902.133
#define FLOAT_NOTE_C9   8372.018
#define FLOAT_NOTE_CS9  8869.844
#define FLOAT_NOTE_D9   9397.273
#define FLOAT_NOTE_DS9  9956.063
#define FLOAT_NOTE_E9   10548.082
#define FLOAT_NOTE_F9   11175.303
#define FLOAT_NOTE_FS9  11839.822
#define FLOAT_NOTE_G9   12543.854
#define FLOAT_NOTE_GS9  13289.750
#define FLOAT_NOTE_A9   14080.000
#define FLOAT_NOTE_AS9  14917.240
#define FLOAT_NOTE_B9   15804.266
#define FLOAT_NOTE_C10  16744.036


typedef enum
{
   EA_INDEX_MAIN_LINE_VOLUME_SLIDER = 0,
   EA_INDEX_MAIN_PHONES_VOLUME_SLIDER,
} EA_INDEX;

struct NOTE_ENTRY
{
   float noteValue;
   int   durationInMillis;
};

#define TOTAL_NOTE_ENTRIES 12

NOTE_ENTRY notes[TOTAL_NOTE_ENTRIES] =
{  {FLOAT_NOTE_C4,    50}, {FLOAT_NOTE_OFF, 100},
   {FLOAT_NOTE_DS4,   50}, {FLOAT_NOTE_OFF, 100},
   {FLOAT_NOTE_FS4,   50}, {FLOAT_NOTE_OFF, 100},
   {FLOAT_NOTE_A4,    50}, {FLOAT_NOTE_OFF, 100},
   {FLOAT_NOTE_C5,    50}, {FLOAT_NOTE_OFF, 100},
   {FLOAT_NOTE_C3,   250},
   {FLOAT_NOTE_OFF, 1000},
};


int runningAvgVolumePotValue = analogRead(A1);

#define VOLUME_POT_SAMPLE_MILLIS 250

unsigned long targetMillis = millis();
unsigned long nextVolumeSampleMillis = millis() + VOLUME_POT_SAMPLE_MILLIS;
int noteIndex = -1;

// main loop
void loop()
{
   int newVolumePotValue = analogRead(A1);

   if (millis() > nextVolumeSampleMillis)
   {
      nextVolumeSampleMillis += VOLUME_POT_SAMPLE_MILLIS;

      if (runningAvgVolumePotValue != (int)((((float)runningAvgVolumePotValue * 2.0) + (float)newVolumePotValue) / 3.0))
      {
         runningAvgVolumePotValue = (int)((((float)runningAvgVolumePotValue  * 2.0) + (float)newVolumePotValue) / 3.0);

         sgtl5000_1.lineOutLevel(map(runningAvgVolumePotValue, 0, 1023, 0, 15));
         sgtl5000_1.volume(0.8 * ((float)runningAvgVolumePotValue / 1024.0));

         sendEAsliderValue(EA_INDEX_MAIN_PHONES_VOLUME_SLIDER, runningAvgVolumePotValue);
         sendEAsliderValue(EA_INDEX_MAIN_LINE_VOLUME_SLIDER, map(runningAvgVolumePotValue, 0, 1023, 0, 15));
      }
   }

   // if it's time to play the next note
   if (millis() >= targetMillis)
   {
      if (++noteIndex > (TOTAL_NOTE_ENTRIES - 1))
      {
         noteIndex = 0;
      }

      targetMillis = millis() + notes[noteIndex].durationInMillis;

      if (notes[noteIndex].noteValue != FLOAT_NOTE_END)
      {
         if (notes[noteIndex].noteValue == FLOAT_NOTE_OFF)
         {
            VFOsquare.amplitude(0.0);
         } else {
            VFOsquare.frequency(notes[noteIndex].noteValue);
            VFOsquare.amplitude(1.0);
         }
      } else {
         VFOsquare.amplitude(0.0);
      }
   }
}  // loop()


// send an external audio (EA) slider value
void sendEAsliderValue(EA_INDEX index, float value)
{
   float localValue = ((float)((int)(value * 100))) / 100.00;

   EAserial.print("EAs:0x");

   // send the first digit of the index (in hex)
   if ((index / 256) <= 9)
   {
      EAserial.print((char)((index / 256) + '0'));
   } else {
      EAserial.print((char)((index / 256) + 'A' - 10));
   }

   // send the second digit of the index (in hex)
   if (((index / 16) % 16) <= 9)
   {
      EAserial.print((char)(((index / 16) % 16) + '0'));
   } else {
      EAserial.print((char)(((index / 16) % 16) + 'A' - 10));
   }

   // send the third digit of the index (in hex)
   if ((index % 16) <= 9)
   {
      EAserial.print((char)((index % 16) + '0'));
   } else {
      EAserial.print((char)((index % 16) + 'A' - 10));
   }

   EAserial.print(":");

   if (value >= 0.00)
   {
      EAserial.print("+");
   } else {
      EAserial.print("-");
   }

   localValue = abs(localValue);
   EAserial.print((char)((int)(localValue / 10000) + '0'));
   EAserial.print((char)(((int)(localValue / 1000) % 10) + '0'));
   EAserial.print((char)(((int)(localValue / 100) % 10) + '0'));
   EAserial.print((char)(((int)(localValue / 10) % 10) + '0'));
   EAserial.print((char)(((int)localValue % 10) + '0'));

   EAserial.print(".");

   EAserial.print((char)(((int)(localValue * 10) % 10) + '0'));
   EAserial.print((char)(((int)(localValue * 100) % 10) + '0'));

   EAserial.println(";");
}  // sendEAsliderValue()


// one-time setup
void setup()
{
   Serial.begin(57600);
   while (!Serial && (millis() <= 1000));

   EAserial.begin(115200);
   while (!EAserial && (millis() <= 1000));

   AudioMemory(128);

   sgtl5000_1.enable();
}  // setup()


// EOF placeholder


Because I've consumed a large number of the T4.1 pins already, I am relegated to using either Serial4 or Serial5 on the DISPLAY T4.1. Now, here's where things get weird: If "EAserial" is defined to be "Serial4", everything (with the exception of being able to monitor the data in the Serial Monitor, since it is no longer using Serial) works just like it did before (when using Serial). The audio is generated as expected by the Audio Adapter Board.

Code:
#define EAserial Serial4  // works just fine with the Audio Adapter Board

However, if "EAserial" is defined to be "Serial5", then no audio is generated by the Audio Adapter Board.

Code:
#define EAserial Serial5  // conflicts with the Audio Adapter Board

I did try adding varying amounts of delay before, between, & after the Serialx.begin() calls, but did not find any combinations that made any difference. I am content using Serial4, but am really curious why Serial5 does not also work ?!?!?

Mark J Culross
KD5RXT
 
The audio shield needs pins 20 and 21 for audio clocks.

https://www.pjrc.com/store/teensy3_audio.html

Wow, how did I miss that ?? Don't I feel a little foolish right now !! Thanks for the quick reply, Paul. For my more complex projects (like this one), I always include a "pin usage" diagram in the project source, so I'll complete the diagram in my Teensy-MIDI-RA8875-PolySynth by adding that info.

Thanks again !!

Mark J Culross
KD5RXT
 
Back
Top