This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430FR6989: FFT analysis using DSPLib.h

Part Number: MSP430FR6989

Hello,

I am using the MSP430FR6989 and BMI160 IMU (Sensors BoosterPack). They are communicating over I2C. I am measuring the Z axis acceleration and trying to do the FFT on the received data. I have a test platform which is vibrating at a frequency of 3.5Hz. I'm using the FFT function from DSPLib.h. After the FFT is completed, I am taking the first 5 FFT values with the higiest amplitude and displaying them on the terminal, but I am always getting random frequency values. As I am not an expert on FFT algorithms, I am not sure what I am doing wrong. I would appreciate it greatly if someone could give some suggestions on how to fix this issue.

Thank you.

Here is my code:

 

#include <msp430.h> 
#include "driverlib.h"
#include "DSPLib.h"
#include <gpio.h>
#include <intrinsics.h>
#include <msp430fr5xx_6xxgeneric.h>
#include <stdint.h>
#include <stdio.h>

//new
//#include "myClocks.h"
//new

//******************************************************************************
// Defines *********************************************************************
//******************************************************************************
//Address if the BMI160
#define SLAVE_ADDR 0x69
//Maximum I2C buffer size
#define MAX_BUFFER_SIZE 20

//Number of FFT samples
#define SAMPLES 4096

/* Declare as persistent to move variable from RAM to FRAM */
#pragma PERSISTENT(input)
int16_t input[SAMPLES] = {0}; //Store samples

#pragma PERSISTENT(max)
int16_t max[SAMPLES] = {0}; //Store frequencies with maximum amplitudes

#pragma PERSISTENT(amp)
int16_t amp[SAMPLES] = {0}; //Store the maximum amplitudes

/* Temporary data array for processing */
DSPLIB_DATA(temp,4)
/* Declare as persistent to move variable to FRAM */
#pragma PERSISTENT(temp)
int16_t temp[3*SAMPLES/2] = {0};

//Global flags for sensor interrupts to avoid interrupt nesting
volatile int motion_trigger = 0;

/* Benchmark cycle counts */
volatile uint32_t cycleCount;



//new

#define UP              0x0010                          // Timer_A Up mode
#define CONTINUOUS      0x0020                          // Timer_A Continuous mode
#define ACLK            0x0100                          // Timer_A SMCLK source
#define DEVELOPMENT     0x5A80                          // Stop the watchdog timer
#define BOUNCE_DELAY    0xA000                          // Delay for Button Bounce
#define MS_10           400                             // Approximate value to count for 10ms
#define SMCLK           0x0200                          // Timer_A SMCLK source



//new



//******************************************************************************
// Frequency Functions *********************************************************
//******************************************************************************

void bubbleSort(int amp[], int max[], int n)
{
   int i, j, temp;
   for (i = 0; i < n-1; i++)
   {
       // Last i elements are already in place
       for (j = 0; j < n-i-1; j++)
       {
           if (amp[j] < amp[j+1])
           {
              temp = max[j];
              max[j] = max[j+1];
              max[j+1] = temp;

              temp = amp[j];
              amp[j] = amp[j+1];
              amp[j+1] = temp;
           }
       }
   }
}

void getMaximums(int16_t input[], int samples, int16_t max[], int16_t amp[])
{
    int i,j = 0;
    for(i=1; i<samples-1; i++)
        {
            if((input[i-1] < input[i]) && (input[i] > input[i+1]))
            {
                amp[j] = input[i];
                max[j++] = i;
            }
        }
    bubbleSort(amp, max, samples);
}

//******************************************************************************
// Timer Functions *************************************************************
//******************************************************************************

int delay(int count)
{
    if(TA1CTL & TAIFG)                                  // If Timer_1 is done counting
    {
        count = count-1;                                        // Decrement count
        TA1CTL = TA1CTL & (~TAIFG);                             // Reset Timer_1
    }
    return count;                                       // Return the value of count
} // end delay
//******************************************************************************
// UART Functions **************************************************************
//******************************************************************************
void UART_transmitString( char *pStr ) //Transmits a string over UART0
{
    while( *pStr )
    {
        while(!(UCA0IFG&UCTXIFG));
        UCA0TXBUF = *pStr;
        pStr++;
    }
}

//******************************************************************************
// I2C Functions ***************************************************************
//******************************************************************************
typedef enum I2C_ModeEnum{
    IDLE_MODE,
    NACK_MODE,
    TX_REG_ADDRESS_MODE,
    RX_REG_ADDRESS_MODE,
    TX_DATA_MODE,
    RX_DATA_MODE,
    SWITCH_TO_RX_MODE,
    SWITHC_TO_TX_MODE,
    TIMEOUT_MODE
} I2C_Mode;

/* Used to track the state of the software state machine*/
I2C_Mode MasterMode = IDLE_MODE;

