This post explores interfacing a PDM microphone with an NXP LPC810 ARM CORTEX-M0 microprocessor, taking advantage of its existing peripherals to do the heavy lifting at the high bit rate, allowing there to be enough power to implement a practical calculation based on the audio input.
This is part of a series of articles on the general subject of audio signal processing from air to information. Previous installments include:
- Introduction to MEMS Microphones
- Using a PDM Microphone
- PDM in a Tiny CPU
- Resolution of audio recovered from a PDM data stream
- PDM in ATTiny85 Source Code
- SPL is Logarithmic
A PDM Microphone Stunt
As a stunt, we will implement the signal processing chain for a PDM microphone in an LPC810. This is a 32-bit ARM CORTEX-M0 CPU in an 8-pin DIP package priced under $3.50 in single pieces.
It has family members with higher pin counts and with additional FLASH and RAM, but this model is sufficient for this demonstration, and easy to use play with in a solderless breadboard due to its classic 8-pin DIP package.
The LPC810 has 1K of SRAM and 4K of FLASH on the die, runs the CPU core at 30 MHz from a 1.8V to 3.6V supply. It includes an SPI interface which we will (mis-)use to capture the PDM bitstream.
We will run our LPC810 at 3.3V, with a 30MHz core clock.
Obviously, in a system of this scale we are not planning to record the audio in any form. Instead, we will measure its “loudness”, and output that measure as a PWM signal which can be easily converted to an analog level.
Other simple outputs that could be computed would include comparing the loudness to a threshold, perhaps with hysteresis and outputing a simple digital result.
For the purpose of this demo, we will connect the PWM output directly to a classic analog voltmeter so that the needle could be marked in dB SPL.
Parts is parts
There are only two significant parts to this demonstration, aside from the usual collection of passive components and hookup wire.
LPC810
The LCP810 used here was purchased along with a suitable USB serial adapter, 3.3V regulator, and a handful of useful passive components as a starter kit from Adafruit.
This is the LPC810M021FN8 in the friendly and classic DIP-8 package. Get your first one from Adafruit for the handy accessories, but get them in quantity from Digi-Key or your favorite distributor.
Oddly, all of the family members from NXP that are available in smaller packages also have higher pin counts, more FLASH and more RAM. I am slightly surprised that this exact die isn’t also available in a much smaller 8-pin package.
Knowles Microphone
We purchased a Knowles SPM0437HD4H-B microphone from Digi-Key, and made a simple breakout board for it by hand with copper-clad perfboard as described in a previous post.
Wiring
Power will be supplied via the USB serial adapter that allows the firmware to be reflashed using the ISP boot loader included in the LPC810. The adapter provides a connection to USB Vbus, which is nominally 5V (and limited to 100 mA unless the USB device negotiated for more, and bounded at 500 mA regardless).
Since both the LPC810 and microphone are 3.3V devices, a regulator is required. The kit included a Microchip MCP1700-3.3V regulator and bypass caps, so that is what we use.
Using a solderless breadboard, we wire the LPC810, power supply, and microphone as follows:
LPC810 Pins Usage
1 ~RESET Reset button
2 TXD White wire to serial
3 SCLK Mic clock from SPI
4 P2 PWM Analog SPL Output
5 MISO/~ISP Mic data input to SPI
6 Vdd 3V3
7 Vss GND
8 RXD Green wire to serial
MCP1700-3.3
1 GND GND Black wire to serial
2 Vin 5V (Vbus) Red wire to serial
3 Vout 3V3
MIC Breakout
Wht/Blu Mic clock
Wht/Grn Mic data
Blu 3V3
Grn GND
USB Serial Cable (PL2303HX based, TTL levels)
Blk GND
Red 5V USB Vbus
Wht LPC810 Txd, PC Rxd
Grn LPC810 Rxd, PC Txd
Include bypass caps from both 5V and 3V3 to GND near the MCP1700.
Connect an analog meter from the PWM pin to ground. For best results, a simple 1 pole low-pass filter made from a 680 ohm resistor and 10 µF capacitor will block all the PWM carrier leaving just the measured SPL analog level.
Handling PDM
Microphone clock and PDM Data
The SPI peripheral in master mode will generate a configured clock as long as the master has data to send. We just need to map SPI0_SCK
to an external pin. For the demo, I chose pin 3.
The microphone data drives SPI0_MISO
, which I mapped to pin 5. That pin is also the boot-time selector that chooses the ISP boot loader if held low at reset. Since SPI0_SCK
is not operating at reset, the microphone is not driving its data output at all, and the CPU correctly interprets that as a normal boot. The microphone data is clocked into a 16-bit shift register in the SPI0 peripheral.
Note that we don’t need to map SPI0_MOSI
to any pins at all. We may be sending data, but we don’t need it. It could be used as a kind of debug sonar if it were mapped. Various distinct status could be coded on it, and sampled with a logic analyzer or scope.
We configure the SPI clock for 1 MHz by dividing by 30.
The SPI receiver signals that data is available after 16 clocks. The data is then passed into the CIC filter 8 bits at a time to reconstruct lower sample rate, higher bit depth PCM samples.
CIC Filter Implementation
Our goal is to downsample the 1 MHz PDM bit stream input by a factor of 128, producing PCM samples at 7812.5 Hz. We do this in two stages. The first CIC stage downsamples by 8. The second by 16.
This is essentially the same filter model found in the ATTiny85 stunt. See that post and the following posts for explanation the gory details.
The simplest approach to a straight SPL meter in this platform does not use interrupts at all. Rather, it polls for the SPI to be ready, and does all the needed arithmetic on each block of PDM bits.
Testing shows that for SPL based on mean absolute sample value over reasonably sized windows and using the same log base 2 we used in the ATTiny85, it all works smoothly.
Software Tools
LPCXpresso
The LPC8xx family is well supported by the free development environment provided by NXP. Their LPCXpresso package provides a customized Eclipse IDE along with a GCC cross tool chain targeting ARM in a single installation. You do need to register with Code Red to download, but the download is free.
A good place to start for code samples and a nice tutorial for the LPC810 specifically is the help system at Adafruit. The tutorial covers collecting the tools, laying out the CPU, regulator, LED, and serial cable, and getting a simple blinky sample firmware to compile, load, and run.
If you need and want a complete IDE for NXP ARM devices, LPCXpresso is clearly the tool to have. However, you won’t be able to debug in the LPC8xx without a compatible JTAG or SWD interface. A compatible interface can be had as part of the eval board for the LPC812, priced on the street1 at under $27.
Of course, debugging in the LPC810 requires mapping SWDIO
and SWCLK
to specific pins in addition to ~RESET
. That only leaves three free pins for in-circuit functions (two of which are the UART needed for using the boot loader for program loading), so we haven’t attempted to leave the debugger available on this breadboard.
One nice feature of the LPCXpresso IDE is the custom state machine editor provided by Code Red. It can generate configuration for the LPC810’s SCT
peripheral (State Configurable Timer, a hardware implemented finite state machine that is tightly coupled with two timers/counters). I didn’t use it for this as a simple PWM output is easy to directly configure from C code, but it could be useful for more complex applications.
Loading Firmware
Loading firmware into the LPC8xx family can be done over the hardware debug channel (single wire debug, or SWD), or over an async serial connection by exercising a special version of the factory boot loader.
The serial ISP is particularly handy if you already plan to use the serial port with a PC. A simple TTL level USB serial adapter cable can power the LPC810 for both programming and running.
There seem to be two standard options for ISP programming from a PC, FlashMagic and lpc2isp
, ignoring the third option of rolling your own programming tool based on data sheets and app notes from NXP.
FlashMagic is a free version of a commercial product. It is free for development use, costs money to license for use in a production line or field service setting, and can not be distributed independently. I used FlashMagic for playing with the LPC810 and it works slickly and does what I need done.
lpc2isp
is an open source command-line utility, suitable for use in either a development or production environment. It may require some fussing to get it built right and to use it. I expect I will post about that once I’ve tried to use it.
Either way, with only 4 kB of FLASH memory to load, a serial port loader is a perfectly sane and sensible way to go.
Building the Demo
This demo is my first LCP810 project, so I built it stages. I took the advice of the Adafruit help system and started with a known to work LED Blinky project to prove that the development tools were all working, then modified it to add the microphone handling and SPL calculation.
Get the Blinky Running
Work through the whole Adafruit tutorial. At the end, you’ll have a build environment and hardware running firmware you compiled and flashed.
Modify it to listen to the mic
Connect the mic to 3V3
, GND
, SCLK
, and MISO
. The mic will default to left channel timing, and that happens to match the timing that the provided framework code assumes for the SPI
port.
Adjust the pin configuration in configurePins()
found in main.c
. I simply replaced its provided content with this. The magic numbers come from playing with NXP’s web-based pin mux tool. I’ve retained the LED output which we will use for PWM, and mapped SCK
and MISO
for the mic. I’ve also assumed that SWD is impractical, and simply dropped the #if
that supported it in the sample code.
void configurePins() { /* Enable SWM clock */ // LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 7); // this is already done in SystemInit() /* Pin setup from web-based mux tool http://www.lpcware.com/content/tools/lpc-initializer ------------------------------------------------ 1 PIO0_5 = RESET 2 PIO0_4 = U0_TXD 3 PIO0_3 = SPI0_SCK Mic clock 1MHz 4 PIO0_2 = CTOUT_0 LED 5 PIO0_1 = SPI0_MISO Mic data, boot flash select 6 Vdd 7 Vss 8 PIO0_0 = U0_RXD ------------------------------------------------ NOTE: SWD is disabled to free GPIO pins! ------------------------------------------------ */ /* Pin Assign 8 bit Configuration */ /* U0_TXD */ /* U0_RXD */ LPC_SWM->PINASSIGN0 = 0xffff0004UL; /* SPI0_SCK */ LPC_SWM->PINASSIGN3 = 0x03ffffffUL; /* SPI0_MISO */ LPC_SWM->PINASSIGN4 = 0xffff01ffUL; /* CTOUT_0 */ LPC_SWM->PINASSIGN6 = 0x02ffffffUL; /* Pin Assign 1 bit Configuration */ /* RESET */ LPC_SWM->PINENABLE0 = 0xffffffbfUL; }
Declare some variables and configure the hardware at the top of main()
:
int main(void) { uint8_t scount = CIC2_R/2; // 2nd CIC decimation counter uint8_t pcount = WINDOWSPERPRINT; // SPLs to wait between UART prints uint32_t n = 0; // PCM samples in SPL window uint32_t sabs = 0; // sum of absolute valued samples int32_t sum = 0; // sum of signed samples int avg = 0 ; // mean of previous SPL window /* Initialise the GPIO block */ gpioInit(); /* Initialise the UART0 block for printf output */ uart0Init(115200); /* Configure the multi-rate timer for 1ms ticks */ mrtInit(__SYSTEM_CLOCK/1000); /* Configure SPI0 for PDM microphone at 1 MHz */ spiInit(LPC_SPI0, 29, 0); /* Configure PWM based on the SCT */ pwmInit(); /* Configure the switch matrix (setup pins for UART0 and GPIO) */ configurePins();
Add code for a read-only transfer of 16 bits to the framework’s spi.h
:
uint16_t spiRead16 (LPC_SPI_TypeDef *SPIx);
and spi.c
. This function keeps sending 16-bit long words as long as the transmitter is ready, and returns as soon as the receiver is ready. This keeps the clock output running, while polling for the next available mic data.
/* receive 16 bits from the SPI bus */ uint16_t spiRead16(LPC_SPI_TypeDef *SPIx) { if (SPIx->STAT & SPI_STAT_TXRDY) SPIx->TXDATCTL = SPI_TXDATCTL_FSIZE(16-1) | 0; while ( (SPIx->STAT & SPI_STAT_RXRDY) == 0 ) if (SPIx->STAT & SPI_STAT_TXRDY) SPIx->TXDATCTL = SPI_TXDATCTL_FSIZE(16-1) | 0; return SPIx->RXDAT; }
We also need the implementation of lg2()
and the first stage CIC table in main.c
:
/** Fixed point log base 2 of an unsigned 32 bit integer. * * Return an unsigned 8-bit value ranging from 0 to 255 which includes * 3 bits of fraction. Except for some special case returns for inputs * less than 8, the fraction bits are exactly the next three * significan bits of the input value below its most significant set * bit. The integer part is the bit number of the most significant set * bit. The output values approximate the log curve to within a * few percent over the whole range. * * \param v Value to compute logarithm of. * * \return Returns 8*log2(v). Returns 0 if v is 0. */ uint8_t lg2(uint32_t v) { static const uint8_t log2table[] = { 0,0,8,13,16,19,21,22 }; if (!(v&~7)) { return log2table[v]; } uint8_t r = 3; // r will be 8*log2(v) while (v & ~0xF) { v>>=1; ++r; } return (r<<3) | (v&0x7); } #define WINDOWSIZE 781 // Fs=7812.5 Hz #define PWM_OFFSET 75 // nominally == 8*log2(WINDOWSIZE) #define WINDOWSPERPRINT 20 const int8_t pdmsum8[256] = { # define S(n) (2*(n)-8) # define B2(n) S(n), S(n+1), S(n+1), S(n+2) # define B4(n) B2(n), B2(n+1), B2(n+1), B2(n+2) # define B6(n) B4(n), B4(n+1), B4(n+1), B4(n+2) B6(0), B6(1), B6(1), B6(2) };
The CIC filters need some global variables for their state in main.c
:
typedef int16_t CICREG; CICREG s2_sum1 = 0; CICREG s2_comb1_1 = 0; CICREG s2_comb1_2 = 0; CICREG s2_sum2 = 0; CICREG s2_comb2_1 = 0; CICREG s2_comb2_2 = 0; #define CIC2_R 16
Finally, the bulk of main()
becomes this infinite loop, that polls for 16 PDM bits to be received, then processes them. We've left the blink's configuration of the UART in place, and freely use it to announce values on the serial port occasionally. Since we haven't added any interrupt handling for the UART, any strings printed will interrupt the sample collection process, including the microphone's clock. As long as those interruptions are short enough, the mic doesn't seem to care and we don't care much either since we are only computing SPL and a few noisy PDM bits cannot disrupt that calculation by enough to matter.
printf("LPC810 PDM Mic Demo!rn"); while(1) { uint16_t pdm = spiRead16(LPC_SPI0);
As in the ATTiny85 code, the first stage of PDM to PCM is to count the set bits in each of the captured bytes, with each bit rescaled to a signed +/- 1 range. This is the equivalent of an order-1 CIC filter with R=8, M=1, N=1. The bit growth of the output of this filter is then or 3, for 4 total significant bits out from the single bit in. The numeric range at this stage is -8 to 8, so it can't fit in a 4-bit 2's complement variable, but that is moot.
The actual counting is done by lookup in the pdmsum8[]
table, which is done for each of the two bytes in the 16 bit SPI receive register. Then we feed the 4 bit result to a second CIC with N=2, R=16, M=2 which has bit growth of 10, for a total of 14 significant bits out. The counter scount
is used to implement the decimation, which must be by a multiple of 2 (which was also a requirement of the CIC filter) since scount
is actually counting byte pairs here.
s2_sum1 += pdmsum8[pdm&0xff] ; s2_sum2 += s2_sum1; s2_sum1 += pdmsum8[pdm>>8] ; s2_sum2 += s2_sum1; if (!--scount) { CICREG Rout2 = s2_sum2; CICREG stage1, stage2; scount = CIC2_R/2; stage1 = Rout2 - s2_comb1_2; s2_comb1_2 = s2_comb1_1; s2_comb1_1 = Rout2; stage2 = stage1 - s2_comb2_2; s2_comb2_2 = s2_comb2_1; s2_comb2_1 = stage1;
The finished PCM sample is in the variable stage2
. We add it to the accumulators to implement SPL via mean of absolute values, also keeping a signed mean value to use to eliminate any DC offset. (Eliminating the DC offset is actually required because the mic is documented to have a 6% of full scale offset which swamps anything but really loud noises if not removed.) The samples in the SPL window are counted by n
.
sum += stage2; sabs += (stage2-avg) > 0 ? (stage2-avg) : -(stage2-avg); ++n;
After a window's worth of samples have been accumulated, we finish the SPL calculation and set the PWM output unless this is the first window after we've printed a result according to pcount
which runs from WINDOWSPERPRINT
down to 0
if (n == WINDOWSIZE) { int spl; spl = (lg2(sabs) - PWM_OFFSET) * 2; // set the PWM unless we just interrupted the mic's data for printing if (pcount != WINDOWSPERPRINT) pwmSet(spl); avg = sum / WINDOWSIZE; sum = 0; sabs = 0; n = 0; if (!--pcount) { pcount = WINDOWSPERPRINT; printf("%d %drn", spl, avg); } } } } // bottom of while(1) loop } // end of main()
The PWM output is implemented with the state controlled timer (SCT
) peripheral. It is a general purpose finite state machine engine that can control one 32-bit or two 16-bit timers. We use it for a simple PWM output by setting it to divide the core clock by 30000 to get a base rate of 1 kHz. We use one SCT event to turn on the output, and a second to turn it off. The pulse width is controlled by the number of counts between the on and off events, and if phase mattered it could be controlled by moving the position of both events together.
The implementation is in pwm.c
:
#include "pwm.h" #include "LPC8xx.h" #define SCT_CONFIG_UNIFY (1<<0) #define SCT_CONFIG_NORELOAD_L (1<<7) #define SCT_CONFIG_NORELOAD_H (1<<8) #define SCT_CONFIG_AUTOLIMIT_L (1<<17) #define SCT_CONFIG_AUTOLIMIT_H (1<<18) #define SCT_CTRL_DOWN_L (1<<0) #define SCT_CTRL_STOP_L (1<<1) #define SCT_CTRL_HALT_L (1<<2) #define SCT_CTRL_CLRCTR_L (1<<3) #define SCT_CTRL_BIDIR_L (1<<4) #define SCT_CTRL_PRE_L(n) (((n)&0xff)<<5) #define SCT_CTRL_DOWN_H (1<<16) #define SCT_CTRL_STOP_H (1<<17) #define SCT_CTRL_HALT_H (1<<18) #define SCT_CTRL_CLRCTR_H (1<<19) #define SCT_CTRL_BIDIR_H (1<<20) #define SCT_CTRL_PRE_H(n) (((n)&0xff)<<21) #define SCT_EVCTRL_MATCHSEL(n) (((n)&0x0f)<<0) #define SCT_EVCTRL_COMBMODE(n) (((n)&0x03)<SYSAHBCLKCTRL |= (1 <PRESETCTRL &= ~(1 <PRESETCTRL |= (1 <CTRL_U = (0 | SCT_CTRL_HALT_L ); LPC_SCT->CONFIG = (0 | SCT_CONFIG_UNIFY | SCT_CONFIG_AUTOLIMIT_L ); LPC_SCT->REGMODE_L = 0; // 30 MHz Count to 30000 for 1 kHz PWM base rate LPC_SCT->MATCH[0].U = 30000-1; LPC_SCT->MATCHREL[0].U = 30000-1; // Match values for the events, default 50% duty LPC_SCT->MATCH[1].U = 0; // PWM Pulse Start LPC_SCT->MATCH[2].U = 15000; // PWM Pulse End LPC_SCT->MATCHREL[1].U = 0; // PWM Pulse Start LPC_SCT->MATCHREL[2].U = 15000; // PWM Pulse End // Event 0 creates the rising edge of the pulse // at MATCH[1], which will be count 0 unless // phase control is desired LPC_SCT->EVENT[0].CTRL = (0 | SCT_EVCTRL_MATCHSEL(1) | SCT_EVCTRL_COMBMODE(1) ); LPC_SCT->EVENT[0].STATE = 1; // active on state 0 // Event 1 creates the falling edge of the pulse // at MATCH[2], which can vary based on the desired // pulse width. LPC_SCT->EVENT[1].CTRL = (0 | SCT_EVCTRL_MATCHSEL(2) | SCT_EVCTRL_COMBMODE(1) ); LPC_SCT->EVENT[1].STATE = 1; // active on state 0 // remaining events are unused LPC_SCT->EVENT[2].STATE = 0; LPC_SCT->EVENT[3].STATE = 0; LPC_SCT->EVENT[4].STATE = 0; LPC_SCT->EVENT[5].STATE = 0; // Set CTOUT_0 on Event 0 and clear it on Event 1 LPC_SCT->OUT[0].SET = (1<OUT[0].CLR = (1<OUT[1].SET = 0; LPC_SCT->OUT[1].CLR = 0; LPC_SCT->OUT[2].SET = 0; LPC_SCT->OUT[2].CLR = 0; LPC_SCT->OUT[3].SET = 0; LPC_SCT->OUT[3].CLR = 0; LPC_SCT->COUNT_U = 0; LPC_SCT->CTRL_U = (0 | SCT_CTRL_CLRCTR_L //| SCT_CTRL_CLRCTRL_H | SCT_CTRL_PRE_L(0) ); } void pwmSet(int val) { int v = (30000*val/255); if (v = 30000) { v = 30000; } LPC_SCT->MATCHREL[2].U = v-1; // PWM Pulse End }
With the public interface declared in pwm.h
. This is not a completely general PWM implementation, but it is sufficient for my needs here.
extern void pwmInit(void); extern void pwmSet(int v);
Demo and Next Steps
After flashing the firmware and letting it run, the PWM output will modulate the LED based on SPL, and periodic SPL values will be output on the serial port.
A future post will provide some video of the demo code jumping in response to various noisy objects.
A future post will document the placement of the source code into the fossil repository, along with instructions for building and flashing it with GCC and lpc2isp
instead of the LPCXpresso IDE.
One refinement would be to use a running average SPL value for the serial port output, with the average set long enough to properly antialias the values printed. This would make the printed value roughly equivalent to a standard SPL meter’s slow setting, while the PWM output is roughly equivalent to the fast setting.
Another refinement would be to introduce interrupt handlers for both the UART transmitter and the SPI. That would allow the SPI clock to run without interruption and for data to collect and be reduced to PCM samples in parallel with the UART printing.
(Written with StackEdit.)
Hi,
Thank you so much for the information and the source code for the LPC810. I am trying to reconstruct the PCM from the PDM input of MEMS microphone. But I am quite a novice on the dsp. I am using LPC1769 and want to output audio from the DAC.
What changes should I do in the code? I would be grateful if anyone could provide me some assistance.
Thanks
LikeLike
At the point in the main loop where the second stage CIC filter has finished its work, the PCM sample is “in the variable
stage2
.” In this SPL meter application, that value is immediately added to the running accumulators used to implement the SPL calculation.You could instead scale the value as required and write it directly to the DAC’s output register. I suspect that will introduce some sample timing jitter, however, and so it might be better to deliberately introduce at least one more output sample of delay and write finished PCM samples to a FIFO buffer, which you empty into your DAC using an interrupt at a fixed sample rate.
Another factor to pay attention to is that for the purposes of SPL, I have not worried very much about the passband response. The CIC filter does not have a flat response in its passband, and for audio recording is usually followed by an FIR filter that equalizes the passband.
LikeLike
Hi,
Thank you very much for the clarification and guidance. Can you please clarify,
what do you mean by “so it might be better to deliberately introduce at least one more output sample of delay ?”
Writing ‘stage 2’ directly into DAC register with scaling using FIFO implementation did not work out for me.It is irritatingly loud with no useful audio output. I suspect I need to do FIR into the raw PCM sample and currently working on it.
Thanks
LikeLike
The concern about jitter is that there is no hard real-time control over the timing relationship between the input PDM samples and the production of a finished PCM sample. The easiest way to gain some control is to use a FIFO between the PCM sample production and the DAC. You should drive both the PDM clock and the DAC output sample rate from the same time reference so that the two rates remain locked. By using a short FIFO between the PCM calculation and the DAC output you allow for some variation in the time it takes to compute each PCM sample while retaining a fixed sample rate at the DAC output.
I am reasonably confident that the code does produce meaningful PCM samples from PDM inputs, but I have not tried feeding the PCM samples to a DAC or otherwise inspected them in situ myself. The LPC810 does not have any high bandwidth channels to send finished samples off-chip that I can trivially use for the purpose. Your LCP1769 does have several high bandwidth channels you could use to send the samples up in bulk for inspection in a PC, if your hardware has the necessary interfaces and connectors available.
If you are seeing unexpected signals, I would start by capturing a buffer full of the PCM samples and inspecting them in Excel. It should be possible to attach an earphone to the microphone’s audio port and use that to drive test tones into it without annoying the whole building. If you play a fairly loud 500 Hz tone, you should be able to see if your reconstruction filters are working with only a few dozen samples at 8 kHz sample rate.
It would also be easy to synthesize tones to your DAC, and verify that you do understand the scaling and operation of your output channel. A full cycle of a 1 kHz sine wave at 8 kHz sample rate would be only 8 samples, so just using a table of constants would work. Square and triangle waves are also easy to synthesize, and will be easy to probe and verify with an oscilloscope.
LikeLike
Hi
Thanks a lot for your explanations.
But I have some doubts could you please clear them up
I have a MEMS mic that has range up to 80 kHz
I want to calculate A-weighted SPL and display the frequency present in the singal upto 75 kHz
The mic is clocked at 4.8 MHz
So in order to have singal upto 75kHz shouldn’t I decimate from by 32
using your code snippet by 8 in first stage and by 4 in second stage, so that I get 4.8 MHz downsampled to 150 kHz ( twice of the maximum desired frequency )
or should I decimate it by a factor 64 to get signals upto 75kHz?
if I use the snippet of your code and change the decimation to 4
#define CIC2_R 16 to #define CIC2_R 4
I would end up with 150 kHz 10 bits PCM?
I would be grateful if you could help me with this
Thanks & regards
LikeLike