Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to set I2S APLL Clock for 48khz 32bit 2ch (IDFGH-432) #2634

Closed
pad52 opened this issue Oct 25, 2018 · 19 comments
Closed

Unable to set I2S APLL Clock for 48khz 32bit 2ch (IDFGH-432) #2634

pad52 opened this issue Oct 25, 2018 · 19 comments

Comments

@pad52
Copy link

pad52 commented Oct 25, 2018

Environment

  • Core (if using chip or module): ESP-WROOM32
  • IDF version 7c29a39
  • Development Env: Eclipse
  • Operating System: MacOS
  • Power Supply: external 3.3V

Problem Description

In I2S Slave TX mode
i2s_mode_t mode = I2S_MODE_SLAVE | I2S_MODE_TX; i2s_comm_format_t comm_fmt = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB;

If I set a frequency of 48Khz and APLL, it will not set the frequency correctly and I got audio problems, I imagine is because of DMA buffer under/overrun.

I (352) phy: phy_version: 4000, b6198fa, Sep 3 2018, 15:11:06, 0, 2 I (902) I2S: queue free spaces: 1 I (912) I2S: DMA Malloc info, datalen=blocksize=4088, dma_buf_count=16 I (912) I2S: APLL: Req RATE: 48000, real rate: 43945.238, BITS: 32, CLKM: 1, BCK_M: 8, MCLK: 22499962.000, SCLK: 2812495.250000, diva: 1, divb: 0 I (932) BT_APP_CORE: bt_app_task_handler, sig 0x1, 0x0 I (932) main: RAM left 75544

If I disable APLL, then the frequency is correctly set - but I get crappy sound due to fractional PLL.

In case I set mclk_fixed (at 24573600.000) I get 50Khz:

I (781) I2S: queue free spaces: 1 I (781) I2S: DMA Malloc info, datalen=blocksize=4088, dma_buf_count=16 I (791) I2S: APLL: Req RATE: 48000, real rate: 50223.129, BITS: 32, CLKM: 1, BCK_M: 7, MCLK: 22499962.000, SCLK: 2812495.250000, diva: 1, divb: 0 I (801) BT_APP_CORE: bt_app_task_handler, sig 0x1, 0x0 I (811) main: RAM left 75668

I imagine that the problem could be in function i2s_apll_calculate_fi2s
that have a bad behaviour in case of 48Khz 32bit 2ch.

this issue could be related

Then further investigation get me through an error in the documentation:
APLL_MAX_FREQ = 500Mhz and
APLL_MIN_FREQ = 250 Mhz

but in the description of we read:
* The dividend in this expression should be in the range of 240 - 600 MHz.
and
* 1. We will choose the parameters with the highest level of change,
* With 350MHz<fout<500MHz, we limit the sdm2 from 4 to 9,

What's the trouth?

Steps to reproduce

Take A2DP_Sink Example and set I2S at 48Khz, Slave, 32Bit.

