draft

Dependencies:   FastAnalogIn HSI2RGBW_PWM NVIC_set_all_priorities mbed-dsp mbed MMA8451Q

Fork of KL25Z_FFT_Demo by Frank Vannieuwkerke

main.cpp

Committer:
yangyulounk
Date:
2016-04-25
Revision:
4:5d4bcc4751d7
Parent:
3:a8238ddc2868

File content as of revision 4:5d4bcc4751d7:

// Audio Spectrum Display
// Copyright 2013 Tony DiCola (tony@tonydicola.com)
// Code ported from the guide at http://learn.adafruit.com/fft-fun-with-fourier-transforms?view=all

#include "mbed.h"
#include "NVIC_set_all_priorities.h"
#include <ctype.h>
#include "arm_math.h"
#include "arm_const_structs.h"
#include "hsi2rgbw_pwm.h"
#include "FastAnalogIn.h"
#include "MMA8451Q.h"

Serial pc(USBTX, USBRX);

FastAnalogIn   Audio(PTC2);


PinName const SDA = PTE25;
PinName const SCL = PTE24;

SPI spi(PTD2, PTD3, PTD1);          // Arduino compatible MOSI, MISO, SCLK
DigitalOut cs(PTD0);                // Chip select
//#define RGBW_ext // Disable this line when you want to use the KL25Z on-board RGB LED.
#define MMA8451_I2C_ADDRESS (0x1d<<1)
/*
#ifndef RGBW_ext
// HSI to RGB conversion with direct output to PWM channels - on-board RGB LED
hsi2rgbw_pwm led(LED_RED, LED_GREEN, LED_BLUE);
#else
// HSI to RGBW conversion with direct output to external PWM channels - RGBW LED
hsi2rgbw_pwm led(PTD4, PTA12, PTA4, PTA5); //Red, Green, Blue, White
#endif
*/
// Dummy ISR for disabling NMI on PTA4 - !! DO NOT REMOVE THIS !!
// More info at https://mbed.org/questions/1387/How-can-I-access-the-FTFA_FOPT-register-/
extern "C" void NMI_Handler() {
    DigitalIn test(PTA4);
}


////////////////////////////////////////////////////////////////////////////////
// CONFIGURATION
// These values can be changed to alter the behavior of the spectrum display.
// KL25Z limitations
// -----------------
// - When used with the Spectrogram python script :
//   There is a substantial time lag between the music and the screen output.
//   Max allowed SAMPLE_RATE_HZ is 40000
//   Max allowed FFT_SIZE is 64
////////////////////////////////////////////////////////////////////////////////

int SLOWDOWN = 4;                       // Create an optical delay in spectrumLoop - useful when only one RGB led is used.
                                        // Only active when nonzero.
                                        // A value >= 1000 and <= 1000 + PIXEL_COUNT fixes the output to a single frequency
                                        // window = a single color.
int SAMPLE_RATE_HZ = 20000;             // Sample rate of the audio in hertz.
float SPECTRUM_MIN_DB = 30.0;           // Audio intensity (in decibels) that maps to low LED brightness.
float SPECTRUM_MAX_DB = 80.0;           // Audio intensity (in decibels) that maps to high LED brightness.
int LEDS_ENABLED = 1;                   // Control if the LED's should display the spectrum or not.  1 is true, 0 is false.
                                        // Useful for turning the LED display on and off with commands from the serial port.
const int FFT_SIZE = 64;                // Size of the FFT.
const int PIXEL_COUNT = 8;             // Number of pixels.  You should be able to increase this without
                                        // any other changes to the program.
const int MAX_CHARS = 65;               // Max size of the input command buffer

const unsigned char tempCurrentLevel[] = {0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF};


////////////////////////////////////////////////////////////////////////////////
// INTERNAL STATE
// These shouldn't be modified unless you know what you're doing.
////////////////////////////////////////////////////////////////////////////////
const static arm_cfft_instance_f32 *S;
Ticker samplingTimer;
float samples[FFT_SIZE*2];
float magnitudes[FFT_SIZE];
int sampleCounter = 0;
char commandBuffer[MAX_CHARS];
float frequencyWindow[PIXEL_COUNT+1];
float hues[PIXEL_COUNT];
bool commandRecv = 0;
////////////////////////////////////////////////////////////////////////////////
// UTILITY FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

