diff --git a/Marlin/AO_FT810_Constants.h b/Marlin/AO_FT810_Constants.h index b0b82c13e..b873da7df 100644 --- a/Marlin/AO_FT810_Constants.h +++ b/Marlin/AO_FT810_Constants.h @@ -298,6 +298,20 @@ namespace FTDI { const uint16_t OPT_NOHANDS = 49152; } +// GPIO Bits + +namespace FT800 { + const uint8_t GPIO_GP0 = 1 << 0; + const uint8_t GPIO_GP1 = 1 << 1; + const uint8_t GPIO_DISP = 1 << 7; +} + +namespace FT810 { + const uint16_t GPIOX_GP0 = 1 << 0; + const uint16_t GPIOX_GP1 = 1 << 1; + const uint16_t GPIOX_DISP = 1 << 15; +} + // GLOBAL LCD REGISTER SET VALUES FOR WQVGA 480x272 DISPLAY /* diff --git a/Marlin/AO_FT810_Functions.h b/Marlin/AO_FT810_Functions.h index cbd051b67..ba69f68e3 100644 --- a/Marlin/AO_FT810_Functions.h +++ b/Marlin/AO_FT810_Functions.h @@ -105,13 +105,10 @@ #ifndef _AO_FT810_FUNC_H #define _AO_FT810_FUNC_H -#if defined(LCD_IS_FT800) - #define DL_CACHE_START 0x035000 -#else - #define DL_CACHE_START 0x0F5000 -#endif +#define DL_CACHE_START RAM_G_SIZE - 0xFFFF // Uncomment the following to disable the DL caching mechanism +// (note, this will keep the LCD status message from working) //#define DL_CACHE_DISABLED #if defined(LCD_800x480) @@ -272,7 +269,7 @@ class CLCD::CommandFifo { void Cmd (uint32_t cmd32); void Cmd (void* data, uint16_t len); - void Cmd_Str (char* data); + void Cmd_Str (const char * const data); void Cmd_Str (progmem_str data); void Cmd_Clear_Color (uint32_t rgb); void Cmd_Clear (bool Clr, bool Stl, bool Tag); @@ -577,7 +574,7 @@ void CLCD::CommandFifo::Cmd_Append (uint32_t ptr, uint32_t size) * The DLCache can be used like so: * * void some_function() { - * static CLCD::DLCache dlcache; + * CLCD::DLCache dlcache(UNIQUE_ID); * * if(dlcache.hasData()) { * dlcache.append(); @@ -585,26 +582,88 @@ void CLCD::CommandFifo::Cmd_Append (uint32_t ptr, uint32_t size) * // Add stuff to the DL * dlcache.store(); * } + * + * + * Layout of Cache memory: + * + * The cache memory begins with a table at + * DL_CACHE_START: each table entry contains + * an address and size for a cached DL slot. + * + * Immediately following the table is the + * DL_FREE_ADDR, which points to free cache + * space; following this is occupied DL space, + * and after that free space that is yet to + * be used. + * + * location data sizeof + * + * DL_CACHE_START slot0_addr 4 + * slot0_size 4 + * slot1_addr 4 + * slot1_size 4 + * ... + * slotN_addr 4 + * slotN_size 4 + * DL_FREE_ADDR dl_free_ptr 4 + * cached data + * ... + * dl_free_ptr empty space + * ... */ class CLCD::DLCache { private: - static uint16_t dl_free; - uint16_t dl_addr = 0; - uint16_t dl_size = 0; + uint8_t dl_slot; + uint32_t dl_addr; + uint16_t dl_size; + + void load_slot(); + static void save_slot(uint8_t dl_slot, uint32_t dl_addr, uint32_t dl_size); + public: + static void init(); + + DLCache(uint8_t slot) { + dl_slot = slot; + load_slot(); + } + bool hasData(); - void store(); + bool store(uint32_t num_bytes = 0); void append(); }; -uint16_t CLCD::DLCache::dl_free = 0; +#define DL_CACHE_SLOTS 250 +#define DL_FREE_ADDR DL_CACHE_START + DL_CACHE_SLOTS * 8 + +// The init function ensures all cache locatios are marked as empty + +void CLCD::DLCache::init() { + Mem_Write32(DL_FREE_ADDR, DL_FREE_ADDR + 4); + for(uint8_t slot = 0; slot < DL_CACHE_SLOTS; slot++) { + save_slot(slot, 0, 0); + } +} bool CLCD::DLCache::hasData() { + #if !defined(DL_CACHE_DISABLED) return dl_size != 0; + #else + return false; + #endif } -void CLCD::DLCache::store() { +/* This caches the current display list in RAMG so + * that it can be appended later. The memory is + * dynamically allocated following DL_FREE_ADDR. + * + * If num_bytes is provided, then that many bytes + * will be reserved so that the cache may be re-written + * later with potentially a bigger DL. + */ + +bool CLCD::DLCache::store(uint32_t num_bytes /* = 0*/) { #if !defined(DL_CACHE_DISABLED) CLCD::CommandFifo cmd; @@ -613,45 +672,79 @@ void CLCD::DLCache::store() { cmd.Cmd_Wait_Until_Idle(); // Figure out how long the display list is - dl_size = Mem_Read32(REG_CMD_DL) & 0x1FFF; - dl_addr = dl_free; + uint32_t new_dl_size = Mem_Read32(REG_CMD_DL) & 0x1FFF; + uint32_t free_space = 0; + uint32_t dl_alloc = 0; + + if(dl_addr == 0) { + // If we are allocating new space... + dl_addr = Mem_Read32(DL_FREE_ADDR); + free_space = RAM_G_SIZE - dl_addr; + dl_alloc = num_bytes ? num_bytes : new_dl_size; + dl_size = new_dl_size; + } else { + // Otherwise, we can only store as much space + // as was previously allocated. + free_space = num_bytes ? num_bytes : dl_size; + dl_alloc = 0; + dl_size = new_dl_size; + } - if((dl_addr + dl_size) > RAM_G_SIZE) { + if(dl_size > free_space) { // Not enough memory to cache the display list. - dl_addr = 0; - dl_size = 0; #if defined(UI_FRAMEWORK_DEBUG) #if defined (SERIAL_PROTOCOLLNPAIR) - SERIAL_PROTOCOLLNPAIR("Not enough space in GRAM to cache display list, free space: ", RAM_G_SIZE - dl_free); + SERIAL_PROTOCOLPAIR("Not enough space in GRAM to cache display list, free space: ", free_space); + SERIAL_PROTOCOLLNPAIR(" Required: ", dl_size); #else Serial.print(F("Not enough space in GRAM to cache display list, free space:")); - Serial.println(RAM_G_SIZE - dl_free); + Serial.print(free_space); + Serial.print(F(" Required: ")); + Serial.println(dl_size); #endif #endif + return false; } else { #if defined(UI_FRAMEWORK_DEBUG) #if defined (SERIAL_PROTOCOLLNPAIR) SERIAL_PROTOCOLPAIR("Saving DL to RAMG cache, bytes: ", dl_size); - SERIAL_PROTOCOLPAIR(" (Free space: ", RAM_G_SIZE - dl_free); + SERIAL_PROTOCOLPAIR(" (Free space: ", free_space); SERIAL_PROTOCOLLNPGM(")"); #else Serial.print(F("Saving DL to RAMG cache, bytes: ")); Serial.println(dl_size); Serial.print(F(" (Free space: ")); - Serial.println(RAM_G_SIZE - dl_free); + Serial.println(free_space); Serial.print(F(")")); #endif #endif - cmd.Cmd_Mem_Cpy(DL_CACHE_START + dl_addr, RAM_DL, dl_size); + cmd.Cmd_Mem_Cpy(dl_addr, RAM_DL, dl_size); cmd.Cmd_Execute(); - dl_free += dl_size; + save_slot(dl_slot, dl_addr, dl_size); + if(dl_alloc > 0) { + // If we allocated space dynamically, then adjust dl_free_addr. + Mem_Write32(DL_FREE_ADDR, dl_addr + dl_alloc); + } + return true; } #endif } +void CLCD::DLCache::save_slot(uint8_t dl_slot, uint32_t dl_addr, uint32_t dl_size) { + Mem_Write32(DL_CACHE_START + dl_slot * 8 + 0, dl_addr); + Mem_Write32(DL_CACHE_START + dl_slot * 8 + 4, dl_size); +} + +void CLCD::DLCache::load_slot() { + dl_addr = Mem_Read32(DL_CACHE_START + dl_slot * 8 + 0); + dl_size = Mem_Read32(DL_CACHE_START + dl_slot * 8 + 4); +} + void CLCD::DLCache::append() { CLCD::CommandFifo cmd; - cmd.Cmd_Append(DL_CACHE_START + dl_addr, dl_size); + #if !defined(DL_CACHE_DISABLED) + cmd.Cmd_Append(dl_addr, dl_size); + #endif #if defined(UI_FRAMEWORK_DEBUG) cmd.Cmd_Execute(); cmd.Cmd_Wait_Until_Idle(); @@ -758,14 +851,15 @@ void CLCD::Init (void) { Mem_Write8(REG_DLSWAP, 0x02); // Swap on New Frame /* - * Turn On the Display + * Turn on the Display (set DISP high) + * Turn on the Audio Amplifier (set GP0 high; on the AO CLCD board, this is tied to the amplifier control) */ #if defined(LCD_IS_FT800) - Mem_Write8(REG_GPIO_DIR, 0x80); // Turn ON Display Enable - Mem_Write8(REG_GPIO, 0x80); + Mem_Write8(REG_GPIO_DIR, GPIO_DISP | GPIO_GP0); + Mem_Write8(REG_GPIO, GPIO_DISP | GPIO_GP0); #else - Mem_Write16(REG_GPIOX_DIR, 1 << 15); // Turn ON Display Enable - Mem_Write16(REG_GPIOX, 1 << 15); + Mem_Write16(REG_GPIOX_DIR, GPIOX_DISP | GPIOX_GP0); + Mem_Write16(REG_GPIOX, GPIOX_DISP | GPIOX_GP0); #endif Enable(); // Turns on Clock by setting PCLK Register to 5 @@ -794,6 +888,8 @@ class CLCD::SoundPlayer { uint8_t sixteenths; // Duration of note, in sixteeths of a second, or zero to play to completion }; + const uint8_t WAIT = 0; + private: const sound_t *sequence; uint32_t next; @@ -838,18 +934,23 @@ bool CLCD::SoundPlayer::soundPlaying() { void CLCD::SoundPlayer::onIdle() { if(!sequence) return; - const bool readyForNextNote = next != 0 ? (millis() > next) : !soundPlaying(); + const bool readyForNextNote = (next == WAIT) ? !soundPlaying() : (millis() > next); if(readyForNextNote) { const effect_t fx = effect_t(pgm_read_byte_near(&sequence->effect)); - const note_t nt = note_t(pgm_read_byte_near(&sequence->note)); - const uint16_t ms = uint32_t(pgm_read_byte_near(&sequence->sixteenths)) * 1000 / 16; + const note_t nt = note_t(pgm_read_byte_near(&sequence->note)); + const uint32_t ms = uint32_t(pgm_read_byte_near(&sequence->sixteenths)) * 1000 / 16; if(ms == 0 && fx == SILENCE && nt == 0) { sequence = 0; } else { - next = ms ? (millis() + ms) : 0; - play(fx, nt != 0 ? nt : NOTE_C4); + #if defined(UI_FRAMEWORK_DEBUG) + #if defined (SERIAL_PROTOCOLLNPAIR) + SERIAL_PROTOCOLLNPAIR("Scheduling note in ", ms); + #endif + #endif + next = (ms == WAIT) ? 0 : (millis() + ms); + play(fx, (nt == 0) ? NOTE_C4 : nt); sequence++; } } diff --git a/Marlin/AO_FT810_SPI.h b/Marlin/AO_FT810_SPI.h index 99cf1aff0..f436ac8a5 100644 --- a/Marlin/AO_FT810_SPI.h +++ b/Marlin/AO_FT810_SPI.h @@ -398,7 +398,7 @@ void CLCD::CommandFifo::Cmd (void* data, uint16_t len) { } -void CLCD::CommandFifo::Cmd_Str (char* data) { +void CLCD::CommandFifo::Cmd_Str (const char * const data) { write(data, strlen(data)+1); } diff --git a/Marlin/AO_FT810_UI_Screens.h b/Marlin/AO_FT810_UI_Screens.h index 5c405a8f8..d47c836b5 100644 --- a/Marlin/AO_FT810_UI_Screens.h +++ b/Marlin/AO_FT810_UI_Screens.h @@ -16,12 +16,14 @@ * location: . * ****************************************************************************/ -#define STATUS_MESSAGE_BUFFER_LENGTH 32 -char lcd_status_message[STATUS_MESSAGE_BUFFER_LENGTH] = WELCOME_MSG; +#define DISPLAY_UPDATE_INTERVAL 1000 +#define TOUCH_REPEATS_PER_SECOND 4 + +// To save MCU RAM, the status message is "baked" in to the status screen +// cache, so we reserve a large chunk of memory for the DL cache + +#define RAMG_STATUS_SCREEN_DL_SIZE 2048 -static float marlin_x_axis = 100; -static float marlin_y_axis = 50; -static float marlin_z_axis = 170; static float marlin_x_steps = 100; static float marlin_y_steps = 100; static float marlin_z_steps = 100; @@ -94,6 +96,8 @@ static float marlin_z_offset = 0.150; #define MENU_BTN_STYLE Theme::font_medium, OPT_3D +#define EXEC_GCODE(cmd) Marlin_LCD_API::runGCode(cmd) + /************************* MENU SCREEN DECLARATIONS *************************/ class BootScreen : public UIScreen { @@ -106,14 +110,14 @@ class AboutScreen : public UIScreen { public: static void onEntry(); static void onRefresh(); - static void onTouchStart(uint8_t tag); - static void onIdle(); + static bool onTouchStart(uint8_t tag); }; -class KillScreen : public UIScreen { +class KillScreen { + // The KillScreen is behaves differently than the + // others, so we do not bother extending UIScreen. public: - static void onEntry(); - static void onRefresh(); + static void show(progmem_str msg); }; class StatusScreen : public UIScreen { @@ -122,24 +126,25 @@ class StatusScreen : public UIScreen { static void static_temperature(); static void static_progress(); static void static_interaction_buttons(); + static void static_status_message(const char * const message); static void dynamic_axis_position(); static void dynamic_temperature(); static void dynamic_progress(); - static void dynamic_status_message(); public: + static void setStatusMessage(const char * message); static void onRefresh(); static void onStartup(); static void onEntry(); static void onIdle(); - static void onTouchStart(uint8_t tag); + static bool onTouchStart(uint8_t tag); }; class MenuScreen : public UIScreen { public: static void onRefresh(); - static void onTouchStart(uint8_t tag); + static bool onTouchStart(uint8_t tag); }; class CalibrationScreen : public UIScreen { @@ -151,13 +156,13 @@ class CalibrationScreen : public UIScreen { class CalibrationRegistersScreen : public UIScreen { public: static void onRefresh(); - static void onTouchStart(uint8_t tag); + static bool onTouchStart(uint8_t tag); }; class AdvancedSettingsScreen : public UIScreen { public: static void onRefresh(); - static void onTouchStart(uint8_t tag); + static bool onTouchStart(uint8_t tag); }; class ValueAdjusters : public UIScreen { @@ -199,31 +204,37 @@ class ValueAdjusters : public UIScreen { static float getIncrement(); public: - static void onTouchStart(uint8_t tag); + static bool onTouchStart(uint8_t tag); }; class MoveAxisScreen : public ValueAdjusters { public: static void onRefresh(); - static void onTouchHeld(uint8_t tag); + static bool onTouchHeld(uint8_t tag); }; class StepsScreen : public ValueAdjusters { public: static void onRefresh(); - static void onTouchHeld(uint8_t tag); + static bool onTouchHeld(uint8_t tag); }; class ZOffsetScreen : public ValueAdjusters { public: static void onRefresh(); - static void onTouchHeld(uint8_t tag); + static bool onTouchHeld(uint8_t tag); }; class TemperatureScreen : public ValueAdjusters { public: static void onRefresh(); - static void onTouchHeld(uint8_t tag); + static bool onTouchHeld(uint8_t tag); +}; + +class FilesScreen : public UIScreen { + public: + static void onRefresh(); + static bool onTouchHeld(uint8_t tag); }; /******************************* MENU SCREEN TABLE ******************************/ @@ -231,7 +242,6 @@ class TemperatureScreen : public ValueAdjusters { SCREEN_TABLE { DECL_SCREEN(BootScreen), DECL_SCREEN(AboutScreen), - DECL_SCREEN(KillScreen), DECL_SCREEN(CalibrationScreen), DECL_SCREEN(StatusScreen), DECL_SCREEN(MenuScreen), @@ -240,11 +250,29 @@ SCREEN_TABLE { DECL_SCREEN(StepsScreen), DECL_SCREEN(ZOffsetScreen), DECL_SCREEN(TemperatureScreen), - DECL_SCREEN(CalibrationRegistersScreen) + DECL_SCREEN(CalibrationRegistersScreen), + DECL_SCREEN(FilesScreen), }; SCREEN_TABLE_POST +/********************************* DL CACHE SLOTS ******************************/ + +// In order to reduce SPI traffic, we cache display lists (DL) in RAMG. This +// is done using the CLCD::DLCache class, which takes a unique ID for each +// cache location. These IDs are defined here: + +enum { + STATUS_SCREEN_CACHE, + MENU_SCREEN_CACHE, + ADVANCED_SETTINGS_SCREEN_CACHE, + MOVE_AXIS_SCREEN_CACHE, + TEMPERATURE_SCREEN_CACHE, + STEPS_SCREEN_CACHE, + ZOFFSET_SCREEN_CACHE, + FILES_SCREEN_CACHE +}; + /************************************ MENU THEME ********************************/ namespace Theme { @@ -345,37 +373,6 @@ void BootScreen::onIdle() { CLCD::SoundPlayer sound; -const PROGMEM CLCD::SoundPlayer::sound_t chimes[] = { - {CHIMES, NOTE_G3, 13}, - {CHIMES, NOTE_E4, 13}, - {CHIMES, NOTE_C4, 19}, - {SILENCE, END_SONG, 0} -}; - -const PROGMEM CLCD::SoundPlayer::sound_t samples[] = { - {HARP}, - {XYLOPHONE}, - {TUBA}, - {GLOCKENSPIEL}, - {ORGAN}, - {TRUMPET}, - {PIANO}, - {CHIMES}, - {MUSIC_BOX}, - {BELL}, - {CLICK}, - {SWITCH}, - {COWBELL}, - {NOTCH}, - {HIHAT}, - {KICKDRUM}, - {SWITCH}, - {POP}, - {CLACK}, - {CHACK}, - {SILENCE} -}; - void AboutScreen::onEntry() { UIScreen::onEntry(); @@ -405,42 +402,22 @@ void AboutScreen::onRefresh() { cmd.Cmd_Execute(); } -void AboutScreen::onTouchStart(uint8_t tag) { +bool AboutScreen::onTouchStart(uint8_t tag) { switch(tag) { - case 1: GOTO_PREVIOUS(); return; - case 2: GOTO_SCREEN(CalibrationRegistersScreen); return; + case 1: GOTO_PREVIOUS(); return true; + case 2: GOTO_SCREEN(CalibrationRegistersScreen); return true; } } -void AboutScreen::onIdle() { - sound.onIdle(); -} - /************************************ KILL SCREEN *******************************/ -const PROGMEM CLCD::SoundPlayer::sound_t sad_trombone[] = { - {TRUMPET, NOTE_A3S, 10}, - {TRUMPET, NOTE_A3 , 10}, - {TRUMPET, NOTE_G3S, 10}, - {TRUMPET, NOTE_G3, 20}, - {SILENCE, END_SONG, 0} -}; - -void KillScreen::onEntry() { - UIScreen::onEntry(); - - CLCD::Mem_Write8(REG_VOL_SOUND, 0xFF); - sound.play(sad_trombone); +// The kill screen is an oddball that happens after Marlin has killed the events +// loop. So we only have a show() method rather than onRefresh(). The KillScreen +// should not be used as a model for other UI screens as it is an exception. - // Marlin won't call the idle function anymore, so we - // have to do it to play the sounds. - while(sound.hasMoreNotes()) { - sound.onIdle(); - } -} - -void KillScreen::onRefresh() { +void KillScreen::show(progmem_str message) { CLCD::CommandFifo cmd; + cmd.Cmd(CMD_DLSTART); cmd.Cmd_Clear_Color(Theme::about_bg); cmd.Cmd_Clear(1,1,1); @@ -448,7 +425,7 @@ void KillScreen::onRefresh() { #define GRID_COLS 4 #define GRID_ROWS 8 - BTX( BTN_POS(1,2), BTN_SIZE(4,1), lcd_status_message, FONT_LRG); + BTX( BTN_POS(1,2), BTN_SIZE(4,1), message, FONT_LRG); BTX( BTN_POS(1,3), BTN_SIZE(4,1), F("PRINTER HALTED"), FONT_LRG); @@ -457,6 +434,14 @@ void KillScreen::onRefresh() { cmd.Cmd(DL_DISPLAY); cmd.Cmd(CMD_SWAP); cmd.Cmd_Execute(); + + sound.play(sad_trombone); + + // Marlin won't call the idle function anymore, + // so we have to loop here to play the sounds. + while(sound.hasMoreNotes()) { + sound.onIdle(); + } } /*********************************** STATUS SCREEN ******************************/ @@ -590,7 +575,7 @@ void StatusScreen::dynamic_temperature() { sprintf_P( fan_str, PSTR("%-3d %%"), - Marlin_LCD_API::getFan_percent(0) + int8_t(Marlin_LCD_API::getFan_percent(0)) ); sprintf_P( @@ -696,14 +681,39 @@ void StatusScreen::static_interaction_buttons() { #define GRID_COLS 1 -void StatusScreen::dynamic_status_message() { +void StatusScreen::static_status_message(const char * const message) { + CLCD::CommandFifo cmd; + + #if defined(LCD_PORTRAIT) + THEME(status_msg) BTN( BTN_POS(1,4), BTN_SIZE(1,1), message, FONT_LRG, OPT_FLAT); + #else + THEME(status_msg) BTN( BTN_POS(1,3), BTN_SIZE(1,2), message, FONT_LRG, OPT_FLAT); + #endif +} + +void StatusScreen::setStatusMessage(const char * const message) { + CLCD::DLCache dlcache(STATUS_SCREEN_CACHE); + CLCD::CommandFifo cmd; + cmd.Cmd(CMD_DLSTART); - #if defined(LCD_PORTRAIT) - THEME(status_msg) BTN( BTN_POS(1,4), BTN_SIZE(1,1), lcd_status_message, FONT_LRG, OPT_FLAT); - #else - THEME(status_msg) BTN( BTN_POS(1,3), BTN_SIZE(1,2), lcd_status_message, FONT_LRG, OPT_FLAT); - #endif + cmd.Cmd_Clear_Color(Theme::background); + cmd.Cmd_Clear(1,1,1); + + static_temperature(); + static_progress(); + static_axis_position(); + static_interaction_buttons(); + static_status_message(message); + + if(!dlcache.store(RAMG_STATUS_SCREEN_DL_SIZE)) { + #if defined (SERIAL_PROTOCOLLNPAIR) + SERIAL_PROTOCOLLNPAIR("Unable to set the status message, not enough DL cache space: ",message); + #else + Serial.print(F("Unable to set the status message, not enough DL cache space: ")); + Serial.println(message); + #endif + } } #if defined(LCD_PORTRAIT) @@ -722,29 +732,21 @@ void StatusScreen::onStartup() { } void StatusScreen::onRefresh() { - static CLCD::DLCache dlcache; + CLCD::DLCache dlcache(STATUS_SCREEN_CACHE); CLCD::CommandFifo cmd; cmd.Cmd(CMD_DLSTART); if(dlcache.hasData()) { dlcache.append(); } else { - cmd.Cmd_Clear_Color(Theme::background); - cmd.Cmd_Clear(1,1,1); - - static_temperature(); - static_progress(); - static_axis_position(); - static_interaction_buttons(); - - dlcache.store(); + // This should not happen, as setStatusMessage will + // populate the cache. } /* Dynamic content, non-cached data follows */ dynamic_temperature(); dynamic_progress(); - dynamic_status_message(); dynamic_axis_position(); cmd.Cmd(DL_DISPLAY); @@ -760,12 +762,13 @@ void StatusScreen::onIdle() { onRefresh(); } -void StatusScreen::onTouchStart(uint8_t tag) { +bool StatusScreen::onTouchStart(uint8_t tag) { switch(tag) { case 4: GOTO_SCREEN(MenuScreen); break; case 5: GOTO_SCREEN(TemperatureScreen); break; - case 6: GOTO_SCREEN(MoveAxisScreen); break; + case 6: GOTO_SCREEN(MoveAxisScreen); break; } + return true; } /************************************ MENU SCREEN *******************************/ @@ -779,7 +782,7 @@ void StatusScreen::onTouchStart(uint8_t tag) { #endif void MenuScreen::onRefresh() { - static CLCD::DLCache dlcache; + CLCD::DLCache dlcache(MENU_SCREEN_CACHE); CLCD::CommandFifo cmd; cmd.Cmd(CMD_DLSTART); @@ -826,15 +829,20 @@ void MenuScreen::onRefresh() { cmd.Cmd_Execute(); } -void MenuScreen::onTouchStart(uint8_t tag) { +bool MenuScreen::onTouchStart(uint8_t tag) { switch(tag) { case 1: GOTO_PREVIOUS(); break; + case 2: EXEC_GCODE(F("G28")); break; case 3: GOTO_SCREEN(MoveAxisScreen); break; + case 4: EXEC_GCODE(F("M84")); break; case 5: GOTO_SCREEN(TemperatureScreen); break; case 6: GOTO_SCREEN(AdvancedSettingsScreen); break; case 7: GOTO_SCREEN(AboutScreen); break; case 8: GOTO_SCREEN(CalibrationScreen); break; + default: + return false; } + return true; } /******************************* CONFIGURATION SCREEN ****************************/ @@ -848,7 +856,7 @@ void MenuScreen::onTouchStart(uint8_t tag) { #endif void AdvancedSettingsScreen::onRefresh() { - static CLCD::DLCache dlcache; + CLCD::DLCache dlcache(ADVANCED_SETTINGS_SCREEN_CACHE); CLCD::CommandFifo cmd; cmd.Cmd(CMD_DLSTART); @@ -889,13 +897,16 @@ void AdvancedSettingsScreen::onRefresh() { cmd.Cmd_Execute(); } -void AdvancedSettingsScreen::onTouchStart(uint8_t tag) { +bool AdvancedSettingsScreen::onTouchStart(uint8_t tag) { switch(tag) { case 1: GOTO_PREVIOUS(); break; case 2: GOTO_PREVIOUS(); break; case 3: GOTO_SCREEN(ZOffsetScreen); break; case 4: GOTO_SCREEN(StepsScreen); break; + default: + return false; } + return true; } /******************************** CALIBRATION SCREEN ****************************/ @@ -984,10 +995,13 @@ void CalibrationRegistersScreen::onRefresh() { cmd.Cmd_Execute(); } -void CalibrationRegistersScreen::onTouchStart(uint8_t tag) { +bool CalibrationRegistersScreen::onTouchStart(uint8_t tag) { switch(tag) { - case 1: GOTO_PREVIOUS(); return; + case 1: GOTO_PREVIOUS(); break; + default: + return false; } + return true; } /*************************** GENERIC VALUE ADJUSTMENT SCREEN ******************************/ @@ -1135,13 +1149,14 @@ void ValueAdjusters::adjuster_t::dynamic_parts(stacker_t &s, float value) const s.line++; } -void ValueAdjusters::onTouchStart(uint8_t tag) { +bool ValueAdjusters::onTouchStart(uint8_t tag) { switch(tag) { - case 1: GOTO_PREVIOUS(); return; + case 1: GOTO_PREVIOUS(); return true; case 240 ... 245: increment = tag; break; - default: current_screen.onTouchHeld(tag); return; + default: return current_screen.onTouchHeld(tag); } current_screen.onRefresh(); + return true; } float ValueAdjusters::getIncrement() { @@ -1162,7 +1177,7 @@ uint8_t ValueAdjusters::increment = 20; /******************************** MOVE AXIS SCREEN ******************************/ void MoveAxisScreen::onRefresh() { - static CLCD::DLCache dlcache; + CLCD::DLCache dlcache(MOVE_AXIS_SCREEN_CACHE); CLCD::CommandFifo cmd; cmd.Cmd(CMD_DLSTART); @@ -1187,9 +1202,9 @@ void MoveAxisScreen::onRefresh() { } s.dynamic_parts(); h.dynamic_parts(s); - x.dynamic_parts(s,marlin_x_axis); - y.dynamic_parts(s,marlin_y_axis); - z.dynamic_parts(s,marlin_z_axis); + x.dynamic_parts(s,Marlin_LCD_API::getAxisPosition_mm(Marlin_LCD_API::X)); + y.dynamic_parts(s,Marlin_LCD_API::getAxisPosition_mm(Marlin_LCD_API::Y)); + z.dynamic_parts(s,Marlin_LCD_API::getAxisPosition_mm(Marlin_LCD_API::Z)); i.dynamic_parts(s); cmd.Cmd(DL_DISPLAY); @@ -1197,22 +1212,34 @@ void MoveAxisScreen::onRefresh() { cmd.Cmd_Execute(); } -void MoveAxisScreen::onTouchHeld(uint8_t tag) { +bool MoveAxisScreen::onTouchHeld(uint8_t tag) { + // We don't want to stack up moves, so wait until the + // machine is idle before sending another. + if(Marlin_LCD_API::isMoving()) { + return false; + } + + float inc = getIncrement(); + Marlin_LCD_API::axis_t axis; + const float feedrate_mm_s = inc * TOUCH_REPEATS_PER_SECOND; + switch(tag) { - case 2: marlin_x_axis -= getIncrement(); break; - case 3: marlin_x_axis += getIncrement(); break; - case 4: marlin_y_axis -= getIncrement(); break; - case 5: marlin_y_axis += getIncrement(); break; - case 6: marlin_z_axis -= getIncrement(); break; - case 7: marlin_z_axis += getIncrement(); break; + case 2: axis = Marlin_LCD_API::X; inc *= -1; break; + case 3: axis = Marlin_LCD_API::X; inc *= 1; break; + case 4: axis = Marlin_LCD_API::Y; inc *= -1; break; + case 5: axis = Marlin_LCD_API::Y; inc *= 1; break; + case 6: axis = Marlin_LCD_API::Z; inc *= -1; break; + case 7: axis = Marlin_LCD_API::Z; inc *= 1; break; } + Marlin_LCD_API::setAxisPosition_mm(axis, Marlin_LCD_API::getAxisPosition_mm(axis) + inc, feedrate_mm_s); onRefresh(); + return true; } /******************************* TEMPERATURE SCREEN ******************************/ void TemperatureScreen::onRefresh() { - static CLCD::DLCache dlcache; + CLCD::DLCache dlcache(TEMPERATURE_SCREEN_CACHE); CLCD::CommandFifo cmd; cmd.Cmd(CMD_DLSTART); @@ -1252,7 +1279,7 @@ void TemperatureScreen::onRefresh() { cmd.Cmd_Execute(); } -void TemperatureScreen::onTouchHeld(uint8_t tag) { +bool TemperatureScreen::onTouchHeld(uint8_t tag) { switch(tag) { case 20: Marlin_LCD_API::setTargetTemp_celsius(0, Marlin_LCD_API::getTargetTemp_celsius(0) - getIncrement()); break; case 21: Marlin_LCD_API::setTargetTemp_celsius(0, Marlin_LCD_API::getTargetTemp_celsius(0) + getIncrement()); break; @@ -1262,14 +1289,17 @@ void TemperatureScreen::onTouchHeld(uint8_t tag) { case 5: Marlin_LCD_API::setTargetTemp_celsius(2, Marlin_LCD_API::getTargetTemp_celsius(2) + getIncrement()); break; case 10: Marlin_LCD_API::setFan_percent( 0, Marlin_LCD_API::getFan_percent(0) - getIncrement()); break; case 11: Marlin_LCD_API::setFan_percent( 0, Marlin_LCD_API::getFan_percent(0) + getIncrement()); break; + default: + return false; } onRefresh(); + return true; } /******************************* STEPS SCREEN ******************************/ void StepsScreen::onRefresh() { - static CLCD::DLCache dlcache; + CLCD::DLCache dlcache(STEPS_SCREEN_CACHE); CLCD::CommandFifo cmd; cmd.Cmd(CMD_DLSTART); @@ -1307,7 +1337,7 @@ void StepsScreen::onRefresh() { cmd.Cmd_Execute(); } -void StepsScreen::onTouchHeld(uint8_t tag) { +bool StepsScreen::onTouchHeld(uint8_t tag) { switch(tag) { case 2: marlin_x_steps -= getIncrement(); break; case 3: marlin_x_steps += getIncrement(); break; @@ -1317,14 +1347,17 @@ void StepsScreen::onTouchHeld(uint8_t tag) { case 7: marlin_z_steps += getIncrement(); break; case 8: marlin_e0_steps -= getIncrement(); break; case 9: marlin_e0_steps += getIncrement(); break; + default: + return false; } onRefresh(); + return true; } /***************************** Z-OFFSET SCREEN ***************************/ void ZOffsetScreen::onRefresh() { - static CLCD::DLCache dlcache; + CLCD::DLCache dlcache(ZOFFSET_SCREEN_CACHE); CLCD::CommandFifo cmd; cmd.Cmd(CMD_DLSTART); @@ -1353,20 +1386,48 @@ void ZOffsetScreen::onRefresh() { cmd.Cmd_Execute(); } -void ZOffsetScreen::onTouchHeld(uint8_t tag) { +bool ZOffsetScreen::onTouchHeld(uint8_t tag) { switch(tag) { case 4: marlin_z_offset -= getIncrement(); break; case 5: marlin_z_offset += getIncrement(); break; + default: + return false; } onRefresh(); + return true; +} + +/***************************** FILES SCREEN ***************************/ + +void FilesScreen::onRefresh() { + CLCD::DLCache dlcache(FILES_SCREEN_CACHE); + CLCD::CommandFifo cmd; + cmd.Cmd(CMD_DLSTART); + + cmd.Cmd(DL_DISPLAY); + cmd.Cmd(CMD_SWAP); + cmd.Cmd_Execute(); +} + +bool FilesScreen::onTouchHeld(uint8_t tag) { + switch(tag) { + default: + return false; + } + onRefresh(); + return true; } /******************************** MAIN EVENT HANDLER *******************************/ -#define DISPLAY_UPDATE_INTERVAL 1000 +void lcd_setstatusPGM(const char * const message, int8_t level = 0); void lcd_init() { CLCD::Init(); + CLCD::DLCache::init(); + + lcd_setstatusPGM(PSTR(WELCOME_MSG)); + current_screen.start(); } @@ -1377,6 +1438,8 @@ void lcd_update() { static uint32_t last_repeat = 0; static uint32_t last_update = 0; + sound.onIdle(); + if(millis() - last_update > DISPLAY_UPDATE_INTERVAL) { current_screen.onIdle(); last_update = millis(); @@ -1418,19 +1481,20 @@ void lcd_update() { else if(pressed == NONE) { // When the user taps on a button, activate the onTouchStart handler const uint8_t lastScreen = current_screen.getScreen(); - current_screen.onTouchStart(tag); - last_repeat = millis(); - sound.play(Theme::press_sound); + if(current_screen.onTouchStart(tag)) { + last_repeat = millis(); + sound.play(Theme::press_sound); - #if defined(UI_FRAMEWORK_DEBUG) - #if defined (SERIAL_PROTOCOLLNPAIR) - SERIAL_PROTOCOLLNPAIR("Touch start: ", tag); - #else - Serial.print("Touch start: "); - Serial.println(tag); + #if defined(UI_FRAMEWORK_DEBUG) + #if defined (SERIAL_PROTOCOLLNPAIR) + SERIAL_PROTOCOLLNPAIR("Touch start: ", tag); + #else + Serial.print("Touch start: "); + Serial.println(tag); + #endif #endif - #endif + } if(lastScreen != current_screen.getScreen()) { // In the case in which a touch event triggered a new screen to be @@ -1442,10 +1506,11 @@ void lcd_update() { } } else if(tag == pressed) { // The user is holding down a button. - if((millis() - last_repeat) > 250) { - current_screen.onTouchHeld(tag); - sound.play(Theme::repeat_sound); - last_repeat = millis(); + if((millis() - last_repeat) > (1000 / TOUCH_REPEATS_PER_SECOND)) { + if(current_screen.onTouchHeld(tag)) { + sound.play(Theme::repeat_sound); + last_repeat = millis(); + } } } } @@ -1453,18 +1518,22 @@ void lcd_update() { inline bool lcd_hasstatus() { return true; } void lcd_setstatus(const char * const message, const bool persist = false) { - strncpy(lcd_status_message, message, STATUS_MESSAGE_BUFFER_LENGTH); + StatusScreen::setStatusMessage(message); } -void lcd_setstatusPGM(const char * const message, int8_t level = 0) { - strncpy_P(lcd_status_message, message, STATUS_MESSAGE_BUFFER_LENGTH); +void lcd_setstatusPGM(const char * const message, int8_t level /* = 0 */) { + char buff[64]; + strncpy_P(buff, message, sizeof(buff)); + StatusScreen::setStatusMessage(buff); } void lcd_status_printf_P(const uint8_t level, const char * const fmt, ...) { + char buff[64]; va_list args; va_start(args, fmt); - vsnprintf_P(lcd_status_message, STATUS_MESSAGE_BUFFER_LENGTH, fmt, args); + vsnprintf_P(buff, sizeof(buff), fmt, args); va_end(args); + StatusScreen::setStatusMessage(buff); } void lcd_setalertstatusPGM(const char * const message) { @@ -1478,6 +1547,5 @@ inline bool lcd_detected() { return true; } inline void lcd_refresh() {current_screen.onIdle();} void kill_screen(const char* lcd_msg) { - strncpy_P(lcd_status_message, lcd_msg, STATUS_MESSAGE_BUFFER_LENGTH); - GOTO_SCREEN(KillScreen); + KillScreen::show(progmem_str(lcd_msg)); } diff --git a/Marlin/AO_FT810_UI_Sounds.h b/Marlin/AO_FT810_UI_Sounds.h new file mode 100644 index 000000000..5cf333975 --- /dev/null +++ b/Marlin/AO_FT810_UI_Sounds.h @@ -0,0 +1,110 @@ +/**************************************************************************** + * Written By Marcio Teixeira 2018 - Aleph Objects, Inc. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * To view a copy of the GNU General Public License, go to the following * + * location: . * + ****************************************************************************/ + +#ifndef _AO_FT810_SNDS_H +#define _AO_FT810_SNDS_H + +/* A sound sequence consists of an array of the following: + + struct sound_t { + effect_t effect; // The sound effect number + note_t note; // The MIDI note value, or C4 if 0 + uint8_t sixteenths; // Note duration in 1/16th of a sec; + // If 0, play until sample is finished. + }; + + Constants are defined in "AO_FT810_Constants.h". + + Both note and sixteenths are optional. If omitted, the compiler + will fill them in with 0 which will be interpreted by play C4 + for the duration of the sample. + + The sequence must be terminated by "{SILENCE, END_SONG, WAIT}", i.e + all zeros. + */ + +const PROGMEM CLCD::SoundPlayer::sound_t chimes[] = { + {CHIMES, NOTE_G3, 5}, + {CHIMES, NOTE_E4, 5}, + {CHIMES, NOTE_C4, 5}, + {SILENCE, END_SONG, 0} +}; + +const PROGMEM CLCD::SoundPlayer::sound_t sad_trombone[] = { + {TRUMPET, NOTE_A3S, 10}, + {TRUMPET, NOTE_A3 , 10}, + {TRUMPET, NOTE_G3S, 10}, + {TRUMPET, NOTE_G3, 20}, + {SILENCE, END_SONG, 0} +}; + +const PROGMEM CLCD::SoundPlayer::sound_t js_bach_joy[] = { + {PIANO, NOTE_G3, 10}, + {PIANO, NOTE_A3, 10}, + {PIANO, NOTE_B3, 10}, + {PIANO, NOTE_D4, 10}, + {PIANO, NOTE_C4, 10}, + {PIANO, NOTE_C4, 10}, + {PIANO, NOTE_E4, 10}, + {PIANO, NOTE_D4, 10}, + {PIANO, NOTE_D4, 10}, + {PIANO, NOTE_G4 , 10}, + {PIANO, NOTE_F4S, 10}, + {PIANO, NOTE_G4, 10}, + {PIANO, NOTE_D4, 10}, + {PIANO, NOTE_B3, 10}, + {PIANO, NOTE_G3, 10}, + {PIANO, NOTE_A3, 10}, + {PIANO, NOTE_B3, 10}, + {PIANO, NOTE_C4, 10}, + {PIANO, NOTE_D4, 10}, + {PIANO, NOTE_E4, 10}, + {PIANO, NOTE_D4, 10}, + {PIANO, NOTE_C4, 10}, + {PIANO, NOTE_B3, 10}, + {PIANO, NOTE_A3, 10}, + {PIANO, NOTE_B3, 10}, + {PIANO, NOTE_G3, 10}, + {PIANO, NOTE_G3, 10}, + {SILENCE, END_SONG, 0} +}; + +const PROGMEM CLCD::SoundPlayer::sound_t all_instruments[] = { + {HARP}, + {XYLOPHONE}, + {TUBA}, + {GLOCKENSPIEL}, + {ORGAN}, + {TRUMPET}, + {PIANO}, + {CHIMES}, + {MUSIC_BOX}, + {BELL}, + {CLICK}, + {SWITCH}, + {COWBELL}, + {NOTCH}, + {HIHAT}, + {KICKDRUM}, + {SWITCH}, + {POP}, + {CLACK}, + {CHACK}, + {SILENCE, END_SONG, 0} +}; + +#endif // _AO_FT810_SNDS_H \ No newline at end of file diff --git a/Marlin/AO_UI_Framework.h b/Marlin/AO_UI_Framework.h index cc4e8844a..f559fc922 100644 --- a/Marlin/AO_UI_Framework.h +++ b/Marlin/AO_UI_Framework.h @@ -46,9 +46,9 @@ class ScreenRef { typedef void onExit_func_t(void); typedef void onIdle_func_t(void); typedef void onRefresh_func_t(void); - typedef void onTouchStart_func_t(uint8_t); - typedef void onTouchHeld_func_t(uint8_t); - typedef void onTouchEnd_func_t(uint8_t); + typedef bool onTouchStart_func_t(uint8_t); + typedef bool onTouchHeld_func_t(uint8_t); + typedef bool onTouchEnd_func_t(uint8_t); private: typedef struct { @@ -103,9 +103,9 @@ class ScreenRef { void onExit() {GET_METHOD(type, onExit)();} void onIdle() {GET_METHOD(type, onIdle)();} void onRefresh() {GET_METHOD(type, onRefresh)();} - void onTouchStart(uint8_t tag) {GET_METHOD(type, onTouchStart)(tag);} - void onTouchHeld(uint8_t tag) {GET_METHOD(type, onTouchHeld)(tag);} - void onTouchEnd(uint8_t tag) {GET_METHOD(type, onTouchEnd)(tag);} + bool onTouchStart(uint8_t tag) {return GET_METHOD(type, onTouchStart)(tag);} + bool onTouchHeld(uint8_t tag) {return GET_METHOD(type, onTouchHeld)(tag);} + bool onTouchEnd(uint8_t tag) {return GET_METHOD(type, onTouchEnd)(tag);} void initializeAll() { for(uint8_t type = 0; type < functionTableSize; type++) { @@ -171,9 +171,9 @@ class UIScreen { static void onEntry() {current_screen.onRefresh();} static void onExit() {} static void onIdle() {} - static void onTouchStart(uint8_t) {} - static void onTouchHeld(uint8_t) {} - static void onTouchEnd(uint8_t) {} + static bool onTouchStart(uint8_t) {return false;} + static bool onTouchHeld(uint8_t) {return false;} + static bool onTouchEnd(uint8_t) {return false;} }; #define GOTO_SCREEN(screen) current_screen.goTo(screen::onRefresh); diff --git a/Marlin/AO_UI_Marlin_LCD_API.h b/Marlin/AO_UI_Marlin_LCD_API.h index 128c6cdfa..52e883631 100644 --- a/Marlin/AO_UI_Marlin_LCD_API.h +++ b/Marlin/AO_UI_Marlin_LCD_API.h @@ -41,11 +41,15 @@ class Marlin_LCD_API { static const uint8_t getFeedRate_percent(); static const float getZOffset_mm(); static const bool isAxisPositionKnown(const axis_t axis); + static const bool isMoving(); static const progmem_str getFirmwareName(); static const void setTargetTemp_celsius(const uint8_t extruder, float temp); static const void setFan_percent(const uint8_t fan, float percent); + static const void setAxisPosition_mm(const Marlin_LCD_API::axis_t axis, float position, float _feedrate_mm_s); + + static const void runGCode(progmem_str gcode); static float clamp(float value, float minimum, float maximum) {return max(min(value,maximum),minimum);}; @@ -86,6 +90,26 @@ const float Marlin_LCD_API::getAxisPosition_mm(const Marlin_LCD_API::axis_t axis } } +const void Marlin_LCD_API::setAxisPosition_mm(const Marlin_LCD_API::axis_t axis, float position, float _feedrate_mm_s) { + set_destination_from_current(); + switch(axis) { + case X: destination[X_AXIS] = position; break; + case Y: destination[Y_AXIS] = position; break; + case Z: destination[Z_AXIS] = position; break; + case E0: destination[E_AXIS] = position; break; + case E1: destination[E_AXIS+1] = position; break; + } + + const float old_feedrate = feedrate_mm_s; + feedrate_mm_s = _feedrate_mm_s; + prepare_move_to_destination(); + feedrate_mm_s = old_feedrate; +} + +const bool Marlin_LCD_API::isMoving() { + return planner.blocks_queued(); +} + const float Marlin_LCD_API::getAxisSteps_per_mm(const Marlin_LCD_API::axis_t axis) { return 0; } @@ -105,6 +129,10 @@ const uint8_t Marlin_LCD_API::getFeedRate_percent() { return feedrate_percentage; } +const void Marlin_LCD_API::runGCode(progmem_str gcode) { + enqueue_and_echo_commands_P((const char*)gcode); +} + const bool Marlin_LCD_API::isAxisPositionKnown(const axis_t axis) { switch(axis) { case X: return axis_known_position[X_AXIS]; break; diff --git a/Marlin/Conditionals_LulzBot.h b/Marlin/Conditionals_LulzBot.h index 854d1544e..15f31fda2 100644 --- a/Marlin/Conditionals_LulzBot.h +++ b/Marlin/Conditionals_LulzBot.h @@ -13,7 +13,7 @@ * got disabled. */ -#define LULZBOT_FW_VERSION ".8" // Change this with each update +#define LULZBOT_FW_VERSION ".9" // Change this with each update #if ( \ !defined(LULZBOT_Gladiola_Mini) && \ diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index 47378a8b6..69aa9f55e 100644 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -356,6 +356,7 @@ #include "AO_FT810_Pins.h" #include "AO_FT810_SPI.h" #include "AO_FT810_UI_Bitmaps.h" + #include "AO_FT810_UI_Sounds.h" #include "AO_FT810_UI_Screens.h" #endif