S/pdif

Status
Not open for further replies.

Frank B

Senior Member

EDIT:

Working version: https://github.com/FrankBoesing/Teensy3-SPDIF
Only PIN22 is needed.



Today I read me a bit in S / PDIF.
I think we could make it happen on a Teensy using SPI.
The protocol is not too complicated, actually we could even use the 2 SPI device of T3.1, because there is no need for SCK.
Unfortunately there is a problem: The baud rate.

We need exactly 64 * 44100 Hz (= 2.8224 MHz)

I do not believe that the SPI device this baud rate can generate, but maybe there is a trick?

What comes to mind:
- An external clock with the SPI device in slave mode (perhaps with a tiny avr which generates the clk?)
- Somehow use the built-in timer to generate (2x ?) 2.8224 MHz

Unfortunately, we can then no longer use SPI for other things, such as rading from SD.

Another idea: a Teensy LC as a "slave" for the T3.1. The LC would do S / PDIF output (again we have to think about the clock), the T3.1 provide the audio data to the LC.

What do you think?
Do you have any other ideas?
 
Last edited:
You might (ab)use the I2S FIFO to generate the SPDIF signal. The I2S clock has somewhat more flexibility and allows you to generate the exact clockrate.

Edit: This suggestion above will not work in combination with the audio shield.
Also the audio library works at 44118 Hz. I do not know how if this falls within the timing requirements of SPDIF (Did not check, but I suspect it is outside of specification). The internal ADC cannot be set closer to 44.1 kHz. The audio shield can be set at an exact 44.1 kHz.

Edit2: SPDIF clock requirement:
high accuracy mode: 50 ppm, so 44100 +/- 2 Hz
normal accuracy mode: 1000 ppm, so 44100 +/- 44 Hz
Conclusion: The default clock used in the audio library is within spec for SPDIF.
 
Last edited:
You might (ab)use the I2S FIFO to generate the SPDIF signal. The I2S clock has somewhat more flexibility and allows you to generate the exact clockrate.

Edit: This suggestion above will not work in combination with the audio shield.
Also the audio library works at 44118 Hz. I do not know how if this falls within the timing requirements of SPDIF (Did not check, but I suspect it is outside of specification). The internal ADC cannot be set closer to 44.1 kHz. The audio shield can be set at an exact 44.1 kHz.

Edit2: SPDIF clock requirement:
high accuracy mode: 50 ppm, so 44100 +/- 2 Hz
normal accuracy mode: 1000 ppm, so 44100 +/- 44 Hz
Conclusion: The default clock used in the audio library is within spec for SPDIF.

I2S is a good idea. We don't loose the SPI then, which is more important than i2s in this case, i think. And you can use cheap spdif/analog converters (worst case).
Currently i have not much time, but I did s/pdif on my list. I will definately try this, perhaps in the second half of the year.
 
The archiveable clocks (bitrate) are very close to 2822400 Hz:
Code:
144MHz FRACT: 48 DIV: 2499 FREQ: 2822400 ERR:0
120MHz FRACT: 88 DIV: 3783 FREQ: 2822410.15 ERR:10.15
96MHz FRACT: 73 DIV: 2516 FREQ: 2822407.63 ERR:7.63
72MHz FRACT: 48 DIV: 1249 FREQ: 2822400 ERR:0
48MHz FRACT: 146 DIV: 2499 FREQ: 2822400 ERR:0
24MHz FRACT: 146 DIV: 1249 FREQ: 2822400 ERR:0
That looks good.
We need a translation-table for Bi-phase mark encoding and a bit of additional conversion and we are done.

http://whitefiles.org/b1_s/1_free_guides/fg1mt/pgs/h13f.htm
 
Last edited:
Correction, i think we need a clock of 2 * 64 * 44100 Hz to archive the bitrate of 2822400 because of the encoding
(Am i wrong?)
Code:
144MHz FRACT: 48 DIV: 1249 FREQ: 5644800 ERR:0
120MHz FRACT: 146 DIV: 3124 FREQ: 5644800 ERR:0
96MHz FRACT: 146 DIV: 2499 FREQ: 5644800 ERR:0
72MHz FRACT: 48 DIV: 624 FREQ: 5644800 ERR:0
48MHz FRACT: 146 DIV: 1249 FREQ: 5644800 ERR:0
24MHz FRACT: 146 DIV: 624 FREQ: 5644800 ERR:0
12MHz FRACT: 293 DIV: 624 FREQ: 5644800 ERR:0
 
Do any of the SPDIF chips have options to generate the high accuracy clock? Or maybe a shield could be made with the right crystal?

I've been looking for a good reason to work on (read: actually fix & test) the I2S slave mode objects.....
 