/// Send two bytes to SPI bus
void SPI_Write2(unsigned char MSB, unsigned char LSB)
{
    cs = 0;                         // Set CS Low
    spi.write(MSB);                 // Send two bytes
    spi.write(LSB);
    cs = 1;                         // Set CS High
}


/// MAX7219 initialisation
void Init_MAX7219(void)
{
    SPI_Write2(0x09, 0x00);         // Decoding off
    SPI_Write2(0x0A, 0x08);         // Brightness to intermediate
    SPI_Write2(0x0B, 0x07);         // Scan limit = 7
    SPI_Write2(0x0C, 0x01);         // Normal operation mode
    SPI_Write2(0x0F, 0x0F);         // Enable display test
    wait_ms(500);                   // 500 ms delay
    SPI_Write2(0x01, 0x00);         // Clear row 0.
    SPI_Write2(0x02, 0x00);         // Clear row 1.
    SPI_Write2(0x03, 0x00);         // Clear row 2.
    SPI_Write2(0x04, 0x00);         // Clear row 3.
    SPI_Write2(0x05, 0x00);         // Clear row 4.
    SPI_Write2(0x06, 0x00);         // Clear row 5.
    SPI_Write2(0x07, 0x00);         // Clear row 6.
    SPI_Write2(0x08, 0x00);         // Clear row 7.
    SPI_Write2(0x0F, 0x00);         // Disable display test
    wait_ms(500);                   // 500 ms delay
}
/*
void rxisr() {
    char c = pc.getc();
    // Add any characters that aren't the end of a command (semicolon) to the input buffer.
    if (c != ';') {
        c = toupper(c);
        strncat(commandBuffer, &c, 1);
    } else {
        // Parse the command because an end of command token was encountered.
        commandRecv = 1;
    }
}
*/
// Compute the average magnitude of a target frequency window vs. all other frequencies.
void windowMean(float* magnitudes, int lowBin, int highBin, float* windowMean, float* otherMean)
{
    *windowMean = 0;
    *otherMean = 0;
    // Notice the first magnitude bin is skipped because it represents the
    // average power of the signal.
    for (int i = 1; i < FFT_SIZE/2; ++i) {
        if (i >= lowBin && i <= highBin) {
            *windowMean += magnitudes[i];
        } else {
            *otherMean += magnitudes[i];
        }
    }
    *windowMean /= (highBin - lowBin) + 1;
    *otherMean /= (FFT_SIZE / 2 - (highBin - lowBin));
}

// Convert a frequency to the appropriate FFT bin it will fall within.
int frequencyToBin(float frequency)
{
    float binFrequency = float(SAMPLE_RATE_HZ) / float(FFT_SIZE);
    return int(frequency / binFrequency);
}


////////////////////////////////////////////////////////////////////////////////
// SPECTRUM DISPLAY FUNCTIONS
///////////////////////////////////////////////////////////////////////////////

void spectrumSetup()
{
    // Set the frequency window values by evenly dividing the possible frequency
    // spectrum across the number of neo pixels.
    float windowSize = (SAMPLE_RATE_HZ / 2.0) / float(PIXEL_COUNT);
    for (int i = 0; i < PIXEL_COUNT+1; ++i) {
        frequencyWindow[i] = i*windowSize;
    }
    // Evenly spread hues across all pixels.
    for (int i = 0; i < PIXEL_COUNT; ++i) {
        hues[i] = 360.0*(float(i)/float(PIXEL_COUNT-1));
    }
}

