11-03-2022 04:21 PM
Hello,
I am uisng BMI160. I am trying to convert the raw data to "g m/s2" value. I am uinsg the +-8g range in the 0x41 register.
The outout value is printed in serial terminal:
Reading samples
Sent acc: 8664
Sent acc: 8778
Sent acc: 8678
Sent acc: 8623
Sent acc: 8695
Sent acc: 8680
Sent acc: 8741
Sent acc: 8636
Sent acc: 8692
Sent acc: 8688
Sent acc: 8760
Sent acc: 8816
Sent acc: 8731
Sent acc: 8718
Sent acc: 8724
How would I convert it into readable acceleration value?
Thanks.
Here is my code:
#define GLOBAL_Q 15
#include "QmathLib.h"
#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>
//#define GLOBAL_Q 15
//#include "QmathLib.h"
_q15 q15FFT;
_q15 q15Amp; // Q variables using Q15 type
volatile float qFFT_out;
volatile float qAmp_out;
//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 1024
#define TimeStampSample 10
/* 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;
//#pragma PERSISTENT(SENSORTIME)
//volatile int32_t SENSORTIME[SAMPLES] = {0}; //Store samples
//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]=0b0011; //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");
}
//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
//I2C_Master_ReadReg(SLAVE_ADDR, 0x18, 3); //Read the acceleration value from the BMI160 registers
//SENSORTIME[i] = ((uint32_t)ReceiveBuffer[2] << 16) | ((uint32_t)ReceiveBuffer[1] << 😎 | ((uint32_t)ReceiveBuffer[0] << 0);
//delay_ms(1); //Determines sampling frequency, up to 10kHz
//__delay_cycles(4500);
printf("Sent acc: %u\n",(unsigned)input[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
}
11-04-2022 07:48 AM
Hi,
Thanks for your inquiry.
According to BMI160 datasheet, the accel sensitivity is 4096LSB/g at +/-8g full scale range. So the integer 8664 is corresponding to 8664 LSBs / 4096LSB/g = 2.115g = 2.115 * 9.81m/s^2 = 20.748 m/s^2.
Thanks.
11-04-2022 09:15 AM - edited 11-04-2022 10:41 AM
Thank you @FAE_CA1 , I have found some errors in my code and rewritten it with only the necessary values. I am still getting a wrong value based on that calculation. My device is completely stationary. In that case, I should read 1g in the "Z" axis or something close to 1g.
I have a few doubts:
1. First of all, what does LSB mean? Is it the least significant bit in this context?
2. As the accelerometer data is 16 bits long, I am using the following method to store the data. Is this the correct procedure to do it?
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
printf("Sent acc: %u\n",(unsigned)input[i]);
3. Or should I only do the calculation with 8 least significant bits?
4. In the 0x41 register, I have put the value "0b1000" to configure it as the +-8g range. So the LSB/g value should be 4096. Is it correct? Or, along with this register, do I need to configure some other registers as well to get the desired effect?
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");
}
5. Currently, the device is sitting on a table, so I am expecting 1 g on the "Z" axis. I am receiving this data on the serial terminal:
Reading samples
Sent acc: 2144
Sent acc: 2113
Sent acc: 2171
Sent acc: 2124
Sent acc: 2107
Sent acc: 2121
Sent acc: 2124
Sent acc: 2132
So based on the calculation method that you have shared, the result is 5.106 g, but the device is completely stationary, so I should read 1 g. Am I missing something?
(2132/4096)*9.81= 5.106g
Again, thank you very much for your time and consideration. I'm incredibly grateful for it.
Here is my new modified code (I have removed most of the irrelevant codes):
#define GLOBAL_Q 15
#include "QmathLib.h"
#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 10
/* Declare as persistent to move variable from RAM to FRAM */
#pragma PERSISTENT(input)
int16_t input[SAMPLES] = {0}; //Store samples
//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
//******************************************************************************
// 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");
}
//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 */
// 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
// 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');
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
printf("Sent acc: %u\n",(unsigned)input[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;
}
}
// 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
}
11-05-2022 08:37 AM
Hi,
LSB actually means signed integer value. After you read two byte BMI160 accel data registers, you can combine it as a signed integer. Multiplied by sensitivity you will get the meaningful acceleration such as 1g, 0g, -1g, etc. Please see the attached PDF file "How to perform BMI160 self-test v1.2.pdf" for more information.
When you place the board with BMI160 z axis pointing to sky stationary, you should get around 0g for x and y axes and 1g for z axis. If you place the board upside down, then you should get around 0g for x and y axes and -1g for z axis.
Thanks.
11-07-2022 03:55 PM
Thank you @FAE_CA1 for your reply. I completed the self-test. The sensor passed the evaluation. However, if I return to my code, I receive the same outcome. Is the sensor's calibration changeable? How does one go about doing that?
Thanks
Here is the result of the self test (serial terminal):
soft reset BMI160 complete
Accelerometer set to normal mode
enable self-test positive
Sent acc pos X: -7575
Sent acc pos Y: -28894
Sent acc pos Z: -1575
All sensor positive read complete
enable self-test negative
Sent acc neg X: 8999
Sent acc neg Y: 28258
Sent acc neg Z: 11586
All sensor negative read complete
Self test passed
soft reset BMI160 complete