draft
Dependencies: FastAnalogIn HSI2RGBW_PWM NVIC_set_all_priorities mbed-dsp mbed MMA8451Q
Fork of KL25Z_FFT_Demo by
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); } */ } }