|
|
@@ -24,7 +24,7 @@ |
|
|
|
* THE SOFTWARE. |
|
|
|
*/ |
|
|
|
|
|
|
|
#if !defined(__IMXRT1052__) && !defined(__IMXRT1062__) |
|
|
|
#if defined(KINETISK) |
|
|
|
|
|
|
|
#include <Arduino.h> |
|
|
|
#include "input_adc.h" |
|
|
@@ -33,7 +33,7 @@ |
|
|
|
|
|
|
|
#define COEF_HPF_DCBLOCK (1048300<<10) // DC Removal filter coefficient in S1.30 |
|
|
|
|
|
|
|
DMAMEM static uint16_t analog_rx_buffer[AUDIO_BLOCK_SAMPLES]; |
|
|
|
DMAMEM __attribute__((aligned(32))) static uint16_t analog_rx_buffer[AUDIO_BLOCK_SAMPLES]; |
|
|
|
audio_block_t * AudioInputAnalog::block_left = NULL; |
|
|
|
uint16_t AudioInputAnalog::block_offset = 0; |
|
|
|
int32_t AudioInputAnalog::hpf_y1 = 0; |
|
|
@@ -44,7 +44,7 @@ DMAChannel AudioInputAnalog::dma(false); |
|
|
|
|
|
|
|
void AudioInputAnalog::init(uint8_t pin) |
|
|
|
{ |
|
|
|
int32_t tmp; |
|
|
|
int32_t tmp; |
|
|
|
|
|
|
|
// Configure the ADC and run at least one software-triggered |
|
|
|
// conversion. This completes the self calibration stuff and |
|
|
@@ -57,15 +57,14 @@ void AudioInputAnalog::init(uint8_t pin) |
|
|
|
analogReadAveraging(4); |
|
|
|
#endif |
|
|
|
// Note for review: |
|
|
|
// Probably not useful to spin cycles here stabilizing |
|
|
|
// since DC blocking is similar to te external analog filters |
|
|
|
tmp = (uint16_t) analogRead(pin); |
|
|
|
tmp = ( ((int32_t) tmp) << 14); |
|
|
|
hpf_x1 = tmp; // With constant DC level x1 would be x0 |
|
|
|
hpf_y1 = 0; // Output will settle here when stable |
|
|
|
// Probably not useful to spin cycles here stabilizing |
|
|
|
// since DC blocking is similar to te external analog filters |
|
|
|
tmp = (uint16_t) analogRead(pin); |
|
|
|
tmp = ( ((int32_t) tmp) << 14); |
|
|
|
hpf_x1 = tmp; // With constant DC level x1 would be x0 |
|
|
|
hpf_y1 = 0; // Output will settle here when stable |
|
|
|
|
|
|
|
// set the programmable delay block to trigger the ADC at 44.1 kHz |
|
|
|
#if defined(KINETISK) |
|
|
|
if (!(SIM_SCGC6 & SIM_SCGC6_PDB) |
|
|
|
|| (PDB0_SC & PDB_CONFIG) != PDB_CONFIG |
|
|
|
|| PDB0_MOD != PDB_PERIOD |
|
|
@@ -78,13 +77,11 @@ void AudioInputAnalog::init(uint8_t pin) |
|
|
|
PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG; |
|
|
|
PDB0_CH0C1 = 0x0101; |
|
|
|
} |
|
|
|
#endif |
|
|
|
// enable the ADC for hardware trigger and DMA |
|
|
|
ADC0_SC2 |= ADC_SC2_ADTRG | ADC_SC2_DMAEN; |
|
|
|
|
|
|
|
// set up a DMA channel to store the ADC data |
|
|
|
dma.begin(true); |
|
|
|
#if defined(KINETISK) |
|
|
|
dma.TCD->SADDR = &ADC0_RA; |
|
|
|
dma.TCD->SOFF = 0; |
|
|
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); |
|
|
@@ -96,7 +93,6 @@ void AudioInputAnalog::init(uint8_t pin) |
|
|
|
dma.TCD->DLASTSGA = -sizeof(analog_rx_buffer); |
|
|
|
dma.TCD->BITER_ELINKNO = sizeof(analog_rx_buffer) / 2; |
|
|
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; |
|
|
|
#endif |
|
|
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0); |
|
|
|
update_responsibility = update_setup(); |
|
|
|
dma.enable(); |
|
|
@@ -111,9 +107,7 @@ void AudioInputAnalog::isr(void) |
|
|
|
uint16_t *dest_left; |
|
|
|
audio_block_t *left; |
|
|
|
|
|
|
|
#if defined(KINETISK) |
|
|
|
daddr = (uint32_t)(dma.TCD->DADDR); |
|
|
|
#endif |
|
|
|
dma.clearInterrupt(); |
|
|
|
|
|
|
|
if (daddr < (uint32_t)analog_rx_buffer + sizeof(analog_rx_buffer) / 2) { |
|
|
@@ -213,4 +207,221 @@ void AudioInputAnalog::update(void) |
|
|
|
transmit(out_left); |
|
|
|
release(out_left); |
|
|
|
} |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(__IMXRT1062__) |
|
|
|
|
|
|
|
#include <Arduino.h> |
|
|
|
#include "input_adc.h" |
|
|
|
|
|
|
|
extern "C" void xbar_connect(unsigned int input, unsigned int output); |
|
|
|
|
|
|
|
#define FILTERLEN 15 |
|
|
|
|
|
|
|
DMAChannel AudioInputAnalog::dma(false); |
|
|
|
// TODO: how much extra space is needed to avoid wrap-around timing? 200 seems a safe guess |
|
|
|
static __attribute__((aligned(32))) uint16_t adc_buffer[AUDIO_BLOCK_SAMPLES*4+200]; |
|
|
|
static int16_t capture_buffer[AUDIO_BLOCK_SAMPLES*4+FILTERLEN]; |
|
|
|
// TODO: these big buffers should be in DMAMEM, rather than consuming precious DTCM |
|
|
|
|
|
|
|
PROGMEM static const uint8_t adc2_pin_to_channel[] = { |
|
|
|
7, // 0/A0 AD_B1_02 |
|
|
|
8, // 1/A1 AD_B1_03 |
|
|
|
12, // 2/A2 AD_B1_07 |
|
|
|
11, // 3/A3 AD_B1_06 |
|
|
|
6, // 4/A4 AD_B1_01 |
|
|
|
5, // 5/A5 AD_B1_00 |
|
|
|
15, // 6/A6 AD_B1_10 |
|
|
|
0, // 7/A7 AD_B1_11 |
|
|
|
13, // 8/A8 AD_B1_08 |
|
|
|
14, // 9/A9 AD_B1_09 |
|
|
|
255, // 10/A10 AD_B0_12 - only on ADC1, 1 - can't use for audio |
|
|
|
255, // 11/A11 AD_B0_13 - only on ADC1, 2 - can't use for audio |
|
|
|
3, // 12/A12 AD_B1_14 |
|
|
|
4, // 13/A13 AD_B1_15 |
|
|
|
7, // 14/A0 AD_B1_02 |
|
|
|
8, // 15/A1 AD_B1_03 |
|
|
|
12, // 16/A2 AD_B1_07 |
|
|
|
11, // 17/A3 AD_B1_06 |
|
|
|
6, // 18/A4 AD_B1_01 |
|
|
|
5, // 19/A5 AD_B1_00 |
|
|
|
15, // 20/A6 AD_B1_10 |
|
|
|
0, // 21/A7 AD_B1_11 |
|
|
|
13, // 22/A8 AD_B1_08 |
|
|
|
14, // 23/A9 AD_B1_09 |
|
|
|
255, // 24/A10 AD_B0_12 - only on ADC1, 1 - can't use for audio |
|
|
|
255, // 25/A11 AD_B0_13 - only on ADC1, 2 - can't use for audio |
|
|
|
3, // 26/A12 AD_B1_14 - only on ADC2, do not use analogRead() |
|
|
|
4, // 27/A13 AD_B1_15 - only on ADC2, do not use analogRead() |
|
|
|
#ifdef ARDUINO_TEENSY41 |
|
|
|
255, // 28 |
|
|
|
255, // 29 |
|
|
|
255, // 30 |
|
|
|
255, // 31 |
|
|
|
255, // 32 |
|
|
|
255, // 33 |
|
|
|
255, // 34 |
|
|
|
255, // 35 |
|
|
|
255, // 36 |
|
|
|
255, // 37 |
|
|
|
1, // 38/A14 AD_B1_12 - only on ADC2, do not use analogRead() |
|
|
|
2, // 39/A15 AD_B1_13 - only on ADC2, do not use analogRead() |
|
|
|
9, // 40/A16 AD_B1_04 |
|
|
|
10, // 41/A17 AD_B1_05 |
|
|
|
#endif |
|
|
|
}; |
|
|
|
|
|
|
|
static const int16_t filter[FILTERLEN] = { |
|
|
|
1449, |
|
|
|
3676, |
|
|
|
6137, |
|
|
|
9966, |
|
|
|
13387, |
|
|
|
16896, |
|
|
|
18951, |
|
|
|
19957, |
|
|
|
18951, |
|
|
|
16896, |
|
|
|
13387, |
|
|
|
9966, |
|
|
|
6137, |
|
|
|
3676, |
|
|
|
1449 |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
void AudioInputAnalog::init(uint8_t pin) |
|
|
|
{ |
|
|
|
if (pin >= sizeof(adc2_pin_to_channel)) return; |
|
|
|
const uint8_t adc_channel = adc2_pin_to_channel[pin]; |
|
|
|
if (adc_channel == 255) return; |
|
|
|
|
|
|
|
// configure a timer to trigger ADC |
|
|
|
// TODO: sample rate should be slightly lower than 4X AUDIO_SAMPLE_RATE_EXACT |
|
|
|
// linear interpolation is supposed to resample it to exactly 4X |
|
|
|
// the sample rate, so we avoid artifacts boundaries between captures |
|
|
|
const int comp1 = ((float)F_BUS_ACTUAL) / (AUDIO_SAMPLE_RATE_EXACT * 4.0f) / 2.0f + 0.5f; |
|
|
|
TMR4_ENBL &= ~(1<<3); |
|
|
|
TMR4_SCTRL3 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE; |
|
|
|
TMR4_CSCTRL3 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN; |
|
|
|
TMR4_CNTR3 = 0; |
|
|
|
TMR4_LOAD3 = 0; |
|
|
|
TMR4_COMP13 = comp1; |
|
|
|
TMR4_CMPLD13 = comp1; |
|
|
|
TMR4_CTRL3 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(3); |
|
|
|
TMR4_DMA3 = TMR_DMA_CMPLD1DE; |
|
|
|
TMR4_CNTR3 = 0; |
|
|
|
TMR4_ENBL |= (1<<3); |
|
|
|
|
|
|
|
// connect the timer output the ADC_ETC input |
|
|
|
const int trigger = 4; // 0-3 for ADC1, 4-7 for ADC2 |
|
|
|
CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON); |
|
|
|
xbar_connect(XBARA1_IN_QTIMER4_TIMER3, XBARA1_OUT_ADC_ETC_TRIG00 + trigger); |
|
|
|
|
|
|
|
// turn on ADC_ETC and configure to receive trigger |
|
|
|
if (ADC_ETC_CTRL & (ADC_ETC_CTRL_SOFTRST | ADC_ETC_CTRL_TSC_BYPASS)) { |
|
|
|
ADC_ETC_CTRL = 0; // clears SOFTRST only |
|
|
|
ADC_ETC_CTRL = 0; // clears TSC_BYPASS |
|
|
|
} |
|
|
|
ADC_ETC_CTRL |= ADC_ETC_CTRL_TRIG_ENABLE(1 << trigger) | ADC_ETC_CTRL_DMA_MODE_SEL; |
|
|
|
ADC_ETC_DMA_CTRL |= ADC_ETC_DMA_CTRL_TRIQ_ENABLE(trigger); |
|
|
|
|
|
|
|
// configure ADC_ETC trigger4 to make one ADC2 measurement on pin A2 |
|
|
|
const int len = 1; |
|
|
|
IMXRT_ADC_ETC.TRIG[trigger].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(len - 1) | |
|
|
|
ADC_ETC_TRIG_CTRL_TRIG_PRIORITY(7); |
|
|
|
IMXRT_ADC_ETC.TRIG[trigger].CHAIN_1_0 = ADC_ETC_TRIG_CHAIN_HWTS0(1) | |
|
|
|
ADC_ETC_TRIG_CHAIN_CSEL0(adc2_pin_to_channel[pin]) | ADC_ETC_TRIG_CHAIN_B2B0; |
|
|
|
|
|
|
|
// set up ADC2 for 12 bit mode, hardware trigger |
|
|
|
Serial.printf("ADC2_CFG = %08X\n", ADC2_CFG); |
|
|
|
ADC2_CFG |= ADC_CFG_ADTRG; |
|
|
|
ADC2_CFG = ADC_CFG_MODE(2) | ADC_CFG_ADSTS(3) | ADC_CFG_ADLSMP | ADC_CFG_ADTRG | |
|
|
|
ADC_CFG_ADICLK(1) | ADC_CFG_ADIV(0) /*| ADC_CFG_ADHSC*/; |
|
|
|
ADC2_GC &= ~ADC_GC_AVGE; // single sample, no averaging |
|
|
|
ADC2_HC0 = ADC_HC_ADCH(16); // 16 = controlled by ADC_ETC |
|
|
|
|
|
|
|
// use a DMA channel to capture ADC_ETC output |
|
|
|
dma.begin(); |
|
|
|
dma.TCD->SADDR = &(IMXRT_ADC_ETC.TRIG[4].RESULT_1_0); |
|
|
|
dma.TCD->SOFF = 0; |
|
|
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); |
|
|
|
dma.TCD->NBYTES_MLNO = 2; |
|
|
|
dma.TCD->SLAST = 0; |
|
|
|
dma.TCD->DADDR = adc_buffer; |
|
|
|
dma.TCD->DOFF = 2; |
|
|
|
dma.TCD->CITER_ELINKNO = sizeof(adc_buffer) / 2; |
|
|
|
dma.TCD->DLASTSGA = -sizeof(adc_buffer); |
|
|
|
dma.TCD->BITER_ELINKNO = sizeof(adc_buffer) / 2; |
|
|
|
dma.TCD->CSR = 0; |
|
|
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC_ETC); |
|
|
|
dma.enable(); |
|
|
|
|
|
|
|
// TODO: configure I2S1 to interrupt every 128 audio samples |
|
|
|
} |
|
|
|
|
|
|
|
static int16_t fir(const int16_t *data, const int16_t *impulse, int len) |
|
|
|
{ |
|
|
|
int64_t sum=0; |
|
|
|
|
|
|
|
while (len > 0) { |
|
|
|
sum += *data++ * *impulse++; // TODO: optimize with DSP inst and filter symmetry |
|
|
|
len --; |
|
|
|
} |
|
|
|
sum = sum >> 15; // TODO: adjust filter coefficients for proper gain, 12 to 16 bits |
|
|
|
if (sum > 32767) return 32767; |
|
|
|
if (sum < -32768) return -32768; |
|
|
|
return sum; |
|
|
|
} |
|
|
|
|
|
|
|
void AudioInputAnalog::update(void) |
|
|
|
{ |
|
|
|
audio_block_t *output=NULL; |
|
|
|
output = allocate(); |
|
|
|
if (output == NULL) return; |
|
|
|
|
|
|
|
uint16_t *p = (uint16_t *)dma.TCD->DADDR; |
|
|
|
//int offset = p - adc_buffer; |
|
|
|
//if (--offset < 0) offset = sizeof(adc_buffer) / 2 - 1; |
|
|
|
//Serial.printf("offset = %4d, val = %4d\n", offset + 1, adc_buffer[offset]); |
|
|
|
|
|
|
|
// copy adc buffer to capture buffer |
|
|
|
// FIXME: this should be done from the I2S interrupt, for precise capture timing |
|
|
|
const unsigned int capture_len = sizeof(capture_buffer) / 2; |
|
|
|
for (unsigned int i=0; i < capture_len; i++) { |
|
|
|
// TODO: linear interpolate to exactly 4X sample rate |
|
|
|
if (--p < adc_buffer) p = adc_buffer + (sizeof(adc_buffer) / 2 - 1); |
|
|
|
|
|
|
|
// remove DC offset |
|
|
|
// TODO: very slow low pass filter for DC offset |
|
|
|
int dc_offset = 550; // FIXME: quick kludge for testing!! |
|
|
|
|
|
|
|
int n = (int)*p - dc_offset; |
|
|
|
if (n > 4095) n = 4095; |
|
|
|
if (n < -4095) n = -4095; |
|
|
|
|
|
|
|
capture_buffer[i] = n; |
|
|
|
} |
|
|
|
//printbuf(capture_buffer, 8); |
|
|
|
|
|
|
|
// low pass filter and subsample (this part belongs here) |
|
|
|
int16_t *dest = output->data + AUDIO_BLOCK_SAMPLES - 1; |
|
|
|
for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) { |
|
|
|
#if 1 |
|
|
|
// proper low-pass filter sounds pretty good |
|
|
|
*dest-- = fir(capture_buffer + i * 4, filter, sizeof(filter)/2); |
|
|
|
#else |
|
|
|
// just averge 4 samples together, lower quality but much faster |
|
|
|
*dest-- = capture_buffer[i * 4] + capture_buffer[i * 4 + 1] |
|
|
|
+ capture_buffer[i * 4 + 2] + capture_buffer[i * 4 + 3]; |
|
|
|
#endif |
|
|
|
} |
|
|
|
transmit(output); |
|
|
|
release(output); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif |