Jump to content
43oh

Boosterpack LCD (Lars) on StellarisTI


Recommended Posts

Hi,

 

Ive started to port the code Lars and I have made for the MSP430 and C2K now also to the StellarisTI Launchpad. Based on a test using the code from Euphonistichack's frequency analyzer I created a simple new project. You can see the code below, however when I tried compiling this I got a load of errors (see the 2nd section). Its unclear to me why this happens as the necessary libraries are linked and the linkpaths are also working as planned. 

 

I allready saw the initial problems when I was using the original code: GPIOPinConfigure(GPIO_PB4_SSI2CLK);
This refers to a function in gpio.h and a define in pin_map.h. Without declaring GPIO_PB4_SSI2CLK as a define in the program itself the compiler did not work.

 

Well here's the full list of issues reported,anybody with a clue ??

 

cheers

Cor

P.S I am sure this is a user problem. Never made a project for the stellaris.

 

Errors reported:

 undefined              first referenced
  symbol                    in file     
 ---------              ----------------
 GPIOPinConfigure       ./hello.obj     
 GPIOPinTypeGPIOOutput  ./hello.obj     
>> Compilation failure
 GPIOPinTypeSSI         ./hello.obj     
 GPIOPinWrite           ./hello.obj     
 SSIBusy                ./hello.obj     
 SSIConfigSetExpClk     ./hello.obj     
 SSIDataPut             ./hello.obj     
 SSIEnable              ./hello.obj     
 SysCtlClockGet         ./hello.obj     
 SysCtlClockSet         ./hello.obj     
 SysCtlPeripheralEnable ./hello.obj     

error #10234-D: unresolved symbols remain

 

 

 

Code

#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/ssi.h"
#include "driverlib/sysctl.h"
#include "inc/glcd_hwSPI.c"

#define SSI_CLK			GPIO_PIN_4
#define SSI_TX			GPIO_PIN_7
#define GPIO_PB4_SSI2CLK        0x00011002
#define GPIO_PB7_SSI2TX         0x00011C02

int main(void) 
  {
	SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ |  SYSCTL_OSC_MAIN);

	SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI2);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
	// CS ENABLE
	GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_5);
	// CS LOW
	GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_5, 0);

	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

	GPIOPinConfigure(GPIO_PB4_SSI2CLK);
	GPIOPinConfigure(GPIO_PB7_SSI2TX);
	GPIOPinTypeSSI(GPIO_PORTB_BASE, SSI_CLK | SSI_TX);
	//
	// Configure SSI2
	SSIConfigSetExpClk(SSI2_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
					   SSI_MODE_MASTER, SysCtlClockGet()/2, 16);
	//
	// Enable the SSI module.
	SSIEnable(SSI2_BASE);

        // initialize the LCD and write a few strings
	LCD_Init();
	writeString(0,0,"LARS LCD");
	writeString(0,1,"Test");
}

 

 

 

Link to post
Share on other sites

 

Hi,

 

Ive started to port the code Lars and I have made for the MSP430 and C2K now also to the StellarisTI Launchpad. Based on a test using the code from Euphonistichack's frequency analyzer I created a simple new project. You can see the code below, however when I tried compiling this I got a load of errors (see the 2nd section). Its unclear to me why this happens as the necessary libraries are linked and the linkpaths are also working as planned. 

 

I allready saw the initial problems when I was using the original code: GPIOPinConfigure(GPIO_PB4_SSI2CLK);

This refers to a function in gpio.h and a define in pin_map.h. Without declaring GPIO_PB4_SSI2CLK as a define in the program itself the compiler did not work.

 

Well here's the full list of issues reported,anybody with a clue ??

 

cheers

Cor

P.S I am sure this is a user problem. Never made a project for the stellaris.

 

Did you add your driver lib to the linker settings as shown below? Path will change depending on your PC.

 

post-1-0-14427900-1354886635.png

Link to post
Share on other sites

Ive just added the lib (as used in the project by the original Olimex setup) but still no progress, it spits out that it cannot find a file ...

 

Snippet from the errors:

'Invoking: ARM Linker'

 

"C:/TI/ccsv5/ccsv5/tools/compiler/arm_5.0.1/bin/armcl" -mv7M4 --code_state=16 --float_support=FPv4SPD16 --abi=eabi -me -g --diag_warning=225 --display_error_number --diag_wrap=off -z --stack_size=256 -m"TEST.map" --heap_size=0 -i"C:/TI/ccsv5/ccsv5/tools/compiler/arm_5.0.1/lib" -i"C:/TI/ccsv5/ccsv5/tools/compiler/arm_5.0.1/include" --reread_libs --warn_sections --display_error_number --diag_wrap=off --rom_model -o "TEST.out"  "./hello.obj" -l"libc.a" -l"/driverlib/ccs-cm4f/Debug/driverlib-cm4f.lib" "../lm4f120h5qr.cmd" 
<Linking>
error #10008-D: cannot find file
 
CorB
Link to post
Share on other sites

Found the culprit ...

using this in the linker

"/driverlib/ccs-cm4f/Debug/driverlib-cm4f.lib"

did not work

I had to add the full directorylink including the location of the original StellarisWare.

 

