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.
sgtl5000.cpp@10:49bb33f71d32, 2017-09-26 (annotated)
- Committer:
- aidan1971
- Date:
- Tue Sep 26 23:03:32 2017 +0000
- Revision:
- 10:49bb33f71d32
- Parent:
- 9:40e0ff8c2ba2
Significant re-work.; The library now supports dual codecs. Using channel 0 and channel 1 of the I2S silicon of Teensy 3.2.; replaced c code with inline assembler reduces time of pointer swaps < 800nS.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
aidan1971 | 3:62c03088f256 | 1 | /*! |
aidan1971 | 0:8f28f25e3435 | 2 | @ author Aidan Walton, aidan.walton@gmail.com |
aidan1971 | 0:8f28f25e3435 | 3 | |
aidan1971 | 0:8f28f25e3435 | 4 | @section LICENSE |
aidan1971 | 0:8f28f25e3435 | 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
aidan1971 | 0:8f28f25e3435 | 6 | * of this software and associated documentation files (the "Software"), to deal |
aidan1971 | 0:8f28f25e3435 | 7 | * in the Software without restriction, including without limitation the rights |
aidan1971 | 0:8f28f25e3435 | 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
aidan1971 | 0:8f28f25e3435 | 9 | * copies of the Software, and to permit persons to whom the Software is |
aidan1971 | 0:8f28f25e3435 | 10 | * furnished to do so, subject to the following conditions: |
aidan1971 | 0:8f28f25e3435 | 11 | * |
aidan1971 | 0:8f28f25e3435 | 12 | * The above copyright notice, development funding notice, and this permission |
aidan1971 | 0:8f28f25e3435 | 13 | * notice shall be included in all copies or substantial portions of the Software. |
aidan1971 | 0:8f28f25e3435 | 14 | * |
aidan1971 | 0:8f28f25e3435 | 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
aidan1971 | 0:8f28f25e3435 | 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
aidan1971 | 0:8f28f25e3435 | 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
aidan1971 | 0:8f28f25e3435 | 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
aidan1971 | 0:8f28f25e3435 | 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
aidan1971 | 0:8f28f25e3435 | 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
aidan1971 | 0:8f28f25e3435 | 21 | * THE SOFTWARE. |
aidan1971 | 0:8f28f25e3435 | 22 | |
aidan1971 | 0:8f28f25e3435 | 23 | @section DESCRIPTION |
aidan1971 | 0:8f28f25e3435 | 24 | Library for NXP SGTL5000 Codec |
aidan1971 | 0:8f28f25e3435 | 25 | */ |
aidan1971 | 0:8f28f25e3435 | 26 | |
aidan1971 | 0:8f28f25e3435 | 27 | #include "sgtl5000.h" |
aidan1971 | 0:8f28f25e3435 | 28 | |
aidan1971 | 0:8f28f25e3435 | 29 | namespace SGTL5000 |
aidan1971 | 0:8f28f25e3435 | 30 | { |
aidan1971 | 0:8f28f25e3435 | 31 | |
aidan1971 | 0:8f28f25e3435 | 32 | // Static variables required within ISRs |
aidan1971 | 10:49bb33f71d32 | 33 | volatile uint32_t * volatile SGTL5000::BufRX_L_safe; // Private pointers assigned to users data pointers |
aidan1971 | 10:49bb33f71d32 | 34 | volatile uint32_t * volatile SGTL5000::BufRX_R_safe; // These are used to flip user pointers between safe 'active' regions of |
aidan1971 | 10:49bb33f71d32 | 35 | volatile uint32_t * volatile SGTL5000::BufTX_L_safe; // double buffered space. |
aidan1971 | 10:49bb33f71d32 | 36 | volatile uint32_t * volatile SGTL5000::BufTX_R_safe; |
aidan1971 | 10:49bb33f71d32 | 37 | volatile uint32_t * volatile SGTL5000::BufRX_L_safe2; // Private pointers assigned to users data pointers |
aidan1971 | 10:49bb33f71d32 | 38 | volatile uint32_t * volatile SGTL5000::BufRX_R_safe2; // These are used to flip user pointers between safe 'active' regions of |
aidan1971 | 10:49bb33f71d32 | 39 | volatile uint32_t * volatile SGTL5000::BufTX_L_safe2; // double buffered space. |
aidan1971 | 10:49bb33f71d32 | 40 | volatile uint32_t * volatile SGTL5000::BufTX_R_safe2; |
aidan1971 | 10:49bb33f71d32 | 41 | |
aidan1971 | 10:49bb33f71d32 | 42 | uint32_t SGTL5000::BufRX_L_safeA; // Precalculated double buffer addresses |
aidan1971 | 10:49bb33f71d32 | 43 | uint32_t SGTL5000::BufRX_R_safeA; |
aidan1971 | 10:49bb33f71d32 | 44 | uint32_t SGTL5000::BufTX_L_safeA; |
aidan1971 | 10:49bb33f71d32 | 45 | uint32_t SGTL5000::BufTX_R_safeA; |
aidan1971 | 10:49bb33f71d32 | 46 | uint32_t SGTL5000::BufRX_L_safeB; // Precalculated double buffer addresses |
aidan1971 | 10:49bb33f71d32 | 47 | uint32_t SGTL5000::BufRX_R_safeB; |
aidan1971 | 10:49bb33f71d32 | 48 | uint32_t SGTL5000::BufTX_L_safeB; |
aidan1971 | 10:49bb33f71d32 | 49 | uint32_t SGTL5000::BufTX_R_safeB; |
aidan1971 | 10:49bb33f71d32 | 50 | uint32_t SGTL5000::BufRX_L_safeA2; // Precalculated double buffer addresses 2nd codec |
aidan1971 | 10:49bb33f71d32 | 51 | uint32_t SGTL5000::BufRX_R_safeA2; |
aidan1971 | 10:49bb33f71d32 | 52 | uint32_t SGTL5000::BufTX_L_safeA2; |
aidan1971 | 10:49bb33f71d32 | 53 | uint32_t SGTL5000::BufTX_R_safeA2; |
aidan1971 | 10:49bb33f71d32 | 54 | uint32_t SGTL5000::BufRX_L_safeB2; // Precalculated double buffer addresses 2nd codec |
aidan1971 | 10:49bb33f71d32 | 55 | uint32_t SGTL5000::BufRX_R_safeB2; |
aidan1971 | 10:49bb33f71d32 | 56 | uint32_t SGTL5000::BufTX_L_safeB2; |
aidan1971 | 10:49bb33f71d32 | 57 | uint32_t SGTL5000::BufTX_R_safeB2; |
aidan1971 | 10:49bb33f71d32 | 58 | |
aidan1971 | 10:49bb33f71d32 | 59 | uint32_t SGTL5000::I2S_RX_Buffer[16] = {0}; // Data buffers |
aidan1971 | 10:49bb33f71d32 | 60 | uint32_t SGTL5000::I2S_TX_Buffer[16] = {0}; |
aidan1971 | 10:49bb33f71d32 | 61 | uint32_t SGTL5000::I2S_RX_Buffer2[16] = {0}; |
aidan1971 | 10:49bb33f71d32 | 62 | uint32_t SGTL5000::I2S_TX_Buffer2[16] = {0}; |
aidan1971 | 6:4ab5aaeaa064 | 63 | IRQn SGTL5000::SYNC_swIRQ; // IRQn assigned by user to the software IRQ triggered by the FIFO queues. |
aidan1971 | 0:8f28f25e3435 | 64 | IRQn SGTL5000::TX_swIRQ; |
aidan1971 | 0:8f28f25e3435 | 65 | IRQn SGTL5000::RX_swIRQ; |
aidan1971 | 10:49bb33f71d32 | 66 | IRQn SGTL5000::CODEC_CTRL_IRQ; // Stream ctrl IRQ |
aidan1971 | 6:4ab5aaeaa064 | 67 | uint32_t SGTL5000::RX_DMAch; // User defined RX DMA channel number |
aidan1971 | 6:4ab5aaeaa064 | 68 | uint32_t SGTL5000::TX_DMAch; // User defined TX DMA channel number |
aidan1971 | 10:49bb33f71d32 | 69 | uint32_t SGTL5000::active_RX_DMAch_bm; |
aidan1971 | 10:49bb33f71d32 | 70 | uint32_t SGTL5000::active_TX_DMAch_bm; |
aidan1971 | 10:49bb33f71d32 | 71 | uint32_t SGTL5000::sync_dma_irq_acks = 0; |
aidan1971 | 0:8f28f25e3435 | 72 | Callback<void()> SGTL5000::TX_user_func = NULL; |
aidan1971 | 0:8f28f25e3435 | 73 | Callback<void()> SGTL5000::RX_user_func = NULL; |
aidan1971 | 6:4ab5aaeaa064 | 74 | Callback<void()> SGTL5000::SYNC_user_func = NULL; // User defined callback function |
aidan1971 | 9:40e0ff8c2ba2 | 75 | uint32_t SGTL5000::db_phase_sync; |
aidan1971 | 9:40e0ff8c2ba2 | 76 | uint32_t SGTL5000::db_phase_tx; |
aidan1971 | 9:40e0ff8c2ba2 | 77 | uint32_t SGTL5000::db_phase_rx; |
aidan1971 | 10:49bb33f71d32 | 78 | bool SGTL5000::SYNC_run; |
aidan1971 | 10:49bb33f71d32 | 79 | bool SGTL5000::TX_run; |
aidan1971 | 10:49bb33f71d32 | 80 | bool SGTL5000::RX_run; |
aidan1971 | 10:49bb33f71d32 | 81 | |
aidan1971 | 10:49bb33f71d32 | 82 | uint32_t SGTL5000::ctrl_command = 0; |
aidan1971 | 10:49bb33f71d32 | 83 | bool SGTL5000::i2s_configured = false; // Shared I2S state |
aidan1971 | 10:49bb33f71d32 | 84 | bool SGTL5000::codec2_active = false; // Which codecs are instantiated |
aidan1971 | 10:49bb33f71d32 | 85 | bool SGTL5000::codec1_active = false; |
aidan1971 | 10:49bb33f71d32 | 86 | uint32_t SGTL5000::SYNC_attach_type; |
aidan1971 | 10:49bb33f71d32 | 87 | uint32_t SGTL5000::TX_attach_type; |
aidan1971 | 10:49bb33f71d32 | 88 | uint32_t SGTL5000::RX_attach_type; |
aidan1971 | 0:8f28f25e3435 | 89 | |
aidan1971 | 6:4ab5aaeaa064 | 90 | uint32_t SGTL5000::debug[16] = {0}; |
aidan1971 | 0:8f28f25e3435 | 91 | |
aidan1971 | 10:49bb33f71d32 | 92 | |
aidan1971 | 10:49bb33f71d32 | 93 | uint32_t SGTL5000::t1; // Used to debug timing issues |
aidan1971 | 10:49bb33f71d32 | 94 | uint32_t SGTL5000::proc_time; |
aidan1971 | 10:49bb33f71d32 | 95 | uint32_t SGTL5000::SYST_CVAL = 0xE000E018; |
aidan1971 | 10:49bb33f71d32 | 96 | bool SGTL5000::debug_read = false; |
aidan1971 | 10:49bb33f71d32 | 97 | |
aidan1971 | 10:49bb33f71d32 | 98 | |
aidan1971 | 10:49bb33f71d32 | 99 | |
aidan1971 | 10:49bb33f71d32 | 100 | SGTL5000::SGTL5000(PinName i2c_sda, PinName i2c_scl, int _i2c_freq, bool i2c_ctrl_adr0_cs, IRQn _ctrl_IRQn) |
aidan1971 | 9:40e0ff8c2ba2 | 101 | : mI2C(i2c_sda, i2c_scl), i2c_freq(_i2c_freq) |
aidan1971 | 0:8f28f25e3435 | 102 | { |
aidan1971 | 10:49bb33f71d32 | 103 | if(i2c_ctrl_adr0_cs) { |
aidan1971 | 10:49bb33f71d32 | 104 | i2c_addr = SGTL5000_I2C_ADDR_CS_HIGH << 1; |
aidan1971 | 10:49bb33f71d32 | 105 | ctrl_codec = false; |
aidan1971 | 10:49bb33f71d32 | 106 | } else { |
aidan1971 | 10:49bb33f71d32 | 107 | i2c_addr = SGTL5000_I2C_ADDR_CS_LOW << 1; |
aidan1971 | 10:49bb33f71d32 | 108 | SGTL5000::CODEC_CTRL_IRQ = _ctrl_IRQn; |
aidan1971 | 10:49bb33f71d32 | 109 | NVIC_SetVector(SGTL5000::CODEC_CTRL_IRQ, (uint32_t)&SGTL5000::stream_ctrl_ISR); // Set ctrl command IRQ vector |
aidan1971 | 10:49bb33f71d32 | 110 | NVIC_SetPriority(SGTL5000::CODEC_CTRL_IRQ, 0); // Set irq priority |
aidan1971 | 10:49bb33f71d32 | 111 | NVIC_EnableIRQ(SGTL5000::CODEC_CTRL_IRQ); |
aidan1971 | 10:49bb33f71d32 | 112 | ctrl_codec = true; |
aidan1971 | 10:49bb33f71d32 | 113 | } |
aidan1971 | 8:9fdf8501d14b | 114 | codec_configured = false; |
aidan1971 | 10:49bb33f71d32 | 115 | SGTL5000::SYNC_run = false; |
aidan1971 | 10:49bb33f71d32 | 116 | SGTL5000::TX_run = false; |
aidan1971 | 10:49bb33f71d32 | 117 | SGTL5000::RX_run = false; |
aidan1971 | 0:8f28f25e3435 | 118 | SYNC_attached = false; |
aidan1971 | 0:8f28f25e3435 | 119 | TX_attached = false; |
aidan1971 | 0:8f28f25e3435 | 120 | RX_attached = false; |
aidan1971 | 0:8f28f25e3435 | 121 | } |
aidan1971 | 0:8f28f25e3435 | 122 | |
aidan1971 | 8:9fdf8501d14b | 123 | int32_t SGTL5000::init(void) |
aidan1971 | 8:9fdf8501d14b | 124 | { |
aidan1971 | 10:49bb33f71d32 | 125 | if(SGTL5000::codec1_active && SGTL5000::codec2_active) return -1; // We can only have 2 actively configured codec objects, if both are already active and configured bomb out. |
aidan1971 | 10:49bb33f71d32 | 126 | if((i2c_addr == (SGTL5000_I2C_ADDR_CS_LOW << 1)) && SGTL5000::codec1_active) return -2; |
aidan1971 | 10:49bb33f71d32 | 127 | if((i2c_addr == (SGTL5000_I2C_ADDR_CS_HIGH << 1)) && SGTL5000::codec2_active) return -3; |
aidan1971 | 10:49bb33f71d32 | 128 | if(this->ctrl_codec && !SGTL5000::i2s_configured) init_i2s(); |
aidan1971 | 9:40e0ff8c2ba2 | 129 | mI2C.frequency(i2c_freq); |
aidan1971 | 9:40e0ff8c2ba2 | 130 | int32_t error = init_codec(); |
aidan1971 | 9:40e0ff8c2ba2 | 131 | if(error) return error; |
aidan1971 | 8:9fdf8501d14b | 132 | else codec_configured = true; |
aidan1971 | 9:40e0ff8c2ba2 | 133 | |
aidan1971 | 8:9fdf8501d14b | 134 | return 0; |
aidan1971 | 8:9fdf8501d14b | 135 | } |
aidan1971 | 8:9fdf8501d14b | 136 | |
aidan1971 | 10:49bb33f71d32 | 137 | int32_t SGTL5000::sample_rate(uint32_t rate) |
aidan1971 | 0:8f28f25e3435 | 138 | { |
aidan1971 | 10:49bb33f71d32 | 139 | if(!codec_configured || !this->ctrl_codec) return -1; |
aidan1971 | 0:8f28f25e3435 | 140 | uint32_t I2S_MCLK_M; |
aidan1971 | 0:8f28f25e3435 | 141 | uint32_t I2S_MCLK_D; |
aidan1971 | 0:8f28f25e3435 | 142 | uint32_t codec_SYS_FS; |
aidan1971 | 0:8f28f25e3435 | 143 | uint32_t codec_RATE_MODE; |
aidan1971 | 0:8f28f25e3435 | 144 | switch(rate) { |
aidan1971 | 0:8f28f25e3435 | 145 | case 8: |
aidan1971 | 10:49bb33f71d32 | 146 | switch(SystemCoreClock) { |
aidan1971 | 10:49bb33f71d32 | 147 | case 96000000: |
aidan1971 | 10:49bb33f71d32 | 148 | I2S_MCLK_M = 16; |
aidan1971 | 10:49bb33f71d32 | 149 | I2S_MCLK_D = 750; |
aidan1971 | 10:49bb33f71d32 | 150 | codec_SYS_FS = 0x02; |
aidan1971 | 10:49bb33f71d32 | 151 | codec_RATE_MODE = 0x01; |
aidan1971 | 10:49bb33f71d32 | 152 | break; |
aidan1971 | 10:49bb33f71d32 | 153 | case 120000000: |
aidan1971 | 10:49bb33f71d32 | 154 | I2S_MCLK_M = 16; |
aidan1971 | 10:49bb33f71d32 | 155 | I2S_MCLK_D = 936; |
aidan1971 | 10:49bb33f71d32 | 156 | codec_SYS_FS = 0x02; |
aidan1971 | 10:49bb33f71d32 | 157 | codec_RATE_MODE = 0x01; |
aidan1971 | 10:49bb33f71d32 | 158 | break; |
aidan1971 | 10:49bb33f71d32 | 159 | } |
aidan1971 | 0:8f28f25e3435 | 160 | break; |
aidan1971 | 0:8f28f25e3435 | 161 | case 11: |
aidan1971 | 10:49bb33f71d32 | 162 | switch(SystemCoreClock) { |
aidan1971 | 10:49bb33f71d32 | 163 | case 96000000: |
aidan1971 | 10:49bb33f71d32 | 164 | I2S_MCLK_M = 1; |
aidan1971 | 10:49bb33f71d32 | 165 | I2S_MCLK_D = 34; |
aidan1971 | 10:49bb33f71d32 | 166 | codec_SYS_FS = 0x01; |
aidan1971 | 10:49bb33f71d32 | 167 | codec_RATE_MODE = 0x01; |
aidan1971 | 10:49bb33f71d32 | 168 | break; |
aidan1971 | 10:49bb33f71d32 | 169 | case 120000000: |
aidan1971 | 10:49bb33f71d32 | 170 | I2S_MCLK_M = 1; |
aidan1971 | 10:49bb33f71d32 | 171 | I2S_MCLK_D = 42; |
aidan1971 | 10:49bb33f71d32 | 172 | codec_SYS_FS = 0x01; |
aidan1971 | 10:49bb33f71d32 | 173 | codec_RATE_MODE = 0x01; |
aidan1971 | 10:49bb33f71d32 | 174 | break; |
aidan1971 | 10:49bb33f71d32 | 175 | } |
aidan1971 | 0:8f28f25e3435 | 176 | break; |
aidan1971 | 0:8f28f25e3435 | 177 | case 12: |
aidan1971 | 10:49bb33f71d32 | 178 | switch(SystemCoreClock) { |
aidan1971 | 10:49bb33f71d32 | 179 | case 96000000: |
aidan1971 | 10:49bb33f71d32 | 180 | I2S_MCLK_M = 16; |
aidan1971 | 10:49bb33f71d32 | 181 | I2S_MCLK_D = 500; |
aidan1971 | 10:49bb33f71d32 | 182 | codec_SYS_FS = 0x02; |
aidan1971 | 10:49bb33f71d32 | 183 | codec_RATE_MODE = 0x01; |
aidan1971 | 10:49bb33f71d32 | 184 | break; |
aidan1971 | 10:49bb33f71d32 | 185 | case 120000000: |
aidan1971 | 10:49bb33f71d32 | 186 | I2S_MCLK_M = 16; |
aidan1971 | 10:49bb33f71d32 | 187 | I2S_MCLK_D = 624; |
aidan1971 | 10:49bb33f71d32 | 188 | codec_SYS_FS = 0x02; |
aidan1971 | 10:49bb33f71d32 | 189 | codec_RATE_MODE = 0x01; |
aidan1971 | 10:49bb33f71d32 | 190 | break; |
aidan1971 | 10:49bb33f71d32 | 191 | } |
aidan1971 | 0:8f28f25e3435 | 192 | break; |
aidan1971 | 0:8f28f25e3435 | 193 | case 16: |
aidan1971 | 10:49bb33f71d32 | 194 | switch(SystemCoreClock) { |
aidan1971 | 10:49bb33f71d32 | 195 | case 96000000: |
aidan1971 | 10:49bb33f71d32 | 196 | I2S_MCLK_M = 32; |
aidan1971 | 10:49bb33f71d32 | 197 | I2S_MCLK_D = 750; |
aidan1971 | 10:49bb33f71d32 | 198 | codec_SYS_FS = 0x00; |
aidan1971 | 10:49bb33f71d32 | 199 | codec_RATE_MODE = 0x01; |
aidan1971 | 10:49bb33f71d32 | 200 | break; |
aidan1971 | 10:49bb33f71d32 | 201 | case 120000000: |
aidan1971 | 10:49bb33f71d32 | 202 | I2S_MCLK_M = 32; |
aidan1971 | 10:49bb33f71d32 | 203 | I2S_MCLK_D = 936; |
aidan1971 | 10:49bb33f71d32 | 204 | codec_SYS_FS = 0x00; |
aidan1971 | 10:49bb33f71d32 | 205 | codec_RATE_MODE = 0x01; |
aidan1971 | 10:49bb33f71d32 | 206 | break; |
aidan1971 | 10:49bb33f71d32 | 207 | } |
aidan1971 | 0:8f28f25e3435 | 208 | break; |
aidan1971 | 0:8f28f25e3435 | 209 | case 22: |
aidan1971 | 10:49bb33f71d32 | 210 | switch(SystemCoreClock) { |
aidan1971 | 10:49bb33f71d32 | 211 | case 96000000: |
aidan1971 | 10:49bb33f71d32 | 212 | I2S_MCLK_M = 1; |
aidan1971 | 10:49bb33f71d32 | 213 | I2S_MCLK_D = 17; |
aidan1971 | 10:49bb33f71d32 | 214 | codec_SYS_FS = 0x01; |
aidan1971 | 10:49bb33f71d32 | 215 | codec_RATE_MODE = 0x01; |
aidan1971 | 10:49bb33f71d32 | 216 | break; |
aidan1971 | 10:49bb33f71d32 | 217 | case 120000000: |
aidan1971 | 10:49bb33f71d32 | 218 | I2S_MCLK_M = 1; |
aidan1971 | 10:49bb33f71d32 | 219 | I2S_MCLK_D = 21; |
aidan1971 | 10:49bb33f71d32 | 220 | codec_SYS_FS = 0x01; |
aidan1971 | 10:49bb33f71d32 | 221 | codec_RATE_MODE = 0x01; |
aidan1971 | 10:49bb33f71d32 | 222 | break; |
aidan1971 | 10:49bb33f71d32 | 223 | } |
aidan1971 | 0:8f28f25e3435 | 224 | break; |
aidan1971 | 0:8f28f25e3435 | 225 | case 24: |
aidan1971 | 10:49bb33f71d32 | 226 | switch(SystemCoreClock) { |
aidan1971 | 10:49bb33f71d32 | 227 | case 96000000: |
aidan1971 | 10:49bb33f71d32 | 228 | I2S_MCLK_M = 32; |
aidan1971 | 10:49bb33f71d32 | 229 | I2S_MCLK_D = 500; |
aidan1971 | 10:49bb33f71d32 | 230 | codec_SYS_FS = 0x02; |
aidan1971 | 10:49bb33f71d32 | 231 | codec_RATE_MODE = 0x01; |
aidan1971 | 10:49bb33f71d32 | 232 | break; |
aidan1971 | 10:49bb33f71d32 | 233 | case 120000000: |
aidan1971 | 10:49bb33f71d32 | 234 | I2S_MCLK_M = 32; |
aidan1971 | 10:49bb33f71d32 | 235 | I2S_MCLK_D = 624; |
aidan1971 | 10:49bb33f71d32 | 236 | codec_SYS_FS = 0x02; |
aidan1971 | 10:49bb33f71d32 | 237 | codec_RATE_MODE = 0x01; |
aidan1971 | 10:49bb33f71d32 | 238 | break; |
aidan1971 | 10:49bb33f71d32 | 239 | } |
aidan1971 | 0:8f28f25e3435 | 240 | break; |
aidan1971 | 0:8f28f25e3435 | 241 | case 32: |
aidan1971 | 10:49bb33f71d32 | 242 | switch(SystemCoreClock) { |
aidan1971 | 10:49bb33f71d32 | 243 | case 96000000: |
aidan1971 | 10:49bb33f71d32 | 244 | I2S_MCLK_M = 64; |
aidan1971 | 10:49bb33f71d32 | 245 | I2S_MCLK_D = 750; |
aidan1971 | 10:49bb33f71d32 | 246 | codec_SYS_FS = 0x00; |
aidan1971 | 10:49bb33f71d32 | 247 | codec_RATE_MODE = 0x0; |
aidan1971 | 10:49bb33f71d32 | 248 | break; |
aidan1971 | 10:49bb33f71d32 | 249 | case 120000000: |
aidan1971 | 10:49bb33f71d32 | 250 | I2S_MCLK_M = 64; |
aidan1971 | 10:49bb33f71d32 | 251 | I2S_MCLK_D = 936; |
aidan1971 | 10:49bb33f71d32 | 252 | codec_SYS_FS = 0x00; |
aidan1971 | 10:49bb33f71d32 | 253 | codec_RATE_MODE = 0x0; |
aidan1971 | 10:49bb33f71d32 | 254 | break; |
aidan1971 | 10:49bb33f71d32 | 255 | } |
aidan1971 | 0:8f28f25e3435 | 256 | break; |
aidan1971 | 0:8f28f25e3435 | 257 | case 44: |
aidan1971 | 10:49bb33f71d32 | 258 | switch(SystemCoreClock) { |
aidan1971 | 10:49bb33f71d32 | 259 | case 96000000: |
aidan1971 | 10:49bb33f71d32 | 260 | I2S_MCLK_M = 2; |
aidan1971 | 10:49bb33f71d32 | 261 | I2S_MCLK_D = 17; |
aidan1971 | 10:49bb33f71d32 | 262 | codec_SYS_FS = 0x01; |
aidan1971 | 10:49bb33f71d32 | 263 | codec_RATE_MODE = 0x0; |
aidan1971 | 10:49bb33f71d32 | 264 | break; |
aidan1971 | 10:49bb33f71d32 | 265 | case 120000000: |
aidan1971 | 10:49bb33f71d32 | 266 | I2S_MCLK_M = 2; |
aidan1971 | 10:49bb33f71d32 | 267 | I2S_MCLK_D = 21; |
aidan1971 | 10:49bb33f71d32 | 268 | codec_SYS_FS = 0x01; |
aidan1971 | 10:49bb33f71d32 | 269 | codec_RATE_MODE = 0x0; |
aidan1971 | 10:49bb33f71d32 | 270 | break; |
aidan1971 | 10:49bb33f71d32 | 271 | } |
aidan1971 | 0:8f28f25e3435 | 272 | break; |
aidan1971 | 0:8f28f25e3435 | 273 | case 48: |
aidan1971 | 10:49bb33f71d32 | 274 | switch(SystemCoreClock) { |
aidan1971 | 10:49bb33f71d32 | 275 | case 96000000: |
aidan1971 | 10:49bb33f71d32 | 276 | I2S_MCLK_M = 32; |
aidan1971 | 10:49bb33f71d32 | 277 | I2S_MCLK_D = 250; |
aidan1971 | 10:49bb33f71d32 | 278 | codec_SYS_FS = 0x02; |
aidan1971 | 10:49bb33f71d32 | 279 | codec_RATE_MODE = 0x0; |
aidan1971 | 10:49bb33f71d32 | 280 | break; |
aidan1971 | 10:49bb33f71d32 | 281 | case 120000000: |
aidan1971 | 10:49bb33f71d32 | 282 | I2S_MCLK_M = 32; |
aidan1971 | 10:49bb33f71d32 | 283 | I2S_MCLK_D = 312; |
aidan1971 | 10:49bb33f71d32 | 284 | codec_SYS_FS = 0x02; |
aidan1971 | 10:49bb33f71d32 | 285 | codec_RATE_MODE = 0x0; |
aidan1971 | 10:49bb33f71d32 | 286 | break; |
aidan1971 | 10:49bb33f71d32 | 287 | } |
aidan1971 | 0:8f28f25e3435 | 288 | break; |
aidan1971 | 0:8f28f25e3435 | 289 | case 96: |
aidan1971 | 9:40e0ff8c2ba2 | 290 | switch(SystemCoreClock) { |
aidan1971 | 9:40e0ff8c2ba2 | 291 | case 96000000: |
aidan1971 | 9:40e0ff8c2ba2 | 292 | I2S_MCLK_M = 32; |
aidan1971 | 9:40e0ff8c2ba2 | 293 | I2S_MCLK_D = 125; |
aidan1971 | 9:40e0ff8c2ba2 | 294 | codec_SYS_FS = 0x03; |
aidan1971 | 9:40e0ff8c2ba2 | 295 | codec_RATE_MODE = 0x0; |
aidan1971 | 9:40e0ff8c2ba2 | 296 | break; |
aidan1971 | 9:40e0ff8c2ba2 | 297 | case 120000000: |
aidan1971 | 9:40e0ff8c2ba2 | 298 | I2S_MCLK_M = 32; |
aidan1971 | 9:40e0ff8c2ba2 | 299 | I2S_MCLK_D = 156; |
aidan1971 | 9:40e0ff8c2ba2 | 300 | codec_SYS_FS = 0x03; |
aidan1971 | 9:40e0ff8c2ba2 | 301 | codec_RATE_MODE = 0x0; |
aidan1971 | 9:40e0ff8c2ba2 | 302 | break; |
aidan1971 | 9:40e0ff8c2ba2 | 303 | } |
aidan1971 | 10:49bb33f71d32 | 304 | break; |
aidan1971 | 10:49bb33f71d32 | 305 | case 72: |
aidan1971 | 10:49bb33f71d32 | 306 | switch(SystemCoreClock) { |
aidan1971 | 10:49bb33f71d32 | 307 | case 96000000: |
aidan1971 | 10:49bb33f71d32 | 308 | I2S_MCLK_M = 24; |
aidan1971 | 10:49bb33f71d32 | 309 | I2S_MCLK_D = 125; |
aidan1971 | 10:49bb33f71d32 | 310 | codec_SYS_FS = 0x03; |
aidan1971 | 10:49bb33f71d32 | 311 | codec_RATE_MODE = 0x0; |
aidan1971 | 10:49bb33f71d32 | 312 | break; |
aidan1971 | 10:49bb33f71d32 | 313 | case 120000000: |
aidan1971 | 10:49bb33f71d32 | 314 | I2S_MCLK_M = 24; |
aidan1971 | 10:49bb33f71d32 | 315 | I2S_MCLK_D = 156; |
aidan1971 | 10:49bb33f71d32 | 316 | codec_SYS_FS = 0x03; |
aidan1971 | 10:49bb33f71d32 | 317 | codec_RATE_MODE = 0x0; |
aidan1971 | 10:49bb33f71d32 | 318 | break; |
aidan1971 | 10:49bb33f71d32 | 319 | } |
aidan1971 | 10:49bb33f71d32 | 320 | break; |
aidan1971 | 4:91354c908416 | 321 | case 144: |
aidan1971 | 6:4ab5aaeaa064 | 322 | switch(SystemCoreClock) { |
aidan1971 | 6:4ab5aaeaa064 | 323 | case 96000000: |
aidan1971 | 6:4ab5aaeaa064 | 324 | I2S_MCLK_M = 48; |
aidan1971 | 6:4ab5aaeaa064 | 325 | I2S_MCLK_D = 125; |
aidan1971 | 6:4ab5aaeaa064 | 326 | codec_SYS_FS = 0x03; |
aidan1971 | 6:4ab5aaeaa064 | 327 | codec_RATE_MODE = 0x0; |
aidan1971 | 6:4ab5aaeaa064 | 328 | break; |
aidan1971 | 6:4ab5aaeaa064 | 329 | case 120000000: |
aidan1971 | 6:4ab5aaeaa064 | 330 | I2S_MCLK_M = 48; |
aidan1971 | 6:4ab5aaeaa064 | 331 | I2S_MCLK_D = 156; |
aidan1971 | 6:4ab5aaeaa064 | 332 | codec_SYS_FS = 0x03; |
aidan1971 | 6:4ab5aaeaa064 | 333 | codec_RATE_MODE = 0x0; |
aidan1971 | 6:4ab5aaeaa064 | 334 | break; |
aidan1971 | 6:4ab5aaeaa064 | 335 | } |
aidan1971 | 4:91354c908416 | 336 | break; |
aidan1971 | 9:40e0ff8c2ba2 | 337 | case 192: |
aidan1971 | 9:40e0ff8c2ba2 | 338 | switch(SystemCoreClock) { |
aidan1971 | 9:40e0ff8c2ba2 | 339 | case 96000000: // Not officially supported by the codec, but it seems to work. |
aidan1971 | 9:40e0ff8c2ba2 | 340 | I2S_MCLK_M = 64; |
aidan1971 | 9:40e0ff8c2ba2 | 341 | I2S_MCLK_D = 125; |
aidan1971 | 9:40e0ff8c2ba2 | 342 | codec_SYS_FS = 0x03; |
aidan1971 | 9:40e0ff8c2ba2 | 343 | codec_RATE_MODE = 0x0; |
aidan1971 | 9:40e0ff8c2ba2 | 344 | break; |
aidan1971 | 9:40e0ff8c2ba2 | 345 | case 120000000: |
aidan1971 | 9:40e0ff8c2ba2 | 346 | I2S_MCLK_M = 64; |
aidan1971 | 9:40e0ff8c2ba2 | 347 | I2S_MCLK_D = 156; |
aidan1971 | 9:40e0ff8c2ba2 | 348 | codec_SYS_FS = 0x03; |
aidan1971 | 9:40e0ff8c2ba2 | 349 | codec_RATE_MODE = 0x0; |
aidan1971 | 9:40e0ff8c2ba2 | 350 | break; |
aidan1971 | 9:40e0ff8c2ba2 | 351 | } |
aidan1971 | 0:8f28f25e3435 | 352 | default: |
aidan1971 | 0:8f28f25e3435 | 353 | return -1; |
aidan1971 | 0:8f28f25e3435 | 354 | } |
aidan1971 | 0:8f28f25e3435 | 355 | if(modify_i2c(SGTL5000_CLK_CTRL, codec_SYS_FS, SGTL5000_CLK_CTRL_SYS_FS_MASK)) return -1; // Set CODEC clocking |
aidan1971 | 0:8f28f25e3435 | 356 | if(modify_i2c(SGTL5000_CLK_CTRL, codec_RATE_MODE, SGTL5000_CLK_CTRL_RATE_MODE_MASK)) return -1; // Set CODEC clocking |
aidan1971 | 10:49bb33f71d32 | 357 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 358 | if(modify_i2c(SGTL5000_CLK_CTRL, codec_SYS_FS, SGTL5000_CLK_CTRL_SYS_FS_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1; // Set 2nd CODEC clocking |
aidan1971 | 10:49bb33f71d32 | 359 | if(modify_i2c(SGTL5000_CLK_CTRL, codec_RATE_MODE, SGTL5000_CLK_CTRL_RATE_MODE_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1; // Set 2nd CODEC clocking |
aidan1971 | 10:49bb33f71d32 | 360 | } |
aidan1971 | 0:8f28f25e3435 | 361 | while(I2S0->MCR & I2S_MCR_DUF_MASK); // Check for active changes then set I2S Clock Fractions |
aidan1971 | 0:8f28f25e3435 | 362 | I2S0->MDR = (I2S0->MDR & ~(I2S_MDR_FRACT_MASK | I2S_MDR_DIVIDE_MASK)) | (I2S_MDR_FRACT(I2S_MCLK_M - 1) | I2S_MDR_DIVIDE(I2S_MCLK_D - 1)); |
aidan1971 | 0:8f28f25e3435 | 363 | return 0; |
aidan1971 | 0:8f28f25e3435 | 364 | } |
aidan1971 | 0:8f28f25e3435 | 365 | |
aidan1971 | 10:49bb33f71d32 | 366 | int32_t SGTL5000::read_i2c(uint32_t reg_addr, uint32_t mask, int _i2c_addr) |
aidan1971 | 0:8f28f25e3435 | 367 | { |
aidan1971 | 10:49bb33f71d32 | 368 | if(!_i2c_addr) _i2c_addr = i2c_addr; |
aidan1971 | 0:8f28f25e3435 | 369 | if(mask == 0x0 || mask > 0xFFFF) return -1; |
aidan1971 | 0:8f28f25e3435 | 370 | uint32_t shift; |
aidan1971 | 0:8f28f25e3435 | 371 | for (shift = 0; !(mask & (1<<shift)); ++shift) {}; |
aidan1971 | 0:8f28f25e3435 | 372 | uint32_t wire_data; |
aidan1971 | 0:8f28f25e3435 | 373 | uint32_t wire_addr = __rev(reg_addr) >> 16; |
aidan1971 | 7:d65476c153a4 | 374 | mI2C.lock(); |
aidan1971 | 10:49bb33f71d32 | 375 | if(mI2C.write(_i2c_addr, (char *)&wire_addr, 2, false)) { |
aidan1971 | 7:d65476c153a4 | 376 | mI2C.unlock(); |
aidan1971 | 7:d65476c153a4 | 377 | return -1; |
aidan1971 | 7:d65476c153a4 | 378 | } |
aidan1971 | 10:49bb33f71d32 | 379 | if(mI2C.read(_i2c_addr, (char *)&wire_data, 2, false)) { |
aidan1971 | 7:d65476c153a4 | 380 | mI2C.unlock(); |
aidan1971 | 7:d65476c153a4 | 381 | return -1; |
aidan1971 | 7:d65476c153a4 | 382 | } |
aidan1971 | 0:8f28f25e3435 | 383 | return ((__rev(wire_data) >> 16) & mask) >> shift; |
aidan1971 | 0:8f28f25e3435 | 384 | } |
aidan1971 | 0:8f28f25e3435 | 385 | |
aidan1971 | 10:49bb33f71d32 | 386 | int32_t SGTL5000::write_i2c(uint32_t reg_addr, uint32_t data, int _i2c_addr) |
aidan1971 | 0:8f28f25e3435 | 387 | { |
aidan1971 | 10:49bb33f71d32 | 388 | if(!_i2c_addr) _i2c_addr = i2c_addr; |
aidan1971 | 0:8f28f25e3435 | 389 | uint32_t wire_data = (__rev(reg_addr) >> 16) | (__rev(data)); |
aidan1971 | 7:d65476c153a4 | 390 | mI2C.lock(); |
aidan1971 | 10:49bb33f71d32 | 391 | if(mI2C.write(_i2c_addr, (char *)&wire_data, 4, false)) { |
aidan1971 | 7:d65476c153a4 | 392 | mI2C.unlock(); |
aidan1971 | 7:d65476c153a4 | 393 | return -1; |
aidan1971 | 7:d65476c153a4 | 394 | } |
aidan1971 | 0:8f28f25e3435 | 395 | return 0; |
aidan1971 | 0:8f28f25e3435 | 396 | } |
aidan1971 | 0:8f28f25e3435 | 397 | |
aidan1971 | 10:49bb33f71d32 | 398 | int32_t SGTL5000::modify_i2c(uint32_t reg_addr, uint32_t data, uint32_t mask, int _i2c_addr) |
aidan1971 | 0:8f28f25e3435 | 399 | { |
aidan1971 | 10:49bb33f71d32 | 400 | if(!_i2c_addr) _i2c_addr = i2c_addr; |
aidan1971 | 0:8f28f25e3435 | 401 | if(mask == 0x0 || mask > 0xFFFF) return -1; |
aidan1971 | 0:8f28f25e3435 | 402 | uint32_t shift; |
aidan1971 | 0:8f28f25e3435 | 403 | for (shift = 0; !(mask & (1<<shift)); ++shift) {}; |
aidan1971 | 0:8f28f25e3435 | 404 | uint32_t wire_data = (__rev(reg_addr) >> 16) | (__rev((read_i2c(reg_addr) & (~mask & 0xFFFF)) | ((data << shift) & mask))); |
aidan1971 | 7:d65476c153a4 | 405 | mI2C.lock(); |
aidan1971 | 10:49bb33f71d32 | 406 | if(mI2C.write(_i2c_addr, (char *)&wire_data, 4, false)) { |
aidan1971 | 7:d65476c153a4 | 407 | mI2C.unlock(); |
aidan1971 | 7:d65476c153a4 | 408 | return -1; |
aidan1971 | 7:d65476c153a4 | 409 | } |
aidan1971 | 0:8f28f25e3435 | 410 | return 0; |
aidan1971 | 0:8f28f25e3435 | 411 | } |
aidan1971 | 0:8f28f25e3435 | 412 | |
aidan1971 | 0:8f28f25e3435 | 413 | |
aidan1971 | 10:49bb33f71d32 | 414 | int32_t SGTL5000::init_i2s(void) |
aidan1971 | 0:8f28f25e3435 | 415 | { |
aidan1971 | 10:49bb33f71d32 | 416 | if(!this->ctrl_codec) return -1; |
aidan1971 | 10:49bb33f71d32 | 417 | |
aidan1971 | 10:49bb33f71d32 | 418 | // Set default rate at 48Khz |
aidan1971 | 10:49bb33f71d32 | 419 | uint32_t I2S_MCLK_MULT; |
aidan1971 | 10:49bb33f71d32 | 420 | uint32_t I2S_MCLK_DIV; |
aidan1971 | 10:49bb33f71d32 | 421 | switch(SystemCoreClock) { |
aidan1971 | 10:49bb33f71d32 | 422 | case 96000000: |
aidan1971 | 10:49bb33f71d32 | 423 | I2S_MCLK_MULT = 32; |
aidan1971 | 10:49bb33f71d32 | 424 | I2S_MCLK_DIV = 250; |
aidan1971 | 10:49bb33f71d32 | 425 | break; |
aidan1971 | 10:49bb33f71d32 | 426 | case 120000000: |
aidan1971 | 10:49bb33f71d32 | 427 | I2S_MCLK_MULT = 32; |
aidan1971 | 10:49bb33f71d32 | 428 | I2S_MCLK_DIV = 312; |
aidan1971 | 10:49bb33f71d32 | 429 | break; |
aidan1971 | 10:49bb33f71d32 | 430 | } |
aidan1971 | 0:8f28f25e3435 | 431 | |
aidan1971 | 0:8f28f25e3435 | 432 | // Configure System Clock Distribution for I2S |
aidan1971 | 0:8f28f25e3435 | 433 | SIM->SCGC5 |= SIM_SCGC5_PORTC_MASK; // Enable Clock to PORTC control module. |
aidan1971 | 0:8f28f25e3435 | 434 | SIM->SCGC6 |= SIM_SCGC6_I2S_MASK; // Enable Clocking to I2S Module |
aidan1971 | 0:8f28f25e3435 | 435 | SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Enable DMA Clock Gate Control |
aidan1971 | 0:8f28f25e3435 | 436 | SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK; // Enable DMA Mux Clock Gate Control |
aidan1971 | 0:8f28f25e3435 | 437 | |
aidan1971 | 0:8f28f25e3435 | 438 | // Configure I2S Master Clocking |
aidan1971 | 0:8f28f25e3435 | 439 | I2S0->MCR |= I2S_MCR_MICS(3); // Configure I2S MCLK Divder Source as System PLL |
aidan1971 | 0:8f28f25e3435 | 440 | while(I2S0->MCR & I2S_MCR_DUF_MASK); // Check for active changes then set I2S Clock Fractions |
aidan1971 | 0:8f28f25e3435 | 441 | I2S0->MDR = (I2S0->MDR & ~(I2S_MDR_FRACT_MASK | I2S_MDR_DIVIDE_MASK)) | (I2S_MDR_FRACT(I2S_MCLK_MULT - 1) | I2S_MDR_DIVIDE(I2S_MCLK_DIV - 1)); |
aidan1971 | 0:8f28f25e3435 | 442 | I2S0->MCR |= I2S_MCR_MOE_MASK; // Enable MCLK Output |
aidan1971 | 0:8f28f25e3435 | 443 | |
aidan1971 | 0:8f28f25e3435 | 444 | // Configure PIN Muxing |
aidan1971 | 0:8f28f25e3435 | 445 | |
aidan1971 | 0:8f28f25e3435 | 446 | PORTC->PCR[6] = (PORTC->PCR[6] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6); // Set PTC6(pin51)(Teensy pin11) <> I2S0_TX_MCLK |
aidan1971 | 0:8f28f25e3435 | 447 | PORTC->PCR[2] = (PORTC->PCR[2] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6); // Set PTC2(pin45)(Teensy pin23) <> I2S0_TX_FS(LRCLK) |
aidan1971 | 0:8f28f25e3435 | 448 | PORTC->PCR[3] = (PORTC->PCR[3] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6); // Set PTC3(pin46)(Teensy pin9) <> I2S0_TX_BCLK |
aidan1971 | 0:8f28f25e3435 | 449 | PORTC->PCR[1] = (PORTC->PCR[1] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6); // Set PTC1(pin44)(Teensy pin22) <> I2S0_TXD0 |
aidan1971 | 0:8f28f25e3435 | 450 | PORTC->PCR[5] = (PORTC->PCR[5] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(4); // Set PTC5(pin50)(Teensy pin13) <> I2S0_RXD0 |
aidan1971 | 10:49bb33f71d32 | 451 | PORTC->PCR[0] = (PORTC->PCR[0] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6); // Set PTC0(pin43)(Teensy pin15) <> I2S0_TXD1 |
aidan1971 | 10:49bb33f71d32 | 452 | PORTC->PCR[11] = (PORTC->PCR[11] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(4); // Set PTC11(pin56)(Teensy pin30) <> I2S0_RXD1 |
aidan1971 | 0:8f28f25e3435 | 453 | |
aidan1971 | 0:8f28f25e3435 | 454 | // Config. TX |
aidan1971 | 0:8f28f25e3435 | 455 | I2S0->TMR = 0; // I2S TX Mask cleared. |
aidan1971 | 0:8f28f25e3435 | 456 | I2S0->TCR2 = (I2S0->TCR2 & ~I2S_TCR2_SYNC_MASK) | I2S_TCR2_SYNC(0); // Set TX Mode to Async |
aidan1971 | 0:8f28f25e3435 | 457 | I2S0->TCR2 |= I2S_TCR2_BCP_MASK; // Set TX Bit Clock Polarity sample riseing edge |
aidan1971 | 0:8f28f25e3435 | 458 | I2S0->TCR2 = (I2S0->TCR2 & ~I2S_TCR2_MSEL_MASK) | I2S_TCR2_MSEL(1); // Set TX MCLK Audio Master Clock to Master Clock 1. |
aidan1971 | 0:8f28f25e3435 | 459 | I2S0->TCR2 |= I2S_TCR2_BCD_MASK; // Set TX Clock Dir. internally generated |
aidan1971 | 0:8f28f25e3435 | 460 | I2S0->TCR2 = (I2S0->TCR2 & ~I2S_TCR2_DIV_MASK) | I2S_TCR2_DIV(3); // Set TX Bit Clock Divide |
aidan1971 | 0:8f28f25e3435 | 461 | I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_WDFL_MASK) | I2S_TCR3_WDFL(0); // Set Word Flag Config to be 1st word in frame. |
aidan1971 | 0:8f28f25e3435 | 462 | I2S0->TCR4 = (I2S0->TCR4 & ~I2S_TCR4_FRSZ_MASK) | I2S_TCR4_FRSZ(1); // Set TX Frame Size @ 2 words per frame |
aidan1971 | 0:8f28f25e3435 | 463 | I2S0->TCR4 = (I2S0->TCR4 & ~I2S_TCR4_SYWD_MASK) | I2S_TCR4_SYWD(15); // Set TX frame sync width to 16bits |
aidan1971 | 0:8f28f25e3435 | 464 | I2S0->TCR4 |= I2S_TCR4_MF_MASK; // Set TX MSBit first |
aidan1971 | 0:8f28f25e3435 | 465 | I2S0->TCR4 |= I2S_TCR4_FSE_MASK; // Set TX Frame sync asserts before first bit |
aidan1971 | 0:8f28f25e3435 | 466 | I2S0->TCR4 |= I2S_TCR4_FSP_MASK; // Set TX Frame sync polarity active low. |
aidan1971 | 0:8f28f25e3435 | 467 | I2S0->TCR4 |= I2S_TCR4_FSD_MASK; // Set TX Frame sync direction from master clock |
aidan1971 | 0:8f28f25e3435 | 468 | I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_WNW_MASK) | I2S_TCR5_WNW(15); // Set TX to 16bits per word. |
aidan1971 | 0:8f28f25e3435 | 469 | I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_W0W_MASK) | I2S_TCR5_W0W(15); // Set TX to 16bits per first word. |
aidan1971 | 0:8f28f25e3435 | 470 | I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_FBT_MASK) | I2S_TCR5_FBT(15); // Set TX first bit transmitted index to 16th bit . |
aidan1971 | 0:8f28f25e3435 | 471 | |
aidan1971 | 0:8f28f25e3435 | 472 | //Config RX |
aidan1971 | 0:8f28f25e3435 | 473 | I2S0->RMR = 0; // I2S RX Mask cleared. |
aidan1971 | 0:8f28f25e3435 | 474 | I2S0->RCR2 = (I2S0->RCR2 & ~I2S_RCR2_SYNC_MASK) | I2S_RCR2_SYNC(1); // Set RX Mode to Sync with TX |
aidan1971 | 0:8f28f25e3435 | 475 | I2S0->RCR2 |= I2S_TCR2_BCP_MASK; // Set RX Bit Clock Polarity sample riseing edge |
aidan1971 | 0:8f28f25e3435 | 476 | I2S0->RCR2 = (I2S0->RCR2 & ~I2S_RCR2_MSEL_MASK) | I2S_RCR2_MSEL(1); // Set RX MCLK Audio Master Clock to Master Clock 1. |
aidan1971 | 0:8f28f25e3435 | 477 | I2S0->RCR2 |= I2S_RCR2_BCD_MASK; // Set RX Clock Dir. internally generated |
aidan1971 | 0:8f28f25e3435 | 478 | I2S0->RCR2 = (I2S0->RCR2 & ~I2S_RCR2_DIV_MASK) | I2S_RCR2_DIV(3); // Set RX Bit Clock Divide |
aidan1971 | 0:8f28f25e3435 | 479 | I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_WDFL_MASK) | I2S_RCR3_WDFL(0); // Set Word Flag Config to be 1st word in frame. |
aidan1971 | 0:8f28f25e3435 | 480 | I2S0->RCR4 = (I2S0->RCR4 & ~I2S_RCR4_FRSZ_MASK) | I2S_RCR4_FRSZ(1); // Set RX Frame Size @ 2 words per frame |
aidan1971 | 0:8f28f25e3435 | 481 | I2S0->RCR4 = (I2S0->RCR4 & ~I2S_RCR4_SYWD_MASK) | I2S_RCR4_SYWD(15); // Set RX frame sync width to 16bits |
aidan1971 | 0:8f28f25e3435 | 482 | I2S0->RCR4 |= I2S_RCR4_MF_MASK; // Set RX MSBit first |
aidan1971 | 0:8f28f25e3435 | 483 | I2S0->RCR4 |= I2S_RCR4_FSE_MASK; // Set RX Frame sync asserts before first bit |
aidan1971 | 0:8f28f25e3435 | 484 | I2S0->RCR4 |= I2S_RCR4_FSP_MASK; // Set RX Frame sync polarity active low. |
aidan1971 | 0:8f28f25e3435 | 485 | I2S0->RCR4 |= I2S_RCR4_FSD_MASK; // Set RX Frame sync direction from master clock |
aidan1971 | 0:8f28f25e3435 | 486 | I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_WNW_MASK) | I2S_RCR5_WNW(15); // Set RX to 16bits per word. |
aidan1971 | 0:8f28f25e3435 | 487 | I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_W0W_MASK) | I2S_RCR5_W0W(15); // Set RX to 16bits per first word. |
aidan1971 | 0:8f28f25e3435 | 488 | I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_FBT_MASK) | I2S_RCR5_FBT(15); // Set RX first bit transmitted index to 16 . |
aidan1971 | 0:8f28f25e3435 | 489 | |
aidan1971 | 0:8f28f25e3435 | 490 | // Configure I2S Peripheral |
aidan1971 | 0:8f28f25e3435 | 491 | I2S0->TCSR |= I2S_TCSR_BCE_MASK; // Enable I2S Tx bit clock |
aidan1971 | 0:8f28f25e3435 | 492 | I2S0->RCSR |= I2S_RCSR_BCE_MASK; // Enable I2S Rx Bit Clock |
aidan1971 | 0:8f28f25e3435 | 493 | |
aidan1971 | 10:49bb33f71d32 | 494 | SGTL5000::i2s_configured = true; |
aidan1971 | 10:49bb33f71d32 | 495 | return 0; |
aidan1971 | 0:8f28f25e3435 | 496 | } |
aidan1971 | 0:8f28f25e3435 | 497 | |
aidan1971 | 8:9fdf8501d14b | 498 | int32_t SGTL5000::init_codec(void) |
aidan1971 | 0:8f28f25e3435 | 499 | { |
aidan1971 | 0:8f28f25e3435 | 500 | // Default Configure Codec |
aidan1971 | 8:9fdf8501d14b | 501 | if( |
aidan1971 | 8:9fdf8501d14b | 502 | write_i2c(SGTL5000_ANA_POWER,( |
aidan1971 | 10:49bb33f71d32 | 503 | (SGTL5000_ANA_POWER_STARTUP_POWERUP_MASK & (0x1 << SGTL5000_ANA_POWER_STARTUP_POWERUP_SHIFT)) | // Startup power sequence and reset. |
aidan1971 | 10:49bb33f71d32 | 504 | (SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_MASK & (0x1 << SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_SHIFT)) // Enable simple linear REG |
aidan1971 | 10:49bb33f71d32 | 505 | )) |
aidan1971 | 10:49bb33f71d32 | 506 | ) return -1; |
aidan1971 | 10:49bb33f71d32 | 507 | wait_us(10000); |
aidan1971 | 10:49bb33f71d32 | 508 | if( // Wait 10mS for powerup and reset |
aidan1971 | 10:49bb33f71d32 | 509 | write_i2c(SGTL5000_ANA_POWER,( |
aidan1971 | 10:49bb33f71d32 | 510 | (SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_SHIFT)) | // Disable simple digital lineregulator. Teensy shield has external reg but sequence requires simple linereg on for reset and power-up |
aidan1971 | 10:49bb33f71d32 | 511 | (SGTL5000_ANA_POWER_STARTUP_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_STARTUP_POWERUP_SHIFT)) | // Disable reset and powerup circuits. This Can be cleared after powerup as VDDD is external on Teensy |
aidan1971 | 8:9fdf8501d14b | 512 | (SGTL5000_ANA_POWER_VDDC_CHRGPMP_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_VDDC_CHRGPMP_POWERUP_SHIFT)) | // Disable charge pump as VDDA & VDDIO > 3.1V |
aidan1971 | 8:9fdf8501d14b | 513 | (SGTL5000_ANA_POWER_PLL_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_PLL_POWERUP_SHIFT)) | // PLL disabled as codec clocking is synchronous to I2S clock. |
aidan1971 | 8:9fdf8501d14b | 514 | (SGTL5000_ANA_POWER_LINREG_D_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_LINREG_D_POWERUP_SHIFT)) | // Disable the VDDD line regulator as Teensy externally regulated at 1.85V |
aidan1971 | 8:9fdf8501d14b | 515 | (SGTL5000_ANA_POWER_VCOAMP_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_VCOAMP_POWERUP_SHIFT)) | // Disable the PLL VCO Amp, we don't use PLL |
aidan1971 | 8:9fdf8501d14b | 516 | (SGTL5000_ANA_POWER_VAG_POWERUP_MASK & (0x1 << SGTL5000_ANA_POWER_VAG_POWERUP_SHIFT)) | // Powerup VAG ref buffer. Should be enabled before powering up headphone and lineout and disabled before powering down HP and LINEOUT |
aidan1971 | 10:49bb33f71d32 | 517 | (SGTL5000_ANA_POWER_DAC_MONO_MASK & (0x1 << SGTL5000_ANA_POWER_DAC_MONO_SHIFT)) | // Select stereo mode for DAC |
aidan1971 | 8:9fdf8501d14b | 518 | (SGTL5000_ANA_POWER_ADC_MONO_MASK & (0x1 << SGTL5000_ANA_POWER_ADC_MONO_SHIFT)) | // ADC in stereo mode |
aidan1971 | 8:9fdf8501d14b | 519 | (SGTL5000_ANA_POWER_REFTOP_POWERUP_MASK & (0x1 << SGTL5000_ANA_POWER_REFTOP_POWERUP_SHIFT)) | // Enable bias currents |
aidan1971 | 8:9fdf8501d14b | 520 | (SGTL5000_ANA_POWER_HEADPHONE_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_HEADPHONE_POWERUP_SHIFT)) | // Disable HP amp until we set the reference levels |
aidan1971 | 8:9fdf8501d14b | 521 | (SGTL5000_ANA_POWER_DAC_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_DAC_POWERUP_SHIFT)) | // Disable DAC until we set refs |
aidan1971 | 8:9fdf8501d14b | 522 | (SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_SHIFT)) | // Disable capless headphones |
aidan1971 | 8:9fdf8501d14b | 523 | (SGTL5000_ANA_POWER_ADC_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_ADC_POWERUP_SHIFT)) | // Disable ADC power until we set refs |
aidan1971 | 8:9fdf8501d14b | 524 | (SGTL5000_ANA_POWER_LINEOUT_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_LINEOUT_POWERUP_SHIFT)) // Disable LINEOUT power until we set refs |
aidan1971 | 8:9fdf8501d14b | 525 | )) |
aidan1971 | 8:9fdf8501d14b | 526 | ) return -1; |
aidan1971 | 8:9fdf8501d14b | 527 | if( |
aidan1971 | 8:9fdf8501d14b | 528 | write_i2c(SGTL5000_LINREG_CTRL,( |
aidan1971 | 8:9fdf8501d14b | 529 | (SGTL5000_LINREG_CTRL_VDDC_MAN_ASSN_MASK & (0x0 << SGTL5000_LINREG_CTRL_VDDC_MAN_ASSN_SHIFT)) | // Manually assign charge pump rail (VDDC) source as VDDA. |
aidan1971 | 8:9fdf8501d14b | 530 | (SGTL5000_LINREG_CTRL_VDDC_ASSN_OVRD_MASK & (0x1 << SGTL5000_LINREG_CTRL_VDDC_ASSN_OVRD_SHIFT)) | // chargepump source manually assigned |
aidan1971 | 8:9fdf8501d14b | 531 | (SGTL5000_LINREG_CTRL_D_PROGRAMMING_MASK & (0xC << SGTL5000_LINREG_CTRL_D_PROGRAMMING_SHIFT)) // VDDD Line Reg not used so leave default |
aidan1971 | 8:9fdf8501d14b | 532 | )) |
aidan1971 | 9:40e0ff8c2ba2 | 533 | ) return -2; |
aidan1971 | 8:9fdf8501d14b | 534 | if( |
aidan1971 | 8:9fdf8501d14b | 535 | write_i2c(SGTL5000_REF_CTRL,( |
aidan1971 | 8:9fdf8501d14b | 536 | (SGTL5000_REF_CTRL_VAG_VAL_MASK & (0x1F << SGTL5000_REF_CTRL_VAG_VAL_SHIFT)) | // Set VAG ref to 1.575V |
aidan1971 | 8:9fdf8501d14b | 537 | (SGTL5000_REF_CTRL_BIAS_CTRL_MASK & (0x1 << SGTL5000_REF_CTRL_BIAS_CTRL_SHIFT)) | // Set analog bias currents to +12.5% |
aidan1971 | 8:9fdf8501d14b | 538 | (SGTL5000_REF_CTRL_SMALL_POP_MASK & (0x0 << SGTL5000_REF_CTRL_SMALL_POP_SHIFT)) // Set small pop ramp normal |
aidan1971 | 8:9fdf8501d14b | 539 | )) |
aidan1971 | 9:40e0ff8c2ba2 | 540 | ) return -3; |
aidan1971 | 8:9fdf8501d14b | 541 | if( |
aidan1971 | 8:9fdf8501d14b | 542 | write_i2c(SGTL5000_LINE_OUT_CTRL,( |
aidan1971 | 8:9fdf8501d14b | 543 | (SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_MASK & (0x3 << SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_SHIFT)) | // Set Lineout bias curent 0.36mA |
aidan1971 | 8:9fdf8501d14b | 544 | (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) |
aidan1971 | 8:9fdf8501d14b | 545 | )) |
aidan1971 | 9:40e0ff8c2ba2 | 546 | ) return -4; |
aidan1971 | 8:9fdf8501d14b | 547 | if( |
aidan1971 | 8:9fdf8501d14b | 548 | write_i2c(SGTL5000_SHORT_CTRL,( |
aidan1971 | 8:9fdf8501d14b | 549 | (SGTL5000_SHORT_CTRL_LVLADJR_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJR_SHIFT)) | // HP R short detect 125mA |
aidan1971 | 8:9fdf8501d14b | 550 | (SGTL5000_SHORT_CTRL_LVLADJL_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJL_SHIFT)) | // HP L short detect 125mA |
aidan1971 | 8:9fdf8501d14b | 551 | (SGTL5000_SHORT_CTRL_LVLADJC_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJC_SHIFT)) | // Headphone capless short detect 250mA |
aidan1971 | 8:9fdf8501d14b | 552 | (SGTL5000_SHORT_CTRL_MODE_LR_MASK & (0x1 << SGTL5000_SHORT_CTRL_MODE_LR_SHIFT)) | // en short det reset 50mS |
aidan1971 | 8:9fdf8501d14b | 553 | (SGTL5000_SHORT_CTRL_MODE_CM_MASK & (0x2 << SGTL5000_SHORT_CTRL_MODE_CM_SHIFT)) // En capless short det reset vol rise |
aidan1971 | 8:9fdf8501d14b | 554 | )) |
aidan1971 | 9:40e0ff8c2ba2 | 555 | ) return -5; |
aidan1971 | 8:9fdf8501d14b | 556 | if( |
aidan1971 | 8:9fdf8501d14b | 557 | write_i2c(SGTL5000_ANA_CTRL,( |
aidan1971 | 8:9fdf8501d14b | 558 | (SGTL5000_ANA_CTRL_MUTE_LO_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_LO_SHIFT)) | // Mute LINEOUT |
aidan1971 | 8:9fdf8501d14b | 559 | (SGTL5000_ANA_CTRL_SELECT_HP_MASK & (0x0 << SGTL5000_ANA_CTRL_SELECT_HP_SHIFT)) | // Select DAC as input to HP amp |
aidan1971 | 8:9fdf8501d14b | 560 | (SGTL5000_ANA_CTRL_EN_ZCD_HP_MASK & (0x1 << SGTL5000_ANA_CTRL_EN_ZCD_HP_SHIFT)) | // Enable ZCD on HP |
aidan1971 | 8:9fdf8501d14b | 561 | (SGTL5000_ANA_CTRL_MUTE_HP_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_HP_SHIFT)) | // Mute the headphones |
aidan1971 | 8:9fdf8501d14b | 562 | (SGTL5000_ANA_CTRL_SELECT_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_SELECT_ADC_SHIFT)) | // Select LINEIN as input to ADC |
aidan1971 | 8:9fdf8501d14b | 563 | (SGTL5000_ANA_CTRL_EN_ZCD_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_EN_ZCD_ADC_SHIFT)) | // Enable ADC ZCD |
aidan1971 | 8:9fdf8501d14b | 564 | (SGTL5000_ANA_CTRL_MUTE_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_ADC_SHIFT)) // Mute ADC pre-amp |
aidan1971 | 8:9fdf8501d14b | 565 | )) |
aidan1971 | 9:40e0ff8c2ba2 | 566 | ) return -6; |
aidan1971 | 9:40e0ff8c2ba2 | 567 | if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_HEADPHONE_POWERUP_MASK)) return -7; // Power up Headphone amp |
aidan1971 | 9:40e0ff8c2ba2 | 568 | if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_DAC_POWERUP_MASK)) return -8; // Power up DAC |
aidan1971 | 9:40e0ff8c2ba2 | 569 | if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_MASK)) return -9; // Power up capless HP block |
aidan1971 | 10:49bb33f71d32 | 570 | if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_ADC_POWERUP_MASK)) return -10; // Power up ADC |
aidan1971 | 10:49bb33f71d32 | 571 | if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_LINEOUT_POWERUP_MASK)) return -11; // Power up Lineout Amp |
aidan1971 | 10:49bb33f71d32 | 572 | if(modify_i2c(SGTL5000_ANA_POWER, 0x0, SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_MASK)) return -12; // Turn of simple regulator now the blocks are powered-up |
aidan1971 | 0:8f28f25e3435 | 573 | |
aidan1971 | 8:9fdf8501d14b | 574 | if( |
aidan1971 | 8:9fdf8501d14b | 575 | write_i2c(SGTL5000_DIG_POWER,( |
aidan1971 | 8:9fdf8501d14b | 576 | (SGTL5000_DIG_POWER_ADC_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_ADC_POWERUP_SHIFT)) | |
aidan1971 | 8:9fdf8501d14b | 577 | (SGTL5000_DIG_POWER_DAC_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_DAC_POWERUP_SHIFT)) | |
aidan1971 | 8:9fdf8501d14b | 578 | (SGTL5000_DIG_POWER_DAP_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_DAP_POWERUP_SHIFT)) | |
aidan1971 | 8:9fdf8501d14b | 579 | (SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_I2S_OUT_POWERUP_SHIFT)) | // Keep I2S data lines disabled until config complete |
aidan1971 | 8:9fdf8501d14b | 580 | (SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_I2S_IN_POWERUP_SHIFT)) |
aidan1971 | 8:9fdf8501d14b | 581 | )) |
aidan1971 | 9:40e0ff8c2ba2 | 582 | ) return -13; |
aidan1971 | 10:49bb33f71d32 | 583 | 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 |
aidan1971 | 10:49bb33f71d32 | 584 | (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 |
aidan1971 | 0:8f28f25e3435 | 585 | )); |
aidan1971 | 8:9fdf8501d14b | 586 | if( |
aidan1971 | 8:9fdf8501d14b | 587 | write_i2c(SGTL5000_PAD_STRENGTH,( |
aidan1971 | 8:9fdf8501d14b | 588 | (SGTL5000_PAD_STRENGTH_I2S_LRCLK_MASK & (0x0 << SGTL5000_PAD_STRENGTH_I2S_LRCLK_SHIFT)) | // I2S LR_CLK drive dissabled (codec is slave) |
aidan1971 | 8:9fdf8501d14b | 589 | (SGTL5000_PAD_STRENGTH_I2S_SCLK_MASK & (0x0 << SGTL5000_PAD_STRENGTH_I2S_SCLK_SHIFT)) | // I2S SCLK drive dissabled (codec is slave) |
aidan1971 | 8:9fdf8501d14b | 590 | (SGTL5000_PAD_STRENGTH_I2S_DOUT_MASK & (0x1 << SGTL5000_PAD_STRENGTH_I2S_DOUT_SHIFT)) | // I2S DOUT drive @ 4.02mA |
aidan1971 | 8:9fdf8501d14b | 591 | (SGTL5000_PAD_STRENGTH_CTRL_DATA_MASK & (0x3 << SGTL5000_PAD_STRENGTH_CTRL_DATA_SHIFT)) | // I2C DATA drive @ 12.05mA. |
aidan1971 | 8:9fdf8501d14b | 592 | (SGTL5000_PAD_STRENGTH_CTRL_CLK_MASK & (0x3 << SGTL5000_PAD_STRENGTH_CTRL_CLK_SHIFT)) // I2C CLOCK drive @ 12.05mA. |
aidan1971 | 8:9fdf8501d14b | 593 | )) |
aidan1971 | 9:40e0ff8c2ba2 | 594 | )return -14; |
aidan1971 | 8:9fdf8501d14b | 595 | if( |
aidan1971 | 8:9fdf8501d14b | 596 | write_i2c(SGTL5000_CLK_CTRL,( |
aidan1971 | 8:9fdf8501d14b | 597 | (SGTL5000_CLK_CTRL_RATE_MODE_MASK & (0x0 << SGTL5000_CLK_CTRL_RATE_MODE_SHIFT)) | // Sample rate mode Fs = SYS_FS |
aidan1971 | 8:9fdf8501d14b | 598 | (SGTL5000_CLK_CTRL_SYS_FS_MASK & (0x02 << SGTL5000_CLK_CTRL_SYS_FS_SHIFT)) | // Set SYS_FS (sampling rate depending on mode) @ 48Khz |
aidan1971 | 8:9fdf8501d14b | 599 | (SGTL5000_CLK_CTRL_MCLK_FREQ_MASK & (0x0 << SGTL5000_CLK_CTRL_MCLK_FREQ_SHIFT)) // Set SYS MCLK @ 256*Fs |
aidan1971 | 8:9fdf8501d14b | 600 | )) |
aidan1971 | 9:40e0ff8c2ba2 | 601 | ) return -15; |
aidan1971 | 8:9fdf8501d14b | 602 | if( |
aidan1971 | 8:9fdf8501d14b | 603 | write_i2c(SGTL5000_I2S_CTRL,( |
aidan1971 | 8:9fdf8501d14b | 604 | (SGTL5000_I2S_CTRL_SCLKFREQ_MASK & (0x1 << SGTL5000_I2S_CTRL_SCLKFREQ_SHIFT)) | // I2S SCLK 32*Fs |
aidan1971 | 8:9fdf8501d14b | 605 | (SGTL5000_I2S_CTRL_MS_MASK & (0x0 << SGTL5000_I2S_CTRL_MS_SHIFT)) | // Slave |
aidan1971 | 8:9fdf8501d14b | 606 | (SGTL5000_I2S_CTRL_SCLK_INV_MASK & (0x0 << SGTL5000_I2S_CTRL_SCLK_INV_SHIFT)) | // Data on riseing edge |
aidan1971 | 8:9fdf8501d14b | 607 | (SGTL5000_I2S_CTRL_DLEN_MASK & (0x3 << SGTL5000_I2S_CTRL_DLEN_SHIFT)) | // Data 16bits |
aidan1971 | 8:9fdf8501d14b | 608 | (SGTL5000_I2S_CTRL_MODE_MASK & (0x0 << SGTL5000_I2S_CTRL_MODE_SHIFT)) | // I2S mode |
aidan1971 | 8:9fdf8501d14b | 609 | (SGTL5000_I2S_CTRL_LRALIGN_MASK & (0x0 << SGTL5000_I2S_CTRL_LRALIGN_SHIFT)) | // I2S format |
aidan1971 | 8:9fdf8501d14b | 610 | (SGTL5000_I2S_CTRL_LRPOL_MASK & (0x0 << SGTL5000_I2S_CTRL_LRPOL_SHIFT)) // Left word on LRCLK low |
aidan1971 | 8:9fdf8501d14b | 611 | )) |
aidan1971 | 9:40e0ff8c2ba2 | 612 | ) return -16; |
aidan1971 | 8:9fdf8501d14b | 613 | if( |
aidan1971 | 8:9fdf8501d14b | 614 | write_i2c(SGTL5000_SSS_CTRL,( |
aidan1971 | 8:9fdf8501d14b | 615 | (SGTL5000_SSS_CTRL_DAP_MIX_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_MIX_LRSWAP_SHIFT)) | // Define all inputs un-swapped |
aidan1971 | 8:9fdf8501d14b | 616 | (SGTL5000_SSS_CTRL_DAP_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_LRSWAP_SHIFT)) | |
aidan1971 | 8:9fdf8501d14b | 617 | (SGTL5000_SSS_CTRL_DAC_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAC_LRSWAP_SHIFT)) | |
aidan1971 | 8:9fdf8501d14b | 618 | (SGTL5000_SSS_CTRL_I2S_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_I2S_LRSWAP_SHIFT)) | |
aidan1971 | 8:9fdf8501d14b | 619 | (SGTL5000_SSS_CTRL_DAP_MIX_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_MIX_SELECT_SHIFT)) | // DAP mixer source ADC |
aidan1971 | 8:9fdf8501d14b | 620 | (SGTL5000_SSS_CTRL_DAP_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_SELECT_SHIFT)) | // DAP Input source ADC |
aidan1971 | 8:9fdf8501d14b | 621 | (SGTL5000_SSS_CTRL_DAC_SELECT_MASK & (0x1 << SGTL5000_SSS_CTRL_DAC_SELECT_SHIFT)) | // DAC Input source I2SIN |
aidan1971 | 8:9fdf8501d14b | 622 | (SGTL5000_SSS_CTRL_I2S_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_I2S_SELECT_SHIFT)) // I2SOUT source ADC |
aidan1971 | 8:9fdf8501d14b | 623 | )) |
aidan1971 | 9:40e0ff8c2ba2 | 624 | ) return -17; |
aidan1971 | 8:9fdf8501d14b | 625 | if( |
aidan1971 | 8:9fdf8501d14b | 626 | write_i2c(SGTL5000_ADCDAC_CTRL,( |
aidan1971 | 8:9fdf8501d14b | 627 | (SGTL5000_ADCDAC_CTRL_VOL_RAMP_EN_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_VOL_RAMP_EN_SHIFT)) | // Disable Vol ramp |
aidan1971 | 8:9fdf8501d14b | 628 | (SGTL5000_ADCDAC_CTRL_VOL_EXPO_RAMP_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_VOL_EXPO_RAMP_SHIFT)) | |
aidan1971 | 8:9fdf8501d14b | 629 | (SGTL5000_ADCDAC_CTRL_DAC_MUTE_RIGHT_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_DAC_MUTE_RIGHT_SHIFT)) | |
aidan1971 | 8:9fdf8501d14b | 630 | (SGTL5000_ADCDAC_CTRL_DAC_MUTE_LEFT_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_DAC_MUTE_LEFT_SHIFT)) | |
aidan1971 | 8:9fdf8501d14b | 631 | (SGTL5000_ADCDAC_CTRL_ADC_HPF_FREEZE_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_ADC_HPF_FREEZE_SHIFT)) | // ADC HPF normal operation |
aidan1971 | 8:9fdf8501d14b | 632 | (SGTL5000_ADCDAC_CTRL_ADC_HPF_BYPASS_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_ADC_HPF_BYPASS_SHIFT)) // ADC HPF enabled |
aidan1971 | 8:9fdf8501d14b | 633 | )) |
aidan1971 | 9:40e0ff8c2ba2 | 634 | ) return -18; |
aidan1971 | 8:9fdf8501d14b | 635 | if( |
aidan1971 | 8:9fdf8501d14b | 636 | write_i2c(SGTL5000_DAC_VOL,( |
aidan1971 | 8:9fdf8501d14b | 637 | (SGTL5000_DAC_VOL_DAC_VOL_RIGHT_MASK & (0x3C << SGTL5000_DAC_VOL_DAC_VOL_RIGHT_SHIFT)) | // DAC R Vol 0dB |
aidan1971 | 8:9fdf8501d14b | 638 | (SGTL5000_DAC_VOL_DAC_VOL_LEFT_MASK & (0x3C << SGTL5000_DAC_VOL_DAC_VOL_LEFT_SHIFT)) // DAC L Vol 0dB |
aidan1971 | 8:9fdf8501d14b | 639 | )) |
aidan1971 | 9:40e0ff8c2ba2 | 640 | ) return -19; |
aidan1971 | 8:9fdf8501d14b | 641 | if( |
aidan1971 | 8:9fdf8501d14b | 642 | write_i2c(SGTL5000_ANA_HP_CTRL,( |
aidan1971 | 8:9fdf8501d14b | 643 | (SGTL5000_ANA_HP_CTRL_HP_VOL_RIGHT_MASK & (0x7F << SGTL5000_ANA_HP_CTRL_HP_VOL_RIGHT_SHIFT)) | // HP Vol set minimum |
aidan1971 | 8:9fdf8501d14b | 644 | (SGTL5000_ANA_HP_CTRL_HP_VOL_LEFT_MASK & (0x7F << SGTL5000_ANA_HP_CTRL_HP_VOL_LEFT_SHIFT)) |
aidan1971 | 8:9fdf8501d14b | 645 | )) |
aidan1971 | 9:40e0ff8c2ba2 | 646 | ) return -20; |
aidan1971 | 9:40e0ff8c2ba2 | 647 | if(modify_i2c(SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_LO_MASK)) return -21; // Un-mute Lineout |
aidan1971 | 9:40e0ff8c2ba2 | 648 | if(modify_i2c(SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_HP_MASK)) return -22; // Un-mute HP |
aidan1971 | 9:40e0ff8c2ba2 | 649 | if(modify_i2c(SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_ADC_MASK)) return -23; // Un-mute ADC pre-amp |
aidan1971 | 9:40e0ff8c2ba2 | 650 | if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -24; // I2S DOUT enable |
aidan1971 | 9:40e0ff8c2ba2 | 651 | if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK)) return -25; // I2S DIN enable |
aidan1971 | 10:49bb33f71d32 | 652 | |
aidan1971 | 10:49bb33f71d32 | 653 | if(this->i2c_addr == (SGTL5000_I2C_ADDR_CS_LOW << 1)) SGTL5000::codec1_active = true; |
aidan1971 | 10:49bb33f71d32 | 654 | if(this->i2c_addr == (SGTL5000_I2C_ADDR_CS_HIGH << 1)) SGTL5000::codec2_active = true; |
aidan1971 | 10:49bb33f71d32 | 655 | |
aidan1971 | 8:9fdf8501d14b | 656 | return 0; |
aidan1971 | 0:8f28f25e3435 | 657 | } |
aidan1971 | 0:8f28f25e3435 | 658 | |
aidan1971 | 0:8f28f25e3435 | 659 | |
aidan1971 | 0:8f28f25e3435 | 660 | int32_t SGTL5000::attach_SYNC(Callback<void()> func) |
aidan1971 | 0:8f28f25e3435 | 661 | { |
aidan1971 | 10:49bb33f71d32 | 662 | if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow for SYNC. |
aidan1971 | 0:8f28f25e3435 | 663 | if(SYNC_attached) return -1; // Assign Callback function |
aidan1971 | 0:8f28f25e3435 | 664 | SGTL5000::SYNC_user_func = func; |
aidan1971 | 10:49bb33f71d32 | 665 | SGTL5000::SYNC_attach_type = 0; |
aidan1971 | 0:8f28f25e3435 | 666 | SYNC_attached = true; |
aidan1971 | 0:8f28f25e3435 | 667 | return 0; |
aidan1971 | 0:8f28f25e3435 | 668 | } |
aidan1971 | 0:8f28f25e3435 | 669 | |
aidan1971 | 10:49bb33f71d32 | 670 | |
aidan1971 | 10:49bb33f71d32 | 671 | int32_t SGTL5000::attach_SYNC_NB(void* user_ISR, uint32_t irq_pri, IRQn sw_irq) |
aidan1971 | 0:8f28f25e3435 | 672 | { |
aidan1971 | 10:49bb33f71d32 | 673 | if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow. |
aidan1971 | 0:8f28f25e3435 | 674 | SGTL5000::SYNC_swIRQ = sw_irq; // Assign ISR address and enable users IRQ |
aidan1971 | 10:49bb33f71d32 | 675 | NVIC_SetVector(SGTL5000::SYNC_swIRQ, (uint32_t)user_ISR); |
aidan1971 | 0:8f28f25e3435 | 676 | NVIC_SetPriority(SGTL5000::SYNC_swIRQ, irq_pri); |
aidan1971 | 0:8f28f25e3435 | 677 | NVIC_EnableIRQ(SGTL5000::SYNC_swIRQ); |
aidan1971 | 10:49bb33f71d32 | 678 | SGTL5000::SYNC_attach_type = 1; |
aidan1971 | 0:8f28f25e3435 | 679 | SYNC_attached = true; |
aidan1971 | 8:9fdf8501d14b | 680 | return 0; |
aidan1971 | 0:8f28f25e3435 | 681 | } |
aidan1971 | 0:8f28f25e3435 | 682 | |
aidan1971 | 8:9fdf8501d14b | 683 | int32_t SGTL5000::detach_SYNC(void) |
aidan1971 | 0:8f28f25e3435 | 684 | { |
aidan1971 | 10:49bb33f71d32 | 685 | if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow. |
aidan1971 | 8:9fdf8501d14b | 686 | if(SYNC_attached) { |
aidan1971 | 10:49bb33f71d32 | 687 | if(SGTL5000::SYNC_attach_type) NVIC_DisableIRQ(SGTL5000::SYNC_swIRQ); // When using Interrupt driven NB calls, we disable the IRQ. The SYNC channel continues running, a different user function can be assigned with another call to the attach_NB function |
aidan1971 | 10:49bb33f71d32 | 688 | else SGTL5000::stop_SYNC(); // When using callback we must stop SYNC channel before we detach. |
aidan1971 | 8:9fdf8501d14b | 689 | SYNC_attached = false; |
aidan1971 | 8:9fdf8501d14b | 690 | } |
aidan1971 | 8:9fdf8501d14b | 691 | return 0; |
aidan1971 | 0:8f28f25e3435 | 692 | } |
aidan1971 | 0:8f28f25e3435 | 693 | |
aidan1971 | 10:49bb33f71d32 | 694 | void SGTL5000::stream_ctrl_ISR(void) |
aidan1971 | 0:8f28f25e3435 | 695 | { |
aidan1971 | 10:49bb33f71d32 | 696 | switch(SGTL5000::ctrl_command) { |
aidan1971 | 10:49bb33f71d32 | 697 | case STOP_SYNC: |
aidan1971 | 10:49bb33f71d32 | 698 | I2S0->TMR = 0xFFFFFFFF; // Mask TX channels to stop stream |
aidan1971 | 10:49bb33f71d32 | 699 | I2S0->RMR = 0xFFFFFFFF; // Mask RX channels to stop stream |
aidan1971 | 10:49bb33f71d32 | 700 | while(!(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete |
aidan1971 | 10:49bb33f71d32 | 701 | while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete |
aidan1971 | 10:49bb33f71d32 | 702 | I2S0->TCSR &= ~I2S_TCSR_FRDE_MASK; // Disable TX FIFO DMA request based on watermark |
aidan1971 | 10:49bb33f71d32 | 703 | I2S0->RCSR &= ~I2S_RCSR_FRDE_MASK; // Disable RX FIFO DMA request based on watermark |
aidan1971 | 10:49bb33f71d32 | 704 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 705 | I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(3); // Disable TX channels 0 & 1. |
aidan1971 | 10:49bb33f71d32 | 706 | I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(3); // Disable RX channels 0 & 1. |
aidan1971 | 10:49bb33f71d32 | 707 | } else { |
aidan1971 | 10:49bb33f71d32 | 708 | I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(1); // Disable TX channel 0. |
aidan1971 | 10:49bb33f71d32 | 709 | I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(1); // Disable RX channel 0 |
aidan1971 | 10:49bb33f71d32 | 710 | } |
aidan1971 | 10:49bb33f71d32 | 711 | I2S0->TCSR &= ~I2S_TCSR_TE_MASK; // TX Disable |
aidan1971 | 10:49bb33f71d32 | 712 | I2S0->RCSR &= ~I2S_RCSR_RE_MASK; // RX Disable |
aidan1971 | 10:49bb33f71d32 | 713 | while((I2S0->RCSR & I2S_RCSR_RE_MASK) || (I2S0->TCSR & I2S_TCSR_TE_MASK)); // Ensure TX & RX have stopped. |
aidan1971 | 10:49bb33f71d32 | 714 | SGTL5000::SYNC_run = false; |
aidan1971 | 10:49bb33f71d32 | 715 | break; |
aidan1971 | 10:49bb33f71d32 | 716 | case PAUSE_SYNC: |
aidan1971 | 10:49bb33f71d32 | 717 | I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag |
aidan1971 | 10:49bb33f71d32 | 718 | while(!(I2S0->RCSR & I2S_RCSR_WSF_MASK)); // wait for Frame Start |
aidan1971 | 10:49bb33f71d32 | 719 | I2S0->RMR = 0xFFFFFFFF; // Mask RX channels to halt stream |
aidan1971 | 10:49bb33f71d32 | 720 | I2S0->TMR = 0xFFFFFFFF; // Mask TX channels to halt stream |
aidan1971 | 10:49bb33f71d32 | 721 | while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK) || !(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete |
aidan1971 | 10:49bb33f71d32 | 722 | break; |
aidan1971 | 10:49bb33f71d32 | 723 | case RESUME_SYNC: |
aidan1971 | 10:49bb33f71d32 | 724 | I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag |
aidan1971 | 10:49bb33f71d32 | 725 | while(!(I2S0->RCSR & I2S_RCSR_WSF_MASK)); // wait for Frame Start |
aidan1971 | 10:49bb33f71d32 | 726 | I2S0->RMR = 0x0; // Unmask RX channels to resume stream |
aidan1971 | 10:49bb33f71d32 | 727 | I2S0->TMR = 0x0; // Unmask TX channels to resume stream |
aidan1971 | 10:49bb33f71d32 | 728 | break; |
aidan1971 | 10:49bb33f71d32 | 729 | case STOP_TX: |
aidan1971 | 10:49bb33f71d32 | 730 | I2S0->TMR = 0xFFFFFFFF; // Mask TX channels to halt stream |
aidan1971 | 10:49bb33f71d32 | 731 | while(!(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete |
aidan1971 | 10:49bb33f71d32 | 732 | I2S0->TCSR &= ~I2S_TCSR_FRDE_MASK; // Disable TX FIFO DMA request based on watermark |
aidan1971 | 10:49bb33f71d32 | 733 | if(SGTL5000::codec2_active) I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(3); // Disable TX channel 0 & 1. |
aidan1971 | 10:49bb33f71d32 | 734 | else (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(1); // Disable TX channel 0. |
aidan1971 | 10:49bb33f71d32 | 735 | I2S0->TCSR &= ~I2S_TCSR_TE_MASK; // TX Disable |
aidan1971 | 10:49bb33f71d32 | 736 | while(I2S0->TCSR & I2S_TCSR_TE_MASK); // Ensure TX has stopped. |
aidan1971 | 10:49bb33f71d32 | 737 | SGTL5000::TX_run = false; |
aidan1971 | 10:49bb33f71d32 | 738 | break; |
aidan1971 | 10:49bb33f71d32 | 739 | case PAUSE_TX: |
aidan1971 | 10:49bb33f71d32 | 740 | I2S0->TCSR |= I2S_TCSR_WSF_MASK; // Clear TX Word Start Flag |
aidan1971 | 10:49bb33f71d32 | 741 | while(!(I2S0->TCSR & I2S_TCSR_WSF_MASK)); // wait for Frame Start |
aidan1971 | 10:49bb33f71d32 | 742 | I2S0->TMR = 0xFFFFFFFF; // Mask TX channels to halt stream |
aidan1971 | 10:49bb33f71d32 | 743 | while(!(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete |
aidan1971 | 10:49bb33f71d32 | 744 | break; |
aidan1971 | 10:49bb33f71d32 | 745 | case RESUME_TX: |
aidan1971 | 10:49bb33f71d32 | 746 | I2S0->TCSR |= I2S_TCSR_WSF_MASK; // Clear TX Word Start Flag |
aidan1971 | 10:49bb33f71d32 | 747 | while(!(I2S0->TCSR & I2S_TCSR_WSF_MASK)); // wait for Frame Start |
aidan1971 | 10:49bb33f71d32 | 748 | I2S0->TMR = 0x0; // Clear TX mask to start stream |
aidan1971 | 10:49bb33f71d32 | 749 | break; |
aidan1971 | 10:49bb33f71d32 | 750 | case STOP_RX: |
aidan1971 | 10:49bb33f71d32 | 751 | I2S0->RMR = 0xFFFFFFFF; // Mask RX channels to halt stream |
aidan1971 | 10:49bb33f71d32 | 752 | while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete |
aidan1971 | 10:49bb33f71d32 | 753 | I2S0->RCSR &= ~I2S_RCSR_FRDE_MASK; // Disable RX FIFO DMA request based on watermark |
aidan1971 | 10:49bb33f71d32 | 754 | if(SGTL5000::codec2_active) I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(3); // Disable RX channel 0 & 1. |
aidan1971 | 10:49bb33f71d32 | 755 | else (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(1); // Disable RX channel 0. |
aidan1971 | 10:49bb33f71d32 | 756 | I2S0->RCSR &= ~I2S_RCSR_RE_MASK; // RX Disable |
aidan1971 | 10:49bb33f71d32 | 757 | while(I2S0->RCSR & I2S_RCSR_RE_MASK); // Ensure RX has stopped. |
aidan1971 | 10:49bb33f71d32 | 758 | SGTL5000::RX_run = false; |
aidan1971 | 10:49bb33f71d32 | 759 | break; |
aidan1971 | 10:49bb33f71d32 | 760 | case PAUSE_RX: |
aidan1971 | 10:49bb33f71d32 | 761 | I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag |
aidan1971 | 10:49bb33f71d32 | 762 | while(!(I2S0->RCSR & I2S_RCSR_WSF_MASK)); // wait for Frame Start |
aidan1971 | 10:49bb33f71d32 | 763 | I2S0->RMR = 0xFFFFFFFF; // Mask TX channels to halt stream |
aidan1971 | 10:49bb33f71d32 | 764 | while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete |
aidan1971 | 10:49bb33f71d32 | 765 | break; |
aidan1971 | 10:49bb33f71d32 | 766 | case RESUME_RX: |
aidan1971 | 10:49bb33f71d32 | 767 | I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag |
aidan1971 | 10:49bb33f71d32 | 768 | while(!(I2S0->RCSR & I2S_RCSR_WSF_MASK)); // wait for Frame Start |
aidan1971 | 10:49bb33f71d32 | 769 | I2S0->RMR = 0x0; // Unmask TX channels to resume stream |
aidan1971 | 10:49bb33f71d32 | 770 | break; |
aidan1971 | 10:49bb33f71d32 | 771 | } |
aidan1971 | 10:49bb33f71d32 | 772 | SGTL5000::ctrl_command = 0; |
aidan1971 | 10:49bb33f71d32 | 773 | } |
aidan1971 | 10:49bb33f71d32 | 774 | |
aidan1971 | 10:49bb33f71d32 | 775 | |
aidan1971 | 10:49bb33f71d32 | 776 | 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, |
aidan1971 | 10:49bb33f71d32 | 777 | 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, uint32_t _BufRX_L_safe2, uint32_t _BufRX_R_safe2, uint32_t _BufTX_L_safe2, uint32_t _BufTX_R_safe2) |
aidan1971 | 10:49bb33f71d32 | 778 | { |
aidan1971 | 10:49bb33f71d32 | 779 | if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 780 | if(SGTL5000::SYNC_run || SGTL5000::TX_run || SGTL5000::RX_run ) return -1; // Check if i2s is already started |
aidan1971 | 10:49bb33f71d32 | 781 | if(!SYNC_attached && !SGTL5000::SYNC_attach_type) return -1; // Check we have a handler if using callback |
aidan1971 | 0:8f28f25e3435 | 782 | if(_RX_DMAch > 15 || _TX_DMAch > 15) return -1; // Sanity check DMAMUX channels |
aidan1971 | 10:49bb33f71d32 | 783 | if (!(_block_size == 2 || _block_size == 4 || _block_size == 8)) return -1; // Only accept block size 2^n within range. |
aidan1971 | 10:49bb33f71d32 | 784 | // Enable I2S interfaces for active codecs |
aidan1971 | 10:49bb33f71d32 | 785 | if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1)) == 0) { |
aidan1971 | 10:49bb33f71d32 | 786 | if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1))) return -1; |
aidan1971 | 10:49bb33f71d32 | 787 | } |
aidan1971 | 10:49bb33f71d32 | 788 | if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1)) == 0) { |
aidan1971 | 10:49bb33f71d32 | 789 | if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1))) return -1; |
aidan1971 | 9:40e0ff8c2ba2 | 790 | } |
aidan1971 | 10:49bb33f71d32 | 791 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 792 | if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)) == 0) { |
aidan1971 | 10:49bb33f71d32 | 793 | if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1; |
aidan1971 | 10:49bb33f71d32 | 794 | } |
aidan1971 | 10:49bb33f71d32 | 795 | if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)) == 0) { |
aidan1971 | 10:49bb33f71d32 | 796 | if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1; |
aidan1971 | 10:49bb33f71d32 | 797 | } |
aidan1971 | 9:40e0ff8c2ba2 | 798 | } |
aidan1971 | 4:91354c908416 | 799 | packed_RX = _packed_RX; |
aidan1971 | 4:91354c908416 | 800 | packed_TX = _packed_TX; |
aidan1971 | 9:40e0ff8c2ba2 | 801 | TX_block_size = _block_size; |
aidan1971 | 6:4ab5aaeaa064 | 802 | RX_block_size = TX_block_size; |
aidan1971 | 9:40e0ff8c2ba2 | 803 | TX_bs_bytes = _block_size * 4; |
aidan1971 | 0:8f28f25e3435 | 804 | RX_bs_bytes = TX_bs_bytes; |
aidan1971 | 4:91354c908416 | 805 | if(packed_RX) RX_shift = false; |
aidan1971 | 4:91354c908416 | 806 | else RX_shift = _RX_shift; |
aidan1971 | 4:91354c908416 | 807 | if(packed_TX) TX_shift = false; |
aidan1971 | 4:91354c908416 | 808 | else TX_shift = _TX_shift; |
aidan1971 | 0:8f28f25e3435 | 809 | SGTL5000::RX_DMAch = _RX_DMAch; |
aidan1971 | 0:8f28f25e3435 | 810 | SGTL5000::TX_DMAch = _TX_DMAch; |
aidan1971 | 10:49bb33f71d32 | 811 | SGTL5000::active_RX_DMAch_bm = 0x1 << _RX_DMAch; |
aidan1971 | 10:49bb33f71d32 | 812 | SGTL5000::active_TX_DMAch_bm = 0x1 << _TX_DMAch; |
aidan1971 | 10:49bb33f71d32 | 813 | |
aidan1971 | 10:49bb33f71d32 | 814 | SGTL5000::BufRX_L_safe = (uint32_t*)_BufRX_L_safe; // Assign the users pointer addresses codec 1 |
aidan1971 | 10:49bb33f71d32 | 815 | SGTL5000::BufRX_R_safe = (uint32_t*)_BufRX_R_safe; |
aidan1971 | 10:49bb33f71d32 | 816 | SGTL5000::BufTX_L_safe = (uint32_t*)_BufTX_L_safe; |
aidan1971 | 10:49bb33f71d32 | 817 | SGTL5000::BufTX_R_safe = (uint32_t*)_BufTX_R_safe; |
aidan1971 | 10:49bb33f71d32 | 818 | SGTL5000::BufRX_L_safeA = (uint32_t)&SGTL5000::I2S_RX_Buffer[0]; // Precalculate double buffer addresses |
aidan1971 | 10:49bb33f71d32 | 819 | SGTL5000::BufRX_R_safeA = (uint32_t)&SGTL5000::I2S_RX_Buffer[0 + (RX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 820 | SGTL5000::BufTX_L_safeA = (uint32_t)&SGTL5000::I2S_TX_Buffer[0]; |
aidan1971 | 10:49bb33f71d32 | 821 | SGTL5000::BufTX_R_safeA = (uint32_t)&SGTL5000::I2S_TX_Buffer[0 + (TX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 822 | SGTL5000::BufRX_L_safeB = (uint32_t)&SGTL5000::I2S_RX_Buffer[8]; |
aidan1971 | 10:49bb33f71d32 | 823 | SGTL5000::BufRX_R_safeB = (uint32_t)&SGTL5000::I2S_RX_Buffer[8 + (RX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 824 | SGTL5000::BufTX_L_safeB = (uint32_t)&SGTL5000::I2S_TX_Buffer[8]; |
aidan1971 | 10:49bb33f71d32 | 825 | SGTL5000::BufTX_R_safeB = (uint32_t)&SGTL5000::I2S_TX_Buffer[8 + (TX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 826 | |
aidan1971 | 10:49bb33f71d32 | 827 | *SGTL5000::BufRX_L_safe = SGTL5000::BufRX_L_safeB; // Assign starting addresses to double buffer pointers |
aidan1971 | 10:49bb33f71d32 | 828 | *SGTL5000::BufRX_R_safe = SGTL5000::BufRX_R_safeB; |
aidan1971 | 10:49bb33f71d32 | 829 | *SGTL5000::BufTX_L_safe = SGTL5000::BufTX_L_safeB; |
aidan1971 | 10:49bb33f71d32 | 830 | *SGTL5000::BufTX_R_safe = SGTL5000::BufTX_L_safeB; |
aidan1971 | 0:8f28f25e3435 | 831 | |
aidan1971 | 10:49bb33f71d32 | 832 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 833 | SGTL5000::BufRX_L_safe2 = (uint32_t*)_BufRX_L_safe2; // Assign the users pointer addresses codec 2 |
aidan1971 | 10:49bb33f71d32 | 834 | SGTL5000::BufRX_R_safe2 = (uint32_t*)_BufRX_R_safe2; |
aidan1971 | 10:49bb33f71d32 | 835 | SGTL5000::BufTX_L_safe2 = (uint32_t*)_BufTX_L_safe2; |
aidan1971 | 10:49bb33f71d32 | 836 | SGTL5000::BufTX_R_safe2 = (uint32_t*)_BufTX_R_safe2; |
aidan1971 | 10:49bb33f71d32 | 837 | SGTL5000::BufRX_L_safeA2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0]; // Precalculate double buffer addresses |
aidan1971 | 10:49bb33f71d32 | 838 | SGTL5000::BufRX_R_safeA2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0 + (RX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 839 | SGTL5000::BufTX_L_safeA2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0]; |
aidan1971 | 10:49bb33f71d32 | 840 | SGTL5000::BufTX_R_safeA2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0 + (TX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 841 | SGTL5000::BufRX_L_safeB2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8]; |
aidan1971 | 10:49bb33f71d32 | 842 | SGTL5000::BufRX_R_safeB2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8 + (RX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 843 | SGTL5000::BufTX_L_safeB2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8]; |
aidan1971 | 10:49bb33f71d32 | 844 | SGTL5000::BufTX_R_safeB2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8 + (TX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 845 | *SGTL5000::BufRX_L_safe2 = SGTL5000::BufRX_L_safeB2; // Assign starting addresses to double buffer pointers |
aidan1971 | 10:49bb33f71d32 | 846 | *SGTL5000::BufRX_R_safe2 = SGTL5000::BufRX_R_safeB2; |
aidan1971 | 10:49bb33f71d32 | 847 | *SGTL5000::BufTX_L_safe2 = SGTL5000::BufTX_L_safeB2; |
aidan1971 | 10:49bb33f71d32 | 848 | *SGTL5000::BufTX_R_safe2 = SGTL5000::BufTX_L_safeB2; |
aidan1971 | 10:49bb33f71d32 | 849 | } |
aidan1971 | 10:49bb33f71d32 | 850 | |
aidan1971 | 10:49bb33f71d32 | 851 | I2S0->TCSR &= ~I2S_TCSR_TE_MASK; // TX Disable |
aidan1971 | 10:49bb33f71d32 | 852 | I2S0->RCSR &= ~I2S_RCSR_RE_MASK; // RX Disable |
aidan1971 | 10:49bb33f71d32 | 853 | I2S0->TMR = 0xFFFFFFFF; // Mask TX & RX traffic to prevent buffer under/overflow before we aquire sync. |
aidan1971 | 10:49bb33f71d32 | 854 | I2S0->RMR = 0xFFFFFFFF; |
aidan1971 | 9:40e0ff8c2ba2 | 855 | I2S0->TCSR |= I2S_TCSR_SR_MASK; // Reset TX & RX logic |
aidan1971 | 9:40e0ff8c2ba2 | 856 | I2S0->RCSR |= I2S_RCSR_SR_MASK; |
aidan1971 | 9:40e0ff8c2ba2 | 857 | I2S0->TCSR &= ~I2S_TCSR_SR_MASK; |
aidan1971 | 9:40e0ff8c2ba2 | 858 | I2S0->RCSR &= ~I2S_RCSR_SR_MASK; |
aidan1971 | 9:40e0ff8c2ba2 | 859 | I2S0->TCR1 = (I2S0->TCR1 & ~I2S_TCR1_TFW_MASK) | I2S_TCR1_TFW(8 - TX_block_size); // Set TX FIFO watermark |
aidan1971 | 9:40e0ff8c2ba2 | 860 | I2S0->RCR1 = (I2S0->RCR1 & ~I2S_RCR1_RFW_MASK) | I2S_RCR1_RFW(RX_block_size - 1); // Set RX FIFO watermark |
aidan1971 | 10:49bb33f71d32 | 861 | I2S0->TCSR |= I2S_TCSR_FRDE_MASK; // Enable DMA request based on TX FIFO watermark |
aidan1971 | 10:49bb33f71d32 | 862 | I2S0->RCSR |= I2S_RCSR_FRDE_MASK; // Enable DMA request based on RX FIFO watermark |
aidan1971 | 10:49bb33f71d32 | 863 | |
aidan1971 | 10:49bb33f71d32 | 864 | while((I2S0->RCSR & I2S_RCSR_RE_MASK) || (I2S0->TCSR & I2S_TCSR_TE_MASK)); // Make sure TX & RX are disabled before enabling corresponding channels |
aidan1971 | 9:40e0ff8c2ba2 | 865 | |
aidan1971 | 10:49bb33f71d32 | 866 | NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::SYNC_TX_dma_ISR); // Set TX_DMAch transfer complete vector |
aidan1971 | 10:49bb33f71d32 | 867 | NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::SYNC_RX_dma_ISR); // Set RX_DMAch transfer complete vector |
aidan1971 | 10:49bb33f71d32 | 868 | NVIC_SetPriority((IRQn)SGTL5000::TX_DMAch, _DMA_irq_pri); // Set irq priorities the same |
aidan1971 | 10:49bb33f71d32 | 869 | NVIC_SetPriority((IRQn)SGTL5000::RX_DMAch, _DMA_irq_pri); |
aidan1971 | 10:49bb33f71d32 | 870 | NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch); |
aidan1971 | 10:49bb33f71d32 | 871 | NVIC_EnableIRQ((IRQn)SGTL5000::RX_DMAch); |
aidan1971 | 10:49bb33f71d32 | 872 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 873 | I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(3); // Enable TX channels 0 & 1. |
aidan1971 | 10:49bb33f71d32 | 874 | I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(3); // Enable RX channels 0 & 1 |
aidan1971 | 6:4ab5aaeaa064 | 875 | } else { |
aidan1971 | 10:49bb33f71d32 | 876 | I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(1); // Enable TX channel 0. |
aidan1971 | 10:49bb33f71d32 | 877 | I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(1); // Enable RX channel 0 |
aidan1971 | 2:a9d8242f76ea | 878 | } |
aidan1971 | 9:40e0ff8c2ba2 | 879 | |
aidan1971 | 9:40e0ff8c2ba2 | 880 | NVIC_SetVector(I2S0_Rx_IRQn, (uint32_t)&SGTL5000::sync_I2S_WS_ISR); // Set vector for SYNC word start ISR |
aidan1971 | 2:a9d8242f76ea | 881 | NVIC_SetPriority(I2S0_Rx_IRQn, 0); // Set priority of SYNC word start ISR |
aidan1971 | 2:a9d8242f76ea | 882 | NVIC_EnableIRQ(I2S0_Rx_IRQn); // Enable SYNC word start ISR using RX direction |
aidan1971 | 9:40e0ff8c2ba2 | 883 | I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag |
aidan1971 | 9:40e0ff8c2ba2 | 884 | I2S0->RCSR |= I2S_RCSR_WSIE_MASK; // Enable I2S RX word start IRQ |
aidan1971 | 10:49bb33f71d32 | 885 | |
aidan1971 | 10:49bb33f71d32 | 886 | SGTL5000::db_phase_sync = 0; |
aidan1971 | 10:49bb33f71d32 | 887 | SGTL5000::SYNC_run = true; |
aidan1971 | 10:49bb33f71d32 | 888 | init_DMA(); |
aidan1971 | 10:49bb33f71d32 | 889 | I2S0->TCSR |= I2S_TCSR_TE_MASK; // Start TX |
aidan1971 | 10:49bb33f71d32 | 890 | I2S0->RCSR |= I2S_RCSR_RE_MASK; // Start RX |
aidan1971 | 10:49bb33f71d32 | 891 | return 0; |
aidan1971 | 10:49bb33f71d32 | 892 | } |
aidan1971 | 10:49bb33f71d32 | 893 | |
aidan1971 | 10:49bb33f71d32 | 894 | int32_t SGTL5000::pause_SYNC(void) |
aidan1971 | 10:49bb33f71d32 | 895 | { |
aidan1971 | 10:49bb33f71d32 | 896 | if(!codec_configured || !this->ctrl_codec || !SGTL5000::SYNC_run) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 897 | SGTL5000::ctrl_command = PAUSE_SYNC; |
aidan1971 | 10:49bb33f71d32 | 898 | NVIC->STIR = CODEC_CTRL_IRQ; |
aidan1971 | 10:49bb33f71d32 | 899 | while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ)); |
aidan1971 | 10:49bb33f71d32 | 900 | return 0; |
aidan1971 | 10:49bb33f71d32 | 901 | } |
aidan1971 | 10:49bb33f71d32 | 902 | |
aidan1971 | 10:49bb33f71d32 | 903 | int32_t SGTL5000::resume_SYNC(void) |
aidan1971 | 10:49bb33f71d32 | 904 | { |
aidan1971 | 10:49bb33f71d32 | 905 | if(!codec_configured || !this->ctrl_codec || !SGTL5000::SYNC_run) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 906 | SGTL5000::ctrl_command = RESUME_SYNC; |
aidan1971 | 10:49bb33f71d32 | 907 | NVIC->STIR = CODEC_CTRL_IRQ; |
aidan1971 | 10:49bb33f71d32 | 908 | while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ)); |
aidan1971 | 0:8f28f25e3435 | 909 | return 0; |
aidan1971 | 0:8f28f25e3435 | 910 | } |
aidan1971 | 0:8f28f25e3435 | 911 | |
aidan1971 | 8:9fdf8501d14b | 912 | int32_t SGTL5000::stop_SYNC(void) |
aidan1971 | 0:8f28f25e3435 | 913 | { |
aidan1971 | 10:49bb33f71d32 | 914 | if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 915 | modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK); // codec I2S data lines tristate |
aidan1971 | 10:49bb33f71d32 | 916 | modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK); |
aidan1971 | 10:49bb33f71d32 | 917 | if(SGTL5000::codec2_active) { // codec2 I2S data lines tristate |
aidan1971 | 10:49bb33f71d32 | 918 | modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)); |
aidan1971 | 10:49bb33f71d32 | 919 | modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)); |
aidan1971 | 10:49bb33f71d32 | 920 | } |
aidan1971 | 10:49bb33f71d32 | 921 | SGTL5000::ctrl_command = STOP_SYNC; |
aidan1971 | 10:49bb33f71d32 | 922 | NVIC->STIR = CODEC_CTRL_IRQ; |
aidan1971 | 10:49bb33f71d32 | 923 | while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ)); |
aidan1971 | 8:9fdf8501d14b | 924 | return 0; |
aidan1971 | 0:8f28f25e3435 | 925 | } |
aidan1971 | 0:8f28f25e3435 | 926 | |
aidan1971 | 9:40e0ff8c2ba2 | 927 | void SGTL5000::sync_I2S_WS_ISR(void) |
aidan1971 | 0:8f28f25e3435 | 928 | { |
aidan1971 | 9:40e0ff8c2ba2 | 929 | I2S0->RCSR &= ~I2S_RCSR_WSIE_MASK; // Disable RX word start IRQs |
aidan1971 | 0:8f28f25e3435 | 930 | I2S0->TCSR |= I2S_TCSR_FR_MASK; // Reset TX FIFO pointers |
aidan1971 | 0:8f28f25e3435 | 931 | I2S0->RCSR |= I2S_RCSR_FR_MASK; // Reset RX FIFO pointers |
aidan1971 | 10:49bb33f71d32 | 932 | |
aidan1971 | 10:49bb33f71d32 | 933 | for(uint32_t i = 0; i <= ((I2S0->RCR1 & I2S_RCR1_RFW_MASK) >> I2S_RCR1_RFW_SHIFT); ++i) { // Stuff the TX FIFO(s) with samples, to bring RX & TX FIFO demand requests to within 1 I2S frame length of each other. Hopefully giving max processing time for DSP work |
aidan1971 | 10:49bb33f71d32 | 934 | I2S0->TDR[0] = 0; |
aidan1971 | 10:49bb33f71d32 | 935 | } |
aidan1971 | 10:49bb33f71d32 | 936 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 937 | for(uint32_t i = 0; i <= ((I2S0->RCR1 & I2S_RCR1_RFW_MASK) >> I2S_RCR1_RFW_SHIFT); ++i) { |
aidan1971 | 10:49bb33f71d32 | 938 | I2S0->TDR[1] = 0; |
aidan1971 | 10:49bb33f71d32 | 939 | } |
aidan1971 | 10:49bb33f71d32 | 940 | } |
aidan1971 | 10:49bb33f71d32 | 941 | I2S0->TMR = 0x0; // Clear TX & RX Masks to start data flowing |
aidan1971 | 10:49bb33f71d32 | 942 | I2S0->RMR = 0x0; |
aidan1971 | 0:8f28f25e3435 | 943 | } |
aidan1971 | 0:8f28f25e3435 | 944 | |
aidan1971 | 10:49bb33f71d32 | 945 | void SGTL5000::SYNC_TX_dma_ISR(void) |
aidan1971 | 0:8f28f25e3435 | 946 | { |
aidan1971 | 10:49bb33f71d32 | 947 | SGTL5000::t_stamp_stop(); |
aidan1971 | 10:49bb33f71d32 | 948 | register uint32_t ACKS; |
aidan1971 | 10:49bb33f71d32 | 949 | register uint32_t TX_IRQ_BM; |
aidan1971 | 10:49bb33f71d32 | 950 | register uint32_t DMA_INT; |
aidan1971 | 10:49bb33f71d32 | 951 | __asm { |
aidan1971 | 10:49bb33f71d32 | 952 | LDR TX_IRQ_BM, [&SGTL5000::active_TX_DMAch_bm] |
aidan1971 | 10:49bb33f71d32 | 953 | LDR DMA_INT, [&DMA0->INT] |
aidan1971 | 10:49bb33f71d32 | 954 | LDR ACKS, [&sync_dma_irq_acks] |
aidan1971 | 10:49bb33f71d32 | 955 | |
aidan1971 | 10:49bb33f71d32 | 956 | ANDS TX_IRQ_BM, TX_IRQ_BM, DMA_INT |
aidan1971 | 10:49bb33f71d32 | 957 | ITT NE |
aidan1971 | 10:49bb33f71d32 | 958 | ORRNE ACKS, #1 |
aidan1971 | 10:49bb33f71d32 | 959 | STRNE TX_IRQ_BM, [&DMA0->INT] |
aidan1971 | 10:49bb33f71d32 | 960 | |
aidan1971 | 10:49bb33f71d32 | 961 | TEQ ACKS, #3 |
aidan1971 | 10:49bb33f71d32 | 962 | IT NE |
aidan1971 | 10:49bb33f71d32 | 963 | BNE finish_isr |
aidan1971 | 10:49bb33f71d32 | 964 | MOV ACKS, #0 |
aidan1971 | 10:49bb33f71d32 | 965 | BL SGTL5000::SYNC_pointer_swap |
aidan1971 | 10:49bb33f71d32 | 966 | |
aidan1971 | 10:49bb33f71d32 | 967 | finish_isr: |
aidan1971 | 10:49bb33f71d32 | 968 | STR ACKS, [&sync_dma_irq_acks] |
aidan1971 | 10:49bb33f71d32 | 969 | } |
aidan1971 | 10:49bb33f71d32 | 970 | } |
aidan1971 | 0:8f28f25e3435 | 971 | |
aidan1971 | 10:49bb33f71d32 | 972 | void SGTL5000::SYNC_RX_dma_ISR(void) |
aidan1971 | 10:49bb33f71d32 | 973 | { |
aidan1971 | 10:49bb33f71d32 | 974 | SGTL5000::t_stamp_start(); |
aidan1971 | 10:49bb33f71d32 | 975 | register uint32_t ACKS; |
aidan1971 | 10:49bb33f71d32 | 976 | register uint32_t RX_IRQ_BM; |
aidan1971 | 10:49bb33f71d32 | 977 | register uint32_t DMA_INT; |
aidan1971 | 10:49bb33f71d32 | 978 | __asm { |
aidan1971 | 10:49bb33f71d32 | 979 | LDR RX_IRQ_BM, [&SGTL5000::active_RX_DMAch_bm] |
aidan1971 | 10:49bb33f71d32 | 980 | LDR DMA_INT, [&DMA0->INT] |
aidan1971 | 10:49bb33f71d32 | 981 | LDR ACKS, [&sync_dma_irq_acks] |
aidan1971 | 10:49bb33f71d32 | 982 | |
aidan1971 | 10:49bb33f71d32 | 983 | ANDS RX_IRQ_BM, RX_IRQ_BM, DMA_INT |
aidan1971 | 10:49bb33f71d32 | 984 | ITT NE |
aidan1971 | 10:49bb33f71d32 | 985 | ORRNE ACKS, #2 |
aidan1971 | 10:49bb33f71d32 | 986 | STRNE RX_IRQ_BM, [&DMA0->INT] |
aidan1971 | 0:8f28f25e3435 | 987 | |
aidan1971 | 10:49bb33f71d32 | 988 | TEQ ACKS, #3 |
aidan1971 | 10:49bb33f71d32 | 989 | IT NE |
aidan1971 | 10:49bb33f71d32 | 990 | BNE finish_isr |
aidan1971 | 10:49bb33f71d32 | 991 | MOV ACKS, #0 |
aidan1971 | 10:49bb33f71d32 | 992 | BL SGTL5000::SYNC_pointer_swap |
aidan1971 | 0:8f28f25e3435 | 993 | |
aidan1971 | 10:49bb33f71d32 | 994 | finish_isr: |
aidan1971 | 10:49bb33f71d32 | 995 | STR ACKS, [&sync_dma_irq_acks] |
aidan1971 | 10:49bb33f71d32 | 996 | } |
aidan1971 | 10:49bb33f71d32 | 997 | } |
aidan1971 | 10:49bb33f71d32 | 998 | |
aidan1971 | 10:49bb33f71d32 | 999 | |
aidan1971 | 10:49bb33f71d32 | 1000 | void SGTL5000::SYNC_pointer_swap(void) |
aidan1971 | 10:49bb33f71d32 | 1001 | { |
aidan1971 | 10:49bb33f71d32 | 1002 | register uint32_t CODEC2; |
aidan1971 | 10:49bb33f71d32 | 1003 | register uint32_t IRQNR; |
aidan1971 | 10:49bb33f71d32 | 1004 | register uint32_t DSP_BUSY; |
aidan1971 | 10:49bb33f71d32 | 1005 | register uint32_t DB_PHASE; |
aidan1971 | 10:49bb33f71d32 | 1006 | register uint32_t TYPE; |
aidan1971 | 6:4ab5aaeaa064 | 1007 | register uint32_t BU_RX_L; |
aidan1971 | 6:4ab5aaeaa064 | 1008 | register uint32_t BU_RX_R; |
aidan1971 | 6:4ab5aaeaa064 | 1009 | register uint32_t BU_TX_L; |
aidan1971 | 6:4ab5aaeaa064 | 1010 | register uint32_t BU_TX_R; |
aidan1971 | 10:49bb33f71d32 | 1011 | register uint32_t BU_RX_L2; |
aidan1971 | 10:49bb33f71d32 | 1012 | register uint32_t BU_RX_R2; |
aidan1971 | 10:49bb33f71d32 | 1013 | register uint32_t BU_TX_L2; |
aidan1971 | 10:49bb33f71d32 | 1014 | register uint32_t BU_TX_R2; |
aidan1971 | 6:4ab5aaeaa064 | 1015 | __asm { |
aidan1971 | 9:40e0ff8c2ba2 | 1016 | LDR DB_PHASE, [&SGTL5000::db_phase_sync] |
aidan1971 | 10:49bb33f71d32 | 1017 | LDR TYPE, [&SGTL5000::SYNC_attach_type] |
aidan1971 | 10:49bb33f71d32 | 1018 | LDRB CODEC2, [&SGTL5000::codec2_active] |
aidan1971 | 10:49bb33f71d32 | 1019 | TEQ CODEC2, #0x1 |
aidan1971 | 10:49bb33f71d32 | 1020 | IT EQ |
aidan1971 | 10:49bb33f71d32 | 1021 | BEQ dual_codecs |
aidan1971 | 6:4ab5aaeaa064 | 1022 | |
aidan1971 | 6:4ab5aaeaa064 | 1023 | TEQ DB_PHASE, #0x0 |
aidan1971 | 6:4ab5aaeaa064 | 1024 | IT EQ |
aidan1971 | 6:4ab5aaeaa064 | 1025 | BEQ buf_base |
aidan1971 | 6:4ab5aaeaa064 | 1026 | |
aidan1971 | 10:49bb33f71d32 | 1027 | LDR BU_RX_L, [&SGTL5000::BufRX_L_safeB] |
aidan1971 | 10:49bb33f71d32 | 1028 | LDR BU_RX_R, [&SGTL5000::BufRX_R_safeB] |
aidan1971 | 10:49bb33f71d32 | 1029 | LDR BU_TX_L, [&SGTL5000::BufTX_L_safeB] |
aidan1971 | 10:49bb33f71d32 | 1030 | LDR BU_TX_R, [&SGTL5000::BufTX_R_safeB] |
aidan1971 | 6:4ab5aaeaa064 | 1031 | SUB DB_PHASE, #0x1 |
aidan1971 | 6:4ab5aaeaa064 | 1032 | B store |
aidan1971 | 6:4ab5aaeaa064 | 1033 | |
aidan1971 | 6:4ab5aaeaa064 | 1034 | buf_base: |
aidan1971 | 10:49bb33f71d32 | 1035 | LDR BU_RX_L, [&SGTL5000::BufRX_L_safeA] |
aidan1971 | 10:49bb33f71d32 | 1036 | LDR BU_RX_R, [&SGTL5000::BufRX_R_safeA] |
aidan1971 | 10:49bb33f71d32 | 1037 | LDR BU_TX_L, [&SGTL5000::BufTX_L_safeA] |
aidan1971 | 10:49bb33f71d32 | 1038 | LDR BU_TX_R, [&SGTL5000::BufTX_R_safeA] |
aidan1971 | 6:4ab5aaeaa064 | 1039 | ADD DB_PHASE, #0x1 |
aidan1971 | 6:4ab5aaeaa064 | 1040 | |
aidan1971 | 6:4ab5aaeaa064 | 1041 | store: |
aidan1971 | 6:4ab5aaeaa064 | 1042 | STR BU_RX_L, [SGTL5000::BufRX_L_safe] // Pipeline memory access |
aidan1971 | 6:4ab5aaeaa064 | 1043 | STR BU_RX_R, [SGTL5000::BufRX_R_safe] |
aidan1971 | 6:4ab5aaeaa064 | 1044 | STR BU_TX_L, [SGTL5000::BufTX_L_safe] |
aidan1971 | 6:4ab5aaeaa064 | 1045 | STR BU_TX_R, [SGTL5000::BufTX_R_safe] |
aidan1971 | 9:40e0ff8c2ba2 | 1046 | STR DB_PHASE, [&SGTL5000::db_phase_sync] |
aidan1971 | 10:49bb33f71d32 | 1047 | B finish_update |
aidan1971 | 10:49bb33f71d32 | 1048 | |
aidan1971 | 10:49bb33f71d32 | 1049 | |
aidan1971 | 10:49bb33f71d32 | 1050 | dual_codecs: |
aidan1971 | 10:49bb33f71d32 | 1051 | TEQ DB_PHASE, #0x0 |
aidan1971 | 10:49bb33f71d32 | 1052 | IT EQ |
aidan1971 | 10:49bb33f71d32 | 1053 | BEQ buf_base2 |
aidan1971 | 10:49bb33f71d32 | 1054 | |
aidan1971 | 10:49bb33f71d32 | 1055 | LDR BU_RX_L, [&SGTL5000::BufRX_L_safeB] |
aidan1971 | 10:49bb33f71d32 | 1056 | LDR BU_RX_R, [&SGTL5000::BufRX_R_safeB] |
aidan1971 | 10:49bb33f71d32 | 1057 | LDR BU_TX_L, [&SGTL5000::BufTX_L_safeB] |
aidan1971 | 10:49bb33f71d32 | 1058 | LDR BU_TX_R, [&SGTL5000::BufTX_R_safeB] |
aidan1971 | 10:49bb33f71d32 | 1059 | LDR BU_RX_L2, [&SGTL5000::BufRX_L_safeB2] |
aidan1971 | 10:49bb33f71d32 | 1060 | LDR BU_RX_R2, [&SGTL5000::BufRX_R_safeB2] |
aidan1971 | 10:49bb33f71d32 | 1061 | LDR BU_TX_L2, [&SGTL5000::BufTX_L_safeB2] |
aidan1971 | 10:49bb33f71d32 | 1062 | LDR BU_TX_R2, [&SGTL5000::BufTX_R_safeB2] |
aidan1971 | 10:49bb33f71d32 | 1063 | SUB DB_PHASE, #0x1 |
aidan1971 | 10:49bb33f71d32 | 1064 | B store2 |
aidan1971 | 10:49bb33f71d32 | 1065 | |
aidan1971 | 10:49bb33f71d32 | 1066 | buf_base2: |
aidan1971 | 10:49bb33f71d32 | 1067 | LDR BU_RX_L, [&SGTL5000::BufRX_L_safeA] |
aidan1971 | 10:49bb33f71d32 | 1068 | LDR BU_RX_R, [&SGTL5000::BufRX_R_safeA] |
aidan1971 | 10:49bb33f71d32 | 1069 | LDR BU_TX_L, [&SGTL5000::BufTX_L_safeA] |
aidan1971 | 10:49bb33f71d32 | 1070 | LDR BU_TX_R, [&SGTL5000::BufTX_R_safeA] |
aidan1971 | 10:49bb33f71d32 | 1071 | LDR BU_RX_L2, [&SGTL5000::BufRX_L_safeA2] |
aidan1971 | 10:49bb33f71d32 | 1072 | LDR BU_RX_R2, [&SGTL5000::BufRX_R_safeA2] |
aidan1971 | 10:49bb33f71d32 | 1073 | LDR BU_TX_L2, [&SGTL5000::BufTX_L_safeA2] |
aidan1971 | 10:49bb33f71d32 | 1074 | LDR BU_TX_R2, [&SGTL5000::BufTX_R_safeA2] |
aidan1971 | 10:49bb33f71d32 | 1075 | ADD DB_PHASE, #0x1 |
aidan1971 | 6:4ab5aaeaa064 | 1076 | |
aidan1971 | 10:49bb33f71d32 | 1077 | store2: |
aidan1971 | 10:49bb33f71d32 | 1078 | STR BU_RX_L, [SGTL5000::BufRX_L_safe] // Pipeline memory access |
aidan1971 | 10:49bb33f71d32 | 1079 | STR BU_RX_R, [SGTL5000::BufRX_R_safe] |
aidan1971 | 10:49bb33f71d32 | 1080 | STR BU_TX_L, [SGTL5000::BufTX_L_safe] |
aidan1971 | 10:49bb33f71d32 | 1081 | STR BU_TX_R, [SGTL5000::BufTX_R_safe] |
aidan1971 | 10:49bb33f71d32 | 1082 | STR BU_RX_L2, [SGTL5000::BufRX_L_safe2] // Pipeline memory access |
aidan1971 | 10:49bb33f71d32 | 1083 | STR BU_RX_R2, [SGTL5000::BufRX_R_safe2] |
aidan1971 | 10:49bb33f71d32 | 1084 | STR BU_TX_L2, [SGTL5000::BufTX_L_safe2] |
aidan1971 | 10:49bb33f71d32 | 1085 | STR BU_TX_R2, [SGTL5000::BufTX_R_safe2] |
aidan1971 | 10:49bb33f71d32 | 1086 | STR DB_PHASE, [&SGTL5000::db_phase_sync] |
aidan1971 | 6:4ab5aaeaa064 | 1087 | |
aidan1971 | 10:49bb33f71d32 | 1088 | finish_update: |
aidan1971 | 10:49bb33f71d32 | 1089 | TEQ TYPE, #1 |
aidan1971 | 10:49bb33f71d32 | 1090 | IT EQ |
aidan1971 | 10:49bb33f71d32 | 1091 | BEQ call_sync_nb |
aidan1971 | 10:49bb33f71d32 | 1092 | } |
aidan1971 | 10:49bb33f71d32 | 1093 | SGTL5000::SYNC_user_func.call(); |
aidan1971 | 10:49bb33f71d32 | 1094 | __asm { |
aidan1971 | 10:49bb33f71d32 | 1095 | B finish_isr |
aidan1971 | 10:49bb33f71d32 | 1096 | |
aidan1971 | 10:49bb33f71d32 | 1097 | call_sync_nb: |
aidan1971 | 10:49bb33f71d32 | 1098 | LDRB IRQNR, [&SGTL5000::SYNC_swIRQ] |
aidan1971 | 10:49bb33f71d32 | 1099 | BL NVIC_GetActive, {r0=IRQNR}, {DSP_BUSY=r0} |
aidan1971 | 10:49bb33f71d32 | 1100 | TEQ DSP_BUSY, #1 |
aidan1971 | 10:49bb33f71d32 | 1101 | IT EQ |
aidan1971 | 10:49bb33f71d32 | 1102 | BEQ finish_isr |
aidan1971 | 10:49bb33f71d32 | 1103 | STR IRQNR, [&NVIC->STIR] // Trigger swIRQ only if previous call completed. This will keep things flowing, but if the irq is still active, good luck with the pointers! |
aidan1971 | 10:49bb33f71d32 | 1104 | |
aidan1971 | 10:49bb33f71d32 | 1105 | finish_isr: |
aidan1971 | 10:49bb33f71d32 | 1106 | |
aidan1971 | 10:49bb33f71d32 | 1107 | } |
aidan1971 | 0:8f28f25e3435 | 1108 | } |
aidan1971 | 0:8f28f25e3435 | 1109 | |
aidan1971 | 0:8f28f25e3435 | 1110 | |
aidan1971 | 0:8f28f25e3435 | 1111 | int32_t SGTL5000::attach_TX(Callback<void()> func) |
aidan1971 | 0:8f28f25e3435 | 1112 | { |
aidan1971 | 10:49bb33f71d32 | 1113 | if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow for SYNC. |
aidan1971 | 10:49bb33f71d32 | 1114 | if(TX_attached) return -1; // Assign Callback function |
aidan1971 | 0:8f28f25e3435 | 1115 | SGTL5000::TX_user_func = func; |
aidan1971 | 10:49bb33f71d32 | 1116 | SGTL5000::TX_attach_type = 0; |
aidan1971 | 0:8f28f25e3435 | 1117 | TX_attached = true; |
aidan1971 | 0:8f28f25e3435 | 1118 | return 0; |
aidan1971 | 0:8f28f25e3435 | 1119 | } |
aidan1971 | 0:8f28f25e3435 | 1120 | |
aidan1971 | 10:49bb33f71d32 | 1121 | |
aidan1971 | 10:49bb33f71d32 | 1122 | int32_t SGTL5000::attach_TX_NB(void* user_ISR, uint32_t irq_pri, IRQn sw_irq) |
aidan1971 | 0:8f28f25e3435 | 1123 | { |
aidan1971 | 10:49bb33f71d32 | 1124 | if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 1125 | SGTL5000::TX_swIRQ = sw_irq; // Assign ISR address and enable users IRQ |
aidan1971 | 10:49bb33f71d32 | 1126 | NVIC_SetVector(SGTL5000::TX_swIRQ, (uint32_t)user_ISR); |
aidan1971 | 0:8f28f25e3435 | 1127 | NVIC_SetPriority(SGTL5000::TX_swIRQ, irq_pri); |
aidan1971 | 0:8f28f25e3435 | 1128 | NVIC_EnableIRQ(SGTL5000::TX_swIRQ); |
aidan1971 | 10:49bb33f71d32 | 1129 | SGTL5000::TX_attach_type = 1; |
aidan1971 | 0:8f28f25e3435 | 1130 | TX_attached = true; |
aidan1971 | 8:9fdf8501d14b | 1131 | return 0; |
aidan1971 | 0:8f28f25e3435 | 1132 | } |
aidan1971 | 0:8f28f25e3435 | 1133 | |
aidan1971 | 10:49bb33f71d32 | 1134 | |
aidan1971 | 8:9fdf8501d14b | 1135 | int32_t SGTL5000::detach_TX(void) |
aidan1971 | 0:8f28f25e3435 | 1136 | { |
aidan1971 | 10:49bb33f71d32 | 1137 | if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow. |
aidan1971 | 8:9fdf8501d14b | 1138 | if(TX_attached) { |
aidan1971 | 10:49bb33f71d32 | 1139 | if(SGTL5000::TX_attach_type) NVIC_DisableIRQ(SGTL5000::TX_swIRQ); // When using Interrupt driven NB calls, we disable the IRQ. The TX channel continues running, a different user function can be assigned with another call to the attach_NB function |
aidan1971 | 10:49bb33f71d32 | 1140 | else SGTL5000::stop_TX(); // When using callback we must stop TX channel before we detach. |
aidan1971 | 8:9fdf8501d14b | 1141 | TX_attached = false; |
aidan1971 | 8:9fdf8501d14b | 1142 | } |
aidan1971 | 8:9fdf8501d14b | 1143 | return 0; |
aidan1971 | 0:8f28f25e3435 | 1144 | } |
aidan1971 | 0:8f28f25e3435 | 1145 | |
aidan1971 | 9:40e0ff8c2ba2 | 1146 | int32_t SGTL5000::start_TX(uint32_t _BufTX_L_safe, uint32_t _BufTX_R_safe, |
aidan1971 | 10:49bb33f71d32 | 1147 | uint32_t _block_size, bool _packed_TX, bool _TX_shift, uint32_t _TX_DMAch, uint32_t _DMA_irq_pri, uint32_t _BufTX_L_safe2, uint32_t _BufTX_R_safe2) |
aidan1971 | 0:8f28f25e3435 | 1148 | { |
aidan1971 | 10:49bb33f71d32 | 1149 | if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 1150 | if(SGTL5000::SYNC_run || SGTL5000::TX_run ) return -1; // Check if i2s TX is already started |
aidan1971 | 10:49bb33f71d32 | 1151 | if(!TX_attached && !SGTL5000::TX_attach_type) return -1; // Check we have a handler if using callback |
aidan1971 | 0:8f28f25e3435 | 1152 | if(_TX_DMAch > 15) return -1; // Sanity check DMAMUX channels |
aidan1971 | 10:49bb33f71d32 | 1153 | if (!(_block_size == 2 || _block_size == 4 || _block_size == 8)) return -1; // Only accept block size 2^n within range. |
aidan1971 | 10:49bb33f71d32 | 1154 | // Enable I2S TX interfaces for active codecs |
aidan1971 | 10:49bb33f71d32 | 1155 | if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1)) == 0) { |
aidan1971 | 10:49bb33f71d32 | 1156 | if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1))) return -1; |
aidan1971 | 10:49bb33f71d32 | 1157 | } |
aidan1971 | 10:49bb33f71d32 | 1158 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 1159 | if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)) == 0) { |
aidan1971 | 10:49bb33f71d32 | 1160 | if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1; |
aidan1971 | 10:49bb33f71d32 | 1161 | } |
aidan1971 | 10:49bb33f71d32 | 1162 | } |
aidan1971 | 9:40e0ff8c2ba2 | 1163 | |
aidan1971 | 10:49bb33f71d32 | 1164 | packed_TX = _packed_TX; |
aidan1971 | 9:40e0ff8c2ba2 | 1165 | TX_block_size = _block_size; |
aidan1971 | 9:40e0ff8c2ba2 | 1166 | TX_bs_bytes = _block_size * 4; |
aidan1971 | 4:91354c908416 | 1167 | if(packed_TX) TX_shift = false; |
aidan1971 | 4:91354c908416 | 1168 | else TX_shift = _TX_shift; |
aidan1971 | 0:8f28f25e3435 | 1169 | SGTL5000::TX_DMAch = _TX_DMAch; |
aidan1971 | 10:49bb33f71d32 | 1170 | SGTL5000::active_TX_DMAch_bm = 0x1 << _TX_DMAch; |
aidan1971 | 10:49bb33f71d32 | 1171 | |
aidan1971 | 10:49bb33f71d32 | 1172 | SGTL5000::BufTX_L_safe = (uint32_t*)_BufTX_L_safe; // Assign the users pointer addresses codec 1 |
aidan1971 | 10:49bb33f71d32 | 1173 | SGTL5000::BufTX_R_safe = (uint32_t*)_BufTX_R_safe; |
aidan1971 | 10:49bb33f71d32 | 1174 | SGTL5000::BufTX_L_safeA = (uint32_t)&SGTL5000::I2S_TX_Buffer[0]; // Precalculate double buffer addresses |
aidan1971 | 10:49bb33f71d32 | 1175 | SGTL5000::BufTX_R_safeA = (uint32_t)&SGTL5000::I2S_TX_Buffer[0 + (TX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 1176 | SGTL5000::BufTX_L_safeB = (uint32_t)&SGTL5000::I2S_TX_Buffer[8]; |
aidan1971 | 10:49bb33f71d32 | 1177 | SGTL5000::BufTX_R_safeB = (uint32_t)&SGTL5000::I2S_TX_Buffer[8 + (TX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 1178 | |
aidan1971 | 10:49bb33f71d32 | 1179 | *SGTL5000::BufTX_L_safe = SGTL5000::BufTX_L_safeB; // Assign starting addresses to double buffer pointers |
aidan1971 | 10:49bb33f71d32 | 1180 | *SGTL5000::BufTX_R_safe = SGTL5000::BufTX_L_safeB; |
aidan1971 | 0:8f28f25e3435 | 1181 | |
aidan1971 | 10:49bb33f71d32 | 1182 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 1183 | SGTL5000::BufTX_L_safe2 = (uint32_t*)_BufTX_L_safe2; // Assign the users pointer addresses codec 2 |
aidan1971 | 10:49bb33f71d32 | 1184 | SGTL5000::BufTX_R_safe2 = (uint32_t*)_BufTX_R_safe2; |
aidan1971 | 10:49bb33f71d32 | 1185 | SGTL5000::BufTX_L_safeA2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0]; // Precalculate double buffer addresses |
aidan1971 | 10:49bb33f71d32 | 1186 | SGTL5000::BufTX_R_safeA2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0 + (TX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 1187 | SGTL5000::BufTX_L_safeB2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8]; |
aidan1971 | 10:49bb33f71d32 | 1188 | SGTL5000::BufTX_R_safeB2 = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8 + (TX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 1189 | *SGTL5000::BufTX_L_safe2 = SGTL5000::BufTX_L_safeB2; // Assign starting addresses to double buffer pointers |
aidan1971 | 10:49bb33f71d32 | 1190 | *SGTL5000::BufTX_R_safe2 = SGTL5000::BufTX_L_safeB2; |
aidan1971 | 10:49bb33f71d32 | 1191 | } |
aidan1971 | 10:49bb33f71d32 | 1192 | |
aidan1971 | 10:49bb33f71d32 | 1193 | I2S0->TCSR &= ~I2S_TCSR_TE_MASK; // TX Disable |
aidan1971 | 10:49bb33f71d32 | 1194 | I2S0->TMR = 0xFFFFFFFF; // Mask TX traffic to prevent buffer underflow before we aquire sync. |
aidan1971 | 10:49bb33f71d32 | 1195 | I2S0->TCSR |= I2S_TCSR_SR_MASK; // Reset TX logic |
aidan1971 | 9:40e0ff8c2ba2 | 1196 | I2S0->TCSR &= ~I2S_TCSR_SR_MASK; |
aidan1971 | 6:4ab5aaeaa064 | 1197 | I2S0->TCR1 = (I2S0->TCR1 & ~I2S_TCR1_TFW_MASK) | I2S_TCR1_TFW(8 - TX_block_size); // Set TX FIFO watermark |
aidan1971 | 10:49bb33f71d32 | 1198 | I2S0->TCSR |= I2S_TCSR_FRDE_MASK; // Enable DMA request based on TX FIFO watermark |
aidan1971 | 10:49bb33f71d32 | 1199 | |
aidan1971 | 9:40e0ff8c2ba2 | 1200 | while(I2S0->TCSR & I2S_TCSR_TE_MASK); // Make sure TX is disabled before enabling corresponding channel |
aidan1971 | 10:49bb33f71d32 | 1201 | |
aidan1971 | 10:49bb33f71d32 | 1202 | NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::TX_dma_ISR); // Set TX_DMAch transfer complete vector |
aidan1971 | 10:49bb33f71d32 | 1203 | NVIC_SetPriority((IRQn)SGTL5000::TX_DMAch, _DMA_irq_pri); // Set irq priority |
aidan1971 | 10:49bb33f71d32 | 1204 | NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch); |
aidan1971 | 10:49bb33f71d32 | 1205 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 1206 | I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(3); // Enable TX channels 0 & 1. |
aidan1971 | 10:49bb33f71d32 | 1207 | } else { |
aidan1971 | 10:49bb33f71d32 | 1208 | I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(1); // Enable TX channel 0. |
aidan1971 | 10:49bb33f71d32 | 1209 | } |
aidan1971 | 10:49bb33f71d32 | 1210 | |
aidan1971 | 10:49bb33f71d32 | 1211 | NVIC_SetVector(I2S0_Tx_IRQn, (uint32_t)&SGTL5000::tx_I2S_WS_ISR); // Set vector for TX word start ISR |
aidan1971 | 0:8f28f25e3435 | 1212 | NVIC_SetPriority(I2S0_Tx_IRQn, 0); // Set priority of TX word start ISR |
aidan1971 | 0:8f28f25e3435 | 1213 | NVIC_EnableIRQ(I2S0_Tx_IRQn); // Enable TX word start ISR |
aidan1971 | 0:8f28f25e3435 | 1214 | I2S0->TCSR |= I2S_TCSR_WSF_MASK; // Clear TX Word Start Flag |
aidan1971 | 0:8f28f25e3435 | 1215 | I2S0->TCSR |= I2S_TCSR_WSIE_MASK; // Enable I2S TX word start IRQ |
aidan1971 | 10:49bb33f71d32 | 1216 | |
aidan1971 | 10:49bb33f71d32 | 1217 | SGTL5000::db_phase_tx = 0; |
aidan1971 | 10:49bb33f71d32 | 1218 | SGTL5000::TX_run = true; |
aidan1971 | 10:49bb33f71d32 | 1219 | init_DMA(); |
aidan1971 | 10:49bb33f71d32 | 1220 | I2S0->TCSR |= I2S_TCSR_TE_MASK; // TX Enable |
aidan1971 | 0:8f28f25e3435 | 1221 | return 0; |
aidan1971 | 0:8f28f25e3435 | 1222 | } |
aidan1971 | 0:8f28f25e3435 | 1223 | |
aidan1971 | 10:49bb33f71d32 | 1224 | int32_t SGTL5000::pause_TX(void) |
aidan1971 | 10:49bb33f71d32 | 1225 | { |
aidan1971 | 10:49bb33f71d32 | 1226 | if(!codec_configured || !this->ctrl_codec || !SGTL5000::TX_run) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 1227 | SGTL5000::ctrl_command = PAUSE_TX; |
aidan1971 | 10:49bb33f71d32 | 1228 | NVIC->STIR = CODEC_CTRL_IRQ; |
aidan1971 | 10:49bb33f71d32 | 1229 | while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ)); |
aidan1971 | 10:49bb33f71d32 | 1230 | return 0; |
aidan1971 | 10:49bb33f71d32 | 1231 | } |
aidan1971 | 10:49bb33f71d32 | 1232 | |
aidan1971 | 10:49bb33f71d32 | 1233 | int32_t SGTL5000::resume_TX(void) |
aidan1971 | 10:49bb33f71d32 | 1234 | { |
aidan1971 | 10:49bb33f71d32 | 1235 | if(!codec_configured || !this->ctrl_codec || !SGTL5000::TX_run) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 1236 | SGTL5000::ctrl_command = RESUME_TX; |
aidan1971 | 10:49bb33f71d32 | 1237 | NVIC->STIR = CODEC_CTRL_IRQ; |
aidan1971 | 10:49bb33f71d32 | 1238 | while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ)); |
aidan1971 | 10:49bb33f71d32 | 1239 | return 0; |
aidan1971 | 10:49bb33f71d32 | 1240 | } |
aidan1971 | 10:49bb33f71d32 | 1241 | |
aidan1971 | 10:49bb33f71d32 | 1242 | |
aidan1971 | 8:9fdf8501d14b | 1243 | int32_t SGTL5000::stop_TX(void) |
aidan1971 | 0:8f28f25e3435 | 1244 | { |
aidan1971 | 10:49bb33f71d32 | 1245 | if(!codec_configured || !this->ctrl_codec || !SGTL5000::TX_run) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 1246 | modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK); // codec I2S data lines tristate |
aidan1971 | 10:49bb33f71d32 | 1247 | if(SGTL5000::codec2_active) modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)); |
aidan1971 | 10:49bb33f71d32 | 1248 | SGTL5000::ctrl_command = STOP_TX; |
aidan1971 | 10:49bb33f71d32 | 1249 | NVIC->STIR = CODEC_CTRL_IRQ; |
aidan1971 | 10:49bb33f71d32 | 1250 | while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ)); |
aidan1971 | 8:9fdf8501d14b | 1251 | return 0; |
aidan1971 | 0:8f28f25e3435 | 1252 | } |
aidan1971 | 0:8f28f25e3435 | 1253 | |
aidan1971 | 10:49bb33f71d32 | 1254 | void SGTL5000::tx_I2S_WS_ISR(void) |
aidan1971 | 0:8f28f25e3435 | 1255 | { |
aidan1971 | 10:49bb33f71d32 | 1256 | I2S0->TCSR &= ~I2S_TCSR_WSIE_MASK; // Disable TX word start IRQs |
aidan1971 | 10:49bb33f71d32 | 1257 | I2S0->TCSR |= I2S_TCSR_FR_MASK; // Reset TX FIFO pointers |
aidan1971 | 10:49bb33f71d32 | 1258 | for(uint32_t i = 1; i <= (8 - ((I2S0->TCR1 & I2S_TCR1_TFW_MASK) >> I2S_TCR1_TFW_SHIFT)); ++i) { // Stuff the TX FIFO(s) with samples. This is to avoid underrun before we DMA some data into the buffers. |
aidan1971 | 10:49bb33f71d32 | 1259 | I2S0->TDR[0] = 0; // It allows the time between FIFO wm and an empty buffer at the current sample rate before a DMA request is made. |
aidan1971 | 10:49bb33f71d32 | 1260 | } // This can be critical as at 192Ks/s we only have 5nS after starting TX fifo and an underrun, unless we stuff. |
aidan1971 | 10:49bb33f71d32 | 1261 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 1262 | for(uint32_t i = 1; i <= (8 - ((I2S0->TCR1 & I2S_TCR1_TFW_MASK) >> I2S_TCR1_TFW_SHIFT)); ++i) { |
aidan1971 | 10:49bb33f71d32 | 1263 | I2S0->TDR[1] = 0; |
aidan1971 | 10:49bb33f71d32 | 1264 | } |
aidan1971 | 10:49bb33f71d32 | 1265 | } |
aidan1971 | 10:49bb33f71d32 | 1266 | I2S0->TMR = 0x0; // Clear TX Mask to start data flowing |
aidan1971 | 10:49bb33f71d32 | 1267 | } |
aidan1971 | 0:8f28f25e3435 | 1268 | |
aidan1971 | 10:49bb33f71d32 | 1269 | void SGTL5000::TX_dma_ISR(void) |
aidan1971 | 10:49bb33f71d32 | 1270 | { |
aidan1971 | 10:49bb33f71d32 | 1271 | register uint32_t TX_IRQ_BM; |
aidan1971 | 10:49bb33f71d32 | 1272 | register uint32_t DMA_INT; |
aidan1971 | 10:49bb33f71d32 | 1273 | register uint32_t CODEC2; |
aidan1971 | 10:49bb33f71d32 | 1274 | register uint32_t IRQNR; |
aidan1971 | 10:49bb33f71d32 | 1275 | register uint32_t DSP_BUSY; |
aidan1971 | 10:49bb33f71d32 | 1276 | register uint32_t DB_PHASE; |
aidan1971 | 10:49bb33f71d32 | 1277 | register uint32_t TYPE; |
aidan1971 | 6:4ab5aaeaa064 | 1278 | register uint32_t BU_TX_L; |
aidan1971 | 6:4ab5aaeaa064 | 1279 | register uint32_t BU_TX_R; |
aidan1971 | 10:49bb33f71d32 | 1280 | register uint32_t BU_TX_L2; |
aidan1971 | 10:49bb33f71d32 | 1281 | register uint32_t BU_TX_R2; |
aidan1971 | 6:4ab5aaeaa064 | 1282 | __asm { |
aidan1971 | 10:49bb33f71d32 | 1283 | LDR TX_IRQ_BM, [&SGTL5000::active_TX_DMAch_bm] |
aidan1971 | 10:49bb33f71d32 | 1284 | LDR DMA_INT, [&DMA0->INT] |
aidan1971 | 6:4ab5aaeaa064 | 1285 | |
aidan1971 | 10:49bb33f71d32 | 1286 | AND TX_IRQ_BM, TX_IRQ_BM, DMA_INT // Clear DMA IRQ flag |
aidan1971 | 10:49bb33f71d32 | 1287 | STR TX_IRQ_BM, [&DMA0->INT] |
aidan1971 | 9:40e0ff8c2ba2 | 1288 | |
aidan1971 | 10:49bb33f71d32 | 1289 | LDR DB_PHASE, [&SGTL5000::db_phase_tx] |
aidan1971 | 10:49bb33f71d32 | 1290 | LDR TYPE, [&SGTL5000::TX_attach_type] |
aidan1971 | 10:49bb33f71d32 | 1291 | LDRB CODEC2, [&SGTL5000::codec2_active] |
aidan1971 | 10:49bb33f71d32 | 1292 | TEQ CODEC2, #0x1 |
aidan1971 | 10:49bb33f71d32 | 1293 | IT EQ |
aidan1971 | 10:49bb33f71d32 | 1294 | BEQ dual_codecs |
aidan1971 | 9:40e0ff8c2ba2 | 1295 | |
aidan1971 | 9:40e0ff8c2ba2 | 1296 | TEQ DB_PHASE, #0x0 |
aidan1971 | 9:40e0ff8c2ba2 | 1297 | IT EQ |
aidan1971 | 9:40e0ff8c2ba2 | 1298 | BEQ buf_base |
aidan1971 | 9:40e0ff8c2ba2 | 1299 | |
aidan1971 | 10:49bb33f71d32 | 1300 | LDR BU_TX_L, [&SGTL5000::BufTX_L_safeB] |
aidan1971 | 10:49bb33f71d32 | 1301 | LDR BU_TX_R, [&SGTL5000::BufTX_R_safeB] |
aidan1971 | 9:40e0ff8c2ba2 | 1302 | SUB DB_PHASE, #0x1 |
aidan1971 | 9:40e0ff8c2ba2 | 1303 | B store |
aidan1971 | 9:40e0ff8c2ba2 | 1304 | |
aidan1971 | 9:40e0ff8c2ba2 | 1305 | buf_base: |
aidan1971 | 10:49bb33f71d32 | 1306 | LDR BU_TX_L, [&SGTL5000::BufTX_L_safeA] |
aidan1971 | 10:49bb33f71d32 | 1307 | LDR BU_TX_R, [&SGTL5000::BufTX_R_safeA] |
aidan1971 | 9:40e0ff8c2ba2 | 1308 | ADD DB_PHASE, #0x1 |
aidan1971 | 9:40e0ff8c2ba2 | 1309 | |
aidan1971 | 10:49bb33f71d32 | 1310 | store: |
aidan1971 | 10:49bb33f71d32 | 1311 | STR BU_TX_L, [SGTL5000::BufTX_L_safe] // Pipeline memory access |
aidan1971 | 9:40e0ff8c2ba2 | 1312 | STR BU_TX_R, [SGTL5000::BufTX_R_safe] |
aidan1971 | 9:40e0ff8c2ba2 | 1313 | STR DB_PHASE, [&SGTL5000::db_phase_tx] |
aidan1971 | 10:49bb33f71d32 | 1314 | B finish_update |
aidan1971 | 10:49bb33f71d32 | 1315 | |
aidan1971 | 10:49bb33f71d32 | 1316 | dual_codecs: |
aidan1971 | 10:49bb33f71d32 | 1317 | TEQ DB_PHASE, #0x0 |
aidan1971 | 10:49bb33f71d32 | 1318 | IT EQ |
aidan1971 | 10:49bb33f71d32 | 1319 | BEQ buf_base2 |
aidan1971 | 10:49bb33f71d32 | 1320 | |
aidan1971 | 10:49bb33f71d32 | 1321 | LDR BU_TX_L, [&SGTL5000::BufTX_L_safeB] |
aidan1971 | 10:49bb33f71d32 | 1322 | LDR BU_TX_R, [&SGTL5000::BufTX_R_safeB] |
aidan1971 | 10:49bb33f71d32 | 1323 | LDR BU_TX_L2, [&SGTL5000::BufTX_L_safeB2] |
aidan1971 | 10:49bb33f71d32 | 1324 | LDR BU_TX_R2, [&SGTL5000::BufTX_R_safeB2] |
aidan1971 | 10:49bb33f71d32 | 1325 | SUB DB_PHASE, #0x1 |
aidan1971 | 10:49bb33f71d32 | 1326 | B store2 |
aidan1971 | 10:49bb33f71d32 | 1327 | |
aidan1971 | 10:49bb33f71d32 | 1328 | buf_base2: |
aidan1971 | 10:49bb33f71d32 | 1329 | LDR BU_TX_L, [&SGTL5000::BufTX_L_safeA] |
aidan1971 | 10:49bb33f71d32 | 1330 | LDR BU_TX_R, [&SGTL5000::BufTX_R_safeA] |
aidan1971 | 10:49bb33f71d32 | 1331 | LDR BU_TX_L2, [&SGTL5000::BufTX_L_safeA2] |
aidan1971 | 10:49bb33f71d32 | 1332 | LDR BU_TX_R2, [&SGTL5000::BufTX_R_safeA2] |
aidan1971 | 10:49bb33f71d32 | 1333 | ADD DB_PHASE, #0x1 |
aidan1971 | 10:49bb33f71d32 | 1334 | |
aidan1971 | 10:49bb33f71d32 | 1335 | store2: |
aidan1971 | 10:49bb33f71d32 | 1336 | STR BU_TX_L, [SGTL5000::BufTX_L_safe] // Pipeline memory access |
aidan1971 | 10:49bb33f71d32 | 1337 | STR BU_TX_R, [SGTL5000::BufTX_R_safe] |
aidan1971 | 10:49bb33f71d32 | 1338 | STR BU_TX_L2, [SGTL5000::BufTX_L_safe2] |
aidan1971 | 10:49bb33f71d32 | 1339 | STR BU_TX_R2, [SGTL5000::BufTX_R_safe2] |
aidan1971 | 10:49bb33f71d32 | 1340 | STR DB_PHASE, [&SGTL5000::db_phase_tx] |
aidan1971 | 10:49bb33f71d32 | 1341 | |
aidan1971 | 10:49bb33f71d32 | 1342 | finish_update: |
aidan1971 | 10:49bb33f71d32 | 1343 | TEQ TYPE, #1 |
aidan1971 | 10:49bb33f71d32 | 1344 | IT EQ |
aidan1971 | 10:49bb33f71d32 | 1345 | BEQ call_tx_nb |
aidan1971 | 6:4ab5aaeaa064 | 1346 | } |
aidan1971 | 10:49bb33f71d32 | 1347 | SGTL5000::TX_user_func.call(); |
aidan1971 | 10:49bb33f71d32 | 1348 | __asm { |
aidan1971 | 10:49bb33f71d32 | 1349 | B finish_isr |
aidan1971 | 10:49bb33f71d32 | 1350 | |
aidan1971 | 10:49bb33f71d32 | 1351 | call_tx_nb: |
aidan1971 | 10:49bb33f71d32 | 1352 | LDRB IRQNR, [&SGTL5000::TX_swIRQ] |
aidan1971 | 10:49bb33f71d32 | 1353 | BL NVIC_GetActive, {r0=IRQNR}, {DSP_BUSY=r0} |
aidan1971 | 10:49bb33f71d32 | 1354 | TEQ DSP_BUSY, #1 |
aidan1971 | 10:49bb33f71d32 | 1355 | IT EQ |
aidan1971 | 10:49bb33f71d32 | 1356 | BEQ finish_isr |
aidan1971 | 10:49bb33f71d32 | 1357 | STR IRQNR, [&NVIC->STIR] // Trigger swIRQ only if previous call completed. This will keep things flowing, but if the irq is still active, good luck with the pointers! |
aidan1971 | 10:49bb33f71d32 | 1358 | |
aidan1971 | 10:49bb33f71d32 | 1359 | finish_isr: |
aidan1971 | 10:49bb33f71d32 | 1360 | |
aidan1971 | 10:49bb33f71d32 | 1361 | } |
aidan1971 | 0:8f28f25e3435 | 1362 | } |
aidan1971 | 0:8f28f25e3435 | 1363 | |
aidan1971 | 0:8f28f25e3435 | 1364 | int32_t SGTL5000::attach_RX(Callback<void()> func) |
aidan1971 | 0:8f28f25e3435 | 1365 | { |
aidan1971 | 10:49bb33f71d32 | 1366 | if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow for SYNC. |
aidan1971 | 10:49bb33f71d32 | 1367 | if(RX_attached) return -1; // Assign Callback function |
aidan1971 | 0:8f28f25e3435 | 1368 | SGTL5000::RX_user_func = func; |
aidan1971 | 10:49bb33f71d32 | 1369 | SGTL5000::RX_attach_type = 0; |
aidan1971 | 0:8f28f25e3435 | 1370 | RX_attached = true; |
aidan1971 | 0:8f28f25e3435 | 1371 | return 0; |
aidan1971 | 0:8f28f25e3435 | 1372 | } |
aidan1971 | 0:8f28f25e3435 | 1373 | |
aidan1971 | 10:49bb33f71d32 | 1374 | int32_t SGTL5000::attach_RX_NB(void* user_ISR, uint32_t irq_pri, IRQn sw_irq) |
aidan1971 | 0:8f28f25e3435 | 1375 | { |
aidan1971 | 10:49bb33f71d32 | 1376 | if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 1377 | SGTL5000::RX_swIRQ = sw_irq; // Assign ISR address and enable users IRQ |
aidan1971 | 10:49bb33f71d32 | 1378 | NVIC_SetVector(SGTL5000::RX_swIRQ, (uint32_t)user_ISR); |
aidan1971 | 0:8f28f25e3435 | 1379 | NVIC_SetPriority(SGTL5000::RX_swIRQ, irq_pri); |
aidan1971 | 0:8f28f25e3435 | 1380 | NVIC_EnableIRQ(SGTL5000::RX_swIRQ); |
aidan1971 | 10:49bb33f71d32 | 1381 | SGTL5000::RX_attach_type = 1; |
aidan1971 | 0:8f28f25e3435 | 1382 | RX_attached = true; |
aidan1971 | 8:9fdf8501d14b | 1383 | return 0; |
aidan1971 | 0:8f28f25e3435 | 1384 | } |
aidan1971 | 0:8f28f25e3435 | 1385 | |
aidan1971 | 10:49bb33f71d32 | 1386 | |
aidan1971 | 8:9fdf8501d14b | 1387 | int32_t SGTL5000::detach_RX(void) |
aidan1971 | 0:8f28f25e3435 | 1388 | { |
aidan1971 | 10:49bb33f71d32 | 1389 | if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow. |
aidan1971 | 8:9fdf8501d14b | 1390 | if(RX_attached) { |
aidan1971 | 10:49bb33f71d32 | 1391 | if(SGTL5000::RX_attach_type) NVIC_DisableIRQ(SGTL5000::RX_swIRQ); // When using Interrupt driven NB calls, we disable the IRQ. The RX channel continues running, a different user function can be assigned with another call to the attach_NB function |
aidan1971 | 10:49bb33f71d32 | 1392 | else SGTL5000::stop_RX(); // When using callback we must stop RX channel before we detach. |
aidan1971 | 8:9fdf8501d14b | 1393 | RX_attached = false; |
aidan1971 | 8:9fdf8501d14b | 1394 | } |
aidan1971 | 8:9fdf8501d14b | 1395 | return 0; |
aidan1971 | 0:8f28f25e3435 | 1396 | } |
aidan1971 | 0:8f28f25e3435 | 1397 | |
aidan1971 | 10:49bb33f71d32 | 1398 | int32_t SGTL5000::start_RX(uint32_t _BufRX_L_safe, uint32_t _BufRX_R_safe, |
aidan1971 | 10:49bb33f71d32 | 1399 | uint32_t _block_size, bool _packed_RX, bool _RX_shift, uint32_t _RX_DMAch, uint32_t _DMA_irq_pri, uint32_t _BufRX_L_safe2, uint32_t _BufRX_R_safe2) |
aidan1971 | 0:8f28f25e3435 | 1400 | { |
aidan1971 | 10:49bb33f71d32 | 1401 | if(!codec_configured || !this->ctrl_codec) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 1402 | if(SGTL5000::SYNC_run || SGTL5000::RX_run ) return -1; // Check if i2s RX is already started |
aidan1971 | 10:49bb33f71d32 | 1403 | if(!RX_attached && !SGTL5000::RX_attach_type) return -1; // Check we have a handler if using callback |
aidan1971 | 0:8f28f25e3435 | 1404 | if(_RX_DMAch > 15) return -1; // Sanity check DMAMUX channels |
aidan1971 | 10:49bb33f71d32 | 1405 | if (!(_block_size == 2 || _block_size == 4 || _block_size == 8)) return -1; // Only accept block size 2^n within range. |
aidan1971 | 10:49bb33f71d32 | 1406 | // Enable I2S RX interfaces for active codecs |
aidan1971 | 10:49bb33f71d32 | 1407 | if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1)) == 0) { |
aidan1971 | 10:49bb33f71d32 | 1408 | if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_LOW << 1))) return -1; |
aidan1971 | 9:40e0ff8c2ba2 | 1409 | } |
aidan1971 | 10:49bb33f71d32 | 1410 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 1411 | if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1)) == 0) { |
aidan1971 | 10:49bb33f71d32 | 1412 | if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1; |
aidan1971 | 10:49bb33f71d32 | 1413 | } |
aidan1971 | 10:49bb33f71d32 | 1414 | } |
aidan1971 | 10:49bb33f71d32 | 1415 | packed_RX = _packed_RX; |
aidan1971 | 9:40e0ff8c2ba2 | 1416 | RX_block_size = _block_size; |
aidan1971 | 9:40e0ff8c2ba2 | 1417 | RX_bs_bytes = _block_size * 4; |
aidan1971 | 4:91354c908416 | 1418 | if(packed_RX) RX_shift = false; |
aidan1971 | 4:91354c908416 | 1419 | else RX_shift = _RX_shift; |
aidan1971 | 0:8f28f25e3435 | 1420 | SGTL5000::RX_DMAch = _RX_DMAch; |
aidan1971 | 10:49bb33f71d32 | 1421 | SGTL5000::active_RX_DMAch_bm = 0x1 << _RX_DMAch; |
aidan1971 | 10:49bb33f71d32 | 1422 | |
aidan1971 | 10:49bb33f71d32 | 1423 | SGTL5000::BufRX_L_safe = (uint32_t*)_BufRX_L_safe; // Assign the users pointer addresses codec 1 |
aidan1971 | 10:49bb33f71d32 | 1424 | SGTL5000::BufRX_R_safe = (uint32_t*)_BufRX_R_safe; |
aidan1971 | 10:49bb33f71d32 | 1425 | SGTL5000::BufRX_L_safeA = (uint32_t)&SGTL5000::I2S_RX_Buffer[0]; // Precalculate double buffer addresses |
aidan1971 | 10:49bb33f71d32 | 1426 | SGTL5000::BufRX_R_safeA = (uint32_t)&SGTL5000::I2S_RX_Buffer[0 + (RX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 1427 | SGTL5000::BufRX_L_safeB = (uint32_t)&SGTL5000::I2S_RX_Buffer[8]; |
aidan1971 | 10:49bb33f71d32 | 1428 | SGTL5000::BufRX_R_safeB = (uint32_t)&SGTL5000::I2S_RX_Buffer[8 + (RX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 1429 | |
aidan1971 | 10:49bb33f71d32 | 1430 | *SGTL5000::BufRX_L_safe = SGTL5000::BufRX_L_safeB; // Assign starting addresses to double buffer pointers |
aidan1971 | 10:49bb33f71d32 | 1431 | *SGTL5000::BufRX_R_safe = SGTL5000::BufRX_L_safeB; |
aidan1971 | 0:8f28f25e3435 | 1432 | |
aidan1971 | 10:49bb33f71d32 | 1433 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 1434 | SGTL5000::BufRX_L_safe2 = (uint32_t*)_BufRX_L_safe2; // Assign the users pointer addresses codec 2 |
aidan1971 | 10:49bb33f71d32 | 1435 | SGTL5000::BufRX_R_safe2 = (uint32_t*)_BufRX_R_safe2; |
aidan1971 | 10:49bb33f71d32 | 1436 | SGTL5000::BufRX_L_safeA2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0]; // Precalculate double buffer addresses |
aidan1971 | 10:49bb33f71d32 | 1437 | SGTL5000::BufRX_R_safeA2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0 + (RX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 1438 | SGTL5000::BufRX_L_safeB2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8]; |
aidan1971 | 10:49bb33f71d32 | 1439 | SGTL5000::BufRX_R_safeB2 = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8 + (RX_block_size / 2)]; |
aidan1971 | 10:49bb33f71d32 | 1440 | *SGTL5000::BufRX_L_safe2 = SGTL5000::BufRX_L_safeB2; // Assign starting addresses to double buffer pointers |
aidan1971 | 10:49bb33f71d32 | 1441 | *SGTL5000::BufRX_R_safe2 = SGTL5000::BufRX_L_safeB2; |
aidan1971 | 10:49bb33f71d32 | 1442 | } |
aidan1971 | 10:49bb33f71d32 | 1443 | |
aidan1971 | 10:49bb33f71d32 | 1444 | I2S0->RCSR &= ~I2S_RCSR_RE_MASK; // TX Disable |
aidan1971 | 10:49bb33f71d32 | 1445 | I2S0->RMR = 0xFFFFFFFF; // Mask RX traffic to prevent buffer underflow before we aquire sync. |
aidan1971 | 10:49bb33f71d32 | 1446 | I2S0->RCSR |= I2S_RCSR_SR_MASK; // Reset RX logic |
aidan1971 | 9:40e0ff8c2ba2 | 1447 | I2S0->RCSR &= ~I2S_RCSR_SR_MASK; |
aidan1971 | 6:4ab5aaeaa064 | 1448 | I2S0->RCR1 = (I2S0->RCR1 & ~I2S_RCR1_RFW_MASK) | I2S_RCR1_RFW(RX_block_size - 1); // Set RX FIFO watermark |
aidan1971 | 10:49bb33f71d32 | 1449 | I2S0->RCSR |= I2S_RCSR_FRDE_MASK; // Enable DMA request based on RX FIFO watermark |
aidan1971 | 10:49bb33f71d32 | 1450 | |
aidan1971 | 9:40e0ff8c2ba2 | 1451 | while(I2S0->RCSR & I2S_RCSR_RE_MASK); // Make sure RX is disabled before enabling corresponding channel |
aidan1971 | 10:49bb33f71d32 | 1452 | |
aidan1971 | 10:49bb33f71d32 | 1453 | NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::RX_dma_ISR); // Set RX_DMAch transfer complete vector |
aidan1971 | 10:49bb33f71d32 | 1454 | NVIC_SetPriority((IRQn)SGTL5000::RX_DMAch, _DMA_irq_pri); // Set irq priority |
aidan1971 | 10:49bb33f71d32 | 1455 | NVIC_EnableIRQ((IRQn)SGTL5000::RX_DMAch); // Enable RX DMA complete IRQs |
aidan1971 | 10:49bb33f71d32 | 1456 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 1457 | I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(3); // Enable RX channels 0 & 1. |
aidan1971 | 10:49bb33f71d32 | 1458 | } else { |
aidan1971 | 10:49bb33f71d32 | 1459 | I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(1); // Enable RX channel 0. |
aidan1971 | 10:49bb33f71d32 | 1460 | } |
aidan1971 | 10:49bb33f71d32 | 1461 | |
aidan1971 | 10:49bb33f71d32 | 1462 | NVIC_SetVector(I2S0_Rx_IRQn, (uint32_t)&SGTL5000::rx_I2S_WS_ISR); // Set vector for RX word start ISR |
aidan1971 | 0:8f28f25e3435 | 1463 | NVIC_SetPriority(I2S0_Rx_IRQn, 0); // Set priority of RX word start ISR |
aidan1971 | 0:8f28f25e3435 | 1464 | NVIC_EnableIRQ(I2S0_Rx_IRQn); // Enable RX word start ISR |
aidan1971 | 0:8f28f25e3435 | 1465 | I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag |
aidan1971 | 0:8f28f25e3435 | 1466 | I2S0->RCSR |= I2S_RCSR_WSIE_MASK; // Enable I2S RX word start IRQ |
aidan1971 | 10:49bb33f71d32 | 1467 | |
aidan1971 | 10:49bb33f71d32 | 1468 | SGTL5000::db_phase_rx = 0; |
aidan1971 | 10:49bb33f71d32 | 1469 | SGTL5000::RX_run = true; |
aidan1971 | 10:49bb33f71d32 | 1470 | init_DMA(); |
aidan1971 | 10:49bb33f71d32 | 1471 | I2S0->RCSR |= I2S_RCSR_RE_MASK; // RX Enable |
aidan1971 | 10:49bb33f71d32 | 1472 | return 0; |
aidan1971 | 10:49bb33f71d32 | 1473 | } |
aidan1971 | 10:49bb33f71d32 | 1474 | |
aidan1971 | 10:49bb33f71d32 | 1475 | int32_t SGTL5000::pause_RX(void) |
aidan1971 | 10:49bb33f71d32 | 1476 | { |
aidan1971 | 10:49bb33f71d32 | 1477 | if(!codec_configured || !this->ctrl_codec || !SGTL5000::RX_run) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 1478 | SGTL5000::ctrl_command = PAUSE_RX; |
aidan1971 | 10:49bb33f71d32 | 1479 | NVIC->STIR = CODEC_CTRL_IRQ; |
aidan1971 | 10:49bb33f71d32 | 1480 | while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ)); |
aidan1971 | 10:49bb33f71d32 | 1481 | return 0; |
aidan1971 | 10:49bb33f71d32 | 1482 | } |
aidan1971 | 10:49bb33f71d32 | 1483 | |
aidan1971 | 10:49bb33f71d32 | 1484 | int32_t SGTL5000::resume_RX(void) |
aidan1971 | 10:49bb33f71d32 | 1485 | { |
aidan1971 | 10:49bb33f71d32 | 1486 | if(!codec_configured || !this->ctrl_codec || !SGTL5000::RX_run) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 1487 | SGTL5000::ctrl_command = RESUME_RX; |
aidan1971 | 10:49bb33f71d32 | 1488 | NVIC->STIR = CODEC_CTRL_IRQ; |
aidan1971 | 10:49bb33f71d32 | 1489 | while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ)); |
aidan1971 | 0:8f28f25e3435 | 1490 | return 0; |
aidan1971 | 0:8f28f25e3435 | 1491 | } |
aidan1971 | 0:8f28f25e3435 | 1492 | |
aidan1971 | 8:9fdf8501d14b | 1493 | int32_t SGTL5000::stop_RX(void) |
aidan1971 | 0:8f28f25e3435 | 1494 | { |
aidan1971 | 10:49bb33f71d32 | 1495 | if(!codec_configured || !this->ctrl_codec || !SGTL5000::RX_run) return -1; // Must use codec1 to control data flow. |
aidan1971 | 10:49bb33f71d32 | 1496 | if(modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -1; // codec I2S data lines tristate |
aidan1971 | 10:49bb33f71d32 | 1497 | if(SGTL5000::codec2_active) if(modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK, (SGTL5000_I2C_ADDR_CS_HIGH << 1))) return -1; |
aidan1971 | 10:49bb33f71d32 | 1498 | SGTL5000::ctrl_command = STOP_RX; |
aidan1971 | 10:49bb33f71d32 | 1499 | NVIC->STIR = CODEC_CTRL_IRQ; |
aidan1971 | 10:49bb33f71d32 | 1500 | while(NVIC_GetActive(CODEC_CTRL_IRQ) || NVIC_GetPendingIRQ(CODEC_CTRL_IRQ)); |
aidan1971 | 8:9fdf8501d14b | 1501 | return 0; |
aidan1971 | 0:8f28f25e3435 | 1502 | } |
aidan1971 | 0:8f28f25e3435 | 1503 | |
aidan1971 | 10:49bb33f71d32 | 1504 | |
aidan1971 | 10:49bb33f71d32 | 1505 | void SGTL5000::rx_I2S_WS_ISR(void) |
aidan1971 | 0:8f28f25e3435 | 1506 | { |
aidan1971 | 10:49bb33f71d32 | 1507 | I2S0->RCSR &= ~I2S_RCSR_WSIE_MASK; // Disable RX word start IRQs |
aidan1971 | 10:49bb33f71d32 | 1508 | I2S0->RCSR |= I2S_RCSR_FR_MASK; // Reset RX FIFO pointers |
aidan1971 | 10:49bb33f71d32 | 1509 | I2S0->RMR = 0x0; // Clear RX Mask to start data flowing |
aidan1971 | 10:49bb33f71d32 | 1510 | } |
aidan1971 | 0:8f28f25e3435 | 1511 | |
aidan1971 | 10:49bb33f71d32 | 1512 | void SGTL5000::RX_dma_ISR(void) |
aidan1971 | 10:49bb33f71d32 | 1513 | { |
aidan1971 | 10:49bb33f71d32 | 1514 | register uint32_t RX_IRQ_BM; |
aidan1971 | 10:49bb33f71d32 | 1515 | register uint32_t DMA_INT; |
aidan1971 | 10:49bb33f71d32 | 1516 | register uint32_t CODEC2; |
aidan1971 | 10:49bb33f71d32 | 1517 | register uint32_t IRQNR; |
aidan1971 | 10:49bb33f71d32 | 1518 | register uint32_t DSP_BUSY; |
aidan1971 | 10:49bb33f71d32 | 1519 | register uint32_t DB_PHASE; |
aidan1971 | 10:49bb33f71d32 | 1520 | register uint32_t TYPE; |
aidan1971 | 6:4ab5aaeaa064 | 1521 | register uint32_t BU_RX_L; |
aidan1971 | 6:4ab5aaeaa064 | 1522 | register uint32_t BU_RX_R; |
aidan1971 | 10:49bb33f71d32 | 1523 | register uint32_t BU_RX_L2; |
aidan1971 | 10:49bb33f71d32 | 1524 | register uint32_t BU_RX_R2; |
aidan1971 | 6:4ab5aaeaa064 | 1525 | __asm { |
aidan1971 | 10:49bb33f71d32 | 1526 | LDR RX_IRQ_BM, [&SGTL5000::active_RX_DMAch_bm] |
aidan1971 | 10:49bb33f71d32 | 1527 | LDR DMA_INT, [&DMA0->INT] |
aidan1971 | 10:49bb33f71d32 | 1528 | |
aidan1971 | 10:49bb33f71d32 | 1529 | AND RX_IRQ_BM, RX_IRQ_BM, DMA_INT // Clear DMA IRQ flag |
aidan1971 | 10:49bb33f71d32 | 1530 | STR RX_IRQ_BM, [&DMA0->INT] |
aidan1971 | 10:49bb33f71d32 | 1531 | |
aidan1971 | 10:49bb33f71d32 | 1532 | LDR DB_PHASE, [&SGTL5000::db_phase_rx] |
aidan1971 | 10:49bb33f71d32 | 1533 | LDR TYPE, [&SGTL5000::RX_attach_type] |
aidan1971 | 10:49bb33f71d32 | 1534 | LDRB CODEC2, [&SGTL5000::codec2_active] |
aidan1971 | 10:49bb33f71d32 | 1535 | TEQ CODEC2, #0x1 |
aidan1971 | 10:49bb33f71d32 | 1536 | IT EQ |
aidan1971 | 10:49bb33f71d32 | 1537 | BEQ dual_codecs |
aidan1971 | 6:4ab5aaeaa064 | 1538 | |
aidan1971 | 6:4ab5aaeaa064 | 1539 | TEQ DB_PHASE, #0x0 |
aidan1971 | 6:4ab5aaeaa064 | 1540 | IT EQ |
aidan1971 | 6:4ab5aaeaa064 | 1541 | BEQ buf_base |
aidan1971 | 6:4ab5aaeaa064 | 1542 | |
aidan1971 | 10:49bb33f71d32 | 1543 | LDR BU_RX_L, [&SGTL5000::BufRX_L_safeB] |
aidan1971 | 10:49bb33f71d32 | 1544 | LDR BU_RX_R, [&SGTL5000::BufRX_R_safeB] |
aidan1971 | 6:4ab5aaeaa064 | 1545 | SUB DB_PHASE, #0x1 |
aidan1971 | 6:4ab5aaeaa064 | 1546 | B store |
aidan1971 | 6:4ab5aaeaa064 | 1547 | |
aidan1971 | 6:4ab5aaeaa064 | 1548 | buf_base: |
aidan1971 | 10:49bb33f71d32 | 1549 | LDR BU_RX_L, [&SGTL5000::BufRX_L_safeA] |
aidan1971 | 10:49bb33f71d32 | 1550 | LDR BU_RX_R, [&SGTL5000::BufRX_R_safeA] |
aidan1971 | 6:4ab5aaeaa064 | 1551 | ADD DB_PHASE, #0x1 |
aidan1971 | 6:4ab5aaeaa064 | 1552 | |
aidan1971 | 10:49bb33f71d32 | 1553 | store: |
aidan1971 | 10:49bb33f71d32 | 1554 | STR BU_RX_L, [SGTL5000::BufRX_L_safe] // Pipeline memory access |
aidan1971 | 6:4ab5aaeaa064 | 1555 | STR BU_RX_R, [SGTL5000::BufRX_R_safe] |
aidan1971 | 9:40e0ff8c2ba2 | 1556 | STR DB_PHASE, [&SGTL5000::db_phase_rx] |
aidan1971 | 10:49bb33f71d32 | 1557 | B finish_update |
aidan1971 | 6:4ab5aaeaa064 | 1558 | |
aidan1971 | 10:49bb33f71d32 | 1559 | dual_codecs: |
aidan1971 | 6:4ab5aaeaa064 | 1560 | TEQ DB_PHASE, #0x0 |
aidan1971 | 6:4ab5aaeaa064 | 1561 | IT EQ |
aidan1971 | 10:49bb33f71d32 | 1562 | BEQ buf_base2 |
aidan1971 | 6:4ab5aaeaa064 | 1563 | |
aidan1971 | 10:49bb33f71d32 | 1564 | LDR BU_RX_L, [&SGTL5000::BufRX_L_safeB] |
aidan1971 | 10:49bb33f71d32 | 1565 | LDR BU_RX_R, [&SGTL5000::BufRX_R_safeB] |
aidan1971 | 10:49bb33f71d32 | 1566 | LDR BU_RX_L2, [&SGTL5000::BufRX_L_safeB2] |
aidan1971 | 10:49bb33f71d32 | 1567 | LDR BU_RX_R2, [&SGTL5000::BufRX_R_safeB2] |
aidan1971 | 6:4ab5aaeaa064 | 1568 | SUB DB_PHASE, #0x1 |
aidan1971 | 10:49bb33f71d32 | 1569 | B store2 |
aidan1971 | 6:4ab5aaeaa064 | 1570 | |
aidan1971 | 10:49bb33f71d32 | 1571 | buf_base2: |
aidan1971 | 10:49bb33f71d32 | 1572 | LDR BU_RX_L, [&SGTL5000::BufRX_L_safeA] |
aidan1971 | 10:49bb33f71d32 | 1573 | LDR BU_RX_R, [&SGTL5000::BufRX_R_safeA] |
aidan1971 | 10:49bb33f71d32 | 1574 | LDR BU_RX_L2, [&SGTL5000::BufRX_L_safeA2] |
aidan1971 | 10:49bb33f71d32 | 1575 | LDR BU_RX_R2, [&SGTL5000::BufRX_R_safeA2] |
aidan1971 | 6:4ab5aaeaa064 | 1576 | ADD DB_PHASE, #0x1 |
aidan1971 | 6:4ab5aaeaa064 | 1577 | |
aidan1971 | 10:49bb33f71d32 | 1578 | store2: |
aidan1971 | 10:49bb33f71d32 | 1579 | STR BU_RX_L, [SGTL5000::BufRX_L_safe] // Pipeline memory access |
aidan1971 | 6:4ab5aaeaa064 | 1580 | STR BU_RX_R, [SGTL5000::BufRX_R_safe] |
aidan1971 | 10:49bb33f71d32 | 1581 | STR BU_RX_L2, [SGTL5000::BufRX_L_safe2] |
aidan1971 | 10:49bb33f71d32 | 1582 | STR BU_RX_R2, [SGTL5000::BufRX_R_safe2] |
aidan1971 | 9:40e0ff8c2ba2 | 1583 | STR DB_PHASE, [&SGTL5000::db_phase_rx] |
aidan1971 | 10:49bb33f71d32 | 1584 | |
aidan1971 | 10:49bb33f71d32 | 1585 | finish_update: |
aidan1971 | 10:49bb33f71d32 | 1586 | TEQ TYPE, #1 |
aidan1971 | 10:49bb33f71d32 | 1587 | IT EQ |
aidan1971 | 10:49bb33f71d32 | 1588 | BEQ call_rx_nb |
aidan1971 | 6:4ab5aaeaa064 | 1589 | } |
aidan1971 | 10:49bb33f71d32 | 1590 | SGTL5000::RX_user_func.call(); |
aidan1971 | 10:49bb33f71d32 | 1591 | __asm { |
aidan1971 | 10:49bb33f71d32 | 1592 | B finish_isr |
aidan1971 | 10:49bb33f71d32 | 1593 | |
aidan1971 | 10:49bb33f71d32 | 1594 | call_rx_nb: |
aidan1971 | 10:49bb33f71d32 | 1595 | LDRB IRQNR, [&SGTL5000::RX_swIRQ] |
aidan1971 | 10:49bb33f71d32 | 1596 | BL NVIC_GetActive, {r0=IRQNR}, {DSP_BUSY=r0} |
aidan1971 | 10:49bb33f71d32 | 1597 | TEQ DSP_BUSY, #1 |
aidan1971 | 10:49bb33f71d32 | 1598 | IT EQ |
aidan1971 | 10:49bb33f71d32 | 1599 | BEQ finish_isr |
aidan1971 | 10:49bb33f71d32 | 1600 | STR IRQNR, [&NVIC->STIR] // Trigger swIRQ only if previous call completed. This will keep things flowing, but if the irq is still active, good luck with the pointers! |
aidan1971 | 10:49bb33f71d32 | 1601 | |
aidan1971 | 10:49bb33f71d32 | 1602 | finish_isr: |
aidan1971 | 10:49bb33f71d32 | 1603 | |
aidan1971 | 10:49bb33f71d32 | 1604 | } |
aidan1971 | 0:8f28f25e3435 | 1605 | } |
aidan1971 | 0:8f28f25e3435 | 1606 | |
aidan1971 | 0:8f28f25e3435 | 1607 | void SGTL5000::init_DMA(void) |
aidan1971 | 0:8f28f25e3435 | 1608 | { |
aidan1971 | 3:62c03088f256 | 1609 | /*! |
aidan1971 | 0:8f28f25e3435 | 1610 | 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. |
aidan1971 | 0:8f28f25e3435 | 1611 | The chain is configured as follows: |
aidan1971 | 0:8f28f25e3435 | 1612 | 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. |
aidan1971 | 4:91354c908416 | 1613 | 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 |
aidan1971 | 0:8f28f25e3435 | 1614 | uppper half of the double buffer. |
aidan1971 | 0:8f28f25e3435 | 1615 | eg. |
aidan1971 | 4:91354c908416 | 1616 | Unpacked buffer layout: |
aidan1971 | 4:91354c908416 | 1617 | Block Size = 2 Block Size = 4 Block Size = 8 |
aidan1971 | 4:91354c908416 | 1618 | Double Buffer A Double Buffer B Double Buffer A Double Buffer B Double Buffer A Double Buffer B |
aidan1971 | 4:91354c908416 | 1619 | |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| |
aidan1971 | 6:4ab5aaeaa064 | 1620 | |
aidan1971 | 4:91354c908416 | 1621 | Packed buffer layout: |
aidan1971 | 4:91354c908416 | 1622 | Block Size = 2 Block Size = 4 Block Size = 8 |
aidan1971 | 4:91354c908416 | 1623 | Double Buffer A Double Buffer B Double Buffer A Double Buffer B Double Buffer A Double Buffer B |
aidan1971 | 4:91354c908416 | 1624 | |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| |
aidan1971 | 6:4ab5aaeaa064 | 1625 | |
aidan1971 | 4:91354c908416 | 1626 | The users pointers are always updated to point to L0 & R0 of the current safe double buffer area. |
aidan1971 | 0:8f28f25e3435 | 1627 | |
aidan1971 | 10:49bb33f71d32 | 1628 | If unpacked and TX or RX shift option is specified the 16 LSBits of each word are shifted to the 16MSB bits of each word |
aidan1971 | 10:49bb33f71d32 | 1629 | |
aidan1971 | 0:8f28f25e3435 | 1630 | */ |
aidan1971 | 0:8f28f25e3435 | 1631 | |
aidan1971 | 10:49bb33f71d32 | 1632 | static uint32_t SG_rx_TCD_A[8] __attribute ((aligned (0x20)));// __attribute__((at(0x14000500))); // Allocate memory for scatter gather TCD definitions |
aidan1971 | 10:49bb33f71d32 | 1633 | static uint32_t SG_rx_TCD_B[8] __attribute ((aligned (0x20)));// __attribute__((at(0x14000520))); |
aidan1971 | 10:49bb33f71d32 | 1634 | static uint32_t SG_tx_TCD_A[8] __attribute ((aligned (0x20)));// __attribute__((at(0x14000540))); |
aidan1971 | 10:49bb33f71d32 | 1635 | static uint32_t SG_tx_TCD_B[8] __attribute ((aligned (0x20)));// __attribute__((at(0x14000560))); |
aidan1971 | 10:49bb33f71d32 | 1636 | static uint32_t SG_rx_TCD_A2[8] __attribute ((aligned (0x20)));// __attribute__((at(0x14000580))); // Allocate memory for scatter gather TCD2 definitions |
aidan1971 | 10:49bb33f71d32 | 1637 | static uint32_t SG_rx_TCD_B2[8] __attribute ((aligned (0x20)));// __attribute__((at(0x140005A0))); |
aidan1971 | 10:49bb33f71d32 | 1638 | static uint32_t SG_tx_TCD_A2[8] __attribute ((aligned (0x20)));// __attribute__((at(0x140005C0))); |
aidan1971 | 10:49bb33f71d32 | 1639 | static uint32_t SG_tx_TCD_B2[8] __attribute ((aligned (0x20)));// __attribute__((at(0x140005E0))); |
aidan1971 | 0:8f28f25e3435 | 1640 | |
aidan1971 | 0:8f28f25e3435 | 1641 | // Clock Control config to DMA Controller and DMAMux and common DMA Setup |
aidan1971 | 0:8f28f25e3435 | 1642 | SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK; // Enable Clocking to DMAMUX Module |
aidan1971 | 0:8f28f25e3435 | 1643 | SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Enable Clocking to DMA Module |
aidan1971 | 0:8f28f25e3435 | 1644 | DMA0->CR |= DMA_CR_EMLM_MASK; // Enable minor loop mapping |
aidan1971 | 0:8f28f25e3435 | 1645 | |
aidan1971 | 10:49bb33f71d32 | 1646 | if(SGTL5000::SYNC_run || SGTL5000::RX_run) { |
aidan1971 | 0:8f28f25e3435 | 1647 | // DMAMUX Config |
aidan1971 | 0:8f28f25e3435 | 1648 | DMAMUX->CHCFG[SGTL5000::RX_DMAch] &= ~DMAMUX_CHCFG_ENBL_MASK; // MUX channels disabled while we configure |
aidan1971 | 0:8f28f25e3435 | 1649 | DMAMUX->CHCFG[SGTL5000::RX_DMAch] &= ~DMAMUX_CHCFG_TRIG_MASK; // Trigger Modes Normal |
aidan1971 | 10:49bb33f71d32 | 1650 | DMAMUX->CHCFG[SGTL5000::RX_DMAch] = (DMAMUX->CHCFG[SGTL5000::RX_DMAch] & ~DMAMUX_CHCFG_SOURCE_MASK) | DMAMUX_CHCFG_SOURCE(14); // Route I2S FIFO RX request to DMA. i2s0 RX =14 TX = 15 |
aidan1971 | 10:49bb33f71d32 | 1651 | DMA0->CERQ = DMA_CERQ_CERQ(SGTL5000::RX_DMAch); // Disable requests for chosen DMA channel first while its configured |
aidan1971 | 10:49bb33f71d32 | 1652 | //if(SGTL5000::codec2_active) DMA0->CERQ = DMA_CERQ_CERQ(RX_DMAch2); |
aidan1971 | 0:8f28f25e3435 | 1653 | |
aidan1971 | 10:49bb33f71d32 | 1654 | //Configure RX DMA Transfer Control Descriptor for channel 0 |
aidan1971 | 0:8f28f25e3435 | 1655 | DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES &= ~DMA_NBYTES_MLOFFYES_SMLOE_MASK; // Disable source offset on minor loop |
aidan1971 | 0:8f28f25e3435 | 1656 | DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_DMLOE_MASK; // Enable dest offset on minor loop |
aidan1971 | 4:91354c908416 | 1657 | 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 |
aidan1971 | 4:91354c908416 | 1658 | 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 |
aidan1971 | 0:8f28f25e3435 | 1659 | 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. |
aidan1971 | 0:8f28f25e3435 | 1660 | DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_DONE_MASK; // Clear channels CSR DONE bit to allow configuration of linkng fields in CSR register |
aidan1971 | 0:8f28f25e3435 | 1661 | DMA0->TCD[SGTL5000::RX_DMAch].CSR = (DMA0->TCD[SGTL5000::RX_DMAch].CSR & ~DMA_CSR_BWC_MASK) | DMA_CSR_BWC(0); // Disable Bandwidth control |
aidan1971 | 0:8f28f25e3435 | 1662 | DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_ESG_MASK; // Enable scatter gather |
aidan1971 | 0:8f28f25e3435 | 1663 | DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_DREQ_MASK; // Disable auto reset of request enable on major |
aidan1971 | 0:8f28f25e3435 | 1664 | DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_INTHALF_MASK; // Disable IRQ at Half Completion of Major cycle |
aidan1971 | 0:8f28f25e3435 | 1665 | 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 |
aidan1971 | 0:8f28f25e3435 | 1666 | DMA0->TCD[SGTL5000::RX_DMAch].SOFF = 0; // Signed Source offset set to zero (always read from RDR[0]). |
aidan1971 | 4:91354c908416 | 1667 | DMA0->TCD[SGTL5000::RX_DMAch].DOFF = (RX_bs_bytes / 2); // After each write step into upper half of the buffer |
aidan1971 | 0:8f28f25e3435 | 1668 | DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO &= ~DMA_CITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop |
aidan1971 | 6:4ab5aaeaa064 | 1669 | DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO = (DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO & ~DMA_CITER_ELINKNO_CITER_MASK) | DMA_CITER_ELINKNO_CITER(RX_block_size / 2); // Major loop current iter count starting value |
aidan1971 | 0:8f28f25e3435 | 1670 | DMA0->TCD[SGTL5000::RX_DMAch].BITER_ELINKNO &= ~DMA_BITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop |
aidan1971 | 6:4ab5aaeaa064 | 1671 | DMA0->TCD[SGTL5000::RX_DMAch].BITER_ELINKNO = (DMA0->TCD[SGTL5000::RX_DMAch].BITER_ELINKNO & ~DMA_BITER_ELINKNO_BITER_MASK) | DMA_BITER_ELINKNO_BITER(RX_block_size / 2); // Major loop iter count to load again at after major completes |
aidan1971 | 10:49bb33f71d32 | 1672 | DMA0->TCD[SGTL5000::RX_DMAch].CSR = (DMA0->TCD[SGTL5000::RX_DMAch].CSR & ~DMA_CSR_MAJORLINKCH_MASK) | DMA_CSR_MAJORLINKCH(SGTL5000::RX_DMAch); // Major link to second channel |
aidan1971 | 0:8f28f25e3435 | 1673 | DMA0->TCD[SGTL5000::RX_DMAch].SLAST = 0; |
aidan1971 | 10:49bb33f71d32 | 1674 | |
aidan1971 | 10:49bb33f71d32 | 1675 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 1676 | DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_INTMAJOR_MASK; // Disable IRQ at Completion of Major cycle irq generated by major complete on second channel |
aidan1971 | 10:49bb33f71d32 | 1677 | DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_MAJORELINK_MASK; // Enable major loop linking |
aidan1971 | 10:49bb33f71d32 | 1678 | DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_A2[0]; // Set scatter gather TCD definition location to transfer channel 2 |
aidan1971 | 10:49bb33f71d32 | 1679 | |
aidan1971 | 10:49bb33f71d32 | 1680 | } else { |
aidan1971 | 10:49bb33f71d32 | 1681 | DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; // Enable IRQ at Completion of Major cycle for first channel |
aidan1971 | 10:49bb33f71d32 | 1682 | DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; // Disable major loop linking |
aidan1971 | 10:49bb33f71d32 | 1683 | DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B[0]; // Set scatter gather TCD definition location |
aidan1971 | 10:49bb33f71d32 | 1684 | } |
aidan1971 | 10:49bb33f71d32 | 1685 | if(!packed_RX && RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0] + 2; |
aidan1971 | 10:49bb33f71d32 | 1686 | else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0]; |
aidan1971 | 10:49bb33f71d32 | 1687 | DMA0->TCD[SGTL5000::RX_DMAch].SADDR = (uint32_t)&I2S0->RDR[0]; // Set rxDMA Source addr pointer to 1st bit of I2S RX Data Reg |
aidan1971 | 0:8f28f25e3435 | 1688 | memcpy(&SG_rx_TCD_A[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32); // Copy TCD A to memory |
aidan1971 | 10:49bb33f71d32 | 1689 | DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B[0]; // Set scatter gather TCD definition location |
aidan1971 | 10:49bb33f71d32 | 1690 | if(!packed_RX && RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0] + 2; |
aidan1971 | 10:49bb33f71d32 | 1691 | else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer2[0]; |
aidan1971 | 10:49bb33f71d32 | 1692 | DMA0->TCD[SGTL5000::RX_DMAch].SADDR = (uint32_t)&I2S0->RDR[1]; |
aidan1971 | 10:49bb33f71d32 | 1693 | DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; // Enable IRQ at Completion of Major cycle for first channel |
aidan1971 | 10:49bb33f71d32 | 1694 | DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; |
aidan1971 | 10:49bb33f71d32 | 1695 | memcpy(&SG_rx_TCD_A2[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32); // Copy TCD B to memory |
aidan1971 | 0:8f28f25e3435 | 1696 | // Set TCD elements in the DMA controller back to initial TCD A |
aidan1971 | 10:49bb33f71d32 | 1697 | if(SGTL5000::codec2_active) { |
aidan1971 | 10:49bb33f71d32 | 1698 | DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_INTMAJOR_MASK; // Disable IRQ at Completion of Major cycle irq generated by major complete on second channel |
aidan1971 | 10:49bb33f71d32 | 1699 | DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_MAJORELINK_MASK; // Enable major loop linking |
aidan1971 | 10:49bb33f71d32 | 1700 | DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B2[0]; // Set scatter gather TCD definition location to transfer channel 2 |
aidan1971 | 10:49bb33f71d32 | 1701 | |
aidan1971 | 10:49bb33f71d32 | 1702 | } else { |
aidan1971 | 10:49bb33f71d32 | 1703 | DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; // Enable IRQ at Completion of Major cycle for first channel |
aidan1971 | 10:49bb33f71d32 | 1704 | DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; // Disable major loop linking |
aidan1971 | 10:49bb33f71d32 | 1705 | DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_A[0]; // Set scatter gather TCD definition location |
aidan1971 | 10:49bb33f71d32 | 1706 | } // Set scatter gather TCD definition location |
aidan1971 | 10:49bb33f71d32 | 1707 | if(!packed_RX && RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[8] + 2; |
aidan1971 | 10:49bb33f71d32 | 1708 | else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[8]; |
aidan1971 | 10:49bb33f71d32 | 1709 | DMA0->TCD[SGTL5000::RX_DMAch].SADDR = (uint32_t)&I2S0->RDR[0]; |
aidan1971 | 10:49bb33f71d32 | 1710 | memcpy(&SG_rx_TCD_B[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32); |
aidan1971 | 10:49bb33f71d32 | 1711 | DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; // Enable IRQ at Completion of Major cycle for first channel |
aidan1971 | 10:49bb33f71d32 | 1712 | DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; |
aidan1971 | 10:49bb33f71d32 | 1713 | if(!packed_RX && RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8] + 2; |
aidan1971 | 10:49bb33f71d32 | 1714 | else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer2[8]; |
aidan1971 | 10:49bb33f71d32 | 1715 | DMA0->TCD[SGTL5000::RX_DMAch].SADDR = (uint32_t)&I2S0->RDR[1]; |
aidan1971 | 10:49bb33f71d32 | 1716 | DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_A[0]; |
aidan1971 | 10:49bb33f71d32 | 1717 | memcpy(&SG_rx_TCD_B2[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32); |
aidan1971 | 10:49bb33f71d32 | 1718 | |
aidan1971 | 10:49bb33f71d32 | 1719 | memcpy((void*)&DMA0->TCD[SGTL5000::RX_DMAch], &SG_rx_TCD_A[0], 32); // Initialise RX TCD to first SG definition |
aidan1971 | 0:8f28f25e3435 | 1720 | } |
aidan1971 | 0:8f28f25e3435 | 1721 | |
aidan1971 | 10:49bb33f71d32 | 1722 | if(SGTL5000::SYNC_run || SGTL5000::TX_run) { |
aidan1971 | 0:8f28f25e3435 | 1723 | DMAMUX->CHCFG[SGTL5000::TX_DMAch] &= ~DMAMUX_CHCFG_ENBL_MASK; |
aidan1971 | 0:8f28f25e3435 | 1724 | DMAMUX->CHCFG[SGTL5000::TX_DMAch] &= ~DMAMUX_CHCFG_TRIG_MASK; |
aidan1971 | 10:49bb33f71d32 | 1725 | DMAMUX->CHCFG[SGTL5000::TX_DMAch] = (DMAMUX->CHCFG[SGTL5000::TX_DMAch] & ~DMAMUX_CHCFG_SOURCE_MASK) | DMAMUX_CHCFG_SOURCE(15); // Route I2S FIFO TX request to DMA. |
aidan1971 | 10:49bb33f71d32 | 1726 | DMA0->CERQ = DMA_CERQ_CERQ(SGTL5000::TX_DMAch); // Disable requests for first DMA channel while its configured. |
aidan1971 | 0:8f28f25e3435 | 1727 | |
aidan1971 | 10:49bb33f71d32 | 1728 | //Configure TX DMA Transfer Control Descriptor channel 1 |
aidan1971 | 10:49bb33f71d32 | 1729 | DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_SMLOE_MASK; // Enable source offset on minor loop |
aidan1971 | 10:49bb33f71d32 | 1730 | DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES &= ~DMA_NBYTES_MLOFFYES_DMLOE_MASK; // Disable dest offset on minor loop |
aidan1971 | 4:91354c908416 | 1731 | 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)); |
aidan1971 | 4:91354c908416 | 1732 | 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 |
aidan1971 | 0:8f28f25e3435 | 1733 | 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. |
aidan1971 | 0:8f28f25e3435 | 1734 | DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_DONE_MASK; // Clear channels CSR DONE bit to allow configuration of linkng fields in CSR register |
aidan1971 | 0:8f28f25e3435 | 1735 | DMA0->TCD[SGTL5000::TX_DMAch].CSR = (DMA0->TCD[SGTL5000::TX_DMAch].CSR & ~DMA_CSR_BWC_MASK) | DMA_CSR_BWC(0); // Disable Bandwidth control |
aidan1971 | 0:8f28f25e3435 | 1736 | DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_ESG_MASK; // Enable scatter gather |
aidan1971 | 0:8f28f25e3435 | 1737 | DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_DREQ_MASK; // Disable auto reset of request enable on major |
aidan1971 | 10:49bb33f71d32 | 1738 | DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_INTHALF_MASK; |
aidan1971 | 10:49bb33f71d32 | 1739 | DMA0->TCD[SGTL5000::TX_DMAch].CSR = (DMA0->TCD[SGTL5000::TX_DMAch].CSR & ~DMA_CSR_MAJORLINKCH_MASK) | DMA_CSR_MAJORLINKCH(SGTL5000::TX_DMAch); |
aidan1971 | 0:8f28f25e3435 | 1740 | 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 |
aidan1971 | 10:49bb33f71d32 | 1741 | DMA0->TCD[SGTL5000::TX_DMAch].DOFF = 0; // Signed Dest offset set to zero (always write TDR[0]). |
aidan1971 | 0:8f28f25e3435 | 1742 | DMA0->TCD[SGTL5000::TX_DMAch].SOFF = (TX_bs_bytes / 2); // After each write step into upper half of the buffer |
aidan1971 | 0:8f28f25e3435 | 1743 | DMA0->TCD[SGTL5000::TX_DMAch].CITER_ELINKNO &= ~DMA_CITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop |
aidan1971 | 6:4ab5aaeaa064 | 1744 | DMA0->TCD[SGTL5000::TX_DMAch].CITER_ELINKNO = (DMA0->TCD[SGTL5000::TX_DMAch].CITER_ELINKNO & ~DMA_CITER_ELINKNO_CITER_MASK) | DMA_CITER_ELINKNO_CITER(TX_block_size / 2); // Major loop current iter count starting value |
aidan1971 | 0:8f28f25e3435 | 1745 | DMA0->TCD[SGTL5000::TX_DMAch].BITER_ELINKNO &= ~DMA_BITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop |
aidan1971 | 6:4ab5aaeaa064 | 1746 | DMA0->TCD[SGTL5000::TX_DMAch].BITER_ELINKNO = (DMA0->TCD[SGTL5000::TX_DMAch].BITER_ELINKNO & ~DMA_BITER_ELINKNO_BITER_MASK) | DMA_BITER_ELINKNO_BITER(TX_block_size / 2); // Major loop iter count to load again at after major completes // Reset dest addr to start address. |
aidan1971 | 10:49bb33f71d32 | 1747 | if(SGTL5000::codec2_active) { // Disable IRQ at Half Completion of Major cycle |
aidan1971 | 10:49bb33f71d32 | 1748 | DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_INTMAJOR_MASK; |
aidan1971 | 10:49bb33f71d32 | 1749 | DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_MAJORELINK_MASK; |
aidan1971 | 10:49bb33f71d32 | 1750 | DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_A2[0]; // Enable IRQ at Completion of Major cycle for first channel |
aidan1971 | 10:49bb33f71d32 | 1751 | } else { |
aidan1971 | 10:49bb33f71d32 | 1752 | DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; |
aidan1971 | 10:49bb33f71d32 | 1753 | DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; |
aidan1971 | 10:49bb33f71d32 | 1754 | DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0]; |
aidan1971 | 10:49bb33f71d32 | 1755 | } |
aidan1971 | 10:49bb33f71d32 | 1756 | if(!packed_TX && TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0] + 2; |
aidan1971 | 10:49bb33f71d32 | 1757 | else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0]; |
aidan1971 | 10:49bb33f71d32 | 1758 | DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[0]; |
aidan1971 | 0:8f28f25e3435 | 1759 | memcpy(&SG_tx_TCD_A[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32); // Copy TCD A to memory |
aidan1971 | 0:8f28f25e3435 | 1760 | DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0]; // Set scatter gather TCD definition location |
aidan1971 | 10:49bb33f71d32 | 1761 | if(!packed_TX && TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0] + 2; |
aidan1971 | 10:49bb33f71d32 | 1762 | else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer2[0]; |
aidan1971 | 10:49bb33f71d32 | 1763 | DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[1]; |
aidan1971 | 10:49bb33f71d32 | 1764 | DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; |
aidan1971 | 10:49bb33f71d32 | 1765 | DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; |
aidan1971 | 10:49bb33f71d32 | 1766 | memcpy(&SG_tx_TCD_A2[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32); // Copy TCD B to memory |
aidan1971 | 10:49bb33f71d32 | 1767 | if(!packed_TX && TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[8] + 2; |
aidan1971 | 10:49bb33f71d32 | 1768 | else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[8]; |
aidan1971 | 10:49bb33f71d32 | 1769 | DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[0]; |
aidan1971 | 10:49bb33f71d32 | 1770 | if(SGTL5000::codec2_active) { // Disable IRQ at Half Completion of Major cycle |
aidan1971 | 10:49bb33f71d32 | 1771 | DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_INTMAJOR_MASK; |
aidan1971 | 10:49bb33f71d32 | 1772 | DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_MAJORELINK_MASK; |
aidan1971 | 10:49bb33f71d32 | 1773 | DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B2[0]; // Enable IRQ at Completion of Major cycle for first channel |
aidan1971 | 10:49bb33f71d32 | 1774 | } else { |
aidan1971 | 10:49bb33f71d32 | 1775 | DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; |
aidan1971 | 10:49bb33f71d32 | 1776 | DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; |
aidan1971 | 10:49bb33f71d32 | 1777 | DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_A[0]; |
aidan1971 | 10:49bb33f71d32 | 1778 | } |
aidan1971 | 10:49bb33f71d32 | 1779 | memcpy(&SG_tx_TCD_B[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32); |
aidan1971 | 10:49bb33f71d32 | 1780 | DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_A[0]; |
aidan1971 | 10:49bb33f71d32 | 1781 | if(!packed_TX && TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8] + 2; |
aidan1971 | 10:49bb33f71d32 | 1782 | else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer2[8]; |
aidan1971 | 10:49bb33f71d32 | 1783 | DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[1]; |
aidan1971 | 10:49bb33f71d32 | 1784 | DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; |
aidan1971 | 10:49bb33f71d32 | 1785 | DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; |
aidan1971 | 10:49bb33f71d32 | 1786 | memcpy(&SG_tx_TCD_B2[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32); |
aidan1971 | 10:49bb33f71d32 | 1787 | |
aidan1971 | 10:49bb33f71d32 | 1788 | memcpy((void*)&DMA0->TCD[SGTL5000::TX_DMAch], &SG_tx_TCD_A[0], 32); // Initialise TX TCD to first SG definition |
aidan1971 | 0:8f28f25e3435 | 1789 | } |
aidan1971 | 0:8f28f25e3435 | 1790 | |
aidan1971 | 10:49bb33f71d32 | 1791 | if(SGTL5000::SYNC_run || SGTL5000::RX_run) { |
aidan1971 | 10:49bb33f71d32 | 1792 | DMA0->SERQ = DMA_SERQ_SERQ(SGTL5000::RX_DMAch); // Enable requests for RX DMA channel 0 |
aidan1971 | 0:8f28f25e3435 | 1793 | DMAMUX->CHCFG[SGTL5000::RX_DMAch] |= DMAMUX_CHCFG_ENBL_MASK; // DMAMUX channel enabled. Pass DMA requests from I2S to the DMA controller. |
aidan1971 | 0:8f28f25e3435 | 1794 | } |
aidan1971 | 10:49bb33f71d32 | 1795 | if(SGTL5000::SYNC_run || SGTL5000::TX_run) { |
aidan1971 | 0:8f28f25e3435 | 1796 | DMA0->SERQ = DMA_SERQ_SERQ(SGTL5000::TX_DMAch); // Enable requests for TX DMA channel |
aidan1971 | 10:49bb33f71d32 | 1797 | DMAMUX->CHCFG[SGTL5000::TX_DMAch] |= DMAMUX_CHCFG_ENBL_MASK; // DMAMUX channel enabled. Pass DMA requests from I2S to the DMA controller. |
aidan1971 | 0:8f28f25e3435 | 1798 | } |
aidan1971 | 0:8f28f25e3435 | 1799 | } |
aidan1971 | 0:8f28f25e3435 | 1800 | |
aidan1971 | 10:49bb33f71d32 | 1801 | int32_t SGTL5000::read_debug(uint32_t index, bool finished) |
aidan1971 | 0:8f28f25e3435 | 1802 | { |
aidan1971 | 10:49bb33f71d32 | 1803 | if(finished) SGTL5000::debug_read = true; |
aidan1971 | 0:8f28f25e3435 | 1804 | return SGTL5000::debug[index]; |
aidan1971 | 0:8f28f25e3435 | 1805 | }; |
aidan1971 | 10:49bb33f71d32 | 1806 | |
aidan1971 | 10:49bb33f71d32 | 1807 | // Time stamp functions |
aidan1971 | 10:49bb33f71d32 | 1808 | |
aidan1971 | 10:49bb33f71d32 | 1809 | void SGTL5000::t_stamp_start(void) |
aidan1971 | 10:49bb33f71d32 | 1810 | { |
aidan1971 | 10:49bb33f71d32 | 1811 | SGTL5000::t1 = *(uint32_t*)SGTL5000::SYST_CVAL; |
aidan1971 | 10:49bb33f71d32 | 1812 | } |
aidan1971 | 10:49bb33f71d32 | 1813 | |
aidan1971 | 10:49bb33f71d32 | 1814 | void SGTL5000::t_stamp_stop(void) |
aidan1971 | 10:49bb33f71d32 | 1815 | { |
aidan1971 | 10:49bb33f71d32 | 1816 | SGTL5000::proc_time = SGTL5000::t1 - *(uint32_t*)SGTL5000::SYST_CVAL; |
aidan1971 | 10:49bb33f71d32 | 1817 | } |
aidan1971 | 10:49bb33f71d32 | 1818 | |
aidan1971 | 0:8f28f25e3435 | 1819 | } |