uint8_t TransmitRegAddr = 0; //Register address for transmission
uint8_t ReceiveBuffer[MAX_BUFFER_SIZE] = {0}; //Buffer for received values
uint8_t RXByteCtr = 0; //Count received bytes
uint8_t ReceiveIndex = 0; //Index of received data
uint8_t TransmitBuffer[MAX_BUFFER_SIZE] = {0}; //Buffer for transmitted values
uint8_t TXByteCtr = 0; //Count transmitted bytes
uint8_t TransmitIndex = 0; //Index of transmitted data

void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count)
{
    uint8_t copyIndex = 0;
    for (copyIndex = 0; copyIndex < count; copyIndex++)
    {
        dest[copyIndex] = source[copyIndex];
    }
}

I2C_Mode I2C_Master_ReadReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t count)
{
    //printf("R\n");
    /* Initialize state machine */
    MasterMode = TX_REG_ADDRESS_MODE;
    TransmitRegAddr = reg_addr;
    RXByteCtr = count;
    TXByteCtr = 0;
    ReceiveIndex = 0;
    TransmitIndex = 0;

    /* Initialize slave address and interrupts */
    UCB1I2CSA = dev_addr;
    UCB1IFG &= ~(UCTXIFG + UCRXIFG);       // Clear any pending interrupts
    UCB1IE &= ~UCRXIE;                       // Disable RX interrupt
    UCB1IE |= UCTXIE;                        // Enable TX interrupt

    UCB1CTLW0 |= UCTR + UCTXSTT;             // I2C TX, start condition
    __bis_SR_register(LPM0_bits + GIE);              // Enter LPM0 w/ interrupts
 //   UCB1IE &= ~UCRXIE;                       // Disable RX interrupt


    return MasterMode;

}

I2C_Mode I2C_Master_WriteReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t count)
{
    /* Initialize state machine */
    MasterMode = TX_REG_ADDRESS_MODE;
    TransmitRegAddr = reg_addr;

    //Copy register data to TransmitBuffer
    CopyArray(reg_data, TransmitBuffer, count);

    TXByteCtr = count;
    RXByteCtr = 0;
    ReceiveIndex = 0;
    TransmitIndex = 0;

    /* Initialize slave address and interrupts */
    UCB1I2CSA = dev_addr;
    UCB1IFG &= ~(UCTXIFG + UCRXIFG);       // Clear any pending interrupts
    UCB1IE &= ~UCRXIE;                       // Disable RX interrupt
    UCB1IE |= UCTXIE;                        // Enable TX interrupt

    UCB1CTLW0 |= UCTR + UCTXSTT;             // I2C TX, start condition
    __bis_SR_register(LPM0_bits + GIE);              // Enter LPM0 w/ interrupts
    //printf("W\n");
    return MasterMode;
}

