Library to control and transfer data from NXP SGTL5000. As used on the Teensy Audio Shield. It uses DMA to transfer I2S FIFO data.

The Library now supports dual codecs. Allowing all 4 channels of the Teensy I2S interface to RX and TX data to separate SGTL5000 devices.

The ISR routines that handles pointer swaps for double buffering has been fully coded in assembler to reduce overhead and now takes < 800nS per FIFO transfer when using all 4 channels.

Support added for all typical sample rates and system Clock speeds of 96Mhz or 120Mhz.

Pause and Resume functions added to allow quick and simple suppression of IRQs and stream halting and restart. This required software triggered IRQ, in order to ensure accurate word sync control.

Revision:
4:91354c908416
Parent:
3:62c03088f256
Child:
5:664802e89661
--- a/sgtl5000.cpp	Fri Jun 16 12:57:54 2017 +0000
+++ b/sgtl5000.cpp	Fri Jun 30 09:33:42 2017 +0000
@@ -25,6 +25,11 @@
 */
 
 #include "sgtl5000.h"
+#include "arm_math.h"
+
+extern bool grab;
+extern q31_t RX_L_dir_DMA[];
+extern q31_t RX_R_dir_DMA[];
 
 namespace SGTL5000
 {
@@ -135,6 +140,12 @@
             codec_SYS_FS = 0x03;
             codec_RATE_MODE = 0x0;
             break;
+        case 144:
+            I2S_MCLK_M = 48;
+            I2S_MCLK_D = 125;
+            codec_SYS_FS = 0x03;
+            codec_RATE_MODE = 0x0;
+            break;
         case 192:                                                               // Not officially supported by the codec, but it seems to work.
             I2S_MCLK_M = 64;
             I2S_MCLK_D = 125;
@@ -278,8 +289,8 @@
                                             (SGTL5000_REF_CTRL_BIAS_CTRL_MASK & (0x1 << SGTL5000_REF_CTRL_BIAS_CTRL_SHIFT)) |                                       // Set analog bias currents to +12.5%
                                             (SGTL5000_REF_CTRL_SMALL_POP_MASK & (0x0 << SGTL5000_REF_CTRL_SMALL_POP_SHIFT))                                         // Set small pop ramp normal
                                         ));
-    write_i2c(SGTL5000_LINE_OUT_CTRL,   (   (SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_MASK & (0xF << SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_SHIFT)) |                         // Set Lineout bias curent 0.54mA
-                                            (SGTL5000_LINE_OUT_CTRL_LO_VAGCNTRL_MASK & (0x22 << SGTL5000_LINE_OUT_CTRL_LO_VAGCNTRL_SHIFT))                          // Lineout AGND = 1.65v
+    write_i2c(SGTL5000_LINE_OUT_CTRL,   (   (SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_MASK & (0x3 << SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_SHIFT)) |                         // Set Lineout bias curent 0.36mA
+                                            (SGTL5000_LINE_OUT_CTRL_LO_VAGCNTRL_MASK & (0x22 << SGTL5000_LINE_OUT_CTRL_LO_VAGCNTRL_SHIFT))                          // Lineout AGND = 1.65v (line out will drive between the rails, so set at VDDA/2)
                                         ));
     write_i2c(SGTL5000_SHORT_CTRL,      (   (SGTL5000_SHORT_CTRL_LVLADJR_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJR_SHIFT)) |                                       // HP R short detect 125mA
                                             (SGTL5000_SHORT_CTRL_LVLADJL_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJL_SHIFT)) |                                       // HP L short detect 125mA
@@ -309,8 +320,8 @@
                                             (SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_I2S_OUT_POWERUP_SHIFT)) |                         // Keep I2S data lines disabled until config complete
                                             (SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_I2S_IN_POWERUP_SHIFT))
                                         ));
