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:
10:49bb33f71d32
Parent:
9:40e0ff8c2ba2
Child:
11:392c09372ae1
--- a/sgtl5000.h	Sat Jul 15 13:15:08 2017 +0000
+++ b/sgtl5000.h	Tue Sep 26 23:03:32 2017 +0000
@@ -32,14 +32,17 @@
 #include "Callback.h"
 #include "sgtl5000_defs.h"
 
+
 /** SGTL5000 namespace
 */
 namespace SGTL5000
 {
 
+
 /*! SGTL5000 codec driver class
 */
 /*! Class for NXP SGTL5000 codec instance.
+*   Supports dual codecs. One using I2S TX&RX channel_0 the other I2S TX&RX channel_1. The instance created for codec1 (I2S channel_0) is the master, all ctrl functions are synchronous to this master codec. 
 *   @code
 *   #include 'SGTL5000.h'
 *
@@ -81,12 +84,18 @@
     /*!
     @brief  Create an SGTL5000 object defined on the I2C port using DMA transfers of I2S data. The class is not defined as a singleton, as future development may require
             multiple instances. However currently it should only be instantiated once. The class is wrapped in the SGTL5000 namespace to avoid collisions with statics
-            needed by the ISRs.
+            needed by the ISRs. Only the CODEC using CTRL_ADR0_CS = 0 can be used to manage the I2S setup and data flow, such as sample_rate, attach, start, stop etc. If a second
+            CODEC is available then its data flow is locked to the 1st, TX & RX FIFO buffers of both CODECs will be synchronised and only one DMA channel is used to TX data
+            to both codecs and one DMA channel to RX data from both codecs.
 
     @param  i2c_sda             i2c Serial data pin (D18 Teensy 3.2 header / PTB3 MK20DX256)
     @param  i2c_scl             i2c Serial clock pin (D19 Teensy 3.2 header / PTB2 MK20DX256)
     @param  i2c_freq            Frequency in Hz at which the i2c codec interface clocks data
     @param  i2c_ctrl_adr0_cs    State on SGTL5000 CTRL_ADR0_CS pin   i2c addr = 0n01010(R/W) :R/W = 1 to write R/W = 0 to read, n = 0 pull down / n = 1 pull up on CTRL_ADR0_CS pin of SGTL5000)
+    @param  _ctrl_IRQn          A system IRQ number used to control the codec. All time sensitive commands are elevated to highest IRQ priority using this IRQ number.
+                                Note: This is only used by the master codec (which is the codec with CS pin LOW). All commands to control data flow must be issued through the master codec object.
+                                This includes setting sample rate. Both codecs are linked by default at the same rate. Setting the master codecs rate also sets the same rate for the 2nd codec.
+                                However each codec instance has fully independent access to all the other codec internal features, through its respective object.
 
 
     // Pin Configs for i2s hardcoded as follows to match Teensy Audio Shield
@@ -97,24 +106,26 @@
     i2s_tx             i2s rx_data (from bus master perspective) (D13 Teensy 3.2 header /PTC5 MK20DX256)
 
     */
-    SGTL5000(PinName i2c_sda, PinName i2c_scl, int _i2c_freq = 100000, bool i2c_ctrl_adr0_cs = 0);
+    SGTL5000(PinName i2c_sda, PinName i2c_scl, int _i2c_freq = 100000, bool i2c_ctrl_adr0_cs = 0, IRQn _ctrl_IRQn = Reserved109_IRQn);
 
     /*!
     @brief  Read 16bit register of SGTL5000
     @param  reg_addr    16bit address of the codec control register
     @param  data        16bit data to read from the address
     @param  mask        16bit mask applied over the data read from the codec. The final returned value is the register data automatically shifted to the position of the first masked bit.
+    @param  _i2c_addr   Default = 0. If none zero, overrides the address associated with the current object.
     @returns            0 = register data, -1 = fail
     */
-    int32_t read_i2c(uint32_t reg_addr, uint32_t mask = 0xFFFF);
+    int32_t read_i2c(uint32_t reg_addr, uint32_t mask = 0xFFFF, int _i2c_addr = 0);
 
     /*!
     @brief  Write 16bit register of SGTL5000
     @param  reg_addr    16bit address of the codec control register
     @param  data        16bit data to write into the address
+    @param  _i2c_addr   Default = 0. If none zero, overrides the address associated with the current object.
     @returns            0 = success, -1 = fail
     */
-    int32_t write_i2c(uint32_t reg_addr, uint32_t data);
+    int32_t write_i2c(uint32_t reg_addr, uint32_t data, int _i2c_addr = 0);
 
     /*!
     @brief  Modify masked bits within 16bit register of SGTL5000
@@ -122,9 +133,10 @@
     @param  data        16bit data to write into the address
     @param  mask        16bit mask of the bits to modify.
                         The function automatically shifts the data to the position of the first masked bit.
+    @param  _i2c_addr   Default = 0. If none zero, overrides the address associated with the current object.
     @returns            0 = success, -1 = fail
     */
-    int32_t modify_i2c(uint32_t reg_addr, uint32_t data, uint32_t mask);
+    int32_t modify_i2c(uint32_t reg_addr, uint32_t data, uint32_t mask, int _i2c_addr = 0);
 
     /*!
     @brief Attach a callback function to TX
@@ -138,14 +150,14 @@
 
     /*!
     @brief Attach an ISR function to DMA TX
-    @param user_ISR     Address of the user function to be assigned as the NVIC vector for the DMA TX FIFO triggered user_ISR.
+    @param user_ISR     User function address pointer to be assigned as the NVIC vector for the DMA TX FIFO triggered user_ISR.
     @param irq_pri      Set the system wide priority of the user_ISR.
     @param sw_irq       The IRQ assigned. Default uses Reserved54_IRQn. See "MK20DX256.h" for available.
                         This is non-blocking provided the priority of the IRQ associated with user_ISR is lower than the priority of the DMA triggered ISR.
                         It can be useful to use a non-blocking call, however this involves the extra time needed to push the stack and manageing IRQ priorities
                         across the whole system needs consideration.
     */
-    int32_t attach_TX_NB(uint32_t user_ISR, uint32_t irq_pri = 1, IRQn sw_irq = Reserved54_IRQn);
+    int32_t attach_TX_NB(void* user_ISR, uint32_t irq_pri = 1, IRQn sw_irq = Reserved54_IRQn);
 
     /*!
     @brief Stop TX channel and flag as detached.
@@ -154,19 +166,31 @@
     int32_t detach_TX(void);
 
     /*!
-    @brief Stops i2s TX channel. Stops all DMA requests and supresses any IRQs from the driver.
-           This can be used to suspend the codec when a user wishes to run critical tasks where IRQs must be disabled. To restart call the start function again.
+    @brief Disables I2S TX function. This stops all DMA requests and supresses any IRQs from the driver and tristates the inbound CODEC I2S interface.
+                            It also stops bit clocks and word sync clocks.
+                            Note: Stopping the TX will also stop the RX stream because the RX is synchronous to the TX function. It is recommended that TX is the last enabled and first disabled.
     */
     int32_t stop_TX(void);
 
     /*!
+    @brief Pauses I2S TX channels. Halts the TX stream(s) by masking all data words.
+           This can be used to suspend the codec when a user wishes to run critical tasks where IRQs must be suppressed. To restart call the resume function.
+    */
+    int32_t pause_TX(void);
+
+    /*!
+    @brief Resumes a paused I2S TX channels. Resumes the TX stream(s) by un-masking all data words.
+    */
+    int32_t resume_TX(void);
+
+    /*!
     @brief  Starts the codec I2S interface and begins transferring TX buffers. Transfers use DMA.
-    @param  _BufTX_L_safe   A pointer address to the TX Left channel data.
+    @param  _BufTX_L_safe   A pointer address to the TX Left channel_0 data.
                             The pointer address is managed by the driver and changes to implement a double buffer.
                             It is suggested that a suitable declaration in the users code would be in the form: 'q31_t *TX_AudioL = NULL;'
                             Although volatile is not strictly necessary both the pointer address and the data pointed to, are changing outside the flow of the user code.
                             To pass into the class, dereference this pointer and cast as uint32_t, as follows: 'codec.start_SYNC((uint32_t)&TX_AudioL .....'
-    @param  _BufTX_R_safe   A pointer address to the TX Right channel data.
+    @param  _BufTX_R_safe   A pointer address to the TX Right channel_0 data.
     @param  _block_size     2 | 4 | 8 words of both Left and Right channels combined.
                             This defines the number of samples that are transferred to the TX FIFO each time a FIFO demand is detected.
     @param  _packed_TX      If true 2 * 16bit words for wire transmission are expected packed into a single 32bit word.
@@ -178,11 +202,13 @@
                             15 is used as default to avoid using channels 0 - 3 which are the only channels available for gated triggers.
                             Gated triggering is not needed, so these 4 channels are avoided.
     @param  _DMA_irq_pri    Default = 0. Highest priority. This is the priority of the I2S TX DMA demands.
+    @param  _BufTX_L_safe2  A pointer address to the TX Left channel_1 data.
+    @param  _BufTX_R_safe2  A pointer address to the TX Right channel_1 data.
     @returns                0 = success, -1 = fail
                             Fails on variable sanity checks.
     */
     int32_t start_TX(uint32_t _BufTX_L_safe, uint32_t _BufTX_R_safe,
-                     uint32_t _block_size = 4, bool _packed_TX = false, bool _TX_shift = true,  uint32_t _TX_DMAch = 15, uint32_t _DMA_irq_pri = 0);
+                     uint32_t _block_size = 4, bool _packed_TX = false, bool _TX_shift = true,  uint32_t _TX_DMAch = 15, uint32_t _DMA_irq_pri = 0, uint32_t _BufTX_L_safe2 = NULL, uint32_t _BufTX_R_safe2 = NULL);
 
     /*!
     @brief Attach a callback function to RX
@@ -196,14 +222,14 @@
 
     /*!
     @brief Attach an ISR function to DMA RX
-    @param user_ISR     User function to be assigned as the NVIC vector for the DMA RX FIFO triggered user_ISR.
+    @param user_ISR     User function address pointer to be assigned as the NVIC vector for the DMA RX FIFO triggered user_ISR.
     @param irq_pri      Set the system wide priority of the user_ISR.
     @param sw_irq       The IRQ assigned. Default uses Reserved55_IRQn. See "MK20DX256.h" for available.
                         This is non-blocking provided the priority of the IRQ associated with user_ISR is lower than the priority of the DMA triggered ISR.
                         It can be useful to use a non-blocking call, however this involves the extra time needed to push the stack and manageing IRQ priorities
                         across the whole system needs consideration.
     */
