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:
Thu Jun 15 09:27:42 2017 +0000
Revision:
0:8f28f25e3435
Child:
1:d48e64f611fb
version 0.1

Who changed what in which revision?

UserRevisionLine numberNew contents of line
aidan1971 0:8f28f25e3435 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::I2S_TX_Buffer[16]; // Private double buffer space
aidan1971 0:8f28f25e3435 34 uint32_t SGTL5000::I2S_RX_Buffer[16];
aidan1971 0:8f28f25e3435 35 uint32_t *SGTL5000::BufRX_L_safe; // Private pointers assigned to users data pointers
aidan1971 0:8f28f25e3435 36 uint32_t *SGTL5000::BufRX_R_safe; // These are used to flip user pointers between safe 'active' regions of
aidan1971 0:8f28f25e3435 37 uint32_t *SGTL5000::BufTX_L_safe; // double buffered space.
aidan1971 0:8f28f25e3435 38 uint32_t *SGTL5000::BufTX_R_safe;
aidan1971 0:8f28f25e3435 39 uint32_t SGTL5000::TX_block_size; // User defined block size per call. This is the no. of 32bit words pushed or pulled from FIFO both left and right channel.
aidan1971 0:8f28f25e3435 40 uint32_t SGTL5000::RX_block_size;
aidan1971 0:8f28f25e3435 41 uint32_t SGTL5000::SYNC_attach_type = 0; // User defined, blocking or non-blocking calls from DMA ISR. non-blocking uses software triggered IRQ, blocking uses callback.
aidan1971 0:8f28f25e3435 42 uint32_t SGTL5000::TX_attach_type = 0;
aidan1971 0:8f28f25e3435 43 uint32_t SGTL5000::RX_attach_type = 0;
aidan1971 0:8f28f25e3435 44 IRQn SGTL5000::SYNC_swIRQ; // IRQn assigned by user to the software IRQ triggered by the FIFO queues.
aidan1971 0:8f28f25e3435 45 IRQn SGTL5000::TX_swIRQ;
aidan1971 0:8f28f25e3435 46 IRQn SGTL5000::RX_swIRQ;
aidan1971 0:8f28f25e3435 47 uint32_t SGTL5000::RX_DMAch; // User defined RX DMA channel number
aidan1971 0:8f28f25e3435 48 uint32_t SGTL5000::TX_DMAch; // User defined TX DMA channel number
aidan1971 0:8f28f25e3435 49 Callback<void()> SGTL5000::TX_user_func = NULL;
aidan1971 0:8f28f25e3435 50 Callback<void()> SGTL5000::RX_user_func = NULL;
aidan1971 0:8f28f25e3435 51 Callback<void()> SGTL5000::SYNC_user_func = NULL; // User defined callback function
aidan1971 0:8f28f25e3435 52
aidan1971 0:8f28f25e3435 53 uint32_t volatile SGTL5000::debug[16] = {0};
aidan1971 0:8f28f25e3435 54
aidan1971 0:8f28f25e3435 55 SGTL5000::SGTL5000(PinName i2c_sda, PinName i2c_scl, int i2c_freq, bool i2c_ctrl_adr0_cs)
aidan1971 0:8f28f25e3435 56 : mI2C(i2c_sda, i2c_scl)
aidan1971 0:8f28f25e3435 57 {
aidan1971 0:8f28f25e3435 58 if(i2c_ctrl_adr0_cs) i2c_addr = SGTL5000_I2C_ADDR_CS_HIGH << 1;
aidan1971 0:8f28f25e3435 59 else i2c_addr = SGTL5000_I2C_ADDR_CS_LOW << 1;
aidan1971 0:8f28f25e3435 60 mI2C.frequency(i2c_freq);
aidan1971 0:8f28f25e3435 61 init_i2s();
aidan1971 0:8f28f25e3435 62 init_codec();
aidan1971 0:8f28f25e3435 63 SYNC_run = false;
aidan1971 0:8f28f25e3435 64 TX_run = false;
aidan1971 0:8f28f25e3435 65 RX_run = false;
aidan1971 0:8f28f25e3435 66 SYNC_attached = false;
aidan1971 0:8f28f25e3435 67 TX_attached = false;
aidan1971 0:8f28f25e3435 68 RX_attached = false;
aidan1971 0:8f28f25e3435 69 }
aidan1971 0:8f28f25e3435 70
aidan1971 0:8f28f25e3435 71 int32_t SGTL5000::freq(uint32_t rate)
aidan1971 0:8f28f25e3435 72 {
aidan1971 0:8f28f25e3435 73 uint32_t I2S_MCLK_M;
aidan1971 0:8f28f25e3435 74 uint32_t I2S_MCLK_D;
aidan1971 0:8f28f25e3435 75 uint32_t codec_SYS_FS;
aidan1971 0:8f28f25e3435 76 uint32_t codec_RATE_MODE;
aidan1971 0:8f28f25e3435 77 switch(rate) {
aidan1971 0:8f28f25e3435 78 case 8:
aidan1971 0:8f28f25e3435 79 I2S_MCLK_M = 16;
aidan1971 0:8f28f25e3435 80 I2S_MCLK_D = 750;
aidan1971 0:8f28f25e3435 81 codec_SYS_FS = 0x02;
aidan1971 0:8f28f25e3435 82 codec_RATE_MODE = 0x01;
aidan1971 0:8f28f25e3435 83 break;
aidan1971 0:8f28f25e3435 84 case 11:
aidan1971 0:8f28f25e3435 85 I2S_MCLK_M = 1;
aidan1971 0:8f28f25e3435 86 I2S_MCLK_D = 34;
aidan1971 0:8f28f25e3435 87 codec_SYS_FS = 0x01;
aidan1971 0:8f28f25e3435 88 codec_RATE_MODE = 0x01;
aidan1971 0:8f28f25e3435 89 break;
aidan1971 0:8f28f25e3435 90 case 12:
aidan1971 0:8f28f25e3435 91 I2S_MCLK_M = 16;
aidan1971 0:8f28f25e3435 92 I2S_MCLK_D = 500;
aidan1971 0:8f28f25e3435 93 codec_SYS_FS = 0x02;
aidan1971 0:8f28f25e3435 94 codec_RATE_MODE = 0x01;
aidan1971 0:8f28f25e3435 95 break;
aidan1971 0:8f28f25e3435 96 case 16:
aidan1971 0:8f28f25e3435 97 I2S_MCLK_M = 32;
aidan1971 0:8f28f25e3435 98 I2S_MCLK_D = 750;
aidan1971 0:8f28f25e3435 99 codec_SYS_FS = 0x00;
aidan1971 0:8f28f25e3435 100 codec_RATE_MODE = 0x01;
aidan1971 0:8f28f25e3435 101 break;
aidan1971 0:8f28f25e3435 102 case 22:
aidan1971 0:8f28f25e3435 103 I2S_MCLK_M = 1;
aidan1971 0:8f28f25e3435 104 I2S_MCLK_D = 17;
aidan1971 0:8f28f25e3435 105 codec_SYS_FS = 0x01;
aidan1971 0:8f28f25e3435 106 codec_RATE_MODE = 0x01;
aidan1971 0:8f28f25e3435 107 break;
aidan1971 0:8f28f25e3435 108 case 24:
aidan1971 0:8f28f25e3435 109 I2S_MCLK_M = 32;
aidan1971 0:8f28f25e3435 110 I2S_MCLK_D = 500;
aidan1971 0:8f28f25e3435 111 codec_SYS_FS = 0x02;
aidan1971 0:8f28f25e3435 112 codec_RATE_MODE = 0x01;
aidan1971 0:8f28f25e3435 113 break;
aidan1971 0:8f28f25e3435 114 case 32:
aidan1971 0:8f28f25e3435 115 I2S_MCLK_M = 64;
aidan1971 0:8f28f25e3435 116 I2S_MCLK_D = 750;
aidan1971 0:8f28f25e3435 117 codec_SYS_FS = 0x00;
aidan1971 0:8f28f25e3435 118 codec_RATE_MODE = 0x0;
aidan1971 0:8f28f25e3435 119 break;
aidan1971 0:8f28f25e3435 120 case 44:
aidan1971 0:8f28f25e3435 121 I2S_MCLK_M = 2;
aidan1971 0:8f28f25e3435 122 I2S_MCLK_D = 17;
aidan1971 0:8f28f25e3435 123 codec_SYS_FS = 0x01;
aidan1971 0:8f28f25e3435 124 codec_RATE_MODE = 0x0;
aidan1971 0:8f28f25e3435 125 break;
aidan1971 0:8f28f25e3435 126 case 48:
aidan1971 0:8f28f25e3435 127 I2S_MCLK_M = 32;
aidan1971 0:8f28f25e3435 128 I2S_MCLK_D = 250;
aidan1971 0:8f28f25e3435 129 codec_SYS_FS = 0x02;
aidan1971 0:8f28f25e3435 130 codec_RATE_MODE = 0x0;
aidan1971 0:8f28f25e3435 131 break;
aidan1971 0:8f28f25e3435 132 case 96:
aidan1971 0:8f28f25e3435 133 I2S_MCLK_M = 32;
aidan1971 0:8f28f25e3435 134 I2S_MCLK_D = 125;
aidan1971 0:8f28f25e3435 135 codec_SYS_FS = 0x03;
aidan1971 0:8f28f25e3435 136 codec_RATE_MODE = 0x0;
aidan1971 0:8f28f25e3435 137 break;
aidan1971 0:8f28f25e3435 138 case 192:
aidan1971 0:8f28f25e3435 139 I2S_MCLK_M = 64;
aidan1971 0:8f28f25e3435 140 I2S_MCLK_D = 125;
aidan1971 0:8f28f25e3435 141 codec_SYS_FS = 0x03;
aidan1971 0:8f28f25e3435 142 codec_RATE_MODE = 0x0;
aidan1971 0:8f28f25e3435 143 break;
aidan1971 0:8f28f25e3435 144 default:
aidan1971 0:8f28f25e3435 145 return -1;
aidan1971 0:8f28f25e3435 146 }
aidan1971 0:8f28f25e3435 147 if(modify_i2c(SGTL5000_CLK_CTRL, codec_SYS_FS, SGTL5000_CLK_CTRL_SYS_FS_MASK)) return -1; // Set CODEC clocking
aidan1971 0:8f28f25e3435 148 if(modify_i2c(SGTL5000_CLK_CTRL, codec_RATE_MODE, SGTL5000_CLK_CTRL_RATE_MODE_MASK)) return -1; // Set CODEC clocking
aidan1971 0:8f28f25e3435 149 while(I2S0->MCR & I2S_MCR_DUF_MASK); // Check for active changes then set I2S Clock Fractions
aidan1971 0:8f28f25e3435 150 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 151 return 0;
aidan1971 0:8f28f25e3435 152 }
aidan1971 0:8f28f25e3435 153
aidan1971 0:8f28f25e3435 154 int32_t SGTL5000::read_i2c(uint32_t reg_addr, uint32_t mask)
aidan1971 0:8f28f25e3435 155 {
aidan1971 0:8f28f25e3435 156 if(mask == 0x0 || mask > 0xFFFF) return -1;
aidan1971 0:8f28f25e3435 157 uint32_t shift;
aidan1971 0:8f28f25e3435 158 for (shift = 0; !(mask & (1<<shift)); ++shift) {};
aidan1971 0:8f28f25e3435 159 uint32_t wire_data;
aidan1971 0:8f28f25e3435 160 uint32_t wire_addr = __rev(reg_addr) >> 16;
aidan1971 0:8f28f25e3435 161 if(mI2C.write(i2c_addr, (char *)&wire_addr, 2, false)) return -1;
aidan1971 0:8f28f25e3435 162 if(mI2C.read(i2c_addr, (char *)&wire_data, 2, false)) return -1;
aidan1971 0:8f28f25e3435 163 return ((__rev(wire_data) >> 16) & mask) >> shift;
aidan1971 0:8f28f25e3435 164 }
aidan1971 0:8f28f25e3435 165
aidan1971 0:8f28f25e3435 166 int32_t SGTL5000::write_i2c(uint32_t reg_addr, uint32_t data)
aidan1971 0:8f28f25e3435 167 {
aidan1971 0:8f28f25e3435 168 uint32_t wire_data = (__rev(reg_addr) >> 16) | (__rev(data));
aidan1971 0:8f28f25e3435 169 if(mI2C.write(i2c_addr, (char *)&wire_data, 4, false)) return -1;
aidan1971 0:8f28f25e3435 170 return 0;
aidan1971 0:8f28f25e3435 171 }
aidan1971 0:8f28f25e3435 172
aidan1971 0:8f28f25e3435 173 int32_t SGTL5000::modify_i2c(uint32_t reg_addr, uint32_t data, uint32_t mask)
aidan1971 0:8f28f25e3435 174 {
aidan1971 0:8f28f25e3435 175 if(mask == 0x0 || mask > 0xFFFF) return -1;
aidan1971 0:8f28f25e3435 176 uint32_t shift;
aidan1971 0:8f28f25e3435 177 for (shift = 0; !(mask & (1<<shift)); ++shift) {};
aidan1971 0:8f28f25e3435 178 uint32_t wire_data = (__rev(reg_addr) >> 16) | (__rev((read_i2c(reg_addr) & (~mask & 0xFFFF)) | ((data << shift) & mask)));
aidan1971 0:8f28f25e3435 179 if(mI2C.write(i2c_addr, (char *)&wire_data, 4, false)) return -1;
aidan1971 0:8f28f25e3435 180 return 0;
aidan1971 0:8f28f25e3435 181 }
aidan1971 0:8f28f25e3435 182
aidan1971 0:8f28f25e3435 183
aidan1971 0:8f28f25e3435 184 void SGTL5000::init_i2s(void)
aidan1971 0:8f28f25e3435 185 {
aidan1971 0:8f28f25e3435 186 //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 187 const uint32_t I2S_MCLK_MULT = 32; // 32 = 48Khz 2 = 44.1Khz
aidan1971 0:8f28f25e3435 188 const uint32_t I2S_MCLK_DIV = 250; // 250 = 48Khz 17 = 44.1Khz
aidan1971 0:8f28f25e3435 189
aidan1971 0:8f28f25e3435 190 // Configure System Clock Distribution for I2S
aidan1971 0:8f28f25e3435 191 SIM->SCGC5 |= SIM_SCGC5_PORTC_MASK; // Enable Clock to PORTC control module.
aidan1971 0:8f28f25e3435 192 SIM->SCGC6 |= SIM_SCGC6_I2S_MASK; // Enable Clocking to I2S Module
aidan1971 0:8f28f25e3435 193 SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Enable DMA Clock Gate Control
aidan1971 0:8f28f25e3435 194 SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK; // Enable DMA Mux Clock Gate Control
aidan1971 0:8f28f25e3435 195
aidan1971 0:8f28f25e3435 196 // Configure I2S Master Clocking
aidan1971 0:8f28f25e3435 197 I2S0->MCR |= I2S_MCR_MICS(3); // Configure I2S MCLK Divder Source as System PLL
aidan1971 0:8f28f25e3435 198 while(I2S0->MCR & I2S_MCR_DUF_MASK); // Check for active changes then set I2S Clock Fractions
aidan1971 0:8f28f25e3435 199 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 200 I2S0->MCR |= I2S_MCR_MOE_MASK; // Enable MCLK Output
aidan1971 0:8f28f25e3435 201
aidan1971 0:8f28f25e3435 202 // Configure PIN Muxing
aidan1971 0:8f28f25e3435 203
aidan1971 0:8f28f25e3435 204 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 205 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 206 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 207 PORTC->PCR[1] = (PORTC->PCR[1] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(6); // Set PTC1(pin44)(Teensy pin22) <> I2S0_TXD0
aidan1971 0:8f28f25e3435 208 PORTC->PCR[5] = (PORTC->PCR[5] & ~PORT_PCR_MUX_MASK) | PORT_PCR_MUX(4); // Set PTC5(pin50)(Teensy pin13) <> I2S0_RXD0
aidan1971 0:8f28f25e3435 209
aidan1971 0:8f28f25e3435 210 // Config. TX
aidan1971 0:8f28f25e3435 211 I2S0->TMR = 0; // I2S TX Mask cleared.
aidan1971 0:8f28f25e3435 212 I2S0->TCR2 = (I2S0->TCR2 & ~I2S_TCR2_SYNC_MASK) | I2S_TCR2_SYNC(0); // Set TX Mode to Async
aidan1971 0:8f28f25e3435 213 I2S0->TCR2 |= I2S_TCR2_BCP_MASK; // Set TX Bit Clock Polarity sample riseing edge
aidan1971 0:8f28f25e3435 214 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 215 I2S0->TCR2 |= I2S_TCR2_BCD_MASK; // Set TX Clock Dir. internally generated
aidan1971 0:8f28f25e3435 216 I2S0->TCR2 = (I2S0->TCR2 & ~I2S_TCR2_DIV_MASK) | I2S_TCR2_DIV(3); // Set TX Bit Clock Divide
aidan1971 0:8f28f25e3435 217 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 218 I2S0->TCR4 = (I2S0->TCR4 & ~I2S_TCR4_FRSZ_MASK) | I2S_TCR4_FRSZ(1); // Set TX Frame Size @ 2 words per frame
aidan1971 0:8f28f25e3435 219 I2S0->TCR4 = (I2S0->TCR4 & ~I2S_TCR4_SYWD_MASK) | I2S_TCR4_SYWD(15); // Set TX frame sync width to 16bits
aidan1971 0:8f28f25e3435 220 I2S0->TCR4 |= I2S_TCR4_MF_MASK; // Set TX MSBit first
aidan1971 0:8f28f25e3435 221 I2S0->TCR4 |= I2S_TCR4_FSE_MASK; // Set TX Frame sync asserts before first bit
aidan1971 0:8f28f25e3435 222 I2S0->TCR4 |= I2S_TCR4_FSP_MASK; // Set TX Frame sync polarity active low.
aidan1971 0:8f28f25e3435 223 I2S0->TCR4 |= I2S_TCR4_FSD_MASK; // Set TX Frame sync direction from master clock
aidan1971 0:8f28f25e3435 224 I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_WNW_MASK) | I2S_TCR5_WNW(15); // Set TX to 16bits per word.
aidan1971 0:8f28f25e3435 225 I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_W0W_MASK) | I2S_TCR5_W0W(15); // Set TX to 16bits per first word.
aidan1971 0:8f28f25e3435 226 I2S0->TCR5 = (I2S0->TCR5 & ~I2S_TCR5_FBT_MASK) | I2S_TCR5_FBT(15); // Set TX first bit transmitted index to 16th bit .
aidan1971 0:8f28f25e3435 227
aidan1971 0:8f28f25e3435 228 //Config RX
aidan1971 0:8f28f25e3435 229 I2S0->RMR = 0; // I2S RX Mask cleared.
aidan1971 0:8f28f25e3435 230 I2S0->RCR2 = (I2S0->RCR2 & ~I2S_RCR2_SYNC_MASK) | I2S_RCR2_SYNC(1); // Set RX Mode to Sync with TX
aidan1971 0:8f28f25e3435 231 I2S0->RCR2 |= I2S_TCR2_BCP_MASK; // Set RX Bit Clock Polarity sample riseing edge
aidan1971 0:8f28f25e3435 232 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 233 I2S0->RCR2 |= I2S_RCR2_BCD_MASK; // Set RX Clock Dir. internally generated
aidan1971 0:8f28f25e3435 234 I2S0->RCR2 = (I2S0->RCR2 & ~I2S_RCR2_DIV_MASK) | I2S_RCR2_DIV(3); // Set RX Bit Clock Divide
aidan1971 0:8f28f25e3435 235 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 236 I2S0->RCR4 = (I2S0->RCR4 & ~I2S_RCR4_FRSZ_MASK) | I2S_RCR4_FRSZ(1); // Set RX Frame Size @ 2 words per frame
aidan1971 0:8f28f25e3435 237 I2S0->RCR4 = (I2S0->RCR4 & ~I2S_RCR4_SYWD_MASK) | I2S_RCR4_SYWD(15); // Set RX frame sync width to 16bits
aidan1971 0:8f28f25e3435 238 I2S0->RCR4 |= I2S_RCR4_MF_MASK; // Set RX MSBit first
aidan1971 0:8f28f25e3435 239 I2S0->RCR4 |= I2S_RCR4_FSE_MASK; // Set RX Frame sync asserts before first bit
aidan1971 0:8f28f25e3435 240 I2S0->RCR4 |= I2S_RCR4_FSP_MASK; // Set RX Frame sync polarity active low.
aidan1971 0:8f28f25e3435 241 I2S0->RCR4 |= I2S_RCR4_FSD_MASK; // Set RX Frame sync direction from master clock
aidan1971 0:8f28f25e3435 242 I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_WNW_MASK) | I2S_RCR5_WNW(15); // Set RX to 16bits per word.
aidan1971 0:8f28f25e3435 243 I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_W0W_MASK) | I2S_RCR5_W0W(15); // Set RX to 16bits per first word.
aidan1971 0:8f28f25e3435 244 I2S0->RCR5 = (I2S0->RCR5 & ~I2S_RCR5_FBT_MASK) | I2S_RCR5_FBT(15); // Set RX first bit transmitted index to 16 .
aidan1971 0:8f28f25e3435 245
aidan1971 0:8f28f25e3435 246 // Configure I2S Peripheral
aidan1971 0:8f28f25e3435 247 I2S0->TCSR |= I2S_TCSR_BCE_MASK; // Enable I2S Tx bit clock
aidan1971 0:8f28f25e3435 248 I2S0->RCSR |= I2S_RCSR_BCE_MASK; // Enable I2S Rx Bit Clock
aidan1971 0:8f28f25e3435 249
aidan1971 0:8f28f25e3435 250 }
aidan1971 0:8f28f25e3435 251
aidan1971 0:8f28f25e3435 252 void SGTL5000::init_codec(void)
aidan1971 0:8f28f25e3435 253 {
aidan1971 0:8f28f25e3435 254 // Default Configure Codec
aidan1971 0:8f28f25e3435 255 wait(0.2);// Let codec run start-up ramp ?
aidan1971 0:8f28f25e3435 256
aidan1971 0:8f28f25e3435 257 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 258 (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 259 (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 260 (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 261 (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 262 (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 263 (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 264 (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 265 (SGTL5000_ANA_POWER_ADC_MONO_MASK & (0x1 << SGTL5000_ANA_POWER_ADC_MONO_SHIFT)) | // ADC in stereo mode
aidan1971 0:8f28f25e3435 266 (SGTL5000_ANA_POWER_REFTOP_POWERUP_MASK & (0x1 << SGTL5000_ANA_POWER_REFTOP_POWERUP_SHIFT)) | // Enable bias currents
aidan1971 0:8f28f25e3435 267 (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 268 (SGTL5000_ANA_POWER_DAC_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_DAC_POWERUP_SHIFT)) | // Disable DAC until we set refs
aidan1971 0:8f28f25e3435 269 (SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_SHIFT)) | // Disable capless headphones
aidan1971 0:8f28f25e3435 270 (SGTL5000_ANA_POWER_ADC_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_ADC_POWERUP_SHIFT)) | // Disable ADC power until we set refs
aidan1971 0:8f28f25e3435 271 (SGTL5000_ANA_POWER_LINEOUT_POWERUP_MASK & (0x0 << SGTL5000_ANA_POWER_LINEOUT_POWERUP_SHIFT)) // Disable LINEOUT power until we set refs
aidan1971 0:8f28f25e3435 272 ));
aidan1971 0:8f28f25e3435 273 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 274 (SGTL5000_LINREG_CTRL_VDDC_ASSN_OVRD_MASK & (0x1 << SGTL5000_LINREG_CTRL_VDDC_ASSN_OVRD_SHIFT)) | // chargepump source manually assigned
aidan1971 0:8f28f25e3435 275 (SGTL5000_LINREG_CTRL_D_PROGRAMMING_MASK & (0xC << SGTL5000_LINREG_CTRL_D_PROGRAMMING_SHIFT)) // VDDD Line Reg not used so leave default
aidan1971 0:8f28f25e3435 276 ));
aidan1971 0:8f28f25e3435 277 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 278 (SGTL5000_REF_CTRL_BIAS_CTRL_MASK & (0x1 << SGTL5000_REF_CTRL_BIAS_CTRL_SHIFT)) | // Set analog bias currents to +12.5%
aidan1971 0:8f28f25e3435 279 (SGTL5000_REF_CTRL_SMALL_POP_MASK & (0x0 << SGTL5000_REF_CTRL_SMALL_POP_SHIFT)) // Set small pop ramp normal
aidan1971 0:8f28f25e3435 280 ));
aidan1971 0:8f28f25e3435 281 write_i2c(SGTL5000_LINE_OUT_CTRL, ( (SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_MASK & (0xF << SGTL5000_LINE_OUT_CTRL_OUT_CURRENT_SHIFT)) | // Set Lineout bias curent 0.54mA
aidan1971 0:8f28f25e3435 282 (SGTL5000_LINE_OUT_CTRL_LO_VAGCNTRL_MASK & (0x22 << SGTL5000_LINE_OUT_CTRL_LO_VAGCNTRL_SHIFT)) // Lineout AGND = 1.65v
aidan1971 0:8f28f25e3435 283 ));
aidan1971 0:8f28f25e3435 284 write_i2c(SGTL5000_SHORT_CTRL, ( (SGTL5000_SHORT_CTRL_LVLADJR_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJR_SHIFT)) | // HP R short detect 125mA
aidan1971 0:8f28f25e3435 285 (SGTL5000_SHORT_CTRL_LVLADJL_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJL_SHIFT)) | // HP L short detect 125mA
aidan1971 0:8f28f25e3435 286 (SGTL5000_SHORT_CTRL_LVLADJC_MASK & (0x4 << SGTL5000_SHORT_CTRL_LVLADJC_SHIFT)) | // Headphone capless short detect 250mA
aidan1971 0:8f28f25e3435 287 (SGTL5000_SHORT_CTRL_MODE_LR_MASK & (0x1 << SGTL5000_SHORT_CTRL_MODE_LR_SHIFT)) | // en short det reset 50mS
aidan1971 0:8f28f25e3435 288 (SGTL5000_SHORT_CTRL_MODE_CM_MASK & (0x2 << SGTL5000_SHORT_CTRL_MODE_CM_SHIFT)) // En capless short det reset vol rise
aidan1971 0:8f28f25e3435 289 ));
aidan1971 0:8f28f25e3435 290 write_i2c(SGTL5000_ANA_CTRL, ( (SGTL5000_ANA_CTRL_MUTE_LO_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_LO_SHIFT)) | // Mute LINEOUT
aidan1971 0:8f28f25e3435 291 (SGTL5000_ANA_CTRL_SELECT_HP_MASK & (0x0 << SGTL5000_ANA_CTRL_SELECT_HP_SHIFT)) | // Select DAC as input to HP amp
aidan1971 0:8f28f25e3435 292 (SGTL5000_ANA_CTRL_EN_ZCD_HP_MASK & (0x1 << SGTL5000_ANA_CTRL_EN_ZCD_HP_SHIFT)) | // Enable ZCD on HP
aidan1971 0:8f28f25e3435 293 (SGTL5000_ANA_CTRL_MUTE_HP_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_HP_SHIFT)) | // Mute the headphones
aidan1971 0:8f28f25e3435 294 (SGTL5000_ANA_CTRL_SELECT_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_SELECT_ADC_SHIFT)) | // Select LINEIN as input to ADC
aidan1971 0:8f28f25e3435 295 (SGTL5000_ANA_CTRL_EN_ZCD_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_EN_ZCD_ADC_SHIFT)) | // Enable ADC ZCD
aidan1971 0:8f28f25e3435 296 (SGTL5000_ANA_CTRL_MUTE_ADC_MASK & (0x1 << SGTL5000_ANA_CTRL_MUTE_ADC_SHIFT)) // Mute ADC pre-amp
aidan1971 0:8f28f25e3435 297 ));
aidan1971 0:8f28f25e3435 298 modify_i2c (SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_HEADPHONE_POWERUP_MASK); // Power up Headphone amp
aidan1971 0:8f28f25e3435 299 modify_i2c (SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_DAC_POWERUP_MASK); // Power up DAC
aidan1971 0:8f28f25e3435 300 modify_i2c (SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_CAPLESS_HEADPHONE_POWERUP_MASK); // Power up capless HP block
aidan1971 0:8f28f25e3435 301 modify_i2c (SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_ADC_POWERUP_MASK); // Power up ADC
aidan1971 0:8f28f25e3435 302 modify_i2c (SGTL5000_ANA_POWER, 0x1, SGTL5000_ANA_POWER_LINEOUT_POWERUP_MASK); // Power up Lineout Amp
aidan1971 0:8f28f25e3435 303
aidan1971 0:8f28f25e3435 304 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 305
aidan1971 0:8f28f25e3435 306 write_i2c(SGTL5000_DIG_POWER, ( (SGTL5000_DIG_POWER_ADC_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_ADC_POWERUP_SHIFT)) |
aidan1971 0:8f28f25e3435 307 (SGTL5000_DIG_POWER_DAC_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_DAC_POWERUP_SHIFT)) |
aidan1971 0:8f28f25e3435 308 (SGTL5000_DIG_POWER_DAP_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_DAP_POWERUP_SHIFT)) |
aidan1971 0:8f28f25e3435 309 (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 310 (SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK & (0x1 << SGTL5000_DIG_POWER_I2S_IN_POWERUP_SHIFT))
aidan1971 0:8f28f25e3435 311 ));
aidan1971 0:8f28f25e3435 312 write_i2c(SGTL5000_LINE_OUT_VOL, ( (SGTL5000_LINE_OUT_VOL_LO_VOL_RIGHT_MASK & (0x1D << SGTL5000_LINE_OUT_VOL_LO_VOL_RIGHT_SHIFT)) | // Lineout Vol normalised
aidan1971 0:8f28f25e3435 313 (SGTL5000_LINE_OUT_VOL_LO_VOL_LEFT_MASK & (0x1D << SGTL5000_LINE_OUT_VOL_LO_VOL_LEFT_SHIFT))
aidan1971 0:8f28f25e3435 314 ));
aidan1971 0:8f28f25e3435 315 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 316 (SGTL5000_PAD_STRENGTH_I2S_SCLK_MASK & (0x0 << SGTL5000_PAD_STRENGTH_I2S_SCLK_SHIFT)) | // I2S SCLK drive dissabled (codec is slave)
aidan1971 0:8f28f25e3435 317 (SGTL5000_PAD_STRENGTH_I2S_DOUT_MASK & (0x1 << SGTL5000_PAD_STRENGTH_I2S_DOUT_SHIFT)) | // I2S DOUT drive @ 4.02mA
aidan1971 0:8f28f25e3435 318 (SGTL5000_PAD_STRENGTH_CTRL_DATA_MASK & (0x3 << SGTL5000_PAD_STRENGTH_CTRL_DATA_SHIFT)) | // I2C DATA drive @ 12.05mA.
aidan1971 0:8f28f25e3435 319 (SGTL5000_PAD_STRENGTH_CTRL_CLK_MASK & (0x3 << SGTL5000_PAD_STRENGTH_CTRL_CLK_SHIFT)) // I2C CLOCK drive @ 12.05mA.
aidan1971 0:8f28f25e3435 320 ));
aidan1971 0:8f28f25e3435 321 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 322 (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 323 (SGTL5000_CLK_CTRL_MCLK_FREQ_MASK & (0x0 << SGTL5000_CLK_CTRL_MCLK_FREQ_SHIFT)) // Set SYS MCLK @ 256*Fs
aidan1971 0:8f28f25e3435 324 ));
aidan1971 0:8f28f25e3435 325 write_i2c(SGTL5000_I2S_CTRL, ( (SGTL5000_I2S_CTRL_SCLKFREQ_MASK & (0x1 << SGTL5000_I2S_CTRL_SCLKFREQ_SHIFT)) | // I2S SCLK 32*Fs
aidan1971 0:8f28f25e3435 326 (SGTL5000_I2S_CTRL_MS_MASK & (0x0 << SGTL5000_I2S_CTRL_MS_SHIFT)) | // Slave
aidan1971 0:8f28f25e3435 327 (SGTL5000_I2S_CTRL_SCLK_INV_MASK & (0x0 << SGTL5000_I2S_CTRL_SCLK_INV_SHIFT)) | // Data on riseing edge
aidan1971 0:8f28f25e3435 328 (SGTL5000_I2S_CTRL_DLEN_MASK & (0x3 << SGTL5000_I2S_CTRL_DLEN_SHIFT)) | // Data 16bits
aidan1971 0:8f28f25e3435 329 (SGTL5000_I2S_CTRL_MODE_MASK & (0x0 << SGTL5000_I2S_CTRL_MODE_SHIFT)) | // I2S mode
aidan1971 0:8f28f25e3435 330 (SGTL5000_I2S_CTRL_LRALIGN_MASK & (0x0 << SGTL5000_I2S_CTRL_LRALIGN_SHIFT)) | // I2S format
aidan1971 0:8f28f25e3435 331 (SGTL5000_I2S_CTRL_LRPOL_MASK & (0x0 << SGTL5000_I2S_CTRL_LRPOL_SHIFT)) // Left word on LRCLK low
aidan1971 0:8f28f25e3435 332 ));
aidan1971 0:8f28f25e3435 333 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 334 (SGTL5000_SSS_CTRL_DAP_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_LRSWAP_SHIFT)) |
aidan1971 0:8f28f25e3435 335 (SGTL5000_SSS_CTRL_DAC_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_DAC_LRSWAP_SHIFT)) |
aidan1971 0:8f28f25e3435 336 (SGTL5000_SSS_CTRL_I2S_LRSWAP_MASK & (0x0 << SGTL5000_SSS_CTRL_I2S_LRSWAP_SHIFT)) |
aidan1971 0:8f28f25e3435 337 (SGTL5000_SSS_CTRL_DAP_MIX_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_MIX_SELECT_SHIFT)) | // DAP mixer source ADC
aidan1971 0:8f28f25e3435 338 (SGTL5000_SSS_CTRL_DAP_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_DAP_SELECT_SHIFT)) | // DAP Input source ADC
aidan1971 0:8f28f25e3435 339 (SGTL5000_SSS_CTRL_DAC_SELECT_MASK & (0x1 << SGTL5000_SSS_CTRL_DAC_SELECT_SHIFT)) | // DAC Input source I2SIN
aidan1971 0:8f28f25e3435 340 (SGTL5000_SSS_CTRL_I2S_SELECT_MASK & (0x0 << SGTL5000_SSS_CTRL_I2S_SELECT_SHIFT)) // I2SOUT source ADC
aidan1971 0:8f28f25e3435 341 ));
aidan1971 0:8f28f25e3435 342 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 343 (SGTL5000_ADCDAC_CTRL_VOL_EXPO_RAMP_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_VOL_EXPO_RAMP_SHIFT)) |
aidan1971 0:8f28f25e3435 344 (SGTL5000_ADCDAC_CTRL_DAC_MUTE_RIGHT_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_DAC_MUTE_RIGHT_SHIFT)) |
aidan1971 0:8f28f25e3435 345 (SGTL5000_ADCDAC_CTRL_DAC_MUTE_LEFT_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_DAC_MUTE_LEFT_SHIFT)) |
aidan1971 0:8f28f25e3435 346 (SGTL5000_ADCDAC_CTRL_ADC_HPF_FREEZE_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_ADC_HPF_FREEZE_SHIFT)) | // ADC HPF normal operation
aidan1971 0:8f28f25e3435 347 (SGTL5000_ADCDAC_CTRL_ADC_HPF_BYPASS_MASK & (0x0 << SGTL5000_ADCDAC_CTRL_ADC_HPF_BYPASS_SHIFT)) // ADC HPF enabled
aidan1971 0:8f28f25e3435 348 ));
aidan1971 0:8f28f25e3435 349 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 350 (SGTL5000_DAC_VOL_DAC_VOL_LEFT_MASK & (0x3C << SGTL5000_DAC_VOL_DAC_VOL_LEFT_SHIFT)) // DAC L Vol 0dB
aidan1971 0:8f28f25e3435 351 ));
aidan1971 0:8f28f25e3435 352 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 353 (SGTL5000_ANA_HP_CTRL_HP_VOL_LEFT_MASK & (0x7F << SGTL5000_ANA_HP_CTRL_HP_VOL_LEFT_SHIFT))
aidan1971 0:8f28f25e3435 354 ));
aidan1971 0:8f28f25e3435 355 modify_i2c (SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_LO_MASK); // Un-mute Lineout
aidan1971 0:8f28f25e3435 356 modify_i2c (SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_HP_MASK); // Un-mute HP
aidan1971 0:8f28f25e3435 357 modify_i2c (SGTL5000_ANA_CTRL, 0x0, SGTL5000_ANA_CTRL_MUTE_ADC_MASK); // Un-mute ADC pre-amp
aidan1971 0:8f28f25e3435 358 modify_i2c (SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_OUT_POWERUP_MASK); // I2S DOUT enable
aidan1971 0:8f28f25e3435 359 modify_i2c (SGTL5000_DIG_POWER, 0x1, SGTL5000_DIG_POWER_I2S_IN_POWERUP_MASK); // I2S DIN enable
aidan1971 0:8f28f25e3435 360 }
aidan1971 0:8f28f25e3435 361
aidan1971 0:8f28f25e3435 362
aidan1971 0:8f28f25e3435 363 int32_t SGTL5000::attach_SYNC(Callback<void()> func)
aidan1971 0:8f28f25e3435 364 {
aidan1971 0:8f28f25e3435 365 if(SYNC_attached) return -1; // Assign Callback function
aidan1971 0:8f28f25e3435 366 SGTL5000::SYNC_user_func = func;
aidan1971 0:8f28f25e3435 367 SGTL5000::SYNC_attach_type = 0;
aidan1971 0:8f28f25e3435 368 SYNC_attached = true;
aidan1971 0:8f28f25e3435 369 return 0;
aidan1971 0:8f28f25e3435 370 }
aidan1971 0:8f28f25e3435 371
aidan1971 0:8f28f25e3435 372 int32_t SGTL5000::attach_SYNC_NB(uint32_t user_ISR, uint32_t irq_pri, IRQn sw_irq)
aidan1971 0:8f28f25e3435 373 {
aidan1971 0:8f28f25e3435 374 SGTL5000::SYNC_swIRQ = sw_irq; // Assign ISR address and enable users IRQ
aidan1971 0:8f28f25e3435 375 NVIC_SetVector(SGTL5000::SYNC_swIRQ, user_ISR);
aidan1971 0:8f28f25e3435 376 NVIC_SetPriority(SGTL5000::SYNC_swIRQ, irq_pri);
aidan1971 0:8f28f25e3435 377 NVIC_EnableIRQ(SGTL5000::SYNC_swIRQ);
aidan1971 0:8f28f25e3435 378 SGTL5000::SYNC_attach_type = 1;
aidan1971 0:8f28f25e3435 379 SYNC_attached = true;
aidan1971 0:8f28f25e3435 380 return 0;
aidan1971 0:8f28f25e3435 381 }
aidan1971 0:8f28f25e3435 382
aidan1971 0:8f28f25e3435 383 void SGTL5000::detach_SYNC(void)
aidan1971 0:8f28f25e3435 384 {
aidan1971 0:8f28f25e3435 385 stop_SYNC();
aidan1971 0:8f28f25e3435 386 SYNC_attached = false;
aidan1971 0:8f28f25e3435 387 }
aidan1971 0:8f28f25e3435 388
aidan1971 0:8f28f25e3435 389 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 0:8f28f25e3435 390 uint32_t block_size, bool RX_shift, bool TX_shift, uint32_t _RX_DMAch, uint32_t _TX_DMAch, uint32_t DMA_irq_pri)
aidan1971 0:8f28f25e3435 391 {
aidan1971 0:8f28f25e3435 392 if(!SYNC_attached && !SGTL5000::SYNC_attach_type) return -1; // Check we have a handler if using callback
aidan1971 0:8f28f25e3435 393 if(SYNC_run || TX_run || RX_run ) return -1; // Check if i2s is already started
aidan1971 0:8f28f25e3435 394 if(_RX_DMAch > 15 || _TX_DMAch > 15) return -1; // Sanity check DMAMUX channels
aidan1971 0:8f28f25e3435 395 if (!(block_size == 2 || block_size == 4 || block_size == 8)) return -1; // Only accept block size 2^n within range.
aidan1971 0:8f28f25e3435 396 SGTL5000::TX_block_size = block_size;
aidan1971 0:8f28f25e3435 397 SGTL5000::RX_block_size = SGTL5000::TX_block_size;
aidan1971 0:8f28f25e3435 398 TX_bs_bytes = block_size * 4;
aidan1971 0:8f28f25e3435 399 RX_bs_bytes = TX_bs_bytes;
aidan1971 0:8f28f25e3435 400 SGTL5000::BufRX_L_safe = (uint32_t*)BufRX_L_safe; // Assign the users pointer addresses
aidan1971 0:8f28f25e3435 401 SGTL5000::BufRX_R_safe = (uint32_t*)BufRX_R_safe;
aidan1971 0:8f28f25e3435 402 SGTL5000::BufTX_L_safe = (uint32_t*)BufTX_L_safe;
aidan1971 0:8f28f25e3435 403 SGTL5000::BufTX_R_safe = (uint32_t*)BufTX_R_safe;
aidan1971 0:8f28f25e3435 404
aidan1971 0:8f28f25e3435 405 SGTL5000::TX_shift = TX_shift;
aidan1971 0:8f28f25e3435 406 SGTL5000::RX_shift = RX_shift;
aidan1971 0:8f28f25e3435 407 SGTL5000::RX_DMAch = _RX_DMAch;
aidan1971 0:8f28f25e3435 408 SGTL5000::TX_DMAch = _TX_DMAch;
aidan1971 0:8f28f25e3435 409 SYNC_run = true;
aidan1971 0:8f28f25e3435 410 init_DMA();
aidan1971 0:8f28f25e3435 411
aidan1971 0:8f28f25e3435 412 I2S0->TCR1 = (I2S0->TCR1 & ~I2S_TCR1_TFW_MASK) | I2S_TCR1_TFW(8 - SGTL5000::TX_block_size); // Set TX FIFO watermark
aidan1971 0:8f28f25e3435 413 I2S0->RCR1 = (I2S0->RCR1 & ~I2S_RCR1_RFW_MASK) | I2S_RCR1_RFW(SGTL5000::RX_block_size - 1); // Set RX FIFO watermark
aidan1971 0:8f28f25e3435 414 I2S0->TCSR |= I2S_TCSR_TE_MASK; // TX enable
aidan1971 0:8f28f25e3435 415 I2S0->RCSR |= I2S_RCSR_RE_MASK; // RX enable
aidan1971 0:8f28f25e3435 416 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(1); // Enable TX channel 0.
aidan1971 0:8f28f25e3435 417 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(1); // Enable RX channels 0
aidan1971 0:8f28f25e3435 418 //SGTL5000::_db_sync_phase = 0;
aidan1971 0:8f28f25e3435 419 NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::sync_dma_ISR); // Set DMA TX handler vector
aidan1971 0:8f28f25e3435 420 NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::sync_dma_ISR); // Set DMA RX handler vector
aidan1971 0:8f28f25e3435 421 NVIC_SetPriority((IRQn)SGTL5000::TX_DMAch, DMA_irq_pri); // Set irq priorities the same
aidan1971 0:8f28f25e3435 422 NVIC_SetPriority((IRQn)SGTL5000::RX_DMAch, DMA_irq_pri);
aidan1971 0:8f28f25e3435 423 NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch); // Enable IRQ for chosen TX DMA channel
aidan1971 0:8f28f25e3435 424 NVIC_EnableIRQ((IRQn)SGTL5000::RX_DMAch); // Disable IRQ for chosen RX DMA channel
aidan1971 0:8f28f25e3435 425 NVIC_SetVector(I2S0_Tx_IRQn, (uint32_t)&SGTL5000::sync_I2S_ISR); // Set vector for SYNC word start ISR
aidan1971 0:8f28f25e3435 426 NVIC_SetPriority(I2S0_Tx_IRQn, 0); // Set priority of SYNC word start ISR
aidan1971 0:8f28f25e3435 427 NVIC_EnableIRQ(I2S0_Tx_IRQn); // Enable SYNC word start ISR using TX direction
aidan1971 0:8f28f25e3435 428 I2S0->TCSR |= I2S_TCSR_WSF_MASK; // Clear TX Word Start Flag
aidan1971 0:8f28f25e3435 429 I2S0->TCSR |= I2S_TCSR_WSIE_MASK; // Enable I2S TX word start IRQ
aidan1971 0:8f28f25e3435 430 return 0;
aidan1971 0:8f28f25e3435 431 }
aidan1971 0:8f28f25e3435 432
aidan1971 0:8f28f25e3435 433 int32_t SGTL5000::stop_SYNC(void)
aidan1971 0:8f28f25e3435 434 {
aidan1971 0:8f28f25e3435 435 I2S0->TCSR &= ~I2S_TCSR_FRDE_MASK; // Disable TX FIFO IRQs based on watermark
aidan1971 0:8f28f25e3435 436 I2S0->RCSR &= ~I2S_RCSR_FRDE_MASK; // Disable RX FIFO IRQs based on watermark
aidan1971 0:8f28f25e3435 437 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(0); // Disable TX channels.
aidan1971 0:8f28f25e3435 438 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(0); // Disable RX channels.
aidan1971 0:8f28f25e3435 439 SYNC_run = false;
aidan1971 0:8f28f25e3435 440 return 0;
aidan1971 0:8f28f25e3435 441 }
aidan1971 0:8f28f25e3435 442
aidan1971 0:8f28f25e3435 443 void SGTL5000::rx_I2S_ISR(void)
aidan1971 0:8f28f25e3435 444 {
aidan1971 0:8f28f25e3435 445 I2S0->RCSR &= ~I2S_RCSR_WSIE_MASK; // Disable RX word start IRQs
aidan1971 0:8f28f25e3435 446 I2S0->RCSR |= I2S_RCSR_FR_MASK; // Reset RX FIFO pointers
aidan1971 0:8f28f25e3435 447 I2S0->RCSR |= I2S_RCSR_FRDE_MASK; // Enable DMA request based on RX FIFO watermark
aidan1971 0:8f28f25e3435 448 }
aidan1971 0:8f28f25e3435 449
aidan1971 0:8f28f25e3435 450 void SGTL5000::tx_I2S_ISR(void)
aidan1971 0:8f28f25e3435 451 {
aidan1971 0:8f28f25e3435 452 I2S0->TCSR &= ~I2S_TCSR_WSIE_MASK; // Disable TX word start IRQs
aidan1971 0:8f28f25e3435 453 I2S0->TCSR |= I2S_TCSR_FR_MASK; // Reset TX FIFO pointers
aidan1971 0:8f28f25e3435 454 I2S0->TCSR |= I2S_TCSR_FRDE_MASK; // Enable DMA request based on TX FIFO watermark
aidan1971 0:8f28f25e3435 455 }
aidan1971 0:8f28f25e3435 456
aidan1971 0:8f28f25e3435 457 void SGTL5000::sync_I2S_ISR(void)
aidan1971 0:8f28f25e3435 458 {
aidan1971 0:8f28f25e3435 459 I2S0->TCSR &= ~I2S_TCSR_WSIE_MASK; // Disable TX word start IRQs
aidan1971 0:8f28f25e3435 460 I2S0->TCSR |= I2S_TCSR_FR_MASK; // Reset TX FIFO pointers
aidan1971 0:8f28f25e3435 461 I2S0->RCSR |= I2S_RCSR_FR_MASK; // Reset RX FIFO pointers
aidan1971 0:8f28f25e3435 462 I2S0->TCSR |= I2S_TCSR_FRDE_MASK; // Enable DMA request based on TX FIFO watermark
aidan1971 0:8f28f25e3435 463 I2S0->RCSR |= I2S_RCSR_FRDE_MASK; // Enable DMA request based on RX FIFO watermark
aidan1971 0:8f28f25e3435 464 }
aidan1971 0:8f28f25e3435 465
aidan1971 0:8f28f25e3435 466 void SGTL5000::sync_dma_ISR(void)
aidan1971 0:8f28f25e3435 467 {
aidan1971 0:8f28f25e3435 468 /*
aidan1971 0:8f28f25e3435 469 The RX and TX buffers are each 16 * 32bit words, which allows the FIFO to be, at a maximum,
aidan1971 0:8f28f25e3435 470 fully emptied or filled in each DMA request. Each buffer is arranged firstly in 2 halves.
aidan1971 0:8f28f25e3435 471 The Left channel in the lower half, Right channel in the upper half. Within each half there is double buffering.
aidan1971 0:8f28f25e3435 472 With A & B sections. When A is active, B is free for the user to read and write. When B is active, A is free
aidan1971 0:8f28f25e3435 473 to read and write. The pointers BufTX_L etc. are modified before the users code is called, in this way
aidan1971 0:8f28f25e3435 474 no index tracking is needed in user-space. The pointers will always refer to the safe part of the double buffer.
aidan1971 0:8f28f25e3435 475
aidan1971 0:8f28f25e3435 476 buffer layout with Block Size = 2 Block Size = 4 Block Size = 8
aidan1971 0:8f28f25e3435 477 Double Buffer A Double Buffer B Double Buffer A Double Buffer B Double Buffer A Double Buffer B
aidan1971 0:8f28f25e3435 478 |L|R|x|x|x|x|x|x:L|R|x|x|x|x|x|x| |L|L|R|R|x|x|x|x:L|L|R|R|x|x|x|x| |L|L|L|L|R|R|R|R:L|L|L|L|R|R|R|R|
aidan1971 0:8f28f25e3435 479 */
aidan1971 0:8f28f25e3435 480 static uint32_t db_sync_phase = 0;
aidan1971 0:8f28f25e3435 481 static uint32_t TX_DMA_Complete = 0;
aidan1971 0:8f28f25e3435 482 static uint32_t RX_DMA_Complete = 0;
aidan1971 0:8f28f25e3435 483 static uint32_t dbA_rx_L = (uint32_t)&SGTL5000::I2S_RX_Buffer[0]; // Pre-compute buffer offsets etc to save cycles in ISR
aidan1971 0:8f28f25e3435 484 static uint32_t dbA_rx_R = (uint32_t)&SGTL5000::I2S_RX_Buffer[SGTL5000::RX_block_size / 2];
aidan1971 0:8f28f25e3435 485 static uint32_t dbA_tx_L = (uint32_t)&SGTL5000::I2S_TX_Buffer[0];
aidan1971 0:8f28f25e3435 486 static uint32_t dbA_tx_R = (uint32_t)&SGTL5000::I2S_TX_Buffer[SGTL5000::TX_block_size / 2];
aidan1971 0:8f28f25e3435 487 static uint32_t dbB_rx_L = (uint32_t)&SGTL5000::I2S_RX_Buffer[8];
aidan1971 0:8f28f25e3435 488 static uint32_t dbB_rx_R = (uint32_t)&SGTL5000::I2S_RX_Buffer[8 + (SGTL5000::RX_block_size / 2)];
aidan1971 0:8f28f25e3435 489 static uint32_t dbB_tx_L = (uint32_t)&SGTL5000::I2S_TX_Buffer[8];
aidan1971 0:8f28f25e3435 490 static uint32_t dbB_tx_R = (uint32_t)&SGTL5000::I2S_TX_Buffer[8 + (SGTL5000::TX_block_size / 2)];
aidan1971 0:8f28f25e3435 491 static uint32_t RX_DMA_int_mask = 0x1 << SGTL5000::RX_DMAch;
aidan1971 0:8f28f25e3435 492 static uint32_t TX_DMA_int_mask = 0x1 << SGTL5000::TX_DMAch;
aidan1971 0:8f28f25e3435 493
aidan1971 0:8f28f25e3435 494 if(DMA0->INT & RX_DMA_int_mask) {
aidan1971 0:8f28f25e3435 495 DMA0->CINT = DMA_CINT_CINT(SGTL5000::RX_DMAch);
aidan1971 0:8f28f25e3435 496 ++RX_DMA_Complete;
aidan1971 0:8f28f25e3435 497 }
aidan1971 0:8f28f25e3435 498 if(DMA0->INT & TX_DMA_int_mask) {
aidan1971 0:8f28f25e3435 499 DMA0->CINT = DMA_CINT_CINT(SGTL5000::TX_DMAch);
aidan1971 0:8f28f25e3435 500 ++TX_DMA_Complete = true;
aidan1971 0:8f28f25e3435 501 }
aidan1971 0:8f28f25e3435 502
aidan1971 0:8f28f25e3435 503 if(TX_DMA_Complete && RX_DMA_Complete) {
aidan1971 0:8f28f25e3435 504 --TX_DMA_Complete;
aidan1971 0:8f28f25e3435 505 --RX_DMA_Complete;
aidan1971 0:8f28f25e3435 506 } else return;
aidan1971 0:8f28f25e3435 507
aidan1971 0:8f28f25e3435 508 if(db_sync_phase) { // Swap double buffer pointers with pre-computed indecies
aidan1971 0:8f28f25e3435 509 *SGTL5000::BufRX_L_safe = dbB_rx_L;
aidan1971 0:8f28f25e3435 510 *SGTL5000::BufRX_R_safe = dbB_rx_R;
aidan1971 0:8f28f25e3435 511 *SGTL5000::BufTX_L_safe = dbB_tx_L;
aidan1971 0:8f28f25e3435 512 *SGTL5000::BufTX_R_safe = dbB_tx_R;
aidan1971 0:8f28f25e3435 513 --db_sync_phase;
aidan1971 0:8f28f25e3435 514 } else {
aidan1971 0:8f28f25e3435 515 *SGTL5000::BufRX_L_safe = dbA_rx_L;
aidan1971 0:8f28f25e3435 516 *SGTL5000::BufRX_R_safe = dbA_rx_R;
aidan1971 0:8f28f25e3435 517 *SGTL5000::BufTX_L_safe = dbA_tx_L;
aidan1971 0:8f28f25e3435 518 *SGTL5000::BufTX_R_safe = dbA_tx_R;
aidan1971 0:8f28f25e3435 519 ++db_sync_phase;
aidan1971 0:8f28f25e3435 520 }
aidan1971 0:8f28f25e3435 521 if(SGTL5000::SYNC_attach_type) { // Trigger swIRQ or call Callback
aidan1971 0:8f28f25e3435 522 if(NVIC_GetActive(SGTL5000::SYNC_swIRQ) == 0) NVIC->STIR = SGTL5000::SYNC_swIRQ;
aidan1971 0:8f28f25e3435 523 } else SGTL5000::SYNC_user_func.call();
aidan1971 0:8f28f25e3435 524 }
aidan1971 0:8f28f25e3435 525
aidan1971 0:8f28f25e3435 526
aidan1971 0:8f28f25e3435 527
aidan1971 0:8f28f25e3435 528 int32_t SGTL5000::attach_TX(Callback<void()> func)
aidan1971 0:8f28f25e3435 529 {
aidan1971 0:8f28f25e3435 530 if(TX_attached) return -1; // Assign Callback function
aidan1971 0:8f28f25e3435 531 SGTL5000::TX_user_func = func;
aidan1971 0:8f28f25e3435 532 SGTL5000::TX_attach_type = 0;
aidan1971 0:8f28f25e3435 533 TX_attached = true;
aidan1971 0:8f28f25e3435 534 return 0;
aidan1971 0:8f28f25e3435 535 }
aidan1971 0:8f28f25e3435 536
aidan1971 0:8f28f25e3435 537 int32_t SGTL5000::attach_TX_NB(uint32_t user_ISR, uint32_t irq_pri, IRQn sw_irq)
aidan1971 0:8f28f25e3435 538 {
aidan1971 0:8f28f25e3435 539 SGTL5000::TX_swIRQ = sw_irq; // Assign ISR address and enable users IRQ
aidan1971 0:8f28f25e3435 540 NVIC_SetVector(SGTL5000::TX_swIRQ, user_ISR);
aidan1971 0:8f28f25e3435 541 NVIC_SetPriority(SGTL5000::TX_swIRQ, irq_pri);
aidan1971 0:8f28f25e3435 542 NVIC_EnableIRQ(SGTL5000::TX_swIRQ);
aidan1971 0:8f28f25e3435 543 SGTL5000::TX_attach_type = 1;
aidan1971 0:8f28f25e3435 544 TX_attached = true;
aidan1971 0:8f28f25e3435 545 return 0;
aidan1971 0:8f28f25e3435 546 }
aidan1971 0:8f28f25e3435 547
aidan1971 0:8f28f25e3435 548 void SGTL5000::detach_TX(void)
aidan1971 0:8f28f25e3435 549 {
aidan1971 0:8f28f25e3435 550 SGTL5000::stop_TX();
aidan1971 0:8f28f25e3435 551 TX_attached = false;
aidan1971 0:8f28f25e3435 552 }
aidan1971 0:8f28f25e3435 553
aidan1971 0:8f28f25e3435 554 int32_t SGTL5000::start_TX(uint32_t BufTX_L_safe, uint32_t BufTX_R_safe,
aidan1971 0:8f28f25e3435 555 uint32_t block_size, bool _TX_shift, uint32_t _TX_DMAch, uint32_t DMA_irq_pri)
aidan1971 0:8f28f25e3435 556 {
aidan1971 0:8f28f25e3435 557 if(!TX_attached && !SGTL5000::TX_attach_type) return -1; // Check we have a handler if using callback
aidan1971 0:8f28f25e3435 558 if(SYNC_run || TX_run) return -1; // Check if i2s is already started on tx
aidan1971 0:8f28f25e3435 559 if(_TX_DMAch > 15) return -1; // Sanity check DMAMUX channels
aidan1971 0:8f28f25e3435 560 if (!(block_size == 2 || block_size == 4 || block_size == 8)) return -1; // Only accept block size 2^n within range.
aidan1971 0:8f28f25e3435 561 SGTL5000::TX_block_size = block_size;
aidan1971 0:8f28f25e3435 562 TX_bs_bytes = block_size * 4;
aidan1971 0:8f28f25e3435 563 SGTL5000::BufTX_L_safe = (uint32_t*)BufTX_L_safe; // Assign the users pointer addresses
aidan1971 0:8f28f25e3435 564 SGTL5000::BufTX_R_safe = (uint32_t*)BufTX_R_safe;
aidan1971 0:8f28f25e3435 565
aidan1971 0:8f28f25e3435 566 SGTL5000::TX_shift = _TX_shift;
aidan1971 0:8f28f25e3435 567 SGTL5000::TX_DMAch = _TX_DMAch;
aidan1971 0:8f28f25e3435 568 TX_run = true;
aidan1971 0:8f28f25e3435 569 init_DMA();
aidan1971 0:8f28f25e3435 570
aidan1971 0:8f28f25e3435 571 I2S0->TCR1 = (I2S0->TCR1 & ~I2S_TCR1_TFW_MASK) | I2S_TCR1_TFW(8 - SGTL5000::TX_block_size); // Set TX FIFO watermark
aidan1971 0:8f28f25e3435 572 I2S0->TCSR |= I2S_TCSR_TE_MASK; // TX enable
aidan1971 0:8f28f25e3435 573 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(1); // Enable TX channel 0.
aidan1971 0:8f28f25e3435 574 NVIC_SetVector((IRQn)SGTL5000::TX_DMAch, (uint32_t)&SGTL5000::tx_dma_ISR); // Set DMA TX handler vector
aidan1971 0:8f28f25e3435 575 NVIC_SetPriority((IRQn)SGTL5000::TX_DMAch, DMA_irq_pri); // Set irq priorities the same
aidan1971 0:8f28f25e3435 576 NVIC_EnableIRQ((IRQn)SGTL5000::TX_DMAch); // Enable IRQ for chosen TX DMA channel
aidan1971 0:8f28f25e3435 577 NVIC_SetVector(I2S0_Tx_IRQn, (uint32_t)&SGTL5000::tx_I2S_ISR); // Set vector for TX word start ISR
aidan1971 0:8f28f25e3435 578 NVIC_SetPriority(I2S0_Tx_IRQn, 0); // Set priority of TX word start ISR
aidan1971 0:8f28f25e3435 579 NVIC_EnableIRQ(I2S0_Tx_IRQn); // Enable TX word start ISR
aidan1971 0:8f28f25e3435 580 I2S0->TCSR |= I2S_TCSR_WSF_MASK; // Clear TX Word Start Flag
aidan1971 0:8f28f25e3435 581 I2S0->TCSR |= I2S_TCSR_WSIE_MASK; // Enable I2S TX word start IRQ
aidan1971 0:8f28f25e3435 582 return 0;
aidan1971 0:8f28f25e3435 583 }
aidan1971 0:8f28f25e3435 584
aidan1971 0:8f28f25e3435 585 int32_t SGTL5000::stop_TX(void)
aidan1971 0:8f28f25e3435 586 {
aidan1971 0:8f28f25e3435 587
aidan1971 0:8f28f25e3435 588 I2S0->TCSR &= ~I2S_TCSR_FRDE_MASK; // Disable TX FIFO IRQs based on watermark
aidan1971 0:8f28f25e3435 589 I2S0->TCR3 = (I2S0->TCR3 & ~I2S_TCR3_TCE_MASK) | I2S_TCR3_TCE(0); // Disable TX channels.
aidan1971 0:8f28f25e3435 590 TX_run = false;
aidan1971 0:8f28f25e3435 591 return 0;
aidan1971 0:8f28f25e3435 592 }
aidan1971 0:8f28f25e3435 593
aidan1971 0:8f28f25e3435 594 void SGTL5000::tx_dma_ISR(void)
aidan1971 0:8f28f25e3435 595 {
aidan1971 0:8f28f25e3435 596 static uint32_t db_phase = 0;
aidan1971 0:8f28f25e3435 597 static uint32_t dbA_tx_L = (uint32_t)&SGTL5000::I2S_TX_Buffer[0]; // Pre-compute buffer offsets etc to save cycles in ISR
aidan1971 0:8f28f25e3435 598 static uint32_t dbA_tx_R = (uint32_t)&SGTL5000::I2S_TX_Buffer[SGTL5000::TX_block_size / 2];
aidan1971 0:8f28f25e3435 599 static uint32_t dbB_tx_L = (uint32_t)&SGTL5000::I2S_TX_Buffer[8];
aidan1971 0:8f28f25e3435 600 static uint32_t dbB_tx_R = (uint32_t)&SGTL5000::I2S_TX_Buffer[8 + (SGTL5000::TX_block_size / 2)];
aidan1971 0:8f28f25e3435 601
aidan1971 0:8f28f25e3435 602 DMA0->CINT = DMA_CINT_CINT(SGTL5000::TX_DMAch);
aidan1971 0:8f28f25e3435 603
aidan1971 0:8f28f25e3435 604 if(db_phase) { // Swap double buffer pointers with pre-computed indecies
aidan1971 0:8f28f25e3435 605 *SGTL5000::BufTX_L_safe = dbB_tx_L;
aidan1971 0:8f28f25e3435 606 *SGTL5000::BufTX_R_safe = dbB_tx_R;
aidan1971 0:8f28f25e3435 607 --db_phase;
aidan1971 0:8f28f25e3435 608 } else {
aidan1971 0:8f28f25e3435 609 *SGTL5000::BufTX_L_safe = dbA_tx_L;
aidan1971 0:8f28f25e3435 610 *SGTL5000::BufTX_R_safe = dbA_tx_R;
aidan1971 0:8f28f25e3435 611 ++db_phase;
aidan1971 0:8f28f25e3435 612 }
aidan1971 0:8f28f25e3435 613 if(SGTL5000::TX_attach_type) {
aidan1971 0:8f28f25e3435 614 if(NVIC_GetActive(SGTL5000::TX_swIRQ) == 0) NVIC->STIR = SGTL5000::TX_swIRQ; // Trigger swIRQ or call Callback
aidan1971 0:8f28f25e3435 615 } else SGTL5000::TX_user_func.call();
aidan1971 0:8f28f25e3435 616 }
aidan1971 0:8f28f25e3435 617
aidan1971 0:8f28f25e3435 618 int32_t SGTL5000::attach_RX(Callback<void()> func)
aidan1971 0:8f28f25e3435 619 {
aidan1971 0:8f28f25e3435 620 if(RX_attached) return -1; // Assign Callback function
aidan1971 0:8f28f25e3435 621 SGTL5000::RX_user_func = func;
aidan1971 0:8f28f25e3435 622 SGTL5000::RX_attach_type = 0;
aidan1971 0:8f28f25e3435 623 RX_attached = true;
aidan1971 0:8f28f25e3435 624 return 0;
aidan1971 0:8f28f25e3435 625 }
aidan1971 0:8f28f25e3435 626
aidan1971 0:8f28f25e3435 627 int32_t SGTL5000::attach_RX_NB(uint32_t user_ISR, uint32_t irq_pri, IRQn sw_irq)
aidan1971 0:8f28f25e3435 628 {
aidan1971 0:8f28f25e3435 629 SGTL5000::RX_swIRQ = sw_irq; // Assign ISR address and enable users IRQ
aidan1971 0:8f28f25e3435 630 NVIC_SetVector(SGTL5000::RX_swIRQ, user_ISR);
aidan1971 0:8f28f25e3435 631 NVIC_SetPriority(SGTL5000::RX_swIRQ, irq_pri);
aidan1971 0:8f28f25e3435 632 NVIC_EnableIRQ(SGTL5000::RX_swIRQ);
aidan1971 0:8f28f25e3435 633 SGTL5000::RX_attach_type = 1;
aidan1971 0:8f28f25e3435 634 RX_attached = true;
aidan1971 0:8f28f25e3435 635 return 0;
aidan1971 0:8f28f25e3435 636 }
aidan1971 0:8f28f25e3435 637
aidan1971 0:8f28f25e3435 638 void SGTL5000::detach_RX(void)
aidan1971 0:8f28f25e3435 639 {
aidan1971 0:8f28f25e3435 640 SGTL5000::stop_RX();
aidan1971 0:8f28f25e3435 641 RX_attached = false;
aidan1971 0:8f28f25e3435 642 }
aidan1971 0:8f28f25e3435 643
aidan1971 0:8f28f25e3435 644 int32_t SGTL5000::start_RX(uint32_t BufRX_L_safe, uint32_t BufRX_R_safe,
aidan1971 0:8f28f25e3435 645 uint32_t block_size, bool _RX_shift, uint32_t _RX_DMAch, uint32_t DMA_irq_pri)
aidan1971 0:8f28f25e3435 646 {
aidan1971 0:8f28f25e3435 647 if(!RX_attached && !SGTL5000::RX_attach_type) return -1; // Check we have a handler if using callback
aidan1971 0:8f28f25e3435 648 if(SYNC_run || RX_run) return -1; // Check if i2s is already started on rx
aidan1971 0:8f28f25e3435 649 if(_RX_DMAch > 15) return -1; // Sanity check DMAMUX channels
aidan1971 0:8f28f25e3435 650 if (!(block_size == 2 || block_size == 4 || block_size == 8)) return -1; // Only accept block size 2^n within range.
aidan1971 0:8f28f25e3435 651 SGTL5000::RX_block_size = block_size;
aidan1971 0:8f28f25e3435 652 RX_bs_bytes = block_size * 4;
aidan1971 0:8f28f25e3435 653 SGTL5000::BufRX_L_safe = (uint32_t*)BufRX_L_safe; // Assign the users pointer addresses
aidan1971 0:8f28f25e3435 654 SGTL5000::BufRX_R_safe = (uint32_t*)BufRX_R_safe;
aidan1971 0:8f28f25e3435 655
aidan1971 0:8f28f25e3435 656 SGTL5000::RX_shift = _RX_shift;
aidan1971 0:8f28f25e3435 657 SGTL5000::RX_DMAch = _RX_DMAch;
aidan1971 0:8f28f25e3435 658 RX_run = true;
aidan1971 0:8f28f25e3435 659 init_DMA();
aidan1971 0:8f28f25e3435 660
aidan1971 0:8f28f25e3435 661 I2S0->RCR1 = (I2S0->RCR1 & ~I2S_RCR1_RFW_MASK) | I2S_RCR1_RFW(SGTL5000::RX_block_size - 1); // Set RX FIFO watermark
aidan1971 0:8f28f25e3435 662 I2S0->RCSR |= I2S_RCSR_RE_MASK; // RX enable
aidan1971 0:8f28f25e3435 663 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(1); // Enable RX channel 0.
aidan1971 0:8f28f25e3435 664 //SGTL5000::_db_rx_phase = 0;
aidan1971 0:8f28f25e3435 665 NVIC_SetVector((IRQn)SGTL5000::RX_DMAch, (uint32_t)&SGTL5000::rx_dma_ISR); // Set DMA RX handler vector
aidan1971 0:8f28f25e3435 666 NVIC_SetPriority((IRQn)SGTL5000::RX_DMAch, DMA_irq_pri); // Set irq priorities the same
aidan1971 0:8f28f25e3435 667 NVIC_EnableIRQ((IRQn)SGTL5000::RX_DMAch); // Enable IRQ for chosen RX DMA channel
aidan1971 0:8f28f25e3435 668 NVIC_SetVector(I2S0_Rx_IRQn, (uint32_t)&SGTL5000::rx_I2S_ISR); // Set vector for RX word start ISR
aidan1971 0:8f28f25e3435 669 NVIC_SetPriority(I2S0_Rx_IRQn, 0); // Set priority of RX word start ISR
aidan1971 0:8f28f25e3435 670 NVIC_EnableIRQ(I2S0_Rx_IRQn); // Enable RX word start ISR
aidan1971 0:8f28f25e3435 671 I2S0->RCSR |= I2S_RCSR_WSF_MASK; // Clear RX Word Start Flag
aidan1971 0:8f28f25e3435 672 I2S0->RCSR |= I2S_RCSR_WSIE_MASK; // Enable I2S RX word start IRQ
aidan1971 0:8f28f25e3435 673 return 0;
aidan1971 0:8f28f25e3435 674 }
aidan1971 0:8f28f25e3435 675
aidan1971 0:8f28f25e3435 676 int32_t SGTL5000::stop_RX(void)
aidan1971 0:8f28f25e3435 677 {
aidan1971 0:8f28f25e3435 678
aidan1971 0:8f28f25e3435 679 I2S0->RCSR &= ~I2S_RCSR_FRDE_MASK; // Disable RX FIFO IRQs based on watermark
aidan1971 0:8f28f25e3435 680 I2S0->RCR3 = (I2S0->RCR3 & ~I2S_RCR3_RCE_MASK) | I2S_RCR3_RCE(0); // Disable RX channels.
aidan1971 0:8f28f25e3435 681 RX_run = false;
aidan1971 0:8f28f25e3435 682 return 0;
aidan1971 0:8f28f25e3435 683 }
aidan1971 0:8f28f25e3435 684
aidan1971 0:8f28f25e3435 685 void SGTL5000::rx_dma_ISR(void)
aidan1971 0:8f28f25e3435 686 {
aidan1971 0:8f28f25e3435 687 static uint32_t db_phase = 0;
aidan1971 0:8f28f25e3435 688 static uint32_t dbA_rx_L = (uint32_t)&SGTL5000::I2S_RX_Buffer[0]; // Pre-compute buffer offsets etc to save cycles in ISR
aidan1971 0:8f28f25e3435 689 static uint32_t dbA_rx_R = (uint32_t)&SGTL5000::I2S_RX_Buffer[SGTL5000::RX_block_size / 2];
aidan1971 0:8f28f25e3435 690 static uint32_t dbB_rx_L = (uint32_t)&SGTL5000::I2S_RX_Buffer[8];
aidan1971 0:8f28f25e3435 691 static uint32_t dbB_rx_R = (uint32_t)&SGTL5000::I2S_RX_Buffer[8 + (SGTL5000::RX_block_size / 2)];
aidan1971 0:8f28f25e3435 692
aidan1971 0:8f28f25e3435 693 DMA0->CINT = DMA_CINT_CINT(SGTL5000::RX_DMAch);
aidan1971 0:8f28f25e3435 694
aidan1971 0:8f28f25e3435 695 if(db_phase) { // Swap double buffer pointers with pre-computed indecies
aidan1971 0:8f28f25e3435 696 *SGTL5000::BufRX_L_safe = dbB_rx_L;
aidan1971 0:8f28f25e3435 697 *SGTL5000::BufRX_R_safe = dbB_rx_R;
aidan1971 0:8f28f25e3435 698 --db_phase;
aidan1971 0:8f28f25e3435 699 } else {
aidan1971 0:8f28f25e3435 700 *SGTL5000::BufRX_L_safe = dbA_rx_L;
aidan1971 0:8f28f25e3435 701 *SGTL5000::BufRX_R_safe = dbA_rx_R;
aidan1971 0:8f28f25e3435 702 ++db_phase;
aidan1971 0:8f28f25e3435 703 }
aidan1971 0:8f28f25e3435 704 if(SGTL5000::RX_attach_type) { // Trigger swIRQ or call Callback
aidan1971 0:8f28f25e3435 705 if(NVIC_GetActive(SGTL5000::RX_swIRQ) == 0) NVIC->STIR = SGTL5000::RX_swIRQ;
aidan1971 0:8f28f25e3435 706 } else SGTL5000::RX_user_func.call();
aidan1971 0:8f28f25e3435 707 }
aidan1971 0:8f28f25e3435 708
aidan1971 0:8f28f25e3435 709
aidan1971 0:8f28f25e3435 710
aidan1971 0:8f28f25e3435 711 void SGTL5000::init_DMA(void)
aidan1971 0:8f28f25e3435 712 {
aidan1971 0:8f28f25e3435 713 /*
aidan1971 0:8f28f25e3435 714 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 715 The chain is configured as follows:
aidan1971 0:8f28f25e3435 716 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 0:8f28f25e3435 717 At the end of a minor loop MLOFF jumps back (BLOCK_SIZE - 1) 32bit 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 718 uppper half of the double buffer.
aidan1971 0:8f28f25e3435 719 eg.
aidan1971 0:8f28f25e3435 720 buffer layout with Block Size = 2 Block Size = 4 Block Size = 8
aidan1971 0:8f28f25e3435 721 Double Buffer A Double Buffer B Double Buffer A Double Buffer B Double Buffer A Double Buffer B
aidan1971 0:8f28f25e3435 722 |L|R|x|x|x|x|x|x:L|R|x|x|x|x|x|x| |L|L|R|R|x|x|x|x:L|L|R|R|x|x|x|x| |L|L|L|L|R|R|R|R:L|L|L|L|R|R|R|R|
aidan1971 0:8f28f25e3435 723
aidan1971 0:8f28f25e3435 724 */
aidan1971 0:8f28f25e3435 725
aidan1971 0:8f28f25e3435 726 static uint32_t SG_rx_TCD_A[8] __attribute ((aligned (0x20))); // Allocate memory for scatter gather TCD definitions
aidan1971 0:8f28f25e3435 727 static uint32_t SG_rx_TCD_B[8] __attribute ((aligned (0x20)));
aidan1971 0:8f28f25e3435 728 static uint32_t SG_tx_TCD_A[8] __attribute ((aligned (0x20)));
aidan1971 0:8f28f25e3435 729 static uint32_t SG_tx_TCD_B[8] __attribute ((aligned (0x20)));
aidan1971 0:8f28f25e3435 730
aidan1971 0:8f28f25e3435 731 // Clock Control config to DMA Controller and DMAMux and common DMA Setup
aidan1971 0:8f28f25e3435 732 SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK; // Enable Clocking to DMAMUX Module
aidan1971 0:8f28f25e3435 733 SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Enable Clocking to DMA Module
aidan1971 0:8f28f25e3435 734 DMA0->CR |= DMA_CR_EMLM_MASK; // Enable minor loop mapping
aidan1971 0:8f28f25e3435 735
aidan1971 0:8f28f25e3435 736 if(SYNC_run || RX_run) {
aidan1971 0:8f28f25e3435 737 // DMAMUX Config
aidan1971 0:8f28f25e3435 738 DMAMUX->CHCFG[SGTL5000::RX_DMAch] &= ~DMAMUX_CHCFG_ENBL_MASK; // MUX channels disabled while we configure
aidan1971 0:8f28f25e3435 739 DMAMUX->CHCFG[SGTL5000::RX_DMAch] &= ~DMAMUX_CHCFG_TRIG_MASK; // Trigger Modes Normal
aidan1971 0:8f28f25e3435 740 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 741 DMA0->CERQ =DMA_CERQ_CERQ(SGTL5000::RX_DMAch); // Disable requests for chosen DMA channels
aidan1971 0:8f28f25e3435 742
aidan1971 0:8f28f25e3435 743 //Configure RX DMA Transfer Control Descriptor
aidan1971 0:8f28f25e3435 744 DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES &= ~DMA_NBYTES_MLOFFYES_SMLOE_MASK; // Disable source offset on minor loop
aidan1971 0:8f28f25e3435 745 DMA0->TCD[SGTL5000::RX_DMAch].NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_DMLOE_MASK; // Enable dest offset on minor loop
aidan1971 0:8f28f25e3435 746 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 747 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 748 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 749 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 750 DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_ESG_MASK; // Enable scatter gather
aidan1971 0:8f28f25e3435 751 DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_DREQ_MASK; // Disable auto reset of request enable on major
aidan1971 0:8f28f25e3435 752 DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_INTHALF_MASK; // Disable IRQ at Half Completion of Major cycle
aidan1971 0:8f28f25e3435 753 DMA0->TCD[SGTL5000::RX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; // Disable major loop linking
aidan1971 0:8f28f25e3435 754 DMA0->TCD[SGTL5000::RX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; // Enable IRQ at Completion of Major cycle
aidan1971 0:8f28f25e3435 755 DMA0->TCD[SGTL5000::RX_DMAch].ATTR = DMA_ATTR_SMOD(0) | DMA_ATTR_SSIZE(1) | DMA_ATTR_DMOD(0) | DMA_ATTR_DSIZE(1); // Set data transfer size @ 16bits per memory access across the memory bus
aidan1971 0:8f28f25e3435 756 if(SGTL5000::RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0] + 2;
aidan1971 0:8f28f25e3435 757 else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0];
aidan1971 0:8f28f25e3435 758 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 759 DMA0->TCD[SGTL5000::RX_DMAch].SOFF = 0; // Signed Source offset set to zero (always read from RDR[0]).
aidan1971 0:8f28f25e3435 760 DMA0->TCD[SGTL5000::RX_DMAch].DOFF = (RX_bs_bytes / 2); // After each write step into upper half of the buffer
aidan1971 0:8f28f25e3435 761 DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO &= ~DMA_CITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop
aidan1971 0:8f28f25e3435 762 DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO = (DMA0->TCD[SGTL5000::RX_DMAch].CITER_ELINKNO & ~DMA_CITER_ELINKNO_CITER_MASK) | DMA_CITER_ELINKNO_CITER(SGTL5000::RX_block_size / 2); // Major loop current iter count starting value
aidan1971 0:8f28f25e3435 763 DMA0->TCD[SGTL5000::RX_DMAch].BITER_ELINKNO &= ~DMA_BITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop
aidan1971 0:8f28f25e3435 764 DMA0->TCD[SGTL5000::RX_DMAch].BITER_ELINKNO = (DMA0->TCD[SGTL5000::RX_DMAch].BITER_ELINKNO & ~DMA_BITER_ELINKNO_BITER_MASK) | DMA_BITER_ELINKNO_BITER(SGTL5000::RX_block_size / 2); // Major loop iter count to load again at after major completes
aidan1971 0:8f28f25e3435 765 DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B[0]; // Set scatter gather TCD definition location
aidan1971 0:8f28f25e3435 766 DMA0->TCD[SGTL5000::RX_DMAch].SLAST = 0;
aidan1971 0:8f28f25e3435 767 memcpy(&SG_rx_TCD_A[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32); // Copy TCD A to memory
aidan1971 0:8f28f25e3435 768 DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_A[0]; // Set scatter gather TCD definition location
aidan1971 0:8f28f25e3435 769 if(SGTL5000::RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[8] + 2; // Swap RX double buffer
aidan1971 0:8f28f25e3435 770 else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[8];
aidan1971 0:8f28f25e3435 771 memcpy(&SG_rx_TCD_B[0], (void*)&DMA0->TCD[SGTL5000::RX_DMAch], 32); // Copy TCD B to memory
aidan1971 0:8f28f25e3435 772 // Set TCD elements in the DMA controller back to initial TCD A
aidan1971 0:8f28f25e3435 773 DMA0->TCD[SGTL5000::RX_DMAch].DLAST_SGA = (uint32_t)&SG_rx_TCD_B[0]; // Set scatter gather TCD definition location
aidan1971 0:8f28f25e3435 774 if(SGTL5000::RX_shift) DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0] + 2;
aidan1971 0:8f28f25e3435 775 else DMA0->TCD[SGTL5000::RX_DMAch].DADDR = (uint32_t)&SGTL5000::I2S_RX_Buffer[0];
aidan1971 0:8f28f25e3435 776 }
aidan1971 0:8f28f25e3435 777
aidan1971 0:8f28f25e3435 778
aidan1971 0:8f28f25e3435 779 if(SYNC_run || TX_run) {
aidan1971 0:8f28f25e3435 780 DMAMUX->CHCFG[SGTL5000::TX_DMAch] &= ~DMAMUX_CHCFG_ENBL_MASK;
aidan1971 0:8f28f25e3435 781 DMAMUX->CHCFG[SGTL5000::TX_DMAch] &= ~DMAMUX_CHCFG_TRIG_MASK;
aidan1971 0:8f28f25e3435 782 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 783 DMA0->CERQ = DMA_CERQ_CERQ(SGTL5000::TX_DMAch);
aidan1971 0:8f28f25e3435 784
aidan1971 0:8f28f25e3435 785 //Configure TX DMA Transfer Control Descriptor
aidan1971 0:8f28f25e3435 786 DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES |= DMA_NBYTES_MLOFFYES_SMLOE_MASK; // Disable source offset on minor loop
aidan1971 0:8f28f25e3435 787 DMA0->TCD[SGTL5000::TX_DMAch].NBYTES_MLOFFYES &= ~DMA_NBYTES_MLOFFYES_DMLOE_MASK; // Enable dest offset on minor loop
aidan1971 0:8f28f25e3435 788 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 789 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 790 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 791 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 792 DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_ESG_MASK; // Enable scatter gather
aidan1971 0:8f28f25e3435 793 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_DREQ_MASK; // Disable auto reset of request enable on major
aidan1971 0:8f28f25e3435 794 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_INTHALF_MASK; // Disable IRQ at Half Completion of Major cycle
aidan1971 0:8f28f25e3435 795 DMA0->TCD[SGTL5000::TX_DMAch].CSR &= ~DMA_CSR_MAJORELINK_MASK; // Disable major loop linking
aidan1971 0:8f28f25e3435 796 DMA0->TCD[SGTL5000::TX_DMAch].CSR |= DMA_CSR_INTMAJOR_MASK; // Enable IRQ at Completion of Major cycle
aidan1971 0:8f28f25e3435 797 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 0:8f28f25e3435 798 if(SGTL5000::TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0] + 2;
aidan1971 0:8f28f25e3435 799 else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0];
aidan1971 0:8f28f25e3435 800 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 801 DMA0->TCD[SGTL5000::TX_DMAch].DOFF = 0; // Signed Source offset set to zero (always write TDR[0]).
aidan1971 0:8f28f25e3435 802 DMA0->TCD[SGTL5000::TX_DMAch].SOFF = (TX_bs_bytes / 2); // After each write step into upper half of the buffer
aidan1971 0:8f28f25e3435 803 DMA0->TCD[SGTL5000::TX_DMAch].CITER_ELINKNO &= ~DMA_CITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop
aidan1971 0:8f28f25e3435 804 DMA0->TCD[SGTL5000::TX_DMAch].CITER_ELINKNO = (DMA0->TCD[SGTL5000::TX_DMAch].CITER_ELINKNO & ~DMA_CITER_ELINKNO_CITER_MASK) | DMA_CITER_ELINKNO_CITER(SGTL5000::TX_block_size / 2); // Major loop current iter count starting value
aidan1971 0:8f28f25e3435 805 DMA0->TCD[SGTL5000::TX_DMAch].BITER_ELINKNO &= ~DMA_BITER_ELINKNO_ELINK_MASK; // Disable channel linking minor loop
aidan1971 0:8f28f25e3435 806 DMA0->TCD[SGTL5000::TX_DMAch].BITER_ELINKNO = (DMA0->TCD[SGTL5000::TX_DMAch].BITER_ELINKNO & ~DMA_BITER_ELINKNO_BITER_MASK) | DMA_BITER_ELINKNO_BITER(SGTL5000::TX_block_size / 2); // Major loop iter count to load again at after major completes // Reset dest addr to start address.
aidan1971 0:8f28f25e3435 807 DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0]; // Set scatter gather TCD definition location
aidan1971 0:8f28f25e3435 808 memcpy(&SG_tx_TCD_A[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32); // Copy TCD A to memory
aidan1971 0:8f28f25e3435 809 DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_A[0]; // Set scatter gather TCD definition location
aidan1971 0:8f28f25e3435 810 if(SGTL5000::TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[8] + 2; // Swap TX double buffer
aidan1971 0:8f28f25e3435 811 else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[8];
aidan1971 0:8f28f25e3435 812 memcpy(&SG_tx_TCD_B[0], (void*)&DMA0->TCD[SGTL5000::TX_DMAch], 32); // Copy TCD B to memory
aidan1971 0:8f28f25e3435 813 // Set TCD elements in the DMA controller back to initial TCD A
aidan1971 0:8f28f25e3435 814 DMA0->TCD[SGTL5000::TX_DMAch].DLAST_SGA = (uint32_t)&SG_tx_TCD_B[0]; // Set scatter gather TCD definition location
aidan1971 0:8f28f25e3435 815 if(SGTL5000::TX_shift) DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0] + 2;
aidan1971 0:8f28f25e3435 816 else DMA0->TCD[SGTL5000::TX_DMAch].SADDR = (uint32_t)&SGTL5000::I2S_TX_Buffer[0];
aidan1971 0:8f28f25e3435 817 }
aidan1971 0:8f28f25e3435 818
aidan1971 0:8f28f25e3435 819 if(SYNC_run || RX_run) {
aidan1971 0:8f28f25e3435 820 DMA0->SERQ = DMA_SERQ_SERQ(SGTL5000::RX_DMAch); // Enable requests for RX DMA channel
aidan1971 0:8f28f25e3435 821 DMAMUX->CHCFG[SGTL5000::RX_DMAch] |= DMAMUX_CHCFG_ENBL_MASK; // DMAMUX channel enabled. Pass DMA requests from I2S to the DMA controller.
aidan1971 0:8f28f25e3435 822 }
aidan1971 0:8f28f25e3435 823 if(SYNC_run || TX_run) {
aidan1971 0:8f28f25e3435 824 DMA0->SERQ = DMA_SERQ_SERQ(SGTL5000::TX_DMAch); // Enable requests for TX DMA channel
aidan1971 0:8f28f25e3435 825 DMAMUX->CHCFG[SGTL5000::TX_DMAch] |= DMAMUX_CHCFG_ENBL_MASK; // DMAMUX channel enabled. Pass DMA requests from I2S to the DMA controller.
aidan1971 0:8f28f25e3435 826 }
aidan1971 0:8f28f25e3435 827 }
aidan1971 0:8f28f25e3435 828
aidan1971 0:8f28f25e3435 829 uint32_t SGTL5000::read_debug(uint32_t index)
aidan1971 0:8f28f25e3435 830 {
aidan1971 0:8f28f25e3435 831 //SGTL5000::debug[0] =
aidan1971 0:8f28f25e3435 832 return SGTL5000::debug[index];
aidan1971 0:8f28f25e3435 833 };
aidan1971 0:8f28f25e3435 834 }