From 44252b347f632132fc7010e56748199050748774 Mon Sep 17 00:00:00 2001 From: Jordan Aceto Date: Wed, 18 Sep 2024 10:45:47 -0400 Subject: [PATCH] Add override for CLI lib, because of MSDK github issue #1177 https://github.com/analogdevicesinc/msdk/issues/1177 --- .../MSDK_overrides/CLI/CLI.mk | 49 +++ .../MSDK_overrides/CLI/README.md | 81 ++++ .../MSDK_overrides/CLI/inc/cli.h | 97 +++++ .../MSDK_overrides/CLI/libinfo.json | 10 + .../MSDK_overrides/CLI/src/cli.c | 400 ++++++++++++++++++ 5 files changed, 637 insertions(+) create mode 100644 main_microcontroller_firmware/MSDK_overrides/CLI/CLI.mk create mode 100644 main_microcontroller_firmware/MSDK_overrides/CLI/README.md create mode 100644 main_microcontroller_firmware/MSDK_overrides/CLI/inc/cli.h create mode 100644 main_microcontroller_firmware/MSDK_overrides/CLI/libinfo.json create mode 100644 main_microcontroller_firmware/MSDK_overrides/CLI/src/cli.c diff --git a/main_microcontroller_firmware/MSDK_overrides/CLI/CLI.mk b/main_microcontroller_firmware/MSDK_overrides/CLI/CLI.mk new file mode 100644 index 0000000..39d58ab --- /dev/null +++ b/main_microcontroller_firmware/MSDK_overrides/CLI/CLI.mk @@ -0,0 +1,49 @@ +############################################################################### + # + # Copyright (C) 2022-2023 Maxim Integrated Products, Inc. All Rights Reserved. + # (now owned by Analog Devices, Inc.), + # Copyright (C) 2023 Analog Devices, Inc. All Rights Reserved. This software + # is proprietary to Analog Devices, Inc. and its licensors. + # + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + # + ############################################################################## + +################################################################################ +# This file can be included in a project makefile to build the library for the +# project. +############################################################################### + +ifeq "$(LIB_CLI_DIR)" "" +# If CLI_DIR is not specified, this Makefile will locate itself. +LIB_CLI_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +endif + +IPATH += ${LIB_CLI_DIR}/inc +VPATH += ${LIB_CLI_DIR}/src +SRCS += cli.c + +# By default, with USE_CLI_LIB_IRQHANDLER defined, the CLI library will handle the +# UART interrupts internally. Users have the option to define their own UART IRQ +# Handler for the CLI UART in their application. If users choose to define their own +# IRQ handler they should delete this definition of USE_CLI_LIB_IRQHANDLER and call +# MXC_CLI_Handler in their handler function when the CLI is in use. +LIB_CLI_USE_DEFAULT_HANDLER ?= 1 +ifeq "$(LIB_CLI_USE_DEFAULT_HANDLER)" "1" +PROJ_CFLAGS += -DUSE_CLI_LIB_IRQHANDLER +endif + +# Use absolute paths if building within eclipse environment. +ifeq "$(ECLIPSE)" "1" +SRCS := $(abspath $(SRCS)) +endif diff --git a/main_microcontroller_firmware/MSDK_overrides/CLI/README.md b/main_microcontroller_firmware/MSDK_overrides/CLI/README.md new file mode 100644 index 0000000..9e8bf02 --- /dev/null +++ b/main_microcontroller_firmware/MSDK_overrides/CLI/README.md @@ -0,0 +1,81 @@ +# MSDK CLI Library + +## Description + +#### What is a Command Line Interface(CLI)? + +A command-line interface or command language interpreter (CLI), also known as command-line user interface, console user interface, and character user interface (CUI), is a means of interacting with an embedded system where the user (or client) issues commands to the program in the form of successive lines of text (command lines). + +This library provides an extensible command processor to: + +- Quickly and easily wrap user functions with a CLI +- Allow developers to get diagnostics and change device parameters interactively +- Easily automate testing of the device, through PC-side scripting + +The current CLI Library includes the following features: + +- UART interface support +- Custom commands with support for multiple arguments +- No modification of functions required. Users implement a command table and simple wrapper functions. +- Case insensitive commands & arguments. +- White-space insensitive. +- Easy built-in 'help' command and custom help strings. + +## Usage Guide + +To use the CLI library, users are expected to implement the following steps in their application: + +1. Add the following line to your [project.mk](../../USERGUIDE.md#build-configuration-variables) file to enable the CLI library for your project: + + :::Makefile + LIB_CLI = 1 + +2. `#include "cli.h"` in your application code. + +3. Define an array of type `const command_t`. This is your command table. Include an array element for each command you want your CLI to support. Each element should define: + + 1. The name of the command + 2. A string showing how to enter the command in the terminal + 3. A description of what the command does + 4. A function pointer to a wrapper function. + + For example, the following is subset of the SDHC example command set: + + :::C + const command_t user_commands[] = {{ "format", "format", "Format the Card", handle_format }, + { "mkdir", "mkdir ", "Create a directory", handle_mkdir }} + +4. Implement a handler function for each command. + + The handler functions provided to the `user_commands` table are "wrappers" around the code that should be run for each received command. The handlers must conform to the following definition: + + Parameters + + - `int argc` - This tells the handler function how many arguments were received. + - `char* argv[]` - This array holds the received arguments (in order) as strings. (Note: argv[0] holds the command string, argv[1:argc] are the arguments (if any are passed), and the last element in the argument vector is always a NULL pointer.) + + Return Value + + - The function needs to return an integer type. It should return 0 (E_NO_ERROR) if the command was executed successfuly, otherwise it should return a negative integer as an error code. + + Below is a sample handler function for a "make directory" command, where `mkdir` is some function in the user's application code that does the work. + + :::C + int handle_mkdir(int argc, char *argv[]) { + mkdir(argv[1]); + } + + As an example, suppose a user entered the command: + + :::C + mkdir new_folder + + The CLI library will tokenize the command string "mkdir new_folder" into "mkdir" and "new_folder" and assigns them to argv[0] and argv[1] respectively. The library would then determine that this is the "make directory" command and would call "handle_mkdir" with argc=2 and a pointer to the argument vector. + +5. Add a call to `MXC_CLI_Init` to the application's startup code. + + Pass in the UART instance for the CLI to use, a pointer to the command table, and the number of commands in the command table. + +6. (OPTIONAL) The CLI library will enable and handle UART interrupts automatically. Users who would like more control over the interrupt handling have the option to disable the default handler and define their own. + + To do so, set the `LIB_CLI_USE_DEFAULT_HANDLER` [build configuration variable](../../USERGUIDE.md#build-configuration-variables) to `0`. Users may now enable and handle the interrupt with a custom function. However, users must call `MXC_CLI_Handler()` from the custom function for the CLI to work. diff --git a/main_microcontroller_firmware/MSDK_overrides/CLI/inc/cli.h b/main_microcontroller_firmware/MSDK_overrides/CLI/inc/cli.h new file mode 100644 index 0000000..3c126a0 --- /dev/null +++ b/main_microcontroller_firmware/MSDK_overrides/CLI/inc/cli.h @@ -0,0 +1,97 @@ +/****************************************************************************** + * + * Copyright (C) 2022-2023 Maxim Integrated Products, Inc. All Rights Reserved. + * (now owned by Analog Devices, Inc.), + * Copyright (C) 2023 Analog Devices, Inc. All Rights Reserved. This software + * is proprietary to Analog Devices, Inc. and its licensors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/*! \file cli.h + \brief A CLI Implementation using a command table to store command string, function pointer and a help string is dispatched. + + Details. + + 1. Line Accumlator + + Reads the incoming bytes + Accumulates into a line buffer + Echos char back to the emulator + Handles backspace + + 2. Prcoess Command + + Processes input into a series of tokens + All tokes are seperated by whitespace characters + Lookup first token in a table of functions + Dispatch to handler functions +*/ + +#ifndef LIBRARIES_CLI_INC_CLI_H_ +#define LIBRARIES_CLI_INC_CLI_H_ + +#include +#include "uart.h" + +/** + * @brief Command handler function prototype. Once a command is entered in the CLI, it is parsed + * ("tokenized") into an argument vector. The argument counter and argument vector are + * passed to the command's handler function where the command is executed. + * + * @param argc Number of tokens in the argument vector + * @param argv[] Array of arguments storing different tokens of the command string in the + * same order as they were passed in the command line. (argv[0] is the command, + * argv[1:argc] are the arguments (if any are passed), and the last element in the + * argument vector is always a NULL pointer.) + * + * @returns E_NO_ERROR if successful, otherwise an error code (error code must be a negative integer) + */ +typedef int (*command_handler_t)(int argc, char *argv[]); + +/** + * @brief Structure used to define the commands supported by the CLI + */ +typedef struct { + const char *cmd; /**< name of the command (as it should be entered on the command line) */ + const char *usage; /**< string to show how the command should be entered on the command line */ + const char *description; /**< string describing what the command does */ + command_handler_t handler; /**< function pointer of the command handler function */ +} command_t; + +/** + * @brief Initializes the CLI state variables and configures the uart for CLI operations. + * + * @param uart Pointer to UART instance to use for the CLI + * @param commands Pointer to the command table storing user-defined CLI commands + * @param num_commands Number of commands in the command table + * + * @return E_NO_ERROR if successful, otherwise an error code. + */ +int MXC_CLI_Init(mxc_uart_regs_t *uart, const command_t *commands, unsigned int num_commands); + +/** + * @brief Shuts down the CLI. (UART will remain enabled.) + * + * @return E_NO_ERROR if successful, otheriwse an error code. + */ +int MXC_CLI_Shutdown(void); + +/** + * @brief IRQ Handler for the CLI UART. This function should be called from the + * MXC_UARTx_Handler in the user application. + */ +void MXC_CLI_Handler(void); + +#endif // LIBRARIES_CLI_INC_CLI_H_ diff --git a/main_microcontroller_firmware/MSDK_overrides/CLI/libinfo.json b/main_microcontroller_firmware/MSDK_overrides/CLI/libinfo.json new file mode 100644 index 0000000..59d9517 --- /dev/null +++ b/main_microcontroller_firmware/MSDK_overrides/CLI/libinfo.json @@ -0,0 +1,10 @@ +{ + "name":"CLI", + "ipaths":[ + "inc" + ], + "vpaths":[ + "src" + ], + "whitelist":"False" +} diff --git a/main_microcontroller_firmware/MSDK_overrides/CLI/src/cli.c b/main_microcontroller_firmware/MSDK_overrides/CLI/src/cli.c new file mode 100644 index 0000000..81bdc1f --- /dev/null +++ b/main_microcontroller_firmware/MSDK_overrides/CLI/src/cli.c @@ -0,0 +1,400 @@ +/****************************************************************************** + * + * Copyright (C) 2022-2023 Maxim Integrated Products, Inc. All Rights Reserved. + * (now owned by Analog Devices, Inc.), + * Copyright (C) 2023 Analog Devices, Inc. All Rights Reserved. This software + * is proprietary to Analog Devices, Inc. and its licensors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/* -------------------------------------------------- */ +// INCLUDES +/* -------------------------------------------------- */ +#include +#include +#include + +#include "board.h" +#include "cli.h" +#include "mxc_errors.h" +#include "nvic_table.h" + +/* -------------------------------------------------- */ +// MACROS +/* -------------------------------------------------- */ +// Characters +#define ENTER 0x0D +#define NEW_LINE 0x0A +#define SPACE 0x20 +#define BACKSPACE 0x08 +#define DOLLAR 0x24 + +// Define a buffer length to store commands +#define MAX_COMMAND_LENGTH 256 +#define MAX_COMMAND_TOKENS 10 + +#define UART_BAUD 115200 +#define BUFF_SIZE 1 + +// Macro to call MXC_UART_Init function with appropriate parameters +#if (TARGET_NUM == 32520 || TARGET_NUM == 32570 || TARGET_NUM == 32650) +#define UART_INIT(uart) MXC_UART_Init(uart, UART_BAUD) +#elif (TARGET_NUM == 32660 || TARGET_NUM == 32665 || TARGET_NUM == 32666) +#define UART_INIT(uart) MXC_UART_Init(uart, UART_BAUD, MAP_A) +#elif TARGET_NUM == 32662 +#define UART_INIT(uart) MXC_UART_Init(uart, UART_BAUD, MXC_UART_APB_CLK, MAP_A) +#else +#define UART_INIT(uart) MXC_UART_Init(uart, UART_BAUD, MXC_UART_APB_CLK) +#endif + +/* -------------------------------------------------- */ +// FUNCTION PROTOTYPES +/* -------------------------------------------------- */ +void line_accumulator(uint8_t user_char); +void process_command(char *input); +int handle_help(int argc, char *argv[]); + +/* -------------------------------------------------- */ +// GLOBAL VARIABLES +/* -------------------------------------------------- */ +char cmd_buf[MAX_COMMAND_LENGTH]; // Command buffer +uint16_t buf_idx = 0; + +uint8_t char_recv; // Variable to store characters received by CLI UART +mxc_uart_req_t cli_req; // CLI UART transaction request structure + +// Help Command +const command_t help_command = {"Help", "help", + "Prints details regarding the usage of the supported commands.", + handle_help}; + +// Command table parameters; +const command_t *command_table = NULL; +unsigned int command_table_sz = 0; + +// UART instance used for CLI operations +mxc_uart_regs_t *cli_uart = NULL; + +/* -------------------------------------------------- */ +// PRIVATE FUNCTION DEFINITIONS +/* -------------------------------------------------- */ +/** + * @brief Prints the CLI prompt + */ +void User_Prompt_Sequence(void) +{ + if (cli_uart == NULL) + { + return; + } + + MXC_UART_WriteCharacter(cli_uart, NEW_LINE); + MXC_UART_WriteCharacter(cli_uart, DOLLAR); + MXC_UART_WriteCharacter(cli_uart, SPACE); +} + +/** + * @brief Clears the buffer storing the command string + */ +void Clear_buffer(void) +{ + memset(cmd_buf, '\0', MAX_COMMAND_LENGTH); +} + +/** + * @brief Writes the backspace sequence to the UART console + */ +void Console_Backspace_Sequence(void) +{ + if (cli_uart == NULL) + { + return; + } + + MXC_UART_WriteCharacter(cli_uart, BACKSPACE); + MXC_UART_WriteCharacter(cli_uart, SPACE); + MXC_UART_WriteCharacter(cli_uart, BACKSPACE); +} + +/** + * @brief Clears the line on the UART console + */ +void Console_Cmd_Clear(void) +{ + for (int i = 0; i < buf_idx; i++) + { + Console_Backspace_Sequence(); + } +} + +/** + * @brief Adds characters to the command string as they are received and echos them to the terminal + */ +void line_accumulator(uint8_t user_char) +{ + if (cli_uart == NULL) + { + return; + } + + switch (user_char) + { + case BACKSPACE: + // Handle Backspace and Delete + if (buf_idx > 0) + { + // Sequence to implement a backspace on the terminal + Console_Backspace_Sequence(); + buf_idx--; + cmd_buf[buf_idx] = '\0'; + } + break; + + case NEW_LINE: + // Do nothing. We don't want to store or echo new lines + break; + + case ENTER: + // Handle Enter or carriage return + MXC_UART_WriteCharacter(cli_uart, NEW_LINE); + MXC_UART_WriteCharacter(cli_uart, ENTER); + + // Parse and execute command + process_command(cmd_buf); + + // Reset command buffer + buf_idx = 0; + Clear_buffer(); + break; + + default: + // Handle all other characters + if (buf_idx < MAX_COMMAND_LENGTH) + { + cmd_buf[buf_idx++] = user_char; // pushes characters into the buffer + MXC_UART_WriteCharacter(cli_uart, user_char); + } + break; + } +} + +/** + * @brief Determines whether the command received was valid and calls the appropriate handler + * + * @param input Command string received by the CLI + */ +void process_command(char *input) +{ + // Initialize variables + char *argv[MAX_COMMAND_TOKENS + 1]; // Plus 1 so that argv can always be null terminated + int argc = 0; + int success_flag = 1; + + // Initialize command string and token pointers + char *cmd = input; + char *token; + + // Parse command string (delimiters: space and tab) + while (argc < MAX_COMMAND_TOKENS && (token = strtok_r(cmd, " \t", &cmd))) + { + argv[argc++] = token; + } + + // Set last argv value to NULL + argv[argc] = NULL; + + // If no arguments, return + if (argc == 0) + { + return; + } + + // Check for a valid command + if (strcasecmp(argv[0], help_command.cmd) == 0) + { + // Help command received + success_flag = help_command.handler(argc, argv); + } + else + { + // Help command not received, iterate over all user-defined commands + for (int i = 0; i < command_table_sz; i++) + { + if (strcasecmp(argv[0], command_table[i].cmd) == 0) + { + // Call corresponding command's handler + success_flag = command_table[i].handler(argc, argv); + break; + } + } + } + + // Check for errors + if (success_flag == 1) + { + // Command entered is not supported + printf("\nCommand isn't valid!\n"); + } + else if (success_flag < E_NO_ERROR) + { + // Command entered is supported, but arguments entered incorrectly + printf("\nEnter 'help' for details on how to use the '%s' command.\n", argv[0]); + } + + // Print prompt + User_Prompt_Sequence(); +} + +/** + * @brief Prints the help string of each command from the command table + * + * @param argc The command element number within the command string + * @param argv[] Array of arguments storing different tokens of the + * command string in the same order as they were passed + * in the command line. + * + * @returns E_NO_ERROR if successful, otherwise an error code. + */ +int handle_help(int argc, char *argv[]) +{ + // Print out name, usage, and description of each supported command + for (int i = 0; i < command_table_sz; i++) + { + printf("\n%s:\n", command_table[i].cmd); + printf(" Usage: %s\n", command_table[i].usage); + printf(" Description: %s\n", command_table[i].description); + } + + return E_NO_ERROR; +} + +/** + * @brief Callback function for when a character is received by the CLI + */ +void CLI_Callback(mxc_uart_req_t *req, int error) +{ + if (error == E_ABORT) + { + // Shutdown called, nothing to do in callback + return; + } + + // Process received character + line_accumulator(char_recv); + + // Get ready to receive next character + MXC_UART_TransactionAsync(req); +} + +/* -------------------------------------------------- */ +// PUBLIC FUNCTION DEFINITIONS +/* -------------------------------------------------- */ +int MXC_CLI_Init(mxc_uart_regs_t *uart, const command_t *commands, unsigned int num_commands) +{ + int error; + int uart_idx = MXC_UART_GET_IDX(uart); + + // Check for valid parameters + if (uart_idx < 0) + { + return E_BAD_PARAM; + } + else if (commands == NULL) + { + return E_NULL_PTR; + } + else if (num_commands <= 0) + { + return E_BAD_PARAM; + } + + // Return error if CLI is already initialized + if (cli_uart != NULL) + { + return E_BAD_STATE; + } + + // Save the command table + cli_uart = uart; + command_table = commands; + command_table_sz = num_commands; + + // Initialize UART + if ((error = UART_INIT(uart)) != E_NO_ERROR) + { + printf("-->Error initializing CLI UART: %d\n", error); + return error; + } + + // Initialize an asynchoronous request for the first character + cli_req.uart = cli_uart; + cli_req.rxData = &char_recv; + cli_req.rxLen = BUFF_SIZE; + cli_req.txLen = 0; + cli_req.callback = CLI_Callback; + if ((error = MXC_UART_TransactionAsync(&cli_req)) != E_NO_ERROR) + { + return error; + } + + // Print success message and prompt + printf("CLI Initialized! Enter 'help' to see a list of available commands.\n"); + User_Prompt_Sequence(); + while (MXC_UART_GetActive(uart)) + { + } + +#ifdef USE_CLI_LIB_IRQHANDLER + // Give users the option to define their own IRQ handler in their application. By default, + // we point the interrupt vector at MXC_CLI_Handler. + MXC_NVIC_SetVector(MXC_UART_GET_IRQ(uart_idx), MXC_CLI_Handler); +#endif // USE_CLI_LIB_IRQHANDLER + + // Enable interrupts + NVIC_EnableIRQ(MXC_UART_GET_IRQ(uart_idx)); + + return E_NO_ERROR; +} + +int MXC_CLI_Shutdown(void) +{ + // Return if CLI is uninitialized + if (cli_uart == NULL) + { + return E_BAD_STATE; + } + + // Abort existing async transaction + MXC_UART_AbortAsync(cli_uart); + + // Reset state variables + cli_uart = NULL; + command_table = NULL; + command_table_sz = 0; + buf_idx = 0; + + return E_NO_ERROR; +} + +void MXC_CLI_Handler(void) +{ + // Return if CLI is uninitialized + if (cli_uart == NULL) + { + return; + } + + MXC_UART_AsyncHandler(cli_uart); +}