Now it works as planned, thanks !

 

But after launching on the Stellaris the debugsystem states:

 

Can't find a source file at "/tmp/TI_MKLIBZyhUyK/SRC/exit.c" 

Locate the file or edit the source lookup path to include its location.
 
Looks as if the lookup paths are still not OK.

 

Cor

Link to post
Share on other sites

Hi.

 

Find attached the current testcode. A CCS5.3 project (Larslcd.zip) and 2 glcd_xxx files that need to be put inside the inc directory of stellarisware (or anywhere else if you change the links in the project).

 

 

LarsLCD.zipglcd_charsets.cglcd_hwSPI.c

 

This works OK, the stellaris runs the code but the debugger is disabled after the upload.

 

cheers

 

CorB

 

Link to post
Share on other sites

Spend most of my morning trying to overcome the debug issue. The debugger states:

 

Stellaris In-Circuit Debug Interface 0/CORTEX_M4_0 (suspended - HW Breakpoint)

main() at projectc0:15 0x00000580

_c_int00() at boot,asm:217 0x00000AD2 (_c_int has only skeletal debug info).

 

Its really unclear to me where this restriction comes from ... and thusfar I cannot surpass it which makes it difficult to test things ... 

 

Anybody with a wild guess ?

 

Cor

Link to post
Share on other sites

Spend most of my morning trying to overcome the debug issue. The debugger states:

 

Stellaris In-Circuit Debug Interface 0/CORTEX_M4_0 (suspended - HW Breakpoint)

main() at projectc0:15 0x00000580

_c_int00() at boot,asm:217 0x00000AD2 (_c_int has only skeletal debug info).

 

Its really unclear to me where this restriction comes from ... and thusfar I cannot surpass it which makes it difficult to test things ... 

 

Anybody with a wild guess ?

 

Cor

Are you setting up any pins on port C?

Link to post
Share on other sites

Ive got everything up and running now. Ive adapted the original frequency analyzer code to display 96 columns of data continously as a spectrum. Ive also made several changes to the original code, now I am working on the HW part to get a decent signal in. Testing using a kitchentimer signal just in front of an Ultrasonic Transceiver (UST) microphone shows the main frequency of the signal and also several harmonics. But I also still get loads of noise in the signal.

 

CorB

Link to post
Share on other sites

Hi,

 

Find below the adapted code to use the application Jordan Wills has made for usage with the LARS LCD booster. Next to setting up the SPI for the LCD Ive also changed many aspects of the code to my specific needs. According to my knowledge the original FFT usage fo the CMSIS library wasnt exactly as it should be.

This refers first to the call of arm_cmplx_mag_f32(g_fFFTResult, g_fFFTResult, NUM_SAMPLES) which original was using NUM_SAMPLES*2 as a parameter.

Secondly it refers to the original code using the full FFT Output to populate the frequency bins. The FFTsize is 2048 for this application, the output after calculation of the powers per frequency bin is half of that so 1024 bins. The data in these bins are however mirrored around the midpoint, so bin [0] is the same as bin[511] and bin[1] equals bin[510]. So in the code below you will see that I am only using the first half of the FFToutput.

Also not that I am not using the Hamming WIndow as a prefiltering, this might be necessary for other setups but in my current setup it is not.

 

The code is not yet fully documented but should be working directly ... i hope. To use it you first need to install the original code and then use the new Led_equalizer.c as the main file.

 

cheers

 

Cor

 

//*****************************************************************************
//
// led_equalizer.c - A project that uses the ADC to capture analog input, the
// CMSIS DSP library to analyze that input, and an Olimex 8x8 LED tile to
// display the power of 8 logarithmicly spaced frequency ranges.
//
// Copyright (c) Jordan Wills, aka EuphonistiHack, wills.jordan@gmail.com
// For more information, see writeup at
// http://www.euphonistihack.blogspot.com/2012/08/the-writeup.html
//
// adapted for LARS LCD usage by CorB. dec 2012
//
// This project was intended to run on a TI LM4F120XL on a Stellaris Launchpad
// evaluation kit.  It was developed in my free time without using company
// resources.  As such, feel free to copy and redistribute this code as you see
// fit.  I politely ask that you only use this code on Texas Instruments
// hardware, as it would be frustrating to see my hard work running on a board
// manufactured by one of my employer's competitors (I'm lookikng at you, ST
// and Freescale).  However, my polite request in no way constitutes a legally
// enforcable demand, so if you really want to use it on an ST board, I won't
// stop you.  At least give me some credit in your comments though 
//
// This project contains ABSOLUTELY NO WARRANTY, meaning it does not even have
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE.  If there's a bug in this code that smokes your board, sorry, but
// you're on your own.
//
//*****************************************************************************

#include <string.h>
#include "inc/hw_adc.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/adc.h"
#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/ssi.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/timer.h"
#include "driverlib/udma.h"

#include "arm_math.h"

