Source code generator for Command Line Interpreter (CLI)

Microcontroller with serial bus or USB CDC (virtual COM port) is usually connected to some terminal. User then type commands and firmware in the microcontroller interprets entered commands.

Developing simple interpreter is not very scientific task, more like PITA with repeating chunks of the code. After couple of successful projects with such interpreter I can say my code is somehow tested and proven in the practice. Now I prepared one application which can shorten development process and make my life easier when I start application in new device.

The source code generator generates only command line interpreter. It provides all identifiers for selected commands, provides function prototypes for each command and text template for help. Finally only the functions for executing specific commands should be then implemented.

The final application can be either multi-threaded with RTOS, or flat single thread. In both cases, there should be some function or ISR checking for new char and feeding this char to the command line editor. When new line “arrives”, the function cmd_proc() is called and then the magic happens.

cli1

General structure of the caller thread or application

Click here for the application for generating CLI source code<<<<   —-    DOWNLOAD

Application requires maximum length of the command (including optional parameters) and list of commands. The commands are listed in the text box on the left:

cli2

When all commands are listed, hit the button “Generate code” and check the source code in the right text box. Button “Copy all” put the whole text in clipboard.

 

Here is example of using the code…

In main part of the microcontroller application (function main() or one thread in RTOS), put something like this:

int main(void)
{
  /* MCU Configuration----------------------------------------------------------*/
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
///////// further initializations....
 
 
 
  /* Infinite loop */
  while (1)
  {
      if (is_line_received())
        {
//     Process received line
            cmd_proc(get_line_buffer());
        }  // if
  }  // while
}

 

Then provide three functions:

  1. process_rx_char(char rx_char)

    —   for command line editor and another

  2. unsigned char is_line_received(void)

    —   for indicating new line received

  3. char *get_line_buffer(void)

    —   which only returns pointer to the newly received line

Example source code:

/**
 * Command line edit chars
 */
#define CNTLQ      0x11
#define CNTLS      0x13
#define DEL        0x7F
#define BACKSPACE  0x08
#define CR         0x0D
#define LF         0x0A
 
static unsigned char line_flag; 			// Flag to indicate new received line
char line_buf[256];
int line_idx = 0;
extern void abort_autorun(void);
 
/**
 * Process received char, check if LF or CR received
 * Set flag when line is done
 */
void process_rx_char(char rx_char)
{
		abort_autorun();
 
    if (rx_char == CR)  rx_char = LF;   
    if (rx_char == BACKSPACE  ||  rx_char == DEL) 
    {    // process backspace
      	if (line_idx != 0)  
      	{            
        	line_idx--;                      // decrement index
        	#ifdef LOCALECHO
        		putchar (BACKSPACE);               // echo backspace
        		putchar (' ');
        		putchar (BACKSPACE);
        	#endif
      	}
    }
    else 
    {
      	#ifdef LOCALECHO
      		putchar (rx_char);                   // echo 
      	#endif 
      	line_buf[line_idx++] = rx_char; 	   // store character and increment index
    }
 
    // check limit and end line feed
  	if ((line_idx == 0xff)  ||  (rx_char == LF))
  	{
  		line_buf[line_idx-1] = 0;                  // mark end of string
  		line_idx = 0;
			line_flag = 1;
  	}
}
 
/**
 * Indicate new line received via UART. 
 * Return 1 when new line received. Status flag is cleared to 0.
 * Return 0 if new line is not received yet
 */
unsigned char is_line_received(void)
{
	if (line_flag == 1)
	{
		line_flag = 0;
		return 1;
	}
	else
	{
		return 0;
	}
}
 
char *get_line_buffer(void)
{
	return line_buf;
}

 

The commands in the autogenerated code are at the end of the source code:

}/**
   * CMD_COMMAND1
   * @brief 
   * @param Arguments string from AUD command
   * @param None
   * @retval None
   */
void cmd_COMMAND1(char *argstr_buf)
 
{
 
  // add code here --------
 
 
 
}

 

 

 

 

Leave a Reply