SLAAE83 January 2024 MSPM0G1105 , MSPM0G1106 , MSPM0G1107 , MSPM0G1505 , MSPM0G1506 , MSPM0G1507 , MSPM0G3105 , MSPM0G3106 , MSPM0G3107 , MSPM0G3505 , MSPM0G3506 , MSPM0G3507 , MSPM0L1105 , MSPM0L1106 , MSPM0L1303 , MSPM0L1304 , MSPM0L1305 , MSPM0L1306 , MSPM0L1343 , MSPM0L1344 , MSPM0L1345 , MSPM0L1346
This subsystem serves as an interface for the BP-BASSSENSORSMKII BoosterPack™ plug-in module. This module features a temperature and humidity sensor, a hall effect sensor, an ambient light sensor, an inertial measurement unit, and a magnetometer. The module is designed to interface with TI LaunchPad™ development kits. This subsystem collects data from these sensors using the I2C interface and transmits the data out using the UART interface. This helps users rapidly move into prototyping and experimenting with the MSPM0 and BASSSENSORSMKII BoosterPack module.
The MSPM0 is connected to the BP-BASSSENSORSMKII using an I2C interface. The MSPM0 passes on processed data using the UART interface.
Peripheral Used | Notes |
---|---|
I2C | Called I2C_INST in code |
UART | Called UART_0_INST in code |
DMA | Used for UART TX |
GPIO | The five GPIOs are referred to as: HDC_V, DRV_V, OPT_V, INT1, and INT2 |
ADC | Called ADC12_0_INST in code |
Events | Used to transfer data into the UART TX FIFO |
Based on the requirements shown in Required Peripherals, this example is compatible with the devices shown in the following table. The corresponding EVM can be used for prototyping.
Compatible Devices | EVM |
---|---|
MSPM0Lxxxx | LP-MSPM0L1306 |
MSPM0Gxxxx | LP-MSPM0G3507 |
The following flowchart shows a high-level overview of the software steps performed to read, collect, process, and transmit the data from the sensor BoosterPack plug-in module.
This application makes use of TI System Configuration Tool (SysConfig) graphical interface to generate the configuration code of the device peripherals. Using a graphical interface to configure the device peripherals streamlines the application prototyping process.
The code for what is described in Software Flowchart can be found in the beginning of main() in the data_sensor_aggregator.c file.
This application starts by setting the sizes for UART and I2C transfers, then allocating memory to store the values to be transferred. Then it allocates memory for the final post-processing measurements to be saved for transmitting through UART. It also defines an enum for recording the I2C controller status. You may want to adjust some of the packet sizes and change some of the data storage in your own implementation. Additionally, it is encouraged to add error handling for some applications.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "ti_msp_dl_config.h"
/* Initializing functions */
void DataCollection(void);
void TxFunction(void);
void RxFunction(void);
void Transmit(void);
void UART_Console_write(const uint8_t *data, uint16_t size);
/* Earth's gravity in m/s^2 */
#define GRAVITY_EARTH (9.80665f)
/* Maximum size of TX packet */
#define I2C_TX_MAX_PACKET_SIZE (16)
/* Number of bytes to send to target device */
#define I2C_TX_PACKET_SIZE (3)
/* Maximum size of RX packet */
#define I2C_RX_MAX_PACKET_SIZE (16)
/* Number of bytes to received from target */
#define I2C_RX_PACKET_SIZE (16)
/*
* Number of bytes for UART packet size
* The packet will be transmitted by the UART.
* This example uses FIFOs with polling, and the maximum FIFO size is 4.
* Refer to interrupt examples to handle larger packets.
*/
#define UART_PACKET_SIZE (8)
uint8_t gSpace[] = "\r\n";
volatile bool gConsoleTxTransmitted;
volatile bool gConsoleTxDMATransmitted;
/* Data for UART to transmit */
uint8_t gTxData[UART_PACKET_SIZE];
/* Booleans for interrupts */
bool gCheckADC;
bool gDataReceived;
/* Variable to change the target address */
uint8_t gTargetAdd;
/* I2C variables for data collection */
float gHumidity, gTempHDC, gAmbient;
uint16_t gAmbientE, gAmbientR, gDRV;
uint16_t gMagX, gMagY, gMagZ, gGyrX, gGyrY, gGyrZ, gAccX, gAccY, gAccZ;
/* Data sent to the Target */
uint8_t gTxPacket[I2C_TX_MAX_PACKET_SIZE];
/* Counters for TX length and bytes sent */
uint32_t gTxLen, gTxCount;
/* Data received from Target */
uint8_t gRxPacket[I2C_RX_MAX_PACKET_SIZE];
/* Counters for TX length and bytes sent */
uint32_t gRxLen, gRxCount;
/* Indicates status of I2C */
enum I2cControllerStatus {
I2C_STATUS_IDLE = 0,
I2C_STATUS_TX_STARTED,
I2C_STATUS_TX_INPROGRESS,
I2C_STATUS_TX_COMPLETE,
I2C_STATUS_RX_STARTED,
I2C_STATUS_RX_INPROGRESS,
I2C_STATUS_RX_COMPLETE,
I2C_STATUS_ERROR,
} gI2cControllerStatus;
Main() in this application initializes all of our peripheral modules, then in the main loop the device just collects all data from the sensors, and transmits it after processing.
int main(void)
{
SYSCFG_DL_init();
NVIC_EnableIRQ(I2C_INST_INT_IRQN);
NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);
NVIC_EnableIRQ(UART_0_INST_INT_IRQN);
DL_SYSCTL_disableSleepOnExit();
while(1) {
DataCollection();
Transmit();
/* This delay is to the data is transmitted every few seconds */
delay_cycles(100000000);
}
}
The next block of code contains all of the interrupt service routines. The first is the I2C routine, next is the ADC routine, and finally the UART routine. The I2C routine mainly serves to update some flags, and update the controller status variable. It also manages the TX and RX FIFOs. The ADC interrupt service routine sets a flag so the main loop can check when the ADC value is valid. The UART interrupt service routine also just sets flags to confirm the validity of the UART data.
void I2C_INST_IRQHandler(void)
{
switch (DL_I2C_getPendingInterrupt(I2C_INST)) {
case DL_I2C_IIDX_CONTROLLER_RX_DONE:
gI2cControllerStatus = I2C_STATUS_RX_COMPLETE;
break;
case DL_I2C_IIDX_CONTROLLER_TX_DONE:
DL_I2C_disableInterrupt(
I2C_INST, DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER);
gI2cControllerStatus = I2C_STATUS_TX_COMPLETE;
break;
case DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER:
gI2cControllerStatus = I2C_STATUS_RX_INPROGRESS;
/* Receive all bytes from target */
while (DL_I2C_isControllerRXFIFOEmpty(I2C_INST) != true) {
if (gRxCount < gRxLen) {
gRxPacket[gRxCount++] =
DL_I2C_receiveControllerData(I2C_INST);
} else {
/* Ignore and remove from FIFO if the buffer is full */
DL_I2C_receiveControllerData(I2C_INST);
}
}
break;
case DL_I2C_IIDX_CONTROLLER_TXFIFO_TRIGGER:
gI2cControllerStatus = I2C_STATUS_TX_INPROGRESS;
/* Fill TX FIFO with next bytes to send */
if (gTxCount < gTxLen) {
gTxCount += DL_I2C_fillControllerTXFIFO(
I2C_INST, &gTxPacket[gTxCount], gTxLen - gTxCount);
}
break;
/* Not used for this example */
case DL_I2C_IIDX_CONTROLLER_ARBITRATION_LOST:
case DL_I2C_IIDX_CONTROLLER_NACK:
if ((gI2cControllerStatus == I2C_STATUS_RX_STARTED) ||
(gI2cControllerStatus == I2C_STATUS_TX_STARTED)) {
/* NACK interrupt if I2C Target is disconnected */
gI2cControllerStatus = I2C_STATUS_ERROR;
}
case DL_I2C_IIDX_CONTROLLER_RXFIFO_FULL:
case DL_I2C_IIDX_CONTROLLER_TXFIFO_EMPTY:
case DL_I2C_IIDX_CONTROLLER_START:
case DL_I2C_IIDX_CONTROLLER_STOP:
case DL_I2C_IIDX_CONTROLLER_EVENT1_DMA_DONE:
case DL_I2C_IIDX_CONTROLLER_EVENT2_DMA_DONE:
default:
break;
}
}
void ADC12_0_INST_IRQHandler(void)
{
switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) {
case DL_ADC12_IIDX_MEM0_RESULT_LOADED:
gCheckADC = true;
break;
default:
break;
}
}
void UART_0_INST_IRQHandler(void)
{
switch (DL_UART_Main_getPendingInterrupt(UART_0_INST)) {
case DL_UART_MAIN_IIDX_EOT_DONE:
gConsoleTxTransmitted = true;
break;
case DL_UART_MAIN_IIDX_DMA_DONE_TX:
gConsoleTxDMATransmitted = true;
break;
default:
break;
}
}
This block formats the data for sending out using the UART interface. It passes the data on in an easily readable format for viewing on a device like a UART terminal. In your own implementation it is likely that you will want to change the format of the data being transmitted.
/* This function formats and transmits all of the collected data over UART */
void Transmit(void)
{
int count = 1;
char buffer[20];
while (count < 14)
{
/* Formatting the name and converting int to string for transfer */
switch(count){
case 1:
gTxData[0] = 84;
gTxData[1] = 67;
gTxData[2] = 58;
gTxData[3] = 32;
sprintf(buffer, "%f", gTempHDC);
break;
case 2:
gTxData[0] = 72;
gTxData[1] = 37;
gTxData[2] = 58;
gTxData[3] = 32;
sprintf(buffer, "%f", gHumidity);
break;
case 3:
gTxData[0] = 65;
gTxData[1] = 109;
gTxData[2] = 58;
gTxData[3] = 32;
sprintf(buffer, "%f", gAmbient);
break;
case 4:
gTxData[0] = 77;
gTxData[1] = 120;
gTxData[2] = 58;
gTxData[3] = 32;
sprintf(buffer, "%i", gMagX);
break;
case 5:
gTxData[0] = 77;
gTxData[1] = 121;
gTxData[2] = 58;
gTxData[3] = 32;
sprintf(buffer, "%i", gMagY);
break;
case 6:
gTxData[0] = 77;
gTxData[1] = 122;
gTxData[2] = 58;
gTxData[3] = 32;
sprintf(buffer, "%i", gMagZ);
break;
case 7:
gTxData[0] = 71;
gTxData[1] = 120;
gTxData[2] = 58;
gTxData[3] = 32;
sprintf(buffer, "%i", gGyrX);
break;
case 8:
gTxData[0] = 71;
gTxData[1] = 121;
gTxData[2] = 58;
gTxData[3] = 32;
sprintf(buffer, "%i", gGyrY);
break;
case 9:
gTxData[0] = 71;
gTxData[1] = 122;
gTxData[2] = 58;
gTxData[3] = 32;
sprintf(buffer, "%i", gGyrZ);
break;
case 10:
gTxData[0] = 65;
gTxData[1] = 120;
gTxData[2] = 58;
gTxData[3] = 32;
sprintf(buffer, "%i", gAccX);
break;
case 11:
gTxData[0] = 65;
gTxData[1] = 121;
gTxData[2] = 58;
gTxData[3] = 32;
sprintf(buffer, "%i", gAccY);
break;
case 12:
gTxData[0] = 65;
gTxData[1] = 122;
gTxData[2] = 58;
gTxData[3] = 32;
sprintf(buffer, "%i", gAccZ);
break;
case 13:
gTxData[0] = 68;
gTxData[1] = 82;
gTxData[2] = 86;
gTxData[3] = 32;
sprintf(buffer, "%i", gDRV);
break;
}
count++;
/* Filling the UART transfer variable */
gTxData[4] = buffer[0];
gTxData[5] = buffer[1];
gTxData[6] = buffer[2];
gTxData[7] = buffer[3];
/* Optional delay to ensure UART TX is idle before starting transmission */
delay_cycles(160000);
UART_Console_write(&gTxData[0], 8);
UART_Console_write(&gSpace[0], sizeof(gSpace));
}
UART_Console_write(&gSpace[0], sizeof(gSpace));
}