@pad52 pad52 changed the title Unable to set I2S Clock for 48khz 32bit 2ch Unable to set I2S APLL Clock for 48khz 32bit 2ch Oct 25, 2018
@Alvin1Zhang Alvin1Zhang changed the title Unable to set I2S APLL Clock for 48khz 32bit 2ch [TW#27004] Unable to set I2S APLL Clock for 48khz 32bit 2ch Oct 26, 2018
@pad52
Copy link
Author

pad52 commented Oct 29, 2018

For who is interested in setting up manually the APLL Clock (setting up manually odir, sdm0, sdm1, sdm2) I made this not really nice C program but maybe could be useful for someone and could be a starting point to make a better APLL setup procedure:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>


#define APLL_MIN_FREQ (250000000)
#define APLL_MAX_FREQ (500000000)

int bits_per_sample = 32; //<-- Put your Bits per sample preferences here according to menuconfig

static float i2s_apll_get_fi2s(int bits_per_sample, int sdm0, int sdm1, int sdm2, int odir)
{
    int f_xtal = 40 * 1000000; //<-- Put your XTAL preferences here according to menuconfig
    
    float fout = f_xtal * (sdm2 + sdm1 / 256.0f + sdm0 / 65536.0f + 4);
    if (fout < APLL_MIN_FREQ || fout > APLL_MAX_FREQ) {
        return APLL_MAX_FREQ;
    }
    float fpll = fout / (2 * (odir+2)); 
    return fpll/2;
}

int main(){
    int odir, sdm0, sdm1, sdm2;

    int odir_def, sdm0_def, sdm1_def, sdm2_def;
    
    float avg;
    float rate, rate_wanted, min_rate, max_rate, min_diff;
    float error, error_tmp;

    sdm0 = 0;
    sdm1 = 0;
    sdm2 = 0;
    odir = 0;
    min_diff = APLL_MAX_FREQ;

    rate_wanted = 24573696.000; //<-- Put your Bit Clock Wanted 

    rate = i2s_apll_get_fi2s(bits_per_sample, sdm0, sdm1, sdm2, odir);

    error_tmp = abs(rate - rate_wanted);

    printf("error_tmp %f\n", error_tmp );

    for (sdm2 = 4; sdm2 < 9; sdm2 ++) {
        for (odir = 0; odir < 32; odir ++) {
            for (sdm1 = 0; sdm1 < 256; sdm1 ++) {
                for (sdm0 = 0; sdm0 < 256; sdm0 ++) {

                    rate = i2s_apll_get_fi2s(bits_per_sample, sdm0, sdm1, sdm2, odir);

                    error_tmp = abs(rate - rate_wanted);

                    if( abs(error_tmp <= error) ){  
                        error = error_tmp;
                        sdm0_def = sdm0;
                        sdm1_def = sdm1;
                        sdm2_def = sdm2;
                        odir_def = odir;
                    }

                }
            }
        }
    }

    printf("sdm0= %d, sdm1= %d, sdm2= %d, odir= %d\n",sdm0_def, sdm1_def, sdm2_def, odir_def);
    printf("freq wanted: %f, freq reached: %f, error: %f\n", rate_wanted, i2s_apll_get_fi2s(bits_per_sample, sdm0_def, sdm1_def, sdm2_def, odir_def), error );
}

Said so, I'm not sure that in case I2S is in Slave mode, setting up a apll frequency by hand it's useful at all.. but maybe in Master mode?

@macaba
Copy link

macaba commented Nov 19, 2018

@pad52 Thank you for this code snippet, I will be trying this out for my I2S master - I have not been able to get the exact APLL frequency I needed either at 48kHz, I had to use 44.1kHz to get a frequency multiple that my I2S codec could support. (i.e. 384x)

@macaba
Copy link

macaba commented Nov 19, 2018

I tried the code to search all possible APLL parameters at various sampling rates and have arrived at a startling conclusion: the APLL inside the ESP32 can only usefully support 44.1kHz and that 16934400hz is the only useful frequency that the APLL can generate for driving the MCLK on I2S codecs (44.1k x384).

@pad52
Copy link
Author

pad52 commented Nov 21, 2018

Hey, try to set this values at the end of the function i2s_apll_calculate_fi2s() in esp-idf/components/drivers/i2s.c
sdm0=149; sdm1= 212; sdm2= 5; odir= 2;
I think you can arrive at 47.999Khz.

or sdm0= 40, sdm1= 15, sdm2= 7, odir= 4
In case you want a Master Clock that is 384x the freq.

@HanVertegaal
Copy link

HanVertegaal commented Dec 7, 2018

Almost the same problem here. Using a ALB32-WROVER running IDF v3.0.6, reading a 2-channel 32-bit 48 kHz signal in slave mode, I see the following:

image

The I2S log says: I2S: APLL: Req RATE: 48000, real rate: 43945.016, BITS: 32, CLKM: 1, BCK: 8, MCLK: 12292917.167, SCLK: 2812481.000000, diva: 1, divb: 0

The exact same software (and signal) using IDF v3.1.1 shows:

image

In this case the I2S log shows: I2S: PLL_D2: Req RATE: 48000, real rate: 48076.000, BITS: 32, CLKM: 13, BCK: 4, MCLK: 12292917.167, SCLK: 3076864.000000, diva: 64, divb: 1

Tried different settings for use_appl but didn't see any change.

@HanVertegaal
Copy link

Seems I was to quick to complain here. With use_appl = true, this works on both IDF v3.0.x and v3.1.1,
Configuration:
i2s_config.mode = (i2s_mode_t)(I2S_MODE_SLAVE | I2S_MODE_RX); i2s_config.sample_rate = 48000; i2s_config.bits_per_sample = 32; i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; // 2-channels i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB); i2s_config.dma_buf_count = 8; i2s_config.dma_buf_len = 480; i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; i2s_config.use_apll = true; i2s_config.fixed_mclk = 0;

@fknrdcls
Copy link

fknrdcls commented Feb 5, 2019

Here is the I2S driver modification I made last year: APLL should get within < 10 ppm.

If the problem still exists in esp-idf, perhaps someone should replace i2s_apll_calculate_fi2s with this version.

For example:
I (712) I2S: APLL: Req RATE: 48000, real rate: 47999.992, BITS: 32, CLKM: 1, BCK_M: 8, MCLK: 24575996.000, SCLK: 3071999.500000, diva: 1, divb: 0
The MCLK rate checks out on my frequency counter.

static esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm0, int *sdm1, int *sdm2, int *odir)
{
    int _odir, _sdm0, _sdm1, _sdm2;
    float avg;
    float min_rate, max_rate, min_diff;
    if (rate/bits_per_sample/2/8 < APLL_I2S_MIN_RATE) {
        return ESP_ERR_INVALID_ARG;
    }

    *sdm0 = 0;
    *sdm1 = 0;
    *sdm2 = 0;
    *odir = 0;
    min_diff = APLL_MAX_FREQ;

    // Roughly calculate coarse values 

    for (_sdm2 = 4; _sdm2 < 9; _sdm2 ++) {
        max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, _sdm2, 0);
        min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, _sdm2, 31);
        avg = (max_rate + min_rate)/2;
        if(abs(avg - rate) < min_diff) {
            min_diff = abs(avg - rate);
            *sdm2 = _sdm2;
        }
    }
    
    min_diff = APLL_MAX_FREQ;
    for (_odir = 0; _odir < 32; _odir ++) {
        max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, *sdm2, _odir);
        min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, *sdm2, _odir);
        avg = (max_rate + min_rate)/2;
        if(abs(avg - rate) < min_diff) {
            min_diff = abs(avg - rate);
            *odir = _odir;
        }
    }

    // Now calculate best values

    for (_sdm2 = 4; _sdm2 < 9; _sdm2 ++) {
        min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, _sdm2, *odir);
        if(min_rate <= rate) {   //Less or equal ONLY don't overshoot
            *sdm2 = _sdm2;
        }
    }

    min_diff = APLL_MAX_FREQ;
    for (_sdm1 = 0; _sdm1 < 256; _sdm1 ++) {
        //max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, _sdm1, *sdm2, *odir);
        min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, _sdm1, *sdm2, *odir);
        //avg = (max_rate + min_rate)/2;
        if (min_rate <= rate) {  //Less or equal ONLY don't overshoot
            *sdm1 = _sdm1;
        }
    }


    min_diff = APLL_MAX_FREQ;
    for (_sdm0 = 0; _sdm0 < 256; _sdm0 ++) {
        avg = i2s_apll_get_fi2s(bits_per_sample, _sdm0, *sdm1, *sdm2, *odir);
        if (abs(avg - rate) < min_diff) {  // NOW as close as possible
            min_diff = abs(avg - rate);
            *sdm0 = _sdm0;
        }
    }
    
    return ESP_OK;
}