-    int32_t attach_RX_NB(uint32_t user_ISR, uint32_t irq_pri = 1, IRQn sw_irq = Reserved55_IRQn);
+    int32_t attach_RX_NB(void* user_ISR, uint32_t irq_pri = 1, IRQn sw_irq = Reserved55_IRQn);
 
     /*!
     @brief Stop RX channel and flag as detached.
@@ -212,18 +238,29 @@
     int32_t detach_RX(void);
 
     /*!
-    @brief Stops i2s RX channel. Stops all DMA requests and supresses any IRQs from the driver.
-           This can be used to suspend the codec when a user wishes to run critical tasks where IRQs must be disabled. To restart call the start function again.
+    @brief Disables I2S RX function. Stops all DMA requests and supresses any IRQs from the driver and tristates the outbound CODEC I2S interface(s).
+                        Note: Bit clock and Word Sync clock will continue as long as TX is running (started).
     */
     int32_t stop_RX(void);
 
     /*!
+    @brief Pauses I2S RX channels. Halts the RX stream(s) by masking all data words.
+           This can be used to suspend the codec when a user wishes to run critical tasks where IRQs must be suppressed. To restart call the resume function.
+    */
+    int32_t pause_RX(void);
+
+    /*!
+    @brief Resumes a paused I2S RX channels. Resumes the RX stream(s) by un-masking all data words.
+    */
+    int32_t resume_RX(void);
+
+    /*!
     @brief  Starts the codec I2S interface and begins transferring RX buffers. Transfers use DMA.
-    @param  _BufRX_L_safe   A pointer address to the RX Left channel data.
+    @param  _BufRX_L_safe   A pointer address to the RX Left channel_0 data.
                             The pointer address is managed by the driver and changes to implement a double buffer.
                             It is suggested that a suitable declaration in the users code would be in the form: 'q31_t *RX_AudioL = NULL;'
                             To pass into the class, dereference this pointer and cast as uint32_t, as follows: 'codec.start_SYNC((uint32_t)&RX_AudioL .....'
-    @param  _BufRX_R_safe   A pointer address to the RX Right channel data.
+    @param  _BufRX_R_safe   A pointer address to the RX Right channel_0 data.
     @param  _block_size     2 | 4 | 8 words of both Left and Right channels combined.
                             This defines the number of samples that are transferred to the RX FIFO each time a FIFO demand is detected.
     @param  _packed_RX      If true the 2 * 16bit words from the codec are packed into a single 32bit word towards the user. This allows user code to use SIMD operations on the data
@@ -236,11 +273,13 @@
                             14 is used as default to avoid using channels 0 - 3 which are the only channels available for gated triggers.
                             Gated triggering is not needed, so these 4 channels are avoided.
     @param  _DMA_irq_pri    Default = 0. Highest priority. This is the priority of the I2S RX DMA demands.
+    @param  _BufRX_L_safe2  A pointer address to the RX Left channel_1 data.
+    @param  _BufRX_R_safe2  A pointer address to the RX Right channel_1 data.
     @returns                0 = success, -1 = fail
                             Fails on variable sanity checks.
     */
     int32_t start_RX(uint32_t _BufRX_L_safe, uint32_t _BufRX_R_safe,
-                     uint32_t _block_size = 4, bool _packed_RX = false, bool _RX_shift = true,  uint32_t _RX_DMAch = 14, uint32_t _DMA_irq_pri = 0);
+                     uint32_t _block_size = 4, bool _packed_RX = false, bool _RX_shift = true,  uint32_t _RX_DMAch = 14, uint32_t _DMA_irq_pri = 0, uint32_t _BufRX_L_safe2 = NULL, uint32_t _BufRX_R_safe2 = NULL);
 
     /*!
     @brief Attach a callback function to DMA SYNC
@@ -252,9 +291,10 @@
     */
     int32_t attach_SYNC(Callback<void()> func);
 