//             ______________________
//            |                      |
//       VCC  |J1.1              J2.1| GND
//            |J1.2  pb5   pb2   J2.2|
//            |J1.3  pb0   pe0   J2.3|
//            |J1.4  pb1   pf0   J2.4|
//            |J1.5  pe4   xxx   J2.5|
//  glcd-cs   |J1.6  pe5   pb7   J2.6|<-------| glcd-MISO
//  glcd-CLK  |J1.7  pb4   pb6   J2.7|
//            |J1.8  pa5   pa4   J2.8|
//            |J1.9  pa6   pa3   J2.9|
//            |J1.10 pa7   pa2  J.210|
//            |                      |
//             ----------------------

//             ______________________
//            |                      |
//       	  |J3.1 VBUS   pf2   J4.1|
//            |J3.2  GND   pf3   J4.2|
//            |J3.3  pd0   pb3   J4.3|
//            |J3.4  pd1   pc4   J4.4|
//            |J3.5  pd2   pc5   J4.5|
//  		  |J3.6  pd3   pc6   J4.6|
//  		  |J3.7  pe1   pc7   J4.7|
//            |J3.8  pe2   pd6   J4.8|
//    IN ->>  |J3.9  pe3   pd7   J4.9|
//            |J3.10 pf1   pf4  J4.10|
//            |                      |
//             ----------------------

//*****************************************************************************
//! This project uses the following peripherals and pins:
//! ADC input 0 for audio capture
//! SSI2 for communication with the LARS LCD
//!
//  Hardware conditioning circuit
//  BUS J3
//  AIN			        = PE3 - AIN0
//
//  BUS J1
//  Pin 1 - Vcc			= J1[1] - VBUS
//  Pin 6 - SR_LATCH	= PE5 - GPIO
//  Pin 7 - SR_SCK      = PB4 - SSI2Clk
//
//  BUS J2
//  Pin 1 - GND			= J1[2] - GND
//  Pin 6 - SR_DATA_IN  = PB7 - SSI2Tx // CORRECTED BY CORB
//
//*****************************************************************************

//*****************************************************************************
//
// Preprocessor directives
//
//*****************************************************************************

//
// The error routine that is called if the driver library encounters an error.
//
#ifdef DEBUG
void
__error__(char *pcFilename, unsigned long ulLine)
{
}
#endif

//*****************************************************************************
//
// SSI and display macros
//
//*****************************************************************************
//
// audio sampling frequency = 2 * Nyquist = 2 * 22.3 kHz = 44600
//
#define SAMPLING_FREQ			44600
// Number of samples to capture for each FFT process.  Set to CMSIS max for
// best resolution
//
#define NUM_SAMPLES				2048

#define FFTSIZE                 2048
#define DISPLAYFFT              FFTSIZE/4
//
// REFRESH THE DISPLAY, HIGHER IS MORE FREQUENT
#define REFRESH_RATE			1000

//LCD COMMANDS
#define DATA 0x01
#define CMD 0x00
#define DISPLAYWIDTH 96

//DISPLAY SETUP
#define HORZ                    0
#define VERT                    1
#define SCREENDIR				VERT
// horizontal display 8 frequencybins
#if SCREENDIR==HORZ
#define DISPLAYMAX              96
#define NUM_F_BINS              8
#endif
// vertial display up to 94 frequencybins
#if SCREENDIR==VERT
#define DISPLAYMAX              64
#define BIN_PIXELS              10
#define NUM_F_BINS				(DISPLAYWIDTH-2)/BIN_PIXELS
#endif

// Pins used for SSI interface
//
#define SSI_CLK					GPIO_PIN_4
#define SSI_TX					GPIO_PIN_7

//*****************************************************************************
//
// Audio capture and signal processing macros
//
//*****************************************************************************
//
// The ADC sequencer to be used to sample the audio signal
//
#define ADC_SEQUENCER			3
//
// Flag used to determine whether to calculate forward (0) or inverse(1) fft
//
#define INVERT_FFT				0
//
// Flag used to determine if fft result will be output in standard bit order(1)
// or reversed bit order (0)
//
#define BIT_ORDER_FFT			1
//
// The maximum number of transfers one UDMA transaction can complete
//
#define UDMA_XFER_MAX			1024

//*****************************************************************************
//
// Global variables
//
//*****************************************************************************
//
// The control table used by the uDMA controller.  This table must be aligned
// to a 1024 byte boundary.
//
#if defined(ewarm)
#pragma data_alignment=1024
unsigned char ucControlTable[1024];
#elif defined(ccs)
#pragma DATA_ALIGN(ucControlTable, 1024)
unsigned char ucControlTable[1024];
#else
unsigned char ucControlTable[1024] __attribute__ ((aligned(1024)));
#endif


void SPIWrite(uint16_t command, uint16_t data)
{
	uint16_t spidata;

	spidata = command<<15;
	spidata = spidata | data <<7;

	SSIDataPut(SSI2_BASE, spidata);
	//
	// Wait until SSI is done transferring all the data in the transmit FIFO
	//
	while(SSIBusy(SSI2_BASE))
	{
	}

	//
	// Hit the SSI latch, locking in the data
	//
	GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_5, GPIO_PIN_5);
	GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_5, 0);

}