I did not verify your FRACT/DIV values, but you might want to check this thread. This code will perform the calculations based on the pll clock frequency setting. It might seem a pretty big function, but it actually only compiles to a surprisingly few instructions. Also no floating point involved in setting the frequency.
Code:
setI2SmasterClock(44100, 2 * 64);
getI2SmasterClock(2 * 64)
The code does not (yet) set the bitclock counter.

And yes, you need the double clock frequency, because of the Manchester coding. I would generate a lookup table of 256 with the manchester coded versions (16 bit) of each byte. One lookup table for the preamble byte and one for all other bytes.
 
If i only had more time...

KPC, i wrote a short PHP script:
Code:
 function calcDiv($F_CPU) {
 
 $x = 0;
 $y = 0;
 
 $mx = 0;
 $my = 0;
 $mz = 100000000;
 $t = 44100 * 64 * 2;
 
 for ($x = 0; $x<=511; $x++){ 
	for ($y=0; $y<=4095; $y++) { 
		$z = abs($F_CPU * (($x+1) / ($y+1)) - $t);
	    if ($z < $mz) { $mx = $x; $my = $y; $mz = $z; }		
	}
 }	
 echo "\r\n<br>",	
	$F_CPU/1000000,
	"MHz FRACT: $mx \tDIV: $my \t FREQ: ",round($F_CPU * (($mx+1) / ($my+1)),2),"\tERR:",round( $F_CPU * (($mx+1) / ($my+1)) - $t,2 );
 }

 calcDiv(144 * 1000000);
 calcDiv(120 * 1000000);
 calcDiv(96 * 1000000);
 calcDiv(72 * 1000000); 
 calcDiv(48 * 1000000);  
 calcDiv(24 * 1000000);   
 calcDiv(12 * 1000000);

But thanks for the link, seems to be interesting.
 
I see you used F_CPU, I think you should use F_PLL.
Furthermore, beware that the actual register values are the calulated values minus one.

Edit: In Audio/output_i2s.cpp Paul lists the values he is using, to achieve 256 * 44118.
Edit2:
And you can use cheap spdif/analog converters (worst case).
For testing, just use an LED.
IMG_0525.jpg
 
Last edited:
Great Toslink :)

Ok, my script spits out the same values for 256 * 44118 as Paul uses (-1), so it seems to be ok.
Why 11MHz clock ? I don#t understand..
 
Last edited:
The master clock is used for the delta/sigma modulator and the digital filters. The actual multiplication factor is determined by the I2S device, 256 is a commonly used factor. The SGTL5000 on the audio shield can also accept 384 and 512, bit this will only raise the clock frequency, as well as being out of spec for the Teensy.
 
We need a translation-table for Bi-phase mark encoding and a bit of additional conversion and we are done.
Lookuptable: Done. :)

Code:
{const ? perhaps ram is faster..todo: test speed}
uint16_t bmclookup = { //biphase mark econded values for 0x00..0xff
	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,
};
Sorry, i'm using this thread a bit like a a notepad for later.. but perhaps others might be interested too.
Question: How (and when: before or after encoding?) is the paritybit calculated.
 
You can find some versions of IEC60958 on law.resource.org.
According to the spec:
Time slot 31 (parity bit) carries a parity bit such that time slots 4 to 31 inclusive carry an even number of ones and an even number of zeros (even parity).
NOTE 3: The preambles have even parity as an expllclt property
This is before encoding, since after encoding, the number of ones is fixed. For each unencoded bit due to the manchester coding, after encoding, it will always have 1 high bit.
 
Last edited:
You can find some versions of IEC60958 on law.resource.org.
According to the spec:

This is before encoding, since after encoding, the number of ones is fixed. For each unencoded bit due to the manchester coding, after encoding, it will always have 1 high bit.

Hm, there is a GCC built-in function int __builtin_parity... maybe it is useful..i try this..
 
It may suprise, but the parity is the same for bmc !
So. technically, it does not matter wether the parity-calculation is done before or after encoding.
(But i think before is faster (less bits) )

I did not test what is faster (__builtin or lookup) (i'll do that later) but I was playing with __builtin_parity anyway, so I created a table that I can use it later if I decide to...
Code:
//for (int i = 0; i <= 255; i++) Serial.printf("%d, %s",__builtin_parity(i),((i+1) % 16==0) ? "\r\n": "");
const 
uint16_t paritylookup[256] = {
        0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
        1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
        1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
        0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
        1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
        0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
        0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
        1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
        1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
        0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
        0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
        1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
        0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 
        1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
        1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 
        0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0
};
Nice pattern :)

The next for me to do is to read the manual again and figure out how to setup i2s for LSB first, 64bit, 1 channel (i hope this is possible), dma..
 