+
     /*!
     @brief Attach a ISR function to DMA SYNC
-    @param user_ISR     User function to be assigned as the NVIC vector for the DMA SYNC FIFO triggered user_ISR.
+    @param user_ISR     User function address pointer to be assigned as the NVIC vector for the DMA SYNC FIFO triggered user_ISR.
     @param irq_pri      Set the system wide priority of the user_ISR.
     @param sw_irq       The IRQ assigned. Default uses Reserved53_IRQn. See "MK20DX256.h" for available.
                         This creates a non-blocking call, which tests to see if the users ISR has completed before calling again.
@@ -262,7 +302,7 @@
                         It can be useful to use a non-blocking call, however this involves the extra time needed to push the stack and manageing IRQ priorities
                         across the whole system needs consideration.
     */
-    int32_t attach_SYNC_NB(uint32_t user_ISR, uint32_t irq_pri = 1, IRQn sw_irq = Reserved53_IRQn);
+    int32_t attach_SYNC_NB(void* user_ISR, uint32_t irq_pri = 1, IRQn sw_irq = Reserved53_IRQn);
 
     /*!
     @brief Stop both TX & RX channels and flag as detached.
@@ -272,13 +312,13 @@
 
     /**
     @brief  Starts the codec I2S interface and begins transferring RX and TX buffers. Transfers use DMA.
-    @param  _BufRX_L_safe   A pointer address to the RX Left channel data.
+    @param  _BufRX_L_safe   A pointer address to the RX Left channel_0 data.
                             The pointer address is managed by the driver and changes to implement a double buffer.
                             It is suggested that a suitable declaration in the users code would be in the form: 'q31_t *RX_AudioL = NULL;'
                             To pass into the class, dereference this pointer and cast as uint32_t, as follows: 'codec.start_SYNC((uint32_t)&RX_AudioL .....'
-    @param  _BufRX_R_safe   A pointer address to the RX Right channel data.
-    @param  _BufTX_L_safe   A pointer address to the TX Left channel data.
-    @param  _BufTX_R_safe   A pointer address to the TX Right channel data.
+    @param  _BufRX_R_safe   A pointer address to the RX Right channel_0 data.
+    @param  _BufTX_L_safe   A pointer address to the TX Left channel_0 data.
+    @param  _BufTX_R_safe   A pointer address to the TX Right channel_0 data.
     @param  _block_size     2 | 4 | 8 words of both Left and Right channels combined.
                             This defines the number of samples that are transferred to both FIFOs each time a FIFO demand is detected.
     @param  _packed_TX      If true the 2 * 16bit words for wire transmission are expected packed into a single 32bit word.
@@ -297,77 +337,113 @@
                             14 & 15 are used as default to avoid using channels 0 - 3 which are the only channels available for gated triggers.
                             Gated triggering is not needed, so these 4 channels are avoided.
     @param  _DMA_irq_pri    Default = 0. Highest priority. This is the priority of the I2S DMA demands.
+    @param  _BufRX_L_safe2  A pointer address to the RX Left channel_1 data.
+    @param  _BufRX_R_safe2  A pointer address to the RX Right channel_1 data.
+    @param  _BufTX_L_safe2  A pointer address to the TX Left channel_1 data.
+    @param  _BufTX_R_safe2  A pointer address to the TX Right channel_1 data.
     @returns                0 = success, -1 = fail
                             Fails on variable sanity checks.
     */
     int32_t 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 = 4, bool _packed_RX = false, bool _packed_TX = false, bool _RX_shift = true, bool _TX_shift = true, uint32_t _RX_DMAch = 14, uint32_t _TX_DMAch = 15, uint32_t _DMA_irq_pri = 0);
