BSP430  20141115
Board Support Package for MSP430 microcontrollers
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
Utilities: Command Line Interface

The <bsp430/utility/cli.h> header provides a simple way to implement a command line interface within a program that uses <bsp430/utility/console.h>. Each command is implemented in its own function, and registered as available by linking them into a chain of static structures at compile-time. The example below shows how this can be done in a way that makes it simple to include or exclude commands based on development needs and available options.

The module also provides utilities that make it easy to add commands for users to reconfigure parameters of various types, obtain help on the available commands, and implement nested commands.

The example below supports the following commands:

> help
help [cmd] # Show help on cmd or all commands
say {word}... # Customized completion demonstration
complete # No help available
  component # No help available
  common # No help available
quote [qstr]... # Extract one or more quoted tokens
set # No help available
  ulval [unsigned 32-bit integer in octal, decimal, or hex]
  lval [signed 32-bit integer in octal, decimal, or hex]
  uival [unsigned 16-bit integer in octal, decimal, or hex]
  ival [signed 16-bit integer in octal, decimal, or hex]
  all [ival] [ [uival] [ [lval] [ [ulval] ] ] ]
show # Display the value of variables
expand [command...] # Show the expansion of the command without executing it
uptime # Show system uptime
hup # No help available
  two # valid if no three
    three # invalid if no four
      four # No help available

Below is a capture from an interactive session with the application. Where the sequence <TAB> appears a tab character was typed. Where the sequence <UP> appears the up-arrow on an ANSI terminal was pressed. See iBSP430cliConsoleBufferProcessInput() for a table showing command metacharacters.

> uptime
Up  1:25.337
> up
Up  1:26.129
> exp h t t 4
Expanded: ( h t t 4)
> exp hu t t 4
Expanded: hup two three( 4)
> hu t t f
Display: hup two three four
WTH are we fighting for? Walk!

> sh
ival 0
uival 0 (0x0)
lval 0
ulval 0 (0x0)
> set i 0x23
> sh
ival 35
uival 0 (0x0)
lval 0
ulval 0 (0x0)
> set all -5 -010 -1 -1
> sh
ival -5
uival 65528 (0xfff8)
lval -1
ulval 4294967295 (0xffffffff)
> quote "one two" and "three four"
Extracting text tokens from 27 characters
7-char token <one two>
3-char token <and>
10-char token <three four>
> s<TAB>
Candidates: say set show
> say <TAB>
Candidates: zero one two three four (+ 6 more)
> say t<TAB>
Candidates: two three ten
> say three
Match three for 6 consumed position 3
1 matches found
> say ten five sev
Match ten for 4 consumed position 10
Match five for 5 consumed position 5
Match seven for 4 consumed position 7
3 matches found
>
> <UP>escape char 0x5b (91) '['
escape char 0x41 (65) 'A'
Leaving escape mode

main.c

