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:
Wed Jul 05 17:30:08 2017 +0000
Revision:
7:d65476c153a4
Parent:
6:4ab5aaeaa064
Child:
8:9fdf8501d14b
Placed locks around I2C access.

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