-    write_i2c(SGTL5000_LINE_OUT_VOL,    (   (SGTL5000_LINE_OUT_VOL_LO_VOL_RIGHT_MASK & (0x1D << SGTL5000_LINE_OUT_VOL_LO_VOL_RIGHT_SHIFT)) |                        // Lineout Vol normalised
-                                            (SGTL5000_LINE_OUT_VOL_LO_VOL_LEFT_MASK & (0x1D << SGTL5000_LINE_OUT_VOL_LO_VOL_LEFT_SHIFT))
+    write_i2c(SGTL5000_LINE_OUT_VOL,    (   (SGTL5000_LINE_OUT_VOL_LO_VOL_RIGHT_MASK & (0x11 << SGTL5000_LINE_OUT_VOL_LO_VOL_RIGHT_SHIFT)) |                        // Lineout Vol normalised
+                                            (SGTL5000_LINE_OUT_VOL_LO_VOL_LEFT_MASK & (0x11 << SGTL5000_LINE_OUT_VOL_LO_VOL_LEFT_SHIFT))                            // Verified with oscope at p2p = 2.82v driving ~30k load
                                         ));
     write_i2c(SGTL5000_PAD_STRENGTH,    (   (SGTL5000_PAD_STRENGTH_I2S_LRCLK_MASK & (0x0 << SGTL5000_PAD_STRENGTH_I2S_LRCLK_SHIFT)) |                               // I2S LR_CLK drive dissabled (codec is slave)
                                             (SGTL5000_PAD_STRENGTH_I2S_SCLK_MASK & (0x0 << SGTL5000_PAD_STRENGTH_I2S_SCLK_SHIFT)) |                                 // I2S SCLK drive dissabled (codec is slave)
@@ -386,12 +397,14 @@
 }
 
 int32_t SGTL5000::start_SYNC(uint32_t BufRX_L_safe, uint32_t BufRX_R_safe, uint32_t BufTX_L_safe, uint32_t BufTX_R_safe,
-                             uint32_t block_size, bool RX_shift, bool TX_shift, uint32_t _RX_DMAch, uint32_t _TX_DMAch, uint32_t DMA_irq_pri)
+                             uint32_t block_size, bool _packed_RX, bool _packed_TX, bool _RX_shift, bool _TX_shift, uint32_t _RX_DMAch, uint32_t _TX_DMAch, uint32_t DMA_irq_pri)
 {
     if(!SYNC_attached && !SGTL5000::SYNC_attach_type) return -1;                                        // Check we have a handler if using callback
     if(SYNC_run || TX_run || RX_run ) return -1;                                                        // Check if i2s is already started
     if(_RX_DMAch > 15 || _TX_DMAch > 15) return -1;                                                     // Sanity check DMAMUX channels
     if (!(block_size == 2 || block_size == 4 || block_size == 8)) return -1;                            // Only accept block size 2^n within range.
+    packed_RX = _packed_RX;
+    packed_TX = _packed_TX;
     SGTL5000::TX_block_size = block_size;
     SGTL5000::RX_block_size = SGTL5000::TX_block_size;
     TX_bs_bytes = block_size * 4;
@@ -400,9 +413,10 @@
     SGTL5000::BufRX_R_safe = (uint32_t*)BufRX_R_safe;
     SGTL5000::BufTX_L_safe = (uint32_t*)BufTX_L_safe;
     SGTL5000::BufTX_R_safe = (uint32_t*)BufTX_R_safe;
-
-    SGTL5000::TX_shift = TX_shift;
-    SGTL5000::RX_shift = RX_shift;
+    if(packed_RX) RX_shift = false;
+    else RX_shift = _RX_shift;
+    if(packed_TX) TX_shift = false;
+    else TX_shift = _TX_shift;
     SGTL5000::RX_DMAch = _RX_DMAch;
     SGTL5000::TX_DMAch = _TX_DMAch;
     SYNC_run = true;
@@ -421,9 +435,9 @@
     NVIC_SetPriority((IRQn)SGTL5000::RX_DMAch, DMA_irq_pri);
     if(SGTL5000::TX_DMAch > SGTL5000::RX_DMAch) {
         NVIC_EnableIRQ((IRQn)SGTL5000::RX_DMAch);                                                       // Enable IRQ for chosen RX DMA channel if using lower priority DMA channel (lower channel number). This assumes fixed priority encoding and pre-emption, (which are default).
-        NVIC_DisableIRQ((IRQn)SGTL5000::TX_DMAch);                                                      // Disable IRQ for chosen TX DMA channel. We must wait for the lowest priority channel to trigger the DMA IRQ.
+        NVIC_DisableIRQ((IRQn)SGTL5000::TX_DMAch);                                                      // Disable IRQ for chosen TX DMA channel. We must wait for the lowest priority channel (DMA priority) to trigger the DMA IRQ.
     } else {
-        NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch);                                                       
+        NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch);
         NVIC_DisableIRQ((IRQn)SGTL5000::RX_DMAch);
     }
     NVIC_SetVector(I2S0_Rx_IRQn, (uint32_t)&SGTL5000::sync_I2S_ISR);                                    // Set vector for SYNC word start ISR
@@ -469,22 +483,15 @@
 void SGTL5000::sync_dma_ISR(void)
 {
     /*!
-    The RX and TX buffers are each 16 * 32bit words, which allows the FIFO to be, at a maximum,
-    fully emptied or filled in each DMA request. Each buffer is arranged firstly in 2 halves.
-    The Left channel in the lower half, Right channel in the upper half. Within each half there is double buffering.
-    With A & B sections. When A is active, B is free for the user to read and write. When B is active, A is free
-    to read and write. The pointers BufTX_L etc. are modified before the users code is called, in this way
-    no index tracking is needed in user-space. The pointers will always refer to the safe part of the double buffer.
+    Refer to the DMA_init function for details of buffer layouts.
 
-    buffer layout with Block Size = 2               Block Size = 4                          Block Size = 8
-    Double Buffer A Double Buffer B                 Double Buffer A Double Buffer B         Double Buffer A Double Buffer B
-    |L|R|x|x|x|x|x|x:L|R|x|x|x|x|x|x|               |L|L|R|R|x|x|x|x:L|L|R|R|x|x|x|x|       |L|L|L|L|R|R|R|R:L|L|L|L|R|R|R|R|
-    
-    When running both TX & RX synchronously, only 1 direction has its IRQ enabled in the NVIC, which is enabled is determined by the priority of the DMA channels.  In sync mode TX & RX DMAs transfer
-    the same number of bytes to the FIFO, therefore we should see only 1 FIFO word difference between DMA demands for TX or RX. This measn the DMA transfers will be pre-empting each other,
-    dependant on relative priority. Therefore before the user ISR is called we must be sure that the lowest priority channel has completed its transfer and it is this channel that has its IRQ enabled.
-    We clear both flags here, but the active IRQ is chosen in the start_SYNC function. This avoids servicing an extra IRQ
-    therby saving a few cycles in avoiding an extra stack operation.
+    When running both TX & RX synchronously, only 1 direction has its IRQ enabled in the NVIC, which is enabled is determined by the priority of the DMA channels.
+    This assumes strict prioriy ordering of DMA channels. In sync mode TX & RX DMAs transfer the same number of bytes to the FIFO, therefore we should see only 1 FIFO word 
+    difference between DMA demands for TX or RX.
+    The DMA transfers will be pre-empting each other, dependant on relative priority.
+    Therefore before the user ISR is called we must be sure that the lowest priority channel has completed its transfer and it is this channel that has its IRQ enabled.
+    We clear both flags here to avoid checking which is active as the active IRQ is chosen in the start_SYNC function (saves a couple of cycles).
+    Activating only one of the IRQs avoids servicing an extra IRQ stack operation and dealing with pending IRQs.
     */
     static uint32_t db_sync_phase = 0;
     static uint32_t dbA_rx_L = (uint32_t)&SGTL5000::I2S_RX_Buffer[0];                                   // Pre-compute buffer offsets etc to save cycles in ISR
@@ -512,10 +519,10 @@
         *SGTL5000::BufTX_R_safe  = dbA_tx_R;
         ++db_sync_phase;
     }
+    
     if(SGTL5000::SYNC_attach_type) {                                                                    // Trigger swIRQ or call Callback
         if(NVIC_GetActive(SGTL5000::SYNC_swIRQ) == 0) NVIC->STIR = SGTL5000::SYNC_swIRQ;
     } else SGTL5000::SYNC_user_func.call();
-    
 }
 
 