+                       uint32_t _block_size = 4, bool _packed_RX = false, bool _packed_TX = false, bool _RX_shift = true, bool _TX_shift = true, uint32_t _RX_DMAch = 14, uint32_t _TX_DMAch = 15, uint32_t _DMA_irq_pri = 0, uint32_t _BufRX_L_safe2 = NULL, uint32_t _BufRX_R_safe2 = NULL, uint32_t _BufTX_L_safe2 = NULL, uint32_t _BufTX_R_safe2 = NULL);
 
     /*!
-    @brief Stops i2s TX & RX channels. Stops all DMA requests and supresses any IRQs from the driver.
-           This can be used to suspend the codec when a user wishes to run critical tasks where IRQs must be disabled. To restart call the start function again.
+    @brief Stops I2S TX & RX channels. Stops all DMA requests and supresses any IRQs from the driver and tristates the CODEC I2S interface.
     */
     int32_t stop_SYNC(void);
 
     /*!
-    @brief Set codec and i2s Sampling frequency
+    @brief Pauses I2S RX & TX channels. Halts the RX & TX stream(s) by masking all data words.
+           This can be used to suspend the codec when a user wishes to run critical tasks where IRQs must be suppressed. To restart call the resume function.
+    */
+    int32_t pause_SYNC(void);
+
+    /*!
+    @brief Resumes a paused I2S RX & TX channels. Resumes the RX & TX stream(s) by un-masking all data words.
+    */
+    int32_t resume_SYNC(void);
+
+    /*!
+    @brief Set codec and I2S Sampling frequency
     @param  rate        8, 11, 12, 16, 22, 24, 32, 44, 48, 96, 192
                         Base sampling rate of the codec
 
                         In all cases the SGTL5000 is programmed to use MCLK 256 times faster than sampling freq.
                         MCU MCLK output = MCLK_Input((FRACT + 1)/(DIVIDE + 1))
                         MCU MCLK Divide Register ratio is therefore = (Fs * 256)/PLL Clk
-                        The Teensy 3.1 & 3.2 have PLL freq @ 96MHz
+                        The Teensy 3.1 & 3.2 have PLL freq @ 96MHz. However 120Mhz is supported dependent on the global 'SystemCoreClock' variable indicating this.
 
                         Note: To achieve some of these rates the codec SYS_FS is adjusted.
                         This needs to be considered for several internal codec processes such as filter co-efficients and AVC.
     @returns            0 = success, -1 = fail
     */