void setCursor(unsigned char column, unsigned char line) // position in pixels to the LCD-writeposition at a byte
{
  column = (column % DISPLAYWIDTH);
  //line = (line % (height/9 + 1));
  line = (line %9);
  SPIWrite(CMD, 0x80 | column);
  SPIWrite(CMD, 0x40 | line);
}


// display vertical line at position X with length=value
void Vbar(char x, char value)
{  volatile char y; char line,bit;
   char lines[8];

   for (y=0; y<8; y++)
    {
     lines[y]=0;
    }

    x=x*BIN_PIXELS;
    // set bits
    for (y=64; y>64-value; y--)
	     { line=y/8; bit=(1<<(y%8));
		   lines[line] |= (bit);
		 }
    // display bar
	for (y=0; y<8; y++)
	    {
		  SPIWrite(CMD, 0x80 | x);
		  SPIWrite(CMD, 0x40 | y);
		  for (line=0; line<BIN_PIXELS; line++)
		   {
	        SPIWrite(DATA,lines[y]);
		   }

	    }

}


// erase LCDscreen and screenmemory
void clear()
{
  int i,j;
  SPIWrite(CMD, 0x80); // set cursor at beginning
  SPIWrite(CMD, 0x40);
  for (i = 0; i < 9; i++) {
      for ( j = 0; j < 102; j++)
      {
          SPIWrite(DATA, 0x01);
      }
  }
  setCursor(0, 0);
}

void LCD_Init()
{

	SPIWrite(CMD, 0x21);  //Function set PD=0,H1=0,H0=1
    SPIWrite(CMD, 0x10+0x03);  //SET Bias system
    SPIWrite(CMD, 0X80+0x20);  //SET V0
    SPIWrite(CMD, 0x20);  //Function set PD=0 ,H1=0, H0=0
    SPIWrite(CMD, 0x05);  //Set VLCD Range(PRS) 0x04 = VLo-low, 0x05 VO-high
    SPIWrite(CMD, 0x0C);  //Display control D=1 E=0 (Normal) 0x0C = normal display 0x0D = reversed
    SPIWrite(CMD, 0x40);  //Set Y startingline BIT6        line0=0x40
    SPIWrite(CMD, 0x80);  //Set y startingline BIT5-0  line0=0x80
    SPIWrite(CMD, 0x28);  //Function set PD=0,H1=0,H0=0 reverse directions
    clear(); // clear the screen
        
}

//
// The ADC storage array
//
unsigned long g_ulADCValues[NUM_SAMPLES];
float32_t g_fFFTResult[NUM_SAMPLES * 2];

//
// FFT and CMSIS config structures
//
arm_rfft_instance_f32 fftStructure;
arm_cfft_radix4_instance_f32 cfftStructure;

//
// Hamming window, used to prepare samples for fft and correct for the fact
// we're using an algorithm meant for a continuous, infinite signal on a
// signal that is finite and not always continuous.
//
extern float ti_hamming_window_vector[NUM_SAMPLES];

//
// The range in Hertz of each frequency bin
//
float g_HzPerBin;

//
// Flag from the uDMA engine signaling that data is ready to be processed
//
volatile unsigned char g_ucDataReady=0;

//
// The count of uDMA errors.  This value is incremented by the uDMA error
// handler.
//
static volatile unsigned long g_uluDMAErrCount = 0;

//
// The count of times various uDMA error conditions detected.
//
static volatile unsigned long g_ulBadPeriphIsr1 =0; // CHANGED CORB
static volatile unsigned long g_ulBadPeriphIsr2 =0; // CHANGED CORB

//
// The character array used to update the LED display
//
static float32_t LEDPower[NUM_F_BINS];

unsigned int LEDDisplay[NUM_F_BINS];
unsigned int  LEDFreqBreakpoints[NUM_F_BINS + 1];

//*****************************************************************************
//
// Private function declarations
//
//*****************************************************************************


//*****************************************************************************
//
// The interrupt handler for Timer0A.  Originally, this was used to display
// debug information once per second.  Keeping this around for debug, but in
// the final system, this isn't necessary, as ADC capture is triggered at the
// HW level, alleviating the need for a software interrupt handler to trigger.
//
//*****************************************************************************
void
Timer0AIntHandler(void)
{
    // Clear the timer interrupt.
    //
    TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
}


//*****************************************************************************
//
// The interrupt handler for Timer1A.  Used for refreshing the LED display.
//
//*****************************************************************************
void
Timer1AIntHandler(void)
{
    unsigned long ulData;
    static unsigned char ucLED = 0;

    //
    // Clear the timer interrupt.
    //
    TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
    ulData = LEDDisplay[ucLED];

    // check for display overflows
if (ulData>DISPLAYMAX)
      {ulData=DISPLAYMAX;}
    
#if SCREENDIR==HORZ
    setCursor(0,ucLED);
    
    char i;
    for (i = 0; i < ulData; i++) {
              SPIWrite(DATA, 0xFF);
          }
    for (i=ulData; i< DISPLAYMAX; i++)
    {
        SPIWrite(DATA, 0x00);	
    }

#endif

#if SCREENDIR==VERT
    Vbar(ucLED,ulData);
#endif

	//
	// Move on to the next display element
	//
	ucLED++;
	if(ucLED >= NUM_F_BINS)
	{
		ucLED = 0;
	}

	//
	// Reset the timer to trigger again on the next refresh cycle
	//
	TimerLoadSet(TIMER1_BASE, TIMER_A, SysCtlClockGet()/REFRESH_RATE);
	TimerEnable(TIMER1_BASE, TIMER_A);
}