@@ -546,7 +553,7 @@
 }
 
 int32_t SGTL5000::start_TX(uint32_t BufTX_L_safe, uint32_t BufTX_R_safe,
-                           uint32_t block_size, bool _TX_shift, uint32_t _TX_DMAch, uint32_t DMA_irq_pri)
+                           uint32_t block_size, bool _packed_TX, bool _TX_shift, uint32_t _TX_DMAch, uint32_t DMA_irq_pri)
 {
     if(!TX_attached && !SGTL5000::TX_attach_type) return -1;                                            // Check we have a handler if using callback
     if(SYNC_run || TX_run) return -1;                                                                   // Check if i2s is already started on tx
@@ -556,8 +563,9 @@
     TX_bs_bytes = block_size * 4;
     SGTL5000::BufTX_L_safe = (uint32_t*)BufTX_L_safe;                                                   // Assign the users pointer addresses
     SGTL5000::BufTX_R_safe = (uint32_t*)BufTX_R_safe;
-
-    SGTL5000::TX_shift = _TX_shift;
+    packed_TX = _packed_TX;
+    if(packed_TX) TX_shift = false;
+    else TX_shift = _TX_shift;
     SGTL5000::TX_DMAch = _TX_DMAch;
     TX_run = true;
     init_DMA();
@@ -634,7 +642,7 @@
 }
 
 int32_t SGTL5000::start_RX(uint32_t BufRX_L_safe, uint32_t BufRX_R_safe,
-                           uint32_t block_size, bool _RX_shift, uint32_t _RX_DMAch, uint32_t DMA_irq_pri)
+                           uint32_t block_size, bool _packed_RX, bool _RX_shift, uint32_t _RX_DMAch, uint32_t DMA_irq_pri)
 {
     if(!RX_attached && !SGTL5000::RX_attach_type) return -1;                                            // Check we have a handler if using callback
     if(SYNC_run || RX_run) return -1;                                                                   // Check if i2s is already started on rx
@@ -644,8 +652,9 @@
     RX_bs_bytes = block_size * 4;
     SGTL5000::BufRX_L_safe = (uint32_t*)BufRX_L_safe;                                                   // Assign the users pointer addresses
     SGTL5000::BufRX_R_safe = (uint32_t*)BufRX_R_safe;
-
-    SGTL5000::RX_shift = _RX_shift;
+    packed_RX = _packed_RX;
+    if(packed_RX) RX_shift = false;
+    else RX_shift = _RX_shift;
     SGTL5000::RX_DMAch = _RX_DMAch;
     RX_run = true;
     init_DMA();
@@ -705,12 +714,20 @@
     The DMA transfer uses 2 channels. 1 for RX data 1 for TX data. Each DMA splits the Left and Right channels across the respective buffer.
     The chain is configured as follows:
     Each minor loop moves 4 bytes of data (2 * 16bit words, 1 left 1 right channel). Bus reads/writes are 16bit and DOFF is used to jump forward BLOCK_SIZE/2 32bit words on each bus read or write respectively.
-    At the end of a minor loop MLOFF jumps back (BLOCK_SIZE - 1) 32bit words. At Major loop termination the source or dest address is reloaded with a new TCD which points to the source or dest respectively at the
+    At the end of a minor loop MLOFF jumps back (BLOCK_SIZE - 1) 32bit words or packed (BLOCK_SIZE - 0.5) words. At Major loop termination the source or dest address is reloaded with a new TCD which points to the source or dest respectively at the
     uppper half of the double buffer.
     eg.
-    buffer layout with Block Size = 2               Block Size = 4                          Block Size = 8
-    Double Buffer A Double Buffer B                 Double Buffer A Double Buffer B         Double Buffer A Double Buffer B
-    |L|R|x|x|x|x|x|x:L|R|x|x|x|x|x|x|               |L|L|R|R|x|x|x|x:L|L|R|R|x|x|x|x|       |L|L|L|L|R|R|R|R:L|L|L|L|R|R|R|R|
+    Unpacked buffer layout:
+    Block Size = 2                                                            Block Size = 4                                                                Block Size = 8
+    Double Buffer A                     Double Buffer B                       Double Buffer A                       Double Buffer B                         Double Buffer A                          Double Buffer B
+    |L0:x|R0:x|x:x|x:x|x:x|x:x|x:x|x:x||L0:x|R0:x|x:x|x:x|x:x|x:x|x:x|x:x|    |L0:x|L1:x|R0:x|R1:x|x:x|x:x|x:x|x:x||L0:x|L1:x|R0:x|R1:x|x:x|x:x|x:x|x:x|    |L0:x|L1:x|L2:x|L3:x|R0:x|R1:x|R2:x|R3:x||L0:x|L1:x|L2:x|L3:x|R0:x|R1:x|R2:x|R3:x|
+    
+    Packed buffer layout:
+    Block Size = 2                                                            Block Size = 4                                                                Block Size = 8
+    Double Buffer A                     Double Buffer B                       Double Buffer A                       Double Buffer B                         Double Buffer A                          Double Buffer B
+    |L0:x|R0:x|x:x|x:x|x:x|x:x|x:x|x:x||L0:x|R0:x|x:x|x:x|x:x|x:x|x:x|x:x|    |L0:L1|x:x|R0:R1|x:x|x:x|x:x|x:x|x:x||L0:L1|x:x|R0:R1|x:x|x:x|x:x|x:x|x:x|    |L0:L1|L2:L3|x:x|x:x|R0:R1|R2:R3|x:x|x:x||L0:L1|L2:L3|x:x|x:x|R0:R1|R2:R3|x:x|x:x|
+    
+    The users pointers are always updated to point to L0 & R0 of the current safe double buffer area.
 
     */
 
@@ -734,7 +751,8 @@
         //Configure RX DMA Transfer Control Descriptor
         DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES &= ~DMA_NBYTES_MLOFFYES_SMLOE_MASK;                                                                                                   // Disable source offset on minor loop
         DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_DMLOE_MASK;                                                                                                    // Enable dest offset on minor loop
-        DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_MLOFF_MASK) | DMA_NBYTES_MLOFFYES_MLOFF((uint32_t)-(int32_t)(RX_bs_bytes - 4));  // After each minor loop step back to lower half of buffer and increment
+        if(packed_RX) DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_MLOFF_MASK) | DMA_NBYTES_MLOFFYES_MLOFF((uint32_t)-(int32_t)(RX_bs_bytes - 2));  // After each minor loop step back to lower half of buffer and increment
+        else DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_MLOFF_MASK) | DMA_NBYTES_MLOFFYES_MLOFF((uint32_t)-(int32_t)(RX_bs_bytes - 4));  // After each minor loop step back to lower half of buffer and increment
         DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_NBYTES_MASK) | DMA_NBYTES_MLOFFYES_NBYTES(4);                 // 4 Bytes (2 * 16bits words one left one right channel) transferred in each minor cycle.
         DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_DONE_MASK;                                                                                                                            // Clear channels CSR DONE bit to allow configuration of linkng fields in CSR register
         DMA0->TCD[SGTL5000::RX_DMAch].CSR = (DMA0->TCD[SGTL5000::RX_DMAch].CSR & ~DMA_CSR_BWC_MASK) | DMA_CSR_BWC(0);                                                                       // Disable Bandwidth control