//******************************************************************************
// BMI160 Functions ************************************************************
//******************************************************************************
void bmi160_init(char FOC_axis)
{
    uint8_t writeData[1];
    //Read Chip ID, which is D1
    I2C_Master_ReadReg(SLAVE_ADDR, 0x00, 1);
    if(ReceiveBuffer[0] != 0xD1)
    {
        UART_transmitString(" Incorrect sensor chip ID ");
        printf("Incorrect sensor chip ID\n");
    }




    //Configure the accelerometer
    writeData[0]=0b00101100; //Set acc_us to 0 for off, and acc_bwp must then be 010. Set acc_odr to 1011(800Hz),1100(1600Hz),1000(100Hz),0001(25/32Hz)
    I2C_Master_WriteReg(SLAVE_ADDR, 0x40, writeData, 1);
    //Check if configuration worked
    I2C_Master_ReadReg(SLAVE_ADDR, 0x40, 1);
    if(ReceiveBuffer[0] != writeData[0])
    {
        UART_transmitString(" Accelerometer config failed ");
        printf("Accelerometer config failed\n");
    }
    //Set the range of the accelerometer
    writeData[0]=0b1000; //0b0011 for 2g, 0b0101 for 4g, 0b1000 for 8g
    I2C_Master_WriteReg(SLAVE_ADDR, 0x41, writeData, 1);
    //Check if range is set
    I2C_Master_ReadReg(SLAVE_ADDR, 0x41, 1);
    if(ReceiveBuffer[0] != writeData[0])
    {
        UART_transmitString(" Accelerometer range set failed ");
        printf("Accelerometer range set failed\n");
    }

    //Any motion setup

    //Set the successive slope threshold
    writeData[0]=0b00000001; //0b00000001 + 1 so two successive slopes
    I2C_Master_WriteReg(SLAVE_ADDR, 0x5F, writeData, 1);
    //Check if slope threshold is set
    I2C_Master_ReadReg(SLAVE_ADDR, 0x5F, 1);
    if(ReceiveBuffer[0] != writeData[0])
    {
        UART_transmitString(" Slope threshold not set ");
        printf("Slope threshold not set\n");
    }
    //Set trigger level
    writeData[0]=0b01001101; //15.63mg*value for 8g range, 0b00000000 gives 7.81mg
    I2C_Master_WriteReg(SLAVE_ADDR, 0x60, writeData, 1);
    //Check trigger is set
    I2C_Master_ReadReg(SLAVE_ADDR, 0x60, 1);
    if(ReceiveBuffer[0] != writeData[0])
    {
        UART_transmitString(" Trigger not set ");
        printf("Trigger not set\n");
    }

    //Double tap setup

    //Set single and double tap timings
    writeData[0] = 0b00000110; //Quiet of 30ms, shock of 50ms, double tap within 500ms
    I2C_Master_WriteReg(SLAVE_ADDR, 0x63, writeData, 1);
    //Read the timings
    I2C_Master_ReadReg(SLAVE_ADDR, 0x63, 1);
    if(ReceiveBuffer[0] != writeData[0])
    {
        UART_transmitString(" Tap timings not set ");
        printf("Tap timings not set\n");
    }
    //Set tap threshold
    writeData[0] = 0b0000; //125mg threshold
    I2C_Master_WriteReg(SLAVE_ADDR, 0x64, writeData, 1);
    //Read the threshold
    I2C_Master_ReadReg(SLAVE_ADDR, 0x64, 1);
    if(ReceiveBuffer[0] != writeData[0])
    {
        UART_transmitString(" Tap timings not set ");
        printf("Tap timings not set\n");
    }

    //Interrupt setup

    //Enable any-motion and double tap interrupts
    writeData[0] = 0b00010100; //Enables double tap and any-motion z interrupt
    I2C_Master_WriteReg(SLAVE_ADDR, 0x50, writeData, 1);
    //Read interrupt enable status
    I2C_Master_ReadReg(SLAVE_ADDR, 0x50, 1);
    if(ReceiveBuffer[0] != 0b00010100)
    {
        UART_transmitString("Interrupts not enabled");
        printf("Interrupts not enabled\n");
    }
    //Set pins
    writeData[0] = 0b10001000; //Output, push-pull, active low for int1 and int2
    I2C_Master_WriteReg(SLAVE_ADDR, 0x53, writeData, 1);
    //Read pin status
    I2C_Master_ReadReg(SLAVE_ADDR, 0x53, 1);
    if(ReceiveBuffer[0] != writeData[0])
    {
        UART_transmitString(" Pins not set ");
        printf("Pins not set\n");
    }
    //Set interrupts to temporary instead of latched (permanent till cleared)
    writeData[0] = 0b1101; //Temp for 1.28s
    I2C_Master_WriteReg(SLAVE_ADDR, 0x54, writeData, 1);
    //Check interrupts
    I2C_Master_ReadReg(SLAVE_ADDR, 0x54, 1);
    if(ReceiveBuffer[0] != writeData[0])
    {
        UART_transmitString(" Interrupts not temp ");
        printf("Interrupts not temp");
    }
    //Map any motion detection to Int1
    writeData[0] = 0b00000100; //Mapped any motion to int1
    I2C_Master_WriteReg(SLAVE_ADDR, 0x55, writeData, 1);
    //Check interrupt
    I2C_Master_ReadReg(SLAVE_ADDR, 0x55, 1);
    if(ReceiveBuffer[0] != writeData[0])
    {
        UART_transmitString(" Any motion not mapped ");
        printf("Any motion not mapped\n");
    }
    //Map double tap to Int2
    writeData[0] = 0b00010000; //Mapped double tap to int2
    I2C_Master_WriteReg(SLAVE_ADDR, 0x57, writeData, 1);
    //Check interrupt
    I2C_Master_ReadReg(SLAVE_ADDR, 0x57, 1);
    if(ReceiveBuffer[0] != writeData[0])
    {
        UART_transmitString(" Double tap not mapped ");
        printf("Double tap not mapped\n");
    }
    //Set tap data filtering
    writeData[0] = 0b1000; //Filtered data instead of pre-filtered
    I2C_Master_WriteReg(SLAVE_ADDR, 0x58, writeData, 1);
    //Check filtering
    I2C_Master_ReadReg(SLAVE_ADDR, 0x58, 1);
    if(ReceiveBuffer[0] != writeData[0])
    {
        UART_transmitString(" Filter not set ");
        printf("Filter not set\n");
    }
    //Set the Accelerometer to normal power mode
    writeData[0] = 0x11;
    I2C_Master_WriteReg(SLAVE_ADDR, 0x7E, writeData, 1);
    //Read power mode status of sensors
    I2C_Master_ReadReg(SLAVE_ADDR, 0x03, 1);
    if(ReceiveBuffer[0] != 0x10)
    {
        UART_transmitString(" Accelerometer not on ");
        printf("Accelerometer not on\n");
    }

    //Fast Offset Compensation (FOC) setup

    //0 for reserved, 0 for gyroscope, 00 for x, 00 for y, 0 for z (10 = -1g, 00 = 0g, 01 = 1g)
    switch(FOC_axis)
    {
        case 'X':
            writeData[0] = 0b00100000;
            break;
        case 'Y':
            writeData[0] = 0b00001000;
            break;
        case 'Z':
            writeData[0] = 0b00000010;
            break;
        default:
            writeData[0] = 0b00000000; //Default of all 0g
            break;
    }
    I2C_Master_WriteReg(SLAVE_ADDR, 0x69, writeData, 1);
    //Check FOC
    I2C_Master_ReadReg(SLAVE_ADDR, 0x69, 1);
    if(ReceiveBuffer[0] != writeData[0])
    {
        UART_transmitString(" FOC not set ");
        printf("Incorrect sensor chip ID\n");
    }
    //Start FOC
    writeData[0] = 0x03;
    I2C_Master_WriteReg(SLAVE_ADDR, 0x7E, writeData, 1);
    //Wait until FOC is finished
    int finish = 0;
    while(finish==0)
    {
        I2C_Master_ReadReg(SLAVE_ADDR, 0x1B, 1);
        if((ReceiveBuffer[0] & 0b00001000) == 0b00001000)
        {
            finish = 1;
        }
    }
    //Enable offset compensation
    writeData[0] = 0b01000000; //Enable accelerometer offset compensation
    I2C_Master_WriteReg(SLAVE_ADDR, 0x77, writeData, 1);
    //Check offset compensation
    I2C_Master_ReadReg(SLAVE_ADDR, 0x77, 1);
    if(ReceiveBuffer[0] != writeData[0])
    {
        UART_transmitString(" Offset not enabled ");
        printf("Incorrect sensor chip ID\n");
    }
    //UART_transmitString(" BMI160 Initialized \n");
}