I am also playing a little with spdif. Nothing working yet, but it seems that only very few modifications to output_i2s are needed.
Also my interpretation of the spec might be wrong, but I get a different lookup table. Only the first value is the same. My first line is:
Code:
0xcccc, 0xb333, 0xd333, 0xaccc, 0xcb33, 0xb4cc, 0xd4cc, 0xab33,
And the encoding:
Code:
static void inline bmc_encode(uint32_t *dest, int16_t sample, uint16_t preamble)  __attribute__((always_inline));
static void inline bmc_encode(uint32_t *dest, int16_t sample, uint16_t preamble)
{
        uint32_t bmc1, bmc2, bmc3, val;
        val = sample << 4;
        val |= __builtin_parity(val) << 23;
        bmc1 = bmc_lookup[val & 0xff];
        dest[0] = (preamble << 16) | (bmc1 & 0xffff);
        bmc2 = bmc_lookup[(val >> 8) & 0xff] ^ -(bmc1 & 1);
        bmc3 = bmc_lookup[(val >> 16) & 0xff] ^ -(bmc2 & 1);
        dest[1] = (bmc2 << 16) | (bmc3 & 0xffff);
}
The preamble is depending on the status bit counter: 0xe8cc, 0xe2cc or 0xe4cc
As said, nothing tested yet on the hardware, so this code might be wrong.


Edit: The cortex m4 does not seem to have a builtin parity instruction, it gets translated to library call. I have not checked the library code.
The following code I used before on other microcontrollers. On the cortex m4 it is only 6 assembly instructions (also only 6 cycles)
Code:
inline uint32_t parity(uint32_t v)  __attribute__((always_inline));
inline uint32_t parity(uint32_t v)
{
        v ^= v >> 1;
        v ^= v >> 2;
        v = (v & 0x11111111ul) * 0x11111111ul;
        return = (v >> 28) & 1;
}

Edit2: The library code is actually 7 assembly instructions (also 7 cycles) + 4 cycles for the branch instructions. So in total, takes 83% more time than the code above.
 
Last edited:
I am also playing a little with spdif. Nothing working yet, but it seems that only very few modifications to output_i2s are needed.
Also my interpretation of the spec might be wrong, but I get a different lookup table. Only the first value is the same. My first line is:
Code:
0xcccc, 0xb333, 0xd333, 0xaccc, 0xcb33, 0xb4cc, 0xd4cc, 0xab33,
And the encoding:
Code:
static void inline bmc_encode(uint32_t *dest, int16_t sample, uint16_t preamble)  __attribute__((always_inline,unused));
static void inline bmc_encode(uint32_t *dest, int16_t sample, uint16_t preamble)
{
        uint32_t bmc1, bmc2, bmc3, val;
        val = sample << 4;
        val |= __builtin_parity(val) << 23;
        bmc1 = bmc_lookup[val & 0xff];
        dest[0] = (preamble << 16) | (bmc1 & 0xffff);
        bmc2 = bmc_lookup[(val >> 8) & 0xff] ^ -(bmc1 & 1);
        bmc3 = bmc_lookup[(val >> 16) & 0xff] ^ -(bmc2 & 1);
        dest[1] = (bmc2 << 16) | (bmc3 & 0xffff);
}
The preamble is depending on the status bit counter: 0xe8cc, 0xe2cc or 0xe4cc
As said, nothing tested yet on the hardware, so this code might be wrong.


Edit: The cortex m4 does not seem to have a builtin parity instruction, it gets translated to library call. I have not checked the library code.
The following code I used before on other microcontrollers. On the cortex m4 it is only 6 assembly instructions
Code:
        v ^= v >> 1;
        v ^= v >> 2;
        v = (v & 0x11111111ul) * 0x11111111ul;
        parity = (v >> 28) & 1;

kpc, as i understood the descriptions, the parity bit is calculated for a whole subframe except the the preamble.
__builtin_parity can do 32 Bit at once, so it might be more efficient to first create the the subrame without preamble, calculate and add then the parity, then encode. then as last step add the preamble.

this code for raspberry might help:
https://github.com/humppe/spdif-encoder

Maye we can use it to test our encodings, (whole blocks of 192 frames) and compare the output.

The code is pretty straight forward , but slowly .Each bit is processed individually .
 
Last edited:
bmc lookup:
Code:
your value for 1 is : 0xb333 = 1011001100110011
mine is :                      0100110011001100
so we are near, it's almost the same but one is inversed, or backward
 
In my encoding, I only calculate the parity on the final three bytes, the preamble is even by definition, as are the following 4 LSB bits, since for 16 bit data these will be zeros.
Since the header ends with a 0, I would expect every following bit to be a 1, so the MSB of the encoded values is by definition 1.
Anyway, I will check the code you linked.
 
Status
Not open for further replies.
Back
Top