From 23354ca24eaa533377609f4583ddb88b894ecc4f Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Wed, 21 Jun 2023 00:20:34 -0500 Subject: [PATCH 01/39] README.md change --- Examples/MAX78000/SDHC_FTHR/README.md | 240 +++++++++++++++++++++++++- 1 file changed, 239 insertions(+), 1 deletion(-) diff --git a/Examples/MAX78000/SDHC_FTHR/README.md b/Examples/MAX78000/SDHC_FTHR/README.md index 9931a3c7bd5..d8048e8bd8b 100644 --- a/Examples/MAX78000/SDHC_FTHR/README.md +++ b/Examples/MAX78000/SDHC_FTHR/README.md @@ -1,5 +1,7 @@ ## Description +This example demonstrates the SDHC FAT Filesystem. The terminal prompts with a list of user-selectable tasks to run on the inserted Micro SD Card. + ## Software ### Project Usage @@ -8,4 +10,240 @@ Universal instructions on building, flashing, and debugging this project can be ### Project-Specific Build Notes -* This project comes pre-configured for the MAX78000EVKIT. See [Board Support Packages](https://analog-devices-msdk.github.io/msdk/USERGUIDE/#board-support-packages) in the UG for instructions on changing the target board. \ No newline at end of file +* This project comes pre-configured for the MAX78000FTHR. See [Board Support Packages](https://analog-devices-msdk.github.io/msdk/USERGUIDE/#board-support-packages) in the UG for instructions on changing the target board. + +## Required Connections + +- Connect a USB cable between the PC and the CN1 (USB/PWR) connector. +- Open a terminal application on the PC and connect to the EV kit's console UART at 115200, 8-N-1. +- Insert the SD card into the Micro SD Card Connector. + +## Expected Output + +``` +***** MAX78000 SDHC FAT Filesystem Example ***** +Card inserted. + +Choose one of the following options: +0. Find the Size of the SD Card and Free Space +1. Format the Card +2. Manually Mount Card +3. List Contents of Current Directory +4. Create a Directory +5. Move into a Directory (cd) +6. Create a File of Random Data +7. Add Random Data to an Existing File +8. Delete a File +9. Format Card and Run Exmaple of FatFS Operations +10. Unmount Card and Quit +>>0 +SD card mounted. +Disk Size: 7849984 bytes +Available: 7849920 bytes +Function Returned with code: FR_OK + +Choose one of the following options: +0. Find the Size of the SD Card and Free Space +1. Format the Card +2. Manually Mount Card +3. List Contents of Current Directory +4. Create a Directory +5. Move into a Directory (cd) +6. Create a File of Random Data +7. Add Random Data to an Existing File +8. Delete a File +9. Format Card and Run Exmaple of FatFS Operations +10. Unmount Card and Quit +/>>1 + + +*****THE DRIVE WILL BE FORMATTED IN 5 SECONDS***** +**************PRESS ANY KEY TO ABORT************** + +FORMATTING DRIVE +Drive formatted. +SD card mounted. +SD card unmounted. +Function Returned with code: FR_OK + +Choose one of the following options: +0. Find the Size of the SD Card and Free Space +1. Format the Card +2. Manually Mount Card +3. List Contents of Current Directory +4. Create a Directory +5. Move into a Directory (cd) +6. Create a File of Random Data +7. Add Random Data to an Existing File +8. Delete a File +9. Format Card and Run Exmaple of FatFS Operations +10. Unmount Card and Quit +>>2 +SD card mounted. +Function Returned with code: FR_OK + +Choose one of the following options: +0. Find the Size of the SD Card and Free Space +1. Format the Card +2. Manually Mount Card +3. List Contents of Current Directory +4. Create a Directory +5. Move into a Directory (cd) +6. Create a File of Random Data +7. Add Random Data to an Existing File +8. Delete a File +9. Format Card and Run Exmaple of FatFS Operations +10. Unmount Card and Quit +/>>4 +Enter directory name: +Creating directory... +Directory Analog-Devices created. +Function Returned with code: FR_OK + +Choose one of the following options: +0. Find the Size of the SD Card and Free Space +1. Format the Card +2. Manually Mount Card +3. List Contents of Current Directory +4. Create a Directory +5. Move into a Directory (cd) +6. Create a File of Random Data +7. Add Random Data to an Existing File +8. Delete a File +9. Format Card and Run Exmaple of FatFS Operations +10. Unmount Card and Quit +/>>5 +Directory to change into: +Changed to Analog-Devices +Function Returned with code: FR_OK + +Choose one of the following options: +0. Find the Size of the SD Card and Free Space +1. Format the Card +2. Manually Mount Card +3. List Contents of Current Directory +4. Create a Directory +5. Move into a Directory (cd) +6. Create a File of Random Data +7. Add Random Data to an Existing File +8. Delete a File +9. Format Card and Run Exmaple of FatFS Operations +10. Unmount Card and Quit +/Analog-Devices>>6 +Enter the name of the text file: +Enter the length of the file: (256 max) +Creating file Maxim with length 200 +File opened! +200 bytes written to file! +File Closed! +Function Returned with code: FR_OK + +Choose one of the following options: +0. Find the Size of the SD Card and Free Space +1. Format the Card +2. Manually Mount Card +3. List Contents of Current Directory +4. Create a Directory +5. Move into a Directory (cd) +6. Create a File of Random Data +7. Add Random Data to an Existing File +8. Delete a File +9. Format Card and Run Exmaple of FatFS Operations +10. Unmount Card and Quit +/Analog-Devices>>7 +Enter name of file to append: +Enter length of random data to append: (256 max) +File opened! +50 bytes written to file +File closed. +Function Returned with code: FR_OK + +Choose one of the following options: +0. Find the Size of the SD Card and Free Space +1. Format the Card +2. Manually Mount Card +3. List Contents of Current Directory +4. Create a Directory +5. Move into a Directory (cd) +6. Create a File of Random Data +7. Add Random Data to an Existing File +8. Delete a File +9. Format Card and Run Exmaple of FatFS Operations +10. Unmount Card and Quit +/Analog-Devices>>3 +Listing Contents of /Analog-Devices - +/Analog-Devices/Maxim + +Finished listing contents +Function Returned with code: FR_OK + +Choose one of the following options: +0. Find the Size of the SD Card and Free Space +1. Format the Card +2. Manually Mount Card +3. List Contents of Current Directory +4. Create a Directory +5. Move into a Directory (cd) +6. Create a File of Random Data +7. Add Random Data to an Existing File +8. Delete a File +9. Format Card and Run Exmaple of FatFS Operations +10. Unmount Card and Quit +/Analog-Devices>>8 +File or directory to delete (always recursive!) +Deleted file Maxim +Function Returned with code: FR_OK + +Choose one of the following options: +0. Find the Size of the SD Card and Free Space +1. Format the Card +2. Manually Mount Card +3. List Contents of Current Directory +4. Create a Directory +5. Move into a Directory (cd) +6. Create a File of Random Data +7. Add Random Data to an Existing File +8. Delete a File +9. Format Card and Run Exmaple of FatFS Operations +10. Unmount Card and Quit +/Analog-Devices>>9 + + +*****THE DRIVE WILL BE FORMATTED IN 5 SECONDS***** +**************PRESS ANY KEY TO ABORT************** + +FORMATTING DRIVE +Drive formatted. +SD card mounted. +SD card unmounted. +SD card mounted. +SD Card Opened! +File opened! +256 bytes written to file! +File Closed! +Creating Directory... +Renaming File... +Attempting to read back file... +Read Back 256 bytes +Message: Vvt.BR0Vy4#YwqiNYj'jYnX8j7ePtuzJO?t-sTCGvibwYn81?Sutq'Q0s7udLTie5QZ-3d0mo6Hk5wL1z3!8GQFLbTYGQ!xtNwGI,eWiCJMbdqf7Ko?3T4dd7?Di4''4Cf2w.I?UPiSa'xfuMi5PV8Tn!ZJc83MccYv7BtHJ'VWcw#qbSh8rsqgi!EjFX3jbXhx8--6ZY6jvd1DmG5iAN,etVm3vtRVhr2Mgml3jJ?d.a0tjwx,lXT7.e,WC!wJ4 +File Closed! +Function Returned with code: FR_OK + +Choose one of the following options: +0. Find the Size of the SD Card and Free Space +1. Format the Card +2. Manually Mount Card +3. List Contents of Current Directory +4. Create a Directory +5. Move into a Directory (cd) +6. Create a File of Random Data +7. Add Random Data to an Existing File +8. Delete a File +9. Format Card and Run Exmaple of FatFS Operations +10. Unmount Card and Quit +>>10 +SD card unmounted. +Function Returned with code: FR_OK +End of example, please try to read the card. +``` + From b0d72fca01c38c942c7da3a0856a09e542d83e48 Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Thu, 22 Jun 2023 16:59:22 -0500 Subject: [PATCH 02/39] cli changes --- .../MAX78000/SDHC_FTHR/.vscode/settings.json | 7 +- Examples/MAX78000/SDHC_FTHR/include/cli.h | 137 ++++ Examples/MAX78000/SDHC_FTHR/include/sdhc.h | 61 ++ Examples/MAX78000/SDHC_FTHR/main.c | 653 +++--------------- Examples/MAX78000/SDHC_FTHR/src/cli.c | 340 +++++++++ Examples/MAX78000/SDHC_FTHR/src/sdhc.c | 473 +++++++++++++ 6 files changed, 1108 insertions(+), 563 deletions(-) create mode 100644 Examples/MAX78000/SDHC_FTHR/include/cli.h create mode 100644 Examples/MAX78000/SDHC_FTHR/include/sdhc.h create mode 100644 Examples/MAX78000/SDHC_FTHR/src/cli.c create mode 100644 Examples/MAX78000/SDHC_FTHR/src/sdhc.c diff --git a/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json b/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json index a9f2357266b..6ff9a4e750b 100755 --- a/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json +++ b/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json @@ -74,6 +74,11 @@ "TARGET=${config:target}", "TARGET_REV=0x4131", "__GNUC__" - ] + ], + "files.associations": { + "stdint.h": "c", + "cli.h": "c", + "sdhc.h": "c" + } } diff --git a/Examples/MAX78000/SDHC_FTHR/include/cli.h b/Examples/MAX78000/SDHC_FTHR/include/cli.h new file mode 100644 index 00000000000..351e8de624f --- /dev/null +++ b/Examples/MAX78000/SDHC_FTHR/include/cli.h @@ -0,0 +1,137 @@ +/****************************************************************************** + * Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Maxim Integrated + * Products, Inc. shall not be used except as stated in the Maxim Integrated + * Products, Inc. Branding Policy. + * + * The mere transfer of this software does not imply any licenses + * of trade secrets, proprietary technology, copyrights, patents, + * trademarks, maskwork rights, or any other form of intellectual + * property whatsoever. Maxim Integrated Products, Inc. retains all + * ownership rights. + * + ******************************************************************************/ + +/* -------------------------------------------------- */ +// INCLUDE GUARD +/* -------------------------------------------------- */ +#ifndef EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_CLI_H_ +#define EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_CLI_H_ + +/* -------------------------------------------------- */ +// INCLUDES +/* -------------------------------------------------- */ +#include +#include +#include +#include +#include +#include +#include +#include "board.h" +#include "sdhc.h" +#include "led.h" +#include "mxc_device.h" +#include "mxc_errors.h" +#include "uart.h" +#include "sdhc.h" + +/* -------------------------------------------------- */ +// MACROS +/* -------------------------------------------------- */ +#define ENTER 0X0D +#define SPACE 0x20 +#define TAB 0x09 +#define BACKSPACE 0X08 +#define MAXBUFF 2000 +#define DELETE 0x7F + +void line_accumlator(uint8_t user_char); + +// Command table hander prototype with parameters +typedef void (*command_handler_t)(int, char *argv[]); + +//Command table structure +typedef struct +{ + const char *name; + command_handler_t handler; + const char *help_string; +} command_table_t; + + +/* -------------------------------------------------- */ +// FUNCTION PROTOTYPES +/* -------------------------------------------------- */ +void handle_size(int argc, char *argv[]); + +void handle_format(int argc, char *argv[]); + +void hande_mount(int argc, char *argv[]); + +void handle_ls(int argc, char *argv[]); + +void handle_mkdir(int argc, char *argv[]); + +void handle_createfile(int argc, char *argv[]); + +void handle_cd(int argc, char *argv[]); + +void handle_add_data(int argc, char *argv[]); + +void handle_del(int argc, char *argv[]); + +void handle_fatfs(int argc, char *argv[]); + +void handle_unmount(int argc, char *argv[]); + +void handle_help(int argc, char *argv[]); + +void process_command(char *input); + +/* + * This table is an array of command_table_t structures that defines a set of supported commands in the program. + * Each command_table_t structure contains the name of the command, a function pointer to the corresponding command handler function, + * and a short description of what the command does. + * + * The structure of this lookup table makes it trivially easy to add a new command to this command processor. + */ + +static const command_table_t commands[] = {{"Size", handle_size, "Find the Size of the SD Card and Free Space\n\r"}, + {"Format", handle_format, "Format the Card\n\r"}, + {"Mount", hande_mount, "Manually Mount Card"}, + {"ls", handle_ls, "list the contents of the current directory\n\r"}, + {"mkdir", handle_mkdir, "Create a directory\n\r"}, + {"file_create", handle_createfile, "Create a file of random data\n\r"}, + {"cd", handle_cd, "Move into a directory\n\r"}, + {"add_data", handle_add_data, "Add random Data to an Existing File\n\r"}, + {"Del", handle_del, "Delete a file\n\r"}, + {"FatFs", handle_fatfs, "Format Card and Run Example of FatFS Operations"}, + {"Unmount", handle_unmount, "Unmount card and Quit"}, + {"Help", handle_help, "Prints a help message with info about all of the supported commands.\n\r"}}; +//Calculates the number of commands based on commands and the command table +static const int num_commands = sizeof(commands) / sizeof(command_table_t); + + + + +#endif /* EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_CLI_H_ */ \ No newline at end of file diff --git a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h new file mode 100644 index 00000000000..dc4ac0177d4 --- /dev/null +++ b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h @@ -0,0 +1,61 @@ +#ifndef SDHC_HEADER +#define SDHC_HEADER + +/***** Includes *****/ +#include +#include +#include +#include + +#include "board.h" +#include "mxc_delay.h" +#include "mxc_device.h" +#include "gpio.h" +#include "uart.h" + +#include "ff.h" +#include "cli.h" + +/***** Definitions *****/ + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define MAXLEN 256 +/***** Globals *****/ +FATFS *fs; //FFat Filesystem Object +FATFS fs_obj; +FIL file; //FFat File Object +FRESULT err; //FFat Result (Struct) +FILINFO fno; //FFat File Information Object +DIR dir; //FFat Directory Object + +/***** FUNCTION PROTOTYPES *****/ + +void generateMessage(unsigned length); + +int mount(); + +int umount(); + +int formatSDHC(); + +int getSize(); + +int ls(); + +int createFile(); + +int appendFile(); + +int mkdir(); + +int cd(); + +int delete(); + +int example(); + +void waitCardInserted(); + + +#endif //SDHC_HEADER \ No newline at end of file diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index cc34a7fdd75..6daa4e422a0 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -54,6 +54,8 @@ #include "uart.h" #include "ff.h" +#include "sdhc.h" +#include "cli.h" #ifdef BOARD_EVKIT_V1 #warning This example is not supported by the MAX78000EVKIT. @@ -61,485 +63,7 @@ /***** Definitions *****/ -#define STRINGIFY(x) #x -#define TOSTRING(x) STRINGIFY(x) - -#define MAXLEN 256 - -/***** Globals *****/ -FATFS *fs; //FFat Filesystem Object -FATFS fs_obj; -FIL file; //FFat File Object -FRESULT err; //FFat Result (Struct) -FILINFO fno; //FFat File Information Object -DIR dir; //FFat Directory Object -TCHAR message[MAXLEN], directory[MAXLEN], cwd[MAXLEN], filename[MAXLEN], volume_label[24], - volume = '0'; -TCHAR *FF_ERRORS[20]; -DWORD clusters_free = 0, sectors_free = 0, sectors_total = 0, volume_sn = 0; -UINT bytes_written = 0, bytes_read = 0, mounted = 0; -BYTE work[4096]; -static char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,.-#'?!"; -mxc_gpio_cfg_t SDPowerEnablePin = { MXC_GPIO1, MXC_GPIO_PIN_12, MXC_GPIO_FUNC_OUT, - MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO }; - -/***** FUNCTIONS *****/ - -void generateMessage(unsigned length) -{ - for (int i = 0; i < length; i++) { - /*Generate some random data to put in file*/ - message[i] = charset[rand() % (sizeof(charset) - 1)]; - } -} - -int mount() -{ - fs = &fs_obj; - - if ((err = f_mount(fs, "", 1)) != FR_OK) { //Mount the default drive to fs now - printf("Error opening SD card: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - } else { - printf("SD card mounted.\n"); - mounted = 1; - } - - f_getcwd(cwd, sizeof(cwd)); //Set the Current working directory - - return err; -} - -int umount() -{ - if ((err = f_mount(NULL, "", 0)) != FR_OK) { //Unmount the default drive from its mount point - printf("Error unmounting volume: %s\n", FF_ERRORS[err]); - } else { - printf("SD card unmounted.\n"); - mounted = 0; - } - return err; -} - -int formatSDHC() -{ - printf("\n\n*****THE DRIVE WILL BE FORMATTED IN 5 SECONDS*****\n"); - printf("**************PRESS ANY KEY TO ABORT**************\n\n"); - MXC_UART_ClearRXFIFO(MXC_UART0); - MXC_Delay(MSEC(5000)); - - if (MXC_UART_GetRXFIFOAvailable(MXC_UART0) > 0) { - return E_ABORT; - } - - printf("FORMATTING DRIVE\n"); - - if ((err = f_mkfs("", FM_ANY, 0, work, sizeof(work))) != - FR_OK) { //Format the default drive to FAT32 - printf("Error formatting SD card: %s\n", FF_ERRORS[err]); - } else { - printf("Drive formatted.\n"); - } - - mount(); - - if ((err = f_setlabel("MAXIM")) != FR_OK) { - printf("Error setting drive label: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - } - - umount(); - - return err; -} - -int getSize() -{ - if (!mounted) { - mount(); - } - - if ((err = f_getfree(&volume, &clusters_free, &fs)) != FR_OK) { - printf("Error finding free size of card: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - } - - sectors_total = (fs->n_fatent - 2) * fs->csize; - sectors_free = clusters_free * fs->csize; - - printf("Disk Size: %u bytes\n", sectors_total / 2); - printf("Available: %u bytes\n", sectors_free / 2); - - return err; -} - -int ls() -{ - if (!mounted) { - mount(); - } - - printf("Listing Contents of %s - \n", cwd); - - if ((err = f_opendir(&dir, cwd)) == FR_OK) { - while (1) { - err = f_readdir(&dir, &fno); - - if (err != FR_OK || fno.fname[0] == 0) { - break; - } - - printf("%s/%s", cwd, fno.fname); - - if (fno.fattrib & AM_DIR) { - printf("/"); - } - - printf("\n"); - } - - f_closedir(&dir); - } else { - printf("Error opening directory!\n"); - return err; - } - - printf("\nFinished listing contents\n"); - - return err; -} - -int createFile() -{ - unsigned int length = 128; - - if (!mounted) { - mount(); - } - - printf("Enter the name of the text file: \n"); - scanf("%255s", filename); - printf("Enter the length of the file: (%d max)\n", MAXLEN); - scanf("%d", &length); - - if (length > MAXLEN) { - printf("Error. File size limit for this example is %d bytes.\n", MAXLEN); - return FR_INVALID_PARAMETER; - } - - printf("Creating file %s with length %d\n", filename, length); - - if ((err = f_open(&file, (const TCHAR *)filename, FA_CREATE_ALWAYS | FA_WRITE)) != FR_OK) { - printf("Error opening file: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - printf("File opened!\n"); - - generateMessage(length); - - if ((err = f_write(&file, &message, length, &bytes_written)) != FR_OK) { - printf("Error writing file: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - printf("%d bytes written to file!\n", bytes_written); - - if ((err = f_close(&file)) != FR_OK) { - printf("Error closing file: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - printf("File Closed!\n"); - return err; -} - -int appendFile() -{ - unsigned int length = 0; - - if (!mounted) { - mount(); - } - - printf("Enter name of file to append: \n"); - scanf("%255s", filename); - printf("Enter length of random data to append: (%d max)\n", MAXLEN); - scanf("%d", &length); - - if ((err = f_stat((const TCHAR *)filename, &fno)) == FR_NO_FILE) { - printf("File %s doesn't exist!\n", (const TCHAR *)filename); - return err; - } - - if (length > MAXLEN) { - printf("Error. Size limit for this example is %d bytes.\n", MAXLEN); - return FR_INVALID_PARAMETER; - } - - if ((err = f_open(&file, (const TCHAR *)filename, FA_OPEN_APPEND | FA_WRITE)) != FR_OK) { - printf("Error opening file %s\n", FF_ERRORS[err]); - return err; - } - - printf("File opened!\n"); - - generateMessage(length); - - if ((err = f_write(&file, &message, length, &bytes_written)) != FR_OK) { - printf("Error writing file: %s\n", FF_ERRORS[err]); - return err; - } - - printf("%d bytes written to file\n", bytes_written); - - if ((err = f_close(&file)) != FR_OK) { - printf("Error closing file: %s\n", FF_ERRORS[err]); - return err; - } - - printf("File closed.\n"); - return err; -} - -int mkdir() -{ - if (!mounted) { - mount(); - } - - printf("Enter directory name: \n"); - scanf("%255s", directory); - - err = f_stat((const TCHAR *)directory, &fno); - - if (err == FR_NO_FILE) { - printf("Creating directory...\n"); - - if ((err = f_mkdir((const TCHAR *)directory)) != FR_OK) { - printf("Error creating directory: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } else { - printf("Directory %s created.\n", directory); - } - - } else { - printf("Directory already exists.\n"); - } - - return err; -} - -int cd() -{ - if (!mounted) { - mount(); - } - - printf("Directory to change into: \n"); - scanf("%255s", directory); - - if ((err = f_stat((const TCHAR *)directory, &fno)) == FR_NO_FILE) { - printf("Directory doesn't exist (Did you mean mkdir?)\n"); - return err; - } - - if ((err = f_chdir((const TCHAR *)directory)) != FR_OK) { - printf("Error in chdir: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - printf("Changed to %s\n", directory); - f_getcwd(cwd, sizeof(cwd)); - - return err; -} - -int delete () -{ - if (!mounted) { - mount(); - } - - printf("File or directory to delete (always recursive!)\n"); - scanf("%255s", filename); - - if ((err = f_stat((const TCHAR *)filename, &fno)) == FR_NO_FILE) { - printf("File or directory doesn't exist\n"); - return err; - } - - if ((err = f_unlink(filename)) != FR_OK) { - printf("Error deleting file\n"); - return err; - } - - printf("Deleted file %s\n", filename); - return err; -} - -int example() -{ - unsigned int length = 256; - - if ((err = formatSDHC()) != FR_OK) { - printf("Error Formatting SD Card: %s\n", FF_ERRORS[err]); - return err; - } - - //open SD Card - if ((err = mount()) != FR_OK) { - printf("Error opening SD Card: %s\n", FF_ERRORS[err]); - return err; - } - - printf("SD Card Opened!\n"); - - if ((err = f_setlabel("MAXIM")) != FR_OK) { - printf("Error setting drive label: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - if ((err = f_getfree(&volume, &clusters_free, &fs)) != FR_OK) { - printf("Error finding free size of card: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - if ((err = f_getlabel(&volume, volume_label, &volume_sn)) != FR_OK) { - printf("Error reading drive label: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - if ((err = f_open(&file, "0:HelloWorld.txt", FA_CREATE_ALWAYS | FA_WRITE)) != FR_OK) { - printf("Error opening file: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - printf("File opened!\n"); - - generateMessage(length); - - if ((err = f_write(&file, &message, length, &bytes_written)) != FR_OK) { - printf("Error writing file: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - printf("%d bytes written to file!\n", bytes_written); - - if ((err = f_close(&file)) != FR_OK) { - printf("Error closing file: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - printf("File Closed!\n"); - - if ((err = f_chmod("HelloWorld.txt", 0, AM_RDO | AM_ARC | AM_SYS | AM_HID)) != FR_OK) { - printf("Error in chmod: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - err = f_stat("MaximSDHC", &fno); - - if (err == FR_NO_FILE) { - printf("Creating Directory...\n"); - - if ((err = f_mkdir("MaximSDHC")) != FR_OK) { - printf("Error creating directory: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - } - - printf("Renaming File...\n"); - - if ((err = f_rename("0:HelloWorld.txt", "0:MaximSDHC/HelloMaxim.txt")) != - FR_OK) { //cr: clearify 0:file notation - printf("Error moving file: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - if ((err = f_chdir("/MaximSDHC")) != FR_OK) { - printf("Error in chdir: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - printf("Attempting to read back file...\n"); - - if ((err = f_open(&file, "HelloMaxim.txt", FA_READ)) != FR_OK) { - printf("Error opening file: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - if ((err = f_read(&file, &message, bytes_written, &bytes_read)) != FR_OK) { - printf("Error reading file: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - printf("Read Back %d bytes\n", bytes_read); - printf("Message: "); - printf("%s", message); - printf("\n"); - - if ((err = f_close(&file)) != FR_OK) { - printf("Error closing file: %s\n", FF_ERRORS[err]); - f_mount(NULL, "", 0); - return err; - } - - printf("File Closed!\n"); - - //unmount SD Card - //f_mount(fs, "", 0); - if ((err = f_mount(NULL, "", 0)) != FR_OK) { - printf("Error unmounting volume: %s\n", FF_ERRORS[err]); - return err; - } - - return 0; -} - -void waitCardInserted() -{ - // On the MAX78000FTHR board, P0.12 will be pulled low when a card is inserted. - mxc_gpio_cfg_t cardDetect; - cardDetect.port = MXC_GPIO0; - cardDetect.mask = MXC_GPIO_PIN_12; - cardDetect.func = MXC_GPIO_FUNC_IN; - cardDetect.pad = MXC_GPIO_PAD_NONE; - cardDetect.vssel = MXC_GPIO_VSSEL_VDDIOH; - - MXC_GPIO_Config(&cardDetect); - - // Exit function if card is already inserted - if (MXC_GPIO_InGet(MXC_GPIO0, MXC_GPIO_PIN_12) == 0) { - return; - } - - printf("Insert SD card to continue.\n"); - - while (MXC_GPIO_InGet(MXC_GPIO0, MXC_GPIO_PIN_12) != 0) { - // Spin waiting for card to be inserted. - } - - // Card has been detected, exit the function. -} /******************************************************************************/ int main(void) @@ -565,7 +89,7 @@ int main(void) FF_ERRORS[18] = "FR_TOO_MANY_OPEN_FILES"; FF_ERRORS[19] = "FR_INVALID_PARAMETER"; srand(12347439); - int run = 1, input = -1; + //int run = 1, input = -1; printf("\n\n***** " TOSTRING(TARGET) " SDHC FAT Filesystem Example *****\n"); @@ -573,89 +97,94 @@ int main(void) printf("Card inserted.\n"); - while (run) { - f_getcwd(cwd, sizeof(cwd)); - - printf("\nChoose one of the following options: \n"); - printf("0. Find the Size of the SD Card and Free Space\n"); - printf("1. Format the Card\n"); - printf("2. Manually Mount Card\n"); - printf("3. List Contents of Current Directory\n"); - printf("4. Create a Directory\n"); - printf("5. Move into a Directory (cd)\n"); - printf("6. Create a File of Random Data\n"); - printf("7. Add Random Data to an Existing File\n"); - printf("8. Delete a File\n"); - printf("9. Format Card and Run Exmaple of FatFS Operations\n"); - printf("10. Unmount Card and Quit\n"); - printf("%s>>", cwd); - - input = -1; - scanf("%d", &input); - printf("%d\n", input); - - err = 0; - - switch (input) { - case 0: - getSize(); - break; - - case 1: - formatSDHC(); - break; - - case 3: - ls(); - break; - - case 6: - createFile(); - break; - - case 7: - appendFile(); - break; - - case 4: - mkdir(); - break; - - case 5: - cd(); - break; - - case 9: - example(); - break; - - case 10: - umount(); - run = 0; - break; - - case 2: - mount(); - break; - - case 8: - delete (); - break; - - default: - printf("Invalid Selection %d!\n", input); - err = -1; - break; - } - - if (err >= 0 && err <= 20) { - printf("Function Returned with code: %s\n", FF_ERRORS[err]); - } else { - printf("Function Returned with code: %d\n", err); - } - - MXC_Delay(MSEC(500)); - } + while(1){ + char character = getchar(); + line_accumlator(character); + } + + // while (run) { + // f_getcwd(cwd, sizeof(cwd)); + + // printf("\nChoose one of the following options: \n"); + // printf("0. Find the Size of the SD Card and Free Space\n"); + // printf("1. Format the Card\n"); + // printf("2. Manually Mount Card\n"); + // printf("3. List Contents of Current Directory\n"); + // printf("4. Create a Directory\n"); + // printf("5. Move into a Directory (cd)\n"); + // printf("6. Create a File of Random Data\n"); + // printf("7. Add Random Data to an Existing File\n"); + // printf("8. Delete a File\n"); + // printf("9. Format Card and Run Exmaple of FatFS Operations\n"); + // printf("10. Unmount Card and Quit\n"); + // printf("%s>>", cwd); + + // input = -1; + // scanf("%d", &input); + // printf("%d\n", input); + + // err = 0; + + // switch (input) { + // case 0: + // getSize(); + // break; + + // case 1: + // formatSDHC(); + // break; + + // case 3: + // ls(); + // break; + + // case 6: + // createFile(); + // break; + + // case 7: + // appendFile(); + // break; + + // case 4: + // mkdir(); + // break; + + // case 5: + // cd(); + // break; + + // case 9: + // example(); + // break; + + // case 10: + // umount(); + // run = 0; + // break; + + // case 2: + // mount(); + // break; + + // case 8: + // delete (); + // break; + + // default: + // printf("Invalid Selection %d!\n", input); + // err = -1; + // break; + // } + + // if (err >= 0 && err <= 20) { + // printf("Function Returned with code: %s\n", FF_ERRORS[err]); + // } else { + // printf("Function Returned with code: %d\n", err); + // } + + // MXC_Delay(MSEC(500)); + // } printf("End of example, please try to read the card.\n"); return 0; diff --git a/Examples/MAX78000/SDHC_FTHR/src/cli.c b/Examples/MAX78000/SDHC_FTHR/src/cli.c new file mode 100644 index 00000000000..28973e5e84f --- /dev/null +++ b/Examples/MAX78000/SDHC_FTHR/src/cli.c @@ -0,0 +1,340 @@ +/****************************************************************************** + * Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Maxim Integrated + * Products, Inc. shall not be used except as stated in the Maxim Integrated + * Products, Inc. Branding Policy. + * + * The mere transfer of this software does not imply any licenses + * of trade secrets, proprietary technology, copyrights, patents, + * trademarks, maskwork rights, or any other form of intellectual + * property whatsoever. Maxim Integrated Products, Inc. retains all + * ownership rights. + * + ******************************************************************************/\ + +///* -------------------------------------------------- */ +//// INCLUDES +///* -------------------------------------------------- */ + +#include "cli.h" + + +/* -------------------------------------------------- */ +// FUNCTION PROTOTYPES +/* -------------------------------------------------- */ +bool white_space_present(char *p); + +bool white_space_not_present(char *p); + + +/* + * @name line_accumlator + * + * @brief Reads incoming bytes, Accumulate into line buffer, Echo's chars back to the other side and handles backspace. + * It calls the process_command function upon pressing the ENTER key + * + * @param: + * uint8_t user_char + * User input of characters + */ +void line_accumlator(uint8_t user_char) +{ + // Declare static variables + static int idx = 0; + static char buf[256]; + + switch (user_char) { + case BACKSPACE: + case DELETE: { + // Handle Backspace and Delete + if (idx > 0) { + //Sequence to actually implement a backspace on the terminal + putchar(BACKSPACE); + putchar(SPACE); + putchar(BACKSPACE); + idx--; + buf[idx] = '\0'; + } + break; + } + case ENTER: { + // Handle Enter or carriage return + printf("\r"); + buf[idx++] = '\r'; + buf[idx] = '\0'; + idx = 0; + char *accum = buf; //Assign buf + process_command(accum); + break; + } + default: { + // Handle all other characters + if (idx < 255) { + buf[idx++] = user_char; //pushes characters into the buffer + putchar(user_char); + } + break; + } + } +} + +/* + * @name handle_size + * + * @brief Finds the Size of the SD Card and Free Space + * + * @param argc and *argv[] + * + * + * @return + * void + */ +void handle_size(int argc, char *argv[]){ + getSize(); +} + +void handle_format(int argc, char *argv[]){ + formatSDHC(); +} + +void hande_mount(int argc, char *argv[]){ + mount(); +} + +void handle_ls(int argc, char *argv[]){ + ls(); +} + +void handle_mkdir(int argc, char *argv[]){ + mkdir(); +} + +void handle_createfile(int argc, char *argv[]){ + createFile(); +} + +void handle_cd(int argc, char *argv[]){ + cd(); +} + +void handle_add_data(int argc, char *argv[]){ + appendFile(); +} + +void handle_del(int argc, char *argv[]){ + delete(); +} + +void handle_fatfs(int argc, char *argv[]){ + example(); +} + +void handle_unmount(int argc, char *argv[]){ + umount(); +} +/* @name handle_help + * + * @brief: Prints a help message with info about all of the supported commands. + * + */ +void handle_help(int argc, char *argv[]) +{ + printf("\n\r"); + for (int i = 0; i < num_commands;i++) + printf("%s --> %s", commands[i].name, commands[i].help_string); +} +/* @name handle_sp + * + * @brief: Prints the current stack pointer in hex which is fetched from the MSP [Main stack pointer register] + */ +// void handle_sp(int argc, char *argv[]) +// { +// uint32_t *msp; +// asm("mrs %0, msp" : "=r" (msp)); // inline assembly to read the MSP (Main Stack Pointer) register of the Cortex-M0+ processor and store its value in the msp variable. +// printf("\n\rStack Pointer (MSP): %08x\n\r", (unsigned int)msp); //The value of the msp variable is then printed to the terminal using printf() +// } + +/* @name handle_dump + * + * @brief: Prints a hexdump of the memory requested, with up to 8 bytes per line of output. + */ +// void handle_dump(int argc, char *argv[]) +// { +// printf("invalid command\n\r"); +// hexdump(argv[1], argv[2]); +// } +/* @name handle_info + * + * @brief: Prints a string with build information that is dynamically generated at build time from your machine. + * Referencing macros defined in the makefile.def file + * + */ +// void handle_info(int argc, char *argv[]) +// { +// printf("\n\rVersion"); +// // printf("built on %s", MACHINE); +// // printf(" at %s\n\r", DATE_TIME); +// // printf("Commit %s\n\r", COMMIT_HASH); +// } +/* + * @name process_command + * + * @brief Performs a Lexical analysis and tokenisis the user's commands + * Lookup first token in a table of functions, dispatch to handler function + * + * @param char *input + * character pointer containing the line accumulator input + * + * @return + * void + */ +void process_command(char *input) +{ + //Initialize p and end pointers + char *p = input; + char *end; + + //Find end of string + for (end = input; *end != '\0'; end++); + + //Initialize variables + bool in_token = false; + bool beginning_space; + if(*p == SPACE || *p == TAB) + beginning_space = true; + char *argv[10]; + int argc = 0; + memset(argv, 0, sizeof(argv)); + + //Iterate over each character in input + for (p = input; p < end; p++) + { + //If in a token + if (in_token) + { + //If whitespace found, token ends + if (white_space_present(p)) + { + in_token = true; + } + //Else, token continues + else if(white_space_not_present(p)) + { + input = p; + in_token = false; + } + } + //If not in a token + else + { + //If ENTER or whitespace found + if(*p == ENTER || (white_space_present(p))) + { + //If ENTER, end of command + if(*p == ENTER) + { + *p = '\0'; + argv[argc++] = input; + break; + } + + //If whitespace found, end of token + if(white_space_present(p) && !beginning_space) + { + *p = '\0'; + argv[argc++] = input; + in_token = true; + } + } + //If not in token and whitespace not found, new token starts + else if (white_space_not_present(p) && beginning_space) + { + input = p; + in_token = false; + beginning_space = false; + } + //If not in token and whitespace not found and beginning_space is false, token continues + else if (white_space_not_present(p) && !beginning_space) + { + in_token = false; + } + } + } + + //Set last argv value to NULL + argv[argc] = NULL; + + //If no arguments, return + if (argc == 0) + return; + + bool success_flag = 0; //True if input command matches + + //Iterate over all commands to check if input command matches + for (int i=0; i < num_commands; i++) + { + if (strcasecmp(argv[0], commands[i].name) == 0) + { + //Call corresponding command's handler + commands[i].handler(argc, argv); + success_flag = 1; + break; + } + } + + //If no commands match, print error message + if (success_flag == 0) + { + printf("\n\rCommand isn't valid!\n\r"); + } + + //Print prompt + printf("$$ "); +} +/* + * @name white_space_present + * + * @brief Checks if white space is present in the given character pointer + * + * @param char *p + * character pointer containing the line accumulator input + * + * @return 1 if white space is present + * 0 if white space isn't present + */ +bool white_space_present(char *p){ + return *p == SPACE || *p == TAB; +} +/* + * @name white_space_not_present + * + * @brief Checks if there is no white space present in the given character pointer + * + * @param char *p + * character pointer containing the line accumulator input + * + * @return 1 if no white space is present + * 0 if white space is present + */ +bool white_space_not_present(char *p){ + return *p != SPACE || *p != TAB; +} \ No newline at end of file diff --git a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c new file mode 100644 index 00000000000..7c563872cd6 --- /dev/null +++ b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c @@ -0,0 +1,473 @@ + + +#include "sdhc.h" + +TCHAR *FF_ERRORS[20]; +BYTE work[4096]; + +DWORD clusters_free = 0, sectors_free = 0, sectors_total = 0, volume_sn = 0; +UINT bytes_written = 0, bytes_read = 0, mounted = 0; + +static char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,.-#'?!"; +TCHAR message[MAXLEN], directory[MAXLEN], cwd[MAXLEN], filename[MAXLEN], volume_label[24], + volume = '0'; +mxc_gpio_cfg_t SDPowerEnablePin = { MXC_GPIO1, MXC_GPIO_PIN_12, MXC_GPIO_FUNC_OUT, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO }; + +// /***** FUNCTIONS *****/ + +void generateMessage(unsigned length) +{ + for (int i = 0; i < length; i++) { + /*Generate some random data to put in file*/ + message[i] = charset[rand() % (sizeof(charset) - 1)]; + } +} + +int mount() +{ + fs = &fs_obj; + + if ((err = f_mount(fs, "", 1)) != FR_OK) { //Mount the default drive to fs now + printf("Error opening SD card: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + } else { + printf("SD card mounted.\n"); + mounted = 1; + } + + f_getcwd(cwd, sizeof(cwd)); //Set the Current working directory + + return err; +} + +int umount() +{ + if ((err = f_mount(NULL, "", 0)) != FR_OK) { //Unmount the default drive from its mount point + printf("Error unmounting volume: %s\n", FF_ERRORS[err]); + } else { + printf("SD card unmounted.\n"); + mounted = 0; + } + + return err; +} + +int formatSDHC() +{ + printf("\n\n*****THE DRIVE WILL BE FORMATTED IN 5 SECONDS*****\n"); + printf("**************PRESS ANY KEY TO ABORT**************\n\n"); + MXC_UART_ClearRXFIFO(MXC_UART0); + MXC_Delay(MSEC(5000)); + + if (MXC_UART_GetRXFIFOAvailable(MXC_UART0) > 0) { + return E_ABORT; + } + + printf("FORMATTING DRIVE\n"); + + if ((err = f_mkfs("", FM_ANY, 0, work, sizeof(work))) != + FR_OK) { //Format the default drive to FAT32 + printf("Error formatting SD card: %s\n", FF_ERRORS[err]); + } else { + printf("Drive formatted.\n"); + } + + mount(); + + if ((err = f_setlabel("MAXIM")) != FR_OK) { + printf("Error setting drive label: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + } + + umount(); + + return err; +} + +int getSize() +{ + if (!mounted) { + mount(); + } + + if ((err = f_getfree(&volume, &clusters_free, &fs)) != FR_OK) { + printf("Error finding free size of card: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + } + + sectors_total = (fs->n_fatent - 2) * fs->csize; + sectors_free = clusters_free * fs->csize; + + printf("Disk Size: %u bytes\n", sectors_total / 2); + printf("Available: %u bytes\n", sectors_free / 2); + + return err; +} + +int ls() +{ + if (!mounted) { + mount(); + } + + printf("Listing Contents of %s - \n", cwd); + + if ((err = f_opendir(&dir, cwd)) == FR_OK) { + while (1) { + err = f_readdir(&dir, &fno); + + if (err != FR_OK || fno.fname[0] == 0) { + break; + } + + printf("%s/%s", cwd, fno.fname); + + if (fno.fattrib & AM_DIR) { + printf("/"); + } + + printf("\n"); + } + + f_closedir(&dir); + } else { + printf("Error opening directory!\n"); + return err; + } + + printf("\nFinished listing contents\n"); + + return err; +} + +int createFile() +{ + unsigned int length = 128; + + if (!mounted) { + mount(); + } + + printf("Enter the name of the text file: \n"); + scanf("%255s", filename); + printf("Enter the length of the file: (%d max)\n", MAXLEN); + scanf("%d", &length); + + if (length > MAXLEN) { + printf("Error. File size limit for this example is %d bytes.\n", MAXLEN); + return FR_INVALID_PARAMETER; + } + + printf("Creating file %s with length %d\n", filename, length); + + if ((err = f_open(&file, (const TCHAR *)filename, FA_CREATE_ALWAYS | FA_WRITE)) != FR_OK) { + printf("Error opening file: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + printf("File opened!\n"); + + generateMessage(length); + + if ((err = f_write(&file, &message, length, &bytes_written)) != FR_OK) { + printf("Error writing file: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + printf("%d bytes written to file!\n", bytes_written); + + if ((err = f_close(&file)) != FR_OK) { + printf("Error closing file: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + printf("File Closed!\n"); + return err; +} + +int appendFile() +{ + unsigned int length = 0; + + if (!mounted) { + mount(); + } + + printf("Enter name of file to append: \n"); + scanf("%255s", filename); + printf("Enter length of random data to append: (%d max)\n", MAXLEN); + scanf("%d", &length); + + if ((err = f_stat((const TCHAR *)filename, &fno)) == FR_NO_FILE) { + printf("File %s doesn't exist!\n", (const TCHAR *)filename); + return err; + } + + if (length > MAXLEN) { + printf("Error. Size limit for this example is %d bytes.\n", MAXLEN); + return FR_INVALID_PARAMETER; + } + + if ((err = f_open(&file, (const TCHAR *)filename, FA_OPEN_APPEND | FA_WRITE)) != FR_OK) { + printf("Error opening file %s\n", FF_ERRORS[err]); + return err; + } + + printf("File opened!\n"); + + generateMessage(length); + + if ((err = f_write(&file, &message, length, &bytes_written)) != FR_OK) { + printf("Error writing file: %s\n", FF_ERRORS[err]); + return err; + } + + printf("%d bytes written to file\n", bytes_written); + + if ((err = f_close(&file)) != FR_OK) { + printf("Error closing file: %s\n", FF_ERRORS[err]); + return err; + } + + printf("File closed.\n"); + return err; +} + +int mkdir() +{ + if (!mounted) { + mount(); + } + + printf("Enter directory name: \n"); + scanf("%255s", directory); + + err = f_stat((const TCHAR *)directory, &fno); + + if (err == FR_NO_FILE) { + printf("Creating directory...\n"); + + if ((err = f_mkdir((const TCHAR *)directory)) != FR_OK) { + printf("Error creating directory: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } else { + printf("Directory %s created.\n", directory); + } + + } else { + printf("Directory already exists.\n"); + } + + return err; +} + +int cd() +{ + if (!mounted) { + mount(); + } + + printf("Directory to change into: \n"); + scanf("%255s", directory); + + if ((err = f_stat((const TCHAR *)directory, &fno)) == FR_NO_FILE) { + printf("Directory doesn't exist (Did you mean mkdir?)\n"); + return err; + } + + if ((err = f_chdir((const TCHAR *)directory)) != FR_OK) { + printf("Error in chdir: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + printf("Changed to %s\n", directory); + f_getcwd(cwd, sizeof(cwd)); + + return err; +} + +int delete () +{ + if (!mounted) { + mount(); + } + + printf("File or directory to delete (always recursive!)\n"); + scanf("%255s", filename); + + if ((err = f_stat((const TCHAR *)filename, &fno)) == FR_NO_FILE) { + printf("File or directory doesn't exist\n"); + return err; + } + + if ((err = f_unlink(filename)) != FR_OK) { + printf("Error deleting file\n"); + return err; + } + + printf("Deleted file %s\n", filename); + return err; +} + +int example() +{ + unsigned int length = 256; + + if ((err = formatSDHC()) != FR_OK) { + printf("Error Formatting SD Card: %s\n", FF_ERRORS[err]); + return err; + } + + //open SD Card + if ((err = mount()) != FR_OK) { + printf("Error opening SD Card: %s\n", FF_ERRORS[err]); + return err; + } + + printf("SD Card Opened!\n"); + + if ((err = f_setlabel("MAXIM")) != FR_OK) { + printf("Error setting drive label: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + if ((err = f_getfree(&volume, &clusters_free, &fs)) != FR_OK) { + printf("Error finding free size of card: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + if ((err = f_getlabel(&volume, volume_label, &volume_sn)) != FR_OK) { + printf("Error reading drive label: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + if ((err = f_open(&file, "0:HelloWorld.txt", FA_CREATE_ALWAYS | FA_WRITE)) != FR_OK) { + printf("Error opening file: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + printf("File opened!\n"); + + generateMessage(length); + + if ((err = f_write(&file, &message, length, &bytes_written)) != FR_OK) { + printf("Error writing file: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + printf("%d bytes written to file!\n", bytes_written); + + if ((err = f_close(&file)) != FR_OK) { + printf("Error closing file: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + printf("File Closed!\n"); + + if ((err = f_chmod("HelloWorld.txt", 0, AM_RDO | AM_ARC | AM_SYS | AM_HID)) != FR_OK) { + printf("Error in chmod: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + err = f_stat("MaximSDHC", &fno); + + if (err == FR_NO_FILE) { + printf("Creating Directory...\n"); + + if ((err = f_mkdir("MaximSDHC")) != FR_OK) { + printf("Error creating directory: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + } + + printf("Renaming File...\n"); + + if ((err = f_rename("0:HelloWorld.txt", "0:MaximSDHC/HelloMaxim.txt")) != + FR_OK) { //cr: clearify 0:file notation + printf("Error moving file: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + if ((err = f_chdir("/MaximSDHC")) != FR_OK) { + printf("Error in chdir: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + printf("Attempting to read back file...\n"); + + if ((err = f_open(&file, "HelloMaxim.txt", FA_READ)) != FR_OK) { + printf("Error opening file: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + if ((err = f_read(&file, &message, bytes_written, &bytes_read)) != FR_OK) { + printf("Error reading file: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + printf("Read Back %d bytes\n", bytes_read); + printf("Message: "); + printf("%s", message); + printf("\n"); + + if ((err = f_close(&file)) != FR_OK) { + printf("Error closing file: %s\n", FF_ERRORS[err]); + f_mount(NULL, "", 0); + return err; + } + + printf("File Closed!\n"); + + //unmount SD Card + //f_mount(fs, "", 0); + if ((err = f_mount(NULL, "", 0)) != FR_OK) { + printf("Error unmounting volume: %s\n", FF_ERRORS[err]); + return err; + } + + return 0; +} + +void waitCardInserted() +{ + // On the MAX78000FTHR board, P0.12 will be pulled low when a card is inserted. + mxc_gpio_cfg_t cardDetect; + cardDetect.port = MXC_GPIO0; + cardDetect.mask = MXC_GPIO_PIN_12; + cardDetect.func = MXC_GPIO_FUNC_IN; + cardDetect.pad = MXC_GPIO_PAD_NONE; + cardDetect.vssel = MXC_GPIO_VSSEL_VDDIOH; + + MXC_GPIO_Config(&cardDetect); + + // Exit function if card is already inserted + if (MXC_GPIO_InGet(MXC_GPIO0, MXC_GPIO_PIN_12) == 0) { + return; + } + + printf("Insert SD card to continue.\n"); + + while (MXC_GPIO_InGet(MXC_GPIO0, MXC_GPIO_PIN_12) != 0) { + // Spin waiting for card to be inserted. + } + + // Card has been detected, exit the function. +} \ No newline at end of file From 61de614168d45bf79cc9d746837b8e64a1e6c479 Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Fri, 23 Jun 2023 11:18:11 -0500 Subject: [PATCH 03/39] Bugs removed --- Examples/MAX78000/SDHC_FTHR/include/sdhc.h | 40 ++++++++++++++++++---- Examples/MAX78000/SDHC_FTHR/main.c | 2 +- Examples/MAX78000/SDHC_FTHR/src/cli.c | 4 ++- Examples/MAX78000/SDHC_FTHR/src/sdhc.c | 40 +++++++++++++++++++++- 4 files changed, 76 insertions(+), 10 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h index dc4ac0177d4..d128e6b6e9c 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h +++ b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h @@ -1,3 +1,35 @@ +/****************************************************************************** + * Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Maxim Integrated + * Products, Inc. shall not be used except as stated in the Maxim Integrated + * Products, Inc. Branding Policy. + * + * The mere transfer of this software does not imply any licenses + * of trade secrets, proprietary technology, copyrights, patents, + * trademarks, maskwork rights, or any other form of intellectual + * property whatsoever. Maxim Integrated Products, Inc. retains all + * ownership rights. + * + ******************************************************************************/ #ifndef SDHC_HEADER #define SDHC_HEADER @@ -21,13 +53,7 @@ #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) #define MAXLEN 256 -/***** Globals *****/ -FATFS *fs; //FFat Filesystem Object -FATFS fs_obj; -FIL file; //FFat File Object -FRESULT err; //FFat Result (Struct) -FILINFO fno; //FFat File Information Object -DIR dir; //FFat Directory Object + /***** FUNCTION PROTOTYPES *****/ diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index 6daa4e422a0..bd89137ebed 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -63,7 +63,7 @@ /***** Definitions *****/ - +extern TCHAR *FF_ERRORS[20]; /******************************************************************************/ int main(void) diff --git a/Examples/MAX78000/SDHC_FTHR/src/cli.c b/Examples/MAX78000/SDHC_FTHR/src/cli.c index 28973e5e84f..8992ca55df1 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/cli.c +++ b/Examples/MAX78000/SDHC_FTHR/src/cli.c @@ -162,6 +162,7 @@ void handle_help(int argc, char *argv[]) for (int i = 0; i < num_commands;i++) printf("%s --> %s", commands[i].name, commands[i].help_string); } + /* @name handle_sp * * @brief: Prints the current stack pointer in hex which is fetched from the MSP [Main stack pointer register] @@ -169,7 +170,7 @@ void handle_help(int argc, char *argv[]) // void handle_sp(int argc, char *argv[]) // { // uint32_t *msp; -// asm("mrs %0, msp" : "=r" (msp)); // inline assembly to read the MSP (Main Stack Pointer) register of the Cortex-M0+ processor and store its value in the msp variable. +// asm("mrs %0, msp" : "=r" (msp)); // inline assembly to read the MSP (Main Stack Pointer) register of the Cortex-M4 processor and store its value in the msp variable. // printf("\n\rStack Pointer (MSP): %08x\n\r", (unsigned int)msp); //The value of the msp variable is then printed to the terminal using printf() // } @@ -182,6 +183,7 @@ void handle_help(int argc, char *argv[]) // printf("invalid command\n\r"); // hexdump(argv[1], argv[2]); // } + /* @name handle_info * * @brief: Prints a string with build information that is dynamically generated at build time from your machine. diff --git a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c index 7c563872cd6..88055933ca8 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c +++ b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c @@ -1,7 +1,45 @@ - +/****************************************************************************** + * Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Maxim Integrated + * Products, Inc. shall not be used except as stated in the Maxim Integrated + * Products, Inc. Branding Policy. + * + * The mere transfer of this software does not imply any licenses + * of trade secrets, proprietary technology, copyrights, patents, + * trademarks, maskwork rights, or any other form of intellectual + * property whatsoever. Maxim Integrated Products, Inc. retains all + * ownership rights. + * + ******************************************************************************/ #include "sdhc.h" +/***** Globals *****/ +FATFS *fs; //FFat Filesystem Object +FATFS fs_obj; +FIL file; //FFat File Object +FRESULT err; //FFat Result (Struct) +FILINFO fno; //FFat File Information Object +DIR dir; //FFat Directory Object TCHAR *FF_ERRORS[20]; BYTE work[4096]; From 86e191bd7d270b9a420c6e2649f262a5e617661d Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Mon, 3 Jul 2023 09:41:23 -0500 Subject: [PATCH 04/39] Working CLI --- Examples/MAX78000/SDHC_FTHR/main.c | 14 ++++++++++++-- Examples/MAX78000/SDHC_FTHR/src/cli.c | 13 ++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index bd89137ebed..c8c4df7d38b 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -63,6 +63,14 @@ /***** Definitions *****/ +// void UART_Handler(void) +// { +// MXC_UART_AsyncHandler(MXC_UART_GET_UART(CONSOLE_UART)); +// } + +// void readCallback(mxc_uart_req_t *req, int error){ + +// } extern TCHAR *FF_ERRORS[20]; /******************************************************************************/ @@ -93,12 +101,14 @@ int main(void) printf("\n\n***** " TOSTRING(TARGET) " SDHC FAT Filesystem Example *****\n"); - waitCardInserted(); + //waitCardInserted(); printf("Card inserted.\n"); + + while(1){ - char character = getchar(); + char character = MXC_UART_ReadCharacter(MXC_UART_GET_UART(CONSOLE_UART)); line_accumlator(character); } diff --git a/Examples/MAX78000/SDHC_FTHR/src/cli.c b/Examples/MAX78000/SDHC_FTHR/src/cli.c index 8992ca55df1..08051906bdd 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/cli.c +++ b/Examples/MAX78000/SDHC_FTHR/src/cli.c @@ -45,7 +45,7 @@ bool white_space_present(char *p); bool white_space_not_present(char *p); - +char buf[256]; /* * @name line_accumlator * @@ -60,17 +60,16 @@ void line_accumlator(uint8_t user_char) { // Declare static variables static int idx = 0; - static char buf[256]; switch (user_char) { case BACKSPACE: - case DELETE: { + { // Handle Backspace and Delete if (idx > 0) { //Sequence to actually implement a backspace on the terminal - putchar(BACKSPACE); - putchar(SPACE); - putchar(BACKSPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),SPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); idx--; buf[idx] = '\0'; } @@ -90,7 +89,7 @@ void line_accumlator(uint8_t user_char) // Handle all other characters if (idx < 255) { buf[idx++] = user_char; //pushes characters into the buffer - putchar(user_char); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),user_char); } break; } From e1a9ff7d5df32d9a926186adfa5ce44b2f1525e4 Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Mon, 3 Jul 2023 12:19:26 -0500 Subject: [PATCH 05/39] Interrupt based uart --- Examples/MAX78000/SDHC_FTHR/include/cli.h | 4 +- Examples/MAX78000/SDHC_FTHR/main.c | 53 ++++++++++++++++++----- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/include/cli.h b/Examples/MAX78000/SDHC_FTHR/include/cli.h index 351e8de624f..fa55191e445 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/cli.h +++ b/Examples/MAX78000/SDHC_FTHR/include/cli.h @@ -125,8 +125,8 @@ static const command_table_t commands[] = {{"Size", handle_size, "Find the Size {"cd", handle_cd, "Move into a directory\n\r"}, {"add_data", handle_add_data, "Add random Data to an Existing File\n\r"}, {"Del", handle_del, "Delete a file\n\r"}, - {"FatFs", handle_fatfs, "Format Card and Run Example of FatFS Operations"}, - {"Unmount", handle_unmount, "Unmount card and Quit"}, + {"FatFs", handle_fatfs, "Format Card and Run Example of FatFS Operations\n\r"}, + {"Unmount", handle_unmount, "Unmount card and Quit\n\r"}, {"Help", handle_help, "Prints a help message with info about all of the supported commands.\n\r"}}; //Calculates the number of commands based on commands and the command table static const int num_commands = sizeof(commands) / sizeof(command_table_t); diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index c8c4df7d38b..fa1ea5a4c3e 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -56,21 +56,33 @@ #include "ff.h" #include "sdhc.h" #include "cli.h" +#include "nvic_table.h" #ifdef BOARD_EVKIT_V1 #warning This example is not supported by the MAX78000EVKIT. #endif -/***** Definitions *****/ -// void UART_Handler(void) -// { -// MXC_UART_AsyncHandler(MXC_UART_GET_UART(CONSOLE_UART)); -// } +/***** Definitions *****/ +#define UART_BAUD 115200 +#define BUFF_SIZE 1 + +/****** Globals *********/ +volatile int READ_FLAG; +volatile uint8_t RxData; +volatile mxc_uart_req_t read_req; +/******* Functions ********/ +void UART_Handler(void) +{ + MXC_UART_AsyncHandler(MXC_UART_GET_UART(CONSOLE_UART)); +} -// void readCallback(mxc_uart_req_t *req, int error){ +void readCallback(mxc_uart_req_t *req, int error){ -// } + line_accumlator(RxData); + READ_FLAG = error; + MXC_UART_TransactionAsync(req); +} extern TCHAR *FF_ERRORS[20]; /******************************************************************************/ @@ -97,7 +109,6 @@ int main(void) FF_ERRORS[18] = "FR_TOO_MANY_OPEN_FILES"; FF_ERRORS[19] = "FR_INVALID_PARAMETER"; srand(12347439); - //int run = 1, input = -1; printf("\n\n***** " TOSTRING(TARGET) " SDHC FAT Filesystem Example *****\n"); @@ -105,11 +116,33 @@ int main(void) printf("Card inserted.\n"); + //UART initialized + NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); + MXC_NVIC_SetVector(MXC_UART_GET_IRQ(CONSOLE_UART), UART_Handler); + NVIC_EnableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); + + /* Initialize Console UART*/ + int error; + if ((error = MXC_UART_Init(MXC_UART_GET_UART(CONSOLE_UART), UART_BAUD, MXC_UART_APB_CLK)) != + E_NO_ERROR) { + printf("-->Error initializing UART: %d\n", error); + printf("-->Example Failed\n"); + return error; + } + + printf("-->UART Initialized\n\n"); + + read_req.uart = MXC_UART_GET_UART(CONSOLE_UART); + read_req.rxData = &RxData; + read_req.rxLen = BUFF_SIZE; + read_req.txLen = 0; + read_req.callback = readCallback; + error = MXC_UART_TransactionAsync(&read_req); while(1){ - char character = MXC_UART_ReadCharacter(MXC_UART_GET_UART(CONSOLE_UART)); - line_accumlator(character); + } // while (run) { From d957156a70602c405e2cd78ac4fb5a63cad22632 Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Wed, 5 Jul 2023 10:10:34 -0500 Subject: [PATCH 06/39] Enable SD Card detection --- Examples/MAX78000/SDHC_FTHR/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index fa1ea5a4c3e..5473374ad2f 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -112,11 +112,11 @@ int main(void) printf("\n\n***** " TOSTRING(TARGET) " SDHC FAT Filesystem Example *****\n"); - //waitCardInserted(); + waitCardInserted(); printf("Card inserted.\n"); - //UART initialized + // UART interrupt setup NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); NVIC_DisableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); MXC_NVIC_SetVector(MXC_UART_GET_IRQ(CONSOLE_UART), UART_Handler); From da80146993ebfe9e6049bcc4a588bca4e33b919c Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Wed, 5 Jul 2023 16:17:18 -0500 Subject: [PATCH 07/39] Enter bug Fix --- Examples/MAX78000/SDHC_FTHR/include/cli.h | 5 +- Examples/MAX78000/SDHC_FTHR/include/sdhc.h | 1 - Examples/MAX78000/SDHC_FTHR/main.c | 96 ++-------------------- Examples/MAX78000/SDHC_FTHR/src/cli.c | 5 +- 4 files changed, 10 insertions(+), 97 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/include/cli.h b/Examples/MAX78000/SDHC_FTHR/include/cli.h index fa55191e445..c67d0c70445 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/cli.h +++ b/Examples/MAX78000/SDHC_FTHR/include/cli.h @@ -59,6 +59,7 @@ // MACROS /* -------------------------------------------------- */ #define ENTER 0X0D +#define NEW_LINE 0x0A #define SPACE 0x20 #define TAB 0x09 #define BACKSPACE 0X08 @@ -126,12 +127,10 @@ static const command_table_t commands[] = {{"Size", handle_size, "Find the Size {"add_data", handle_add_data, "Add random Data to an Existing File\n\r"}, {"Del", handle_del, "Delete a file\n\r"}, {"FatFs", handle_fatfs, "Format Card and Run Example of FatFS Operations\n\r"}, - {"Unmount", handle_unmount, "Unmount card and Quit\n\r"}, + {"Unmount", handle_unmount, "Unmount card\n\r"}, {"Help", handle_help, "Prints a help message with info about all of the supported commands.\n\r"}}; //Calculates the number of commands based on commands and the command table static const int num_commands = sizeof(commands) / sizeof(command_table_t); - - #endif /* EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_CLI_H_ */ \ No newline at end of file diff --git a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h index d128e6b6e9c..b2cdb35b709 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h +++ b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h @@ -83,5 +83,4 @@ int example(); void waitCardInserted(); - #endif //SDHC_HEADER \ No newline at end of file diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index 5473374ad2f..5d406cc8457 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -115,7 +115,8 @@ int main(void) waitCardInserted(); printf("Card inserted.\n"); - + MXC_Delay(1000); //Delay inserted here to avoid weird printf values between previous and next printf command. + // UART interrupt setup NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); NVIC_DisableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); @@ -130,10 +131,9 @@ int main(void) printf("-->Example Failed\n"); return error; } - + printf("-->UART Initialized\n\n"); - read_req.uart = MXC_UART_GET_UART(CONSOLE_UART); read_req.rxData = &RxData; read_req.rxLen = BUFF_SIZE; @@ -141,94 +141,8 @@ int main(void) read_req.callback = readCallback; error = MXC_UART_TransactionAsync(&read_req); - while(1){ - - } + //while(1){} - // while (run) { - // f_getcwd(cwd, sizeof(cwd)); - - // printf("\nChoose one of the following options: \n"); - // printf("0. Find the Size of the SD Card and Free Space\n"); - // printf("1. Format the Card\n"); - // printf("2. Manually Mount Card\n"); - // printf("3. List Contents of Current Directory\n"); - // printf("4. Create a Directory\n"); - // printf("5. Move into a Directory (cd)\n"); - // printf("6. Create a File of Random Data\n"); - // printf("7. Add Random Data to an Existing File\n"); - // printf("8. Delete a File\n"); - // printf("9. Format Card and Run Exmaple of FatFS Operations\n"); - // printf("10. Unmount Card and Quit\n"); - // printf("%s>>", cwd); - - // input = -1; - // scanf("%d", &input); - // printf("%d\n", input); - - // err = 0; - - // switch (input) { - // case 0: - // getSize(); - // break; - - // case 1: - // formatSDHC(); - // break; - - // case 3: - // ls(); - // break; - - // case 6: - // createFile(); - // break; - - // case 7: - // appendFile(); - // break; - - // case 4: - // mkdir(); - // break; - - // case 5: - // cd(); - // break; - - // case 9: - // example(); - // break; - - // case 10: - // umount(); - // run = 0; - // break; - - // case 2: - // mount(); - // break; - - // case 8: - // delete (); - // break; - - // default: - // printf("Invalid Selection %d!\n", input); - // err = -1; - // break; - // } - - // if (err >= 0 && err <= 20) { - // printf("Function Returned with code: %s\n", FF_ERRORS[err]); - // } else { - // printf("Function Returned with code: %d\n", err); - // } - - // MXC_Delay(MSEC(500)); - // } - - printf("End of example, please try to read the card.\n"); + //printf("End of example, please try to read the card.\n"); return 0; } diff --git a/Examples/MAX78000/SDHC_FTHR/src/cli.c b/Examples/MAX78000/SDHC_FTHR/src/cli.c index 08051906bdd..2ddaf6a518f 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/cli.c +++ b/Examples/MAX78000/SDHC_FTHR/src/cli.c @@ -77,7 +77,8 @@ void line_accumlator(uint8_t user_char) } case ENTER: { // Handle Enter or carriage return - printf("\r"); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),NEW_LINE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),ENTER); buf[idx++] = '\r'; buf[idx] = '\0'; idx = 0; @@ -309,7 +310,7 @@ void process_command(char *input) } //Print prompt - printf("$$ "); + //printf("$$ "); } /* * @name white_space_present From 41ba63d828bb8cdf4ce4e659a3bb4a8748fdb365 Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Thu, 13 Jul 2023 11:58:19 -0500 Subject: [PATCH 08/39] Compatility with SDHC functions tested --- Examples/MAX78000/SDHC_FTHR/include/cli.h | 2 ++ Examples/MAX78000/SDHC_FTHR/include/sdhc.h | 10 +++--- Examples/MAX78000/SDHC_FTHR/src/cli.c | 38 +++++++++++++++++++--- Examples/MAX78000/SDHC_FTHR/src/sdhc.c | 32 +++++++----------- 4 files changed, 51 insertions(+), 31 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/include/cli.h b/Examples/MAX78000/SDHC_FTHR/include/cli.h index c67d0c70445..808af5b6a52 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/cli.h +++ b/Examples/MAX78000/SDHC_FTHR/include/cli.h @@ -109,6 +109,8 @@ void handle_help(int argc, char *argv[]); void process_command(char *input); +int str_to_dec(const char *str); + /* * This table is an array of command_table_t structures that defines a set of supported commands in the program. * Each command_table_t structure contains the name of the command, a function pointer to the corresponding command handler function, diff --git a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h index b2cdb35b709..527f566b478 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h +++ b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h @@ -69,15 +69,15 @@ int getSize(); int ls(); -int createFile(); +int createFile(char* file_name, unsigned int length); -int appendFile(); +int appendFile(char* file_name, unsigned int length); -int mkdir(); +int mkdir(char* dir_name); -int cd(); +int cd(char* dir_name); -int delete(); +int delete(char* file_name); int example(); diff --git a/Examples/MAX78000/SDHC_FTHR/src/cli.c b/Examples/MAX78000/SDHC_FTHR/src/cli.c index 2ddaf6a518f..e28addfa7dd 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/cli.c +++ b/Examples/MAX78000/SDHC_FTHR/src/cli.c @@ -45,6 +45,8 @@ bool white_space_present(char *p); bool white_space_not_present(char *p); +char *prev_commands[100]; +int cmd_idx = 0; char buf[256]; /* * @name line_accumlator @@ -84,6 +86,8 @@ void line_accumlator(uint8_t user_char) idx = 0; char *accum = buf; //Assign buf process_command(accum); + prev_commands[cmd_idx++] = accum; //Storing the excecuted command into a buffer + break; } default: { @@ -125,23 +129,25 @@ void handle_ls(int argc, char *argv[]){ } void handle_mkdir(int argc, char *argv[]){ - mkdir(); + mkdir(argv[1]); } void handle_createfile(int argc, char *argv[]){ - createFile(); + unsigned int length = str_to_dec(argv[2]); + createFile(argv[1],length); } void handle_cd(int argc, char *argv[]){ - cd(); + cd(argv[1]); } void handle_add_data(int argc, char *argv[]){ - appendFile(); + unsigned int length = str_to_dec(argv[2]); + appendFile(argv[1],length); } void handle_del(int argc, char *argv[]){ - delete(); + delete(argv[1]); } void handle_fatfs(int argc, char *argv[]){ @@ -339,4 +345,26 @@ bool white_space_present(char *p){ */ bool white_space_not_present(char *p){ return *p != SPACE || *p != TAB; +} + + +int str_to_dec(const char *str) +{ + int val = 0; + + // Loop through each character in the string + for (int i = 0; str[i] != '\0'; i++) { + char c = str[i]; + + // Check if the character is a valid hexadecimal digit + if (isdigit(c)) { + val = (val * 10) + (c - '0'); + // } else if (isxdigit(c)) { + // val = (val << 4) + (toupper(c) - 'A' + 10); + } else { + // Invalid character + return -1; + } + } + return val; } \ No newline at end of file diff --git a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c index 88055933ca8..4eba3b8eee9 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c +++ b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c @@ -179,18 +179,15 @@ int ls() return err; } -int createFile() +int createFile(char* file_name, unsigned int length) { - unsigned int length = 128; + // unsigned int length = 128; if (!mounted) { mount(); } - printf("Enter the name of the text file: \n"); - scanf("%255s", filename); - printf("Enter the length of the file: (%d max)\n", MAXLEN); - scanf("%d", &length); + strcpy(filename, file_name); if (length > MAXLEN) { printf("Error. File size limit for this example is %d bytes.\n", MAXLEN); @@ -227,18 +224,14 @@ int createFile() return err; } -int appendFile() +int appendFile(char* file_name, unsigned int length) { - unsigned int length = 0; if (!mounted) { mount(); } - printf("Enter name of file to append: \n"); - scanf("%255s", filename); - printf("Enter length of random data to append: (%d max)\n", MAXLEN); - scanf("%d", &length); + strcpy(filename, file_name); if ((err = f_stat((const TCHAR *)filename, &fno)) == FR_NO_FILE) { printf("File %s doesn't exist!\n", (const TCHAR *)filename); @@ -275,14 +268,13 @@ int appendFile() return err; } -int mkdir() +int mkdir(char* dir_name) { if (!mounted) { mount(); } - printf("Enter directory name: \n"); - scanf("%255s", directory); + strcpy(directory, dir_name); err = f_stat((const TCHAR *)directory, &fno); @@ -304,14 +296,13 @@ int mkdir() return err; } -int cd() +int cd(char* dir_name) { if (!mounted) { mount(); } - printf("Directory to change into: \n"); - scanf("%255s", directory); + strcpy(directory, dir_name); if ((err = f_stat((const TCHAR *)directory, &fno)) == FR_NO_FILE) { printf("Directory doesn't exist (Did you mean mkdir?)\n"); @@ -330,14 +321,13 @@ int cd() return err; } -int delete () +int delete(char* file_name) { if (!mounted) { mount(); } - printf("File or directory to delete (always recursive!)\n"); - scanf("%255s", filename); + strcpy(filename,file_name); if ((err = f_stat((const TCHAR *)filename, &fno)) == FR_NO_FILE) { printf("File or directory doesn't exist\n"); From 4483da763f89bca2f17881b6d93852524f327fec Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Thu, 13 Jul 2023 12:07:27 -0500 Subject: [PATCH 09/39] tested with new functionality --- Examples/MAX78000/SDHC_FTHR/README.md | 220 ++++---------------------- 1 file changed, 34 insertions(+), 186 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/README.md b/Examples/MAX78000/SDHC_FTHR/README.md index d8048e8bd8b..0796436e22a 100644 --- a/Examples/MAX78000/SDHC_FTHR/README.md +++ b/Examples/MAX78000/SDHC_FTHR/README.md @@ -23,38 +23,26 @@ Universal instructions on building, flashing, and debugging this project can be ``` ***** MAX78000 SDHC FAT Filesystem Example ***** Card inserted. - -Choose one of the following options: -0. Find the Size of the SD Card and Free Space -1. Format the Card -2. Manually Mount Card -3. List Contents of Current Directory -4. Create a Directory -5. Move into a Directory (cd) -6. Create a File of Random Data -7. Add Random Data to an Existing File -8. Delete a File -9. Format Card and Run Exmaple of FatFS Operations -10. Unmount Card and Quit ->>0 +-->UART Initialized + +help + +Size --> Find the Size of the SD Card and Free Space +Format --> Format the Card +Mount --> Manually Mount Cardls --> list the contents of the current directory +mkdir --> Create a directory +file_create --> Create a file of random data +cd --> Move into a directory +add_data --> Add random Data to an Existing File +Del --> Delete a file +FatFs --> Format Card and Run Example of FatFS Operations +Unmount --> Unmount card +Help --> Prints a help message with info about all of the supported commands. + size SD card mounted. -Disk Size: 7849984 bytes -Available: 7849920 bytes -Function Returned with code: FR_OK - -Choose one of the following options: -0. Find the Size of the SD Card and Free Space -1. Format the Card -2. Manually Mount Card -3. List Contents of Current Directory -4. Create a Directory -5. Move into a Directory (cd) -6. Create a File of Random Data -7. Add Random Data to an Existing File -8. Delete a File -9. Format Card and Run Exmaple of FatFS Operations -10. Unmount Card and Quit -/>>1 +Disk Size: 31163072 bytes +Available: 31163040 bytes +FORMAT *****THE DRIVE WILL BE FORMATTED IN 5 SECONDS***** @@ -64,149 +52,25 @@ FORMATTING DRIVE Drive formatted. SD card mounted. SD card unmounted. -Function Returned with code: FR_OK - -Choose one of the following options: -0. Find the Size of the SD Card and Free Space -1. Format the Card -2. Manually Mount Card -3. List Contents of Current Directory -4. Create a Directory -5. Move into a Directory (cd) -6. Create a File of Random Data -7. Add Random Data to an Existing File -8. Delete a File -9. Format Card and Run Exmaple of FatFS Operations -10. Unmount Card and Quit ->>2 +mount SD card mounted. -Function Returned with code: FR_OK - -Choose one of the following options: -0. Find the Size of the SD Card and Free Space -1. Format the Card -2. Manually Mount Card -3. List Contents of Current Directory -4. Create a Directory -5. Move into a Directory (cd) -6. Create a File of Random Data -7. Add Random Data to an Existing File -8. Delete a File -9. Format Card and Run Exmaple of FatFS Operations -10. Unmount Card and Quit -/>>4 -Enter directory name: +mkdir Maxim Creating directory... -Directory Analog-Devices created. -Function Returned with code: FR_OK - -Choose one of the following options: -0. Find the Size of the SD Card and Free Space -1. Format the Card -2. Manually Mount Card -3. List Contents of Current Directory -4. Create a Directory -5. Move into a Directory (cd) -6. Create a File of Random Data -7. Add Random Data to an Existing File -8. Delete a File -9. Format Card and Run Exmaple of FatFS Operations -10. Unmount Card and Quit -/>>5 -Directory to change into: -Changed to Analog-Devices -Function Returned with code: FR_OK - -Choose one of the following options: -0. Find the Size of the SD Card and Free Space -1. Format the Card -2. Manually Mount Card -3. List Contents of Current Directory -4. Create a Directory -5. Move into a Directory (cd) -6. Create a File of Random Data -7. Add Random Data to an Existing File -8. Delete a File -9. Format Card and Run Exmaple of FatFS Operations -10. Unmount Card and Quit -/Analog-Devices>>6 -Enter the name of the text file: -Enter the length of the file: (256 max) -Creating file Maxim with length 200 +Directory Maxim created. +cd Maxim +Changed to Maxim +file_create ADI 30 +Creating file ADI with length 30 File opened! -200 bytes written to file! +30 bytes written to file! File Closed! -Function Returned with code: FR_OK - -Choose one of the following options: -0. Find the Size of the SD Card and Free Space -1. Format the Card -2. Manually Mount Card -3. List Contents of Current Directory -4. Create a Directory -5. Move into a Directory (cd) -6. Create a File of Random Data -7. Add Random Data to an Existing File -8. Delete a File -9. Format Card and Run Exmaple of FatFS Operations -10. Unmount Card and Quit -/Analog-Devices>>7 -Enter name of file to append: -Enter length of random data to append: (256 max) +add_data ADI 22 File opened! -50 bytes written to file +22 bytes written to file File closed. -Function Returned with code: FR_OK - -Choose one of the following options: -0. Find the Size of the SD Card and Free Space -1. Format the Card -2. Manually Mount Card -3. List Contents of Current Directory -4. Create a Directory -5. Move into a Directory (cd) -6. Create a File of Random Data -7. Add Random Data to an Existing File -8. Delete a File -9. Format Card and Run Exmaple of FatFS Operations -10. Unmount Card and Quit -/Analog-Devices>>3 -Listing Contents of /Analog-Devices - -/Analog-Devices/Maxim - -Finished listing contents -Function Returned with code: FR_OK - -Choose one of the following options: -0. Find the Size of the SD Card and Free Space -1. Format the Card -2. Manually Mount Card -3. List Contents of Current Directory -4. Create a Directory -5. Move into a Directory (cd) -6. Create a File of Random Data -7. Add Random Data to an Existing File -8. Delete a File -9. Format Card and Run Exmaple of FatFS Operations -10. Unmount Card and Quit -/Analog-Devices>>8 -File or directory to delete (always recursive!) -Deleted file Maxim -Function Returned with code: FR_OK - -Choose one of the following options: -0. Find the Size of the SD Card and Free Space -1. Format the Card -2. Manually Mount Card -3. List Contents of Current Directory -4. Create a Directory -5. Move into a Directory (cd) -6. Create a File of Random Data -7. Add Random Data to an Existing File -8. Delete a File -9. Format Card and Run Exmaple of FatFS Operations -10. Unmount Card and Quit -/Analog-Devices>>9 +Del ADI +Deleted file ADI +Fatfs *****THE DRIVE WILL BE FORMATTED IN 5 SECONDS***** @@ -225,25 +89,9 @@ Creating Directory... Renaming File... Attempting to read back file... Read Back 256 bytes -Message: Vvt.BR0Vy4#YwqiNYj'jYnX8j7ePtuzJO?t-sTCGvibwYn81?Sutq'Q0s7udLTie5QZ-3d0mo6Hk5wL1z3!8GQFLbTYGQ!xtNwGI,eWiCJMbdqf7Ko?3T4dd7?Di4''4Cf2w.I?UPiSa'xfuMi5PV8Tn!ZJc83MccYv7BtHJ'VWcw#qbSh8rsqgi!EjFX3jbXhx8--6ZY6jvd1DmG5iAN,etVm3vtRVhr2Mgml3jJ?d.a0tjwx,lXT7.e,WC!wJ4 +Message: loZStiUH7-HxP!fffB,q.DqMnI2nvSnchst.ZaLTpG'w8I7Tg0,l6VVkEsehp#IHSMZN94WDc'N#-0qkBlAil,'#DvMJZ!zzf,j?Lm,H5cbfFfVNHUlPF9GsTWbrop0EG7VV57qZZzjdvzJH5Xh2'82'fz9t#R,kzoaqYBJVRhrlD5W1mZItggYqyIICUvWOOppJQIVvt.BR0Vy4#YwqiNYj'jYnX8j7ePtuzJO?t-sTCGvibwYn81?Sutq'Q0s7 File Closed! -Function Returned with code: FR_OK - -Choose one of the following options: -0. Find the Size of the SD Card and Free Space -1. Format the Card -2. Manually Mount Card -3. List Contents of Current Directory -4. Create a Directory -5. Move into a Directory (cd) -6. Create a File of Random Data -7. Add Random Data to an Existing File -8. Delete a File -9. Format Card and Run Exmaple of FatFS Operations -10. Unmount Card and Quit ->>10 +unmount SD card unmounted. -Function Returned with code: FR_OK -End of example, please try to read the card. ``` From 2a9d6682abc57e3f9a4ca447896e1101c7ea9b60 Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Thu, 13 Jul 2023 15:11:11 -0500 Subject: [PATCH 10/39] User prompt --- Examples/MAX78000/SDHC_FTHR/include/cli.h | 23 +++-- Examples/MAX78000/SDHC_FTHR/main.c | 1 + Examples/MAX78000/SDHC_FTHR/src/cli.c | 103 +++++++++++++--------- 3 files changed, 79 insertions(+), 48 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/include/cli.h b/Examples/MAX78000/SDHC_FTHR/include/cli.h index 808af5b6a52..cb5e7b70b69 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/cli.h +++ b/Examples/MAX78000/SDHC_FTHR/include/cli.h @@ -58,13 +58,20 @@ /* -------------------------------------------------- */ // MACROS /* -------------------------------------------------- */ -#define ENTER 0X0D -#define NEW_LINE 0x0A -#define SPACE 0x20 -#define TAB 0x09 -#define BACKSPACE 0X08 -#define MAXBUFF 2000 -#define DELETE 0x7F +#define ENTER 0X0D +#define NEW_LINE 0x0A +#define SPACE 0x20 +#define TAB 0x09 +#define BACKSPACE 0X08 +#define MAXBUFF 2000 +#define DELETE 0x7F +#define ARROW_KEY_CODE_1 0x1B +#define ARROW_KEY_CODE_2 0x5B +#define ARROW_KEY_CODE_LEFT 0x44 +#define ARROW_KEY_CODE_RIGHT 0x43 +#define ARROW_KEY_CODE_UP 0x41 +#define ARROW_KEY_CODE_DOWN 0x42 +#define DOLLAR 0x24 void line_accumlator(uint8_t user_char); @@ -111,6 +118,8 @@ void process_command(char *input); int str_to_dec(const char *str); +void user_prompt_sequence(void); + /* * This table is an array of command_table_t structures that defines a set of supported commands in the program. * Each command_table_t structure contains the name of the command, a function pointer to the corresponding command handler function, diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index 5d406cc8457..2be930a7f4d 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -133,6 +133,7 @@ int main(void) } printf("-->UART Initialized\n\n"); + user_prompt_sequence(); read_req.uart = MXC_UART_GET_UART(CONSOLE_UART); read_req.rxData = &RxData; diff --git a/Examples/MAX78000/SDHC_FTHR/src/cli.c b/Examples/MAX78000/SDHC_FTHR/src/cli.c index e28addfa7dd..eab50384568 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/cli.c +++ b/Examples/MAX78000/SDHC_FTHR/src/cli.c @@ -45,7 +45,8 @@ bool white_space_present(char *p); bool white_space_not_present(char *p); -char *prev_commands[100]; +char *cmd_history_str[100]; +// int cmd_history_len[100]; int cmd_idx = 0; char buf[256]; /* @@ -83,12 +84,41 @@ void line_accumlator(uint8_t user_char) MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),ENTER); buf[idx++] = '\r'; buf[idx] = '\0'; + + // cmd_history_len[cmd_idx] = idx; //Storing the length of the command into an array + cmd_history_str[cmd_idx++] = buf; //Storing the excecuted command into a buffer idx = 0; char *accum = buf; //Assign buf process_command(accum); - prev_commands[cmd_idx++] = accum; //Storing the excecuted command into a buffer + break; + } + case ARROW_KEY_CODE_LEFT: + case ARROW_KEY_CODE_RIGHT: + case ARROW_KEY_CODE_UP: + case ARROW_KEY_CODE_DOWN:{ + // Check the sequence to see if one of the arrow keys were pressed + if (buf[idx] == ARROW_KEY_CODE_2 && buf[idx - 1] == ARROW_KEY_CODE_1){ + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); + + if(user_char == ARROW_KEY_CODE_UP && cmd_idx > 0){ + //Erase what's currently on the prompt + //MXC_UART_Write(MXC_UART_GET_UART(CONSOLE_UART), ) + MXC_UART_Write(MXC_UART_GET_UART(CONSOLE_UART),cmd_history_str[--cmd_idx],strlen(cmd_history_str[cmd_idx])); + + //Empty current value of buf and assign respective cmd_history_str + idx = strlen(cmd_history_str[cmd_idx]); + } + } + // This else statement is excecuted when the arrow keys weren't pressed and the user entered W,A,S or D keys + else { + if (idx < 255) { + buf[idx++] = user_char; //pushes characters into the buffer + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),user_char); + } break; + } } default: { // Handle all other characters @@ -116,6 +146,17 @@ void handle_size(int argc, char *argv[]){ getSize(); } +/* + * @name handle_format + * + * @brief han + * + * @param argc and *argv[] + * + * + * @return + * void + */ void handle_format(int argc, char *argv[]){ formatSDHC(); } @@ -169,40 +210,6 @@ void handle_help(int argc, char *argv[]) printf("%s --> %s", commands[i].name, commands[i].help_string); } -/* @name handle_sp - * - * @brief: Prints the current stack pointer in hex which is fetched from the MSP [Main stack pointer register] - */ -// void handle_sp(int argc, char *argv[]) -// { -// uint32_t *msp; -// asm("mrs %0, msp" : "=r" (msp)); // inline assembly to read the MSP (Main Stack Pointer) register of the Cortex-M4 processor and store its value in the msp variable. -// printf("\n\rStack Pointer (MSP): %08x\n\r", (unsigned int)msp); //The value of the msp variable is then printed to the terminal using printf() -// } - -/* @name handle_dump - * - * @brief: Prints a hexdump of the memory requested, with up to 8 bytes per line of output. - */ -// void handle_dump(int argc, char *argv[]) -// { -// printf("invalid command\n\r"); -// hexdump(argv[1], argv[2]); -// } - -/* @name handle_info - * - * @brief: Prints a string with build information that is dynamically generated at build time from your machine. - * Referencing macros defined in the makefile.def file - * - */ -// void handle_info(int argc, char *argv[]) -// { -// printf("\n\rVersion"); -// // printf("built on %s", MACHINE); -// // printf(" at %s\n\r", DATE_TIME); -// // printf("Commit %s\n\r", COMMIT_HASH); -// } /* * @name process_command * @@ -316,7 +323,7 @@ void process_command(char *input) } //Print prompt - //printf("$$ "); + user_prompt_sequence(); } /* * @name white_space_present @@ -347,7 +354,17 @@ bool white_space_not_present(char *p){ return *p != SPACE || *p != TAB; } - +/* +* +* @name str_to_dec +* +* @brief Converts a string into an integer +* +* @param const char *str +* +* @return int +* converted integer +*/ int str_to_dec(const char *str) { int val = 0; @@ -356,15 +373,19 @@ int str_to_dec(const char *str) for (int i = 0; str[i] != '\0'; i++) { char c = str[i]; - // Check if the character is a valid hexadecimal digit + // Check if the character is a valid decimal digit if (isdigit(c)) { val = (val * 10) + (c - '0'); - // } else if (isxdigit(c)) { - // val = (val << 4) + (toupper(c) - 'A' + 10); } else { // Invalid character return -1; } } return val; +} + +void user_prompt_sequence(void) +{ + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),DOLLAR); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),SPACE); } \ No newline at end of file From fb2760b129cc5f5a8b2aa2c27b3a4e15c516e9fa Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Mon, 17 Jul 2023 08:24:33 -0500 Subject: [PATCH 11/39] User prompt --- .../MAX78000/SDHC_FTHR/.vscode/settings.json | 6 +- Examples/MAX78000/SDHC_FTHR/README.md | 46 +++--- Examples/MAX78000/SDHC_FTHR/main.c | 18 +-- Examples/MAX78000/SDHC_FTHR/project.mk | 2 + Examples/MAX78000/SDHC_FTHR/user-cli.c | 130 ++++++++++++++++ Examples/MAX78000/SDHC_FTHR/user-cli.h | 73 +++++++++ Libraries/CLI/CLI.mk | 47 ++++++ .../SDHC_FTHR => Libraries/CLI}/src/cli.c | 146 +----------------- .../include => Libraries/CLI/src}/cli.h | 66 ++------ 9 files changed, 309 insertions(+), 225 deletions(-) create mode 100644 Examples/MAX78000/SDHC_FTHR/user-cli.c create mode 100644 Examples/MAX78000/SDHC_FTHR/user-cli.h create mode 100644 Libraries/CLI/CLI.mk rename {Examples/MAX78000/SDHC_FTHR => Libraries/CLI}/src/cli.c (68%) rename {Examples/MAX78000/SDHC_FTHR/include => Libraries/CLI/src}/cli.h (64%) diff --git a/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json b/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json index 6ff9a4e750b..4e94fed84c2 100755 --- a/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json +++ b/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json @@ -78,7 +78,11 @@ "files.associations": { "stdint.h": "c", "cli.h": "c", - "sdhc.h": "c" + "sdhc.h": "c", + "user-cli.h": "c", + "uart.h": "c", + "nvic_table.h": "c", + "cstdlib": "c" } } diff --git a/Examples/MAX78000/SDHC_FTHR/README.md b/Examples/MAX78000/SDHC_FTHR/README.md index 0796436e22a..4e329ab9b26 100644 --- a/Examples/MAX78000/SDHC_FTHR/README.md +++ b/Examples/MAX78000/SDHC_FTHR/README.md @@ -23,13 +23,12 @@ Universal instructions on building, flashing, and debugging this project can be ``` ***** MAX78000 SDHC FAT Filesystem Example ***** Card inserted. --->UART Initialized - -help +$ Help Size --> Find the Size of the SD Card and Free Space Format --> Format the Card -Mount --> Manually Mount Cardls --> list the contents of the current directory +Mount --> Manually Mount Card +ls --> list the contents of the current directory mkdir --> Create a directory file_create --> Create a file of random data cd --> Move into a directory @@ -38,11 +37,11 @@ Del --> Delete a file FatFs --> Format Card and Run Example of FatFS Operations Unmount --> Unmount card Help --> Prints a help message with info about all of the supported commands. - size +$ SIZE SD card mounted. Disk Size: 31163072 bytes -Available: 31163040 bytes -FORMAT +Available: 31163008 bytes +$ Format *****THE DRIVE WILL BE FORMATTED IN 5 SECONDS***** @@ -52,25 +51,30 @@ FORMATTING DRIVE Drive formatted. SD card mounted. SD card unmounted. -mount +$ mount SD card mounted. -mkdir Maxim +$ mkdir Analog_Devices Creating directory... -Directory Maxim created. -cd Maxim -Changed to Maxim -file_create ADI 30 -Creating file ADI with length 30 +Directory Analog_Devices created. +$ cd Analog_Devices +Changed to Analog_Devices +$ file_create Maxim 30 +Creating file Maxim with length 30 File opened! 30 bytes written to file! File Closed! -add_data ADI 22 +$ add_data Maxim 25 File opened! -22 bytes written to file +25 bytes written to file File closed. -Del ADI -Deleted file ADI -Fatfs +$ ls +Listing Contents of 0:/Analog_Devices - +0:/Analog_Devices/Maxim + +Finished listing contents +$ Del Maxim +Deleted file Maxim +$ fatfs *****THE DRIVE WILL BE FORMATTED IN 5 SECONDS***** @@ -89,9 +93,9 @@ Creating Directory... Renaming File... Attempting to read back file... Read Back 256 bytes -Message: loZStiUH7-HxP!fffB,q.DqMnI2nvSnchst.ZaLTpG'w8I7Tg0,l6VVkEsehp#IHSMZN94WDc'N#-0qkBlAil,'#DvMJZ!zzf,j?Lm,H5cbfFfVNHUlPF9GsTWbrop0EG7VV57qZZzjdvzJH5Xh2'82'fz9t#R,kzoaqYBJVRhrlD5W1mZItggYqyIICUvWOOppJQIVvt.BR0Vy4#YwqiNYj'jYnX8j7ePtuzJO?t-sTCGvibwYn81?Sutq'Q0s7 +Message: StiUH7-HxP!fffB,q.DqMnI2nvSnchst.ZaLTpG'w8I7Tg0,l6VVkEsehp#IHSMZN94WDc'N#-0qkBlAil,'#DvMJZ!zzf,j?Lm,H5cbfFfVNHUlPF9GsTWbrop0EG7VV57qZZzjdvzJH5Xh2'82'fz9t#R,kzoaqYBJVRhrlD5W1mZItggYqyIICUvWOOppJQIVvt.BR0Vy4#YwqiNYj'jYnX8j7ePtuzJO?t-sTCGvibwYn81?Sutq'Q0s7udL File Closed! -unmount +$ unmount SD card unmounted. ``` diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index 2be930a7f4d..f7bef85d687 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -51,11 +51,10 @@ #include "mxc_delay.h" #include "mxc_device.h" #include "gpio.h" -#include "uart.h" +#include "nvic_table.h" -#include "ff.h" -#include "sdhc.h" -#include "cli.h" +//#include "cli.h" +#include "user-cli.h" #include "nvic_table.h" #ifdef BOARD_EVKIT_V1 @@ -69,8 +68,8 @@ /****** Globals *********/ volatile int READ_FLAG; -volatile uint8_t RxData; -volatile mxc_uart_req_t read_req; +uint8_t RxData; +mxc_uart_req_t read_req; /******* Functions ********/ void UART_Handler(void) { @@ -132,8 +131,8 @@ int main(void) return error; } - printf("-->UART Initialized\n\n"); - user_prompt_sequence(); + //printf("-->UART Initialized"); + User_Prompt_Sequence(); read_req.uart = MXC_UART_GET_UART(CONSOLE_UART); read_req.rxData = &RxData; @@ -142,8 +141,7 @@ int main(void) read_req.callback = readCallback; error = MXC_UART_TransactionAsync(&read_req); - //while(1){} + while(1){} - //printf("End of example, please try to read the card.\n"); return 0; } diff --git a/Examples/MAX78000/SDHC_FTHR/project.mk b/Examples/MAX78000/SDHC_FTHR/project.mk index 08f6be48995..b5222a6bdf8 100644 --- a/Examples/MAX78000/SDHC_FTHR/project.mk +++ b/Examples/MAX78000/SDHC_FTHR/project.mk @@ -21,3 +21,5 @@ endif LIB_SDHC = 1 +include ${MAXIM_PATH}/Libraries/CLI/CLI.mk + diff --git a/Examples/MAX78000/SDHC_FTHR/user-cli.c b/Examples/MAX78000/SDHC_FTHR/user-cli.c new file mode 100644 index 00000000000..0552b4823b4 --- /dev/null +++ b/Examples/MAX78000/SDHC_FTHR/user-cli.c @@ -0,0 +1,130 @@ +/****************************************************************************** + * Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Maxim Integrated + * Products, Inc. shall not be used except as stated in the Maxim Integrated + * Products, Inc. Branding Policy. + * + * The mere transfer of this software does not imply any licenses + * of trade secrets, proprietary technology, copyrights, patents, + * trademarks, maskwork rights, or any other form of intellectual + * property whatsoever. Maxim Integrated Products, Inc. retains all + * ownership rights. + * + ******************************************************************************/ + + +#include "user-cli.h" + + +const command_table_t commands[] = {{"Size", handle_size, "Find the Size of the SD Card and Free Space\n\r"}, + {"Format", handle_format, "Format the Card\n\r"}, + {"Mount", hande_mount, "Manually Mount Card\n\r"}, + {"ls", handle_ls, "list the contents of the current directory\n\r"}, + {"mkdir", handle_mkdir, "Create a directory\n\r"}, + {"file_create", handle_createfile, "Create a file of random data\n\r"}, + {"cd", handle_cd, "Move into a directory\n\r"}, + {"add_data", handle_add_data, "Add random Data to an Existing File\n\r"}, + {"Del", handle_del, "Delete a file\n\r"}, + {"FatFs", handle_fatfs, "Format Card and Run Example of FatFS Operations\n\r"}, + {"Unmount", handle_unmount, "Unmount card\n\r"}, + {"Help", handle_help, "Prints a help message with info about all of the supported commands.\n\r"}}; + +const int num_commands = sizeof(commands) / sizeof(command_table_t); +/* + * @name handle_size + * + * @brief Finds the Size of the SD Card and Free Space + * + * @param argc and *argv[] + * + * + * @return + * void + */ +void handle_size(int argc, char *argv[]){ + getSize(); +} + +/* + * @name handle_format + * + * @brief han + * + * @param argc and *argv[] + * + * + * @return + * void + */ +void handle_format(int argc, char *argv[]){ + formatSDHC(); +} + +void hande_mount(int argc, char *argv[]){ + mount(); +} + +void handle_ls(int argc, char *argv[]){ + ls(); +} + +void handle_mkdir(int argc, char *argv[]){ + mkdir(argv[1]); +} + +void handle_createfile(int argc, char *argv[]){ + unsigned int length = atoi(argv[2]); + createFile(argv[1],length); +} + +void handle_cd(int argc, char *argv[]){ + cd(argv[1]); +} + +void handle_add_data(int argc, char *argv[]){ + unsigned int length = atoi(argv[2]); + appendFile(argv[1],length); +} + +void handle_del(int argc, char *argv[]){ + delete(argv[1]); +} + +void handle_fatfs(int argc, char *argv[]){ + example(); +} + +void handle_unmount(int argc, char *argv[]){ + umount(); + +} +/* @name handle_help + * + * @brief: Prints a help message with info about all of the supported commands. + * + */ +void handle_help(int argc, char *argv[]) +{ + printf("\n\r"); + for (int i = 0; i < num_commands;i++) + printf("%s --> %s", commands[i].name, commands[i].help_string); +} \ No newline at end of file diff --git a/Examples/MAX78000/SDHC_FTHR/user-cli.h b/Examples/MAX78000/SDHC_FTHR/user-cli.h new file mode 100644 index 00000000000..98bc32a9b3d --- /dev/null +++ b/Examples/MAX78000/SDHC_FTHR/user-cli.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Maxim Integrated + * Products, Inc. shall not be used except as stated in the Maxim Integrated + * Products, Inc. Branding Policy. + * + * The mere transfer of this software does not imply any licenses + * of trade secrets, proprietary technology, copyrights, patents, + * trademarks, maskwork rights, or any other form of intellectual + * property whatsoever. Maxim Integrated Products, Inc. retains all + * ownership rights. + * + ******************************************************************************/ + +/* -------------------------------------------------- */ +// INCLUDE GUARD +/* -------------------------------------------------- */ +#ifndef USER_CLI_H +#define USER_CLI_H + +/* -------------------------------------------------- */ +// INCLUDES +/* -------------------------------------------------- */ +#include "cli.h" +#include +#include "sdhc.h" +/* -------------------------------------------------- */ +// FUNCTION PROTOTYPES +/* -------------------------------------------------- */ +void handle_size(int argc, char *argv[]); + +void handle_format(int argc, char *argv[]); + +void hande_mount(int argc, char *argv[]); + +void handle_ls(int argc, char *argv[]); + +void handle_mkdir(int argc, char *argv[]); + +void handle_createfile(int argc, char *argv[]); + +void handle_cd(int argc, char *argv[]); + +void handle_add_data(int argc, char *argv[]); + +void handle_del(int argc, char *argv[]); + +void handle_fatfs(int argc, char *argv[]); + +void handle_unmount(int argc, char *argv[]); + +void handle_help(int argc, char *argv[]); + +#endif /* USER_CLI_H */ \ No newline at end of file diff --git a/Libraries/CLI/CLI.mk b/Libraries/CLI/CLI.mk new file mode 100644 index 00000000000..939d26187e7 --- /dev/null +++ b/Libraries/CLI/CLI.mk @@ -0,0 +1,47 @@ +################################################################################ + # Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved. + # + # Permission is hereby granted, free of charge, to any person obtaining a + # copy of this software and associated documentation files (the "Software"), + # to deal in the Software without restriction, including without limitation + # the rights to use, copy, modify, merge, publish, distribute, sublicense, + # and/or sell copies of the Software, and to permit persons to whom the + # Software is furnished to do so, subject to the following conditions: + # + # The above copyright notice and this permission notice shall be included + # in all copies or substantial portions of the Software. + # + # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + # IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES + # OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + # OTHER DEALINGS IN THE SOFTWARE. + # + # Except as contained in this notice, the name of Maxim Integrated + # Products, Inc. shall not be used except as stated in the Maxim Integrated + # Products, Inc. Branding Policy. + # + # The mere transfer of this software does not imply any licenses + # of trade secrets, proprietary technology, copyrights, patents, + # trademarks, maskwork rights, or any other form of intellectual + # property whatsoever. Maxim Integrated Products, Inc. retains all + # ownership rights. + # + ############################################################################### + +################################################################################ +# This file can be included in a project makefile to build the library for the +# project. +############################################################################### + +ifeq "$(CLI_DIR)" "" +# If CLI_DIR is not specified, this Makefile will locate itself. +CLI_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +endif + +IPATH += ${CLI_DIR}/src +VPATH += ${CLI_DIR}/src + +SRCS += cli.c diff --git a/Examples/MAX78000/SDHC_FTHR/src/cli.c b/Libraries/CLI/src/cli.c similarity index 68% rename from Examples/MAX78000/SDHC_FTHR/src/cli.c rename to Libraries/CLI/src/cli.c index eab50384568..85c2066bcdf 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -37,7 +37,6 @@ #include "cli.h" - /* -------------------------------------------------- */ // FUNCTION PROTOTYPES /* -------------------------------------------------- */ @@ -45,8 +44,9 @@ bool white_space_present(char *p); bool white_space_not_present(char *p); -char *cmd_history_str[100]; -// int cmd_history_len[100]; +/* -------------------------------------------------- */ +// GLOBALS +/* -------------------------------------------------- */ int cmd_idx = 0; char buf[256]; /* @@ -84,42 +84,11 @@ void line_accumlator(uint8_t user_char) MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),ENTER); buf[idx++] = '\r'; buf[idx] = '\0'; - - // cmd_history_len[cmd_idx] = idx; //Storing the length of the command into an array - cmd_history_str[cmd_idx++] = buf; //Storing the excecuted command into a buffer idx = 0; char *accum = buf; //Assign buf process_command(accum); break; } - case ARROW_KEY_CODE_LEFT: - case ARROW_KEY_CODE_RIGHT: - case ARROW_KEY_CODE_UP: - case ARROW_KEY_CODE_DOWN:{ - // Check the sequence to see if one of the arrow keys were pressed - if (buf[idx] == ARROW_KEY_CODE_2 && buf[idx - 1] == ARROW_KEY_CODE_1){ - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); - - if(user_char == ARROW_KEY_CODE_UP && cmd_idx > 0){ - //Erase what's currently on the prompt - //MXC_UART_Write(MXC_UART_GET_UART(CONSOLE_UART), ) - MXC_UART_Write(MXC_UART_GET_UART(CONSOLE_UART),cmd_history_str[--cmd_idx],strlen(cmd_history_str[cmd_idx])); - - //Empty current value of buf and assign respective cmd_history_str - idx = strlen(cmd_history_str[cmd_idx]); - - } - } - // This else statement is excecuted when the arrow keys weren't pressed and the user entered W,A,S or D keys - else { - if (idx < 255) { - buf[idx++] = user_char; //pushes characters into the buffer - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),user_char); - } - break; - } - } default: { // Handle all other characters if (idx < 255) { @@ -130,86 +99,6 @@ void line_accumlator(uint8_t user_char) } } } - -/* - * @name handle_size - * - * @brief Finds the Size of the SD Card and Free Space - * - * @param argc and *argv[] - * - * - * @return - * void - */ -void handle_size(int argc, char *argv[]){ - getSize(); -} - -/* - * @name handle_format - * - * @brief han - * - * @param argc and *argv[] - * - * - * @return - * void - */ -void handle_format(int argc, char *argv[]){ - formatSDHC(); -} - -void hande_mount(int argc, char *argv[]){ - mount(); -} - -void handle_ls(int argc, char *argv[]){ - ls(); -} - -void handle_mkdir(int argc, char *argv[]){ - mkdir(argv[1]); -} - -void handle_createfile(int argc, char *argv[]){ - unsigned int length = str_to_dec(argv[2]); - createFile(argv[1],length); -} - -void handle_cd(int argc, char *argv[]){ - cd(argv[1]); -} - -void handle_add_data(int argc, char *argv[]){ - unsigned int length = str_to_dec(argv[2]); - appendFile(argv[1],length); -} - -void handle_del(int argc, char *argv[]){ - delete(argv[1]); -} - -void handle_fatfs(int argc, char *argv[]){ - example(); -} - -void handle_unmount(int argc, char *argv[]){ - umount(); -} -/* @name handle_help - * - * @brief: Prints a help message with info about all of the supported commands. - * - */ -void handle_help(int argc, char *argv[]) -{ - printf("\n\r"); - for (int i = 0; i < num_commands;i++) - printf("%s --> %s", commands[i].name, commands[i].help_string); -} - /* * @name process_command * @@ -323,7 +212,7 @@ void process_command(char *input) } //Print prompt - user_prompt_sequence(); + User_Prompt_Sequence(); } /* * @name white_space_present @@ -356,35 +245,16 @@ bool white_space_not_present(char *p){ /* * -* @name str_to_dec +* @name User_Prompt_Sequence * -* @brief Converts a string into an integer +* @brief Prints out the user command prompt sequence o the uart console * -* @param const char *str +* @param void * * @return int * converted integer */ -int str_to_dec(const char *str) -{ - int val = 0; - - // Loop through each character in the string - for (int i = 0; str[i] != '\0'; i++) { - char c = str[i]; - - // Check if the character is a valid decimal digit - if (isdigit(c)) { - val = (val * 10) + (c - '0'); - } else { - // Invalid character - return -1; - } - } - return val; -} - -void user_prompt_sequence(void) +void User_Prompt_Sequence(void) { MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),DOLLAR); MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),SPACE); diff --git a/Examples/MAX78000/SDHC_FTHR/include/cli.h b/Libraries/CLI/src/cli.h similarity index 64% rename from Examples/MAX78000/SDHC_FTHR/include/cli.h rename to Libraries/CLI/src/cli.h index cb5e7b70b69..2f7996e9150 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/cli.h +++ b/Libraries/CLI/src/cli.h @@ -48,12 +48,11 @@ #include #include #include "board.h" -#include "sdhc.h" #include "led.h" #include "mxc_device.h" #include "mxc_errors.h" #include "uart.h" -#include "sdhc.h" +#include "user-cli.h" /* -------------------------------------------------- */ // MACROS @@ -65,16 +64,18 @@ #define BACKSPACE 0X08 #define MAXBUFF 2000 #define DELETE 0x7F -#define ARROW_KEY_CODE_1 0x1B -#define ARROW_KEY_CODE_2 0x5B -#define ARROW_KEY_CODE_LEFT 0x44 -#define ARROW_KEY_CODE_RIGHT 0x43 -#define ARROW_KEY_CODE_UP 0x41 -#define ARROW_KEY_CODE_DOWN 0x42 #define DOLLAR 0x24 + +/* -------------------------------------------------- */ +// FUNCTION PROTOTYPES +/* -------------------------------------------------- */ void line_accumlator(uint8_t user_char); +void process_command(char *input); + +void User_Prompt_Sequence(void); + // Command table hander prototype with parameters typedef void (*command_handler_t)(int, char *argv[]); @@ -86,40 +87,6 @@ typedef struct const char *help_string; } command_table_t; - -/* -------------------------------------------------- */ -// FUNCTION PROTOTYPES -/* -------------------------------------------------- */ -void handle_size(int argc, char *argv[]); - -void handle_format(int argc, char *argv[]); - -void hande_mount(int argc, char *argv[]); - -void handle_ls(int argc, char *argv[]); - -void handle_mkdir(int argc, char *argv[]); - -void handle_createfile(int argc, char *argv[]); - -void handle_cd(int argc, char *argv[]); - -void handle_add_data(int argc, char *argv[]); - -void handle_del(int argc, char *argv[]); - -void handle_fatfs(int argc, char *argv[]); - -void handle_unmount(int argc, char *argv[]); - -void handle_help(int argc, char *argv[]); - -void process_command(char *input); - -int str_to_dec(const char *str); - -void user_prompt_sequence(void); - /* * This table is an array of command_table_t structures that defines a set of supported commands in the program. * Each command_table_t structure contains the name of the command, a function pointer to the corresponding command handler function, @@ -127,21 +94,10 @@ void user_prompt_sequence(void); * * The structure of this lookup table makes it trivially easy to add a new command to this command processor. */ +extern const command_table_t commands[]; -static const command_table_t commands[] = {{"Size", handle_size, "Find the Size of the SD Card and Free Space\n\r"}, - {"Format", handle_format, "Format the Card\n\r"}, - {"Mount", hande_mount, "Manually Mount Card"}, - {"ls", handle_ls, "list the contents of the current directory\n\r"}, - {"mkdir", handle_mkdir, "Create a directory\n\r"}, - {"file_create", handle_createfile, "Create a file of random data\n\r"}, - {"cd", handle_cd, "Move into a directory\n\r"}, - {"add_data", handle_add_data, "Add random Data to an Existing File\n\r"}, - {"Del", handle_del, "Delete a file\n\r"}, - {"FatFs", handle_fatfs, "Format Card and Run Example of FatFS Operations\n\r"}, - {"Unmount", handle_unmount, "Unmount card\n\r"}, - {"Help", handle_help, "Prints a help message with info about all of the supported commands.\n\r"}}; //Calculates the number of commands based on commands and the command table -static const int num_commands = sizeof(commands) / sizeof(command_table_t); +extern const int num_commands; #endif /* EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_CLI_H_ */ \ No newline at end of file From 8dcfc5706bbc677987eeb5aabdfe5eb559014f41 Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Wed, 19 Jul 2023 10:37:20 -0500 Subject: [PATCH 12/39] Doxygen comments and Userguide update --- Libraries/CLI/src/cli.c | 164 ++++++++++++++++++++++------------------ Libraries/CLI/src/cli.h | 58 +++++++++++++- USERGUIDE.md | 6 ++ 3 files changed, 152 insertions(+), 76 deletions(-) diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index 85c2066bcdf..15b46e7cf95 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -31,64 +31,85 @@ * ******************************************************************************/\ -///* -------------------------------------------------- */ -//// INCLUDES -///* -------------------------------------------------- */ + +/* -------------------------------------------------- */ +// INCLUDES +/* -------------------------------------------------- */ #include "cli.h" /* -------------------------------------------------- */ // FUNCTION PROTOTYPES /* -------------------------------------------------- */ + +/** Checks if there is white space present in the given character pointer + * + * @param p + * character pointer containing the line accumulator input + * + * @return 1 if white space is present + * 0 if no white space is present + */ bool white_space_present(char *p); +/** Checks if there is no white space present in the given character pointer + * + * @param p + * character pointer containing the line accumulator input + * + * @return 1 if no white space is present + * 0 if white space is present + */ bool white_space_not_present(char *p); +void User_Prompt_Sequence(void); + +void Console_Backspace_Sequence(void); + +void Console_Cmd_Clear(void); + +void Clear_buffer(void); + /* -------------------------------------------------- */ // GLOBALS /* -------------------------------------------------- */ -int cmd_idx = 0; + +/*! \name buf + \brief Buffer to store the characters of the command +*/ char buf[256]; -/* - * @name line_accumlator - * - * @brief Reads incoming bytes, Accumulate into line buffer, Echo's chars back to the other side and handles backspace. - * It calls the process_command function upon pressing the ENTER key - * - * @param: - * uint8_t user_char - * User input of characters - */ +int idx = 0; + +// Define a buffer to store commands +#define MAX_COMMAND_LENGTH 256 + void line_accumlator(uint8_t user_char) { - // Declare static variables - static int idx = 0; switch (user_char) { case BACKSPACE: { // Handle Backspace and Delete if (idx > 0) { - //Sequence to actually implement a backspace on the terminal - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),SPACE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); + //Sequence to implement a backspace on the terminal + Console_Backspace_Sequence(); idx--; buf[idx] = '\0'; } break; } - case ENTER: { - // Handle Enter or carriage return - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),NEW_LINE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),ENTER); - buf[idx++] = '\r'; - buf[idx] = '\0'; - idx = 0; - char *accum = buf; //Assign buf - process_command(accum); - break; + case ENTER: { + // Handle Enter or carriage return + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),NEW_LINE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),ENTER); + buf[idx++] = '\r'; + buf[idx] = '\0'; + idx = 0; + process_command(buf); + Clear_buffer(); + break; } + default: { // Handle all other characters if (idx < 255) { @@ -99,18 +120,7 @@ void line_accumlator(uint8_t user_char) } } } -/* - * @name process_command - * - * @brief Performs a Lexical analysis and tokenisis the user's commands - * Lookup first token in a table of functions, dispatch to handler function - * - * @param char *input - * character pointer containing the line accumulator input - * - * @return - * void - */ + void process_command(char *input) { //Initialize p and end pointers @@ -214,48 +224,56 @@ void process_command(char *input) //Print prompt User_Prompt_Sequence(); } -/* - * @name white_space_present - * - * @brief Checks if white space is present in the given character pointer - * - * @param char *p - * character pointer containing the line accumulator input - * - * @return 1 if white space is present - * 0 if white space isn't present - */ + + + bool white_space_present(char *p){ return *p == SPACE || *p == TAB; } -/* - * @name white_space_not_present - * - * @brief Checks if there is no white space present in the given character pointer - * - * @param char *p - * character pointer containing the line accumulator input - * - * @return 1 if no white space is present - * 0 if white space is present - */ + + bool white_space_not_present(char *p){ return *p != SPACE || *p != TAB; } -/* +void User_Prompt_Sequence(void) +{ + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),DOLLAR); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),SPACE); +} + +/** Clears the buffer storing each character of the user command input * -* @name User_Prompt_Sequence +* @param void * -* @brief Prints out the user command prompt sequence o the uart console +* @return void +*/ +void Clear_buffer(void) { + for (int i = 0; i < MAX_COMMAND_LENGTH; i++) { + buf[i] = '\0'; + } +} + +/** CLears the line on the uart console * * @param void * -* @return int -* converted integer +* @return void */ -void User_Prompt_Sequence(void) -{ - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),DOLLAR); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),SPACE); +void Console_Cmd_Clear(void){ + for (int i = 0; i < idx; i++){ + Console_Backspace_Sequence(); + } +} + +/** Writes the backspace in sequence to the uart console +* +* @param void +* +* @return void +*/ +void Console_Backspace_Sequence(void){ + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),SPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); } \ No newline at end of file diff --git a/Libraries/CLI/src/cli.h b/Libraries/CLI/src/cli.h index 2f7996e9150..5c1f8a2a29f 100644 --- a/Libraries/CLI/src/cli.h +++ b/Libraries/CLI/src/cli.h @@ -31,6 +31,27 @@ * ******************************************************************************/ +/*! \file cli.c + \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 + +*/ + /* -------------------------------------------------- */ // INCLUDE GUARD /* -------------------------------------------------- */ @@ -67,13 +88,44 @@ #define DOLLAR 0x24 +#define ARROW_KEY_CODE_1 0x1B +#define ARROW_KEY_CODE_2 0x5B +#define ARROW_KEY_CODE_LEFT 0x44 +#define ARROW_KEY_CODE_RIGHT 0x43 +#define ARROW_KEY_CODE_UP 0x41 +#define ARROW_KEY_CODE_DOWN 0x42 + + /* -------------------------------------------------- */ // FUNCTION PROTOTYPES /* -------------------------------------------------- */ + +/** + * @brief Reads incoming bytes, Accumulate into line buffer, Echo's chars back to the other side and handles backspace. + * It calls the process_command function upon pressing the ENTER key + * + * @param user_char User input of each character + */ void line_accumlator(uint8_t user_char); +/** + * @name process_command + * + * @brief Performs a Lexical analysis and tokenisis the user's commands + * Lookup first token in a table of functions, dispatch to handler function + * + * @param input Character pointer containing the line accumulator input when enter key is pressed + * + * @return void + */ void process_command(char *input); +/** Prints out the user command prompt sequence o the uart console +* +* @param void +* +* @return void +*/ void User_Prompt_Sequence(void); // Command table hander prototype with parameters @@ -82,9 +134,9 @@ typedef void (*command_handler_t)(int, char *argv[]); //Command table structure typedef struct { - const char *name; - command_handler_t handler; - const char *help_string; + const char *name; /**< command string */ + command_handler_t handler; /**< function pointer of the handler function */ + const char *help_string; /**< help string of each command */ } command_table_t; /* diff --git a/USERGUIDE.md b/USERGUIDE.md index abe76bfb508..850456f5fa9 100644 --- a/USERGUIDE.md +++ b/USERGUIDE.md @@ -1832,3 +1832,9 @@ FreeRTOS is supported by all parts in the MSDK. See the `FreeRTOSDemo` example - [FreeRTOS-Plus-CLI](https://www.freertos.org/FreeRTOS-Plus/index.html): **Supported** - [FreeRTOS-Plus-TCP](https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/index.html): **Not supported** (Contributions welcome!) + + +### CLI + +The [`Libraries/CLI`](Libraries/CLI) folder of the MSDK contains a ready to use CLI feature which uses a 2-step process of a line accumulator and a process command function to perform the CLI operationgs. Refer to [`Examples/MAX78000/SDHC_FTHR`](Examples/MAX78000/SDHC_FTHR) for implementation. + From 0f2d99e030e40d66647d67a3400a036c865eed36 Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Wed, 19 Jul 2023 11:05:45 -0500 Subject: [PATCH 13/39] Resolve conflicts, fix Typos --- Examples/MAX78000/SDHC_FTHR/.vscode/settings.json | 3 ++- Libraries/CLI/src/cli.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json b/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json index 4e94fed84c2..c43bb35be67 100755 --- a/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json +++ b/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json @@ -82,7 +82,8 @@ "user-cli.h": "c", "uart.h": "c", "nvic_table.h": "c", - "cstdlib": "c" + "cstdlib": "c", + "adc_reva.h": "c" } } diff --git a/Libraries/CLI/src/cli.h b/Libraries/CLI/src/cli.h index 5c1f8a2a29f..b6a05e75f9a 100644 --- a/Libraries/CLI/src/cli.h +++ b/Libraries/CLI/src/cli.h @@ -31,7 +31,7 @@ * ******************************************************************************/ -/*! \file cli.c +/*! \file cli.h \brief A CLI Implementation using a command table to store command string, function pointer and a help string is dispatched. Details. From 3581e0f4cea0d4887c2b923314076313837748af Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Wed, 19 Jul 2023 16:51:26 +0000 Subject: [PATCH 14/39] clang-format bot reformatting. --- Examples/MAX78000/SDHC_FTHR/include/sdhc.h | 11 +-- Examples/MAX78000/SDHC_FTHR/main.c | 16 +-- Examples/MAX78000/SDHC_FTHR/src/sdhc.c | 13 ++- Examples/MAX78000/SDHC_FTHR/user-cli.c | 75 ++++++++------ Libraries/CLI/src/cli.c | 110 +++++++++------------ 5 files changed, 110 insertions(+), 115 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h index 527f566b478..7518e4ea0cc 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h +++ b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h @@ -54,7 +54,6 @@ #define TOSTRING(x) STRINGIFY(x) #define MAXLEN 256 - /***** FUNCTION PROTOTYPES *****/ void generateMessage(unsigned length); @@ -69,15 +68,15 @@ int getSize(); int ls(); -int createFile(char* file_name, unsigned int length); +int createFile(char *file_name, unsigned int length); -int appendFile(char* file_name, unsigned int length); +int appendFile(char *file_name, unsigned int length); -int mkdir(char* dir_name); +int mkdir(char *dir_name); -int cd(char* dir_name); +int cd(char *dir_name); -int delete(char* file_name); +int delete (char *file_name); int example(); diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index f7bef85d687..331e0cde6b8 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -61,7 +61,6 @@ #warning This example is not supported by the MAX78000EVKIT. #endif - /***** Definitions *****/ #define UART_BAUD 115200 #define BUFF_SIZE 1 @@ -76,9 +75,9 @@ void UART_Handler(void) MXC_UART_AsyncHandler(MXC_UART_GET_UART(CONSOLE_UART)); } -void readCallback(mxc_uart_req_t *req, int error){ - - line_accumlator(RxData); +void readCallback(mxc_uart_req_t *req, int error) +{ + line_accumlator(RxData); READ_FLAG = error; MXC_UART_TransactionAsync(req); } @@ -114,8 +113,9 @@ int main(void) waitCardInserted(); printf("Card inserted.\n"); - MXC_Delay(1000); //Delay inserted here to avoid weird printf values between previous and next printf command. - + MXC_Delay( + 1000); //Delay inserted here to avoid weird printf values between previous and next printf command. + // UART interrupt setup NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); NVIC_DisableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); @@ -130,7 +130,7 @@ int main(void) printf("-->Example Failed\n"); return error; } - + //printf("-->UART Initialized"); User_Prompt_Sequence(); @@ -141,7 +141,7 @@ int main(void) read_req.callback = readCallback; error = MXC_UART_TransactionAsync(&read_req); - while(1){} + while (1) {} return 0; } diff --git a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c index 4eba3b8eee9..8ebbf826582 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c +++ b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c @@ -179,7 +179,7 @@ int ls() return err; } -int createFile(char* file_name, unsigned int length) +int createFile(char *file_name, unsigned int length) { // unsigned int length = 128; @@ -224,9 +224,8 @@ int createFile(char* file_name, unsigned int length) return err; } -int appendFile(char* file_name, unsigned int length) +int appendFile(char *file_name, unsigned int length) { - if (!mounted) { mount(); } @@ -268,7 +267,7 @@ int appendFile(char* file_name, unsigned int length) return err; } -int mkdir(char* dir_name) +int mkdir(char *dir_name) { if (!mounted) { mount(); @@ -296,7 +295,7 @@ int mkdir(char* dir_name) return err; } -int cd(char* dir_name) +int cd(char *dir_name) { if (!mounted) { mount(); @@ -321,13 +320,13 @@ int cd(char* dir_name) return err; } -int delete(char* file_name) +int delete (char *file_name) { if (!mounted) { mount(); } - strcpy(filename,file_name); + strcpy(filename, file_name); if ((err = f_stat((const TCHAR *)filename, &fno)) == FR_NO_FILE) { printf("File or directory doesn't exist\n"); diff --git a/Examples/MAX78000/SDHC_FTHR/user-cli.c b/Examples/MAX78000/SDHC_FTHR/user-cli.c index 0552b4823b4..f6a33100deb 100644 --- a/Examples/MAX78000/SDHC_FTHR/user-cli.c +++ b/Examples/MAX78000/SDHC_FTHR/user-cli.c @@ -31,22 +31,23 @@ * ******************************************************************************/ - #include "user-cli.h" - -const command_table_t commands[] = {{"Size", handle_size, "Find the Size of the SD Card and Free Space\n\r"}, - {"Format", handle_format, "Format the Card\n\r"}, - {"Mount", hande_mount, "Manually Mount Card\n\r"}, - {"ls", handle_ls, "list the contents of the current directory\n\r"}, - {"mkdir", handle_mkdir, "Create a directory\n\r"}, - {"file_create", handle_createfile, "Create a file of random data\n\r"}, - {"cd", handle_cd, "Move into a directory\n\r"}, - {"add_data", handle_add_data, "Add random Data to an Existing File\n\r"}, - {"Del", handle_del, "Delete a file\n\r"}, - {"FatFs", handle_fatfs, "Format Card and Run Example of FatFS Operations\n\r"}, - {"Unmount", handle_unmount, "Unmount card\n\r"}, - {"Help", handle_help, "Prints a help message with info about all of the supported commands.\n\r"}}; +const command_table_t commands[] = { + { "Size", handle_size, "Find the Size of the SD Card and Free Space\n\r" }, + { "Format", handle_format, "Format the Card\n\r" }, + { "Mount", hande_mount, "Manually Mount Card\n\r" }, + { "ls", handle_ls, "list the contents of the current directory\n\r" }, + { "mkdir", handle_mkdir, "Create a directory\n\r" }, + { "file_create", handle_createfile, "Create a file of random data\n\r" }, + { "cd", handle_cd, "Move into a directory\n\r" }, + { "add_data", handle_add_data, "Add random Data to an Existing File\n\r" }, + { "Del", handle_del, "Delete a file\n\r" }, + { "FatFs", handle_fatfs, "Format Card and Run Example of FatFS Operations\n\r" }, + { "Unmount", handle_unmount, "Unmount card\n\r" }, + { "Help", handle_help, + "Prints a help message with info about all of the supported commands.\n\r" } +}; const int num_commands = sizeof(commands) / sizeof(command_table_t); /* @@ -60,7 +61,8 @@ const int num_commands = sizeof(commands) / sizeof(command_table_t); * @return * void */ -void handle_size(int argc, char *argv[]){ +void handle_size(int argc, char *argv[]) +{ getSize(); } @@ -75,47 +77,56 @@ void handle_size(int argc, char *argv[]){ * @return * void */ -void handle_format(int argc, char *argv[]){ +void handle_format(int argc, char *argv[]) +{ formatSDHC(); } -void hande_mount(int argc, char *argv[]){ +void hande_mount(int argc, char *argv[]) +{ mount(); } -void handle_ls(int argc, char *argv[]){ +void handle_ls(int argc, char *argv[]) +{ ls(); } -void handle_mkdir(int argc, char *argv[]){ +void handle_mkdir(int argc, char *argv[]) +{ mkdir(argv[1]); } -void handle_createfile(int argc, char *argv[]){ +void handle_createfile(int argc, char *argv[]) +{ unsigned int length = atoi(argv[2]); - createFile(argv[1],length); + createFile(argv[1], length); } -void handle_cd(int argc, char *argv[]){ +void handle_cd(int argc, char *argv[]) +{ cd(argv[1]); } -void handle_add_data(int argc, char *argv[]){ +void handle_add_data(int argc, char *argv[]) +{ unsigned int length = atoi(argv[2]); - appendFile(argv[1],length); + appendFile(argv[1], length); } -void handle_del(int argc, char *argv[]){ - delete(argv[1]); +void handle_del(int argc, char *argv[]) +{ + delete (argv[1]); } -void handle_fatfs(int argc, char *argv[]){ +void handle_fatfs(int argc, char *argv[]) +{ example(); } -void handle_unmount(int argc, char *argv[]){ +void handle_unmount(int argc, char *argv[]) +{ umount(); - } /* @name handle_help * @@ -124,7 +135,7 @@ void handle_unmount(int argc, char *argv[]){ */ void handle_help(int argc, char *argv[]) { - printf("\n\r"); - for (int i = 0; i < num_commands;i++) - printf("%s --> %s", commands[i].name, commands[i].help_string); + printf("\n\r"); + for (int i = 0; i < num_commands; i++) + printf("%s --> %s", commands[i].name, commands[i].help_string); } \ No newline at end of file diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index 15b46e7cf95..778ba1919ef 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -29,8 +29,7 @@ * property whatsoever. Maxim Integrated Products, Inc. retains all * ownership rights. * - ******************************************************************************/\ - + ******************************************************************************/ /* -------------------------------------------------- */ // INCLUDES @@ -85,39 +84,37 @@ int idx = 0; void line_accumlator(uint8_t user_char) { - switch (user_char) { - case BACKSPACE: - { + case BACKSPACE: { // Handle Backspace and Delete if (idx > 0) { - //Sequence to implement a backspace on the terminal + //Sequence to implement a backspace on the terminal Console_Backspace_Sequence(); idx--; buf[idx] = '\0'; } break; - } + } case ENTER: { // Handle Enter or carriage return - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),NEW_LINE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),ENTER); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), NEW_LINE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), ENTER); buf[idx++] = '\r'; buf[idx] = '\0'; idx = 0; - process_command(buf); + process_command(buf); Clear_buffer(); break; - } + } - default: { - // Handle all other characters - if (idx < 255) { - buf[idx++] = user_char; //pushes characters into the buffer - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),user_char); - } - break; + default: { + // Handle all other characters + if (idx < 255) { + buf[idx++] = user_char; //pushes characters into the buffer + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), user_char); } + break; + } } } @@ -128,67 +125,57 @@ void process_command(char *input) char *end; //Find end of string - for (end = input; *end != '\0'; end++); + for (end = input; *end != '\0'; end++) {} //Initialize variables bool in_token = false; bool beginning_space; - if(*p == SPACE || *p == TAB) + if (*p == SPACE || *p == TAB) beginning_space = true; char *argv[10]; int argc = 0; memset(argv, 0, sizeof(argv)); //Iterate over each character in input - for (p = input; p < end; p++) - { + for (p = input; p < end; p++) { //If in a token - if (in_token) - { + if (in_token) { //If whitespace found, token ends - if (white_space_present(p)) - { + if (white_space_present(p)) { in_token = true; } //Else, token continues - else if(white_space_not_present(p)) - { + else if (white_space_not_present(p)) { input = p; in_token = false; } } //If not in a token - else - { + else { //If ENTER or whitespace found - if(*p == ENTER || (white_space_present(p))) - { + if (*p == ENTER || (white_space_present(p))) { //If ENTER, end of command - if(*p == ENTER) - { + if (*p == ENTER) { *p = '\0'; argv[argc++] = input; break; } //If whitespace found, end of token - if(white_space_present(p) && !beginning_space) - { + if (white_space_present(p) && !beginning_space) { *p = '\0'; argv[argc++] = input; in_token = true; } } //If not in token and whitespace not found, new token starts - else if (white_space_not_present(p) && beginning_space) - { + else if (white_space_not_present(p) && beginning_space) { input = p; in_token = false; beginning_space = false; } //If not in token and whitespace not found and beginning_space is false, token continues - else if (white_space_not_present(p) && !beginning_space) - { + else if (white_space_not_present(p) && !beginning_space) { in_token = false; } } @@ -204,10 +191,8 @@ void process_command(char *input) bool success_flag = 0; //True if input command matches //Iterate over all commands to check if input command matches - for (int i=0; i < num_commands; i++) - { - if (strcasecmp(argv[0], commands[i].name) == 0) - { + for (int i = 0; i < num_commands; i++) { + if (strcasecmp(argv[0], commands[i].name) == 0) { //Call corresponding command's handler commands[i].handler(argc, argv); success_flag = 1; @@ -216,8 +201,7 @@ void process_command(char *input) } //If no commands match, print error message - if (success_flag == 0) - { + if (success_flag == 0) { printf("\n\rCommand isn't valid!\n\r"); } @@ -225,21 +209,20 @@ void process_command(char *input) User_Prompt_Sequence(); } - - -bool white_space_present(char *p){ - return *p == SPACE || *p == TAB; +bool white_space_present(char *p) +{ + return *p == SPACE || *p == TAB; } - -bool white_space_not_present(char *p){ - return *p != SPACE || *p != TAB; +bool white_space_not_present(char *p) +{ + return *p != SPACE || *p != TAB; } void User_Prompt_Sequence(void) { - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),DOLLAR); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),SPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), DOLLAR); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), SPACE); } /** Clears the buffer storing each character of the user command input @@ -248,7 +231,8 @@ void User_Prompt_Sequence(void) * * @return void */ -void Clear_buffer(void) { +void Clear_buffer(void) +{ for (int i = 0; i < MAX_COMMAND_LENGTH; i++) { buf[i] = '\0'; } @@ -260,8 +244,9 @@ void Clear_buffer(void) { * * @return void */ -void Console_Cmd_Clear(void){ - for (int i = 0; i < idx; i++){ +void Console_Cmd_Clear(void) +{ + for (int i = 0; i < idx; i++) { Console_Backspace_Sequence(); } } @@ -272,8 +257,9 @@ void Console_Cmd_Clear(void){ * * @return void */ -void Console_Backspace_Sequence(void){ - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),SPACE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); +void Console_Backspace_Sequence(void) +{ + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), BACKSPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), SPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), BACKSPACE); } \ No newline at end of file From 180bac7b9c37b795f9e3d7e6e2106a13f02cf64a Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Mon, 24 Jul 2023 09:24:51 -0500 Subject: [PATCH 15/39] CLI library ReadMe and CLI_UART added --- Examples/MAX78000/SDHC_FTHR/include/sdhc.h | 2 - Examples/MAX78000/SDHC_FTHR/main.c | 58 ++----------- Examples/MAX78000/SDHC_FTHR/user-cli.c | 35 +------- Examples/MAX78000/SDHC_FTHR/user-cli.h | 3 +- Libraries/CLI/CLI.mk | 9 +- Libraries/CLI/README.md | 87 +++++++++++++++++++ Libraries/CLI/{src => inc}/cli.h | 57 +++++++------ Libraries/CLI/inc/cli_uart.h | 45 ++++++++++ Libraries/CLI/res/CLI-Processing-steps.png | Bin 0 -> 349303 bytes Libraries/CLI/src/cli.c | 47 +++++------ Libraries/CLI/src/cli_uart.c | 93 +++++++++++++++++++++ 11 files changed, 295 insertions(+), 141 deletions(-) create mode 100644 Libraries/CLI/README.md rename Libraries/CLI/{src => inc}/cli.h (73%) create mode 100644 Libraries/CLI/inc/cli_uart.h create mode 100644 Libraries/CLI/res/CLI-Processing-steps.png create mode 100644 Libraries/CLI/src/cli_uart.c diff --git a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h index 527f566b478..56da1b767fa 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h +++ b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h @@ -44,9 +44,7 @@ #include "mxc_device.h" #include "gpio.h" #include "uart.h" - #include "ff.h" -#include "cli.h" /***** Definitions *****/ diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index f7bef85d687..0136b15c687 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -36,7 +36,8 @@ * @brief read and write sdhc * @details This example uses the sdhc and ffat to read/write the file system on * an SD card. The Fat library used supports long filenames (see ffconf.h) - * the max length is 256 characters. + * the max length is 256 characters. It uses the CLI library for taking user + * user commands. * * You must connect an sd card to the sd card slot. */ @@ -52,36 +53,14 @@ #include "mxc_device.h" #include "gpio.h" #include "nvic_table.h" - -//#include "cli.h" -#include "user-cli.h" -#include "nvic_table.h" +#include "ff.h" +#include "cli_uart.h" +#include "sdhc.h" #ifdef BOARD_EVKIT_V1 #warning This example is not supported by the MAX78000EVKIT. #endif - -/***** Definitions *****/ -#define UART_BAUD 115200 -#define BUFF_SIZE 1 - -/****** Globals *********/ -volatile int READ_FLAG; -uint8_t RxData; -mxc_uart_req_t read_req; -/******* Functions ********/ -void UART_Handler(void) -{ - MXC_UART_AsyncHandler(MXC_UART_GET_UART(CONSOLE_UART)); -} - -void readCallback(mxc_uart_req_t *req, int error){ - - line_accumlator(RxData); - READ_FLAG = error; - MXC_UART_TransactionAsync(req); -} extern TCHAR *FF_ERRORS[20]; /******************************************************************************/ @@ -116,32 +95,9 @@ int main(void) printf("Card inserted.\n"); MXC_Delay(1000); //Delay inserted here to avoid weird printf values between previous and next printf command. - // UART interrupt setup - NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); - NVIC_DisableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); - MXC_NVIC_SetVector(MXC_UART_GET_IRQ(CONSOLE_UART), UART_Handler); - NVIC_EnableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); - - /* Initialize Console UART*/ - int error; - if ((error = MXC_UART_Init(MXC_UART_GET_UART(CONSOLE_UART), UART_BAUD, MXC_UART_APB_CLK)) != - E_NO_ERROR) { - printf("-->Error initializing UART: %d\n", error); - printf("-->Example Failed\n"); - return error; - } - - //printf("-->UART Initialized"); - User_Prompt_Sequence(); - - read_req.uart = MXC_UART_GET_UART(CONSOLE_UART); - read_req.rxData = &RxData; - read_req.rxLen = BUFF_SIZE; - read_req.txLen = 0; - read_req.callback = readCallback; - - error = MXC_UART_TransactionAsync(&read_req); + if(MXC_CLI_Uart_Init() != E_NO_ERROR); while(1){} + return 0; } diff --git a/Examples/MAX78000/SDHC_FTHR/user-cli.c b/Examples/MAX78000/SDHC_FTHR/user-cli.c index 0552b4823b4..16cf6e462ce 100644 --- a/Examples/MAX78000/SDHC_FTHR/user-cli.c +++ b/Examples/MAX78000/SDHC_FTHR/user-cli.c @@ -49,32 +49,12 @@ const command_table_t commands[] = {{"Size", handle_size, "Find the Size of the {"Help", handle_help, "Prints a help message with info about all of the supported commands.\n\r"}}; const int num_commands = sizeof(commands) / sizeof(command_table_t); -/* - * @name handle_size - * - * @brief Finds the Size of the SD Card and Free Space - * - * @param argc and *argv[] - * - * - * @return - * void - */ + void handle_size(int argc, char *argv[]){ getSize(); } -/* - * @name handle_format - * - * @brief han - * - * @param argc and *argv[] - * - * - * @return - * void - */ + void handle_format(int argc, char *argv[]){ formatSDHC(); } @@ -117,14 +97,3 @@ void handle_unmount(int argc, char *argv[]){ umount(); } -/* @name handle_help - * - * @brief: Prints a help message with info about all of the supported commands. - * - */ -void handle_help(int argc, char *argv[]) -{ - printf("\n\r"); - for (int i = 0; i < num_commands;i++) - printf("%s --> %s", commands[i].name, commands[i].help_string); -} \ No newline at end of file diff --git a/Examples/MAX78000/SDHC_FTHR/user-cli.h b/Examples/MAX78000/SDHC_FTHR/user-cli.h index 98bc32a9b3d..22a03a5a9f0 100644 --- a/Examples/MAX78000/SDHC_FTHR/user-cli.h +++ b/Examples/MAX78000/SDHC_FTHR/user-cli.h @@ -37,6 +37,8 @@ #ifndef USER_CLI_H #define USER_CLI_H +//mxc_cli_config.c/.h + /* -------------------------------------------------- */ // INCLUDES /* -------------------------------------------------- */ @@ -68,6 +70,5 @@ void handle_fatfs(int argc, char *argv[]); void handle_unmount(int argc, char *argv[]); -void handle_help(int argc, char *argv[]); #endif /* USER_CLI_H */ \ No newline at end of file diff --git a/Libraries/CLI/CLI.mk b/Libraries/CLI/CLI.mk index 939d26187e7..1ddfe91874d 100644 --- a/Libraries/CLI/CLI.mk +++ b/Libraries/CLI/CLI.mk @@ -41,7 +41,12 @@ ifeq "$(CLI_DIR)" "" CLI_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) endif -IPATH += ${CLI_DIR}/src +IPATH += ${CLI_DIR}/inc VPATH += ${CLI_DIR}/src -SRCS += cli.c +VPATH += $(dir $(SRCS)) + +# Use absolute paths if building within eclipse environment. +ifeq "$(ECLIPSE)" "1" +SRCS := $(abspath $(SRCS)) +endif diff --git a/Libraries/CLI/README.md b/Libraries/CLI/README.md new file mode 100644 index 00000000000..3522239af8a --- /dev/null +++ b/Libraries/CLI/README.md @@ -0,0 +1,87 @@ +# Command Line Interface + +## 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 on device to + +* Allows developers to get diagnostics and change device parameters interactively + +* Also lend itself to easy automated testing of the device, through PC-side scripting + +The current features include the following + +- Case insensitive commands & arguments +- Can have spaces at beginning or end of string +- Can have multiple spaces between words +- Backspace +- Arrow keys +- Tab completion + + +## Software +Point 2 seperate input and output. +Change the formatting update ADI style guide +![Processing steps](res/CLI-Processing-steps.png) + +(Above: Describes the steps in which the CLI command processes are being excecuted) + +The library is present in the `Libraries/CLI` folder of the MSDK. + +### Project Usage + +### Enabling the library +Go to project.mk file and include the path to the MAXIM SDK CLI Libaries as this below line + +``` +include ${MAXIM_PATH}/Libraries/CLI/CLI.mk +``` + +## Porting Guide + +The CLI library excepts the user to implement the following steps + +### Instructions + +1. Define an array commands[] of type const command_table_t to contain the name of the command, a function pointer to the corresponding command handler function, +nd the help string which provides a short description of what the command does. + +``` +const command_table_t commands[] = {{"Mount", handle_mount, "Manually Mount Card"}, + {"Format", handle_format, "Format the Card\n\r"}} +``` + +3. Define and initialize an integer num_commands to the sizeof your command_table_t structure instance i.e commands divided by the sizeof the structure. Giving the number of commands. + We will need this to iterate through all the command in your command_table. + +``` +const int num_commands = sizeof(commands) / sizeof(command_table_t); +``` + +4. Each handler function must have the same prototype. + +- argc is used to determine which token element of the commands string is being used. +- argv[] is a character array of tokens of the command string entered by the user. + +Suppose a user entered the command +``` +mkdir new_folder +``` +The Lexical Analysis performed by the CLI library tokenizes mkdir and assigns it to argv[0]. new_folder is assigned to argv[1]. + +The define command handler function as below. + +``` +void handle_help(int argc, char *argv[]) +``` +Write the functions in user-cli.c and give a function protoype in user-cli.h so it is accessable by the CLI library when it needs to dispatch the handlers + +5. You can change the name of your file to something other than user-cli.c/.h but make sure that you also change the headers in [Libraries/CLI/src/cli.h](Libraries/CLI/src/cli.h) to the your newly named header file. + + + + + diff --git a/Libraries/CLI/src/cli.h b/Libraries/CLI/inc/cli.h similarity index 73% rename from Libraries/CLI/src/cli.h rename to Libraries/CLI/inc/cli.h index b6a05e75f9a..5792eecdaf7 100644 --- a/Libraries/CLI/src/cli.h +++ b/Libraries/CLI/inc/cli.h @@ -55,8 +55,8 @@ /* -------------------------------------------------- */ // INCLUDE GUARD /* -------------------------------------------------- */ -#ifndef EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_CLI_H_ -#define EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_CLI_H_ +#ifndef MXC_CLI_H +#define MXC_CLI_H /* -------------------------------------------------- */ // INCLUDES @@ -73,7 +73,7 @@ #include "mxc_device.h" #include "mxc_errors.h" #include "uart.h" -#include "user-cli.h" +#include "user-cli.h" // Change to user implementation header /* -------------------------------------------------- */ // MACROS @@ -87,7 +87,6 @@ #define DELETE 0x7F #define DOLLAR 0x24 - #define ARROW_KEY_CODE_1 0x1B #define ARROW_KEY_CODE_2 0x5B #define ARROW_KEY_CODE_LEFT 0x44 @@ -100,19 +99,15 @@ // FUNCTION PROTOTYPES /* -------------------------------------------------- */ -/** - * @brief Reads incoming bytes, Accumulate into line buffer, Echo's chars back to the other side and handles backspace. +/** Reads incoming bytes, Accumulate into line buffer, Echo's chars back to the other side and handles backspace. * It calls the process_command function upon pressing the ENTER key * * @param user_char User input of each character */ -void line_accumlator(uint8_t user_char); +void line_accumulator(uint8_t user_char); -/** - * @name process_command - * - * @brief Performs a Lexical analysis and tokenisis the user's commands - * Lookup first token in a table of functions, dispatch to handler function +/** Performs a Lexical analysis and tokenisis the user's commands + * Lookup first token in a table of functions, dispatch to handler function * * @param input Character pointer containing the line accumulator input when enter key is pressed * @@ -120,18 +115,30 @@ void line_accumlator(uint8_t user_char); */ void process_command(char *input); -/** Prints out the user command prompt sequence o the uart console +/** Prints the help string of each command from the command table * -* @param void +* @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. * * @return void */ -void User_Prompt_Sequence(void); +void handle_help(int argc, char *argv[]); -// Command table hander prototype with parameters + +/** Command table hander prototype with parameters + * + * @param argc Used to determine which token element of the commands string is being used. + * + * @param argv[] Char Array of tokens of the command string entered by the user. + * */ typedef void (*command_handler_t)(int, char *argv[]); -//Command table structure +/** This command table structure contains the name of the command, a function pointer to the corresponding command handler function, + * and the help string which provides a short description of what the command does. + * + * */ typedef struct { const char *name; /**< command string */ @@ -139,17 +146,15 @@ typedef struct const char *help_string; /**< help string of each command */ } command_table_t; -/* - * This table is an array of command_table_t structures that defines a set of supported commands in the program. - * Each command_table_t structure contains the name of the command, a function pointer to the corresponding command handler function, - * and a short description of what the command does. +/** This table is an array of command_table_t structures which should be initialized by the user in user-cli.c to define a set of supported commands in the program. + * The structure of this lookup table makes it trivially easy to add a new command to this command processor. * - * The structure of this lookup table makes it trivially easy to add a new command to this command processor. - */ + * */ extern const command_table_t commands[]; -//Calculates the number of commands based on commands and the command table +/** Calculates the number of commands based on commands and the command table + * + * */ extern const int num_commands; - -#endif /* EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_CLI_H_ */ \ No newline at end of file +#endif /* MXC_CLI_H */ \ No newline at end of file diff --git a/Libraries/CLI/inc/cli_uart.h b/Libraries/CLI/inc/cli_uart.h new file mode 100644 index 00000000000..6c22483d355 --- /dev/null +++ b/Libraries/CLI/inc/cli_uart.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Maxim Integrated + * Products, Inc. shall not be used except as stated in the Maxim Integrated + * Products, Inc. Branding Policy. + * + * The mere transfer of this software does not imply any licenses + * of trade secrets, proprietary technology, copyrights, patents, + * trademarks, maskwork rights, or any other form of intellectual + * property whatsoever. Maxim Integrated Products, Inc. retains all + * ownership rights. + * + ******************************************************************************/ + + +#ifndef CLI_UART_HEADER +#define CLI_UART_HEADER + +#include "uart.h" +#include "nvic_table.h" +#include "cli.h" +#include "board.h" + +int MXC_CLI_Uart_Init(void); + +#endif //CLI_UART_HEADER \ No newline at end of file diff --git a/Libraries/CLI/res/CLI-Processing-steps.png b/Libraries/CLI/res/CLI-Processing-steps.png new file mode 100644 index 0000000000000000000000000000000000000000..9de200d925af390963652329040ba91ef3262f9e GIT binary patch literal 349303 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

