Documentation
/** \file MicDemo.c
   \brief Demonstrate use of a PDM MEMS microphone with an ATTiny.
    
   This is a demonstration of sampling audio data from a digital MEMS
   microphone, delivered in PDM (Pulse Density Modulation) format at a
   1 MHz sample rate.
 
   This code is built entirely independently of the Arduino framework,
   although it does run in an ATTiny85 on an Adafruit Trinket board
   which was built to be used as an inexpensive and small Arduino.

   The demonstration uses a Knowles SPM0437HD4H-B microphone and a 5V
   Adafruit Trinket board, along with some passive components.

   Visible outputs are provided by the LED already present on the
   Trinket, a 5V scale analog meter movement, and a standard sized
   hobby servo.

   Power is supplied by the USB cable that also supports flashing the
   firmware.
   
	  Trinket Pins    Usage
	   0 PB0/DI        Mic data input to USI 
	   1 PB1/DO/OC0B   PWM Analog SPL Output
	   2 PB2/USCLK     Mic clock input to USI
	   3 PB3           Sonar, Hobby Servo (USB D-)
	   4 PB4/OC1B      Mic clock output   (USB D+)
	   5 PB5/RESET     Reset button

   The microphone is a 3V nominal part. We could wire it directly to a
   3V ATTiny85, there is a 3V model of the Trinket that suits, but when
   running at 3V the ATTiny85 is limitted to too low a core clock to
   keep up with the computation required per 8 audio bits.

   For a breadboad demonstration, resistor dividers are used to power
   the mic and adjust the level of the clock drive. In the finished
   PCB, those must be replaced by an actual 3.3V supply and level
   translators. To make the breadboard work, we had to drive the mic at
   a higher than recommended voltage so that its data output would
   cross the Vih threshold for the ATTiny85's input pins, which is 70%
   of Vcc or 3.5V when running at 5V.

   The microphone's clock is generated by Timer 1 based on the internal
   64 MHz PLL. The clock output on PB4 is wired to the USI clock input
   on PB2, as well as to the microphone.

   The microphone's data is gathered on PB0 which is the DI pin to the
   USI peripheral.

   The USI provides an 8-bit shift register and can capture input data
   on either clock edge, and provide an interrupt once per 8 bits
   received. The interrupt will do all processing that must be done at
   the source sample rate, and hand off to foreground code to finish
   the processing and do something useful with the recovered audio.

   Through a pair of CIC filter structures, each 128 one-bit PDM
   samples will be downconverted to a single signed 15-bit PCM sample.
   Starting from the 1 MHz source clock, the PCM sample rate is then
   7812.5 Hz.

   The PCM samples will be fed into an RMS calculation, which is a fair
   proxy for SPL at the microphone. We know the mic itself is specified
   to convert a 94 dB 1Hz tone into -26 dBFS in its PDM bit stream. By
   recovering a 15 bit PCM sample, we can in principle measure a 90 dB
   range, topping out at 0 dBFS or 120 dB SPL.

*/

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/power.h>


/* Define the pin used for the PWM output proportional to measured SPL.
 */
#define SPLOUT PORTB1

/* Define the pin used for the Hobby Servo drive signal. Undefine to
 * not generate a hobby servo output at all.
 */
#define SERVOPIN PORTB3

/* Define the pin used for debug sonar. Note that there aren't any
 * spare pins once both the SPL and SERVO outputs are defined. It would
 * be bad if this collided with either of those, so don't do that.
 */
//#define SONARPIN PORTB3

/*
 * Macros lifted from wiring internals to make direct manipulation of
 * io register bits easier. While not obviously implemented in terms of 
 * the AVR instructions SBI and CBI, it would be a reasonable optimization 
 * for avr-gcc to do so, and inspection of the generated assembly shows
 * that it often does do so.
 */
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif


/*
 * Macros to make a pin wiggle as a debug aid. To use these, place the
 * SONARSET() and SONARCLEAR() around areas of interest. Be careful
 * that each set is balanced by a clear, or you won't get a pulse that
 * can be observed.  
 */
#ifdef SONARPIN
#define SONARSET() do{sbi(PORTB,SONARPIN);}while(0)
#define SONARCLR() do{cbi(PORTB,SONARPIN);}while(0)
#else
#define SONARSET() do{}while(0)
#define SONARCLR() do{}while(0)
#endif


/** Global Variables
 */

// Set by the foreground to schedule the ISR to clear the pin
volatile uint8_t pinset = 0;

// Number of 8 bit samples captured by the USI shift register
// to accumulate into a single audio PCM sample.
#define WINBYTES 16

// Set by the ISR when a total is ready
volatile uint8_t winflag = 0;