#include <bsp430/clock.h>
#include <string.h>
#include <ctype.h>
struct data_t {
int ival;
unsigned int uival;
long lval;
unsigned long ulval;
} data;
void
dumpData (void)
{
cprintf("ival %d\n", data.ival);
cprintf("uival %u (0x%x)\n", data.uival, data.uival);
cprintf("lval %ld\n", data.lval);
cprintf("ulval %lu (0x%lx)\n", data.ulval, data.ulval);
}
/* Recommended pattern: a global variable that holds the entrypoint to
* the supported commands, and a macro that is redefined after each
* command is hooked in. The macro allows commands to be disabled or
* rearranged without having to change the links. */
const sBSP430cliCommand * commandSet;
#define LAST_COMMAND NULL
static int
cmd_h234 (sBSP430cliCommandLink * chain,
void * param,
const char * argstr,
size_t argstr_len)
{
cputtext("Display: ");
if (0 == argstr_len) {
cputs("\nWTH are we fighting for? Walk!");
}
cputchar('\n');
return 0;
}
static int
cmd_hup_two (const char * argstr)
{
cprintf("Give me a 'three'\n");
return 0;
}
static const sBSP430cliCommand dcmd_hup_two_three_four = {
.key = "four",
.handler = cmd_h234
};
static const sBSP430cliCommand dcmd_hup_two_three = {
.key = "three",
.help = "# invalid if no four",
.child = &dcmd_hup_two_three_four,
};
static const sBSP430cliCommand dcmd_hup_two = {
.key = "two",
.help = "# valid if no three",
.child = &dcmd_hup_two_three,
.param.simple_handler = cmd_hup_two
};
static const sBSP430cliCommand dcmd_hup = {
.key = "hup",
.child = &dcmd_hup_two,
.next = LAST_COMMAND,
};
#undef LAST_COMMAND
#define LAST_COMMAND &dcmd_hup
static int
cmd_uptime (const char * argstr)
{
return 0;
}
static const sBSP430cliCommand dcmd_uptime = {
.key = "uptime",
.help = "# Show system uptime",
.next = LAST_COMMAND,
.param.simple_handler = cmd_uptime
};
#undef LAST_COMMAND
#define LAST_COMMAND &dcmd_uptime
static int
cmd_expand_ (sBSP430cliCommandLink * chain,
void * param,
const char * argstr,
size_t argstr_len)
{
cputtext("Expanded: ");
cputchar('\n');
return 0;
}
static int
cmd_expand (const char * argstr)
{
return iBSP430cliParseCommand(commandSet, NULL, argstr, NULL, cmd_expand_);
}
static const sBSP430cliCommand dcmd_expand = {
.key = "expand",
.help = "[command...] # Show the expansion of the command without executing it",
.next = LAST_COMMAND,
.param.simple_handler = cmd_expand
};
#undef LAST_COMMAND
#define LAST_COMMAND &dcmd_expand
static int
cmd_show (const char * argstr)
{
dumpData();
return 0;
}
static const sBSP430cliCommand dcmd_show = {
.key = "show",
.help = "# Display the value of variables",
.next = LAST_COMMAND,
.param.simple_handler = cmd_show
};
#undef LAST_COMMAND
#define LAST_COMMAND &dcmd_show
static int
cmd_set_all (const char * argstr)
{
size_t argstr_len = strlen(argstr);
int rv;
rv = iBSP430cliStoreExtractedI(&argstr, &argstr_len, &data.ival);
if (0 == rv) {
rv = iBSP430cliStoreExtractedUI(&argstr, &argstr_len, &data.uival);
}
if (0 == rv) {
rv = iBSP430cliStoreExtractedL(&argstr, &argstr_len, &data.lval);
}
if (0 == rv) {
rv = iBSP430cliStoreExtractedUL(&argstr, &argstr_len, &data.ulval);
}
return rv;
}
static const sBSP430cliCommand dcmd_set_all = {
.key = "all",
.help = "[ival] [ [uival] [ [lval] [ [ulval] ] ] ]",
.param.simple_handler = cmd_set_all
};
static const sBSP430cliCommand dcmd_set_ival = {
.key = "ival",
.help = "[signed 16-bit integer in octal, decimal, or hex]",
.next = &dcmd_set_all,
.param.ptr = &data.ival
};
static const sBSP430cliCommand dcmd_set_uival = {
.key = "uival",
.help = "[unsigned 16-bit integer in octal, decimal, or hex]",
.next = &dcmd_set_ival,
.param.ptr = &data.uival
};
static const sBSP430cliCommand dcmd_set_lval = {
.key = "lval",
.help = "[signed 32-bit integer in octal, decimal, or hex]",
.next = &dcmd_set_uival,
.param.ptr = &data.lval
};
static const sBSP430cliCommand dcmd_set_ulval = {
.key = "ulval",
.help = "[unsigned 32-bit integer in octal, decimal, or hex]",
.next = &dcmd_set_lval,
.param.ptr = &data.ulval
};
static const sBSP430cliCommand dcmd_set = {
.key = "set",
.child = &dcmd_set_ulval,
.next = LAST_COMMAND,
};
#undef LAST_COMMAND
#define LAST_COMMAND &dcmd_set
static int
cmd_quote (const char * argstr)
{
size_t arglen = strlen(argstr);
size_t len;
cprintf("Extracting text tokens from %u characters\n", (unsigned int)arglen);
while (0 < arglen) {
const char * tp = xBSP430cliNextQToken(&argstr, &arglen, &len);
cprintf("%u-char token <", (unsigned int)len);
while (len--) {
cputchar(*tp++);
}
cprintf(">\n");
}
return 0;
}
static const sBSP430cliCommand dcmd_quote = {
.key = "quote",
.help = "[qstr]... # Extract one or more quoted tokens",
.next = LAST_COMMAND,
.param.simple_handler = cmd_quote
};
#undef LAST_COMMAND
#define LAST_COMMAND &dcmd_quote
static int
cmd_dummy (const char * argstr)
{
cprintf("dummy %s\n", argstr);
return 0;
}
#undef LAST_SUBCOMMAND
#define LAST_SUBCOMMAND NULL
static const sBSP430cliCommand dcmd_complete_common = {
.key = "common",
.next = LAST_SUBCOMMAND,
.param.simple_handler = cmd_dummy
};
#undef LAST_SUBCOMMAND
#define LAST_SUBCOMMAND &dcmd_complete_common
static const sBSP430cliCommand dcmd_complete_component = {
.key = "component",
.next = LAST_SUBCOMMAND,
.param.simple_handler = cmd_dummy
};
#undef LAST_SUBCOMMAND
#define LAST_SUBCOMMAND &dcmd_complete_component
static const sBSP430cliCommand dcmd_complete = {
.key = "complete",
.next = LAST_COMMAND,
.child = LAST_SUBCOMMAND
};
#undef LAST_COMMAND
#define LAST_COMMAND &dcmd_complete
static const char * const numbers[] = {
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten"
};
static const size_t number_len = sizeof(numbers)/sizeof(*numbers);
#if (configBSP430_CLI_COMMAND_COMPLETION_HELPER - 0)
static const sBSP430cliCompletionHelperStrings completion_helper_say = {
.strings = numbers,
.len = sizeof(numbers)/sizeof(*numbers)
};
#endif /* configBSP430_CLI_COMMAND_COMPLETION_HELPER */
static int
cmd_say (sBSP430cliCommandLink * chain,
void * param,
const char * command,
size_t command_len)
{
const char * const * np = &command;
int nmatches = 0;
while (NULL != np) {
size_t ocl = command_len;
np = xBSP430cliHelperStringsExtract(&completion_helper_say, &command, &command_len);
if (NULL != np) {
++nmatches;
cprintf("Match %s for %u consumed position %u\n", *np,
(unsigned int)(ocl - command_len),
(unsigned int)(np - numbers));
}
}
cprintf("%u matches found\n", nmatches);
return 0;
}
static const sBSP430cliCommand dcmd_say = {
.key = "say",
.help = "{word}... # Customized completion demonstration",
#if (configBSP430_CLI_COMMAND_COMPLETION_HELPER - 0)
.completion_helper = &completion_helper_say.completion_helper,
#endif /* configBSP430_CLI_COMMAND_COMPLETION_HELPER */
.next = LAST_COMMAND,
.handler = cmd_say,
};
#undef LAST_COMMAND
#define LAST_COMMAND (&dcmd_say)
#ifndef RESPONSIVE_CCIDX
#define RESPONSIVE_CCIDX 1
#endif /* RESPONSIVE_CCIDX */
typedef struct sResponsive {
volatile unsigned long wakeups;
unsigned long interval_tck;
volatile unsigned long max_late;
unsigned long lastDump_tck;
} sResponsive;
static sResponsive responsiveData;
static int
responsive_cb_ni (hBSP430timerAlarm alarm)
{
sResponsive * rdp = (sResponsive *)alarm;
unsigned long now_tck = ulBSP430timerCounter_ni(alarm->timer, NULL);
unsigned long late_tck = now_tck - alarm->setting_tck;
unsigned long setting_tck;
int rc;
if (0 == rdp->wakeups) {
rdp->max_late = late_tck;
} else if (late_tck > rdp->max_late) {
rdp->max_late = late_tck;
}
rdp->wakeups++;
/* Set the next setting. Jump over all the missed alarms if we're
* more than an interval late. */
setting_tck = alarm->setting_tck;
setting_tck += (late_tck / rdp->interval_tck) * rdp->interval_tck;
do {
setting_tck += rdp->interval_tck;
rc = iBSP430timerAlarmSet_ni(alarm, setting_tck);
} while (0 < rc);
return 0;
}
static int
cmd_responsive (const char * argstr)
{
sResponsive state;
unsigned long resp_Hz;
unsigned long now_utt;
do {
now_utt = ulBSP430uptime_ni();
state = responsiveData;
responsiveData.wakeups = 0;
responsiveData.lastDump_tck = now_utt;
} while (0);
cprintf("State: %lu tick span with %lu wakeups\n"
"\twakeup interval %lu tick (%lu us)\n"
"\tlate maximum %lu tick (%lu us)\n",
(now_utt - state.lastDump_tck), state.wakeups,
state.interval_tck, BSP430_CORE_TICKS_TO_US(state.interval_tck, resp_Hz),
state.max_late, BSP430_CORE_TICKS_TO_US(state.max_late, resp_Hz));
return 0;
}
static const sBSP430cliCommand dcmd_responsive = {
.key = "responsive",
.help = "# Display responsiveness data",
.next = LAST_COMMAND,
.param.simple_handler = cmd_responsive
};
#undef LAST_COMMAND
#define LAST_COMMAND &dcmd_responsive
static int
cmd_help (sBSP430cliCommandLink * chain,
void * param,
const char * command,
size_t command_len)
{
return 0;
}
static const sBSP430cliCommand dcmd_help = {
.key = "help",
.help = "[cmd] # Show help on cmd or all commands",
.next = LAST_COMMAND,
.handler = cmd_help
};
#undef LAST_COMMAND
#define LAST_COMMAND &dcmd_help
void main ()
{
const char * command;
int flags;
cprintf("\ncli example " __DATE__ " " __TIME__ "\n");
#if (configBSP430_CLI_COMMAND_COMPLETION - 0)
cprintf("Command completion is available.\n");
#endif /* configBSP430_CLI_COMMAND_COMPLETION */
#if (BSP430_PMM_SUPPORTS_SVSM - 0)
#endif // BSP430_PMM_SUPPORTS_SVSM
rh = hBSP430timerAlarmInitialize(&responsiveData.alarm,
RESPONSIVE_CCIDX,
responsive_cb_ni);
if (NULL == rh) {
cprintf("Failed to initialize responsiveness alarm\n");
} else {
int rc;
responsiveData.wakeups = 0;
responsiveData.interval_tck = 10;
responsiveData.max_late = 0;
responsiveData.lastDump_tck = 32768 + ulBSP430uptime_ni();
if (0 == rc) {
rc = iBSP430timerAlarmSet_ni(rh, responsiveData.lastDump_tck);
}
if (0 != rc) {
cprintf("Failed to initialize alarm\n");
}
}
cprintf("\nLED lit when not awaiting input\n");
/* NOTE: The control flow in this is a bit tricky, as we're trying
* to leave interrupts enabled during the main body of the loop,
* while they must be disabled when processing input to recognize a
* command. The flags variable preserves state across multiple loop
* iterations until all relevant activities have completed. */
commandSet = LAST_COMMAND;
command = NULL;
while (1) {
int c;
while (0 <= ((c = cgetchar()))) {
cprintf("escape char 0x%02x (%u) '%c'\n", c, c, isprint(c) ? c : '.');
/* Technically CSI is a single character 0x9b representing
* ESC+[. In the two-character mode accepted here, we use the
* value for the second character. */
#define KEY_CSI '['
if ((KEY_CSI == c) && (flags & eBSP430cliConsole_PROCESS_ESCAPE)) {
flags &= ~eBSP430cliConsole_PROCESS_ESCAPE;
} else if ((64 <= c) && (c <= 126)) {
flags &= ~eBSP430cliConsole_ANY_ESCAPE;
cprintf("Leaving escape mode\n");
break;
}
}
}
if (flags & eBSP430cliConsole_DO_COMPLETION) {
flags &= ~eBSP430cliConsole_DO_COMPLETION;
flags |= iBSP430cliConsoleBufferCompletion(commandSet, &command);
}
if (flags & eBSP430cliConsole_READY) {
int rv;
rv = iBSP430cliExecuteCommand(commandSet, 0, command);
if (0 != rv) {
cprintf("Command execution returned %d\n", rv);
}
/* Ensure prompt is rewritten, but not the command we just
* ran */
command = NULL;
}
if (flags & eBSP430cliConsole_REPAINT) {
/* Draw the prompt along with whatever's left in the command
* buffer. Note use of leading carriage return in case an edit
* left material on the current line. */
cprintf("\r> %s", command ? command : "");
flags &= ~eBSP430cliConsole_REPAINT;
}
if (flags & eBSP430cliConsole_REPAINT_BEL) {
cputchar('\a');
flags &= ~eBSP430cliConsole_REPAINT_BEL;
}
if (flags & eBSP430cliConsole_READY) {
/* Clear the command we just completed */
flags &= ~eBSP430cliConsole_READY;
}
do {
/* Unless we're processing application-specific escape
* characters let iBSP430cliConsoleBufferProcessInput_ni()
* process any input characters that have already been
* received. */
if (! (flags & eBSP430cliConsole_ANY_ESCAPE)) {
}
if (0 == flags) {
/* Sleep until something wakes us, such as console input.
* Then turn off interrupts and loop back to read that
* input. */
}
/* Repeat if still nothing to do */
} while (! flags);
/* Got something to do; get the command contents in place so
* we can update the screen. */
}
}

bsp430_config.h

/* Use a crystal if one is installed. Much more accurate timing
* results. */
#define BSP430_PLATFORM_BOOT_CONFIGURE_LFXT1 1
/* Application does output: support spin-for-jumper */
#define configBSP430_PLATFORM_SPIN_FOR_JUMPER 1
/* Support console output */
#define configBSP430_CONSOLE 1
/* Enable an 8-character rx buffer for the console */
#define BSP430_CONSOLE_RX_BUFFER_SIZE 8
/* Enable an 80-character command buffer */
#define BSP430_CLI_CONSOLE_BUFFER_SIZE 80
/* Enable command completion, and completion helper */
#define configBSP430_CLI_COMMAND_COMPLETION 1
#define configBSP430_CLI_COMMAND_COMPLETION_HELPER 1
/* Monitor uptime and provide generic ACLK-driven timer */
#define configBSP430_UPTIME 1
/* Get platform defaults */

Makefile

PLATFORM ?= exp430f5438
MODULES=$(MODULES_PLATFORM)
MODULES += $(MODULES_UPTIME)
MODULES += $(MODULES_CONSOLE)
MODULES += utility/cli
SRC=main.c
include $(BSP430_ROOT)/make/Makefile.common