asynchronous board synching

Status
Not open for further replies.

damo1976

Well-known member
is it possible to introduce micro or nanosecond delays into the I2S stream on either end and if so where would be a good place to put it.

What I'm trying to do is more closely align the I2S streams of 2 teensy boards running asynchronously.

I understand it will never be perfect but one teensy master clock is running slightly faster than the other one and I was hoping i could compensate this using minute delays every so often into the I2S stream to compensate.

The differences are very small but over time the gap becomes larger and larger. Any suggestions other than dont do it would be very welcome. TIA Cheers
 
Maybe run one of the boards in I2S slave mode?

That is, if you're not also using the other non-I2S inputs or outputs that can't work together with slave mode controlling the sample rate.
 
Other than using the hardware audio inputs (I2S, spdif), I'm not aware of any way to get a teensy audio output to precisely sync with an asynchronous clock from another source. I think you could get closer (on T4?) by measuring the two rates and changing AUDIO_SAMPLE_RATE_EXACT on one of them.

You could build an external board that had a tunable clock (eg, Si5351) and would produce an audio sync input into the teensy. Then you just need something like PTP to determine sync between non-wire-connected devices.
 
A tuneable clock is an interesting idea. With I2S slave mode it might work, but you would also need a divide-by-64 circuit, since both BCLK and LRCLK need to be provided.

Perhaps compensating on the software side, by occasionally dropping or duplicating a sample might be the simplest approach?

But to be honest, I really don't have a clear understanding of what you're really trying to do. If the 2 boards have only RF communication between them, I would imagine you'll need very either direct access to the modulation or a very low latency protocol to communicate feedback from one side to another reliably enough to adjust the speed or data.
 
I find the manual quite dense, but as I see it, a clock can be applied to the SPDIF_EXT_CLK pin and this can be used as the SPDIF clock, Then the SAIs can use the SPDIF clock as their clock (see SAIx MCLK3 source select = spdif.spdif_outclock).

I'm not quite motivated enough to order a Si5351 module to try it.
 
Regarding clock rate sync between two non-wired-together sources, just adding a GPS module to each one might be easier than implementing PTP.
 
To be more clear, I could see from the scope that one of the teensy boards is running the I2S clocks slightly faster so over time they go out. I was hoping to do something in software to slow this clock creep down. I could hopefully increase the time it takes for the clocks to go out of time by a factor of 10. Slight delays in all the clocks at the same time is all I need. It wont be heard but it will make a huge difference to the timing.
 
so adjusting this might help?
C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy4\AudioStream.h (3 hits)
Line 57: #ifndef AUDIO_SAMPLE_RATE_EXACT
Line 58: #define AUDIO_SAMPLE_RATE_EXACT 48000.0f
Line 61: #define AUDIO_SAMPLE_RATE AUDIO_SAMPLE_RATE_EXACT
 
AUDIO_SAMPLE_RATE_EXACT is a constant. You need to recompile to change it.

On Teensy 4, the I2S ports run from a clock generated by PLL4, also called the Audio PLL. PLL4 runs in a range between 650 to 1300 MHz and can be programmed with better than 1 Hz resolution. That high frequency goes through a number of configurable integer divisions to become the audio clock. The end result is you can create pretty much any audio clock you want with very high resolution. The dividers have to be integers, but the ultimate PLL4 source is a very high frequency with very fine adjustability.

Frank wrote this code which set PLL4. Here is the relevant code, which you can see uses AUDIO_SAMPLE_RATE_EXACT as the beginning of its calculation of the PLL4 settings.

int fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);

double C = ((double)fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2);

The set_audioClock() function is in utility/imxrt_hw.cpp, where it just configures the hardware with those computed numbers. The PLL4 hardware registers are documented in the reference manual starting on page 1110. Documentation about the various clock dividers and other clock configuration is also in that chapter of the manual. We generally use the same register names in the source code as shown in the manual, so if you find code writing to a hardware register, usually you can just search the huge PDF for that name to get right to the specific documentation for each hardware feature.

The main issue is you can't change the PLL4 settings while it's running. Or at least NXP's documentation says you can't. Whether it can actually / somehow update while running and if it can, how smoothly it might do so, or if shutting it off and changing settings quickly and restarting work well enough are sort of open questions which haven't been explored.

I want to emphasize using PLL4 in creative ways is a matter of experimentation. As you can see in the existing code Frank wrote, so far we've only ever used it the simplest way where it's configured just once at startup and then remains running with that setting.

Hopefully this message can save you some time searching for the relevant tech details and help you get up to speed quickly. This question of dynamically altering the sample rate comes up occasionally, so when / if you do get into this sort of experimentation, I hope you'll share whatever you learn along the way.
 
