Introduction
Good old printf
is one of the first ways you learn to debug an application.
This page will show why printf
is not the best API to use to debug on
embedded devices.
Instead we'll explore the following APIs that are also available in the
SimpleLink™ SDKs:
Display_printf
System_printf
UART_PRINT
UARTprintf
We'll look at all the above APIs along with printf
and compare them based on
footprint size, stack implications, and calling context. We'll
focus on outputting the debug information via UART and the IDE's console.
For the hands-on tasks, we'll move between the different APIs. During this portion, the focus will be for the TI CCS compiler, but the concepts are applicable for IAR and other compilers.
The goals of this lab is to show options that are available. It's up to you to decide which API works best for you.
What's not covered...
This module is focused on printf-like APIs and does not cover log (or trace) capabilities in the SDK.
Prerequisites
Software
- CCS (version specified in the SimpleLink SDK's Release Notes)
- Any SimpleLink SDK
- Any terminal program with the following settings (if you are using Windows, use Device Manager to determine the port)
Baud-rate: 115200
Data bits: 8
Stop bits: 1
Parity: None
Flow Control: None
Hardware
- Any supported SimpleLink LaunchPad™ Development Kit
API Descriptions
This section gives an overview of each API and any related nuances.
printf
printf
has been part of the 'C' language from the beginning. It is a
well-known API that has been used by probably every 'C' programmer. printf
works on TI-RTOS, FreeRTOS and no-RTOS applications.
printf
is not ideal
for embedded development though. Let's see how printf
works to see the reason
why.
IDE Console Output
By default in CCS (and similarily in IAR), printf
output goes to a CIO
('C' Input/Output)
buffer. When the CIO buffer is full or a EOL character (i.e. '\n') is written,
a breakpoint is hit and CCS reads the contents of the buffer. CCS then resumes
the target. As one can guess, this action can have very bad implications for
real-time performance.
Non IDE Console Output
printf
output can be redirected. For the below comparisons, we've redirected
it to a UART via the add_device
, freopen
and setvbuf
APIs.
IAR uses a different mechanism for redirecting printf
. Please refer to IAR
documentation.
Calling Restrictions
IDE Console
printf
is callable from any location (e.g. main
, ISR, task, etc) when
outputting to the IDE console with one exception. By default, TI-RTOS plugs
in a locking mechanism (GateMutex
) into the TI Compiler to guarantee
thread-safety. The TI compiler's implementation of printf
calls this
locking mechanism. Therefore, by default, printf
cannot be called in a
Hwi or Swi in applications that use TI-RTOS and the TI Compiler. If you
have asserts enabled in the TI-RTOS kernel, you'll get an assert failure.
If you don't have asserts enabled, you might get nondeterministic results.
The lock can be disabled by setting the setting the BIOS.rtsGateType
variable in the kernel configuration file (i.e. .cfg file) to
BIOS.NoLocking
.
UART
When outputting to the UART, the allowable calling context depends on
the type of UART API call you are making. For the examples in the labs below,
printf
can be called from a task on an RTOS-based application or from main
(or function called within main
's context) in no-RTOS based applications.
Misc.
The TI Compiler offers a couple of different options with printf
. We'll compare
the "minimal" printf along with the "full" printf.
Additional Information
Display_printf
The Display_printf
allows a user to send output to various locations: LCD,
IDE console, or UART. We'll focus on the IDE console and UART.
Display_printf
's format options are very similar to printf
. Display_printf
is available on both RTOS and no-RTOS (bare-metal) based applications.
Many of the SDK examples use Display to output data via the UART to
allow isolation from an OS and IDE.
The parameters specified in the Display_open
API and Display_config
variable dictates what physical mechanism is used.
IDE Console (Host)
Display_printf
can be directed to the IDE's console also by using
DisplayHost. One of the below labs will show how to move to DisplayHost.
The line and
column parameters in Display_printf
are ignored when using the HOST.
#include <ti/display/Display.h>
Display_Handle display;
display = Display_open(Display_Type_HOST, NULL);
...
Display_printf(display, 0, 0, "Count = %d and it's address is 0x%x\n", count, &count);
UART
There are two types of UART interfaces:
- DisplayUartAnsi: Feature rich module (e.g. better cursor management) at the expense of footprint.
- DisplayUartMin: Simple UART management with less features, but smaller.
The default for the majority of the examples is DisplayUartMin. The line and
column parameters in Display_printf
are ignored when using the UART.
#include <ti/display/Display.h>
Display_Handle display;
display = Display_open(Display_Type_UART, NULL);
...
Display_printf(display, 0, 0, "Count = %d and it's address is 0x%x\n", count, &count);
Calling Restrictions
IDE Console
DisplayHost has no calling restriction (as long as the potential real-time impact incurred by writting to CIO buffer is acceptable).
UART
If you are using an RTOS, DisplayUart can only be called from a task
(or pthread).
If you are not using an RTOS, DisplayUart can be called from main
(or by
any function called within main
's context).
Additional Information
More details about the Display module can be found in the "SimpleLink MCU SDK User's Guide" and "TI Drivers Runtime APIs (doxygen)"
System_printf
System_printf
is only available if the application is using TI-RTOS.
System_printf
supports the same format strings as printf
and some
additional ones.
The System module allows an application to plug in a different System "Proxy". The proxy is responsible for the underlying implementation. The two main Support proxies are
- SysMin
- SysCallback
The proxy can be assigned in the kernel configuration file (e.g. .cfg file). Here is an example of the code to assign the SysMin as the Support proxy.
System.SupportProxy = SysMin;
The top-level System_printf
is the same no matter what the underlying
proxy is. Here's an example of the API.
#include <xdc/runtime/System.h>
System_printf("Count = %d and it's address is 0x%x\n", count, &count);
Let's look at the two proxies in more details.
SysMin (ROV/IDE Console Output)
The SysMin module stores the ASCII data into an internal buffer. The size of the
internal buffer is configurable. The data is not flushed to the CIO buffer
until instructed to do so (e.g. call System_flush
or configured to flush on
an application exit
). So SysMin does not impact real-time performance like
Display_printf
(out the IDE console) or printf
.
The buffer can be viewed by looking at
the ROV → SysMin → OutputBuffer. For example here are the contents
of SysMin's internal buffer with the Hello example after the System_printf
is called.
The "debug" kernel project uses the SysMin module.
Please refer to the SimpleLink SDK User's Guide for details about the kernel projects.
SysCallback (UART Output)
The SysCallback module allows a user to plug in a putc-like function (putchFxn)
that is called within System_printf
after the formatting is done. The default
putchFxn function does nothing. A common use-case is to plug in a putchFxn
function that writes the character to a UART. One of the below labs shows this.
The "release" kernel project uses the SysCallback module.
The functions plugged into SysCallback in the release.cfg file are empty to minimize code footprint. Please refer to the SimpleLink SDK User's Guide for details about the kernel projects.
Calling Restrictions
IDE Console
When using SysMin, there are no calling restrictions.
UART
When using SysCallback and using the UART, the calling restrictions
are dependent on the underlying code.
For the examples in the labs, System_printf
can be called
from a task (or pthread) on a TI-RTOS based application.
Additional Information
Additional information about System, SysMin and SysCallback can be found in the
<SimpleLink_SDK_Install_Dir>\Docs\Documentation_Overview.html
and click "TI-RTOS Kernel Runtime APIs and Configuration (cdoc)".
UART_PRINT
UART_PRINT
is only available in the SimpleLink CC32xx SDK. It is included for
legacy reasons. Only output via the UART is supported with this API.
UART_PRINT
supports similar format strings that printf
supports.
The code is maintained in the uart_term.c
and uart_term.h
files that are included with some of the SimpleLink CC322x SDK examples.
#include "uart_term.h"
InitTerm();
UART_PRINT("Count = %d and it's address is 0x%x\n", count, &count);
Calling Restrictions
IDE Console
N/A
UART
There are no calling restrictions. The code uses the UART_writePolling
API,
so the API can be called from any calling context after the board has been
initialized.
Additional Information
Please refer to the source code in the uart_term.c
and uart_term.h
files
in the SDK examples that use it.
UARTprintf
UARTprintf
is only available for no-RTOS examples in the SimpleLink MSP432E4
SDK. It is included for legacy reasons. Only output via the UART is supported
with this API.
UARTprintf
supports similar format strings that printf
supports.
The code is maintained in the uartstdio.c
and uartstdio.h
files that are included with some of the SimpleLink MSP432E4 SDK examples.
#include "uartstdio.h"
UARTprintf("Count = %d and it's address is 0x%x\n", count, &count);
Calling Restrictions
IDE Console
N/A
UART
This API can be called from any calling context, but has only been tested on no-RTOS examples for the MSP432E4 device.
Additional Information
Please refer to the source code in the uartstdio.c
and uartstdio.h
files
in the SDK examples that use the API.
Footprint Comparisons
The following is a comparison of the different debug APIs when displaying to the IDE console and UART. All the test were done with
- TI Compiler v17.6.0.STS
- SimpleLink SDK v1.50.00.xx
- The CCS Debug Build Configuration was used as the baseline
- The GPIO Interrupt example was used as the base
The below tables show the additional memory impact of adding one debug call.
So for the IDE Console Output (for CC3220SF w/ TI-RTOS),
adding the first printf
adds 10025 bytes of flash
and 936 bytes of RAM. Alternatively, adding one Display_printf
instead
adds 6679 bytes of flash and 1188 bytes of RAM.
Of course the majority of the memory impact is due to bringing in the
basic functionality. The overhead for additional debug calls would be
relatively minimal.
The test string was one of the following:
printf("This is a debug string %d, 0x%x, %f\n", 1, &someVariable, 3.14);
Display_printf(display, 0, 0, "This is a debug string %d, 0x%x, %f\n", 1, &someVariable, 3.14);
System_printf("This is a debug string %d, 0x%x, %f\n", 1, &someVariable, 3.14);
UART_PRINT("This is a debug string %d, 0x%x, %f\n", 1, &someVariable, 3.14);
Please note: TI-RTOS has mechanisms to determine the stack usage for an API call so we've included the stack usage column for TI-RTOS tables.
Note: for the printf (min) test case, the format string used %d instead of %f.
Please note the numbers can vary based on what is already in the application, compiler , compiler options, and device.
IDE Console Output
Here is the summary of the changes made to the GPIO Interrupt example:
- Added debug API along with the necessary initialization calls/changes.
- SysMin was used as the System Proxy and its internal buffer was 256 bytes
for testing
System_printf
. - MAXPRINTLEN was set to 256 to reduce the size of the Display buffer (
displayBuf
).
GPIO Interrupt on CC3220SF_LAUNCHXL with TI-RTOS
API | Flash Memory | RAM Memory | Stack Usage |
---|---|---|---|
Display_printf | 6679 | 1188 | 344 |
printf | 10025 | 936 | 936 |
printf (min) | 4064 | 932 | 400 |
System_printf | 3580 | 564 | 240 |
GPIO Interrupt on CC3220SF_LAUNCHXL with FreeRTOS
API | Flash Memory | RAM Memory |
---|---|---|
Display_printf | 6198 | 1184 |
printf | 10044 | 924 |
printf (min) | 4056 | 920 |
GPIO Interrupt on CC3220SF_LAUNCHXL with no-RTOS
API | Flash Memory | RAM Memory |
---|---|---|
Display_printf | 6532 | 1198 |
printf | 10962 | 938 |
printf (min) | 4974 | 934 |
UART Output
Using the same test debug API, these tables show the impact when moving the data out of a UART. We are going to omit the FreeRTOS and no-RTOS tables since they are basically the same as the TI-RTOS ones (as shown in the Console numbers above).
We've also included CC2640R2_LAUNCHXL for additional comparisons.
We are not including UARTprintf since it has only been tested for MSP432E4 no-RTOS applications.
Here is the summary of the changes made to the GPIO Interrupt example
- Added debug APIs along with the necessary initialization calls/changes.
THREADSTACKSIZE
inmain_tirtos.c
was increased to 2048 since a couple of the APIs required more stack space. All the examples had the change made (including the base example with no debug API calls) to keep the comparison meaningful.- SysCallback was used but different
SysCallback.putchFxn
andSysCallback.readyFxn
functions were used. Please refer to the below labs for more details.
GPIO Interrupt on CC3220SF_LAUNCHXL with TI-RTOS
API | Flash Memory | RAM Memory | Stack Usage |
---|---|---|---|
Display_printf | 9293 | 629 | 496 |
printf | 18541 | 1317 | 1320 |
printf (min) | 13636 | 1313 | 572 |
System_printf | 9284 | 385 | 472 |
UART_PRINT | 12897 | 365 | 1348 |
GPIO Interrupt on CC2640R2_LAUNCHXL with TI-RTOS
API | Flash Memory | RAM Memory | Stack Usage |
---|---|---|---|
Display_printf | 7176 | 479 | 420 |
printf | 16360 | 1291 | 1328 |
printf (min) | 11492 | 1287 | 468 |
System_printf | 7148 | 359 | 368 |
API Summary
API | RTOS Dependency | Summary |
---|---|---|
printf |
None | Easy to use at the expensive of impacting real-time performance when using the default setup |
printf over UART |
None | Easy to use with possible calling restrictions |
Display_printf |
None | Additional setup but is flexible and OS/IDE independent |
System_printf |
TI-RTOS | Flexible and potentially the smallest impact on the application |
UART_PRINT |
None | Available on CC3220 devices only |
UARTprintf |
No-RTOS | Available on MSP432E4 when using no-RTOS |
Receiving on the UART
When using the UART, the Display_printf
API sets up the UART instance to be
transmit only (unless you want to modify the code in the SDK).
The other debug APIs allow the UART instance to be set up for transmit
and receive.
Be careful using the different debug APIs together
While using the UART, care must be taken to make sure there is only
one call to UART_open
and the handle is shared. A common issue is that
an application calls Display_open
(over a UART) and uses UART_PRINT
.
If both modules are trying to use the same UART, the second one to call
UART_open
will fail.
Labs: Getting started
We have several tasks below that show the different debug APIs and how to add them into your application. Please refer to the prerequisites listed at the beginning of this page.
Task 1: Showing printf Downside
For this task, we are going to import the GPIO Interrupt example.
We are going to change the buttons to call printf
and
System_printf
instead of toggling LEDs. The goal is to show
that printf
has an impact on real-time.
Please import the GPIO Interrupt example with TI-RTOS and the CCS compiler for your desired LaunchPad.
Remove the
GPIO_toggle
calls from the first GPIO callback function and replace it with the followingprintf
in thegpiointerrupt.c
file. Please add the include also to get rid of any compiler warnings.#include <stdio.h> void gpioButtonFxn0(uint_least8_t index) { printf("Hello world via printf\n"); }
gpiointerrupt.c
...wait a minute
Question: I thought you said that
printf
could not be called in the context of an interrupt for TI-RTOS based applications that are using the TI Compiler. Why does this work?!Answer: The release kernel configuration does not have asserts enabled and with such a simple application, it's not a problem. Doing this on a real application is not recommended!
Remove the
GPIO_toggle
calls from the second GPIO callback function and replace the with the followingSystem_printf
. Please add the include also to get rid of any compiler warnings/errors.#include <xdc/runtime/System.h> void gpioButtonFxn1(uint_least8_t index) { System_printf("Hello world via System_printf\n"); }
gpiointerrupt.c
Add the following code at the end (but before the
return
) ofmainThread
to toggle the LED. Note: this will probably give you an unreachable compiler warning. Please ignore the warning.while (1) { GPIO_toggle(Board_GPIO_LED0); }
gpiointerrupt.c :: mainThread() – right before the
return
Build, load and run the application. Note: the brightness of LED0.
Press Button0. What happens? (more than one answer)
printf real-time impact
The moral of the story here is that while
printf
is easy to use, it can have negative impact on your real-time performance.Press Button1. What happens? (one answer)
Task 2: Getting System_printf
to work
In the previous task we added printf
and saw the negative real-time impact it
can have. We also added System_printf
but it seemed to do nothing...what's up
with that?!
Remember that most of the examples have SysCallback as the default System proxy and the default functions do nothing.
var SysCallback = xdc.useModule('xdc.runtime.SysCallback');
System.SupportProxy = SysCallback;
//SysCallback.abortFxn = "&myUserAbort";
//SysCallback.exitFxn = "&myUserExit";
//SysCallback.flushFxn = "&myUserFlush";
//SysCallback.putchFxn = "&myUserPutch";
//SysCallback.readyFxn = "&myUserReady";
release.cfg
On this task, we'll enable move to the SysMin System proxy. This can be accomplished in one of three ways to allow the output string to be placed in an internal buffer that can be viewed via ROV.
- Change the release.cfg file to use SysMin: Easiest way, but you should see
if this will have an impact on other projects using this kernel project.
For CC13xx/CC26xx Devices
To use SysMin, you cannot use the kernel in the ROM. The release kernel project uses the kernel in the ROM.
- Create new kernel project and use it: You could copy/paste the release kernel project, rename it, use SysMin and point GPIO Interrupt to the new kernel project. This would minimize impact on other projects.
- Move to the debug kernel project: This will also work since the
debug kernel project uses SysMin as well as many other debug features.
Footprint impact
The debug kernel project enables many debug features. This has a non-trivial impact on footprint. Additionally for CC13xx/CC26xx devices, the debug kernel configuration does not use the kernel in the ROM, thus causing an even larger footprint impact.
printf usage
The debug kernel project enables assert checking in the TI-RTOS kernel. So as noted earlier, if the
printf
in thegpioButtonFxn0
function executes, an assert occurs.
We are going to do the last one. Note for more details on the kernel projects, please refer to the SDK's User Guide and TI-RTOS Basic Lab
Import the debug kernel for your LaunchPad (Project → Import CCS Projects...) The kernel project is located in the
<SimpleLink_SDK_Install_Dir>\kernel\tirtos\builds\<Board>\debug
directory.Select Project Properties on the GPIO Interrupt project and navigate to Build → Dependencies. Select Add and select the debug kernel. Now Remove the release kernel. Once completed, your dependency list should look like this (the board name may be different).
Build, load and run the GPIO Interrupt project. Note: the debug kernel project will get built first.
Let's repeat the last step in the previous task...Press Button1. What happens (one answer)?
Let's make the
System_printf
output come out on the CCS console. We can simply add aSystem_flush
to the while loop inmainThread
:while (1) { GPIO_toggle(Board_GPIO_LED0); System_flush(); }
gpiointerrupt.c
Note that adding a call to
System_flush
ingpioButtonFxn1
would result in an error, as flushing is an expensive operation that should not be called in a hardware interrupt such as a GPIO callback.
Task 3: Display_printf
out the UART
For this task, we are going to import the drivers/empty example.
We are going to add in a Display_printf
into the empty task
that is toggling the LED.
Please import the driver's Empty example with TI-RTOS and the CCS compiler for your favorite LaunchPad.
Add the following Display lines at the beginning of the
mainThread
function inempty.c
.void *mainThread(void *arg0) { Display_Handle display; display = Display_open(Display_Type_UART, NULL);
empty.c
Add the following
Display_printf
line in the while loop.while (1) { sleep(time); GPIO_toggle(Board_GPIO_LED0); Display_printf(display, 0, 0, "Hello world via Display_printf"); }
empty.c
Then finally add the following header include
// #include <ti/drivers/Watchdog.h> #include <ti/display/Display.h>
empty.c
Build, load and run that application.
Connect your favorite terminal program to the UART port.
You should be seeing the debug string coming out every second.
Quiz
Where can
Display_printf
be placed when it's sending data out the UART and you are using an RTOS (more than one answer)?Where can
Display_printf
be placed when it's sending data out the UART and you are using an not RTOS (more than one answer)?
Task 4: Display_printf
out the IDE console
Let's take the example we did in Task 3 and have it use the IDE Console instead of the UART. The two areas to change will be
Display_open
callDisplay_config
settings in the Board.c file
Let's get started...
Modify the
Display_open
call to useDisplay_Type_HOST
.void *mainThread(void *arg0) { Display_Handle display; display = Display_open(Display_Type_HOST, NULL);
empty.c
Open the Board.c file (e.g. CC2640R2_LAUNCHXL.c) and locate the
Display_config
variable.For simplicity's sake, please comment out (or
#if
out) the entire section of code that configures the Display module. We'll add the configuration for using the console IDE instead. For example,/* * =============================== Display =============================== */ #if 0 #include <ti/display/Display.h> ... #else // we are going to add Display configuration // for console IDE (i.e. Host) here... #endif /* * =============================== GPIO =============================== */
CC2640R2_LAUNCHXL.c or whatever board file you are using
Add the following into the space we reserved in the previous step. This supplies the memory for the Host display object, supplies the internal buffer and sets up the function table for the Display module.
/* * =============================== Display =============================== */ #if 0 ... #else #include <ti/display/Display.h> #include <ti/display/DisplayHost.h> #define MAXPRINTLEN 256 static char displayBuf[MAXPRINTLEN]; DisplayHost_Object displayHostObject; const DisplayHost_HWAttrs displayHostHWAttrs = { .strBuf = displayBuf, .strBufLen = MAXPRINTLEN }; const Display_Config Display_config[] = { { .fxnTablePtr = &DisplayHost_fxnTable, .object = &displayHostObject, .hwAttrs = &displayHostHWAttrs } }; const uint_least8_t Display_count = sizeof(Display_config) / sizeof(Display_Config); #endif
CC2640R2_LAUNCHXL.c or whatever board file you are using
Build, load and run that application. The debug output should be coming out the CCS Console now.
Quiz
Where can
Display_printf
be placed when it's sending data out the IDE console?
Task 5: System_printf
output via UART
Let's have the System_printf
output go out the UART now. So building
upon Task 4, do the following steps.
First modify the release kernel configuration to change the proxy and send the characters out the UART. In the release kernel, change the System proxy to the following:
///var SysMin = xdc.useModule('xdc.runtime.SysMin'); ///SysMin.bufSize = 1024; ///System.SupportProxy = SysMin; var SysCallback = xdc.useModule('xdc.runtime.SysCallback'); System.SupportProxy = SysCallback; //SysCallback.abortFxn = "&myUserAbort"; //SysCallback.exitFxn = "&myUserExit"; //SysCallback.flushFxn = "&myUserFlush"; SysCallback.putchFxn = "&UARTUtils_systemPutch"; SysCallback.readyFxn = "&UARTUtils_systemReady";
release.cfg
Add the
UARTUtils.c
andUARTUtils.h
files to the GPIO Interrupt project. These files can be found at the bottom of this page.Add the header and UART initialization code into
mainThread
#include <xdc/runtime/System.h> #include "UARTUtils.h" void *mainThread(void *arg0) { Display_Handle display; display = Display_open(Display_Type_HOST, NULL); UART_init(); UARTUtils_systemInit(0);
empty.c
Add the
System_printf
call along side theDisplay_printf
while (1) { sleep(time); GPIO_toggle(Board_GPIO_LED0); Display_printf(display, 0, 0, "Hello world via Display_printf"); System_printf("Hello world via System_printf\r\n"); }
empty.c
Build, load and run that application.
Connect your terminal application to the desired port to see the debug strings.
Quiz
Can we have both
System_printf
andDisplay_printf
go out the same UART?
Please note that UARTUtils.c/UARTUtils.h is just one example implementation. It can be used as a template for other implementations. For example: Different Implementation
Task 6: printf
out a UART
Following from Task 5, let's add a printf
that sends data out the UART.
The UARTUtil.c
and UARTUtil.h
files that were added in Task 5 also contain
support for sending printf
output via the UART.
Add the initialization code to direct the
printf
output to the UART#include <xdc/runtime/System.h> #include "UARTUtils.h" void *mainThread(void *arg0) { Display_Handle display; display = Display_open(Display_Type_HOST, NULL); UART_init(); UARTUtils_systemInit(0); add_device("UART", _MSA, UARTUtils_deviceopen, UARTUtils_deviceclose, UARTUtils_deviceread, UARTUtils_devicewrite, UARTUtils_devicelseek, UARTUtils_deviceunlink, UARTUtils_devicerename); /* Open UART0 for writing to stdout and set buffer */ freopen("UART:0", "w", stdout); setvbuf(stdout, NULL, _IOLBF, 128);
empty.c
Add the
printf
call along side theDisplay_printf
while (1) { sleep(time); GPIO_toggle(Board_GPIO_LED0); Display_printf(display, 0, 0, "Hello world via Display_printf"); System_printf("Hello world via System_printf\r\n"); printf("Hello world via printf\r\n"); }
empty.c
Finally increase the stack size (1024 → 2048 of
mainThread
inmain_tirtos.c
) sinceprintf
out the UART needs a larger stack.#define THREADSTACKSIZE 2048
main_tirtos.c
- Build, load and run that application. You should see both the
System_printf
andprintf
output now.
- Build, load and run that application. You should see both the
Quiz
Why did the Display_printf output start going to the UART?!
How could be make the output look a little nicer?
Task 7: UART_PRINT
CC32xx users only
UART_PRINT is only included in the SimpleLink CC32xx SDK
Let's add a UART_PRINT
call into the driver/empty project for the CC3220SF-LAUNCHXL.
Please import the driver's Empty example with TI-RTOS and the CCS compiler for the CC3220SF_LAUNCHXL
Add the following header and initialization at the beginning of the
mainThread
function inempty.c
.#include "uart_term.h" void *mainThread(void *arg0) { InitTerm();
empty.c
Add the following
UART_PRINT
line in the while loop.while (1) { sleep(time); GPIO_toggle(Board_GPIO_LED0); UART_PRINT("Hello world via UART_PRINT\r\n"); }
empty.c
Steal the
uart_term.c
anduart_term.h
files from another CC3220 example (e.g.<simplelink_cc32xx_sdk_1_xx_yy_zz>\examples\rtos\CC3220SF_LAUNCHXL\demos\out_of_box
and add them into the empty project.Finally increase the stack size (1024 → 2048) of
mainThread
inmain_tirtos.c
sinceUART_PRINT
needs a larger stack.#define THREADSTACKSIZE 2048
main_tirtos.c
Build, load and run that application.
Connect your terminal application to the desired port to see the debug strings.
You should now see the debug output every second
Quiz
What would have happened if we had not increased the stack size for the task that called
UART_PRINT
?
Lab Files
/*
* Copyright (c) 2017, Texas Instruments Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* ======== UARTUtils.c ========
*/
/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/runtime/Assert.h>
#include <xdc/runtime/Diags.h>
#include <xdc/runtime/Log.h>
#include <xdc/runtime/System.h>
/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
/* TI-RTOS Header files */
#include <ti/drivers/UART.h>
/* Example/Board Header files */
#include "Board.h"
#include "UARTUtils.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#define NUM_PORTS 1
/* Typedefs */
typedef struct {
UART_Handle handle; /* Handle for all UART APIs */
unsigned int base; /* Base address of the UART */
unsigned int open; /* Number of users for this UART */
bool binary; /* UART has been opened in binary mode */
} UARTPorts;
/* Static variables and handles */
static UART_Handle systemHandle = NULL;
static UART_Handle loggerHandle = NULL;
/* This example only uses UART0 */
static UARTPorts ports[NUM_PORTS] = {{NULL, Board_UART0, 0, false}};
/*
* ======== openHandle ========
* The UART driver will return NULL if there was an error creating a UART
*
* @param index Index into the ports array of UARTPorts
* @param binary Open the UART in binary mode
* @return UART_Handle to the opened UART
*/
static UART_Handle openHandle(unsigned int index, bool binary)
{
UART_Params uartParams;
/* Only UART 0 is supported in this example. */
if (index >= NUM_PORTS) {
System_printf("UART index %d not supported, valid range is 0-%d", index, (NUM_PORTS - 1));
return (NULL);
}
/* The UART driver only allows creating once, return if its already open. */
if (ports[index].open) {
/* Make sure the index is not already opened in the wrong mode */
if (binary != ports[index].binary) {
return (NULL);
}
ports[index].open++;
return (ports[index].handle);
}
/* Create a UART with the parameters below. */
UART_Params_init(&uartParams);
if (binary == true) {
uartParams.readEcho = UART_ECHO_OFF;
uartParams.writeDataMode = UART_DATA_BINARY;
ports[index].binary = true;
}
else {
ports[index].binary = false;
}
ports[index].handle = UART_open(ports[index].base, &uartParams);
if (ports[index].handle != NULL) {
ports[index].open = 1;
}
return (ports[index].handle);
}
/*
* ======== closeHandle ========
*/
static void closeHandle(unsigned int index)
{
ports[index].open--;
if (ports[index].open == 0) {
UART_close(ports[index].handle);
}
}
/*
* ======== UARTUtils_loggerIdleInit ========
*/
void UARTUtils_loggerIdleInit(unsigned int index)
{
Assert_isTrue(ports[index].open == false, NULL);
loggerHandle = openHandle(index, true);
if (loggerHandle == NULL) {
System_printf("Failed to open UART %d", index);
}
}
/*
* ======== UARTUtils_loggerIdleSend ========
* Plugged into LoggerIdle to send log data during idle.
*/
Int UARTUtils_loggerIdleSend(UChar *a, Int size)
{
/* Make sure UART is initialized */
if (loggerHandle) {
/*
* Write up to 16 bytes at a time. This function runs during idle and
* should not tie up other idle functions from running. The idle loop
* is generally short enough that this function will run again before all
* 16 bytes have been transmitted from the FIFO.
*/
if (size < 16) {
return (UART_writePolling(loggerHandle, (void *)a, size));
}
else {
return (UART_writePolling(loggerHandle, (void *)a, 16));
}
}
else {
return (0);
}
}
/*
* ======== UARTUtils_deviceclose ========
*/
int UARTUtils_deviceclose(int fd)
{
/* Return if a UART other than UART 0 was specified. */
if (fd != 0) {
return (-1);
}
closeHandle(fd);
return (0);
}
/*
* ======== UARTUtils_devicelseek ========
*/
off_t UARTUtils_devicelseek(int fd, off_t offset, int origin)
{
return (-1);
}
/*
* ======== UARTUtils_deviceopen ========
*/
int UARTUtils_deviceopen(const char *path, unsigned flags, int mode)
{
int fd;
UART_Handle handle;
/* Get the UART specified for opening. */
fd = path[0] - '0';
handle = openHandle(fd, false);
if(handle == NULL) {
return (-1);
}
else {
return (fd);
}
}
/*
* ======== UARTUtils_deviceread ========
*/
int UARTUtils_deviceread(int fd, char *buffer, unsigned size)
{
int ret;
/* Return if a UART other than UART 0 was specified. */
if (fd != 0) {
return (-1);
}
/* Read character from the UART and block until a newline is received. */
ret = UART_read(ports[fd].handle, (uint8_t *)buffer, size);
return (ret);
}
/*
* ======== UARTUtils_devicewrite ========
*/
int UARTUtils_devicewrite(int fd, const char *buffer, unsigned size)
{
int ret;
/* Return if a UART other than UART 0 was specified. */
if (fd != 0) {
return (-1);
}
/* Write to the UART and block until the transfer is finished. */
ret = UART_write(ports[fd].handle, (uint8_t *)buffer, size);
return (ret);
}
/*
* ======== UARTUtils_deviceunlink ========
*/
int UARTUtils_deviceunlink(const char *path)
{
return (-1);
}
/*
* ======== UARTUtils_devicerename ========
*/
int UARTUtils_devicerename(const char *old_name, const char *new_name)
{
return (-1);
}
/*
* ======== UARTUtils_systemAbort ========
*/
Void UARTUtils_systemAbort(String str)
{
/* Make sure UART is initialized */
if (systemHandle) {
UART_writePolling(systemHandle, (uint8_t *)str, strlen(str));
}
}
/*
* ======== UARTUtils_systemInit ========
*/
void UARTUtils_systemInit(unsigned int index)
{
systemHandle = openHandle(index, false);
if (systemHandle == NULL) {
Log_print1(Diags_USER1, "Failed to open UART %d", index);
}
}
/*
* ======== UARTUtils_systemPutch ========
*/
Void UARTUtils_systemPutch(Char a)
{
/* Make sure UART is initialized */
if (systemHandle) {
UART_write(systemHandle, (void *)&a, 1);
}
}
/*
* ======== UARTUtils_systemReady ========
*/
Bool UARTUtils_systemReady(Void)
{
return (systemHandle ? TRUE : FALSE);
}
UARTUtils.c
/*
* Copyright (c) 2017, Texas Instruments Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** ============================================================================
* @file UARTUtils.h
*
* @brief UART related utilities
*
* The UARTUtils header file should be included in an application as follows:
* @code
* #include <UARTUtils.h>
* @endcode
*
* ============================================================================
*/
#ifndef __UARTUtils_H
#define __UARTUtils_H
#ifdef __cplusplus
extern "C" {
#endif
#include <xdc/std.h>
#include <stdio.h>
#if defined(__TI_COMPILER_VERSION__)
#include <file.h>
#endif
/*!
* @brief Open the UART to be used with LoggerIdle
*
* This function opens the UART to be used by the LoggerIdle plugged function.
*
* @param index UART to use for LoggerIdle's send function from the ports
* array. Matches the same number uart base (0 -> UART_BASE0).
*/
extern void UARTUtils_loggerIdleInit(unsigned int index);
/*!
* @brief Sends Log data out the UART
*
* This function should not be called directly, it will be called by the
* LoggerIdle module during Idle.
*
* @param a Pointer to unsigned char buffer
* @param len length of unsigned char buffer
* @return Number of bytes sent
*
* To configure LoggerIdle to upload via UART in the .cfg:
* @code
* var LoggingSetup = xdc.useModule('ti.uia.sysbios.LoggingSetup');
* LoggingSetup.loggerType = LoggingSetup.UploadMode_IDLE;
* var LoggerIdle = xdc.useModule('ti.uia.sysbios.LoggerIdle');
* LoggerIdle.transportFxn = "&UARTUtils_loggerIdleSend";
* LoggerIdle.transportType = LoggerIdle.TransportType_UART;
* @endcode
*
*/
extern Int UARTUtils_loggerIdleSend(UChar *a, Int len);
/*!
* @brief close function for add_device rts function
*
* This function should not be called directly
*/
extern int UARTUtils_deviceclose(int fd);
/*!
* @brief seek function for add_device rts function
*
* This function should not be called directly
*/
extern off_t UARTUtils_devicelseek(int fd, off_t offset, int origin);
/*!
* @brief open function for add_device rts function
*
* This function should not be called directly
*/
extern int UARTUtils_deviceopen(const char *path, unsigned flags, int mode);
/*!
* @brief read function for add_device rts function
*
* This function should not be called directly
*/
extern int UARTUtils_deviceread(int fd, char *buffer, unsigned size);
/*!
* @brief rename function for add_device rts function
*
* This function should not be called directly
*/
extern int UARTUtils_devicerename(const char *old_name, const char *new_name);
/*!
* @brief unlink function for add_device rts function
*
* This function should not be called directly
*/
extern int UARTUtils_deviceunlink(const char *path);
/*!
* @brief write function for add_device rts function
*
* This function should not be called directly
*/
extern int UARTUtils_devicewrite(int fd, const char *buffer, unsigned size);
/*!
* @brief abort function called within System_abort
*
* This function should not be called directly
*/
extern Void UARTUtils_systemAbort(String str);
/*!
* @brief Opens the UART to be used with SysCallback
*
* This function opens the UART to be used by the SysCallback plugged functions.
*
* @param index UART to use for System output from ports array. Matches the
* same number uart base (0 -> UART_BASE0).
*/
extern void UARTUtils_systemInit(unsigned int index);
/*!
* @brief putch function for System_printf
*
* This function should not be called directly
*/
extern Void UARTUtils_systemPutch(Char a);
/*!
* @brief ready function for System module
*
* This function should not be called directly
*/
extern Bool UARTUtils_systemReady();
#ifdef __cplusplus
}
#endif
#endif /* __UARTUtils_H */
UARTUtils.h
Additional Resources
Forums
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.