Allows Asynchronous reading from the analog pins to memory. Supports: K64F
Embed:
(wiki syntax)
Show/hide line numbers
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 ÷r, 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 ÷r, 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
Generated on Wed Jul 13 2022 19:36:48 by 1.7.2