Shame on me - I see that I did not add a note who wrote that code. The part with the calculation was not written by me.
It was WMXZ, maybe... not sure.. :-( It must be somewhere in the long T4 beta thread.
grmbl... big sorry.

It's best to recreate that in Excel to see what is possible.
The problem is, as Pauls said, you can't modify that at runtime, as the PLL needs to be restarted. This will cause short breaks.

Other problems with 48kHz are: USB-Audio needs adjustments, and some of the Audio Objects have 44.1k hardcoded.
Some months ago I asked who is interested in 48Khz for the whole library. There was (almost) no response.
 
Last edited:
Shame on me - I see that I did not add a note who wrote that code. The part with the calculation was not written by me.
It was WMXZ, maybe... not sure.. :-( It must be somewhere in the long T4 beta thread.
grmbl... sorry.
No problem, but it was your program (contribution), that is referred to.
 
The AUDIO_SAMPLE_RATE_EXACT is a float but when it hits your code wont it lose the decimal accuracy? I'm trying to experiment with 48000.nnn and 47999.nnn . I've only been programming in C seriously for about 6 months so I'm still learning. My project only uses I2S so no need to worry about USB or anything. I am also considering bitbanging the clocks but as yet thats above my experience.

And thanks Paul for your detailed answer. I spend a few hours reading the manual but its quite complicated. I'm also slowly trying to analyse your teensy code and understand how the i2S actually works. What I'd like to do ultimately is delay the clocks dynamically based on another clock coming in from another module. I have an RF module that is receiving packets at (samplerate / 16) so Id love to find a way to send a clock pulse from the RF board after every packet and then with that somehow derive the I2S clocks in the teensy. I know it sounds backwards but the issue I have had from the start is that the RF board does not accurately produce 48k so even though the TX part of this project is using the teensy clocks in 48K and so the radio TX is locking perfectly to that (therefore sending pa ckets at the TX teensy sample rate / 16 (packet size is 16), on the RX end the RF module does not lock accurately at 48k enough for me to use it as the master clock for the RX teensy.. To clarify this is the setup I currently have :
Audio board -> I2S -> TX Teensy (Master) -> I2S -> TX RF Module .......(air)..... RX RF Module - > I2S -> RX Teensy(Master) -> I2S -> Audio Board.

I did it this way because I thought the 2 teensy boards would be closely aligned .. but one is a fraction faster.

Again thanks for yours and everyone's mind on this.

FYI.. its working and I got stereo 48k 16bit streaming at just over 3ms latency from end to end, but the frame mismatch between the RX radio and the RX teensy slides so every so often you get a buffer fram crossover that causes a slight glitch. Happens about every minutes or 2. If only I was able to delay the clocks periodically so over time they stay more in sync with each other based on the speed the radio packets arrive that would be great. Thanks again all.!!
 
As Paul said, the solution is to use one Teensy in slave mode. No "Air".
Or transfer the analog signal. Or use SPDIF.
Not sure about the role of the 2nd Teensy.. is its only role to convert I2S to analog?
Don't know what to add, what's not already written .. :)
 
If i use 1 teensy and dont use (air) then its no longer wireless, which is the whole point of the project I'm doing. The role of the second teensy is to provide user interface functionality ie. LCD, buttons, control of the audio board features.

I'm playing with the code in excel now. I'm still not understanding how the float sample rate with that decimal precision can be implemented when the first thing the calculations do is strip away those decimals and turn it into an integer. If it remained as a float then I could use the precision there to adjust the final PLL clock speed. Otherwise I could just leave it at 48000 and manually adjust the variables in set_audioClock(c0, c1, c2) manually so that I get more precision. Do you know if that would work, ie the code will think its running at 48000 for audio library purposes but the PLL its running from will be different?? I'm not talking drastic changes here but i found if I increase C2 to 1000000 it allows me to adjust C1 to obtain very small changes in the PLL clock speed. I guess what I'm ultimately asking is , does this final PLL clock speed change the i2s clocks?
again, thanks
 
I'm still not understanding how the float sample rate with that decimal precision can be implemented when the first thing the calculations do is strip away those decimals and turn it into an integer.

Code:
#define AUDIO_SAMPLE_RATE_EXACT 44100.0f
?? on your statement "strip away those decimals and turn it into an integer."
 
Yes, it throws the decimals away. They are not needed for the samplefrequency, normally.
Other parts of the lib, however use it as float.

How do you plan to transfer I2s ? you know its speed?
Your RF sems to be in sync. What happens on transmission-errors?

Edit: Yes, if you change the PLL-Freq, you'll change the I2S freq.
 
Last edited:
ok so I can just ditch that calculation code and make the numbers anything I want as calculated manually in excel? are there any limitations? it says c1 and C2 are 30 bit so looks like I can.. This might actually do the trick
 
As Paul explains, the decimal portion of AUDIO_SAMPLE_RATE_EXACT *is* used. On a T4, you can use it (at compile time) to get two asynchronous clocks to be close to the same rate. But clocks change from when you measure them, so it won't be perfect. That would require a dynamically adjustable (aka tunable) clock.

No need to read the manual or use excel - just change AUDIO_SAMPLE_RATE_EXACT.
 
there must be some magic that restores the decimals from an int value.. and can adjust the pll to exactly these deleted sub-hz..
 
Status
Not open for further replies.
Back
Top