mems microphone test on the STM32L4 DISCOVERY dev board using bit banging. very primitive and not recommended, but works.

Dependencies:   BSP_DISCO_L476VG mbed

a shitty MICROPHONE RECORDER for the DISCOVERY STM32L476 dev board

Records audio and sends it via UART (to the PC) as text-encoded samples sample rate: 25600 Hz (ish) sample format: 8 bit unsigned (0-255) uart baud rate: 115,200

press the UP and DOWN keys to set the recording size press RIGHT to perform recording. The result is sent via UART after it is recorded can record up to 31744 samples, doesn't want to allocate more memory (I don't know why though)

you can encode the recording from the UART text dump into a .wav file in Matlab, by doing this:

wavedata = wavedata - 128; wavedata = wavedata / 256; audiowrite('wave.wav', wavedata, 25600);

where "wavedata" is a vector containing just the NUMBERS from the imported UART log

PRINCIPAL OF OPERATION:

The microphone is read by bit-banning it's clock pin (PE_9) and reading it's data pin (PE_7). The hardware PDM decoder should have been used for this, but was not. The hardware SPI or I2C would also be a good choice, but there happens to be no such hardware functionality on that specific set of pins.

The microphone returns PDM encoded audio data. This is a 1-bit format. The data is more likely to be a "1" the higher the current readout from the microphone. In practice this means that if we sample this fast enough and then low-pass it in a 8 or 16 bit format, we should get an audio waveform.

Method of filtering

The MCU collects 64 1-bit samples and adds them. This returns a value between 0 and 64 (a 6 bit sample). The 1-bit sampling is at a clock of about 1.64Mhz. Therefore, the 6bit samples are produced at 1,700,000 / 64 = 25,600 Hz

Theese 6bit samples are stored in a 7-slot cyclic buffer. The content of this buffer is summed whenever a new 6bit sample is added. The "middle" sample is counted twice.

This produces a 9-bit sample, which is halved to produce the final 8bit sample that is stored in RAM

After the recording is finished, the content of the RAM is sent over UART in text form.

This is a silly implementation of the microphone, but you may find some of it useful.

main.cpp

Committer:
Mawrk
Date:
2016-07-15
Revision:
0:dcefe9e5ec82

File content as of revision 0:dcefe9e5ec82:

/*
MICROPHONE RECORDER for the DISCOVERY STM32L476 dev board

Records audio and sends it via UART as text-encoded samples
sample rate: 25600 Hz
sample format: 8bit signed (0-255)

can record up to 31744 samples
press the UP and DOWN keys to set the recording size
press RIGHT to perform recording. The result is sent via UART after it is recorded


you can deode the recording into a .wav file in matlab, by doing this:

wavedata = wavedata - 128;
wavedata = wavedata / 256;
audiowrite('wave.wav', wavedata, 25600);


*/


#include "mbed.h"

DigitalOut led_red(LED2);
DigitalOut mic_clk(PE_9);
DigitalIn mic_data(PE_7);

InterruptIn right(JOYSTICK_RIGHT);
InterruptIn up(JOYSTICK_UP);
InterruptIn down(JOYSTICK_DOWN);

Serial pc(USBTX, USBRX); // tx, rx

int recsize = 16384;

void record() {
    
    unsigned int i;
    uint8_t j;
    uint8_t k;
    
    uint8_t *readouts;
    volatile uint8_t sum;
    volatile int readout;
    uint16_t properSample;
    
    uint8_t integratingBuffer[7];
    uint8_t bufferPointer = 6;
    
    readouts = (uint8_t*)malloc(recsize);
    if (readouts == NULL){
        pc.printf("FAILED TO ALLOCATE MEMORY!\n\r");
        return;
    }
    
    pc.printf("RECORDING!\n\r");
             
    //Step 1: pre-heat the microphone with 10ms+ of stable clocks
    //otherwise the microphone returns GARBAGE (see datasheet)
    for(i=0; i<80000; i++){
        mic_clk = 0;
        readout = mic_data; //fake smapling to keep the clock rythm
        mic_clk = 1;
    } 
    
    //Step 2: pre-fill the cyclyc integrating buffer
    for(i=0; i<7; i++){
        sum = 0;
        for(j=0; j<32; j++){
            mic_clk = 1;
            readout = mic_data;
            mic_clk = 0;
            sum += readout;
        }
        bufferPointer = (bufferPointer+1)%7;
        integratingBuffer[bufferPointer] = sum;
    }
   
    //step 3: Actual sampling
    led_red = 1;
    for(i=0; i<recsize; i++){
        //collect a 64x downsampled sample by summing the 1-bit input
        sum = 0;
        for(j=0; j<64; j++){
            mic_clk = 1;
            readout = mic_data;
            mic_clk = 0;
            sum += readout;
        } 
        
        //push it into the cyclic integrating buffer
        bufferPointer = (bufferPointer+1)%7;
        integratingBuffer[bufferPointer] = sum;
        
        //sum the current content of the buffer to create one proper sample
        properSample = 0;
        for(k=0; k<7; k++){
            properSample += integratingBuffer[k];
        }
        
        //the sample taken 3 samples ago is counted TWICE, as the middle key sample
        if(bufferPointer>=3){
             properSample+=integratingBuffer[bufferPointer-3];
        }else{
             properSample+=integratingBuffer[bufferPointer+4];
        }
        
        //proper sample is now a sum of 8 numbers from the range 0-64
        //this can be up to 512, so we need to devide this by 2 to fit into 0-255
        readouts[i] = (uint8_t)(properSample>>1);
    }
    led_red = 0;
    mic_clk=1;
    
    //step 4: serial print
     for(i=0; i<recsize; i++){
       pc.printf("%u\n\r", readouts[i]);
     }
     
    //step 5: clean
    free(readouts);
    pc.printf("DONE!\n\r");
}

void up_released() {
    recsize+=1024;
    pc.printf("recording size: %d\n\r", recsize);
}


void down_released() {
     if(recsize>1024){
        recsize-=1024;
    }
    pc.printf("recording size: %d\n\r", recsize);
}

void right_released() {
    record();
}

int main()
{
    right.fall(&right_released);
    up.fall(&up_released);
    down.fall(&down_released);

    right.mode(PullDown);
    up.mode(PullDown);
    down.mode(PullDown);


    pc.baud(115200);    
    pc.printf("booting\n\r");

    
    while(1){
        ;
    }   
  
}