Lesson 004: Redirecting printf to UART

Today we’ll be redirecting printf to use the UART module we created in Lesson 003.

I’ve created a new project and added the references to SCI Common and UART Driver.

Previous microcontrollers I’ve used required you to override a function that printf ultimately calls. Unfortunately, the implementation of printf determines which function is called, and I don’t already know (and I couldn’t find an online reference that didn’t redirect printf to the debugging console).

After opening the UART module, and added a simple printf statement and added a breakpoint. Stepping through the code on the breakpoint, I get the following message:

No source available for "puts() at 0x3564"

OK, so it looks like I need to define the puts function!

In the code, I place

// Redefine puts to handle printf output
int puts(const char *str)
{
    // Write String to UART
    g_uart.p_api->write(g_uart.p_ctrl, (const uint8_t *)str, strlen(str));

    return 0;
}

above the hal_entry function, rerun my code and…it works!

My excitement is short lived however; if I have to printf statements back to back, the second one cuts off the first one. The g_uart.p_api.write function is not blocking. One one hand this is good, as while the uart is moving data behind the scenes, the microcontroller is free to do other tasks.

On the other hand, it means that I have to wait for one printf to finish before I start another. The only good way to do this is to use the interrupt functions.

In the Synergy Configuration window, the UART Driver has a property titled “Name of UART callback function to be defined by user” with the name of a function to be called on an interrupt.

This function is called when any of the for interrupt sources trigger:

SCI8 RXI
SCI8 TXI
SCI8 TEI
SCI8 ERI

Looking at the S7 User’s Manual, Table 19.3 Associations between event signal names set in ELSRn.ELS bits and signal numbers contains the definitions for the RXI, TXI, TEI, and ERI interrupt sources for each SCI source.

SCI8_RXI*: Receive data full
SCI8_TXI*: Transmit data empty
SCI8_TEI: Transmit end
SCI8_ERI*: Receive error
SCI8_AM: Address match event
*This event is not supported in FIFO mode.

The event we are interested in is the Transmit End event (SCI8_TEI).

Looking in the Synergy Software Package User Manual, we can find the function definition for the uart callback function. When the callback function is called, it passes a pointer to a uart_callback_args_t structure. This structure holds a value of uart_event_t.

uart_callback_args_t

typedef struct{
    uint32_t channel;
    uart_event_t event;
    uint32_t data;
    void const * p_context;
} uart_callback_args_t

UART Event codes

Name Description
UART_EVENT_RX_COMPLETE Receive complete event.
 UART_EVENT_TX_COMPLETE  Transmit complete event.
 UART_EVENT_ERR_PARITY  Parity error event.
 UART_EVENT_ERR_FRAMING  Mode fault error event.
 UART_EVENT_BREAK_DETECT  Break detect error event.
 UART_EVENT_ERR_OVERFLOW  FIFO Overflow error event.
 UART_EVENT_ERR_RXBUF_OVERFLOW  Receive buffer overflow error event.
 UART_EVENT_RX_CHAR  Character received.

The event we are looking for is the UART_EVENT_TX_COMPLETE event.

The last piece of the puzzle is to add a flag signaling that the transmission has completed. The fina code to handle the printf redirection is shown below:

/* HAL-only entry function */
#include "hal_data.h"
#include 
#include 

// Create global variable to hold transmission station
volatile bool transmitComplete;

// Redefine puts to handle printf output
int puts(const char *str)
{
    // Set Transmit Flag to false
    transmitComplete = false;

    // Write String to UART
    g_uart.p_api->write(g_uart.p_ctrl, (const uint8_t *)str, strlen(str));

    // Wait until flag is set
    while (!transmitComplete) {}

    // Reset Flag
    transmitComplete = false;

    return 0;
}


// Callback function to set Transmit Done flag when transmission is complete
void user_uart_callback(uart_callback_args_t *p_args)
{
    // Return if event type is not for completed transmission
    if (p_args->event != UART_EVENT_TX_COMPLETE)
    {
        return;
    }

    // Set Transmit Complete Flag
    transmitComplete = true;
}

void hal_entry(void)
{
    // Initialize Transmission Complete Flag
    transmitComplete = false;

    // Open the UART driver
    g_uart.p_api->open(g_uart.p_ctrl, g_uart.p_cfg);

    // Clear Transmission Complete Flag
    transmitComplete = false;

    // Send the message data
    g_uart.p_api->write(g_uart.p_ctrl, (const uint8_t *)"Test\r\n", 6);

    // Wait for Transmission to complete
    while (!transmitComplete) {}

    // Clear Transmission Complete Flag
    transmitComplete = false;

    // Try a printf
    printf("\r\nThis is a test!\r\n");

    // Try another printf to make sure we are waiting for the previous one to complete
    printf("\r\nThis is another test!\r\n");
}

This concludes day Five! As always, full source code for this tutorial is available on GitHub at https://github.com/lycannon/ajwrs.

Update

The above code works great, until you try using format specifiers (IE: printf(“%d”, 5);

Once you run this code, everything breaks 😦

The fix is pretty straight forward and is covered in Day 6: Redirecting printf to UART Part 2.

Advertisements

One thought on “Lesson 004: Redirecting printf to UART

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s