Skip to content

Commit

Permalink
Add Quantum Painter, including support for ILI9341/9488.
Browse files Browse the repository at this point in the history
  • Loading branch information
tzarc committed May 10, 2021
1 parent 0e7c66e commit b61e16b
Show file tree
Hide file tree
Showing 19 changed files with 2,077 additions and 0 deletions.
40 changes: 40 additions & 0 deletions common_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,46 @@ ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
SRC += $(QUANTUM_DIR)/pointing_device.c
endif

VALID_QUANTUM_PAINTER_DRIVERS := ili9341 ili9488
QUANTUM_PAINTER_DRIVERS ?=
ifneq ($(strip $(QUANTUM_PAINTER_DRIVERS)),)
OPT_DEFS += -DQUANTUM_PAINTER_ENABLE
COMMON_VPATH += \
$(LIB_PATH)/lzf \
$(QUANTUM_DIR)/painter \
$(DRIVER_PATH)/painter/fallback
SRC += \
$(LIB_PATH)/lzf/lzf_d.c \
$(QUANTUM_DIR)/painter/qp.c \
$(QUANTUM_DIR)/painter/qp_utils.c \
$(DRIVER_PATH)/painter/fallback/qp_fallback.c
endif
define handle_quantum_painter_driver
CURRENT_PAINTER_DRIVER := $1
ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ili9341)
OPT_DEFS += -DQUANTUM_PAINTER_ILI9341_ENABLE
QUANTUM_LIB_SRC += spi_master.c
COMMON_VPATH += \
$(DRIVER_PATH)/painter/ili9xxx_common \
$(DRIVER_PATH)/painter/ili9341
SRC += \
$(DRIVER_PATH)/painter/ili9xxx_common/qp_ili9xxx.c \
$(DRIVER_PATH)/painter/ili9341/qp_ili9341.c
else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ili9488)
OPT_DEFS += -DQUANTUM_PAINTER_ILI9488_ENABLE
QUANTUM_LIB_SRC += spi_master.c
COMMON_VPATH += \
$(DRIVER_PATH)/painter/ili9xxx_common \
$(DRIVER_PATH)/painter/ili9488
SRC += \
$(DRIVER_PATH)/painter/ili9xxx_common/qp_ili9xxx.c \
$(DRIVER_PATH)/painter/ili9488/qp_ili9488.c
else
$$(error "$$(CURRENT_PAINTER_DRIVER)" is not a valid quantum painter driver)
endif
endef
$(foreach qp_driver,$(QUANTUM_PAINTER_DRIVERS),$(eval $(call handle_quantum_painter_driver,$(qp_driver))))

VALID_EEPROM_DRIVER_TYPES := vendor custom transient i2c spi
EEPROM_DRIVER ?= vendor
ifeq ($(filter $(EEPROM_DRIVER),$(VALID_EEPROM_DRIVER_TYPES)),)
Expand Down
68 changes: 68 additions & 0 deletions drivers/painter/fallback/qp_fallback.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* Copyright 2020 Nick Brassel (@tzarc)
*
* 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 <http://www.gnu.org/licenses/>.
*/

#include "qp_fallback.h"

// Fallback implementation for drawing lines
bool qp_fallback_line(painter_device_t device, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t hue, uint8_t sat, uint8_t val) {
if (x0 == x1) {
// Vertical line
for (uint16_t y = y0; y <= y1; ++y) {
if (!qp_setpixel(device, x0, y, hue, sat, val)) {
return false;
}
}
} else if (y0 == y1) {
// Horizontal line
for (uint16_t x = x0; x <= x1; ++x) {
if (!qp_setpixel(device, x, y0, hue, sat, val)) {
return false;
}
}
} else {
// TODO: Angled lines
}

return true;
}

// Fallback implementation for drawing rectangles
bool qp_fallback_rect(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom, uint8_t hue, uint8_t sat, uint8_t val, bool filled) {
if (filled) {
// Draw a filled rectangle by drawing horizontal lines for the width/height requested
for (uint16_t y = top; y <= bottom; ++y) {
if (!qp_line(device, left, y, right, y, hue, sat, val)) {
return false;
}
}
} else {
// Draw a non-filled rectangle by drawing horizontal and vertical lines along each edge
if (!qp_line(device, left, top, right, top, hue, sat, val)) {
return false;
}
if (!qp_line(device, left, bottom, right, bottom, hue, sat, val)) {
return false;
}
if (!qp_line(device, left, top + 1, left, bottom - 1, hue, sat, val)) {
return false;
}
if (!qp_line(device, right, top + 1, right, bottom - 1, hue, sat, val)) {
return false;
}
}

return true;
}
29 changes: 29 additions & 0 deletions drivers/painter/fallback/qp_fallback.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* Copyright 2020 Nick Brassel (@tzarc)
*
* 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 <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "qp.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter fallback drawing implementations
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Fallback method of drawing lines via setpixel
bool qp_fallback_line(painter_device_t device, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t hue, uint8_t sat, uint8_t val);

