/********************* * FT810_Functions.h * *********************/ /**************************************************************************** * Written By Mark Pelletier 2017 - 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: . * ****************************************************************************/ /**************************************************************************** * FUNCTION MAP * * * * SPI and FT800/810 Commands * * * * CLCD::spiSelect() Set CS line to 0 * * CLCD::spiDeselect() Set CS Line to 1 * * CLCD::Reset() Toggle FT800/810 Power Down Line 50 ms * * CLCD::spiInit() Configure I/O Lines for SPI * * CLCD::spiTransfer() Send/Receive 1 SPI Byte * * CLCD::Init() Set FT800/810 Registers * * CLCD::Enable() Turn On FT800/810 PCLK * * CLCD::Disable() Turn Off FT8880/810 PCLK * * CLCD::Set_Backlight() Set LCD Backlight Level * * * * MEMORY READ FUNCTIONS * * * * CLCD::Mem_ReadAddr() Send 32-Bit Address * * CLCD::Mem_Read8() Read 1 Byte * * CLCD::Mem_Read16() Read 2 Bytes * * CLCD::Mem_Read32() Read 4 Bytes * * * * MEMORY WRITE FUNCTIONS * * * * CLCD::Mem_WriteAddr() Send 24-Bit Address * * CLCD::Mem_Write8() Write 1 Byte * * CLCD::Mem_Write16() Write 2 Bytes * * CLCD::Mem_Write32() Write 4 Bytes * * * * HOST COMMAND FUNCTION * * * * CLCD::Host_Cmd() Send 24-Bit Host Command * * * * COMMAND BUFFER FUNCTIONS * * * * CLCD::Cmd() Send 32-Bit Value(4 Bytes)CMD Buffer * * CLCD::Cmd() Send Data Structure with 32-Bit Cmd * * CLCD::Cmd_Str() Send Text String in 32-Bit Multiples * * * * FT800/810 GRAPHIC COMMANDS * * * * class CLCD:CommandFifo {} Class to control Cmd FIFO * * Fifo::Start() Wait for CP finish - Set FIFO Ptr * * Fifo::Execute() Set REG_CMD_WRITE and start CP * * Fifo::Reset() Set Cmd Buffer Pointers to 0 * * * Fifo::Cmd_Clear_Color() Set Clear Screen Color * * Fifo::Cmd_Clear() Clear Color-Stencil-Tag Buffer(s) * * Fifo::Cmd_Color() Set Current Color * * Fifo::Cmd_Set_Foreground_Color Set Graphic Item Foreground Color * * Fifo::Cmd_Set_Background_Color Set Graphic Item Background Color * * Fifo::Cmd_Bitmap_Source() Set RAM Address of Bitmap Data * * Fifo::Cmd_Bitmap_Layout() Configure Bitmap Format and Layout * * Fifo::Cmd_Bitmap_Size() Set Bitmap Dimensions * * Fifo::Cmd_Bitmap_Handle() Assign Handle ID to a Bitmap * * Fifo::Cmd_Begin() Begin Drawing a Primative * * Fifo::Cmd_Mem_Copy() Copy a Block of Memory * * Fifo::Cmd_Append() Append Commands to Current DL * * Fifo::Cmd_Gradient_Color() Set 3D Button Highlight Color * * Fifo::Cmd_Vertex_2II() Set Coordinates of Graphics Primative * * Fifo::Cmd_Draw_Button() Draw Button with Bulk Write * * Fifo::Cmd_Draw_Text() Draw Text with Bulk Write * * * */ /************************************************** * RAM_G Graphics RAM Allocation * * * * Address Use * * * * 8000 Extruder Bitmap * * 8100 Bed Heat Bitmap * * 8200 Fan Bitmap * * 8300 Thumb Drive Symbol Bitmap * * 35000 Static DL Space (FT800) * * F5000 Static DL Space (FT810) * **************************************************/ #ifndef _AO_FT810_FUNC_H #define _AO_FT810_FUNC_H #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) using namespace FTDI_LCD_800x480; #elif defined(LCD_480x272) using namespace FTDI_LCD_480x272; #endif using namespace FTDI; typedef const __FlashStringHelper *progmem_str; #define FORCEDINLINE __attribute__((always_inline)) class CLCD { private: static void spiInit (void); static void spiSelect (void); static void spiDeselect (void); static uint8_t spiTransfer (uint8_t spiOutByte); static void Mem_ReadAddr (uint32_t Reg_Address); static void Mem_WriteAddr (uint32_t Reg_Address); static void Mem_Read_Bulk (uint32_t Reg_Address, uint8_t *Data, uint16_t Len); static void Mem_Write_Bulk (uint32_t Reg_Address, const void *Data, uint16_t Len, uint8_t Padding = 0); static void Mem_Write_Bulk (uint32_t Reg_Address, progmem_str Str, uint16_t Len, uint8_t Padding = 0); public: static uint8_t Mem_Read8 (uint32_t Reg_Address); static uint16_t Mem_Read16 (uint32_t Reg_Address); static uint32_t Mem_Read32 (uint32_t Reg_Address); static void Mem_Write8 (uint32_t Reg_Address, uint8_t W_Data); static void Mem_Write16 (uint32_t Reg_Address, uint16_t W_Data); static void Mem_Write32 (uint32_t Reg_Address, uint32_t W_Data); static inline uint32_t pack_rgb(uint8_t r, uint8_t g, uint8_t b); public: typedef struct { const uint8_t format; const uint16_t linestride; const uint8_t filter; const uint8_t wrapx; const uint8_t wrapy; const uint32_t RAMG_addr; const uint16_t width; const uint16_t height; } bitmap_info_t; class CommandFifo; class SoundPlayer; class DLCache; public: static void Init (void); static void Turn_On_Backlight (void); static void Reset (void); static void Test_Pulse(void); static void Enable (void); static void Disable (void); static void Set_Backlight (uint16_t Freq, uint8_t Duty); static void Host_Cmd (unsigned char Host_Command, unsigned char Byte2); static void Get_Font_Metrics(uint8_t font, struct FontMetrics &fm); static void Flash_Write_RGB332_Bitmap(uint32_t Mem_Address, const unsigned char* pRGB332_Array, uint16_t Num_Bytes); static uint8_t Get_Tag() {return Mem_Read8(REG_TOUCH_TAG);} static bool Is_Touching() {return (Mem_Read32(REG_TOUCH_DIRECT_XY) & 0x80000000) == 0;} }; /********************************* FT800/810 Commands *********************************/ #define MULTIPLE_OF_4(val) ((((val)+3)>>2)<<2) void CLCD::Enable (void) { Mem_Write8(REG_PCLK, Pclk); } void CLCD::Disable (void) { Mem_Write8(REG_PCLK, 0x00); } void CLCD::Set_Backlight (uint16_t Freq, uint8_t Duty) { Mem_Write16(REG_PWM_HZ, Freq); Mem_Write8(REG_PWM_DUTY, Duty); } void CLCD::Turn_On_Backlight (void) { Set_Backlight(0x00FA, 128); } struct FontMetrics { uint8_t char_widths[128]; uint32_t font_format; uint32_t font_stride; uint32_t font_width; uint32_t font_height; uint32_t font_ptr; }; void CLCD::Get_Font_Metrics(uint8_t font, struct FontMetrics &fm) { Mem_Read_Bulk(ROM_FONT_ADDR + 148 * (font - 16), (uint8_t*) &fm, 148); } // HOST COMMAND FUNCTION void CLCD::Host_Cmd (unsigned char Host_Command, unsigned char Byte2) { // Sends 24-Bit Host Command to LCD if(Host_Command != ACTIVE) { Host_Command |= 0x40; } spiSelect(); spiTransfer(Host_Command); // Write Byte 1 spiTransfer(Byte2); // Write Byte 2 spiTransfer(0x00); // Write Byte 3 spiDeselect(); } void CLCD::Flash_Write_RGB332_Bitmap(uint32_t Mem_Address, const unsigned char* pRGB332_Array, uint16_t Num_Bytes) { unsigned char Flash_Byte; for(unsigned int i = 0; i < Num_Bytes; i++) { Flash_Byte = pgm_read_byte_near(pRGB332_Array + i); Mem_Write8((Mem_Address + i), Flash_Byte); } } /******************* FT800/810 Graphic Commands *********************************/ class CLCD::CommandFifo { protected: static uint32_t getRegCmdWrite(); static uint32_t getRegCmdRead(); #if defined(LCD_IS_FT800) static uint32_t command_write_ptr; template void _write_unaligned(T data, uint16_t len); #else uint32_t getRegCmdBSpace(); #endif void Cmd_Start(void); public: template void write(T data, uint16_t len); public: CommandFifo() {Cmd_Start();} static void Cmd_Reset (void); static bool Cmd_Is_Idle(); static void Cmd_Wait_Until_Idle(); void Cmd_Execute(void); void Cmd (uint32_t cmd32); void Cmd (void* data, uint16_t len); void Cmd_Str (const char * const data); void Cmd_Str (progmem_str data); void Cmd_Set_Clear_Color (uint32_t rgb); void Cmd_Clear (bool Clr, bool Stl, bool Tag); void Cmd_Set_Color (uint32_t rgb); void Cmd_Set_Foreground_Color (uint32_t rgb); void Cmd_Set_Background_Color (uint32_t rgb); void Cmd_Set_Tag (uint8_t Tag); void Cmd_Bitmap_Source (uint32_t RAM_G_Addr); // The following functions *must* be inlined since we are relying on the compiler to do // substitution of the constants from the data structure rather than actually storing // it in PROGMEM (which would fail, since we are not using pgm_read_near to read them). // Plus, by inlining, all the equations are evaluated at compile-time as everything // should be a constant. FORCEDINLINE void Cmd_Bitmap_Source (const bitmap_info_t& info) {Cmd_Bitmap_Source (info.RAMG_addr);}; FORCEDINLINE void Cmd_Bitmap_Layout (const bitmap_info_t& info) {Cmd_Bitmap_Layout (info.format, info.linestride, info.height);}; FORCEDINLINE void Cmd_Bitmap_Size(const bitmap_info_t& info) {Cmd_Bitmap_Size (info.filter, info.wrapx, info.wrapy, info.width, info.height);} FORCEDINLINE void Cmd_Draw_Button_Icon(int16_t x, int16_t y, int16_t w, int16_t h, const bitmap_info_t& info, const float scale = 1) { Cmd_Begin(BEGIN_BITMAPS); if(scale != 1) { Cmd(BITMAP_TRANSFORM_A | uint32_t(float(256)/scale) & 0xFFFF); Cmd(BITMAP_TRANSFORM_E | uint32_t(float(256)/scale) & 0xFFFF); } Cmd_Bitmap_Size(info.filter, info.wrapx, info.wrapy, info.width*scale, info.height*scale); Cmd_Vertex_2F((x + w/2 - info.width*scale/2)*16, (y + h/2 - info.height*scale/2)*16); if(scale != 1) { Cmd(BITMAP_TRANSFORM_A | 256); Cmd(BITMAP_TRANSFORM_E | 256); } } template FORCEDINLINE void Cmd_Draw_Button_Text(int16_t x, int16_t y, int16_t w, int16_t h, T text, int16_t font, uint16_t options = OPT_CENTER) { Cmd_Draw_Text( x + ((options & OPT_CENTERX) ? w/2 : ((options & OPT_RIGHTX) ? w : 0)), y + ((options & OPT_CENTERY) ? h/2 : h), text, font, options); } void Cmd_Bitmap_Layout (uint8_t format, uint16_t linestride, uint16_t height); void Cmd_Bitmap_Size(uint8_t filter, uint8_t wrapx, uint8_t wrapy, uint16_t width, uint16_t height); void Cmd_Bitmap_Handle (uint16_t Handle); void Cmd_Begin (uint32_t Primitive); void Cmd_Vertex_2F (uint16_t X_Coord, uint16_t Y_Coord); void Cmd_Vertex_2II (uint16_t X_Coord, uint16_t Y_Coord, uint8_t B_Handle, uint8_t Cell); template void Cmd_Draw_Button(int16_t x, int16_t y, int16_t w, int16_t h, T text, int16_t font, uint16_t option); template void Cmd_Draw_Text(int16_t x, int16_t y, T text, int16_t font, uint16_t options); void Cmd_Draw_Clock (int16_t x, int16_t y, int16_t r, uint16_t option, int16_t h, int16_t m, int16_t s, int16_t ms); void Cmd_Draw_Progress_Bar (int16_t x, int16_t y, int16_t w, int16_t h, int16_t val, int16_t range); void Cmd_Draw_Slider (int16_t x, int16_t y, int16_t w, int16_t h, uint16_t options, uint16_t val, uint16_t range); void Cmd_Mem_Cpy (uint32_t dst, uint32_t src, uint32_t size); void Cmd_Append (uint32_t ptr, uint32_t size); }; #if defined(LCD_IS_FT800) uint32_t CLCD::CommandFifo::command_write_ptr = 0xFFFFFFFFul; #endif inline uint32_t CLCD::pack_rgb(uint8_t r, uint8_t g, uint8_t b) { return (uint32_t(r) << 16) | (uint32_t(g) << 8) | uint32_t(b); } void CLCD::CommandFifo::Cmd_Set_Clear_Color (uint32_t rgb) // DL Command - Set Clear Screen Color { Cmd(CLEAR_COLOR_RGB | rgb); } void CLCD::CommandFifo::Cmd_Clear (bool Clr, bool Stl, bool Tag) // DL Command - Clear Color-Stencil-Tag Buffer(s) { Cmd( CLEAR | (Clr ? 0b00000001 : 0) | (Stl ? 0b00000010 : 0) | (Tag ? 0b00000100 : 0) ); } void CLCD::CommandFifo::Cmd_Set_Color (uint32_t rgb) // DL Command - Set Current Color { Cmd(COLOR_RGB | rgb); } void CLCD::CommandFifo::Cmd_Set_Foreground_Color (uint32_t rgb) // Co-Processor Command - Set Foreground Color for Widgets { Cmd(CMD_FGCOLOR); Cmd(rgb); } void CLCD::CommandFifo::Cmd_Set_Background_Color (uint32_t rgb) // Co-Processor Command - Set Background Color for Widgets { Cmd(CMD_BGCOLOR); Cmd(rgb); } void CLCD::CommandFifo::Cmd_Set_Tag (uint8_t Tag) { Cmd(TAG | Tag); } void CLCD::CommandFifo::Cmd_Bitmap_Source (uint32_t RAM_G_Addr) { Cmd(BITMAP_SOURCE | (RAM_G_Addr & 0x000FFFFF)); } void CLCD::CommandFifo::Cmd_Bitmap_Layout (uint8_t format, uint16_t linestride, uint16_t height) { Cmd( BITMAP_LAYOUT | (uint32_t(height) << 0) | (uint32_t(linestride) << 9) | (uint32_t(format) << 19) ); } void CLCD::CommandFifo::Cmd_Bitmap_Size(uint8_t filter, uint8_t wrapx, uint8_t wrapy, uint16_t width, uint16_t height) { Cmd( BITMAP_SIZE | (uint32_t(height) << 0) | (uint32_t(width) << 9) | (uint32_t(wrapy) << 18) | (uint32_t(wrapx) << 19) | (uint32_t(filter) << 20) ); } void CLCD::CommandFifo::Cmd_Bitmap_Handle (uint16_t Handle) { Cmd( BITMAP_HANDLE | Handle); } void CLCD::CommandFifo::Cmd_Begin (uint32_t Primitive) { Cmd(BEGIN + Primitive); } void CLCD::CommandFifo::Cmd_Vertex_2II (uint16_t X_Coord, uint16_t Y_Coord, uint8_t B_Handle, uint8_t Cell) { Cmd( VERTEX2II | (uint32_t(Cell) << 0) | (uint32_t(B_Handle) << 7) | (uint32_t(Y_Coord) << 12) | (uint32_t(X_Coord) << 21) ); } void CLCD::CommandFifo::Cmd_Vertex_2F (uint16_t X_Coord, uint16_t Y_Coord) { Cmd( VERTEX2F | (uint32_t(Y_Coord) << 0) | (uint32_t(X_Coord) << 15) ); } template void CLCD::CommandFifo::Cmd_Draw_Button(int16_t x, int16_t y, int16_t w, int16_t h, T text, int16_t font, uint16_t option) { struct { int32_t type = CMD_BUTTON; int16_t x; int16_t y; int16_t w; int16_t h; int16_t font; uint16_t option; } cmd_data; cmd_data.x = x; cmd_data.y = y; cmd_data.w = w; cmd_data.h = h; cmd_data.font = font; cmd_data.option = option; Cmd( &cmd_data, sizeof(cmd_data) ); Cmd_Str(text); } template void CLCD::CommandFifo::Cmd_Draw_Text(int16_t x, int16_t y, T text, int16_t font, uint16_t options) { struct { int32_t type = CMD_TEXT; int16_t x; int16_t y; int16_t font; uint16_t options; } cmd_data; cmd_data.x = x; cmd_data.y = y; cmd_data.font = font; cmd_data.options = options; Cmd( &cmd_data, sizeof(cmd_data) ); Cmd_Str(text); } void CLCD::CommandFifo::Cmd_Draw_Clock (int16_t x, int16_t y, int16_t r, uint16_t option, int16_t h, int16_t m, int16_t s, int16_t ms) { struct { int32_t type = CMD_CLOCK; int16_t x; int16_t y; int16_t r; uint16_t option; int16_t h; int16_t m; int16_t s; int16_t ms; } cmd_data; cmd_data.x = x; cmd_data.y = y; cmd_data.r = r; cmd_data.option = option; cmd_data.h = h; cmd_data.m = m; cmd_data.s = s; cmd_data.ms = ms; Cmd( &cmd_data, sizeof(cmd_data) ); } void CLCD::CommandFifo::Cmd_Draw_Progress_Bar (int16_t x, int16_t y, int16_t w, int16_t h, int16_t val, int16_t range) { struct { int32_t type = CMD_PROGRESS; int16_t x; int16_t y; int16_t w; uint16_t h; uint16_t val; uint16_t range; } cmd_data; cmd_data.x = x; cmd_data.y = y; cmd_data.w = w; cmd_data.h = h; cmd_data.val = val; cmd_data.range = range; Cmd( &cmd_data, sizeof(cmd_data) ); } void CLCD::CommandFifo::Cmd_Draw_Slider (int16_t x, int16_t y, int16_t w, int16_t h, uint16_t options, uint16_t val, uint16_t range) { struct { int32_t type = CMD_SLIDER; int16_t x; int16_t y; int16_t w; int16_t h; uint16_t options; uint16_t val; uint16_t range; } cmd_data; cmd_data.x = x; cmd_data.y = y; cmd_data.w = w; cmd_data.h = h; cmd_data.options = options; cmd_data.val = val; cmd_data.range = range; Cmd( &cmd_data, sizeof(cmd_data) ); } void CLCD::CommandFifo::Cmd_Mem_Cpy (uint32_t dst, uint32_t src, uint32_t size) { struct { uint32_t type = CMD_MEMCPY; uint32_t dst; uint32_t src; uint32_t size; } cmd_data; cmd_data.dst = dst; cmd_data.src = src; cmd_data.size = size; Cmd( &cmd_data, sizeof(cmd_data) ); } void CLCD::CommandFifo::Cmd_Append (uint32_t ptr, uint32_t size) { struct { uint32_t type = CMD_APPEND; uint32_t ptr; uint32_t size; } cmd_data; cmd_data.ptr = ptr; cmd_data.size = size; Cmd( &cmd_data, sizeof(cmd_data) ); } /******************* DISPLAY LIST CACHE MANAGEMENT ************************/ /* The Display List Cache mechanism stores the display list corresponding * to a menu into RAM_G so that on subsequent calls drawing the menu does * not require as much SPI traffic. Dynamic content, such as indicators, * should not be cached. * * The DLCache can be used like so: * * void some_function() { * CLCD::DLCache dlcache(UNIQUE_ID); * * if(dlcache.hasData()) { * dlcache.append(); * } else { * // 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: 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(); bool store(uint32_t num_bytes = 0); void append(); }; #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 } /* 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; // Execute any commands already in the FIFO cmd.Cmd_Execute(); cmd.Cmd_Wait_Until_Idle(); // Figure out how long the display list is 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_size > free_space) { // Not enough memory to cache the display list. #if defined(UI_FRAMEWORK_DEBUG) #if defined (SERIAL_PROTOCOLLNPAIR) 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.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: ", 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(free_space); Serial.print(F(")")); #endif #endif cmd.Cmd_Mem_Cpy(dl_addr, RAM_DL, dl_size); cmd.Cmd_Execute(); 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; #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(); #if defined (SERIAL_PROTOCOLLNPAIR) SERIAL_PROTOCOLPAIR("Appending to DL from RAMG cache, bytes: ", dl_size); SERIAL_PROTOCOLPAIR(" (REG_CMD_DL: ", Mem_Read32(REG_CMD_DL)); SERIAL_PROTOCOLLNPGM(")"); #else Serial.print(F("Appending to DL from RAMG cache, bytes: ")); Serial.print(dl_size); Serial.print(F(" (REG_CMD_DL: ")); Serial.print(Mem_Read32(REG_CMD_DL)); Serial.println(F(")")); #endif #endif } /******************* LCD INITIALIZATION ************************/ void CLCD::Init (void) { spiInit(); // Set Up I/O Lines for SPI and FT800/810 Control delay(50); Reset(); // Power Down the FT800/810 for 50 ms delay(50); /* * If driving the 4D Systems 4DLCD-FT843 Board, the following Init sequence is needed for its FT800 Driver */ #ifdef LCD_IS_FT800 // Use External Crystal and 48 MHz System Clock Host_Cmd(CLKEXT, 0); delay(20); Host_Cmd(CLK48M, 0); #else Host_Cmd(CLKINT, 0); delay(20); Host_Cmd(CLKSEL, Clksel); // Use Internal RC Oscillator and 48 MHz System Clock #endif delay(20); Host_Cmd(ACTIVE, 0); // Activate the System Clock delay(50); delay(400); uint8_t Device_ID = Mem_Read8(REG_ID); // Read Device ID, Should Be 0x7C; #if defined(UI_FRAMEWORK_DEBUG) if(Device_ID != 0x7C) { #if defined (SERIAL_PROTOCOLLNPAIR) SERIAL_PROTOCOLLNPAIR("Incorrect device ID, should be 7C, got ", Device_ID); #else Serial.print(F("Incorrect device ID, should be 7C, got ")); Serial.println(Device_ID, HEX); #endif } else { #if defined (SERIAL_PROTOCOLLNPGM) SERIAL_PROTOCOLLNPGM("Device is correct "); #else Serial.println(F("Device is correct ")); #endif } #endif delay(400); Mem_Write8(REG_GPIO, 0x00); // Turn OFF Display Enable (GPIO Bit 7); Mem_Write8(REG_PCLK, 0x00); // Turn OFF LCD PCLK Set_Backlight(0x00FA, 0); /* * Configure the FT800/810 Registers */ Mem_Write16(REG_HCYCLE, Hcycle); Mem_Write16(REG_HOFFSET, Hoffset); Mem_Write16(REG_HSYNC0, Hsync0); Mem_Write16(REG_HSYNC1, Hsync1); Mem_Write16(REG_VCYCLE, Vcycle); Mem_Write16(REG_VOFFSET, Voffset); Mem_Write16(REG_VSYNC0, Vsync0); Mem_Write16(REG_VSYNC1, Vsync1); Mem_Write16(REG_HSIZE, Hsize); Mem_Write16(REG_VSIZE, Vsize); Mem_Write8(REG_SWIZZLE, Swizzle); Mem_Write8(REG_PCLK_POL, Pclkpol); Mem_Write8(REG_CSPREAD, 1); #if defined(LCD_PORTRAIT) && defined(LCD_UPSIDE_DOWN) Mem_Write8(REG_ROTATE, 3); #elif defined(LCD_PORTRAIT) && !defined(LCD_UPSIDE_DOWN) Mem_Write8(REG_ROTATE, 2); #elif !defined(LCD_PORTRAIT) && defined(LCD_UPSIDE_DOWN) Mem_Write8(REG_ROTATE, 1); #else !defined(LCD_PORTRAIT) && !defined(LCD_UPSIDE_DOWN) Mem_Write8(REG_ROTATE, 0); #endif Mem_Write8(REG_TOUCH_MODE, 0x03); // Configure the Touch Screen Mem_Write8(REG_TOUCH_ADC_MODE, 0x01); Mem_Write8(REG_TOUCH_OVERSAMPLE, 0x0F); Mem_Write16(REG_TOUCH_RZTHRESH, 5000); Mem_Write8(REG_VOL_SOUND, 0x00); // Turn Synthesizer Volume Off Mem_Write8(REG_DLSWAP, 0x02); // Swap on New Frame /* * 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, GPIO_DISP | GPIO_GP0); Mem_Write8(REG_GPIO, GPIO_DISP | GPIO_GP0); #else 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 delay(50); CommandFifo::Cmd_Reset(); delay(50); // Set Initial Values for Touch Transform Registers CLCD::Mem_Write32(REG_TOUCH_TRANSFORM_A, default_transform_a); CLCD::Mem_Write32(REG_TOUCH_TRANSFORM_B, default_transform_b); CLCD::Mem_Write32(REG_TOUCH_TRANSFORM_C, default_transform_c); CLCD::Mem_Write32(REG_TOUCH_TRANSFORM_D, default_transform_d); CLCD::Mem_Write32(REG_TOUCH_TRANSFORM_E, default_transform_e); CLCD::Mem_Write32(REG_TOUCH_TRANSFORM_F, default_transform_f); } /******************* TINY INTERVAL CLASS ***********************/ /* tiny_interval() downsamples a 32-bit millis() value into a 8-bit value which can record periods of a few seconds with a rougly 1/16th of second resolution. This allows us to measure small intervals without needing to use four-byte counters. However, dues to wrap-arounds, this class may have a burst of misfires every 16 seconds or so and thus should only be used where this is harmless and memory savings outweigh accuracy. */ class tiny_interval_t { private: uint8_t end; public: static inline uint8_t tiny_interval(uint32_t ms) { return uint8_t(ms / 64); } inline void wait_for(uint32_t ms) { uint32_t now = millis(); end = tiny_interval(now + ms); if(tiny_interval(now + ms*2) < end) { // Avoid special case where timer // might get wedged and stop firing. end = 0; } } inline bool elapsed() { uint8_t now = tiny_interval(millis()); if(now > end) { return true; } else { return false; } } }; /******************* SOUND HELPER CLASS ************************/ class CLCD::SoundPlayer { public: struct sound_t { effect_t effect; // The sound effect number note_t note; // The MIDI note value uint16_t sixteenths; // Duration of note, in sixteeths of a second, or zero to play to completion }; const uint8_t WAIT = 0; static const PROGMEM sound_t silence[]; private: const sound_t *sequence; uint8_t next; note_t frequencyToMidiNote(const uint16_t frequency); public: static void setVolume(uint8_t volume); static void play(effect_t effect, note_t note = NOTE_C4); static bool soundPlaying(); void play(const sound_t* seq); void playTone(const uint16_t frequency_hz, const uint16_t duration_ms); void onIdle(); bool hasMoreNotes() {return sequence != 0;}; }; const PROGMEM CLCD::SoundPlayer::sound_t CLCD::SoundPlayer::silence[] = { {SILENCE, END_SONG, 0} }; void CLCD::SoundPlayer::setVolume(uint8_t vol) { CLCD::Mem_Write8(REG_VOL_SOUND, vol); } void CLCD::SoundPlayer::play(effect_t effect, note_t note) { CLCD::Mem_Write16(REG_SOUND, (note << 8) | effect); CLCD::Mem_Write8( REG_PLAY, 1); #if defined(UI_FRAMEWORK_DEBUG) #if defined (SERIAL_PROTOCOLLNPAIR) SERIAL_PROTOCOLPAIR("Playing note ", note); SERIAL_PROTOCOLLNPAIR(", instrument ", effect); #endif #endif } note_t CLCD::SoundPlayer::frequencyToMidiNote(const uint16_t frequency_hz) { const float f0 = 440; return note_t(NOTE_A4 + (log(frequency_hz)-log(f0))*12/log(2) + 0.5); } // Plays a tone of a given frequency and duration. Since the FTDI FT810 only // supports MIDI notes, we round down to the nearest note. void CLCD::SoundPlayer::playTone(const uint16_t frequency_hz, const uint16_t duration_ms) { play(ORGAN, frequencyToMidiNote(frequency_hz)); // Schedule silence to squelch the note after the duration expires. sequence = silence; next = tiny_interval_t::tiny_interval(millis() + duration_ms); } void CLCD::SoundPlayer::play(const sound_t* seq) { sequence = seq; // Delaying the start of the sound seems to prevent glitches. Not sure why... next = tiny_interval_t::tiny_interval(millis()+250); } bool CLCD::SoundPlayer::soundPlaying() { return CLCD::Mem_Read8( REG_PLAY ) & 0x1; } void CLCD::SoundPlayer::onIdle() { if(!sequence) return; const uint8_t tiny_millis = tiny_interval_t::tiny_interval(millis()); const bool readyForNextNote = (next == WAIT) ? !soundPlaying() : (tiny_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; if(ms == 0 && fx == SILENCE && nt == 0) { sequence = 0; play(SILENCE, REST); } else { #if defined(UI_FRAMEWORK_DEBUG) #if defined (SERIAL_PROTOCOLLNPAIR) SERIAL_PROTOCOLLNPAIR("Scheduling note in ", ms); #endif #endif next = (ms == WAIT) ? 0 : (tiny_interval_t::tiny_interval(millis() + ms)); play(fx, (nt == 0) ? NOTE_C4 : nt); sequence++; } } } #endif // _AO_FT810_FUNC_H