Allows Asynchronous reading from the analog pins to memory. Supports: K64F

Dependents:   MoppyController

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers asyncADC.cpp Source File

asyncADC.cpp

00001 #include "asyncADC.h"
00002 #include "pinmap.h"
00003 #include "PeripheralPins.h"
00004 #include "PeripheralNames.h"
00005 
00006 #define MAX_MOD             (0xFFFF)
00007 
00008 static bool _asyncReadActive = false;
00009 static edma_handle_t _handle;
00010 static edma_tcd_t _tcd;
00011 static asyncISR _callback = NULL;
00012 static uint32_t _bufSize;
00013 
00014 // MATH SUPPORT FUNCTIONS
00015 
00016 float f_abs(float f) {
00017     return f>=0.0f ? f : -f;
00018 }
00019 
00020 int i_abs(int i) {
00021     return i>=0 ? i : -i;
00022 }
00023 
00024 bool isPowerOfTwo(unsigned int x) {
00025     return (x != 0) && ((x & (x - 1)) == 0);
00026 }
00027 
00028 int log2(unsigned int i) {
00029     int l = 0;
00030     while(i>>=1) l++;
00031     return l;
00032 }
00033 
00034 //  CLOCK SUPPORT FUNTIONS
00035 
00036 void calcClockDiv(const uint32_t targetFrequency, int &divider, bool &useMult, int &modulus) {
00037     
00038     uint32_t busClock = CLOCK_GetFreq(kCLOCK_BusClk);
00039     
00040     if(targetFrequency > busClock) {
00041         useMult = false;
00042         divider = 0;
00043         modulus = 0;
00044     }
00045         
00046     useMult = false;
00047     divider = 0;
00048     modulus = (((busClock<<1)/targetFrequency+1)>>1)-1; // round(clock/freq)-1
00049     int bestErr = i_abs(targetFrequency - busClock/(modulus+1));
00050 
00051     for(int i=1; i<=7; i++) {
00052         int mod = (((busClock>>(i-1))/targetFrequency+1)>>1)-1;
00053         if(mod<0) mod=0;
00054         else if(mod > MAX_MOD) mod = MAX_MOD;
00055         int err = i_abs(targetFrequency - busClock/((1<<i) * (mod+1)));
00056 
00057         if(err<bestErr || (err==bestErr && mod<modulus)) {
00058             useMult = false;
00059             divider = i;
00060             modulus = mod;
00061         }
00062 
00063         mod = (((busClock>>(i-1))/(targetFrequency*5)+1)>>1)-1;
00064         if(mod<0) mod=0;
00065         else if(mod > MAX_MOD) mod = MAX_MOD;
00066         err = i_abs(targetFrequency - busClock/((1<<i) * (mod+1) * 5));
00067 
00068         if(err<bestErr || (err==bestErr && mod<modulus)) {
00069             useMult = true;
00070             divider = i;
00071             modulus = mod;
00072         }
00073 
00074     }
00075     for(int i=8; i<=11; i++) {
00076         int mod = (((busClock>>(i-1))/(targetFrequency*5)+1)>>1)-1;
00077         if(mod<0) mod=0;
00078         else if(mod > MAX_MOD) mod = MAX_MOD;
00079         int err = i_abs(targetFrequency - busClock/((1<<i) * (mod+1) * 5));
00080 
00081         if(err<bestErr || (err==bestErr && mod<modulus)) {
00082             useMult = true;
00083             divider = i;
00084             modulus = mod;
00085         }
00086     }
00087 
00088     if(modulus > MAX_MOD)
00089         modulus = MAX_MOD;
00090 }
00091 
00092 void calcClockDiv(const float targetFrequency, int &divider, bool &useMult, int &modulus) {
00093     
00094     uint32_t busClock = CLOCK_GetFreq(kCLOCK_BusClk);
00095     
00096     if(targetFrequency > busClock) {
00097         useMult = false;
00098         divider = 0;
00099         modulus = 0;
00100         return;
00101     }
00102     
00103     useMult = false;
00104     divider = 0;
00105     modulus = (int)((busClock/targetFrequency)+0.5f); // round(clock/freq)
00106     float bestErr = f_abs(targetFrequency - busClock/(modulus+1));
00107 
00108     for(int i=1; i<=7; i++) {
00109         int mod = (int)(((busClock>>i)/targetFrequency)+0.5f);
00110         if(mod<0) mod=0;
00111         else if(mod > MAX_MOD) mod = MAX_MOD;
00112         float err = f_abs(targetFrequency - busClock/(float)((1<<i) * (mod+1)));
00113 
00114         if(err<bestErr || (err==bestErr && mod<modulus)) {
00115             useMult = false;
00116             divider = i;
00117             modulus = mod;
00118         }
00119 
00120         mod = (int)(((busClock>>i)/(targetFrequency*5.0f))+0.5f);
00121         if(mod<0) mod=0;
00122         else if(mod > MAX_MOD) mod = MAX_MOD;
00123         err = f_abs(targetFrequency - busClock/(float)((1<<i) * (mod+1) * 5));
00124 
00125         if(err<bestErr || (err==bestErr && mod<modulus)) {
00126             useMult = true;
00127             divider = i;
00128             modulus = mod;
00129         }
00130 
00131     }
00132     for(int i=8; i<=11; i++) {
00133         int mod = (int)(((busClock>>i)/(targetFrequency*5.0f))+0.5f);
00134         if(mod<0) mod=0;
00135         else if(mod > MAX_MOD) mod = MAX_MOD;
00136         float err = f_abs(targetFrequency - busClock/(float)((1<<i) * (mod+1) * 5));
00137 
00138         if(err<bestErr || (err==bestErr && mod<modulus)) {
00139             useMult = true;
00140             divider = i;
00141             modulus = mod;
00142         }
00143     }
00144 }
00145 
00146 // INTERRUPT SERVICE ROUTINES
00147 
00148 void singleISR(edma_handle_t *_handle, void *userData, bool transferDone, uint32_t tcds) {
00149     PDB_Enable(PDB0, false);
00150     _asyncReadActive = false;
00151     if(_callback!=NULL) _callback(0, _bufSize);
00152 }
00153 
00154 void rollingISR(edma_handle_t *_handle, void *userData, bool transferDone, uint32_t tcds) {
00155     if(_callback!=NULL) {
00156         if(_handle->base->TCD[_handle->channel].CSR & DMA_CSR_DONE_MASK)
00157             _callback(_bufSize>>1, _bufSize-1);
00158         else
00159             _callback(0, (_bufSize>>1)-1);
00160     }
00161 }
00162 
00163 // INIT METHODS
00164 
00165 void prepareADC(uint8_t adcIndex, uint8_t channelGroup, uint8_t channel) {
00166     
00167     ADC_Type* adc;
00168     
00169     if(adcIndex) {
00170         SIM->SCGC3 |= SIM_SCGC3_ADC1_MASK;
00171         adc = ADC1;
00172     } else {
00173         SIM->SCGC6 |= SIM_SCGC6_ADC0_MASK;
00174         adc = ADC0;
00175     }
00176     
00177     adc16_config_t adc16ConfigStruct;
00178     
00179     ADC16_GetDefaultConfig(&adc16ConfigStruct);
00180     ADC16_Init(adc, &adc16ConfigStruct);
00181     ADC16_EnableHardwareTrigger(adc, true);
00182     ADC16_DoAutoCalibration(adc);
00183     ADC16_EnableHardwareTrigger(adc, true);
00184     ADC16_EnableDMA(adc, true);
00185     
00186     adc16_channel_config_t channelConfig;
00187     channelConfig.channelNumber = channel;
00188     channelConfig.enableInterruptOnConversionCompleted = false;
00189     channelConfig.enableDifferentialConversion = false;
00190     
00191     ADC16_SetChannelConfig(adc, channelGroup, &channelConfig);
00192 }
00193 
00194 status_t prepareDMA(uint16_t* destination, uint32_t destSize, int16_t dmaChannel, uint8_t adcIndex, uint8_t adcGroup, bool rollingMode) {
00195     if(dmaChannel<0) {
00196         //Find free DMA channel
00197         for(int i=0; i<16; i++) {
00198             if((DMA0->ERQ & (1U<<i)) == 0) {
00199                 dmaChannel=i;
00200                 break;
00201             }
00202         }
00203         if(dmaChannel<0)
00204             return kStatus_EDMA_Busy;
00205     } else {
00206         //check if requested DMA channel is free
00207         if((dmaChannel>15) || DMA0->ERQ & (1U<<dmaChannel))
00208             return kStatus_EDMA_Busy;
00209     }
00210     
00211     /* Configure DMAMUX */
00212     DMAMUX_Init(DMAMUX0);
00213     DMAMUX_SetSource(DMAMUX0, dmaChannel, 40+adcIndex); //Trigger 40 = ADC0, 41 = ADC1
00214     DMAMUX_EnableChannel(DMAMUX0, dmaChannel);
00215     
00216     edma_config_t edma_config;
00217     
00218     /* Init the eDMA driver */
00219     EDMA_GetDefaultConfig(&edma_config);
00220     
00221     EDMA_Init(DMA0, &edma_config);
00222     
00223     EDMA_CreateHandle(&_handle, DMA0, dmaChannel);
00224     EDMA_InstallTCDMemory(&_handle, NULL, 0); //make sure tcd memory pool is clear
00225     EDMA_SetCallback(&_handle, rollingMode ? rollingISR : singleISR, NULL);
00226     
00227     
00228     if(rollingMode) {
00229         EDMA_SetModulo(DMA0, dmaChannel, (edma_modulo_t)(log2(destSize)+1), kEDMA_ModuloDisable);
00230         _handle.base->TCD[dmaChannel].DLAST_SGA = -2*(int32_t)destSize;
00231     }
00232         
00233     edma_transfer_config_t transfer_config;
00234     EDMA_PrepareTransfer(&transfer_config, (void*)&((adcIndex?ADC1:ADC0)->R[adcGroup]), 2, destination, 2, 2, 2*destSize, kEDMA_PeripheralToMemory);
00235     status_t ret = EDMA_SubmitTransfer(&_handle, &transfer_config);
00236     
00237     if(rollingMode)
00238         EDMA_EnableAutoStopRequest(DMA0, dmaChannel, false); //must go after submit
00239     
00240     if(ret == kStatus_Success) {
00241         EDMA_EnableChannelInterrupts(DMA0, dmaChannel, rollingMode ? (kEDMA_MajorInterruptEnable|kEDMA_HalfInterruptEnable) : kEDMA_MajorInterruptEnable);
00242         EDMA_StartTransfer(&_handle);
00243     }
00244         
00245     return ret;
00246 }
00247 
00248 void preparePDB(int divider, bool useMult, int modulus, uint8_t adcIndex, uint8_t adcGroup) {
00249     SIM->SCGC6 |= SIM_SCGC6_PDB_MASK;
00250     
00251     pdb_config_t config;
00252     PDB_GetDefaultConfig(&config);
00253     if(useMult) {
00254         switch(divider) {
00255             case 1:
00256                 config.dividerMultiplicationFactor = kPDB_DividerMultiplicationFactor10;
00257                 divider = 0;
00258                 break;
00259             case 2:
00260                 config.dividerMultiplicationFactor = kPDB_DividerMultiplicationFactor20;
00261                 divider = 0;
00262                 break;
00263             default:
00264                 config.dividerMultiplicationFactor = kPDB_DividerMultiplicationFactor40;
00265                 divider-=3;
00266                 break;
00267         }
00268     } else {
00269         config.dividerMultiplicationFactor = kPDB_DividerMultiplicationFactor1;
00270     }
00271     
00272     switch(divider) {
00273         case 0:
00274             config.prescalerDivider = kPDB_PrescalerDivider1;
00275             break;
00276         case 1:
00277             config.prescalerDivider = kPDB_PrescalerDivider2;
00278             break;
00279         case 2:
00280             config.prescalerDivider = kPDB_PrescalerDivider4;
00281             break;
00282         case 3:
00283             config.prescalerDivider = kPDB_PrescalerDivider8;
00284             break;
00285         case 4:
00286             config.prescalerDivider = kPDB_PrescalerDivider16;
00287             break;
00288         case 5:
00289             config.prescalerDivider = kPDB_PrescalerDivider32;
00290             break;
00291         case 6:
00292             config.prescalerDivider = kPDB_PrescalerDivider64;
00293             break;
00294         default: 
00295             config.prescalerDivider = kPDB_PrescalerDivider128;
00296             break;
00297     }
00298     
00299     config.loadValueMode = kPDB_LoadValueImmediately;
00300     config.triggerInputSource = kPDB_TriggerSoftware;
00301     config.enableContinuousMode = true;
00302     PDB_Init(PDB0, &config);
00303     
00304     PDB_EnableDMA(PDB0, false); //set if PDB signals DMA directly instead of generating hardware interrupt
00305     PDB_DisableInterrupts(PDB0, kPDB_SequenceErrorInterruptEnable); //signal errors with interrupt?
00306     PDB_EnableInterrupts(PDB0, kPDB_DelayInterruptEnable); //send interrupt after delay?
00307     
00308     pdb_adc_pretrigger_config_t adcConfig;
00309     adcConfig.enablePreTriggerMask = 1; //enable pre-trigger 0
00310     adcConfig.enableOutputMask = 1; //enable output for pre-trigger 0
00311     adcConfig.enableBackToBackOperationMask = 0; //disable back-to-back for all pre-triggers
00312     PDB_SetADCPreTriggerConfig(PDB0, adcIndex, &adcConfig);
00313     
00314     PDB_SetADCPreTriggerDelayValue(PDB0, 0, 0, 0); //set delay on ADC0->R[0]
00315     PDB_SetADCPreTriggerDelayValue(PDB0, 0, 1, 0); //set delay on ADC0->R[1]
00316     PDB_SetADCPreTriggerDelayValue(PDB0, 1, 0, 0); //set delay on ADC1->R[0]
00317     PDB_SetADCPreTriggerDelayValue(PDB0, 1, 1, 0); //set delay on ADC1->R[1]
00318     
00319     PDB_SetModulusValue(PDB0, modulus); //actual frequency = clock frequency / ((modulus+1) * prescale divider * divider multiplier)
00320     
00321     PDB_Enable(PDB0, true); //do at end before load
00322     PDB_DoLoadValues(PDB0);
00323 }
00324 
00325 // API
00326 
00327 uint32_t approximateFrequency(uint32_t targetFrequency) {
00328     int divider, modulus; bool useMult;
00329     calcClockDiv(targetFrequency, divider, useMult, modulus);
00330     return (CLOCK_GetFreq(kCLOCK_BusClk)/((1<<divider) * (modulus+1) * (useMult ? 10 : 2))+1)>>1;
00331 }
00332 
00333 float approximateFrequency(float targetFrequency) {
00334     int divider, modulus; bool useMult;
00335     calcClockDiv(targetFrequency, divider, useMult, modulus);
00336     return CLOCK_GetFreq(kCLOCK_BusClk)/(float)((1<<divider) * (modulus+1) * (useMult ? 5 : 1));
00337 }
00338 
00339 async_error_t asyncAnalogToBuffer(PinName source, uint16_t* destination, uint32_t destSize, uint32_t sampleFrequency, asyncISR callback, int16_t dmaChannel) {
00340     if(_asyncReadActive)
00341         return E_ASYNC_ADC_ACTIVE;
00342     _asyncReadActive = true;
00343     
00344     if(destSize<=0) {
00345         return E_INVALID_BUFFER_SIZE; //Destination buffer size must be greater that 0
00346     }
00347     
00348     uint32_t adc = pinmap_peripheral(source, PinMap_ADC);
00349     
00350     if(adc == (ADCName)NC) {
00351         error("Pin doese not support Analog to Digital Conversion");
00352     }
00353     
00354     int adcIndex = adc >> ADC_INSTANCE_SHIFT, adcGroup = 0, adcChannel = adc & 0xF;
00355     prepareADC(adcIndex, adcGroup, adcChannel);
00356     
00357     switch(prepareDMA(destination, destSize, dmaChannel, adcIndex, adcGroup, false)) {
00358         case kStatus_EDMA_QueueFull:
00359         case kStatus_EDMA_Busy:
00360             return E_DMA_IN_USE;
00361         default:
00362             break;
00363     }
00364     
00365     int divider, modulus; bool useMult;
00366     calcClockDiv(sampleFrequency, divider, useMult, modulus);
00367     preparePDB(divider, useMult, modulus, adcIndex, adcGroup);
00368     
00369     _bufSize = destSize;
00370     _callback = callback;
00371     
00372     PDB_DoSoftwareTrigger(PDB0);
00373     
00374     return SUCCESS;
00375 }
00376 
00377 async_error_t asyncAnalogToCircularBuffer(PinName source, uint16_t* destination, uint32_t destSize, uint32_t sampleFrequency, asyncISR callback, int16_t dmaChannel) {
00378     if(_asyncReadActive)
00379         return E_ASYNC_ADC_ACTIVE;
00380     _asyncReadActive = true;
00381     
00382     if(destSize<=0 && !isPowerOfTwo(destSize)) {
00383         return E_INVALID_BUFFER_SIZE; //Destination buffer size must be greater that 0 and a power of 2
00384     }
00385     
00386     uint32_t adc = pinmap_peripheral(source, PinMap_ADC);
00387     
00388     if(adc == (ADCName)NC) {
00389         error("Pin doese not support Analog to Digital Conversion");
00390     }
00391     
00392     int adcIndex = adc >> ADC_INSTANCE_SHIFT, adcGroup = 0, adcChannel = adc & 0xF;
00393     prepareADC(adcIndex, adcGroup, adcChannel);
00394     
00395     switch(prepareDMA(destination, destSize, dmaChannel, adcIndex, adcGroup, true)) {
00396         case kStatus_EDMA_QueueFull:
00397         case kStatus_EDMA_Busy:
00398             return E_DMA_IN_USE;
00399         default:
00400             break;
00401     }
00402     
00403     int divider, modulus; bool useMult;
00404     calcClockDiv(sampleFrequency, divider, useMult, modulus);
00405     preparePDB(divider, useMult, modulus, adcIndex, adcGroup);
00406     
00407     _bufSize = destSize;
00408     _callback = callback;
00409     
00410     PDB_DoSoftwareTrigger(PDB0);
00411     
00412     return SUCCESS;
00413 }
00414 
00415 async_error_t asyncAnalogToBuffer_f(PinName source, uint16_t* destination, uint32_t destSize, float sampleFrequency, asyncISR callback, int16_t dmaChannel) {
00416     if(_asyncReadActive)
00417         return E_ASYNC_ADC_ACTIVE;
00418     _asyncReadActive = true;
00419     
00420     if(destSize<=0) {
00421         return E_INVALID_BUFFER_SIZE; //Destination buffer size must be greater that 0
00422     }
00423     
00424     uint32_t adc = pinmap_peripheral(source, PinMap_ADC);
00425     
00426     if(adc == (ADCName)NC) {
00427         error("Pin doese not support Analog to Digital Conversion");
00428     }
00429     
00430     int adcIndex = adc >> ADC_INSTANCE_SHIFT, adcGroup = 0, adcChannel = adc & 0xF;
00431     prepareADC(adcIndex, adcGroup, adcChannel);
00432     
00433     switch(prepareDMA(destination, destSize, dmaChannel, adcIndex, adcGroup, false)) {
00434         case kStatus_EDMA_QueueFull:
00435         case kStatus_EDMA_Busy:
00436             return E_DMA_IN_USE;
00437         default:
00438             break;
00439     }
00440     
00441     int divider, modulus; bool useMult;
00442     calcClockDiv(sampleFrequency, divider, useMult, modulus);
00443     preparePDB(divider, useMult, modulus, adcIndex, adcGroup);
00444     
00445     _bufSize = destSize;
00446     _callback = callback;
00447     
00448     PDB_DoSoftwareTrigger(PDB0);
00449     
00450     return SUCCESS;
00451 }
00452 
00453 async_error_t asyncAnalogToCircularBuffer_f(PinName source, uint16_t* destination, uint32_t destSize, float sampleFrequency, asyncISR callback, int16_t dmaChannel) {
00454     if(_asyncReadActive)
00455         return E_ASYNC_ADC_ACTIVE;
00456     _asyncReadActive = true;
00457     
00458     if(destSize<=0 && !isPowerOfTwo(destSize)) {
00459         return E_INVALID_BUFFER_SIZE; //Destination buffer size must be greater that 0 and a power of 2
00460     }
00461     
00462     uint32_t adc = pinmap_peripheral(source, PinMap_ADC);
00463     
00464     if(adc == (ADCName)NC) {
00465         error("Pin doese not support Analog to Digital Conversion");
00466     }
00467     
00468     int adcIndex = adc >> ADC_INSTANCE_SHIFT, adcGroup = 0, adcChannel = adc & 0xF;
00469     prepareADC(adcIndex, adcGroup, adcChannel);
00470     
00471     switch(prepareDMA(destination, destSize, dmaChannel, adcIndex, adcGroup, true)) {
00472         case kStatus_EDMA_QueueFull:
00473         case kStatus_EDMA_Busy:
00474             return E_DMA_IN_USE;
00475         default:
00476             break;
00477     }
00478     
00479     int divider, modulus; bool useMult;
00480     calcClockDiv(sampleFrequency, divider, useMult, modulus);
00481     preparePDB(divider, useMult, modulus, adcIndex, adcGroup);
00482     
00483     _bufSize = destSize;
00484     _callback = callback;
00485     
00486     PDB_DoSoftwareTrigger(PDB0);
00487     
00488     return SUCCESS;
00489 }
00490 
00491 void terminateAsyncRead() {
00492     PDB_Enable(PDB0, false);
00493     EDMA_AbortTransfer(&_handle);
00494     _asyncReadActive = false;
00495 }
00496