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 01 10:20:45 2017 +0000
Revision:
6:4ab5aaeaa064
Parent:
5:664802e89661
Child:
7:d65476c153a4
Separated ISRs into Callback type and IRQ type to avoid unnecessary checks. ; Replaced ISR code with inline assembler to optimize memory calls.

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