void spectrumLoop()
{
    // Update each LED based on the intensity of the audio
    // in the associated frequency window.
    static int SLrpt = 0, SLpixcnt = 0;
    int SLpixend = 8;
    float intensity, otherMean;

    int intensity_4x8[32]={0};
    int offset=0;

    for (int i = SLpixcnt; i < SLpixend; ++i) {
        windowMean(magnitudes,
                   frequencyToBin(frequencyWindow[i]),
                   frequencyToBin(frequencyWindow[i+1]),
                   &intensity,
                   &otherMean);
            printf("%d: %d \n",i, ((int)intensity)/3);
            int j = ((int)intensity)/5;
            j = ((j>280) ? 280 : j)/40;
            printf("%d \n",j);

            SPI_Write2(8-i,tempCurrentLevel[j]);

            

    }
}


////////////////////////////////////////////////////////////////////////////////
// SAMPLING FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

void samplingCallback()
{
    // Read from the ADC and store the sample data
    samples[sampleCounter] = (1023 * Audio) - 511.0f;
    // Complex FFT functions require a coefficient for the imaginary part of the input.
    // Since we only have real data, set this coefficient to zero.
    samples[sampleCounter+1] = 0.0;
    // Update sample buffer position and stop after the buffer is filled
    sampleCounter += 2;
    if (sampleCounter >= FFT_SIZE*2) {
        samplingTimer.detach();
    }
}

void samplingBegin()
{
    // Reset sample buffer position and start callback at necessary rate.
    sampleCounter = 0;
    samplingTimer.attach_us(&samplingCallback, 1000000/SAMPLE_RATE_HZ);
}

bool samplingIsDone()
{
    return sampleCounter >= FFT_SIZE*2;
}


////////////////////////////////////////////////////////////////////////////////
// COMMAND PARSING FUNCTIONS
// These functions allow parsing simple commands input on the serial port.
// Commands allow reading and writing variables that control the device.
//
// All commands must end with a semicolon character.
//
// Example commands are:
// GET SAMPLE_RATE_HZ;
// - Get the sample rate of the device.
// SET SAMPLE_RATE_HZ 400;
// - Set the sample rate of the device to 400 hertz.
//
////////////////////////////////////////////////////////////////////////////////
/*
void parseCommand(char* command)
{
    if (strcmp(command, "GET MAGNITUDES") == 0) {
        for (int i = 0; i < FFT_SIZE; ++i) {
            printf("%f\r\n", magnitudes[i]);
        }
    } else if (strcmp(command, "GET SAMPLES") == 0) {
        for (int i = 0; i < FFT_SIZE*2; i+=2) {
            printf("%f\r\n", samples[i]);
        }
    } else if (strcmp(command, "GET FFT_SIZE") == 0) {
        printf("%d\r\n", FFT_SIZE);
    } else if (strcmp(command, "GET SAMPLE_RATE_HZ") == 0) {
        printf("%d\r\n", SAMPLE_RATE_HZ);
    } else if (strstr(command, "SET SAMPLE_RATE_HZ") != NULL) {
        SAMPLE_RATE_HZ = (typeof(SAMPLE_RATE_HZ)) atof(command+(sizeof("SET SAMPLE_RATE_HZ")-1));
    } else if (strcmp(command, "GET LEDS_ENABLED") == 0) {
        printf("%d\r\n", LEDS_ENABLED);
    } else if (strstr(command, "SET LEDS_ENABLED") != NULL) {
        LEDS_ENABLED = (typeof(LEDS_ENABLED)) atof(command+(sizeof("SET LEDS_ENABLED")-1));
    } else if (strcmp(command, "GET SPECTRUM_MIN_DB") == 0) {
        printf("%f\r\n", SPECTRUM_MIN_DB);
    } else if (strstr(command, "SET SPECTRUM_MIN_DB") != NULL) {
        SPECTRUM_MIN_DB = (typeof(SPECTRUM_MIN_DB)) atof(command+(sizeof("SET SPECTRUM_MIN_DB")-1));
    } else if (strcmp(command, "GET SPECTRUM_MAX_DB") == 0) {
        printf("%f\r\n", SPECTRUM_MAX_DB);
    } else if (strstr(command, "SET SPECTRUM_MAX_DB") != NULL) {
        SPECTRUM_MAX_DB = (typeof(SPECTRUM_MAX_DB)) atof(command+(sizeof("SET SPECTRUM_MAX_DB")-1));
    } else if (strcmp(command, "GET SLOWDOWN") == 0) {
        printf("%d\r\n", SLOWDOWN);
    } else if (strstr(command, "SET SLOWDOWN") != NULL) {
        SLOWDOWN = (typeof(SLOWDOWN)) atoi(command+(sizeof("SET SLOWDOWN")-1));
    }

    // Update spectrum display values if sample rate was changed.
    if (strstr(command, "SET SAMPLE_RATE_HZ ") != NULL) {
        spectrumSetup();
    }

    // Turn off the LEDs if the state changed.
    if (LEDS_ENABLED == 0) {
    }
}

void parserLoop()
{
    // Process any incoming characters from the serial port
    while (pc.readable()) {
        char c = pc.getc();
        // Add any characters that aren't the end of a command (semicolon) to the input buffer.
        if (c != ';') {
            c = toupper(c);
            strncat(commandBuffer, &c, 1);
        } else {
            // Parse the command because an end of command token was encountered.
            parseCommand(commandBuffer);
            // Clear the input buffer
            memset(commandBuffer, 0, sizeof(commandBuffer));
        }
    }
}
*/
////////////////////////////////////////////////////////////////////////////////
// MAIN FUNCTION
////////////////////////////////////////////////////////////////////////////////