-    int32_t freq(uint32_t rate);
-    
+    int32_t sample_rate(uint32_t rate);
+
     /*!
     @brief  Initialise codec.
-                        Sends initial default configuration data to the codec over I2C.
+                        Resets the codec and sends initial default configuration data to the codec over I2C.
                         This function must be called after instantiation and before most other functions. It allows control over when I2C communications with the codec takes place.
                         Failure to initialize will prevent operation of the codec. However it possible to attach and detach functions before init.
     @returns            0 = success, -1 = fail
     */
     int32_t init(void);
-    
+
     /*!
     @brief  Read debug data from the codec
     @param  index       0-15
+    @param  finished    a simple semaphore, indicating that debug data should be aquired again. (Can be used in combination with the internal bool SGTL5000::debug_read, to gate data collection.) 
                         Just a simple way for user code to grab running variables if you need it.
     @returns            0 = success, -1 = fail
     */
-    int32_t read_debug(uint32_t index);
+    int32_t read_debug(uint32_t index, bool finished = false);
+    static bool debug_read;
 
 
 protected:
     static uint32_t debug[16];
+    static void t_stamp_start(void);
+    static void t_stamp_stop(void);
+    static uint32_t t1;
+    static uint32_t proc_time;
+    static uint32_t SYST_CVAL;// = 0xE000E018;                                  // Address of SysTick current value register
 
 
 private:
