/*********************
* 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