int main()
{
    cs = 1;                         // CS initially High
    spi.format(8,0);                // 8-bit format, mode 0,0
    spi.frequency(1000000);         // SCLK = 1 MHz
    Init_MAX7219();                 // Initialize the LED controller
    MMA8451Q acc(SDA, SCL, MMA8451_I2C_ADDRESS);

 //   NVIC_set_all_irq_priorities(1);
 //   NVIC_SetPriority(UART0_IRQn, 0);
    // Set up serial port.
  //  pc.baud (9600);
   // pc.attach(&rxisr);
   /*
#ifndef RGBW_ext
    led.invertpwm(1); //On-board KL25Z RGB LED uses common anode.
#endif 
*/
    // Clear the input command buffer
  //  memset(commandBuffer, 0, sizeof(commandBuffer));
    
    // Initialize spectrum display
    
    spectrumSetup();

    // Begin sampling audio
    samplingBegin();

    // Init arm_ccft_32
    switch (FFT_SIZE)
    {
    case 16:
        S = & arm_cfft_sR_f32_len16;
        break;
    case 32:
        S = & arm_cfft_sR_f32_len32;
        break;
    case 64:
        S = & arm_cfft_sR_f32_len64;
        break;
    case 128:
        S = & arm_cfft_sR_f32_len128;
        break;
    case 256:
        S = & arm_cfft_sR_f32_len256;
        break;
    case 512:
        S = & arm_cfft_sR_f32_len512;
        break;
    case 1024:
        S = & arm_cfft_sR_f32_len1024;
        break;
    case 2048:
        S = & arm_cfft_sR_f32_len2048;
        break;
    case 4096:
        S = & arm_cfft_sR_f32_len4096;
        break;
    }

    while(1) {
        // Calculate FFT if a full sample is available.
        if (samplingIsDone()) {
            // Run FFT on sample data.
            // Run FFT on sample data.
            arm_cfft_f32(S, samples, 0, 1);
            // Calculate magnitude of complex numbers output by the FFT.
            arm_cmplx_mag_f32(samples, magnitudes, FFT_SIZE);

            if (LEDS_ENABLED == 1) {
                spectrumLoop();
            }

            // Restart audio sampling.
            samplingBegin();
        }
/*
        // Parse any pending commands.
        if(commandRecv) {
//            pc.attach(NULL);
            parseCommand(commandBuffer);
            commandRecv = 0;
            // Clear the input buffer
            memset(commandBuffer, 0, sizeof(commandBuffer));
//            pc.attach(&rxisr);
        }
*/      }
}