ADAT (Alesis Digital Audio Tape) interface codec using STM32F411RE Nucleo board. ADAT frame encoding / decoding rate is ~113000 frames/s, faster than real-time needs (48000 frames/s) Future work will attempt to implement an 8-in / 8-out digital mixer using DMA SPI Rx+Tx with a behringer ada8200.

Dependencies:   mbed mbprt

?

main.cpp

Committer:
graffou
Date:
2019-01-06
Revision:
2:79b8132da1d6
Parent:
1:6349099b6962

File content as of revision 2:79b8132da1d6:

// MCU ADAT frames encoding (proof of concept)
// 8 24-bit audio data -> 8 32-bit NRZI encoded ADAT frames
// 480000 ADAT frames are encoded within ~3s, so encoding should require 30% CPU time (STM32F411RE)
// This might leave margin for:
//   ADAT decoding (NRZI decoding is less CPU intensive)
//   Audio mixing 8 in -> 8 out
//   Rx SPI w/ DMA
//   Tx SPI w/ DMA
//
// Encoding is fully unrolled (faster?), starting from last frame bits so that the compiler can make use of ROR instruction (does it?)
// NOT TESTED with ADAT device
// Result is displayed on terminal

#include "mbed.h"
#include "mbprt.h"
#include <sstream>
 
Timer t;

// NRZI encoding LUT
char lut[256] = {0x0, 0xff, 0xfe, 0x1, 0xfc, 0x3, 0x2, 0xfd, 0xf8, 0x7, 0x6, 0xf9, 0x4, 0xfb, 0xfa, 0x5,
0xf0, 0xf, 0xe, 0xf1, 0xc, 0xf3, 0xf2, 0xd, 0x8, 0xf7, 0xf6, 0x9, 0xf4, 0xb, 0xa, 0xf5,
0xe0, 0x1f, 0x1e, 0xe1, 0x1c, 0xe3, 0xe2, 0x1d, 0x18, 0xe7, 0xe6, 0x19, 0xe4, 0x1b, 0x1a, 0xe5,
0x10, 0xef, 0xee, 0x11, 0xec, 0x13, 0x12, 0xed, 0xe8, 0x17, 0x16, 0xe9, 0x14, 0xeb, 0xea, 0x15,
0xc0, 0x3f, 0x3e, 0xc1, 0x3c, 0xc3, 0xc2, 0x3d, 0x38, 0xc7, 0xc6, 0x39, 0xc4, 0x3b, 0x3a, 0xc5,
0x30, 0xcf, 0xce, 0x31, 0xcc, 0x33, 0x32, 0xcd, 0xc8, 0x37, 0x36, 0xc9, 0x34, 0xcb, 0xca, 0x35,
0x20, 0xdf, 0xde, 0x21, 0xdc, 0x23, 0x22, 0xdd, 0xd8, 0x27, 0x26, 0xd9, 0x24, 0xdb, 0xda, 0x25,
0xd0, 0x2f, 0x2e, 0xd1, 0x2c, 0xd3, 0xd2, 0x2d, 0x28, 0xd7, 0xd6, 0x29, 0xd4, 0x2b, 0x2a, 0xd5,
0x80, 0x7f, 0x7e, 0x81, 0x7c, 0x83, 0x82, 0x7d, 0x78, 0x87, 0x86, 0x79, 0x84, 0x7b, 0x7a, 0x85,
0x70, 0x8f, 0x8e, 0x71, 0x8c, 0x73, 0x72, 0x8d, 0x88, 0x77, 0x76, 0x89, 0x74, 0x8b, 0x8a, 0x75,
0x60, 0x9f, 0x9e, 0x61, 0x9c, 0x63, 0x62, 0x9d, 0x98, 0x67, 0x66, 0x99, 0x64, 0x9b, 0x9a, 0x65,
0x90, 0x6f, 0x6e, 0x91, 0x6c, 0x93, 0x92, 0x6d, 0x68, 0x97, 0x96, 0x69, 0x94, 0x6b, 0x6a, 0x95,
0x40, 0xbf, 0xbe, 0x41, 0xbc, 0x43, 0x42, 0xbd, 0xb8, 0x47, 0x46, 0xb9, 0x44, 0xbb, 0xba, 0x45,
0xb0, 0x4f, 0x4e, 0xb1, 0x4c, 0xb3, 0xb2, 0x4d, 0x48, 0xb7, 0xb6, 0x49, 0xb4, 0x4b, 0x4a, 0xb5,
0xa0, 0x5f, 0x5e, 0xa1, 0x5c, 0xa3, 0xa2, 0x5d, 0x58, 0xa7, 0xa6, 0x59, 0xa4, 0x5b, 0x5a, 0xa5,
0x50, 0xaf, 0xae, 0x51, 0xac, 0x53, 0x52, 0xad, 0xa8, 0x57, 0x56, 0xa9, 0x54, 0xab, 0xaa, 0x55};

uint32_t data[8]; // 24-bit audio in 
uint32_t adat[8]; // raw ADAT frame
uint32_t adat_nrz[8]; // NRZI encoded ADAT frame
uint32_t dec_nrz[8]; // NRZI decoded ADAT frame
uint32_t adec[8]; // audio decoded frame

DigitalIn pb(USER_BUTTON);
DigitalOut myled(LED1);

// To display 32-bit binary data
const char *byte_to_binary(int x)
{
    static char b[33];
    b[32] = '\0';

    int z;
    for (z=0;z<32;z++)
    {
        b[z] = 48 + ( (x & (1<<(31-z))) != 0); 
    }

    return b;
}


