Skip to content

Commit

Permalink
CLI for Controller Board via UART Rx (#278)
Browse files Browse the repository at this point in the history
* tmp

* Validated UART Rx - MCU can receive UART data and echo back to terminal

* Command Line Interface for Controller Board via UART Rx

* Updated formatting of UART CLI

* Formatted and linted UART CLI

* Formatted UART smoke file

* Fixed issues with UART CLI resulting from lint

* Fixed build error for UART CLI

* Cleaned up parse functionality

* Improved CLI help message

* Reorganized private functions and added main file

* Improved behaviour when cmd_buffer is filled

* Added comments for other members adding peripherals to cli

* Added comment for NOLINT and other minor changes

* Fixed formatting error
  • Loading branch information
et312 committed May 27, 2024
1 parent 826d7ec commit e94622f
Show file tree
Hide file tree
Showing 12 changed files with 474 additions and 20 deletions.
5 changes: 3 additions & 2 deletions libraries/ms-common/src/arm/uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ StatusCode uart_init(UartPort uart, UartSettings *settings) {

USART_InitTypeDef usart_init;
USART_StructInit(&usart_init);
usart_init.USART_Mode = USART_Mode_Tx;
usart_init.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
usart_init.USART_BaudRate = settings->baudrate;
USART_Init(s_port[uart].base, &usart_init);

Expand All @@ -78,6 +78,7 @@ StatusCode uart_init(UartPort uart, UartSettings *settings) {
// Init with disabled TX interrupts otherwise we will get TX
// buffer empty continuously when there is no data to send
USART_ITConfig(s_port[uart].base, USART_IT_TXE, DISABLE);
USART_ITConfig(s_port[uart].base, USART_IT_RXNE, ENABLE);
stm32f10x_interrupt_nvic_enable(s_port[uart].irq, INTERRUPT_PRIORITY_LOW);

USART_Cmd(s_port[uart].base, ENABLE);
Expand Down Expand Up @@ -152,7 +153,7 @@ static void prv_handle_irq(UartPort uart) {
errQUEUE_FULL) {
// Drop oldest data if queue is full
uint8_t buf = 0;
xQueueReceiveFromISR(s_port_queues[uart].tx_queue.handle, &buf, pdFALSE);
xQueueReceiveFromISR(s_port_queues[uart].rx_queue.handle, &buf, pdFALSE);
xQueueSendFromISR(s_port_queues[uart].rx_queue.handle, &rx_data, pdFALSE);
}
}
Expand Down
4 changes: 2 additions & 2 deletions libraries/ms-common/src/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
char g_log_buffer[MAX_LOG_SIZE];
Mutex s_log_mutex;

UartSettings log_uart_settings = { .tx = { GPIO_PORT_A, TX_PIN }, // tx pin
.rx = { GPIO_PORT_A, RX_PIN }, // rx pin
UartSettings log_uart_settings = { .tx = { GPIO_PORT_B, TX_PIN }, // tx pin
.rx = { GPIO_PORT_B, RX_PIN }, // rx pin
.baudrate = 115200 };
6 changes: 6 additions & 0 deletions projects/uart_cli/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"libs": [
"FreeRTOS",
"ms-common"
]
}
24 changes: 24 additions & 0 deletions projects/uart_cli/inc/cli.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once
#include <stdio.h>

/*
For new peripherals:
- Create a new C and header file titled "*peripheral*_cli.c" and "*peripheral*_cli.h"
- Peripheral files should include a general help message for the peripheral and a help message for
each function
- Refer to gpio_cli.c for help message formatting
- Create private functions within each peripheral file to process parameters after calling
tok_cmd
- Create a CmdStruct lookup table to store functions for the peripheral - this structure maps
the keyword for an action to the corresponding prv function
- Add a CmdStruct object to cmd_lookup for the peripheral
- Add the necessary init function for the peripheral
*/

#include "gpio_cli.h"

void cli_init();
void cli_run();
char *get_cmd();
void cmd_parse(char *cmd);
void print_help();
15 changes: 15 additions & 0 deletions projects/uart_cli/inc/cli_base.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#define MAX_CMD_LEN 50

typedef struct CmdStruct {
const char *cmd_name;
void (*cmd_func)(char *input);
} CmdStruct;

// Separates first token from rest of string
// Token stored in tok_out, remaining string stored in cmd_in
void tok_cmd(char *cmd_in, char *tok_out);

// Strips whitespace before a string
void strip_ws(char *str);
11 changes: 11 additions & 0 deletions projects/uart_cli/inc/gpio_cli.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma once
#include <stdbool.h>

#include "cli_base.h"

void gpio_cmd(char *input);

bool valid_addr(char *port);
bool valid_state(char *state);
int valid_pin_mode(char *pin_mode);
bool state_to_int(char *state);
105 changes: 105 additions & 0 deletions projects/uart_cli/src/cli.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include "cli.h"

#include <stdio.h>

#include "ctype.h"
#include "interrupt.h"
#include "log.h"
#include "string.h"
#include "tasks.h"
#include "uart.h"

static const char cli_help[] =
"MSXV Controller Board CLI. Usage: \n\r"
"<peripheral> <action> <parameters> \n\r\n"
"Enter \"help\" after any argument for detailed reference. \n\r\n"
"List of Peripherals: gpio";

static char cmd_buffer[MAX_CMD_LEN + 1];
// Add additional peripherals to lookup array
static const CmdStruct cmd_lookup[] = { { .cmd_name = "gpio", .cmd_func = &gpio_cmd } };

void cli_init() {
// Add peripheral init calls here
gpio_init();
setbuf(stdout, NULL);
printf("\n\rCLI Launched\n\r");
}

void cli_run() {
printf("\n\r> ");
char *input = get_cmd();
if (input != NULL) {
cmd_parse(input);
}
}

char *get_cmd() {
size_t idx = 0;
memset(cmd_buffer, 0, sizeof(cmd_buffer));

while (true) {
size_t len = 1;
uint8_t data = 0;
StatusCode status = STATUS_CODE_EMPTY;
while (status != STATUS_CODE_OK) {
len = 1;
status = uart_rx(UART_PORT_1, &data, &len);
}

if (idx == MAX_CMD_LEN && (data != '\r' && data != '\b')) {
continue;
}

if (data == '\r') {
if (idx == 0) {
return NULL;
}
printf("\n\r");
return cmd_buffer;
} else if (data == '\b') {
if (idx == 0) {
continue;
}
--idx;
cmd_buffer[idx % MAX_CMD_LEN] = 0;
printf("\b \b");
} else {
cmd_buffer[idx % MAX_CMD_LEN] = data;
++idx;
printf("%c", data);
}
}
}

void cmd_parse(char *cmd) {
for (size_t i = 0; i < MAX_CMD_LEN; ++i) {
cmd[i] = tolower(cmd[i]);
}
char peripheral[MAX_CMD_LEN + 1] = { 0 };
tok_cmd(cmd, peripheral);

if (strcmp(peripheral, "help") == 0 || strcmp(peripheral, "h") == 0) {
print_help();
return;
}

for (size_t i = 0; i < SIZEOF_ARRAY(cmd_lookup); ++i) {
if (strcmp(peripheral, cmd_lookup[i].cmd_name) == 0) {
cmd_lookup[i].cmd_func(cmd);
return;
}
}

// ERROR: Invalid peripheral
printf("Invalid peripheral\n\r");
print_help();
}

void print_help() {
printf("\r%s", cli_help);
for (size_t i = 1; i < SIZEOF_ARRAY(cmd_lookup); ++i) {
printf(", %s", cmd_lookup[i].cmd_name);
}
printf("\n\r");
}
32 changes: 32 additions & 0 deletions projects/uart_cli/src/cli_base.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include "cli_base.h"

#include <stdio.h>
#include <string.h>

void tok_cmd(char *cmd_in, char *tok_out) {
char tmp[MAX_CMD_LEN + 1] = { 0 };
const char delim[2] = " ";
strip_ws(cmd_in);
strncpy(tmp, cmd_in, sizeof(tmp) - 1);
char *tmp_tok;
// Linter rejects strtok and suggests strtok_r, but compiler gives implicit-function-declaration
// error for strtok_r strtok and strtok_r perform the same functionality but strtok_r is
// thread-safe
tmp_tok = strtok(tmp, delim); // NOLINT

if (tmp_tok != NULL) {
snprintf(tok_out, MAX_CMD_LEN + 1, "%s", tmp_tok);
snprintf(cmd_in, MAX_CMD_LEN + 1, "%s", cmd_in + strlen(tmp_tok));
strip_ws(cmd_in);
} else {
tok_out[0] = '\0';
}
}

void strip_ws(char *str) {
char *start = str;
while (*start == ' ') {
start++;
}
memmove(str, start, strlen(start) + 1);
}
Loading

0 comments on commit e94622f

Please sign in to comment.