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