Vb72xm=KgGt(i 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

Knc3exaI78oumi+B<e%|^CvibP4 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�zg=BUD7;2IRX|JRZ82UsqfA|Mr;Om< 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 literal 0 HcmV?d00001 diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index 15b46e7cf95..bda12f1d3be 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -38,28 +38,16 @@ #include "cli.h" +// Define a buffer length to store commands +#define MAX_COMMAND_LENGTH 256 + /* -------------------------------------------------- */ // FUNCTION PROTOTYPES /* -------------------------------------------------- */ -/** Checks if there is white space present in the given character pointer - * - * @param p - * character pointer containing the line accumulator input - * - * @return 1 if white space is present - * 0 if no white space is present - */ + bool white_space_present(char *p); -/** Checks if there is no white space present in the given character pointer - * - * @param p - * character pointer containing the line accumulator input - * - * @return 1 if no white space is present - * 0 if white space is present - */ bool white_space_not_present(char *p); void User_Prompt_Sequence(void); @@ -77,13 +65,14 @@ void Clear_buffer(void); /*! \name buf \brief Buffer to store the characters of the command */ -char buf[256]; -int idx = 0; +char buf[MAX_COMMAND_LENGTH]; -// Define a buffer to store commands -#define MAX_COMMAND_LENGTH 256 +/*! \name idx + \brief Idx to keep track of the buffer which stores the characters of the command +*/ +uint16_t idx = 0; -void line_accumlator(uint8_t user_char) +void line_accumulator(uint8_t user_char) { switch (user_char) { @@ -112,7 +101,7 @@ void line_accumlator(uint8_t user_char) default: { // Handle all other characters - if (idx < 255) { + if (idx < MAX_COMMAND_LENGTH - 1) { buf[idx++] = user_char; //pushes characters into the buffer MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),user_char); } @@ -249,12 +238,10 @@ void User_Prompt_Sequence(void) * @return void */ void Clear_buffer(void) { - for (int i = 0; i < MAX_COMMAND_LENGTH; i++) { - buf[i] = '\0'; - } + memset(buf, '\0', MAX_COMMAND_LENGTH); } -/** CLears the line on the uart console +/** Clears the line on the uart console * * @param void * @@ -276,4 +263,12 @@ void Console_Backspace_Sequence(void){ MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),SPACE); MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); +} + + +void handle_help(int argc, char *argv[]) +{ + printf("\n\r"); + for (int i = 0; i < num_commands;i++) + printf("%s --> %s", commands[i].name, commands[i].help_string); } \ No newline at end of file diff --git a/Libraries/CLI/src/cli_uart.c b/Libraries/CLI/src/cli_uart.c new file mode 100644 index 00000000000..ec29a6c0086 --- /dev/null +++ b/Libraries/CLI/src/cli_uart.c @@ -0,0 +1,93 @@ + +/****************************************************************************** + * Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Maxim Integrated + * Products, Inc. shall not be used except as stated in the Maxim Integrated + * Products, Inc. Branding Policy. + * + * The mere transfer of this software does not imply any licenses + * of trade secrets, proprietary technology, copyrights, patents, + * trademarks, maskwork rights, or any other form of intellectual + * property whatsoever. Maxim Integrated Products, Inc. retains all + * ownership rights. + * + ******************************************************************************/ + +#include "cli_uart.h" + +/***** Definitions *****/ +#define UART_BAUD 115200 +#define BUFF_SIZE 1 + +/****** Globals *********/ +volatile int READ_FLAG; +uint8_t RxData; +mxc_uart_req_t read_req; + +/******* Functions ********/ +void UART_Handler(void) +{ + MXC_UART_AsyncHandler(MXC_UART_GET_UART(CONSOLE_UART)); +} + +void readCallback(mxc_uart_req_t *req, int error){ + + line_accumulator(RxData); + READ_FLAG = error; + MXC_UART_TransactionAsync(req); +} + +/** Initializes an asychronous uart transaction for CLI operations. This enables the console read + * + * @param none + * + * @return void + */ +int MXC_CLI_Uart_Init(void){ + + // UART interrupt setup + NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); + MXC_NVIC_SetVector(MXC_UART_GET_IRQ(CONSOLE_UART), UART_Handler); + NVIC_EnableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); + + /* Initialize Console UART*/ + int error; + if ((error = MXC_UART_Init(MXC_UART_GET_UART(CONSOLE_UART), UART_BAUD, MXC_UART_APB_CLK)) != + E_NO_ERROR) { + printf("-->Error initializing UART: %d\n", error); + printf("-->Example Failed\n"); + return error; + } + + User_Prompt_Sequence(); + + read_req.uart = MXC_UART_GET_UART(CONSOLE_UART); + read_req.rxData = &RxData; + read_req.rxLen = BUFF_SIZE; + read_req.txLen = 0; + read_req.callback = readCallback; + + error = MXC_UART_TransactionAsync(&read_req); + + return error; +} \ No newline at end of file From f39afce62d7d672f100bfe6cb635f67172de62c3 Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Mon, 24 Jul 2023 10:47:36 -0500 Subject: [PATCH 16/39] Readme cli and cli_uart --- Examples/MAX78000/SDHC_FTHR/Makefile | 2 +- Examples/MAX78000/SDHC_FTHR/include/sdhc.h | 11 +-- Examples/MAX78000/SDHC_FTHR/main.c | 1 + Examples/MAX78000/SDHC_FTHR/src/sdhc.c | 13 +-- Examples/MAX78000/SDHC_FTHR/user-cli.c | 64 ++++++-------- Libraries/CLI/src/cli.c | 98 +++++++++++++--------- 6 files changed, 99 insertions(+), 90 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/Makefile b/Examples/MAX78000/SDHC_FTHR/Makefile index ed8289f7c63..e28d58f8f45 100644 --- a/Examples/MAX78000/SDHC_FTHR/Makefile +++ b/Examples/MAX78000/SDHC_FTHR/Makefile @@ -72,7 +72,7 @@ TARGET_LC := $(subst M,m,$(subst A,a,$(subst X,x,$(TARGET)))) endif # Default board. -BOARD ?= FTHR_RevA +BOARD ?= EvKit_V1 # ******************************************************************************* # Locate the MaximSDK diff --git a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h index 0bee3b5b2d6..56da1b767fa 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h +++ b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h @@ -52,6 +52,7 @@ #define TOSTRING(x) STRINGIFY(x) #define MAXLEN 256 + /***** FUNCTION PROTOTYPES *****/ void generateMessage(unsigned length); @@ -66,15 +67,15 @@ int getSize(); int ls(); -int createFile(char *file_name, unsigned int length); +int createFile(char* file_name, unsigned int length); -int appendFile(char *file_name, unsigned int length); +int appendFile(char* file_name, unsigned int length); -int mkdir(char *dir_name); +int mkdir(char* dir_name); -int cd(char *dir_name); +int cd(char* dir_name); -int delete (char *file_name); +int delete(char* file_name); int example(); diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index e83de75b732..0136b15c687 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -98,5 +98,6 @@ int main(void) if(MXC_CLI_Uart_Init() != E_NO_ERROR); while(1){} + return 0; } diff --git a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c index 8ebbf826582..4eba3b8eee9 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c +++ b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c @@ -179,7 +179,7 @@ int ls() return err; } -int createFile(char *file_name, unsigned int length) +int createFile(char* file_name, unsigned int length) { // unsigned int length = 128; @@ -224,8 +224,9 @@ int createFile(char *file_name, unsigned int length) return err; } -int appendFile(char *file_name, unsigned int length) +int appendFile(char* file_name, unsigned int length) { + if (!mounted) { mount(); } @@ -267,7 +268,7 @@ int appendFile(char *file_name, unsigned int length) return err; } -int mkdir(char *dir_name) +int mkdir(char* dir_name) { if (!mounted) { mount(); @@ -295,7 +296,7 @@ int mkdir(char *dir_name) return err; } -int cd(char *dir_name) +int cd(char* dir_name) { if (!mounted) { mount(); @@ -320,13 +321,13 @@ int cd(char *dir_name) return err; } -int delete (char *file_name) +int delete(char* file_name) { if (!mounted) { mount(); } - strcpy(filename, file_name); + strcpy(filename,file_name); if ((err = f_stat((const TCHAR *)filename, &fno)) == FR_NO_FILE) { printf("File or directory doesn't exist\n"); diff --git a/Examples/MAX78000/SDHC_FTHR/user-cli.c b/Examples/MAX78000/SDHC_FTHR/user-cli.c index 6ed700d2b9b..16cf6e462ce 100644 --- a/Examples/MAX78000/SDHC_FTHR/user-cli.c +++ b/Examples/MAX78000/SDHC_FTHR/user-cli.c @@ -31,23 +31,22 @@ * ******************************************************************************/ + #include "user-cli.h" -const command_table_t commands[] = { - { "Size", handle_size, "Find the Size of the SD Card and Free Space\n\r" }, - { "Format", handle_format, "Format the Card\n\r" }, - { "Mount", hande_mount, "Manually Mount Card\n\r" }, - { "ls", handle_ls, "list the contents of the current directory\n\r" }, - { "mkdir", handle_mkdir, "Create a directory\n\r" }, - { "file_create", handle_createfile, "Create a file of random data\n\r" }, - { "cd", handle_cd, "Move into a directory\n\r" }, - { "add_data", handle_add_data, "Add random Data to an Existing File\n\r" }, - { "Del", handle_del, "Delete a file\n\r" }, - { "FatFs", handle_fatfs, "Format Card and Run Example of FatFS Operations\n\r" }, - { "Unmount", handle_unmount, "Unmount card\n\r" }, - { "Help", handle_help, - "Prints a help message with info about all of the supported commands.\n\r" } -}; + +const command_table_t commands[] = {{"Size", handle_size, "Find the Size of the SD Card and Free Space\n\r"}, + {"Format", handle_format, "Format the Card\n\r"}, + {"Mount", hande_mount, "Manually Mount Card\n\r"}, + {"ls", handle_ls, "list the contents of the current directory\n\r"}, + {"mkdir", handle_mkdir, "Create a directory\n\r"}, + {"file_create", handle_createfile, "Create a file of random data\n\r"}, + {"cd", handle_cd, "Move into a directory\n\r"}, + {"add_data", handle_add_data, "Add random Data to an Existing File\n\r"}, + {"Del", handle_del, "Delete a file\n\r"}, + {"FatFs", handle_fatfs, "Format Card and Run Example of FatFS Operations\n\r"}, + {"Unmount", handle_unmount, "Unmount card\n\r"}, + {"Help", handle_help, "Prints a help message with info about all of the supported commands.\n\r"}}; const int num_commands = sizeof(commands) / sizeof(command_table_t); @@ -57,53 +56,44 @@ void handle_size(int argc, char *argv[]){ void handle_format(int argc, char *argv[]){ - formatSDHC(); } -void hande_mount(int argc, char *argv[]) -{ +void hande_mount(int argc, char *argv[]){ mount(); } -void handle_ls(int argc, char *argv[]) -{ +void handle_ls(int argc, char *argv[]){ ls(); } -void handle_mkdir(int argc, char *argv[]) -{ +void handle_mkdir(int argc, char *argv[]){ mkdir(argv[1]); } -void handle_createfile(int argc, char *argv[]) -{ +void handle_createfile(int argc, char *argv[]){ unsigned int length = atoi(argv[2]); - createFile(argv[1], length); + createFile(argv[1],length); } -void handle_cd(int argc, char *argv[]) -{ +void handle_cd(int argc, char *argv[]){ cd(argv[1]); } -void handle_add_data(int argc, char *argv[]) -{ +void handle_add_data(int argc, char *argv[]){ unsigned int length = atoi(argv[2]); - appendFile(argv[1], length); + appendFile(argv[1],length); } -void handle_del(int argc, char *argv[]) -{ - delete (argv[1]); +void handle_del(int argc, char *argv[]){ + delete(argv[1]); } -void handle_fatfs(int argc, char *argv[]) -{ +void handle_fatfs(int argc, char *argv[]){ example(); } -void handle_unmount(int argc, char *argv[]) -{ +void handle_unmount(int argc, char *argv[]){ umount(); + } diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index 2fb479ee812..bda12f1d3be 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -29,7 +29,8 @@ * property whatsoever. Maxim Integrated Products, Inc. retains all * ownership rights. * - ******************************************************************************/ + ******************************************************************************/\ + /* -------------------------------------------------- */ // INCLUDES @@ -73,37 +74,39 @@ uint16_t idx = 0; void line_accumulator(uint8_t user_char) { + switch (user_char) { - case BACKSPACE: { + case BACKSPACE: + { // Handle Backspace and Delete if (idx > 0) { - //Sequence to implement a backspace on the terminal + //Sequence to implement a backspace on the terminal Console_Backspace_Sequence(); idx--; buf[idx] = '\0'; } break; - } + } case ENTER: { // Handle Enter or carriage return - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), NEW_LINE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), ENTER); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),NEW_LINE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),ENTER); buf[idx++] = '\r'; buf[idx] = '\0'; idx = 0; - process_command(buf); + process_command(buf); Clear_buffer(); break; - } + } - default: { - // Handle all other characters - if (idx < 255) { - buf[idx++] = user_char; //pushes characters into the buffer - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), user_char); + default: { + // Handle all other characters + if (idx < MAX_COMMAND_LENGTH - 1) { + buf[idx++] = user_char; //pushes characters into the buffer + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),user_char); + } + break; } - break; - } } } @@ -114,57 +117,67 @@ void process_command(char *input) char *end; //Find end of string - for (end = input; *end != '\0'; end++) {} + for (end = input; *end != '\0'; end++); //Initialize variables bool in_token = false; bool beginning_space; - if (*p == SPACE || *p == TAB) + if(*p == SPACE || *p == TAB) beginning_space = true; char *argv[10]; int argc = 0; memset(argv, 0, sizeof(argv)); //Iterate over each character in input - for (p = input; p < end; p++) { + for (p = input; p < end; p++) + { //If in a token - if (in_token) { + if (in_token) + { //If whitespace found, token ends - if (white_space_present(p)) { + if (white_space_present(p)) + { in_token = true; } //Else, token continues - else if (white_space_not_present(p)) { + else if(white_space_not_present(p)) + { input = p; in_token = false; } } //If not in a token - else { + else + { //If ENTER or whitespace found - if (*p == ENTER || (white_space_present(p))) { + if(*p == ENTER || (white_space_present(p))) + { //If ENTER, end of command - if (*p == ENTER) { + if(*p == ENTER) + { *p = '\0'; argv[argc++] = input; break; } //If whitespace found, end of token - if (white_space_present(p) && !beginning_space) { + if(white_space_present(p) && !beginning_space) + { *p = '\0'; argv[argc++] = input; in_token = true; } } //If not in token and whitespace not found, new token starts - else if (white_space_not_present(p) && beginning_space) { + else if (white_space_not_present(p) && beginning_space) + { input = p; in_token = false; beginning_space = false; } //If not in token and whitespace not found and beginning_space is false, token continues - else if (white_space_not_present(p) && !beginning_space) { + else if (white_space_not_present(p) && !beginning_space) + { in_token = false; } } @@ -180,8 +193,10 @@ void process_command(char *input) bool success_flag = 0; //True if input command matches //Iterate over all commands to check if input command matches - for (int i = 0; i < num_commands; i++) { - if (strcasecmp(argv[0], commands[i].name) == 0) { + for (int i=0; i < num_commands; i++) + { + if (strcasecmp(argv[0], commands[i].name) == 0) + { //Call corresponding command's handler commands[i].handler(argc, argv); success_flag = 1; @@ -190,7 +205,8 @@ void process_command(char *input) } //If no commands match, print error message - if (success_flag == 0) { + if (success_flag == 0) + { printf("\n\rCommand isn't valid!\n\r"); } @@ -198,20 +214,21 @@ void process_command(char *input) User_Prompt_Sequence(); } -bool white_space_present(char *p) -{ - return *p == SPACE || *p == TAB; + + +bool white_space_present(char *p){ + return *p == SPACE || *p == TAB; } -bool white_space_not_present(char *p) -{ - return *p != SPACE || *p != TAB; + +bool white_space_not_present(char *p){ + return *p != SPACE || *p != TAB; } void User_Prompt_Sequence(void) { - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), DOLLAR); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), SPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),DOLLAR); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),SPACE); } /** Clears the buffer storing each character of the user command input @@ -230,9 +247,8 @@ void Clear_buffer(void) { * * @return void */ -void Console_Cmd_Clear(void) -{ - for (int i = 0; i < idx; i++) { +void Console_Cmd_Clear(void){ + for (int i = 0; i < idx; i++){ Console_Backspace_Sequence(); } } From d2d8faa1eb1b305f5a12fb17b2e18dfd9eb43276 Mon Sep 17 00:00:00 2001 From: Suraj-Ajjampur Date: Mon, 24 Jul 2023 16:25:13 +0000 Subject: [PATCH 17/39] clang-format bot reformatting. --- Examples/MAX78000/SDHC_FTHR/include/sdhc.h | 11 +- Examples/MAX78000/SDHC_FTHR/main.c | 8 +- Examples/MAX78000/SDHC_FTHR/src/sdhc.c | 13 ++- Examples/MAX78000/SDHC_FTHR/user-cli.c | 70 ++++++------ Examples/MAX78000/SDHC_FTHR/user-cli.h | 1 - Libraries/CLI/inc/cli.h | 45 ++++---- Libraries/CLI/src/cli.c | 118 +++++++++------------ Libraries/CLI/src/cli_uart.c | 12 +-- 8 files changed, 133 insertions(+), 145 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h index 56da1b767fa..0bee3b5b2d6 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h +++ b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h @@ -52,7 +52,6 @@ #define TOSTRING(x) STRINGIFY(x) #define MAXLEN 256 - /***** FUNCTION PROTOTYPES *****/ void generateMessage(unsigned length); @@ -67,15 +66,15 @@ int getSize(); int ls(); -int createFile(char* file_name, unsigned int length); +int createFile(char *file_name, unsigned int length); -int appendFile(char* file_name, unsigned int length); +int appendFile(char *file_name, unsigned int length); -int mkdir(char* dir_name); +int mkdir(char *dir_name); -int cd(char* dir_name); +int cd(char *dir_name); -int delete(char* file_name); +int delete (char *file_name); int example(); diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index 0136b15c687..d6ac85fc6ea 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -93,11 +93,11 @@ int main(void) waitCardInserted(); printf("Card inserted.\n"); - MXC_Delay(1000); //Delay inserted here to avoid weird printf values between previous and next printf command. - - if(MXC_CLI_Uart_Init() != E_NO_ERROR); - while(1){} + MXC_Delay( + 1000); //Delay inserted here to avoid weird printf values between previous and next printf command. + if (MXC_CLI_Uart_Init() != E_NO_ERROR) {} + while (1) {} return 0; } diff --git a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c index 4eba3b8eee9..8ebbf826582 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c +++ b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c @@ -179,7 +179,7 @@ int ls() return err; } -int createFile(char* file_name, unsigned int length) +int createFile(char *file_name, unsigned int length) { // unsigned int length = 128; @@ -224,9 +224,8 @@ int createFile(char* file_name, unsigned int length) return err; } -int appendFile(char* file_name, unsigned int length) +int appendFile(char *file_name, unsigned int length) { - if (!mounted) { mount(); } @@ -268,7 +267,7 @@ int appendFile(char* file_name, unsigned int length) return err; } -int mkdir(char* dir_name) +int mkdir(char *dir_name) { if (!mounted) { mount(); @@ -296,7 +295,7 @@ int mkdir(char* dir_name) return err; } -int cd(char* dir_name) +int cd(char *dir_name) { if (!mounted) { mount(); @@ -321,13 +320,13 @@ int cd(char* dir_name) return err; } -int delete(char* file_name) +int delete (char *file_name) { if (!mounted) { mount(); } - strcpy(filename,file_name); + strcpy(filename, file_name); if ((err = f_stat((const TCHAR *)filename, &fno)) == FR_NO_FILE) { printf("File or directory doesn't exist\n"); diff --git a/Examples/MAX78000/SDHC_FTHR/user-cli.c b/Examples/MAX78000/SDHC_FTHR/user-cli.c index 16cf6e462ce..c7391c81d47 100644 --- a/Examples/MAX78000/SDHC_FTHR/user-cli.c +++ b/Examples/MAX78000/SDHC_FTHR/user-cli.c @@ -31,69 +31,79 @@ * ******************************************************************************/ - #include "user-cli.h" - -const command_table_t commands[] = {{"Size", handle_size, "Find the Size of the SD Card and Free Space\n\r"}, - {"Format", handle_format, "Format the Card\n\r"}, - {"Mount", hande_mount, "Manually Mount Card\n\r"}, - {"ls", handle_ls, "list the contents of the current directory\n\r"}, - {"mkdir", handle_mkdir, "Create a directory\n\r"}, - {"file_create", handle_createfile, "Create a file of random data\n\r"}, - {"cd", handle_cd, "Move into a directory\n\r"}, - {"add_data", handle_add_data, "Add random Data to an Existing File\n\r"}, - {"Del", handle_del, "Delete a file\n\r"}, - {"FatFs", handle_fatfs, "Format Card and Run Example of FatFS Operations\n\r"}, - {"Unmount", handle_unmount, "Unmount card\n\r"}, - {"Help", handle_help, "Prints a help message with info about all of the supported commands.\n\r"}}; +const command_table_t commands[] = { + { "Size", handle_size, "Find the Size of the SD Card and Free Space\n\r" }, + { "Format", handle_format, "Format the Card\n\r" }, + { "Mount", hande_mount, "Manually Mount Card\n\r" }, + { "ls", handle_ls, "list the contents of the current directory\n\r" }, + { "mkdir", handle_mkdir, "Create a directory\n\r" }, + { "file_create", handle_createfile, "Create a file of random data\n\r" }, + { "cd", handle_cd, "Move into a directory\n\r" }, + { "add_data", handle_add_data, "Add random Data to an Existing File\n\r" }, + { "Del", handle_del, "Delete a file\n\r" }, + { "FatFs", handle_fatfs, "Format Card and Run Example of FatFS Operations\n\r" }, + { "Unmount", handle_unmount, "Unmount card\n\r" }, + { "Help", handle_help, + "Prints a help message with info about all of the supported commands.\n\r" } +}; const int num_commands = sizeof(commands) / sizeof(command_table_t); -void handle_size(int argc, char *argv[]){ +void handle_size(int argc, char *argv[]) +{ getSize(); } - -void handle_format(int argc, char *argv[]){ +void handle_format(int argc, char *argv[]) +{ formatSDHC(); } -void hande_mount(int argc, char *argv[]){ +void hande_mount(int argc, char *argv[]) +{ mount(); } -void handle_ls(int argc, char *argv[]){ +void handle_ls(int argc, char *argv[]) +{ ls(); } -void handle_mkdir(int argc, char *argv[]){ +void handle_mkdir(int argc, char *argv[]) +{ mkdir(argv[1]); } -void handle_createfile(int argc, char *argv[]){ +void handle_createfile(int argc, char *argv[]) +{ unsigned int length = atoi(argv[2]); - createFile(argv[1],length); + createFile(argv[1], length); } -void handle_cd(int argc, char *argv[]){ +void handle_cd(int argc, char *argv[]) +{ cd(argv[1]); } -void handle_add_data(int argc, char *argv[]){ +void handle_add_data(int argc, char *argv[]) +{ unsigned int length = atoi(argv[2]); - appendFile(argv[1],length); + appendFile(argv[1], length); } -void handle_del(int argc, char *argv[]){ - delete(argv[1]); +void handle_del(int argc, char *argv[]) +{ + delete (argv[1]); } -void handle_fatfs(int argc, char *argv[]){ +void handle_fatfs(int argc, char *argv[]) +{ example(); } -void handle_unmount(int argc, char *argv[]){ +void handle_unmount(int argc, char *argv[]) +{ umount(); - } diff --git a/Examples/MAX78000/SDHC_FTHR/user-cli.h b/Examples/MAX78000/SDHC_FTHR/user-cli.h index 22a03a5a9f0..43827a6d423 100644 --- a/Examples/MAX78000/SDHC_FTHR/user-cli.h +++ b/Examples/MAX78000/SDHC_FTHR/user-cli.h @@ -70,5 +70,4 @@ void handle_fatfs(int argc, char *argv[]); void handle_unmount(int argc, char *argv[]); - #endif /* USER_CLI_H */ \ No newline at end of file diff --git a/Libraries/CLI/inc/cli.h b/Libraries/CLI/inc/cli.h index 5792eecdaf7..09c62eea517 100644 --- a/Libraries/CLI/inc/cli.h +++ b/Libraries/CLI/inc/cli.h @@ -78,22 +78,21 @@ /* -------------------------------------------------- */ // MACROS /* -------------------------------------------------- */ -#define ENTER 0X0D -#define NEW_LINE 0x0A -#define SPACE 0x20 -#define TAB 0x09 -#define BACKSPACE 0X08 -#define MAXBUFF 2000 -#define DELETE 0x7F -#define DOLLAR 0x24 - -#define ARROW_KEY_CODE_1 0x1B -#define ARROW_KEY_CODE_2 0x5B -#define ARROW_KEY_CODE_LEFT 0x44 -#define ARROW_KEY_CODE_RIGHT 0x43 -#define ARROW_KEY_CODE_UP 0x41 -#define ARROW_KEY_CODE_DOWN 0x42 - +#define ENTER 0X0D +#define NEW_LINE 0x0A +#define SPACE 0x20 +#define TAB 0x09 +#define BACKSPACE 0X08 +#define MAXBUFF 2000 +#define DELETE 0x7F +#define DOLLAR 0x24 + +#define ARROW_KEY_CODE_1 0x1B +#define ARROW_KEY_CODE_2 0x5B +#define ARROW_KEY_CODE_LEFT 0x44 +#define ARROW_KEY_CODE_RIGHT 0x43 +#define ARROW_KEY_CODE_UP 0x41 +#define ARROW_KEY_CODE_DOWN 0x42 /* -------------------------------------------------- */ // FUNCTION PROTOTYPES @@ -126,24 +125,22 @@ void process_command(char *input); */ void handle_help(int argc, char *argv[]); - /** Command table hander prototype with parameters * * @param argc Used to determine which token element of the commands string is being used. * * @param argv[] Char Array of tokens of the command string entered by the user. - * */ + * */ typedef void (*command_handler_t)(int, char *argv[]); /** This command table structure contains the name of the command, a function pointer to the corresponding command handler function, * and the help string which provides a short description of what the command does. * * */ -typedef struct -{ - const char *name; /**< command string */ - command_handler_t handler; /**< function pointer of the handler function */ - const char *help_string; /**< help string of each command */ +typedef struct { + const char *name; /**< command string */ + command_handler_t handler; /**< function pointer of the handler function */ + const char *help_string; /**< help string of each command */ } command_table_t; /** This table is an array of command_table_t structures which should be initialized by the user in user-cli.c to define a set of supported commands in the program. @@ -157,4 +154,4 @@ extern const command_table_t commands[]; * */ extern const int num_commands; -#endif /* MXC_CLI_H */ \ No newline at end of file +#endif /* MXC_CLI_H */ \ No newline at end of file diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index bda12f1d3be..c66996967b1 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -29,8 +29,7 @@ * property whatsoever. Maxim Integrated Products, Inc. retains all * ownership rights. * - ******************************************************************************/\ - + ******************************************************************************/ /* -------------------------------------------------- */ // INCLUDES @@ -45,7 +44,6 @@ // FUNCTION PROTOTYPES /* -------------------------------------------------- */ - bool white_space_present(char *p); bool white_space_not_present(char *p); @@ -74,39 +72,37 @@ uint16_t idx = 0; void line_accumulator(uint8_t user_char) { - switch (user_char) { - case BACKSPACE: - { + case BACKSPACE: { // Handle Backspace and Delete if (idx > 0) { - //Sequence to implement a backspace on the terminal + //Sequence to implement a backspace on the terminal Console_Backspace_Sequence(); idx--; buf[idx] = '\0'; } break; - } + } case ENTER: { // Handle Enter or carriage return - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),NEW_LINE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),ENTER); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), NEW_LINE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), ENTER); buf[idx++] = '\r'; buf[idx] = '\0'; idx = 0; - process_command(buf); + process_command(buf); Clear_buffer(); break; - } + } - default: { - // Handle all other characters - if (idx < MAX_COMMAND_LENGTH - 1) { - buf[idx++] = user_char; //pushes characters into the buffer - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),user_char); - } - break; + default: { + // Handle all other characters + if (idx < MAX_COMMAND_LENGTH - 1) { + buf[idx++] = user_char; //pushes characters into the buffer + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), user_char); } + break; + } } } @@ -117,67 +113,57 @@ void process_command(char *input) char *end; //Find end of string - for (end = input; *end != '\0'; end++); + for (end = input; *end != '\0'; end++) {} //Initialize variables bool in_token = false; bool beginning_space; - if(*p == SPACE || *p == TAB) + if (*p == SPACE || *p == TAB) beginning_space = true; char *argv[10]; int argc = 0; memset(argv, 0, sizeof(argv)); //Iterate over each character in input - for (p = input; p < end; p++) - { + for (p = input; p < end; p++) { //If in a token - if (in_token) - { + if (in_token) { //If whitespace found, token ends - if (white_space_present(p)) - { + if (white_space_present(p)) { in_token = true; } //Else, token continues - else if(white_space_not_present(p)) - { + else if (white_space_not_present(p)) { input = p; in_token = false; } } //If not in a token - else - { + else { //If ENTER or whitespace found - if(*p == ENTER || (white_space_present(p))) - { + if (*p == ENTER || (white_space_present(p))) { //If ENTER, end of command - if(*p == ENTER) - { + if (*p == ENTER) { *p = '\0'; argv[argc++] = input; break; } //If whitespace found, end of token - if(white_space_present(p) && !beginning_space) - { + if (white_space_present(p) && !beginning_space) { *p = '\0'; argv[argc++] = input; in_token = true; } } //If not in token and whitespace not found, new token starts - else if (white_space_not_present(p) && beginning_space) - { + else if (white_space_not_present(p) && beginning_space) { input = p; in_token = false; beginning_space = false; } //If not in token and whitespace not found and beginning_space is false, token continues - else if (white_space_not_present(p) && !beginning_space) - { + else if (white_space_not_present(p) && !beginning_space) { in_token = false; } } @@ -193,10 +179,8 @@ void process_command(char *input) bool success_flag = 0; //True if input command matches //Iterate over all commands to check if input command matches - for (int i=0; i < num_commands; i++) - { - if (strcasecmp(argv[0], commands[i].name) == 0) - { + for (int i = 0; i < num_commands; i++) { + if (strcasecmp(argv[0], commands[i].name) == 0) { //Call corresponding command's handler commands[i].handler(argc, argv); success_flag = 1; @@ -205,8 +189,7 @@ void process_command(char *input) } //If no commands match, print error message - if (success_flag == 0) - { + if (success_flag == 0) { printf("\n\rCommand isn't valid!\n\r"); } @@ -214,21 +197,20 @@ void process_command(char *input) User_Prompt_Sequence(); } - - -bool white_space_present(char *p){ - return *p == SPACE || *p == TAB; +bool white_space_present(char *p) +{ + return *p == SPACE || *p == TAB; } - -bool white_space_not_present(char *p){ - return *p != SPACE || *p != TAB; +bool white_space_not_present(char *p) +{ + return *p != SPACE || *p != TAB; } void User_Prompt_Sequence(void) { - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),DOLLAR); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),SPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), DOLLAR); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), SPACE); } /** Clears the buffer storing each character of the user command input @@ -237,7 +219,8 @@ void User_Prompt_Sequence(void) * * @return void */ -void Clear_buffer(void) { +void Clear_buffer(void) +{ memset(buf, '\0', MAX_COMMAND_LENGTH); } @@ -247,8 +230,9 @@ void Clear_buffer(void) { * * @return void */ -void Console_Cmd_Clear(void){ - for (int i = 0; i < idx; i++){ +void Console_Cmd_Clear(void) +{ + for (int i = 0; i < idx; i++) { Console_Backspace_Sequence(); } } @@ -259,16 +243,16 @@ void Console_Cmd_Clear(void){ * * @return void */ -void Console_Backspace_Sequence(void){ - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),SPACE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART),BACKSPACE); +void Console_Backspace_Sequence(void) +{ + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), BACKSPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), SPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), BACKSPACE); } - void handle_help(int argc, char *argv[]) { - printf("\n\r"); - for (int i = 0; i < num_commands;i++) - printf("%s --> %s", commands[i].name, commands[i].help_string); + printf("\n\r"); + for (int i = 0; i < num_commands; i++) + printf("%s --> %s", commands[i].name, commands[i].help_string); } \ No newline at end of file diff --git a/Libraries/CLI/src/cli_uart.c b/Libraries/CLI/src/cli_uart.c index ec29a6c0086..2be4df7f0ef 100644 --- a/Libraries/CLI/src/cli_uart.c +++ b/Libraries/CLI/src/cli_uart.c @@ -49,9 +49,9 @@ void UART_Handler(void) MXC_UART_AsyncHandler(MXC_UART_GET_UART(CONSOLE_UART)); } -void readCallback(mxc_uart_req_t *req, int error){ - - line_accumulator(RxData); +void readCallback(mxc_uart_req_t *req, int error) +{ + line_accumulator(RxData); READ_FLAG = error; MXC_UART_TransactionAsync(req); } @@ -62,8 +62,8 @@ void readCallback(mxc_uart_req_t *req, int error){ * * @return void */ -int MXC_CLI_Uart_Init(void){ - +int MXC_CLI_Uart_Init(void) +{ // UART interrupt setup NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); NVIC_DisableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); @@ -78,7 +78,7 @@ int MXC_CLI_Uart_Init(void){ printf("-->Example Failed\n"); return error; } - + User_Prompt_Sequence(); read_req.uart = MXC_UART_GET_UART(CONSOLE_UART); From ad03f2047ac7b0f7edb39b0df50e0a00baf65727 Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Mon, 11 Sep 2023 14:18:20 -0500 Subject: [PATCH 18/39] Refactor CLI driver (pt. 1) --- Libraries/CLI/inc/cli.h | 19 --- Libraries/CLI/inc/cli_uart.h | 11 +- Libraries/CLI/src/cli.c | 229 ++++++++++++++++++----------------- Libraries/CLI/src/cli_uart.c | 12 +- 4 files changed, 126 insertions(+), 145 deletions(-) diff --git a/Libraries/CLI/inc/cli.h b/Libraries/CLI/inc/cli.h index 09c62eea517..7d8f49f6069 100644 --- a/Libraries/CLI/inc/cli.h +++ b/Libraries/CLI/inc/cli.h @@ -75,25 +75,6 @@ #include "uart.h" #include "user-cli.h" // Change to user implementation header -/* -------------------------------------------------- */ -// MACROS -/* -------------------------------------------------- */ -#define ENTER 0X0D -#define NEW_LINE 0x0A -#define SPACE 0x20 -#define TAB 0x09 -#define BACKSPACE 0X08 -#define MAXBUFF 2000 -#define DELETE 0x7F -#define DOLLAR 0x24 - -#define ARROW_KEY_CODE_1 0x1B -#define ARROW_KEY_CODE_2 0x5B -#define ARROW_KEY_CODE_LEFT 0x44 -#define ARROW_KEY_CODE_RIGHT 0x43 -#define ARROW_KEY_CODE_UP 0x41 -#define ARROW_KEY_CODE_DOWN 0x42 - /* -------------------------------------------------- */ // FUNCTION PROTOTYPES /* -------------------------------------------------- */ diff --git a/Libraries/CLI/inc/cli_uart.h b/Libraries/CLI/inc/cli_uart.h index 6c22483d355..f20481519d6 100644 --- a/Libraries/CLI/inc/cli_uart.h +++ b/Libraries/CLI/inc/cli_uart.h @@ -35,11 +35,10 @@ #ifndef CLI_UART_HEADER #define CLI_UART_HEADER -#include "uart.h" -#include "nvic_table.h" -#include "cli.h" -#include "board.h" - -int MXC_CLI_Uart_Init(void); +/** Initializes the Console UART for CLI operations. + * + * @return E_NO_ERROR if successful, otherwise an error code. + */ +int MXC_CLI_UART_Init(void); #endif //CLI_UART_HEADER \ No newline at end of file diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index c66996967b1..81be17d5757 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -34,76 +34,132 @@ /* -------------------------------------------------- */ // INCLUDES /* -------------------------------------------------- */ - #include "cli.h" +/* -------------------------------------------------- */ +// MACROS +/* -------------------------------------------------- */ +#define ENTER 0X0D +#define NEW_LINE 0x0A +#define SPACE 0x20 +#define TAB 0x09 +#define BACKSPACE 0X08 +#define MAXBUFF 2000 +#define DELETE 0x7F +#define DOLLAR 0x24 + +#define ARROW_KEY_CODE_1 0x1B +#define ARROW_KEY_CODE_2 0x5B +#define ARROW_KEY_CODE_LEFT 0x44 +#define ARROW_KEY_CODE_RIGHT 0x43 +#define ARROW_KEY_CODE_UP 0x41 +#define ARROW_KEY_CODE_DOWN 0x42 + // Define a buffer length to store commands #define MAX_COMMAND_LENGTH 256 /* -------------------------------------------------- */ -// FUNCTION PROTOTYPES +// GLOBALS /* -------------------------------------------------- */ +char cmd_buf[MAX_COMMAND_LENGTH]; // Command buffer +uint16_t buf_idx = 0; -bool white_space_present(char *p); - -bool white_space_not_present(char *p); +/* -------------------------------------------------- */ +// PRIVATE FUNCTION DEFINITIONS +/* -------------------------------------------------- */ +/** + * @brief Checks whether the current character is a white space character + * + * @param p Character to check + * + * @returns True - chacracter is whitespace, False - character is not whitespace + */ +bool white_space_present(char *p) +{ + return *p == SPACE || *p == TAB; +} -void User_Prompt_Sequence(void); +// /** +// * @brief Clears the buffer storing each character of the user command input +// */ +// bool white_space_not_present(char *p) +// { +// return *p != SPACE || *p != TAB; +// } + +/** + * @brief Clears the buffer storing each character of the user command input + */ +void User_Prompt_Sequence(void) +{ + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), NEW_LINE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), DOLLAR); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), SPACE); +} -void Console_Backspace_Sequence(void); +/** + * @brief Clears the buffer storing each character of the user command input + */ +void Clear_buffer(void) +{ + memset(cmd_buf, '\0', MAX_COMMAND_LENGTH); +} -void Console_Cmd_Clear(void); +/** + * @brief Writes the backspace sequence to the UART console + */ +void Console_Backspace_Sequence(void) +{ + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), BACKSPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), SPACE); + MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), BACKSPACE); +} -void Clear_buffer(void); +/** + * @brief Clears the line on the UART console + */ +void Console_Cmd_Clear(void) +{ + for (int i = 0; i < buf_idx; i++) { + Console_Backspace_Sequence(); + } +} /* -------------------------------------------------- */ -// GLOBALS +// PUBLIC FUNCTION DEFINITIONS /* -------------------------------------------------- */ - -/*! \name buf - \brief Buffer to store the characters of the command -*/ -char buf[MAX_COMMAND_LENGTH]; - -/*! \name idx - \brief Idx to keep track of the buffer which stores the characters of the command -*/ -uint16_t idx = 0; - void line_accumulator(uint8_t user_char) { switch (user_char) { - case BACKSPACE: { + case BACKSPACE: // Handle Backspace and Delete - if (idx > 0) { + if (buf_idx > 0) { //Sequence to implement a backspace on the terminal Console_Backspace_Sequence(); - idx--; - buf[idx] = '\0'; + buf_idx--; + cmd_buf[buf_idx] = '\0'; } break; - } - case ENTER: { + + case ENTER: // Handle Enter or carriage return MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), NEW_LINE); MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), ENTER); - buf[idx++] = '\r'; - buf[idx] = '\0'; - idx = 0; - process_command(buf); + cmd_buf[buf_idx++] = '\r'; + cmd_buf[buf_idx] = '\0'; + buf_idx = 0; + process_command(cmd_buf); Clear_buffer(); break; - } - default: { + default: // Handle all other characters - if (idx < MAX_COMMAND_LENGTH - 1) { - buf[idx++] = user_char; //pushes characters into the buffer + if (buf_idx < MAX_COMMAND_LENGTH - 1) { + cmd_buf[buf_idx++] = user_char; //pushes characters into the buffer MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), user_char); } break; } - } } void process_command(char *input) @@ -118,52 +174,51 @@ void process_command(char *input) //Initialize variables bool in_token = false; bool beginning_space; - if (*p == SPACE || *p == TAB) - beginning_space = true; char *argv[10]; int argc = 0; memset(argv, 0, sizeof(argv)); + if (*p == SPACE || *p == TAB) { + beginning_space = true; + } + //Iterate over each character in input for (p = input; p < end; p++) { - //If in a token if (in_token) { - //If whitespace found, token ends + //If in a token if (white_space_present(p)) { + //If whitespace found, token ends in_token = true; - } - //Else, token continues - else if (white_space_not_present(p)) { + // } else if (white_space_not_present(p)) { + // //Else, token continues + // input = p; + // in_token = false; + } else { + //Else, token continues input = p; in_token = false; } - } - //If not in a token - else { - //If ENTER or whitespace found + } else { + //If not in a token if (*p == ENTER || (white_space_present(p))) { - //If ENTER, end of command + //If ENTER or whitespace found if (*p == ENTER) { + //If ENTER, end of command *p = '\0'; argv[argc++] = input; - break; - } - - //If whitespace found, end of token - if (white_space_present(p) && !beginning_space) { + } else if (white_space_present(p) && !beginning_space) { + //If whitespace found, end of token *p = '\0'; argv[argc++] = input; in_token = true; } - } - //If not in token and whitespace not found, new token starts - else if (white_space_not_present(p) && beginning_space) { + } else if (!white_space_present(p) && beginning_space) { + //If not in token and whitespace not found, new token starts input = p; in_token = false; beginning_space = false; - } - //If not in token and whitespace not found and beginning_space is false, token continues - else if (white_space_not_present(p) && !beginning_space) { + } else if (!white_space_present(p) && !beginning_space) { + //If not in token and whitespace not found and beginning_space is false, token continues in_token = false; } } @@ -197,62 +252,10 @@ void process_command(char *input) User_Prompt_Sequence(); } -bool white_space_present(char *p) -{ - return *p == SPACE || *p == TAB; -} - -bool white_space_not_present(char *p) -{ - return *p != SPACE || *p != TAB; -} - -void User_Prompt_Sequence(void) -{ - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), DOLLAR); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), SPACE); -} - -/** Clears the buffer storing each character of the user command input -* -* @param void -* -* @return void -*/ -void Clear_buffer(void) -{ - memset(buf, '\0', MAX_COMMAND_LENGTH); -} - -/** Clears the line on the uart console -* -* @param void -* -* @return void -*/ -void Console_Cmd_Clear(void) -{ - for (int i = 0; i < idx; i++) { - Console_Backspace_Sequence(); - } -} - -/** Writes the backspace in sequence to the uart console -* -* @param void -* -* @return void -*/ -void Console_Backspace_Sequence(void) -{ - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), BACKSPACE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), SPACE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), BACKSPACE); -} - void handle_help(int argc, char *argv[]) { printf("\n\r"); - for (int i = 0; i < num_commands; i++) + for (int i = 0; i < num_commands; i++) { printf("%s --> %s", commands[i].name, commands[i].help_string); + } } \ No newline at end of file diff --git a/Libraries/CLI/src/cli_uart.c b/Libraries/CLI/src/cli_uart.c index 2be4df7f0ef..6cc4e94cede 100644 --- a/Libraries/CLI/src/cli_uart.c +++ b/Libraries/CLI/src/cli_uart.c @@ -32,7 +32,11 @@ * ******************************************************************************/ +#include "board.h" +#include "cli.h" #include "cli_uart.h" +#include "nvic_table.h" +#include "uart.h" /***** Definitions *****/ #define UART_BAUD 115200 @@ -56,13 +60,7 @@ void readCallback(mxc_uart_req_t *req, int error) MXC_UART_TransactionAsync(req); } -/** Initializes an asychronous uart transaction for CLI operations. This enables the console read - * - * @param none - * - * @return void - */ -int MXC_CLI_Uart_Init(void) +int MXC_CLI_UART_Init(void) { // UART interrupt setup NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); From 47499ddca4777f2a78c7b93d0121b377a4dbf955 Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Mon, 11 Sep 2023 17:24:19 -0500 Subject: [PATCH 19/39] Refactor CLI library (pt. 2) --- Libraries/CLI/inc/cli.h | 109 ++++++++++++++++------------------- Libraries/CLI/inc/cli_uart.h | 44 -------------- Libraries/CLI/src/cli.c | 88 +++++++++++++++++++++++----- Libraries/CLI/src/cli_uart.c | 91 ----------------------------- 4 files changed, 122 insertions(+), 210 deletions(-) delete mode 100644 Libraries/CLI/inc/cli_uart.h delete mode 100644 Libraries/CLI/src/cli_uart.c diff --git a/Libraries/CLI/inc/cli.h b/Libraries/CLI/inc/cli.h index 7d8f49f6069..632c1e27f79 100644 --- a/Libraries/CLI/inc/cli.h +++ b/Libraries/CLI/inc/cli.h @@ -52,87 +52,76 @@ */ -/* -------------------------------------------------- */ -// INCLUDE GUARD -/* -------------------------------------------------- */ #ifndef MXC_CLI_H #define MXC_CLI_H -/* -------------------------------------------------- */ -// INCLUDES -/* -------------------------------------------------- */ -#include -#include -#include -#include -#include #include -#include -#include "board.h" -#include "led.h" -#include "mxc_device.h" -#include "mxc_errors.h" -#include "uart.h" -#include "user-cli.h" // Change to user implementation header - -/* -------------------------------------------------- */ -// FUNCTION PROTOTYPES -/* -------------------------------------------------- */ - -/** Reads incoming bytes, Accumulate into line buffer, Echo's chars back to the other side and handles backspace. - * It calls the process_command function upon pressing the ENTER key - * - * @param user_char User input of each character - */ -void line_accumulator(uint8_t user_char); -/** Performs a Lexical analysis and tokenisis the user's commands - * Lookup first token in a table of functions, dispatch to handler function +// /** +// * @brief Reads incoming bytes, Accumulate into line buffer, Echo's chars back to the other side and handles backspace. +// * It calls the process_command function upon pressing the ENTER key +// * +// * @param user_char User input of each character +// */ +// void line_accumulator(uint8_t user_char); + +// /** +// * @brief Performs a Lexical analysis and tokenisis the user's commands +// * Lookup first token in a table of functions, dispatch to handler function +// * +// * @param input Character pointer containing the line accumulator input when enter key is pressed +// * +// * @return void +// */ +// void process_command(char *input); + +/** + * @brief Prints the help string of each command from the command table * - * @param input Character pointer containing the line accumulator input when enter key is pressed + * @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. * - * @return void + * @return void */ -void process_command(char *input); - -/** 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. -* -* @return void -*/ void handle_help(int argc, char *argv[]); -/** Command table hander prototype with parameters +/** Initializes the Console UART for CLI operations. + * + * @return E_NO_ERROR if successful, otherwise an error code. + */ +int CLI_Init(void); + +/** + * @brief Command table hander prototype with parameters * * @param argc Used to determine which token element of the commands string is being used. * * @param argv[] Char Array of tokens of the command string entered by the user. - * */ + */ typedef void (*command_handler_t)(int, char *argv[]); -/** This command table structure contains the name of the command, a function pointer to the corresponding command handler function, - * and the help string which provides a short description of what the command does. - * - * */ +/** + * @brief This command table structure contains the name of the command, a function pointer to the corresponding command handler function, + * and the help string which provides a short description of what the command does. + */ typedef struct { const char *name; /**< command string */ + const char *usage; /**< help string of each command */ + const char *description; /**< help string of each command */ command_handler_t handler; /**< function pointer of the handler function */ - const char *help_string; /**< help string of each command */ -} command_table_t; +} command_t; -/** This table is an array of command_table_t structures which should be initialized by the user in user-cli.c to define a set of supported commands in the program. - * The structure of this lookup table makes it trivially easy to add a new command to this command processor. - * - * */ -extern const command_table_t commands[]; +/** + * @brief This table is an array of command_table_t structures which should be initialized by the user in user-cli.c to define a set of supported commands in the program. + * The structure of this lookup table makes it trivially easy to add a new command to this command processor. + */ +extern const command_t commands[]; -/** Calculates the number of commands based on commands and the command table - * - * */ +/** + * @brief Calculates the number of commands based on commands and the command table + */ extern const int num_commands; #endif /* MXC_CLI_H */ \ No newline at end of file diff --git a/Libraries/CLI/inc/cli_uart.h b/Libraries/CLI/inc/cli_uart.h deleted file mode 100644 index f20481519d6..00000000000 --- a/Libraries/CLI/inc/cli_uart.h +++ /dev/null @@ -1,44 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES - * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Except as contained in this notice, the name of Maxim Integrated - * Products, Inc. shall not be used except as stated in the Maxim Integrated - * Products, Inc. Branding Policy. - * - * The mere transfer of this software does not imply any licenses - * of trade secrets, proprietary technology, copyrights, patents, - * trademarks, maskwork rights, or any other form of intellectual - * property whatsoever. Maxim Integrated Products, Inc. retains all - * ownership rights. - * - ******************************************************************************/ - - -#ifndef CLI_UART_HEADER -#define CLI_UART_HEADER - -/** Initializes the Console UART for CLI operations. - * - * @return E_NO_ERROR if successful, otherwise an error code. - */ -int MXC_CLI_UART_Init(void); - -#endif //CLI_UART_HEADER \ No newline at end of file diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index 81be17d5757..32bb2ff64dd 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -32,9 +32,17 @@ ******************************************************************************/ /* -------------------------------------------------- */ -// INCLUDES +// INCLUDES /* -------------------------------------------------- */ +#include +#include +#include + +#include "board.h" #include "cli.h" +#include "nvic_table.h" +#include "uart.h" +#include "user-cli.h" // Change to user implementation header /* -------------------------------------------------- */ // MACROS @@ -58,12 +66,24 @@ // Define a buffer length to store commands #define MAX_COMMAND_LENGTH 256 +#define UART_BAUD 115200 +#define BUFF_SIZE 1 + +/* -------------------------------------------------- */ +// FUNCTION PROTOTYPES +/* -------------------------------------------------- */ +void line_accumulator(uint8_t user_char); +void process_command(char *input); + /* -------------------------------------------------- */ -// GLOBALS +// 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 + /* -------------------------------------------------- */ // PRIVATE FUNCTION DEFINITIONS /* -------------------------------------------------- */ @@ -79,14 +99,6 @@ bool white_space_present(char *p) return *p == SPACE || *p == TAB; } -// /** -// * @brief Clears the buffer storing each character of the user command input -// */ -// bool white_space_not_present(char *p) -// { -// return *p != SPACE || *p != TAB; -// } - /** * @brief Clears the buffer storing each character of the user command input */ @@ -125,6 +137,23 @@ void Console_Cmd_Clear(void) } } +/** + * @brief IRQ Handler for the CLI UART + */ +void UART_Handler(void) +{ + MXC_UART_AsyncHandler(MXC_UART_GET_UART(CONSOLE_UART)); +} + +/** + * @brief Callback function for when a character is received by the CLI + */ +void CLI_Callback(mxc_uart_req_t *req, int error) +{ + line_accumulator(char_recv); + MXC_UART_TransactionAsync(req); +} + /* -------------------------------------------------- */ // PUBLIC FUNCTION DEFINITIONS /* -------------------------------------------------- */ @@ -189,10 +218,6 @@ void process_command(char *input) if (white_space_present(p)) { //If whitespace found, token ends in_token = true; - // } else if (white_space_not_present(p)) { - // //Else, token continues - // input = p; - // in_token = false; } else { //Else, token continues input = p; @@ -256,6 +281,39 @@ void handle_help(int argc, char *argv[]) { printf("\n\r"); for (int i = 0; i < num_commands; i++) { - printf("%s --> %s", commands[i].name, commands[i].help_string); + printf("%s:\n", commands[i].name); + printf(" Usage: %s\n", commands[i].usage); + printf(" Description: %s\n\n", commands[i].description); + } +} + +int CLI_Init(void) +{ + int error; + + // UART interrupt setup + NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); + MXC_NVIC_SetVector(MXC_UART_GET_IRQ(CONSOLE_UART), UART_Handler); + NVIC_EnableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); + + /* Initialize Console UART*/ + if ((error = MXC_UART_Init(MXC_UART_GET_UART(CONSOLE_UART), UART_BAUD, MXC_UART_APB_CLK)) != + E_NO_ERROR) { + printf("-->Error initializing UART: %d\n", error); + printf("-->Example Failed\n"); + return error; } + + User_Prompt_Sequence(); + + cli_req.uart = MXC_UART_GET_UART(CONSOLE_UART); + cli_req.rxData = &char_recv; + cli_req.rxLen = BUFF_SIZE; + cli_req.txLen = 0; + cli_req.callback = CLI_Callback; + + error = MXC_UART_TransactionAsync(&cli_req); + + return error; } \ No newline at end of file diff --git a/Libraries/CLI/src/cli_uart.c b/Libraries/CLI/src/cli_uart.c deleted file mode 100644 index 6cc4e94cede..00000000000 --- a/Libraries/CLI/src/cli_uart.c +++ /dev/null @@ -1,91 +0,0 @@ - -/****************************************************************************** - * Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES - * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Except as contained in this notice, the name of Maxim Integrated - * Products, Inc. shall not be used except as stated in the Maxim Integrated - * Products, Inc. Branding Policy. - * - * The mere transfer of this software does not imply any licenses - * of trade secrets, proprietary technology, copyrights, patents, - * trademarks, maskwork rights, or any other form of intellectual - * property whatsoever. Maxim Integrated Products, Inc. retains all - * ownership rights. - * - ******************************************************************************/ - -#include "board.h" -#include "cli.h" -#include "cli_uart.h" -#include "nvic_table.h" -#include "uart.h" - -/***** Definitions *****/ -#define UART_BAUD 115200 -#define BUFF_SIZE 1 - -/****** Globals *********/ -volatile int READ_FLAG; -uint8_t RxData; -mxc_uart_req_t read_req; - -/******* Functions ********/ -void UART_Handler(void) -{ - MXC_UART_AsyncHandler(MXC_UART_GET_UART(CONSOLE_UART)); -} - -void readCallback(mxc_uart_req_t *req, int error) -{ - line_accumulator(RxData); - READ_FLAG = error; - MXC_UART_TransactionAsync(req); -} - -int MXC_CLI_UART_Init(void) -{ - // UART interrupt setup - NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); - NVIC_DisableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); - MXC_NVIC_SetVector(MXC_UART_GET_IRQ(CONSOLE_UART), UART_Handler); - NVIC_EnableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); - - /* Initialize Console UART*/ - int error; - if ((error = MXC_UART_Init(MXC_UART_GET_UART(CONSOLE_UART), UART_BAUD, MXC_UART_APB_CLK)) != - E_NO_ERROR) { - printf("-->Error initializing UART: %d\n", error); - printf("-->Example Failed\n"); - return error; - } - - User_Prompt_Sequence(); - - read_req.uart = MXC_UART_GET_UART(CONSOLE_UART); - read_req.rxData = &RxData; - read_req.rxLen = BUFF_SIZE; - read_req.txLen = 0; - read_req.callback = readCallback; - - error = MXC_UART_TransactionAsync(&read_req); - - return error; -} \ No newline at end of file From 2e8b0b5b12fef698bd8060b85e1deae8f532db58 Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Tue, 12 Sep 2023 11:36:03 -0500 Subject: [PATCH 20/39] Refactor CLI library (pt 3) --- Libraries/CLI/inc/cli.h | 75 +++++------------- Libraries/CLI/src/cli.c | 166 ++++++++++++++++++++++++++-------------- 2 files changed, 128 insertions(+), 113 deletions(-) diff --git a/Libraries/CLI/inc/cli.h b/Libraries/CLI/inc/cli.h index 632c1e27f79..dff1408f036 100644 --- a/Libraries/CLI/inc/cli.h +++ b/Libraries/CLI/inc/cli.h @@ -57,71 +57,36 @@ #include -// /** -// * @brief Reads incoming bytes, Accumulate into line buffer, Echo's chars back to the other side and handles backspace. -// * It calls the process_command function upon pressing the ENTER key -// * -// * @param user_char User input of each character -// */ -// void line_accumulator(uint8_t user_char); - -// /** -// * @brief Performs a Lexical analysis and tokenisis the user's commands -// * Lookup first token in a table of functions, dispatch to handler function -// * -// * @param input Character pointer containing the line accumulator input when enter key is pressed -// * -// * @return void -// */ -// void process_command(char *input); - -/** - * @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. - * - * @return void - */ -void handle_help(int argc, char *argv[]); - -/** Initializes the Console UART for CLI operations. - * - * @return E_NO_ERROR if successful, otherwise an error code. - */ -int CLI_Init(void); - /** - * @brief Command table hander prototype with parameters + * @brief Command handler function prototype * - * @param argc Used to determine which token element of the commands string is being used. + * @param argc Used to determine which token element of the + * commands string is being used. + * @param argv[] Char Array of tokens of the command string + * entered by the user. * - * @param argv[] Char Array of tokens of the command string entered by the user. + * @returns E_NO_ERROR if successful, otherwise an error code. */ -typedef void (*command_handler_t)(int, char *argv[]); +typedef int (*command_handler_t)(int argc, char *argv[]); /** - * @brief This command table structure contains the name of the command, a function pointer to the corresponding command handler function, - * and the help string which provides a short description of what the command does. + * @brief Structure used to define the commands supported by the CLI */ typedef struct { - const char *name; /**< command string */ - const char *usage; /**< help string of each command */ - const char *description; /**< help string of each command */ - command_handler_t handler; /**< function pointer of the handler function */ + const char *name; /**< name of the command */ + 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 This table is an array of command_table_t structures which should be initialized by the user in user-cli.c to define a set of supported commands in the program. - * The structure of this lookup table makes it trivially easy to add a new command to this command processor. - */ -extern const command_t commands[]; - -/** - * @brief Calculates the number of commands based on commands and the command table +/** + * @brief Initializes the Console UART for CLI operations. + * + * @param commands Pointer to the list of user-defined CLI commands + * @param num_commands Number of commands in the command table + * + * @return E_NO_ERROR if successful, otherwise an error code. */ -extern const int num_commands; +int CLI_Init(const command_t *commands, unsigned int num_commands); #endif /* MXC_CLI_H */ \ No newline at end of file diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index 32bb2ff64dd..95307adbcfb 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -74,6 +74,7 @@ /* -------------------------------------------------- */ void line_accumulator(uint8_t user_char); void process_command(char *input); +int handle_help(int argc, char *argv[]); /* -------------------------------------------------- */ // GLOBAL VARIABLES @@ -84,6 +85,13 @@ 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; +unsigned int command_table_sz; + /* -------------------------------------------------- */ // PRIVATE FUNCTION DEFINITIONS /* -------------------------------------------------- */ @@ -137,26 +145,9 @@ void Console_Cmd_Clear(void) } } -/** - * @brief IRQ Handler for the CLI UART - */ -void UART_Handler(void) -{ - MXC_UART_AsyncHandler(MXC_UART_GET_UART(CONSOLE_UART)); -} - -/** - * @brief Callback function for when a character is received by the CLI - */ -void CLI_Callback(mxc_uart_req_t *req, int error) -{ - line_accumulator(char_recv); - MXC_UART_TransactionAsync(req); -} - -/* -------------------------------------------------- */ -// PUBLIC FUNCTION DEFINITIONS -/* -------------------------------------------------- */ +/** + * @brief Adds characters to the command string as they are received and echos them to the terminal + */ void line_accumulator(uint8_t user_char) { switch (user_char) { @@ -191,16 +182,21 @@ void line_accumulator(uint8_t user_char) } } +/** + * @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 p and end pointers + // Initialize p and end pointers char *p = input; char *end; - //Find end of string + // Find end of string for (end = input; *end != '\0'; end++) {} - //Initialize variables + // Initialize variables bool in_token = false; bool beginning_space; char *argv[10]; @@ -211,102 +207,156 @@ void process_command(char *input) beginning_space = true; } - //Iterate over each character in input + // Iterate over each character in input for (p = input; p < end; p++) { if (in_token) { - //If in a token + // If in a token if (white_space_present(p)) { - //If whitespace found, token ends + // If whitespace found, token ends in_token = true; } else { - //Else, token continues + // Else, token continues input = p; in_token = false; } } else { - //If not in a token + // If not in a token if (*p == ENTER || (white_space_present(p))) { - //If ENTER or whitespace found + // If ENTER or whitespace found if (*p == ENTER) { - //If ENTER, end of command + // If ENTER, end of command *p = '\0'; argv[argc++] = input; } else if (white_space_present(p) && !beginning_space) { - //If whitespace found, end of token + // If whitespace found, end of token *p = '\0'; argv[argc++] = input; in_token = true; } } else if (!white_space_present(p) && beginning_space) { - //If not in token and whitespace not found, new token starts + // If not in token and whitespace not found, new token starts input = p; in_token = false; beginning_space = false; } else if (!white_space_present(p) && !beginning_space) { - //If not in token and whitespace not found and beginning_space is false, token continues + // If not in token and whitespace not found and beginning_space is false, token continues in_token = false; } } } - //Set last argv value to NULL + // Set last argv value to NULL argv[argc] = NULL; - //If no arguments, return - if (argc == 0) + // If no arguments, return + if (argc == 0) { return; + } - bool success_flag = 0; //True if input command matches - - //Iterate over all commands to check if input command matches - for (int i = 0; i < num_commands; i++) { - if (strcasecmp(argv[0], commands[i].name) == 0) { - //Call corresponding command's handler - commands[i].handler(argc, argv); - success_flag = 1; - break; + int success_flag = 1; + + // Check for a valid command + if (strcasecmp(argv[0], help_command.name) == 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].name) == 0) { + // Call corresponding command's handler + success_flag = command_table[i].handler(argc, argv); + break; + } } } - //If no commands match, print error message - if (success_flag == 0) { + // If no commands match, print error message + if (success_flag == 1) { printf("\n\rCommand isn't valid!\n\r"); + } else if (success_flag < E_NO_ERROR) { + printf("\n\rEnter 'help' to see a list of available commands.\n\r"); } - //Print prompt + // Print prompt User_Prompt_Sequence(); } -void handle_help(int argc, char *argv[]) +/** + * @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[]) { printf("\n\r"); - for (int i = 0; i < num_commands; i++) { - printf("%s:\n", commands[i].name); - printf(" Usage: %s\n", commands[i].usage); - printf(" Description: %s\n\n", commands[i].description); + for (int i = 0; i < command_table_sz; i++) { + printf("%s:\n", command_table[i].name); + printf(" Usage: %s\n", command_table[i].usage); + printf(" Description: %s\n\n", command_table[i].description); } + + return E_NO_ERROR; } -int CLI_Init(void) +/** + * @brief IRQ Handler for the CLI UART + */ +void UART_Handler(void) +{ + MXC_UART_AsyncHandler(MXC_UART_GET_UART(CONSOLE_UART)); +} + +/** + * @brief Callback function for when a character is received by the CLI + */ +void CLI_Callback(mxc_uart_req_t *req, int error) +{ + line_accumulator(char_recv); + MXC_UART_TransactionAsync(req); +} + +/* -------------------------------------------------- */ +// PUBLIC FUNCTION DEFINITIONS +/* -------------------------------------------------- */ +int CLI_Init(const command_t *commands, unsigned int num_commands) { int error; + // Check for valid parameters + if (commands == NULL) { + return E_NULL_PTR; + } else if (num_commands <= 0) { + return E_BAD_PARAM; + } + + // Save the command table + command_table = commands; + command_table_sz = num_commands; + // UART interrupt setup NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); NVIC_DisableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); MXC_NVIC_SetVector(MXC_UART_GET_IRQ(CONSOLE_UART), UART_Handler); NVIC_EnableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); - /* Initialize Console UART*/ + // Initialize Console UART if ((error = MXC_UART_Init(MXC_UART_GET_UART(CONSOLE_UART), UART_BAUD, MXC_UART_APB_CLK)) != E_NO_ERROR) { - printf("-->Error initializing UART: %d\n", error); - printf("-->Example Failed\n"); + printf("-->Error initializing CLI UART: %d\n", 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(MXC_UART_GET_UART(CONSOLE_UART))) {} + // Initialize an asynchoronous request for the first character cli_req.uart = MXC_UART_GET_UART(CONSOLE_UART); cli_req.rxData = &char_recv; cli_req.rxLen = BUFF_SIZE; @@ -316,4 +366,4 @@ int CLI_Init(void) error = MXC_UART_TransactionAsync(&cli_req); return error; -} \ No newline at end of file +} From a7ac928a1bec8a29b967d73341a7d7d214a3d40e Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Tue, 12 Sep 2023 14:13:09 -0500 Subject: [PATCH 21/39] Update SDHC_FTHR example to use refactored CLI library --- Examples/MAX78000/SDHC_FTHR/README.md | 109 +++++++++--- .../SDHC_FTHR/{ => include}/user-cli.h | 38 ++-- Examples/MAX78000/SDHC_FTHR/main.c | 54 ++---- Examples/MAX78000/SDHC_FTHR/src/user-cli.c | 165 ++++++++++++++++++ Examples/MAX78000/SDHC_FTHR/user-cli.c | 109 ------------ 5 files changed, 276 insertions(+), 199 deletions(-) rename Examples/MAX78000/SDHC_FTHR/{ => include}/user-cli.h (73%) create mode 100644 Examples/MAX78000/SDHC_FTHR/src/user-cli.c delete mode 100644 Examples/MAX78000/SDHC_FTHR/user-cli.c diff --git a/Examples/MAX78000/SDHC_FTHR/README.md b/Examples/MAX78000/SDHC_FTHR/README.md index 4e329ab9b26..4bbf3354c77 100644 --- a/Examples/MAX78000/SDHC_FTHR/README.md +++ b/Examples/MAX78000/SDHC_FTHR/README.md @@ -23,25 +23,58 @@ Universal instructions on building, flashing, and debugging this project can be ``` ***** MAX78000 SDHC FAT Filesystem Example ***** Card inserted. -$ Help - -Size --> Find the Size of the SD Card and Free Space -Format --> Format the Card -Mount --> Manually Mount Card -ls --> list the contents of the current directory -mkdir --> Create a directory -file_create --> Create a file of random data -cd --> Move into a directory -add_data --> Add random Data to an Existing File -Del --> Delete a file -FatFs --> Format Card and Run Example of FatFS Operations -Unmount --> Unmount card -Help --> Prints a help message with info about all of the supported commands. -$ SIZE -SD card mounted. -Disk Size: 31163072 bytes -Available: 31163008 bytes -$ Format +CLI Initialized! Enter 'help' to see a list of available commands. + +$ help +help + +size: + Usage: size + Description: Find the Size of the SD Card and Free Space + +format: + Usage: format + Description: Format the Card + +mount: + Usage: mount + Description: Manually Mount Card + +ls: + Usage: ls + Description: list the contents of the current directory + +mkdir: + Usage: mkdir + Description: Create a directory + +file_create: + Usage: file_create + Description: Create a file of random data + +cd: + Usage: cd + Description: Move into a directory + +add_data: + Usage: add_data + Description: Add random Data to an Existing File + +del: + Usage: del + Description: Delete a file + +fatfs: + Usage: fatfs + Description: Format Card and Run Example of FatFS Operations + +unmount: + Usage: unmount + Description: Unmount card + + +$ format +format *****THE DRIVE WILL BE FORMATTED IN 5 SECONDS***** @@ -51,30 +84,52 @@ FORMATTING DRIVE Drive formatted. SD card mounted. SD card unmounted. + +$ size +size +SD card mounted. +Disk Size: 7760896 bytes +Available: 7760864 bytes + $ mount +mount SD card mounted. + $ mkdir Analog_Devices +mkdir Analog_Devices Creating directory... Directory Analog_Devices created. + $ cd Analog_Devices +cd Analog_Devices Changed to Analog_Devices -$ file_create Maxim 30 -Creating file Maxim with length 30 + +$ file_create ADI 30 +file_create ADI 30 +Creating file ADI with length 30 File opened! 30 bytes written to file! File Closed! -$ add_data Maxim 25 + +$ add_data ADI 25 +add_data ADI 25 File opened! 25 bytes written to file File closed. + $ ls +ls Listing Contents of 0:/Analog_Devices - -0:/Analog_Devices/Maxim +0:/Analog_Devices/ADI Finished listing contents -$ Del Maxim -Deleted file Maxim + +$ del ADI +del ADI +Deleted file ADI + $ fatfs +fatfs *****THE DRIVE WILL BE FORMATTED IN 5 SECONDS***** @@ -93,9 +148,11 @@ Creating Directory... Renaming File... Attempting to read back file... Read Back 256 bytes -Message: StiUH7-HxP!fffB,q.DqMnI2nvSnchst.ZaLTpG'w8I7Tg0,l6VVkEsehp#IHSMZN94WDc'N#-0qkBlAil,'#DvMJZ!zzf,j?Lm,H5cbfFfVNHUlPF9GsTWbrop0EG7VV57qZZzjdvzJH5Xh2'82'fz9t#R,kzoaqYBJVRhrlD5W1mZItggYqyIICUvWOOppJQIVvt.BR0Vy4#YwqiNYj'jYnX8j7ePtuzJO?t-sTCGvibwYn81?Sutq'Q0s7udL +Message: sjVNXdwfl-owoXGcTZ,5z,Sy8lfsNqDGrzio'O6vntRMoWODcIKP!C'y7tF.'W88ZjR81BpiibPhokQfa3w'cvmnr0EgE1MNDIhXKfBJGP6b?0tvHEPK-WNc7yuPdFNL6FPq10',Q,GSf3jSyY?MU0wv'FToTI!ct.E6Q4nbVuavg6h'48D5sR5mcepxf1l!MesddI7aZ9s?KIVnybRwZ.UBJpX1b?5oXP9wLKZcgW-k,gZ5HMIMwAcy!n9S?E57Analog_Devices File Closed! + $ unmount +unmount SD card unmounted. ``` diff --git a/Examples/MAX78000/SDHC_FTHR/user-cli.h b/Examples/MAX78000/SDHC_FTHR/include/user-cli.h similarity index 73% rename from Examples/MAX78000/SDHC_FTHR/user-cli.h rename to Examples/MAX78000/SDHC_FTHR/include/user-cli.h index 43827a6d423..7a0ab94722b 100644 --- a/Examples/MAX78000/SDHC_FTHR/user-cli.h +++ b/Examples/MAX78000/SDHC_FTHR/include/user-cli.h @@ -30,44 +30,38 @@ * ownership rights. * ******************************************************************************/ - -/* -------------------------------------------------- */ -// INCLUDE GUARD -/* -------------------------------------------------- */ #ifndef USER_CLI_H #define USER_CLI_H -//mxc_cli_config.c/.h - /* -------------------------------------------------- */ -// INCLUDES +// GLOBAL VARIABLE /* -------------------------------------------------- */ -#include "cli.h" -#include -#include "sdhc.h" +extern const command_t user_commands[]; +extern const unsigned int num_user_commands; + /* -------------------------------------------------- */ // FUNCTION PROTOTYPES /* -------------------------------------------------- */ -void handle_size(int argc, char *argv[]); +int handle_size(int argc, char *argv[]); -void handle_format(int argc, char *argv[]); +int handle_format(int argc, char *argv[]); -void hande_mount(int argc, char *argv[]); +int handle_mount(int argc, char *argv[]); -void handle_ls(int argc, char *argv[]); +int handle_ls(int argc, char *argv[]); -void handle_mkdir(int argc, char *argv[]); +int handle_mkdir(int argc, char *argv[]); -void handle_createfile(int argc, char *argv[]); +int handle_createfile(int argc, char *argv[]); -void handle_cd(int argc, char *argv[]); +int handle_cd(int argc, char *argv[]); -void handle_add_data(int argc, char *argv[]); +int handle_add_data(int argc, char *argv[]); -void handle_del(int argc, char *argv[]); +int handle_del(int argc, char *argv[]); -void handle_fatfs(int argc, char *argv[]); +int handle_fatfs(int argc, char *argv[]); -void handle_unmount(int argc, char *argv[]); +int handle_unmount(int argc, char *argv[]); -#endif /* USER_CLI_H */ \ No newline at end of file +#endif /* USER_CLI_H */ diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index d6ac85fc6ea..8f0b123833a 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -43,61 +43,31 @@ */ /***** Includes *****/ -#include -#include -#include -#include - -#include "board.h" -#include "mxc_delay.h" -#include "mxc_device.h" -#include "gpio.h" -#include "nvic_table.h" -#include "ff.h" -#include "cli_uart.h" +#include "cli.h" #include "sdhc.h" +#include "user-cli.h" #ifdef BOARD_EVKIT_V1 #warning This example is not supported by the MAX78000EVKIT. #endif -extern TCHAR *FF_ERRORS[20]; - /******************************************************************************/ int main(void) { - FF_ERRORS[0] = "FR_OK"; - FF_ERRORS[1] = "FR_DISK_ERR"; - FF_ERRORS[2] = "FR_INT_ERR"; - FF_ERRORS[3] = "FR_NOT_READY"; - FF_ERRORS[4] = "FR_NO_FILE"; - FF_ERRORS[5] = "FR_NO_PATH"; - FF_ERRORS[6] = "FR_INVLAID_NAME"; - FF_ERRORS[7] = "FR_DENIED"; - FF_ERRORS[8] = "FR_EXIST"; - FF_ERRORS[9] = "FR_INVALID_OBJECT"; - FF_ERRORS[10] = "FR_WRITE_PROTECTED"; - FF_ERRORS[11] = "FR_INVALID_DRIVE"; - FF_ERRORS[12] = "FR_NOT_ENABLED"; - FF_ERRORS[13] = "FR_NO_FILESYSTEM"; - FF_ERRORS[14] = "FR_MKFS_ABORTED"; - FF_ERRORS[15] = "FR_TIMEOUT"; - FF_ERRORS[16] = "FR_LOCKED"; - FF_ERRORS[17] = "FR_NOT_ENOUGH_CORE"; - FF_ERRORS[18] = "FR_TOO_MANY_OPEN_FILES"; - FF_ERRORS[19] = "FR_INVALID_PARAMETER"; - srand(12347439); - - printf("\n\n***** " TOSTRING(TARGET) " SDHC FAT Filesystem Example *****\n"); + int err; + printf("\n\n***** MAX78000 SDHC FAT Filesystem Example *****\n"); + // Wait for SD Card to be inserted waitCardInserted(); printf("Card inserted.\n"); - MXC_Delay( - 1000); //Delay inserted here to avoid weird printf values between previous and next printf command. + while(MXC_UART_GetActive(MXC_UART_GET_UART(CONSOLE_UART))) {} - if (MXC_CLI_Uart_Init() != E_NO_ERROR) {} - while (1) {} + // Initialize CLI + if ((err = CLI_Init(user_commands, num_user_commands)) != E_NO_ERROR) { + return err; + } - return 0; + // Run CLI + while (1) {} } diff --git a/Examples/MAX78000/SDHC_FTHR/src/user-cli.c b/Examples/MAX78000/SDHC_FTHR/src/user-cli.c new file mode 100644 index 00000000000..c933f030738 --- /dev/null +++ b/Examples/MAX78000/SDHC_FTHR/src/user-cli.c @@ -0,0 +1,165 @@ +/****************************************************************************** + * Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Maxim Integrated + * Products, Inc. shall not be used except as stated in the Maxim Integrated + * Products, Inc. Branding Policy. + * + * The mere transfer of this software does not imply any licenses + * of trade secrets, proprietary technology, copyrights, patents, + * trademarks, maskwork rights, or any other form of intellectual + * property whatsoever. Maxim Integrated Products, Inc. retains all + * ownership rights. + * + ******************************************************************************/ + +#include +#include "cli.h" +#include "sdhc.h" +#include "user-cli.h" + +const command_t user_commands[] = { + { "size", "size", "Find the Size of the SD Card and Free Space", handle_size }, + { "format", "format", "Format the Card", handle_format }, + { "mount", "mount", "Manually Mount Card", handle_mount }, + { "ls", "ls", "list the contents of the current directory", handle_ls }, + { "mkdir", "mkdir ", "Create a directory", handle_mkdir }, + { "file_create", "file_create ", "Create a file of random data", handle_createfile }, + { "cd", "cd ", "Move into a directory", handle_cd }, + { "add_data", "add_data ", "Add random Data to an Existing File", handle_add_data }, + { "del", "del ", "Delete a file", handle_del }, + { "fatfs", "fatfs", "Format Card and Run Example of FatFS Operations", handle_fatfs }, + { "unmount", "unmount", "Unmount card", handle_unmount }, +}; + +const unsigned int num_user_commands = sizeof(user_commands) / sizeof(command_t); + +int handle_size(int argc, char *argv[]) +{ + if(argc != 1) { + printf("Incorrect usage. Too many parameters.\n"); + return E_INVALID; + } + + return getSize(); +} + +int handle_format(int argc, char *argv[]) +{ + if(argc != 1) { + printf("Incorrect usage. Too many parameters.\n"); + return E_INVALID; + } + + return formatSDHC(); +} + +int handle_mount(int argc, char *argv[]) +{ + if(argc != 1) { + printf("Incorrect usage. Too many parameters.\n"); + return E_INVALID; + } + + return mount(); +} + +int handle_ls(int argc, char *argv[]) +{ + if(argc != 1) { + printf("Incorrect usage. Too many parameters.\n"); + return E_INVALID; + } + + return ls(); +} + +int handle_mkdir(int argc, char *argv[]) +{ + if(argc != 2) { + printf("Incorrect usage. Please provide directory name.\n"); + return E_INVALID; + } + + return mkdir(argv[1]); +} + +int handle_createfile(int argc, char *argv[]) +{ + if(argc != 3) { + printf("Incorrect usage. Please provide filename and length.\n"); + return E_INVALID; + } + + unsigned int length = atoi(argv[2]); + return createFile(argv[1], length); +} + +int handle_cd(int argc, char *argv[]) +{ + if(argc != 2) { + printf("Incorrect usage. Please provide directory name.\n"); + return E_INVALID; + } + + return cd(argv[1]); +} + +int handle_add_data(int argc, char *argv[]) +{ + if(argc != 3) { + printf("Incorrect usage. Please provide filename and length.\n"); + return E_INVALID; + } + + unsigned int length = atoi(argv[2]); + return appendFile(argv[1], length); +} + +int handle_del(int argc, char *argv[]) +{ + if(argc != 2) { + printf("Incorrect usage. Please provide filename.\n"); + return E_INVALID; + } + + return delete(argv[1]); +} + +int handle_fatfs(int argc, char *argv[]) +{ + if(argc != 1) { + printf("Incorrect usage. Too many parameters.\n"); + return E_INVALID; + } + + return example(); +} + +int handle_unmount(int argc, char *argv[]) +{ + if(argc != 1) { + printf("Incorrect usage. Too many parameters.\n"); + return E_INVALID; + } + + return umount(); +} diff --git a/Examples/MAX78000/SDHC_FTHR/user-cli.c b/Examples/MAX78000/SDHC_FTHR/user-cli.c deleted file mode 100644 index c7391c81d47..00000000000 --- a/Examples/MAX78000/SDHC_FTHR/user-cli.c +++ /dev/null @@ -1,109 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES - * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Except as contained in this notice, the name of Maxim Integrated - * Products, Inc. shall not be used except as stated in the Maxim Integrated - * Products, Inc. Branding Policy. - * - * The mere transfer of this software does not imply any licenses - * of trade secrets, proprietary technology, copyrights, patents, - * trademarks, maskwork rights, or any other form of intellectual - * property whatsoever. Maxim Integrated Products, Inc. retains all - * ownership rights. - * - ******************************************************************************/ - -#include "user-cli.h" - -const command_table_t commands[] = { - { "Size", handle_size, "Find the Size of the SD Card and Free Space\n\r" }, - { "Format", handle_format, "Format the Card\n\r" }, - { "Mount", hande_mount, "Manually Mount Card\n\r" }, - { "ls", handle_ls, "list the contents of the current directory\n\r" }, - { "mkdir", handle_mkdir, "Create a directory\n\r" }, - { "file_create", handle_createfile, "Create a file of random data\n\r" }, - { "cd", handle_cd, "Move into a directory\n\r" }, - { "add_data", handle_add_data, "Add random Data to an Existing File\n\r" }, - { "Del", handle_del, "Delete a file\n\r" }, - { "FatFs", handle_fatfs, "Format Card and Run Example of FatFS Operations\n\r" }, - { "Unmount", handle_unmount, "Unmount card\n\r" }, - { "Help", handle_help, - "Prints a help message with info about all of the supported commands.\n\r" } -}; - -const int num_commands = sizeof(commands) / sizeof(command_table_t); - -void handle_size(int argc, char *argv[]) -{ - getSize(); -} - -void handle_format(int argc, char *argv[]) -{ - formatSDHC(); -} - -void hande_mount(int argc, char *argv[]) -{ - mount(); -} - -void handle_ls(int argc, char *argv[]) -{ - ls(); -} - -void handle_mkdir(int argc, char *argv[]) -{ - mkdir(argv[1]); -} - -void handle_createfile(int argc, char *argv[]) -{ - unsigned int length = atoi(argv[2]); - createFile(argv[1], length); -} - -void handle_cd(int argc, char *argv[]) -{ - cd(argv[1]); -} - -void handle_add_data(int argc, char *argv[]) -{ - unsigned int length = atoi(argv[2]); - appendFile(argv[1], length); -} - -void handle_del(int argc, char *argv[]) -{ - delete (argv[1]); -} - -void handle_fatfs(int argc, char *argv[]) -{ - example(); -} - -void handle_unmount(int argc, char *argv[]) -{ - umount(); -} From 399ef10e3194b4cf438ff698334548facd41839c Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Tue, 12 Sep 2023 16:47:02 -0500 Subject: [PATCH 22/39] Remove the user-cli header file include in cli.c --- Libraries/CLI/src/cli.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index 95307adbcfb..47f8baadcbf 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -42,7 +42,6 @@ #include "cli.h" #include "nvic_table.h" #include "uart.h" -#include "user-cli.h" // Change to user implementation header /* -------------------------------------------------- */ // MACROS From cd86680d4e46c50f34b7b13008c1b2744086cce9 Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Tue, 12 Sep 2023 16:48:39 -0500 Subject: [PATCH 23/39] Update CLI library README --- Libraries/CLI/README.md | 78 ++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 51 deletions(-) diff --git a/Libraries/CLI/README.md b/Libraries/CLI/README.md index 3522239af8a..a440eb5d963 100644 --- a/Libraries/CLI/README.md +++ b/Libraries/CLI/README.md @@ -6,82 +6,58 @@ 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 on device to - -* Allows developers to get diagnostics and change device parameters interactively - -* Also lend itself to easy automated testing of the device, through PC-side scripting - -The current features include the following +This library provides an extensible command processor on device to: +* 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: - Case insensitive commands & arguments - Can have spaces at beginning or end of string - Can have multiple spaces between words - Backspace - Arrow keys - Tab completion +- An internally defined 'help' command which lists information about each of the user-defined commands - -## Software -Point 2 seperate input and output. -Change the formatting update ADI style guide +## CLI Software Flow +The following graphic describes the software flow of the CLI. ![Processing steps](res/CLI-Processing-steps.png) -(Above: Describes the steps in which the CLI command processes are being excecuted) - -The library is present in the `Libraries/CLI` folder of the MSDK. - -### Project Usage +## Porting Guide -### Enabling the library -Go to project.mk file and include the path to the MAXIM SDK CLI Libaries as this below line +The CLI library expects the user to implement the following steps: +1. Add the following line to your project.mk file to include the CLI library in your project: ``` include ${MAXIM_PATH}/Libraries/CLI/CLI.mk ``` -## Porting Guide - -The CLI library excepts the user to implement the following steps - -### Instructions - -1. Define an array commands[] of type const command_table_t to contain the name of the command, a function pointer to the corresponding command handler function, -nd the help string which provides a short description of what the command does. +2. 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 command_t element should define the name of the command, a string showing how to enter the command in the terminal, a description of what the command does, and a function pointer to the corresponding handler function. +For example, the following is subset of the SDHC example command set: ``` -const command_table_t commands[] = {{"Mount", handle_mount, "Manually Mount Card"}, - {"Format", handle_format, "Format the Card\n\r"}} +const command_t user_commands[] = {{ "format", "format", "Format the Card", handle_format }, + { "mkdir", "mkdir ", "Create a directory", handle_mkdir }} ``` -3. Define and initialize an integer num_commands to the sizeof your command_table_t structure instance i.e commands divided by the sizeof the structure. Giving the number of commands. - We will need this to iterate through all the command in your command_table. +3. Define a handler function for each command. -``` -const int num_commands = sizeof(commands) / sizeof(command_table_t); -``` +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. -4. Each handler function must have the same prototype. +Parameters +- argc - Argument counter, number of tokens in the argument vector +- argv[] - Argument vector, tokenized command string -- argc is used to determine which token element of the commands string is being used. -- argv[] is a character array of tokens of the command string entered by the user. +Below is a sample handler function prototype for a "make directory" command. +``` +int handle_mkdir(int argc, char *argv[]); +``` -Suppose a user entered the command +As an example, suppose a user entered the command: ``` mkdir new_folder ``` -The Lexical Analysis performed by the CLI library tokenizes mkdir and assigns it to argv[0]. new_folder is assigned to argv[1]. - -The define command handler function as below. - -``` -void handle_help(int argc, char *argv[]) -``` -Write the functions in user-cli.c and give a function protoype in user-cli.h so it is accessable by the CLI library when it needs to dispatch the handlers - -5. You can change the name of your file to something other than user-cli.c/.h but make sure that you also change the headers in [Libraries/CLI/src/cli.h](Libraries/CLI/src/cli.h) to the your newly named header file. - - - - +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. +4. Include cli.h in the approriate source file and initialize the CLI by calling the CLI_Init function with a pointer to the command table, along with the number of entries in the command table. From e0b27f624899e8165f57bd9b075b89f8c996d791 Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Wed, 13 Sep 2023 16:08:18 -0500 Subject: [PATCH 24/39] Revert SDHC_FTHR example settings.json file to standard. --- .../MAX78000/SDHC_FTHR/.vscode/settings.json | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json b/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json index e42afe65b1d..89e67decb16 100755 --- a/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json +++ b/Examples/MAX78000/SDHC_FTHR/.vscode/settings.json @@ -69,20 +69,10 @@ "${config:MAXIM_PATH}/Libraries/SDHC/ff14/Source" ], "C_Cpp.default.defines": [ - "${config:board}", - "TARGET=${config:target}", - "TARGET_REV=0x4131", - "__GNUC__" + ], - "files.associations": { - "stdint.h": "c", - "cli.h": "c", - "sdhc.h": "c", - "user-cli.h": "c", - "uart.h": "c", - "nvic_table.h": "c", - "cstdlib": "c", - "adc_reva.h": "c" - } + "C_Cpp.default.forcedInclude": [ + "${workspaceFolder}/build/project_defines.h" + ] } From ebf80d34a2bafc6f8246438949cfa333df58176e Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Wed, 13 Sep 2023 16:10:12 -0500 Subject: [PATCH 25/39] Refactor CLI library (pt 4) --- Libraries/CLI/inc/cli.h | 17 +++++++- Libraries/CLI/src/cli.c | 88 ++++++++++++++++++++++++++++------------- 2 files changed, 76 insertions(+), 29 deletions(-) diff --git a/Libraries/CLI/inc/cli.h b/Libraries/CLI/inc/cli.h index dff1408f036..6519c4a9c90 100644 --- a/Libraries/CLI/inc/cli.h +++ b/Libraries/CLI/inc/cli.h @@ -56,6 +56,7 @@ #define MXC_CLI_H #include +#include "uart.h" /** * @brief Command handler function prototype @@ -82,11 +83,25 @@ typedef struct { /** * @brief Initializes the Console UART for CLI operations. * + * @param uart Pointer to UART instance to use for the CLI * @param commands Pointer to the list of 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 CLI_Init(const command_t *commands, unsigned int num_commands); +int MXC_CLI_Init(mxc_uart_regs_t *uart, const command_t *commands, unsigned int num_commands); + +/** + * @brief Shutdown the CLI. (Console 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 /* MXC_CLI_H */ \ No newline at end of file diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index 47f8baadcbf..6193ee20866 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -41,7 +41,6 @@ #include "board.h" #include "cli.h" #include "nvic_table.h" -#include "uart.h" /* -------------------------------------------------- */ // MACROS @@ -83,13 +82,14 @@ 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 +mxc_uart_regs_t *cli_uart = NULL; // 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; -unsigned int command_table_sz; +const command_t *command_table = NULL; +unsigned int command_table_sz = 0; /* -------------------------------------------------- */ // PRIVATE FUNCTION DEFINITIONS @@ -302,67 +302,99 @@ int handle_help(int argc, char *argv[]) return E_NO_ERROR; } -/** - * @brief IRQ Handler for the CLI UART - */ -void UART_Handler(void) -{ - MXC_UART_AsyncHandler(MXC_UART_GET_UART(CONSOLE_UART)); -} - /** * @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); + + // Start transaction to receive next character MXC_UART_TransactionAsync(req); } /* -------------------------------------------------- */ // PUBLIC FUNCTION DEFINITIONS /* -------------------------------------------------- */ -int CLI_Init(const command_t *commands, unsigned int num_commands) +int MXC_CLI_Init(mxc_uart_regs_t *uart, const command_t *commands, unsigned int num_commands) { int error; // Check for valid parameters - if (commands == NULL) { + if (MXC_UART_GET_IDX(uart) < 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; - // UART interrupt setup - NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); - NVIC_DisableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); - MXC_NVIC_SetVector(MXC_UART_GET_IRQ(CONSOLE_UART), UART_Handler); - NVIC_EnableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); - // Initialize Console UART - if ((error = MXC_UART_Init(MXC_UART_GET_UART(CONSOLE_UART), UART_BAUD, MXC_UART_APB_CLK)) != + if ((error = MXC_UART_Init(uart, UART_BAUD, MXC_UART_APB_CLK)) != E_NO_ERROR) { printf("-->Error initializing CLI UART: %d\n", 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(MXC_UART_GET_UART(CONSOLE_UART))) {} - // Initialize an asynchoronous request for the first character - cli_req.uart = MXC_UART_GET_UART(CONSOLE_UART); + 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; + } - error = MXC_UART_TransactionAsync(&cli_req); + // 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)) {} + + 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(MXC_UART_GET_UART(CONSOLE_UART)); - return error; + // 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(MXC_UART_GET_UART(CONSOLE_UART)); } From 0d54953eb342f593506566e9b5ed8679c45c103c Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Wed, 13 Sep 2023 16:12:07 -0500 Subject: [PATCH 26/39] Update SDHC_FTHR example to use refactored CLI library --- Examples/MAX78000/SDHC_FTHR/include/user-cli.h | 2 ++ Examples/MAX78000/SDHC_FTHR/main.c | 9 ++++++++- Examples/MAX78000/SDHC_FTHR/src/user-cli.c | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Examples/MAX78000/SDHC_FTHR/include/user-cli.h b/Examples/MAX78000/SDHC_FTHR/include/user-cli.h index 7a0ab94722b..857ead2392c 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/user-cli.h +++ b/Examples/MAX78000/SDHC_FTHR/include/user-cli.h @@ -42,6 +42,8 @@ extern const unsigned int num_user_commands; /* -------------------------------------------------- */ // FUNCTION PROTOTYPES /* -------------------------------------------------- */ +void CLI_IRQHandler(void); + int handle_size(int argc, char *argv[]); int handle_format(int argc, char *argv[]); diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index 8f0b123833a..8c405c6cbf6 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -43,8 +43,11 @@ */ /***** Includes *****/ +#include "board.h" #include "cli.h" +#include "nvic_table.h" #include "sdhc.h" +#include "uart.h" #include "user-cli.h" #ifdef BOARD_EVKIT_V1 @@ -64,10 +67,14 @@ int main(void) while(MXC_UART_GetActive(MXC_UART_GET_UART(CONSOLE_UART))) {} // Initialize CLI - if ((err = CLI_Init(user_commands, num_user_commands)) != E_NO_ERROR) { + if ((err = MXC_CLI_Init(MXC_UART_GET_UART(CONSOLE_UART), user_commands, num_user_commands)) != E_NO_ERROR) { return err; } + // Enable Console/CLI UART IRQ vector + MXC_NVIC_SetVector(MXC_UART_GET_IRQ(CONSOLE_UART), CLI_IRQHandler); + NVIC_EnableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); + // Run CLI while (1) {} } diff --git a/Examples/MAX78000/SDHC_FTHR/src/user-cli.c b/Examples/MAX78000/SDHC_FTHR/src/user-cli.c index c933f030738..dfd67cfcd79 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/user-cli.c +++ b/Examples/MAX78000/SDHC_FTHR/src/user-cli.c @@ -52,6 +52,11 @@ const command_t user_commands[] = { const unsigned int num_user_commands = sizeof(user_commands) / sizeof(command_t); +void CLI_IRQHandler(void) +{ + MXC_CLI_Handler(); +} + int handle_size(int argc, char *argv[]) { if(argc != 1) { From c0bdda60576f825b0bb762aaeb7e17c5701e8d96 Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Thu, 14 Sep 2023 13:26:03 -0500 Subject: [PATCH 27/39] Make requested changes to CLI library --- Libraries/CLI/inc/cli.h | 24 ++++---- Libraries/CLI/src/cli.c | 124 +++++++++++----------------------------- 2 files changed, 46 insertions(+), 102 deletions(-) diff --git a/Libraries/CLI/inc/cli.h b/Libraries/CLI/inc/cli.h index 6519c4a9c90..314444378c8 100644 --- a/Libraries/CLI/inc/cli.h +++ b/Libraries/CLI/inc/cli.h @@ -49,7 +49,6 @@ All tokes are seperated by whitespace characters Lookup first token in a table of functions Dispatch to handler functions - */ #ifndef MXC_CLI_H @@ -59,14 +58,17 @@ #include "uart.h" /** - * @brief Command handler function prototype + * @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 Used to determine which token element of the - * commands string is being used. - * @param argv[] Char Array of tokens of the command string - * entered by the user. + * @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. + * @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[]); @@ -74,17 +76,17 @@ typedef int (*command_handler_t)(int argc, char *argv[]); * @brief Structure used to define the commands supported by the CLI */ typedef struct { - const char *name; /**< name of the command */ + 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 Console UART for CLI operations. + * @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 list of user-defined CLI commands + * @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. @@ -92,7 +94,7 @@ typedef struct { int MXC_CLI_Init(mxc_uart_regs_t *uart, const command_t *commands, unsigned int num_commands); /** - * @brief Shutdown the CLI. (Console UART will remain enabled.) + * @brief Shuts down the CLI. (UART will remain enabled.) * * @return E_NO_ERROR if successful, otheriwse an error code. */ diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index 6193ee20866..52992f710fc 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -45,24 +45,16 @@ /* -------------------------------------------------- */ // MACROS /* -------------------------------------------------- */ -#define ENTER 0X0D +// Characters +#define ENTER 0x0D #define NEW_LINE 0x0A #define SPACE 0x20 -#define TAB 0x09 -#define BACKSPACE 0X08 -#define MAXBUFF 2000 -#define DELETE 0x7F +#define BACKSPACE 0x08 #define DOLLAR 0x24 -#define ARROW_KEY_CODE_1 0x1B -#define ARROW_KEY_CODE_2 0x5B -#define ARROW_KEY_CODE_LEFT 0x44 -#define ARROW_KEY_CODE_RIGHT 0x43 -#define ARROW_KEY_CODE_UP 0x41 -#define ARROW_KEY_CODE_DOWN 0x42 - // 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 @@ -82,7 +74,6 @@ 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 -mxc_uart_regs_t *cli_uart = NULL; // Help Command const command_t help_command = { "Help", "help", "Prints details regarding the usage of the supported commands.", handle_help }; @@ -91,23 +82,14 @@ const command_t help_command = { "Help", "help", "Prints details regarding the u 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 Checks whether the current character is a white space character - * - * @param p Character to check - * - * @returns True - chacracter is whitespace, False - character is not whitespace - */ -bool white_space_present(char *p) -{ - return *p == SPACE || *p == TAB; -} - -/** - * @brief Clears the buffer storing each character of the user command input + * @brief Prints the CLI prompt */ void User_Prompt_Sequence(void) { @@ -117,7 +99,7 @@ void User_Prompt_Sequence(void) } /** - * @brief Clears the buffer storing each character of the user command input + * @brief Clears the buffer storing the command string */ void Clear_buffer(void) { @@ -164,16 +146,18 @@ void line_accumulator(uint8_t user_char) // Handle Enter or carriage return MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), NEW_LINE); MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), ENTER); - cmd_buf[buf_idx++] = '\r'; - cmd_buf[buf_idx] = '\0'; - buf_idx = 0; + + // 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 - 1) { + if (buf_idx < MAX_COMMAND_LENGTH) { cmd_buf[buf_idx++] = user_char; //pushes characters into the buffer MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), user_char); } @@ -188,60 +172,18 @@ void line_accumulator(uint8_t user_char) */ void process_command(char *input) { - // Initialize p and end pointers - char *p = input; - char *end; - - // Find end of string - for (end = input; *end != '\0'; end++) {} - // Initialize variables - bool in_token = false; - bool beginning_space; - char *argv[10]; + char *argv[MAX_COMMAND_TOKENS+1]; // Plus 1 so that argv can always be null terminated int argc = 0; - memset(argv, 0, sizeof(argv)); + int success_flag = 1; - if (*p == SPACE || *p == TAB) { - beginning_space = true; - } + // Initialize command string and token pointers + char *cmd = input; + char *token; - // Iterate over each character in input - for (p = input; p < end; p++) { - if (in_token) { - // If in a token - if (white_space_present(p)) { - // If whitespace found, token ends - in_token = true; - } else { - // Else, token continues - input = p; - in_token = false; - } - } else { - // If not in a token - if (*p == ENTER || (white_space_present(p))) { - // If ENTER or whitespace found - if (*p == ENTER) { - // If ENTER, end of command - *p = '\0'; - argv[argc++] = input; - } else if (white_space_present(p) && !beginning_space) { - // If whitespace found, end of token - *p = '\0'; - argv[argc++] = input; - in_token = true; - } - } else if (!white_space_present(p) && beginning_space) { - // If not in token and whitespace not found, new token starts - input = p; - in_token = false; - beginning_space = false; - } else if (!white_space_present(p) && !beginning_space) { - // If not in token and whitespace not found and beginning_space is false, token continues - in_token = false; - } - } + // 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 @@ -252,16 +194,14 @@ void process_command(char *input) return; } - int success_flag = 1; - // Check for a valid command - if (strcasecmp(argv[0], help_command.name) == 0) { + 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].name) == 0) { + if (strcasecmp(argv[0], command_table[i].cmd) == 0) { // Call corresponding command's handler success_flag = command_table[i].handler(argc, argv); break; @@ -269,11 +209,13 @@ void process_command(char *input) } } - // If no commands match, print error message + // Check for errors if (success_flag == 1) { - printf("\n\rCommand isn't valid!\n\r"); + // Command entered is not supported + printf("\nCommand isn't valid!\n"); } else if (success_flag < E_NO_ERROR) { - printf("\n\rEnter 'help' to see a list of available commands.\n\r"); + // 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 @@ -292,9 +234,9 @@ void process_command(char *input) */ int handle_help(int argc, char *argv[]) { - printf("\n\r"); + // Print out name, usage, and description of each supported command for (int i = 0; i < command_table_sz; i++) { - printf("%s:\n", command_table[i].name); + printf("\n%s:\n", command_table[i].cmd); printf(" Usage: %s\n", command_table[i].usage); printf(" Description: %s\n\n", command_table[i].description); } @@ -315,7 +257,7 @@ void CLI_Callback(mxc_uart_req_t *req, int error) // Process received character line_accumulator(char_recv); - // Start transaction to receive next character + // Get ready to receive next character MXC_UART_TransactionAsync(req); } From 24399f3fdbcf3dc43b471ad84ea4ca0e2112ff01 Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Thu, 14 Sep 2023 13:49:40 -0500 Subject: [PATCH 28/39] Update CLI README according to most recent changes in the CLI source. --- Libraries/CLI/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Libraries/CLI/README.md b/Libraries/CLI/README.md index a440eb5d963..daaaaad57df 100644 --- a/Libraries/CLI/README.md +++ b/Libraries/CLI/README.md @@ -25,7 +25,7 @@ The following graphic describes the software flow of the CLI. ## Porting Guide -The CLI library expects the user to implement the following steps: +The CLI library expects the user to implement the following steps in their application: 1. Add the following line to your project.mk file to include the CLI library in your project: ``` @@ -60,4 +60,8 @@ 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. -4. Include cli.h in the approriate source file and initialize the CLI by calling the CLI_Init function with a pointer to the command table, along with the number of entries in the command table. +4. Define an IRQ handler for the UART used in the CLI (i.e. UARTx_IRQHandler). This function should call MXC_CLI_Handler() which handles all of the interrupt processing for the CLI. + +5. Include cli.h in the approriate source file and initialize the CLI by calling the MXC_CLI_Init function with a pointer to to the UART for the CLI to use, a pointer to the command table, and the number of commands in the command table. + +6. Enable the appropriate interupt vector for the UART used in the CLI (NVIC_EnableIRQ(...)). From 6c4afa5004117cc4039d93686c153cdbac538548 Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Thu, 14 Sep 2023 14:37:08 -0500 Subject: [PATCH 29/39] Resolve linter errors --- Examples/MAX78000/SDHC_FTHR/include/sdhc.h | 6 +- .../MAX78000/SDHC_FTHR/include/user-cli.h | 6 +- Examples/MAX78000/SDHC_FTHR/main.c | 8 +- Examples/MAX78000/SDHC_FTHR/src/sdhc.c | 12 +-- Examples/MAX78000/SDHC_FTHR/src/user-cli.c | 90 +++++++++---------- Libraries/CLI/inc/cli.h | 6 +- Libraries/CLI/src/cli.c | 22 ++--- 7 files changed, 75 insertions(+), 75 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h index 0bee3b5b2d6..52db12d7149 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h +++ b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h @@ -30,8 +30,8 @@ * ownership rights. * ******************************************************************************/ -#ifndef SDHC_HEADER -#define SDHC_HEADER +#ifndef EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_SDHC_H_ +#define EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_SDHC_H_ /***** Includes *****/ #include @@ -80,4 +80,4 @@ int example(); void waitCardInserted(); -#endif //SDHC_HEADER \ No newline at end of file +#endif // EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_SDHC_H_ \ No newline at end of file diff --git a/Examples/MAX78000/SDHC_FTHR/include/user-cli.h b/Examples/MAX78000/SDHC_FTHR/include/user-cli.h index 857ead2392c..3d6c4125608 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/user-cli.h +++ b/Examples/MAX78000/SDHC_FTHR/include/user-cli.h @@ -30,8 +30,8 @@ * ownership rights. * ******************************************************************************/ -#ifndef USER_CLI_H -#define USER_CLI_H +#ifndef EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_USER_CLI_H_ +#define EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_USER_CLI_H_ /* -------------------------------------------------- */ // GLOBAL VARIABLE @@ -66,4 +66,4 @@ int handle_fatfs(int argc, char *argv[]); int handle_unmount(int argc, char *argv[]); -#endif /* USER_CLI_H */ +#endif // EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_USER_CLI_H_ diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index 8c405c6cbf6..a8eeb648c57 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -57,10 +57,10 @@ /******************************************************************************/ int main(void) { - int err; - printf("\n\n***** MAX78000 SDHC FAT Filesystem Example *****\n"); + int err; + printf("\n\n***** MAX78000 SDHC FAT Filesystem Example *****\n"); - // Wait for SD Card to be inserted + // Wait for SD Card to be inserted waitCardInserted(); printf("Card inserted.\n"); @@ -68,7 +68,7 @@ int main(void) // Initialize CLI if ((err = MXC_CLI_Init(MXC_UART_GET_UART(CONSOLE_UART), user_commands, num_user_commands)) != E_NO_ERROR) { - return err; + return err; } // Enable Console/CLI UART IRQ vector diff --git a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c index 8ebbf826582..dd51d59ba99 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c +++ b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c @@ -187,7 +187,7 @@ int createFile(char *file_name, unsigned int length) mount(); } - strcpy(filename, file_name); + snprintf(filename, MAXLEN, "%s", file_name); if (length > MAXLEN) { printf("Error. File size limit for this example is %d bytes.\n", MAXLEN); @@ -230,7 +230,7 @@ int appendFile(char *file_name, unsigned int length) mount(); } - strcpy(filename, file_name); + snprintf(filename, MAXLEN, "%s", file_name); if ((err = f_stat((const TCHAR *)filename, &fno)) == FR_NO_FILE) { printf("File %s doesn't exist!\n", (const TCHAR *)filename); @@ -273,7 +273,7 @@ int mkdir(char *dir_name) mount(); } - strcpy(directory, dir_name); + snprintf(directory, MAXLEN, "%s", dir_name); err = f_stat((const TCHAR *)directory, &fno); @@ -301,7 +301,7 @@ int cd(char *dir_name) mount(); } - strcpy(directory, dir_name); + snprintf(directory, MAXLEN, "%s", dir_name); if ((err = f_stat((const TCHAR *)directory, &fno)) == FR_NO_FILE) { printf("Directory doesn't exist (Did you mean mkdir?)\n"); @@ -326,7 +326,7 @@ int delete (char *file_name) mount(); } - strcpy(filename, file_name); + snprintf(filename, MAXLEN, "%s", file_name); if ((err = f_stat((const TCHAR *)filename, &fno)) == FR_NO_FILE) { printf("File or directory doesn't exist\n"); @@ -497,4 +497,4 @@ void waitCardInserted() } // Card has been detected, exit the function. -} \ No newline at end of file +} diff --git a/Examples/MAX78000/SDHC_FTHR/src/user-cli.c b/Examples/MAX78000/SDHC_FTHR/src/user-cli.c index dfd67cfcd79..a85e49c411e 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/user-cli.c +++ b/Examples/MAX78000/SDHC_FTHR/src/user-cli.c @@ -54,65 +54,65 @@ const unsigned int num_user_commands = sizeof(user_commands) / sizeof(command_t) void CLI_IRQHandler(void) { - MXC_CLI_Handler(); + MXC_CLI_Handler(); } int handle_size(int argc, char *argv[]) { - if(argc != 1) { - printf("Incorrect usage. Too many parameters.\n"); - return E_INVALID; - } + if (argc != 1) { + printf("Incorrect usage. Too many parameters.\n"); + return E_INVALID; + } return getSize(); } int handle_format(int argc, char *argv[]) { - if(argc != 1) { - printf("Incorrect usage. Too many parameters.\n"); - return E_INVALID; - } + if (argc != 1) { + printf("Incorrect usage. Too many parameters.\n"); + return E_INVALID; + } return formatSDHC(); } int handle_mount(int argc, char *argv[]) { - if(argc != 1) { - printf("Incorrect usage. Too many parameters.\n"); - return E_INVALID; - } + if (argc != 1) { + printf("Incorrect usage. Too many parameters.\n"); + return E_INVALID; + } return mount(); } int handle_ls(int argc, char *argv[]) { - if(argc != 1) { - printf("Incorrect usage. Too many parameters.\n"); - return E_INVALID; - } + if (argc != 1) { + printf("Incorrect usage. Too many parameters.\n"); + return E_INVALID; + } return ls(); } int handle_mkdir(int argc, char *argv[]) { - if(argc != 2) { - printf("Incorrect usage. Please provide directory name.\n"); - return E_INVALID; - } + if (argc != 2) { + printf("Incorrect usage. Please provide directory name.\n"); + return E_INVALID; + } return mkdir(argv[1]); } int handle_createfile(int argc, char *argv[]) { - if(argc != 3) { - printf("Incorrect usage. Please provide filename and length.\n"); - return E_INVALID; - } + if (argc != 3) { + printf("Incorrect usage. Please provide filename and length.\n"); + return E_INVALID; + } unsigned int length = atoi(argv[2]); return createFile(argv[1], length); @@ -120,20 +120,20 @@ int handle_createfile(int argc, char *argv[]) int handle_cd(int argc, char *argv[]) { - if(argc != 2) { - printf("Incorrect usage. Please provide directory name.\n"); - return E_INVALID; - } + if (argc != 2) { + printf("Incorrect usage. Please provide directory name.\n"); + return E_INVALID; + } return cd(argv[1]); } int handle_add_data(int argc, char *argv[]) { - if(argc != 3) { - printf("Incorrect usage. Please provide filename and length.\n"); - return E_INVALID; - } + if (argc != 3) { + printf("Incorrect usage. Please provide filename and length.\n"); + return E_INVALID; + } unsigned int length = atoi(argv[2]); return appendFile(argv[1], length); @@ -141,30 +141,30 @@ int handle_add_data(int argc, char *argv[]) int handle_del(int argc, char *argv[]) { - if(argc != 2) { - printf("Incorrect usage. Please provide filename.\n"); - return E_INVALID; - } + if (argc != 2) { + printf("Incorrect usage. Please provide filename.\n"); + return E_INVALID; + } return delete(argv[1]); } int handle_fatfs(int argc, char *argv[]) { - if(argc != 1) { - printf("Incorrect usage. Too many parameters.\n"); - return E_INVALID; - } + if (argc != 1) { + printf("Incorrect usage. Too many parameters.\n"); + return E_INVALID; + } return example(); } int handle_unmount(int argc, char *argv[]) { - if(argc != 1) { - printf("Incorrect usage. Too many parameters.\n"); - return E_INVALID; - } + if (argc != 1) { + printf("Incorrect usage. Too many parameters.\n"); + return E_INVALID; + } return umount(); } diff --git a/Libraries/CLI/inc/cli.h b/Libraries/CLI/inc/cli.h index 314444378c8..e7fb9b8614b 100644 --- a/Libraries/CLI/inc/cli.h +++ b/Libraries/CLI/inc/cli.h @@ -51,8 +51,8 @@ Dispatch to handler functions */ -#ifndef MXC_CLI_H -#define MXC_CLI_H +#ifndef LIBRARIES_CLI_INC_CLI_H_ +#define LIBRARIES_CLI_INC_CLI_H_ #include #include "uart.h" @@ -106,4 +106,4 @@ int MXC_CLI_Shutdown(void); */ void MXC_CLI_Handler(void); -#endif /* MXC_CLI_H */ \ No newline at end of file +#endif // LIBRARIES_CLI_INC_CLI_H_ \ No newline at end of file diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index 52992f710fc..8207f598b79 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -182,7 +182,7 @@ void process_command(char *input) char *token; // Parse command string (delimiters: space and tab) - while(argc < MAX_COMMAND_TOKENS && (token = strtok_r(cmd, " \t", &cmd))) { + while (argc < MAX_COMMAND_TOKENS && (token = strtok_r(cmd, " \t", &cmd))) { argv[argc++] = token; } @@ -197,7 +197,7 @@ void process_command(char *input) // Check for a valid command if (strcasecmp(argv[0], help_command.cmd) == 0) { // Help command received - success_flag = help_command.handler(argc, argv); + 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++) { @@ -215,7 +215,7 @@ void process_command(char *input) 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]); + printf("\nEnter 'help' for details on how to use the '%s' command.\n", argv[0]); } // Print prompt @@ -249,10 +249,10 @@ int handle_help(int argc, char *argv[]) */ void CLI_Callback(mxc_uart_req_t *req, int error) { - if(error == E_ABORT) { - // Shutdown called, nothing to do in callback - return; - } + if (error == E_ABORT) { + // Shutdown called, nothing to do in callback + return; + } // Process received character line_accumulator(char_recv); @@ -305,9 +305,9 @@ int MXC_CLI_Init(mxc_uart_regs_t *uart, const command_t *commands, unsigned int } // 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)) {} + printf("CLI Initialized! Enter 'help' to see a list of available commands.\n"); + User_Prompt_Sequence(); + while (MXC_UART_GetActive(uart)) {} return E_NO_ERROR; } @@ -327,7 +327,7 @@ int MXC_CLI_Shutdown(void) command_table = NULL; command_table_sz = 0; buf_idx = 0; - + return E_NO_ERROR; } From 4c54fb2814d9e50e9407636edb3c03385283e460 Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Thu, 14 Sep 2023 14:38:20 -0500 Subject: [PATCH 30/39] Update CLI library README --- Libraries/CLI/README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Libraries/CLI/README.md b/Libraries/CLI/README.md index daaaaad57df..8ea6a275207 100644 --- a/Libraries/CLI/README.md +++ b/Libraries/CLI/README.md @@ -6,7 +6,7 @@ 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 on device to: +This library provides an extensible command processor to: * Allow developers to get diagnostics and change device parameters interactively * Easily automate testing of the device, through PC-side scripting @@ -15,8 +15,6 @@ The current CLI Library includes the following features: - Can have spaces at beginning or end of string - Can have multiple spaces between words - Backspace -- Arrow keys -- Tab completion - An internally defined 'help' command which lists information about each of the user-defined commands ## CLI Software Flow @@ -47,7 +45,7 @@ Return Value Parameters - argc - Argument counter, number of tokens in the argument vector -- argv[] - Argument vector, tokenized command string +- argv[] - Argument vector, an srray 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.) Below is a sample handler function prototype for a "make directory" command. ``` @@ -64,4 +62,4 @@ The CLI library will tokenize the command string "mkdir new_folder" into "mkdir" 5. Include cli.h in the approriate source file and initialize the CLI by calling the MXC_CLI_Init function with a pointer to to the UART for the CLI to use, a pointer to the command table, and the number of commands in the command table. -6. Enable the appropriate interupt vector for the UART used in the CLI (NVIC_EnableIRQ(...)). +6. Enable the appropriate interupt vector for the UART used in the CLI (i.e. NVIC_EnableIRQ(...)). From 2ef153d1da513a286f40ca39b1ec249fd58f9091 Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Thu, 14 Sep 2023 14:48:01 -0500 Subject: [PATCH 31/39] Resolve clang-format errors --- Examples/MAX78000/SDHC_FTHR/include/sdhc.h | 2 +- Examples/MAX78000/SDHC_FTHR/main.c | 5 +++-- Examples/MAX78000/SDHC_FTHR/src/sdhc.c | 2 +- Examples/MAX78000/SDHC_FTHR/src/user-cli.c | 8 +++++--- Libraries/CLI/inc/cli.h | 4 ++-- Libraries/CLI/src/cli.c | 13 +++++++------ 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h index 52db12d7149..eac6f1a82c4 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h +++ b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h @@ -74,7 +74,7 @@ int mkdir(char *dir_name); int cd(char *dir_name); -int delete (char *file_name); +int deleteFile(char *file_name); int example(); diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index a8eeb648c57..581b6902022 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -64,10 +64,11 @@ int main(void) waitCardInserted(); printf("Card inserted.\n"); - while(MXC_UART_GetActive(MXC_UART_GET_UART(CONSOLE_UART))) {} + while (MXC_UART_GetActive(MXC_UART_GET_UART(CONSOLE_UART))) {} // Initialize CLI - if ((err = MXC_CLI_Init(MXC_UART_GET_UART(CONSOLE_UART), user_commands, num_user_commands)) != E_NO_ERROR) { + if ((err = MXC_CLI_Init(MXC_UART_GET_UART(CONSOLE_UART), user_commands, num_user_commands)) != + E_NO_ERROR) { return err; } diff --git a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c index dd51d59ba99..f842e42f69d 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/sdhc.c +++ b/Examples/MAX78000/SDHC_FTHR/src/sdhc.c @@ -320,7 +320,7 @@ int cd(char *dir_name) return err; } -int delete (char *file_name) +int deleteFile(char *file_name) { if (!mounted) { mount(); diff --git a/Examples/MAX78000/SDHC_FTHR/src/user-cli.c b/Examples/MAX78000/SDHC_FTHR/src/user-cli.c index a85e49c411e..cb631ab20a6 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/user-cli.c +++ b/Examples/MAX78000/SDHC_FTHR/src/user-cli.c @@ -42,9 +42,11 @@ const command_t user_commands[] = { { "mount", "mount", "Manually Mount Card", handle_mount }, { "ls", "ls", "list the contents of the current directory", handle_ls }, { "mkdir", "mkdir ", "Create a directory", handle_mkdir }, - { "file_create", "file_create ", "Create a file of random data", handle_createfile }, + { "file_create", "file_create ", + "Create a file of random data", handle_createfile }, { "cd", "cd ", "Move into a directory", handle_cd }, - { "add_data", "add_data ", "Add random Data to an Existing File", handle_add_data }, + { "add_data", "add_data ", + "Add random Data to an Existing File", handle_add_data }, { "del", "del ", "Delete a file", handle_del }, { "fatfs", "fatfs", "Format Card and Run Example of FatFS Operations", handle_fatfs }, { "unmount", "unmount", "Unmount card", handle_unmount }, @@ -146,7 +148,7 @@ int handle_del(int argc, char *argv[]) return E_INVALID; } - return delete(argv[1]); + return deleteFile(argv[1]); } int handle_fatfs(int argc, char *argv[]) diff --git a/Libraries/CLI/inc/cli.h b/Libraries/CLI/inc/cli.h index e7fb9b8614b..d131e9897f3 100644 --- a/Libraries/CLI/inc/cli.h +++ b/Libraries/CLI/inc/cli.h @@ -103,7 +103,7 @@ 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_ \ No newline at end of file +#endif // LIBRARIES_CLI_INC_CLI_H_ diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index 8207f598b79..6b27ea0127f 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -76,7 +76,9 @@ 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 }; +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; @@ -173,7 +175,7 @@ void line_accumulator(uint8_t user_char) void process_command(char *input) { // Initialize variables - char *argv[MAX_COMMAND_TOKENS+1]; // Plus 1 so that argv can always be null terminated + char *argv[MAX_COMMAND_TOKENS + 1]; // Plus 1 so that argv can always be null terminated int argc = 0; int success_flag = 1; @@ -246,7 +248,7 @@ int handle_help(int argc, char *argv[]) /** * @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) { @@ -278,7 +280,7 @@ int MXC_CLI_Init(mxc_uart_regs_t *uart, const command_t *commands, unsigned int } // Return error if CLI is already initialized - if(cli_uart != NULL) { + if (cli_uart != NULL) { return E_BAD_STATE; } @@ -288,8 +290,7 @@ int MXC_CLI_Init(mxc_uart_regs_t *uart, const command_t *commands, unsigned int command_table_sz = num_commands; // Initialize Console UART - if ((error = MXC_UART_Init(uart, UART_BAUD, MXC_UART_APB_CLK)) != - E_NO_ERROR) { + if ((error = MXC_UART_Init(uart, UART_BAUD, MXC_UART_APB_CLK)) != E_NO_ERROR) { printf("-->Error initializing CLI UART: %d\n", error); return error; } From b03762d924056a37214dc97aaee3bb2aa5d5e5ac Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Thu, 14 Sep 2023 14:50:47 -0500 Subject: [PATCH 32/39] Resolve linter errors (pt. 2) --- Examples/MAX78000/SDHC_FTHR/include/sdhc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h index eac6f1a82c4..0ed2f804344 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/sdhc.h +++ b/Examples/MAX78000/SDHC_FTHR/include/sdhc.h @@ -80,4 +80,4 @@ int example(); void waitCardInserted(); -#endif // EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_SDHC_H_ \ No newline at end of file +#endif // EXAMPLES_MAX78000_SDHC_FTHR_INCLUDE_SDHC_H_ From cc71c8f7e72855ea86335491ef815995e21532d2 Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Wed, 20 Sep 2023 12:43:51 -0500 Subject: [PATCH 33/39] Adding ability to select whether CLI handler is defined in application or library --- Libraries/CLI/CLI.mk | 7 +++++++ Libraries/CLI/README.md | 6 ++---- Libraries/CLI/src/cli.c | 46 ++++++++++++++++++++++++++++++----------- 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/Libraries/CLI/CLI.mk b/Libraries/CLI/CLI.mk index 1ddfe91874d..7e45d881ab8 100644 --- a/Libraries/CLI/CLI.mk +++ b/Libraries/CLI/CLI.mk @@ -46,6 +46,13 @@ VPATH += ${CLI_DIR}/src VPATH += $(dir $(SRCS)) +# 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. +PROJ_CFLAGS += -DUSE_CLI_LIB_IRQHANDLER + # Use absolute paths if building within eclipse environment. ifeq "$(ECLIPSE)" "1" SRCS := $(abspath $(SRCS)) diff --git a/Libraries/CLI/README.md b/Libraries/CLI/README.md index 8ea6a275207..1749d1a4cd9 100644 --- a/Libraries/CLI/README.md +++ b/Libraries/CLI/README.md @@ -58,8 +58,6 @@ 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. -4. Define an IRQ handler for the UART used in the CLI (i.e. UARTx_IRQHandler). This function should call MXC_CLI_Handler() which handles all of the interrupt processing for the CLI. +4. Include cli.h in the approriate source file and initialize the CLI by calling the MXC_CLI_Init function with a pointer to to the UART for the CLI to use, a pointer to the command table, and the number of commands in the command table. -5. Include cli.h in the approriate source file and initialize the CLI by calling the MXC_CLI_Init function with a pointer to to the UART for the CLI to use, a pointer to the command table, and the number of commands in the command table. - -6. Enable the appropriate interupt vector for the UART used in the CLI (i.e. NVIC_EnableIRQ(...)). +5. THIS IS OPTIONAL! Users have the option to either allow the CLI library to override the CLI UART interrupt vector and handle interrupts internally (default behavior, no additional steps necessary) or to define an IRQ handler for the CLI UART in their application. If the user chooses to define an IRQ handler in their application they will also need to comment out the definition of "USE_CLI_LIB_IRQHANDLER" in the CLI.mk file and call MXC_CLI_Handler() from their handler function whenever they are using the CLI. diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index 6b27ea0127f..35afb88ef90 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -95,9 +95,13 @@ mxc_uart_regs_t *cli_uart = NULL; */ void User_Prompt_Sequence(void) { - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), NEW_LINE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), DOLLAR); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), SPACE); + if (cli_uart == NULL) { + return; + } + + MXC_UART_WriteCharacter(cli_uart, NEW_LINE); + MXC_UART_WriteCharacter(cli_uart, DOLLAR); + MXC_UART_WriteCharacter(cli_uart, SPACE); } /** @@ -113,9 +117,13 @@ void Clear_buffer(void) */ void Console_Backspace_Sequence(void) { - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), BACKSPACE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), SPACE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), BACKSPACE); + if (cli_uart == NULL) { + return; + } + + MXC_UART_WriteCharacter(cli_uart, BACKSPACE); + MXC_UART_WriteCharacter(cli_uart, SPACE); + MXC_UART_WriteCharacter(cli_uart, BACKSPACE); } /** @@ -133,6 +141,10 @@ void Console_Cmd_Clear(void) */ void line_accumulator(uint8_t user_char) { + if (cli_uart == NULL) { + return; + } + switch (user_char) { case BACKSPACE: // Handle Backspace and Delete @@ -146,8 +158,8 @@ void line_accumulator(uint8_t user_char) case ENTER: // Handle Enter or carriage return - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), NEW_LINE); - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), ENTER); + MXC_UART_WriteCharacter(cli_uart, NEW_LINE); + MXC_UART_WriteCharacter(cli_uart, ENTER); // Parse and execute command process_command(cmd_buf); @@ -161,7 +173,7 @@ void line_accumulator(uint8_t user_char) // Handle all other characters if (buf_idx < MAX_COMMAND_LENGTH) { cmd_buf[buf_idx++] = user_char; //pushes characters into the buffer - MXC_UART_WriteCharacter(MXC_UART_GET_UART(CONSOLE_UART), user_char); + MXC_UART_WriteCharacter(cli_uart, user_char); } break; } @@ -269,9 +281,10 @@ void CLI_Callback(mxc_uart_req_t *req, int error) 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 (MXC_UART_GET_IDX(uart) < 0) { + if (uart_idx < 0) { return E_BAD_PARAM; } else if (commands == NULL) { return E_NULL_PTR; @@ -310,6 +323,15 @@ int MXC_CLI_Init(mxc_uart_regs_t *uart, const command_t *commands, unsigned int 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; } @@ -321,7 +343,7 @@ int MXC_CLI_Shutdown(void) } // Abort existing async transaction - MXC_UART_AbortAsync(MXC_UART_GET_UART(CONSOLE_UART)); + MXC_UART_AbortAsync(cli_uart); // Reset state variables cli_uart = NULL; @@ -339,5 +361,5 @@ void MXC_CLI_Handler(void) return; } - MXC_UART_AsyncHandler(MXC_UART_GET_UART(CONSOLE_UART)); + MXC_UART_AsyncHandler(cli_uart); } From d90901faa724af6bdbd5de4c8b3a17bea5cf454f Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Wed, 20 Sep 2023 14:37:47 -0500 Subject: [PATCH 34/39] Resolve clang-format errors --- Libraries/CLI/src/cli.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Libraries/CLI/src/cli.c b/Libraries/CLI/src/cli.c index 35afb88ef90..008e2aa8bfa 100644 --- a/Libraries/CLI/src/cli.c +++ b/Libraries/CLI/src/cli.c @@ -323,11 +323,11 @@ int MXC_CLI_Init(mxc_uart_regs_t *uart, const command_t *commands, unsigned int User_Prompt_Sequence(); while (MXC_UART_GetActive(uart)) {} - #ifdef USE_CLI_LIB_IRQHANDLER +#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 +#endif // USE_CLI_LIB_IRQHANDLER // Enable interrupts NVIC_EnableIRQ(MXC_UART_GET_IRQ(uart_idx)); From 836d6351613b94c2a47e80db177518d0ac565bb6 Mon Sep 17 00:00:00 2001 From: Scheiffler Date: Wed, 20 Sep 2023 15:16:05 -0500 Subject: [PATCH 35/39] Update SDHC_FTHR examples to be compatible with latest CLI changes --- Examples/MAX78000/SDHC_FTHR/include/user-cli.h | 2 -- Examples/MAX78000/SDHC_FTHR/main.c | 4 ---- Examples/MAX78000/SDHC_FTHR/src/user-cli.c | 5 ----- 3 files changed, 11 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/include/user-cli.h b/Examples/MAX78000/SDHC_FTHR/include/user-cli.h index 3d6c4125608..ecb8850eab8 100644 --- a/Examples/MAX78000/SDHC_FTHR/include/user-cli.h +++ b/Examples/MAX78000/SDHC_FTHR/include/user-cli.h @@ -42,8 +42,6 @@ extern const unsigned int num_user_commands; /* -------------------------------------------------- */ // FUNCTION PROTOTYPES /* -------------------------------------------------- */ -void CLI_IRQHandler(void); - int handle_size(int argc, char *argv[]); int handle_format(int argc, char *argv[]); diff --git a/Examples/MAX78000/SDHC_FTHR/main.c b/Examples/MAX78000/SDHC_FTHR/main.c index 581b6902022..fb72a39f3e7 100644 --- a/Examples/MAX78000/SDHC_FTHR/main.c +++ b/Examples/MAX78000/SDHC_FTHR/main.c @@ -72,10 +72,6 @@ int main(void) return err; } - // Enable Console/CLI UART IRQ vector - MXC_NVIC_SetVector(MXC_UART_GET_IRQ(CONSOLE_UART), CLI_IRQHandler); - NVIC_EnableIRQ(MXC_UART_GET_IRQ(CONSOLE_UART)); - // Run CLI while (1) {} } diff --git a/Examples/MAX78000/SDHC_FTHR/src/user-cli.c b/Examples/MAX78000/SDHC_FTHR/src/user-cli.c index cb631ab20a6..5800768c471 100644 --- a/Examples/MAX78000/SDHC_FTHR/src/user-cli.c +++ b/Examples/MAX78000/SDHC_FTHR/src/user-cli.c @@ -54,11 +54,6 @@ const command_t user_commands[] = { const unsigned int num_user_commands = sizeof(user_commands) / sizeof(command_t); -void CLI_IRQHandler(void) -{ - MXC_CLI_Handler(); -} - int handle_size(int argc, char *argv[]) { if (argc != 1) { From 8d621d8c09cd8149ff20cbc8b8fa2107a8d690fc Mon Sep 17 00:00:00 2001 From: Jake Carter Date: Wed, 20 Sep 2023 18:23:21 -0500 Subject: [PATCH 36/39] Update documentation, add LIB_CLI build toggle, tweak makefiles --- Examples/MAX78000/SDHC_FTHR/project.mk | 5 +- Libraries/CLI/CLI.mk | 14 +++-- Libraries/CLI/README.md | 78 ++++++++++++++++---------- Libraries/libs.mk | 9 +++ USERGUIDE.md | 5 +- 5 files changed, 72 insertions(+), 39 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/project.mk b/Examples/MAX78000/SDHC_FTHR/project.mk index b5222a6bdf8..8b063ff751d 100644 --- a/Examples/MAX78000/SDHC_FTHR/project.mk +++ b/Examples/MAX78000/SDHC_FTHR/project.mk @@ -19,7 +19,8 @@ ifneq ($(BOARD),FTHR_RevA) $(error ERR_NOTSUPPORTED: This project requires an SD card slot and is only supported for the MAX78000FTHR) endif +# Enable SDHC library LIB_SDHC = 1 -include ${MAXIM_PATH}/Libraries/CLI/CLI.mk - +# Enable CLI library +LIB_CLI = 1 diff --git a/Libraries/CLI/CLI.mk b/Libraries/CLI/CLI.mk index 7e45d881ab8..fcc5d551ca5 100644 --- a/Libraries/CLI/CLI.mk +++ b/Libraries/CLI/CLI.mk @@ -36,22 +36,24 @@ # project. ############################################################################### -ifeq "$(CLI_DIR)" "" +ifeq "$(LIB_CLI_DIR)" "" # If CLI_DIR is not specified, this Makefile will locate itself. -CLI_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +LIB_CLI_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) endif -IPATH += ${CLI_DIR}/inc -VPATH += ${CLI_DIR}/src - -VPATH += $(dir $(SRCS)) +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" diff --git a/Libraries/CLI/README.md b/Libraries/CLI/README.md index 1749d1a4cd9..7e85f0d9cd4 100644 --- a/Libraries/CLI/README.md +++ b/Libraries/CLI/README.md @@ -7,57 +7,75 @@ 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: -* Allow developers to get diagnostics and change device parameters interactively -* Easily automate testing of the device, through PC-side scripting + +- 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: -- Case insensitive commands & arguments -- Can have spaces at beginning or end of string -- Can have multiple spaces between words -- Backspace -- An internally defined 'help' command which lists information about each of the user-defined commands + +- 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. ## CLI Software Flow + The following graphic describes the software flow of the CLI. ![Processing steps](res/CLI-Processing-steps.png) ## Porting Guide -The CLI library expects the user to implement the following steps in their application: +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 file to include the CLI library in your project: -``` -include ${MAXIM_PATH}/Libraries/CLI/CLI.mk -``` +1. Add the following line to your [project.mk](../../USERGUIDE.md#build-configuration-variables) file to enable the CLI library for your project: -2. 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 command_t element should define the name of the command, a string showing how to enter the command in the terminal, a description of what the command does, and a function pointer to the corresponding handler function. + :::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: + ``` const command_t user_commands[] = {{ "format", "format", "Format the Card", handle_format }, { "mkdir", "mkdir ", "Create a directory", handle_mkdir }} ``` -3. Define a handler function for each command. +4. Implement a handler function for each command. -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. + 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 -- argc - Argument counter, number of tokens in the argument vector -- argv[] - Argument vector, an srray 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.) + Parameters -Below is a sample handler function prototype for a "make directory" command. -``` -int handle_mkdir(int argc, char *argv[]); -``` + - 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.) -As an example, suppose a user entered the command: -``` -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. + 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 prototype for a "make directory" command. + + :::C + int handle_mkdir(int argc, char *argv[]); + + 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. -4. Include cli.h in the approriate source file and initialize the CLI by calling the MXC_CLI_Init function with a pointer to to the UART for the CLI to use, a pointer to the command table, and the number of commands in the command table. +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. -5. THIS IS OPTIONAL! Users have the option to either allow the CLI library to override the CLI UART interrupt vector and handle interrupts internally (default behavior, no additional steps necessary) or to define an IRQ handler for the CLI UART in their application. If the user chooses to define an IRQ handler in their application they will also need to comment out the definition of "USE_CLI_LIB_IRQHANDLER" in the CLI.mk file and call MXC_CLI_Handler() from their handler function whenever they are using the CLI. +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/Libraries/libs.mk b/Libraries/libs.mk index b71a9e56a00..75ca70bd53c 100644 --- a/Libraries/libs.mk +++ b/Libraries/libs.mk @@ -231,3 +231,12 @@ BARCODE_DECODER_DIR ?= $(LIBS_DIR)/MiscDrivers/BarcodeDecoder/zbar include $(BARCODE_DECODER_DIR)/barcode_decoder.mk endif # ************************ + +# CLI (Disabled by default) +# ************************ +LIB_CLI ?= 0 +ifeq ($(LIB_CLI), 1) +LIB_CLI_DIR ?= $(LIBS_DIR)/CLI +include $(LIB_CLI_DIR)/CLI.mk +endif +# ************************ diff --git a/USERGUIDE.md b/USERGUIDE.md index 4dc9bd14580..0bc635ef6b7 100644 --- a/USERGUIDE.md +++ b/USERGUIDE.md @@ -1894,6 +1894,7 @@ The following variables can be used to modularly toggle the [available libraries | `LIB_LWIP` | Include the lwIP library | | | `LIB_MAXUSB` | Include the MaxUSB library | This option toggles the inclusion of the MAXUSB library, which facilitates the use of the native USB peripherals on some microcontrollers. Set to `0` to disable or `1` to enable. | | `LIB_SDHC` | Include the SDHC library | This option toggles the Secure Digital High Capacity (SDHC) library, which can be used to interface with SD cards. Additionally, it enables the [FatFS](http://elm-chan.org/fsw/ff/00index_e.html) library, which implements a generic FAT filesystem. | +| `LIB_CLI` | Include the MSDK's built-in CLI library | This option toggles the MSDK's built-in CLI library, which can be used to process received commands over UART. | #### Build Variables for Secure Boot Tools (SBTs) @@ -2302,7 +2303,9 @@ FreeRTOS is supported by all parts in the MSDK. See the `FreeRTOSDemo` example ### CLI -The [`Libraries/CLI`](Libraries/CLI) folder of the MSDK contains a ready to use CLI feature which uses a 2-step process of a line accumulator and a process command function to perform the CLI operationgs. Refer to [`Examples/MAX78000/SDHC_FTHR`](Examples/MAX78000/SDHC_FTHR) for implementation. +Developing a UART Command-Line Interface (CLI) is a common task while developing embedded firmware. The MSDK contains a pre-made command processing library in the `Libraries/CLI` that can be used to simplify and speed up development. + +See the [`Libraries/CLI/README.md`](Libraries/CLI/README.md) document for more details. ### CoreMark From 375ad1940cfac0ff2f31cfaa364377887940cd7e Mon Sep 17 00:00:00 2001 From: Jake Carter Date: Wed, 20 Sep 2023 18:25:03 -0500 Subject: [PATCH 37/39] Update project files --- Examples/MAX78000/SDHC_FTHR/.cproject | 3 +-- Examples/MAX78000/SDHC_FTHR/.vscode/settings.json | 5 ++--- Examples/MAX78000/SDHC_FTHR/Makefile | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Examples/MAX78000/SDHC_FTHR/.cproject b/Examples/MAX78000/SDHC_FTHR/.cproject index b001f11bddc..7b66070ece0 100644 --- a/Examples/MAX78000/SDHC_FTHR/.cproject +++ b/Examples/MAX78000/SDHC_FTHR/.cproject @@ -32,8 +32,7 @@ - - +