//******************************************************************************
// Device Initialization *******************************************************
//******************************************************************************
void initGPIO()
{
    /* Terminate all GPIO pins to Output LOW to minimize power consumption */
    GPIO_setAsOutputPin(GPIO_PORT_PA, GPIO_PIN_ALL16);
    GPIO_setAsOutputPin(GPIO_PORT_PB, GPIO_PIN_ALL16);
    GPIO_setAsOutputPin(GPIO_PORT_PC, GPIO_PIN_ALL16);
    GPIO_setAsOutputPin(GPIO_PORT_PD, GPIO_PIN_ALL16);
    GPIO_setAsOutputPin(GPIO_PORT_PE, GPIO_PIN_ALL16);
    GPIO_setAsOutputPin(GPIO_PORT_PF, GPIO_PIN_ALL16);
    GPIO_setOutputLowOnPin(GPIO_PORT_PA, GPIO_PIN_ALL16);
    GPIO_setOutputLowOnPin(GPIO_PORT_PB, GPIO_PIN_ALL16);
    GPIO_setOutputLowOnPin(GPIO_PORT_PC, GPIO_PIN_ALL16);
    GPIO_setOutputLowOnPin(GPIO_PORT_PD, GPIO_PIN_ALL16);
    GPIO_setOutputLowOnPin(GPIO_PORT_PE, GPIO_PIN_ALL16);
    GPIO_setOutputLowOnPin(GPIO_PORT_PF, GPIO_PIN_ALL16);

    // I2C pins (P4.0 is SDA, P4.1 is SCL)
    P4SEL1 |= BIT0 | BIT1;
    P4SEL0 &= ~(BIT0 | BIT1);

    // Configure P3.4 and P3.5 to UART (Primary, TX and RX respectively) for NeoCortec
    P3SEL0 |= BIT4 | BIT5;                    // USCI_A1 UART operation
    P3SEL1 &= ~(BIT4 | BIT5);                 // SEL1 is 0 and SEL0 is 1 for primary operation, inverse for secondary

    // Configure P2.0 and P2.1 to UART (Primary, TX and RX respectively) for PC
    P2SEL0 |= BIT0 | BIT1;                    // USCI_A0 UART operation
    P2SEL1 &= ~(BIT0 | BIT1);                 // SEL1 is 0 and SEL0 is 1 for primary operation, inverse for secondary

    // Configure button S1 (P1.1) interrupt
    GPIO_selectInterruptEdge(GPIO_PORT_P1, GPIO_PIN1, GPIO_HIGH_TO_LOW_TRANSITION);
    GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P1, GPIO_PIN1);
    GPIO_clearInterrupt(GPIO_PORT_P1, GPIO_PIN1);
    GPIO_enableInterrupt(GPIO_PORT_P1, GPIO_PIN1);

    // Configure button S2 (P1.2) interrupt
    GPIO_selectInterruptEdge(GPIO_PORT_P1, GPIO_PIN2, GPIO_HIGH_TO_LOW_TRANSITION);
    GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P1, GPIO_PIN2);
    GPIO_clearInterrupt(GPIO_PORT_P1, GPIO_PIN2);
    GPIO_enableInterrupt(GPIO_PORT_P1, GPIO_PIN2);

    // Configure CTS active (P1.3) interrupt
    GPIO_selectInterruptEdge(GPIO_PORT_P1, GPIO_PIN3, GPIO_HIGH_TO_LOW_TRANSITION);
    GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P1, GPIO_PIN3);
    GPIO_clearInterrupt(GPIO_PORT_P1, GPIO_PIN3);
    GPIO_enableInterrupt(GPIO_PORT_P1, GPIO_PIN3);

    // Configure Nwu (P1.4) interrupt
    GPIO_selectInterruptEdge(GPIO_PORT_P1, GPIO_PIN4, GPIO_HIGH_TO_LOW_TRANSITION);
    GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P1, GPIO_PIN4);
    GPIO_clearInterrupt(GPIO_PORT_P1, GPIO_PIN4);
    GPIO_enableInterrupt(GPIO_PORT_P1, GPIO_PIN4);

    // Configure INT1 (P3.2) interrupt
    GPIO_selectInterruptEdge(GPIO_PORT_P3, GPIO_PIN2, GPIO_HIGH_TO_LOW_TRANSITION);
    GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P3, GPIO_PIN2);
    GPIO_clearInterrupt(GPIO_PORT_P3, GPIO_PIN2);
    GPIO_enableInterrupt(GPIO_PORT_P3, GPIO_PIN2);

    // Configure INT2 (P2.5) interrupt
    GPIO_selectInterruptEdge(GPIO_PORT_P2, GPIO_PIN5, GPIO_HIGH_TO_LOW_TRANSITION);
    GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P2, GPIO_PIN5);
    GPIO_clearInterrupt(GPIO_PORT_P2, GPIO_PIN5);
    GPIO_enableInterrupt(GPIO_PORT_P2, GPIO_PIN5);

    // Disable the GPIO power-on default high-impedance mode to activate
    // previously configured port settings
    PM5CTL0 &= ~LOCKLPM5;
    __bis_SR_register(GIE);
}

