Skip to article frontmatterSkip to article content

C2000 and serial driver

C2000 is a very popular MCU family, especially in power electronics projects - unfortunately there is no API provided by Texas Instruments for the debug probes, so MCUViewer has a slightly different approach for reading data out of C2000 MCUs.

Serial driver setup

Instead of using only the debug probe, the MCUViewer needs an additional UART/RS485 connection to your PC. You can do it either by a dedicated UART to USB converter or use the debug probe’s serial port. The general approach is described in Serial Setup section, however here we will go through a simple C2000 example.

Hardware setup

In this tutorial we will be using a launchpad LAUNCHXL-F2800137 from Texas Instruments. To simplify the setup we will not be using an additional USB<>UART dongle, only the built-in one.

Software

We will be using Code Composer Studio and MCUViewer. You might also need a terminal program, such as RealTerm, to test the UART communication.

Project setup

First we have to create a new project based on the launchpad board that we’ll be using. We can select “lab_sci_launchpad” as the starting example.

Let’s change to FLASH build configuration to make our executable persistent:

Code Composer Studio project with the FLASH build configuration selected

And we can move on to the sysconfig setup - there we need to fill all the required fields as shown in the picture below (note that we need the RX interrupt):

SysConfig SCI (UART) settings for the C2000 serial driver with the RX interrupt enabled

Next, we need to replace the “lab_main.c” file with the following content:

#include <stdint.h>
#include "board.h"
#include "device.h"
#include "driverlib.h"

__interrupt void INT_mySCIA_RX_ISR(void);

void main(void)
{
    Device_init();
    Interrupt_initModule();
    Interrupt_initVectorTable();

    Device_initGPIO();

    Board_init();

    EINT;
    ERTM;

    for (;;)
    {
    }
}

__interrupt void INT_mySCIA_RX_ISR(void)
{
    /* read from FIFO */
    char rxChar = SCI_readCharBlockingFIFO(mySCIA_BASE);
    /* clear interrupt flags */
    SCI_clearInterruptStatus(mySCIA_BASE, SCI_INT_RXFF);
    Interrupt_clearACKGroup(INT_mySCIA_RX_INTERRUPT_ACK_GROUP);
    /* send back the character */
    SCI_writeCharArray(mySCIA_BASE, &rxChar, 1);
}

This code is essentially a loopback test - you can also include an initial message to test the transmission alone. In our case it was tested with RealTerm:

RealTerm terminal showing the C2000 UART loopback test

Great - now we are sure the UART communication works and we can move on to adding the serial driver.

Adding Serial driver

Adding the serial driver is pretty straightforward - simply copy the serialDriver folder to the project and adjust the serialDriverDefines.h file to match your system configuration. The serialDriver folder can be downloaded together with MCUViewer executable here.

In our case it is essential to remember about the ____SERIAL_DRIVER_C2000_SUPPORT macro.

MCUViewer serial driver folder added to the C2000 project in Code Composer Studio

Now let’s modify our code, by implementing the serial driver send and receive functionalities (plus some test sine wave):

#include <stdint.h>
#include "board.h"
#include "device.h"
#include "driverlib.h"
#include "SerialDriver/serialDriver.h"
#include <math.h>

__interrupt void INT_mySCIA_RX_ISR(void);

float test_x = 0.0f;
float test_sin = 0.0f;

void main(void)
{
    Device_init();
    Interrupt_initModule();
    Interrupt_initVectorTable();

    Device_initGPIO();

    Board_init();

    EINT;
    ERTM;

    for (;;)
    {
        test_sin = sinf(test_x);
        test_x += 0.00001f; 

        if(test_x > 6.28f)
            test_x = 0.0f;
    }
}

/* serial driver send data function - called whenever there's data to send */
void serialDriverSendData(uint16_t* buf, uint16_t size)
{  
    SCI_writeCharArray(mySCIA_BASE, buf, size);
}

__interrupt void INT_mySCIA_RX_ISR(void)
{
    /* read from FIFO */
    char rxChar = SCI_readCharBlockingFIFO(mySCIA_BASE);
    /* clear interrupt flags */
    SCI_clearInterruptStatus(mySCIA_BASE, SCI_INT_RXFF);
    Interrupt_clearACKGroup(INT_mySCIA_RX_INTERRUPT_ACK_GROUP);
    /* call serial driver function */
    serialDriverReceiveByte(rxChar);
}

Great - now we’re ready to test the solution.

First Acquisition

Before we can import the variables into MCUViewer it is essential to switch to *.elf file as output of our linking process. Right click on the project and select properties. There navigate to the linker tab and change the output to *.elf.

Code Composer Studio project properties with the output format switched to *.elf

