With this it's possible to change parameters in the running program without flashing the MCU.
Source:
#define CLI_MAX_ARGS 8 // Max arguments
#define CLI_MAX_LENGTH 8 // Max characters per argument
#define CLI_MAX_LENGTH_PAD CLI_MAX_LENGTH+1
#define CLI_ERROR_MAX_ARGS -1
#define CLI_ERROR_MAX_LENGTH -2
struct CLI_Parser_Context{
uint8_t currentArg;
uint8_t currentMode;
uint8_t currentSize;
char args[CLI_MAX_ARGS][CLI_MAX_LENGTH_PAD];
};
void CLI_Parser_Clear(struct CLI_Parser_Context * ctx){
ctx->currentArg = 0;
ctx->currentMode = 0;
ctx->currentSize = 0;
for(uint8_t i = 0;i < CLI_MAX_ARGS;i++)
memset(ctx->args[i],0,CLI_MAX_LENGTH_PAD);
}
int8_t CLI_Parse_Character(struct CLI_Parser_Context * ctx, char ch)
{
if(ctx->currentArg >= CLI_MAX_ARGS)
return CLI_ERROR_MAX_ARGS;
if(ch == ' ' || ch == '\t'){
if(ctx->currentMode == 0){
ctx->currentArg++;
ctx->currentSize = 0;
ctx->currentMode = 1;
}
}
else{
if(ctx->currentSize >= CLI_MAX_LENGTH)
return CLI_ERROR_MAX_LENGTH;
ctx->args[ctx->currentArg][ctx->currentSize++] = ch;
ctx->args[ctx->currentArg][ctx->currentSize] = '\0';
ctx->currentMode = 0;
}
return ctx->currentArg;
}
Sample:
#define F_CPU 16000000
#define BAUD 9600
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#define USART_RX_SIZE 64 // Receive queue max size
#define USART_TX_SIZE 64 // Transmit queue max size
#define USART_RX_MASK ( USART_RX_SIZE - 1 )
#define USART_TX_MASK ( USART_TX_SIZE - 1 )
static uint8_t USART_RxBuf [USART_RX_SIZE];
static uint8_t USART_TxBuf [USART_TX_SIZE];
int USART_Tx_File(char ch, FILE *stream);
static FILE USART_StdOut = FDEV_SETUP_STREAM(USART_Tx_File, NULL, _FDEV_SETUP_WRITE);
static volatile uint8_t USART_RxHead;
static volatile uint8_t USART_RxTail;
static volatile uint8_t USART_TxHead;
static volatile uint8_t USART_TxTail;
void (*USART_RxCallback)(uint8_t ch) = NULL;
void USART_Init(long baudrate){
long baud_setting = (F_CPU / 8 / baudrate - 1) / 2;
memset(USART_RxBuf,0,USART_RX_SIZE);
memset(USART_TxBuf,0,USART_TX_SIZE);
// Set STDOUT to custom file stream.
stdout = &USART_StdOut;
UBRRH = (baud_setting >> 8);
UBRRL = baud_setting;
// Don't use U2X
UCSRA &= ~(1 << U2X);
// Enable TX(TXEN), Enable RX(RXEN), Enable RX Interrupt(RXCIE)
UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE);
// Asyncronous, Write to UCSRC(URSEL), 8 bit
UCSRC = (1<<URSEL) | (3<<UCSZ0);
USART_RxTail = 0;
USART_RxHead = 0;
USART_TxTail = 0;
USART_TxHead = 0;
}
void USART_Tx(uint8_t data){
uint8_t tmphead;
tmphead = ( USART_TxHead + 1 ) & USART_TX_MASK;
// Wait while full
while ( tmphead == USART_TxTail ){};
USART_TxBuf[tmphead] = data;
USART_TxHead = tmphead;
// If the UART ain't busy sending bytes, send the first byte.
if((UCSRB & (1<<TXCIE)) != (1<<TXCIE)){
// Enable Tx interrupt for the following bytes
UCSRB |= (1<<TXCIE);
// Emulate the TX interruption for the first character
uint8_t tmptail = ( USART_TxTail + 1 ) & USART_TX_MASK;
USART_TxTail = tmptail;
UDR = USART_TxBuf[tmptail];
}
}
int USART_Tx_File(char ch, FILE *stream){
USART_Tx(ch);
return 0;
}
uint8_t USART_RxByte(void){
uint8_t tmptail;
while ( USART_RxHead == USART_RxTail ){};
tmptail = ( USART_RxTail + 1 ) & USART_RX_MASK;
USART_RxTail = tmptail;
return USART_RxBuf[tmptail];
}
ISR(USART_RXC_vect){
uint8_t tmphead;
uint8_t data = UDR;
tmphead = ( USART_RxHead + 1 ) & USART_RX_MASK;
USART_RxHead = tmphead;
if ( tmphead != USART_RxTail )
USART_RxBuf[tmphead] = data;
if( USART_RxCallback != NULL )
USART_RxCallback(data);
}
ISR(USART_TXC_vect){
uint8_t tmptail;
if ( USART_TxHead != USART_TxTail ){
tmptail = ( USART_TxTail + 1 ) & USART_TX_MASK;
USART_TxTail = tmptail;
UDR = USART_TxBuf[tmptail];
}
else{
// Empty queue, disable interrupt.
UCSRB &= ~(1<<TXCIE);
}
}
#define CLI_MAX_ARGS 8 // Max arguments
#define CLI_MAX_LENGTH 8 // Max characters per argument
#define CLI_MAX_LENGTH_PAD CLI_MAX_LENGTH+1
#define CLI_ERROR_MAX_ARGS -1
#define CLI_ERROR_MAX_LENGTH -2
struct CLI_Parser_Context{
uint8_t currentArg;
uint8_t currentMode;
uint8_t currentSize;
char args[CLI_MAX_ARGS][CLI_MAX_LENGTH_PAD];
};
void CLI_Parser_Clear(struct CLI_Parser_Context * ctx){
ctx->currentArg = 0;
ctx->currentMode = 0;
ctx->currentSize = 0;
for(uint8_t i = 0;i < CLI_MAX_ARGS;i++)
memset(ctx->args[i],0,CLI_MAX_LENGTH_PAD);
}
int8_t CLI_Parse_Character(struct CLI_Parser_Context * ctx, char ch)
{
if(ctx->currentArg >= CLI_MAX_ARGS)
return CLI_ERROR_MAX_ARGS;
if(ch == ' ' || ch == '\t'){
if(ctx->currentMode == 0){
ctx->currentArg++;
ctx->currentSize = 0;
ctx->currentMode = 1;
}
}
else{
if(ctx->currentSize >= CLI_MAX_LENGTH)
return CLI_ERROR_MAX_LENGTH;
ctx->args[ctx->currentArg][ctx->currentSize++] = ch;
ctx->args[ctx->currentArg][ctx->currentSize] = '\0';
ctx->currentMode = 0;
}
return ctx->currentArg;
}
struct CLI_Parser_Context cli_context;
void cli_led(uint8_t argc, char argv[][CLI_MAX_LENGTH_PAD]);
void InputCallback(uint8_t ch){
if(ch == '\r'){
USART_Tx('\n');
USART_Tx('\r');
if(strcmp(cli_context.args[0],"led") == 0){
cli_led(cli_context.currentArg+1,cli_context.args);
}
CLI_Parser_Clear(&cli_context);
}
else{
int8_t flag = CLI_Parse_Character(&cli_context,ch);
if(flag < 0){
printf("CLI_Parse Error : %d\n\r",flag);
CLI_Parser_Clear(&cli_context);
}
else
USART_Tx(ch);
}
}
void cli_led(uint8_t argc, char argv[][CLI_MAX_LENGTH_PAD]){
if(argc == 2){
if(strcmp(argv[1],"on") == 0){
PORTB |= (1 << PB1);
printf("Led is ON\n\r");
}
else if(strcmp(argv[1],"off") == 0){
PORTB &= ~(1 << PB1);
printf("Led is OFF\n\r");
}
}
}
int main(void)
{
CLI_Parser_Clear(&cli_context);
USART_RxCallback = InputCallback;
USART_Init(BAUD);
DDRB |= (1<<PB1);
sei();
printf("CLI Led Switch, Build %s at %s\n\r",__DATE__,__TIME__);
while(1){}
return 0;
}
#define BAUD 9600
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#define USART_RX_SIZE 64 // Receive queue max size
#define USART_TX_SIZE 64 // Transmit queue max size
#define USART_RX_MASK ( USART_RX_SIZE - 1 )
#define USART_TX_MASK ( USART_TX_SIZE - 1 )
static uint8_t USART_RxBuf [USART_RX_SIZE];
static uint8_t USART_TxBuf [USART_TX_SIZE];
int USART_Tx_File(char ch, FILE *stream);
static FILE USART_StdOut = FDEV_SETUP_STREAM(USART_Tx_File, NULL, _FDEV_SETUP_WRITE);
static volatile uint8_t USART_RxHead;
static volatile uint8_t USART_RxTail;
static volatile uint8_t USART_TxHead;
static volatile uint8_t USART_TxTail;
void (*USART_RxCallback)(uint8_t ch) = NULL;
void USART_Init(long baudrate){
long baud_setting = (F_CPU / 8 / baudrate - 1) / 2;
memset(USART_RxBuf,0,USART_RX_SIZE);
memset(USART_TxBuf,0,USART_TX_SIZE);
// Set STDOUT to custom file stream.
stdout = &USART_StdOut;
UBRRH = (baud_setting >> 8);
UBRRL = baud_setting;
// Don't use U2X
UCSRA &= ~(1 << U2X);
// Enable TX(TXEN), Enable RX(RXEN), Enable RX Interrupt(RXCIE)
UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE);
// Asyncronous, Write to UCSRC(URSEL), 8 bit
UCSRC = (1<<URSEL) | (3<<UCSZ0);
USART_RxTail = 0;
USART_RxHead = 0;
USART_TxTail = 0;
USART_TxHead = 0;
}
void USART_Tx(uint8_t data){
uint8_t tmphead;
tmphead = ( USART_TxHead + 1 ) & USART_TX_MASK;
// Wait while full
while ( tmphead == USART_TxTail ){};
USART_TxBuf[tmphead] = data;
USART_TxHead = tmphead;
// If the UART ain't busy sending bytes, send the first byte.
if((UCSRB & (1<<TXCIE)) != (1<<TXCIE)){
// Enable Tx interrupt for the following bytes
UCSRB |= (1<<TXCIE);
// Emulate the TX interruption for the first character
uint8_t tmptail = ( USART_TxTail + 1 ) & USART_TX_MASK;
USART_TxTail = tmptail;
UDR = USART_TxBuf[tmptail];
}
}
int USART_Tx_File(char ch, FILE *stream){
USART_Tx(ch);
return 0;
}
uint8_t USART_RxByte(void){
uint8_t tmptail;
while ( USART_RxHead == USART_RxTail ){};
tmptail = ( USART_RxTail + 1 ) & USART_RX_MASK;
USART_RxTail = tmptail;
return USART_RxBuf[tmptail];
}
ISR(USART_RXC_vect){
uint8_t tmphead;
uint8_t data = UDR;
tmphead = ( USART_RxHead + 1 ) & USART_RX_MASK;
USART_RxHead = tmphead;
if ( tmphead != USART_RxTail )
USART_RxBuf[tmphead] = data;
if( USART_RxCallback != NULL )
USART_RxCallback(data);
}
ISR(USART_TXC_vect){
uint8_t tmptail;
if ( USART_TxHead != USART_TxTail ){
tmptail = ( USART_TxTail + 1 ) & USART_TX_MASK;
USART_TxTail = tmptail;
UDR = USART_TxBuf[tmptail];
}
else{
// Empty queue, disable interrupt.
UCSRB &= ~(1<<TXCIE);
}
}
#define CLI_MAX_ARGS 8 // Max arguments
#define CLI_MAX_LENGTH 8 // Max characters per argument
#define CLI_MAX_LENGTH_PAD CLI_MAX_LENGTH+1
#define CLI_ERROR_MAX_ARGS -1
#define CLI_ERROR_MAX_LENGTH -2
struct CLI_Parser_Context{
uint8_t currentArg;
uint8_t currentMode;
uint8_t currentSize;
char args[CLI_MAX_ARGS][CLI_MAX_LENGTH_PAD];
};
void CLI_Parser_Clear(struct CLI_Parser_Context * ctx){
ctx->currentArg = 0;
ctx->currentMode = 0;
ctx->currentSize = 0;
for(uint8_t i = 0;i < CLI_MAX_ARGS;i++)
memset(ctx->args[i],0,CLI_MAX_LENGTH_PAD);
}
int8_t CLI_Parse_Character(struct CLI_Parser_Context * ctx, char ch)
{
if(ctx->currentArg >= CLI_MAX_ARGS)
return CLI_ERROR_MAX_ARGS;
if(ch == ' ' || ch == '\t'){
if(ctx->currentMode == 0){
ctx->currentArg++;
ctx->currentSize = 0;
ctx->currentMode = 1;
}
}
else{
if(ctx->currentSize >= CLI_MAX_LENGTH)
return CLI_ERROR_MAX_LENGTH;
ctx->args[ctx->currentArg][ctx->currentSize++] = ch;
ctx->args[ctx->currentArg][ctx->currentSize] = '\0';
ctx->currentMode = 0;
}
return ctx->currentArg;
}
struct CLI_Parser_Context cli_context;
void cli_led(uint8_t argc, char argv[][CLI_MAX_LENGTH_PAD]);
void InputCallback(uint8_t ch){
if(ch == '\r'){
USART_Tx('\n');
USART_Tx('\r');
if(strcmp(cli_context.args[0],"led") == 0){
cli_led(cli_context.currentArg+1,cli_context.args);
}
CLI_Parser_Clear(&cli_context);
}
else{
int8_t flag = CLI_Parse_Character(&cli_context,ch);
if(flag < 0){
printf("CLI_Parse Error : %d\n\r",flag);
CLI_Parser_Clear(&cli_context);
}
else
USART_Tx(ch);
}
}
void cli_led(uint8_t argc, char argv[][CLI_MAX_LENGTH_PAD]){
if(argc == 2){
if(strcmp(argv[1],"on") == 0){
PORTB |= (1 << PB1);
printf("Led is ON\n\r");
}
else if(strcmp(argv[1],"off") == 0){
PORTB &= ~(1 << PB1);
printf("Led is OFF\n\r");
}
}
}
int main(void)
{
CLI_Parser_Clear(&cli_context);
USART_RxCallback = InputCallback;
USART_Init(BAUD);
DDRB |= (1<<PB1);
sei();
printf("CLI Led Switch, Build %s at %s\n\r",__DATE__,__TIME__);
while(1){}
return 0;
}
Usage:
1) Declare parser context and init/clear:
struct CLI_Parser_Context cli_context;
CLI_Parser_Clear(&cli_context);
2) Parse received character:
char rxChar = ...
CLI_Parse_Character(&cli_context, rxChar);
3) On line end character (CR or LF), check the arguments and clear the context:
for(uint8_t i = 0;i<cli_context.currentArg+1;i++)
printf("%s\n\r",cli_context.args[i]);
CLI_Parse_Clear(&cli_context);
Updated: 2014/12/20, changed to avoid using malloc.