Check-in [5218c33e38]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Replaced subtracting average sample value with a simple single pole IIR high pass filter rolling off at 20Hz. Added mechanism for dumping scaled 8-bit PCM samples to the UART for analysis on the PC. Tested with recording of room audio.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 5218c33e381dc904512840778a2f57fb3f34ea93
User & Date: rberteig 2015-03-10 22:54:18
Context
2015-03-17
00:48
Added a mu-Law codec to the record over UART feature to provide for 14-bit effective dynamic range in the captured recordings, which matches the dynamic range actually produced by the PDM to PCM conversion as implemented. check-in: 1d38441bbc user: rberteig tags: trunk
2015-03-10
22:54
Replaced subtracting average sample value with a simple single pole IIR high pass filter rolling off at 20Hz. Added mechanism for dumping scaled 8-bit PCM samples to the UART for analysis on the PC. Tested with recording of room audio. check-in: 5218c33e38 user: rberteig tags: trunk
2015-03-06
00:24
Merged latest demo board documentation improvements. check-in: 8de46c10f4 user: rberteig tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to LPCWorkspace/PDMSPL/src/main.c.

30
31
32
33
34
35
36











37
38














39
40
41
42
43
44
45
46

/* Systick timer tick rate, to change duty cycle */
#define TICKRATE_HZ     1000		/* 1 ms Tick rate */

/*
 * SPL parameters
 */











#define WINDOWSIZE 781	// Fs=7812.5 Hz
#define PWM_OFFSET 75	// nominally == 8*log2(WINDOWSIZE)














#define WINDOWSPERPRINT 20


// TODO: insert other definitions and declarations here
extern void initUART(void);

