Part Number: MSP-EXP432P401R
Tool/software: TI C/C++ Compiler
Real-time FIR filtering with triple buffering
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h> //Exact-width integer types
#include <ti\devices\msp432p4xx\driverlib\driverlib.h> //Driver library
#define CLOCK_HF 48000000 //48MHz
#define CLOCK_LF 32000 //32kHz
#define TIMER0_FREQ 1 //unit: Hz
#define SAMPLE_FREQ 8000 //Frequency in Hz, common sampling rates for digital audio: 8000, 32000, 44100, 48000
#define MCP4921_SPI_BITRATE 20e6 //needs to > SAMPLE_FREQ*16, and < SMCLK/2, max 20M
#define MCP4921_SPI_DATA_MASK 0x0FFF //See datasheet, MCP4921
#define MCP4921_SPI_CTRL_MASK 0x7000 //See datasheet, MCP4921
#define RED_LED GPIO_PIN0
#define GREEN_LED GPIO_PIN1
#define BLUE_LED GPIO_PIN2
#define NUM_DISP_TEXT_LINE 4
// Definitions for triple buffering.
#define NUM_DATA_BUFFER 3
#define DATA_BUFFER_LEN 200
#define FILTER_LEN_M 28
//Function prototypes
void initDevice_HFXT(void);
void initGPIO(void);
void initTimer(void);
void initUART(void);
void initADC14(void);
void initSPI(void);
void initDataBuffer(void);
void processData(void);
void uart0_transmitStr(const char *str);
//Global variables
uint32_t clockMCLK, clockSMCLK;
uint8_t currentLED = RED_LED;
volatile uint32_t dataBuffer[NUM_DATA_BUFFER][DATA_BUFFER_LEN];
volatile uint32_t buf_inputIndex=0, buf_outputIndex=1, buf_dataIndex=2;
volatile bool buf_dataReady=false, buf_overrunError=false;
float filter_Bcoef[FILTER_LEN_M+1] =
{
-0.0014541062, -0.0012749733, -0.0009739225, +0.0000396414, +0.0024743259,
+0.0070030369, +0.0140943302, +0.0238646359, +0.0359851042, +0.0496673460,
+0.0637372145, +0.0767880565, +0.0873882711, +0.0943060927, +0.0967098938,
+0.0943060927, +0.0873882711, +0.0767880565, +0.0637372145, +0.0496673460,
+0.0359851042, +0.0238646359, +0.0140943302, +0.0070030369, +0.0024743259,
+0.0000396414, -0.0009739225, -0.0012749733, -0.0014541062
};
const char *terminalDisplayText[NUM_DISP_TEXT_LINE] =
{
"\r\n",
"Real-time FIR filtering with triple buffering\r\n",
"R: Red, G: Green, B: Blue, H: Help\r\n",
"> "
};
void main(void)
{
uint32_t i;
uint8_t data;
initDevice_HFXT();
initGPIO();
initTimer();
initUART();
initADC14();
initSPI();
initDataBuffer();
Interrupt_enableMaster();
Timer32_startTimer(TIMER32_0_BASE, false);
Timer32_startTimer(TIMER32_1_BASE, false);
// Initial display on terminal.
for(i=0; i<NUM_DISP_TEXT_LINE; i++)
{
uart0_transmitStr(terminalDisplayText[i]);
}
while(1)
{
if(UART_getInterruptStatus(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG))
{
data = UART_receiveData(EUSCI_A0_BASE);
UART_clearInterruptFlag(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG);
switch(data)
{
case 'R':
case 'r':
currentLED = RED_LED;
uart0_transmitStr("Blink red LED.\r\n> ");
break;
case 'G':
case 'g':
currentLED = GREEN_LED;
uart0_transmitStr("Blink green LED.\r\n> ");
break;
case 'B':
case 'b':
currentLED = BLUE_LED;
uart0_transmitStr("Blink blue LED.\r\n> ");
break;
case 'H':
case 'h':
for(i=0; i<NUM_DISP_TEXT_LINE; i++)
{
uart0_transmitStr(terminalDisplayText[i]);
}
break;
}
} //end of if
if(buf_overrunError)
{
//This error indicates the processData() routine takes too much time to complete.
//Optimize this routine to run faster or set the BUFFER_LEN larger.
//uart0_transmitStr("Error: buffer overrun!\r\n");
printf("Error: buffer overrun!\r\n");
buf_overrunError = false;
}
if(buf_dataReady)
{
processData();
buf_dataReady = false;
}
} //end of while
}
void initDevice_HFXT(void)
{
WDT_A_holdTimer(); //Stop watchdog timer
//Change VCORE to 1 to support a frequency higher than 24MHz.
//See data sheet for Flash wait-state requirement for a given frequency.
PCM_setPowerState(PCM_AM_DCDC_VCORE1);
FlashCtl_setWaitState(FLASH_BANK0, 1);
FlashCtl_setWaitState(FLASH_BANK1, 1);
FPU_enableModule();
FPU_enableLazyStacking(); //Required to use FPU within ISR.
//Configure PJ.2 and PJ.3 in HFXT mode.
//Initialize external clock sources HFXT.
GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_PJ, GPIO_PIN2|GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION);
CS_setExternalClockSourceFrequency(CLOCK_LF, CLOCK_HF);
CS_startHFXT(false);
CS_initClockSignal(CS_MCLK, CS_HFXTCLK_SELECT, CS_CLOCK_DIVIDER_1);
CS_initClockSignal(CS_HSMCLK, CS_HFXTCLK_SELECT, CS_CLOCK_DIVIDER_16);
CS_initClockSignal(CS_SMCLK, CS_HFXTCLK_SELECT, CS_CLOCK_DIVIDER_1); //48MHz to support SPI
clockMCLK = CS_getMCLK();
clockSMCLK = CS_getSMCLK();
}
void initGPIO(void)
{
//Configure P2.0, P2.1, P2.2 as output.
//P2.0, P2.1, P2.2 are connected to a RGB tri-color LED on LaunchPad.
GPIO_setAsOutputPin(GPIO_PORT_P2, GPIO_PIN0|GPIO_PIN1|GPIO_PIN2);
}
void initTimer(void)
{
// TIMER32_0 to blink heartbeat LED
Timer32_initModule(TIMER32_0_BASE, TIMER32_PRESCALER_1, TIMER32_32BIT, TIMER32_PERIODIC_MODE);
Timer32_setCount(TIMER32_0_BASE, clockMCLK/TIMER0_FREQ - 1);
Timer32_enableInterrupt(TIMER32_0_BASE);
Interrupt_enableInterrupt(INT_T32_INT1); //Enable Timer32_0 interrupt in the interrupt controller.
// TIMER32_1 to do input/ADC and output/DAC
Timer32_initModule(TIMER32_1_BASE, TIMER32_PRESCALER_1, TIMER32_32BIT, TIMER32_PERIODIC_MODE);
Timer32_setCount(TIMER32_1_BASE, clockMCLK/SAMPLE_FREQ - 1);
Timer32_enableInterrupt(TIMER32_1_BASE);
Interrupt_enableInterrupt(INT_T32_INT2); //Enable Timer32_1 interrupt in the interrupt controller.
}
void initUART(void)
{
//Configuration for 48MHz SMCLK, 115200 baud rate.
//Calculated using the online calculator that TI provides at:
//software-dl.ti.com/.../index.html
const eUSCI_UART_Config config =
{
EUSCI_A_UART_CLOCKSOURCE_SMCLK, //SMCLK Clock Source, 48MHz
26, //BRDIV
0, //UCxBRF
111, //UCxBRS
EUSCI_A_UART_NO_PARITY, //No Parity
EUSCI_A_UART_LSB_FIRST, //MSB First
EUSCI_A_UART_ONE_STOP_BIT, //One stop bit
EUSCI_A_UART_MODE, //UART mode
EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION //Oversampling
};
// Configure GPIO pins for UART. RX: P1.2, TX:P1.3.
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1, GPIO_PIN2|GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION);
UART_initModule(EUSCI_A0_BASE, &config);
UART_enableModule(EUSCI_A0_BASE);
}
//Use P6.1/A14 to sample
void initADC14(void)
{
ADC14_enableModule();
ADC14_initModule(ADC_CLOCKSOURCE_SMCLK, ADC_PREDIVIDER_1, ADC_DIVIDER_1, ADC_NOROUTE);
//Configure P6.1 as A14
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P6, GPIO_PIN1, GPIO_TERTIARY_MODULE_FUNCTION);
ADC14_setResolution(ADC_12BIT);
ADC14_configureSingleSampleMode(ADC_MEM0, true); //repeatMode = true
ADC14_configureConversionMemory(ADC_MEM0, ADC_VREFPOS_AVCC_VREFNEG_VSS, ADC_INPUT_A14, false);
//See TechRef Section 20.2.6 for sample timing consideration.
ADC14_enableSampleTimer(ADC_MANUAL_ITERATION);
ADC14_setSampleHoldTime(ADC_PULSE_WIDTH_4, ADC_PULSE_WIDTH_4);
ADC14_enableConversion();
}
void initSPI(void)
{
const eUSCI_SPI_MasterConfig spiMasterConfig =
{
EUSCI_B_SPI_CLOCKSOURCE_SMCLK, //SMCLK Clock Source
clockSMCLK, //Clock source frequency
MCP4921_SPI_BITRATE, //desiredSpiClock
EUSCI_B_SPI_MSB_FIRST, //MSB First
EUSCI_B_SPI_PHASE_DATA_CHANGED_ONFIRST_CAPTURED_ON_NEXT, //Phase
EUSCI_B_SPI_CLOCKPOLARITY_INACTIVITY_LOW, //Low polarity
EUSCI_B_SPI_3PIN //3Wire SPI Mode
};
//P1.5: SCK, P1.6: SDI, P5.2: CS
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1, GPIO_PIN5|GPIO_PIN6, GPIO_PRIMARY_MODULE_FUNCTION);
GPIO_setAsOutputPin(GPIO_PORT_P5, GPIO_PIN2);
GPIO_setOutputHighOnPin(GPIO_PORT_P5, GPIO_PIN2);
//Configuring SPI in 3wire master mode
SPI_initMaster(EUSCI_B0_BASE, &spiMasterConfig);
//Enable SPI module
SPI_enableModule(EUSCI_B0_BASE);
}
void initDataBuffer(void)
{
uint32_t i, j;
for(i=0; i<NUM_DATA_BUFFER; i++)
{
for(j=0; j<DATA_BUFFER_LEN; j++)
{
dataBuffer[i][j] = 0;
}
}
}
void processData(void)
{
static float data[DATA_BUFFER_LEN + FILTER_LEN_M] = {0}; //augmented data frame, initialized to 0
float y;
uint32_t *pBuf = (uint32_t *) dataBuffer[buf_dataIndex];
uint32_t i, j, k;
// Copy data from the data frame to the augmented data frame.
// Start to fill ater FILTER_LEN_M elements.
for(i=0, j=FILTER_LEN_M; i<DATA_BUFFER_LEN; i++, j++)
{
data[j] = (float) pBuf[i];
}
// FIR filtering: y[n] = sum_{i=0}^{M}(B[i]x[n-i]).
for(i=0; i<DATA_BUFFER_LEN; i++)
{
y = 0;
for(j=0, k=i; j<=FILTER_LEN_M; j++, k++)
{
y += filter_Bcoef[j]*data[k];
}
pBuf[i] = (uint32_t) y;
}
// Save the last FILTER_LEN_M number of samples to the beginning of the augmented data frame.
for(i=0; i<FILTER_LEN_M; i++)
{
data[i] = data[DATA_BUFFER_LEN+i];
}
}
//Transmit a string through UART0.
void uart0_transmitStr(const char *str)
{
uint32_t len, i=0;
len = strlen(str);
while(i < len)
{
UART_transmitData(EUSCI_A0_BASE, str[i++]);
while(!UART_getInterruptStatus(EUSCI_A0_BASE, EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT_FLAG));
UART_clearInterruptFlag(EUSCI_A0_BASE, EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT_FLAG);
}
}
//Timer32_0 ISR
void T32_INT1_IRQHandler(void)
{
Timer32_clearInterruptFlag(TIMER32_0_BASE);
if(GPIO_getInputPinValue(GPIO_PORT_P2, GPIO_PIN0|GPIO_PIN1|GPIO_PIN2))
{
GPIO_setOutputLowOnPin(GPIO_PORT_P2, GPIO_PIN0|GPIO_PIN1|GPIO_PIN2);
}
else
{
GPIO_setOutputHighOnPin(GPIO_PORT_P2, currentLED);
}
}
//Timer32_1 ISR
void T32_INT2_IRQHandler(void)
{
static uint32_t sampleIndex=0;
uint32_t tmpIndex;
uint16_t data;
uint8_t byte1, byte0;
Timer32_clearInterruptFlag(TIMER32_1_BASE);
//Read from ADC.
ADC14_toggleConversionTrigger();
while(!(ADC_INT0 & ADC14_getInterruptStatus()));
ADC14_clearInterruptFlag(ADC_INT0);
dataBuffer[buf_inputIndex][sampleIndex] = ADC14_getResult(ADC_MEM0);
//Prepare data for SPI.
data = MCP4921_SPI_CTRL_MASK | (MCP4921_SPI_DATA_MASK & dataBuffer[buf_outputIndex][sampleIndex]);
byte1 = (uint8_t) (data>>8);
byte0 = (uint8_t) data;
//SPI is written with register-level operation to support 16-bit operation.
P5->OUT &= ~BIT2; //Set CS to LOW on SPI slave.
EUSCI_B0->TXBUF = byte1; //Transmit Byte 1
EUSCI_B0->TXBUF = byte0; //Transmit Byte 0
while(!(EUSCI_B0->IFG & EUSCI_B_IFG_TXIFG)); //Check if TX buffer is ready.
P5->OUT |= BIT2; //Set CS to HIGH on SPI slave.
//Update buffer indices and sample index.
if(++sampleIndex >= DATA_BUFFER_LEN)
{
sampleIndex = 0;
if(buf_dataReady)
{
buf_overrunError = true;
}
tmpIndex = buf_inputIndex;
buf_inputIndex = buf_outputIndex;
buf_outputIndex = buf_dataIndex;
buf_dataIndex = tmpIndex;
buf_dataReady = true;
}
}
problem 2. How to do FIR filter design with Matlab.
1• In Matlab, use “filterDesigner” to design a lowpass FIR equiripple filter, with order 50 =M , sampling frequency kHz 8=sF , passband cutoff frequency Hz 1500=passF , stopband cutoff frequency Hz 2000=stopF , and equal weight 1 for both passband and stopband. Export the designed filter to the Matlab workspace as a coefficient variable and plot its time domain unit sample response using the Matlab function “plot” and plot its frequency response using the Matlab function “freqz”.
2• Create a new CCS project “Lab9_2”. Add the sample code “Lab9_1_main.c” to the project. • The input and output setup of this lab is exactly same as Problem 1.
3• Replace the filter used in “Lab9_1_main.c” with the new FIR filter that you just design. Some of the parameter/variable values in “Lab9_1_main.c” will need to be changed according the specification of your new filter. • Repeat the filtering experiment procedure using the test signals in Problem 1 to test the implementation of your new FIR filter design. • Explain your code and experiment results in your report.
Problem 3. Real-time FIR filtering with Ping-Pong buffering.
• Create a new CCS project “Lab9_3”. • The input and output setup of this lab is exactly same as Problem 1. • Use the Ping-Pong buffering technique to implement the same real-time FIR filtering functionality as in Lab9_2. • Explain in your report how the Ping-Pong buffering based FIR filtering works. • Explain your own code in your report.