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.

Committer:
aidan1971
Date:
Sat Jul 15 13:15:08 2017 +0000
Revision:
9:40e0ff8c2ba2
Parent:
8:9fdf8501d14b
Child:
10:49bb33f71d32
Changed the stop functions to suspend IRQs and DMA requests, also the codec's I2S interface is disabled. During start, the MCUs I2S state machine is now reset, before acquiring sync.

Who changed what in which revision?

UserRevisionLine numberNew 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 0:8f28f25e3435 33 uint32_t *SGTL5000::BufRX_L_safe; // Private pointers assigned to users data pointers
aidan1971 0:8f28f25e3435 34 uint32_t *SGTL5000::BufRX_R_safe; // These are used to flip user pointers between safe 'active' regions of
aidan1971 0:8f28f25e3435 35 uint32_t *SGTL5000::BufTX_L_safe; // double buffered space.
aidan1971 0:8f28f25e3435 36 uint32_t *SGTL5000::BufTX_R_safe;
aidan1971 6:4ab5aaeaa064 37 IRQn SGTL5000::SYNC_swIRQ; // IRQn assigned by user to the software IRQ triggered by the FIFO queues.
aidan1971 0:8f28f25e3435 38 IRQn SGTL5000::TX_swIRQ;
aidan1971 0:8f28f25e3435 39 IRQn SGTL5000::RX_swIRQ;
aidan1971 6:4ab5aaeaa064 40 uint32_t SGTL5000::RX_DMAch; // User defined RX DMA channel number
aidan1971 6:4ab5aaeaa064 41 uint32_t SGTL5000::TX_DMAch; // User defined TX DMA channel number
aidan1971 0:8f28f25e3435 42 Callback<void()> SGTL5000::TX_user_func = NULL;
aidan1971 0:8f28f25e3435 43 Callback<void()> SGTL5000::RX_user_func = NULL;
aidan1971 6:4ab5aaeaa064 44 Callback<void()> SGTL5000::SYNC_user_func = NULL; // User defined callback function
aidan1971 9:40e0ff8c2ba2 45 uint32_t SGTL5000::db_phase_sync;
aidan1971 9:40e0ff8c2ba2 46 uint32_t SGTL5000::db_phase_tx;
aidan1971 9:40e0ff8c2ba2 47 uint32_t SGTL5000::db_phase_rx;
aidan1971 0:8f28f25e3435 48
aidan1971 6:4ab5aaeaa064 49 uint32_t SGTL5000::debug[16] = {0};
aidan1971 0:8f28f25e3435 50
aidan1971 9:40e0ff8c2ba2 51 SGTL5000::SGTL5000(PinName i2c_sda, PinName i2c_scl, int _i2c_freq, bool i2c_ctrl_adr0_cs)
aidan1971 9:40e0ff8c2ba2 52 : mI2C(i2c_sda, i2c_scl), i2c_freq(_i2c_freq)
aidan1971 0:8f28f25e3435 53 {
aidan1971 0:8f28f25e3435 54 if(i2c_ctrl_adr0_cs) i2c_addr = SGTL5000_I2C_ADDR_CS_HIGH << 1;
aidan1971 0:8f28f25e3435 55 else i2c_addr = SGTL5000_I2C_ADDR_CS_LOW << 1;
aidan1971 8:9fdf8501d14b 56 codec_configured = false;
aidan1971 0:8f28f25e3435 57 SYNC_run = false;
aidan1971 0:8f28f25e3435 58 TX_run = false;
aidan1971 0:8f28f25e3435 59 RX_run = false;
aidan1971 0:8f28f25e3435 60 SYNC_attached = false;
aidan1971 0:8f28f25e3435 61 TX_attached = false;
aidan1971 0:8f28f25e3435 62 RX_attached = false;
aidan1971 0:8f28f25e3435 63 }
aidan1971 0:8f28f25e3435 64
aidan1971 8:9fdf8501d14b 65 int32_t SGTL5000::init(void)
aidan1971 8:9fdf8501d14b 66 {
aidan1971 9:40e0ff8c2ba2 67 init_i2s();
aidan1971 9:40e0ff8c2ba2 68 mI2C.frequency(i2c_freq);
aidan1971 9:40e0ff8c2ba2 69 int32_t error = init_codec();
aidan1971 9:40e0ff8c2ba2 70 if(error) return error;
aidan1971 8:9fdf8501d14b 71 else codec_configured = true;
aidan1971 9:40e0ff8c2ba2 72
aidan1971 8:9fdf8501d14b 73 return 0;
aidan1971 8:9fdf8501d14b 74 }
aidan1971 8:9fdf8501d14b 75
aidan1971 0:8f28f25e3435 76 int32_t SGTL5000::freq(uint32_t rate)
aidan1971 0:8f28f25e3435 77 {
aidan1971 8:9fdf8501d14b 78 if(!codec_configured) return -1;
aidan1971 0:8f28f25e3435 79 uint32_t I2S_MCLK_M;
aidan1971 0:8f28f25e3435 80 uint32_t I2S_MCLK_D;
aidan1971 0:8f28f25e3435 81 uint32_t codec_SYS_FS;
aidan1971 0:8f28f25e3435 82 uint32_t codec_RATE_MODE;
aidan1971 0:8f28f25e3435 83 switch(rate) {
aidan1971 0:8f28f25e3435 84 case 8:
aidan1971 0:8f28f25e3435 85 I2S_MCLK_M = 16;
aidan1971 0:8f28f25e3435 86 I2S_MCLK_D = 750;
aidan1971 0:8f28f25e3435 87 codec_SYS_FS = 0x02;
aidan1971 0:8f28f25e3435 88 codec_RATE_MODE = 0x01;
aidan1971 0:8f28f25e3435 89 break;
aidan1971 0:8f28f25e3435 90 case 11:
aidan1971 0:8f28f25e3435 91 I2S_MCLK_M = 1;
aidan1971 0:8f28f25e3435 92 I2S_MCLK_D = 34;
aidan1971 0:8f28f25e3435 93 codec_SYS_FS = 0x01;
aidan1971 0:8f28f25e3435 94 codec_RATE_MODE = 0x01;
aidan1971 0:8f28f25e3435 95 break;
aidan1971 0:8f28f25e3435 96 case 12:
aidan1971 0:8f28f25e3435 97 I2S_MCLK_M = 16;
aidan1971 0:8f28f25e3435 98 I2S_MCLK_D = 500;
aidan1971 0:8f28f25e3435 99 codec_SYS_FS = 0x02;
aidan1971 0:8f28f25e3435 100 codec_RATE_MODE = 0x01;
aidan1971 0:8f28f25e3435 101 break;
aidan1971 0:8f28f25e3435 102 case 16:
aidan1971 0:8f28f25e3435 103 I2S_MCLK_M = 32;
aidan1971 0:8f28f25e3435 104 I2S_MCLK_D = 750;
aidan1971 0:8f28f25e3435 105 codec_SYS_FS = 0x00;
aidan1971 0:8f28f25e3435 106 codec_RATE_MODE = 0x01;
aidan1971 0:8f28f25e3435 107 break;
aidan1971 0:8f28f25e3435 108 case 22:
aidan1971 0:8f28f25e3435 109 I2S_MCLK_M = 1;
aidan1971 0:8f28f25e3435 110 I2S_MCLK_D = 17;
aidan1971 0:8f28f25e3435 111 codec_SYS_FS = 0x01;
aidan1971 0:8f28f25e3435 112 codec_RATE_MODE = 0x01;
aidan1971 0:8f28f25e3435 113 break;
aidan1971 0:8f28f25e3435 114 case 24:
aidan1971 0:8f28f25e3435 115 I2S_MCLK_M = 32;
aidan1971 0:8f28f25e3435 116 I2S_MCLK_D = 500;
aidan1971 0:8f28f25e3435 117 codec_SYS_FS = 0x02;
aidan1971 0:8f28f25e3435 118 codec_RATE_MODE = 0x01;
aidan1971 0:8f28f25e3435 119 break;
aidan1971 0:8f28f25e3435 120 case 32:
aidan1971 0:8f28f25e3435 121 I2S_MCLK_M = 64;
aidan1971 0:8f28f25e3435 122 I2S_MCLK_D = 750;
aidan1971 0:8f28f25e3435 123 codec_SYS_FS = 0x00;
aidan1971 0:8f28f25e3435 124 codec_RATE_MODE = 0x0;
aidan1971 0:8f28f25e3435 125 break;
aidan1971 0:8f28f25e3435 126 case 44:
aidan1971 0:8f28f25e3435 127 I2S_MCLK_M = 2;
aidan1971 0:8f28f25e3435 128 I2S_MCLK_D = 17;
aidan1971 0:8f28f25e3435 129 codec_SYS_FS = 0x01;
aidan1971 0:8f28f25e3435 130 codec_RATE_MODE = 0x0;
aidan1971 0:8f28f25e3435 131 break;
aidan1971 0:8f28f25e3435 132 case 48:
aidan1971 0:8f28f25e3435 133 I2S_MCLK_M = 32;
aidan1971 0:8f28f25e3435 134 I2S_MCLK_D = 250;
aidan1971 0:8f28f25e3435 135 codec_SYS_FS = 0x02;
aidan1971 0:8f28f25e3435 136 codec_RATE_MODE = 0x0;
aidan1971 0:8f28f25e3435 137 break;
aidan1971 0:8f28f25e3435 138 case 96:
aidan1971 9:40e0ff8c2ba2 139 switch(SystemCoreClock) {
aidan1971 9:40e0ff8c2ba2 140 case 96000000:
aidan1971 9:40e0ff8c2ba2 141 I2S_MCLK_M = 32;
aidan1971 9:40e0ff8c2ba2 142 I2S_MCLK_D = 125;
aidan1971 9:40e0ff8c2ba2 143 codec_SYS_FS = 0x03;
aidan1971 9:40e0ff8c2ba2 144 codec_RATE_MODE = 0x0;
aidan1971 9:40e0ff8c2ba2 145 break;
aidan1971 9:40e0ff8c2ba2 146 case 120000000:
aidan1971 9:40e0ff8c2ba2 147 I2S_MCLK_M = 32;
aidan1971 9:40e0ff8c2ba2 148 I2S_MCLK_D = 156;
aidan1971 9:40e0ff8c2ba2 149 codec_SYS_FS = 0x03;
aidan1971 9:40e0ff8c2ba2 150 codec_RATE_MODE = 0x0;
aidan1971 9:40e0ff8c2ba2 151 break;
aidan1971 9:40e0ff8c2ba2 152 }
aidan1971 4:91354c908416 153 case 144:
aidan1971 6:4ab5aaeaa064 154 switch(SystemCoreClock) {
aidan1971 6:4ab5aaeaa064 155 case 96000000:
aidan1971 6:4ab5aaeaa064 156 I2S_MCLK_M = 48;
aidan1971 6:4ab5aaeaa064 157 I2S_MCLK_D = 125;
aidan1971 6:4ab5aaeaa064 158 codec_SYS_FS = 0x03;
aidan1971 6:4ab5aaeaa064 159 codec_RATE_MODE = 0x0;
aidan1971 6:4ab5aaeaa064 160 break;
aidan1971 6:4ab5aaeaa064 161 case 120000000:
aidan1971 6:4ab5aaeaa064 162 I2S_MCLK_M = 48;
aidan1971 6:4ab5aaeaa064 163 I2S_MCLK_D = 156;
aidan1971 6:4ab5aaeaa064 164 codec_SYS_FS = 0x03;
aidan1971 6:4ab5aaeaa064 165 codec_RATE_MODE = 0x0;
aidan1971 6:4ab5aaeaa064 166 break;
aidan1971 6:4ab5aaeaa064 167 }
aidan1971 4:91354c908416 168 break;
aidan1971 9:40e0ff8c2ba2 169 case 192:
aidan1971 9:40e0ff8c2ba2 170 switch(SystemCoreClock) {
aidan1971 9:40e0ff8c2ba2 171 case 96000000: // Not officially supported by the codec, but it seems to work.
aidan1971 9:40e0ff8c2ba2 172 I2S_MCLK_M = 64;
aidan1971 9:40e0ff8c2ba2 173 I2S_MCLK_D = 125;
aidan1971 9:40e0ff8c2ba2 174 codec_SYS_FS = 0x03;
aidan1971 9:40e0ff8c2ba2 175 codec_RATE_MODE = 0x0;
aidan1971 9:40e0ff8c2ba2 176 break;
aidan1971 9:40e0ff8c2ba2 177 case 120000000:
aidan1971 9:40e0ff8c2ba2 178 I2S_MCLK_M = 64;
aidan1971 9:40e0ff8c2ba2 179 I2S_MCLK_D = 156;
aidan1971 9:40e0ff8c2ba2 180 codec_SYS_FS = 0x03;
aidan1971 9:40e0ff8c2ba2 181 codec_RATE_MODE = 0x0;
aidan1971 9:40e0ff8c2ba2 182 break;
aidan1971 9:40e0ff8c2ba2 183 }
aidan1971 0:8f28f25e3435 184 default:
aidan1971 0:8f28f25e3435 185 return -1;
aidan1971 0:8f28f25e3435 186 }
aidan1971 0:8f28f25e3435 187 if(modify_i2c(SGTL5000_CLK_CTRL, codec_SYS_FS, SGTL5000_CLK_CTRL_SYS_FS_MASK)) return -1; // Set CODEC clocking
aidan1971 0:8f28f25e3435 188 if(modify_i2c(SGTL5000_CLK_CTRL, codec_RATE_MODE, SGTL5000_CLK_CTRL_RATE_MODE_MASK)) return -1; // Set CODEC clocking
aidan1971 0:8f28f25e3435 189 while(I2S0->MCR & I2S_MCR_DUF_MASK); // Check for active changes then set I2S Clock Fractions
aidan1971 0:8f28f25e3435 190 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 191 return 0;
aidan1971 0:8f28f25e3435 192 }
aidan1971 0:8f28f25e3435 193
aidan1971 0:8f28f25e3435 194 int32_t SGTL5000::read_i2c(uint32_t reg_addr, uint32_t mask)
aidan1971 0:8f28f25e3435 195 {
aidan1971 7:d65476c153a4 196
aidan1971 0:8f28f25e3435 197 if(mask == 0x0 || mask > 0xFFFF) return -1;
aidan1971 0:8f28f25e3435 198 uint32_t shift;
aidan1971 0:8f28f25e3435 199 for (shift = 0; !(mask & (1<<shift)); ++shift) {};
aidan1971 0:8f28f25e3435 200 uint32_t wire_data;
aidan1971 0:8f28f25e3435 201 uint32_t wire_addr = __rev(reg_addr) >> 16;
aidan1971 7:d65476c153a4 202 mI2C.lock();
aidan1971 7:d65476c153a4 203 if(mI2C.write(i2c_addr, (char *)&wire_addr, 2, false)) {
aidan1971 7:d65476c153a4 204 mI2C.unlock();
aidan1971 7:d65476c153a4 205 return -1;
aidan1971 7:d65476c153a4 206 }
aidan1971 7:d65476c153a4 207 if(mI2C.read(i2c_addr, (char *)&wire_data, 2, false)) {
aidan1971 7:d65476c153a4 208 mI2C.unlock();
aidan1971 7:d65476c153a4 209 return -1;
aidan1971 7:d65476c153a4 210 }
aidan1971 0:8f28f25e3435 211 return ((__rev(wire_data) >> 16) & mask) >> shift;
aidan1971 0:8f28f25e3435 212 }
aidan1971 0:8f28f25e3435 213
aidan1971 0:8f28f25e3435 214 int32_t SGTL5000::write_i2c(uint32_t reg_addr, uint32_t data)
aidan1971 0:8f28f25e3435 215 {
aidan1971 0:8f28f25e3435 216 uint32_t wire_data = (__rev(reg_addr) >> 16) | (__rev(data));
aidan1971 7:d65476c153a4 217 mI2C.lock();
aidan1971 7:d65476c153a4 218 if(mI2C.write(i2c_addr, (char *)&wire_data, 4, false)) {
aidan1971 7:d65476c153a4 219 mI2C.unlock();
aidan1971 7:d65476c153a4 220 return -1;
aidan1971 7:d65476c153a4 221 }
aidan1971 0:8f28f25e3435 222 return 0;
aidan1971 0:8f28f25e3435 223 }
aidan1971 0:8f28f25e3435 224
aidan1971 0:8f28f25e3435 225 int32_t SGTL5000::modify_i2c(uint32_t reg_addr, uint32_t data, uint32_t mask)
aidan1971 0:8f28f25e3435 226 {
aidan1971 0:8f28f25e3435 227 if(mask == 0x0 || mask > 0xFFFF) return -1;
aidan1971 0:8f28f25e3435 228 uint32_t shift;
aidan1971 0:8f28f25e3435 229 for (shift = 0; !(mask & (1<<shift)); ++shift) {};
aidan1971 0:8f28f25e3435 230 uint32_t wire_data = (__rev(reg_addr) >> 16) | (__rev((read_i2c(reg_addr) & (~mask & 0xFFFF)) | ((data << shift) & mask)));
aidan1971 7:d65476c153a4 231 mI2C.lock();
aidan1971 7:d65476c153a4 232 if(mI2C.write(i2c_addr, (char *)&wire_data, 4, false)) {
aidan1971 7:d65476c153a4 233 mI2C.unlock();
aidan1971 7:d65476c153a4 234 return -1;
aidan1971 7:d65476c153a4 235 }
aidan1971 0:8f28f25e3435 236 return 0;
aidan1971 0:8f28f25e3435 237 }
aidan1971 0:8f28f25e3435 238
aidan1971 0:8f28f25e3435 239
aidan1971 0:8f28f25e3435 240 void SGTL5000::init_i2s(void)
aidan1971 0:8f28f25e3435 241 {
aidan1971 0:8f28f25e3435 242 //const int F_CPU = mcgpllfll_frequency(); // Get system Pll freq and Calculate. ** To Do. If not Teensy 3.1 or 3.2 **
aidan1971 0:8f28f25e3435 243 const uint32_t I2S_MCLK_MULT = 32; // 32 = 48Khz 2 = 44.1Khz
aidan1971 0:8f28f25e3435 244 const uint32_t I2S_MCLK_DIV = 250; // 250 = 48Khz 17 = 44.1Khz
aidan1971 0:8f28f25e3435 245
aidan1971 0:8f28f25e3435 246 // Configure System Clock Distribution for I2S
aidan1971 0:8f28f25e3435 247 SIM->SCGC5 |= SIM_SCGC5_PORTC_MASK; // Enable Clock to PORTC control module.
aidan1971 0:8f28f25e3435 248 SIM->SCGC6 |= SIM_SCGC6_I2S_MASK; // Enable Clocking to I2S Module
aidan1971 0:8f28f25e3435 249 SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Enable DMA Clock Gate Control
aidan1971 0:8f28f25e3435 250 SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK; // Enable DMA Mux Clock Gate Control
aidan1971 0:8f28f25e3435 251
aidan1971 0:8f28f25e3435 252 // Configure I2S Master Clocking
aidan1971 0:8f28f25e3435 253 I2S0->MCR |= I2S_MCR_MICS(3); // Configure I2S MCLK Divder Source as System PLL
aidan1971 0:8f28f25e3435 254 while(I2S0->MCR & I2S_MCR_DUF_MASK); // Check for active changes then set I2S Clock Fractions
aidan1971 0:8f28f25e3435 255 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 256 I2S0->MCR |= I2S_MCR_MOE_MASK; // Enable MCLK Output
aidan1971 0:8f28f25e3435 257
aidan1971 0:8f28f25e3435 258 // Configure PIN Muxing
aidan1971 0:8f28f25e3435 259
aidan1971 0:8f28f25e3435 260 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 261 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 262 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 263 PORTC->PCR[1] = (PORTC->PCR[1] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6); // Set PTC1(pin44)(Teensy pin22) <> I2S0_TXD0
aidan1971 0:8f28f25e3435 264 PORTC->PCR[5] = (PORTC->PCR[5] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(4); // Set PTC5(pin50)(Teensy pin13) <> I2S0_RXD0
aidan1971 0:8f28f25e3435 265
aidan1971 0:8f28f25e3435 266 // Config. TX
aidan1971 0:8f28f25e3435 267 I2S0->TMR = 0; // I2S TX Mask cleared.
aidan1971 0:8f28f25e3435 268 I2S0->TCR2 = (I2S0->TCR2 & ~I2S_TCR2_SYNC_MASK) | I2S_TCR2_SYNC(0); // Set TX Mode to Async
aidan1971 0:8f28f25e3435 269 I2S0->TCR2 |= I2S_TCR2_BCP_MASK; // Set TX Bit Clock Polarity sample riseing edge
aidan1971 0:8f28f25e3435 270 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 271 I2S0->TCR2 |= I2S_TCR2_BCD_MASK; // Set TX Clock Dir. internally generated
aidan1971 0:8f28f25e3435 272 I2S0->TCR2 = (I2S0->TCR2 & ~I2S_TCR2_DIV_MASK) | I2S_TCR2_DIV(3); // Set TX Bit Clock Divide
aidan1971 0:8f28f25e3435 273 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 274 I2S0->TCR4 = (I2S0->TCR4 & ~I2S_TCR4_FRSZ_MASK) | I2S_TCR4_FRSZ(1); // Set TX Frame Size @ 2 words per frame
aidan1971 0:8f28f25e3435 275 I2S0->TCR4 = (I2S0->TCR4 & ~I2S_TCR4_SYWD_MASK) | I2S_TCR4_SYWD(15); // Set TX frame sync width to 16bits
aidan1971 0:8f28f25e3435 276 I2S0->TCR4 |= I2S_TCR4_MF_MASK; // Set TX MSBit first
aidan1971 0:8f28f25e3435 277 I2S0->TCR4 |= I2S_TCR4_FSE_MASK; // Set TX Frame sync asserts before first bit
aidan1971 0:8f28f25e3435 278 I2S0->TCR4 |= I2S_TCR4_FSP_MASK; // Set TX Frame sync polarity active low.
aidan1971 0:8f28f25e3435 279 I2S0->TCR4 |= I2S_TCR4_FSD_MASK; // Set TX Frame sync direction from master clock
aidan1971 0:8f28f25e3435 280 I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_WNW_MASK) | I2S_TCR5_WNW(15); // Set TX to 16bits per word.
aidan1971 0:8f28f25e3435 281 I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_W0W_MASK) | I2S_TCR5_W0W(15); // Set TX to 16bits per first word.
aidan1971 0:8f28f25e3435 282 I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_FBT_MASK) | I2S_TCR5_FBT(15); // Set TX first bit transmitted index to 16th bit .
aidan1971 0:8f28f25e3435 283
aidan1971 0:8f28f25e3435 284 //Config RX
aidan1971 0:8f28f25e3435 285 I2S0->RMR = 0; // I2S RX Mask cleared.
aidan1971 0:8f28f25e3435 286 I2S0->RCR2 = (I2S0->RCR2 & ~I2S_RCR2_SYNC_MASK) | I2S_RCR2_SYNC(1); // Set RX Mode to Sync with TX
aidan1971 0:8f28f25e3435 287 I2S0->RCR2 |= I2S_TCR2_BCP_MASK; // Set RX Bit Clock Polarity sample riseing edge
aidan1971 0:8f28f25e3435 288 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 289 I2S0->RCR2 |= I2S_RCR2_BCD_MASK; // Set RX Clock Dir. internally generated
aidan1971 0:8f28f25e3435 290 I2S0->RCR2 = (I2S0->RCR2 & ~I2S_RCR2_DIV_MASK) | I2S_RCR2_DIV(3); // Set RX Bit Clock Divide
aidan1971 0:8f28f25e3435 291 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 292 I2S0->RCR4 = (I2S0->RCR4 & ~I2S_RCR4_FRSZ_MASK) | I2S_RCR4_FRSZ(1); // Set RX Frame Size @ 2 words per frame
aidan1971 0:8f28f25e3435 293 I2S0->RCR4 = (I2S0->RCR4 & ~I2S_RCR4_SYWD_MASK) | I2S_RCR4_SYWD(15); // Set RX frame sync width to 16bits
aidan1971 0:8f28f25e3435 294 I2S0->RCR4 |= I2S_RCR4_MF_MASK; // Set RX MSBit first
aidan1971 0:8f28f25e3435 295 I2S0->RCR4 |= I2S_RCR4_FSE_MASK; // Set RX Frame sync asserts before first bit
aidan1971 0:8f28f25e3435 296 I2S0->RCR4 |= I2S_RCR4_FSP_MASK; // Set RX Frame sync polarity active low.
aidan1971 0:8f28f25e3435 297 I2S0->RCR4 |= I2S_RCR4_FSD_MASK; // Set RX Frame sync direction from master clock
aidan1971 0:8f28f25e3435 298 I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_WNW_MASK) | I2S_RCR5_WNW(15); // Set RX to 16bits per word.
aidan1971 0:8f28f25e3435 299 I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_W0W_MASK) | I2S_RCR5_W0W(15); // Set RX to 16bits per first word.
aidan1971 0:8f28f25e3435 300 I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_FBT_MASK) | I2S_RCR5_FBT(15); // Set RX first bit transmitted index to 16 .
aidan1971 0:8f28f25e3435 301
aidan1971 0:8f28f25e3435 302 // Configure I2S Peripheral
aidan1971 0:8f28f25e3435 303 I2S0->TCSR |= I2S_TCSR_BCE_MASK; // Enable I2S Tx bit clock
aidan1971 0:8f28f25e3435 304 I2S0->RCSR |= I2S_RCSR_BCE_MASK; // Enable I2S Rx Bit Clock
aidan1971 0:8f28f25e3435 305
aidan1971 0:8f28f25e3435 306 }
aidan1971 0:8f28f25e3435 307
aidan1971 8:9fdf8501d14b 308 int32_t SGTL5000::init_codec(void)
aidan1971 0:8f28f25e3435 309 {
aidan1971 0:8f28f25e3435 310 // Default Configure Codec
aidan1971 8:9fdf8501d14b 311 if(
aidan1971 8:9fdf8501d14b 312 write_i2c(SGTL5000_ANA_POWER,(
aidan1971 8:9fdf8501d14b 313 (SGTL5000_ANA_POWER_DAC_MONO_MASK & (0x1 << SGTL5000_ANA_POWER_DAC_MONO_SHIFT)) | // Select stereo mode for DAC
aidan1971 8:9fdf8501d14b 314 (SGTL5000_ANA_POWER_LINREG_SIMPLE_POWERUP_MASK & (0x1 << 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 8:9fdf8501d14b 315 (SGTL5000_ANA_POWER_STARTUP_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_STARTUP_POWERUP_SHIFT)) | // Disable reset and powerup circuits. Can be cleared after powerup as VDDD is external on Teensy
aidan1971 8:9fdf8501d14b 316 (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 317 (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 318 (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 319 (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 320 (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 8:9fdf8501d14b 321 (SGTL5000_ANA_POWER_ADC_MONO_MASK & (0x1 << SGTL5000_ANA_POWER_ADC_MONO_SHIFT)) | // ADC in stereo mode
aidan1971 8:9fdf8501d14b 322 (SGTL5000_ANA_POWER_REFTOP_POWERUP_MASK & (0x1 << SGTL5000_ANA_POWER_REFTOP_POWERUP_SHIFT)) | // Enable bias currents
aidan1971 8:9fdf8501d14b 323 (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 324 (SGTL5000_ANA_POWER_DAC_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_DAC_POWERUP_SHIFT)) | // Disable DAC until we set refs
aidan1971 8:9fdf8501d14b 325 (SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_SHIFT)) | // Disable capless headphones
aidan1971 8:9fdf8501d14b 326 (SGTL5000_ANA_POWER_ADC_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_ADC_POWERUP_SHIFT)) | // Disable ADC power until we set refs
aidan1971 8:9fdf8501d14b 327 (SGTL5000_ANA_POWER_LINEOUT_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_LINEOUT_POWERUP_SHIFT)) // Disable LINEOUT power until we set refs
aidan1971 8:9fdf8501d14b 328 ))
aidan1971 8:9fdf8501d14b 329 ) return -1;
aidan1971 8:9fdf8501d14b 330 if(
aidan1971 8:9fdf8501d14b 331 write_i2c(SGTL5000_LINREG_CTRL,(
aidan1971 8:9fdf8501d14b 332 (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 333 (SGTL5000_LINREG_CTRL_VDDC_ASSN_OVRD_MASK & (0x1 << SGTL5000_LINREG_CTRL_VDDC_ASSN_OVRD_SHIFT)) | // chargepump source manually assigned
aidan1971 8:9fdf8501d14b 334 (SGTL5000_LINREG_CTRL_D_PROGRAMMING_MASK & (0xC << SGTL5000_LINREG_CTRL_D_PROGRAMMING_SHIFT)) // VDDD Line Reg not used so leave default
aidan1971 8:9fdf8501d14b 335 ))
aidan1971 9:40e0ff8c2ba2 336 ) return -2;
aidan1971 8:9fdf8501d14b 337 if(
aidan1971 8:9fdf8501d14b 338 write_i2c(SGTL5000_REF_CTRL,(
aidan1971 8:9fdf8501d14b 339 (SGTL5000_REF_CTRL_VAG_VAL_MASK & (0x1F << SGTL5000_REF_CTRL_VAG_VAL_SHIFT)) | // Set VAG ref to 1.575V
aidan1971 8:9fdf8501d14b 340 (SGTL5000_REF_CTRL_BIAS_CTRL_MASK & (0x1 << SGTL5000_REF_CTRL_BIAS_CTRL_SHIFT)) | // Set analog bias currents to +12.5%
aidan1971 8:9fdf8501d14b 341 (SGTL5000_REF_CTRL_SMALL_POP_MASK & (0x0 << SGTL5000_REF_CTRL_SMALL_POP_SHIFT)) // Set small pop ramp normal
aidan1971 8:9fdf8501d14b 342 ))
aidan1971 9:40e0ff8c2ba2 343 ) return -3;
aidan1971 8:9fdf8501d14b 344 if(
aidan1971 8:9fdf8501d14b 345 write_i2c(SGTL5000_LINE_OUT_CTRL,(
aidan1971 8:9fdf8501d14b 346 (SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_MASK & (0x3 << SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_SHIFT)) | // Set Lineout bias curent 0.36mA
aidan1971 8:9fdf8501d14b 347 (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 348 ))
aidan1971 9:40e0ff8c2ba2 349 ) return -4;
aidan1971 8:9fdf8501d14b 350 if(
aidan1971 8:9fdf8501d14b 351 write_i2c(SGTL5000_SHORT_CTRL,(
aidan1971 8:9fdf8501d14b 352 (SGTL5000_SHORT_CTRL_LVLADJR_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJR_SHIFT)) | // HP R short detect 125mA
aidan1971 8:9fdf8501d14b 353 (SGTL5000_SHORT_CTRL_LVLADJL_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJL_SHIFT)) | // HP L short detect 125mA
aidan1971 8:9fdf8501d14b 354 (SGTL5000_SHORT_CTRL_LVLADJC_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJC_SHIFT)) | // Headphone capless short detect 250mA
aidan1971 8:9fdf8501d14b 355 (SGTL5000_SHORT_CTRL_MODE_LR_MASK & (0x1 << SGTL5000_SHORT_CTRL_MODE_LR_SHIFT)) | // en short det reset 50mS
aidan1971 8:9fdf8501d14b 356 (SGTL5000_SHORT_CTRL_MODE_CM_MASK & (0x2 << SGTL5000_SHORT_CTRL_MODE_CM_SHIFT)) // En capless short det reset vol rise
aidan1971 8:9fdf8501d14b 357 ))
aidan1971 9:40e0ff8c2ba2 358 ) return -5;
aidan1971 8:9fdf8501d14b 359 if(
aidan1971 8:9fdf8501d14b 360 write_i2c(SGTL5000_ANA_CTRL,(
aidan1971 8:9fdf8501d14b 361 (SGTL5000_ANA_CTRL_MUTE_LO_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_LO_SHIFT)) | // Mute LINEOUT
aidan1971 8:9fdf8501d14b 362 (SGTL5000_ANA_CTRL_SELECT_HP_MASK & (0x0 << SGTL5000_ANA_CTRL_SELECT_HP_SHIFT)) | // Select DAC as input to HP amp
aidan1971 8:9fdf8501d14b 363 (SGTL5000_ANA_CTRL_EN_ZCD_HP_MASK & (0x1 << SGTL5000_ANA_CTRL_EN_ZCD_HP_SHIFT)) | // Enable ZCD on HP
aidan1971 8:9fdf8501d14b 364 (SGTL5000_ANA_CTRL_MUTE_HP_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_HP_SHIFT)) | // Mute the headphones
aidan1971 8:9fdf8501d14b 365 (SGTL5000_ANA_CTRL_SELECT_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_SELECT_ADC_SHIFT)) | // Select LINEIN as input to ADC
aidan1971 8:9fdf8501d14b 366 (SGTL5000_ANA_CTRL_EN_ZCD_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_EN_ZCD_ADC_SHIFT)) | // Enable ADC ZCD
aidan1971 8:9fdf8501d14b 367 (SGTL5000_ANA_CTRL_MUTE_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_ADC_SHIFT)) // Mute ADC pre-amp
aidan1971 8:9fdf8501d14b 368 ))
aidan1971 9:40e0ff8c2ba2 369 ) return -6;
aidan1971 9:40e0ff8c2ba2 370 if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_HEADPHONE_POWERUP_MASK)) return -7; // Power up Headphone amp
aidan1971 9:40e0ff8c2ba2 371 if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_DAC_POWERUP_MASK)) return -8; // Power up DAC
aidan1971 9:40e0ff8c2ba2 372 if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_MASK)) return -9; // Power up capless HP block
aidan1971 9:40e0ff8c2ba2 373 if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_ADC_POWERUP_MASK)) return -10; // Power up ADC
aidan1971 9:40e0ff8c2ba2 374 if(modify_i2c(SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_LINEOUT_POWERUP_MASK)) return -11; // Power up Lineout Amp
aidan1971 9:40e0ff8c2ba2 375 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 376
aidan1971 8:9fdf8501d14b 377 if(
aidan1971 8:9fdf8501d14b 378 write_i2c(SGTL5000_DIG_POWER,(
aidan1971 8:9fdf8501d14b 379 (SGTL5000_DIG_POWER_ADC_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_ADC_POWERUP_SHIFT)) |
aidan1971 8:9fdf8501d14b 380 (SGTL5000_DIG_POWER_DAC_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_DAC_POWERUP_SHIFT)) |
aidan1971 8:9fdf8501d14b 381 (SGTL5000_DIG_POWER_DAP_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_DAP_POWERUP_SHIFT)) |
aidan1971 8:9fdf8501d14b 382 (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 383 (SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_I2S_IN_POWERUP_SHIFT))
aidan1971 8:9fdf8501d14b 384 ))
aidan1971 9:40e0ff8c2ba2 385 ) return -13;
aidan1971 8:9fdf8501d14b 386 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 8:9fdf8501d14b 387 (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 388 ));
aidan1971 8:9fdf8501d14b 389 if(
aidan1971 8:9fdf8501d14b 390 write_i2c(SGTL5000_PAD_STRENGTH,(
aidan1971 8:9fdf8501d14b 391 (SGTL5000_PAD_STRENGTH_I2S_LRCLK_MASK & (0x0 << SGTL5000_PAD_STRENGTH_I2S_LRCLK_SHIFT)) | // I2S LR_CLK drive dissabled (codec is slave)
aidan1971 8:9fdf8501d14b 392 (SGTL5000_PAD_STRENGTH_I2S_SCLK_MASK & (0x0 << SGTL5000_PAD_STRENGTH_I2S_SCLK_SHIFT)) | // I2S SCLK drive dissabled (codec is slave)
aidan1971 8:9fdf8501d14b 393 (SGTL5000_PAD_STRENGTH_I2S_DOUT_MASK & (0x1 << SGTL5000_PAD_STRENGTH_I2S_DOUT_SHIFT)) | // I2S DOUT drive @ 4.02mA
aidan1971 8:9fdf8501d14b 394 (SGTL5000_PAD_STRENGTH_CTRL_DATA_MASK & (0x3 << SGTL5000_PAD_STRENGTH_CTRL_DATA_SHIFT)) | // I2C DATA drive @ 12.05mA.
aidan1971 8:9fdf8501d14b 395 (SGTL5000_PAD_STRENGTH_CTRL_CLK_MASK & (0x3 << SGTL5000_PAD_STRENGTH_CTRL_CLK_SHIFT)) // I2C CLOCK drive @ 12.05mA.
aidan1971 8:9fdf8501d14b 396 ))
aidan1971 9:40e0ff8c2ba2 397 )return -14;
aidan1971 8:9fdf8501d14b 398 if(
aidan1971 8:9fdf8501d14b 399 write_i2c(SGTL5000_CLK_CTRL,(
aidan1971 8:9fdf8501d14b 400 (SGTL5000_CLK_CTRL_RATE_MODE_MASK & (0x0 << SGTL5000_CLK_CTRL_RATE_MODE_SHIFT)) | // Sample rate mode Fs = SYS_FS
aidan1971 8:9fdf8501d14b 401 (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 402 (SGTL5000_CLK_CTRL_MCLK_FREQ_MASK & (0x0 << SGTL5000_CLK_CTRL_MCLK_FREQ_SHIFT)) // Set SYS MCLK @ 256*Fs
aidan1971 8:9fdf8501d14b 403 ))
aidan1971 9:40e0ff8c2ba2 404 ) return -15;
aidan1971 8:9fdf8501d14b 405 if(
aidan1971 8:9fdf8501d14b 406 write_i2c(SGTL5000_I2S_CTRL,(
aidan1971 8:9fdf8501d14b 407 (SGTL5000_I2S_CTRL_SCLKFREQ_MASK & (0x1 << SGTL5000_I2S_CTRL_SCLKFREQ_SHIFT)) | // I2S SCLK 32*Fs
aidan1971 8:9fdf8501d14b 408 (SGTL5000_I2S_CTRL_MS_MASK & (0x0 << SGTL5000_I2S_CTRL_MS_SHIFT)) | // Slave
aidan1971 8:9fdf8501d14b 409 (SGTL5000_I2S_CTRL_SCLK_INV_MASK & (0x0 << SGTL5000_I2S_CTRL_SCLK_INV_SHIFT)) | // Data on riseing edge
aidan1971 8:9fdf8501d14b 410 (SGTL5000_I2S_CTRL_DLEN_MASK & (0x3 << SGTL5000_I2S_CTRL_DLEN_SHIFT)) | // Data 16bits
aidan1971 8:9fdf8501d14b 411 (SGTL5000_I2S_CTRL_MODE_MASK & (0x0 << SGTL5000_I2S_CTRL_MODE_SHIFT)) | // I2S mode
aidan1971 8:9fdf8501d14b 412 (SGTL5000_I2S_CTRL_LRALIGN_MASK & (0x0 << SGTL5000_I2S_CTRL_LRALIGN_SHIFT)) | // I2S format
aidan1971 8:9fdf8501d14b 413 (SGTL5000_I2S_CTRL_LRPOL_MASK & (0x0 << SGTL5000_I2S_CTRL_LRPOL_SHIFT)) // Left word on LRCLK low
aidan1971 8:9fdf8501d14b 414 ))
aidan1971 9:40e0ff8c2ba2 415 ) return -16;
aidan1971 8:9fdf8501d14b 416 if(
aidan1971 8:9fdf8501d14b 417 write_i2c(SGTL5000_SSS_CTRL,(
aidan1971 8:9fdf8501d14b 418 (SGTL5000_SSS_CTRL_DAP_MIX_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_MIX_LRSWAP_SHIFT)) | // Define all inputs un-swapped
aidan1971 8:9fdf8501d14b 419 (SGTL5000_SSS_CTRL_DAP_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_LRSWAP_SHIFT)) |
aidan1971 8:9fdf8501d14b 420 (SGTL5000_SSS_CTRL_DAC_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAC_LRSWAP_SHIFT)) |
aidan1971 8:9fdf8501d14b 421 (SGTL5000_SSS_CTRL_I2S_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_I2S_LRSWAP_SHIFT)) |
aidan1971 8:9fdf8501d14b 422 (SGTL5000_SSS_CTRL_DAP_MIX_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_MIX_SELECT_SHIFT)) | // DAP mixer source ADC
aidan1971 8:9fdf8501d14b 423 (SGTL5000_SSS_CTRL_DAP_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_SELECT_SHIFT)) | // DAP Input source ADC
aidan1971 8:9fdf8501d14b 424 (SGTL5000_SSS_CTRL_DAC_SELECT_MASK & (0x1 << SGTL5000_SSS_CTRL_DAC_SELECT_SHIFT)) | // DAC Input source I2SIN
aidan1971 8:9fdf8501d14b 425 (SGTL5000_SSS_CTRL_I2S_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_I2S_SELECT_SHIFT)) // I2SOUT source ADC
aidan1971 8:9fdf8501d14b 426 ))
aidan1971 9:40e0ff8c2ba2 427 ) return -17;
aidan1971 8:9fdf8501d14b 428 if(
aidan1971 8:9fdf8501d14b 429 write_i2c(SGTL5000_ADCDAC_CTRL,(
aidan1971 8:9fdf8501d14b 430 (SGTL5000_ADCDAC_CTRL_VOL_RAMP_EN_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_VOL_RAMP_EN_SHIFT)) | // Disable Vol ramp
aidan1971 8:9fdf8501d14b 431 (SGTL5000_ADCDAC_CTRL_VOL_EXPO_RAMP_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_VOL_EXPO_RAMP_SHIFT)) |
aidan1971 8:9fdf8501d14b 432 (SGTL5000_ADCDAC_CTRL_DAC_MUTE_RIGHT_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_DAC_MUTE_RIGHT_SHIFT)) |
aidan1971 8:9fdf8501d14b 433 (SGTL5000_ADCDAC_CTRL_DAC_MUTE_LEFT_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_DAC_MUTE_LEFT_SHIFT)) |
aidan1971 8:9fdf8501d14b 434 (SGTL5000_ADCDAC_CTRL_ADC_HPF_FREEZE_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_ADC_HPF_FREEZE_SHIFT)) | // ADC HPF normal operation
aidan1971 8:9fdf8501d14b 435 (SGTL5000_ADCDAC_CTRL_ADC_HPF_BYPASS_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_ADC_HPF_BYPASS_SHIFT)) // ADC HPF enabled
aidan1971 8:9fdf8501d14b 436 ))
aidan1971 9:40e0ff8c2ba2 437 ) return -18;
aidan1971 8:9fdf8501d14b 438 if(
aidan1971 8:9fdf8501d14b 439 write_i2c(SGTL5000_DAC_VOL,(
aidan1971 8:9fdf8501d14b 440 (SGTL5000_DAC_VOL_DAC_VOL_RIGHT_MASK & (0x3C << SGTL5000_DAC_VOL_DAC_VOL_RIGHT_SHIFT)) | // DAC R Vol 0dB
aidan1971 8:9fdf8501d14b 441 (SGTL5000_DAC_VOL_DAC_VOL_LEFT_MASK & (0x3C << SGTL5000_DAC_VOL_DAC_VOL_LEFT_SHIFT)) // DAC L Vol 0dB
aidan1971 8:9fdf8501d14b 442 ))
aidan1971 9:40e0ff8c2ba2 443 ) return -19;
aidan1971 8:9fdf8501d14b 444 if(
aidan1971 8:9fdf8501d14b 445 write_i2c(SGTL5000_ANA_HP_CTRL,(
aidan1971 8:9fdf8501d14b 446 (SGTL5000_ANA_HP_CTRL_HP_VOL_RIGHT_MASK & (0x7F << SGTL5000_ANA_HP_CTRL_HP_VOL_RIGHT_SHIFT)) | // HP Vol set minimum
aidan1971 8:9fdf8501d14b 447 (SGTL5000_ANA_HP_CTRL_HP_VOL_LEFT_MASK & (0x7F << SGTL5000_ANA_HP_CTRL_HP_VOL_LEFT_SHIFT))
aidan1971 8:9fdf8501d14b 448 ))
aidan1971 9:40e0ff8c2ba2 449 ) return -20;
aidan1971 9:40e0ff8c2ba2 450 if(modify_i2c(SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_LO_MASK)) return -21; // Un-mute Lineout
aidan1971 9:40e0ff8c2ba2 451 if(modify_i2c(SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_HP_MASK)) return -22; // Un-mute HP
aidan1971 9:40e0ff8c2ba2 452 if(modify_i2c(SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_ADC_MASK)) return -23; // Un-mute ADC pre-amp
aidan1971 9:40e0ff8c2ba2 453 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -24; // I2S DOUT enable
aidan1971 9:40e0ff8c2ba2 454 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK)) return -25; // I2S DIN enable
aidan1971 8:9fdf8501d14b 455 return 0;
aidan1971 0:8f28f25e3435 456 }
aidan1971 0:8f28f25e3435 457
aidan1971 0:8f28f25e3435 458
aidan1971 0:8f28f25e3435 459 int32_t SGTL5000::attach_SYNC(Callback<void()> func)
aidan1971 0:8f28f25e3435 460 {
aidan1971 0:8f28f25e3435 461 if(SYNC_attached) return -1; // Assign Callback function
aidan1971 0:8f28f25e3435 462 SGTL5000::SYNC_user_func = func;
aidan1971 6:4ab5aaeaa064 463 SYNC_attach_type = 0;
aidan1971 0:8f28f25e3435 464 SYNC_attached = true;
aidan1971 0:8f28f25e3435 465 return 0;
aidan1971 0:8f28f25e3435 466 }
aidan1971 0:8f28f25e3435 467
aidan1971 8:9fdf8501d14b 468 int32_t SGTL5000::attach_SYNC_NB(uint32_t user_ISR, uint32_t irq_pri, IRQn sw_irq)
aidan1971 0:8f28f25e3435 469 {
aidan1971 0:8f28f25e3435 470 SGTL5000::SYNC_swIRQ = sw_irq; // Assign ISR address and enable users IRQ
aidan1971 0:8f28f25e3435 471 NVIC_SetVector(SGTL5000::SYNC_swIRQ, user_ISR);
aidan1971 0:8f28f25e3435 472 NVIC_SetPriority(SGTL5000::SYNC_swIRQ, irq_pri);
aidan1971 0:8f28f25e3435 473 NVIC_EnableIRQ(SGTL5000::SYNC_swIRQ);
aidan1971 6:4ab5aaeaa064 474 SYNC_attach_type = 1;
aidan1971 0:8f28f25e3435 475 SYNC_attached = true;
aidan1971 8:9fdf8501d14b 476 return 0;
aidan1971 0:8f28f25e3435 477 }
aidan1971 0:8f28f25e3435 478
aidan1971 8:9fdf8501d14b 479 int32_t SGTL5000::detach_SYNC(void)
aidan1971 0:8f28f25e3435 480 {
aidan1971 8:9fdf8501d14b 481 if(SYNC_attached) {
aidan1971 8:9fdf8501d14b 482 stop_SYNC();
aidan1971 8:9fdf8501d14b 483 SYNC_attached = false;
aidan1971 8:9fdf8501d14b 484 }
aidan1971 8:9fdf8501d14b 485 return 0;
aidan1971 0:8f28f25e3435 486 }
aidan1971 0:8f28f25e3435 487
aidan1971 9:40e0ff8c2ba2 488 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 9:40e0ff8c2ba2 489 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)
aidan1971 0:8f28f25e3435 490 {
aidan1971 8:9fdf8501d14b 491 if(!codec_configured) return -1;
aidan1971 7:d65476c153a4 492 if(!SYNC_attached && !SYNC_attach_type) return -1; // Check we have a handler if using callback
aidan1971 0:8f28f25e3435 493 if(SYNC_run || TX_run || RX_run ) return -1; // Check if i2s is already started
aidan1971 0:8f28f25e3435 494 if(_RX_DMAch > 15 || _TX_DMAch > 15) return -1; // Sanity check DMAMUX channels
aidan1971 9:40e0ff8c2ba2 495 if (!(_block_size == 2 || _block_size == 4 || _block_size == 8)) return -1; // Only accept block size 2^n within range.
aidan1971 9:40e0ff8c2ba2 496 if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK) == 0) {
aidan1971 9:40e0ff8c2ba2 497 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -1;
aidan1971 9:40e0ff8c2ba2 498 }
aidan1971 9:40e0ff8c2ba2 499 if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK) == 0) {
aidan1971 9:40e0ff8c2ba2 500 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK)) return -1;
aidan1971 9:40e0ff8c2ba2 501 }
aidan1971 4:91354c908416 502 packed_RX = _packed_RX;
aidan1971 4:91354c908416 503 packed_TX = _packed_TX;
aidan1971 9:40e0ff8c2ba2 504 TX_block_size = _block_size;
aidan1971 6:4ab5aaeaa064 505 RX_block_size = TX_block_size;
aidan1971 9:40e0ff8c2ba2 506 TX_bs_bytes = _block_size * 4;
aidan1971 0:8f28f25e3435 507 RX_bs_bytes = TX_bs_bytes;
aidan1971 9:40e0ff8c2ba2 508 SGTL5000::BufRX_L_safe = (uint32_t*)_BufRX_L_safe; // Assign the users pointer addresses
aidan1971 9:40e0ff8c2ba2 509 SGTL5000::BufRX_R_safe = (uint32_t*)_BufRX_R_safe;
aidan1971 9:40e0ff8c2ba2 510 SGTL5000::BufTX_L_safe = (uint32_t*)_BufTX_L_safe;
aidan1971 9:40e0ff8c2ba2 511 SGTL5000::BufTX_R_safe = (uint32_t*)_BufTX_R_safe;
aidan1971 6:4ab5aaeaa064 512 *SGTL5000::BufRX_L_safe = (uint32_t)&I2S_RX_Buffer[8];
aidan1971 6:4ab5aaeaa064 513 *SGTL5000::BufRX_R_safe = (uint32_t)&I2S_RX_Buffer[8 + (RX_block_size / 2)];
aidan1971 6:4ab5aaeaa064 514 *SGTL5000::BufTX_L_safe = (uint32_t)&I2S_TX_Buffer[8];
aidan1971 6:4ab5aaeaa064 515 *SGTL5000::BufTX_R_safe = (uint32_t)&I2S_TX_Buffer[8 + (TX_block_size / 2)];
aidan1971 9:40e0ff8c2ba2 516 SGTL5000::db_phase_sync = 0;
aidan1971 4:91354c908416 517 if(packed_RX) RX_shift = false;
aidan1971 4:91354c908416 518 else RX_shift = _RX_shift;
aidan1971 4:91354c908416 519 if(packed_TX) TX_shift = false;
aidan1971 4:91354c908416 520 else TX_shift = _TX_shift;
aidan1971 0:8f28f25e3435 521 SGTL5000::RX_DMAch = _RX_DMAch;
aidan1971 0:8f28f25e3435 522 SGTL5000::TX_DMAch = _TX_DMAch;
aidan1971 0:8f28f25e3435 523 SYNC_run = true;
aidan1971 0:8f28f25e3435 524 init_DMA();
aidan1971 0:8f28f25e3435 525
aidan1971 9:40e0ff8c2ba2 526 I2S0->TCSR |= I2S_TCSR_SR_MASK; // Reset TX & RX logic
aidan1971 9:40e0ff8c2ba2 527 I2S0->RCSR |= I2S_RCSR_SR_MASK;
aidan1971 9:40e0ff8c2ba2 528 I2S0->TCSR &= ~I2S_TCSR_SR_MASK;
aidan1971 9:40e0ff8c2ba2 529 I2S0->RCSR &= ~I2S_RCSR_SR_MASK;
aidan1971 9:40e0ff8c2ba2 530 I2S0->TCR1 = (I2S0->TCR1 & ~I2S_TCR1_TFW_MASK) | I2S_TCR1_TFW(8 - TX_block_size); // Set TX FIFO watermark
aidan1971 9:40e0ff8c2ba2 531 I2S0->RCR1 = (I2S0->RCR1 & ~I2S_RCR1_RFW_MASK) | I2S_RCR1_RFW(RX_block_size - 1); // Set RX FIFO watermark
aidan1971 9:40e0ff8c2ba2 532 I2S0->TCSR &= ~I2S_TCSR_TE_MASK; // TX Disable
aidan1971 9:40e0ff8c2ba2 533 I2S0->RCSR &= ~I2S_RCSR_RE_MASK; // RX Disable
aidan1971 9:40e0ff8c2ba2 534 while((I2S0->RCSR & I2S_RCSR_RE_MASK) || (I2S0->TCSR & I2S_TCSR_TE_MASK)); // Make sure TX & RX are disabled before enabling corresponding channel
aidan1971 0:8f28f25e3435 535 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(1); // Enable TX channel 0.
aidan1971 0:8f28f25e3435 536 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(1); // Enable RX channels 0
aidan1971 9:40e0ff8c2ba2 537 I2S0->TCSR |= I2S_TCSR_TE_MASK; // Now switch TX enable
aidan1971 9:40e0ff8c2ba2 538 I2S0->RCSR |= I2S_RCSR_RE_MASK; // Now switch RX enable
aidan1971 9:40e0ff8c2ba2 539
aidan1971 6:4ab5aaeaa064 540 if(SYNC_attach_type) {
aidan1971 9:40e0ff8c2ba2 541 NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::sync_dma_ISR_NB); // Set DMA TX handler vector
aidan1971 9:40e0ff8c2ba2 542 NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::sync_dma_ISR_NB); // Set DMA RX handler vector
aidan1971 6:4ab5aaeaa064 543 } else {
aidan1971 9:40e0ff8c2ba2 544 NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::sync_dma_ISR); // Set DMA TX handler vector
aidan1971 9:40e0ff8c2ba2 545 NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::sync_dma_ISR); // Set DMA RX handler vector
aidan1971 6:4ab5aaeaa064 546 }
aidan1971 9:40e0ff8c2ba2 547 NVIC_SetPriority((IRQn)SGTL5000::TX_DMAch, _DMA_irq_pri); // Set irq priorities the same
aidan1971 9:40e0ff8c2ba2 548 NVIC_SetPriority((IRQn)SGTL5000::RX_DMAch, _DMA_irq_pri);
aidan1971 9:40e0ff8c2ba2 549
aidan1971 2:a9d8242f76ea 550 if(SGTL5000::TX_DMAch > SGTL5000::RX_DMAch) {
aidan1971 2:a9d8242f76ea 551 NVIC_EnableIRQ((IRQn)SGTL5000::RX_DMAch); // Enable IRQ for chosen RX DMA channel if using lower priority DMA channel (lower channel number). This assumes fixed priority encoding and pre-emption, (which are default).
aidan1971 4:91354c908416 552 NVIC_DisableIRQ((IRQn)SGTL5000::TX_DMAch); // Disable IRQ for chosen TX DMA channel. We must wait for the lowest priority channel (DMA priority) to trigger the DMA IRQ.
aidan1971 2:a9d8242f76ea 553 } else {
aidan1971 4:91354c908416 554 NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch);
aidan1971 2:a9d8242f76ea 555 NVIC_DisableIRQ((IRQn)SGTL5000::RX_DMAch);
aidan1971 2:a9d8242f76ea 556 }
aidan1971 9:40e0ff8c2ba2 557
aidan1971 9:40e0ff8c2ba2 558 NVIC_SetVector(I2S0_Rx_IRQn, (uint32_t)&SGTL5000::sync_I2S_WS_ISR); // Set vector for SYNC word start ISR
aidan1971 2:a9d8242f76ea 559 NVIC_SetPriority(I2S0_Rx_IRQn, 0); // Set priority of SYNC word start ISR
aidan1971 9:40e0ff8c2ba2 560
aidan1971 2:a9d8242f76ea 561 NVIC_EnableIRQ(I2S0_Rx_IRQn); // Enable SYNC word start ISR using RX direction
aidan1971 9:40e0ff8c2ba2 562 I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag
aidan1971 9:40e0ff8c2ba2 563 I2S0->RCSR |= I2S_RCSR_WSIE_MASK; // Enable I2S RX word start IRQ
aidan1971 0:8f28f25e3435 564 return 0;
aidan1971 0:8f28f25e3435 565 }
aidan1971 0:8f28f25e3435 566
aidan1971 8:9fdf8501d14b 567 int32_t SGTL5000::stop_SYNC(void)
aidan1971 0:8f28f25e3435 568 {
aidan1971 8:9fdf8501d14b 569 if(!codec_configured) return -1;
aidan1971 9:40e0ff8c2ba2 570 if(modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -1; // codec I2S disable
aidan1971 9:40e0ff8c2ba2 571 if(modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK)) return -1;
aidan1971 9:40e0ff8c2ba2 572 I2S0->TCSR &= ~I2S_TCSR_FRDE_MASK; // Disable TX FIFO DMA request based on watermark
aidan1971 9:40e0ff8c2ba2 573 I2S0->RCSR &= ~I2S_RCSR_FRDE_MASK; // Disable RX FIFO DMA request based on watermark
aidan1971 9:40e0ff8c2ba2 574 while(!(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete
aidan1971 9:40e0ff8c2ba2 575 while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete
aidan1971 9:40e0ff8c2ba2 576 I2S0->TCSR &= ~I2S_TCSR_TE_MASK; // TX Disable
aidan1971 9:40e0ff8c2ba2 577 I2S0->RCSR &= ~I2S_RCSR_RE_MASK; // RX Disable
aidan1971 9:40e0ff8c2ba2 578 while((I2S0->RCSR & I2S_RCSR_RE_MASK) || (I2S0->TCSR & I2S_TCSR_TE_MASK)); // Ensure TX & RX have stopped.
aidan1971 9:40e0ff8c2ba2 579 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(1); // Disable TX channel 0.
aidan1971 9:40e0ff8c2ba2 580 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(1); // Disable RX channels 0
aidan1971 0:8f28f25e3435 581 SYNC_run = false;
aidan1971 8:9fdf8501d14b 582 return 0;
aidan1971 0:8f28f25e3435 583 }
aidan1971 0:8f28f25e3435 584
aidan1971 9:40e0ff8c2ba2 585 void SGTL5000::rx_I2S_WS_ISR(void)
aidan1971 0:8f28f25e3435 586 {
aidan1971 0:8f28f25e3435 587 I2S0->RCSR &= ~I2S_RCSR_WSIE_MASK; // Disable RX word start IRQs
aidan1971 0:8f28f25e3435 588 I2S0->RCSR |= I2S_RCSR_FR_MASK; // Reset RX FIFO pointers
aidan1971 0:8f28f25e3435 589 I2S0->RCSR |= I2S_RCSR_FRDE_MASK; // Enable DMA request based on RX FIFO watermark
aidan1971 0:8f28f25e3435 590 }
aidan1971 0:8f28f25e3435 591
aidan1971 9:40e0ff8c2ba2 592 void SGTL5000::tx_I2S_WS_ISR(void)
aidan1971 0:8f28f25e3435 593 {
aidan1971 0:8f28f25e3435 594 I2S0->TCSR &= ~I2S_TCSR_WSIE_MASK; // Disable TX word start IRQs
aidan1971 0:8f28f25e3435 595 I2S0->TCSR |= I2S_TCSR_FR_MASK; // Reset TX FIFO pointers
aidan1971 9:40e0ff8c2ba2 596 I2S0->TDR[0] = 0; // Stuff the TX FIFO with 2 samples 1 Left 1 Right. Timing here is critical as we have just 3 bit clocks to get something into the FIFO
aidan1971 9:40e0ff8c2ba2 597 I2S0->TDR[0] = 0; // before we suffer an under-run. At 192Ks/s that's approx 5nS! So stuff a couple of samples to give some headroom.
aidan1971 0:8f28f25e3435 598 I2S0->TCSR |= I2S_TCSR_FRDE_MASK; // Enable DMA request based on TX FIFO watermark
aidan1971 0:8f28f25e3435 599 }
aidan1971 0:8f28f25e3435 600
aidan1971 9:40e0ff8c2ba2 601 void SGTL5000::sync_I2S_WS_ISR(void)
aidan1971 0:8f28f25e3435 602 {
aidan1971 9:40e0ff8c2ba2 603 I2S0->RCSR &= ~I2S_RCSR_WSIE_MASK; // Disable RX word start IRQs
aidan1971 0:8f28f25e3435 604 I2S0->TCSR |= I2S_TCSR_FR_MASK; // Reset TX FIFO pointers
aidan1971 0:8f28f25e3435 605 I2S0->RCSR |= I2S_RCSR_FR_MASK; // Reset RX FIFO pointers
aidan1971 9:40e0ff8c2ba2 606 I2S0->TDR[0] = 0; // Stuff the TX FIFO with 2 samples 1 Left 1 Right. Timing here is critical as we have just 3 bit clocks to get something into the FIFO
aidan1971 9:40e0ff8c2ba2 607 I2S0->TDR[0] = 0; // before we suffer an under-run. At 192Ks/s that's approx 5nS! So stuff a couple of samples to give some headroom.
aidan1971 0:8f28f25e3435 608 I2S0->TCSR |= I2S_TCSR_FRDE_MASK; // Enable DMA request based on TX FIFO watermark
aidan1971 0:8f28f25e3435 609 I2S0->RCSR |= I2S_RCSR_FRDE_MASK; // Enable DMA request based on RX FIFO watermark
aidan1971 0:8f28f25e3435 610 }
aidan1971 0:8f28f25e3435 611
aidan1971 6:4ab5aaeaa064 612 void SGTL5000::sync_dma_ISR_NB(void)
aidan1971 0:8f28f25e3435 613 {
aidan1971 3:62c03088f256 614 /*!
aidan1971 4:91354c908416 615 Refer to the DMA_init function for details of buffer layouts.
aidan1971 0:8f28f25e3435 616
aidan1971 4:91354c908416 617 When running both TX & RX synchronously, only 1 direction has its IRQ enabled in the NVIC, which is enabled is determined by the priority of the DMA channels.
aidan1971 9:40e0ff8c2ba2 618 This assumes strict prioriy ordering of DMA channels.
aidan1971 4:91354c908416 619 The DMA transfers will be pre-empting each other, dependant on relative priority.
aidan1971 9:40e0ff8c2ba2 620 Therefore before the users function is called we must be sure that the lowest priority channel has completed its transfer and it is this channel that has its IRQ enabled.
aidan1971 4:91354c908416 621 We clear both flags here to avoid checking which is active as the active IRQ is chosen in the start_SYNC function (saves a couple of cycles).
aidan1971 4:91354c908416 622 Activating only one of the IRQs avoids servicing an extra IRQ stack operation and dealing with pending IRQs.
aidan1971 0:8f28f25e3435 623 */
aidan1971 0:8f28f25e3435 624
aidan1971 9:40e0ff8c2ba2 625 DMA0->CINT = DMA_CINT_CINT(SGTL5000::RX_DMAch); // Clear RX DMA IRQ flag
aidan1971 9:40e0ff8c2ba2 626 DMA0->CINT = DMA_CINT_CINT(SGTL5000::TX_DMAch); // Clear TX DMA IRQ flag
aidan1971 0:8f28f25e3435 627
aidan1971 6:4ab5aaeaa064 628 register uint32_t BU_RX_L;
aidan1971 6:4ab5aaeaa064 629 register uint32_t BU_RX_R;
aidan1971 6:4ab5aaeaa064 630 register uint32_t BU_TX_L;
aidan1971 6:4ab5aaeaa064 631 register uint32_t BU_TX_R;
aidan1971 6:4ab5aaeaa064 632 register uint32_t DB_PHASE;
aidan1971 6:4ab5aaeaa064 633 __asm {
aidan1971 9:40e0ff8c2ba2 634 LDR DB_PHASE, [&SGTL5000::db_phase_sync]
aidan1971 9:40e0ff8c2ba2 635 LDR BU_RX_L, [SGTL5000::BufRX_L_safe] // Pipeline memory access
aidan1971 6:4ab5aaeaa064 636 LDR BU_RX_R, [SGTL5000::BufRX_R_safe]
aidan1971 6:4ab5aaeaa064 637 LDR BU_TX_L, [SGTL5000::BufTX_L_safe]
aidan1971 6:4ab5aaeaa064 638 LDR BU_TX_R, [SGTL5000::BufTX_R_safe]
aidan1971 6:4ab5aaeaa064 639
aidan1971 6:4ab5aaeaa064 640 TEQ DB_PHASE, #0x0
aidan1971 6:4ab5aaeaa064 641 IT EQ
aidan1971 6:4ab5aaeaa064 642 BEQ buf_base
aidan1971 6:4ab5aaeaa064 643
aidan1971 6:4ab5aaeaa064 644 ADD BU_RX_L, #32
aidan1971 6:4ab5aaeaa064 645 ADD BU_RX_R, #32
aidan1971 6:4ab5aaeaa064 646 ADD BU_TX_L, #32
aidan1971 6:4ab5aaeaa064 647 ADD BU_TX_R, #32
aidan1971 6:4ab5aaeaa064 648 SUB DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 649 B store
aidan1971 6:4ab5aaeaa064 650
aidan1971 6:4ab5aaeaa064 651 buf_base:
aidan1971 6:4ab5aaeaa064 652 SUB BU_RX_L, #32
aidan1971 6:4ab5aaeaa064 653 SUB BU_RX_R, #32
aidan1971 6:4ab5aaeaa064 654 SUB BU_TX_L, #32
aidan1971 6:4ab5aaeaa064 655 SUB BU_TX_R, #32
aidan1971 6:4ab5aaeaa064 656 ADD DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 657
aidan1971 6:4ab5aaeaa064 658 store:
aidan1971 9:40e0ff8c2ba2 659 STR BU_RX_L, [SGTL5000::BufRX_L_safe] // Pipeline memory access
aidan1971 6:4ab5aaeaa064 660 STR BU_RX_R, [SGTL5000::BufRX_R_safe]
aidan1971 6:4ab5aaeaa064 661 STR BU_TX_L, [SGTL5000::BufTX_L_safe]
aidan1971 6:4ab5aaeaa064 662 STR BU_TX_R, [SGTL5000::BufTX_R_safe]
aidan1971 9:40e0ff8c2ba2 663 STR DB_PHASE, [&SGTL5000::db_phase_sync]
aidan1971 0:8f28f25e3435 664 }
aidan1971 6:4ab5aaeaa064 665
aidan1971 9:40e0ff8c2ba2 666 if(!NVIC_GetActive(SGTL5000::SYNC_swIRQ)) NVIC->STIR = SGTL5000::SYNC_swIRQ;// 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 6:4ab5aaeaa064 667 }
aidan1971 6:4ab5aaeaa064 668
aidan1971 6:4ab5aaeaa064 669 void SGTL5000::sync_dma_ISR(void)
aidan1971 6:4ab5aaeaa064 670 {
aidan1971 6:4ab5aaeaa064 671
aidan1971 6:4ab5aaeaa064 672 DMA0->CINT = DMA_CINT_CINT(SGTL5000::RX_DMAch); // Clear RX DMA IRQ flag
aidan1971 6:4ab5aaeaa064 673 DMA0->CINT = DMA_CINT_CINT(SGTL5000::TX_DMAch); // Clear TX DMA IRQ flag
aidan1971 6:4ab5aaeaa064 674
aidan1971 6:4ab5aaeaa064 675 register uint32_t BU_RX_L;
aidan1971 6:4ab5aaeaa064 676 register uint32_t BU_RX_R;
aidan1971 6:4ab5aaeaa064 677 register uint32_t BU_TX_L;
aidan1971 6:4ab5aaeaa064 678 register uint32_t BU_TX_R;
aidan1971 6:4ab5aaeaa064 679 register uint32_t DB_PHASE;
aidan1971 6:4ab5aaeaa064 680 __asm {
aidan1971 9:40e0ff8c2ba2 681 LDR DB_PHASE, [&SGTL5000::db_phase_sync]
aidan1971 6:4ab5aaeaa064 682 LDR BU_RX_L, [SGTL5000::BufRX_L_safe] // Pipeline memory access
aidan1971 6:4ab5aaeaa064 683 LDR BU_RX_R, [SGTL5000::BufRX_R_safe]
aidan1971 6:4ab5aaeaa064 684 LDR BU_TX_L, [SGTL5000::BufTX_L_safe]
aidan1971 6:4ab5aaeaa064 685 LDR BU_TX_R, [SGTL5000::BufTX_R_safe]
aidan1971 6:4ab5aaeaa064 686
aidan1971 6:4ab5aaeaa064 687 TEQ DB_PHASE, #0x0
aidan1971 6:4ab5aaeaa064 688 IT EQ
aidan1971 6:4ab5aaeaa064 689 BEQ buf_base
aidan1971 6:4ab5aaeaa064 690
aidan1971 6:4ab5aaeaa064 691 ADD BU_RX_L, #32
aidan1971 6:4ab5aaeaa064 692 ADD BU_RX_R, #32
aidan1971 6:4ab5aaeaa064 693 ADD BU_TX_L, #32
aidan1971 6:4ab5aaeaa064 694 ADD BU_TX_R, #32
aidan1971 6:4ab5aaeaa064 695 SUB DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 696 B store
aidan1971 6:4ab5aaeaa064 697
aidan1971 6:4ab5aaeaa064 698 buf_base:
aidan1971 6:4ab5aaeaa064 699 SUB BU_RX_L, #32
aidan1971 6:4ab5aaeaa064 700 SUB BU_RX_R, #32
aidan1971 6:4ab5aaeaa064 701 SUB BU_TX_L, #32
aidan1971 6:4ab5aaeaa064 702 SUB BU_TX_R, #32
aidan1971 6:4ab5aaeaa064 703 ADD DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 704
aidan1971 6:4ab5aaeaa064 705 store:
aidan1971 6:4ab5aaeaa064 706 STR BU_RX_L, [SGTL5000::BufRX_L_safe] // Pipeline memory access
aidan1971 6:4ab5aaeaa064 707 STR BU_RX_R, [SGTL5000::BufRX_R_safe]
aidan1971 6:4ab5aaeaa064 708 STR BU_TX_L, [SGTL5000::BufTX_L_safe]
aidan1971 6:4ab5aaeaa064 709 STR BU_TX_R, [SGTL5000::BufTX_R_safe]
aidan1971 9:40e0ff8c2ba2 710 STR DB_PHASE, [&SGTL5000::db_phase_sync]
aidan1971 6:4ab5aaeaa064 711 }
aidan1971 6:4ab5aaeaa064 712
aidan1971 6:4ab5aaeaa064 713 SGTL5000::SYNC_user_func.call(); // Callback user function
aidan1971 6:4ab5aaeaa064 714
aidan1971 0:8f28f25e3435 715 }
aidan1971 0:8f28f25e3435 716
aidan1971 0:8f28f25e3435 717
aidan1971 0:8f28f25e3435 718
aidan1971 0:8f28f25e3435 719 int32_t SGTL5000::attach_TX(Callback<void()> func)
aidan1971 0:8f28f25e3435 720 {
aidan1971 9:40e0ff8c2ba2 721 if(TX_attached) return -1; // Assign Callback function
aidan1971 0:8f28f25e3435 722 SGTL5000::TX_user_func = func;
aidan1971 6:4ab5aaeaa064 723 TX_attach_type = 0;
aidan1971 0:8f28f25e3435 724 TX_attached = true;
aidan1971 0:8f28f25e3435 725 return 0;
aidan1971 0:8f28f25e3435 726 }
aidan1971 0:8f28f25e3435 727
aidan1971 8:9fdf8501d14b 728 int32_t SGTL5000::attach_TX_NB(uint32_t user_ISR, uint32_t irq_pri, IRQn sw_irq)
aidan1971 0:8f28f25e3435 729 {
aidan1971 9:40e0ff8c2ba2 730 SGTL5000::TX_swIRQ = sw_irq; // Assign ISR address and enable users IRQ
aidan1971 0:8f28f25e3435 731 NVIC_SetVector(SGTL5000::TX_swIRQ, user_ISR);
aidan1971 0:8f28f25e3435 732 NVIC_SetPriority(SGTL5000::TX_swIRQ, irq_pri);
aidan1971 0:8f28f25e3435 733 NVIC_EnableIRQ(SGTL5000::TX_swIRQ);
aidan1971 6:4ab5aaeaa064 734 TX_attach_type = 1;
aidan1971 0:8f28f25e3435 735 TX_attached = true;
aidan1971 8:9fdf8501d14b 736 return 0;
aidan1971 0:8f28f25e3435 737 }
aidan1971 0:8f28f25e3435 738
aidan1971 8:9fdf8501d14b 739 int32_t SGTL5000::detach_TX(void)
aidan1971 0:8f28f25e3435 740 {
aidan1971 8:9fdf8501d14b 741 if(TX_attached) {
aidan1971 8:9fdf8501d14b 742 SGTL5000::stop_TX();
aidan1971 8:9fdf8501d14b 743 TX_attached = false;
aidan1971 8:9fdf8501d14b 744 }
aidan1971 8:9fdf8501d14b 745 return 0;
aidan1971 0:8f28f25e3435 746 }
aidan1971 0:8f28f25e3435 747
aidan1971 9:40e0ff8c2ba2 748 int32_t SGTL5000::start_TX(uint32_t _BufTX_L_safe, uint32_t _BufTX_R_safe,
aidan1971 9:40e0ff8c2ba2 749 uint32_t _block_size, bool _packed_TX, bool _TX_shift, uint32_t _TX_DMAch, uint32_t _DMA_irq_pri)
aidan1971 0:8f28f25e3435 750 {
aidan1971 8:9fdf8501d14b 751 if(!codec_configured) return -1;
aidan1971 6:4ab5aaeaa064 752 if(!TX_attached && !TX_attach_type) return -1; // Check we have a handler if using callback
aidan1971 0:8f28f25e3435 753 if(SYNC_run || TX_run) return -1; // Check if i2s is already started on tx
aidan1971 0:8f28f25e3435 754 if(_TX_DMAch > 15) return -1; // Sanity check DMAMUX channels
aidan1971 9:40e0ff8c2ba2 755 if (!(_block_size == 2 || _block_size == 4 || _block_size == 8)) return -1; // Only accept block size 2^n within range.
aidan1971 9:40e0ff8c2ba2 756
aidan1971 9:40e0ff8c2ba2 757 if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK) == 0) {
aidan1971 9:40e0ff8c2ba2 758 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK)) return -1;
aidan1971 9:40e0ff8c2ba2 759 }
aidan1971 9:40e0ff8c2ba2 760 TX_block_size = _block_size;
aidan1971 9:40e0ff8c2ba2 761 TX_bs_bytes = _block_size * 4;
aidan1971 9:40e0ff8c2ba2 762 SGTL5000::BufTX_L_safe = (uint32_t*)_BufTX_L_safe; // Assign the users pointer addresses
aidan1971 9:40e0ff8c2ba2 763 SGTL5000::BufTX_R_safe = (uint32_t*)_BufTX_R_safe;
aidan1971 6:4ab5aaeaa064 764 *SGTL5000::BufTX_L_safe = (uint32_t)&I2S_TX_Buffer[8];
aidan1971 6:4ab5aaeaa064 765 *SGTL5000::BufTX_R_safe = (uint32_t)&I2S_TX_Buffer[8 + (TX_block_size / 2)];
aidan1971 9:40e0ff8c2ba2 766 SGTL5000::db_phase_tx = 0;
aidan1971 4:91354c908416 767 packed_TX = _packed_TX;
aidan1971 4:91354c908416 768 if(packed_TX) TX_shift = false;
aidan1971 4:91354c908416 769 else TX_shift = _TX_shift;
aidan1971 0:8f28f25e3435 770 SGTL5000::TX_DMAch = _TX_DMAch;
aidan1971 0:8f28f25e3435 771 TX_run = true;
aidan1971 0:8f28f25e3435 772 init_DMA();
aidan1971 0:8f28f25e3435 773
aidan1971 9:40e0ff8c2ba2 774 I2S0->TCSR |= I2S_TCSR_SR_MASK;
aidan1971 9:40e0ff8c2ba2 775 I2S0->TCSR &= ~I2S_TCSR_SR_MASK;
aidan1971 6:4ab5aaeaa064 776 I2S0->TCR1 = (I2S0->TCR1 & ~I2S_TCR1_TFW_MASK) | I2S_TCR1_TFW(8 - TX_block_size); // Set TX FIFO watermark
aidan1971 9:40e0ff8c2ba2 777 I2S0->TCSR &= ~I2S_TCSR_TE_MASK; // TX Disable
aidan1971 9:40e0ff8c2ba2 778 while(I2S0->TCSR & I2S_TCSR_TE_MASK); // Make sure TX is disabled before enabling corresponding channel
aidan1971 9:40e0ff8c2ba2 779 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(1); // Enable TX channel 0.
aidan1971 0:8f28f25e3435 780 I2S0->TCSR |= I2S_TCSR_TE_MASK; // TX enable
aidan1971 6:4ab5aaeaa064 781 if(TX_attach_type) NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::tx_dma_ISR_NB); // Set DMA TX handler vector
aidan1971 6:4ab5aaeaa064 782 else NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::tx_dma_ISR);
aidan1971 9:40e0ff8c2ba2 783 NVIC_SetPriority((IRQn)SGTL5000::TX_DMAch, _DMA_irq_pri); // Set irq priorities the same
aidan1971 0:8f28f25e3435 784 NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch); // Enable IRQ for chosen TX DMA channel
aidan1971 9:40e0ff8c2ba2 785 NVIC_SetVector(I2S0_Tx_IRQn, (uint32_t)&SGTL5000::tx_I2S_WS_ISR); // Set vector for TX word start ISR
aidan1971 0:8f28f25e3435 786 NVIC_SetPriority(I2S0_Tx_IRQn, 0); // Set priority of TX word start ISR
aidan1971 0:8f28f25e3435 787 NVIC_EnableIRQ(I2S0_Tx_IRQn); // Enable TX word start ISR
aidan1971 0:8f28f25e3435 788 I2S0->TCSR |= I2S_TCSR_WSF_MASK; // Clear TX Word Start Flag
aidan1971 0:8f28f25e3435 789 I2S0->TCSR |= I2S_TCSR_WSIE_MASK; // Enable I2S TX word start IRQ
aidan1971 0:8f28f25e3435 790 return 0;
aidan1971 0:8f28f25e3435 791 }
aidan1971 0:8f28f25e3435 792
aidan1971 8:9fdf8501d14b 793 int32_t SGTL5000::stop_TX(void)
aidan1971 0:8f28f25e3435 794 {
aidan1971 8:9fdf8501d14b 795 if(!codec_configured) return -1;
aidan1971 9:40e0ff8c2ba2 796 if(modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK)) return -1;
aidan1971 0:8f28f25e3435 797 I2S0->TCSR &= ~I2S_TCSR_FRDE_MASK; // Disable TX FIFO IRQs based on watermark
aidan1971 9:40e0ff8c2ba2 798 while(!(DMA0->TCD[SGTL5000::TX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete
aidan1971 9:40e0ff8c2ba2 799 I2S0->TCSR &= ~I2S_TCSR_TE_MASK; // TX Disable
aidan1971 9:40e0ff8c2ba2 800 while(I2S0->TCSR & I2S_TCSR_TE_MASK); // Make sure TX is disabled before disabling corresponding channel
aidan1971 9:40e0ff8c2ba2 801 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) & ~I2S_TCR3_TCE(1); // Disable TX channel.
aidan1971 0:8f28f25e3435 802 TX_run = false;
aidan1971 8:9fdf8501d14b 803 return 0;
aidan1971 0:8f28f25e3435 804 }
aidan1971 0:8f28f25e3435 805
aidan1971 6:4ab5aaeaa064 806 void SGTL5000::tx_dma_ISR_NB(void)
aidan1971 0:8f28f25e3435 807 {
aidan1971 0:8f28f25e3435 808
aidan1971 0:8f28f25e3435 809 DMA0->CINT = DMA_CINT_CINT(SGTL5000::TX_DMAch);
aidan1971 0:8f28f25e3435 810
aidan1971 6:4ab5aaeaa064 811 register uint32_t BU_TX_L;
aidan1971 6:4ab5aaeaa064 812 register uint32_t BU_TX_R;
aidan1971 6:4ab5aaeaa064 813 register uint32_t DB_PHASE;
aidan1971 6:4ab5aaeaa064 814 __asm {
aidan1971 9:40e0ff8c2ba2 815 LDR DB_PHASE, [&SGTL5000::db_phase_tx] // Pipeline memory access
aidan1971 6:4ab5aaeaa064 816 LDR BU_TX_L, [SGTL5000::BufTX_L_safe]
aidan1971 6:4ab5aaeaa064 817 LDR BU_TX_R, [SGTL5000::BufTX_R_safe]
aidan1971 6:4ab5aaeaa064 818
aidan1971 6:4ab5aaeaa064 819 TEQ DB_PHASE, #0x0
aidan1971 6:4ab5aaeaa064 820 IT EQ
aidan1971 6:4ab5aaeaa064 821 BEQ buf_base
aidan1971 6:4ab5aaeaa064 822
aidan1971 6:4ab5aaeaa064 823 ADD BU_TX_L, #32
aidan1971 6:4ab5aaeaa064 824 ADD BU_TX_R, #32
aidan1971 6:4ab5aaeaa064 825 SUB DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 826 B store
aidan1971 6:4ab5aaeaa064 827
aidan1971 6:4ab5aaeaa064 828 buf_base:
aidan1971 6:4ab5aaeaa064 829 SUB BU_TX_L, #32
aidan1971 6:4ab5aaeaa064 830 SUB BU_TX_R, #32
aidan1971 6:4ab5aaeaa064 831 ADD DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 832
aidan1971 6:4ab5aaeaa064 833 store: // Pipeline memory access
aidan1971 6:4ab5aaeaa064 834 STR BU_TX_L, [SGTL5000::BufTX_L_safe]
aidan1971 6:4ab5aaeaa064 835 STR BU_TX_R, [SGTL5000::BufTX_R_safe]
aidan1971 9:40e0ff8c2ba2 836 STR DB_PHASE, [&SGTL5000::db_phase_tx]
aidan1971 9:40e0ff8c2ba2 837 }
aidan1971 9:40e0ff8c2ba2 838 if(!NVIC_GetActive(SGTL5000::TX_swIRQ)) NVIC->STIR = SGTL5000::TX_swIRQ; // Trigger swIRQ if not still processing
aidan1971 9:40e0ff8c2ba2 839 }
aidan1971 9:40e0ff8c2ba2 840
aidan1971 9:40e0ff8c2ba2 841 void SGTL5000::tx_dma_ISR(void)
aidan1971 9:40e0ff8c2ba2 842 {
aidan1971 9:40e0ff8c2ba2 843
aidan1971 9:40e0ff8c2ba2 844 DMA0->CINT = DMA_CINT_CINT(SGTL5000::TX_DMAch);
aidan1971 9:40e0ff8c2ba2 845
aidan1971 9:40e0ff8c2ba2 846 register uint32_t BU_TX_L;
aidan1971 9:40e0ff8c2ba2 847 register uint32_t BU_TX_R;
aidan1971 9:40e0ff8c2ba2 848 register uint32_t DB_PHASE;
aidan1971 9:40e0ff8c2ba2 849 __asm {
aidan1971 9:40e0ff8c2ba2 850 LDR DB_PHASE, [&SGTL5000::db_phase_tx] // Pipeline memory access
aidan1971 9:40e0ff8c2ba2 851 LDR BU_TX_L, [SGTL5000::BufTX_L_safe]
aidan1971 9:40e0ff8c2ba2 852 LDR BU_TX_R, [SGTL5000::BufTX_R_safe]
aidan1971 9:40e0ff8c2ba2 853
aidan1971 9:40e0ff8c2ba2 854 TEQ DB_PHASE, #0x0
aidan1971 9:40e0ff8c2ba2 855 IT EQ
aidan1971 9:40e0ff8c2ba2 856 BEQ buf_base
aidan1971 9:40e0ff8c2ba2 857
aidan1971 9:40e0ff8c2ba2 858 ADD BU_TX_L, #32
aidan1971 9:40e0ff8c2ba2 859 ADD BU_TX_R, #32
aidan1971 9:40e0ff8c2ba2 860 SUB DB_PHASE, #0x1
aidan1971 9:40e0ff8c2ba2 861 B store
aidan1971 9:40e0ff8c2ba2 862
aidan1971 9:40e0ff8c2ba2 863 buf_base:
aidan1971 9:40e0ff8c2ba2 864 SUB BU_TX_L, #32
aidan1971 9:40e0ff8c2ba2 865 SUB BU_TX_R, #32
aidan1971 9:40e0ff8c2ba2 866 ADD DB_PHASE, #0x1
aidan1971 9:40e0ff8c2ba2 867
aidan1971 9:40e0ff8c2ba2 868 store: // Pipeline memory access
aidan1971 9:40e0ff8c2ba2 869 STR BU_TX_L, [SGTL5000::BufTX_L_safe]
aidan1971 9:40e0ff8c2ba2 870 STR BU_TX_R, [SGTL5000::BufTX_R_safe]
aidan1971 9:40e0ff8c2ba2 871 STR DB_PHASE, [&SGTL5000::db_phase_tx]
aidan1971 6:4ab5aaeaa064 872 }
aidan1971 6:4ab5aaeaa064 873 SGTL5000::TX_user_func.call(); // Callback user function
aidan1971 0:8f28f25e3435 874 }
aidan1971 0:8f28f25e3435 875
aidan1971 0:8f28f25e3435 876 int32_t SGTL5000::attach_RX(Callback<void()> func)
aidan1971 0:8f28f25e3435 877 {
aidan1971 9:40e0ff8c2ba2 878 if(RX_attached) return -1; // Assign Callback function
aidan1971 0:8f28f25e3435 879 SGTL5000::RX_user_func = func;
aidan1971 6:4ab5aaeaa064 880 RX_attach_type = 0;
aidan1971 0:8f28f25e3435 881 RX_attached = true;
aidan1971 0:8f28f25e3435 882 return 0;
aidan1971 0:8f28f25e3435 883 }
aidan1971 0:8f28f25e3435 884
aidan1971 8:9fdf8501d14b 885 int32_t SGTL5000::attach_RX_NB(uint32_t user_ISR, uint32_t irq_pri, IRQn sw_irq)
aidan1971 0:8f28f25e3435 886 {
aidan1971 9:40e0ff8c2ba2 887 SGTL5000::RX_swIRQ = sw_irq; // Assign ISR address and enable users IRQ
aidan1971 0:8f28f25e3435 888 NVIC_SetVector(SGTL5000::RX_swIRQ, user_ISR);
aidan1971 0:8f28f25e3435 889 NVIC_SetPriority(SGTL5000::RX_swIRQ, irq_pri);
aidan1971 0:8f28f25e3435 890 NVIC_EnableIRQ(SGTL5000::RX_swIRQ);
aidan1971 6:4ab5aaeaa064 891 RX_attach_type = 1;
aidan1971 0:8f28f25e3435 892 RX_attached = true;
aidan1971 8:9fdf8501d14b 893 return 0;
aidan1971 0:8f28f25e3435 894 }
aidan1971 0:8f28f25e3435 895
aidan1971 8:9fdf8501d14b 896 int32_t SGTL5000::detach_RX(void)
aidan1971 0:8f28f25e3435 897 {
aidan1971 8:9fdf8501d14b 898 if(RX_attached) {
aidan1971 8:9fdf8501d14b 899 SGTL5000::stop_RX();
aidan1971 8:9fdf8501d14b 900 RX_attached = false;
aidan1971 8:9fdf8501d14b 901 }
aidan1971 8:9fdf8501d14b 902 return 0;
aidan1971 0:8f28f25e3435 903 }
aidan1971 0:8f28f25e3435 904
aidan1971 0:8f28f25e3435 905 int32_t SGTL5000::start_RX(uint32_t BufRX_L_safe, uint32_t BufRX_R_safe,
aidan1971 9:40e0ff8c2ba2 906 uint32_t _block_size, bool _packed_RX, bool _RX_shift, uint32_t _RX_DMAch, uint32_t _DMA_irq_pri)
aidan1971 0:8f28f25e3435 907 {
aidan1971 8:9fdf8501d14b 908 if(!codec_configured) return -1;
aidan1971 6:4ab5aaeaa064 909 if(!RX_attached && !RX_attach_type) return -1; // Check we have a handler if using callback
aidan1971 0:8f28f25e3435 910 if(SYNC_run || RX_run) return -1; // Check if i2s is already started on rx
aidan1971 0:8f28f25e3435 911 if(_RX_DMAch > 15) return -1; // Sanity check DMAMUX channels
aidan1971 9:40e0ff8c2ba2 912 if (!(_block_size == 2 || _block_size == 4 || _block_size == 8)) return -1; // Only accept block size 2^n within range.
aidan1971 9:40e0ff8c2ba2 913 if(read_i2c(SGTL5000_DIG_POWER, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK) == 0) {
aidan1971 9:40e0ff8c2ba2 914 if(modify_i2c(SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -1;
aidan1971 9:40e0ff8c2ba2 915 }
aidan1971 9:40e0ff8c2ba2 916 RX_block_size = _block_size;
aidan1971 9:40e0ff8c2ba2 917 RX_bs_bytes = _block_size * 4;
aidan1971 0:8f28f25e3435 918 SGTL5000::BufRX_L_safe = (uint32_t*)BufRX_L_safe; // Assign the users pointer addresses
aidan1971 0:8f28f25e3435 919 SGTL5000::BufRX_R_safe = (uint32_t*)BufRX_R_safe;
aidan1971 6:4ab5aaeaa064 920 *SGTL5000::BufRX_L_safe = (uint32_t)&I2S_RX_Buffer[8];
aidan1971 6:4ab5aaeaa064 921 *SGTL5000::BufRX_R_safe = (uint32_t)&I2S_RX_Buffer[8 + (RX_block_size / 2)];
aidan1971 9:40e0ff8c2ba2 922 SGTL5000::db_phase_rx = 0;
aidan1971 4:91354c908416 923 packed_RX = _packed_RX;
aidan1971 4:91354c908416 924 if(packed_RX) RX_shift = false;
aidan1971 4:91354c908416 925 else RX_shift = _RX_shift;
aidan1971 0:8f28f25e3435 926 SGTL5000::RX_DMAch = _RX_DMAch;
aidan1971 0:8f28f25e3435 927 RX_run = true;
aidan1971 0:8f28f25e3435 928 init_DMA();
aidan1971 0:8f28f25e3435 929
aidan1971 9:40e0ff8c2ba2 930 I2S0->RCSR |= I2S_RCSR_SR_MASK;
aidan1971 9:40e0ff8c2ba2 931 I2S0->RCSR &= ~I2S_RCSR_SR_MASK;
aidan1971 6:4ab5aaeaa064 932 I2S0->RCR1 = (I2S0->RCR1 & ~I2S_RCR1_RFW_MASK) | I2S_RCR1_RFW(RX_block_size - 1); // Set RX FIFO watermark
aidan1971 9:40e0ff8c2ba2 933 I2S0->RCSR &= ~I2S_RCSR_RE_MASK; // RX Disable
aidan1971 9:40e0ff8c2ba2 934 while(I2S0->RCSR & I2S_RCSR_RE_MASK); // Make sure RX is disabled before enabling corresponding channel
aidan1971 9:40e0ff8c2ba2 935 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(1); // Enable RX channel 0.
aidan1971 0:8f28f25e3435 936 I2S0->RCSR |= I2S_RCSR_RE_MASK; // RX enable
aidan1971 6:4ab5aaeaa064 937 if(RX_attach_type) NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::rx_dma_ISR_NB); // Set DMA RX handler vector
aidan1971 6:4ab5aaeaa064 938 else NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::rx_dma_ISR);
aidan1971 9:40e0ff8c2ba2 939 NVIC_SetPriority((IRQn)SGTL5000::RX_DMAch, _DMA_irq_pri); // Set irq priorities the same
aidan1971 0:8f28f25e3435 940 NVIC_EnableIRQ((IRQn)SGTL5000::RX_DMAch); // Enable IRQ for chosen RX DMA channel
aidan1971 9:40e0ff8c2ba2 941 NVIC_SetVector(I2S0_Rx_IRQn, (uint32_t)&SGTL5000::rx_I2S_WS_ISR); // Set vector for RX word start ISR
aidan1971 0:8f28f25e3435 942 NVIC_SetPriority(I2S0_Rx_IRQn, 0); // Set priority of RX word start ISR
aidan1971 0:8f28f25e3435 943 NVIC_EnableIRQ(I2S0_Rx_IRQn); // Enable RX word start ISR
aidan1971 0:8f28f25e3435 944 I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag
aidan1971 0:8f28f25e3435 945 I2S0->RCSR |= I2S_RCSR_WSIE_MASK; // Enable I2S RX word start IRQ
aidan1971 0:8f28f25e3435 946 return 0;
aidan1971 0:8f28f25e3435 947 }
aidan1971 0:8f28f25e3435 948
aidan1971 8:9fdf8501d14b 949 int32_t SGTL5000::stop_RX(void)
aidan1971 0:8f28f25e3435 950 {
aidan1971 8:9fdf8501d14b 951 if(!codec_configured) return -1;
aidan1971 9:40e0ff8c2ba2 952 if(modify_i2c(SGTL5000_DIG_POWER, 0x0, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK)) return -1; // codec I2S disable
aidan1971 0:8f28f25e3435 953 I2S0->RCSR &= ~I2S_RCSR_FRDE_MASK; // Disable RX FIFO IRQs based on watermark
aidan1971 9:40e0ff8c2ba2 954 while(!(DMA0->TCD[SGTL5000::RX_DMAch].CSR | DMA_CSR_DONE_MASK)); // Ensure any active DMA transfers are complete
aidan1971 9:40e0ff8c2ba2 955 I2S0->RCSR &= ~I2S_RCSR_RE_MASK; // RX Disable
aidan1971 9:40e0ff8c2ba2 956 while(I2S0->RCSR & I2S_RCSR_RE_MASK); // Make sure RX is disabled before disabling corresponding channel
aidan1971 9:40e0ff8c2ba2 957 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) & ~I2S_RCR3_RCE(1); // Disable RX channel.
aidan1971 0:8f28f25e3435 958 RX_run = false;
aidan1971 8:9fdf8501d14b 959 return 0;
aidan1971 0:8f28f25e3435 960 }
aidan1971 0:8f28f25e3435 961
aidan1971 6:4ab5aaeaa064 962 void SGTL5000::rx_dma_ISR_NB(void)
aidan1971 0:8f28f25e3435 963 {
aidan1971 0:8f28f25e3435 964
aidan1971 0:8f28f25e3435 965 DMA0->CINT = DMA_CINT_CINT(SGTL5000::RX_DMAch);
aidan1971 0:8f28f25e3435 966
aidan1971 6:4ab5aaeaa064 967 register uint32_t BU_RX_L;
aidan1971 6:4ab5aaeaa064 968 register uint32_t BU_RX_R;
aidan1971 6:4ab5aaeaa064 969 register uint32_t DB_PHASE;
aidan1971 6:4ab5aaeaa064 970 __asm {
aidan1971 9:40e0ff8c2ba2 971 LDR DB_PHASE, [&SGTL5000::db_phase_rx] // Pipeline memory access
aidan1971 6:4ab5aaeaa064 972 LDR BU_RX_L, [SGTL5000::BufRX_L_safe]
aidan1971 6:4ab5aaeaa064 973 LDR BU_RX_R, [SGTL5000::BufRX_R_safe]
aidan1971 6:4ab5aaeaa064 974
aidan1971 6:4ab5aaeaa064 975 TEQ DB_PHASE, #0x0
aidan1971 6:4ab5aaeaa064 976 IT EQ
aidan1971 6:4ab5aaeaa064 977 BEQ buf_base
aidan1971 6:4ab5aaeaa064 978
aidan1971 6:4ab5aaeaa064 979 ADD BU_RX_L, #32
aidan1971 6:4ab5aaeaa064 980 ADD BU_RX_R, #32
aidan1971 6:4ab5aaeaa064 981 SUB DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 982 B store
aidan1971 6:4ab5aaeaa064 983
aidan1971 6:4ab5aaeaa064 984 buf_base:
aidan1971 6:4ab5aaeaa064 985 SUB BU_RX_L, #32
aidan1971 6:4ab5aaeaa064 986 SUB BU_RX_R, #32
aidan1971 6:4ab5aaeaa064 987 ADD DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 988
aidan1971 6:4ab5aaeaa064 989 store: // Pipeline memory access
aidan1971 6:4ab5aaeaa064 990 STR BU_RX_L, [SGTL5000::BufRX_L_safe]
aidan1971 6:4ab5aaeaa064 991 STR BU_RX_R, [SGTL5000::BufRX_R_safe]
aidan1971 9:40e0ff8c2ba2 992 STR DB_PHASE, [&SGTL5000::db_phase_rx]
aidan1971 0:8f28f25e3435 993 }
aidan1971 6:4ab5aaeaa064 994
aidan1971 9:40e0ff8c2ba2 995 if(!NVIC_GetActive(SGTL5000::RX_swIRQ)) NVIC->STIR = SGTL5000::RX_swIRQ; // Trigger swIRQ if not still active
aidan1971 6:4ab5aaeaa064 996
aidan1971 6:4ab5aaeaa064 997 }
aidan1971 6:4ab5aaeaa064 998
aidan1971 6:4ab5aaeaa064 999 void SGTL5000::rx_dma_ISR(void)
aidan1971 6:4ab5aaeaa064 1000 {
aidan1971 6:4ab5aaeaa064 1001
aidan1971 6:4ab5aaeaa064 1002 DMA0->CINT = DMA_CINT_CINT(SGTL5000::RX_DMAch);
aidan1971 6:4ab5aaeaa064 1003
aidan1971 6:4ab5aaeaa064 1004 register uint32_t BU_RX_L;
aidan1971 6:4ab5aaeaa064 1005 register uint32_t BU_RX_R;
aidan1971 6:4ab5aaeaa064 1006 register uint32_t DB_PHASE;
aidan1971 6:4ab5aaeaa064 1007 __asm {
aidan1971 9:40e0ff8c2ba2 1008 LDR DB_PHASE, [&SGTL5000::db_phase_rx] // Pipeline memory access
aidan1971 6:4ab5aaeaa064 1009 LDR BU_RX_L, [SGTL5000::BufRX_L_safe]
aidan1971 6:4ab5aaeaa064 1010 LDR BU_RX_R, [SGTL5000::BufRX_R_safe]
aidan1971 6:4ab5aaeaa064 1011
aidan1971 6:4ab5aaeaa064 1012 TEQ DB_PHASE, #0x0
aidan1971 6:4ab5aaeaa064 1013 IT EQ
aidan1971 6:4ab5aaeaa064 1014 BEQ buf_base
aidan1971 6:4ab5aaeaa064 1015
aidan1971 6:4ab5aaeaa064 1016 ADD BU_RX_L, #32
aidan1971 6:4ab5aaeaa064 1017 ADD BU_RX_R, #32
aidan1971 6:4ab5aaeaa064 1018 SUB DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 1019 B store
aidan1971 6:4ab5aaeaa064 1020
aidan1971 6:4ab5aaeaa064 1021 buf_base:
aidan1971 6:4ab5aaeaa064 1022 SUB BU_RX_L, #32
aidan1971 6:4ab5aaeaa064 1023 SUB BU_RX_R, #32
aidan1971 6:4ab5aaeaa064 1024 ADD DB_PHASE, #0x1
aidan1971 6:4ab5aaeaa064 1025
aidan1971 6:4ab5aaeaa064 1026 store: // Pipeline memory access
aidan1971 6:4ab5aaeaa064 1027 STR BU_RX_L, [SGTL5000::BufRX_L_safe]
aidan1971 6:4ab5aaeaa064 1028 STR BU_RX_R, [SGTL5000::BufRX_R_safe]
aidan1971 9:40e0ff8c2ba2 1029 STR DB_PHASE, [&SGTL5000::db_phase_rx]
aidan1971 6:4ab5aaeaa064 1030 }
aidan1971 6:4ab5aaeaa064 1031 SGTL5000::RX_user_func.call(); // Callback user function
aidan1971 0:8f28f25e3435 1032 }
aidan1971 0:8f28f25e3435 1033
aidan1971 0:8f28f25e3435 1034
aidan1971 0:8f28f25e3435 1035
aidan1971 0:8f28f25e3435 1036 void SGTL5000::init_DMA(void)
aidan1971 0:8f28f25e3435 1037 {
aidan1971 3:62c03088f256 1038 /*!
aidan1971 0:8f28f25e3435 1039 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 1040 The chain is configured as follows:
aidan1971 0:8f28f25e3435 1041 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 1042 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 1043 uppper half of the double buffer.
aidan1971 0:8f28f25e3435 1044 eg.
aidan1971 4:91354c908416 1045 Unpacked buffer layout:
aidan1971 4:91354c908416 1046 Block Size = 2 Block Size = 4 Block Size = 8
aidan1971 4:91354c908416 1047 Double Buffer A Double Buffer B Double Buffer A Double Buffer B Double Buffer A Double Buffer B
aidan1971 4:91354c908416 1048 |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 1049
aidan1971 4:91354c908416 1050 Packed buffer layout:
aidan1971 4:91354c908416 1051 Block Size = 2 Block Size = 4 Block Size = 8
aidan1971 4:91354c908416 1052 Double Buffer A Double Buffer B Double Buffer A Double Buffer B Double Buffer A Double Buffer B
aidan1971 4:91354c908416 1053 |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 1054
aidan1971 4:91354c908416 1055 The users pointers are always updated to point to L0 & R0 of the current safe double buffer area.
aidan1971 0:8f28f25e3435 1056
aidan1971 0:8f28f25e3435 1057 */
aidan1971 0:8f28f25e3435 1058
aidan1971 0:8f28f25e3435 1059 static uint32_t SG_rx_TCD_A[8] __attribute ((aligned (0x20))); // Allocate memory for scatter gather TCD definitions
aidan1971 0:8f28f25e3435 1060 static uint32_t SG_rx_TCD_B[8] __attribute ((aligned (0x20)));
aidan1971 0:8f28f25e3435 1061 static uint32_t SG_tx_TCD_A[8] __attribute ((aligned (0x20)));
aidan1971 0:8f28f25e3435 1062 static uint32_t SG_tx_TCD_B[8] __attribute ((aligned (0x20)));
aidan1971 0:8f28f25e3435 1063
aidan1971 0:8f28f25e3435 1064 // Clock Control config to DMA Controller and DMAMux and common DMA Setup
aidan1971 0:8f28f25e3435 1065 SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK; // Enable Clocking to DMAMUX Module
aidan1971 0:8f28f25e3435 1066 SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Enable Clocking to DMA Module
aidan1971 0:8f28f25e3435 1067 DMA0->CR |= DMA_CR_EMLM_MASK; // Enable minor loop mapping
aidan1971 0:8f28f25e3435 1068
aidan1971 0:8f28f25e3435 1069 if(SYNC_run || RX_run) {
aidan1971 0:8f28f25e3435 1070 // DMAMUX Config
aidan1971 0:8f28f25e3435 1071 DMAMUX->CHCFG[SGTL5000::RX_DMAch] &= ~DMAMUX_CHCFG_ENBL_MASK; // MUX channels disabled while we configure
aidan1971 0:8f28f25e3435 1072 DMAMUX->CHCFG[SGTL5000::RX_DMAch] &= ~DMAMUX_CHCFG_TRIG_MASK; // Trigger Modes Normal
aidan1971 0:8f28f25e3435 1073 DMAMUX->CHCFG[SGTL5000::RX_DMAch] = (DMAMUX->CHCFG[SGTL5000::RX_DMAch] & ~DMAMUX_CHCFG_SOURCE_MASK) | DMAMUX_CHCFG_SOURCE(14); // Route I2S FIFO request to DMA. Expect Left channel first. i2s0 RX =14 TX = 15
aidan1971 0:8f28f25e3435 1074 DMA0->CERQ =DMA_CERQ_CERQ(SGTL5000::RX_DMAch); // Disable requests for chosen DMA channels
aidan1971 0:8f28f25e3435 1075
aidan1971 0:8f28f25e3435 1076 //Configure RX DMA Transfer Control Descriptor
aidan1971 0:8f28f25e3435 1077 DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES &= ~DMA_NBYTES_MLOFFYES_SMLOE_MASK; // Disable source offset on minor loop
aidan1971 0:8f28f25e3435 1078 DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_DMLOE_MASK; // Enable dest offset on minor loop
aidan1971 4:91354c908416 1079 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 1080 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 1081 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 1082 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 1083 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 1084 DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_ESG_MASK; // Enable scatter gather
aidan1971 0:8f28f25e3435 1085 DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_DREQ_MASK; // Disable auto reset of request enable on major
aidan1971 0:8f28f25e3435 1086 DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_INTHALF_MASK; // Disable IRQ at Half Completion of Major cycle
aidan1971 0:8f28f25e3435 1087 DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; // Disable major loop linking
aidan1971 0:8f28f25e3435 1088 DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; // Enable IRQ at Completion of Major cycle
aidan1971 0:8f28f25e3435 1089 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 6:4ab5aaeaa064 1090 if(RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&I2S_RX_Buffer[0] + 2;
aidan1971 6:4ab5aaeaa064 1091 else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&I2S_RX_Buffer[0];
aidan1971 0:8f28f25e3435 1092 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 1093 DMA0->TCD[SGTL5000::RX_DMAch].SOFF = 0; // Signed Source offset set to zero (always read from RDR[0]).
aidan1971 4:91354c908416 1094 DMA0->TCD[SGTL5000::RX_DMAch].DOFF = (RX_bs_bytes / 2); // After each write step into upper half of the buffer
aidan1971 0:8f28f25e3435 1095 DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO &= ~DMA_CITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop
aidan1971 6:4ab5aaeaa064 1096 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 1097 DMA0->TCD[SGTL5000::RX_DMAch].BITER_ELINKNO &= ~DMA_BITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop
aidan1971 6:4ab5aaeaa064 1098 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 0:8f28f25e3435 1099 DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B[0]; // Set scatter gather TCD definition location
aidan1971 0:8f28f25e3435 1100 DMA0->TCD[SGTL5000::RX_DMAch].SLAST = 0;
aidan1971 0:8f28f25e3435 1101 memcpy(&SG_rx_TCD_A[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32); // Copy TCD A to memory
aidan1971 0:8f28f25e3435 1102 DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_A[0]; // Set scatter gather TCD definition location
aidan1971 6:4ab5aaeaa064 1103 if(RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&I2S_RX_Buffer[8] + 2; // Swap RX double buffer
aidan1971 6:4ab5aaeaa064 1104 else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&I2S_RX_Buffer[8];
aidan1971 0:8f28f25e3435 1105 memcpy(&SG_rx_TCD_B[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32); // Copy TCD B to memory
aidan1971 0:8f28f25e3435 1106 // Set TCD elements in the DMA controller back to initial TCD A
aidan1971 0:8f28f25e3435 1107 DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B[0]; // Set scatter gather TCD definition location
aidan1971 6:4ab5aaeaa064 1108 if(RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&I2S_RX_Buffer[0] + 2;
aidan1971 6:4ab5aaeaa064 1109 else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&I2S_RX_Buffer[0];
aidan1971 0:8f28f25e3435 1110 }
aidan1971 0:8f28f25e3435 1111
aidan1971 0:8f28f25e3435 1112
aidan1971 0:8f28f25e3435 1113 if(SYNC_run || TX_run) {
aidan1971 0:8f28f25e3435 1114 DMAMUX->CHCFG[SGTL5000::TX_DMAch] &= ~DMAMUX_CHCFG_ENBL_MASK;
aidan1971 0:8f28f25e3435 1115 DMAMUX->CHCFG[SGTL5000::TX_DMAch] &= ~DMAMUX_CHCFG_TRIG_MASK;
aidan1971 0:8f28f25e3435 1116 DMAMUX->CHCFG[SGTL5000::TX_DMAch] = (DMAMUX->CHCFG[SGTL5000::TX_DMAch] & ~DMAMUX_CHCFG_SOURCE_MASK) | DMAMUX_CHCFG_SOURCE(15); // Route I2S FIFO request to DMA.
aidan1971 0:8f28f25e3435 1117 DMA0->CERQ = DMA_CERQ_CERQ(SGTL5000::TX_DMAch);
aidan1971 0:8f28f25e3435 1118
aidan1971 0:8f28f25e3435 1119 //Configure TX DMA Transfer Control Descriptor
aidan1971 0:8f28f25e3435 1120 DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_SMLOE_MASK; // Disable source offset on minor loop
aidan1971 0:8f28f25e3435 1121 DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES &= ~DMA_NBYTES_MLOFFYES_DMLOE_MASK; // Enable dest offset on minor loop
aidan1971 4:91354c908416 1122 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 1123 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 1124 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 1125 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 1126 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 1127 DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_ESG_MASK; // Enable scatter gather
aidan1971 0:8f28f25e3435 1128 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_DREQ_MASK; // Disable auto reset of request enable on major
aidan1971 0:8f28f25e3435 1129 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_INTHALF_MASK; // Disable IRQ at Half Completion of Major cycle
aidan1971 0:8f28f25e3435 1130 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; // Disable major loop linking
aidan1971 0:8f28f25e3435 1131 DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; // Enable IRQ at Completion of Major cycle
aidan1971 0:8f28f25e3435 1132 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 6:4ab5aaeaa064 1133 if(TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&I2S_TX_Buffer[0] + 2;
aidan1971 6:4ab5aaeaa064 1134 else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&I2S_TX_Buffer[0];
aidan1971 0:8f28f25e3435 1135 DMA0->TCD[SGTL5000::TX_DMAch].DADDR = (uint32_t)&I2S0->TDR[0]; // Set rxDMA Source addr pointer to 1st bit of I2S RX Data Reg
aidan1971 0:8f28f25e3435 1136 DMA0->TCD[SGTL5000::TX_DMAch].DOFF = 0; // Signed Source offset set to zero (always write TDR[0]).
aidan1971 0:8f28f25e3435 1137 DMA0->TCD[SGTL5000::TX_DMAch].SOFF = (TX_bs_bytes / 2); // After each write step into upper half of the buffer
aidan1971 0:8f28f25e3435 1138 DMA0->TCD[SGTL5000::TX_DMAch].CITER_ELINKNO &= ~DMA_CITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop
aidan1971 6:4ab5aaeaa064 1139 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 1140 DMA0->TCD[SGTL5000::TX_DMAch].BITER_ELINKNO &= ~DMA_BITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop
aidan1971 6:4ab5aaeaa064 1141 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 0:8f28f25e3435 1142 DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0]; // Set scatter gather TCD definition location
aidan1971 0:8f28f25e3435 1143 memcpy(&SG_tx_TCD_A[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32); // Copy TCD A to memory
aidan1971 0:8f28f25e3435 1144 DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_A[0]; // Set scatter gather TCD definition location
aidan1971 6:4ab5aaeaa064 1145 if(TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&I2S_TX_Buffer[8] + 2; // Swap TX double buffer
aidan1971 6:4ab5aaeaa064 1146 else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&I2S_TX_Buffer[8];
aidan1971 0:8f28f25e3435 1147 memcpy(&SG_tx_TCD_B[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32); // Copy TCD B to memory
aidan1971 0:8f28f25e3435 1148 // Set TCD elements in the DMA controller back to initial TCD A
aidan1971 0:8f28f25e3435 1149 DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0]; // Set scatter gather TCD definition location
aidan1971 6:4ab5aaeaa064 1150 if(TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&I2S_TX_Buffer[0] + 2;
aidan1971 6:4ab5aaeaa064 1151 else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&I2S_TX_Buffer[0];
aidan1971 0:8f28f25e3435 1152 }
aidan1971 0:8f28f25e3435 1153
aidan1971 0:8f28f25e3435 1154 if(SYNC_run || RX_run) {
aidan1971 0:8f28f25e3435 1155 DMA0->SERQ = DMA_SERQ_SERQ(SGTL5000::RX_DMAch); // Enable requests for RX DMA channel
aidan1971 0:8f28f25e3435 1156 DMAMUX->CHCFG[SGTL5000::RX_DMAch] |= DMAMUX_CHCFG_ENBL_MASK; // DMAMUX channel enabled. Pass DMA requests from I2S to the DMA controller.
aidan1971 0:8f28f25e3435 1157 }
aidan1971 0:8f28f25e3435 1158 if(SYNC_run || TX_run) {
aidan1971 0:8f28f25e3435 1159 DMA0->SERQ = DMA_SERQ_SERQ(SGTL5000::TX_DMAch); // Enable requests for TX DMA channel
aidan1971 0:8f28f25e3435 1160 DMAMUX->CHCFG[SGTL5000::TX_DMAch] |= DMAMUX_CHCFG_ENBL_MASK; // DMAMUX channel enabled. Pass DMA requests from I2S to the DMA controller.
aidan1971 0:8f28f25e3435 1161 }
aidan1971 0:8f28f25e3435 1162 }
aidan1971 0:8f28f25e3435 1163
aidan1971 8:9fdf8501d14b 1164 int32_t SGTL5000::read_debug(uint32_t index)
aidan1971 0:8f28f25e3435 1165 {
aidan1971 5:664802e89661 1166 //SGTL5000::debug[0] = packed_RX;
aidan1971 5:664802e89661 1167 //SGTL5000::debug[1] = packed_TX;
aidan1971 6:4ab5aaeaa064 1168 //SGTL5000::debug[2] = I2S_RX_Buffer[0];
aidan1971 0:8f28f25e3435 1169 return SGTL5000::debug[index];
aidan1971 0:8f28f25e3435 1170 };
aidan1971 0:8f28f25e3435 1171 }