EFM32 Wonder Gecko Software Documentation  efm32wg-doc-5.1.2
mic.c
Go to the documentation of this file.
1 /***************************************************************************/
16 #include "em_device.h"
17 #include "em_adc.h"
18 #include "em_emu.h"
19 #include "em_cmu.h"
20 #include "em_prs.h"
21 #include "em_letimer.h"
22 #include "dmadrv.h"
23 
24 #include "thunderboard/util.h"
25 #include "thunderboard/board.h"
26 #include "thunderboard/mic.h"
27 
28 #include <math.h>
29 
30 /***************************************************************************/
38 /***************************************************************************/
44 static unsigned int dmadrvChannelId;
45 static uint16_t *sampleBuffer;
46 static size_t sampleBufferLen;
47 static volatile bool sampleBufferReady;
48 static uint32_t sampleCount;
50 static volatile bool dmaBusy;
51 static float soundLevel;
52 static uint32_t cmuClkoutSel1;
58 /***************************************************************************/
66 static bool dmaCompleteCallback( unsigned int channel, unsigned int sequenceNo, void *userParam );
67 static void adcEnable( bool enable );
68 
71 /***************************************************************************/
87 uint32_t MIC_init( uint32_t fs, uint16_t *buffer, size_t len )
88 {
89 
90  uint32_t status;
91  uint32_t auxhfrcoFreq;
92 
95 
98  CMU ->ADCCTRL = CMU_ADCCTRL_ADC0CLKSEL_AUXHFRCO;
99 
100  auxhfrcoFreq = CMU_ClockFreqGet( cmuClock_AUX );
101 
102  /* Enable microphone circuit and wait for it to settle properly */
103  BOARD_micEnable( true );
104 
105  /* Setup ADC */
106  adcInit.em2ClockConfig = adcEm2ClockOnDemand;
107  adcInit.timebase = ADC_TimebaseCalc( auxhfrcoFreq );
108  adcInit.prescale = ADC_PrescaleCalc( MIC_ADC_CLOCK_FREQ, auxhfrcoFreq );
109  ADC_Init( ADC0, &adcInit );
110 
111  /* Setup ADC channel */
112  adcInitScan.reference = adcRef2V5;
113  adcInitScan.prsEnable = true;
114  adcInitScan.prsSel = MIC_CONFIG_ADC_PRSSEL;
115  adcInitScan.scanDmaEm2Wu = true;
116 
117  /* Add microphone scan channel */
118  ADC_ScanInputClear( &adcInitScan );
119  ADC_ScanSingleEndedInputAdd( &adcInitScan, adcScanInputGroup0, MIC_CONFIG_ADC_POSSEL );
120 
121  ADC_InitScan( ADC0, &adcInitScan );
122 
123  /* Setup PRS channel to trigger ADC */
124  PRS_SourceAsyncSignalSet( MIC_CONFIG_PRS_CH, MIC_CONFIG_PRS_SOURCE, MIC_CONFIG_PRS_SIGNAL );
125 
126 #if MIC_CONFIG_USE_LETIMER
127 
128  /* Setup LETIMER to trigger ADC in EM2 */
130  uint16_t timerTop;
131  uint32_t timerFreq;
132 
134  CMU_ClockEnable( MIC_CONFIG_TIMER_CMU_CLK, true );
135  timerFreq = CMU_ClockFreqGet( MIC_CONFIG_TIMER_CMU_CLK );
136 
137  letimerInit.comp0Top = true;
138  letimerInit.enable = false;
139  letimerInit.ufoa0 = letimerUFOAPulse;
140 
141  LETIMER_Init( MIC_CONFIG_TIMER, &letimerInit );
142  LETIMER_RepeatSet( MIC_CONFIG_TIMER, 0, 1 );
143 
144  timerTop = timerFreq / fs - 1;
145  fs = timerFreq / (timerTop + 1);
146  LETIMER_CompareSet( MIC_CONFIG_TIMER, 0, timerTop );
147 
148 #else
149 
150  if( fs == 1000 ) {
151  /* Use ULFRCO which runs at 1 kHz */
152  cmuClkoutSel1 = CMU_CTRL_CLKOUTSEL1_ULFRCOQ;
153  }
154  else {
155  /* Use LFXO clock which runs at 32.768 kHz */
156  cmuClkoutSel1 = CMU_CTRL_CLKOUTSEL1_LFXOQ;
157  fs = 32768;
158  }
159 
160 #endif
161 
162  /* Setup DMA driver to move samples from ADC to memory */
163  DMADRV_Init();
164  status = DMADRV_AllocateChannel( &dmadrvChannelId, NULL );
165  if( status != ECODE_EMDRV_DMADRV_OK ) {
166  return status;
167  }
168 
169  soundLevel = 35.0;
170  sampleBufferReady = false;
171  sampleBuffer = buffer;
172  sampleBufferLen = len;
173  dmaBusy = false;
174 
175  return fs;
176 
177 }
178 
179 /***************************************************************************/
186 void MIC_deInit( void )
187 {
188 
189  /* Stop sampling */
190  adcEnable( false );
191 
192  /* Clear PRS channel configuration */
193  PRS_SourceAsyncSignalSet( MIC_CONFIG_PRS_CH, 0, 0 );
194 
195  /* Power down microphone */
196  BOARD_micEnable( false );
197 
198  DMADRV_FreeChannel( dmadrvChannelId );
199 
200  return;
201 
202 }
203 
204 /***************************************************************************/
214 void MIC_start( uint32_t nSamples )
215 {
216 
217  if( nSamples > sampleBufferLen ) {
218  sampleCount = sampleBufferLen;
219  }
220  else {
221  sampleCount = nSamples;
222  }
223 
224  if( !dmaBusy ) {
225 
226  /* Configure DMA */
227  DMADRV_PeripheralMemory( dmadrvChannelId, // channelId
228  dmadrvPeripheralSignal_ADC0_SCAN, // peripheralSignal
229  (void *) sampleBuffer, // *dst
230  (void *) &( ADC0 ->SCANDATA ), // *src
231  true, // dstInc
232  sampleCount, // len
233  dmadrvDataSize2, // size
234  dmaCompleteCallback, // callback
235  NULL ); // *cbUserParam
236 
237  adcEnable( true );
238  dmaBusy = true;
239  sampleBufferReady = false;
240  }
241 
242  return;
243 
244 }
245 
246 /***************************************************************************/
253 uint16_t *MIC_getSampleBuffer( void )
254 {
255 
256  return sampleBuffer;
257 
258 }
259 
260 /***************************************************************************/
267 float MIC_getMean( void )
268 {
269 
270  size_t i;
271  float mean;
272 
273  mean = 0.0f;
274 
275  for( i = 0; i < sampleCount; i++ ) {
276  mean += (float) sampleBuffer[i];
277  }
278  mean = mean / (float) sampleCount;
279 
280  return mean;
281 
282 }
283 
284 /***************************************************************************/
294 float MIC_getSoundLevel( float *var )
295 {
296 
297  float sample;
298  float mean;
299  float power;
300  size_t i;
301 
302  if( sampleBufferReady ) {
303 
304  /* Estimate mean */
305  mean = MIC_getMean();
306 
307  /* Estimate variance */
308  power = 0;
309  for( i = 0; i < sampleCount; i++ ) {
310  sample = ( (float) sampleBuffer[i] - mean ) / 2047.5;
311  power += sample * sample;
312  }
313  power = power / (float) sampleCount;
314 
315  /* Convert to decibel*/
316  soundLevel = 10.0f * log10f( power );
317  //soundLevel = soundLevel + 97.94f;
318  sampleBufferReady = false;
319  }
320 
321  if( var != NULL ) {
322  *var = power;
323  }
324 
325  return soundLevel;
326 
327 }
328 
329 /***************************************************************************/
336 bool MIC_isBusy( void )
337 {
338 
339  return dmaBusy;
340 
341 }
342 
345 /***************************************************************************/
361 static bool dmaCompleteCallback( unsigned int channel, unsigned int sequenceNo, void *userParam )
362 {
363 
364  /* Stop ADC samples */
365  adcEnable( false );
366 
367  sampleBufferReady = true;
368  dmaBusy = false;
369 
370  return false;
371 
372 }
373 
374 /***************************************************************************/
384 static void adcEnable( bool enable )
385 {
386 
387 #if MIC_CONFIG_USE_LETIMER
388  /* Enable LETIMER to trigger ADC */
389  LETIMER_Enable( MIC_CONFIG_TIMER, enable );
390 #else
391  if( enable ) {
392  /* Set up CLKOUT1 to start sampling */
393  CMU->CTRL |= cmuClkoutSel1;
394  }
395  else {
396  CMU->CTRL &= ~cmuClkoutSel1;
397  }
398 #endif
399 
400  return;
401 
402 }
403 
Clock management unit (CMU) API.
void CMU_ClockSelectSet(CMU_Clock_TypeDef clock, CMU_Select_TypeDef ref)
Select reference clock/oscillator used for a clock branch.
Definition: em_cmu.c:2521
#define MIC_ADC_CLOCK_FREQ
Definition: mic.h:36
void LETIMER_Enable(LETIMER_TypeDef *letimer, bool enable)
Start/stop LETIMER.
Definition: em_letimer.c:231
DMADRV API definition.
#define ADC_INITSCAN_DEFAULT
Definition: em_adc.h:946
void LETIMER_Init(LETIMER_TypeDef *letimer, const LETIMER_Init_TypeDef *init)
Initialize LETIMER.
Definition: em_letimer.c:327
Ecode_t DMADRV_PeripheralMemory(unsigned int channelId, DMADRV_PeripheralSignal_t peripheralSignal, void *dst, void *src, bool dstInc, int len, DMADRV_DataSize_t size, DMADRV_Callback_t callback, void *cbUserParam)
Start a peripheral to memory DMA transfer.
Definition: dmadrv.c:539
static void adcInit(void)
Initializes the A/D converter.
Definition: util_supply.c:64
#define CMU
uint8_t timebase
Definition: em_adc.h:819
Trig on ADC0_SCAN.
Definition: dmadrv.h:94
Halfword.
Definition: dmadrv.h:298
Low Energy Timer (LETIMER) peripheral API.
CMSIS Cortex-M Peripheral Access Layer for Silicon Laboratories microcontroller devices.
#define ADC_INIT_DEFAULT
Definition: em_adc.h:836
void LETIMER_RepeatSet(LETIMER_TypeDef *letimer, unsigned int rep, uint32_t value)
Set LETIMER repeat counter register value.
Definition: em_letimer.c:464
LETIMER_UFOA_TypeDef ufoa0
Definition: em_letimer.h:108
uint32_t BOARD_micEnable(bool enable)
Enables or disables the MEMS microphone.
Definition: board.c:536
#define LETIMER_INIT_DEFAULT
Definition: em_letimer.h:115
void ADC_Init(ADC_TypeDef *adc, const ADC_Init_TypeDef *init)
Initialize ADC.
Definition: em_adc.c:371
ADC_PRSSEL_TypeDef prsSel
Definition: em_adc.h:888
uint8_t ADC_TimebaseCalc(uint32_t hfperFreq)
Calculate timebase value in order to get a timebase providing at least 1us.
Definition: em_adc.c:1118
Utility Functions for the Thunderboard Sense.
void LETIMER_CompareSet(LETIMER_TypeDef *letimer, unsigned int comp, uint32_t value)
Set LETIMER compare register value.
Definition: em_letimer.c:176
void CMU_ClockEnable(CMU_Clock_TypeDef clock, bool enable)
Enable/disable a clock.
Definition: em_cmu.c:1453
Ecode_t DMADRV_Init(void)
Initialize DMADRV.
Definition: dmadrv.c:262
uint8_t prescale
Definition: em_adc.h:822
#define ECODE_EMDRV_DMADRV_OK
Success return value.
Definition: dmadrv.h:50
uint16_t * MIC_getSampleBuffer(void)
Gets the sample buffer.
Definition: mic.c:253
Driver for the SPV1840LR5H-B MEMS Microphone.
bool MIC_isBusy(void)
Checks if the microphone is in use.
Definition: mic.c:336
Energy management unit (EMU) peripheral API.
Ecode_t DMADRV_AllocateChannel(unsigned int *channelId, void *capabilities)
Allocate (reserve) a DMA channel.
Definition: dmadrv.c:131
uint8_t ADC_PrescaleCalc(uint32_t adcFreq, uint32_t hfperFreq)
Calculate prescaler value used to determine ADC clock.
Definition: em_adc.c:1025
void ADC_InitScan(ADC_TypeDef *adc, const ADC_InitScan_TypeDef *init)
Initialize ADC scan sequence.
Definition: em_adc.c:710
void MIC_deInit(void)
Powers down the MEMS microphone stops the ADC and frees up the DMA channel.
Definition: mic.c:186
uint32_t MIC_init(uint32_t fs, uint16_t *buffer, size_t len)
Initializes MEMS microphone and sets up the DMA, ADC and clocking.
Definition: mic.c:87
#define CMU_CTRL_CLKOUTSEL1_LFXOQ
Definition: efm32wg_cmu.h:209
Analog to Digital Converter (ADC) peripheral API.
float MIC_getSoundLevel(float *var)
Calculates the sound level.
Definition: mic.c:294
uint32_t CMU_ClockFreqGet(CMU_Clock_TypeDef clock)
Get clock frequency for a clock point.
Definition: em_cmu.c:1550
ADC_Ref_TypeDef reference
Definition: em_adc.h:897
void PRS_SourceAsyncSignalSet(unsigned int ch, uint32_t source, uint32_t signal)
Set source and asynchronous signal to be used for a channel.
Definition: em_prs.c:115
BOARD module header file.
float MIC_getMean(void)
Calculates the average value of the samples in the buffer.
Definition: mic.c:267
Peripheral Reflex System (PRS) peripheral API.
Ecode_t DMADRV_FreeChannel(unsigned int channelId)
Free an allocate (reserved) DMA channel.
Definition: dmadrv.c:225
void MIC_start(uint32_t nSamples)
Starts taking samples using DMA from the microphone.
Definition: mic.c:214
#define ADC0