Paul, kpc
I have been trying to get the FFT256IQ code running with the 2.8" TFT and real data coming into the line-in stereo pins.
kpc's code runs properly with synthetic data generated inside the code (see the listing several posts back) but does not seem to make the connection between the line-in pins and the FFT256IQ module as the output data is all zeros except for output[128]. See code below
Code:
// FFT256IQ Test Double sided spectrum display
//
// Compute a 256 point Fast Fourier Transform
// on IQ audio connected to the Left and Right Line-In pins.
// Display spectrum on 2.8" TFT module
// Original FFT256 from PJRC audio library modified by Forum users kpc and Bazz
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <ILI9341_t3.h>
#include "analyze_fft256iq.h"
#define TFT_DC 20
#define TFT_CS 21
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);
float specDat[256];
float sigMax = 0.0;
float sigMin = -40.0;
float m = 200. / (sigMax - sigMin);
float c = 20. - m * sigMin;
const int myInput = AUDIO_INPUT_LINEIN;
// Create the Audio components. These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioInputI2S audioInput; // audio shield: stereo line-in
AudioAnalyzeFFT256IQ fft; // full spectrum display
AudioConnection c1(audioInput, 0, fft, 0);
AudioConnection c2(audioInput, 1, fft, 1);
AudioControlSGTL5000 audioShield; // create object to control audio board
void setup() {
Serial.begin(9600);
AudioMemory(12);
// Enable the audio shield and select input.
audioShield.enable();
audioShield.inputSelect(myInput);
// Configure the window algorithm to use
// myFFT.windowFunction(AudioWindowHanning256);
SPI.setMOSI(7);
SPI.setSCK(14);
tft.begin();
tft.fillScreen(ILI9341_BLACK);
}
void loop() {
if (fft.available()) {
// draw the spectrum display
float datMax = 0.;
for (int i = 1; i < 256; i++)
{
if (fft.output[i] > datMax)datMax = fft.output[i];
}
for (int i = 1; i < 256; i++)
{
float logDat = 20.*log10(fft.output[i] / datMax);
if (logDat < sigMin) logDat = sigMin;
int bar = (int) (m * logDat + c);
tft.drawLine(20, i, bar, i, ILI9341_GREEN);
tft.drawLine(bar, i, 220, i, ILI9341_BLACK);
}
}
tft.drawLine(19, 0, 19, 127, ILI9341_RED);
tft.drawLine(221, 0, 221, 127, ILI9341_YELLOW);
}
I have checked the correct operation of the Teensy3.1 and audio board using some of the code examples in the Audio library.
The other thing which puzzles me is that if I use the "Teensy" connections for the TFT display (CS=10,DC=9,MOSI=11,SCK=13,MISO=12) the latter works correctly but if I use the "Audio Board" connections (CS=21,DC=20,MOSI=7,SCK=14,MISO=12) the display remains blank. I had assumed from reading the display connections page that I had to use the second set of connections when both Teensy and audio board were being used. Is this correct?
The code for the FFT256IQ module is below:
.h file
Code:
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef analyze_fft256iq_h_
#define analyze_fft256iq_h_
#include "AudioStream.h"
#include "arm_math.h"
// windows.c
extern "C" {
extern const int16_t AudioWindowHanning256[];
extern const int16_t AudioWindowBartlett256[];
extern const int16_t AudioWindowBlackman256[];
extern const int16_t AudioWindowFlattop256[];
extern const int16_t AudioWindowBlackmanHarris256[];
extern const int16_t AudioWindowNuttall256[];
extern const int16_t AudioWindowBlackmanNuttall256[];
extern const int16_t AudioWindowWelch256[];
extern const int16_t AudioWindowHamming256[];
extern const int16_t AudioWindowCosine256[];
extern const int16_t AudioWindowTukey256[];
}
class AudioAnalyzeFFT256IQ : public AudioStream
{
public:
AudioAnalyzeFFT256IQ() : AudioStream(2, inputQueueArray),
window(AudioWindowBlackmanNuttall256), prevblock_i(NULL), prevblock_q(NULL),count(0),
naverage(8), outputflag(false) {
arm_cfft_radix4_init_q15(&fft_inst, 256, 0, 1);
}
bool available() {
if (outputflag == true) {
outputflag = false;
return true;
}
return false;
}
float read(unsigned int binNumber) {
if (binNumber > 255) return 0.0;
return (float)(output[binNumber]) * (1.0 / 16384.0);
}
float read(unsigned int binFirst, unsigned int binLast) {
if (binFirst > binLast) {
unsigned int tmp = binLast;
binLast = binFirst;
binFirst = tmp;
}
if (binFirst > 255) return 0.0;
if (binLast > 255) binLast = 255;
uint32_t sum = 0;
do {
sum += output[binFirst++];
} while (binFirst < binLast);
return (float)sum * (1.0 / 16384.0);
}
void averageTogether(uint8_t n) {
if (n == 0) n = 1;
naverage = n;
}
void windowFunction(const int16_t *w) {
window = w;
}
virtual void update(void);
uint16_t output[256] __attribute__ ((aligned (4)));
private:
const int16_t *window;
audio_block_t *prevblock_i,*prevblock_q;
int16_t buffer[512] __attribute__ ((aligned (4)));
uint32_t sum[256];
uint8_t count;
uint8_t naverage;
volatile bool outputflag;
audio_block_t *inputQueueArray[2];
arm_cfft_radix4_instance_q15 fft_inst;
};
#endif
and .cpp file
Code:
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "analyze_fft256iq.h"
#include "sqrt_integer.h"
#include "utility/dspinst.h"
// 140312 - PAH - slightly faster copy
static void copy_to_fft_buffer(void *destination, const void *source1, const void *source2)
{
const uint16_t *src1 = (const uint16_t *)source1;
const uint16_t *src2 = (const uint16_t *)source2;
uint32_t *dst = (uint32_t *)destination;
for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
*dst++ = *src1++ | ((*src2++) <<16);
}
}
static void apply_window_to_fft_buffer(void *buffer, const void *window)
{
int16_t *buf = (int16_t *)buffer;
const int16_t *win = (int16_t *)window;;
for (int i=0; i < 256; i++) {
buf[0] = (buf[0] * *win) >> 15;
buf[1] = (buf[1] * *win) >> 15;
buf += 2;
win++;
}
}
void AudioAnalyzeFFT256IQ::update(void)
{
audio_block_t *block_i,*block_q;
block_i=receiveReadOnly(0);
block_q=receiveReadOnly(1);
if (!block_i || !block_q ) return;
if (!prevblock_i || !prevblock_q) {
prevblock_i = block_i;
prevblock_q = block_q;
return;
}
copy_to_fft_buffer(buffer, prevblock_i->data,prevblock_q->data);
copy_to_fft_buffer(buffer+256, block_i->data,block_q->data);
if (window) apply_window_to_fft_buffer(buffer, window);
arm_cfft_radix4_q15(&fft_inst, buffer);
// G. Heinzel's paper says we're supposed to average the magnitude
// squared, then do the square root at the end.
if (count == 0) {
for (int i=0; i < 256; i++) {
uint32_t tmp = *((uint32_t *)buffer + i);
uint32_t magsq = multiply_16tx16t_add_16bx16b(tmp, tmp);
sum[i] = magsq / naverage;
}
} else {
for (int i=0; i < 256; i++) {
uint32_t tmp = *((uint32_t *)buffer + i);
uint32_t magsq = multiply_16tx16t_add_16bx16b(tmp, tmp);
sum[i] += magsq / naverage;
}
}
if (++count == naverage) {
count = 0;
for (int i = 0; i < 256; i++) {
output[i ^ 128] = sqrt_uint32_approx(sum[i]);
}
outputflag = true;
}
release(prevblock_i);
release(prevblock_q);
prevblock_i = block_i;
prevblock_q = block_q;
}
Bazz