@projectgus projectgus changed the title [TW#27004] Unable to set I2S APLL Clock for 48khz 32bit 2ch Unable to set I2S APLL Clock for 48khz 32bit 2ch (IDFGH-432) Mar 12, 2019
@ajita02
Copy link

ajita02 commented Jun 7, 2019

Try the attached patch. It has solved the inaccurate apll clock rate issue. The patch is w.r.t. latest IDF i.e. 4.0. Kindly share the result after trial. The percentage difference between required sample rate and apll_clock_rate is 0.001.
0001-driver-i2s-fix-apll_clock_rate-for-different-sample-.zip

@philippe44
Copy link

it is still incorrect for 176400/2/16. The algorithm selects sdm2=7,odir=1,sdm1=sdm0=255 which gives 156249 where sdm2=5,odir=0,sdm1=2,sdm0=222 would have given precisely 176400. I guess we need to either iterate more than once (re-consider odir when accuracy is not reached) or have a different first pass on sdm2 selection. Could not find a good solution yet, with minimal changes.

trombik pushed a commit to trombik/esp-idf that referenced this issue Aug 9, 2019
@philippe44
Copy link

philippe44 commented Aug 9, 2019

I can propose that which, although not very elegant, does the job for me in all cases and in less steps as well. It probably assumes the function knows too much about the PLL though ...

static esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm0, int *sdm1, int *sdm2, int *odir) 
 {
 	int _odir, _sdm0, _sdm1, _sdm2;
	float r = rtc_clk_xtal_freq_get() * 1000000. / (rate * 2 * 2);
	int _sdm2_max;
	uint32_t prec = -1;
	int o, s1, s0;
	
	if (rate/bits_per_sample/2/8 < APLL_I2S_MIN_RATE) {
        return ESP_ERR_INVALID_ARG;
    }

	*sdm0 = 0;
	*sdm1 = 0;
	*sdm2 = 0;
	*odir = 0;

	_sdm2 = 1/r * 2 - 4;
	if (_sdm2 < 4) _sdm2 = 4;
	_sdm2_max = ceil(1/r * (31 + 2) - (255/256 + 255/65536 + 4));
	if (_sdm2_max > 8) _sdm2_max = 8;

	// explore up to 5 sdm2 values
	for (; _sdm2 < _sdm2_max; _sdm2++) {
		_odir = r * (_sdm2 + 4) - 2;
		if (_odir < 0) _odir = 0;
		else if (_odir > 31) _odir = 31;

		for (o = 0; o < 2 && _odir + o < 32; o++) {
			_sdm1 = 256*((_odir + o + 2) / r - (_sdm2 + 4));
			if (_sdm1 < 0) _sdm1 = 0;
			else if (_sdm1 > 255) _sdm1 = 255;

			for (s1 = 0; s1 < 2 && _sdm1 + s1 < 256; s1++) {
				_sdm0 = 65536*((_odir + o + 2) / r - (_sdm2 + (float) (_sdm1 + s1)/256 + 4));
				if (_sdm0 < 0) _sdm1 = 0;
				else if (_sdm0 > 255) _sdm0 = 255;

				for (s0 = 0; s0 < 2 && _sdm2 + s0 < 256; s0++) {
					int _fi2s = i2s_apll_get_fi2s(bits_per_sample, _sdm0 + s0, _sdm1 + s1, _sdm2, _odir + o);
					if (abs(_fi2s - rate) < prec) {
						prec = abs(_fi2s - rate);
						*sdm0 = _sdm0 + s0;
						*sdm1 = _sdm1 + s1;
						*sdm2 = _sdm2;
						*odir = _odir + o;
                    }
				}
			}
		}
	}
	
	if (*sdm2 + *sdm0 + *sdm0 + *odir) return ESP_OK;
	else return ESP_ERR_INVALID_ARG;
 }`

@fknrdcls
Copy link

fknrdcls commented Aug 9, 2019

Did you try my version listed above? Or is it the new version igrr pushed recently?

@philippe44
Copy link

No I did not I thought it was the same as the current version but now I see the difference. It still bothers me that we iterate so many loops where we can shoot approximatively to the right place.

@fknrdcls
Copy link

fknrdcls commented Aug 9, 2019

Mine was only a simple patch to the original version, was just wondering if it gets you 176400. What application are you running that freq for?
I was initially concerned by the iterations but it really doesn't take the ESP32 long to run the routine.

@philippe44
Copy link

philippe44 commented Aug 9, 2019

it's for spdif direct connection, by convincing the i2s driver to be used as a spdif interface. But you need 32 bits per sample and 2x rate because it's BMC encoded. So 44.1 requires a 176.4kHz/16 clock

@redchenjs
Copy link
Contributor

redchenjs commented Aug 21, 2019

Hi @fknrdcls, I tried your version listed above but I could only get 162760.422:

I (1269) I2S: APLL: Req RATE: 176400, real rate: 162760.422, BITS: 16, CLKM: 1, BCK_M: 8, MCLK: 41666668.000, SCLK: 5208333.500000, diva: 1, divb: 0

@fknrdcls
Copy link

That's off by a fair amount, it must be odir that's out I guess. Does your code provided above work for all the common clock rates as well as 176400?

If you could add comments to the code I can test it as per igrr's testing method and then maybe we should submit a pull request once it's been standardised as per the espressif style guide. I'm interested in some of your work, I like your idea of pushing spdif out via the I2S peripheral.

@redchenjs
Copy link
Contributor

@fknrdcls , @philippe44 's code test results are as follows:

I2S: APLL: Req RATE: 10675, real rate: 10675.007, BITS: 16, ERR: 0.000064%
I2S: APLL: Req RATE: 10675, real rate: 10675.001, BITS: 24, ERR: 0.000009%
I2S: APLL: Req RATE: 10675, real rate: 10675.000, BITS: 32, ERR: 0.000000%
I2S: APLL: Req RATE: 11025, real rate: 11024.997, BITS: 16, ERR: 0.000027%
I2S: APLL: Req RATE: 11025, real rate: 11025.000, BITS: 24, ERR: 0.000000%
I2S: APLL: Req RATE: 11025, real rate: 11025.001, BITS: 32, ERR: 0.000009%
I2S: APLL: Req RATE: 16000, real rate: 16000.003, BITS: 16, ERR: 0.000018%
I2S: APLL: Req RATE: 16000, real rate: 16000.005, BITS: 24, ERR: 0.000031%
I2S: APLL: Req RATE: 16000, real rate: 16000.000, BITS: 32, ERR: 0.000000%
I2S: APLL: Req RATE: 22050, real rate: 22050.002, BITS: 16, ERR: 0.000009%
I2S: APLL: Req RATE: 22050, real rate: 22049.998, BITS: 24, ERR: 0.000009%
I2S: APLL: Req RATE: 22050, real rate: 22049.994, BITS: 32, ERR: 0.000027%
I2S: APLL: Req RATE: 32000, real rate: 32000.000, BITS: 16, ERR: 0.000000%
I2S: APLL: Req RATE: 32000, real rate: 31999.994, BITS: 24, ERR: 0.000018%
I2S: APLL: Req RATE: 32000, real rate: 32000.006, BITS: 32, ERR: 0.000018%
I2S: APLL: Req RATE: 44100, real rate: 44099.988, BITS: 16, ERR: 0.000027%
I2S: APLL: Req RATE: 44100, real rate: 44100.000, BITS: 24, ERR: 0.000000%
I2S: APLL: Req RATE: 44100, real rate: 44099.988, BITS: 32, ERR: 0.000027%
I2S: APLL: Req RATE: 48000, real rate: 47999.992, BITS: 16, ERR: 0.000016%
I2S: APLL: Req RATE: 48000, real rate: 48000.016, BITS: 24, ERR: 0.000033%
I2S: APLL: Req RATE: 48000, real rate: 47999.992, BITS: 32, ERR: 0.000016%
I2S: APLL: Req RATE: 96000, real rate: 95999.984, BITS: 16, ERR: 0.000016%
I2S: APLL: Req RATE: 96000, real rate: 96000.031, BITS: 24, ERR: 0.000033%
I2S: APLL: Req RATE: 96000, real rate: 95999.984, BITS: 32, ERR: 0.000016%
I2S: APLL: Req RATE: 176400, real rate: 176399.953, BITS: 16, ERR: 0.000027%
I2S: APLL: Req RATE: 176400, real rate: 208333.000, BITS: 24, ERR: 18.102608%
I2S: APLL: Req RATE: 176400, real rate: 208333.000, BITS: 32, ERR: 18.102608%

@philippe44
Copy link

philippe44 commented Aug 21, 2019

I think 176k @ 16bits is the maximum that can be done, taking into account various constraints.
The code is the follòwing. The sampling rate required is 2x the original sampling rate and sample size is 32 bits. Then the resulting buffer shall be send to a led connected to the SD line of the I2S, that's all

 * SPDIF support
 */
#if BYTES_PER_FRAME == 4
typedef ISAMPLE_T s16_t
#else 
typedef ISAMPLE_T s32_t
#endif 
 
#define PREAMBLE_B  (0xE8) //11101000
#define PREAMBLE_M  (0xE2) //11100010
#define PREAMBLE_W  (0xE4) //11100100

#define VUCP   		((0xCC) << 24)
#define VUCP_MUTE 	((0xD4) << 24)	// To mute PCM, set VUCP = invalid.

extern const u16_t spdif_bmclookup[256];

/* 
 SPDIF is supposed to be (before BMC encoding, from LSB to MSB)				
	PPPP AAAA  SSSS SSSS  SSSS SSSS  SSSS VUCP				
 after BMC encoding, each bits becomes 2 hence this becomes a 64 bits word. The
 the trick is to start not with a PPPP sequence but with an VUCP sequence to that
 the 16 bits samples are aligned with a BMC word boundary. Note that the LSB of the
 audio is transmitted first (not the MSB) and that ESP32 libray sends R then L, 
 contrary to what seems to be usually done, so (dst) order had to be changed
*/
void spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count) {
	u16_t hi, lo, aux;
	
	// frames are 2 channels of 16 bits
	frames *= 2;

	while (frames--) {
#if BYTES_PER_FRAME == 4		
		hi  = spdif_bmclookup[(u8_t)(*src >> 8)];
		lo  = spdif_bmclookup[(u8_t) *src];
#else
		hi  = spdif_bmclookup[(u8_t)(*src >> 24)];
		lo  = spdif_bmclookup[(u8_t) *src >> 16];
#endif	
		lo ^= ~((s16_t)hi) >> 16;

		// 16 bits sample:
		*(dst+0) = ((u32_t)lo << 16) | hi;

		// 4 bits auxillary-audio-databits, the first used as parity
		aux = 0xb333 ^ (((u32_t)((s16_t)lo)) >> 17);

		// VUCP-Bits: Valid, Subcode, Channelstatus, Parity = 0
		// As parity is always 0, we can use fixed preambles
		if (++(*count) > 383) {
			*(dst+1) =  VUCP | (PREAMBLE_B << 16 ) | aux; //special preamble for one of 192 frames
			*count = 0;
		} else {
			*(dst+1) = VUCP | ((((*count) & 0x01) ? PREAMBLE_W : PREAMBLE_M) << 16) | aux;
		}
		
		src++;
		dst += 2;
	}
}

const u16_t spdif_bmclookup[256] = { //biphase mark encoded values (least significant bit first)
	0xcccc, 0x4ccc, 0x2ccc, 0xaccc, 0x34cc, 0xb4cc, 0xd4cc, 0x54cc,
	0x32cc, 0xb2cc, 0xd2cc, 0x52cc, 0xcacc, 0x4acc, 0x2acc, 0xaacc,
	0x334c, 0xb34c, 0xd34c, 0x534c, 0xcb4c, 0x4b4c, 0x2b4c, 0xab4c,
	0xcd4c, 0x4d4c, 0x2d4c, 0xad4c, 0x354c, 0xb54c, 0xd54c, 0x554c,
	0x332c, 0xb32c, 0xd32c, 0x532c, 0xcb2c, 0x4b2c, 0x2b2c, 0xab2c,
	0xcd2c, 0x4d2c, 0x2d2c, 0xad2c, 0x352c, 0xb52c, 0xd52c, 0x552c,
	0xccac, 0x4cac, 0x2cac, 0xacac, 0x34ac, 0xb4ac, 0xd4ac, 0x54ac,
	0x32ac, 0xb2ac, 0xd2ac, 0x52ac, 0xcaac, 0x4aac, 0x2aac, 0xaaac,
	0x3334, 0xb334, 0xd334, 0x5334, 0xcb34, 0x4b34, 0x2b34, 0xab34,
	0xcd34, 0x4d34, 0x2d34, 0xad34, 0x3534, 0xb534, 0xd534, 0x5534,
	0xccb4, 0x4cb4, 0x2cb4, 0xacb4, 0x34b4, 0xb4b4, 0xd4b4, 0x54b4,
	0x32b4, 0xb2b4, 0xd2b4, 0x52b4, 0xcab4, 0x4ab4, 0x2ab4, 0xaab4,
	0xccd4, 0x4cd4, 0x2cd4, 0xacd4, 0x34d4, 0xb4d4, 0xd4d4, 0x54d4,
	0x32d4, 0xb2d4, 0xd2d4, 0x52d4, 0xcad4, 0x4ad4, 0x2ad4, 0xaad4,
	0x3354, 0xb354, 0xd354, 0x5354, 0xcb54, 0x4b54, 0x2b54, 0xab54,
	0xcd54, 0x4d54, 0x2d54, 0xad54, 0x3554, 0xb554, 0xd554, 0x5554,
	0x3332, 0xb332, 0xd332, 0x5332, 0xcb32, 0x4b32, 0x2b32, 0xab32,
	0xcd32, 0x4d32, 0x2d32, 0xad32, 0x3532, 0xb532, 0xd532, 0x5532,
	0xccb2, 0x4cb2, 0x2cb2, 0xacb2, 0x34b2, 0xb4b2, 0xd4b2, 0x54b2,
	0x32b2, 0xb2b2, 0xd2b2, 0x52b2, 0xcab2, 0x4ab2, 0x2ab2, 0xaab2,
	0xccd2, 0x4cd2, 0x2cd2, 0xacd2, 0x34d2, 0xb4d2, 0xd4d2, 0x54d2,
	0x32d2, 0xb2d2, 0xd2d2, 0x52d2, 0xcad2, 0x4ad2, 0x2ad2, 0xaad2,
	0x3352, 0xb352, 0xd352, 0x5352, 0xcb52, 0x4b52, 0x2b52, 0xab52,
	0xcd52, 0x4d52, 0x2d52, 0xad52, 0x3552, 0xb552, 0xd552, 0x5552,
	0xccca, 0x4cca, 0x2cca, 0xacca, 0x34ca, 0xb4ca, 0xd4ca, 0x54ca,
	0x32ca, 0xb2ca, 0xd2ca, 0x52ca, 0xcaca, 0x4aca, 0x2aca, 0xaaca,
	0x334a, 0xb34a, 0xd34a, 0x534a, 0xcb4a, 0x4b4a, 0x2b4a, 0xab4a,
	0xcd4a, 0x4d4a, 0x2d4a, 0xad4a, 0x354a, 0xb54a, 0xd54a, 0x554a,
	0x332a, 0xb32a, 0xd32a, 0x532a, 0xcb2a, 0x4b2a, 0x2b2a, 0xab2a,
	0xcd2a, 0x4d2a, 0x2d2a, 0xad2a, 0x352a, 0xb52a, 0xd52a, 0x552a,
	0xccaa, 0x4caa, 0x2caa, 0xacaa, 0x34aa, 0xb4aa, 0xd4aa, 0x54aa,
	0x32aa, 0xb2aa, 0xd2aa, 0x52aa, 0xcaaa, 0x4aaa, 0x2aaa, 0xaaaa
};`

@philippe44
Copy link

Code mainly comes from here https://github.com/FrankBoesing/Teensy3-SPDIF

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants