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:
11:392c09372ae1
Parent:
10:49bb33f71d32
Child:
12:9ce7f3828c6a
--- a/sgtl5000.h	Tue Sep 26 23:03:32 2017 +0000
+++ b/sgtl5000.h	Wed Sep 27 10:39:01 2017 +0000
@@ -42,7 +42,8 @@
 /*! 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. 
+*   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 CODEC CS = LOW) is the master, 
+    all stream ctrl functions (start, stop, pause & resume) are synchronous to this master codec and must be initiated by the master codec object. 
 *   @code
 *   #include 'SGTL5000.h'
 *
@@ -60,9 +61,9 @@
 *   {
 *       codec.modify_i2c(SGTL5000_ANA_HP_CTRL, 0x18, SGTL5000_ANA_HP_CTRL_HP_VOL_RIGHT_MASK);   // Headphone volume control with 0.5 dB steps.0x00 = +12 dB, 0x01 = +11.5 dB, 0x18 = 0 dB,...0x7F = -51.5 dB
 *       codec.modify_i2c(SGTL5000_ANA_HP_CTRL, 0x18, SGTL5000_ANA_HP_CTRL_HP_VOL_LEFT_MASK);
-*
+*       codec.init();
 *       codec.attach_SYNC_NB((uint32_t)&I2S_SYNC_ISR);
-*       codec.freq(96);
+*       codec.sample_rate(96);
 *       codec.start_SYNC((uint32_t)&RX_AudioL, (uint32_t)&RX_AudioR, (uint32_t)&TX_AudioL, (uint32_t)&TX_AudioR, I2S_FIFO_BS)
 *   }
 *
@@ -75,6 +76,54 @@
 *       }
 *   }
 @endcode
+*
+*   To implement dual CODECs
+*   @code
+*   #include 'SGTL5000.h'
+*
+*   SGTL5000::SGTL5000 codec1_ctrl(I2C_SDA, I2C_SCL);
+*   SGTL5000::SGTL5000 codec2(I2C_SDA, I2C_SCL);
+*
+*   static q31_t *RX_AudioL1 = NULL;
+*   static q31_t *RX_AudioR1 = NULL;
+*   static q31_t *TX_AudioL1 = NULL;
+*   static q31_t *TX_AudioR1 = NULL;
+*   static q31_t *RX_AudioL2 = NULL;
+*   static q31_t *RX_AudioR2 = NULL;
+*   static q31_t *TX_AudioL2 = NULL;
+*   static q31_t *TX_AudioR2 = NULL;
+*
+*   const uint32_t I2S_FIFO_BS = 4;
+*
+*
+*   uint32_t main()
+*   {
+*       codec1_ctrl.modify_i2c(SGTL5000_ANA_HP_CTRL, 0x18, SGTL5000_ANA_HP_CTRL_HP_VOL_RIGHT_MASK);     // Headphone volume control with 0.5 dB steps.0x00 = +12 dB, 0x01 = +11.5 dB, 0x18 = 0 dB,...0x7F = -51.5 dB
+*       codec1_ctrl.modify_i2c(SGTL5000_ANA_HP_CTRL, 0x18, SGTL5000_ANA_HP_CTRL_HP_VOL_LEFT_MASK);
+*       codec2.modify_i2c(SGTL5000_ANA_HP_CTRL, 0x01, SGTL5000_ANA_HP_CTRL_HP_VOL_RIGHT_MASK);          // Headphone volume control with 0.5 dB steps.0x00 = +12 dB, 0x01 = +11.5 dB, 0x18 = 0 dB,...0x7F = -51.5 dB
+*       codec2.modify_i2c(SGTL5000_ANA_HP_CTRL, 0x01, SGTL5000_ANA_HP_CTRL_HP_VOL_LEFT_MASK);           // Seperate codec settings.
+*       codec1_ctrl.init();
+*       codec2.init();
+*       codec1_ctrl.attach_SYNC_NB((uint32_t)&I2S_SYNC_ISR);
+*       codec1_ctrl.sample_rate(96);
+*       codec1_ctrl.start_SYNC((uint32_t)&RX_AudioL1, (uint32_t)&RX_AudioR1, (uint32_t)&TX_AudioL1, (uint32_t)&TX_AudioR1, I2S_FIFO_BS, true, true, false, false, 14, 15, 
+*                              (uint32_t)&RX_AudioL2, (uint32_t)&RX_AudioR2, (uint32_t)&TX_AudioL2, (uint32_t)&TX_AudioR2)
+*   }
+*
+*   void I2S_SYNC_ISR(void)
+*   {
+*       for(uint32_t i = 0; i < (I2S_FIFO_BS >> 1); ++i)
+*       {
+*           // CODEC 1
+*           TX_AudioL1[i] = RX_AudioL1[i];
+*           TX_AudioR1[i] = RX_AudioR1[i];
+*
+*           // CODEC 2
+*           TX_AudioL2[i] = RX_AudioL2[i];
+*           TX_AudioR2[i] = RX_AudioR2[i];
+*       }
+*   }
+@endcode
 */
 class SGTL5000
 
@@ -83,10 +132,11 @@
     //Constructor
     /*!
     @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. 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.
+            multiple instances. However currently only a single object for each CODEC should only be instantiated. It is possible to instantiate more, but care must be taken. 
+            After the init() function is called, state within the class becomes indpendent of the object that modified it.
+            The class is wrapped in the SGTL5000 namespace to avoid collisions with statics 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)
@@ -186,9 +236,8 @@
     /*!
     @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_0 data.
-                            The pointer address is managed by the driver and changes to implement a double buffer.
+                            The address pointed to by the users pointer is managed by the library 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_0 data.
     @param  _block_size     2 | 4 | 8 words of both Left and Right channels combined.
@@ -257,7 +306,7 @@
     /*!
     @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_0 data.
-                            The pointer address is managed by the driver and changes to implement a double buffer.
+                            The address pointed to by the users pointer is managed by the library 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_0 data.
@@ -313,7 +362,7 @@
     /**
     @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_0 data.
-                            The pointer address is managed by the driver and changes to implement a double buffer.
+                            The address pointed to by the users pointer is managed by the library 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_0 data.