// Fallback method of drawing rects using lines
bool qp_fallback_rect(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom, uint8_t hue, uint8_t sat, uint8_t val, bool filled);
198 changes: 198 additions & 0 deletions drivers/painter/ili9341/qp_ili9341.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/* Copyright 2020 Nick Brassel (@tzarc)
*
* 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 <http://www.gnu.org/licenses/>.
*/

#include <string.h>

#include "spi_master.h"

#include "qp.h"
#include "qp_ili9341.h"
#include "qp_internal.h"
#include "qp_ili9xxx_internal.h"
#include "qp_ili9xxx_opcodes.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Initialization
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool qp_ili9341_init(painter_device_t device, painter_rotation_t rotation) {
static const uint8_t pgamma[15] = {0x0F, 0x29, 0x24, 0x0C, 0x0E, 0x09, 0x4E, 0x78, 0x3C, 0x09, 0x13, 0x05, 0x17, 0x11, 0x00};
static const uint8_t ngamma[15] = {0x00, 0x16, 0x1B, 0x04, 0x11, 0x07, 0x31, 0x33, 0x42, 0x05, 0x0C, 0x0A, 0x28, 0x2F, 0x0F};

ili9xxx_painter_device_t *lcd = (ili9xxx_painter_device_t *)device;
lcd->rotation = rotation;

// Initialize the SPI peripheral
spi_init();

// Set up pin directions and initial values
setPinOutput(lcd->chip_select_pin);
writePinHigh(lcd->chip_select_pin);

setPinOutput(lcd->data_pin);
writePinLow(lcd->data_pin);

setPinOutput(lcd->reset_pin);

// Perform a reset
writePinLow(lcd->reset_pin);
wait_ms(20);
writePinHigh(lcd->reset_pin);
wait_ms(20);

// Enable the SPI comms to the LCD
qp_ili9xxx_internal_lcd_start(lcd);

// Configure power control
qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_POWER_CTL_A);
qp_ili9xxx_internal_lcd_data(lcd, 0x39);
qp_ili9xxx_internal_lcd_data(lcd, 0x2C);
qp_ili9xxx_internal_lcd_data(lcd, 0x00);
qp_ili9xxx_internal_lcd_data(lcd, 0x34);
qp_ili9xxx_internal_lcd_data(lcd, 0x02);

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_POWER_CTL_B);
qp_ili9xxx_internal_lcd_data(lcd, 0x00);
qp_ili9xxx_internal_lcd_data(lcd, 0xD9);
qp_ili9xxx_internal_lcd_data(lcd, 0X30);

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_POWER_ON_SEQ_CTL);
qp_ili9xxx_internal_lcd_data(lcd, 0x64);
qp_ili9xxx_internal_lcd_data(lcd, 0x03);
qp_ili9xxx_internal_lcd_data(lcd, 0X12);
qp_ili9xxx_internal_lcd_data(lcd, 0X81);

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_PUMP_RATIO_CTL);
qp_ili9xxx_internal_lcd_data(lcd, 0x20);

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_POWER_CTL_1);
qp_ili9xxx_internal_lcd_data(lcd, 0x26);

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_POWER_CTL_2);
qp_ili9xxx_internal_lcd_data(lcd, 0x11);

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_VCOM_CTL_1);
qp_ili9xxx_internal_lcd_data(lcd, 0x35);
qp_ili9xxx_internal_lcd_data(lcd, 0x3E);

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_VCOM_CTL_2);
qp_ili9xxx_internal_lcd_data(lcd, 0xBE);

// Configure timing control
qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_DRV_TIMING_CTL_A);
qp_ili9xxx_internal_lcd_data(lcd, 0x85);
qp_ili9xxx_internal_lcd_data(lcd, 0x10);
qp_ili9xxx_internal_lcd_data(lcd, 0x7A);

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_DRV_TIMING_CTL_B);
qp_ili9xxx_internal_lcd_data(lcd, 0x00);
qp_ili9xxx_internal_lcd_data(lcd, 0x00);