void initClockTo16MHz()
{
    // Configure one FRAM waitstate as required by the device datasheet for MCLK
    // operation beyond 8MHz _before_ configuring the clock system.
    FRCTL0 = FRCTLPW | NWAITS_1;

    // Clock System Setup
    CSCTL0_H = CSKEY_H;                     // Unlock CS registers
    CSCTL1 = DCOFSEL_0;                     // Set DCO to 1MHz

    // Set SMCLK = MCLK = DCO, ACLK = VLOCLK (9.4kHz)
    CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK;

    // Per Device Errata set divider to 4 before changing frequency to
    // prevent out of spec operation from overshoot transient
    CSCTL3 = DIVA__4 | DIVS__4 | DIVM__4;   // Set all corresponding clk sources to divide by 4 for errata
    CSCTL1 = DCOFSEL_4 | DCORSEL;           // Set DCO to 16MHz

    // Delay by ~10us to let DCO settle. 60 cycles = 20 cycles buffer + (10us / (1/4MHz))
    __delay_cycles(60);
    CSCTL3 = DIVA__32 | DIVS__1 | DIVM__1;  // Set ACLK to 239.75Hz, SMCLK to 16MHz, and MCLK to 16MHz
    CSCTL0_H = 0;                           // Lock CS registers
}

void initI2C()
{
    UCB1CTLW0 = UCSWRST;                      // Enable SW reset
    UCB1CTLW0 |= UCMODE_3 | UCMST | UCSSEL__SMCLK | UCSYNC; // I2C master mode, SMCLK
    UCB1BRW = 160;                            // fSCL = ACLK/160 = ~100kHz
    UCB1I2CSA = SLAVE_ADDR;                   // Slave Address
    UCB1CTLW0 &= ~UCSWRST;                    // Clear SW reset, resume operation
    UCB1IE |= UCNACKIE;
}

void UART_init(void)
{
    // Configure USCI_A1 for UART mode
    UCA1CTLW0 = UCSWRST;                      // Put eUSCI in reset
    UCA1CTLW0 |= UCSSEL__SMCLK;               // CLK = SMCLK
    UCA1BR0 = 8;                              // Clock prescaler set to 8
    UCA1BR1 = 0x00;                           // High byte empty, low byte is 8
    UCA1MCTLW |= UCOS16 | UCBRF_10 | 0xF700;  // Over-sampling on, first modulation register set to 10, second modulation register set to 0xF7 (247) for high byte, 0 for low byte
    UCA1CTLW0 &= ~UCSWRST;                    // Initialize eUSCI
    UCA1IE |= UCRXIE;                         // Enable USCI_A1 RX interrupt

    // Configure USCI_A0 for UART mode
    UCA0CTLW0 = UCSWRST;                      // Put eUSCI in reset
    UCA0CTLW0 |= UCSSEL__SMCLK;               // CLK = SMCLK
    UCA0BR0 = 8;                              // Clock prescaler set to 8
    UCA0BR1 = 0x00;                           // High byte empty, low byte is 8
    UCA0MCTLW |= UCOS16 | UCBRF_10 | 0xF700;  // Over-sampling on, first modulation register set to 10, second modulation register set to 0xF7 (247) for high byte, 0 for low byte
    UCA0CTLW0 &= ~UCSWRST;                    // Initialize eUSCI
    UCA0IE |= UCRXIE;                         // Enable USCI_A0 RX interrupt
}