@@ -744,11 +762,11 @@
         DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;                                                                                                                      // Disable major loop linking
         DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;                                                                                                                         // Enable IRQ at Completion of Major cycle
         DMA0->TCD[SGTL5000::RX_DMAch].ATTR = DMA_ATTR_SMOD(0) | DMA_ATTR_SSIZE(1) | DMA_ATTR_DMOD(0) | DMA_ATTR_DSIZE(1);                                                                   // Set data transfer size @ 16bits per memory access across the memory bus
-        if(SGTL5000::RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0] + 2;
+        if(RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0] + 2;
         else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0];
         DMA0->TCD[SGTL5000::RX_DMAch].SADDR = (uint32_t)&I2S0->RDR[0];                                                                                                                      // Set rxDMA Source addr pointer to 1st bit of I2S RX Data Reg
         DMA0->TCD[SGTL5000::RX_DMAch].SOFF = 0;                                                                                                                                             // Signed Source offset set to zero (always read from RDR[0]).
-        DMA0->TCD[SGTL5000::RX_DMAch].DOFF = (RX_bs_bytes / 2);                                                                                                                             // After each write step into upper half of the buffer
+        DMA0->TCD[SGTL5000::RX_DMAch].DOFF = (RX_bs_bytes / 2);                                                                                                                        // After each write step into upper half of the buffer
         DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO &= ~DMA_CITER_ELINKNO_ELINK_MASK;                                                                                                       // Disable channel linking minor loop
         DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO = (DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO & ~DMA_CITER_ELINKNO_CITER_MASK) | DMA_CITER_ELINKNO_CITER(SGTL5000::RX_block_size / 2); // Major loop current iter count starting value
         DMA0->TCD[SGTL5000::RX_DMAch].BITER_ELINKNO &= ~DMA_BITER_ELINKNO_ELINK_MASK;                                                                                                       // Disable channel linking minor loop
