From e8701195e66f2d27ffe17fb514eae8173795aaf7 Mon Sep 17 00:00:00 2001
From: Georgiy Bondarenko <69736697+nehilo@users.noreply.github.com>
Date: Thu, 4 Mar 2021 22:54:23 +0500
Subject: Initial commit
---
Marlin/src/HAL/STM32/tft/tft_fsmc.cpp | 181 ++++++++++++++++++++++++++
Marlin/src/HAL/STM32/tft/tft_fsmc.h | 171 +++++++++++++++++++++++++
Marlin/src/HAL/STM32/tft/tft_spi.cpp | 235 ++++++++++++++++++++++++++++++++++
Marlin/src/HAL/STM32/tft/tft_spi.h | 74 +++++++++++
Marlin/src/HAL/STM32/tft/xpt2046.cpp | 170 ++++++++++++++++++++++++
Marlin/src/HAL/STM32/tft/xpt2046.h | 81 ++++++++++++
6 files changed, 912 insertions(+)
create mode 100644 Marlin/src/HAL/STM32/tft/tft_fsmc.cpp
create mode 100644 Marlin/src/HAL/STM32/tft/tft_fsmc.h
create mode 100644 Marlin/src/HAL/STM32/tft/tft_spi.cpp
create mode 100644 Marlin/src/HAL/STM32/tft/tft_spi.h
create mode 100644 Marlin/src/HAL/STM32/tft/xpt2046.cpp
create mode 100644 Marlin/src/HAL/STM32/tft/xpt2046.h
(limited to 'Marlin/src/HAL/STM32/tft')
diff --git a/Marlin/src/HAL/STM32/tft/tft_fsmc.cpp b/Marlin/src/HAL/STM32/tft/tft_fsmc.cpp
new file mode 100644
index 0000000..87ca2db
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/tft_fsmc.cpp
@@ -0,0 +1,181 @@
+/**
+ * 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 .
+ *
+ */
+#if defined(ARDUINO_ARCH_STM32) && !defined(STM32GENERIC)
+
+#include "../../../inc/MarlinConfig.h"
+
+#if HAS_FSMC_TFT
+
+#include "tft_fsmc.h"
+#include "pinconfig.h"
+
+SRAM_HandleTypeDef TFT_FSMC::SRAMx;
+DMA_HandleTypeDef TFT_FSMC::DMAtx;
+LCD_CONTROLLER_TypeDef *TFT_FSMC::LCD;
+
+void TFT_FSMC::Init() {
+ uint32_t controllerAddress;
+
+ #if PIN_EXISTS(TFT_RESET)
+ OUT_WRITE(TFT_RESET_PIN, HIGH);
+ HAL_Delay(100);
+ #endif
+
+ #if PIN_EXISTS(TFT_BACKLIGHT)
+ OUT_WRITE(TFT_BACKLIGHT_PIN, HIGH);
+ #endif
+
+ FSMC_NORSRAM_TimingTypeDef Timing, ExtTiming;
+
+ uint32_t NSBank = (uint32_t)pinmap_peripheral(digitalPinToPinName(TFT_CS_PIN), PinMap_FSMC_CS);
+
+ // Perform the SRAM1 memory initialization sequence
+ SRAMx.Instance = FSMC_NORSRAM_DEVICE;
+ SRAMx.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
+ // SRAMx.Init
+ SRAMx.Init.NSBank = NSBank;
+ SRAMx.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
+ SRAMx.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
+ SRAMx.Init.MemoryDataWidth = TERN(TFT_INTERFACE_FSMC_8BIT, FSMC_NORSRAM_MEM_BUS_WIDTH_8, FSMC_NORSRAM_MEM_BUS_WIDTH_16);
+ SRAMx.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
+ SRAMx.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
+ SRAMx.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
+ SRAMx.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
+ SRAMx.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
+ SRAMx.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
+ SRAMx.Init.ExtendedMode = FSMC_EXTENDED_MODE_ENABLE;
+ SRAMx.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
+ SRAMx.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
+ #ifdef STM32F4xx
+ SRAMx.Init.PageSize = FSMC_PAGE_SIZE_NONE;
+ #endif
+ // Read Timing - relatively slow to ensure ID information is correctly read from TFT controller
+ // Can be decreases from 15-15-24 to 4-4-8 with risk of stability loss
+ Timing.AddressSetupTime = 15;
+ Timing.AddressHoldTime = 15;
+ Timing.DataSetupTime = 24;
+ Timing.BusTurnAroundDuration = 0;
+ Timing.CLKDivision = 16;
+ Timing.DataLatency = 17;
+ Timing.AccessMode = FSMC_ACCESS_MODE_A;
+ // Write Timing
+ // Can be decreases from 8-15-8 to 0-0-1 with risk of stability loss
+ ExtTiming.AddressSetupTime = 8;
+ ExtTiming.AddressHoldTime = 15;
+ ExtTiming.DataSetupTime = 8;
+ ExtTiming.BusTurnAroundDuration = 0;
+ ExtTiming.CLKDivision = 16;
+ ExtTiming.DataLatency = 17;
+ ExtTiming.AccessMode = FSMC_ACCESS_MODE_A;
+
+ __HAL_RCC_FSMC_CLK_ENABLE();
+
+ for (uint16_t i = 0; PinMap_FSMC[i].pin != NC; i++)
+ pinmap_pinout(PinMap_FSMC[i].pin, PinMap_FSMC);
+ pinmap_pinout(digitalPinToPinName(TFT_CS_PIN), PinMap_FSMC_CS);
+ pinmap_pinout(digitalPinToPinName(TFT_RS_PIN), PinMap_FSMC_RS);
+
+ controllerAddress = FSMC_BANK1_1;
+ #ifdef PF0
+ switch (NSBank) {
+ case FSMC_NORSRAM_BANK2: controllerAddress = FSMC_BANK1_2 ; break;
+ case FSMC_NORSRAM_BANK3: controllerAddress = FSMC_BANK1_3 ; break;
+ case FSMC_NORSRAM_BANK4: controllerAddress = FSMC_BANK1_4 ; break;
+ }
+ #endif
+
+ controllerAddress |= (uint32_t)pinmap_peripheral(digitalPinToPinName(TFT_RS_PIN), PinMap_FSMC_RS);
+
+ HAL_SRAM_Init(&SRAMx, &Timing, &ExtTiming);
+
+ __HAL_RCC_DMA2_CLK_ENABLE();
+
+ #ifdef STM32F1xx
+ DMAtx.Instance = DMA2_Channel1;
+ #elif defined(STM32F4xx)
+ DMAtx.Instance = DMA2_Stream0;
+ DMAtx.Init.Channel = DMA_CHANNEL_0;
+ DMAtx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
+ DMAtx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
+ DMAtx.Init.MemBurst = DMA_MBURST_SINGLE;
+ DMAtx.Init.PeriphBurst = DMA_PBURST_SINGLE;
+ #endif
+
+ DMAtx.Init.Direction = DMA_MEMORY_TO_MEMORY;
+ DMAtx.Init.MemInc = DMA_MINC_DISABLE;
+ DMAtx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
+ DMAtx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
+ DMAtx.Init.Mode = DMA_NORMAL;
+ DMAtx.Init.Priority = DMA_PRIORITY_HIGH;
+
+ LCD = (LCD_CONTROLLER_TypeDef *)controllerAddress;
+}
+
+uint32_t TFT_FSMC::GetID() {
+ uint32_t id;
+ WriteReg(0);
+ id = LCD->RAM;
+
+ if (id == 0)
+ id = ReadID(LCD_READ_ID);
+ if ((id & 0xFFFF) == 0 || (id & 0xFFFF) == 0xFFFF)
+ id = ReadID(LCD_READ_ID4);
+ return id;
+}
+
+uint32_t TFT_FSMC::ReadID(tft_data_t Reg) {
+ uint32_t id;
+ WriteReg(Reg);
+ id = LCD->RAM; // dummy read
+ id = Reg << 24;
+ id |= (LCD->RAM & 0x00FF) << 16;
+ id |= (LCD->RAM & 0x00FF) << 8;
+ id |= LCD->RAM & 0x00FF;
+ return id;
+}
+
+bool TFT_FSMC::isBusy() {
+ #if defined(STM32F1xx)
+ volatile bool dmaEnabled = (DMAtx.Instance->CCR & DMA_CCR_EN) != RESET;
+ #elif defined(STM32F4xx)
+ volatile bool dmaEnabled = DMAtx.Instance->CR & DMA_SxCR_EN;
+ #endif
+ if (dmaEnabled) {
+ if (__HAL_DMA_GET_FLAG(&DMAtx, __HAL_DMA_GET_TC_FLAG_INDEX(&DMAtx)) != 0 || __HAL_DMA_GET_FLAG(&DMAtx, __HAL_DMA_GET_TE_FLAG_INDEX(&DMAtx)) != 0)
+ Abort();
+ }
+ else
+ Abort();
+ return dmaEnabled;
+}
+
+void TFT_FSMC::TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) {
+ DMAtx.Init.PeriphInc = MemoryIncrease;
+ HAL_DMA_Init(&DMAtx);
+ DataTransferBegin();
+ HAL_DMA_Start(&DMAtx, (uint32_t)Data, (uint32_t)&(LCD->RAM), Count);
+ HAL_DMA_PollForTransfer(&DMAtx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
+ Abort();
+}
+
+#endif // HAS_FSMC_TFT
+#endif // ARDUINO_ARCH_STM32 && !STM32GENERIC
diff --git a/Marlin/src/HAL/STM32/tft/tft_fsmc.h b/Marlin/src/HAL/STM32/tft/tft_fsmc.h
new file mode 100644
index 0000000..2200aba
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/tft_fsmc.h
@@ -0,0 +1,171 @@
+/**
+ * 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"
+
+#ifdef STM32F1xx
+ #include "stm32f1xx_hal.h"
+#elif defined(STM32F4xx)
+ #include "stm32f4xx_hal.h"
+#else
+ #error "FSMC TFT is currently only supported on STM32F1 and STM32F4 hardware."
+#endif
+
+#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 SPI_DATASIZE_8BIT
+#define DATASIZE_16BIT SPI_DATASIZE_16BIT
+#define TFT_IO_DRIVER TFT_FSMC
+
+#define TFT_DATASIZE TERN(TFT_INTERFACE_FSMC_8BIT, DATASIZE_8BIT, DATASIZE_16BIT)
+typedef TERN(TFT_INTERFACE_FSMC_8BIT, uint8_t, uint16_t) tft_data_t;
+
+typedef struct {
+ __IO tft_data_t REG;
+ __IO tft_data_t RAM;
+} LCD_CONTROLLER_TypeDef;
+
+class TFT_FSMC {
+ private:
+ static SRAM_HandleTypeDef SRAMx;
+ static DMA_HandleTypeDef DMAtx;
+
+ static LCD_CONTROLLER_TypeDef *LCD;
+
+ static uint32_t ReadID(tft_data_t Reg);
+ static void Transmit(tft_data_t Data) { LCD->RAM = Data; __DSB(); }
+ static void TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count);
+
+ public:
+ static void Init();
+ static uint32_t GetID();
+ static bool isBusy();
+ static void Abort() { __HAL_DMA_DISABLE(&DMAtx); }
+
+ static void DataTransferBegin(uint16_t DataWidth = TFT_DATASIZE) {}
+ static void DataTransferEnd() {};
+
+ static void WriteData(uint16_t Data) { Transmit(tft_data_t(Data)); }
+ static void WriteReg(uint16_t Reg) { LCD->REG = tft_data_t(Reg); __DSB(); }
+
+ static void WriteSequence(uint16_t *Data, uint16_t Count) { TransmitDMA(DMA_PINC_ENABLE, Data, Count); }
+ static void WriteMultiple(uint16_t Color, uint16_t Count) { static uint16_t Data; Data = Color; TransmitDMA(DMA_PINC_DISABLE, &Data, Count); }
+ static void WriteMultiple(uint16_t Color, uint32_t Count) {
+ static uint16_t Data; Data = Color;
+ while (Count > 0) {
+ TransmitDMA(DMA_MINC_DISABLE, &Data, Count > 0xFFFF ? 0xFFFF : Count);
+ Count = Count > 0xFFFF ? Count - 0xFFFF : 0;
+ }
+ }
+};
+
+#ifdef STM32F1xx
+ #define FSMC_PIN_DATA STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, AFIO_NONE)
+#elif defined(STM32F4xx)
+ #define FSMC_PIN_DATA STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF12_FSMC)
+ #define FSMC_BANK1_1 0x60000000U
+ #define FSMC_BANK1_2 0x64000000U
+ #define FSMC_BANK1_3 0x68000000U
+ #define FSMC_BANK1_4 0x6C000000U
+#else
+ #error No configuration for this MCU
+#endif
+
+const PinMap PinMap_FSMC[] = {
+ {PD_14, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D00
+ {PD_15, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D01
+ {PD_0, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D02
+ {PD_1, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D03
+ {PE_7, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D04
+ {PE_8, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D05
+ {PE_9, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D06
+ {PE_10, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D07
+ #if DISABLED(TFT_INTERFACE_FSMC_8BIT)
+ {PE_11, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D08
+ {PE_12, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D09
+ {PE_13, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D10
+ {PE_14, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D11
+ {PE_15, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D12
+ {PD_8, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D13
+ {PD_9, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D14
+ {PD_10, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_D15
+ #endif
+ {PD_4, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_NOE
+ {PD_5, FSMC_NORSRAM_DEVICE, FSMC_PIN_DATA}, // FSMC_NWE
+ {NC, NP, 0}
+};
+
+const PinMap PinMap_FSMC_CS[] = {
+ {PD_7, (void *)FSMC_NORSRAM_BANK1, FSMC_PIN_DATA}, // FSMC_NE1
+ #ifdef PF0
+ {PG_9, (void *)FSMC_NORSRAM_BANK2, FSMC_PIN_DATA}, // FSMC_NE2
+ {PG_10, (void *)FSMC_NORSRAM_BANK3, FSMC_PIN_DATA}, // FSMC_NE3
+ {PG_12, (void *)FSMC_NORSRAM_BANK4, FSMC_PIN_DATA}, // FSMC_NE4
+ #endif
+ {NC, NP, 0}
+};
+
+#if ENABLED(TFT_INTERFACE_FSMC_8BIT)
+ #define FSMC_RS(A) (void *)((2 << (A-1)) - 1)
+#else
+ #define FSMC_RS(A) (void *)((2 << A) - 2)
+#endif
+
+const PinMap PinMap_FSMC_RS[] = {
+ #ifdef PF0
+ {PF_0, FSMC_RS( 0), FSMC_PIN_DATA}, // FSMC_A0
+ {PF_1, FSMC_RS( 1), FSMC_PIN_DATA}, // FSMC_A1
+ {PF_2, FSMC_RS( 2), FSMC_PIN_DATA}, // FSMC_A2
+ {PF_3, FSMC_RS( 3), FSMC_PIN_DATA}, // FSMC_A3
+ {PF_4, FSMC_RS( 4), FSMC_PIN_DATA}, // FSMC_A4
+ {PF_5, FSMC_RS( 5), FSMC_PIN_DATA}, // FSMC_A5
+ {PF_12, FSMC_RS( 6), FSMC_PIN_DATA}, // FSMC_A6
+ {PF_13, FSMC_RS( 7), FSMC_PIN_DATA}, // FSMC_A7
+ {PF_14, FSMC_RS( 8), FSMC_PIN_DATA}, // FSMC_A8
+ {PF_15, FSMC_RS( 9), FSMC_PIN_DATA}, // FSMC_A9
+ {PG_0, FSMC_RS(10), FSMC_PIN_DATA}, // FSMC_A10
+ {PG_1, FSMC_RS(11), FSMC_PIN_DATA}, // FSMC_A11
+ {PG_2, FSMC_RS(12), FSMC_PIN_DATA}, // FSMC_A12
+ {PG_3, FSMC_RS(13), FSMC_PIN_DATA}, // FSMC_A13
+ {PG_4, FSMC_RS(14), FSMC_PIN_DATA}, // FSMC_A14
+ {PG_5, FSMC_RS(15), FSMC_PIN_DATA}, // FSMC_A15
+ #endif
+ {PD_11, FSMC_RS(16), FSMC_PIN_DATA}, // FSMC_A16
+ {PD_12, FSMC_RS(17), FSMC_PIN_DATA}, // FSMC_A17
+ {PD_13, FSMC_RS(18), FSMC_PIN_DATA}, // FSMC_A18
+ {PE_3, FSMC_RS(19), FSMC_PIN_DATA}, // FSMC_A19
+ {PE_4, FSMC_RS(20), FSMC_PIN_DATA}, // FSMC_A20
+ {PE_5, FSMC_RS(21), FSMC_PIN_DATA}, // FSMC_A21
+ {PE_6, FSMC_RS(22), FSMC_PIN_DATA}, // FSMC_A22
+ {PE_2, FSMC_RS(23), FSMC_PIN_DATA}, // FSMC_A23
+ #ifdef PF0
+ {PG_13, FSMC_RS(24), FSMC_PIN_DATA}, // FSMC_A24
+ {PG_14, FSMC_RS(25), FSMC_PIN_DATA}, // FSMC_A25
+ #endif
+ {NC, NP, 0}
+};
diff --git a/Marlin/src/HAL/STM32/tft/tft_spi.cpp b/Marlin/src/HAL/STM32/tft/tft_spi.cpp
new file mode 100644
index 0000000..3cb797d
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/tft_spi.cpp
@@ -0,0 +1,235 @@
+/**
+ * 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 .
+ *
+ */
+#if defined(ARDUINO_ARCH_STM32) && !defined(STM32GENERIC)
+
+#include "../../../inc/MarlinConfig.h"
+
+#if HAS_SPI_TFT
+
+#include "tft_spi.h"
+#include "pinconfig.h"
+
+SPI_HandleTypeDef TFT_SPI::SPIx;
+DMA_HandleTypeDef TFT_SPI::DMAtx;
+
+void TFT_SPI::Init() {
+ SPI_TypeDef *spiInstance;
+
+ OUT_WRITE(TFT_A0_PIN, HIGH);
+ OUT_WRITE(TFT_CS_PIN, HIGH);
+
+ if ((spiInstance = (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TFT_SCK_PIN), PinMap_SPI_SCLK)) == NP) return;
+ if (spiInstance != (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TFT_MOSI_PIN), PinMap_SPI_MOSI)) return;
+
+ #if PIN_EXISTS(TFT_MISO) && TFT_MISO_PIN != TFT_MOSI_PIN
+ if (spiInstance != (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TFT_MISO_PIN), PinMap_SPI_MISO)) return;
+ #endif
+
+ SPIx.Instance = spiInstance;
+ SPIx.State = HAL_SPI_STATE_RESET;
+ SPIx.Init.NSS = SPI_NSS_SOFT;
+ SPIx.Init.Mode = SPI_MODE_MASTER;
+ SPIx.Init.Direction = (TFT_MISO_PIN == TFT_MOSI_PIN) ? SPI_DIRECTION_1LINE : SPI_DIRECTION_2LINES;
+ SPIx.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
+ SPIx.Init.CLKPhase = SPI_PHASE_1EDGE;
+ SPIx.Init.CLKPolarity = SPI_POLARITY_LOW;
+ SPIx.Init.DataSize = SPI_DATASIZE_8BIT;
+ SPIx.Init.FirstBit = SPI_FIRSTBIT_MSB;
+ SPIx.Init.TIMode = SPI_TIMODE_DISABLE;
+ SPIx.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
+ SPIx.Init.CRCPolynomial = 10;
+
+ pinmap_pinout(digitalPinToPinName(TFT_SCK_PIN), PinMap_SPI_SCLK);
+ pinmap_pinout(digitalPinToPinName(TFT_MOSI_PIN), PinMap_SPI_MOSI);
+ #if PIN_EXISTS(TFT_MISO) && TFT_MISO_PIN != TFT_MOSI_PIN
+ pinmap_pinout(digitalPinToPinName(TFT_MISO_PIN), PinMap_SPI_MISO);
+ #endif
+ pin_PullConfig(get_GPIO_Port(STM_PORT(digitalPinToPinName(TFT_SCK_PIN))), STM_LL_GPIO_PIN(digitalPinToPinName(TFT_SCK_PIN)), GPIO_PULLDOWN);
+
+ #ifdef SPI1_BASE
+ if (SPIx.Instance == SPI1) {
+ __HAL_RCC_SPI1_CLK_ENABLE();
+ #ifdef STM32F1xx
+ __HAL_RCC_DMA1_CLK_ENABLE();
+ DMAtx.Instance = DMA1_Channel3;
+ #elif defined(STM32F4xx)
+ __HAL_RCC_DMA2_CLK_ENABLE();
+ DMAtx.Instance = DMA2_Stream3;
+ DMAtx.Init.Channel = DMA_CHANNEL_3;
+ #endif
+ SPIx.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
+ }
+ #endif
+ #ifdef SPI2_BASE
+ if (SPIx.Instance == SPI2) {
+ __HAL_RCC_SPI2_CLK_ENABLE();
+ #ifdef STM32F1xx
+ __HAL_RCC_DMA1_CLK_ENABLE();
+ DMAtx.Instance = DMA1_Channel5;
+ #elif defined(STM32F4xx)
+ __HAL_RCC_DMA1_CLK_ENABLE();
+ DMAtx.Instance = DMA1_Stream4;
+ DMAtx.Init.Channel = DMA_CHANNEL_0;
+ #endif
+ }
+ #endif
+ #ifdef SPI3_BASE
+ if (SPIx.Instance == SPI3) {
+ __HAL_RCC_SPI3_CLK_ENABLE();
+ #ifdef STM32F1xx
+ __HAL_RCC_DMA2_CLK_ENABLE();
+ DMAtx.Instance = DMA2_Channel2;
+ #elif defined(STM32F4xx)
+ __HAL_RCC_DMA1_CLK_ENABLE();
+ DMAtx.Instance = DMA1_Stream5;
+ DMAtx.Init.Channel = DMA_CHANNEL_0;
+ #endif
+ }
+ #endif
+
+ HAL_SPI_Init(&SPIx);
+
+ DMAtx.Init.Direction = DMA_MEMORY_TO_PERIPH;
+ DMAtx.Init.PeriphInc = DMA_PINC_DISABLE;
+ DMAtx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
+ DMAtx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
+ DMAtx.Init.Mode = DMA_NORMAL;
+ DMAtx.Init.Priority = DMA_PRIORITY_LOW;
+ #ifdef STM32F4xx
+ DMAtx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
+ #endif
+}
+
+void TFT_SPI::DataTransferBegin(uint16_t DataSize) {
+ SPIx.Init.DataSize = DataSize == DATASIZE_8BIT ? SPI_DATASIZE_8BIT : SPI_DATASIZE_16BIT;
+ HAL_SPI_Init(&SPIx);
+ WRITE(TFT_CS_PIN, LOW);
+}
+
+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)
+ uint32_t BaudRatePrescaler = SPIx.Init.BaudRatePrescaler;
+ uint32_t i;
+
+ SPIx.Init.BaudRatePrescaler = SPIx.Instance == SPI1 ? SPI_BAUDRATEPRESCALER_8 : SPI_BAUDRATEPRESCALER_4;
+ DataTransferBegin(DATASIZE_8BIT);
+ WriteReg(Reg);
+
+ if (SPIx.Init.Direction == SPI_DIRECTION_1LINE) SPI_1LINE_RX(&SPIx);
+ __HAL_SPI_ENABLE(&SPIx);
+
+ for (i = 0; i < 4; i++) {
+ #if TFT_MISO_PIN != TFT_MOSI_PIN
+ //if (hspi->Init.Direction == SPI_DIRECTION_2LINES) {
+ while ((SPIx.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE) {}
+ SPIx.Instance->DR = 0;
+ //}
+ #endif
+ while ((SPIx.Instance->SR & SPI_FLAG_RXNE) != SPI_FLAG_RXNE) {}
+ Data = (Data << 8) | SPIx.Instance->DR;
+ }
+
+ __HAL_SPI_DISABLE(&SPIx);
+ DataTransferEnd();
+
+ SPIx.Init.BaudRatePrescaler = BaudRatePrescaler;
+ #endif
+
+ return Data >> 7;
+}
+
+bool TFT_SPI::isBusy() {
+ #if defined(STM32F1xx)
+ volatile bool dmaEnabled = (DMAtx.Instance->CCR & DMA_CCR_EN) != RESET;
+ #elif defined(STM32F4xx)
+ volatile bool dmaEnabled = DMAtx.Instance->CR & DMA_SxCR_EN;
+ #endif
+ if (dmaEnabled) {
+ if (__HAL_DMA_GET_FLAG(&DMAtx, __HAL_DMA_GET_TC_FLAG_INDEX(&DMAtx)) != 0 || __HAL_DMA_GET_FLAG(&DMAtx, __HAL_DMA_GET_TE_FLAG_INDEX(&DMAtx)) != 0)
+ Abort();
+ }
+ else
+ Abort();
+ return dmaEnabled;
+}
+
+void TFT_SPI::Abort() {
+ // Wait for any running spi
+ while ((SPIx.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE) {}
+ while ((SPIx.Instance->SR & SPI_FLAG_BSY) == SPI_FLAG_BSY) {}
+ // First, abort any running dma
+ HAL_DMA_Abort(&DMAtx);
+ // DeInit objects
+ HAL_DMA_DeInit(&DMAtx);
+ HAL_SPI_DeInit(&SPIx);
+ // Deselect CS
+ DataTransferEnd();
+}
+
+void TFT_SPI::Transmit(uint16_t Data) {
+ if (TFT_MISO_PIN == TFT_MOSI_PIN)
+ SPI_1LINE_TX(&SPIx);
+
+ __HAL_SPI_ENABLE(&SPIx);
+
+ SPIx.Instance->DR = Data;
+
+ while ((SPIx.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE) {}
+ while ((SPIx.Instance->SR & SPI_FLAG_BSY) == SPI_FLAG_BSY) {}
+
+ if (TFT_MISO_PIN != TFT_MOSI_PIN)
+ __HAL_SPI_CLEAR_OVRFLAG(&SPIx); /* Clear overrun flag in 2 Lines communication mode because received is not read */
+}
+
+void TFT_SPI::TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) {
+ // Wait last dma finish, to start another
+ while(isBusy()) { }
+
+ DMAtx.Init.MemInc = MemoryIncrease;
+ HAL_DMA_Init(&DMAtx);
+
+ if (TFT_MISO_PIN == TFT_MOSI_PIN)
+ SPI_1LINE_TX(&SPIx);
+
+ DataTransferBegin();
+
+ HAL_DMA_Start(&DMAtx, (uint32_t)Data, (uint32_t)&(SPIx.Instance->DR), Count);
+ __HAL_SPI_ENABLE(&SPIx);
+
+ SET_BIT(SPIx.Instance->CR2, SPI_CR2_TXDMAEN); /* Enable Tx DMA Request */
+
+ HAL_DMA_PollForTransfer(&DMAtx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
+ Abort();
+}
+
+#endif // HAS_SPI_TFT
+#endif // ARDUINO_ARCH_STM32 && !STM32GENERIC
diff --git a/Marlin/src/HAL/STM32/tft/tft_spi.h b/Marlin/src/HAL/STM32/tft/tft_spi.h
new file mode 100644
index 0000000..667b5f3
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/tft_spi.h
@@ -0,0 +1,74 @@
+/**
+ * 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
+
+#ifdef STM32F1xx
+ #include "stm32f1xx_hal.h"
+#elif defined(STM32F4xx)
+ #include "stm32f4xx_hal.h"
+#else
+ #error SPI TFT is currently only supported on STM32F1 and STM32F4 hardware.
+#endif
+
+#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 SPI_DATASIZE_8BIT
+#define DATASIZE_16BIT SPI_DATASIZE_16BIT
+#define TFT_IO_DRIVER TFT_SPI
+
+class TFT_SPI {
+private:
+ static SPI_HandleTypeDef SPIx;
+ static DMA_HandleTypeDef DMAtx;
+
+ 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 void Init();
+ static uint32_t GetID();
+ static bool isBusy();
+ static void Abort();
+
+ static void DataTransferBegin(uint16_t DataWidth = DATASIZE_16BIT);
+ static void DataTransferEnd() { WRITE(TFT_CS_PIN, HIGH); };
+ static void DataTransferAbort();
+
+ static void WriteData(uint16_t Data) { Transmit(Data); }
+ static void WriteReg(uint16_t Reg) { WRITE(TFT_A0_PIN, LOW); Transmit(Reg); 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;
+ while (Count > 0) {
+ TransmitDMA(DMA_MINC_DISABLE, &Data, Count > 0xFFFF ? 0xFFFF : Count);
+ Count = Count > 0xFFFF ? Count - 0xFFFF : 0;
+ }
+ }
+};
diff --git a/Marlin/src/HAL/STM32/tft/xpt2046.cpp b/Marlin/src/HAL/STM32/tft/xpt2046.cpp
new file mode 100644
index 0000000..04294e6
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/xpt2046.cpp
@@ -0,0 +1,170 @@
+/**
+ * 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 .
+ *
+ */
+#if defined(ARDUINO_ARCH_STM32) && !defined(STM32GENERIC)
+
+#include "../../../inc/MarlinConfig.h"
+
+#if HAS_TFT_XPT2046 || HAS_TOUCH_BUTTONS
+
+#include "xpt2046.h"
+#include "pinconfig.h"
+
+uint16_t delta(uint16_t a, uint16_t b) { return a > b ? a - b : b - a; }
+
+SPI_HandleTypeDef XPT2046::SPIx;
+
+void XPT2046::Init() {
+ SPI_TypeDef *spiInstance;
+
+ OUT_WRITE(TOUCH_CS_PIN, HIGH);
+
+ #if PIN_EXISTS(TOUCH_INT)
+ // Optional Pendrive interrupt pin
+ SET_INPUT(TOUCH_INT_PIN);
+ #endif
+
+ spiInstance = (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TOUCH_SCK_PIN), PinMap_SPI_SCLK);
+ if (spiInstance != (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TOUCH_MOSI_PIN), PinMap_SPI_MOSI)) spiInstance = NP;
+ if (spiInstance != (SPI_TypeDef *)pinmap_peripheral(digitalPinToPinName(TOUCH_MISO_PIN), PinMap_SPI_MISO)) spiInstance = NP;
+
+ SPIx.Instance = spiInstance;
+
+ if (SPIx.Instance) {
+ SPIx.State = HAL_SPI_STATE_RESET;
+ SPIx.Init.NSS = SPI_NSS_SOFT;
+ SPIx.Init.Mode = SPI_MODE_MASTER;
+ SPIx.Init.Direction = SPI_DIRECTION_2LINES;
+ SPIx.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
+ SPIx.Init.CLKPhase = SPI_PHASE_2EDGE;
+ SPIx.Init.CLKPolarity = SPI_POLARITY_HIGH;
+ SPIx.Init.DataSize = SPI_DATASIZE_8BIT;
+ SPIx.Init.FirstBit = SPI_FIRSTBIT_MSB;
+ SPIx.Init.TIMode = SPI_TIMODE_DISABLE;
+ SPIx.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
+ SPIx.Init.CRCPolynomial = 10;
+
+ pinmap_pinout(digitalPinToPinName(TOUCH_SCK_PIN), PinMap_SPI_SCLK);
+ pinmap_pinout(digitalPinToPinName(TOUCH_MOSI_PIN), PinMap_SPI_MOSI);
+ pinmap_pinout(digitalPinToPinName(TOUCH_MISO_PIN), PinMap_SPI_MISO);
+
+ #ifdef SPI1_BASE
+ if (SPIx.Instance == SPI1) {
+ __HAL_RCC_SPI1_CLK_ENABLE();
+ SPIx.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
+ }
+ #endif
+ #ifdef SPI2_BASE
+ if (SPIx.Instance == SPI2) {
+ __HAL_RCC_SPI2_CLK_ENABLE();
+ }
+ #endif
+ #ifdef SPI3_BASE
+ if (SPIx.Instance == SPI3) {
+ __HAL_RCC_SPI3_CLK_ENABLE();
+ }
+ #endif
+ }
+ else {
+ SPIx.Instance = nullptr;
+ SET_INPUT(TOUCH_MISO_PIN);
+ SET_OUTPUT(TOUCH_MOSI_PIN);
+ SET_OUTPUT(TOUCH_SCK_PIN);
+ }
+
+ getRawData(XPT2046_Z1);
+}
+
+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);
+ return isTouched();
+}
+
+uint16_t XPT2046::getRawData(const XPTCoordinate coordinate) {
+ uint16_t data[3];
+
+ DataTransferBegin();
+
+ for (uint16_t i = 0; i < 3 ; i++) {
+ IO(coordinate);
+ data[i] = (IO() << 4) | (IO() >> 4);
+ }
+
+ DataTransferEnd();
+
+ uint16_t delta01 = delta(data[0], data[1]);
+ uint16_t delta02 = delta(data[0], data[2]);
+ uint16_t delta12 = delta(data[1], data[2]);
+
+ if (delta01 > delta02 || delta01 > delta12) {
+ if (delta02 > delta12)
+ data[0] = data[2];
+ else
+ data[1] = data[2];
+ }
+
+ return (data[0] + data[1]) >> 1;
+}
+
+uint16_t XPT2046::HardwareIO(uint16_t data) {
+ __HAL_SPI_ENABLE(&SPIx);
+ while ((SPIx.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE) {}
+ SPIx.Instance->DR = data;
+ while ((SPIx.Instance->SR & SPI_FLAG_RXNE) != SPI_FLAG_RXNE) {}
+ __HAL_SPI_DISABLE(&SPIx);
+
+ return SPIx.Instance->DR;
+}
+
+uint16_t XPT2046::SoftwareIO(uint16_t data) {
+ uint16_t result = 0;
+
+ for (uint8_t j = 0x80; j > 0; j >>= 1) {
+ WRITE(TOUCH_SCK_PIN, LOW);
+ __DSB();
+ WRITE(TOUCH_MOSI_PIN, data & j ? HIGH : LOW);
+ __DSB();
+ if (READ(TOUCH_MISO_PIN)) result |= j;
+ __DSB();
+ WRITE(TOUCH_SCK_PIN, HIGH);
+ __DSB();
+ }
+ WRITE(TOUCH_SCK_PIN, LOW);
+ __DSB();
+
+ return result;
+}
+
+#endif // HAS_TFT_XPT2046
+#endif // ARDUINO_ARCH_STM32 && !STM32GENERIC
diff --git a/Marlin/src/HAL/STM32/tft/xpt2046.h b/Marlin/src/HAL/STM32/tft/xpt2046.h
new file mode 100644
index 0000000..5b8acf4
--- /dev/null
+++ b/Marlin/src/HAL/STM32/tft/xpt2046.h
@@ -0,0 +1,81 @@
+/**
+ * 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
+
+#ifdef STM32F1xx
+ #include
+#elif defined(STM32F4xx)
+ #include
+#endif
+
+#include "../../../inc/MarlinConfig.h"
+
+// Not using regular SPI interface by default to avoid SPI mode conflicts with other SPI devices
+
+#if !PIN_EXISTS(TOUCH_MISO)
+ #error "TOUCH_MISO_PIN is not defined."
+#elif !PIN_EXISTS(TOUCH_MOSI)
+ #error "TOUCH_MOSI_PIN is not defined."
+#elif !PIN_EXISTS(TOUCH_SCK)
+ #error "TOUCH_SCK_PIN is not defined."
+#elif !PIN_EXISTS(TOUCH_CS)
+ #error "TOUCH_CS_PIN is not defined."
+#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 SPI_HandleTypeDef SPIx;
+
+ static bool isBusy() { return false; }
+
+ static uint16_t getRawData(const XPTCoordinate coordinate);
+ static bool isTouched();
+
+ static inline void DataTransferBegin() { if (SPIx.Instance) { HAL_SPI_Init(&SPIx); } WRITE(TOUCH_CS_PIN, LOW); };
+ static inline void DataTransferEnd() { WRITE(TOUCH_CS_PIN, HIGH); };
+ static uint16_t HardwareIO(uint16_t data);
+ static uint16_t SoftwareIO(uint16_t data);
+ static uint16_t IO(uint16_t data = 0) { return SPIx.Instance ? HardwareIO(data) : SoftwareIO(data); }
+
+public:
+ static void Init();
+ static bool getRawPoint(int16_t *x, int16_t *y);
+};
--
cgit v1.2.3