{"id":925,"date":"2021-07-19T15:22:09","date_gmt":"2021-07-19T15:22:09","guid":{"rendered":"https:\/\/socmaker.com\/?p=925"},"modified":"2021-07-19T15:22:09","modified_gmt":"2021-07-19T15:22:09","slug":"3d-printer-text-based-menu","status":"publish","type":"post","link":"https:\/\/socmaker.com\/?p=925","title":{"rendered":"3D Printer Text Based Menu"},"content":{"rendered":"\n<p>Life is what happens while you are making other plans. (This quote is a secular rephrasing of Proverbs 16:9 circa 350 B.C.  See https:\/\/quoteinvestigator.com\/2012\/05\/06\/other-plans\/)<\/p>\n\n\n\n<p>Well, I have been hit squarely in the head by my proposing and God&#8217;s disposing.  That makes this post *much* later than it should be.  Onward!<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">PSOC Creator, Debugging, and VMWare: An Aside<\/h4>\n\n\n\n<p>With Creator 4.4 (and maybe 4.2), compatibility with VMWare has undergone some changes.  It still works, but it has much personality.  (That is a code word for a mental condition.)  This personality provides some quirky results for debug.<\/p>\n\n\n\n<p>If you are using the CY8CKit-059 break away board (KitProg 2.21) for debugging, when you click on the &#8220;bug&#8221; icon, many times nothing appears to happen (at least under windows 7).  The selection window for what debug device to connect to does not appear.  No amount of clicking on the Creator window appears to work. You are stuck.<\/p>\n\n\n\n<p>Something <em>did<\/em> happen.  The debugging menu did show, <em><strong>behind<\/strong> <\/em>the Creator Main window<em>.<\/em>  The problem with that is the debugging window is a modal dialog.  Modal dialogs steal all the keystrokes and mouse clicks, but only if they are on top.  The Creator main window is waiting until the modal dialog closes before it accepts any input from the user, which can&#8217;t happen because the modal dialog is hiding behind the main window.  Either Microsoft broke the &#8220;window on top flag&#8221; implementation, or Infineon engineers broke displaying the modal dialog.  I suspect different rules for Windows 10 versus Windows 7 are at the root of the problem.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Solution<\/h4>\n\n\n\n<p>The solution is very simple, but took a while to find out.  Go to the VMWare &#8220;USB &amp; Bluetooth&#8221; menu, and select &#8220;Disconnect KitProg&#8221; if it is connected.  Then go back immediately and select &#8220;Connect KitProg.&#8221;  This will force the modal window into the foreground and allow you to select, connect, and start your debug session.  It may take a few seconds for the KitProg to show back up in the Modal Dialog.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">MENUS<\/h4>\n\n\n\n<p>I have written some MENU code for FreeRTOS which allows the RepRap display&#8217;s A\/B phased rotary input to work.  It uses a button press to select the line the menu indicator &#8216;>&#8217; is on, and perform some action.  In my previous experiences with user menu selection (all the way back to 1976), I have found that (my) menu code always follows the following pattern(s):<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Display Line after Line Of Text (LOT) (or icons)<\/li><li>Allow the user to select the LOT (or icon) by some indication.  (Either a number on a keyboard or a button press)<\/li><li>Perform immediate action<\/li><li>Give the User some Feedback that indicates the action was performed.  (Usually a click or a beep)<\/li><\/ol>\n\n\n\n<p>Ideally, the menu text can be modified without having to write new lines of code.  In most cases, as your menu grows, something will be forgotten, and the menu code needs updates.  After a certain amount of time, the menu code can be frozen and used as is, with only menu text\/icons changing.<\/p>\n\n\n\n<p>The following code *should* provide a fairly robust menu.  No guarantees.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">The Main Structure <\/h4>\n\n\n\n<p>The menu task needs a C structure to allow keeping together the items needed for easy access by the menu interpreter\/handler.  This structure consists of several enum variables, a pointer to a function, and a pointer to text to display.  <\/p>\n\n\n\n<p>The use of enum variables are used to allow the compiler to flag you if you enter the wrong kind of value while setting up the array.  I try to allow the compiler to tell me when I make mistakes.  This makes it more likely that my first attempt at running new code does not result in a fire, a real possibility with a 3D printer.  (An Aside: The old Motorola 6800 processor had an unlisted instruction that experimenters called HCF.  If that instruction was executed, the processor would Halt and heat up. If left alone, it would burn up the processor, and could cause paper to burn.  So, Halt and Catch Fire (HCF) became the instruction moniker.  You could literally destroy a computer with that instruction if it were left powered in that state long enough.)<\/p>\n\n\n\n<p>Each Menu can cause another menu to be shown.  (i.e. more menu items)  Each menu can also execute code (i.e. call a function).  The menu interpreter uses the enums to tell it to either exit to a top level menu (or leave the menu system), show a sub-menu, or execute a function when the menu is selected.  <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Header File<\/h4>\n\n\n\n<p>Following is the header file for the submenu system.  This is rather simple.  The #ifndef _MENU_H_  \/ #define _MENU_H_ allows you to include this file in other headers and files without accidentally re-running any #defines, causing compiler complaints or errors: <\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:9px\"><code>   #ifndef _MENU_H_\n    #define _MENU_H_\n\/* SOCino 3d printer firmware, FreeRTOS version\n *\n * Copyright 2021, Wade Maxfield\n * Written by Wade Maxfield\n *\n * Commercial license Available.\n *\n * This program is free software: you can redistribute it and\/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see &lt;http:\/\/www.gnu.org\/licenses\/>.\n *\n   This license does not override previous licenses\n *\/\n#include \"main.h\"\n#include \"Configuration.h\"\n       void vAltStartMenuTask(int16_t priority);\/\/ MenuTask.c\n\n    extern QueueHandle_t       MenuTaskQueue;          \/\/ Queue for messages\n\n\nenum SubMenuSelectionEnum {\n     noSubMenu=0\n    ,subMenuExitToPreviousMenu  \/\/ keep a menu stack and move back\n    ,subMenuExitMenuSystem  \/\/ shut down menus, exit to normal display\n    ,subMenuMainMenu\n    ,subMenuExecuteFunction\n    ,subMenuPrinterInfo\n    ,numberOfItemsInSubMenuSelectionEnum\n};\n\nenum MenuSelectionEnum {\n     noMenuSelection=0\n    ,menuSelectionExitLevel     \/\/ exit this menu level\n    ,menuShowPrintVolume        \/\/ show printer xyz information\n    ,numberOfItemsInMenuSelectionEnum\n};\n    \ntypedef struct menu_map_struct {\n    enum SubMenuSelectionEnum   SubmenuID;          \/\/ This sub menu id\n    enum MenuSelectionEnum      MenuItemID;         \/\/ menu item selection ID\n    enum SubMenuSelectionEnum   SubmenuIDToMoveTo;  \/\/ sub menu to show if this is pressed (0 for no item) \n    uint16 (*MenuFunction)(const struct menu_map_struct *me,TaskMonitor_t *TaskMonitorPtr ); \n    char                       *MenuItemText;    \n} MENU_MAP_t;\n\n\n#define BUTTON_MENU_SELECT_PIN D35_D7_pin\n\n#endif\n\/* &#91;] END OF FILE *\/\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Menu Strings<\/h4>\n\n\n\n<p>For the currently selected display, we have a 16 character wide display.  The &#8216;>&#8217; character is used to show the currently selected menu item.   Pressing the rotary switch in selects that line of text.  Therefore, each menu line must only use 15 characters.  The first character is always a space, and the menu engine replaces that character with the &#8216;>&#8217; character.<\/p>\n\n\n\n<p>If you are showing information, you can use all of the 16 characters across the screen.  I use sprintf() to format lines easily, but that can easily lead to too many characters in the string, so be careful when you craft dynamic menus.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:9px\"><code>#ifndef _MENUSTRINGS_H_\n    #define _MENUSTRINGS_H_\n\/* SOCino 3d printer firmware, FreeRTOS version\n *\n * Copyright 2021, Wade Maxfield\n * Written by Wade Maxfield\n *\n * Commercial license Available.\n *\n * This program is free software: you can redistribute it and\/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see &lt;http:\/\/www.gnu.org\/licenses\/>.\n *\n    This license does not override previous licenses\n *\/\n#include \"includes.h\"\n#include \"Configuration.h\"\n\n    \/*\n       Top Level Menu:\n    \n        Exit Menus\n        Printer Information\n        \n    *\/\n\n#if USE_LANGUAGE == ENGLISH_LANGUAGE\n    \/\/-------------------------------------------------------------------------\n    \/\/ TOP LEVEL MENU\n    \/\/      only 15 chars allowed per menu (the '>' selection char is char 0)\n    \/\/                                      0123456789012345 &lt;--no chars past 5\n    \/\/-------------------------------------------------------------------------\n    #define MENU_EXIT_MENU_STRING          \" Exit Menu      \"\n    #define MENU_PRINTER_INFO_STRING       \" Printer Info   \"\n    \/\/-------------------------------------------------------------------------\n    \/\/ PRINTER INFO MENU\n    \/\/      only 15 chars allowed per menu (the '>' is char 0)\n    \/\/                                           0123456789012345 &lt;--no chars past 5\n    \/\/-------------------------------------------------------------------------\n    #define MENU_PRINT_VOLUME_LIMITS_STRING     \" Print Vol. Info\"\n\n\n    \/\/-------------------------------------------------------------------------\n\n    \n    \n    \/\/ Show Print Volume info\n    \/\/      only 16 chars allowed.  sprintf() format, be careful to not exceed width\n    \/\/                              1234567890123456 &lt;--no chars past 6\n    #define MENU_PRINT_VOLUME_1    \"Printer Capacity\"\n    #define MENU_PRINT_VOLUME_2    \"X Axis: %d mm\"\n    #define MENU_PRINT_VOLUME_3    \"Y Axis: %d mm\"\n    #define MENU_PRINT_VOLUME_4    \"Z Axis: %d mm\"\n    #define MENU_PRINT_VOLUME_5    \"                \"\n    #define MENU_PRINT_VOLUME_6    \"----------------\"\n    #define MENU_PRINT_VOLUME_7    \"Push Button To  \"\n    #define MENU_PRINT_VOLUME_8    \"Return To Menu  \"\n    \n    \n#endif\n\n\n\n#endif\n\n\n\/* &#91;] END OF FILE *\/\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">The Code<\/h4>\n\n\n\n<p>In the following code, the <em>MenuMap<\/em> is the controlling array for the menu, providing the instructions for the menu interpreter\/handler.  Examine the code, read the comments, debug it in Creator to understand how it is working.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">ISR&#8217;s <\/h4>\n\n\n\n<p>The hardware used is shown in the previous post.  It is deceptively simple.<\/p>\n\n\n\n<p>The CY_ISR(EncPressISR){} is an interrupt service routine that posts to the menu task queue the fact that a button press occurred. The menu task runs the menu interpreter, which handles the menu system. (See the previous post for the PSOC internal hardware behind the menu system.)<\/p>\n\n\n\n<p>The CY_ISR(EncoderChangeOccurred){} is an interrupt service routine that also posts to the menu task queue the fact the user twisted the rotary switch.  I found that the ISR can execute while the rotary switch is still bouncing, causing nothing to be read.  Through experimentation, a maximum of 10 consecutive reads allows the code to see if A is high (with B being low), or the reverse.  Once a bit is read high, the encoder has been successfully read, and the routine can continue.  Since this ISR triggers due to user interaction,  the system is not fully in real time mode (more or less), so spending a few extra microseconds in the encoder ISR does not hurt.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:9px\"><code>\/* SOCino 3d printer firmware, FreeRTOS version\n *\n * Copyright 2021, Wade Maxfield\n * Written by Wade Maxfield\n *\n * Commercial license Available.\n *\n * This program is free software: you can redistribute it and\/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see &lt;http:\/\/www.gnu.org\/licenses\/>.\n *\n    This license does not override previous licenses\n *\/\n#include \"Menus\/MenuTask.h\"\n#include \"Menus\/MenuStrings.h\" \/\/ all menu text is in the MenuStrings.h file\n#include \"Beep\/BeepTask.h\"\n#include \"Display\/Display.h\"\n\n#if ENABLE_DISPLAY\n\n\/*\n    Basically, use the menu of the following form:\n    >Menu Option 1\n     Menu Option 2\n     Menu Option 3\n     .\n     Menu Option 8 \/\/ 8 lines 16 chars\n\n    1) Rotating the knob will move the '>' up or down\n    2) Clicking the knob (pressing it down) will select that menu item.\n    \n\n\n*\/\n    \n#define MenuTaskSTACK_SIZE  (configMINIMAL_STACK_SIZE*3) \/\/ sprintf used\n#define MenuTask_QueueSize   (4)\/\/ random number\n\n\n\/\/ variables\nQueueHandle_t       MenuTaskQueue;          \/\/ Queue for messages\n\n\n    \/\/ external menu functions\n#include \"Menus\/MenuHandlerFunctions.h\"\n    \n\/\/ static const puts the following array into flash, consuming no ram.\nstatic const MENU_MAP_t MenuMap&#91;] = {\n\/\/      SubMenuId           MenuItemId              SubmenuIDToMoveTo       MenuFunction        MenuText (MenuStrings.h)\n     {  subMenuMainMenu,    menuSelectionExitLevel, subMenuExitMenuSystem,  0,                  MENU_EXIT_MENU_STRING }\n\/\/                          this selection only moves to sub menu \"PrinterInfo\"   \n    ,{  subMenuMainMenu,    noMenuSelection,        subMenuPrinterInfo,     0,                  MENU_PRINTER_INFO_STRING }\n\n    \/\/ each submenu is listed in order of strings shown on display\n    ,{  subMenuPrinterInfo, menuSelectionExitLevel, subMenuMainMenu,        0,                  MENU_EXIT_MENU_STRING }\n    \/\/ the following submenu line calls the function mhfShowPrintVolume(menuMapPtr,TaskInfoPtr)\n    \/\/ to show text calculated inside the function on the screen \n    \/\/ mhfShowPrintVolume() returns pdTRUE so a button press will magically exit the submenu.\n    ,{  subMenuPrinterInfo, menuShowPrintVolume,    subMenuExecuteFunction,mhfShowPrintVolume,  \"\" }\n         \n};\n\n#define MENU_STACK_DEPTH 32\n\nstatic int16 gMenuStack&#91;MENU_STACK_DEPTH+1];     \/\/ id's of the menus being traveled\n       int16 gMenuItemSelected;                 \/\/ current menu item selected in the MenuMap&#91;]\nstatic int16 gMenuStackPointer;                  \/\/ pointer to current menu id\nstatic int16 gNumberOfMenuItemsOnCurrentSubmenu;\nstatic int16 gMenuIndicator;\nstatic int16 ReturnFromMenuHandlerFunctionWhenButtonPressedFlag;\n\nuint16               gEncoderStatus;            \/\/ updated by the IRQ\nstatic uint16       _encoderChangeCount;        \/\/ incremented by one when we detect encoder changes\nstatic uint16       _buttonPressCount;          \/\/ incremented by one when we detect button press\nstatic int16        gMenuIsControllingDisplay;  \/\/ set when menu is controlling display\n\n\n\n\/\/----------------------------------------------------------------------------------------\n\/\/ display the text for the submenu on the display.\n\/\/----------------------------------------------------------------------------------------\nvoid showSubMenu(enum SubMenuSelectionEnum submenuID,TaskMonitor_t *TaskMonitorPtr,int16 _ClearDisplay){\n    int16 i,i1;\n    int16 max =sizeof(MenuMap)\/sizeof(MENU_MAP_t);\n    int16 found=0;\n    const MENU_MAP_t *menuMapPtr;\n    \n    if (_ClearDisplay)\n        clearDisplay(TaskMonitorPtr);\n    \n    \/\/ find the first matching submenu\n    for (i1 =0 ; i1 &lt; max; i1++){\n        if (MenuMap&#91;i1].SubmenuID==submenuID){\n            found=1;\n            break;\n        }\n    }\n    if (!found)\n        return; \/\/ error, bail silently\n   \n    \/\/ i1 now points to the first submenu.  now print all the one up to 16\n    \/\/ *NO* formatting (sprintf) on menu items for now\n    for (i=0; i &lt; gNumberOfMenuItemsOnCurrentSubmenu &amp;&amp; i1 &lt; max; i1++){\n        menuMapPtr=&amp;MenuMap&#91;i1];\n        if (menuMapPtr->SubmenuID==submenuID){\n            if (menuMapPtr->MenuFunction){\n                \/\/ if the function pointer is corrupted, we are toast here.\n                \/\/ if function returns non-zero, button push will bump up a menu level\n                ReturnFromMenuHandlerFunctionWhenButtonPressedFlag =\n                    (menuMapPtr->MenuFunction)(menuMapPtr,TaskMonitorPtr ); \n            } else {\n                showTextOnTheDisplay(i,0,(uint8*)(menuMapPtr->MenuItemText),1, 50,TaskMonitorPtr);\n                if (i==gMenuIndicator){\n                   char c&#91;3]={ '>',' ',0 };\n                   c&#91;1]= menuMapPtr->MenuItemText&#91;1];\/\/ pick up the character the > is next to\n                    showTextOnTheDisplay(i,0,(uint8*)c,1, 50,TaskMonitorPtr);                \n                }       \n            }\n\n           i++; \/\/increment the item number index, we printed it on display\n        }                   \n    }    \n    \n    \/\/showTextOnTheDisplay(gMenuIndicator,0,\">\",1,50,TaskMonitorPtr);\/\/ show the indicator\n\n}\n\/\/----------------------------------------------------------------------------------------\n\/\/ return the number of items in the submenu selected. Linear search.\n\/\/ We are in the menu system at the moment.  time is not an issue\n\/\/----------------------------------------------------------------------------------------\nstatic int16 getCountOfItemsInCurrentSubmenu(enum SubMenuSelectionEnum subMenu) {\n    int16 i=0;\n    int16 numItems=0;\n    int16 max =sizeof(MenuMap)\/sizeof(MENU_MAP_t);\n    \n    for (i=0; i &lt; max; i++){\n        if (MenuMap&#91;i].SubmenuID==subMenu){\n            numItems++;\n        }\n    }\n    \n    return numItems;\n}\n\n\/\/----------------------------------------------------------------------------------------\n\/\/ return array index of first item in the subMenu\n\/\/ We are in the menu system at the moment.  time is not an issue\n\/\/----------------------------------------------------------------------------------------\nstatic int16 getFirstItemIndexOnSubMenu(enum SubMenuSelectionEnum subMenu){\n    int16 i=0;\n    int16 max =sizeof(MenuMap)\/sizeof(MENU_MAP_t);\n    \n    for (i=0; i &lt; max; i++){\n        if (MenuMap&#91;i].SubmenuID==subMenu){\n            return i;\n        }\n    }\n    \n    return 0;\n}\n\/\/----------------------------------------------------------------------------------------\n\/\/----------------------------------------------------------------------------------------\nvoid handleMenuSetup(enum SubMenuSelectionEnum subMenu,TaskMonitor_t *TaskMonitorPtr){\n\n    gMenuIndicator=0;\n    gMenuItemSelected=getFirstItemIndexOnSubMenu(subMenu); \/\/ item 0 is the \"return to previous menu\" item\n    gNumberOfMenuItemsOnCurrentSubmenu = getCountOfItemsInCurrentSubmenu(subMenu);\n    showSubMenu(subMenu,TaskMonitorPtr,pdTRUE);\n\n}\n\/\/----------------------------------------------------------------------------------------\n\/\/ back up a menu item level and display it\n\/\/----------------------------------------------------------------------------------------\nstatic void popMenuStackAndShowMenu(TaskMonitor_t *TaskMonitorPtr){\n    enum SubMenuSelectionEnum subMenu=subMenuMainMenu;\n    \n    if (gMenuStackPointer >0)\n        gMenuStackPointer--;\n    else\n        gMenuStackPointer=0;\n    \n    subMenu = gMenuStack&#91;gMenuStackPointer];\n    handleMenuSetup(subMenu,TaskMonitorPtr);    \n}\n\n\/\/----------------------------------------------------------------------------------------\n\/\/ The button is pressed. Select the menu item\n\/\/----------------------------------------------------------------------------------------\nvoid handleButtonPress(TaskMonitor_t *TaskMonitorPtr,int16 _PhysicalButtonWasPressed){\n    enum SubMenuSelectionEnum subMenu=subMenuMainMenu;\n    const MENU_MAP_t *MenuMapPtr=0 ;\n    \n        if (!gMenuIsControllingDisplay){\n            gMenuIsControllingDisplay=pdTRUE;\n            gMenuStackPointer=1;\/\/ set up to be first menu item  \n            \/\/                command         from task  to task\n            postTaskMessage(cmdMenuTaskActive,MENU_TASK, DISPLAY_TASK);\n            postTaskMessage(displayClearCommand,MENU_TASK, DISPLAY_TASK);\n            gMenuStack&#91;gMenuStackPointer]=subMenu;\n            handleMenuSetup(subMenu,TaskMonitorPtr);\n            return;\n        }\n    \n       if (_PhysicalButtonWasPressed){\n            \/\/ the rotary switch button was pressed.\n            \n            \/\/ if ReturnFromMenuHandlerFunctionWhenButtonPressedFlag is non-zero, then\n            \/\/ back up a menu level\n            if (ReturnFromMenuHandlerFunctionWhenButtonPressedFlag){\n                ReturnFromMenuHandlerFunctionWhenButtonPressedFlag=0;\/\/clear this flag\n                popMenuStackAndShowMenu(TaskMonitorPtr);\n                return;\n            }\n            \n            \/\/handle the menu item\n            if (!gMenuStackPointer){\n                \/\/ we are not in the menu system.  set up for first menu\n                gMenuStackPointer++;\n                gMenuStack&#91;gMenuStackPointer]=subMenu;\n                handleMenuSetup(subMenu,TaskMonitorPtr);\n                return;\n            } else{\n                MenuMapPtr = &amp;MenuMap&#91;gMenuItemSelected];\n                \n                switch(MenuMapPtr->SubmenuIDToMoveTo){\n                    case   noSubMenu:\/\/=0\n                    break;\n                                            \n                    case subMenuExitToPreviousMenu:  \/\/ keep a menu stack and move back\n                        gMenuStackPointer--;\n                        if (gMenuStackPointer ==0){\n                            gMenuIsControllingDisplay=pdFALSE;\n                            postTaskMessage(displayClearCommand,MENU_TASK, DISPLAY_TASK);\n                            postTaskMessage(cmdMenuTaskFinished,MENU_TASK, DISPLAY_TASK);\n                            return;\n                        }\n                    break;\n\n                    case subMenuExitMenuSystem:  \/\/ shut down menus, exit to normal display\n                        gMenuIsControllingDisplay=pdFALSE;\n                        gMenuStackPointer=0;\n                        postTaskMessage(displayClearCommand,MENU_TASK, DISPLAY_TASK);\n                        postTaskMessage(cmdMenuTaskFinished,MENU_TASK, DISPLAY_TASK);\n                    break;\n                        \n                    case subMenuMainMenu:\n                    case subMenuPrinterInfo:\n                        \/\/ the user has selected a submenu.  display that menu\n                        gMenuStackPointer++;\n                        gMenuStack&#91;gMenuStackPointer]=subMenu=MenuMap&#91;gMenuItemSelected].SubmenuIDToMoveTo;\n\n                        handleMenuSetup(subMenu,TaskMonitorPtr);\n                    break;\n                        \n                    default:\n                        break;\n                }\n            }                   \n        }    \n}\n\/\/----------------------------------------------------------------------------------------\n\/\/ move the indicator down, along with the item selected\n\/\/ return 1 if change occurred, 0 otherwise\n\/\/----------------------------------------------------------------------------------------\nint16 moveIndicatorDown(){\n    \n    \n    \/\/ point to the next menu item\n    gMenuIndicator++,   gMenuItemSelected++;\n    \n    \/\/ if we went past the end of the menus for this submenu, \n    \/\/ then back up and exit\n    if (gMenuIndicator >= gNumberOfMenuItemsOnCurrentSubmenu){\n        gMenuIndicator --,gMenuItemSelected--;\n        return 0;\n    }\n    \/\/ if we are at a no-subMenu selection, then the pointer can't be located here\n    \/\/ because there are no submenus.  Back up and exit\n    if ( MenuMap&#91;gMenuItemSelected].SubmenuIDToMoveTo == noSubMenu){\n        gMenuIndicator --,gMenuItemSelected--;\n        return 0;       \n    }\n\n    \n    return 1;\n}\n\/\/----------------------------------------------------------------------------------------\n\/\/ move the indicator '>' up the display\n\/\/ return 1 if change occurred, 0 otherwise\n\/\/----------------------------------------------------------------------------------------\nint16 moveIndicatorUp(){\n        gMenuIndicator --,gMenuItemSelected--;\n\n    if (gMenuIndicator &lt; 0){\n        gMenuIndicator ++,gMenuItemSelected++;\n        return 0;\n    }\n    \n    return 1;\n    \n}\n\/\/----------------------------------------------------------------------------------------\n\/\/ if encoder status changed, return 1, else return 0\n\/\/----------------------------------------------------------------------------------------\nint16 handleEncoderChange(uint16 *encoderChangeCount,uint16 *encoderStatus,TaskMonitor_t *TaskMonitorPtr){\n    (void)encoderChangeCount;\n    uint16 es = *encoderStatus;\n    \n    if (!es || es==3)\n        return 0; \/\/ ignore\n    \n    if (es == 1 ){\n       if ( !moveIndicatorUp())\n            return 0;\n    } else {\n        \n        \/\/ es == 2\n       if (! moveIndicatorDown())\n            return 0;\n    }\n        \n      \n    showSubMenu(gMenuStack&#91;gMenuStackPointer],TaskMonitorPtr,pdFALSE);\n    \n    return 1;\n}\n\n\/\/-----------------------------------------------------------------------------\n\/\/ This button handler code relies on software debounce.\n\/\/-----------------------------------------------------------------------------\nstatic TASK_MESSAGE_t _bTaskMessage = { cmdButtonPress, MENU_TASK, MENU_TASK, 0, {0}};\nstatic TASK_MESSAGE_t *bMsgPtr = &amp;_bTaskMessage;\nstatic uint32         _buttonTime;\n\/\/-----------------------------------------------------------------------------\nCY_ISR(EncPressISR){\n    BaseType_t xHigherPriorityTaskWoken=pdFALSE;\n\n\/\/ if this is a switch bounce, ignore.\n    if ( getElapsedTimeInMilliseconds(_buttonTime)&lt;MINIMUM_TIME_IN_MS_BETWEEN_BUTTON_PRESSES)\n        return;\n    _buttonTime = getSystemTimeInMs();\n    \n    ++_buttonPressCount;\/\/ visual indicator for debugging\n    xQueueSendFromISR(MenuTaskQueue,&amp;bMsgPtr  , &amp;xHigherPriorityTaskWoken);\n}\n\/\/-----------------------------------------------------------------------------\n\/\/ This IRQ is for encoder button changes. \n\/\/-----------------------------------------------------------------------------\nstatic TASK_MESSAGE_t _eTaskMessage = { cmdEncoderChange, MENU_TASK, MENU_TASK, 0, {0}};\nstatic TASK_MESSAGE_t *eMsgPtr = &amp;_eTaskMessage;\nCY_ISR(EncoderChangeOccurred){\n    int16 es;\n    int16 count=0;\n    \n    es = srEncoder_Read() &amp; 0x3;\/\/ only 2 bits are exposed \n    \/\/ sometimes we are so fast we get there and the transition has not finished\n    while (!es &amp;&amp; count &lt; 10){\n        es = srEncoder_Read() &amp; 0x3;\n    }\n    \n    \/\/ if es == 3, we were too slow to capture the signal.\n    \/\/ nothing to do but return in that case\n    \/\/ if es is still 0, then a glitch happened while both lines were\n    \/\/ going negative or are both negative.  Leave in that case also.\n    if (!es || es==3)\n        return;\n    \n    BaseType_t xHigherPriorityTaskWoken=pdFALSE;\n\n    ++_encoderChangeCount;\/\/ visual indicator for debugging\n    \/\/ read again for those occasional moments we are too fast\n    _eTaskMessage.data&#91;0]=es;\n    xQueueSendFromISR(MenuTaskQueue,&amp;eMsgPtr  , &amp;xHigherPriorityTaskWoken);\n    \n}\n\n\n\/\/-----------------------------------------------------------------------------\n\/\/ Task to Handle Menus\n\/\/-----------------------------------------------------------------------------\nstatic portTASK_FUNCTION( vMenuTask, pvParameters )\n{\n    static TaskMonitor_t *TaskMonitorPtr = &amp;TaskMonitorArray&#91;MENU_TASK];\n    static TASK_MESSAGE_t *mdPtr;\n    (void) pvParameters;\n    TickType_t xTicksToWait = portMAX_DELAY;\n    \/\/static int16 v;\n    \n    \/\/ be sure to add in some padding to queue size for race conditions\n    MenuTaskQueue = xQueueCreate( MenuTask_QueueSize,sizeof( TASK_MESSAGE_t * ) );\n       \n    while( !MenuTaskQueue  )   \t{\n    \t\t\/\/ Queue was not created and must not be used.\n            \/\/ panic here.\n            DebugPrintString(MENU_TASK_Q_NOT_CREATED_STRING);\n            vTaskDelay(pdMS_TO_TICKS(1000));\/\/ warn user the system is dead\n            MenuTaskQueue = xQueueCreate( MenuTask_QueueSize, \n                                            sizeof( TASK_MESSAGE_t * ) );\n\t}     \n\n  extern void initializeMenuHardware();\/\/ hardware init.c\n  initializeMenuHardware();\n    \n    GCodePrintString(MENU_TASK_STARTED_STRING CRLF);\n    \n\n    for(;;){\n        TaskMonitorPtr->runCounter++;\/\/ crude profile indicator.\n        \n        \/\/----------------------------------------------------------------------\n        \/\/ Various tasks post here, and display the strings or graphics (future) posted\n        \/\/----------------------------------------------------------------------\n        if ( QReceive( MenuTaskQueue, &amp;mdPtr, xTicksToWait ,TaskMonitorPtr) == pdPASS ){\n            if (mdPtr){\n                switch(mdPtr->command){\n                    case cmdButtonPress:{\/\/0x801\n                        beep(100,TaskMonitorPtr);\/\/ beep for button pressRotarySwitchQuadratureDecoderCount,(uint16)mdPtr->data&#91;0],TaskMonitorPtr);                                              \n                        handleButtonPress(TaskMonitorPtr,pdTRUE);\n                    }break;\n                    \n                    case cmdEncoderChange:\/\/0x802\n                        gEncoderStatus = mdPtr->data&#91;0];\n                        mdPtr->data&#91;1]=0;\n                        handleEncoderChange(&amp;_encoderChangeCount,&amp;gEncoderStatus,TaskMonitorPtr);\n                                            \n                         \/\/extern void showTextOnTheDisplay(int16 lineNumber,int16 offset,uint8 *text,int16 numberOfTriesToMake, \n                         \/\/       int16 delayMsBetweenTries,TaskMonitor_t *TaskMonitorPtr);\n                    \n                    break;\n                        \n                    case cmdMenuTaskActive: \/\/ 0x803 &lt;-- menuTask takes over display\n                        \n                    break;\n                        \n                    case cmdMenuTaskFinished:\/\/ 0x804 &lt;-- menuTask is finished\n                        \n                    break;\n                        \n                    default: \/\/EndOfCommands\n                    break;\n                                \n                } \/\/switch\n            }\/\/ if(mdPtr)\n        }  \/\/ if QReceive\n          \n    }\/\/ for(;;)\n}\/\/portTASK_FUNCTION\n\n\/\/----------------------------------------------------------------------------------------\n\/\/ start the menu subsystem\n\/\/----------------------------------------------------------------------------------------\nvoid vAltStartMenuTask(int16_t priority){\n    \n        \txTaskCreate( vMenuTask,\n                    MENU_TASK_STRING , \/\/task name\n                    MenuTaskSTACK_SIZE, \n                    (void *)NULL, \n                    priority, \n                    ( TaskHandle_t * ) &amp;TaskMonitorArray&#91;MENU_TASK].taskHandle ); \n}\n\n\n#endif\n\n\/* &#91;] END OF FILE *\/\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">User Provide Menu Handler Functions<\/h4>\n\n\n\n<p>Often, a menu selection requires specific code to be executed.  To allow the menu interpreter to be as stand alone as possible, pointers to functions are included in the structure.  If a menu function is to be called, the user references it in a header file, puts it into the MenuMap array initializer, and indicates through the enum that the function will be called when the user selects that menu.  <\/p>\n\n\n\n<p>The &#8220;static const &#8221; in front of the array initializer forces the initializer contents into flash rather than ram, so it can&#8217;t be re-written due to typical rogue code. (Unless the flash itself is being modified, which can happen!)  The following Header File references the menu function placed in the array through this line of code.  Hopefully the comment lines explain the process:<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:9px\"><code>    \/\/ the following submenu line calls the function mhfShowPrintVolume(menuMapPtr,TaskInfoPtr)\n    \/\/ to show text calculated inside the function on the screen \n    \/\/ mhfShowPrintVolume() returns pdTRUE so a button press will magically exit the submenu.\n    ,{  subMenuPrinterInfo, menuShowPrintVolume,   <strong> subMenuExecuteFunction,mhfShowPrintVolume<\/strong>,  \"\" }<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:9px\"><code>#ifndef _MENU_HANDLER_H_\n    #define _MENU_HANDLER_H_\n\/* SOCino 3d printer firmware, FreeRTOS version\n *\n * Copyright 2021, Wade Maxfield\n * Written by Wade Maxfield\n *\n * Commercial license Available.\n *\n * This program is free software: you can redistribute it and\/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see &lt;http:\/\/www.gnu.org\/licenses\/>.\n *\n   This license does not override previous licenses\n *\/\n#include \"Configuration.h\"\n#include \"Menus\/MenuTask.h\"\n\/*\n    Return 1 from MenuHandlerFunction to cause MenuTask to exit this submenu when\n    the Rotary Switch Button is Pressed.\n*\/\nextern uint16 mhfShowPrintVolume(const struct menu_map_struct *me,TaskMonitor_t *TaskMonitorPtr );\n\n\n#endif\n\n\/* &#91;] END OF FILE *\/\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">The User Menu Handler Function<\/h4>\n\n\n\n<p>A basic menu function showing the current printer configuration follows.  The sprintf() function is used extensively. Note that when the printer menu task is created, enough stack space must be allocated so the sprintf() function does not overflow and cause strange operation of the printer: <\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:9px\"><code>\/* SOCino 3d printer firmware, FreeRTOS version\n *\n * Copyright 2021, Wade Maxfield\n * Written by Wade Maxfield\n *\n * Commercial license Available.\n *\n * This program is free software: you can redistribute it and\/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see &lt;http:\/\/www.gnu.org\/licenses\/>.\n *\n    This license does not override previous licenses\n *\/\n#include \"includes.h\"\n#include \"Menus\/MenuTask.h\"\n#include \"Menus\/MenuStrings.h\" \/\/ all menu text is in the MenuStrings.h file\n#include \"Menus\/MenuHandlerFunctions.h\"\n\n#if ENABLE_DISPLAY\nstatic uint8 outMsg&#91;32];\n    \n\n\/\/----------------------------------------------------------------------------------------\n\/\/ print out information regarding the current printer characteristics\n\/\/ This function requires sprintf and access to the config structure\n\/\/    Return 1 from MenuHandlerFunction to cause MenuTask to exit this submenu when\n\/\/    the Rotary Switch Button is Pressed.\n\/\/----------------------------------------------------------------------------------------\nuint16 mhfShowPrintVolume(const struct menu_map_struct *me,TaskMonitor_t *TaskMonitorPtr ){\n\n\/*   MENU_PRINT_VOLUME_1    \"Printer Capacity\"\n     MENU_PRINT_VOLUME_2    \"X Axis: %d mm\"\n     MENU_PRINT_VOLUME_3    \"Y Axis: %d mm\"\n     MENU_PRINT_VOLUME_4    \"Z Axis: %d mm\"\n     MENU_PRINT_VOLUME_5    \"                \"\n     MENU_PRINT_VOLUME_6    \"                \"\n     MENU_PRINT_VOLUME_7    \"Push Button     \"\n     MENU_PRINT_VOLUME_8    \"To Exit         \"\n*\/\n    showTextOnTheDisplay(1,0,(uint8*)MENU_PRINT_VOLUME_1,1, 50,TaskMonitorPtr);\n    sprintf((char*)outMsg,MENU_PRINT_VOLUME_2,config.X_Maximum-config.X_Minimum);\n    showTextOnTheDisplay(2,0,(uint8*)outMsg             ,1, 50,TaskMonitorPtr);\n    sprintf((char*)outMsg,MENU_PRINT_VOLUME_3,config.Y_Maximum-config.Y_Minimum);\n    showTextOnTheDisplay(3,0,(uint8*)outMsg             ,1, 50,TaskMonitorPtr);\n    sprintf((char*)outMsg,MENU_PRINT_VOLUME_4,config.Z_Maximum-config.Z_Minimum);\n    showTextOnTheDisplay(4,0,(uint8*)outMsg             ,1, 50,TaskMonitorPtr);\n    showTextOnTheDisplay(5,0,(uint8*)MENU_PRINT_VOLUME_5,1, 50,TaskMonitorPtr);\n\/\/ following not shown \n   \/\/showTextOnTheDisplay(6,0,(uint8*)MENU_PRINT_VOLUME_6,1, 50,TaskMonitorPtr);\n    \/\/showTextOnTheDisplay(6,0,(uint8*)MENU_PRINT_VOLUME_7,1, 50,TaskMonitorPtr);\n    \/\/showTextOnTheDisplay(7,0,(uint8*)MENU_PRINT_VOLUME_8,1, 50,TaskMonitorPtr);\n\n    return 1;\/\/ returning 1 causes menu to bump up a level when button pressed\n}\n    \n#endif\n\n\/* &#91;] END OF FILE *\/\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Next Time<\/h4>\n\n\n\n<p>The next time will be delayed again due to Trade Shows and Other Things Of Importance (OTOI).  At this point, none of the M20 through M29 functions are implemented.  Implemented are G0, G1, G4, G20, G21, G28, G90, G91, and G92. Also, M0, M17, M18, M82, M83, M84, M104, M105, M106, M107, M109, M111, M113, M114, M115 are implemented.  Some testing will resume in about 30 days or so (hopefully).  The third generation of the adapter card is completed and delivered.  I have to build it in order to test it.  Once done, I expect to upload the Eagle file(s) at some point. I need to know I can control a printer with no cuts\/jumpers on the board before uploading.<\/p>\n\n\n\n<p>I don&#8217;t know what the next post will be about.  I think it is about time to do a &#8220;dry run&#8221; on a print, to make sure I can print a small cube (conceptually), without any filament in the system (and possibly with the heater at a very low level to not carbonize any left over plastic in the head.)<\/p>\n\n\n\n<p>If I come across any interesting PSOC5LP gotcha&#8217;s, I will do a brief post about them when the RT (round tuit) comes into my posession.<\/p>\n\n\n\n<p>Enjoy!<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Life is what happens while you are making other plans. (This quote is a secular rephrasing of Proverbs 16:9 circa 350 B.C. See https:\/\/quoteinvestigator.com\/2012\/05\/06\/other-plans\/) Well, I have been hit squarely in the head by my proposing and God&#8217;s disposing. That makes this post *much* later than it should be. Onward! PSOC Creator, Debugging, and VMWare: [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-925","post","type-post","status-publish","format-standard","hentry","category-blogposts"],"_links":{"self":[{"href":"https:\/\/socmaker.com\/index.php?rest_route=\/wp\/v2\/posts\/925","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/socmaker.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/socmaker.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/socmaker.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/socmaker.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=925"}],"version-history":[{"count":13,"href":"https:\/\/socmaker.com\/index.php?rest_route=\/wp\/v2\/posts\/925\/revisions"}],"predecessor-version":[{"id":938,"href":"https:\/\/socmaker.com\/index.php?rest_route=\/wp\/v2\/posts\/925\/revisions\/938"}],"wp:attachment":[{"href":"https:\/\/socmaker.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=925"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/socmaker.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=925"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/socmaker.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=925"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}