// Total count of bits over the most recent WINBYTES interrupts
volatile int16_t winval = 0;

// working variables for the ISR, private to each sampling window
uint8_t scount = WINBYTES+1;
int16_t s1_sum = 0;
int16_t s2_sum1 = 0;
int16_t s2_comb1_1 = 0;
int16_t s2_comb1_2 = 0;
int16_t s2_sum2 = 0;
int16_t s2_comb2_1 = 0;
int16_t s2_comb2_2 = 0;

#if defined(SPLOUT) && (SPLOUT == 1)

/** Initialize Timer 0
 *
 *  Timer 0 is used to generate the PWM signal output on pin OC0B. Once
 *  this has been called, the duty cycle is controlled from 0% to 100%
 *  with values ranging from 0 to 255 writen to register OCR0B.
 *
 *  The output pin should be low-pass filtered to change the PWM to an
 *  analog level.
 */
void init_timer_0(void)
{
#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    TIMSK = 0;				// No interrupts
    TCCR0A = (0<<COM0A0) | (2<<COM0B0) | (3<<WGM00); //TCCR0A  OC0A=none OC0B=CLEAR WGM=FAST PWM
    TCCR0B = /*_BV(WGM02) | */ (5<<CS00);	//  CS0 = Fclk/1024 or 15625 Hz
    //OCR0A = 0xff;   
    sbi(DDRB,PORTB1);			// PB.1 = output
    OCR0B = 0x7f;			// PIN 1 is a PWM output, initially 50%
    TCNT0  = 0x00;			// clear timer
#else
#warning Not an ATTiny
#endif
}
#endif

/** Initialize Timer 1
 *
 *  Timer/Counter 1 will generate the 1MHz (or greater) clock for the
 *  MEMS PDM microphone on pin OC1B, which must be wired to the USCLK
 *  pin to actuall receive data.
 *
 *  Indirectly, it also controls the finished sample rate, as that is a
 *  direct decimation from this clock.
 *
 *  And it controls the resolution of the hobby servo output which is
 *  tied to the decimation of the audio samples because it is available
 *  and works.
 */
void init_timer_1(void)
{
#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) 
    sbi(PLLCSR, PCKE);			// Timer1 uses 64MHz PCK
    TIMSK = 0;				// No interrupts

    // prescale stopped, normal async mode, ignore PWMA
    TCCR1 = (1<<CTC1) | (0<<CS10);
    // Make OC1B toggle at count OCR1C, normal async mode, ignore PWMB
    GTCCR = (1<<COM1B0);
    OCR1C = 7;				// timer1 counts 32 PCKs
    OCR1B = 7;				// PIN 1 toggles in phase with USI click
    TCNT1  = 0x00;			// clear timer
    // prescale to PCK/1, normal async mode, ignore PWMA, counter stopped
    TCCR1 = (1<<CTC1) | (3<<CS10);
    sbi(DDRB,PORTB4);			// PB.4 = output
#else
#warning Not an ATTiny
#endif
}


/** Initialize the USI peripheral.
 *
 *  We are using the 8-bit shift register of the USI to receive the PDM
 *  audio samples, clocked from a timer output running at 1 MHz. We
 *  normally only read from the USI, so the shifted data output can be
 *  configured to not drive any pin at all.
 *
 *  Note that since the ATTiny85 does not provide a complete enough pin
 *  mux fabric, we have to depend on an external wire to connect USCLK
 *  to OC1B.
 */
void init_usi(void)
{
  // initialize
    cbi(DDRB,PORTB2);	// PB.2 = input USCLK
    sbi(DDRB,PORTB1);	// PB.1 = output DO
    cbi(DDRB,PORTB0);	// PB.0 = input DI
    cbi(PORTB,PORTB0);  // turn OFF PB.0 pull up

    USISR = _BV(USIOIF); // clear interrupt flag before enabling interrupt
    // USIOIE=1 for Overflow interrupt
    // USIWM=1  for 3-wire SPI mode or 0 for no mode, allowing PB1 to be
    //          used for other things
    // USICS=2  for USCLK rising edge
    // USICLK=0 for USI count external both edges
    USICR = (1<<USIOIE) | (0<<USIWM0) | (2<<USICS0) | (0<<USICLK);
    USIDR = 0x55; // test pattern, but harmless if left in with a mic present
}

// Table of count of set bits for each byte value, stored in the program ROM 
// to avoid using up a significant percentage of the ATTiny's available RAM.
// The macro trick for the table initializer was found at the Bit Twiddling 
// Hacks page, and takes advantage nested macros to generate the entire table
// from just four entries:
// http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable
const int8_t setbits[256] PROGMEM = {
#   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)
};


