Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
206070834This file is pinned to the selected release. Download the original to open it in your design tool.
#include "OC_ADC.h"
#include "OC_gpio.h"
#include "DMAChannel.h"
#include <algorithm>
namespace OC {
/*static*/ ::ADC ADC::adc_;
/*static*/ ADC::CalibrationData *ADC::calibration_data_;
/*static*/ uint32_t ADC::raw_[ADC_CHANNEL_LAST];
/*static*/ uint32_t ADC::smoothed_[ADC_CHANNEL_LAST];
/*static*/ volatile bool ADC::ready_;
constexpr uint16_t ADC::SCA_CHANNEL_ID[DMA_NUM_CH]; // ADCx_SCA register channel numbers
DMAChannel* dma0 = new DMAChannel(false); // dma0 channel, fills adcbuffer_0
DMAChannel* dma1 = new DMAChannel(false); // dma1 channel, updates ADC0_SC1A which holds the channel/pin IDs
DMAMEM static volatile uint16_t __attribute__((aligned(DMA_BUF_SIZE+0))) adcbuffer_0[DMA_BUF_SIZE];
/*static*/ void ADC::Init(CalibrationData *calibration_data) {
adc_.setReference(ADC_REF_3V3);
adc_.setResolution(kAdcScanResolution);
adc_.setConversionSpeed(kAdcConversionSpeed);
adc_.setSamplingSpeed(kAdcSamplingSpeed);
adc_.setAveraging(kAdcScanAverages);
calibration_data_ = calibration_data;
std::fill(raw_, raw_ + ADC_CHANNEL_LAST, 0);
std::fill(smoothed_, smoothed_ + ADC_CHANNEL_LAST, 0);
std::fill(adcbuffer_0, adcbuffer_0 + DMA_BUF_SIZE, 0);
adc_.enableDMA();
}
/*static*/ void ADC::DMA_ISR() {
ADC::ready_ = true;
dma0->TCD->DADDR = &adcbuffer_0[0];
dma0->clearInterrupt();
/* restart DMA in ADC::Scan_DMA() */
}
/*
*
* DMA/ADC à la https://forum.pjrc.com/threads/30171-Reconfigure-ADC-via-a-DMA-transfer-to-allow-multiple-Channel-Acquisition
* basically, this sets up two DMA channels and cycles through the four adc mux channels (until the buffer is full), resets, and so on; dma1 advances SCA_CHANNEL_ID
* somewhat like https://www.nxp.com/docs/en/application-note/AN4590.pdf but w/o the PDB.
*
*/
void ADC::Init_DMA() {
dma0->begin(true); // allocate the DMA channel
dma0->TCD->SADDR = &ADC0_RA;
dma0->TCD->SOFF = 0;
dma0->TCD->ATTR = 0x101;
dma0->TCD->NBYTES = 2;
dma0->TCD->SLAST = 0;
dma0->TCD->DADDR = &adcbuffer_0[0];
dma0->TCD->DOFF = 2;
dma0->TCD->DLASTSGA = -(2 * DMA_BUF_SIZE);
dma0->TCD->BITER = DMA_BUF_SIZE;
dma0->TCD->CITER = DMA_BUF_SIZE;
dma0->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0);
dma0->disableOnCompletion();
dma0->interruptAtCompletion();
dma0->attachInterrupt(DMA_ISR);
dma1->begin(true); // allocate the DMA channel
dma1->TCD->SADDR = &ADC::SCA_CHANNEL_ID[0];
dma1->TCD->SOFF = 2; // source increment each transfer (n bytes)
dma1->TCD->ATTR = 0x101;
dma1->TCD->SLAST = - DMA_NUM_CH*2; // num ADC0 samples * 2
dma1->TCD->BITER = DMA_NUM_CH;
dma1->TCD->CITER = DMA_NUM_CH;
dma1->TCD->DADDR = &ADC0_SC1A;
dma1->TCD->DLASTSGA = 0;
dma1->TCD->NBYTES = 2;
dma1->TCD->DOFF = 0;
dma1->triggerAtTransfersOf(*dma0);
dma1->triggerAtCompletionOf(*dma0);
dma0->enable();
dma1->enable();
}
/*static*/void FASTRUN ADC::Scan_DMA() {
if (ADC::ready_)
{
ADC::ready_ = false;
/*
* collect results from adcbuffer_0; as things are, there's DMA_BUF_SIZE = 16 samples in the buffer.
*/
uint32_t value;
value = (adcbuffer_0[0] + adcbuffer_0[4] + adcbuffer_0[8] + adcbuffer_0[12]) >> 2; // / 4 = DMA_BUF_SIZE / DMA_NUM_CH
update<ADC_CHANNEL_1>(value);
value = (adcbuffer_0[1] + adcbuffer_0[5] + adcbuffer_0[9] + adcbuffer_0[13]) >> 2;
update<ADC_CHANNEL_2>(value);
value = (adcbuffer_0[2] + adcbuffer_0[6] + adcbuffer_0[10] + adcbuffer_0[14]) >> 2;
update<ADC_CHANNEL_3>(value);
value = (adcbuffer_0[3] + adcbuffer_0[7] + adcbuffer_0[11] + adcbuffer_0[15]) >> 2;
update<ADC_CHANNEL_4>(value);
/* restart */
dma0->enable();
}
}
/*static*/ void ADC::CalibratePitch(int32_t c2, int32_t c4) {
// This is the method used by the Mutable Instruments calibration and
// extrapolates from two octaves. I guess an alternative would be to get the
// lowest (-3v) and highest (+6v) and interpolate between them
// *vague handwaving*
if (c2 < c4) {
int32_t scale = (24 * 128 * 4096L) / (c4 - c2);
calibration_data_->pitch_cv_scale = scale;
}
}
}; // namespace OC