/********************* * FT810_SPI.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: . * ****************************************************************************/ #ifndef _AO_FT810_SPI_H #define _AO_FT810_SPI_H #if defined(PIN_DIR_OUT) // If SET_OUTPUT is defined, then map Marlin routines to those #define SET_OUTPUT PIN_DIR_OUT #define SET_INPUT_PULLUP(a) _PIN_DIR_IN(a); _PIN_HIGH(a); #define SET_INPUT PIN_DIR_IN #define WRITE SET_PIN #define READ GET_PIN #endif /********************************* SPI Functions *********************************/ void CLCD::spiInit (void) { #if defined(USE_ARDUINO_HW_SPI) SPI.begin(); SPI.beginTransaction(LCDsettings); #else SET_OUTPUT(CLCD_MOD_RESET); // CLCD_MOD_RST - Module Reset, not SPI WRITE(CLCD_MOD_RESET, 1); SET_OUTPUT(CLCD_BB_SPI_MOSI); // CLCD_MOSI WRITE(CLCD_BB_SPI_MOSI, 1); SET_OUTPUT(CLCD_BB_SPI_SCLK); // CLCD_SCLK WRITE(CLCD_BB_SPI_SCLK, 0); SET_OUTPUT(CLCD_BB_SPI_CS); // CLCD_CS WRITE(CLCD_BB_SPI_CS, 1); SET_INPUT_PULLUP(CLCD_BB_SPI_MISO); // CLCD_MISO delay(50); #endif } void CLCD::spiSelect (void) { // CLCD Bitbanged SPI - Chip Select WRITE(CLCD_BB_SPI_CS, 0); } void CLCD::spiDeselect (void) { // CLCD Bitbanged SPI - Chip Deselect WRITE(CLCD_BB_SPI_CS, 1); } void CLCD::Reset (void) { WRITE(CLCD_MOD_RESET, 0); delay(100); WRITE(CLCD_MOD_RESET, 1); } void CLCD::Test_Pulse(void) { WRITE(CLCD_AUX_0, 1); delayMicroseconds(10); WRITE(CLCD_AUX_0, 0); } uint8_t CLCD::spiTransfer (uint8_t spiOutByte) { #ifdef IS_ARDUINO SPI.transfer(spiOutByte); #else uint8_t spiIndex = 0x80; uint8_t spiInByte = 0; uint8_t k; for(k = 0; k <8; k++) { // Output and Read each bit of spiOutByte and spiInByte if(spiOutByte & spiIndex) { // Output MOSI Bit WRITE(CLCD_BB_SPI_MOSI, 1); } else { WRITE(CLCD_BB_SPI_MOSI, 0); } WRITE(CLCD_BB_SPI_SCLK, 1); // Pulse Clock WRITE(CLCD_BB_SPI_SCLK, 0); if(READ(CLCD_BB_SPI_MISO)) { spiInByte |= spiIndex; } spiIndex >>= 1; } return spiInByte; #endif } // MEMORY READ FUNCTIONS void CLCD::Mem_ReadAddr (uint32_t Reg_Address) { // Write 4-Byte Address CLCD::spiTransfer((Reg_Address >> 16) & 0x3F); // Address [21:16] CLCD::spiTransfer((Reg_Address >> 8 ) & 0xFF); // Address [15:8] CLCD::spiTransfer((Reg_Address >> 0) & 0xFF); // Address [7:0] CLCD::spiTransfer(0x00); // Dummy Byte } void CLCD::Mem_Read_Bulk (uint32_t Reg_Address, uint8_t *Data, uint16_t Len) { // Write 4-Byte Address, Read Multiple Bytes CLCD::spiSelect(); CLCD::Mem_ReadAddr(Reg_Address); while(Len--) { *Data = CLCD::spiTransfer(0x00); // Read 1 Byte *Data++; } CLCD::spiDeselect(); } uint8_t CLCD::Mem_Read8 (uint32_t Reg_Address) { // Write 4-Byte Address, Read 1-Byte Data CLCD::spiSelect(); CLCD::Mem_ReadAddr(Reg_Address); uint8_t R_Data = CLCD::spiTransfer(0x00); // Read 1 Byte CLCD::spiDeselect(); return(R_Data); } uint16_t CLCD::Mem_Read16 (uint32_t Reg_Address) { // Write 4-Byte Address, Read 2-Bytes Data CLCD::spiSelect(); CLCD::Mem_ReadAddr(Reg_Address); uint16_t R_Data = (((uint16_t) CLCD::spiTransfer(0x00)) << 0) | // Read Byte 1 (((uint16_t) CLCD::spiTransfer(0x00)) << 8); // Read Byte 2 CLCD::spiDeselect(); return(R_Data); } uint32_t CLCD::Mem_Read32 (uint32_t Reg_Address) { // Write 4-Byte Address, Read 4-Bytes Data CLCD::spiSelect(); CLCD::Mem_ReadAddr(Reg_Address); uint32_t R_Data = (((uint32_t) CLCD::spiTransfer(0x00)) << 0) | // Read Byte 1 (((uint32_t) CLCD::spiTransfer(0x00)) << 8) | // Read Byte 2 (((uint32_t) CLCD::spiTransfer(0x00)) << 16) | // Read Byte 3 (((uint32_t) CLCD::spiTransfer(0x00)) << 24); // Read Byte 4 CLCD::spiDeselect(); return(R_Data); } // MEMORY WRITE FUNCTIONS void CLCD::Mem_WriteAddr (uint32_t Reg_Address) { // Write 3-Byte Address CLCD::spiTransfer((Reg_Address >> 16) | 0x80); // Address [21:16] CLCD::spiTransfer((Reg_Address >> 8 ) & 0xFF); // Address [15:8] CLCD::spiTransfer((Reg_Address >> 0) & 0xFF); // Address [7:0] } void CLCD::Mem_Write_Bulk (uint32_t Reg_Address, const void *Data, uint16_t Len, uint8_t Padding) { // Write 3-Byte Address, Multiple Bytes, plus padding bytes const uint8_t* p = (const uint8_t *)Data; CLCD::spiSelect(); CLCD::Mem_WriteAddr(Reg_Address); while(Len--) { CLCD::spiTransfer(*p++); // Write 1 Byte } while(Padding--) { CLCD::spiTransfer(0); // Padding Bytes } CLCD::spiDeselect(); } void CLCD::Mem_Write_Bulk (uint32_t Reg_Address, progmem_str Str, uint16_t Len, uint8_t Padding) { // Write 3-Byte Address, Multiple Bytes, plus padding bytes const uint8_t* p = (const uint8_t *)Str; CLCD::spiSelect(); CLCD::Mem_WriteAddr(Reg_Address); while(Len--) { CLCD::spiTransfer(pgm_read_byte_near(p++)); // Write 1 Byte } while(Padding--) { CLCD::spiTransfer(0); // Padding Bytes } CLCD::spiDeselect(); } void CLCD::Mem_Write8 (uint32_t Reg_Address, uint8_t W_Data) { // Write 3-Byte Address, Write 1-Byte Data CLCD::spiSelect(); CLCD::Mem_WriteAddr(Reg_Address); CLCD::spiTransfer(W_Data); // Write 1 Byte CLCD::spiDeselect(); } void CLCD::Mem_Write16 (uint32_t Reg_Address, uint16_t W_Data) { // Write 3-Byte Address, Write 2-Bytes Data CLCD::spiSelect(); CLCD::Mem_WriteAddr(Reg_Address); CLCD::spiTransfer((uint8_t) ((W_Data >> 0) & 0x00FF)); // Write Byte 0 CLCD::spiTransfer((uint8_t) ((W_Data >> 8) & 0x00FF)); // Write Byte 1 CLCD::spiDeselect(); } void CLCD::Mem_Write32 (uint32_t Reg_Address, uint32_t W_Data) { // Write 3-Byte Address, Write 4-Bytes Data CLCD::spiSelect(); CLCD::Mem_WriteAddr(Reg_Address); CLCD::spiTransfer(W_Data >> 0); // Write Byte 0 CLCD::spiTransfer(W_Data >> 8); // Write Byte 1 CLCD::spiTransfer(W_Data >> 16); // Write Byte 2 CLCD::spiTransfer(W_Data >> 24); // Write Byte 3 CLCD::spiDeselect(); } /**************************** FT800/810 Co-Processor Command FIFO ****************************/ uint32_t CLCD::CommandFifo::getRegCmdWrite() { return Mem_Read32(REG_CMD_WRITE) & 0x0FFF; } uint32_t CLCD::CommandFifo::getRegCmdRead() { return Mem_Read32(REG_CMD_READ) & 0x0FFF; } bool CLCD::CommandFifo::Cmd_Is_Idle() { return getRegCmdRead() == getRegCmdWrite(); } void CLCD::CommandFifo::Cmd_Wait_Until_Idle() { #if defined(UI_FRAMEWORK_DEBUG) const uint32_t startTime = millis(); #endif do { #if defined(UI_FRAMEWORK_DEBUG) if(millis() - startTime > 3) { #if defined (SERIAL_PROTOCOLLNPGM) SERIAL_PROTOCOLLNPGM("Timeout on CommandFifo::Wait_Until_Idle()"); #else Serial.println(F("Timeout on CommandFifo::Wait_Until_Idle()")); #endif break; } #endif } while(!Cmd_Is_Idle()); } #if defined(LCD_IS_FT800) void CLCD::CommandFifo::Cmd_Start() { if(command_write_ptr == 0xFFFFFFFFul) { command_write_ptr = getRegCmdWrite(); } } void CLCD::CommandFifo::Cmd_Execute() { if(command_write_ptr != 0xFFFFFFFFul) { Mem_Write32(REG_CMD_WRITE, command_write_ptr); } } void CLCD::CommandFifo::Cmd_Reset() { Mem_Write32(REG_CMD_WRITE, 0x00000000); Mem_Write32(REG_CMD_READ, 0x00000000); command_write_ptr = 0xFFFFFFFFul; }; template void CLCD::CommandFifo::_write_unaligned(T data, uint16_t len) { const char *ptr = (const char*)data; uint32_t bytes_tail, bytes_head; uint32_t command_read_ptr; #if defined(UI_FRAMEWORK_DEBUG) if(command_write_ptr == 0xFFFFFFFFul) { #if defined (SERIAL_PROTOCOLLNPGM) SERIAL_PROTOCOLLNPGM("Attempt to write to FIFO before CommandFifo::Cmd_Start()."); #else Serial.println(F("Attempt to write to FIFO before CommandFifo::Cmd_Start().")); #endif } #endif /* Wait until there is enough space in the circular buffer for the transfer */ do { command_read_ptr = getRegCmdRead(); if (command_read_ptr <= command_write_ptr) { bytes_tail = 4096U - command_write_ptr; bytes_head = command_read_ptr; } else { bytes_tail = command_read_ptr - command_write_ptr; bytes_head = 0; } } while((bytes_tail + bytes_head) < len); /* Write as many bytes as possible following REG_CMD_WRITE */ uint16_t bytes_to_write = min(len, bytes_tail); Mem_Write_Bulk (RAM_CMD + command_write_ptr, T(ptr), bytes_to_write); command_write_ptr += bytes_to_write; ptr += bytes_to_write; len -= bytes_to_write; if(len > 0) { /* Write remaining bytes at start of circular buffer */ Mem_Write_Bulk (RAM_CMD, T(ptr), len); command_write_ptr = len; } if(command_write_ptr == 4096U) { command_write_ptr = 0; } } // Writes len bytes into the FIFO, if len is not // divisible by four, zero bytes will be written // to align to the boundary. template void CLCD::CommandFifo::write(T data, uint16_t len) { const uint8_t padding = MULTIPLE_OF_4(len) - len; uint8_t pad_bytes[] = {0, 0, 0, 0}; _write_unaligned(data, len); _write_unaligned(pad_bytes, padding); } #else uint32_t CLCD::CommandFifo::getRegCmdBSpace() { return Mem_Read32(REG_CMDB_SPACE) & 0x0FFF; } void CLCD::CommandFifo::Cmd_Start() { } void CLCD::CommandFifo::Cmd_Execute() { } void CLCD::CommandFifo::Cmd_Reset() { Mem_Write32(REG_CMD_WRITE, 0x00000000); Mem_Write32(REG_CMD_READ, 0x00000000); }; // Writes len bytes into the FIFO, if len is not // divisible by four, zero bytes will be written // to align to the boundary. template void CLCD::CommandFifo::write(T data, uint16_t len) { const uint8_t padding = MULTIPLE_OF_4(len) - len; // The FT810 provides a special register that can be used // for writing data without us having to do our own FIFO // management. uint32_t Command_Space = getRegCmdBSpace(); while(Command_Space < len + padding) { Command_Space = getRegCmdBSpace(); } Mem_Write_Bulk(REG_CMDB_WRITE, data, len, padding); } #endif // CO_PROCESSOR COMMANDS void CLCD::CommandFifo::Cmd (uint32_t cmd32) { // Writes a 32-bit (4 Bytes) Value to the Co-processor Command Buffer FIFO write(&cmd32, sizeof(uint32_t)); } void CLCD::CommandFifo::Cmd (void* data, uint16_t len) { // Writes a data structure - always a multiple of 32 bits - to the Co_Processor FIFO. // Data structure includes the 32-bit Co_Processor command. write(data, len); } void CLCD::CommandFifo::Cmd_Str (const char * const data) { write(data, strlen(data)+1); } void CLCD::CommandFifo::Cmd_Str (progmem_str data) { write(data, strlen_P((const char*)data)+1); } #endif // _AO_FT810_SPI_H