// Right rotation
inline uint32_t rorn (uint32_t x, uint32_t n)
{
  return ((x)>>n) | ((x)<<(32-n));
}

// 1-bit right rotation
inline void ror1(uint32_t* x)
{
  *x = ((*x)>>1) | ((*x)<<(31));
}


inline uint32_t adat2audio(uint32_t x)
{

    uint32_t R = x&0xF;
    R |= ( (x>>1) & 0xF0);    
    R |= ( (x>>2) & 0xF00);   
    R |= ( (x>>3) & 0xF000);    
    R |= ( (x>>4) & 0xF0000);    
    R |= ( (x>>5) & 0xF00000);

    return R;    
}

inline uint32_t audio2adat(uint32_t x)
{
    uint32_t R = 0x02108421;
    R |= ( (x << 1) & 0x1E );
    R |= ( (x << 2) & 0x3c0 );
    R |= ( (x << 3) & 0x7800 );
    R |= ( (x << 4) & 0xf0000 );
    R |= ( (x << 5) & 0x1E00000 );
    R |= ( (x << 6) & 0x3c000000 );
    return R;
}

inline void adat_enc(uint32_t* in, uint32_t* out)
{
    uint32_t R0, R1;
    R0 = audio2adat(in[0]);
    R1 = audio2adat(in[1]);
    out[0] = R0 | (R1 << 30);
    R0 = audio2adat(in[2]);
    out[1] = (R0 << 28) | (R1 >> 2);
    R1 = audio2adat(in[3]);
    out[2] = (R0 >> 4) | (R1 << 26);
    R0 = audio2adat(in[4]);
    out[3] = (R0 << 24) | (R1 >> 6);
    R1 = audio2adat(in[5]);
    out[4] = (R0 >> 8) | (R1 << 22);
    R0 = audio2adat(in[6]);
    out[5] = (R0 << 20) | (R1 >> 10);
    R1 = audio2adat(in[7]);
    out[6] = (R0 >> 12) | (R1 << 18);    
    out[7] = 0x08010000 | (R1 >> 14);
}

inline void adat_dec(uint32_t* in, uint32_t* out)
{
    uint32_t R0, R1;
    R0 = in[0];
    R1 = in[1];
    out[0] = adat2audio(R0 >> 1);
    out[1] = adat2audio( (R0 >> 31) | (R1 << 1) ); 
    R0 = in[2];       
    out[2] = adat2audio( (R1 >> 29) | (R0 << 3) );   
    R1 = in[3];
    out[3] = adat2audio( (R0 >> 27) | (R1 << 5) );        
    R0 = in[4];       
    out[4] = adat2audio( (R1 >> 25) | (R0 << 7) );   
    R1 = in[5];
    out[5] = adat2audio( (R0 >> 23) | (R1 << 9) );        
    R0 = in[6];       
    out[6] = adat2audio( (R1 >> 21) | (R0 << 11) );   
    R1 = in[7];
    out[7] = adat2audio( (R0 >> 19) | (R1 << 13) );        
}


inline void nrz_enc(uint32_t* in, uint32_t* out)
{
    static char last_bit = 0;
    for (int i = 0; i < 8; i++)
    {
        register uint32_t R = 0;
        uint32_t x = in[i]; 
        unsigned char c;
        for (int j = 0; j < 4; j++)
        {
            c = lut[(unsigned char)(x >> (j*8))];

            c = last_bit ? ~c : c;
           last_bit = c >> 7;
            R = rorn(R | c, 8);
        }
        out[i] = R;
    }   
}

inline void nrz_dec(uint32_t* in, uint32_t* out)
{
    static char dec_last_bit = 0;
     for (int i = 0; i < 8; i++)
    {
        uint32_t x = in[i]; 
        out[i] = x ^ ( (x << 1) | dec_last_bit );
        dec_last_bit = x >> 31;
    }      
}




int main() {
    
    Serial pc(USBTX, USBRX);
    pc.baud(115200);
    
    for (int i = 0; i < 8; i++)    
    {
        data[i] = 0x00042184 + (i << 20);
    }
    
    uint32_t nframes = 480000; // 10s real time data
    gprintf("#VStarting encoding %Y ADAT frames\n", int(nframes));  
    t.start();
    for (int j = 0; j < nframes; j++)
    {
        if (pb) // so that the compiler cannot preprocess anything
        {
            for (int i = 0; i < 8; i++)    
            {
                data[i] = (data[i] - i) &0xffffff;//make it recursive (makes really sure the compiler won't attempt to preprocess anything)
            }

        }
        myled = pb;   
          
        adat_enc(data, adat);     
        nrz_enc(adat, adat_nrz);
        nrz_dec(adat_nrz, dec_nrz);
        adat_dec(dec_nrz, adec);    
    }
    
    
    t.stop();
    std::stringstream toto;
    toto << t.read();
    gprintf("The time taken was %f seconds\n", toto.str());
       for (int i = 0; i < 8; i++)    
    {
        printf("data[%d] = %s\n", i, byte_to_binary(data[i]));
    }   
       for (int i = 0; i < 8; i++)    
    {
        printf("adat[%d] = %s\n", i, byte_to_binary(adat[i]));
    }
        for (int i = 0; i < 8; i++)    
    {
        printf("adat_nrz[%d] = %s\n", i, byte_to_binary(adat_nrz[i]));
    }   
        for (int i = 0; i < 8; i++)    
    {
        printf("dec_nrz[%d] = %s\n", i, byte_to_binary(dec_nrz[i]));
    }   
        for (int i = 0; i < 8; i++)    
    {
        printf("adec[%d] = %s\n", i, byte_to_binary(adec[i]));
    }   
    
    
    
    
    
}