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:
0:8f28f25e3435
Child:
3:62c03088f256
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sgtl5000.h	Thu Jun 15 09:27:42 2017 +0000
@@ -0,0 +1,300 @@
+/*
+@ author Aidan Walton, aidan.walton@gmail.com
+
+@section LICENSE
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice, development funding notice, and this permission
+ * notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+
+@section DESCRIPTION
+    Library for NXP SGTL5000 Codec
+*/
+
+#ifndef MBED_SGTL5000_H
+#define MBED_SGTL5000_H
+
+#include "mbed.h"
+#include "platform.h"
+#include "Callback.h"
+#include "sgtl5000_defs.h"
+
+namespace SGTL5000
+{
+
+class SGTL5000
+
+{
+public:
+    /*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);' 
+
+    @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)
+
+
+    // Pin Configs for i2s hardcoded as follows to match Teensy Audio Shield
+    i2s_mclk           i2s master clock (D11 Teensy 3.2 header / PTC6 MK20DX256)
+    i2s_bclk           i2s bit clock (D9 Teensy 3.2 header / PTC3 MK20DX256)
+    i2s_fs             i2s Frame Sync / L/R clock / WordSelect (D23 Teensy 3.2 header / PTC2 MK20DX256)
+    i2s_rx             i2s tx_data (from bus master perspective) (D22 Teensy 3.2 header / PTC1 MK20DX256)
+    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);
+
+
+    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 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.
+    */
+    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.
+    @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);
+    /*
+    @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);
+    /*
+    @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.
+                            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  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  TX_shift        True = The MS16bits of TX buffer are sent to the TX FIFO.   Default = true.
+                            False = The LS16bits of TX buffer are sent to the TX FIFO.
+    @param  tx_DMAch        Defines the system DMA channel to assign to the TX transfer. Default is 15.
+                            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.
+    */
+    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.
+    */
+    int32_t attach_RX(Callback<void()> func);
+    /*
+    @brief Attach a 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.
+                        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);
+    /*
+    @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);
+    /*
+    @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.
+                            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  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  RX_shift        True = The 16bits of RX FIFO data are shifted to the MSBs of the RX buffer. Default = true
+                            False = The 16bits of RX FIFO data are placed in the LSBs of the RX buffer
+                            Note: If data is not shifted, the 32bit word delivered to the user will not be sign extended.
+    @param  rx_DMAch        Defines the system DMA channel to assign to the RX transfer. Default is 14.
+                            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.
+    */
+    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.
+    */
+    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.
+    @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.
+                        It requires that 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_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.
+                            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_L_safe    A pointer address to the RX Right channel data.
+    @param  BufRX_L_safe    A pointer address to the TX Left channel data.
+    @param  BufRX_L_safe    A pointer address to the TX Right channel 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  _RX_shift       True = The 16bits of RX FIFO data are shifted to the MSBs of the RX buffer. Default = true
+                            False = The 16bits of RX FIFO data are placed in the LSBs of the RX buffer.
+                            Note: If data is not shifted, the 32bit word delivered to the user will not be sign extended.
+    @param  _TX_shift       True = The MS16bits of TX buffer are sent to the TX FIFO.   Default = true.
+                            False = The LS16bits of TX buffer are sent to the TX FIFO.
+    @param  _RX_DMAch       Defines the system DMA channel to assign to the RX transfer. Default is 14.
+    @param  _TX_DMAch       Defines the system DMA channel to assign to the TX transfer. Default is 15.
+                            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.
+    */
+    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);
+
+    /*
+    @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
+
+                        Note: To achieve some of these rates SYS_FS is adjusted.
+                        This needs to be considered for several internal codec processes such as filter co-efficients and AVC.
+    */
+    int32_t freq(uint32_t rate);
+
+    uint32_t read_debug(uint32_t index);
+
+
+protected:
+
+
+    static uint32_t volatile debug[16];
+
+
+
+
+
+private:
+    I2C mI2C;
+    int i2c_addr;
+
+    void init_i2s(void);                                                        // Configure I2S Default Settings
+
+    void init_codec(void);                                                      // Configure codec Default Settings
+
+    void init_DMA(void);                                                        // Configure SYNC DMA settings on MK20DX256
+
+    static void tx_dma_ISR(void);                                               // Handle TX DMA transfers complete
+
+    static void rx_dma_ISR(void);                                               // Handle RX DMA transfers complete
+
+    static void sync_dma_ISR(void);                                             // Handle SYNC DMA transfers complete
+
+    static void tx_I2S_ISR(void);                                               // Handle TX word start allignment
+
+    static void rx_I2S_ISR(void);                                               // Handle RX word start allignment
+
+    static void sync_I2S_ISR(void);                                             // Handle SYNC word start allignment
+
+
+    static uint32_t I2S_RX_Buffer[];                                            // Define global statics needed within ISRs
+    static uint32_t I2S_TX_Buffer[];
+    static uint32_t *BufRX_L_safe;
+    static uint32_t *BufRX_R_safe;
+    static uint32_t *BufTX_L_safe;
+    static uint32_t *BufTX_R_safe;
+    static uint32_t TX_block_size;
+    static uint32_t RX_block_size;
+    static uint32_t SYNC_attach_type;
+    static uint32_t TX_attach_type;
+    static uint32_t RX_attach_type;
+    static uint32_t RX_DMAch;
+    static uint32_t TX_DMAch;
+    static IRQn SYNC_swIRQ;
+    static IRQn TX_swIRQ;
+    static IRQn RX_swIRQ;
+    static Callback<void()> TX_user_func;
+    static Callback<void()> RX_user_func;
+    static Callback<void()> SYNC_user_func;
+
+    uint32_t TX_bs_bytes;
+    uint32_t RX_bs_bytes;
+    bool SYNC_run;
+    bool TX_run;
+    bool RX_run;
+
+    bool SYNC_attached;
+    bool TX_attached;
+    bool RX_attached;
+    bool TX_shift;
+    bool RX_shift;
+};
+}
+#endif
\ No newline at end of file