/** 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
 *  significant 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[] PROGMEM = {
	0,0,8,13,16,19,21,22
    };
    if (!(v&~7)) {
	return pgm_read_byte(&log2table[v&7]);
    }
    uint8_t r = 3;     // r will be 8*log2(v)
    while (v & ~0xF) {
	v>>=1;
	++r;
    }
    return (r<<3) | (v&0x7);
}


/** USI receiver interrupt.
 *
 *  Capture 8 bits of PDM samples, and do the high sample rate work.
 *
 *  This interrupt effectively implements the decimation by 8 initial
 *  CIC filter.
 *
 *  Periodically supply the second CIC filter's integrator to the
 *  foreground thread to implement the decimation for the second CIC
 *  filter. The foreground will do the comb stages to complete the
 *  filter.
 *
 *  Implement the hobby servo control pulse by counting interrupts
 *  until the command pulse with is done, and setting or clearing the
 *  pin as indicated. This does couple the hobby servo control to the
 *  audio sample rate.
 */
ISR(USI_OVF_vect) {
    // Capture the mic data on entry
    uint8_t pdm = USIBR;
    int8_t tmp;
    SONARSET();

#ifdef SERVOPIN
    // If the pin is set, see if it is time to clear the pin.
    if (pinset) {
	if (!--pinset)  
	    cbi(PORTB, SERVOPIN);
	else
	    sbi(PORTB, SERVOPIN);

    }
#endif

    // First stage of PDM to PCM is to count the set bits in the
    // captured byte, rescale to a signed +/- 1 range, and add that to
    // the integrator stage 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.
    tmp = pgm_read_byte(setbits + pdm);
    
    // Now 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.
    s2_sum1 += tmp;
    s2_sum2 += s2_sum1;
    if (--scount == 0) {
	// toss the R=16 downsampled sum to the foreground
	// to complete the CIC cascade.
	winval = s2_sum2;
	scount = WINBYTES;
	winflag = 1;
    }
    SONARCLR();
}


/** Initialize peripherals.
 *
 *  Called from main() after the core clock has been configured. Does
 *  all the one-time initialization of peripherals.
 */
void setup() {
#if defined(SPLOUT) && (SPLOUT == 1)
    init_timer_0();
    sbi(DDRB,PORTB1);	// PB.1 = output PWM
#endif
    init_timer_1();
    init_usi();
#ifdef SERVOPIN
    sbi(DDRB,SERVOPIN);	// PB.3 = output SERVO Control
#endif
#ifdef SONARPIN
    sbi(DDRB,SONARPIN);	// PB.3 = output debug sonar
#endif
    sei();
}

/** Foreground thread loop.
 *
 *  If this function returns, it will be immediately called again from
 *  an infinite loop in main(). This structure is borrowed from Wiring
 *  as implemented on the Arduino platform.
 *
 *  This is the forground thread of our SPL meter demonstration. We
 *  loop until the half of the CIC filter implemented at interrupt
 *  level has signaled that the decimator is ready to deliver a sample.
 *  
 *  When a sample is available, we finish the CIC filter by
 *  implementing the combs, then feed the finished PCM sample into the
 *  running windowed SPL calculation, here based on a mean absolute
 *  value of the samples.
 *
 *  We also maintain a windowed mean sample value to use to remove any
 *  DC offset that might be present. Since the mic's data sheet
 *  documents a typical offset of about 6% of full scale, removing this
 *  offset is actually important in order to support sufficient
 *  sensitivity to quieter rooms.
 */