+
+    static IRQn CODEC_CTRL_IRQ;                                                 // Default IRQ used to elevate priority of user commands.
+    static uint32_t ctrl_command;                                               // Command translation
+    #define STOP_SYNC   0x1
+    #define STOP_TX     0x2
+    #define STOP_RX     0x3
+    #define PAUSE_TX    0x4
+    #define PAUSE_RX    0x5
+    #define PAUSE_SYNC  0x6
+    #define RESUME_TX   0x7
+    #define RESUME_RX   0x8
+    #define RESUME_SYNC 0x9
+    
+    static void stream_ctrl_ISR(void);                                          // High priority control commands trigger by user through software ISR 
+
+
     I2C mI2C;                                                                   // Create I2C instance
-    int i2c_addr;
+    
+    int i2c_addr;    
 
-    void init_i2s(void);                                                        // Configure I2S Default Settings
+    int32_t init_i2s(void);                                                     // Configure I2S Default Settings
 
     int32_t init_codec(void);                                                   // Configure codec Default Settings
 
     void init_DMA(void);                                                        // Configure SYNC DMA settings on MK20DX256
 
-    static void tx_dma_ISR_NB(void);                                            // Handle TX DMA transfers complete using IRQs to user
+    static void SYNC_TX_dma_ISR(void);
 
-    static void rx_dma_ISR_NB(void);                                            // Handle RX DMA transfers complete using IRQs to user
-
-    static void sync_dma_ISR_NB(void);                                          // Handle SYNC DMA transfers complete using IRQs to user
+    static void SYNC_RX_dma_ISR(void);
 
-    static void tx_dma_ISR(void);                                               // Handle TX DMA transfers complete using Callback
+    static void SYNC_pointer_swap(void);
 
-    static void rx_dma_ISR(void);                                               // Handle RX DMA transfers complete using Callback
+    static void TX_dma_ISR(void);                                               // Handle TX DMA transfers complete.
 
-    static void sync_dma_ISR(void);                                             // Handle SYNC DMA transfers complete using Callback
+    static void RX_dma_ISR(void);                                               // Handle RX DMA transfers complete using Callback
 
     static void tx_I2S_WS_ISR(void);                                            // Handle TX word start allignment
 
@@ -376,13 +452,40 @@
     static void sync_I2S_WS_ISR(void);                                          // Handle SYNC word start allignment
 
 
