388 lines
12 KiB
C++
388 lines
12 KiB
C++
/**
|
|
* Marlin 3D Printer Firmware
|
|
* Copyright (c) 2021 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 <https://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
#if defined(ARDUINO_ARCH_STM32) && !defined(STM32GENERIC) && !defined(MAPLE_STM32F1)
|
|
|
|
#include "../../../inc/MarlinConfig.h"
|
|
|
|
#if HAS_LTDC_TFT
|
|
|
|
#include "tft_ltdc.h"
|
|
#include "pinconfig.h"
|
|
|
|
#define FRAME_BUFFER_ADDRESS 0XC0000000 // SDRAM address
|
|
|
|
#define SDRAM_TIMEOUT ((uint32_t)0xFFFF)
|
|
#define REFRESH_COUNT ((uint32_t)0x02A5) // SDRAM refresh counter
|
|
|
|
#define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000)
|
|
#define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001)
|
|
#define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002)
|
|
#define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004)
|
|
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)
|
|
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008)
|
|
#define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020)
|
|
#define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030)
|
|
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000)
|
|
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
|
|
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)
|
|
|
|
void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_CommandTypeDef *Command) {
|
|
|
|
__IO uint32_t tmpmrd =0;
|
|
/* Step 1: Configure a clock configuration enable command */
|
|
Command->CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
|
|
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
|
|
Command->AutoRefreshNumber = 1;
|
|
Command->ModeRegisterDefinition = 0;
|
|
/* Send the command */
|
|
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
|
|
|
|
/* Step 2: Insert 100 us minimum delay */
|
|
/* Inserted delay is equal to 1 ms due to systick time base unit (ms) */
|
|
HAL_Delay(1);
|
|
|
|
/* Step 3: Configure a PALL (precharge all) command */
|
|
Command->CommandMode = FMC_SDRAM_CMD_PALL;
|
|
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
|
|
Command->AutoRefreshNumber = 1;
|
|
Command->ModeRegisterDefinition = 0;
|
|
/* Send the command */
|
|
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
|
|
|
|
/* Step 4 : Configure a Auto-Refresh command */
|
|
Command->CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
|
|
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
|
|
Command->AutoRefreshNumber = 8;
|
|
Command->ModeRegisterDefinition = 0;
|
|
/* Send the command */
|
|
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
|
|
|
|
/* Step 5: Program the external memory mode register */
|
|
tmpmrd = (uint32_t)(SDRAM_MODEREG_BURST_LENGTH_1 |
|
|
SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |
|
|
SDRAM_MODEREG_CAS_LATENCY_2 |
|
|
SDRAM_MODEREG_OPERATING_MODE_STANDARD |
|
|
SDRAM_MODEREG_WRITEBURST_MODE_SINGLE);
|
|
|
|
Command->CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
|
|
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
|
|
Command->AutoRefreshNumber = 1;
|
|
Command->ModeRegisterDefinition = tmpmrd;
|
|
/* Send the command */
|
|
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
|
|
|
|
/* Step 6: Set the refresh rate counter */
|
|
/* Set the device refresh rate */
|
|
HAL_SDRAM_ProgramRefreshRate(hsdram, REFRESH_COUNT);
|
|
}
|
|
|
|
void SDRAM_Config() {
|
|
|
|
__HAL_RCC_SYSCFG_CLK_ENABLE();
|
|
__HAL_RCC_FMC_CLK_ENABLE();
|
|
|
|
SDRAM_HandleTypeDef hsdram;
|
|
FMC_SDRAM_TimingTypeDef SDRAM_Timing;
|
|
FMC_SDRAM_CommandTypeDef command;
|
|
|
|
/* Configure the SDRAM device */
|
|
hsdram.Instance = FMC_SDRAM_DEVICE;
|
|
hsdram.Init.SDBank = FMC_SDRAM_BANK1;
|
|
hsdram.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
|
|
hsdram.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
|
|
hsdram.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
|
|
hsdram.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
|
|
hsdram.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_2;
|
|
hsdram.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
|
|
hsdram.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
|
|
hsdram.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
|
|
hsdram.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
|
|
|
|
/* Timing configuration for 100Mhz as SDRAM clock frequency (System clock is up to 200Mhz) */
|
|
SDRAM_Timing.LoadToActiveDelay = 2;
|
|
SDRAM_Timing.ExitSelfRefreshDelay = 8;
|
|
SDRAM_Timing.SelfRefreshTime = 6;
|
|
SDRAM_Timing.RowCycleDelay = 6;
|
|
SDRAM_Timing.WriteRecoveryTime = 2;
|
|
SDRAM_Timing.RPDelay = 2;
|
|
SDRAM_Timing.RCDDelay = 2;
|
|
|
|
/* Initialize the SDRAM controller */
|
|
if (HAL_SDRAM_Init(&hsdram, &SDRAM_Timing) != HAL_OK)
|
|
{
|
|
/* Initialization Error */
|
|
}
|
|
|
|
/* Program the SDRAM external device */
|
|
SDRAM_Initialization_Sequence(&hsdram, &command);
|
|
}
|
|
|
|
void LTDC_Config() {
|
|
|
|
__HAL_RCC_LTDC_CLK_ENABLE();
|
|
__HAL_RCC_DMA2D_CLK_ENABLE();
|
|
|
|
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
|
|
|
|
/* The PLL3R is configured to provide the LTDC PCLK clock */
|
|
/* PLL3_VCO Input = HSE_VALUE / PLL3M = 25Mhz / 5 = 5 Mhz */
|
|
/* PLL3_VCO Output = PLL3_VCO Input * PLL3N = 5Mhz * 160 = 800 Mhz */
|
|
/* PLLLCDCLK = PLL3_VCO Output/PLL3R = 800Mhz / 16 = 50Mhz */
|
|
/* LTDC clock frequency = PLLLCDCLK = 50 Mhz */
|
|
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
|
|
PeriphClkInitStruct.PLL3.PLL3M = 5;
|
|
PeriphClkInitStruct.PLL3.PLL3N = 160;
|
|
PeriphClkInitStruct.PLL3.PLL3FRACN = 0;
|
|
PeriphClkInitStruct.PLL3.PLL3P = 2;
|
|
PeriphClkInitStruct.PLL3.PLL3Q = 2;
|
|
PeriphClkInitStruct.PLL3.PLL3R = (800 / LTDC_LCD_CLK);
|
|
PeriphClkInitStruct.PLL3.PLL3VCOSEL = RCC_PLL3VCOWIDE;
|
|
PeriphClkInitStruct.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_2;
|
|
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
|
|
|
|
LTDC_HandleTypeDef hltdc_F;
|
|
LTDC_LayerCfgTypeDef pLayerCfg;
|
|
|
|
/* LTDC Initialization -------------------------------------------------------*/
|
|
|
|
/* Polarity configuration */
|
|
/* Initialize the horizontal synchronization polarity as active low */
|
|
hltdc_F.Init.HSPolarity = LTDC_HSPOLARITY_AL;
|
|
/* Initialize the vertical synchronization polarity as active low */
|
|
hltdc_F.Init.VSPolarity = LTDC_VSPOLARITY_AL;
|
|
/* Initialize the data enable polarity as active low */
|
|
hltdc_F.Init.DEPolarity = LTDC_DEPOLARITY_AL;
|
|
/* Initialize the pixel clock polarity as input pixel clock */
|
|
hltdc_F.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
|
|
|
|
/* Timing configuration */
|
|
hltdc_F.Init.HorizontalSync = (LTDC_LCD_HSYNC - 1);
|
|
hltdc_F.Init.VerticalSync = (LTDC_LCD_VSYNC - 1);
|
|
hltdc_F.Init.AccumulatedHBP = (LTDC_LCD_HSYNC + LTDC_LCD_HBP - 1);
|
|
hltdc_F.Init.AccumulatedVBP = (LTDC_LCD_VSYNC + LTDC_LCD_VBP - 1);
|
|
hltdc_F.Init.AccumulatedActiveH = (TFT_HEIGHT + LTDC_LCD_VSYNC + LTDC_LCD_VBP - 1);
|
|
hltdc_F.Init.AccumulatedActiveW = (TFT_WIDTH + LTDC_LCD_HSYNC + LTDC_LCD_HBP - 1);
|
|
hltdc_F.Init.TotalHeight = (TFT_HEIGHT + LTDC_LCD_VSYNC + LTDC_LCD_VBP + LTDC_LCD_VFP - 1);
|
|
hltdc_F.Init.TotalWidth = (TFT_WIDTH + LTDC_LCD_HSYNC + LTDC_LCD_HBP + LTDC_LCD_HFP - 1);
|
|
|
|
/* Configure R,G,B component values for LCD background color : all black background */
|
|
hltdc_F.Init.Backcolor.Blue = 0;
|
|
hltdc_F.Init.Backcolor.Green = 0;
|
|
hltdc_F.Init.Backcolor.Red = 0;
|
|
|
|
hltdc_F.Instance = LTDC;
|
|
|
|
/* Layer0 Configuration ------------------------------------------------------*/
|
|
|
|
/* Windowing configuration */
|
|
pLayerCfg.WindowX0 = 0;
|
|
pLayerCfg.WindowX1 = TFT_WIDTH;
|
|
pLayerCfg.WindowY0 = 0;
|
|
pLayerCfg.WindowY1 = TFT_HEIGHT;
|
|
|
|
/* Pixel Format configuration*/
|
|
pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
|
|
|
|
/* Start Address configuration : frame buffer is located at SDRAM memory */
|
|
pLayerCfg.FBStartAddress = (uint32_t)(FRAME_BUFFER_ADDRESS);
|
|
|
|
/* Alpha constant (255 == totally opaque) */
|
|
pLayerCfg.Alpha = 255;
|
|
|
|
/* Default Color configuration (configure A,R,G,B component values) : no background color */
|
|
pLayerCfg.Alpha0 = 0; /* fully transparent */
|
|
pLayerCfg.Backcolor.Blue = 0;
|
|
pLayerCfg.Backcolor.Green = 0;
|
|
pLayerCfg.Backcolor.Red = 0;
|
|
|
|
/* Configure blending factors */
|
|
pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
|
|
pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
|
|
|
|
/* Configure the number of lines and number of pixels per line */
|
|
pLayerCfg.ImageWidth = TFT_WIDTH;
|
|
pLayerCfg.ImageHeight = TFT_HEIGHT;
|
|
|
|
/* Configure the LTDC */
|
|
if (HAL_LTDC_Init(&hltdc_F) != HAL_OK)
|
|
{
|
|
/* Initialization Error */
|
|
}
|
|
|
|
/* Configure the Layer*/
|
|
if (HAL_LTDC_ConfigLayer(&hltdc_F, &pLayerCfg, 0) != HAL_OK)
|
|
{
|
|
/* Initialization Error */
|
|
}
|
|
}
|
|
|
|
uint16_t TFT_LTDC::x_min = 0;
|
|
uint16_t TFT_LTDC::x_max = 0;
|
|
uint16_t TFT_LTDC::y_min = 0;
|
|
uint16_t TFT_LTDC::y_max = 0;
|
|
uint16_t TFT_LTDC::x_cur = 0;
|
|
uint16_t TFT_LTDC::y_cur = 0;
|
|
uint8_t TFT_LTDC::reg = 0;
|
|
volatile uint16_t* TFT_LTDC::framebuffer = (volatile uint16_t* )FRAME_BUFFER_ADDRESS;
|
|
|
|
void TFT_LTDC::Init() {
|
|
|
|
// SDRAM pins init
|
|
for (uint16_t i = 0; PinMap_SDRAM[i].pin != NC; i++)
|
|
pinmap_pinout(PinMap_SDRAM[i].pin, PinMap_SDRAM);
|
|
|
|
// SDRAM peripheral config
|
|
SDRAM_Config();
|
|
|
|
// LTDC pins init
|
|
for (uint16_t i = 0; PinMap_LTDC[i].pin != NC; i++)
|
|
pinmap_pinout(PinMap_LTDC[i].pin, PinMap_LTDC);
|
|
|
|
// LTDC peripheral config
|
|
LTDC_Config();
|
|
}
|
|
|
|
uint32_t TFT_LTDC::GetID() {
|
|
return 0xABAB;
|
|
}
|
|
|
|
uint32_t TFT_LTDC::ReadID(tft_data_t Reg) {
|
|
return 0xABAB;
|
|
}
|
|
|
|
bool TFT_LTDC::isBusy() {
|
|
return false;
|
|
}
|
|
|
|
uint16_t TFT_LTDC::ReadPoint(uint16_t x, uint16_t y) {
|
|
return framebuffer[(TFT_WIDTH * y) + x];
|
|
}
|
|
|
|
void TFT_LTDC::DrawPoint(uint16_t x, uint16_t y, uint16_t color) {
|
|
framebuffer[(TFT_WIDTH * y) + x] = color;
|
|
}
|
|
|
|
void TFT_LTDC::DrawRect(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t color) {
|
|
|
|
if (sx == ex || sy == ey) return;
|
|
|
|
uint16_t offline = TFT_WIDTH - (ex - sx);
|
|
uint32_t addr = (uint32_t)&framebuffer[(TFT_WIDTH * sy) + sx];
|
|
|
|
CBI(DMA2D->CR, 0);
|
|
DMA2D->CR = 3 << 16;
|
|
DMA2D->OPFCCR = 0X02;
|
|
DMA2D->OOR = offline;
|
|
DMA2D->OMAR = addr;
|
|
DMA2D->NLR = (ey - sy) | ((ex - sx) << 16);
|
|
DMA2D->OCOLR = color;
|
|
SBI(DMA2D->CR, 0);
|
|
|
|
uint32_t timeout = 0;
|
|
while (!TEST(DMA2D->ISR, 1)) {
|
|
timeout++;
|
|
if (timeout > 0x1FFFFF) break;
|
|
}
|
|
SBI(DMA2D->IFCR, 1);
|
|
}
|
|
|
|
void TFT_LTDC::DrawImage(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *colors) {
|
|
|
|
if (sx == ex || sy == ey) return;
|
|
|
|
uint16_t offline = TFT_WIDTH - (ex - sx);
|
|
uint32_t addr = (uint32_t)&framebuffer[(TFT_WIDTH * sy) + sx];
|
|
|
|
CBI(DMA2D->CR, 0);
|
|
DMA2D->CR = 0 << 16;
|
|
DMA2D->FGPFCCR = 0X02;
|
|
DMA2D->FGOR = 0;
|
|
DMA2D->OOR = offline;
|
|
DMA2D->FGMAR = (uint32_t)colors;
|
|
DMA2D->OMAR = addr;
|
|
DMA2D->NLR = (ey - sy) | ((ex - sx) << 16);
|
|
SBI(DMA2D->CR, 0);
|
|
|
|
uint32_t timeout = 0;
|
|
while (!TEST(DMA2D->ISR, 1)) {
|
|
timeout++;
|
|
if (timeout > 0x1FFFFF) break;
|
|
}
|
|
SBI(DMA2D->IFCR, 1);
|
|
}
|
|
|
|
void TFT_LTDC::WriteData(uint16_t data) {
|
|
switch (reg) {
|
|
case 0x01: x_cur = x_min = data; return;
|
|
case 0x02: x_max = data; return;
|
|
case 0x03: y_cur = y_min = data; return;
|
|
case 0x04: y_max = data; return;
|
|
}
|
|
Transmit(data);
|
|
}
|
|
|
|
void TFT_LTDC::Transmit(tft_data_t Data) {
|
|
DrawPoint(x_cur, y_cur, Data);
|
|
x_cur++;
|
|
if (x_cur > x_max) {
|
|
x_cur = x_min;
|
|
y_cur++;
|
|
if (y_cur > y_max) y_cur = y_min;
|
|
}
|
|
}
|
|
|
|
void TFT_LTDC::WriteReg(uint16_t Reg) {
|
|
reg = Reg;
|
|
}
|
|
|
|
void TFT_LTDC::TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) {
|
|
|
|
while (x_cur != x_min && Count) {
|
|
Transmit(*Data);
|
|
if (MemoryIncrease == DMA_PINC_ENABLE) Data++;
|
|
Count--;
|
|
}
|
|
|
|
uint16_t width = x_max - x_min + 1;
|
|
uint16_t height = Count / width;
|
|
uint16_t x_end_cnt = Count - (width * height);
|
|
|
|
if (height) {
|
|
if (MemoryIncrease == DMA_PINC_ENABLE) {
|
|
DrawImage(x_min, y_cur, x_min + width, y_cur + height, Data);
|
|
Data += width * height;
|
|
} else {
|
|
DrawRect(x_min, y_cur, x_min + width, y_cur + height, *Data);
|
|
}
|
|
y_cur += height;
|
|
}
|
|
|
|
while (x_end_cnt) {
|
|
Transmit(*Data);
|
|
if (MemoryIncrease == DMA_PINC_ENABLE) Data++;
|
|
x_end_cnt--;
|
|
}
|
|
}
|
|
|
|
#endif // HAS_LTDC_TFT
|
|
#endif // ARDUINO_ARCH_STM32 && !STM32GENERIC && !MAPLE_STM32F1
|