void loop() {
#define WINDOWSIZE 977			// samples at Fs=7812.5 Hz  
#define LOG2WINDOWSIZE 79		// nominally == 8*log2(WINDOWSIZE)
    uint16_t n = 0;
    int32_t sabs = 0;
    int16_t avg = 0;
    int32_t sum = 0;
    int16_t spl = 0;
#ifdef SERVOPIN
    uint16_t servo;
#ifdef SWEEPTEST
    uint8_t dir = 0;
#endif
#endif
    
    while(1) {
	if (winflag) {
	    // collect the latest sample
	    int16_t v = winval;
	    winflag = 0;

	    // Finish the second CIC filter by implementing the
	    // remaining comb stages
	    {
		int tmp1;//, tmp2;
		tmp1 = v - s2_comb1_2;
		s2_comb1_2 = s2_comb1_1; 
		s2_comb1_1 = v;
		v = tmp1 - s2_comb2_2;
		s2_comb2_2 = s2_comb2_1; 
		s2_comb2_1 = tmp1;
	    }
	    
	    // Add the reconstructed sample to the RMS window.  Each raw
	    // PCM sample is the output of a CIC cascade with 13 bits of
	    // precision and logically ranges from -1 to +1.

	    // Compute an average of this window to use to remove DC
	    // offset from the next window.
	    sum += v;
	    
	    // Take the absolute value for our RMS estimate based on
	    // mean absolute value, after removing the DC offset based
	    // on the previous window's average sample.

	    v -= avg;
	    if (v < 0) { v = -v; }
	    sabs += v;

	    // Count samples in this window
	    ++n; 

	    if (n == WINDOWSIZE) {
		n = 0;
		// Once per RMS window, based on WINDOWSIZE samples at
		// 1 MHz / 128 = 7812.5 Hz sample rate.

		// Compute the mean sample value.
		avg = sum / WINDOWSIZE;
		sum = 0;

		// Compute the mean absolute sample value to estimate
		// SPL. We can treat sabs directly as a fixed point
		// average sample value with log2(WINDOWSIZE) bits of
		// fraction, and ranging from 0.000 (0) to 8192.00
		// (8192*WINDOWSIZE).
		//
		// To convert that to dB FS, we need to compute
		//
		//   20. * log10(sabs/(8192*WINDOWSIZE)) + 3.010
		//
		// ideally without actually computing a log or working
		// anywhere near floating point.
		//
		// Alternatively, we could simply work with the linear
		// scale, and use appropriate labelling on a meter face,
		// or similar downstream processing to convert to dB.
		//
		// It turns out that computing a fixed point log base 2
		// is fast and easy, since it can be approximated by a
		// piece-wise linear function that can be computed by
		// searching for the highest order 1 bit in the
		// integer. The lg2 function does that, producing an
		// 8-bit result which can hold 8*log2 of any 32-bit
		// integer. Converting that result to dB  is then just
		// simple arithmetic to scale and offset as needed.
		
		spl = lg2(sabs);
		
#ifdef SPLOUT
		// Make SPLOUT be a pulse with width proportional to log
		// mean abs level.  We scale and offset spl to fill in
		// the range 0 to 255 for controlling the PWM. We are
		// careful here to use saturation arithmetic so that
		// over and underflows do not wrap.
		spl = (spl - LOG2WINDOWSIZE) * 2;
		if (spl < 0)
		    spl = 0;
		else if (spl > 255)
		    spl = 255;

		OCR0B = spl; // set the PWM width
#endif
		
#ifdef SERVOPIN		
		// Proper hobby servo control is also based on pulse
		// width, where a neutral position is commanded by a
		// pulse width of 1.5 ms, and an overal motion of 180
		// degrees ranging from 1 ms to 2 ms. The USI interrupts
		// are at 125 kHz, so there are 125 counts per ms. We
		// will scale the level so that it ranges from 125 to
		// 250.
		servo = spl + 125;
#ifdef SWEEPTEST
		if (dir) {
		    if (++servo > 250)
			dir = !dir;
		} else {
		    if (--servo < 125)
			dir = !dir;
		}
#endif
		if (servo < 125) servo = 125;
		if (servo > 250) servo = 250;
		pinset = servo;
#endif

		// reset sum for next RMS window  
		sabs = 0;
		
#if defined(SERVOPIN) && defined(MULTIPULSE)
	    } else if ((n==WINDOWSIZE/4)||(n==WINDOWSIZE/2)||(n==3*WINDOWSIZE/4)) {
		// make additional servo pulses identical to the last commanded value
		pinset = servo;
#endif
	    } 
	}
    }
}

/** Entry point.
 *
 * The reset vector sets up the stack, initializes the data and bss
 * segments, then calls here. If we return, the processor will hang, so
 * we don't return. The structure of this is lifted shamelessly from
 * the Arduino platform, but without all of the baggage that Wiring
 * brings with it since we don't have room in an ATTiny85 for very much
 * baggage.
 *
 * We also support a compile-time directive to set the core clock to
 * 16Mhz. Note that this is only documented to work for devices running
 * with VCC at 5V. This faster core clock is required in order to have
 * enough instructions per USI interrupt in order to complete the audio
 * computation in time.
 */
int main(void)
{
#if defined(F_CPU) && (F_CPU==16000000L) 
    // If requested at compile time, make the CPU core clock be 16 MHz
    // instead of the 8 MHz our fuses are likely set for.
    clock_prescale_set(clock_div_1);
#endif

    // Do one-time initialization of our peripherals, as in Wiring.
    setup();

    // Call the actual worker function in a loop, as in Wiring.
    for (;;) {
	loop();
    }
    
    /*NOTREACHED*/
    return 0;
}