//******************************************************************************
// Main ************************************************************************
//******************************************************************************

 int main(void)
 {
    WDTCTL = WDTPW | WDTHOLD;   // Stop watchdog timer
    //Initialize all peripherals
    initClockTo16MHz();
    initGPIO();
    UART_init();
    initI2C();

    bmi160_init('Z');

    // Initialize the FFT parameters
    msp_status status;
    msp_fft_q15_params fftParams;

    /* Initialize the fft parameter structure. */
    fftParams.length = SAMPLES;
    fftParams.bitReverse = true;
    fftParams.twiddleTable = msp_cmplx_twiddle_table_4096_q15; //Twiddle table for 4096 values


    int i = 0;

    //new
    TA0CTL   = TA0CTL | (SMCLK + CONTINUOUS);           // SMCLK:  Counts faster than ACLK
                                                        // CONTINUOUS:  Count 0 to 0xFFFF
    TA0CCTL0 = CCIE;                                    // Timer_0 interrupt

    TA1CTL   = TA1CTL | (ACLK  + UP         );          // Count up from 0 with ACLK
    TA1CCR0  = MS_10;                                   // Duration approximatley 10ms

    _BIS_SR(GIE);                                       // Activate all interrupts

    //new

    while(1)
    {
           printf("Reading samples\n");
           //Read SAMPLES amount of data from the BMI160
           for(i=0;i<SAMPLES;i++)
           {
               I2C_Master_ReadReg(SLAVE_ADDR, 0x16, 2); //Read the acceleration value from the BMI160 registers
               input[i]= ReceiveBuffer[0] | (ReceiveBuffer[1] << 8); //Store the value in an array
               //delay_ms(1); //Determines sampling frequency, up to 10kHz
               __delay_cycles(4500);
               //printf("Sent acc: %u\n",input[i]);
           }
           printf("Samples read\n");


           msp_benchmarkStart(MSP_BENCHMARK_BASE, 16);
           status = msp_fft_fixed_q15(&fftParams, input); //Perform FFT
           cycleCount = msp_benchmarkStop(MSP_BENCHMARK_BASE);
           msp_checkStatus(status);
           printf("FFT completed\n");

           //Transmit all output values to PC


       //Calculate and transmit frequencies with maximum amplitudes
       getMaximums(input, SAMPLES, max, amp);
       printf("Max amp frequencies found\n");


       // Transmit top 5 frequencies via NeoCortec
       for(i=0; i<5; i++)
       {
           printf("Sent freq: %u\n", (unsigned int)max[i]);
       }


       GPIO_toggleOutputOnPin(GPIO_PORT_P1, GPIO_PIN0);
       // Transmit matching amplitudes
       for(i=0; i<5; i++)
       {
           printf("Sent amp: %u\n", (unsigned int)amp[i]);
       }
    }
}


//******************************************************************************
// Interrupts ******************************************************************
//******************************************************************************