//*****************************************************************************
//
// The interrupt handler for the ADC sequencer used to capture all audio data.
// This handler is called each time the uDMA completes a full ADC->memory copy.
// Yes, that's a bit confusing... the uDMA interrupt is only triggered when the
// uDMA behaves abnormally.  When the uDMA engine completes its transfer, it
// calls the interrupt of whatever module fed it the data, not the uDMA
// interrupt.
//
//*****************************************************************************
void
ADC3IntHandler(void)
{
	unsigned long ulStatus;
	static unsigned long uluDMACount = 0;
	static unsigned long ulDataXferd = 0;
	unsigned long ulNextuDMAXferSize = 0;

	//
	// Clear the ADC interrupt
	//
	ADCIntClear(ADC0_BASE, 3);

	//
	// Disable the sampling timer
	//
	TimerDisable(TIMER0_BASE, TIMER_A);

	//
	// If the channel's not done capturing, we have an error
	//
	if(uDMAChannelIsEnabled(UDMA_CHANNEL_ADC3))
	{
		g_ulBadPeriphIsr2++;
		ADCIntDisable(ADC0_BASE, 3);
		IntPendClear(INT_ADC0SS3);
		return;
	}

	//
	// Verify count remaining is 0.
	//
	ulStatus = uDMAChannelSizeGet(UDMA_CHANNEL_ADC3);
	if(ulStatus)
	{
		g_ulBadPeriphIsr1++;
		return;
	}

	//
	// If our sample size can be greater than our maximum uDMA transfer
	// size, we might need to set up another uDMA transfer before signaling
	// that we are ready to process the data.
	//
	uluDMACount++;
	ulDataXferd += UDMA_XFER_MAX;
	if(NUM_SAMPLES > ulDataXferd)
	{
		//
		// Figure out how many more uDMA transfers are required to completely
		// fill our sample array, which will tell us what size we need our next
		// uDMA transfer to be
		//
		if((NUM_SAMPLES - ulDataXferd) > UDMA_XFER_MAX)
		{
			ulNextuDMAXferSize = UDMA_XFER_MAX;
		}
		else
		{
			ulNextuDMAXferSize = NUM_SAMPLES - ulDataXferd;
		}
		uDMAChannelTransferSet(UDMA_CHANNEL_ADC3 | UDMA_PRI_SELECT,
							   UDMA_MODE_BASIC,
							   (void *)(ADC0_BASE + ADC_O_SSFIFO3 +
									    (0x20 * UDMA_ARB_1)),
							   g_ulADCValues + (UDMA_XFER_MAX * uluDMACount),
							   ulNextuDMAXferSize);
		uDMAChannelEnable(UDMA_CHANNEL_ADC3);
		TimerLoadSet(TIMER0_BASE, TIMER_A,
					 SysCtlClockGet()/(SAMPLING_FREQ - 1));
		TimerEnable(TIMER0_BASE, TIMER_A);
	}
	else
	{
		//
		// Since this transfer is now done, disable the interrupt so the
		// handler is not called any more until set up again.  Also need
		// to unpend in case it happened again since we entered this
		// handler
		//
		uluDMACount = 0;
		ulDataXferd = 0;
		ADCIntDisable(ADC0_BASE, 3);
		IntPendClear(INT_ADC0SS3);
		g_ucDataReady = 1;
	}
}


//*****************************************************************************
//
// The interrupt handler for uDMA errors.  This interrupt will occur if the
// uDMA encounters a bus error while trying to perform a transfer.  Hopefully,
// this code will never be executed.
//
//*****************************************************************************
void
uDMAErrorHandler(void)
{
    unsigned long ulStatus;

    //
    // Check for uDMA error bit
    //
    ulStatus = uDMAErrorStatusGet();

    //
    // If there is a uDMA error, then clear the error and increment
    // the error counter.
    //
    if(ulStatus)
    {
        uDMAErrorStatusClear();
        g_uluDMAErrCount++;
    }
}

