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:
3:62c03088f256
Parent:
0:8f28f25e3435
Child:
4:91354c908416
--- a/sgtl5000.h	Thu Jun 15 13:59:26 2017 +0000
+++ b/sgtl5000.h	Fri Jun 16 12:57:54 2017 +0000
@@ -1,4 +1,4 @@
-/*
+/*!
 @ author Aidan Walton, aidan.walton@gmail.com
 
 @section LICENSE
@@ -32,17 +32,56 @@
 #include "Callback.h"
 #include "sgtl5000_defs.h"
 
+/** SGTL5000 namespace
+*/
 namespace SGTL5000
 {
 
+/*! SGTL5000 codec driver class
+*/
+/*! Class for NXP SGTL5000 codec instance.
+*   @code
+*   #include 'SGTL5000.h'
+*
+*   SGTL5000::SGTL5000 codec(I2C_SDA, I2C_SCL);
+*
+*   static q31_t *RX_AudioL = NULL;
+*   static q31_t *RX_AudioR = NULL;
+*   static q31_t *TX_AudioL = NULL;
+*   static q31_t *TX_AudioR = NULL;
+*
+*   const uint32_t I2S_FIFO_BS = 4;
+*
+*
+*   uint32_t main()
+*   {
+*       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.attach_SYNC_NB((uint32_t)&I2S_SYNC_ISR);
+*       codec.freq(96);
+*       codec.start_SYNC((uint32_t)&RX_AudioL, (uint32_t)&RX_AudioR, (uint32_t)&TX_AudioL, (uint32_t)&TX_AudioR, I2S_FIFO_BS)
+*   }
+*
+*   void I2S_SYNC_ISR(void)
+*   {
+*       for(uint32_t i = 0; i < (I2S_FIFO_BS >> 1); ++i)
+*       {
+*           TX_AudioL[i] = RX_AudioL[i];
+*           TX_AudioR[i] = RX_AudioR[i];
+*       }
+*   }
+@endcode
+*/
 class SGTL5000
 
 {
 public:
-    /*constructor
+    //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. Instantiation within the main code exemplified as follows: 'SGTL5000::SGTL5000 codec(I2C_SDA, I2C_SCL);' 
+            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.
 
     @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)
@@ -60,38 +99,66 @@
     */
     SGTL5000(PinName i2c_sda, PinName i2c_scl, int i2c_freq = 100000, bool i2c_ctrl_adr0_cs = 0);
 
-
-    int32_t read_i2c(uint32_t reg_addr, uint32_t mask = 0xFFFF);               // Read 16bit register of SGTL5000
-    int32_t write_i2c(uint32_t reg_addr, uint32_t data);                       //Write 16bit register of SGTL5000
-    int32_t modify_i2c(uint32_t reg_addr, uint32_t data, uint32_t mask);       //Modify masked bits within 16bit register of SGTL5000
-    /*
+    /*!
+    @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.
+    @returns            0 = register data, -1 = fail
+    */
+    int32_t read_i2c(uint32_t reg_addr, uint32_t mask = 0xFFFF);
+    
+    /*!
+    @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
+    @returns            0 = success, -1 = fail
+    */
+    int32_t write_i2c(uint32_t reg_addr, uint32_t data);
+    
+    /*!
+    @brief  Modify masked bits within 16bit register of SGTL5000
+    @param  reg_addr    16bit address of the codec control register
+    @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.
+    @returns            0 = success, -1 = fail
+    */
+    int32_t modify_i2c(uint32_t reg_addr, uint32_t data, uint32_t mask);       
+    
+    /*!
     @brief Attach a callback function to TX
-    @param func         User function to be called from the TX FIFO triggered ISR.
-                        This is blocking. If the user function does not complete before the next DMA completes the system will crash,
-                        however using this function avoids the latency of a stack push.
+    @param func         Address of the user function to be called from the TX FIFO triggered ISR.
+                        This is blocking. If the user function does not complete before the next DMA completes the system will likely crash,
+                        however using this function avoids the latency of an IRQ stack push.
+    @returns            0 = success, -1 = fail.
+                        Fails if already attached, must detach first.
     */
     int32_t attach_TX(Callback<void()> func);
-    /*
-    @brief Attach a ISR function to DMA TX
-    @param user_ISR     User function to be assigned as the NVIC vector for the DMA TX FIFO triggered user_ISR.
+    
+    /*!
+    @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 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);
-    /*
+    void attach_TX_NB(uint32_t user_ISR, uint32_t irq_pri = 1, IRQn sw_irq = Reserved54_IRQn);
+    
+    /*!
     @brief Stop TX channel and flag as detached.
                         During running stream, the callback based function can not be changed. However changes to the NB IRQ based attachment can have the vector changed on-the-fly
     */
     void detach_TX(void);
 
-    /*
+    /*!
     @brief Stops i2s TX channel but maintains clocking.
     */
-    int32_t stop_TX(void);
-    /*
+    void stop_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.
                             The pointer address is managed by the driver and changes to implement a double buffer.
@@ -107,19 +174,24 @@
                             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.
+    @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 _TX_shift = true,  uint32_t _TX_DMAch = 15, uint32_t DMA_irq_pri = 0);
 
-    /*
+    /*!
     @brief Attach a callback function to RX
     @param func         User function to be called from the RX FIFO triggered ISR.
                         This is blocking. If the user function does not complete before the next DMA completes the system will crash,
                         however using this function avoids the latency of a stack push.
+    @returns            0 = success, -1 = fail.
+                        Fails if already attached, must detach first.
     */
     int32_t attach_RX(Callback<void()> func);
-    /*
-    @brief Attach a ISR function to DMA RX
+    
+    /*!
+    @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 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.
@@ -127,18 +199,20 @@
                         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);
-    /*
+    void attach_RX_NB(uint32_t user_ISR, uint32_t irq_pri = 1, IRQn sw_irq = Reserved55_IRQn);
+    
+    /*!
     @brief Stop RX channel and flag as detached.
                         During running stream, the callback based function can not be changed. However changes to the NB IRQ based attachment can have the vector changed on-the-fly
     */
     void detach_RX(void);
 
-    /*
+    /*!
     @brief Stops i2s RX channel but maintains clocking.
     */
-    int32_t stop_RX(void);
-    /*
+    void stop_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.
                             The pointer address is managed by the driver and changes to implement a double buffer.
@@ -154,18 +228,23 @@
                             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.
+    @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 _RX_shift = true,  uint32_t _RX_DMAch = 14, uint32_t DMA_irq_pri = 0);
 
-    /*
+    /*!
     @brief Attach a callback function to DMA SYNC
     @param func         User function to be called from the DMA SYNC FIFO triggered ISR.
                         This is blocking. If the user function does not complete before the next DMA triggered IRQ the system will crash,
                         however using this function avoids the latency of a stack push.
+    @returns            0 = success, -1 = fail.
+                        Fails if already attached, must detach first.
     */
     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 irq_pri      Set the system wide priority of the user_ISR.
@@ -175,16 +254,15 @@
                         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);
+    void attach_SYNC_NB(uint32_t user_ISR, uint32_t irq_pri = 1, IRQn sw_irq = Reserved53_IRQn);
 
-    /*
+    /*!
     @brief Stop both TX & RX channels and flag as detached.
                         During running stream, the callback based function can not be changed. However changes to the NB IRQ based attachment can have the vector changed on-the-fly
     */
     void detach_SYNC(void);
 
-
-    /*
+    /**
     @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.
                             The pointer address is managed by the driver and changes to implement a double buffer.
@@ -205,16 +283,18 @@
                             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.
+    @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 _RX_shift = true, bool _TX_shift = true, uint32_t _RX_DMAch = 14, uint32_t _TX_DMAch = 15, uint32_t DMA_irq_pri = 0);
 
-    /*
+    /*!
     @brief Stops i2s TX & RX channels but maintains clocking.
     */
-    int32_t stop_SYNC(void);
+    void stop_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
@@ -224,11 +304,17 @@
                         MCU MCLK Divide Register ratio is therefore = (Fs * 256)/PLL Clk
                         The Teensy 3.1 & 3.2 have PLL freq @ 96MHz
 
-                        Note: To achieve some of these rates SYS_FS is adjusted.
+                        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);
-
+    /*!
+    @brief  Read debug data from the codec
+    @param  index       0-15
+                        Just a simple way for user code to grab running variables if you need it.
+    @returns            0 = success, -1 = fail
+    */
     uint32_t read_debug(uint32_t index);