//I2C Interrupt
#pragma vector = USCI_B1_VECTOR
__interrupt void USCI_B1_ISR(void)
{
  //Must read from UCB1RXBUF
  uint8_t rx_val = 0;
  switch(__even_in_range(UCB1IV, USCI_I2C_UCBIT9IFG))
  {
    case USCI_NONE:          break;         // Vector 0: No interrupts
    case USCI_I2C_UCALIFG:   break;         // Vector 2: ALIFG
    case USCI_I2C_UCNACKIFG:                // Vector 4: NACKIFG
      UCB1CTLW0 |= UCTXSTT;                 // Re-send start if NACK
      break;
    case USCI_I2C_UCSTTIFG:  break;         // Vector 6: STTIFG
    case USCI_I2C_UCSTPIFG:  break;         // Vector 8: STPIFG
    case USCI_I2C_UCRXIFG3:  break;         // Vector 10: RXIFG3
    case USCI_I2C_UCTXIFG3:  break;         // Vector 12: TXIFG3
    case USCI_I2C_UCRXIFG2:  break;         // Vector 14: RXIFG2
    case USCI_I2C_UCTXIFG2:  break;         // Vector 16: TXIFG2
    case USCI_I2C_UCRXIFG1:  break;         // Vector 18: RXIFG1
    case USCI_I2C_UCTXIFG1:  break;         // Vector 20: TXIFG1
    case USCI_I2C_UCRXIFG0:                 // Vector 22: RXIFG0
        rx_val = UCB1RXBUF;
        if (RXByteCtr)
        {
          ReceiveBuffer[ReceiveIndex++] = rx_val;
          RXByteCtr--;
        }

        if (RXByteCtr == 1)
        {
          UCB1CTLW0 |= UCTXSTP;
        }
        else if (RXByteCtr == 0)
        {
          UCB1IE &= ~UCRXIE;
          MasterMode = IDLE_MODE;
          __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
        }
        break;
    case USCI_I2C_UCTXIFG0:                 // Vector 24: TXIFG0
        switch (MasterMode)
        {
          case TX_REG_ADDRESS_MODE:
              UCB1TXBUF = TransmitRegAddr;
              if (RXByteCtr)
                  MasterMode = SWITCH_TO_RX_MODE;   // Need to start receiving now
              else
                  MasterMode = TX_DATA_MODE;        // Continue to transmision with the data in Transmit Buffer
              break;

          case SWITCH_TO_RX_MODE:
              UCB1IE |= UCRXIE;              // Enable RX interrupt
              UCB1IE &= ~UCTXIE;             // Disable TX interrupt
              UCB1CTLW0 &= ~UCTR;            // Switch to receiver
              MasterMode = RX_DATA_MODE;    // State state is to receive data
              UCB1CTLW0 |= UCTXSTT;          // Send repeated start
              if (RXByteCtr == 1)
              {
                  //Must send stop since this is the N-1 byte
                  while((UCB1CTLW0 & UCTXSTT));
                  UCB1CTLW0 |= UCTXSTP;      // Send stop condition
              }
              break;

          case TX_DATA_MODE:
              if (TXByteCtr)
              {
                  UCB1TXBUF = TransmitBuffer[TransmitIndex++];
                  TXByteCtr--;
              }
              else
              {
                  //Done with transmission
                  UCB1CTLW0 |= UCTXSTP;     // Send stop condition
                  MasterMode = IDLE_MODE;
                  UCB1IE &= ~UCTXIE;                       // disable TX interrupt
                  __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
              }
              break;

          default:
              __no_operation();
              break;
        }
        break;
    default: break;
  }
}


#pragma vector = PORT2_VECTOR
__interrupt void PORT2_ISR(void)
{
    switch(__even_in_range(P2IV, P2IV_P2IFG7))
       {
           case P2IV_NONE : break;
           case P2IV_P2IFG0 : break;
           case P2IV_P2IFG1 : break;
           case P2IV_P2IFG2 : break;
           case P2IV_P2IFG3 : break;
           case P2IV_P2IFG4 : break;
           case P2IV_P2IFG5 :       //Int2 sensor interrupt
               P2IFG = P2IFG & ~(BIT2);
               break;
           case P2IV_P2IFG6 : break;
           case P2IV_P2IFG7 : break;
           default : _never_executed();
       }
}