//*****************************************************************************
//
// Initialize the signal capture chain.
// ADC is initialized to capture based on the sampling timer
// uDMA is set up to transfer the samples from the ADC to the global sample
//   array each time a sample is captured by the ADC.
//
// The end result is a very efficient, 90% hardware handled process in which
// the software says "give me a sample" and the hardware interrupts a short
// time later with the data nicely arranged in a convenient location.  I'm not
// gonna lie... this is pretty awesome.
//
//*****************************************************************************
void
InitADC3Transfer(void)
{
    unsigned int uIdx;

    g_ucDataReady = 0;

    //
    // Init buffers
    //
    for(uIdx = 0; uIdx < NUM_SAMPLES; uIdx++)
    {
    	g_ulADCValues[uIdx] = 0;
    }

    //
	// Configure and enable the uDMA controller
	//
	SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
	IntEnable(INT_UDMAERR);
	uDMAEnable();
	uDMAControlBaseSet(ucControlTable);
	
    //
    // Configure the ADC to capture one sample per sampling timer tick
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    SysCtlPeripheralReset(SYSCTL_PERIPH_ADC0);
    ADCSequenceConfigure(ADC0_BASE, ADC_SEQUENCER, ADC_TRIGGER_TIMER, 0);
    ADCSequenceStepConfigure(ADC0_BASE, ADC_SEQUENCER, 0, ADC_CTL_CH0 |
    							 ADC_CTL_IE | ADC_CTL_END);

    //
    // Enable the sequencer
    //
    ADCSequenceEnable(ADC0_BASE, ADC_SEQUENCER);
    ADCIntEnable(ADC0_BASE, ADC_SEQUENCER);

    //
    // Configure the DMA channel
    //
    uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC3,
								UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
								UDMA_ATTR_HIGH_PRIORITY |
								UDMA_ATTR_REQMASK);
    uDMAChannelControlSet(UDMA_CHANNEL_ADC3 | UDMA_PRI_SELECT,
						  UDMA_SIZE_32 | UDMA_SRC_INC_NONE |
						  UDMA_DST_INC_32 | UDMA_ARB_1);

    //
    // The uDMA engine has an upper limit of the number of transfers it can
    // complete before it must be configured for a new transfer.  As a result,
    // we need to configure the uDMA engine to transfer as many samples as
    // possible, up to its maximum amount
    //
    if(NUM_SAMPLES > UDMA_XFER_MAX)
    {
    	uDMAChannelTransferSet(UDMA_CHANNEL_ADC3 | UDMA_PRI_SELECT,
							   UDMA_MODE_BASIC,
							   (void *)(ADC0_BASE + ADC_O_SSFIFO3 +
									   (0x20 * UDMA_ARB_1)),
							   g_ulADCValues, UDMA_XFER_MAX);
    }
    else
    {
    	uDMAChannelTransferSet(UDMA_CHANNEL_ADC3 | UDMA_PRI_SELECT,
							   UDMA_MODE_BASIC,
							   (void *)(ADC0_BASE + ADC_O_SSFIFO3 +
									   (0x20 * UDMA_ARB_1)),
							   g_ulADCValues, NUM_SAMPLES);
    }

    //
    // Enable the DMA channel
    //
    uDMAChannelEnable(UDMA_CHANNEL_ADC3);
}

//*****************************************************************************
//
// Initialize basic stuff:
//	processor
//  UART0(debug)
//
//*****************************************************************************
void
InitBasics(void)
{
	//
	// Enable lazy stacking for interrupt handlers.  This allows floating-point
	// instructions to be used within interrupt handlers, but at the expense of
	// extra stack usage.
	//
	ROM_FPULazyStackingEnable();

	//
	// Set the clocking to run directly from the crystal.
	//
	ROM_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ |
					   SYSCTL_OSC_MAIN);

	//
	// Initialize the UART.
	//
	
	//
	// Hello!
	//
	

}





//*****************************************************************************
//
// Initialize the SSI interface used to communicate with the Olimex LED panel
// Originally, I was using the software SSI lib to do this, hence the HW in the
// name.
//
//*****************************************************************************
void
InitHWSSI()
{
	//
	// Initialize the 3 pins we will need for SPI communication with the LCD
	//
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI2);
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
	// connect CS to pin E5
	GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_5);
	GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_5, 0);
    // Connect SPI to PB4 (clock) and PB7(TX)
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
	GPIOPinConfigure(GPIO_PB4_SSI2CLK);
	GPIOPinConfigure(GPIO_PB7_SSI2TX);
	GPIOPinTypeSSI(GPIO_PORTB_BASE, SSI_CLK | SSI_TX);

	//
	// Configure SSI2
	//
	SSIConfigSetExpClk(SSI2_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
					   SSI_MODE_MASTER, SysCtlClockGet()/2, 16);

	//
	// Enable the SSI module.
	//
	SSIEnable(SSI2_BASE);

	// initialize the LCD
	LCD_Init();

}

//*****************************************************************************
//
// Initialize the timer that will trigger the ADC captures
//	Timer0 @ 44.6k kHz (macro'd) sampling frequency
//
//*****************************************************************************
void
InitSamplingTimer()
{
    //
    // Set up timer0A to be a one shot that interrupts at 44.6kHz
    // Set up timer1A to be a 32 bit timer that interrupts at 1/sec
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
    TimerControlTrigger(TIMER0_BASE, TIMER_A, true);
    TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet()/(SAMPLING_FREQ - 1));
    IntEnable(INT_TIMER0A);
    TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
	TimerEnable(TIMER0_BASE, TIMER_A);
}

//*****************************************************************************
//
// Initialize the timer that will cause the LED display to refresh
//	Timer1 @ 1 kHz (macro'd) refresh rate (1 column per refresh)
//
//*****************************************************************************
void
InitDisplayTimer()
{
	//
	// Set up timer1A to be the display timer, interrupting at 15 Hz
	//
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
    TimerConfigure(TIMER1_BASE, TIMER_CFG_ONE_SHOT);
    TimerLoadSet(TIMER1_BASE, TIMER_A, SysCtlClockGet()/REFRESH_RATE);
    IntEnable(INT_TIMER1A);
    TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
    TimerEnable(TIMER1_BASE, TIMER_A);
}

