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:
Fri Jun 30 09:46:54 2017 +0000
Revision:
5:664802e89661
Parent:
4:91354c908416
Child:
6:4ab5aaeaa064
Packed data delivered as int16_t !

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