diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b067bad
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.vscode
+.idea
+
+*.o
+*.P
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..81e2502
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2019 Ivan Belokobylskiy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0bc25cc
--- /dev/null
+++ b/README.md
@@ -0,0 +1,179 @@
+ST7789 Driver for MicroPython
+=============================
+
+
+Overview
+--------
+This is a driver for MicroPython to handle cheap displays
+based on ST7789 chip.
+
+
+
+
+
+It is written in pure C, so you have to build
+firmware by yourself.
+Only ESP8266 and ESP32 are supported for now.
+
+
+Building instruction
+---------------------
+
+Prepare build tools as described in the manual.
+You should follow the instruction for building MicroPython and
+ensure that you can build the firmware without this display module.
+
+Clone this module alongside the MPY sources:
+
+ $ git clone https://github.com/devbis/st7789_mpy.git
+
+Go to MicroPython ports directory and for ESP8266 run:
+
+ $ cd micropython/ports/esp8266
+
+for ESP32:
+
+ $ cd micropython/ports/esp32
+
+And then compile the module with specified USER_C_MODULES dir
+
+ $ make USER_C_MODULES=../../../st7789_mpy/ all
+
+
+If you have other user modules, copy the st7789_driver/st7789 to
+the user modules directory
+
+Upload the resulting firmware to your MCU as usual with esptool.py
+(See
+[MicroPython docs](http://docs.micropython.org/en/latest/esp8266/tutorial/intro.html#deploying-the-firmware)
+for more info)
+
+
+Working examples
+----------------
+
+This module was tested on ESP32 and ESP8266 MCUs.
+
+You have to provide `machine.SPI` object and at least two pins for RESET and
+DC pins on the screen for the display object.
+
+
+ # ESP 8266
+
+ import machine
+ import st7789
+ spi = machine.SPI(1, baudrate=40000000, polarity=1)
+ display = st7789.ST7789(spi, 240, 240, reset=machine.Pin(5, machine.Pin.OUT), dc=machine.Pin(4, machine.Pin.OUT))
+
+
+For ESP32 modules you have to provide specific pins for SPI.
+Unfortunately, I was unable to run this display on SPI(1) interface.
+For machine.SPI(2) == VSPI you have to use
+
+- CLK: Pin(18)
+- MOSI: Pin(23)
+
+Other SPI pins are not used.
+
+
+ # ESP32
+
+ import machine
+ import st7789
+ spi = machine.SPI(2, baudrate=40000000, polarity=1, sck=machine.Pin(18), mosi=machine.Pin(23))
+ display = st7789.ST7789(spi, 240, 240, reset=machine.Pin(4, machine.Pin.OUT), dc=machine.Pin(2, machine.Pin.OUT))
+
+
+I couldn't run the display on an SPI with baudrate higher than 40MHZ
+
+Methods
+-------------
+
+This driver supports only 16bit colors in RGB565 notation.
+
+
+- `ST7789.fill(color)`
+
+ Fill the entire display with the specified color.
+
+- `ST7789.pixel(x, y, color)`
+
+ Set the specified pixel to the given color.
+
+- `ST7789.line(x0, y0, x1, y1, color)`
+
+ Draws a single line with the provided `color` from (`x0`, `y0`) to
+ (`x1`, `y1`).
+
+- `ST7789.hline(x, y, length, color)`
+
+ Draws a single horizontal line with the provided `color` and `length`
+ in pixels. Along with `vline`, this is a fast version with reduced
+ number of SPI calls.
+
+- `ST7789.vline(x, y, length, color)`
+
+ Draws a single horizontal line with the provided `color` and `length`
+ in pixels.
+
+- `ST7789.rect(x, y, width, height, color)`
+
+ Draws a rectangle from (`x`, `y`) with corresponding dimensions
+
+- `ST7789.fill_rect(x, y, width, height, color)`
+
+ Fill a rectangle starting from (`x`, `y`) coordinates
+
+- `ST7789.blit_buffer(buffer, x, y, width, height)`
+
+ Copy bytes() or bytearray() content to the screen internal memory.
+ Note: every color requires 2 bytes in the array
+
+Also, the module exposes predefined colors:
+ `BLACK`, `BLUE`, `RED`, `GREEN`, `CYAN`, `MAGENTA`, `YELLOW`, and `WHITE`
+
+
+Performance
+-----------
+
+For the comparison I used an excelent library for Arduino
+that can handle this screen.
+
+https://github.com/ananevilya/Arduino-ST7789-Library/
+
+Also, I used my slow driver for this screen, written in pure python.
+
+https://github.com/devbis/st7789py_mpy/
+
+I used these modules to draw a line from 0,0 to 239,239
+The table represents the time in milliseconds for each case
+
+| | Arduino-ST7789 | st7789py_mpy | st7789_mpy |
+|---------|----------------|--------------|---------------|
+| ESP8266 | 26 | 450 | 12 |
+| ESP32 | 23 | 450 | 47 |
+
+
+As you can see, the ESP32 module draws a line 4 times slower than
+the older ESP8266 module.
+
+
+Troubleshooting
+---------------
+
+#### Overflow of iram1_0_seg
+
+When building a firmware for esp8266 you can see this failure message from
+the linker:
+
+ LINK build/firmware.elf
+ xtensa-lx106-elf-ld: build/firmware.elf section `.text' will not fit in region `iram1_0_seg'
+ xtensa-lx106-elf-ld: region `iram1_0_seg' overflowed by 292 bytes
+ Makefile:192: recipe for target 'build/firmware.elf' failed
+
+To fix this issue, you have to put st7789 module to irom0 section.
+Edit `esp8266_common.ld` file in the `ports/esp8266` dir and add a line
+
+ *st7789/*.o(.literal* .text*)
+
+in the `.irom0.text : ALIGN(4)` section
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..6c6aa7c
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.1.0
\ No newline at end of file
diff --git a/docs/ST7789.jpg b/docs/ST7789.jpg
new file mode 100644
index 0000000..9e5c32f
Binary files /dev/null and b/docs/ST7789.jpg differ
diff --git a/st7789/micropython.mk b/st7789/micropython.mk
new file mode 100644
index 0000000..79dda11
--- /dev/null
+++ b/st7789/micropython.mk
@@ -0,0 +1,6 @@
+ST7789_MOD_DIR := $(USERMOD_DIR)
+SRC_USERMOD += $(addprefix $(ST7789_MOD_DIR)/, \
+ st7789.c \
+)
+CFLAGS_USERMOD += -I$(ST7789_MOD_DIR) -DMODULE_ST7789_ENABLED=1
+# CFLAGS_USERMOD += -DEXPOSE_EXTRA_METHODS=1
diff --git a/st7789/st7789.c b/st7789/st7789.c
new file mode 100644
index 0000000..bacd100
--- /dev/null
+++ b/st7789/st7789.c
@@ -0,0 +1,559 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ivan Belokobylskiy
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#define __ST7789_VERSION__ "0.1.0"
+
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "py/builtin.h"
+#include "py/mphal.h"
+#include "extmod/machine_spi.h"
+
+#include "st7789.h"
+
+#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }
+#define ABS(N) ((N<0)?(-N):(N))
+#define mp_hal_delay_ms(delay) (mp_hal_delay_us(delay * 1000))
+
+#define CS_LOW() { if(self->cs) {mp_hal_pin_write(self->cs, 0);} }
+#define CS_HIGH() { if(self->cs) {mp_hal_pin_write(self->cs, 1);} }
+#define DC_LOW() (mp_hal_pin_write(self->dc, 0))
+#define DC_HIGH() (mp_hal_pin_write(self->dc, 1))
+#define RESET_LOW() (mp_hal_pin_write(self->reset, 0))
+#define RESET_HIGH() (mp_hal_pin_write(self->reset, 1))
+
+
+STATIC void write_spi(mp_obj_base_t *spi_obj, const uint8_t *buf, int len) {
+ mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)spi_obj->type->protocol;
+ spi_p->transfer(spi_obj, len, buf, NULL);
+}
+
+// this is the actual C-structure for our new object
+typedef struct _st7789_ST7789_obj_t {
+ mp_obj_base_t base;
+
+ mp_obj_base_t *spi_obj;
+ uint8_t width;
+ uint8_t height;
+ mp_hal_pin_obj_t reset;
+ mp_hal_pin_obj_t dc;
+ mp_hal_pin_obj_t cs;
+ mp_hal_pin_obj_t backlight;
+} st7789_ST7789_obj_t;
+
+
+// just a definition
+mp_obj_t st7789_ST7789_make_new( const mp_obj_type_t *type,
+ size_t n_args,
+ size_t n_kw,
+ const mp_obj_t *args );
+STATIC void st7789_ST7789_print( const mp_print_t *print,
+ mp_obj_t self_in,
+ mp_print_kind_t kind ) {
+ (void)kind;
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_printf(print, "", self->width, self->height, self->spi_obj);
+}
+
+/* methods start */
+
+STATIC void write_cmd(st7789_ST7789_obj_t *self, uint8_t cmd, const uint8_t *data, int len) {
+ CS_LOW()
+ if (cmd) {
+ DC_LOW();
+ write_spi(self->spi_obj, &cmd, 1);
+ }
+ if (len > 0) {
+ DC_HIGH();
+ write_spi(self->spi_obj, data, len);
+ }
+ CS_HIGH()
+}
+
+STATIC void set_window(st7789_ST7789_obj_t *self, uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) {
+ if (x0 > x1 || x1 > self->width) {
+ return;
+ }
+ if (y0 > y1 || y1 > self->height) {
+ return;
+ }
+ uint8_t bufx[4] = {x0 >> 8, x0 & 0xFF, x1 >> 8, x1 & 0xFF};
+ uint8_t bufy[4] = {y0 >> 8, y0 & 0xFF, y1 >> 8, y1 & 0xFF};
+ write_cmd(self, ST7789_CASET, bufx, 4);
+ write_cmd(self, ST7789_RASET, bufy, 4);
+ write_cmd(self, ST7789_RAMWR, NULL, 0);
+}
+
+STATIC void draw_pixel(st7789_ST7789_obj_t *self, uint8_t x, uint8_t y, uint16_t color) {
+ uint8_t hi = color >> 8, lo = color;
+ set_window(self, x, y, x, y);
+ DC_HIGH();
+ CS_LOW();
+ write_spi(self->spi_obj, &hi, 1);
+ write_spi(self->spi_obj, &lo, 1);
+ CS_HIGH();
+}
+
+STATIC mp_obj_t st7789_ST7789_hard_reset(mp_obj_t self_in) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in);
+
+ CS_LOW();
+ RESET_HIGH();
+ mp_hal_delay_ms(50);
+ RESET_LOW();
+ mp_hal_delay_ms(50);
+ RESET_HIGH();
+ mp_hal_delay_ms(150);
+ CS_HIGH();
+ return mp_const_none;
+}
+
+STATIC mp_obj_t st7789_ST7789_soft_reset(mp_obj_t self_in) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in);
+
+ write_cmd(self, ST7789_SWRESET, NULL, 0);
+ mp_hal_delay_ms(150);
+ return mp_const_none;
+}
+
+// do not expose extra method to reduce size
+#ifdef EXPOSE_EXTRA_METHODS
+STATIC mp_obj_t st7789_ST7789_write(mp_obj_t self_in, mp_obj_t command, mp_obj_t data) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in);
+
+ mp_buffer_info_t src;
+ if (data == mp_const_none) {
+ write_cmd(self, (uint8_t)mp_obj_get_int(command), NULL, 0);
+ } else {
+ mp_get_buffer_raise(data, &src, MP_BUFFER_READ);
+ write_cmd(self, (uint8_t)mp_obj_get_int(command), (const uint8_t*)src.buf, src.len);
+ }
+
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_3(st7789_ST7789_write_obj, st7789_ST7789_write);
+
+MP_DEFINE_CONST_FUN_OBJ_1(st7789_ST7789_hard_reset_obj, st7789_ST7789_hard_reset);
+MP_DEFINE_CONST_FUN_OBJ_1(st7789_ST7789_soft_reset_obj, st7789_ST7789_soft_reset);
+
+STATIC mp_obj_t st7789_ST7789_sleep_mode(mp_obj_t self_in, mp_obj_t value) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ if(mp_obj_is_true(value)) {
+ write_cmd(self, ST7789_SLPIN, NULL, 0);
+ } else {
+ write_cmd(self, ST7789_SLPOUT, NULL, 0);
+ }
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(st7789_ST7789_sleep_mode_obj, st7789_ST7789_sleep_mode);
+
+STATIC mp_obj_t st7789_ST7789_set_window(size_t n_args, const mp_obj_t *args) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ mp_int_t x0 = mp_obj_get_int(args[1]);
+ mp_int_t x1 = mp_obj_get_int(args[2]);
+ mp_int_t y0 = mp_obj_get_int(args[3]);
+ mp_int_t y1 = mp_obj_get_int(args[4]);
+
+ set_window(self, x0, y0, x1, y1);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_set_window_obj, 5, 5, st7789_ST7789_set_window);
+
+#endif
+
+STATIC mp_obj_t st7789_ST7789_inversion_mode(mp_obj_t self_in, mp_obj_t value) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ if(mp_obj_is_true(value)) {
+ write_cmd(self, ST7789_INVON, NULL, 0);
+ } else {
+ write_cmd(self, ST7789_INVOFF, NULL, 0);
+ }
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(st7789_ST7789_inversion_mode_obj, st7789_ST7789_inversion_mode);
+
+
+STATIC void fill_color_buffer(mp_obj_base_t* spi_obj, uint16_t color, int length) {
+ uint8_t hi = color >> 8, lo = color;
+ const int buffer_pixel_size = 128;
+ int chunks = length / buffer_pixel_size;
+ int rest = length % buffer_pixel_size;
+
+ uint8_t buffer[buffer_pixel_size * 2]; // 128 pixels
+ // fill buffer with color data
+ for (int i = 0; i < length && i < buffer_pixel_size; i++) {
+ buffer[i*2] = hi;
+ buffer[i*2 + 1] = lo;
+ }
+
+ if (chunks) {
+ for (int j = 0; j < chunks; j ++) {
+ write_spi(spi_obj, buffer, buffer_pixel_size*2);
+ }
+ }
+ if (rest) {
+ write_spi(spi_obj, buffer, rest*2);
+ }
+}
+
+
+STATIC mp_obj_t st7789_ST7789_fill_rect(size_t n_args, const mp_obj_t *args) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ mp_int_t x = mp_obj_get_int(args[1]);
+ mp_int_t y = mp_obj_get_int(args[2]);
+ mp_int_t w = mp_obj_get_int(args[3]);
+ mp_int_t h = mp_obj_get_int(args[4]);
+ mp_int_t color = mp_obj_get_int(args[5]);
+
+ set_window(self, x, y, x + w - 1, y + h - 1);
+ DC_HIGH();
+ CS_LOW();
+ fill_color_buffer(self->spi_obj, color, w * h);
+ CS_HIGH();
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_fill_rect_obj, 6, 6, st7789_ST7789_fill_rect);
+
+
+STATIC mp_obj_t st7789_ST7789_fill(mp_obj_t self_in, mp_obj_t _color) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_int_t color = mp_obj_get_int(_color);
+
+ set_window(self, 0, 0, self->width, self->height);
+ DC_HIGH();
+ CS_LOW();
+ fill_color_buffer(self->spi_obj, color, self->width * self->height);
+ CS_HIGH();
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(st7789_ST7789_fill_obj, st7789_ST7789_fill);
+
+
+STATIC mp_obj_t st7789_ST7789_pixel(size_t n_args, const mp_obj_t *args) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ mp_int_t x = mp_obj_get_int(args[1]);
+ mp_int_t y = mp_obj_get_int(args[2]);
+ mp_int_t color = mp_obj_get_int(args[3]);
+
+ draw_pixel(self, x, y, color);
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_pixel_obj, 4, 4, st7789_ST7789_pixel);
+
+
+STATIC mp_obj_t st7789_ST7789_line(size_t n_args, const mp_obj_t *args) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ mp_int_t x0 = mp_obj_get_int(args[1]);
+ mp_int_t y0 = mp_obj_get_int(args[2]);
+ mp_int_t x1 = mp_obj_get_int(args[3]);
+ mp_int_t y1 = mp_obj_get_int(args[4]);
+ mp_int_t color = mp_obj_get_int(args[5]);
+
+ int16_t steep = ABS(y1 - y0) > ABS(x1 - x0);
+ if (steep) {
+ _swap_int16_t(x0, y0);
+ _swap_int16_t(x1, y1);
+ }
+
+ if (x0 > x1) {
+ _swap_int16_t(x0, x1);
+ _swap_int16_t(y0, y1);
+ }
+
+ int16_t dx, dy;
+ dx = x1 - x0;
+ dy = ABS(y1 - y0);
+
+ int16_t err = dx / 2;
+ int16_t ystep;
+
+ if (y0 < y1) {
+ ystep = 1;
+ } else {
+ ystep = -1;
+ }
+
+ for (; x0<=x1; x0++) {
+ if (steep) {
+ draw_pixel(self, y0, x0, color);
+ } else {
+ draw_pixel(self, x0, y0, color);
+ }
+ err -= dy;
+ if (err < 0) {
+ y0 += ystep;
+ err += dx;
+ }
+ }
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_line_obj, 6, 6, st7789_ST7789_line);
+
+
+STATIC mp_obj_t st7789_ST7789_blit_buffer(size_t n_args, const mp_obj_t *args) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ mp_buffer_info_t buf_info;
+ mp_get_buffer_raise(args[1], &buf_info, MP_BUFFER_READ);
+ mp_int_t x = mp_obj_get_int(args[2]);
+ mp_int_t y = mp_obj_get_int(args[3]);
+ mp_int_t w = mp_obj_get_int(args[4]);
+ mp_int_t h = mp_obj_get_int(args[5]);
+
+ set_window(self, x, y, x + w - 1, y + h - 1);
+ DC_HIGH();
+ CS_LOW();
+
+ const int buf_size = 256;
+ int i = 0;
+ int chunks = buf_info.len / buf_size;
+ int rest = buf_info.len % buf_size;
+ for (; i < chunks; i ++) {
+ write_spi(self->spi_obj, (const uint8_t*)buf_info.buf + i*buf_size, buf_size);
+ }
+ if (rest) {
+ write_spi(self->spi_obj, (const uint8_t*)buf_info.buf + i*buf_size, rest);
+ }
+ CS_HIGH();
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_blit_buffer_obj, 6, 6, st7789_ST7789_blit_buffer);
+
+
+STATIC mp_obj_t st7789_ST7789_init(mp_obj_t self_in) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ st7789_ST7789_hard_reset(self_in);
+ st7789_ST7789_soft_reset(self_in);
+ write_cmd(self, ST7789_SLPOUT, NULL, 0);
+
+ const uint8_t color_mode[] = { COLOR_MODE_65K | COLOR_MODE_16BIT};
+ write_cmd(self, ST7789_COLMOD, color_mode, 1);
+ mp_hal_delay_ms(10);
+ const uint8_t madctl[] = { ST7789_MADCTL_ML | ST7789_MADCTL_RGB };
+ write_cmd(self, ST7789_MADCTL, madctl, 1);
+
+ write_cmd(self, ST7789_INVON, NULL, 0);
+ mp_hal_delay_ms(10);
+ write_cmd(self, ST7789_NORON, NULL, 0);
+ mp_hal_delay_ms(10);
+
+ const mp_obj_t args[] = {
+ self_in,
+ mp_obj_new_int(0),
+ mp_obj_new_int(0),
+ mp_obj_new_int(self->width),
+ mp_obj_new_int(self->height),
+ mp_obj_new_int(BLACK)
+ };
+ st7789_ST7789_fill_rect(6, args);
+ write_cmd(self, ST7789_DISPON, NULL, 0);
+ mp_hal_delay_ms(500);
+
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(st7789_ST7789_init_obj, st7789_ST7789_init);
+
+
+STATIC void fast_hline(st7789_ST7789_obj_t *self, uint8_t x, uint8_t y, uint16_t w, uint16_t color) {
+ set_window(self, x, y, x + w - 1, y);
+ DC_HIGH();
+ CS_LOW();
+ fill_color_buffer(self->spi_obj, color, w);
+ CS_HIGH();
+}
+
+
+STATIC void fast_vline(st7789_ST7789_obj_t *self, uint8_t x, uint8_t y, uint16_t w, uint16_t color) {
+ set_window(self, x, y, x, y + w - 1);
+ DC_HIGH();
+ CS_LOW();
+ fill_color_buffer(self->spi_obj, color, w);
+ CS_HIGH();
+}
+
+
+STATIC mp_obj_t st7789_ST7789_hline(size_t n_args, const mp_obj_t *args) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ mp_int_t x = mp_obj_get_int(args[1]);
+ mp_int_t y = mp_obj_get_int(args[2]);
+ mp_int_t w = mp_obj_get_int(args[3]);
+ mp_int_t color = mp_obj_get_int(args[4]);
+
+ fast_hline(self, x, y, w, color);
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_hline_obj, 5, 5, st7789_ST7789_hline);
+
+
+STATIC mp_obj_t st7789_ST7789_vline(size_t n_args, const mp_obj_t *args) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ mp_int_t x = mp_obj_get_int(args[1]);
+ mp_int_t y = mp_obj_get_int(args[2]);
+ mp_int_t w = mp_obj_get_int(args[3]);
+ mp_int_t color = mp_obj_get_int(args[4]);
+
+ fast_vline(self, x, y, w, color);
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_vline_obj, 5, 5, st7789_ST7789_vline);
+
+
+STATIC mp_obj_t st7789_ST7789_rect(size_t n_args, const mp_obj_t *args) {
+ st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ mp_int_t x = mp_obj_get_int(args[1]);
+ mp_int_t y = mp_obj_get_int(args[2]);
+ mp_int_t w = mp_obj_get_int(args[3]);
+ mp_int_t h = mp_obj_get_int(args[4]);
+ mp_int_t color = mp_obj_get_int(args[5]);
+
+ fast_hline(self, x, y, w, color);
+ fast_vline(self, x, y, h, color);
+ fast_hline(self, x, y + h - 1, w, color);
+ fast_vline(self, x + w - 1, y, h, color);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_rect_obj, 6, 6, st7789_ST7789_rect);
+
+
+STATIC const mp_rom_map_elem_t st7789_ST7789_locals_dict_table[] = {
+ // Do not expose internal functions to fit iram_0 section
+#ifdef EXPOSE_EXTRA_METHODS
+ { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&st7789_ST7789_write_obj) },
+ { MP_ROM_QSTR(MP_QSTR_hard_reset), MP_ROM_PTR(&st7789_ST7789_hard_reset_obj) },
+ { MP_ROM_QSTR(MP_QSTR_soft_reset), MP_ROM_PTR(&st7789_ST7789_soft_reset_obj) },
+ { MP_ROM_QSTR(MP_QSTR_sleep_mode), MP_ROM_PTR(&st7789_ST7789_sleep_mode_obj) },
+ { MP_ROM_QSTR(MP_QSTR_inversion_mode), MP_ROM_PTR(&st7789_ST7789_inversion_mode_obj) },
+ { MP_ROM_QSTR(MP_QSTR_set_window), MP_ROM_PTR(&st7789_ST7789_set_window_obj) },
+#endif
+ { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&st7789_ST7789_init_obj) },
+ { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&st7789_ST7789_pixel_obj) },
+ { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&st7789_ST7789_line_obj) },
+ { MP_ROM_QSTR(MP_QSTR_blit_buffer), MP_ROM_PTR(&st7789_ST7789_blit_buffer_obj) },
+ { MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&st7789_ST7789_fill_rect_obj) },
+ { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&st7789_ST7789_fill_obj) },
+ { MP_ROM_QSTR(MP_QSTR_hline), MP_ROM_PTR(&st7789_ST7789_hline_obj) },
+ { MP_ROM_QSTR(MP_QSTR_vline), MP_ROM_PTR(&st7789_ST7789_vline_obj) },
+ { MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&st7789_ST7789_rect_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(st7789_ST7789_locals_dict, st7789_ST7789_locals_dict_table);
+/* methods end */
+
+
+const mp_obj_type_t st7789_ST7789_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_ST7789,
+ .print = st7789_ST7789_print,
+ .make_new = st7789_ST7789_make_new,
+ .locals_dict = (mp_obj_dict_t*)&st7789_ST7789_locals_dict,
+};
+
+mp_obj_t st7789_ST7789_make_new(const mp_obj_type_t *type,
+ size_t n_args,
+ size_t n_kw,
+ const mp_obj_t *all_args ) {
+ enum { ARG_spi, ARG_width, ARG_height, ARG_reset, ARG_dc, ARG_cs, ARG_backlight };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_spi, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} },
+ { MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} },
+ { MP_QSTR_reset, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_dc, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_cs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_backlight, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ // create new object
+ st7789_ST7789_obj_t *self = m_new_obj(st7789_ST7789_obj_t);
+ self->base.type = &st7789_ST7789_type;
+
+ // set parameters
+ mp_obj_base_t *spi_obj = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[ARG_spi].u_obj);
+ self->spi_obj = spi_obj;
+ self->width = args[ARG_width].u_int;
+ self->height = args[ARG_height].u_int;
+
+ if (args[ARG_reset].u_obj == MP_OBJ_NULL
+ || args[ARG_dc].u_obj == MP_OBJ_NULL) {
+ mp_raise_ValueError("must specify all of reset/dc pins");
+ }
+
+ self->reset = mp_hal_get_pin_obj(args[ARG_reset].u_obj);
+ self->dc = mp_hal_get_pin_obj(args[ARG_dc].u_obj);
+
+ if (args[ARG_cs].u_obj != MP_OBJ_NULL) {
+ self->cs = mp_hal_get_pin_obj(args[ARG_cs].u_obj);
+ }
+ if (args[ARG_backlight].u_obj != MP_OBJ_NULL) {
+ self->backlight = mp_hal_get_pin_obj(args[ARG_backlight].u_obj);
+ }
+
+ return MP_OBJ_FROM_PTR(self);
+}
+
+
+STATIC uint16_t color565(uint8_t r, uint8_t g, uint8_t b) {
+ return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
+}
+
+
+STATIC mp_obj_t st7789_color565(mp_obj_t r, mp_obj_t g, mp_obj_t b) {
+ return MP_OBJ_NEW_SMALL_INT(color565(
+ (uint8_t)mp_obj_get_int(r),
+ (uint8_t)mp_obj_get_int(g),
+ (uint8_t)mp_obj_get_int(b)
+ ));
+}
+
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(st7789_color565_obj, st7789_color565);
+
+STATIC const mp_map_elem_t st7789_module_globals_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_st7789) },
+ { MP_ROM_QSTR(MP_QSTR_color565), (mp_obj_t)&st7789_color565_obj },
+ { MP_ROM_QSTR(MP_QSTR_ST7789), (mp_obj_t)&st7789_ST7789_type },
+ { MP_ROM_QSTR(MP_QSTR_BLACK), MP_ROM_INT(BLACK) },
+ { MP_ROM_QSTR(MP_QSTR_BLUE), MP_ROM_INT(BLUE) },
+ { MP_ROM_QSTR(MP_QSTR_RED), MP_ROM_INT(RED) },
+ { MP_ROM_QSTR(MP_QSTR_GREEN), MP_ROM_INT(GREEN) },
+ { MP_ROM_QSTR(MP_QSTR_CYAN), MP_ROM_INT(CYAN) },
+ { MP_ROM_QSTR(MP_QSTR_MAGENTA), MP_ROM_INT(MAGENTA) },
+ { MP_ROM_QSTR(MP_QSTR_YELLOW), MP_ROM_INT(YELLOW) },
+ { MP_ROM_QSTR(MP_QSTR_WHITE), MP_ROM_INT(WHITE) },
+};
+
+STATIC MP_DEFINE_CONST_DICT (mp_module_st7789_globals, st7789_module_globals_table );
+
+const mp_obj_module_t mp_module_st7789 = {
+ .base = { &mp_type_module },
+ .globals = (mp_obj_dict_t*)&mp_module_st7789_globals,
+};
+
+MP_REGISTER_MODULE(MP_QSTR_st7789, mp_module_st7789, MODULE_ST7789_ENABLED);
diff --git a/st7789/st7789.h b/st7789/st7789.h
new file mode 100644
index 0000000..b36e39f
--- /dev/null
+++ b/st7789/st7789.h
@@ -0,0 +1,74 @@
+#ifndef __ST7789_H__
+#define __ST7789_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ST7789_TFTWIDTH_240 240
+#define ST7789_TFTHEIGHT_240 240
+
+#define ST7789_240x240_XSTART 0
+#define ST7789_240x240_YSTART 0
+
+
+// color modes
+#define COLOR_MODE_65K 0x50
+#define COLOR_MODE_262K 0x60
+#define COLOR_MODE_12BIT 0x03
+#define COLOR_MODE_16BIT 0x05
+#define COLOR_MODE_18BIT 0x06
+#define COLOR_MODE_16M 0x07
+
+// commands
+#define ST7789_NOP 0x00
+#define ST7789_SWRESET 0x01
+#define ST7789_RDDID 0x04
+#define ST7789_RDDST 0x09
+
+#define ST7789_SLPIN 0x10
+#define ST7789_SLPOUT 0x11
+#define ST7789_PTLON 0x12
+#define ST7789_NORON 0x13
+
+#define ST7789_INVOFF 0x20
+#define ST7789_INVON 0x21
+#define ST7789_DISPOFF 0x28
+#define ST7789_DISPON 0x29
+#define ST7789_CASET 0x2A
+#define ST7789_RASET 0x2B
+#define ST7789_RAMWR 0x2C
+#define ST7789_RAMRD 0x2E
+
+#define ST7789_PTLAR 0x30
+#define ST7789_COLMOD 0x3A
+#define ST7789_MADCTL 0x36
+
+#define ST7789_MADCTL_MY 0x80 // Page Address Order
+#define ST7789_MADCTL_MX 0x40 // Column Address Order
+#define ST7789_MADCTL_MV 0x20 // Page/Column Order
+#define ST7789_MADCTL_ML 0x10 // Line Address Order
+#define ST7789_MADCTL_MH 0x04 // Display Data Latch Order
+#define ST7789_MADCTL_RGB 0x00
+#define ST7789_MADCTL_BGR 0x08
+
+#define ST7789_RDID1 0xDA
+#define ST7789_RDID2 0xDB
+#define ST7789_RDID3 0xDC
+#define ST7789_RDID4 0xDD
+
+// Color definitions
+#define BLACK 0x0000
+#define BLUE 0x001F
+#define RED 0xF800
+#define GREEN 0x07E0
+#define CYAN 0x07FF
+#define MAGENTA 0xF81F
+#define YELLOW 0xFFE0
+#define WHITE 0xFFFF
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ST7789_H__ */