The world lives in a rich universe filled with colors and graphical user interfaces but embedded system developers often still live in terminal applications and command prompts. Console applications have been around for decades but have proven to be extremely effective tools for testing and commanding an embedded system during development and testing. Let’s examine how a developer can quickly get a Console up and running over UART.
Before diving into the technical details, it is useful to understand the Console’s capabilities. The Console Application Framework supports:
Input without parsing
Numerical input
Echo option to echo input to transmitter
Backspace and arrow key navigation
UART driver and other serial drivers
For a developer, we simply add the Console Application Framework to a thread and select whether the Console will communicate through UART, USB or Ethernet. Once those details are configured, a developer needs to make sure that the prompt API function is called periodically to handle the incoming serial stream. A developer can then add commands and menu options that the embedded system will support.
Creating an application that supports the Console Application Framework is simple. Start by creating a new Renesas Synergy™ project that supports ThreadX®. Within the Synergy configuration file, open the threads tab and create a new thread named Console_thread. Add the Console Framework by adding a thread stack located under Frameworks -> Services -> Console Framework on sf_console. The result should look something similar to the image below:
Next we click the “Add Communications Framework” box and select the UART device. Alternatively, we could select USB or Ethernet but that will be a good exercise for the reader to do on their own later. A developer could setup the UART just like in the last post which covered the UART Application Framework. However, this time we will setup the console to use UART2 and access it through the Arduino headers. The SCI block properties should be configured as follows:
The Console Framework block really doesn’t need to be modified but it’s always a good idea to change the components name from the default. In this case, we change g_sf_console0 to g_sf_console. Why not save ourselves from typing one more character? The properties should look as follows:
Finally, we should find that our thread stack looks something similar to the following image:
There is still more work to be done in order to get the console up and running. Before diving into those details, there are two great resources I want to point you towards. The first is a video demonstration for the console framework located at https://www.youtube.com/watch?v=9d_yj_Wwadw. Second, a developer should check out the Console Framework section in the SSP user manual. The manual provides an overview for the console but also shows example code and provides steps on how to configure a menu. I like to use the HTML generated document. Just look under the left side menu within the User Guides -> Framework Layer -> Console Framework.
The last steps before running the Console application is to add menus and commands to the console. Adding a menu and getting the console up and running requires three major steps:
Continuously call the prompt function in the console API
Create callback functions for the actions that the menu items will perform
Create the menu system
First, within the Console_thread_entry.c module, modify the Console_thread_entry function to include a call to the console prompt as follows:
while (1)
{
g_sf_console.p_api->prompt(g_sf_console.p_ctrl, NULL, TX_WAIT_FOREVER);
}
This call continuously watches for new data being transmitting through the console to the embedded system.
Next, we want to create a callback function for every command that the console will support. For this example, we’ll just have a single function that can be used to toggle the LEDs on the development kit. We first create a prototype for the callback as follows:
void led_toggle_callback(sf_console_cb_args_t * p_args);
and then create the implementation:
void led_toggle_callback(sf_console_cb_args_t * p_args)
bsp_leds_t leds;
ioport_level_t level;
R_BSP_LedsGet(&leds);
if(strcmp(p_args->p_remaining_string,"GREEN") == 0)
g_ioport.p_api->pinRead(leds.p_leds[0], &level);
g_ioport.p_api->pinWrite(leds.p_leds[0], !level);
else if(strcmp(p_args->p_remaining_string, "RED") == 0)
g_ioport.p_api->pinRead(leds.p_leds[1], &level);
g_ioport.p_api->pinWrite(leds.p_leds[1], !level);
else if(strcmp(p_args->p_remaining_string,"YELLOW") == 0)
g_ioport.p_api->pinRead(leds.p_leds[2], &level);
g_ioport.p_api->pinWrite(leds.p_leds[2], !level);
The implementation in this case is doing something a bit special that may not be needed for every command. Since the SK-S7G2 board has three LEDs onboard, when sending a command via the terminal to toggle an LED, we can add a text argument that tells the callback function the LED that should be toggled. We do this by doing a strcmp between the p_args p_remaining_string member and a desired string such as “GREEN”. If strcmp returns 0 then we know that the strings match and can change the voltage level associated with the matching LED.
Finally, the developer needs to create the console commands list and the console root menu. Without an example, a developer might wonder how to initialize these so since we want to be able to toggle an LED. There is a great example in the SSP User Manual that developers can use. To save you a moment, I’ve included an example template below for how to create these variables:
const sf_console_command_t g_sf_console_commands[] =
.command = "TOGGLE",
.help = "Toggle an LED by color",
.callback = led_toggle_callback,
.context = NULL}
};
const sf_console_menu_t g_sf_console_root_menu =
.menu_prev = NULL,
.menu_name = "Command",
.num_commands = sizeof(g_sf_console_commands)/ sizeof(g_sf_console_commands[0]),
.command_list = &g_sf_console_commands[0]
The above example is a simple example but it is easily expanded upon to add complex commands and features to the embedded system. Before compiling, double check that the UART2 pins have been changed from their default SPI peripheral to UART2. Once the pins are configured, a developer is ready to compile and run the program.
After compiling, a developer will notice that the build is successful but that quite a few warnings pop up related to near initializations and pointers. The warnings are due to the fact that the sf_console_command_t and the sf_console_menu_t string members are expecting uint8_t pointers instead of char pointers. The warning has to do with the datatype signs being different (signed vs unsigned char). The code compiles and executes just fine but, like any good programmer, if you get a bothersome feeling compiling with warnings there are two options.
First, you can typecast the text to a uint8_t *. For example,
becomes
.command = (uint8_t *)"TOGGLE",
That is a great and explicit way to make the compiler happy. It does require typecasting though and typecasting is something that developers generally should avoid doing. The second option is to add the -Wno-pointer-sign option to the projects Build Properties -> Warnings option. GCC will ignore the sign for a pointer which is okay since a pointer into memory should never be negative anyways.
Loading the code and firing up your favorite terminal should result in Command> being displayed in the terminal. Type ? to get a command list as shown below:
Next, type the TOGGLE command with an LED color such as RED, GREEN or YELLOW. After running the command, the LED on the SK-S7G2 will change state!
The Console Application Framework turns out to be easy to implement and configure. The best part is that if at a later date the developer decided the terminal should be on Ethernet or USB they simply need to update the Thread Stack from the Synergy Configuration! Not a single line of code needs to change just remove and add a new component.
Next time we will dig into the external IRQ framework and see how easy it is to add and use in a project.
Live long and profit!
Professor_IoT
Hot Tip of the Week
The Developer Example Template, available when you create a project for the DK-S7G2 kit, includes a complex Console Framework example that illustrates multi-layer commands and argument parsing. It also provides application projects for ADC, Audio Playback, DAC, AGT, Flash, and several more SSP Framework and HAL modules. When you create the project, a Getting Started Guide is automatically added to your SSP_Documentation directory. This is a very useful resource for just about any design, and is often overlooked.
This did not work for the PE-HMI1. Windows 7 lists the PE-HMI1 device a "Unknown Device".