// Configure brightness / gamma
qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_BRIGHTNESS);
qp_ili9xxx_internal_lcd_data(lcd, 0xFF);

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_ENABLE_3_GAMMA);
qp_ili9xxx_internal_lcd_data(lcd, 0x00);

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_GAMMA);
qp_ili9xxx_internal_lcd_data(lcd, 0x01);

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_PGAMMA);
qp_ili9xxx_internal_lcd_sendbuf(lcd, pgamma, sizeof(pgamma));

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_NGAMMA);
qp_ili9xxx_internal_lcd_sendbuf(lcd, ngamma, sizeof(ngamma));

// Set the pixel format
qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_PIX_FMT);
qp_ili9xxx_internal_lcd_data(lcd, 0x55);

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_FRAME_CTL_NORMAL);
qp_ili9xxx_internal_lcd_data(lcd, 0x00);
qp_ili9xxx_internal_lcd_data(lcd, 0x1B);

qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_FUNCTION_CTL);
qp_ili9xxx_internal_lcd_data(lcd, 0x0A);
qp_ili9xxx_internal_lcd_data(lcd, 0xA2);

// Set the default viewport to be fullscreen
qp_ili9xxx_internal_lcd_viewport(lcd, 0, 0, 239, 319);

// Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)
switch (rotation) {
case QP_ROTATION_0:
qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_MEM_ACS_CTL);
qp_ili9xxx_internal_lcd_data(lcd, 0b00001000);
break;
case QP_ROTATION_90:
qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_MEM_ACS_CTL);
qp_ili9xxx_internal_lcd_data(lcd, 0b10101000);
break;
case QP_ROTATION_180:
qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_MEM_ACS_CTL);
qp_ili9xxx_internal_lcd_data(lcd, 0b11001000);
break;
case QP_ROTATION_270:
qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_SET_MEM_ACS_CTL);
qp_ili9xxx_internal_lcd_data(lcd, 0b01101000);
break;
}

// Disable sleep mode
qp_ili9xxx_internal_lcd_cmd(lcd, ILI9XXX_CMD_SLEEP_OFF);
wait_ms(20);

// Disable the SPI comms to the LCD
qp_ili9xxx_internal_lcd_stop();

return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Device creation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Driver storage
static ili9xxx_painter_device_t drivers[ILI9341_NUM_DEVICES] = {0};

// Factory function for creating a handle to the ILI9341 device
painter_device_t qp_ili9341_make_device(pin_t chip_select_pin, pin_t data_pin, pin_t reset_pin, uint16_t spi_divisor, bool uses_backlight) {
for (uint32_t i = 0; i < ILI9341_NUM_DEVICES; ++i) {
ili9xxx_painter_device_t *driver = &drivers[i];
memset(driver, 0, sizeof(ili9xxx_painter_device_t));
if (!driver->allocated) {
driver->allocated = true;
driver->qp_driver.init = qp_ili9341_init;
driver->qp_driver.clear = qp_ili9xxx_clear;
driver->qp_driver.power = qp_ili9xxx_power;
driver->qp_driver.pixdata = qp_ili9xxx_pixdata;
driver->qp_driver.viewport = qp_ili9xxx_viewport;
driver->qp_driver.setpixel = qp_ili9xxx_setpixel;
driver->qp_driver.line = qp_ili9xxx_line;
driver->qp_driver.rect = qp_ili9xxx_rect;
driver->qp_driver.drawimage = qp_ili9xxx_drawimage;
driver->chip_select_pin = chip_select_pin;
driver->data_pin = data_pin;
driver->reset_pin = reset_pin;
driver->spi_divisor = spi_divisor;
#ifdef BACKLIGHT_ENABLE
driver->uses_backlight = uses_backlight;
#endif
return (painter_device_t)driver;
}
}
return NULL;
}
35 changes: 35 additions & 0 deletions drivers/painter/ili9341/qp_ili9341.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* Copyright 2020 Nick Brassel (@tzarc)
*
* 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 <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "qp.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter ILI9341 configurables
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// The number of ILI9341 devices we're going to be talking to
#ifndef ILI9341_NUM_DEVICES
# define ILI9341_NUM_DEVICES 1
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter ILI9341 device factory
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Factory method for an ILI9341 device
painter_device_t qp_ili9341_make_device(pin_t chip_select_pin, pin_t data_pin, pin_t reset_pin, uint16_t spi_divisor, bool uses_backlight);
Loading

0 comments on commit b61e16b

Please sign in to comment.