#include <Energia.h>
#include <xdc/std.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Memory.h>
#include <xdc/runtime/Types.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/utils/Load.h>
#include <ti/sysbios/hal/Hwi.h>
/*
* This is the sketch for the UART debug console. Nothing else should be using the same
* serial connection.
*
* To add a command, simply create a prototype for the handler function following the
* model of the handler prototypes in the `local functions' section below, add the
* function itself in the `local function defs' section below, and add a line to the
* _consoleCommandTable definition in the `command table' section. The name of your
* command must have a length < MAX_COMMAND_LEN, and the handler's name should follow
* the format `consoleHandler_<NAME>'. Also note that the line that is passed into the
* handler starts right after the command's name; if arguments were supplied to the
* command, *line will be a space character.
*/
#define DM_CMD 1 /* dump memory cmd */
#define WM_CMD 1 /* write to memory cmd */
#define DRW_CMDS 1 /* digital read/write cmds */
#define ARW_CMDS 1 /* analog read/write cmds */
#define SPI_CMD 1 /* SPI transfer cmd */
#define PRI_CMD 1 /* Set Task priority cmd */
#ifdef BOARD_CC2650STK_BLE
#define STATS_CMD 0 /* CPU and task utilzation stats cmd */
#else
#define STATS_CMD 1 /* CPU and task utilzation stats cmd */
#endif
#define MAX_COMMAND_LEN 48
#define MAX_COMMAND_NAME_LEN 8
#define MAX_COMMAND_LINES 5
// Return codes for handlers
#define RETURN_SUCCESS (0)
#define RETURN_FAIL (-1)
#define RETURN_FAIL_PRINT_USAGE (-2)
typedef void (*RepeatFunc)(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3);
static void doRepeat(RepeatFunc func, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3);
static void sanitizeLine(char *line);
static int consoleHandler_help(const char *line);
#if DRW_CMDS == 1
static int consoleHandler_dr(const char *line);
static int consoleHandler_dw(const char *line);
#endif
#if ARW_CMDS == 1
static int consoleHandler_ar(const char *line);
static int consoleHandler_aw(const char *line);
#endif
#if WM_CMD == 1
static int consoleHandler_wm(const char *line);
static int consoleHandler_wm1(const char *line);
static int consoleHandler_wm2(const char *line);
#endif
#if DM_CMD == 1
static int consoleHandler_dm(const char *line);
#endif
#if STATS_CMD == 1
static int consoleHandler_stats(const char *line);
#endif
#if SPI_CMD == 1
static int consoleHandler_spi(const char *line);
#endif
#if PRI_CMD == 1
static int consoleHandler_pri(const char *line);
#endif
static char home[] = "\e[H";
static char clear[] = "\e[2J";
/*
* ======== command table ========
*/
#define GEN_COMMTABLE_ENTRY(name, desc, detail) { #name, consoleHandler_##name, desc, detail }
static const struct {
const char*name; // The name of the command
int (*handler)(const char*line); // Pointer to the command's handler
const char*description; // A short description of the command (for use by help)
const char*detailedUsage; // A detailed description of the command's usage (for use by help)
} _consoleCommandTable[] = {
#if DM_CMD == 1
GEN_COMMTABLE_ENTRY(dm, "dump memory", "usage: dm <address (hex)> <num words> <word size (1/4)>"),
#endif
#if WM_CMD == 1
GEN_COMMTABLE_ENTRY(wm, "write to memory (32 bits)", "usage: wm <address (hex)> <words..>"),
GEN_COMMTABLE_ENTRY(wm2, "write to memory (16 bits)", "usage: wm <address (hex)> <half words..>"),
GEN_COMMTABLE_ENTRY(wm1, "write to memory (8 bits)", "usage: wm <address (hex)> <bytes..>"),
#endif
#if DRW_CMDS == 1
GEN_COMMTABLE_ENTRY(dw, "digitalWrite to pin", "usage: dw <pin> <value>"),
GEN_COMMTABLE_ENTRY(dr, "digitalRead from pin", "usage: dr <pin>"),
#endif
#if ARW_CMDS == 1
GEN_COMMTABLE_ENTRY(aw, "analogWrite to pin", "usage: aw <pin> <value>"),
GEN_COMMTABLE_ENTRY(ar, "analogRead from pin", "usage: ar <pin>"),
#endif
#if PRI_CMD == 1
GEN_COMMTABLE_ENTRY(pri, "Set task priority", "usage: pri <task handle> <priority>"),
#endif
#if SPI_CMD == 1
GEN_COMMTABLE_ENTRY(spi, "SPI transfer", "usage: spi <cs pin> <data>"),
#endif
#if STATS_CMD == 1
GEN_COMMTABLE_ENTRY(stats, "Print CPU utlization info", "usage: stats"),
#endif
GEN_COMMTABLE_ENTRY(help, "Get information on commands. Usage: help [command]", NULL),
{NULL,NULL,NULL,NULL} // Indicates end of table
};
void mon_setup(void)
{
Serial.begin(9600);
Serial.println("Welcome! This is the Serial debug console.");
}
#define UP_ARROW 0x0b /* ctrl K */
#define DOWN_ARROW 0x0C /* ctrl L */
void mon_loop()
{
// each loop iteration is one command
Serial.print("> ");
// ------------------------------------- read line
static char line[MAX_COMMAND_LINES][MAX_COMMAND_LEN];
static int line_num = 0;
int char_index = 0;
bool line_end = false;
int escape_index = 0;
while (true) {
if(!Serial.available()) continue;
char c = Serial.read();
if (escape_index) {
if (++escape_index == 3) {
escape_index = 0;
switch(c) {
case 'A':
c = UP_ARROW;
break;
case 'B':
c = DOWN_ARROW;
break;
default:
continue;
}
}
else {
continue;
}
}
switch (c) {
case 0x1b: /* escape */
escape_index = 1;
continue;
case UP_ARROW:
if (--line_num < 0) {
line_num = MAX_COMMAND_LINES - 1;
}
Serial.print("\r \r> ");
char_index = strlen(line[line_num]);
Serial.print(line[line_num]);
continue;
case DOWN_ARROW:
if (++line_num == MAX_COMMAND_LINES) {
line_num = 0;
}
Serial.print("\r \r> ");
char_index = strlen(line[line_num]);
Serial.print(line[line_num]);
continue;
case '\r':
Serial.println("");
if (char_index == 0) {
Serial.print("> ");
continue;
}
else {
line_end = true;
break;
}
case 0x08:
case 0x7f: /* Backspace */
if (char_index >= 1) {
char_index -= 1;
Serial.print("\b \b");
}
continue;
case 3: /* control 'c' */
Serial.println("^C");
return;
}
if (line_end == false) {
line[line_num][char_index++] = c;
if (char_index == MAX_COMMAND_LEN) {
// The user typed something too long; abort so they don't get surprise commands running
Serial.println("\r\nCommand too long.");
return;
}
// default for Serial is ECHO_OFF, and there doesn't seem to be a way to change that
Serial.print(c);
}
else {
line[line_num][char_index] = 0;
break;
}
}
// ------------------------------------- process line
sanitizeLine(line[line_num]);
char cmdstr[MAX_COMMAND_NAME_LEN+1];
memset(cmdstr, 0, sizeof(cmdstr));
int i;
for (i = 0; i < MAX_COMMAND_NAME_LEN && line[line_num][i] && line[line_num][i] != ' '; i++) {
cmdstr[i] = line[line_num][i];
}
// ignore empty command
if (!*cmdstr) {
return;
}
int commandIndex = -1;
for (i=0; _consoleCommandTable[i].name; i++) {
if (!strncmp(cmdstr,_consoleCommandTable[i].name,MAX_COMMAND_NAME_LEN)) {
commandIndex = i;
break;
}
}
if (commandIndex == -1) {
Serial.print("The command `");
Serial.print(cmdstr);
Serial.println("' was not recognized.");
return;
}
if (!_consoleCommandTable[commandIndex].handler) {
Serial.println("That command has not yet been implemented.");
return;
}
int returnVal = _consoleCommandTable[commandIndex].handler(line[line_num]
+strlen(_consoleCommandTable[commandIndex].name));
if (++line_num == MAX_COMMAND_LINES) {
line_num = 0;
}
switch (returnVal) {
case RETURN_SUCCESS: break;
case RETURN_FAIL: break;
case RETURN_FAIL_PRINT_USAGE: {
const char*toPrint = _consoleCommandTable[commandIndex].detailedUsage
? _consoleCommandTable[commandIndex].detailedUsage
: _consoleCommandTable[commandIndex].description;
Serial.println(toPrint);
break;
}
default:
Serial.println("Warning: unknown return value!");
break;
}
}
static void sanitizeLine(char *line)
{
int len = strlen(line);
char buf[MAX_COMMAND_LEN];
strncpy(buf, line, len); // copy original line to buf (which is our source)
memset(line, 0, len); // zero out provided line (which is our destination)
int bufIndex = 0;
int lineIndex = 0;
for (; bufIndex < len && lineIndex < len; bufIndex++) {
char c = buf[bufIndex];
bool copy = ('A'<=c && c<='Z') ||
('a'<=c && c<='z') ||
('0'<=c && c<='9') ||
c=='_' || c==' ' ||
c=='.' || c==',' ||
c==';' || c==':' ||
c=='*' || c=='-';
if(copy) {
line[lineIndex++] = c;
}
}
}
static int consoleHandler_help(const char *line){
int i;
if (!*line) {
// No command in particular specified; just print them all
Serial.println("Available commands:");
for (i=0; _consoleCommandTable[i].name; i++) {
if(!_consoleCommandTable[i].handler) // if NYI, don't list
continue;
Serial.print(" ");
Serial.print(_consoleCommandTable[i].name);
Serial.print("\t ");
Serial.println(_consoleCommandTable[i].description);
}
} else {
// get past the space so we can parse the command
while (*line && *line==' ') {
line++;
}
char cmdstr[MAX_COMMAND_NAME_LEN+1];
memset(cmdstr,0,sizeof(cmdstr));
for (i=0; i<MAX_COMMAND_NAME_LEN && line[i] && line[i] != ' '; i++) {
cmdstr[i] = line[i];
}
int commandIndex = -1;
for (i=0; _consoleCommandTable[i].name; i++) {
if (!strncmp(cmdstr,_consoleCommandTable[i].name,MAX_COMMAND_NAME_LEN)) {
commandIndex = i;
break;
}
}
if (commandIndex == -1) {
Serial.print("The command `");
Serial.print(cmdstr);
Serial.println("' was not recognized.");
return RETURN_FAIL;
}
const char*toPrint = _consoleCommandTable[i].detailedUsage
? _consoleCommandTable[i].detailedUsage
: _consoleCommandTable[i].description;
Serial.print(cmdstr);
Serial.print(": ");
Serial.println(toPrint);
}
return RETURN_SUCCESS;
}
static void doRepeat(RepeatFunc func, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3)
{
Serial.print(clear);
while(!Serial.available()) {
Serial.print(home);
func(arg0, arg1, arg2, arg3);
}
Serial.read(); /* remove char from input buf */
}
#if DRW_CMDS == 1
static void doDr(uint32_t pin)
{
Serial.print("digitalRead(");
Serial.print(pin);
Serial.print(") = ");
Serial.println(digitalRead(pin));
}
static int consoleHandler_dr(const char *line)
{
if (*line++ != ' ') {
return RETURN_FAIL_PRINT_USAGE;
}
char *endptr = NULL;
uint32_t pin = strtol(line, &endptr, 10);
if (*endptr == ' ') {
doRepeat((RepeatFunc)doDr, pin, 0, 0, 0);
}
else {
doDr(pin);
}
return RETURN_SUCCESS;
}
static int consoleHandler_dw(const char *line)
{
if (*line++ != ' ') {
return RETURN_FAIL_PRINT_USAGE;
}
char *endptr = NULL;
uint32_t pin = strtoul(line, &endptr, 10);
int32_t val = strtol(endptr, NULL, 0);
Serial.print("Calling digitalWrite(");
Serial.print(pin);
Serial.print(", ");
Serial.print(val);
Serial.println(").");
digitalWrite(pin, val);
return RETURN_SUCCESS;
}
#endif /* DRW_CMDS */
#if ARW_CMDS == 1
static void doAr(uint32_t pin)
{
Serial.print("analogRead(");
Serial.print(pin);
Serial.print(") = ");
Serial.println(analogRead(pin));
}
static int consoleHandler_ar(const char *line)
{
if (*line++ != ' ') {
return RETURN_FAIL_PRINT_USAGE;
}
char *endptr = NULL;
uint32_t pin = strtol(line, &endptr, 10);
if (*endptr == ' ') {
doRepeat((RepeatFunc)doAr, pin, 0, 0, 0);
}
else {
doAr(pin);
}
return RETURN_SUCCESS;
}
static int consoleHandler_aw(const char *line)
{
if (*line++ != ' ') {
return RETURN_FAIL_PRINT_USAGE;
}
char *endptr = NULL;
uint32_t pin = strtol(line, &endptr, 10);
int32_t val = strtol(endptr, NULL, 0);
Serial.print("Calling analogWrite(");
Serial.print(pin);
Serial.print(", ");
Serial.print(val);
Serial.println(").");
analogWrite(pin,val);
return RETURN_SUCCESS;
}
#endif /* ARW_CMDS */
#if DM_CMD == 1
static void dumpMemory(uint32_t *address, uint32_t len, uint32_t size)
{
static char response[80];
uint8_t *base_addr = (uint8_t*)address;
int i;
if (size == 1) {
for (i=0; i < len; i+=16) {
uint8_t *addr = base_addr+i;
// for printing literal values
uint8_t asciiBytes[16];
int j;
for (j=0; j<16; j++) {
asciiBytes[j] = (addr[j] > 31 && addr[j] < 127) ? addr[j] : '.';
}
System_snprintf(response,sizeof(response),
"0x%x | %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x | %.16s",
(int)addr,
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15],
asciiBytes);
Serial.println(response);
}
}
if (size == 2) {
for (i=0; i < len; i+=8) {
uint16_t *addr = (uint16_t *)base_addr+i;
uint8_t *baddr = (uint8_t *)addr;
// for printing literal values
uint8_t asciiBytes[16];
int j;
for (j=0; j<16; j++) {
asciiBytes[j] = (baddr[j] > 31 && baddr[j] < 127) ? baddr[j] : '.';
}
System_snprintf(response,sizeof(response),
"0x%x | %04x %04x %04x %04x %04x %04x %04x %04x | %.16s",
(int)addr,
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
asciiBytes);
Serial.println(response);
}
}
if (size == 4) {
for (i=0; i < len; i+=4) {
uint32_t *addr = (uint32_t *)base_addr+i;
uint8_t *baddr = (uint8_t *)addr;
// for printing literal values
uint8_t asciiBytes[16];
int j;
for (j=0; j<16; j++) {
asciiBytes[j] = (baddr[j] > 31 && baddr[j] < 127) ? baddr[j] : '.';
}
System_snprintf(response,sizeof(response),
"0x%x | %08x %08x %08x %08x | %.16s",
(int)addr,
addr[0], addr[1], addr[2], addr[3],
asciiBytes);
Serial.println(response);
}
}
}
static int consoleHandler_dm(const char *line)
{
static uint32_t address = 0x00000000;
static uint32_t len = 0x10;
static uint32_t size = 4;
static bool repeat = false;
char *endptr;
if (*line == ' ') {
address = strtoul(line, &endptr, 16);
repeat = false;
if (*endptr == ' ') {
len = strtoul(endptr, &endptr, 0);
if (*endptr == ' ') {
size = strtoul(endptr, &endptr, 10);
if ((size !=1) && (size != 2) && (size != 4)) {
size = 4;
}
if (*endptr == ' ') {
repeat = true;
}
}
}
}
if (len <= 0) {
Serial.println("Bad number of bytes to dump.");
return RETURN_FAIL_PRINT_USAGE;
}
if (repeat) {
doRepeat((RepeatFunc)dumpMemory, address, len, size, 0);
}
else {
dumpMemory((uint32_t *)address, len, size);
}
return RETURN_SUCCESS;
}
#endif /* DM_CMD */
#if WM_CMD == 1
static int consoleHandler_wm(const char *line)
{
char *endptr;
uint32_t word = 0;
uint32_t *ptr;
if (*line != ' ') {
return RETURN_FAIL_PRINT_USAGE;
}
ptr = (uint32_t *)strtoul(line, &endptr, 16);
if (*endptr != ' ') {
return RETURN_FAIL_PRINT_USAGE;
}
while (*endptr) {
word = strtol(endptr , &endptr, 16);
*ptr++ = word;
}
return RETURN_SUCCESS;
}
static int consoleHandler_wm1(const char *line)
{
char *endptr;
uint8_t word = 0;
uint8_t *ptr;
if (*line != ' ') {
return RETURN_FAIL_PRINT_USAGE;
}
ptr = (uint8_t *)strtoul(line, &endptr, 16);
if (*endptr != ' ') {
return RETURN_FAIL_PRINT_USAGE;
}
while (*endptr) {
word = strtol(endptr , &endptr, 16);
*ptr++ = word;
}
return RETURN_SUCCESS;
}
static int consoleHandler_wm2(const char *line)
{
char *endptr;
uint16_t word = 0;
uint16_t *ptr;
if (*line != ' ') {
return RETURN_FAIL_PRINT_USAGE;
}
ptr = (uint16_t *)strtoul(line, &endptr, 16);
if (*endptr != ' ') {
return RETURN_FAIL_PRINT_USAGE;
}
while (*endptr) {
word = strtol(endptr , &endptr, 16);
*ptr++ = word;
}
return RETURN_SUCCESS;
}
#endif /* WM_CMD */
#if SPI_CMD == 1
static int consoleHandler_spi(const char *line)
{
static bool spiBegun = false;
if (*line++ != ' ') {
return RETURN_FAIL_PRINT_USAGE;
}
char *endptr = NULL;
uint32_t data;
uint32_t cs = 0;
if (spiBegun == false) {
SPI.begin();
spiBegun = true;
}
data = strtol(line, &endptr, 10);
if (*endptr == ' ') {
cs = data;
data = strtol(endptr, &endptr, 10);
}
Serial.print("Calling SPI.transfer(");
if (cs) {
Serial.print(cs);
Serial.print(", ");
}
Serial.print(data);
Serial.println(").");
if (cs) {
SPI.transfer(cs, data);
}
else {
SPI.transfer(data);
}
return RETURN_SUCCESS;
}
#endif /* SPI_CMD */
#if STATS_CMD == 1
static char *getModeStr(xdc_UInt mode)
{
switch (mode) {
case Task_Mode_RUNNING:
return((char *)"RUNNING");
case Task_Mode_READY:
return((char *)"READY");
case Task_Mode_BLOCKED:
return((char *)"BLOCKED");
case Task_Mode_TERMINATED:
return((char *)"TERMINATED");
case Task_Mode_INACTIVE:
return((char *)"INACTIVE");
}
return(NULL);
}
static void printTaskInfo(Task_Handle task)
{
Task_Stat taskStat;
Load_Stat loadStat;
xdc_String name;
static char buf[100];
uint32_t loadInt, loadFrac;
Task_stat(task, &taskStat);
Load_getTaskLoad(task, &loadStat);
if (taskStat.priority == 0) {
name = (xdc_String)"Idle";
}
else {
name = Task_Handle_name(task);
if (name[0] == '{') {
name = (xdc_String)"Unnamed";
}
}
/* only show 1 decimal place of precision */
/* System_snprintf() does not support %2.1f */
loadInt = 100.0*(float)loadStat.threadTime/(float)loadStat.totalTime;
loadFrac = 1000.0*(float)loadStat.threadTime/(float)loadStat.totalTime - 10.0*loadInt;
/* use System_snprintf() because it uses 500 fewer bytes of stack than snprintf() */
System_snprintf(buf, sizeof(buf),
" task: %s/0x%x, pri: %d, stack usage: %d/%d, mode: %s load: %d.%1u",
name, task, taskStat.priority, taskStat.used, taskStat.stackSize, getModeStr(taskStat.mode),
loadInt, loadFrac);
Serial.println(buf);
}
static void printUtilization()
{
xdc_UInt i;
Memory_Stats memStat;
Hwi_StackInfo hwiStackStat;
Load_Stat loadStat;
Task_Handle tsk;
float idleLoad;
uint32_t idleLoadInt, idleLoadFrac;
/* collect current stats */
Load_update();
/* use time NOT spent in idle task for Total CPU Load */
Load_getTaskLoad(Task_getIdleTask(), &loadStat);
idleLoad = 100.0 - 100.0*(float)loadStat.threadTime/(float)loadStat.totalTime;
idleLoadInt = idleLoad;
idleLoadFrac = 10.0*idleLoad - 10.0*idleLoadInt;
Serial.write("Total CPU Load: ");
Serial.print(idleLoadInt);
Serial.print(".");
Serial.println(idleLoadFrac);
Serial.println("");
/* collect stats on all statically Created tasks */
Serial.println("Task info:");
for (i = 0; i < Task_Object_count(); i++) {
tsk = Task_Object_get(NULL, i);
printTaskInfo(tsk);
}
/* collect stats on all dynamically Created tasks */
tsk = Task_Object_first();
while (tsk) {
printTaskInfo(tsk);
tsk = Task_Object_next(tsk);
}
Serial.println("");
Hwi_getStackInfo(&hwiStackStat, TRUE);
Serial.print("Hwi stack usage: ");
Serial.print(hwiStackStat.hwiStackPeak);
Serial.print("/");
Serial.println(hwiStackStat.hwiStackSize);
Serial.println("");
Memory_getStats(NULL, &memStat);
Serial.print("Heap usage: ");
Serial.print(memStat.totalSize - memStat.totalFreeSize);
Serial.print("/");
Serial.println(memStat.totalSize);
}
static int consoleHandler_stats(const char *line)
{
if (*line == ' ') {
doRepeat((RepeatFunc)printUtilization, 0, 0, 0, 0);
}
else {
printUtilization();
}
return RETURN_SUCCESS;
}
#endif /* STATS_CMD */
#if PRI_CMD == 1
static int consoleHandler_pri(const char *line)
{
Task_Handle tsk;
char *endptr;
int pri = 3;
if (*line != ' ') {
return RETURN_FAIL_PRINT_USAGE;
}
tsk = (Task_Handle)strtoul(line, &endptr, 16);
pri = strtol(endptr, NULL, 10);
if ((pri < -1) || (pri >= (int)Task_numPriorities)) {
Serial.println("Invalid priority!");
return RETURN_FAIL_PRINT_USAGE;
}
Task_setPri(tsk, pri);
return RETURN_SUCCESS;
}
#endif /* PRI_CMD */