Now, after compilation the *.elf file should be generated - in our case in CPU1_FLASH/ directory.

Let’s launch MCUViewer, select Options->Acquisition and select the *.elf file. Next, switch the debug probe to SERIAL, and fill in the port and baudrate values. Make sure the port is free (not used by other software such as UART terminal), and click Detect serial button. You should see the serial driver version and max variables in the status text box as in the picture below:

MCUViewer Acquisition Settings with the SERIAL probe, port and baud rate for the C2000 target

This means the serial driver is working and now it’s ready to visualize our data. Select ‘Import Variables’ and pick ‘test_x’ and ‘test_sin’ variables.

Importing the test_x and test_sin variables from the C2000 *.elf file

Drag and drop them on the plot and select the start button. You should see the values being plotted as in the picture below:

Live plot of the C2000 variables test_x and test_sin over the serial driver

In case of any issues please visit the Variable Viewer page and visit the FAQ section.

Adding Recorder

The recorder module is useful when the UART communication (or generally the communication with the target MCU) is slow and you want to visualize high frequency data. Let’s add a 10kHz interrupt to our code so that we can see the issue:

SysConfig CPU timer configured for a 10 kHz interrupt
#include <math.h>
#include <stdint.h>

#include "SerialDriver/serialDriver.h"
#include "board.h"
#include "device.h"
#include "driverlib.h"

__interrupt void INT_mySCIA_RX_ISR(void);
__interrupt void INT_TIMER_10kHz_ISR(void);

float test_x = 0.0f;
float test_sin = 0.0f;
int32_t test_counter = 0;

void main(void)
{
    Device_init();
    Interrupt_initModule();
    Interrupt_initVectorTable();

    Device_initGPIO();

    Board_init();

    EINT;
    ERTM;

    CPUTimer_startTimer(TIMER_10kHz_BASE);

    for (;;)
    {
        test_sin = sinf(test_x);
        test_x += 0.00001f;

        if (test_x > 6.28f)
            test_x = 0.0f;
    }
}

void INT_TIMER_10kHz_ISR(void)
{
    test_counter++;

    if (test_counter > 100)
        test_counter = 0;

    Interrupt_clearACKGroup(INT_TIMER_10kHz_INTERRUPT_ACK_GROUP);
}

/* serial driver send data function - called whenever there's data to send */
void serialDriverSendData(uint16_t* buf, uint16_t size)
{
    SCI_writeCharArray(mySCIA_BASE, buf, size);
}

__interrupt void INT_mySCIA_RX_ISR(void)
{
    /* read from FIFO */
    char rxChar = SCI_readCharBlockingFIFO(mySCIA_BASE);
    /* clear interrupt flags */
    SCI_clearInterruptStatus(mySCIA_BASE, SCI_INT_RXFF);
    Interrupt_clearACKGroup(INT_mySCIA_RX_INTERRUPT_ACK_GROUP);
    /* call serial driver function */
    serialDriverReceiveByte(rxChar);
}

Now when we try to visualize the test_counter in VarViewer we can clearly see the issue:

Variable Viewer plot of the fast test_counter signal showing aliasing

The counter signal is too fast and aliasing occurs - we can no longer trust this output. Now’s the time to try visualizing it using the recorder module.

The integration is described in the Recorder section - basically we need to copy the Recorder directory to the project directory (similar to the serial driver) and modify the recorderDefines.h file to match our system configuration.

Recorder directory copied into the C2000 project

The recorder works by sampling selected variables at regular intervals - usually from within a high frequency interrupt - out 10kHz interrupt fits this requirements perfectly. Let’s add the #include "Recorder/recorder.h" include and recorderStep() call inside the interrupt like so:

void INT_TIMER_10kHz_ISR(void)
{
    test_counter++;

    if (test_counter > 100)
        test_counter = 0;

    Interrupt_clearACKGroup(INT_TIMER_10kHz_INTERRUPT_ACK_GROUP);

    recorderStep();         // <---- recorder call
}

Now when we have to compile and flash the project to our target MCU. The next step is to check if recorder works correctly - go to Options->Acquisition and check Enable recorder checkbox. Click Detect recorder button. If the recorder is detected correctly there should be no errors, just a Recorder detected! information with all fields filled in as in the recorderDefines.h file.

Acquisition Settings showing the recorder detected on the C2000 target

Now create a new group of recorder type and drag and drop the test_counter variable into it.

Creating a recorder group and dragging test_counter onto it

After clicking the Start button the plot should look like in the picture below, showing us all the signal details with no aliasing:

Recorder plot of test_counter showing every signal detail without aliasing

In case of any issues please visit the Recorder page and visit the FAQ section.