//*****************************************************************************
//
// Initialize the digital signal processing engine.
//
//*****************************************************************************
void
InitDSP(void)
{
	volatile int i;

	//
	// set our frequency range breakpoints
	// TODO: we generate all of these with a separate generic algorithm; why
	// don't we just put that algorithm in here and calculate these values on
	// init?
	//

	char steps=(DISPLAYFFT)/(NUM_F_BINS);

	// automatically calculate the breakpoints based on the number of frequency bins.

	i=0;
	while (i<NUM_F_BINS+1)
		{
			LEDFreqBreakpoints[i] = 1+steps*i;
			i++;
		}

	//
	// Call the CMSIS real fft init function
	//

	arm_rfft_init_f32(&fftStructure, &cfftStructure, FFTSIZE, INVERT_FFT,  BIT_ORDER_FFT);

	g_HzPerBin = (float)SAMPLING_FREQ / (float)NUM_SAMPLES;
}

//*****************************************************************************
//
// Run the DSP calculations on the input vector.
//
// Step 1: multiply vector by hamming window
// Step 2: get fast fourier transform of samples
// Step 3: get complex power of each element in fft output
// Step 4: calculate average power in each LED range of bins
// Step 5: compare to previously observed maximum power, and set global LED
//		   array for that LED column accordingly
// Step 5: ???
// Step 6: Profit
//
//*****************************************************************************
void
ProcessData(void)
{
	uint32_t i;
	uint32_t j;
	float32_t power,powerhi;
	volatile float32_t maxValue;

	uint32_t dummy;

	//
	// Ugly, ugly, ugly part where we have to move the ul samples into a float
	// array because the fixed point fft functions in CMSIS seem to be not
	// working.  While we're at it, we might as well center the samples around
	// 0, as the CMSIS algorithm seems to like that.
	//
	for(i=0;i<NUM_SAMPLES;i++)
	{
		g_fFFTResult[i] = ((float)g_ulADCValues[i] - (float)0x800);
	}

	// if you want you can Multiply samples by a hamming window, not used by CORB
	//
	//arm_mult_f32(g_fFFTResult, ti_hamming_window_vector, g_fFFTResult, NUM_SAMPLES);

	// Calculate FFT on samples
	//
	arm_rfft_f32(&fftStructure, g_fFFTResult, g_fFFTResult);

	// Calculate complex power of FFT results
	//
	arm_cmplx_mag_f32(g_fFFTResult, g_fFFTResult, NUM_SAMPLES); // CORB:CODE CHANGED was NUM_SAMPLES*2

    // set max to 100000 to avoid div by zero and too exagerated display of lower values when only lowpower noise is coming in
    maxValue=100000;

	// Calculate power stored in the frequency band each LED represents
	//
	j = LEDFreqBreakpoints[0];
	for(i=1;i<NUM_F_BINS + 1;i++)
	{
		//
		// Find the maximum power of all the LED bins in the freq array. j is
		// the index of the lowest bin used for this LED.  To get the size of
		// range to take the average of, we do highest indexed bin,
		// LEDfreqBreakPoints[i], minus the lowest indexed bin, j, + 1.
		//
		arm_max_f32(g_fFFTResult+j, LEDFreqBreakpoints[i]-j+1, &powerhi, &dummy);
		// other functions
		//arm_mean_f32(g_fFFTResult+j, LEDFreqBreakpoints[i]-j + 1, &powerhi);
		//arm_min_f32(g_fFFTResult+j, LEDFreqBreakpoints[i]-j+1, &powerlo, &dummy);

		power=powerhi;

		//estimate the maximum peak excepting the 1st bin
		if ((i>1)&&(power>maxValue))
		{
			maxValue=power;
		}


		LEDPower[i - 1] = power;
		j = LEDFreqBreakpoints[i] + 1;
	}

	//
	// Update the LED Display array based on how much power is in the band for
	// each LED
	//
	for(i=0;i<NUM_F_BINS;i++)
	{
	//
	// Normalize currently observed power by maximum observed power
		LEDDisplay[i] = (unsigned int)(LEDPower[i] / maxValue * DISPLAYMAX);
	}

	//
	// Clear the data ready bit and set up the next DMA transfer
	//
	g_ucDataReady = 0;
    if(NUM_SAMPLES > UDMA_XFER_MAX)
    {
    	uDMAChannelTransferSet(UDMA_CHANNEL_ADC3 | UDMA_PRI_SELECT,
							   UDMA_MODE_BASIC,
							   (void *)(ADC0_BASE + ADC_O_SSFIFO3 +
									   (0x20 * UDMA_ARB_1)),
							   g_ulADCValues, UDMA_XFER_MAX);
    }
    else
    {
    	uDMAChannelTransferSet(UDMA_CHANNEL_ADC3 | UDMA_PRI_SELECT,
							   UDMA_MODE_BASIC,
							   (void *)(ADC0_BASE + ADC_O_SSFIFO3 +
									   (0x20 * UDMA_ARB_1)),
							   g_ulADCValues, NUM_SAMPLES);
    }

    //
    // Enable the timer and start the sampling timer
    //
	uDMAChannelEnable(UDMA_CHANNEL_ADC3);
    TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet()/(SAMPLING_FREQ - 1));
	TimerEnable(TIMER0_BASE, TIMER_A);
}

