From 1a4b82a5d663ab7f68199bf77183047c1133e479 Mon Sep 17 00:00:00 2001 From: Victor Oliveira Date: Wed, 26 Aug 2020 07:13:58 -0300 Subject: [PATCH] TFT (plus Hardware SPI) for LPC (#19139) --- Marlin/src/HAL/LPC1768/HAL_SPI.cpp | 252 ++++++++++++++++-- Marlin/src/HAL/LPC1768/inc/Conditionals_LCD.h | 11 +- Marlin/src/HAL/LPC1768/include/SPI.h | 140 +++++++++- Marlin/src/HAL/LPC1768/tft/tft_spi.cpp | 153 +++++++++++ Marlin/src/HAL/LPC1768/tft/tft_spi.h | 77 ++++++ Marlin/src/HAL/LPC1768/tft/xpt2046.cpp | 129 +++++++++ Marlin/src/HAL/LPC1768/tft/xpt2046.h | 80 ++++++ Marlin/src/lcd/dogm/HAL_LCD_com_defines.h | 10 +- ...8g_dev_tft_320x240_upscale_from_128x64.cpp | 4 +- Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_4.h | 32 +++ Marlin/src/pins/pinsDebug_list.h | 19 ++ 11 files changed, 860 insertions(+), 47 deletions(-) create mode 100644 Marlin/src/HAL/LPC1768/tft/tft_spi.cpp create mode 100644 Marlin/src/HAL/LPC1768/tft/tft_spi.h create mode 100644 Marlin/src/HAL/LPC1768/tft/xpt2046.cpp create mode 100644 Marlin/src/HAL/LPC1768/tft/xpt2046.h diff --git a/Marlin/src/HAL/LPC1768/HAL_SPI.cpp b/Marlin/src/HAL/LPC1768/HAL_SPI.cpp index e34a102012..00b4310d1d 100644 --- a/Marlin/src/HAL/LPC1768/HAL_SPI.cpp +++ b/Marlin/src/HAL/LPC1768/HAL_SPI.cpp @@ -30,7 +30,7 @@ */ /** - * Hardware SPI and a software SPI implementations are included in this file. + * Hardware SPI and Software SPI implementations are included in this file. * The hardware SPI runs faster and has higher throughput but is not compatible * with some LCD interfaces/adapters. * @@ -51,6 +51,10 @@ #include "../../inc/MarlinConfig.h" #include +// Hardware SPI and SPIClass +#include +#include + // ------------------------ // Public functions // ------------------------ @@ -96,12 +100,6 @@ #else - // Hardware SPI - - #include - #include - #include - // decide which HW SPI device to use #ifndef LPC_HW_SPI_DEV #if (SCK_PIN == P0_07 && MISO_PIN == P0_08 && MOSI_PIN == P0_09) @@ -114,7 +112,7 @@ #endif #endif #endif - #if (LPC_HW_SPI_DEV == 0) + #if LPC_HW_SPI_DEV == 0 #define LPC_SSPn LPC_SSP0 #else #define LPC_SSPn LPC_SSP1 @@ -192,7 +190,7 @@ for (uint16_t i = 0; i < nbyte; i++) buf[i] = doio(0xFF); } - static uint8_t spiTransfer(uint8_t b) { + uint8_t spiTransfer(uint8_t b) { return doio(b); } @@ -211,30 +209,236 @@ #endif // LPC_SOFTWARE_SPI -void SPIClass::begin() { spiBegin(); } +/** + * @brief Wait until TXE (tx empty) flag is set and BSY (busy) flag unset. + */ +static inline void waitSpiTxEnd(LPC_SSP_TypeDef *spi_d) { + while (SSP_GetStatus(spi_d, SSP_STAT_TXFIFO_EMPTY) == RESET) { /* nada */ } // wait until TXE=1 + while (SSP_GetStatus(spi_d, SSP_STAT_BUSY) == SET) { /* nada */ } // wait until BSY=0 +} + +SPIClass::SPIClass(uint8_t device) { + // Init things specific to each SPI device + // clock divider setup is a bit of hack, and needs to be improved at a later date. + + PINSEL_CFG_Type PinCfg; // data structure to hold init values + #if BOARD_NR_SPI >= 1 + _settings[0].spi_d = LPC_SSP0; + // _settings[0].clockDivider = determine_baud_rate(_settings[0].spi_d, _settings[0].clock); + PinCfg.Funcnum = 2; + PinCfg.OpenDrain = 0; + PinCfg.Pinmode = 0; + PinCfg.Pinnum = LPC176x::pin_bit(BOARD_SPI1_SCK_PIN); + PinCfg.Portnum = LPC176x::pin_port(BOARD_SPI1_SCK_PIN); + PINSEL_ConfigPin(&PinCfg); + SET_OUTPUT(BOARD_SPI1_SCK_PIN); + + PinCfg.Pinnum = LPC176x::pin_bit(BOARD_SPI1_MISO_PIN); + PinCfg.Portnum = LPC176x::pin_port(BOARD_SPI1_MISO_PIN); + PINSEL_ConfigPin(&PinCfg); + SET_INPUT(BOARD_SPI1_MISO_PIN); + + PinCfg.Pinnum = LPC176x::pin_bit(BOARD_SPI1_MOSI_PIN); + PinCfg.Portnum = LPC176x::pin_port(BOARD_SPI1_MOSI_PIN); + PINSEL_ConfigPin(&PinCfg); + SET_OUTPUT(BOARD_SPI1_MOSI_PIN); + #endif + + #if BOARD_NR_SPI >= 2 + _settings[1].spi_d = LPC_SSP1; + // _settings[1].clockDivider = determine_baud_rate(_settings[1].spi_d, _settings[1].clock); + PinCfg.Funcnum = 2; + PinCfg.OpenDrain = 0; + PinCfg.Pinmode = 0; + PinCfg.Pinnum = LPC176x::pin_bit(BOARD_SPI2_SCK_PIN); + PinCfg.Portnum = LPC176x::pin_port(BOARD_SPI2_SCK_PIN); + PINSEL_ConfigPin(&PinCfg); + SET_OUTPUT(BOARD_SPI2_SCK_PIN); + + PinCfg.Pinnum = LPC176x::pin_bit(BOARD_SPI2_MISO_PIN); + PinCfg.Portnum = LPC176x::pin_port(BOARD_SPI2_MISO_PIN); + PINSEL_ConfigPin(&PinCfg); + SET_INPUT(BOARD_SPI2_MISO_PIN); + + PinCfg.Pinnum = LPC176x::pin_bit(BOARD_SPI2_MOSI_PIN); + PinCfg.Portnum = LPC176x::pin_port(BOARD_SPI2_MOSI_PIN); + PINSEL_ConfigPin(&PinCfg); + SET_OUTPUT(BOARD_SPI2_MOSI_PIN); + #endif + + setModule(device); + + /* Initialize GPDMA controller */ + //TODO: call once in the constructor? or each time? + GPDMA_Init(); +} + +void SPIClass::begin() { + updateSettings(); + SSP_Cmd(_currentSetting->spi_d, ENABLE); // start SSP running +} void SPIClass::beginTransaction(const SPISettings &cfg) { - uint8_t spiRate; - switch (cfg.spiRate()) { - case 8000000: spiRate = 0; break; - case 4000000: spiRate = 1; break; - case 2000000: spiRate = 2; break; - case 1000000: spiRate = 3; break; - case 500000: spiRate = 4; break; - case 250000: spiRate = 5; break; - case 125000: spiRate = 6; break; - default: spiRate = 2; break; - } - spiInit(spiRate); + setBitOrder(cfg.bitOrder); + setDataMode(cfg.dataMode); + setDataSize(cfg.dataSize); + //setClockDivider(determine_baud_rate(_currentSetting->spi_d, settings.clock)); + begin(); } -uint8_t SPIClass::transfer(const uint8_t B) { return spiTransfer(B); } +uint8_t SPIClass::transfer(const uint16_t b) { + /* send and receive a single byte */ + SSP_ReceiveData(_currentSetting->spi_d); // read any previous data + SSP_SendData(_currentSetting->spi_d, b); + waitSpiTxEnd(_currentSetting->spi_d); // wait for it to finish + return SSP_ReceiveData(_currentSetting->spi_d); +} uint16_t SPIClass::transfer16(const uint16_t data) { return (transfer((data >> 8) & 0xFF) << 8) | (transfer(data & 0xFF) & 0xFF); } -SPIClass SPI; +void SPIClass::end() { + // SSP_Cmd(_currentSetting->spi_d, DISABLE); // stop device or SSP_DeInit? + SSP_DeInit(_currentSetting->spi_d); +} + +void SPIClass::send(uint8_t data) { + SSP_SendData(_currentSetting->spi_d, data); +} + +void SPIClass::dmaSend(void *buf, uint16_t length, bool minc) { + //TODO: LPC dma can only write 0xFFF bytes at once. + GPDMA_Channel_CFG_Type GPDMACfg; + + /* Configure GPDMA channel 0 -------------------------------------------------------------*/ + /* DMA Channel 0 */ + GPDMACfg.ChannelNum = 0; + // Source memory + GPDMACfg.SrcMemAddr = (uint32_t)buf; + // Destination memory - Not used + GPDMACfg.DstMemAddr = 0; + // Transfer size + GPDMACfg.TransferSize = (minc ? length : 1); + // Transfer width + GPDMACfg.TransferWidth = (_currentSetting->dataSize == DATA_SIZE_16BIT) ? GPDMA_WIDTH_HALFWORD : GPDMA_WIDTH_BYTE; + // Transfer type + GPDMACfg.TransferType = GPDMA_TRANSFERTYPE_M2P; + // Source connection - unused + GPDMACfg.SrcConn = 0; + // Destination connection + GPDMACfg.DstConn = (_currentSetting->spi_d == LPC_SSP0) ? GPDMA_CONN_SSP0_Tx : GPDMA_CONN_SSP1_Tx; + + GPDMACfg.DMALLI = 0; + + // Enable dma on SPI + SSP_DMACmd(_currentSetting->spi_d, SSP_DMA_TX, ENABLE); + + // if minc=false, I'm repeating the same byte 'length' times, as I could not find yet how do GPDMA without memory increment + do { + // Setup channel with given parameter + GPDMA_Setup(&GPDMACfg); + + // enabled dma + GPDMA_ChannelCmd(0, ENABLE); + + // wait data transfer + while (!GPDMA_IntGetStatus(GPDMA_STAT_INTTC, 0) && !GPDMA_IntGetStatus(GPDMA_STAT_INTERR, 0)) { } + + // clear err and int + GPDMA_ClearIntPending (GPDMA_STATCLR_INTTC, 0); + GPDMA_ClearIntPending (GPDMA_STATCLR_INTERR, 0); + + // dma disable + GPDMA_ChannelCmd(0, DISABLE); + + --length; + } while (!minc && length > 0); + + waitSpiTxEnd(_currentSetting->spi_d); + + SSP_DMACmd(_currentSetting->spi_d, SSP_DMA_TX, DISABLE); +} + +uint16_t SPIClass::read() { + return SSP_ReceiveData(_currentSetting->spi_d); +} + +void SPIClass::read(uint8_t *buf, uint32_t len) { + for (uint16_t i = 0; i < len; i++) buf[i] = transfer(0xFF); +} + +void SPIClass::setClock(uint32_t clock) { + _currentSetting->clock = clock; +} + +void SPIClass::setModule(uint8_t device) { + _currentSetting = &_settings[device - 1];// SPI channels are called 1 2 and 3 but the array is zero indexed +} + +void SPIClass::setBitOrder(uint8_t bitOrder) { + _currentSetting->bitOrder = bitOrder; +} + +void SPIClass::setDataMode(uint8_t dataMode) { + _currentSetting->dataSize = dataMode; +} + +void SPIClass::setDataSize(uint32_t ds) { + _currentSetting->dataSize = ds; +} + +/** + * Set up/tear down + */ +void SPIClass::updateSettings() { + //SSP_DeInit(_currentSetting->spi_d); //todo: need force de init?! + + // divide PCLK by 2 for SSP0 + CLKPWR_SetPCLKDiv(_currentSetting->spi_d == LPC_SSP0 ? CLKPWR_PCLKSEL_SSP0 : CLKPWR_PCLKSEL_SSP1, CLKPWR_PCLKSEL_CCLK_DIV_2); + + SSP_CFG_Type HW_SPI_init; // data structure to hold init values + SSP_ConfigStructInit(&HW_SPI_init); // set values for SPI mode + HW_SPI_init.ClockRate = _currentSetting->clock; + HW_SPI_init.Databit = _currentSetting->dataSize; + + /** + * SPI Mode CPOL CPHA Shift SCK-edge Capture SCK-edge + * 0 0 0 Falling Rising + * 1 0 1 Rising Falling + * 2 1 0 Rising Falling + * 3 1 1 Falling Rising + */ + switch (_currentSetting->dataMode) { + case SPI_MODE0: + HW_SPI_init.CPHA = SSP_CPHA_FIRST; + HW_SPI_init.CPOL = SSP_CPOL_HI; + break; + case SPI_MODE1: + HW_SPI_init.CPHA = SSP_CPHA_SECOND; + HW_SPI_init.CPOL = SSP_CPOL_HI; + break; + case SPI_MODE2: + HW_SPI_init.CPHA = SSP_CPHA_FIRST; + HW_SPI_init.CPOL = SSP_CPOL_LO; + break; + case SPI_MODE3: + HW_SPI_init.CPHA = SSP_CPHA_SECOND; + HW_SPI_init.CPOL = SSP_CPOL_LO; + break; + default: + break; + } + + // TODO: handle bitOrder + SSP_Init(_currentSetting->spi_d, &HW_SPI_init); // puts the values into the proper bits in the SSP0 registers +} + +#if MISO_PIN == BOARD_SPI1_MISO_PIN + SPIClass SPI(1); +#elif MISO_PIN == BOARD_SPI2_MISO_PIN + SPIClass SPI(2); +#endif #endif // TARGET_LPC1768 diff --git a/Marlin/src/HAL/LPC1768/inc/Conditionals_LCD.h b/Marlin/src/HAL/LPC1768/inc/Conditionals_LCD.h index 7d4de40faa..b9bc0bf82e 100644 --- a/Marlin/src/HAL/LPC1768/inc/Conditionals_LCD.h +++ b/Marlin/src/HAL/LPC1768/inc/Conditionals_LCD.h @@ -21,6 +21,13 @@ */ #pragma once -#if HAS_SPI_TFT || HAS_FSMC_TFT - #error "Sorry! TFT displays are not available for HAL/LPC1768." +#if HAS_FSMC_TFT + #error "Sorry! FSMC TFT displays are not current available for HAL/LPC1768." +#endif + +// This emulated DOGM has 'touch/xpt2046', not 'tft/xpt2046' +#if ENABLED(TOUCH_SCREEN) && !HAS_GRAPHICAL_TFT + #undef TOUCH_SCREEN + #undef TOUCH_SCREEN_CALIBRATION + #define HAS_TOUCH_XPT2046 1 #endif diff --git a/Marlin/src/HAL/LPC1768/include/SPI.h b/Marlin/src/HAL/LPC1768/include/SPI.h index 4acd9e7022..e2645b9290 100644 --- a/Marlin/src/HAL/LPC1768/include/SPI.h +++ b/Marlin/src/HAL/LPC1768/include/SPI.h @@ -24,25 +24,139 @@ #include "../../shared/HAL_SPI.h" #include +#include +#include -#define MSBFIRST 1 -#define SPI_MODE3 0 +//#define MSBFIRST 1 + +#define SPI_MODE0 0 +#define SPI_MODE1 1 +#define SPI_MODE2 2 +#define SPI_MODE3 3 + +#define DATA_SIZE_8BIT SSP_DATABIT_8 +#define DATA_SIZE_16BIT SSP_DATABIT_16 + +#define SPI_CLOCK_DIV2 8333333 //(SCR: 2) desired: 8,000,000 actual: 8,333,333 +4.2% SPI_FULL_SPEED +#define SPI_CLOCK_DIV4 4166667 //(SCR: 5) desired: 4,000,000 actual: 4,166,667 +4.2% SPI_HALF_SPEED +#define SPI_CLOCK_DIV8 2083333 //(SCR: 11) desired: 2,000,000 actual: 2,083,333 +4.2% SPI_QUARTER_SPEED +#define SPI_CLOCK_DIV16 1000000 //(SCR: 24) desired: 1,000,000 actual: 1,000,000 SPI_EIGHTH_SPEED +#define SPI_CLOCK_DIV32 500000 //(SCR: 49) desired: 500,000 actual: 500,000 SPI_SPEED_5 +#define SPI_CLOCK_DIV64 250000 //(SCR: 99) desired: 250,000 actual: 250,000 SPI_SPEED_6 +#define SPI_CLOCK_DIV128 125000 //(SCR:199) desired: 125,000 actual: 125,000 Default from HAL.h + +#define SPI_CLOCK_MAX SPI_CLOCK_DIV2 + +#define BOARD_NR_SPI 2 + +//#define BOARD_SPI1_NSS_PIN PA4 ?! +#define BOARD_SPI1_SCK_PIN P0_15 +#define BOARD_SPI1_MISO_PIN P0_17 +#define BOARD_SPI1_MOSI_PIN P0_18 + +//#define BOARD_SPI2_NSS_PIN PB12 ?! +#define BOARD_SPI2_SCK_PIN P0_07 +#define BOARD_SPI2_MISO_PIN P0_08 +#define BOARD_SPI2_MOSI_PIN P0_09 class SPISettings { - public: - SPISettings(uint32_t speed, int, int) : spi_speed(speed) {}; - uint32_t spiRate() const { return spi_speed; } - private: - uint32_t spi_speed; +public: + SPISettings(uint32_t speed, int, int) : spi_speed(speed) {}; + SPISettings(uint32_t inClock, uint8_t inBitOrder, uint8_t inDataMode, uint32_t inDataSize) { + if (__builtin_constant_p(inClock)) + init_AlwaysInline(inClock, inBitOrder, inDataMode, inDataSize); + else + init_MightInline(inClock, inBitOrder, inDataMode, inDataSize); + } + SPISettings() { + init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0, DATA_SIZE_8BIT); + } + + uint32_t spiRate() const { return spi_speed; } + +private: + void init_MightInline(uint32_t inClock, uint8_t inBitOrder, uint8_t inDataMode, uint32_t inDataSize) { + init_AlwaysInline(inClock, inBitOrder, inDataMode, inDataSize); + } + void init_AlwaysInline(uint32_t inClock, uint8_t inBitOrder, uint8_t inDataMode, uint32_t inDataSize) __attribute__((__always_inline__)) { + clock = inClock; + bitOrder = inBitOrder; + dataMode = inDataMode; + dataSize = inDataSize; + } + + uint32_t spi_speed; + uint32_t clock; + uint32_t dataSize; + //uint32_t clockDivider; + uint8_t bitOrder; + uint8_t dataMode; + LPC_SSP_TypeDef *spi_d; + + friend class SPIClass; }; +/** + * @brief Wirish SPI interface. + * + * This is the same interface is available across HAL + * + * This implementation uses software slave management, so the caller + * is responsible for controlling the slave select line. + */ class SPIClass { - public: - void begin(); - void beginTransaction(const SPISettings&); - void endTransaction() {}; - uint8_t transfer(uint8_t data); - uint16_t transfer16(uint16_t data); +public: + /** + * @param spiPortNumber Number of the SPI port to manage. + */ + SPIClass(uint8_t spiPortNumber); + + /** + * Select and configure the current selected SPI device to use + */ + void begin(); + + /** + * Disable the current SPI device + */ + void end(); + + void beginTransaction(const SPISettings&); + void endTransaction() {}; + + // Transfer using 1 "Data Size" + uint8_t transfer(uint16_t data); + // Transfer 2 bytes in 8 bit mode + uint16_t transfer16(uint16_t data); + + void send(uint8_t data); + + uint16_t read(); + void read(uint8_t *buf, uint32_t len); + + void dmaSend(void *buf, uint16_t length, bool minc); + + /** + * @brief Sets the number of the SPI peripheral to be used by + * this HardwareSPI instance. + * + * @param spi_num Number of the SPI port. 1-2 in low density devices + * or 1-3 in high density devices. + */ + void setModule(uint8_t device); + + void setClock(uint32_t clock); + void setBitOrder(uint8_t bitOrder); + void setDataMode(uint8_t dataMode); + void setDataSize(uint32_t ds); + + inline uint32_t getDataSize() { return _currentSetting->dataSize; } + +private: + SPISettings _settings[BOARD_NR_SPI]; + SPISettings *_currentSetting; + + void updateSettings(); }; extern SPIClass SPI; diff --git a/Marlin/src/HAL/LPC1768/tft/tft_spi.cpp b/Marlin/src/HAL/LPC1768/tft/tft_spi.cpp new file mode 100644 index 0000000000..84907acd07 --- /dev/null +++ b/Marlin/src/HAL/LPC1768/tft/tft_spi.cpp @@ -0,0 +1,153 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "../../../inc/MarlinConfig.h" + +#if HAS_SPI_TFT + +#include "tft_spi.h" + +//TFT_SPI tft; + +SPIClass TFT_SPI::SPIx(1); + +#define TFT_CS_H WRITE(TFT_CS_PIN, HIGH) +#define TFT_CS_L WRITE(TFT_CS_PIN, LOW) + +#define TFT_DC_H WRITE(TFT_DC_PIN, HIGH) +#define TFT_DC_L WRITE(TFT_DC_PIN, LOW) + +#define TFT_RST_H WRITE(TFT_RESET_PIN, HIGH) +#define TFT_RST_L WRITE(TFT_RESET_PIN, LOW) + +#define TFT_BLK_H WRITE(TFT_BACKLIGHT_PIN, HIGH) +#define TFT_BLK_L WRITE(TFT_BACKLIGHT_PIN, LOW) + +void TFT_SPI::Init() { + #if PIN_EXISTS(TFT_RESET) + SET_OUTPUT(TFT_RESET_PIN); + TFT_RST_H; + delay(100); + #endif + + #if PIN_EXISTS(TFT_BACKLIGHT) + SET_OUTPUT(TFT_BACKLIGHT_PIN); + TFT_BLK_H; + #endif + + SET_OUTPUT(TFT_DC_PIN); + SET_OUTPUT(TFT_CS_PIN); + + TFT_DC_H; + TFT_CS_H; + + /** + * STM32F1 APB2 = 72MHz, APB1 = 36MHz, max SPI speed of this MCU if 18Mhz + * STM32F1 has 3 SPI ports, SPI1 in APB2, SPI2/SPI3 in APB1 + * so the minimum prescale of SPI1 is DIV4, SPI2/SPI3 is DIV2 + */ + #if 0 + #if SPI_DEVICE == 1 + #define SPI_CLOCK_MAX SPI_CLOCK_DIV4 + #else + #define SPI_CLOCK_MAX SPI_CLOCK_DIV2 + #endif + uint8_t clock; + uint8_t spiRate = SPI_FULL_SPEED; + switch (spiRate) { + case SPI_FULL_SPEED: clock = SPI_CLOCK_MAX ; break; + case SPI_HALF_SPEED: clock = SPI_CLOCK_DIV4 ; break; + case SPI_QUARTER_SPEED: clock = SPI_CLOCK_DIV8 ; break; + case SPI_EIGHTH_SPEED: clock = SPI_CLOCK_DIV16; break; + case SPI_SPEED_5: clock = SPI_CLOCK_DIV32; break; + case SPI_SPEED_6: clock = SPI_CLOCK_DIV64; break; + default: clock = SPI_CLOCK_DIV2; // Default from the SPI library + } + #endif + + #if TFT_MISO_PIN == BOARD_SPI1_MISO_PIN + SPIx.setModule(1); + #elif TFT_MISO_PIN == BOARD_SPI2_MISO_PIN + SPIx.setModule(2); + #endif + SPIx.setClock(SPI_CLOCK_MAX); + SPIx.setBitOrder(MSBFIRST); + SPIx.setDataMode(SPI_MODE0); +} + +void TFT_SPI::DataTransferBegin(uint16_t DataSize) { + SPIx.setDataSize(DataSize); + SPIx.begin(); + TFT_CS_L; +} + +uint32_t TFT_SPI::GetID() { + uint32_t id; + id = ReadID(LCD_READ_ID); + if ((id & 0xFFFF) == 0 || (id & 0xFFFF) == 0xFFFF) + id = ReadID(LCD_READ_ID4); + return id; +} + +uint32_t TFT_SPI::ReadID(uint16_t Reg) { + uint32_t data = 0; + + #if PIN_EXISTS(TFT_MISO) + uint8_t d = 0; + SPIx.setDataSize(DATASIZE_8BIT); + SPIx.setClock(SPI_CLOCK_DIV64); + SPIx.begin(); + TFT_CS_L; + WriteReg(Reg); + + LOOP_L_N(i, 4) { + SPIx.read((uint8_t*)&d, 1); + data = (data << 8) | d; + } + + DataTransferEnd(); + SPIx.setClock(SPI_CLOCK_MAX); + #endif + + return data >> 7; +} + +bool TFT_SPI::isBusy() { + return false; +} + +void TFT_SPI::Abort() { + DataTransferEnd(); +} + +void TFT_SPI::Transmit(uint16_t Data) { + SPIx.transfer(Data); +} + +void TFT_SPI::TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) { + DataTransferBegin(DATASIZE_16BIT); //16 + TFT_DC_H; + SPIx.dmaSend(Data, Count, MemoryIncrease); + DataTransferEnd(); +} + +#endif // HAS_SPI_TFT diff --git a/Marlin/src/HAL/LPC1768/tft/tft_spi.h b/Marlin/src/HAL/LPC1768/tft/tft_spi.h new file mode 100644 index 0000000000..8d3e3127bb --- /dev/null +++ b/Marlin/src/HAL/LPC1768/tft/tft_spi.h @@ -0,0 +1,77 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#pragma once + +#include "../../../inc/MarlinConfig.h" + +#include +#include +// #include + +#ifndef LCD_READ_ID + #define LCD_READ_ID 0x04 // Read display identification information (0xD3 on ILI9341) +#endif +#ifndef LCD_READ_ID4 + #define LCD_READ_ID4 0xD3 // Read display identification information (0xD3 on ILI9341) +#endif + +#define DATASIZE_8BIT SSP_DATABIT_8 +#define DATASIZE_16BIT SSP_DATABIT_16 +#define TFT_IO TFT_SPI + +#define DMA_MINC_ENABLE 1 +#define DMA_MINC_DISABLE 0 + +class TFT_SPI { +private: + static uint32_t ReadID(uint16_t Reg); + static void Transmit(uint16_t Data); + static void TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count); + +public: + static SPIClass SPIx; + + static void Init(); + static uint32_t GetID(); + static bool isBusy(); + static void Abort(); + + static void DataTransferBegin(uint16_t DataWidth = DATASIZE_16BIT); + static void DataTransferEnd() { OUT_WRITE(TFT_CS_PIN, HIGH); SPIx.end(); }; + static void DataTransferAbort(); + + static void WriteData(uint16_t Data) { Transmit(Data); } + static void WriteReg(uint16_t Reg) { OUT_WRITE(TFT_A0_PIN, LOW); Transmit(Reg); OUT_WRITE(TFT_A0_PIN, HIGH); } + + static void WriteSequence(uint16_t *Data, uint16_t Count) { TransmitDMA(DMA_MINC_ENABLE, Data, Count); } + // static void WriteMultiple(uint16_t Color, uint16_t Count) { static uint16_t Data; Data = Color; TransmitDMA(DMA_MINC_DISABLE, &Data, Count); } + static void WriteMultiple(uint16_t Color, uint32_t Count) { + static uint16_t Data; Data = Color; + //LPC dma can only write 0xFFF bytes at once. + #define MAX_DMA_SIZE (0xFFF - 1) + while (Count > 0) { + TransmitDMA(DMA_MINC_DISABLE, &Data, Count > MAX_DMA_SIZE ? MAX_DMA_SIZE : Count); + Count = Count > MAX_DMA_SIZE ? Count - MAX_DMA_SIZE : 0; + } + #undef MAX_DMA_SIZE + } +}; diff --git a/Marlin/src/HAL/LPC1768/tft/xpt2046.cpp b/Marlin/src/HAL/LPC1768/tft/xpt2046.cpp new file mode 100644 index 0000000000..c72e5f0eac --- /dev/null +++ b/Marlin/src/HAL/LPC1768/tft/xpt2046.cpp @@ -0,0 +1,129 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "../../../inc/MarlinConfig.h" + +#if HAS_TFT_XPT2046 || HAS_TOUCH_XPT2046 + +#include "xpt2046.h" +#include + +uint16_t delta(uint16_t a, uint16_t b) { return a > b ? a - b : b - a; } + +#if ENABLED(TOUCH_BUTTONS_HW_SPI) + #include + + SPIClass XPT2046::SPIx(TOUCH_BUTTONS_HW_SPI_DEVICE); + + static void touch_spi_init(uint8_t spiRate) { + XPT2046::SPIx.setModule(TOUCH_BUTTONS_HW_SPI_DEVICE); + XPT2046::SPIx.setClock(SPI_CLOCK_DIV128); + XPT2046::SPIx.setBitOrder(MSBFIRST); + XPT2046::SPIx.setDataMode(SPI_MODE0); + XPT2046::SPIx.setDataSize(DATA_SIZE_8BIT); + } +#endif + +void XPT2046::Init() { + SET_INPUT(TOUCH_MISO_PIN); + SET_OUTPUT(TOUCH_MOSI_PIN); + SET_OUTPUT(TOUCH_SCK_PIN); + OUT_WRITE(TOUCH_CS_PIN, HIGH); + + #if PIN_EXISTS(TOUCH_INT) + // Optional Pendrive interrupt pin + SET_INPUT(TOUCH_INT_PIN); + #endif + + TERN_(TOUCH_BUTTONS_HW_SPI, touch_spi_init(SPI_SPEED_6)); + + // Read once to enable pendrive status pin + getRawData(XPT2046_X); +} + +bool XPT2046::isTouched() { + return isBusy() ? false : ( + #if PIN_EXISTS(TOUCH_INT) + READ(TOUCH_INT_PIN) != HIGH + #else + getRawData(XPT2046_Z1) >= XPT2046_Z1_THRESHOLD + #endif + ); +} + +bool XPT2046::getRawPoint(int16_t *x, int16_t *y) { + if (isBusy()) return false; + if (!isTouched()) return false; + *x = getRawData(XPT2046_X); + *y = getRawData(XPT2046_Y); + SERIAL_ECHOLNPAIR("X: ", *x, ", Y: ", *y); + return isTouched(); +} + +uint16_t XPT2046::getRawData(const XPTCoordinate coordinate) { + uint16_t data[3]; + + DataTransferBegin(); + TERN_(TOUCH_BUTTONS_HW_SPI, SPIx.begin()); + + for (uint16_t i = 0; i < 3 ; i++) { + IO(coordinate); + data[i] = (IO() << 4) | (IO() >> 4); + } + + TERN_(TOUCH_BUTTONS_HW_SPI, SPIx.end()); + DataTransferEnd(); + + uint16_t delta01 = delta(data[0], data[1]), + delta02 = delta(data[0], data[2]), + delta12 = delta(data[1], data[2]); + + if (delta01 > delta02 || delta01 > delta12) + data[delta02 > delta12 ? 0 : 1] = data[2]; + + return (data[0] + data[1]) >> 1; +} + +uint16_t XPT2046::IO(uint16_t data) { + return TERN(TOUCH_BUTTONS_HW_SPI, HardwareIO, SoftwareIO)(data); +} + +extern uint8_t spiTransfer(uint8_t b); + +#if ENABLED(TOUCH_BUTTONS_HW_SPI) + uint16_t XPT2046::HardwareIO(uint16_t data) { + return SPIx.transfer(data & 0xFF); + } +#endif + +uint16_t XPT2046::SoftwareIO(uint16_t data) { + uint16_t result = 0; + + for (uint8_t j = 0x80; j; j >>= 1) { + WRITE(TOUCH_SCK_PIN, LOW); + WRITE(TOUCH_MOSI_PIN, data & j ? HIGH : LOW); + if (READ(TOUCH_MISO_PIN)) result |= j; + WRITE(TOUCH_SCK_PIN, HIGH); + } + WRITE(TOUCH_SCK_PIN, LOW); + + return result; +} + +#endif // HAS_TFT_XPT2046 diff --git a/Marlin/src/HAL/LPC1768/tft/xpt2046.h b/Marlin/src/HAL/LPC1768/tft/xpt2046.h new file mode 100644 index 0000000000..019f75efce --- /dev/null +++ b/Marlin/src/HAL/LPC1768/tft/xpt2046.h @@ -0,0 +1,80 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#pragma once + +#include "../../../inc/MarlinConfig.h" + +#if ENABLED(TOUCH_BUTTONS_HW_SPI) + #include +#endif + +#ifndef TOUCH_MISO_PIN + #define TOUCH_MISO_PIN MISO_PIN +#endif +#ifndef TOUCH_MOSI_PIN + #define TOUCH_MOSI_PIN MOSI_PIN +#endif +#ifndef TOUCH_SCK_PIN + #define TOUCH_SCK_PIN SCK_PIN +#endif +#ifndef TOUCH_CS_PIN + #define TOUCH_CS_PIN CS_PIN +#endif +#ifndef TOUCH_INT_PIN + #define TOUCH_INT_PIN -1 +#endif + +#define XPT2046_DFR_MODE 0x00 +#define XPT2046_SER_MODE 0x04 +#define XPT2046_CONTROL 0x80 + +enum XPTCoordinate : uint8_t { + XPT2046_X = 0x10 | XPT2046_CONTROL | XPT2046_DFR_MODE, + XPT2046_Y = 0x50 | XPT2046_CONTROL | XPT2046_DFR_MODE, + XPT2046_Z1 = 0x30 | XPT2046_CONTROL | XPT2046_DFR_MODE, + XPT2046_Z2 = 0x40 | XPT2046_CONTROL | XPT2046_DFR_MODE, +}; + +#if !defined(XPT2046_Z1_THRESHOLD) + #define XPT2046_Z1_THRESHOLD 10 +#endif + +class XPT2046 { +private: + static bool isBusy() { return false; } + + static uint16_t getRawData(const XPTCoordinate coordinate); + static bool isTouched(); + + static inline void DataTransferBegin() { WRITE(TOUCH_CS_PIN, LOW); }; + static inline void DataTransferEnd() { WRITE(TOUCH_CS_PIN, HIGH); }; + #if ENABLED(TOUCH_BUTTONS_HW_SPI) + static uint16_t HardwareIO(uint16_t data); + #endif + static uint16_t SoftwareIO(uint16_t data); + static uint16_t IO(uint16_t data = 0); + +public: + #if ENABLED(TOUCH_BUTTONS_HW_SPI) + static SPIClass SPIx; + #endif + + static void Init(); + static bool getRawPoint(int16_t *x, int16_t *y); +}; diff --git a/Marlin/src/lcd/dogm/HAL_LCD_com_defines.h b/Marlin/src/lcd/dogm/HAL_LCD_com_defines.h index 455fc2388e..da7d4f6f1e 100644 --- a/Marlin/src/lcd/dogm/HAL_LCD_com_defines.h +++ b/Marlin/src/lcd/dogm/HAL_LCD_com_defines.h @@ -82,11 +82,6 @@ #define U8G_COM_SSD_I2C_HAL u8g_com_arduino_ssd_i2c_fn - #if EITHER(FSMC_GRAPHICAL_TFT, SPI_GRAPHICAL_TFT) - uint8_t u8g_com_stm32duino_tft_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); - #define U8G_COM_HAL_TFT_FN u8g_com_stm32duino_tft_fn - #endif - #elif defined(TARGET_LPC1768) uint8_t u8g_com_HAL_LPC1768_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); @@ -117,6 +112,9 @@ #ifndef U8G_COM_SSD_I2C_HAL #define U8G_COM_SSD_I2C_HAL u8g_com_null_fn #endif -#ifndef U8G_COM_HAL_TFT_FN +#if EITHER(FSMC_GRAPHICAL_TFT, SPI_GRAPHICAL_TFT) + uint8_t u8g_com_hal_tft_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); + #define U8G_COM_HAL_TFT_FN u8g_com_hal_tft_fn +#else #define U8G_COM_HAL_TFT_FN u8g_com_null_fn #endif diff --git a/Marlin/src/lcd/dogm/u8g_dev_tft_320x240_upscale_from_128x64.cpp b/Marlin/src/lcd/dogm/u8g_dev_tft_320x240_upscale_from_128x64.cpp index b8bfdb293a..39c294defe 100644 --- a/Marlin/src/lcd/dogm/u8g_dev_tft_320x240_upscale_from_128x64.cpp +++ b/Marlin/src/lcd/dogm/u8g_dev_tft_320x240_upscale_from_128x64.cpp @@ -760,7 +760,7 @@ uint8_t u8g_dev_tft_320x240_upscale_from_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, u static uint8_t msgInitCount = 2; // Ignore all messages until 2nd U8G_COM_MSG_INIT -uint8_t u8g_com_stm32duino_tft_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) { +uint8_t u8g_com_hal_tft_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) { if (msgInitCount) { if (msg == U8G_COM_MSG_INIT) msgInitCount--; if (msgInitCount) return -1; @@ -801,7 +801,7 @@ uint8_t u8g_com_stm32duino_tft_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void break; case U8G_COM_MSG_WRITE_SEQ: - tftio.DataTransferBegin(DATASIZE_8BIT); + tftio.DataTransferBegin(DATASIZE_16BIT); for (uint8_t i = 0; i < arg_val; i += 2) tftio.WriteData(*(uint16_t *)(((uint32_t)arg_ptr) + i)); tftio.DataTransferEnd(); diff --git a/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_4.h b/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_4.h index 8fbf382be7..b32f99c68d 100644 --- a/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_4.h +++ b/Marlin/src/pins/lpc1768/pins_BTT_SKR_V1_4.h @@ -274,6 +274,38 @@ #define FORCE_SOFT_SPI #define LCD_BACKLIGHT_PIN -1 + #elif HAS_SPI_TFT // Config for Classic UI (emulated DOGM) and Color UI + #define SS_PIN -1 + //#define ONBOARD_SD_CS_PIN -1 + + #define TFT_CS_PIN P1_22 + #define TFT_A0_PIN P1_23 + #define TFT_DC_PIN P1_23 + #define TFT_MISO_PIN P0_17 + #define TFT_BACKLIGHT_PIN P1_18 + #define TFT_RESET_PIN P1_19 + + #define LPC_HW_SPI_DEV 0 + #define LCD_USE_DMA_SPI + + #define TOUCH_INT_PIN P1_21 + #define TOUCH_CS_PIN P1_20 + #define TOUCH_BUTTONS_HW_SPI + #define TOUCH_BUTTONS_HW_SPI_DEVICE 1 + + #ifndef GRAPHICAL_TFT_UPSCALE + #define GRAPHICAL_TFT_UPSCALE 3 + #endif + + // SPI 1 + #define SCK_PIN P0_15 + #define MISO_PIN P0_17 + #define MOSI_PIN P0_18 + + // Disable any LCD related PINs config + #define LCD_PINS_ENABLE -1 + #define LCD_PINS_RS -1 + #else #define BTN_ENC P0_28 // (58) open-drain diff --git a/Marlin/src/pins/pinsDebug_list.h b/Marlin/src/pins/pinsDebug_list.h index 3307cbde97..ac15a3c9ba 100644 --- a/Marlin/src/pins/pinsDebug_list.h +++ b/Marlin/src/pins/pinsDebug_list.h @@ -1421,3 +1421,22 @@ #if PIN_EXISTS(ESP_WIFI_MODULE_GPIO2) REPORT_NAME_DIGITAL(__LINE__, ESP_WIFI_MODULE_GPIO2_PIN) #endif +// TFT PINS +#if PIN_EXISTS(TFT_CS) + REPORT_NAME_DIGITAL(__LINE__, TFT_CS_PIN) +#endif +#if PIN_EXISTS(TFT_A0) + REPORT_NAME_DIGITAL(__LINE__, TFT_A0_PIN) +#endif +#if PIN_EXISTS(TFT_DC) + REPORT_NAME_DIGITAL(__LINE__, TFT_DC_PIN) +#endif +#if PIN_EXISTS(TFT_MISO) + REPORT_NAME_DIGITAL(__LINE__, TFT_MISO_PIN) +#endif +#if PIN_EXISTS(TFT_BACKLIGHT) + REPORT_NAME_DIGITAL(__LINE__, TFT_BACKLIGHT_PIN) +#endif +#if PIN_EXISTS(TFT_RESET) + REPORT_NAME_DIGITAL(__LINE__, TFT_RESET_PIN) +#endif