@@ -757,12 +775,12 @@
         DMA0->TCD[SGTL5000::RX_DMAch].SLAST = 0;
         memcpy(&SG_rx_TCD_A[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32);                                                                                                                 // Copy TCD A to memory
         DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_A[0];                                                                                                                // Set scatter gather TCD definition location
-        if(SGTL5000::RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[8] + 2;                                                                             // Swap RX double buffer
+        if(RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[8] + 2;                                                                             // Swap RX double buffer
         else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[8];
         memcpy(&SG_rx_TCD_B[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32);                                                                                                                 // Copy TCD B to memory
         // Set TCD elements in the DMA controller back to initial TCD A
         DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B[0];                                                                                                                // Set scatter gather TCD definition location
-        if(SGTL5000::RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0] + 2;
+        if(RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0] + 2;
         else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0];
     }
 
@@ -776,7 +794,8 @@
         //Configure TX DMA Transfer Control Descriptor
         DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_SMLOE_MASK;                                                                                                    // Disable source offset on minor loop
         DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES &= ~DMA_NBYTES_MLOFFYES_DMLOE_MASK;                                                                                                   // Enable dest offset on minor loop
-        DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_MLOFF_MASK) | DMA_NBYTES_MLOFFYES_MLOFF((uint32_t)-(int32_t)(TX_bs_bytes - 4));  // After each minor loop step back to lower half of buffer and increment
+        if(packed_TX) DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_MLOFF_MASK) | DMA_NBYTES_MLOFFYES_MLOFF((uint32_t)-(int32_t)(TX_bs_bytes - 2));
+        else DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_MLOFF_MASK) | DMA_NBYTES_MLOFFYES_MLOFF((uint32_t)-(int32_t)(TX_bs_bytes - 4));  // After each minor loop step back to lower half of buffer and increment
         DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES = (DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES & ~DMA_NBYTES_MLOFFYES_NBYTES_MASK) | DMA_NBYTES_MLOFFYES_NBYTES(4);                 // 4 Bytes (2 * 16bits words one left one right channel) transferred in each minor cycle.
         DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_DONE_MASK;                                                                                                                            // Clear channels CSR DONE bit to allow configuration of linkng fields in CSR register
         DMA0->TCD[SGTL5000::TX_DMAch].CSR = (DMA0->TCD[SGTL5000::TX_DMAch].CSR & ~DMA_CSR_BWC_MASK) | DMA_CSR_BWC(0);                                                                       // Disable Bandwidth control