#if 0
caddr_t _sbrk_r ( int incr ) {







>
>
>
>
>
>
>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

/* Systick timer tick rate, to change duty cycle */
#define TICKRATE_HZ     1000		/* 1 ms Tick rate */

/*
 * SPL parameters
 */

/* SPL Window Size
 *
 * Given PCM Fs=7812.5 Hz
 * Window    SPL Fs           8*log2(Window)
 *  781       10.0032 Hz       76.88
 *  789        9.9018 Hz       76.9987
 *  938        8.3289 Hz       78.
 *  976        8.0046 Hz
 * 1327        5.8873 Hz       82.99989
 */
#define WINDOWSIZE      789	// 9.9 Hz given Fs=7812.5 Hz
#define LG2WINDOWSIZE    77	// nominally == 8*log2(WINDOWSIZE)

/*
 * Ceiling(8*log2(PCM_MAGNITUDE)) is the largest possible scaled SPL value.
 * Note the dependence on the CIC filter structure's bit growth. As presently
 * configured, the CIC filter produces PCM ranging from -8192 to +8192.
 *
 * Scaling the PWM to clip at this value makes 100% PWM modulation from a near
 * physically impossible 100% FS square wave input.
 */
#define MAXLG2SPL       104

/*
 *  SPL monitor interval, and SPL peak interval, in SPL samples
 */
#define WINDOWSPERPRINT  20


// TODO: insert other definitions and declarations here
extern void initUART(void);

#if 0
caddr_t _sbrk_r ( int incr ) {
107
108
109
110
111
112
113










114
115
116
117
118
119
120
121



122
123
124
125
126
127
128
 * @return	Nothing
 */
void SysTick_Handler(void)
{
	++ticks;
}











int main(void) {
	uint8_t pcount = WINDOWSPERPRINT;
	uint32_t n = 0;
	uint32_t sabs = 0;
	float ssq = 0.;
	int32_t sum = 0;
	int avg = 0 ;





#if defined (__USE_LPCOPEN)
#if !defined(NO_BOARD_LIB)
    // Read clock settings and update SystemCoreClock variable
    SystemCoreClockUpdate();
    // Set up and initialize all required blocks and
    // functions related to the board hardware







>
>
>
>
>
>
>
>
>
>




|

|
|
>
>
>







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
 * @return	Nothing
 */
void SysTick_Handler(void)
{
	++ticks;
}

#define PRINT_NOTHING       0
#define PRINT_MONITOR_SPL   1
#define PRINT_SCARED        2
#define PRINT_SAMPLES       4


/**
 * @brief  Main Loop
 * @return Does not return.
 */
int main(void) {
	uint8_t pcount = WINDOWSPERPRINT;
	uint32_t n = 0;
	uint32_t sabs = 0;
	//float ssq = 0.;
	int32_t sum = 0;
	int avg = 0;
	int peak = 0;
	int last = 0;
	volatile int monitor = PRINT_MONITOR_SPL;
	static uint8_t sbuf[64];

#if defined (__USE_LPCOPEN)
#if !defined(NO_BOARD_LIB)
    // Read clock settings and update SystemCoreClock variable
    SystemCoreClockUpdate();
    // Set up and initialize all required blocks and
    // functions related to the board hardware
143
144
145
146
147
148
149
150

151
152
153















154
155

156

157










158
159
160

161

162
163
164
165
166









167










168
169
170
171
172
173
174
175
176
177

178
179
	pwmInit();
	pwmSet_LED(50,100);
	pwmSet_OUT(50,100);

	pdmspi_init();

    while(1) {
    	int16_t pcm;


    	// block for a PCM sample
    	pdmspi_wait_sample(&pcm);















		sum += pcm;
		ssq += (pcm-avg)*(pcm-avg);

		sabs += (pcm-avg) > 0 ? (pcm-avg) : -(pcm-avg);

		++n;










		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_LED(255-spl,255);
				pwmSet_OUT(spl,255);
			//}









			avg = sum / WINDOWSIZE;










			sum = 0;
			sabs = 0;
			n = 0;
			if (!--pcount) {
				//int dB = logf(ssq)
				pcount = WINDOWSPERPRINT;
				printf("%d %d\r\n", spl, avg);
			}
		}
    }

    return 0 ;
}







|
>



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|
>
|
>

>
>
>
>
>
>
>
>
>
>



>
|
>
|
<
|
|
|
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>



<
<
<
<
|
|
|
>


181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230

231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256




257
258
259
260
261
262
	pwmInit();
	pwmSet_LED(50,100);
	pwmSet_OUT(50,100);

	pdmspi_init();

    while(1) {
    	static int16_t pcm;
    	static int pcm0, pcm1;

    	// block for a PCM sample
    	pdmspi_wait_sample(&pcm);

    	// High pass filter to remove DC, roll-off at alpha=63/64 and 7812.5 Hz
    	// implies RC=16.256 ms, or Fc=9.79 Hz

    	// alpha at 7812.5 Hz        Fcut
    	//   255/256    32.64 ms      4.876 Hz
    	//   127/128    16.256 ms     9.79 Hz
    	//    63/64      8.064 ms    19.74 Hz
    	//    31/32      3.968 ms    40.1 Hz

    	pcm1 = 63 * (pcm1 + pcm - pcm0) / 64;
    	pcm0 = pcm;
    	pcm = pcm1;

    	// accumulate PCM and |PCM| to compute DC offset and SPL
		sum += pcm;
		//ssq += (pcm-avg)*(pcm-avg);
		sabs += pcm > 0 ? pcm : -pcm;

		// Finish SPL and apply it at each complete SPL window interval
		++n;
		if (monitor & PRINT_SAMPLES) {
			extern void putLineUARTn(const uint8_t *, size_t);
			int s =  (pcm + 8192) >> 6;

			//s = n & 0xff;

			sbuf[n & 63] = s <= 0 ? 0 : s >=255 ? 255 : s;
			if ((n&31) == 31)
				putLineUARTn(sbuf + (n & 32), 32);
		}
		if (n == WINDOWSIZE) {
			int spl;

			// Compute the SPL over the window that just finished.
			spl = (lg2(sabs) - LG2WINDOWSIZE);

			// set the PWM

			pwmSet_LED(MAXLG2SPL-spl,MAXLG2SPL);
			pwmSet_OUT(spl,MAXLG2SPL);

			// track peak level
			if (peak < spl) { peak = spl; }

			// jump on large change
			if ((monitor & PRINT_SCARED) && last && last < spl && spl - last >= 8)
				printf("Eek! %d, %d, %d\r\n", spl, last, peak);
			last = spl;

			// compute mean sample for DC offset removal
			avg = sum / WINDOWSIZE;

			// monitor SPL occasionally, reset peak level
			if (!--pcount) {
				pcount = WINDOWSPERPRINT;
				if (monitor & PRINT_MONITOR_SPL)
					printf("%d %d %d\r\n", peak, spl, avg);
				peak = 0;
			}

			// reset accumulators and counters for the next iteration
			sum = 0;
			sabs = 0;
			n = 0;




		}
    }

    // Not reached
    return 0 ;
}

Changes to LPCWorkspace/PDMSPL/src/pdmspi.c.

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
		 *    4      206      192      488.28125    		    
		 *    5      208      194      244.140625   		    
		 *    6      208      194      122.0703125  		    
		 *    7      208      194       61.03515625 		    
		 *    8      210      194       30.517578125		    
		 *    9      210      196       15.2587890625    
		 */
//		0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, // 100%FS
		0x5555, 0xf5f5, 0xffff, 0x5f5f, 0x5555, 0x5050, 0x0000, 0x0505, // Aztec
};
uint8_t TESTSHIFT=3;
#define TESTMASK (((sizeof testsamples) / (sizeof testsamples[0]))-1)
#endif

/* CIC filter state */
#define CIC2_R 16







|
|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
		 *    4      206      192      488.28125    		    
		 *    5      208      194      244.140625   		    
		 *    6      208      194      122.0703125  		    
		 *    7      208      194       61.03515625 		    
		 *    8      210      194       30.517578125		    
		 *    9      210      196       15.2587890625    
		 */
		0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, // 100%FS
