Analog Eng Circuit Cookbook
Analog Eng Circuit Cookbook
The MSPM0 portfolio can easily scale to match the size and simplicity of your specific system needs and all subsystem
examples can be easily ported to any device in the MSPM0 portfolio using Sysconfig. Check out the entire MSPM0
portfolio at www.ti.com/mspm0. If you’re new to MCU designs, then we recommend completing our TI Precision Labs
(TIPL) Microcontrollers training series as well as our Zero Code Studio. Finally, please check out our E2E forums for any
questions and support.
Table of Contents
Message from the editors........................................................................................................................................................................... 2
Analog and Sensing.................................................................................................................................................................................... 3
ADC to PWM............................................................................................................................................................................................ 4
DMA Ping Pong With ADC....................................................................................................................................................................... 9
Digital FIR Filter...................................................................................................................................................................................... 13
ADC to I2C..............................................................................................................................................................................................17
Digital IIR Filter ...................................................................................................................................................................................... 21
ADC to SPI............................................................................................................................................................................................. 24
ADC to UART..........................................................................................................................................................................................26
Data Sensor Aggregator Subsystem Design..........................................................................................................................................29
Two OPA Instrumentation Amplifier With M0 Devices............................................................................................................................37
Dynamic Programmable Gain Amplifier................................................................................................................................................. 40
Scanning Comparator............................................................................................................................................................................ 48
Transimpedance Amplifier...................................................................................................................................................................... 54
Thermistor Temperature Sensing........................................................................................................................................................... 59
Communication Bridges........................................................................................................................................................................... 64
CAN to I2C Bridge.................................................................................................................................................................................. 65
I2C to UART Subsystem Design............................................................................................................................................................ 75
CAN to SPI Bridge..................................................................................................................................................................................82
CAN to UART Bridge.............................................................................................................................................................................. 90
Parallel IO to UART Bridge..................................................................................................................................................................... 98
I2C Expander Through UART Bridge....................................................................................................................................................103
UART to I2C Bridge.............................................................................................................................................................................. 109
UART to SPI Bridge.............................................................................................................................................................................. 114
Miscellaneous MCU Functionality..........................................................................................................................................................119
Emulating a Digital MUX.......................................................................................................................................................................120
5V Interface.......................................................................................................................................................................................... 124
Task Scheduler..................................................................................................................................................................................... 126
Timing and Control..................................................................................................................................................................................132
Connected Diode Matrix...................................................................................................................................................................... 133
Frequency Counter: Tone Detection.....................................................................................................................................................140
LED Driver With PWM.......................................................................................................................................................................... 144
Power Sequencer................................................................................................................................................................................. 148
PWM DAC............................................................................................................................................................................................ 152
ADC to PWM
Description
This example demonstrates how to convert an analog signal to a 4kHz PWM output. The analog input signal is sampled
using the MSPM0 integrated ADC. The duty cycle of the PWM output gets updated based on the ADC reading. Two
timers are required for this example; one to trigger the ADC reading and another to generate the PWM output. Download
the code for this example.
Figure 1 displays a functional block diagram of the peripherals used in this example.
MSPM0 MCU
Sample
triggering
Required peripherals
Called
(1x)
Sample triggering TIMER_0_INST in
Timer G
code
Called PWM_0_INST
PWM generation (1x) Timer G
in code
Called
Analog signal capture 1 ADC channel ADC12_0_INST in
code
Compatible devices
Based on the requirements in ,Table 1 this example is compatible with the devices in Table 2. The corresponding EVM
can be used for prototyping.
Table 2.
MSPM0Lxxx EVM
MSPM0Lxxx LP-MSPM0L1306
Table 2. (continued)
MSPM0Lxxx LP-MSPM0G3507
Design steps
1. Determine the required PWM output frequency and resolution. These two parameters are the starting point when
calculating other design parameters. In this example we chose an PWM output frequency of 4kHz and a PWM
resolution of 10bits.
2. Calculate the timer clock frequency. The equation F_clock = F_pwm x resolution can be used to calculate the timer
clock frequency.
3. Determine the ADC sampling rate. The sampling rate is related to the output PWM frequency. In this example,
a single ADC sample determines the duty cyle. F_adc = F_pwm. However, filtering or averaging may require the
application to choose a different sampling rate.
4. Configure peripherals in SysConfig. Select which timer instances will be used. Configure which device pins will be
used for ADC input and PWM output. This example uses PA17 for the PWM output (which is connected to Timer G4)
and A0.4 for the analog input.
5. Write application code. The remaining piece of this application is to transfer the ADC samples to the PWM timer. This
is accomplished in software. See Software Flowchart for an overview of the application or browser the code directly.
Design considerations
1. Max output frequency: Fundamentally, the max PWM output frequency is limited by the IO speed. However, the duty
cycle resolution also affect the max output frequency. More resolution requires more timer counts which increases
the output period.
2. Clocking: Deciding which clocks to use and what clock division ratios to use is an important design consideration for
this application.
a. Select a resolution that is a power of 2 so scaling operations can use shifts instead of multiplies and divides b.
b. In general, don't divide a slower clock down to a lower frequency. Instead pick a slower clock to reduce power
consumption 3.
3. Race conditions on gCheckADC: This application takes care to clear gCheckADC as soon as possible. If the
application waits too long to clear gCheckADC it may inadvertently miss new data.
4. Pipelining: The PWM timer selected in this application supports pipelining the timer compare value. Pipelining
allows the application to schedule an update to the timer compare value without causing a glitch on the output.
Techniques exist to mitigate glitches on timers without support for pipelining. However, this is beyond the scope of
this document.
Software Flowchart
Figure 2 shows the operations performed by application to convert an ADC reading into a PWM output.
Clear gCheckADC
Interrupt occurred
Set gCheckADC
Application code
This application's PWM output has 10bits of resolution. However, the ADC samples are 12bits, so we must convert the
12 bit ADC readings into 10 bit values that can be used to set the compare value of the PWM timer. Depending on an
applications requirements different scaling can be required.
Additionally, more advanced signal processing of the incoming data can be required. For example limiting, averaging or
other filtering may be important in different scenarios. These type of operations can be performed in the function below.
Results
When the input voltage is below a preset deadband value the output is disabled, as shown in figure 1-3.
In figure 1-4 the input voltage is 2.26V. The measured duty cycle is 67.93%. A quick calculation confirms that the
expected duty cycle is 68.4%.
Additional Resources
• Download the MSPM0 SDK
• Learn more about SysConfig
• MSPM0L LaunchPad
• MSPM0G LaunchPad
• MSPM0 Timer academy
• MSPM0 ADC academy
The DMA Ping Pong with ADC example demonstrates how to use the DMA to transfer ADC data between two different
buffers, also known as a DMA Ping Pong. A DMA Ping Pong is commonly used to transfer data to one buffer while the
CPU is working with the other buffer. The blue path in Figure 5 shows that the DMA transfers data to Buffer 1 and the
CPU gets data from Buffer 2. When the paths switch, the DMA transfers data to Buffer 2 and the CPU gets data from
Buffer 1. The benefit to this technique is faster total application runtime because the CPU is free to operate on a section
of data at all times. In this example, the ADC is configured in single conversion mode and the DMA and CPU switches
between buffers after each conversion.
MSPM0 MCU
Buffer 1
ADC DMA CPU
I/O Buffer 2
Required Peripherals
This application requires the integrated ADC and DMA. The internal VREF is an additional option for the ADC reference, if
a different reference value is required.
Table 3. Required Peripherals
Subblock Functionality Peripheral Use Notes
Analog Signal Capture ADC Called ADC12_0_INST in code
Full featured DMA channel is required to utilize the PREIRQ functionality. The example
Moving memory DMA
can be altered to work without the PREIRQ.
Compatible Devices
Based on the requirements in Table 3, some compatible devices are listed in Table 4. The corresponding EVM can be
used for quick evaluation. Other MSPM0 devices work with this subsystem as long as the required peripherals are met.
For quick porting, use the Switch Device option in SysConfig.
Table 4. Compatible Devices
Compatible Devices EVM
MSPM0Cx LP-MSPM0C1104
MSPM0Lx LP-MSPM0L1306
MSPM0Gx LP-MSPM0G3507
Design Steps
1. Determine the configuration for the ADC including reference source, reference value, resolution, and sampling rate
based on the given analog input and design requirements.
2. Generate two array buffers to store the ADC data and set the buffer size and DMA transfer size the same so the DMA
fills the whole buffer.
3. Configure the ADC in SysConfig based on the project requirements discovered in Step 1.
4. Configure the DMA in SysConfig in the ADC section.
5. Write Application Code to dynamically change the destination address of the DMA to alternate between buffers. See
Figure 6 for an overview or view the code directly.
Design Considerations
1. Maximum Sampling Speed: The sampling speed of the ADC is based on input signal frequency, analog front end,
filters, or any other design parameters that affect sampling.
2. ADC Reference: Choose the reference to align with the expected maximum input to utilize the full scale range of the
ADC.
3. Clock Settings: The clock source determines the total time for the conversion. The clock divider in tandem with
the SCOMP setting determines the total sampling time. SysConfig sets the appropriate SCOMP depending on the
sampling time setting.
Initialize Peripherals
True
True
False
Set DMA to send data to the Pong Buffer Set DMA to send data to the Ping Buffer
Design Results
The following contents show the results of the code executing. Figure 7 shows the results of the buffers after the first
execution of the main loop. After the buffer is filled, the code swaps the DMA destination to the second buffer and the
CPU is now free to utilize data in the first buffer.
Figure 8 shows the results of the second buffer after the second execution of the main loop. After the buffer is filled, the
code swaps the DMA destination back to the first buffer and now the CPU can use the data in the second buffer.
Additional Resources
E2E
See TI's E2E™ support forums to view discussions and post new threads to get technical support for utilizing MSPM0
devices in designs.
This subsystem demonstrates how the internal ADC, and math accelerator (MATHACL) modules within the MSPM0G
family of devices can be used to implement a simple, streaming FIR filter of an analog signal. In this configuration, noise
on an analog signal can be filtered based on the desired filter order and coefficients without waiting for software floating
point calculations.
MSPM0 MCU
FIR Filter
Analog Signal
ADC DAC
I/O I/O
Required Peripherals
Required Peripherals
Compatible Devices
Based on the requirements listed in Table 5, this example is compatible with the devices listed in Table 6. The
corresponding EVM can be used for prototyping.
Table 6. Compatible Devices
Compatible Devices EVM
MSPM0G35xx, MSPM0G15xx LP-MSPM0G3507
Design Steps
a. In the example code, a Q16 (16 fractional bits) representation is used. Perform this conversion using the IQMath
library or by multiplying the coefficients by 2n where n is the desired number of fractional bits. Verify that the
selected data type can hold these values without overflowing.
b. The filter coefficients are constant values, and as a result, can be contained in flash to save room in SRAM if
desired.
Design Considerations
1. Input signal bandwidth: The bandwidth of the signal that must be resolved determines the ADC sampling frequency
and the amount of data the code must process.
2. ADC reference voltage: The ADC reference voltage must be selected so the signal amplitude can be fully captured
with good resolution.
3. Filter order: Every increase in filter order is another set of operations the user must perform per each sample.
This increases the overall processing time between samples and limits the amount of other processes the user can
perform. The result is an increase in filter rejection and increased resolution of the desired signal.
SysConfig
Initialization
Start ADC
Conversions
Configure MATHACL
for multiplication
Clear gCheckADC
and set gResult = 0
i<N
For i = 0 to N gResult += gDelayLine[i] * filterCoeff [i]
i >= N
Application Code
#define FILTER_ORDER 24
#define FIXED_POINT_PRECISION 16
volatile bool gCheckADC;
uint32_t gDelayLine[FILTER_ORDER];
uint32_t gResult = 0;
/* Filter coefficients are input as 16-bit Precision fixed point values */
int main(void)
{
SYSCFG_DL_init();
NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);
gCheckADC = false;
DL_ADC12_startConversion(ADC12_0_INST);
while (1) {
while (false == gCheckADC) {
__WFE();
}
gCheckADC = false;
gResult = 0;
/* Append the most recent ADC result to the delay line */
memmove(&gDelayLine[1], gDelayLine, sizeof(gDelayLine) - sizeof(gDelayLine[0]));
gDelayLine[0] = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_0);
/* Set the ADC Result flag to trigger our main loop to process the new data */
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;
}
}
Additional Resources
• Texas Instruments, MSPM0 G-Series 80-MHz Microcontrollers Technical Reference Manual, technical reference manual.
• Texas Instruments, MSPM0G350x Mixed-Signal Microcontrollers With CAN-FD Interface, data sheet.
• Texas Instruments, MSPM0G150x Mixed-Signal Microcontrollers, data sheet.
E2E
See TI's E2E support forums to view discussions and post new threads to get technical support for using MSPM0
devices in designs.
ADC to I2C
Description
The ADC to I2C subsystem example demonstrates how to use the internal ADC to convert an analog signal into a digital
representation and transfer the result through I2C. The example configures the MCU to act as an external ADC, receive
I2C commands from an I2C controller, and execute the received command accordingly. With simple example commands
provided, users can utilize the framework to implement their own commands. Optionally, the MCU can also process the
ADC data before transmitting the data via I2C, which is especially useful in applications that need to process the raw
data into meaningful values. Download the code for the ADC to I2C Subsystem here.
Required Peripherals
The application requires the internal ADC and 1 instance of the I2C.
Compatible Devices
Based on the requirements in the Required Peripherals table some compatible devices and corresponding EVMs are
listed below. Other MSPM0 devices can be used with this subsystem as long as they have the required peripherals.
Design Steps
1. Determine the configuration for the ADC including reference source, reference value, and sampling rate based on the
expected analog input and design requirements.
2. Configure the ADC in SysConfig based on requirements in the previous step.
3. Configure the I2C peripheral in SysConfig, setting the I2C in target mode.
4. Write Application Code to transfer the ADC data from the memory registers to the I2C TX FIFO. See the Software
Flowchart for an overview or view the code directly.
Design Considerations
1. Max Sampling Speed: The sampling speed of the ADC is based on input signal frequency, analog front end, filters or
any other design parameters that affect sampling.
2. ADC Reference: Choose the reference to align with the expected max input to utilize the full scale range of the ADC.
3. Clock Settings: The clock source determined the total time for sample and conversion time. The clock divider
in tandem with the SCOMP setting determines the total sampling time. SysConfig sets the appropriate SCOMP
depending on the sampling time setting.
4. I2C configurations can be adjusted depending on your controller needs, such as the I2C address, addressing mode,
glitch filters, clock stretching, and more.
Additional Resources
• Download the MSPM0 SDK
• Learn more about SysConfig
• MSPM0L1306
• MSPM0G3507
• MSPM0 ADC Academy
• MSPM0 I2C Academy
This subsystem demonstrates how the internal ADC, and math accelerator (MATHACL) modules within the MSPM0G
family of devices can be used to implement a simple, streaming IIR filter of an analog signal. In this configuration, noise
on an analog signal is filtered using a single pole IIR filter. The defined beta value can be adjusted to control the IIR filter
decay over frequency.
MSPM0 MCU
IIR Filter
Analog Signal
ADC DAC
I/O I/O
Required Peripherals
Required Peripherals
Compatible Devices
Based on the requirements in Table 7, this example is compatible with the devices listed in Table 8. The corresponding
EVM can be used for prototyping.
Table 8. Compatible Devices
Compatible Devices EVM
MSPM0G35xx, MSPM0G15xx LP-MSPM0G3507
Design Steps
1. Determine the minimum required ADC sampling frequency. This must be at least twice the bandwidth of the input
signal.
2. Determine the desired rejection coefficient. The rejection coefficients in a single pole IIR filter governs the rate of
decay of the filter over frequency. The rejection coefficient is sometimes referred to as the beta (β) value, or the decay
value.
a. There are different tools for IIR filter coefficient calculation, which is not discussed in this document.
3. Convert the filter coefficient to a fixed point value.
a. In the example code, a Q8 (eight fractional bits) representation is used. Perform this conversion using the IQMath
library or by multiplying the coefficients by 2n where n is the desired number of fractional bits. Verify that the
selected data type can hold these values without overflowing.
b. The filter coefficients are constant values and can be contained in flash to save room in SRAM if desired.
Design Considerations
Start ADC
Conversions
Configure MATHACL
for MAC
Clear gCheckADC
Application Code
uint32_t gADCResult = 0;
while (1) {
while (false == gCheckADC) {
__WFE();
}
gCheckADC = false;
}
}
/* Set the ADC Result flag to trigger our main loop to process the new data */
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;
}
}
Additional Resources
E2E
See TI's E2E support forums to view discussions and post new threads to get technical support for using MSPM0
devices in designs.
ADC to SPI
Description
The ADC to SPI subsystem example demonstrates how to use the internal ADC to convert an analog signal into a digital
representation and transfer the result through SPI. The example configures the MCU to act as an external ADC, receive
SPI commands from a SPI controller, and execute the received command accordingly. With simple example commands
provided, users can utilize the framework to implement their own commands. Optionally, the MCU can also process the
ADC data before transmitting the data through SPI, which is especially useful in applications that need to process the
raw data into meaningful values. Download the code for the ADC to SPI example.
MSPM0 MCU
Analog Signal
I/O
Required Peripherals
The application requires the internal ADC and 1 instance of the SPI.
Compatible Devices
Based on the requirements in the Required Peripherals table some compatible devices and corresponding EVMs are
listed below. Other MSPM0 devices can be used with this subsystem as long as they have the required peripherals.
Design Steps
1. Determine the configuration for the ADC including reference source, reference value, and sampling rate based on the
expected analog input and design requirements.
2. Configure the ADC in SysConfig based on requirements in the previous step.
3. Configure the SPI peripheral in SysConfig, setting the SPI in peripheral mode.
4. Write Application Code to transfer the ADC data from the memory registers to transmit through SPI. Optionally add
commands to perform different tasks. See the Software Flowchart for an overview or view the code directly.
Software Flowchart
Start
Initialize Peripherals
While(1)
False
True
End
True
False
Process Command
Sample ADC Yes Sample ADC and place results into a buffer
No
No
Sample and Send Sample ADC and place results into a buffer,
Yes
ADC Results then transmit the ADC Result
No
Additional Resources
• Download the MSPM0 SDK
• Learn more about SysConfig
• MSPM0L1306
• MSPM0G3507
• MSPM0 ADC Academy
• MSPM0 SPI Academy
ADC to UART
Description
The ADC to UART subsystem example demonstrates how to use the internal ADC to convert an analog signal into a
digital representation and transfer the result through UART. The example configures the MCU to act as an external ADC
and send raw ADC data through UART. Optionally the MCU can also preprocess the data then send it through I2C.
Download the code for the ADC to UART example.
Required Peripherals
Compatible Devices
Based on the requirements in the table above, the compatible devices are listed below. The corresponding EVM may be
used for quick evaluation.
Compatible Devices EVM
MSPM0Lxxx LP-MSPM0L1306
MSPM0Gxxx LP-MSPM0G3507
Design Steps
1. Determine the configuration for the ADC including reference source, reference value, and sampling rate based on the
expected analog input and design requirements.
2. Configure the ADC in SysConfig based on requirements in the previous step.
3. Configure the UART peripheral in SysConfig, setting the UART to the intended baud rate and other UART options for
the intended communication.
4. Write Application Code to transfer the ADC data from the memory registers to the UART. See the Software
Flowchart for an overview or view the code directly.
Design Considerations
1. Max Sampling Speed: The sampling speed of the ADC is based on input signal frequency, analog front end, filters or
any other design parameters that affect sampling.
2. ADC Reference: Choose the reference to align with the expected max input to utilize the full scale range of the ADC.
3. Clock Settings: The clock source determines the total time for sample and conversion time. The clock divider
in tandem with the SCOMP setting determine the total sampling time. SysConfig sets the appropriate SCOMP
depending on the sampling time setting.
4. UART configurations can be adjusted depending on the UART system, such as parity, baud rate, and more.
Software Flowchart
Application Code
The UART peripheral sends data in packets of 8 bits at a time. The ADC module stores the data into a 16-bit register.
To transmit the data through the UART peripheral, the ADC data must be split into high and low bytes. The high byte
contains the upper 8 bits while the low byte contains the lower 8 bits. Below is the code to split the ADC result and
transmit the data through UART.
Additional Resources
• Download the MSPM0 SDK
• Learn more about SysConfig
• MSPM0L1306
• MSPM0G3507
• MSPM0 ADC Academy
• MSPM0 UART Academy
Design Description
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.
3.3V
3.3V
UART
BP-
UART Device MSPM0
BASSENSORSMKII
Required Peripherals
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
Compatible Devices
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.
Design Steps
1. Set up the GPIO module in SysConfig. Add a GPIO titled HDC_V as an output on PB24. Add a second GPIO titled
DRV_V as an output on PA22. Add a third GPIO titled OPT_V as an output on PA24. Add a fourth GPIO titled INT1 as
an output on PA26. Add a fifth and final GPIO titled INT2 as an output on PB6.
2. Set up the ADC12 module in SysConfig. Add an instance using single conversion mode, starting on address zero, in
auto-sampling mode. Set the trigger source to software. Open the ADC Conversion memory configurations tab and
make sure that memory 0 is named 0, using channel 2 on PA25, with VDDA as a reference voltage and Sampling
Timer 0 as a sample period source. Enable the interrupt for MEM0 result loaded in the interrupt configuration tab.
3. Set up the I2C module in SysConfig. Enable controller mode, and set the bus speed to 100kHz. In the interrupt
configuration tab, enable the RX Done, TX Done, RX FIFO Trigger, and Addr/Data NACK interrupts. In the PinMux
section, make sure I2C1 is the selected peripheral, with SDA on PB3 and SCL on PB2.
4. Set up the UART module in SysConfig. Add a UART instance, use 9600 Hz baud rate. In the Interrupt Configuration
Tab, enable the DMA done on transit and the End of Transmission interrupts. In the DMA configuration tab, choose
the DMA TX trigger as UART TX Interrupt, and enable it. Make sure the DMA Channel TX settings uses block to fixed
address mode, with the source and destination length set to Byte. Set the source address direction to increment,
and the transfer mode to single. Source and destination address increment should both be set to "do not change
address after each transfer". In the PinMux section, choose UART0 and PA11 for RX and PA10 for TX.
Design Considerations
1. Make sure that you have checked and verified the maximum packet size defines at the beginning of the code to fit
your usage of the subsystem.
2. Choose appropriate pull-up resistor values for the I2C module you are using. As a rule of thumb, 10kΩ is appropriate
for 100kHz. Higher I2C bus rates require lower valued pull-up resistors. For 400kHz communications, use resistors
closer to 4.7kΩ.
3. To increase the baud rate for the UART, open the UART module in SysConfig, and edit the Target Baud Rate value.
The calculated actual baud rate and calculated error are shown.
4. To help you add error detection and handling here for a more robust application, many modules have error interrupts
that allow for easily monitoring error cases.
5. See the "Transmit" function to edit the format that data is sent through UART.
Software Flowchart
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.
RX Done
Set I2C state to RX_COMPLETE
Initialize variables for
data send and
receive, flags, I2C TX Done Disable TX FIFO Interrupt, set
state machine
status to TX_COMPLETE
Device Configuration
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.
Application Code
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);
/*
* 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)
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];
UART_Console_write(&gTxData[0], 8);
UART_Console_write(&gSpace[0], sizeof(gSpace));
}
UART_Console_write(&gSpace[0], sizeof(gSpace));
}
Additional Resources
1. Download the MSPM0 SDK
2. Learn more about SysConfig
3. MSPM0L LaunchPad Development Kit
4. MSPM0G LaunchPad Development Kit
5. MSPM0 I2C Academy
6. MSPM0 UART Academy
7. MSPM0 ADC Academy
8. MSPM0 DMA Academy
9. MSPM0 Events Manager Academy
This subsystem software example creates a two OPA instrumentation amplifier (INA) using an MSPM0 and external
resistors. In this configuration, the difference between Vi1 and Vi2 is amplified, and outputs a single-ended signal with
high common-mode rejection. The output of the integrated INA can be sampled using an internal ADC channel of the
device.
R4
Vi1 MSPM0
–
+
GND/VREF GND OPA0
–
R3
Rg
R2
R1
ADC
–
OPA1
+
+
Vi2
–
GND
Required Peripherals
This application requires the two OPAs integrated in the MSPM0, and an ADC for sampling the results
Table 9. Required Peripherals
Subblock Functionality Peripheral Use Notes
Compatible Devices
Based on the requirements in Table 9, this example is compatible with the devices in Table 10. The corresponding EVM
can be used for prototyping.
Table 10. Compatible Devices
Compatible Devices EVM
MSPM0Lx LP-MSPM0L1306
MSPM0Gx LP-MSPM0G3507
Design Notes
The design of the two op amp instrumentation amplifier using the integrated amplifiers of the MSPM0 is not different
from the design using discrete op amps. The Two op amp instrumentation amplifier circuit application note covers the design
notes for this circuit. These are paraphrased in the following list for convenience:
1. Rg sets the gain of the circuit.
2. High-value resistors can degrade the phase margin of the circuit and introduce additional noise in the circuit.
3. The ratio of R4 and R3 set the minimum gain when Rg is removed.
4. Ratios of R2 / R1 and R4 / R3 must be matched to avoid degrading the DC CMRR of the instrumentation amplifier and
to make sure the Vref gain is 1V/V.
5. Linear operation is contingent upon the input common-mode and output swing ranges of the discrete op amps used.
The linear output swing ranges are specific under the AOL test conditions in the device data sheet.
Design Steps
Similar to Design Notes, the steps for designing the external circuitry of the two op amp INA is not different from the
discrete method. The following list describes the steps in the document covering the discrete design:
Vo = Vi2 − Vi1 × G
R 2R
where G is the gain of the instrumentation amplifier and G = 1 + R4 + R 2
3 g
R
Gmin = 1 + R4 = 5 V
V (2)
3
Choose R4 = 20kΩ
Gmin = 1 + 20kΩ V
R3 = 5 V
R
R3 = 5 −41 = 20kΩ
4 = 5kΩ R3 = 5 . 1kΩ Standard Value
3. Select R1 and R2. Make sure that R1 / R2 and R3 / R4 ratios are matched to set the gain applied to the reference
voltage at 1V/V.
Vo_ref R3 R2 R3 × R2 V
Vref = − R4 × − R1 = R4 × R1 = 1 V (3)
R2 R4
R1 = R3 R1 = R3 = 5 . 1kΩ and R2 = R4 = 20kΩ Standad Value
R 2R
G = 1 + R4 + R 2 = 1 + 5.1
20 kΩ
kΩ +
2 × 20 kΩ
Rg = 10 V/V (4)
3 g
Rg = 8 kΩ Rg = 7.87 kΩ Standard Value
Device Configuration
1. Configure SysConfig:
a. Select the inverting and non-inverting inputs for the OPAs.
b. Enable the output for both OPAs.
2. Build the external circuit with connections to the correlating pins from SysConfig.
3. Determine two input voltages and gain, details are in Design Considerations.
Design Considerations
1. Voltage reference:
• Vref is set to GND in this example but a voltage can be connected to R4 to change the DC level.
2. Output limitation:
• For the MSPM0 family, the output signal cannot be greater than VDD.
3. The PGA built into the OPA module can be used as well, but the external resistor values need to be adjusted. The
ratios are not necessarily going to be equal; therefore, matching can be imperfect.
4. The ADC can be set for different sampling speeds and conversion resolutions as described. These configurations
can be done in SysConfig and more details on the capabilities of the ADC and OPA are found in the device TRM and
data sheet.
5. LaunchPad Configuration: On the LaunchPad, the OPA inputs and outputs can be connected to different circuitry
such as the onboard photodiode or thermistor circuits. Check the associated LaunchPad user guide to determine
which jumpers to remove.
Reference
E2E
See TI's E2E™ support forums to view discussions and post new threads to get technical support for utilizing MSPM0
devices in designs.
Design Description
This subsystem demonstrates how to setup MSPM0 internal op-amps in a programmable gain amplifier (PGA)
configuration, dynamically change the gain, output the amplified signal, and read the result with the ADC. This
configuration allows a user to maximize resolution with small input voltage signal with high gain, but then still be able to
sample larger signals by changing to a lower gain. Download the code for this example.
MSPM0
–
VRef_ADC
PGA ADC
Required Peripherals
Compatible Devices
Based on the requirements in Table 11, this example is compatible with the devices in Table 12. The corresponding EVM
can be used for prototyping.
Table 12. Compatible Devices
Compatible Devices EVM
MSPM0L13xx LP-MSPM0L1306
MSPM0G35xx,
LP-MSPM0G3507
MSPM0G15xx
Design Steps
1. Determine the highest and lowest gain setting you want to apply to your signal of interest. The lowest gain the OPA
module can provide is a gain of 2 and the max is a gain of 32. See Design Considerations if sampling with ADC as
well.
a. Calculate the minimum gain of the system in relation to your maximum input voltage:
VADC_Ref
Gmin = V (5)
in_max
b. Calculate the maximum gain of the system in relation to your minimum input voltage of interest:
VADC_Ref
Gmax = V (6)
in_min
Where:
• Gmax is the maximum system gain setting chosen for the OPA
• Gmin is the minimum system gain setting chosen for the OPA
• Vin_max is your maximum input voltage.
• Vin_min is your minimum input voltage of interest.
• VADC_Ref is the ADC reference voltage.
2. Calculate the voltage into the ADC for a given input voltage and gain:
Where:
VADC_Ref
VADCin + 0.5 ×
212
NADC = 212 × VADC_Ref (8)
Where:
5. Calculate high side transition level. If ADC reading goes above this value, the example decreases the OPA gain if
possible. For this example, the high side transition level is set to upper 5% of maximum ADC level.
VADC_Ref
VOPA_in > HT × GOPA (10)
VADC_Ref
VOPA_in < LT × GOPA (11)
8. Setup OPA in SysConfig for PGA configuration with external input and external output.
9. Setup ADC in SysConfig for window comparator mode with VCC as reference (VRef_ADC), sampling the OPA output.
10. Convert the transition levels determined in Design Steps 5 and 6 to ADC codes using the equation from Design Step
3, and place these into the ADC window comparator limits in SysConfig.
11. (Optionally) Setup ADC to also sample OPA output with chosen ADCMEMx.
12. Set ADC sample time in SysConfig to a minimum time of tSample_PGA as given in the data sheet of the device.
Design Considerations
1. OPA supply is the VCC of the MSPM0.
2. OPA GBW setting: A lower GBW setting for the OPA consumes less current, but responds slower; conversely, a
higher GBW consumes more current, but has a larger slew rate and faster enable and settling times. Please check
device specific data sheet for exact specification differences between the modes
3. OPA Gain transitions: If it is desired to skip OPA gain levels, additional code must be added to the ADC Window
Comparator interrupt service routine (ISR) to explicitly set OPA gain settings, instead of just increasing or decreasing
levels. Take care that your transition levels calculated in Design Steps 5 and 6 also reflect this type of transition.
4. Minimum OPA Gain: The MSPM0 MCUs have the ability to dynamically change the OPA gain settings without
disabling the OPA. The minimum gain for the OPA in a PGA configuration is 2. To change from a gain of 2 to an OPA
Buffer configuration (OPA gain = 1), an additional procedure outside the scope of this document must be performed
to reconfigure the OPA to this mode.
5. ADC Reference selection: MSPM0 devices can provide a reference voltage to the ADC from the internal reference
generator (VREF), an external source, or MCU VCC. Check your MSPM0 device data sheet for options available
for your chosen device. The reference voltage chosen sets the full scale range the ADC can sample and must
accommodate the maximum OPA output voltage
6. ADC Window Comparator levels:
a. When increasing amplification of your input signal, by transitioning from a lower gain value to a higher gain value
(Example: G = 2 -> 4), use the equation in Design Step 2 to determine if the voltage level chosen for transition
does not rail the signal at the new gain setting.
b. When decreasing amplification of your input signal, by transitioning from a higher gain value down to a lower
gain value (Example: G = 4 -> 2), make sure that the voltage level chosen is greater than transition level chosen
in Design Consideration 6.a. This is to avoid a loop of changing gain that can cause a system instability.
7. ADC sampling: This example continuously samples the OPA output in window comparator mode. If continuous
monitoring of the OPA output is not desired, a timer can be used to set a fixed interval of sampling.
8. ADC results: The code example with optional ADC sampling of OPA output only stores the most current result
captured in the global variable gADCResult . Full applications can store several readings in an array before
performing actions on the data.
9. ADC results: If using the option for capturing ADC results, code must be added to handle the data being processed
in correlation to current OPA Gain settings. This because the ADC full-scale range changes in relation to OPA gain
settings, and thus the same ADC codes can be seen at different input voltage levels of the OPA.
10. Race conditions on gCheckADC: This application clears gCheckADC as soon as possible. If the application waits too
long to clear gCheckADC it may inadvertently miss new data.
Software Flowchart
Figure 21 shows the code flow diagram for Dynamic_PGA_Example2 which explains how the ADC samples the OPA
output and changes the OPA gain. The software flowchart for Dynamic_PGA1_Example is slightly simplified from the
flow below, as the main loop goes to sleep after starting the ADC, and the center switch case for the ADC Interrupt
Service Routine (ISR) is not present.
True
Clear
gCheckADC, False GAIN >= GAIN <= False
get ADC MINGAIN? MAXGAIN?
Results
True True
Device Configuration
This application makes use of TI System Configuration Tool (SysConfig) graphical interface to generate the configuration
code for the OPA and ADC. Using a graphical interface to configure the device peripherals streamlines the application
prototyping process.
The code for what is described in Figure 2 can be found in the beginning of main() in the Dynamic_PGA1_Example.c or
Dynamic_PGA_Example2.c files.
Application Code
The following code snippet shows where to adjust the OPA Gain levels and transition points as a relation of percentage
of maximum ADC codes as described in Design Steps 2. See the MSPM0 SDK and DriverLib documentation for
available OPA Gain defines.
#include "ti_msp_dl_config.h"
The following code snippet shows where to add custom code to perform useful actions after obtaining the ADC results.
Typically this is some sort of math, placing multiple results in an array, filtering, or lookup table access.
while (1) {
//This while loop waits until the next ADC result is loaded
while (false == gCheckADC) {
__WFE();
}
gCheckADC = false;
//Grab latest ADC Result
gADCResult = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_0);
The following code snippet shows where to adjust the ADC result interpretation in relation to OPA Gain setting. It is up to
the user to determine what actions to take, and how to correlate ADC results with OPA Gain setting and input voltage.
switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) {
case DL_ADC12_IIDX_WINDOW_COMP_HIGH:
// Entered high side margin window. Decrease OPA GAIN if possible.
tempGain = DL_OPA_getGain(OPA_0_INST);
if(tempGain > MINGAIN){
//Update OPA gain.
DL_OPA_decreaseGain(OPA_0_INST);
//For full applications, at this point you would want to adjust any math factors or
//look up tables to the new voltage ranges being captured by the ADC, or set a flag to do so
in main while loop.
}
break;
case DL_ADC12_IIDX_WINDOW_COMP_LOW:
// Entered low side margin window. Increase OPA GAIN if possible.
tempGain = DL_OPA_getGain(OPA_0_INST);
if(tempGain < MAXGAIN){
//Update OPA gain.
DL_OPA_increaseGain(OPA_0_INST);
//For full applications, at this point you would want to adjust any math factors or
//look up tables to the new voltage ranges being captured by the ADC, or set a flag to do so
in main while loop.
}
break;
default:
break;
}
Results
The following graphs show captures of the OPA input changing and the corresponding gained output. The OPA Gain
levels are as follows: 2x, 4x, 8x.
1.498 V
776 mv
382 mV
3.123 V 3.138 V
3.009 V
1.498 V
627 mV
305 mV
3.004 V
2.542 V 2.531 V
Additional Resources
• Download the MSPM0 SDK
• Learn more about SysConfig
• MSPM0L Technical Reference Manual (TRM)
Scanning Comparator
Description
This subsystem demonstrates how to represent multiple comparators with a single integrated comparator and software
in an MSPM0 microcontroller. The process allows the designer to maximize the comparator function and utilize more
theoretical comparators than are physically on the device. This example specifically cycles through three different
comparator configurations and input pins while setting three output pins with the results as shown in Figure 24.
Utilizing the customizable IO MUXing for the MSPM0 comparator, this example enables multiple signal inputs for the
same comparator. The three signal inputs for this example are on the COMP_IN0+, COMP_IN0-, and COMP_IN1- pins as
shown in Figure 25.
Required Peripherals
Compatible Devices
Based on the requirements shown in Table 13, this example is compatible with the devices shown in Table 14. The
corresponding EVM can be used for prototyping.
Table 14. Compatible Devices
Compatible Devices EVM Hardware COMP Maximum COMP Inputs
MSPM0L13xx LP-MSPM0L1306 1 4
MSPM0Lx22x LP-MSPM0L2228 1 4
MSPM0Gx5xx LP-MSPM0G3507 3 17
Design Steps
1. Determine multiple configurations for the comparator including operating mode, channel inputs, and voltage
reference based on design requirements.
2. Generate comparator configuration code utilizing SysConfig.
3. Configure the required GPIOs in SysConfig.
4. Create separate functions for each comparator configuration from Steps 1–2.
5. Write application code to call each configuration setting, delay for settling time, and assign the result to the
corresponding IO pin. See Figure 26 for an overview of the software.
Design Considerations
1. Settling time: After updating the configuration of the comparator, the application code requires a delay to allow for
the enable time, DAC settling time, and propagation delay before reading the result. When setting the delay in the
application code, refer to the comparator specifications section of the respective MSPM0 data sheet.
2. Operating mode: The comparator has both a high-speed and a low-power mode. The high-speed mode consumes
more current, but decreases the time between comparator readings. The low-power mode requires longer delays
between readings, but decreases the current consumption of the device. For reference, this example use the
high-speed mode.
3. Response time: As the subsystem cycles through multiple comparator configurations, this process increases the
maximum comparator response time. The maximum response time is a factor of the settling time delay multiplied by
the number of emulated comparator configurations. Standard response time = x; Emulated response time = delay ×
emulated comparator (45μs)
Application Code
The application code cycles through three different comparator configurations by calling the three
functions: update_comp_configA(), update_comp_configB(), and update_comp_configC(). Each time after
reconfiguring the comparator, the application code delays 15μs for the propagation and settling delay before reading the
comparator output and setting the respective GPIO.
Results
Figure 28 shows the results of the scanning comparator subsystem example. Emulated comparators A, B, and C had
reference voltages set to 0.5V, 1.0V, and 1.5V, respectively. The same input signal was measured on each of the three
emulated comparators.
The scope reading demonstrates that one physical comparator was able to mimic three comparators running at the
same time. This example code can be edited to fit different comparator numbers and configurations by changing the
functions within comp_hal.c.
Additional Resources
E2E
See TI's E2E™ support forums to view discussions and post new threads to get technical support for utilizing MSPM0
devices in designs.
Transimpedance Amplifier
Design description
This subsystem demonstrates how to setup MSPM0 internal op-amps to a transimpedance amplifier (TIA) configuration
and read the output with the internal ADC. The transimpedance op amp circuit configuration converts an input current
source into an output voltage. The current to voltage gain is based on the feedback resistance. Download the code for
this example.
MSPM0
+
–
GND VRef_ADC
ADC
I1 CF RF
GND
Required peripherals
Compatible devices
Based on the requirements in Table 15, this example is compatible with the devices in Table 16. The corresponding EVM
can be used for prototyping.
Table 16. Compatible Devices
Compatible Devices EVM
MSPM0L13xx LP-MSPM0L1306
MSPM0G35xx, MSPM0G15xx LP-MSPM0G3507
Design steps
1. Calculate the gain resister, R F
VRef_ADC − VMin
RF = I1Max (12)
where
CF ≤ 2 × π ×1R × f (13)
F p
Ci + CF
GBW > (14)
2 × π × RF × CF2
Design considerations
1. OPA supply is the VCC of the MSPM0.
2. OPA GBW setting: A lower GBW setting for the OPA consumes less current but responds more slowly. conversely,
a higher GBW consumes more current, but has a larger slew rate and faster enable and settling times. See the
device-specific data sheet for specification differences between the modes.
3. OPA noninverting input: Instead of GND potential, the OPA noninverting input can be given a small bias voltage to
keep the output from saturating to GND if a current source is not active ( such as when a photodiode is in a no-light
condition). This can be accomplished through external voltage input, or through internal peripherals such as the
DAC12 or DAC8 inside the COMP peripheral. In the latter case, the pin associated with the OPA noninverting input
can be used for other purposes.
4. ADC sampling: This example continuously samples the OPA output. If this is not desired, a timer can be used to set
a fixed interval of sampling.
5. ADC results: This example only stores the most current result captured in the global variable gADCResult . Full
applications can store several readings in an array before performing actions on the data.
6. ADC reference selection: MSPM0 devices can provide a reference voltage to the ADC from the internal reference
generator (VREF), external source, or MCU VCC. See the device-specific data sheet for options available for your
chosen device. The reference voltage chosen sets the full scale range the ADC can sample and must accommodate
the maximum OPA output voltage.
7. Race conditions on gCheckADC: This application clears gCheckADC as soon as possible. If the application waits too
long to clear gCheckADC it can inadvertently miss new data.
Software flowchart
Figure 30 shows the code flow diagram for this example and explains how the ADC samples the OPA output.
False
Sleep until event gCheckADC?
Break and Return
True
Clear
gCheckADC,
get ADC
Results
Device configuration
This application makes use of TI System Configuration Tool (SysConfig) graphical interface to generate the configuration
code for the OPA and ADC. Using a graphical interface to configure the device peripherals streamlines the application
prototyping process.
Application code
The code for what is described in Figure 30 can be found in the beginning of main() in the TIA_Example.c file. The
following code snippet shows where to add custom code to perform useful actions after obtaining the ADC results of
the measured current source. It is up to the user to determine what actions to take and to correlate ADC results with
current source activity. For example, if connected to a photodiode, a design might average the ADC results to ignore
small fluctuations of light and perform a delta calculation to detect large changes of light.
while (1) {
DL_ADC12_startConversion(ADC12_0_INST);
while (false == gCheckADC) {
__WFE();
}
/* * This is where the ADC result is grabbed from ADC memory.
* A user may want to modify this to place multiple results into an array,
* or add code to perform additional calculations or filters to data obtained.
*/
gADCResult = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_0);
gCheckADC = false;
DL_ADC12_enableConversions(ADC12_0_INST);
}
Additional Resources
• Download the MSPM0 SDK
• Learn more about SysConfig
• MSPM0L LaunchPad development kit
• MSPM0G LaunchPad development kit
• MSPM0 Timer academy
• MSPM0 ADC academy
• MSPM0 OPA academy
Design description
This subsystem uses a resistor in series with a positive temperature coefficient (PTC) thermistor (TMP61) to form a
voltage divider, which has the effect of producing an output voltage that is linear over temperature. This external circuit
is read by setting up the MSPM0 internal op-amp in a buffer configuration and sampling with the ADC. If an increase
of temperature is measured, an RGB LED turns red; if temperature decreases, the LED turns blue; and if no significant
change in temperature, the LED remains green. This document does not go into details of calculating a temperature
value from the ADC readings as such calculations are dependent on the thermistor chosen. Download the code
example here.
Rbias MSPM0
+
RGB
VRef_ADC
RTMP61 –
ADC
GND
Required peripherals
This application requires an integrated OPA, ADC, Timer, and I/O pins.
Table 17.
Sub-block functionality Peripheral Used Notes
Compatible devices
Based on the requirements in Table 17, this example is compatible with the devices in Table 18. The corresponding EVM
may be used for prototyping.
Table 18.
Compatible Devices EVM
MSPM0L13xx LP-MSPM0L1306
MSPM0G35xx, MSPM0G15xx LP-MSPM0G3507
Design Steps
1. Determine Rbias. For the TMP61 thermistor used in this design, it is recommended for Rbias to be 10 kΩ. Other
configurations are available. See the TMP61 data sheet for details.
a. Other thermistors can have different Rbias recommendations or equations available to you for calculating Rbias.
See the documentation for your chosen thermistor for details.
2. Setup OPA in SysConfig for buffer configuration with external input.
3. Setup ADC in SysConfig sample OPA output with chosen ADCMEMx.
4. Set ADC sample time in SysConfig to a minimum of tSample_PGA as given in the device data sheet.
5. Determine the temperature algorithm to use to convert ADC readings to temperature readings. This example uses
raw ADC readings to calculate changes in temperature.
Design considerations
1. Temperature calculation: Different thermistors will have various equations or lookup tables available in order to
calculate temperature from the ADC readings and external circuit. Check your thermistor collateral for these
resources that can be integrated into this design.
a. Lookup tables take less compute time, but may not be valid for every situation and can possibly take up a large
portion of memory.
b. Equations take more compute time, but are more flexible to external variables. The complexity of the equations
will depend on the accuracy or temperature range requirements.
2. OPA supply will be VCC of the MSPM0.
3. OPA GBW setting: A lower GBW setting for the OPA consumes less current, but responds slower; conversely,
a higher GBW consumes more current, but has a larger slew rate and faster enable and settling times. See the
device-specific data sheet for specification differences between the modes.
4. ADC Reference selection: MSPM0 devices can provide a reference voltage to the ADC from an internal reference
generator (VREF), external source, or MCU VCC. Check the MSPM0 device data sheet for options available for the
chosen device. For the configuration of this design, it is recommended to have the ADC reference to be equal to the
bias voltage (VCC) of the external thermistor circuit.
5. ADC sampling: This example periodically samples the external circuit using a timer trigger. To adjust how often the
circuit is sampled, adjust the timer parameters.
6. ADC results: The code example only stores the most current result captured in the global variable
gThermistorADCResult . Full applications may want to store several readings in an array before performing actions
on the data.
7. Race conditions on gCheckThermistor: This application clears gCheckThermistor as soon as possible. If the
application waits too long to clear gCheckThermistor, the application can inadvertently miss new data.
Software flowchart
Figure 32 shows the code flow diagram for this example and explains how the ADC samples the OPA output and the
decision tree for LED illumination.
False
Sleep until event gCheckThermistor?
Break and Return
True
True
Set initial value First Reading?
False
Set Red,
True Clear Blue,
Check if temp increased
Green
LEDs
False
Set Blue,
True Clear Red,
Check if temp decreased Sleep until interrupt
Green
LEDs
False
Set Green,
Clear Red,
Blue LEDs
Device configuration
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 Figure 32 can be found in the beginning of main() in the Thermistor_Example.c file.
Application code
This application does not compute temperature directly, but looks for a change in temperature. The following code
snippet includes a value CHANGEFACTOR which is used to determine a minimal amount of ADC value change before
recognizing a temperature change.
#include"ti_msp_dl_config.h"
#include<math.h>
#define CHANGEFACTOR 10
volatileuint16_tgThermistorADCResult = 0;
volatileboolgCheckThermistor = false;
The following code snippet shows where to add the temperature calculation method for the thermistor to compute
actual temperature values. The current code takes an initial reading (gInitial_reading) at startup and compares the current
reading (gCelcius_reading) with the CHANGEFACTOR adjustment to see if temperature has increased, decreased, or not
changed enough. The RGB LED is then turned red (increase), blue (decrease), or green (no change) respective to the
comparison result.
while (1) {
while (gCheckThermistor == false) {
__WFE();
}
//Insert Thermistor Algorithm
gCelcius_reading = gThermistorADCResult;
if (first_reading) {
gInitial_reading = gCelcius_reading;
first_reading = false;
}
/*
* Change in LEDs is based on current sample compared to previous sample
*
* If the new sample is warmer than CHANGEFACTOR from initial temp, turn LED red
* If the new sample is colder than CHANGEFACTOR from initial temp, turn LED blue
* Else, keep LED green
* Variable gAlivecheck is utilized for debug window to confirm code is executing.
* It is not needed in final applications.
*
*/
gAlivecheck++;
if(gAlivecheck >= 0xFFF0){gAlivecheck =0;}
if (gCelcius_reading - CHANGEFACTOR > gInitial_reading) {
DL_GPIO_clearPins(
RGB_PORT, (RGB_GREEN_PIN | RGB_BLUE_PIN));
DL_GPIO_setPins(RGB_PORT, RGB_RED_PIN);
} else if (gCelcius_reading < gInitial_reading - CHANGEFACTOR) {
DL_GPIO_clearPins(
RGB_PORT, (RGB_RED_PIN | RGB_BLUE_PIN));
DL_GPIO_setPins(RGB_PORT, RGB_BLUE_PIN);
} else {
DL_GPIO_clearPins(
RGB_PORT, (RGB_RED_PIN | RGB_BLUE_PIN));
DL_GPIO_setPins(RGB_PORT, RGB_GREEN_PIN);
}
gCheckThermistor = false;
__WFI();
}
Additional resources
1. Download the MSPM0 SDK
2. Learn more about SysConfig
3. MSPM0L LaunchPad
4. MSPM0G LaunchPad
5. MSPM0 Timer academy
6. MSPM0 ADC academy
7. MSPM0 OPA academy
Communication Bridges
CAN to I2C Bridge •
I2C to UART Subsystem Design •
CAN to SPI Bridge •
CAN to UART Bridge •
Parallel IO to UART Bridge •
I2C Expander Through UART Bridge •
UART to I2C Bridge •
UART to SPI Bridge •
Design Description
This subsystem demonstrates how to build a CAN-I2C bridge. CAN-I2C bridge allows a device to send/receive
information on one interface and receive/send the information on the other interface Download the code for this
example.Two example codes are provided to support I2C to work in controller mode or target mode respectively.
Figure 33 shows a functional diagram of this subsystem. Please note that one line is added for IO interrupt to implement
message transmission from I2C target to I2C controller.
CAN TX
I2C SDA I2C
Required Peripherals
Compatible Devices
Based on the requirements in Table 19, this example is compatible with the devices in Table 20. The corresponding EVM
can be used for prototyping.
Table 20. Compatible Devices
Compatible Devices EVM
MSPM0G35xx LP-MSPM0G3507
Design Steps
1. Determine the basic setting of CAN interface, including CAN mode, bit timing, message RAM configuation and so
on. Consider which setting is fixed and which setting is changed in the application. In example code, CANFD is used
with 250kbit/s arbitration rate and 2Mbit/s data rate.
a. Key features of the CAN-FD peripheral include:
i. Dedicated 1KB message SRAM with ECC
ii. Configurable transmit FIFO, transmit queue and event FIFO (up to 32 elements)
iii. Up to 32 dedicated transmit buffers and 64 dedicated receive buffers. Two configurable receive FIFOs (up to
64 elements each)
iv. Up to 128 filter elements
b. If CANFD mode is enabled:
i. Full support for 64-byte CAN-FD frames
ii. Up to 8Mbit/s bit rate
c. If CANFD mode is disabled:
i. Full support for 8-byte classical CAN frames
ii. Up to 1Mbit/s bit rate
2. Determine the CAN frame, including data length, bit rate switching, identifier, data and so on. Consider which part
is fixed and which part need to be changed in the application. In example code, identifier, data length and data can
change in different frames, while others are fixed. Note that users need to modify the code if protocol communication
is required.
/**
* @brief Structure for MCAN Rx Buffer element.
*/
typedef struct {
/*! Identifier */
uint32_t id;
/*! Remote Transmission Request
* 0 = Received frame is a data frame
* 1 = Received frame is a remote frame
*/
uint32_t rtr;
/*! Extended Identifier
* 0 = 11-bit standard identifier
* 1 = 29-bit extended identifier
*/
uint32_t xtd;
/*! Error State Indicator
* 0 = Transmitting node is error active
* 1 = Transmitting node is error passive
*/
uint32_t esi;
/*! Rx Timestamp */
uint32_t rxts;
/*! Data Length Code
* 0-8 = CAN + CAN FD: received frame has 0-8 data bytes
* 9-15 = CAN: received frame has 8 data bytes
* 9-15 = CAN FD: received frame has 12/16/20/24/32/48/64 data bytes
*/
uint32_t dlc;
/*! Bit Rat Switching
* 0 = Frame received without bit rate switching
* 1 = Frame received with bit rate switching
*/
uint32_t brs;
/*! FD Format
* 0 = Standard frame format
* 1 = CAN FD frame format (new DLC-coding and CRC)
*/
uint32_t fdf;
/*! Filter Index */
uint32_t fidx;
/*! Accepted Non-matching Frame
* 0 = Received frame matching filter index FIDX
* 1 = Received frame did not match any Rx filter element
*/
uint32_t anmf;
/*! Data bytes.
* Only first dlc number of bytes are valid.
*/
uint16_t data[DL_MCAN_MAX_PAYLOAD_BYTES];
} DL_MCAN_RxBufElement;
3. Determine the basic setting of I2C interface, including I2C mode, bus speed, target address, FIFO and so on.
Consider which setting is fixed and which setting is changed in the application. One example code is used for I2C
controller with 400kHz bus speed, the other one is used for I2C target with address 0x48.
a. Key features of the I2C peripheral include:
i. Configurable as a controller or a target with a bit rate up to 1Mbps
ii. Independent 8-byte FIFOs for reception and transmission
iii. Dual target address capability, glitch suppression
iv. Independent controller and target interrupt generation, and hardware support for DMA
v. Controller operation with arbitration, clock synchronization, multiple controller support
4. Determine the I2C message format. Typically I2C is transmitted in bytes. To achieve high-level communication,
users can implement frame communication through software. If necessary, users can also introduce specific
communication protocols. In example code, the message format is < 55 AA ID1 ID2 ID3 ID4 Length Data1 Data2 ...>.
Users can send data through I2C as the same format. 55 AA is the header. ID area is 4 bytes. Length area is 1 byte,
indicating the data length. Note that if users need to modify the I2C packet form, the code for frame acquisition and
parsing also need to be modified.
Table 21. I2C Packet Form
Header Address Data Length Data
5. Determine the bridge structure, including what messages need to be converted, how to convert messages and so
on.
a. Consider whether the bridge is one-way or two-way. Typically each interface has two functions: receiving
and sending. Consider whether only some functions need to be included (such as I2C reception and CAN
transmission). In example code, CAN-I2C bridge is a two-way structure. Since the receiving and transmitting
of the I2C target are controlled by the I2C controller, the I2C target cannot initiate transmission to the I2C
controller. To achieve communication from the target to the controller, a line is added to this design. The target's
IO pull-down notifies the controller that there is information to be sent.
b. Consider what information to convert and the corresponding carrier(variable, FIFO). In example code, identifier,
data and data length are convert from one interface to the other interface. There are two FIFOs defined in code
as shown in Figure 34.
typedef struct {
/*! Identifier */
uint32_t id;
/*! Data Length Code*/
uint32_t dlc;
/*! Data bytes*/ 7 0
uint16_t data[64];
} Custom_Element;
ItoC_out
6 1
Interrupt Main()
ItoC_in++ ItoC_out++
Receive
ItoC_count++ ItoC_count-- Transmit
message message to
from I2C ItoC_FIFO
CAN
5 2
ItoC_count = 2
C2I_in++ C2I_out++
Receive Transmit
message C2I_count++ C2I_count-- message to 4 3
from CAN C2I_FIFO I2C
ItoC_in
6. (Optionally) Consider priority design, congestion situation, error handling, and so on.
Design Considerations
1. Consider the information flow in the application to determine the information to be received or sent by each
interface, the protocols to be followed, and design appropriate information transfer carriers to connect different
interfaces.
2. The recommendation is to test the interface separately first, and then implement the overall bridge function. In
addition, consider the handling of abnormal situations, such as communication failure, overload, frame format error,
and so on.
3. The recommendation is to implement interface functions through interrupts to make sure of timely communication.
In example code, interface functions are usually implemented in the interrupt, and the transfer of information is
completed in the main() function.
Software Flowchart
Figure 35 shows the code flow diagram for CAN-I2C bridge which explains how the messages received in one interface
and sent in the other interface. The CAN-I2C bridge can be divided into four independent tasks: receive from I2C,
receive from CAN, transmit through CAN, transmit through I2C. Two FIFOs implement bidirectional message transfer and
message caching.
Note that I2C is a communication method that I2C controller control the transmit and receive. In general, I2C target
cannot initiate communication. For I2C target-to-controller communication, I2C target can pull down the IO when
messages needed to be sent, as shown in Figure 35. I2C controller can initiate I2C read command in IO interrupt when
IO is detected low, as shown in Figure 36. In this demo, I2C can be configured as I2C target or controller.
Interrupt Main()
IO interrupt
ReadI2CRxMsg
getI2cRxMsg ItoC_FIFO
processCANTxMsg
Overload control
sendCANTxMsg
processI2cRxMsg
getCANRxMsg
Overload control
C2I_FIFO
Transit message to I2C
processCANRxMsg
processI2cTxMsg
Interrupt Main()
ItoC_FIFO
processCANTxMsg
Overload control
processI2cRxMsg sendCANTxMsg
getCANRxMsg
Pulldown IO to trigger
controller to read
I2C TXFIFO Trigger Interrupt
Device Configuration
This application makes use of TI System Configuration Tool (SysConfig) graphical interface to generate the configuration
code for the CAN and I2C. Using a graphical interface to configure the device peripherals streamlines the application
prototyping process.
The code for what is described in Figure 35 can be found in the files from example code as shown in Figure 37.
Application Code
The following code snippet shows where to modify the interface function. Functions in table are categorized into different
files. Functions for I2C receive and transmit are included in bridge_i2c.c and bridge_i2c.h. Functions for CAN receive and
transmit are included in bridge_can.c and bridge_can.h. Structure of FIFO element is defined in user_define.h.
Users can easily separate functions by file. For example, if only I2C functions are needed, users can reserve bridge_i2c.c
and bridge_i2c.h to call the functions.
See the MSPM0 SDK and DriverLib documentation for the basic configuration of peripherals.
Table 22. Functions and Descriptions
Tasks Functions Description Location
I2C receive readI2CRxMsg_controller() Send a read request to slave (I2C master only) bridge_i2c.c
getI2CRxMsg_controller() Get the received I2C message (I2C master only) bridge_i2c.h
getI2CRxMsg_target() Get the received I2C message (I2C slave only)
processI2cRxMsg() Convert the received I2C message format and store it into gI2C_RX_Element
I2C transmit processI2cTxMsg() Convert the gI2C_TX_Element format to be sent through I2C
sendI2CTxMsg_controller() Send message through I2C (I2C master only)
sendI2CTxMsg_target() Send message through I2C (I2C slave only)
CAN receive getCANRxMsg() Get the received CAN message bridge_can.c
processCANRxMsg() Convert the received CAN message format and store the message into
bridge_can.h
gCAN_RX_Element
CAN transmit processCANTxMsg() Convert the gCAN_TX_Element format to be sent through CAN
sendCANTxMsg() Send message through CAN
Custom_Element is the structure defined in user_define.h. Custom_Element is used as the structure of FIFO element,
output element of I2C/CAN transmit and input element of I2C/CAN receive. Users can modify the structure according to
the need.
typedef struct {
/*! Identifier */
uint32_t id;
/*! Data Length Code*/
uint32_t dlc;
/*! Data bytes*/
uint16_t data[64];
} Custom_Element;
For FIFO, there are 2 global variables used as FIFO. 6 global variables are used to trace the FIFO.
Custom_Element ItoC_FIFO[ItoC_FIFO_SIZE];
Custom_Element C2I_FIFO[C2I_FIFO_SIZE];
uint16_t ItoC_in = 0;
uint16_t ItoC_out = 0;
uint16_t ItoC_count = 0;
uint16_t C2I_in = 0;
uint16_t C2I_out = 0;
uint16_t C2I_count = 0;
Results
By using CAN analyzer, users can send and receive messages on the CAN side. As a demonstration, two launchpads
can be used as two CAN-I2C bridges (one I2C master and one I2C slave) to form a loop. When the CAN analyzer sends
CAN messages through master launchpad, the analyzer can receive CAN messages from the slave launchpad.
Figure 39. Messages Sent and Received by CAN Analyzer for the Demo
Additional Resources
• Texas Instruments, Download the MSPM0 SDK
• Texas Instruments, Learn more about SysConfig
• Texas Instruments, MSPM0 G-Series 80-MHz Microcontrollers, technical reference manual
• Texas Instruments, MSPM0G LaunchPad development kit
• Texas Instruments, MSPM0 CAN academy
• Texas Instruments, MSPM0 I2C academy
Design Description
This subsystem serves as an I2C-to-UART bridge. In this subsystem, the MSPM0 device is the I2C target device. When
the I2C controller transmits to the I2C target, the target collects all of the received data. Once the target detects a stop
condition, the target transmits the data out using the UART interface. When the I2C controller attempts to read from the
bridge, the bridge transmits the last byte received from the UART device. When the I2C controller reads two bytes, the
bridge transmits the last byte received from the UART device and the latest error code generated by the bridge.
The MSPM0 is connected to the I2C controller with the I2C SCL and SDA lines. The MSPM0 is also connected to a
UART device using the UART TX and RX lines.
3.3V
3.3V
I2C UART
Figure 41. System Functional Block Diagram
Required Peripherals
Peripheral Used Notes
I2C Called I2C_INST in code
UART Called UART_INST in code
Compatible Devices
Based on the requirements shown in Required Peripherals, this example is compatible with the devices shown in
Compatible Devices. The corresponding EVM can be used for prototyping.
Design Steps
1. Set up the I2C module in SysConfig. Set the device in Target Mode, and enable the RX FIFO Trigger, Start Detection,
Stop Detection, Target Arbitration Lost, TX FIFO Underflow, RX FIFO Overflow, and Interrupt Overflow Interrupts.
2. Set up the UART module in SysConfig. Choose the desired baud rate for the device. Enable the Receive, Transmit,
Overrun error, Break error, Frame error, Parity error, Noise error, and RX Timeout.
Design Considerations
1. In the application code, make sure that I2C_MAX_PACKET_SIZE is large enough to contain the packets to be
transmitted.
2. Make sure to select the appropriate pullup resistor values for the I2C module being used. As a general guideline,
10 kΩ is appropriate for 100 kHz. Higher I2C bus rates require lower valued pullup resistors. For 400-kHz
communications, use resistors closer to 4.7 kΩ.
3. To increase the UART baud rate, adjust the value in the SysConfig UART tab labeled Target Baud Rate. Below this,
observe the Calculated baud rate change to reflect the target baud rate. This is calculated using the available clocks
and dividers.
4. Check error flags and handle them appropriately. The UART and I2C peripherals are both capable of throwing
informative error interrupts. For easy debugging this subsystem uses an enum and a global variable to save error
codes when error codes are thrown. In real-world applications, handle errors in the code so the errors do not break
down the project.
Software Flowchart
Figure 42 shows the code flow diagram for this example and explains how the device fills the data buffers with received
I2C data, then transfers the data out via UART.
Ignore data
Save data to RX
Set bu er over ow
Bu er
error
No
Is UartTxReady ag
set? Exit ISR
Device Configuration
This application makes use of the 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 Figure 42 is found in the beginning of main() in the i2c_to_uart_bridge.c file.
Application Code
This application must allocate memory for the received data and the data to be transmitted out. The application also
needs to keep count for how much data was received and transmitted. A flag is necessary to determine when the data
being received is completed and ready to transmit out via UART. There is also an enum for error codes, along with a
variable to save them. The initialization of the buffers, counters, enum, and flag are shown here:
#include "ti_msp_dl_config.h"
enum error_codes{
NO_ERROR,
DATA_BUFFER_OVERFLOW,
RX_FIFO_FULL,
NO_DATA_RECEIVED,
I2C_TARGET_TXFIFO_UNDERFLOW,
I2C_TARGET_RXFIFO_OVERFLOW,
I2C_TARGET_ARBITRATION_LOST,
I2C_INTERRUPT_OVERFLOW,
UART_OVERRUN_ERROR,
UART_BREAK_ERROR,
UART_PARITY_ERROR,
UART_FRAMING_ERROR,
UART_RX_TIMEOUT_ERROR
};
The main body of the application code is relatively short. First the device and the peripherals are initialized. Then
interrupts and events are enabled. The counter values are also initialized. Finally, the main loop is reached, where a flag
is polled detect when the received data is ready to be transferred back out via UART:
int main(void)
{
SYSCFG_DL_init();
gTxCount = 0;
gTxLen = I2C_TX_MAX_PACKET_SIZE;
DL_I2C_enableInterrupt(I2C_INST, DL_I2C_INTERRUPT_TARGET_TXFIFO_TRIGGER);
NVIC_EnableIRQ(I2C_INST_INT_IRQN);
NVIC_EnableIRQ(UART_INST_INT_IRQN);
while (1) {
if(gUartTxReady){
gUartTxReady = false;
for(int i = 0; i < gRxCount; i++){
/* Transmit data out via UART and wait until transfer is complete */
DL_UART_Main_transmitDataBlocking(UART_INST, gTxPacket[i]);
}
}
}
}
The next piece of this code is the I2C IRQ Handler. This code is used to start and then stop data collection. Next, the
code saves the data as the data is received. When the pending interrupt is an I2C Start condition detected, the device
initializes the counter variables. When the pending interrupt indicates that the RX FIFO has data available, the device
checks to see if there is space left in the data buffer. If there is space, the received value is saved. If there is not any more
space, the received value is ignored. When the pending interrupt is the TX FIFO Trigger, the device checks to see how
many bytes have been sent. If the device has already sent a byte, then the FIFO is filled with the most recently reported
error code. When the pending interrupt is an I2C stop condition, the device checks to see if data was received. If data
was received, the received data buffer is copied into the transmit data buffer, and the UART TX ready flag is set to true.
If no data was received, the device does not send anything. This ISR also handles I2C error interrupts by assigning the
appropriate error code to the gErrorStatus variable.
void I2C_INST_IRQHandler(void)
{
static bool dataRx = false;
switch (DL_I2C_getPendingInterrupt(I2C_INST)) {
case DL_I2C_IIDX_TARGET_START:
/* Initialize RX or TX after Start condition is received */
gTxCount = 0;
gRxCount = 0;
dataRx = false;
/* Flush TX FIFO to refill it */
DL_I2C_flushTargetTXFIFO(I2C_INST);
break;
case DL_I2C_IIDX_TARGET_RXFIFO_TRIGGER:
/* Store received data in buffer */
dataRx = true;
while (DL_I2C_isTargetRXFIFOEmpty(I2C_INST) != true) {
if (gRxCount < gRxLen) {
gRxPacket[gRxCount++] = DL_I2C_receiveTargetData(I2C_INST);
} else {
/* Prevent overflow and just ignore data */
DL_I2C_receiveTargetData(I2C_INST);
}
}
break;
case DL_I2C_IIDX_TARGET_TXFIFO_TRIGGER:
/* Fill TX FIFO if there are more bytes to send */
if (gTxCount < gTxLen) {
gTxCount += DL_I2C_fillTargetTXFIFO(
I2C_INST, &gUARTRxData, (gTxLen - gTxCount));
} else {
/*
* Fill FIFO with error status after sending latest received
* byte
*/
while (DL_I2C_transmitTargetDataCheck(I2C_INST, gErrorStatus) != false)
;
}
break;
case DL_I2C_IIDX_TARGET_STOP:
/* If data was received, echo to TX buffer */
if (dataRx == true) {
for (uint16_t i = 0;
(i < gRxCount) && (i < I2C_TX_MAX_PACKET_SIZE); i++) {
gTxPacket[i] = gRxPacket[i];
DL_I2C_flushTargetTXFIFO(I2C_INST);
}
dataRx = false;
}
/* Set flag to indicate data ready for UART TX */
gUartTxReady = true;
break;
case DL_I2C_IIDX_TARGET_RX_DONE:
/* Not used for this example */
case DL_I2C_IIDX_TARGET_RXFIFO_FULL:
/* Not used for this example */
case DL_I2C_IIDX_TARGET_GENERAL_CALL:
/* Not used for this example */
case DL_I2C_IIDX_TARGET_EVENT1_DMA_DONE:
/* Not used for this example */
case DL_I2C_IIDX_TARGET_EVENT2_DMA_DONE:
/* Not used for this example */
case DL_I2C_IIDX_TARGET_TXFIFO_UNDERFLOW:
gErrorStatus = I2C_TARGET_TXFIFO_UNDERFLOW;
break;
case DL_I2C_IIDX_TARGET_RXFIFO_OVERFLOW:
gErrorStatus = I2C_TARGET_RXFIFO_OVERFLOW;
break;
case DL_I2C_IIDX_TARGET_ARBITRATION_LOST:
gErrorStatus = I2C_TARGET_ARBITRATION_LOST;
break;
case DL_I2C_IIDX_INTERRUPT_OVERFLOW:
gErrorStatus = I2C_INTERRUPT_OVERFLOW;
break;
default:
break;
}
}
The final piece of code in this example is the UART IRQ Handler. The UART IRQ handler is only used to save received
data, and check for errors. When a UART RX interrupt is pending, the device saves the received data to a buffer,
gUARTRxData, then sets a flag to indicate there is new RX data saved. When a UART error does occur, this ISR
executes to assign the correct error code to gErrorStatus.
void UART_INST_IRQHandler(void)
{
switch (DL_UART_Main_getPendingInterrupt(UART_INST)) {
case DL_UART_MAIN_IIDX_RX:
DL_UART_Main_receiveDataCheck(UART_INST, &gUARTRxData);
gUartRxDone = true;
break;
case DL_UART_INTERRUPT_OVERRUN_ERROR:
gErrorStatus = UART_OVERRUN_ERROR;
break;
case DL_UART_INTERRUPT_BREAK_ERROR:
gErrorStatus = UART_BREAK_ERROR;
break;
case DL_UART_INTERRUPT_PARITY_ERROR:
gErrorStatus = UART_PARITY_ERROR;
break;
case DL_UART_INTERRUPT_FRAMING_ERROR:
gErrorStatus = UART_FRAMING_ERROR;
break;
case DL_UART_INTERRUPT_RX_TIMEOUT_ERROR:
gErrorStatus = UART_RX_TIMEOUT_ERROR;
break;
default:
break;
}
}
Additional Resources
1. Texas Instruments, Download the MSPM0 SDK
2. Texas Instruments, Learn more about SysConfig
Design Description
This subsystem demonstrates how to build a CAN-SPI bridge. CAN-SPI bridge allows a device to send or receive
information on one interface and receive or send the information on the other interface Download the code for this
example. The subsystem supports SPI to work in controller mode or peripheral mode.
CAN TX
SPI PICO
SPI CS
I/O
I/O
Required Peripherals
Compatible Devices
Based on the requirements in Table 23, this example is compatible with the devices in Table 24. The corresponding EVM
can be used for prototyping.
Table 24. Compatible Devices
Compatible Devices EVM
MSPM0G35xx LP-MSPM0G3507
Design Steps
1. Determine the basic setting of CAN interface, including CAN mode, bit timing, message RAM configuation and so
on. Consider which setting is fixed and which setting is changed in the application. In example code, CANFD is used
with 250kbit/s arbitration rate and 2Mbit/s data rate.
a. Key features of the CAN-FD peripheral include:
i. Dedicated 1KB message SRAM with ECC
ii. Configurable transmit FIFO, transmit queue and event FIFO (up to 32 elements)
iii. Up to 32 dedicated transmit buffers and 64 dedicated receive buffers. Two configurable receive FIFOs (up to
64 elements each)
iv. Up to 128 filter elements
b. If CANFD mode is enabled:
i. Full support for 64-byte CAN-FD frames
ii. Up to 8Mbit/s bit rate
c. If CANFD mode is disabled:
i. Full support for 8-byte classical CAN frames
ii. Up to 1Mbit/s bit rate
2. Determine the CAN frame, including data length, bit rate switching, identifier, data and so on. Consider which part
is fixed and which part need to be changed in the application. In example code, identifier, data length and data can
change in different frames, while others are fixed. Note that users need to modify the code if protocol communication
is required.
/**
* @brief Structure for MCAN Rx Buffer element.
*/
typedef struct {
/*! Identifier */
uint32_t id;
/*! Remote Transmission Request
* 0 = Received frame is a data frame
* 1 = Received frame is a remote frame
*/
uint32_t rtr;
/*! Extended Identifier
* 0 = 11-bit standard identifier
* 1 = 29-bit extended identifier
*/
uint32_t xtd;
/*! Error State Indicator
* 0 = Transmitting node is error active
* 1 = Transmitting node is error passive
*/
uint32_t esi;
/*! Rx Timestamp */
uint32_t rxts;
/*! Data Length Code
* 0-8 = CAN + CAN FD: received frame has 0-8 data bytes
* 9-15 = CAN: received frame has 8 data bytes
* 9-15 = CAN FD: received frame has 12/16/20/24/32/48/64 data bytes
*/
uint32_t dlc;
/*! Bit Rat Switching
* 0 = Frame received without bit rate switching
* 1 = Frame received with bit rate switching
*/
uint32_t brs;
/*! FD Format
* 0 = Standard frame format
* 1 = CAN FD frame format (new DLC-coding and CRC)
*/
uint32_t fdf;
/*! Filter Index */
uint32_t fidx;
/*! Accepted Non-matching Frame
* 0 = Received frame matching filter index FIDX
* 1 = Received frame did not match any Rx filter element
*/
uint32_t anmf;
/*! Data bytes.
* Only first dlc number of bytes are valid.
*/
uint16_t data[DL_MCAN_MAX_PAYLOAD_BYTES];
} DL_MCAN_RxBufElement;
3. Determine the basic setting of SPI interface, including SPI mode, bit rate, frame size, FIFO, and so on. Consider
which setting is fixed and which setting is changed in the application. In example code, SPI can be set as controller
or peripheral. SPI operates at 500k bit rate in controller mode.
a. Key features of the SPI include:
i. Configurable as a controller or a peripheral
ii. Programmable clock bit rate and prescaler
iii. Separate transmit (TX) and receive (RX) first-in first-out buffers (FIFOs);
iv. Supports PACKEN feature and single bit parity
v. Programmable data frame size and programmable SPI mode
vi. Interrupts for transmit and receive FIFOs, overrun and timeout interrupts, and DMA done
4. Determine the SPI frame. Typically SPI is transmitted in bytes. To achieve high-level communication, users can
implement frame communication through software. If necessary, users can also introduce specific communication
protocols. In example code, the message format is < 55 AA ID1 ID2 ID3 ID4 Length Data1 Data2 ...>. Users can send
data to the CAN bus from the terminal by entering data as the same format. 55 AA is the header. ID area is 4 bytes.
Length area is 1 byte, indicating the data length. Note that if users need to modify the SPI frame, the code for frame
acquisition and parsing also need to be modified.
Table 25. SPI Frame Form
Header Address Data Length Data
5. Determine the bridge structure, including what messages need to be converted, how to convert messages and so
on.
a. Consider whether the bridge is one-way or two-way. Typically each interface has two functions: receiving
and sending. Consider whether only some functions need to be included (such as SPI reception and CAN
transmission). In example code, CAN-SPI bridge is a two-way structure.
b. Consider what information to convert and the corresponding carrier(variable, FIFO). In example code, identifier,
data and data length are convert from one interface to the other interface. There are two FIFOs defined in code
as shown inFigure 44.
typedef struct {
/*! Identifier */
uint32_t id;
/*! Data Length Code*/
uint32_t dlc;
/*! Data bytes*/ 7 0
uint16_t data[64];
} Custom_Element;
S2C_out
6 1
Interrupt Main()
S2C_in++ S2C_out++
Receive
S2C_count++ S2C_count-- Transmit
message message to
from SPI S2C_FIFO
CAN
5 2
S2C_count = 2
C2S_in++ C2S_out++
Receive Transmit
message C2S_count++ C2S_count-- message to 4 3
from CAN C2S_FIFO SPI
S2C_in
6. (Optionally) Consider priority design, congestion situation, error handling and so on.
Design Considerations
1. Consider the information flow in the application to determine the information to be received or sent by each
interface, the protocols to be followed, and design appropriate information transfer carriers to connect different
interfaces.
2. The recommendation is to test the interface separately first, and then implement the overall bridge function. In
addition, consider the handling of abnormal situations, such as communication failure, overload, frame format error,
and so on.
3. The recommendation is to implement interface functions through interrupts to make sure of timely communication.
In example code, interface functions are usually implemented in the interrupt, and the transfer of information is
completed in the main() function.
Software Flowchart
The following figure shows the code flow diagram for CAN-SPI bridge which explains how the messages received in one
interface and sent in the other interface. The CAN-SPI bridge can be divided into four independent tasks: receive from
SPI, receive from CAN, transmit through CAN, transmit through SPI. Two FIFOs implement bidirectional message transfer
and message caching.
Note that SPI is a communication method that sends and receives at the same time. When the controller initiates
sending a byte, the controller expects to receive a byte. In the design of this article, SPI RX interrupt is not only used for
SPI receive, but also used to fill the TX data into SPI TX FIFO. If SPI works in controller mode, SPI communication starts
immediately after SPI TX FIFO is stored by data. If SPI works in peripheral mode, SPI can wait for the controller to initiate
communication after data is stored. In this demo, users can select the mode of SPI.
Interrupt Main()
getSpiRxMsg
S2C_FIFO processCANTxMsg
Overload control
sendCANTxMsg
processSpiRxMsg
sendSpiTxMsg
processSpiTxMsg
Overload control C2S_FIFO
Device Configuration
This application makes use of TI System Configuration Tool (SysConfig) graphical interface to generate the configuration
code for the CAN and SPI. Using a graphical interface to configure the device peripherals streamlines the application
prototyping process.
The user can configure the SPI to be controller or peripheral in the Sysconfig.
The code for what is described in Figure 45 can be found in the files from example code as shown in Figure 46.
Application Code
The following code snippet shows where to modify the interface function. Functions in table are categorized into different
files. Functions for SPI receive and transmit are included in bridge_spi.c and bridge_spi.h. Functions for CAN receive and
transmit are included in bridge_can.c and bridge_can.h. Structure of FIFO element is defined in user_define.h.
Users can easily separate functions by file. For example, if only SPI functions are needed, users can reserve bridge_spi.c
and bridge_spi.h to call the functions.
See the MSPM0 SDK and DriverLib documentation for the basic configuration of peripherals.
Table 26. Functions and Descriptions
Tasks Functions Description Location
SPI receive getSpiRxMsg() Get the received SPI message bridge_spi.c
processSpiRxMsg() Convert the received SPI message format and store it into gSPI_RX_Element bridge_spi.h
SPI transmit processSpiTxMsg() Convert the gSPI_TX_Element format to be sent through SPI
sendSpiTxMsg() Send message through SPI
CAN receive getCANRxMsg() Get the received CAN message bridge_can.c
processCANRxMsg() Convert the received CAN message format and store the message into
bridge_can.h
gCAN_RX_Element
CAN transmit processCANTxMsg() Convert the gCAN_TX_Element format to be sent through CAN
sendCANTxMsg() Send message through CAN
Custom_Element is the structure defined in user_define.h. Custom_Element is used as the structure of FIFO element,
output element of SPI/CAN transmit and input element of SPI/CAN receive. Users can modify the structure according to
the need.
typedef struct {
/*! Identifier */
uint32_t id;
/*! Data Length Code*/
uint32_t dlc;
/*! Data bytes*/
uint16_t data[64];
} Custom_Element;
For FIFO, there are 2 global variables used as FIFO. 6 global variables are used to trace the FIFO.
Custom_Element S2C_FIFO[S2C_FIFO_SIZE];
Custom_Element C2S_FIFO[C2S_FIFO_SIZE];
uint16_t S2C_in = 0;
uint16_t S2C_out = 0;
uint16_t S2C_count = 0;
uint16_t C2S_in = 0;
uint16_t C2S_out = 0;
uint16_t C2S_count = 0;
Results
By using CAN analyzer, users can send and receive messages on the CAN side. As a demonstration, two launchpads
can be used as two CAN-SPI bridges(one SPI controller and one SPI peripheral) to form a loop. When the CAN analyzer
sends CAN messages through controller launchpad, it will receive CAN messages from the peripheral launchpad.
Figure 48. Messages Sent and Received by CAN Analyzer for the Demo
Additional Resources
• Texas Instruments, Download the MSPM0 SDK
• Texas Instruments, Learn more about SysConfig
• Texas Instruments, MSPM0 G-Series 80-MHz Microcontrollers, technical reference manual
• Texas Instruments, MSPM0G LaunchPad development kit
• Texas Instruments, MSPM0 CAN academy
• Texas Instruments, MSPM0 SPI academy
Design Description
This subsystem demonstrates how to build a CAN-UART bridge. CAN-UART bridge allows a device to send or receive
information on one interface and receive or send the information on the other interface Download the code for this
example.
Required Peripherals
Compatible Devices
Based on the requirements in Table 27, this example is compatible with the devices in Table 28. The corresponding EVM
can be used for prototyping.
Table 28. Compatible Devices
Compatible Devices EVM
MSPM0G35xx, LP-MSPM0G3507
Design Steps
1. Determine the basic setting of CAN interface, including CAN mode, bit timing, message RAM configuation and so
on. Consider which setting is fixed and which setting is changed in the application. In example code, CANFD is used
with 250kbit/s arbitration rate and 2Mbit/s data rate.
a. Key features of the CAN-FD peripheral include:
i. Dedicated 1KB message SRAM with ECC
ii. Configurable transmit FIFO, transmit queue and event FIFO (up to 32 elements)
iii. Up to 32 dedicated transmit buffers and 64 dedicated receive buffers. Two configurable receive FIFOs (up to
64 elements each)
iv. Up to 128 filter elements
b. If CANFD mode is enabled:
i. Full support for 64-byte CAN-FD frames
ii. Up to 8Mbit/s bit rate
c. If CANFD mode is disabled:
i. Full support for 8-byte classical CAN frames
ii. Up to 1Mbit/s bit rate
2. Determine the CAN frame, including data length, bit rate switching, identifier, data and so on. Consider which part
is fixed and which part need to be changed in the application. In example code, identifier, data length and data can
change in different frames, while others are fixed. Note that users need to modify the code if protocol communication
is required.
/**
* @brief Structure for MCAN Rx Buffer element.
*/
typedef struct {
/*! Identifier */
uint32_t id;
/*! Remote Transmission Request
* 0 = Received frame is a data frame
* 1 = Received frame is a remote frame
*/
uint32_t rtr;
/*! Extended Identifier
* 0 = 11-bit standard identifier
* 1 = 29-bit extended identifier
*/
uint32_t xtd;
/*! Error State Indicator
* 0 = Transmitting node is error active
* 1 = Transmitting node is error passive
*/
uint32_t esi;
/*! Rx Timestamp */
uint32_t rxts;
/*! Data Length Code
* 0-8 = CAN + CAN FD: received frame has 0-8 data bytes
* 9-15 = CAN: received frame has 8 data bytes
* 9-15 = CAN FD: received frame has 12/16/20/24/32/48/64 data bytes
*/
uint32_t dlc;
/*! Bit Rat Switching
* 0 = Frame received without bit rate switching
* 1 = Frame received with bit rate switching
*/
uint32_t brs;
/*! FD Format
* 0 = Standard frame format
* 1 = CAN FD frame format (new DLC-coding and CRC)
*/
uint32_t fdf;
/*! Filter Index */
uint32_t fidx;
/*! Accepted Non-matching Frame
* 0 = Received frame matching filter index FIDX
* 1 = Received frame did not match any Rx filter element
*/
uint32_t anmf;
/*! Data bytes.
* Only first dlc number of bytes are valid.
*/
uint16_t data[DL_MCAN_MAX_PAYLOAD_BYTES];
} DL_MCAN_RxBufElement;
3. Determine the basic setting of UART interface, including UART mode, baud rate, word length, FIFO and so on.
Consider which setting is fixed and which setting is changed in the application. In example code, UART is used with
9600 baud rate.
a. Key features of the UART peripheral include:
i. Standard asynchronous communication bits for start, stop, and parity
ii. Fully programmable serial interface
iii. Separated transmit and receive FIFOs support DAM data transfer
iv. Support transmit and receive loopback mode operation
4. Determine the UART frame. Typically UART is transmitted in bytes. To achieve high-level communication, users can
implement frame communication through software. If necessary, users can also introduce specific communication
protocols. In example code, the message format is < 55 AA ID1 ID2 ID3 ID4 Length Data1 Data2 ...>. Users can send
data to the CAN bus from the terminal by entering data as the same format. 55 AA is the header. ID area is 4 bytes.
Length area is 1 byte, indicating the data length. Note that if users need to modify the UART frame, the code for
frame acquisition and parsing also need to be modified.
Table 29. UART Frame Form
Header Address Data Length Data
5. Determine the bridge structure, including what messages need to be converted, how to convert messages and so
on.
a. Consider whether the bridge is one-way or two-way. Typically each interface has two functions: receiving
and sending. Consider whether only some functions need to be included (such as UART reception and CAN
transmission). In example code, CAN-UART bridge is a two-way structure.
b. Consider what information to convert and the corresponding carrier(variable, FIFO). In example code, identifier,
data and data length are convert from one interface to the other interface. There are two FIFOs defined in code
as shown below.
typedef struct {
/*! Identifier */
uint32_t id;
/*! Data Length Code*/
uint32_t dlc;
/*! Data bytes*/ 7 0
uint16_t data[64];
} Custom_Element;
U2C_out
6 1
Interrupt Main()
U2C_in++ U2C_out++
Receive
U2C_count++ U2C_count-- Transmit
message message to
from UART U2C_FIFO
CAN
5 2
U2C_count = 2
C2U_in++ C2U_out++
Receive Transmit
message C2U_count++ C2U_count-- message to 4 3
from CAN C2U_FIFO UART
U2C_in
6. (Optionally) Consider priority design, congestion situation, error handling, and so on.
Design Considerations
1. Consider the information flow in the application to determine the information to be received or sent by each
interface, the protocols to be followed, and design appropriate information transfer carriers to connect different
interfaces.
2. The recommendation is to test the interface separately first, and then implement the overall bridge function. In
addition, consider the handling of abnormal situations, such as communication failure, overload, frame format error,
and so on.
3. The recommendation is to implement interface functions through interrupts to make sure of timely communication.
In example code, interface functions are usually implemented in the interrupt, and the transfer of information is
completed in the main() function.
Software Flowchart
The following figure shows the code flow diagram for CAN-UART bridge which explains how the messages received
in one interface and sent in the other interface. The CAN-UART bridge can be divided into four independent tasks:
receive from UART, receive from CAN, transmit through CAN, transmit through UART. Two FIFOs implement bidirectional
message transfer and message caching.
Interrupt Main()
getUartRxMsg
processCANTxMsg
sendCANTxMsg
processUartRxMsg
getCANRxMsg
processCANRxMsg processUartTxMsg
UART TX Interrupt
Device Configuration
This application makes use of TI System Configuration Tool (SysConfig) graphical interface to generate the configuration
code for the CAN and UART. Using a graphical interface to configure the device peripherals streamlines the application
prototyping process.
The code for what is described in Figure 52 can be found in the files from example code as shown in Figure 53.
Application Code
The following code snippet shows where to modify the interface function. Functions in table are categorized into different
files. Functions for UART receive and transmit are included in bridge_uart.c and bridge_uart.h. Functions for CAN receive
and transmit are included in bridge_can.c and bridge_can.h. Structure of FIFO element is defined in user_define.h.
Users can easily separate functions by file. For example, if only UART functions are needed, users can reserve
bridge_uart.c and bridge_uart.h to call the functions.
See the MSPM0 SDK and DriverLib documentation for the basic configuration of peripherals.
Table 30. Functions and Descriptions
Tasks Functions Description Location
UART receive getUartRxMsg() Get the received UART message bridge_uart.c
processUartRxMsg() Convert the received UART message format and store the message
bridge_uart.h
into gUART_RX_Element
UART transmit processUartTxMsg() Convert the gUART_TX_Element format to be sent through UART
sendUartTxMsg() Send message through UART
CAN receive getCANRxMsg() Get the received CAN message bridge_can.c
processCANRxMsg() Convert the received CAN message format and store the message
bridge_can.h
into gCAN_RX_Element
CAN transmit processCANTxMsg() Convert the gCAN_TX_Element format to be sent through CAN
sendCANTxMsg() Send message through CAN
Custom_Element is the structure defined in user_define.h. Custom_Element is used as the structure of FIFO element,
output element of UART/CAN transmit and input element of UART/CAN receive. Users can modify the structure
according to the need.
typedef struct {
/*! Identifier */
uint32_t id;
/*! Data Length Code*/
uint32_t dlc;
/*! Data bytes*/
uint16_t data[64];
} Custom_Element;
For FIFO, there are 2 global variables used as FIFO. 6 global variables are used to trace the FIFO.
Custom_Element U2C_FIFO[U2C_FIFO_SIZE];
Custom_Element C2U_FIFO[C2U_FIFO_SIZE];
uint16_t U2C_in = 0;
uint16_t U2C_out = 0;
uint16_t U2C_count = 0;
uint16_t C2U_in = 0;
uint16_t C2U_out = 0;
uint16_t C2U_count = 0;
Results
By using the XDS110 on the launchpad, users can use the PC to send and receive messages on the UART side. As
a demonstration, two launchpads can be used as two CAN-UART bridges to form a loop. When the PC sends UART
messages through one of the launchpads, XDS110 can receive UART messages from the other launchpad.
Additional Resources
• Download the MSPM0 SDK
• Learn more about SysConfig
• MSPM0G Technical Reference Manual (TRM)
• MSPM0G LaunchPad development kit
• MSPM0 CAN academy
• MSPM0 UART academy
Design Description
Many applications need to capture status change of several GPIOs at the same time, update, and then send the status
to host through UART. Cost-effective microcontroller (MCU) with enough GPIO resource can implement parallel-to-serial
and send data through UART to host, for example PC side, in real time. Download the code for this example.
Timer
(40ms)
Signal #1 PA10
Update data
Signal #2 PA11
& Construct
UART Host
the frame
format
Signal #N PA18
MSPM0 MCU
Required Peripherals
Compatible Devices
Based on the requirements in Table 31, this example is compatible with the devices in Table 32. The corresponding EVM
can be used for prototyping.
Table 32. Compatible Devices
Compatible Devices EVM
MSPM0L1xx LP-MSPM0L1306
MSPM0G3xx/1xx LP-MSPM0G3507
Design Steps
1. Capture 9 GPIO switches’ status.
2. Fill these 9 bits in data segment and transmit one completed frame to host PC through UART.
3. Update the data when any operation is detected or every 40ms.
Design Considerations
This implementation uses 9 GPIO Pins (PA10-PA18) to capture the switches’ status represented the corresponding
operations shown in Table 33:
Table 33. Correspondence Between Pins and Operations
GPIO Pins Operations
PA10 GPIO_Signal_10
PA11 GPIO_Signal_11
PA12 GPIO_Signal_12
PA13 GPIO_Signal_13
PA14 GPIO_Signal_14
PA15 GPIO_Signal_15
PA16 GPIO_Signal_16
PA17 GPIO_Signal_17
PA18 GPIO_Signal_18
In the above pins, PA14 is connected to S2 fixedly in Launch Pad and when S2 is pressed, PA14 is pulled down to
Ground. For the other pins, each pin can be connected to S1 through J11 and when S1 is pressed, the pin can be pulled
up to 3V3. For example, if the S1 is connected to PA18 and both SWs is pressed at same time, the data updates shown
in Table 34:
Table 34. Data Format of the 9 Pins
Bit15 Bit14 Bit13 Bit12 Bit11 Bit10 Bit9 Bit8 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
GPIO Pin PA18 PA17 PA16 PA15 PA14 PA13 PA12 PA11 PA10
Default 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
PA18&14 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
When any SW is pressed, MCU can update the data segment (2 Bytes) and check sum immediately and send the new
data, which is composed in the following format, through UART to PC.
If no SW is pressed for every 40ms, MCU can send the current status to PC. The package sent to PC has the format
shown in Table 35:
Table 35. Package Format Sent by UART
Bytes Header (2Byte) Data Source ID Destination ID Command Data index Data (N Checksum (2Byte)
Length (1Byte) (1Byte) (1Byte) (1Byte) Byte)
(1Byte)
Value 0x5A 0xA5 N 0~63 0~63 0~255 0~255 Data CSumL CSumH
Software Flowchart
Figure 57 shows the code flow diagram of main loop which is main function and GPIO interrupt handling which is
GROUP1_IRQHandler function.
TIMG0 interrupt handling is very simple which is entering Timer interrupt and send current data every 40ms,
Anyone of 9
Start
GPIOs ISR
Application code
Main loop
SYSCFG_DL_init();
NVIC_EnableIRQ(GPIO_MULTIPLE_GPIOA_INT_IRQN);
NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);
DL_TimerG_startCounter(TIMER_0_INST);
while (1) {
__WFI();
}
TIMG0_IRQHandler
switch (DL_TimerG_getPendingInterrupt(TIMER_0_INST)) {
case DL_TIMER_IIDX_ZERO:
transmitPacketBlocking(gTxPacket,UART_PACKET_SIZE);
break;
GPIO GROUP1_IRQHandler
if (DL_Interrupt_getPendingGroup(DL_INTERRUPT_GROUP_1)) {
dataStatus = (GPIOA->DIN31_0);
dataTemp = (dataStatus >> 10);
siganlChecksum = checkSum1ByteIn2ByteOut((gTxPacket+2),7);
DL_TimerG_stopCounter(TIMER_0_INST);
DL_TimerG_setTimerCount(TIMER_0_INST,TIMER_0_INST_LOAD_VALUE);
DL_TimerG_startCounter(TIMER_0_INST);
transmitPacketBlocking(gTxPacket,UART_PACKET_SIZE);
}
Results
Using Logical Analysis to capture data flow and show more details.
When S1 is pressed, PA18 occurs rising edge and data update. Then MCU sends the update data every 40ms.
If the rising edge occurs, but the last package has not been finished, MCU sends a data update after the last
transmission is accomplished.
Additional Resources
• Download the MSPM0 SDK
• Learn more about SysConfig
• MSPM0G Technical Reference Manual (TRM)
• MSPM0L Technical Reference Manual (TRM)
Figure 58 shows how to transfer data or commands from a universal asynchronous receiver-transmitter (UART) interface
to several target I2C controllers using the MSPM0 as an I2C expander. Incoming UART packets are specifically formatted
to facilitate the transition to I2C communication. Figure 58 also illustrates how errors can be communicated back to the
host device. Code for this example is found in the MSPM0 SDK.
VCC
I2C Peripheral 1
MSPM0
I2C
I2C Peripheral 2
Host Device (Controller)
VCC
UART UART
I2C
I2C Peripheral 3
(Controller)
I2C Peripheral 4
Required Peripherals
Compatible Devices
Table 37 lists the compatible devices with the corresponding EVMs based on the requirements in Table 36. Using other
MSPM0 devices and corresponding EVMs is possible if the requirements in Table 36 are met.
Table 37. Compatible Devices
Compatible Devices EVM
MSPM0Lxxxx LP-MSPM0L1306
MSPM0Gxxxx LP-MSPM0G3507
Design Steps
1. Set UART peripheral instance, I2C peripheral instance, and pin out to desired device pins in SysConfig.
2. Set UART baud rate in SysConfig. Default is 9600baud.
3. Set I2C clock speed in SysConfig. Default is 100kHz.
4. Define the maximum I2C packet size the bridge handles.
5. Define key UART header values (optional).
6. Customize error handling (optional).
Design Considerations
• Communication Speeds: Increasing speeds increases data throughput and decreases chances of collision. Adjusting
the external pullup resistors according to I2C specifications is necessary to allow for communication if I2C speeds are
increased. Optimizations include higher device operating speeds, multiple transfer buffers, header size reduction, or
state machine simplification.
• UART Header: The UART packer header and start byte are customizable for the application. Texas Instruments
recommends assigning values that are less likely to occur during the start of typical data transfers.
• Error Handling: Correspond the error values to ASCII numerical values if monitoring UART bus with a computer
terminal. Make sure the host UART device can read error values and know the associated meanings so appropriate
action can be taken by the host. Add additional error types by modifying the ErrorFlags structure type and add
additional error detection code within the Uart_Bridge(). The current implementation detects limited errors and
reports back the corresponding code on the UART interface. The application code then breaks from the current
communication state machine. Users can add additional error handling code to change the behavior of the bridge
when an error occurs. For example, re-sending an I2C packet after a NACK occurs.
Figure 59, Figure 60, and Figure 61 show the code flow diagrams for Main UART Bridge functionality, Main() plus
UART ISR, and I2C ISR, respectively, for Figure 58.
Switch:
Uart_Bridge()
UART Bridge Status
U.B.Status = Waing;
Clear UART Start
I2C Status = TX Started Detect
Wait for full UART RX. True
Transfer data to I2C buf. I2C Write?
U.B.Status = I2C Write
False
All I2C bytes sent and False False Clear UART RX Data
Break All I2C Bytes Recieved?
I2C Status = Idle?
U.B.Status = True
I2C Read?
I2C Read True
True Break
False
Reset I2C counts; U.B.Status = TX UART
Reset UART controls. U.B.Status = Wai ng
Clear I2C R/W
Break
False
Sleep Uart_Bridge()
UART ISR:
RX Interrupt
False
UART Start byte
detected?
True
Set UART
Fill UART Receive buer. True
UART Bu er overow? overow error
gUartStartDetected =true
ag
False
Exit
Figure 60. Software Flow Diagrams for MAIN Loop and UART ISR
RX FIFO Trigger I2C Status = Fill data bu er to Check for buer True Set I2C overrun
INT RX_IN_PROGRESS gI2C_Length overrun error ag
False
Figure 62 shows the required UART packet for proper bridging to the I2C interface. The values shown are the default
header values defined within Figure 58.
• Start Byte: The value used by the bridge to indicate a new transaction is starting. UART transmissions are ignored
until this value is acknowledged by the bridge.
• I2C Address: The address of the I2C target the host communicates with.
• I2C Read or Write Indicator: The value that functions the bridge to read or write from the target I2C device.
• Message Length N: The length of data transferred in bytes. This value cannot be larger than the defined I2C
maximum packet length.
• Bridge Index: The I2C controller that the host communicates on.
• D0, D1...., Dn: The data transferred within the bridge.
Device Configuration
This application makes use of TI System Configuration Tool (SysConfig) graphical interface to generate the configuration
code for the COMP and two TIMER modules. Using a graphical interface to configure the device peripherals streamlines
the application prototyping process.
Application Code
To change the specific values used by the UART packet or the maximum I2C packet size, modify the following #defines
in the beginning of the code example, as demonstrated in the following code block:
Additional Resources
E2E
See TI's E2E™ support forums to view discussions and post new threads to get technical support for utilizing MSPM0
devices in designs.
Description
Figure 63 demonstrates how to transfer data or commands from a UART interface to several target I2C peripherals using
the MSPM0 as an I2C controller. Incoming UART packets are specifically formatted to facilitate the transition to I2C
communication. Figure 63 can communicate errors in communication back to the host device. Code for this example is
found at UART to I2C Bridge Sub-System Code.
VCC
I2C Peripheral 1
I2C
UART UART I2C Peripheral 2
(Controller)
I2C Peripheral 3
Requirements
Compatible Devices
Compatible devices are listed in Table 39 with corresponding EVMs based on the requirements in Table 38. Using other
MSPM0 devices and corresponding EVMs is possible if the requirements in Table 38 are met.
Table 39. Compatible Devices
Compatible Devices EVM
MSPM0Lxxxx LP-MSPM0L1306
MSPM0Gxxxx LP-MSPM0G3507
Design Steps
1. Set UART peripheral instance, I2C peripheral instance, and pin out to desired device pins in Sysconfig.
2. Set UART baud rate in Sysconfig. Default is 9600 baud.
3. Set I2C clock speed in Sysconfig. Default is 100 kHz.
4. Define the max-I2C packet size the bridge handles.
5. Define key UART header values (optional).
6. Customize error handling (optional).
Design Considerations
1. Communication speed.
a. Increasing both interface speeds increases data throughput and decreases chances of data collisions.
b. Adjusting external pull-up resistors according to I2C specifications is necessary to allow for communication if
I2C speeds are increased.
c. Repeated, large data packets at higher speeds can impact overall system performance. Additional optimization
of this code can be necessary to meet increased bridge utilization. Additional optimizations include higher device
operating speeds, multiple transfer buffers, header size reduction or state machine simplification.
Note
Figure 63 example was only tested with default speeds of 9600 baud (UART) and 100 kHz (I2C) speeds.
2. UART header.
a. The UART packet header and start byte values are customizable for your application. Texas Instruments
recommends assigning values that are less likely to occur during the start of typical data transfers.
3. Error handling.
a. Correspond the error values to ASCII numerical values if monitoring UART bus with a computer terminal.
b. Make sure the host UART device can read error values and know the associated meanings so appropriate action
can be taken by the host.
c. Add additional error types by modifying the ErrorFlags structure type and add additional error detection code
within the Uart_Bridge().
d. The current implementation detects limited errors and reports back the corresponding code on the UART
interface. The application code then breaks from the current communication state machine. Users can add
additional error handling code to change the behavior of the bridge when an error occurs. For example,
re-sending an I2C packet after a NACK occurs.
Note
Figure 63 currently flags common errors and assigns them numerical values, as defined in the ErrorFlags
structure type.
Software Flowcharts
Figure 64, Figure 65, and Figure 66 show the code flow diagrams for Main UART Bridge functionality, Main() plus UART
ISR, and I2C ISR, respectively, for Figure 63.
Switch:
Uart_Bridge()
UART Bridge Status
U.B.Status = Waing;
Clear UART Start
I2C Status = TX Started Detect
Wait for full UART RX. True
Transfer data to I2C buf. I2C Write?
U.B.Status = I2C Write
False
All I2C bytes sent and False False Clear UART RX Data
Break All I2C Bytes Recieved?
I2C Status = Idle?
U.B.Status = True
I2C Read?
I2C Read True
True Break
False
Reset I2C counts; U.B.Status = TX UART
Reset UART controls. U.B.Status = Wai ng
Clear I2C R/W
Break
False
Sleep Uart_Bridge()
UART ISR:
RX Interrupt
False
UART Start byte
detected?
True
Set UART
Fill UART Receive buer. True
UART Bu er overow? overow error
gUartStartDetected =true
ag
False
Exit
Figure 65. Software Flow Diagrams for MAIN Loop and UART ISR
RX FIFO Trigger I2C Status = Fill data bu er to Check for buer True Set I2C overrun
INT RX_IN_PROGRESS gI2C_Length overrun error ag
False
Figure 67 shows the required UART packet for proper bridging to the I2C interface. The values shown are the default
header values defined within Figure 63.
• Start Byte: The value used by the bridge to indicate a new transaction is starting. UART transmissions are ignored
until this value is acknowledged by the bridge.
• I2C Address: The address of the I2C target the host communicates with.
• I2C Read or Write Indicator: The value that functions the bridge to read or write from the target I2C device.
• Message Length N: The length of data transferred in bytes. This value cannot be larger than the defined I2C max
packet length.
• D0, D1...., Dn: The data transferred within the bridge.
Device Configuration
Figure 63 application uses the 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.
Application Code
To change the specific values used by the UART Packet or the max I2C packet size, modify the #defines in the beginning
of the document, as demonstrated in the following code block:
Several points in the code are comments around error detection. A user can add custom error handling and additional
error reporting at these points in the code. For brevity, not all error handling code intersections are included here. In
practice, search for comments in the code similar to what is demonstrated in the following code block:
Additional Resources
• Texas Instruments, UART to I2C Bridge Sub-System Code
• Texas Instruments, Learn More About TI Sysconfig, tool
• Texas Instruments, MSPM0 Support Development Kit, tool
• Texas Instruments, MSPM0 Academy: UART, training
• Texas Instruments, MSPM0 Academy: I2C, training
This subsystem demonstrates how to implement the MSPM0 device as a universal asynchronous receiver - transmitter
(UART) to serial peripheral interface (SPI) bridge. Incoming UART packets are expected to be in a specific format to
facilitate SPI communication. This example also has the ability to determine error conditions and communicate them
back to the UART device. The code for this example is found in the MSPM0 SDK.
MSPM0
UART Host Device PICO SPI Device
TX TX POCI
UART UART SPI CS SPI
RX SCLK
RX
Figure 68. System Functional Block Diagram
Required Peripherals
Table 40. Required Peripherals
Peripheral Used Notes
UART Called UART_BRIDGE_INST in code
SPI Called SPI_0_INST in code
Compatible Devices
Based on the requirements in Table 40, this example is compatible with the devices shown in Table 41. Generally, any
device with the capabilities listed in the required peripherals table can support this example.
Table 41. Compatible Devices
Compatible Devices EVM
MSPM0Lxxxx LP-MSPM0L1306
MSPM0Gxxxx LP-MSPM0G3507
Design Steps
1. Set up the SPI module in SysConfig. Put the device in controller mode, and leave the rest of the settings on default.
In the Advanced Configuration tab, make sure that the RX FIFO Threshold level is set to the RX FIFO contains ≥
1 entry. Make sure that the TX FIFO Threshold level is set to the TX FIFO contains ≤ 2 entries. Now navigate to
the Interrupt configuration tab, and enable the Receive, Transmit, RX Timeout, Parity Error, Receive FIFO Overflow,
Receive FIFO Full, and Transmit FIFO Underflow interrupts.
2. Set up the UART module in SysConfig. Set the baud rate to 9600. Enable the Receive interrupt.
Design Considerations
1. In the application code, make sure to check the SPI and UART maximum packet sizes against the requirements of
the application.
2. To increase the UART baud rate, adjust the value in the SysConfig UART tab labeled Target Baud Rate. Below this,
observe the calculated baud rate change to reflect the target baud rate. This is calculated using the available clocks
and dividers.
3. Check error flags and handle them appropriately. The UART and I2C peripherals are both capable of throwing
informative error interrupts. For easy debugging, this subsystem uses an enumeration and a global variable to save
error codes when error codes are thrown. In real-world applications, handle errors in the code so the errors do not
break the project.
4. The current form of the project defines all of the formatted parts of the packet, such as UART_START_BYTE,
UART_READ_SPI_BYTE, and UART_WRITE_SPI_BYTE. These are accompanied by definitions to specify where in
the packet header these commands are found. Values in the implementation can be changed. Make sure that the
UART start and read or write bytes are bytes that are not expected in the application.
Figure 69 shows the code flow diagram for this example and explains the different UART Bridge wait states and the
actions the device takes in each state. The flow chart also shows the Interrupt Service Routines for UART and SPI.
No
Inialize Wait for rst Yes Transmit
peripherals Error detected?
received Uart_Bridge() error code
and state UART byte via UART
machine
Switch:
Uart Bridge
Status
Yes
Error
Save data to
Break status =
Buer
UART full
Device Configuration
This application makes use of the 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 described in the software flow chart is found in the uart_to_spi_bridge.c file.
Required UART Packets
Figure 70 shows the required UART packet for performing reads and writes with the SPI. The values shown are the
default header values defined in the example.
• Start Byte: The value used by the bridge to indicate a new transaction is starting. UART transmissions are ignored
until this value is detected by the bridge.
• SPI Read or Write Indicator: This value tells the bridge whether to perform a read from or a write to the SPI device.
• Message Length N: The length of the data being transferred in bytes.
• D0, D1, ..., D(N – 1): Data being transferred to the bridge
Note
The Read packet includes only the header. When conducting a read, there is no need to send data after the
packet. The bridge device automatically send the correct amount of dummy data to the SPI peripheral to fetch
the read data.
Write Packet
Read Packet
UART Header
Figure 70. UART Write and Read Packet Format
Application Code
Some users want to change the specific values that are used by the UART packet header, or the maximum packet size.
This change is done by modifying the #define values found in the beginning of the uart_to_spi_bridge.c file, as
shown in the following code.
Many portions of the code are intended to be used for error detection and handling. At these points in the code, the user
can use additional error handling or reporting for a more robust application. For example, the following code segment
demonstrates a way to check for errors in SPI transmissions, and sets an error flag in the event of an error. The user can
quit sending and change the UART Bridge Status here to reflect the error. This and many other areas in the code have
options for error consideration.
Additional Resources
E2E
See TI's E2E support forums to view discussions and post new threads to get technical support for utilizing MSPM0
devices in designs.
The Emulating a Digital MUX software example demonstrates how to use GPIO interrupts to emulate a digital MUX. Similar
to a logic based MUX, the MCU uses select signals (S0 and S1) to determine which input channel (C0, C1, C2, and C3)
is output at a given time. Doing this through the MCU not only eliminates the need for an external MUX, but also allows
flexible pin assignments that can help aid PCB routing. This specific example emulates a 4-input channel, 2-select-signal
digital MUX.
C0 I/O
S1 S0 Output
C1 I/O 0 0 C0
1 1 C3
C3 I/O
I/O I/O
S1 S0
Required Peripherals
Compatible Devices
Based on the requirements in Table 42, the compatible devices are listed in Table 43. The corresponding EVM can be
used for quick evaluation.
Table 43. Compatible Devices
Compatible Devices EVM
MSPM0C LP-MSPM0C1104
MSPM0Lx LP-MSPM0L1306
MSPM0Gx LP-MSPM0G3507
Design Steps
1. Determine the amount of GPIOs needed by the application. In this case, there are 4 input channel pins, two select
pins, and one output pin.
2. Configure the GPIO output pin in SysConfig as an output.
3. Configure the GPIO input channel pins and select pins in SysConfig as inputs with interrupts.
4. Write application code for the interrupts to change the output based on the Channel and SELECT digital signals.
Design Considerations
1. Number of input channel and select pins: A 4-input MUX requires two select pins. However, an 8-input MUX requires
three select pins.
2. The logic table: What select pin configuration determines which input channel is selected as the output.
3. Interrupts: Interrupts must be placed on all input channel and select pins as the output signal is generated by setting
or clearing the output signal based on the selected input channel.
4. Propagation delay: There is a possibility for a propagation delay due to interrupts. The propagation delay is based on
the clock speed.
Software Flow Chart
Figure 72 shows the software flow chart for this subsystem example and explains the GPIO interrupt routine used to
emulate a digital MUX.
S1 = 0 S1 = 0 S1 = 1 S1 = 1
S0 = 0 S0 = 1 S0 = 0 S0 = 1
Application Code
This application uses the TI System Configuration tool (SysConfig) graphical interface to generate the configuration code
for the device peripherals. Using a graphical interface to configure the device peripherals streamlines the application
prototyping process.
In addition, this application uses GPIO interrupts on all input pins configured and enabled in the GPIO peripheral in
SysConfig. Based on the GPIO pins configured in SysConfig, the respective GPIO interrupts must also be manually
enabled in the main() portion of the code using the NVIC_EnableIRQ(); function. After enabling the interrupts, the
main() code waits for an interrupt. This means that any time one of the input signals changes state, the GPIO interrupt
service routine starts. The main() portion of this code is as follows:
int main(void)
{
SYSCFG_DL_init();
/* Enable GPIO Port A Interrupts */
NVIC_EnableIRQ(GPIO_MULTIPLE_GPIOA_INT_IRQN);
while (1) {
__WFI();
}
}
The following code snippet showcases the GPIO interrupt service routine. There are two switch cases: one for the
interrupt types, and one to determine which input channel is selected to be output. The second switch case first checks
the select pins to determine the respective states. Depending on those states, the input channel is selected based on the
logic truth table (see Figure 71). For each individual case, the selected input channel pin is checked, and the output pin
is set to match. The code then breaks out of the interrupt service routine, and then returns to wait for another interrupt.
In addition, this example code uses pin PA0 on the LP-MSPM0L1306 as the output pin, which turns a red LED on and off
based on the output signal.
void GROUP1_IRQHandler(void){
switch (DL_Interrupt_getPendingGroup(DL_INTERRUPT_GROUP_1)){
case GPIO_MULTIPLE_GPIOA_INT_IIDX:
switch (DL_GPIO_readPins(SELECT_PORT, SELECT_S1_PIN | SELECT_S0_PIN)){
case 0: /* S1 = 0, S0 = 0 */
/* Check Channel 0 and set output to match */
if (DL_GPIO_readPins(INPUT_PORT, INPUT_CHANNEL_0_PIN)){
DL_GPIO_setPins(OUTPUT_PORT, OUTPUT_LED_PIN);
} else {
DL_GPIO_clearPins(OUTPUT_PORT, OUTPUT_LED_PIN);
}
break;
case SELECT_S0_PIN: /* S1 = 0, S0 = 1 */
/* Check Channel 1 and set output to match */
if (DL_GPIO_readPins(INPUT_PORT, INPUT_CHANNEL_1_PIN)){
DL_GPIO_setPins(OUTPUT_PORT, OUTPUT_LED_PIN);
} else {
DL_GPIO_clearPins(OUTPUT_PORT, OUTPUT_LED_PIN);
}
break;
case SELECT_S1_PIN: /* S1 = 1, S0 = 0 */
/* Check Channel 2 and set output to match */
if (DL_GPIO_readPins(INPUT_PORT, INPUT_CHANNEL_2_PIN)){
DL_GPIO_setPins(OUTPUT_PORT, OUTPUT_LED_PIN);
} else {
DL_GPIO_clearPins(OUTPUT_PORT, OUTPUT_LED_PIN);
}
break;
case SELECT_S1_PIN | SELECT_S0_PIN: /* S1 = 1, S0 = 1 */
/* Check Channel 3 and set output to match */
if (DL_GPIO_readPins(INPUT_PORT, INPUT_CHANNEL_3_PIN)){
DL_GPIO_setPins(OUTPUT_PORT, OUTPUT_LED_PIN);
} else {
DL_GPIO_clearPins(OUTPUT_PORT, OUTPUT_LED_PIN);
}
break;
}
break;
}
}
Results
Figure 73 shows a logic capture of the different input-to-output signals. The input channels C0 through C3 are colored
white, brown, red, and orange, respectively. S0 is yellow and S1 is green. Finally, the output signal is blue. The capture is
marked to showcase how the different inputs change the output signal.
Additional Resources
E2E
See TI's E2E™ support forums to view discussions and post new threads to get technical support for utilizing MSPM0
devices in designs.
5V Interface
Description
This example demonstrates how to interface with signals up to 5 V using open-drain IOs (ODIOs) on an MSPM0 device.
With the use of external pullup resistors, the open-drain IOs allow for communication across multiple voltage domains at
voltage levels higher than the MSPM0 VDD supply voltage.
Figure 74 displays a functional block diagram of the peripherals used in this example.
5V
MSPM0 MCU
VDD
SCL
Open Drain IO
SDA
Open Drain IO
Required Peripherals
Compatible devices
Based on the requirements in Table 44, this example is compatible with the devices in Table 45. The corresponding EVM
can be used for prototyping.
Table 45.
Compatible Devices EVM
MSPM0Lxxx LP-MSPM0L1306
MSPM0Gxxx LP-MSPM0G3507
Design steps
Design considerations
1. Pullup resistor: A pullup resistor is required to output high for I2C and UART functions on ODIOs.
2. Drive strength control: This is not available for ODIO types.
Additional Resources
• Download the MSPM0 SDK
• Learn more about SysConfig
• MSPM0L LaunchPad
• MSPM0G LaunchPad
Task Scheduler
Description
This subsystem example shows how to implement a simple, non-preemptive, run-to-completion (RTC) scheduler in
MSPM0. The example includes both the scheduler, and simple task header and source files, which demonstrate the
minimum requirements for building tasks for this kind of scheduling implementation. In a system, use of an RTC
scheduler is most appropriate when there are multiple tasks which need to be completed by the system, that can be
triggered in any order, and the actual execution time or order of these tasks is not critical.
Task 1 Task 2 Task 3 Task 2 Task 1
Pending Pending Pending Pending Pending
CPU Active
Task 1 Task 2 Task 3 Task 2 Task 1
CPU Idle
Task 1 Task 2 Task 3 Task 2 Task 1
Complete Complete Complete Complete Complete
Required Peripherals
The task scheduler subsystem is generic and appropriate for any device in the MSPM0 portfolio. Table 46 lists the
peripherals used in the example tasks, but these are not required to make use of the scheduler portion of the example.
Table 46. Required Peripherals
Subblock Functionality Peripheral Use Notes
DAC8 (Optional) (1 ×) COMP Shown as COMP_0_INST in code
Buffer (Optional) (1 ×) OPA Shown as OPA_0_INST in code
Timer (Optional) (1 ×) TIMG Shown as TIMER_0_INST in code
LED Output (Optional) (1 ×) GPIO Shown as GPIO_LEDS_USER_LED_1 in code
Shown as
Switch Input (Optional) (1 ×) GPIO
GPIO_SWITCHES_USER_SWITCH_1 in code
Compatible Devices
Based on the requirements shown in Table 46, the example code is compatible with the devices shown in
Table 47.
Table 47. Compatible Devices
Compatible Devices EVM
MSPM0Lx LP-MSPM0L1306
MSPM0Gx LP-MSPM0G3507
Design Steps
1. Either start with the example subsystem project, or add the scheduler source and header files to the existing
project.
2. The scheduler function is constructed to act as the main software loop for the application. After initialization, add a
call to the scheduler function as shown in Section 4.3.7.
3. For each task to be performed in the system, create a function to get, set, and reset the pending flag for the
appropriate task. Also create the actual function to be run when the scheduler attempts execution. The DAC8Driver
and SwitchDriver source and header files provide simple examples of how this can be done.
4. Add the appropriate Interrupt Request (IRQ) handlers to enable the pending tasks based on the required hardware
events. The IRQ handlers set the pending task flags, and increment the pending task counter. These values are
checked by the scheduler when the device is woken from sleep by a system interrupt.
Design Considerations
When integrating tasks into the task scheduler subsystem, consider the following:
1. If multiple interrupts or tasks are queued at the same time, the main scheduler loop services the tasks in the order
the tasks appear in gTasksList. This can be considered a simple priority, although still not preemptive.
2. All tasks are interrupt-driven in this architecture, meaning that the appropriate IRQ handler must set the pending flag
associated with the task to be run. If only a single operation of an event makes sense in the system, only increment
the gTasksPendingCounter if the flag was not already set. If multiple occurrences of an event need to be queued
at the same time, use an integer value for the pending flag, rather than strictly a true or false Boolean value.
Enable Required
Interrupts
Start Required
Peripherals
No
Are any tasks still Yes
pending?
Application Code
Scheduler Code
The scheduler code is stored in the modules/scheduler/scheduler.c file, and includes a list of all function pointers
that the scheduler needs to access in gTasksList. Each task can provide a function for getting and resetting the ready
flag or pending flag, and a pointer to the task to be run.
Within the scheduler loop, the gTasksPendingCounter value keeps track of how many tasks are pending. As the loop
cycles through each pending task flag, when the loop finds one that is pending, the scheduler loop decrements this
counter. After all tasks are cleared, the devices enters low power mode via a call to __WFI.
#include "scheduler.h"
#define NUM_OF_TASKS 2 /* Update to match required number of tasks */
volatile extern int16_t gTasksPendingCounter;
/*
* Update gTasksList to include function pointers to the
* potential tasks you want to run. See DAC8Driver and
* switchDriver code and header files for examples.
*
*/
static struct task gTasksList[NUM_OF_TASKS] =
{
{ .getRdyFlag = getSwitchFlag, .resetFlag = resetSwitchFlag, .taskRun = runSwitchTask },
{ .getRdyFlag = getDACFlag, .resetFlag = resetDACFlag, .taskRun = runDACTask },
/* {.getRdyFlag = , .resetFlag = , .taskRun = }, */
};
void scheduler() {
/*
* Iterate through tasks list until all tasks are completed.
* Checking gTasksPendingCounter prevents us from going to
* sleep in the case where a task was triggered after we
* checked its ready flag, but before we went to sleep.
*/
while(gTasksPendingCounter > 0)
{
for(uint16_t i=0; i < NUM_OF_TASKS; i++)
{
/* Check if current task is ready */
if(gTasksList[i].getRdyFlag())
{
/* Execute current task */
gTasksList[i].taskRun();
/* Reset ready for for current task */
gTasksList[i].resetFlag();
/* Disable interrupts during read, modify, write. */
__disable_irq();
/* Decrement pending tasks counter */
(gTasksPendingCounter)--;
/* Re-enable interrupts */
__enable_irq();
}
}
}
/* Sleep after all pending tasks are completed */
__WFI();
}
}
The initialization of the device for operation of the scheduler and tasks is handled within the main application source
code file, task_scheduler.c. The call to SYSCFG_DL_init configures the hardware peripherals needed in the
example code, then interrupts are enabled, and the TIMER_0_INST counter is started. After that, the code enters the
scheduler loop.
Within the required IRQ handlers, the appropriate flags are set during interrupt to tell the scheduler a task is pending.
#include "ti_msp_dl_config.h"
#include "modules/scheduler/scheduler.h"
int main(void)
{
SYSCFG_DL_init();
/* Enable IRQs */
NVIC_EnableIRQ(GPIO_SWITCHES_INT_IRQN);
NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);
Additional Resources
E2E
See TI's E2E™ support forums to view discussions and post new threads to get technical support for utilizing MSPM0
devices in designs.
The Connected Diode Matrix example demonstrates how to use a matrix format to reduce the number of necessary
GPIO pins when using six or more LEDs. This specific example uses nine LEDs and six GPIOs to form and control a
3 × 3 LED matrix. The matrix format creates a grid that uses two GPIOs per LED (or diode). This format is especially
useful when creating a sign or display out of LEDs. The GPIO pins for the LED matrix are divided into row and column
pins. When the row pins connect the cathodes of the LEDs, as Figure 77 shows, the matrix is a common row cathode.
Common row anode is when the row pins connect the anodes of the LEDs. Depending on the configuration of the LEDs
in the LED matrix, the row and column pins are set to active high or active low. For this subsystem example, the row
pins are active low and the column pins are active high. For the LED matrix to function properly, the LEDs in the matrix
must be controlled one row at a time. The application code for this example uses a state machine to cycle continuously
through the rows to turn the LEDs on and off.
COL 1
COL 2
220 Ω
COL 3
220 Ω
220 Ω
ROW 1
MSPM0Gx/ ROW 2
MSPM0Lx
ROW 3
Required Peripherals
Compatible Devices
Based on the requirements in Table 48, the compatible devices are listed in Table 49. The corresponding EVM can be
used for quick evaluation.
Table 49. Compatible Devices
Compatible Devices EVM
MSPM0Lx LP-MSPM0L1306
MSPM0Gx LP-MSPM0G3507
Design Steps
1. Determine the number of LEDs used in the matrix as well as the matrix dimensions. The matrix dimensions
determine the number of GPIO pins needed.
2. Separate the GPIO pins into row pins and column pins.
3. Configure all row and column pins as outputs.
4. Determine the mask value for the column pins by taking the bit-wise OR of all the column pin GPIO values.
5. Create the memory table and the memory table update function.
6. Create an enumeration table for the row update state machine to cycle between rows.
7. Configure timer interrupts and write application code for the row update state machine and to increment the LED
state.
8. Write application code to set the display period and to update the memory table with new column pin values as the
display changes.
Design Considerations
1. Number of LEDs and matrix dimensions: The matrix dimension determines the number of GPIO pins needed to run
the matrix. For example, a 16 LED matrix can use 8 pins in a 4 × 4 matrix or 10 pins in a 2 × 8 matrix.
2. LED configuration: The active states of the row and column pins is dependent on whether the matrix is in common
row cathode or common row anode.
3. Column pin values: Column pin values are set in a memory table. The exact values are determined by which pins are
selected and the respective column mask. For ease of setup, selecting pins that are in numerical order with no gaps
is the easiest.
4. Column and row pin connections: When connecting pins to the LED matrix, application programming is easiest
if the row pins start from the topmost row (moving down) and the column pins start from the right-most column
(moving left).
5. Timer interrupts: The speed of the interrupts affects the display period and how long each row of LEDs is on per
the state machine cycle. This specific example interrupts every 5ms, preventing the human eye from noticing any
flickering.
6. Updating the memory table: The specific method of updating the memory table depends on the application. This
example increments a counter (otherwise known as the display period) up to a specified value. When the counter
reaches that value, the memory table is updated to set a new display.
Figure 78 shows the software flow chart for this subsystem example and explains the timer interrupt routine and state
machine used to control the LED matrix.
Write to memory map based on Set rowState = ROW_2 Set rowState = ROW_N Set rowState = ROW_1
LED_displayPeriod
Increment LED_displayPeriod
Application Code
This application makes use of the TI System Configuration tool (SysConfig) graphical interface to generate the
configuration code for the device peripherals. Using a graphical interface to configure the device peripherals streamlines
the application prototyping process.
There are a few key variables that this example uses: the number of rows, the column mask value, the display period
duration, and a counter to track the number of interrupts. The number of rows is a defined value that is used to build the
memory table array. The column mask is equivalent to the bitwise OR of the GPIO values of all of the column pins used.
The column mask is used with the memory table to determine which column pins need to be on or off per row at a given
time. The display period variable is multiplied by the duration of time per timer interrupt to determine the amount of time
that a single memory table write is used. For this example, the display period value is defined as 100 which equates to a
display period time of half a second. The counter, or gLedState, is used to track the number of interrupts in relation to
the display period value. This makes sure that the memory table is written to every display period.
#define NUMBER_OF_ROWS 3
#define COL_MASK 0x38
#define LED_DISPLAY_PERIOD 100 /* timer period = 5 ms, so display period = 500 ms */
volatile uint32_t gLedState = 0;
void LED_updateTable(uint8_t rowNumber, uint8_t LEDs);
The next snippet of code shows the enumeration table as well as the timer Interrupt Request (IRQ). The enumeration
table defines the row states that the rowState switch cycles through in the timer IRQ. For each rowState (or row pin),
the current row is turned on, the previous row is turned off, and the columns are set by comparing the column mask
value with the memory table value. The next rowState is then set. This example cycles sequentially from row one to
row N and back to one. Before leaving the timer IRQ, gLedState is incremented to track the number of interrupts for
each display period.
typedef enum {
ROW_1,
ROW_2,
ROW_3
}rowNumber;
void LED_STATE_INST_IRQHandler(void) {
switch (DL_TimerG_getPendingInterrupt(LED_STATE_INST)){
case DL_TIMER_IIDX_ZERO:
/* State machine to auto cycle from row 1 to row N and repeat */
switch (rowState){
case ROW_1:
/* Turn on ROW_1, Turn off ROW_3 */
DL_GPIO_clearPins(ROW_PORT, ROW_ROW_1_PIN);
DL_GPIO_setPins(ROW_PORT, ROW_ROW_3_PIN);
/* Increment LED_STATE */
gLedState++;
break;
default:
break;
}
}
In the main code, all that is done is to write to the memory table every display period. This repeats indefinitely. This
particular code uses binary to make determining which LED is on easier as the layout of 1s and 0s mimics the matrix
layout. The binary value is 1 if an LED is on and 0 if an LED is off.
while(1){
__WFI();
/* Flash TI on repeat in half second increments */
if (gLedState == LED_DISPLAY_PERIOD){ /* Display "T" for one display period */
LED_updateTable(1, 0b111);
LED_updateTable(2, 0b010);
LED_updateTable(3, 0b010);
} else if (gLedState == LED_DISPLAY_PERIOD*2){ /* Blank for one display period */
LED_updateTable(1, 0b000);
LED_updateTable(2, 0b000);
LED_updateTable(3, 0b000);
} else if (gLedState == LED_DISPLAY_PERIOD*3){ /* Display "I" for one display period */
LED_updateTable(1, 0b111);
LED_updateTable(2, 0b010);
LED_updateTable(3, 0b111);
} else if (gLedState == LED_DISPLAY_PERIOD*4){ /* Blank for one display period */
LED_updateTable(1, 0b000);
LED_updateTable(2, 0b000);
LED_updateTable(3, 0b000);
} else if (gLedState > LED_DISPLAY_PERIOD*4){ /* Reset gLedState and start over */
gLedState = 0;
}
}
Hardware Design
This specific subsystem example requires nine LEDs, three resistors, and at least six wires. To setup the matrix, arrange
the LEDs in 3 × 3 rows. Connect the cathodes of each row of LEDs together. Then, connect the anodes of each column
of LEDs together. Connect a 220Ω resistor to each column line. From there, connect the row lines and column lines to
the correct device pins based on the device configuration. See Figure 77 for connection guidelines.
Results
Figure 79 showcases the intended results of the "T" display period for this application. The top half of the figure shows
the state of each row as the application state machine cycles through each row per interrupt. The bottom half of the
figure shows what the composite image over a full cycle is. This is how the matrix appears to the human eye.
ON ON ON
OFF ON OFF
OFF ON OFF
Additional Resources
E2E
See TI's E2E™ support forums to view discussions and post new threads to get technical support for utilizing MSPM0
devices in designs.
This subsystem example in Figure 80 demonstrates how to set up the internal comparator and timers within the
MSPM0L and MSPM0G family of devices to implement a simple frequency detector. The capture period can be
configured to allow for various ranges of frequencies.
DAC8
PMUX
+
Comparator TIMG4
COMP0_OUT
Required Peripherals
Compatible Devices
Compatible devices are listed in Table 51 with corresponding EVMs based on the requirements in Table 50. Using other
MSPM0 devices and corresponding EVMs is possible if the requirements in Table 50 are met.
Table 51. Compatible Devices
Compatible Devices EVM
MSPM0Lxxxx LP-MSPM0L1306
MSPM0Gxxxx LP-MSPM0G3507
Design Steps
1. Set COMP peripheral instance, TIMER - Compare instance, TIMER instance, and pin out to desired device pins in
SysConfig.
2. Set COMP voltage in SysConfig.
3. Set TIMER - Compare clock speed in SysConfig. Default is 4MHz.
4. Set TIMER clock speed in SysConfig. Default is 32,768Hz.
5. Define desired frequency range.
6. Define the capture period based on desired frequency range.
7. Set TIMER - Compare Number of Edges to Detect in SysConfig. Also define MAX_COMPARE_COUNT in code.
(Optional)
Design Considerations
1. Capture Period: The length of the capture period affects what range of frequencies can be measured. Longer
periods allow for slower frequencies to be captured.
2. Clock Speed: Choosing a clock speed that allows for accurate measurement of frequency is important for this
example to work properly.
Figure 81 shows the code flow diagrams for Main() plus TIMER ISR for Figure 80.
Is gFrequency True
Main() Toggle LED On
within range?
False
False False
Figure 81. Software Flow Diagrams for MAIN Loop and TIMER ISR
Device Configuration
This application makes use of TI System Configuration Tool (SysConfig) graphical interface to generate the configuration
code for the COMP and two TIMER modules. Using a graphical interface to configure the device peripherals streamlines
the application prototyping process.
Application Code
To change the specific values used by the TIMERs and desired frequency range, modify the #defines in the beginning of
the document, as demonstrated in the following code block:
Additional Resources
E2E
See TI's E2E™ support forums to view discussions and post new threads to get technical support for utilizing MSPM0
devices in designs.
Description
The PWM duty cycle directly correlates to the brightness of the LED. When using an LED as an indicator or a light source
in an application, you can use a PWM signal to drive the LED brightness and power consumption. The timer modules in
the MPSM0 can be used to generate PWM signals with varying frequency and duty cycles. This example code dims and
brightens the LED in a heartbeat manner to display the full range of PWM duty cycles that can be used to drive an LED.
Figure 82 displays a functional block diagram of the peripherals used in this example.
PWM Output
PWM Generation
I/O
GND
Required peripherals
This application requires one timer, one device pin, and an onboard LED.
Table 52.
Sub-block Functionality Peripheral Use Notes
Compatible devices
Based on the requirements in Table 52, this example is compatible with the devices in Table 53. The corresponding EVM
may be used for prototyping.
Table 53.
Compatible Devices EVM
MSPM0Lxxx LP-MSPM0L1306
MSPM0Gxxx LP-MSPM0G3507
Design steps
1. Determine the required PWM output frequency and resolution. These two parameters will be the starting point
when calculating other design parameters; the frequency should be determined by how quickly the status of an
external component needs to be updated. In this example, we chose a PWM output frequency of 62 Hz and a PWM
resolution of 2000 bits.
2. Calculate the timer clock frequency. The following equation can be used to calculate the timer clock frequency: Fclock
= Fpwm × resolution
3. Configure peripherals in SysConfig. Select which timer instances are used and which device pins are used for PWM
output. This example uses PA13 for the PWM output (which is connected to TIMG0).
4. Write application code. The remaining piece of this application is to change the PWM duty cycle, which is
accomplished in software. See Figure 83 for an overview of the application or browse the code directly.
Design considerations
1. Maximum output frequency: Fundamentally, the max PWM output frequency is limited by both the IO speed and
selected clock source frequency. However, the duty cycle resolution also affects the max output frequency. More
resolution requires more timer counts which increases the output period.
2. Pipelining: The PWM timer selected in this application supports pipelining the timer compare value. Pipelining allows
the application to schedule an update to the timer compare value without causing a glitch on the output.
Software flowchart
Figure 83 shows the operations performed by application to change the duty cycle of the PWM output.
Main Program
Start Timer
Timer Gx ISR
Yes
Application code
In the application code, the PWM duty cycle is increased by 1% each time the timer triggers an interrupt until it reaches
90% and then decreased by 1% until the duty cycle reaches 10%, which generates a heartbeat effect. This application
PWM output has 2000 bits of resolution; therefore, increasing or decreasing the pwm_count variable by 20 changes the
duty cycle by 1%. Depending on an application requirements, different scaling can be required.
void PWM_0_INST_IRQHandler(void){
switch (DL_TimerG_getPendingInterrupt(PWM_0_INST)){
case DL_TIMER_IIDX_LOAD:
if (dc <= 10){mode = 1;} // if reached lowest dc (10%), increase dc
else if (dc >= 90){mode = 0;} // if reached highest dc (90%), decrease dc
if (mode){pwm_count -= 20; dc += 1;} // up
if (!mode){pwm_count += 20; dc -= 1;} // down
DL_TimerG_setCaptureCompareValue(PWM_0_INST, pwm_count, DL_TIMER_CC_1_INDEX); // update ccr1
value
break;
default:
break;
}
}
Results
Additional Resources
• Download the MSPM0 SDK
• Learn more about SysConfig
• MSPM0L LaunchPad development kit
• MSPM0G LaunchPad development kit
• MSPM0 Timer PWM academy
Power Sequencer
Description
The power sequencing example demonstrates turning on multiple rails from one start-up, at different intervals. This
precaution helps prevent damaging devices during start-up that causes power spikes, bus contention, latch-up errors,
and other issues. The MSPM0 allows for use of only one timer to set different intervals for each rail.
Required Peripherals
This application requires the one timer, four output pins, and one input pin. The number of output pins can vary
depending on application needs.
Table 54. Required Peripherals
Subblock Function Peripheral Use Notes
Interrupt trigger 1 pin Signal input for trigger
Output signals 4 pins Output signals for sequencing
Creating sequence 1 Timer G Called TIME_SEQUENCE in code
Compatible Devices
Based on the requirements in Table 54, this example is compatible with the devices in Table 55. The corresponding EVM
can be used for prototyping.
Table 55. Compatible Devices
Compatible Devices EVM
MSPM0Cxxx LP-MSPM0C1104
MSPM0Lxxx LP-MSPM0L1306
MSPM0Gxxx LP-MSPM0G3507
Design Steps
1. Determine the desired time interval between each rail at start-up. The time interval set is counted sequentially from
rail to rail not the start point. See Design Considerations for instructions on calculating the intervals.
2. Determine the desired time interval between each rail at shutdown. The time interval set is counted sequentially from
rail to rail not the start point. Alternatively, outputs can be shutdown all at once.
3. Configure peripherals in SysConfig. Select a timer, configure with a chosen frequency, and set interrupt as a zero
event. Set the input interrupt as rising and falling edge. Choose pins for input voltage and output rails.
4. Modify the desired time intervals in the application code. Time intervals are place at the top of the .c file.
Design Considerations
1. Multiple rails: The number of rails for this application can be increased or decreased. Only minor edits are needed to
implement the number of rails.
a. The array size for the interval time sequences needs to match the number of rails chosen. The two arrays
referenced are gTimerUp[] and gTimerDown[].
b. If rails are added or subtracted edits need to be made to the pinToggle function for each GPIO output.
2. Sequence order: The application as written has a specific order for the sequence. To change the order of rails
triggered, change the # in GPIO_OUT_PIN_#_PIN that is found in the pinToggle function to the desired order in the
if statement.
3. Clock settings: Maximum interval resolution is dependent on the frequency of the timer. Timer clock settings need
to be adjusted depending on system clock settings. There is a direct relationship between how fast to clock the
timer and the resolution of time between rails. The faster you clock the timer, the more resolution there is between;
however, as the input clock frequency increases, the total possible time between rails is decreased.
4. Calculating intervals: SysConfig gives period range and resolution based on the set frequency for the MSPM0
family. In the example code, the resolution is 7.81ms while the clock frequency is set to 128Hz. The period for the
desired intervals can be calculated by dividing the desired time by the resolution.
5. Port settings: Some devices of the MSPM0 family offer multiple ports. If more than one port is in use, the GPIO code
portions of the application have to be modified to use multiple ports.
6. Connection of external devices to output pins: External devices can be controlled in this type of application in
different ways. Three common methods are explored in the following list:
a. Enable Pin: No additional actions are needed for the output.
b. Direct Power: If an external device is being powered by the output, modifications need to be made as well as
considerations about the output current limitation located in the device data sheet.
c. External Power Circuitry: If external circuitry is needed for powering of another device, for example an external
GPAMP, then the output is like the enable pin situation in 6.a. External circuitry varies on each system, and is
beyond the scope of this document.
Design Results
Figure 86 shows the logic graph results from executing the code example. The end is assuming the pins are also turned
off in sequence.
E2E
See TI's E2E™ support forums to view discussions and post new threads to get technical support for utilizing MSPM0
devices in designs.
PWM DAC
Description
The PWM DAC subsystem example demonstrates how to use an MSPM0 timer, and a simple RC filter to create a
PWM DAC. The example software creates a 10-bit DAC with a PWM frequency of 31250Hz. The duty cycle of the PWM
signal updates continuously to create a sinusoidal waveform at the filter output. While the MSPM0Gx50x devices include
a 12-bit DAC, and the internal comparators include 8-bit reference DACs that can buffered through the OPA, a PWM
DAC allows for the generation of analog output voltages on devices lacking these peripherals, or just for additional DAC
outputs whenever needed. Figure 87 shows the block diagram for a single PWM DAC.
MSPM0Gx/MSPM0Lx
Extra Stages
(Optional)
R1 Rn
TIMx
GND GND
Required Peripherals
This application requires the PWM peripheral and a TIMGx instance with shadow capture compare registers.
Table 56. Required Peripherals
Subblock Functionality Peripheral Use Notes
PWM TIMGx Example uses TIMG4 shadow registers to avoid signal glitching
Compatible Devices
Based on the requirements in Table 56, the compatible devices are listed in Table 57. The corresponding EVM can be
used for quick evaluation.
Table 57. Compatible Devices
Compatible Devices EVM
MSPM0Lx LP-MSPM0L1306
MSPM0Gx LP-MSPM0G3507
Design Steps
1. PWM frequency: The PWM frequency is related to the DAC resolution by:
f
2N = fCLOCK (15)
PWM
where
This subsystem example uses either a 32MHz clock frequency or a 16MHz clock frequency to create a 10-bit DAC.
Table 58 details some example PWM DAC resolutions based on clock and PWM frequencies.
2. PWM configuration: This application configures the Timer for edge-aligned PWM, and sets the capture compare
updated value to take effect after the zero event.
3. Synchronization of duty cycle update: Shadow registers are used to prevent missed counter compare value
updates. This is done in MSPM0 by enabling the shadow load functionality of an appropriate timer instance. This
allows the duty cycle to be updated while the timer is running, without worry of a glitch in duty cycle output.
4. PWM Interrupt configuration: Here the timers are configured in down-count mode, so the interrupt is configured
to occur a capture or compare down event. If updating the duty cycle on the very next cycle is desired, using the
capture compare down or up interrupt helps make sure the captured value can be updated before the next load
event or zero event. Any other system interrupt can be used as well, and needs to be synchronized by the enabling
of the shadow load capability.
5. Sample array: When outputting a signal or waveform greater than the number of samples results in a higher
resolution output. The samples values need to be formatted to align with the resolution of the PWM DAC.
6. Filter design: A basic RC filter is usually enough to filter the PWM output. The filter cutoff frequency needs to be at
least an order of magnitude below the PWM frequency.
If better filtering of the PWM edges is desired, a higher order or more complex filter can be employed.
Table 58. PWM DAC Resolutions
fCLOCK fPWM N
32MHz 125kHz 8
32MHz 31.3kHz 10
Figure 88 shows the software flow chart for this subsystem example and shows the software flow of the ISR used to
create the PWM DAC in this example.
Application Code
This application makes use of the TI System Configuration tool (SysConfig) graphical interface to generate the
configuration code for the device peripherals. Using a graphical interface to configure the device peripherals streamlines
the application prototyping process.
This example application code uses an array of 128 samples to continuously change the duty cycle of a single PWM
output. This creates a sinusoidal wave after filtering. The duty cycle is changed through timer interrupt and shadow
registers. The interrupt is generated on a counter compare down event. During this interrupt, the next counter compare
value in the array index is set and ready to be loaded on the next TIMCLK cycle after the timer reaches zero. This helps
prevent the application from missing any PWM duty cycle changes, which can cause glitches in the final output.
void PWM_0_INST_IRQHandler(void){
switch (DL_TimerG_getPendingInterrupt(PWM_0_INST)){
case DL_TIMERG_IIDX_CC0_DN: /* Interrupt on CC0 Down Event */
/*Set new Duty Cycle based on sine array sample value */
DL_TimerG_setCaptureCompareValue(PWM_0_INST, gSine128[gSineCounter%128],
DL_TIMER_CC_0_INDEX);
break;
default:
break;
}
}
Results
Figure 89 displays the PWM digital output compared to the filter output when using a 32MHz clock frequency. The top
half shows a zoomed view of half the final sine wave period to clearly display the duty cycle changes in the PWM signal.
The bottom half shows a more zoomed out view to clearly display the final sine wave output.
Additional Resources
E2E
See TI's E2E™ support forums to view discussions and post new threads to get technical support for utilizing MSPM0
devices in designs.
Mailing Address: Texas Instruments, Post Office Box 655303, Dallas, Texas 75265
Copyright © 2025, Texas Instruments Incorporated