@@ -786,7 +805,7 @@
         DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK;                                                                                                                      // Disable major loop linking
         DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK;                                                                                                                         // Enable IRQ at Completion of Major cycle
         DMA0->TCD[SGTL5000::TX_DMAch].ATTR = DMA_ATTR_SMOD(0) | DMA_ATTR_SSIZE(1) | DMA_ATTR_DMOD(0) | DMA_ATTR_DSIZE(1);                                                                   // Set data transfer size @ 16bits per memory access across the memory bus
-        if(SGTL5000::TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0] + 2;
+        if(TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0] + 2;
         else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0];
         DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[0];                                                                                                                      // Set rxDMA Source addr pointer to 1st bit of I2S RX Data Reg
         DMA0->TCD[SGTL5000::TX_DMAch].DOFF = 0;                                                                                                                                             // Signed Source offset set to zero (always write TDR[0]).
@@ -798,12 +817,12 @@
         DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0];                                                                                                                // Set scatter gather TCD definition location
         memcpy(&SG_tx_TCD_A[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32);                                                                                                                 // Copy TCD A to memory
         DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_A[0];                                                                                                                // Set scatter gather TCD definition location
-        if(SGTL5000::TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[8] + 2;                                                                             // Swap TX double buffer
+        if(TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[8] + 2;                                                                             // Swap TX double buffer
         else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[8];
         memcpy(&SG_tx_TCD_B[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32);                                                                                                                 // Copy TCD B to memory
         // Set TCD elements in the DMA controller back to initial TCD A
         DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0];                                                                                                                // Set scatter gather TCD definition location
-        if(SGTL5000::TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0] + 2;
+        if(TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0] + 2;
         else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0];
     }
 
@@ -819,7 +838,9 @@
 
 uint32_t SGTL5000::read_debug(uint32_t index)
 {
-    //SGTL5000::debug[0] =
+    SGTL5000::debug[0] = packed_RX;
+    SGTL5000::debug[1] = packed_TX;
+    SGTL5000::debug[2] = SGTL5000::I2S_RX_Buffer[0];
     return SGTL5000::debug[index];
 };
 }
\ No newline at end of file