// Timer_0 Interrupt Service Routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A0 (void)
{
    TA0CTL = TA0CTL & (~TAIFG);       // Reset Timer_0 so it keeps counting
}

  • Hello

    //Number of FFT samples
    #define SAMPLES 4096

    Could you please hlep to test if it works if change the samples to 512 samples because of 2KB RAM limitation

  • Thank you for your suggestion, Xiaodong LI. After changing the value, sometimes I get a value close to 3.5Hz and sometimes I get a random value. That is why I am not sure if it is working or not. Also, because when I increase the frequency to 4, 6, or 9 Hz, it again goes back to displaying random values. So maybe changing the sample number did not help.

  • Hello

    if simply reduce the sample number did not work, I need several days to look into the code. I will reply to your questions later.

    Thanks

  • Thank you very much; I greatly appreciate your assistance.

  • Also I  am not sure but as I  am storing the data in the FRAM, maybe the number of samples is not the issue. 

      

  • Hello

    Thanks for your information, I will look into the code and check this point.

  • Hello

    I checked the memory allocation screenshot from you,

    May I know what sample number you adopt here? it is 4K bytes or 1K bytes?

    I don't have idea why the FRAM2 size is ~26Kbytes.

    if you are not sure if the data is stored in the FRAM, you can try to check the data by view --> memory brower on CCS.

    I checked your code, but I havn't found a clue from the code

    Here is the user's guide of dsplib and fft for the reference: https://dev.ti.com/tirex/explore/node?node=APFYDDVAZQjSJ7vLKa8XPg__IOGqZri__LATEST

    Thanks

  • Thank you. I am taking 4096 samples. 

    I don't have idea why the FRAM2 size is ~26Kbytes.

    I am not sure what you meant by that. I would really appreciate if if you can explain a bit more in datail. The total FRAM for this MCU is 128KB.

    I have one question though, Is there a direct way to pass the sampling frequency to the FFT calculation?

    Here is the user's guide of dsplib and fft for the reference:

    I have seen it, but still could not find the issue.

  • Hi,

    Have you tried FFT example in DSPLib?

    For FFT, the signal frequency, sampling frequency and length have some releationship, otherwise will encounter some issue. 

    I think you can check if the example code is normal.

    Thanks!

    Best Regards

    Johnson

  • Thank you for your kind reply. I have seen that example. The problem is that I have read through many FFT analysis theories. But when I tried to compare them with the C programming language examples, I was unable to relate to them. The example codes are not explaining or trying to relate the concept with the code, I hope you have understood what I am trying to explain. Right now, I feel like starting from the beginning. So, keeping in mind the reference example:

    I am saving 4096 samples in an array. I can also get the exact time stamp for the accelerometer data from the BMI160. So, if I take the difference between two consequitive samples or do an average over the total time, I will get the sampling rate. Please correct me if I'm wrong.

    From this point forward, I am a bit lost. At the beginning, I thought that I needed to pass the sampling frequency and the samples to the "msp_fft_fixed_q15()" function and it would complete the process. But it looks like the "msp_fft_fixed_q15()" is only accepting the samples as the input but not the sampling frequency. That is why I am not sure what to expect as an output from the "msp_fft_fixed_q15()" function and how to later use the sampling frequency to find the final FFT value. 

    Let's consider I am taking each sample at an interval of 16.029 ms.

    So my sampling frequency would be, 1/0.016029s = 62.3869237 Hz.

    As the sampling rate is 62.3869237 Hz, the bandwidth would be 62.3869237/2 = 31.1935 Hz (not sure).

    As I am taking 4096 samples, the number of bins would be 4096/2 = 2048.

    So the bin size would be 31.1935/2048 = 0.0152 (not sure).

    This is the calculation that I have made based on my understanding. I would really appreciate it if someone could let me know if the calculations are done correctly.

    Now, I am not sure how I need to use those values to perform the FFT using the "msp_fft_fixed_q15()" function.

  • "As the sampling rate is 62.3869237 Hz, the bandwidth would be 62.3869237/2 = 31.1935 Hz (not sure)."

    *You* control the bandwidth. If you want a sampling frequency of 62 Hz, you must filter the signal so that there is no signal above 31 Hz or you will get aliasing. The TM I am familiar with has, for an example, an accelerator channel filter with a cutoff of 4000 Hz and a sample rate of 17000 Hz.

  • Thanks for your kind response. There is an option to select the band width and filter -3dB point for the sensor, and I have selected them accordingly. But my question is how do I use those previously mentioned variables (sampling frequency, bandwidth, number of bins, bin size) with the "msp_fft_fixed_q15()" function? I am not sure how the "msp_fft_fixed_q15()" function is working, what output to expect after passing the data to the function, and what to do afterwards. I would really appreciate it if you could explain how to perform the FFT operation and then get the FFT values using those variables and the "msp_fft_fixed_q15()" function.

  • Based on your suggestion, I have modified the code a bit and now I am getting very good results. My test platform is vibrating at a frequency of around 3.3~3.5 Hz. And when I am taking 512 samples and 1024 samples, I am getting very good results.

    512 samples: Max FFT 3Hz

    1024 samples: Max FFT at 3 Hz

    But with 4096 samples, I am getting a wrong reading.

    4096 Samples: Maz FFT at 17 Hz

    The BMI160 has an internal counter that increaments the counter at an interval of 39us. So I used it as the timestamp for each data point and the sampling frequency came out to be 933.42Hz. 

     

    Based on the BMI160 datasheet, I realised that I may need to play around with a couple of variables to perform an accurate calculation. One is the bandwidth (page 19), and the second one is the accelerometer range (page 59).

     

    Bandweidth: As the sampling frequency is 933.42Hz, I should have a bandweidth of 466.71Hz. So, I have chosen the 1600 ODR value (Z-axis).

      

    Accelerometer Range: Not sure which one to select and why.

    Even though I am gradually understanding how the process is working, I still have a few questions:

    1. Why is the 4096 sample program way off from the actual value? (I thought with higher samples, I should get more precious values)

    2. How can I calculate a more precious value for FFT (to the decimal points)?

    3. Can I perform FFT with a floating point value? (As the actual FFT value should be 3.3Hz~ 3.5Hz)

  • You are making the wrong assumption that each bin is a Hz, it is actually Fs/numsamples.

  • Hello Keith, have returned to the project after a short break. Thanks for the reply. Would you perhaps elaborate a little more on your comment? I thought after doing the FFT, I am actually receiving the actual frequency values. Are you saying that I am actually receiving the bin numbers?

    Also Is there anyway, I can perform FFT with a floating point value? (As the actual vibrating frequency is 3.3Hz~ 3.5Hz). Based on this link:

    https://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430Ware/3_50_00_04/exports/MSP430Ware_3_50_00_04/MSP430Ware_win/dsplib/html/usage.html

    It looks like I should be able to get fractional values.

  • Hi Ahammand,

    Does this issue have been resolved? 

    Thanks!

    Best Regards

    Johnson

**Attention** This is a public forum