//		0x5555, 0xf5f5, 0xffff, 0x5f5f, 0x5555, 0x5050, 0x0000, 0x0505, // Aztec
};
uint8_t TESTSHIFT=3;
#define TESTMASK (((sizeof testsamples) / (sizeof testsamples[0]))-1)
#endif

/* CIC filter state */
#define CIC2_R 16
121
122
123
124
125
126
127


128
129
130
131
132
133
134
	do {
		if (status & SPI_STAT_RXRDY) {
			uint16_t pdm = LPC_SPI0->RXDAT;

#ifdef PDMTEST
			pdm = testsamples[((++testcount)>>TESTSHIFT) & TESTMASK];
#endif



			// 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 N*log2(R*M)
			// or 3, for 4 total significant bits out from the single bit in.







>
>







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
	do {
		if (status & SPI_STAT_RXRDY) {
			uint16_t pdm = LPC_SPI0->RXDAT;

#ifdef PDMTEST
			pdm = testsamples[((++testcount)>>TESTSHIFT) & TESTMASK];
#endif

			SONARSET;

			// 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 N*log2(R*M)
			// or 3, for 4 total significant bits out from the single bit in.
154
155
156
157
158
159
160
161
162
163
164
165

166
167
168
169
170
171
172
				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;

				SONARSET;
				// queue the finished PCM sample
				RingBuffer_Insert(&pcmring, &stage2);
				SONARCLR;
			}

		}
		if (status & SPI_STAT_TXRDY) {
			/* keep the mic clock active by always transmitting another 16 bits when possible. */
			LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FLEN(15) | SPI_TXDATCTL_DATA(0);
		}
		if (status & (SPI_STAT_SSA|SPI_STAT_SSD)) {
			Chip_SPI_ClearStatus(LPC_SPI0, SPI_STAT_SSA|SPI_STAT_SSD);







<


<

>







156
157
158
159
160
161
162

163
164

165
166
167
168
169
170
171
172
173
				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;


				// queue the finished PCM sample
				RingBuffer_Insert(&pcmring, &stage2);

			}
			SONARCLR;
		}
		if (status & SPI_STAT_TXRDY) {
			/* keep the mic clock active by always transmitting another 16 bits when possible. */
			LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FLEN(15) | SPI_TXDATCTL_DATA(0);
		}
		if (status & (SPI_STAT_SSA|SPI_STAT_SSD)) {
			Chip_SPI_ClearStatus(LPC_SPI0, SPI_STAT_SSA|SPI_STAT_SSD);

Changes to LPCWorkspace/PDMSPL/src/uart_rom_int.c.

160
161
162
163
164
165
166




167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
	/* Interrupt mode, do not append CR/LF to sent data */
	param.transfer_mode = TX_MODE_SZERO;
	param.driver_mode = DRIVER_MODE_INTERRUPT;

	/* Setup the transmit callback, this will get called when the
	   transfer is complete */
	param.callback_func_pt = (UART_CALLBK_T) waitCallback;





	/* Transmit the data using interrupt mode, the function will
	   return */
	gotCallback = false;
	if (LPC_UARTD_API->uart_put_line(uartHandle, &param)) {
		errorUART();
	}

	/* Wait until the transmit callback occurs. When it hits, the
	   transfer is complete. */
	sleepUntilCB();
}



/*static*/ void putLineUART(const char *send_data)
{
	return putLineUARTn(send_data, strlen(send_data));







>
>
>
>








<
<
<







160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178



179
180
181
182
183
184
185
	/* Interrupt mode, do not append CR/LF to sent data */
	param.transfer_mode = TX_MODE_SZERO;
	param.driver_mode = DRIVER_MODE_INTERRUPT;

	/* Setup the transmit callback, this will get called when the
	   transfer is complete */
	param.callback_func_pt = (UART_CALLBK_T) waitCallback;

	/* Wait until the previous transmit callback occurs. When it hits, that
	   transfer is complete. */
	sleepUntilCB();

	/* Transmit the data using interrupt mode, the function will
	   return */
	gotCallback = false;
	if (LPC_UARTD_API->uart_put_line(uartHandle, &param)) {
		errorUART();
	}




}



/*static*/ void putLineUART(const char *send_data)
{
	return putLineUARTn(send_data, strlen(send_data));
239
240
241
242
243
244
245



246
247
248
249
250
251
252

	/* Allocate UART handle, setup UART parameters, and initialize UART
	   clocking */
	setupUART();

	/* Enable the IRQ for the UART */
	NVIC_EnableIRQ(UART0_IRQn);



}


/**
 * @brief Hook stdio output.
 *
 * Supply a low-level system call stub to hook newlib-nano stdio output to the UART.







>
>
>







240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256

	/* Allocate UART handle, setup UART parameters, and initialize UART
	   clocking */
	setupUART();

	/* Enable the IRQ for the UART */
	NVIC_EnableIRQ(UART0_IRQn);


	gotCallback = true;
}


/**
 * @brief Hook stdio output.
 *
 * Supply a low-level system call stub to hook newlib-nano stdio output to the UART.