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 Jul 07 11:38:57 2017 +0000
Revision:
8:9fdf8501d14b
Parent:
7:d65476c153a4
Child:
9:40e0ff8c2ba2
Added init function to allow control over timing of I2C comms.

Who changed what in which revision?

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