//*****************************************************************************
//
// The main function.  DO EVERYTHING.
//
//*****************************************************************************

void main(void)
{
	//
	// Initialize all peripherals
	//
    InitBasics();
    InitDSP();
    InitHWSSI();
    InitSamplingTimer();
    InitDisplayTimer();
	InitADC3Transfer();

	//
	// Once ADC3 interrupts are enabled, our capture engine will start churning
	//
	IntEnable(INT_ADC3);

    while(1)
    {
    	//
    	// TODO: Figure out how processor sleeping works and add a sleep here.
    	// We only take action after an interrupt occurs anyway, so there's no
    	// sense in sitting here polling a flag that gets set in an interrupt
    	// if we can instead just sleep until an interrupt occurs.
    	//
    	//CPUwfi();

    	//
    	// Make sure uDMA hasn't exploded
    	//
    	if(g_ulBadPeriphIsr1 || g_ulBadPeriphIsr2)
    	{
    		   		while(1);
    	}

    	//
    	// If we have new audio data, handle it
    	//
    	if(g_ucDataReady)
    	{
    		ProcessData();
    	}
    }
}

 

 

Link to post
Share on other sites

Here's a few screenshots, ive changed the code so I can better understand whats going on with the spectrum. 

It now also records for each frequency-bin the maximum power recorded, this is done in the same kind of fashion as in the original code. The maxima in each frequency bin slowly decay automatically but stay long enough for you to see something happening in the spectrum albeit shortly.

 

The more or less standard approach delivers an image like this

post-290-14264605171331_thumb.jpg

After beeping in front of a microphone with a kitchentimer the actual measured powers for each bin are low except for the BIN that had a signal coming in just before the picture was token.

 

Another way to show the data is not to show the peak as a separate bar but to create a bar that starts at the active value  instead of starting at zero and ends at the maximum power recorded for a bin. This allows you to see directly which bins have been active recently and especially when showing many frequencybins this is a lot easier to understand than seeing a cloud of dots that represent the maxima.

post-290-14264605171458_thumb.jpg

Again after using the kitchentimer

 

And finally the same graphtype but now with a snap of a finger.

post-290-14264605171605_thumb.jpg

 

cheers

 

Cor

 

Link to post
Share on other sites

Here's a few screenshots, ive changed the code so I can better understand whats going on with the spectrum. 

It now also records for each frequency-bin the maximum power recorded, this is done in the same kind of fashion as in the original code. The maxima in each frequency bin slowly decay automatically but stay long enough for you to see something happening in the spectrum albeit shortly.

 

hahaha! I was wondering how he did that. Thanks for the explanation and good work!

Link to post
Share on other sites

Just changed the HW part of this project based on a simple electret microphone and LM386 opamp scheme from 

http://lowvoltage.wordpress.com/2011/05/15/lm386-mic-amp/

 

This allows you to create a very sensitive setup, fingersnaps at 2 meters can be recorded over the spectrum. When I whistle (dont come to listen) I can see very clearly which frequencies I am "misusing". The "kitchentimer" I used for testing (beeps) can be sensed from over several meters away.

 

Next up is sensing frequencies above 20 Khz since that is my special aim with this project, detecting ultrasonic beeps from bats.

 

cheers

CorB

Link to post
Share on other sites

After changing the HW (see previous) to an electret microphone it was time to see if the electret setup can hear high frequencies properly.

For this I have programmed an MSP430G2553 (launchpad) to simply toggle an IO pin with a delay in between the hi and lo stages. Connected a small 8ohm speaker to that and started testing. I wasnt sure if the speaker would go high enough for my project but it seems it does ... I cant hear anything coming from the speaker but I did see the electret picking up a signal at a constant frequency. Next question was ... how high is the frequency I am producing actually. Ive first turned on my old bat-detector that allows my to tune the receiver to a signal. Turns out there was something like 45Khz coming from the speaker and that 45Khz also was in line with my observations on the spectrumgraphs. Using the spectrum software with a maximum detectable frequency of 60Khz the peak was just over halfway the graph. My batdetector wasnt calibrated recently so it might be slightly off.

 

Unfortunately there are no real bats around yet but I will probably program the MSP430 to generate a frequency sweeper so I can move to the next stage ... showing an incoming ultrasonic beep as a spectrumgraph captured in time. 

 

Onwards again !

 

CorB

 

EDIT: some pictures

 

ultrasonic peak (graph shows frequencies 0 -60000Khz)

post-290-14264605174756_thumb.jpg

ultrasonic peak (graph shows frequencies 0 -80000Khz)

post-290-14264605174882_thumb.jpg

 

In both graphs you can see some dynamic/static noise on the rightside of the graph. That probably hsa to do with the fact that the whole setup of the microphone/opamp is currently setup on a breadboard.

 

 

Edited by CorB
Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...