+    //const uint32_t DMA_INT_ADDR = 0x40008024;        // Hard code the DMA->INT address its faster when inline assembler optimises
 
-    static uint32_t *BufRX_L_safe;                                              // Define statics for ISRs
-    static uint32_t *BufRX_R_safe;
-    static uint32_t *BufTX_L_safe;
-    static uint32_t *BufTX_R_safe;
-    static uint32_t RX_DMAch;
-    static uint32_t TX_DMAch;
+
+    static volatile uint32_t * volatile BufRX_L_safe;                                              // Define statics for ISRs
+    static volatile uint32_t * volatile BufRX_R_safe;
+    static volatile uint32_t * volatile BufTX_L_safe;
+    static volatile uint32_t * volatile BufTX_R_safe;
+    static volatile uint32_t * volatile BufRX_L_safe2;                                              // Define statics for ISRs
+    static volatile uint32_t * volatile BufRX_R_safe2;
+    static volatile uint32_t * volatile BufTX_L_safe2;
+    static volatile uint32_t * volatile BufTX_R_safe2;
+    static uint32_t BufRX_L_safeA;                                                  // Precalculated double buffer addresses
+    static uint32_t BufRX_R_safeA;
+    static uint32_t BufTX_L_safeA;
+    static uint32_t BufTX_R_safeA;
+    static uint32_t BufRX_L_safeB;                                                  // Precalculated double buffer addresses
+    static uint32_t BufRX_R_safeB;
+    static uint32_t BufTX_L_safeB;
+    static uint32_t BufTX_R_safeB;
+    static uint32_t BufRX_L_safeA2;                                                  // Precalculated double buffer addresses
+    static uint32_t BufRX_R_safeA2;
+    static uint32_t BufTX_L_safeA2;
+    static uint32_t BufTX_R_safeA2;
+    static uint32_t BufRX_L_safeB2;                                                  // Precalculated double buffer addresses
+    static uint32_t BufRX_R_safeB2;
+    static uint32_t BufTX_L_safeB2;
+    static uint32_t BufTX_R_safeB2;
+    static uint32_t I2S_RX_Buffer[16];
+    static uint32_t I2S_TX_Buffer[16];
+    static uint32_t I2S_RX_Buffer2[16];
+    static uint32_t I2S_TX_Buffer2[16];
+    static uint32_t active_RX_DMAch_bm;
+    static uint32_t active_TX_DMAch_bm;
+    static uint32_t sync_dma_irq_acks;
     static IRQn SYNC_swIRQ;
     static IRQn TX_swIRQ;
     static IRQn RX_swIRQ;
@@ -392,23 +495,30 @@
     static uint32_t db_phase_sync;
     static uint32_t db_phase_tx;
     static uint32_t db_phase_rx;
-    
-    int i2c_freq;
+    static uint32_t SYNC_attach_type;
+    static uint32_t TX_attach_type;
+    static uint32_t RX_attach_type;
+    static bool i2s_configured;
+    static bool codec2_active;
+    static bool codec1_active;
+    static uint32_t dump;
+    static uint32_t NULL_DMA_TX;
+    static uint32_t NULL_DMA_RX;
+    static uint32_t RX_DMAch;
+    static uint32_t TX_DMAch;
+    static bool SYNC_run;
+    static bool TX_run;
+    static bool RX_run;
 
-    uint32_t I2S_RX_Buffer[16];
-    uint32_t I2S_TX_Buffer[16];
-    uint32_t SYNC_attach_type;
-    uint32_t TX_attach_type;
-    uint32_t RX_attach_type;
+    int i2c_freq;
     uint32_t TX_block_size;
     uint32_t RX_block_size;
     uint32_t TX_bs_bytes;
     uint32_t RX_bs_bytes;
+    bool ctrl_codec;
     bool codec_configured;
     bool codec_I2S_active;
-    bool SYNC_run;
-    bool TX_run;
-    bool RX_run;
+    
     bool SYNC_attached;
     bool TX_attached;
     bool RX_attached;
@@ -416,6 +526,8 @@
     bool RX_shift;
     bool packed_RX;
     bool packed_TX;
+
+
 };
 }
 #endif
\ No newline at end of file