├── .gitignore ├── LICENSE ├── README.md ├── VERSION ├── docs └── ST7789.jpg └── st7789 ├── micropython.mk ├── st7789.c └── st7789.h /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .idea 3 | 4 | *.o 5 | *.P 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Ivan Belokobylskiy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ST7789 Driver for MicroPython 2 | ============================= 3 | 4 | 5 | Overview 6 | -------- 7 | This is a driver for MicroPython to handle cheap displays 8 | based on ST7789 chip. 9 | 10 |

11 | ST7789 display photo 12 |

13 | 14 | It supports both 240x240 and 135x240 variants of displays. 15 | 16 | It is written in pure C, so you have to build 17 | firmware by yourself. 18 | ESP8266, ESP32, and STM32 ports are supported for now. 19 | 20 | 21 | Building instruction 22 | --------------------- 23 | 24 | Prepare build tools as described in the manual. 25 | You should follow the instruction for building MicroPython and 26 | ensure that you can build the firmware without this display module. 27 | 28 | Clone this module alongside the MPY sources: 29 | 30 | git clone https://github.com/devbis/st7789_mpy.git 31 | 32 | Go to MicroPython ports directory and for ESP8266 run: 33 | 34 | cd micropython/ports/esp8266 35 | 36 | for ESP32: 37 | 38 | cd micropython/ports/esp32 39 | 40 | And then compile the module with specified USER_C_MODULES dir 41 | 42 | make USER_C_MODULES=../../../st7789_mpy/ all 43 | 44 | 45 | If you have other user modules, copy the st7789_driver/st7789 to 46 | the user modules directory 47 | 48 | Upload the resulting firmware to your MCU as usual with esptool.py 49 | (See 50 | [MicroPython docs](http://docs.micropython.org/en/latest/esp8266/tutorial/intro.html#deploying-the-firmware) 51 | for more info) 52 | 53 | make deploy 54 | 55 | Working examples 56 | ---------------- 57 | 58 | This module was tested on ESP32 and ESP8266 MCUs. 59 | 60 | You have to provide `machine.SPI` object and at least two pins for RESET and 61 | DC pins on the screen for the display object. 62 | 63 | 64 | # ESP 8266 65 | 66 | import machine 67 | import st7789 68 | spi = machine.SPI(1, baudrate=40000000, polarity=1) 69 | display = st7789.ST7789(spi, 240, 240, reset=machine.Pin(5, machine.Pin.OUT), dc=machine.Pin(4, machine.Pin.OUT)) 70 | display.init() 71 | 72 | 73 | For ESP32 modules you have to provide specific pins for SPI. 74 | Unfortunately, I was unable to run this display on SPI(1) interface. 75 | For machine.SPI(2) == VSPI you have to use 76 | 77 | - CLK: Pin(18) 78 | - MOSI: Pin(23) 79 | 80 | Other SPI pins are not used. 81 | 82 | 83 | # ESP32 84 | 85 | import machine 86 | import st7789 87 | spi = machine.SPI(2, baudrate=40000000, polarity=1, sck=machine.Pin(18), mosi=machine.Pin(23)) 88 | display = st7789.ST7789(spi, 240, 240, reset=machine.Pin(4, machine.Pin.OUT), dc=machine.Pin(2, machine.Pin.OUT)) 89 | display.init() 90 | 91 | 92 | I couldn't run the display on an SPI with baudrate higher than 40MHZ 93 | 94 | Also, the driver was tested on STM32 board: 95 | 96 | 97 | # STM32 98 | 99 | import machine 100 | import st7789 101 | spi = machine.SPI(2, baudrate=12000000, polarity=1) 102 | display = st7789.ST7789(spi, 135, 240, reset=machine.Pin('B3', machine.Pin.OUT), dc=machine.Pin('B6', machine.Pin.OUT)) 103 | display.init() 104 | 105 | 106 | Methods 107 | ------------- 108 | 109 | This driver supports only 16bit colors in RGB565 notation. 110 | 111 | 112 | - `ST7789.fill(color)` 113 | 114 | Fill the entire display with the specified color. 115 | 116 | - `ST7789.pixel(x, y, color)` 117 | 118 | Set the specified pixel to the given color. 119 | 120 | - `ST7789.line(x0, y0, x1, y1, color)` 121 | 122 | Draws a single line with the provided `color` from (`x0`, `y0`) to 123 | (`x1`, `y1`). 124 | 125 | - `ST7789.hline(x, y, length, color)` 126 | 127 | Draws a single horizontal line with the provided `color` and `length` 128 | in pixels. Along with `vline`, this is a fast version with reduced 129 | number of SPI calls. 130 | 131 | - `ST7789.vline(x, y, length, color)` 132 | 133 | Draws a single horizontal line with the provided `color` and `length` 134 | in pixels. 135 | 136 | - `ST7789.rect(x, y, width, height, color)` 137 | 138 | Draws a rectangle from (`x`, `y`) with corresponding dimensions 139 | 140 | - `ST7789.fill_rect(x, y, width, height, color)` 141 | 142 | Fill a rectangle starting from (`x`, `y`) coordinates 143 | 144 | - `ST7789.blit_buffer(buffer, x, y, width, height)` 145 | 146 | Copy bytes() or bytearray() content to the screen internal memory. 147 | Note: every color requires 2 bytes in the array 148 | 149 | Also, the module exposes predefined colors: 150 | `BLACK`, `BLUE`, `RED`, `GREEN`, `CYAN`, `MAGENTA`, `YELLOW`, and `WHITE` 151 | 152 | 153 | Helper functions 154 | ---------------- 155 | 156 | - `color565(r, g, b)` 157 | 158 | Pack a color into 2-bytes rgb565 format 159 | 160 | - `map_bitarray_to_rgb565(bitarray, buffer, width, color=WHITE, bg_color=BLACK)` 161 | 162 | Convert a bitarray to the rgb565 color buffer which is suitable for blitting. 163 | Bit 1 in bitarray is a pixel with `color` and 0 - with `bg_color`. 164 | 165 | This is a helper with a good performance to print text with a high 166 | resolution font. You can use an awesome tool 167 | https://github.com/peterhinch/micropython-font-to-py 168 | to generate a bitmap fonts from .ttf and use them as a frozen bytecode from 169 | the ROM memory. 170 | 171 | Performance 172 | ----------- 173 | 174 | For the comparison I used an excellent library for Arduino 175 | that can handle this screen. 176 | 177 | https://github.com/ananevilya/Arduino-ST7789-Library/ 178 | 179 | Also, I used my slow driver for this screen, written in pure python. 180 | 181 | https://github.com/devbis/st7789py_mpy/ 182 | 183 | I used these modules to draw a line from 0,0 to 239,239 184 | The table represents the time in milliseconds for each case 185 | 186 | | | Arduino-ST7789 | st7789py_mpy | st7789_mpy | 187 | |---------|----------------|--------------|---------------| 188 | | ESP8266 | 26 | 450 | 12 | 189 | | ESP32 | 23 | 450 | 47 | 190 | 191 | 192 | As you can see, the ESP32 module draws a line 4 times slower than 193 | the older ESP8266 module. 194 | 195 | 196 | Troubleshooting 197 | --------------- 198 | 199 | #### Overflow of iram1_0_seg 200 | 201 | When building a firmware for esp8266 you can see this failure message from 202 | the linker: 203 | 204 | LINK build/firmware.elf 205 | xtensa-lx106-elf-ld: build/firmware.elf section `.text' will not fit in region `iram1_0_seg' 206 | xtensa-lx106-elf-ld: region `iram1_0_seg' overflowed by 292 bytes 207 | Makefile:192: recipe for target 'build/firmware.elf' failed 208 | 209 | To fix this issue, you have to put st7789 module to irom0 section. 210 | Edit `esp8266_common.ld` file in the `ports/esp8266` dir and add a line 211 | 212 | *st7789/*.o(.literal* .text*) 213 | 214 | in the `.irom0.text : ALIGN(4)` section 215 | 216 | 217 | #### Unsupported dimensions 218 | 219 | This driver supports only 240x240 and 135x240 pixel displays. 220 | If you have a display with an unsupported resolution, you can pass 221 | `xstart` and `ystart` parameters to the display constructor to set the 222 | required offsets. 223 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.1.5 2 | -------------------------------------------------------------------------------- /docs/ST7789.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devbis/st7789_mpy/23e6f36acff40df7f1429cf5e0e5541f97c2540b/docs/ST7789.jpg -------------------------------------------------------------------------------- /st7789/micropython.mk: -------------------------------------------------------------------------------- 1 | ST7789_MOD_DIR := $(USERMOD_DIR) 2 | SRC_USERMOD += $(addprefix $(ST7789_MOD_DIR)/, \ 3 | st7789.c \ 4 | ) 5 | CFLAGS_USERMOD += -I$(ST7789_MOD_DIR) -DMODULE_ST7789_ENABLED=1 6 | # CFLAGS_USERMOD += -DEXPOSE_EXTRA_METHODS=1 7 | -------------------------------------------------------------------------------- /st7789/st7789.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ivan Belokobylskiy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #define __ST7789_VERSION__ "0.1.5" 26 | 27 | #include "py/obj.h" 28 | #include "py/runtime.h" 29 | #include "py/builtin.h" 30 | #include "py/mphal.h" 31 | #include "extmod/machine_spi.h" 32 | 33 | #include "st7789.h" 34 | 35 | // allow compiling against MP <=1.12 36 | #ifndef MP_ERROR_TEXT 37 | #define MP_ERROR_TEXT(a) a 38 | #endif 39 | 40 | #define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; } 41 | #define ABS(N) (((N)<0)?(-(N)):(N)) 42 | #define mp_hal_delay_ms(delay) (mp_hal_delay_us(delay * 1000)) 43 | 44 | #define CS_LOW() { if(self->cs) {mp_hal_pin_write(self->cs, 0);} } 45 | #define CS_HIGH() { if(self->cs) {mp_hal_pin_write(self->cs, 1);} } 46 | #define DC_LOW() mp_hal_pin_write(self->dc, 0) 47 | #define DC_HIGH() mp_hal_pin_write(self->dc, 1) 48 | #define RESET_LOW() mp_hal_pin_write(self->reset, 0) 49 | #define RESET_HIGH() mp_hal_pin_write(self->reset, 1) 50 | #define DISP_HIGH() mp_hal_pin_write(self->backlight, 1) 51 | #define DISP_LOW() mp_hal_pin_write(self->backlight, 0) 52 | 53 | 54 | STATIC void write_spi(mp_obj_base_t *spi_obj, const uint8_t *buf, int len) { 55 | mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)spi_obj->type->protocol; 56 | spi_p->transfer(spi_obj, len, buf, NULL); 57 | } 58 | 59 | // this is the actual C-structure for our new object 60 | typedef struct _st7789_ST7789_obj_t { 61 | mp_obj_base_t base; 62 | 63 | mp_obj_base_t *spi_obj; 64 | uint8_t width; 65 | uint8_t height; 66 | uint8_t xstart; 67 | uint8_t ystart; 68 | mp_hal_pin_obj_t reset; 69 | mp_hal_pin_obj_t dc; 70 | mp_hal_pin_obj_t cs; 71 | mp_hal_pin_obj_t backlight; 72 | } st7789_ST7789_obj_t; 73 | 74 | 75 | // just a definition 76 | mp_obj_t st7789_ST7789_make_new( const mp_obj_type_t *type, 77 | size_t n_args, 78 | size_t n_kw, 79 | const mp_obj_t *args ); 80 | STATIC void st7789_ST7789_print( const mp_print_t *print, 81 | mp_obj_t self_in, 82 | mp_print_kind_t kind ) { 83 | (void)kind; 84 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in); 85 | mp_printf(print, "", self->width, self->height, self->spi_obj); 86 | } 87 | 88 | /* methods start */ 89 | 90 | STATIC void write_cmd(st7789_ST7789_obj_t *self, uint8_t cmd, const uint8_t *data, int len) { 91 | CS_LOW() 92 | if (cmd) { 93 | DC_LOW(); 94 | write_spi(self->spi_obj, &cmd, 1); 95 | } 96 | if (len > 0) { 97 | DC_HIGH(); 98 | write_spi(self->spi_obj, data, len); 99 | } 100 | CS_HIGH() 101 | } 102 | 103 | STATIC void set_window(st7789_ST7789_obj_t *self, uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { 104 | if (x0 > x1 || x1 >= self->width) { 105 | return; 106 | } 107 | if (y0 > y1 || y1 >= self->height) { 108 | return; 109 | } 110 | uint8_t bufx[4] = {(x0+self->xstart) >> 8, (x0+self->xstart) & 0xFF, (x1+self->xstart) >> 8, (x1+self->xstart) & 0xFF}; 111 | uint8_t bufy[4] = {(y0+self->ystart) >> 8, (y0+self->ystart) & 0xFF, (y1+self->ystart) >> 8, (y1+self->ystart) & 0xFF}; 112 | write_cmd(self, ST7789_CASET, bufx, 4); 113 | write_cmd(self, ST7789_RASET, bufy, 4); 114 | write_cmd(self, ST7789_RAMWR, NULL, 0); 115 | } 116 | 117 | STATIC void fill_color_buffer(mp_obj_base_t* spi_obj, uint16_t color, int length) { 118 | uint8_t hi = color >> 8, lo = color; 119 | const int buffer_pixel_size = 128; 120 | int chunks = length / buffer_pixel_size; 121 | int rest = length % buffer_pixel_size; 122 | 123 | uint8_t buffer[buffer_pixel_size * 2]; // 128 pixels 124 | // fill buffer with color data 125 | for (int i = 0; i < length && i < buffer_pixel_size; i++) { 126 | buffer[i*2] = hi; 127 | buffer[i*2 + 1] = lo; 128 | } 129 | 130 | if (chunks) { 131 | for (int j = 0; j < chunks; j ++) { 132 | write_spi(spi_obj, buffer, buffer_pixel_size*2); 133 | } 134 | } 135 | if (rest) { 136 | write_spi(spi_obj, buffer, rest*2); 137 | } 138 | } 139 | 140 | 141 | STATIC void draw_pixel(st7789_ST7789_obj_t *self, uint8_t x, uint8_t y, uint16_t color) { 142 | uint8_t hi = color >> 8, lo = color; 143 | set_window(self, x, y, x, y); 144 | DC_HIGH(); 145 | CS_LOW(); 146 | write_spi(self->spi_obj, &hi, 1); 147 | write_spi(self->spi_obj, &lo, 1); 148 | CS_HIGH(); 149 | } 150 | 151 | 152 | STATIC void fast_hline(st7789_ST7789_obj_t *self, uint8_t x, uint8_t y, uint16_t w, uint16_t color) { 153 | set_window(self, x, y, x + w - 1, y); 154 | DC_HIGH(); 155 | CS_LOW(); 156 | fill_color_buffer(self->spi_obj, color, w); 157 | CS_HIGH(); 158 | } 159 | 160 | 161 | STATIC void fast_vline(st7789_ST7789_obj_t *self, uint8_t x, uint8_t y, uint16_t w, uint16_t color) { 162 | set_window(self, x, y, x, y + w - 1); 163 | DC_HIGH(); 164 | CS_LOW(); 165 | fill_color_buffer(self->spi_obj, color, w); 166 | CS_HIGH(); 167 | } 168 | 169 | 170 | STATIC mp_obj_t st7789_ST7789_hard_reset(mp_obj_t self_in) { 171 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in); 172 | 173 | CS_LOW(); 174 | RESET_HIGH(); 175 | mp_hal_delay_ms(50); 176 | RESET_LOW(); 177 | mp_hal_delay_ms(50); 178 | RESET_HIGH(); 179 | mp_hal_delay_ms(150); 180 | CS_HIGH(); 181 | return mp_const_none; 182 | } 183 | 184 | STATIC mp_obj_t st7789_ST7789_soft_reset(mp_obj_t self_in) { 185 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in); 186 | 187 | write_cmd(self, ST7789_SWRESET, NULL, 0); 188 | mp_hal_delay_ms(150); 189 | return mp_const_none; 190 | } 191 | 192 | // do not expose extra method to reduce size 193 | #ifdef EXPOSE_EXTRA_METHODS 194 | STATIC mp_obj_t st7789_ST7789_write(mp_obj_t self_in, mp_obj_t command, mp_obj_t data) { 195 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in); 196 | 197 | mp_buffer_info_t src; 198 | if (data == mp_const_none) { 199 | write_cmd(self, (uint8_t)mp_obj_get_int(command), NULL, 0); 200 | } else { 201 | mp_get_buffer_raise(data, &src, MP_BUFFER_READ); 202 | write_cmd(self, (uint8_t)mp_obj_get_int(command), (const uint8_t*)src.buf, src.len); 203 | } 204 | 205 | return mp_const_none; 206 | } 207 | MP_DEFINE_CONST_FUN_OBJ_3(st7789_ST7789_write_obj, st7789_ST7789_write); 208 | 209 | MP_DEFINE_CONST_FUN_OBJ_1(st7789_ST7789_hard_reset_obj, st7789_ST7789_hard_reset); 210 | MP_DEFINE_CONST_FUN_OBJ_1(st7789_ST7789_soft_reset_obj, st7789_ST7789_soft_reset); 211 | 212 | STATIC mp_obj_t st7789_ST7789_sleep_mode(mp_obj_t self_in, mp_obj_t value) { 213 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in); 214 | if(mp_obj_is_true(value)) { 215 | write_cmd(self, ST7789_SLPIN, NULL, 0); 216 | } else { 217 | write_cmd(self, ST7789_SLPOUT, NULL, 0); 218 | } 219 | return mp_const_none; 220 | } 221 | MP_DEFINE_CONST_FUN_OBJ_2(st7789_ST7789_sleep_mode_obj, st7789_ST7789_sleep_mode); 222 | 223 | STATIC mp_obj_t st7789_ST7789_set_window(size_t n_args, const mp_obj_t *args) { 224 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]); 225 | mp_int_t x0 = mp_obj_get_int(args[1]); 226 | mp_int_t x1 = mp_obj_get_int(args[2]); 227 | mp_int_t y0 = mp_obj_get_int(args[3]); 228 | mp_int_t y1 = mp_obj_get_int(args[4]); 229 | 230 | set_window(self, x0, y0, x1, y1); 231 | return mp_const_none; 232 | } 233 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_set_window_obj, 5, 5, st7789_ST7789_set_window); 234 | 235 | #endif 236 | 237 | STATIC mp_obj_t st7789_ST7789_inversion_mode(mp_obj_t self_in, mp_obj_t value) { 238 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in); 239 | if(mp_obj_is_true(value)) { 240 | write_cmd(self, ST7789_INVON, NULL, 0); 241 | } else { 242 | write_cmd(self, ST7789_INVOFF, NULL, 0); 243 | } 244 | return mp_const_none; 245 | } 246 | MP_DEFINE_CONST_FUN_OBJ_2(st7789_ST7789_inversion_mode_obj, st7789_ST7789_inversion_mode); 247 | 248 | 249 | STATIC mp_obj_t st7789_ST7789_fill_rect(size_t n_args, const mp_obj_t *args) { 250 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]); 251 | mp_int_t x = mp_obj_get_int(args[1]); 252 | mp_int_t y = mp_obj_get_int(args[2]); 253 | mp_int_t w = mp_obj_get_int(args[3]); 254 | mp_int_t h = mp_obj_get_int(args[4]); 255 | mp_int_t color = mp_obj_get_int(args[5]); 256 | 257 | set_window(self, x, y, x + w - 1, y + h - 1); 258 | DC_HIGH(); 259 | CS_LOW(); 260 | fill_color_buffer(self->spi_obj, color, w * h); 261 | CS_HIGH(); 262 | 263 | return mp_const_none; 264 | } 265 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_fill_rect_obj, 6, 6, st7789_ST7789_fill_rect); 266 | 267 | 268 | STATIC mp_obj_t st7789_ST7789_fill(mp_obj_t self_in, mp_obj_t _color) { 269 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in); 270 | mp_int_t color = mp_obj_get_int(_color); 271 | 272 | set_window(self, 0, 0, self->width - 1, self->height - 1); 273 | DC_HIGH(); 274 | CS_LOW(); 275 | fill_color_buffer(self->spi_obj, color, self->width * self->height); 276 | CS_HIGH(); 277 | 278 | return mp_const_none; 279 | } 280 | STATIC MP_DEFINE_CONST_FUN_OBJ_2(st7789_ST7789_fill_obj, st7789_ST7789_fill); 281 | 282 | 283 | STATIC mp_obj_t st7789_ST7789_pixel(size_t n_args, const mp_obj_t *args) { 284 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]); 285 | mp_int_t x = mp_obj_get_int(args[1]); 286 | mp_int_t y = mp_obj_get_int(args[2]); 287 | mp_int_t color = mp_obj_get_int(args[3]); 288 | 289 | draw_pixel(self, x, y, color); 290 | 291 | return mp_const_none; 292 | } 293 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_pixel_obj, 4, 4, st7789_ST7789_pixel); 294 | 295 | 296 | STATIC mp_obj_t st7789_ST7789_line(size_t n_args, const mp_obj_t *args) { 297 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]); 298 | mp_int_t x0 = mp_obj_get_int(args[1]); 299 | mp_int_t y0 = mp_obj_get_int(args[2]); 300 | mp_int_t x1 = mp_obj_get_int(args[3]); 301 | mp_int_t y1 = mp_obj_get_int(args[4]); 302 | mp_int_t color = mp_obj_get_int(args[5]); 303 | 304 | bool steep = ABS(y1 - y0) > ABS(x1 - x0); 305 | if (steep) { 306 | _swap_int16_t(x0, y0); 307 | _swap_int16_t(x1, y1); 308 | } 309 | 310 | if (x0 > x1) { 311 | _swap_int16_t(x0, x1); 312 | _swap_int16_t(y0, y1); 313 | } 314 | 315 | int16_t dx = x1 - x0, dy = ABS(y1 - y0); 316 | int16_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0; 317 | 318 | if (y0 < y1) ystep = 1; 319 | 320 | // Split into steep and not steep for FastH/V separation 321 | if (steep) { 322 | for (; x0 <= x1; x0++) { 323 | dlen++; 324 | err -= dy; 325 | if (err < 0) { 326 | err += dx; 327 | if (dlen == 1) draw_pixel(self, y0, xs, color); 328 | else fast_vline(self, y0, xs, dlen, color); 329 | dlen = 0; y0 += ystep; xs = x0 + 1; 330 | } 331 | } 332 | if (dlen) fast_vline(self, y0, xs, dlen, color); 333 | } 334 | else 335 | { 336 | for (; x0 <= x1; x0++) { 337 | dlen++; 338 | err -= dy; 339 | if (err < 0) { 340 | err += dx; 341 | if (dlen == 1) draw_pixel(self, xs, y0, color); 342 | else fast_hline(self, xs, y0, dlen, color); 343 | dlen = 0; y0 += ystep; xs = x0 + 1; 344 | } 345 | } 346 | if (dlen) fast_hline(self, xs, y0, dlen, color); 347 | } 348 | return mp_const_none; 349 | } 350 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_line_obj, 6, 6, st7789_ST7789_line); 351 | 352 | 353 | STATIC mp_obj_t st7789_ST7789_blit_buffer(size_t n_args, const mp_obj_t *args) { 354 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]); 355 | mp_buffer_info_t buf_info; 356 | mp_get_buffer_raise(args[1], &buf_info, MP_BUFFER_READ); 357 | mp_int_t x = mp_obj_get_int(args[2]); 358 | mp_int_t y = mp_obj_get_int(args[3]); 359 | mp_int_t w = mp_obj_get_int(args[4]); 360 | mp_int_t h = mp_obj_get_int(args[5]); 361 | 362 | set_window(self, x, y, x + w - 1, y + h - 1); 363 | DC_HIGH(); 364 | CS_LOW(); 365 | 366 | const int buf_size = 256; 367 | int limit = MIN(buf_info.len, w * h * 2); 368 | int chunks = limit / buf_size; 369 | int rest = limit % buf_size; 370 | int i = 0; 371 | for (; i < chunks; i ++) { 372 | write_spi(self->spi_obj, (const uint8_t*)buf_info.buf + i*buf_size, buf_size); 373 | } 374 | if (rest) { 375 | write_spi(self->spi_obj, (const uint8_t*)buf_info.buf + i*buf_size, rest); 376 | } 377 | CS_HIGH(); 378 | 379 | return mp_const_none; 380 | } 381 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_blit_buffer_obj, 6, 6, st7789_ST7789_blit_buffer); 382 | 383 | 384 | STATIC mp_obj_t st7789_ST7789_init(mp_obj_t self_in) { 385 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in); 386 | st7789_ST7789_hard_reset(self_in); 387 | st7789_ST7789_soft_reset(self_in); 388 | write_cmd(self, ST7789_SLPOUT, NULL, 0); 389 | 390 | const uint8_t color_mode[] = { COLOR_MODE_65K | COLOR_MODE_16BIT}; 391 | write_cmd(self, ST7789_COLMOD, color_mode, 1); 392 | mp_hal_delay_ms(10); 393 | const uint8_t madctl[] = { ST7789_MADCTL_ML | ST7789_MADCTL_RGB }; 394 | write_cmd(self, ST7789_MADCTL, madctl, 1); 395 | 396 | write_cmd(self, ST7789_INVON, NULL, 0); 397 | mp_hal_delay_ms(10); 398 | write_cmd(self, ST7789_NORON, NULL, 0); 399 | mp_hal_delay_ms(10); 400 | 401 | const mp_obj_t args[] = { 402 | self_in, 403 | mp_obj_new_int(0), 404 | mp_obj_new_int(0), 405 | mp_obj_new_int(self->width), 406 | mp_obj_new_int(self->height), 407 | mp_obj_new_int(BLACK) 408 | }; 409 | st7789_ST7789_fill_rect(6, args); 410 | write_cmd(self, ST7789_DISPON, NULL, 0); 411 | mp_hal_delay_ms(100); 412 | 413 | return mp_const_none; 414 | } 415 | MP_DEFINE_CONST_FUN_OBJ_1(st7789_ST7789_init_obj, st7789_ST7789_init); 416 | 417 | STATIC mp_obj_t st7789_ST7789_on(mp_obj_t self_in) { 418 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in); 419 | DISP_HIGH(); 420 | mp_hal_delay_ms(10); 421 | 422 | return mp_const_none; 423 | } 424 | MP_DEFINE_CONST_FUN_OBJ_1(st7789_ST7789_on_obj, st7789_ST7789_on); 425 | 426 | STATIC mp_obj_t st7789_ST7789_off(mp_obj_t self_in) { 427 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(self_in); 428 | DISP_LOW(); 429 | mp_hal_delay_ms(10); 430 | 431 | return mp_const_none; 432 | } 433 | MP_DEFINE_CONST_FUN_OBJ_1(st7789_ST7789_off_obj, st7789_ST7789_off); 434 | 435 | STATIC mp_obj_t st7789_ST7789_hline(size_t n_args, const mp_obj_t *args) { 436 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]); 437 | mp_int_t x = mp_obj_get_int(args[1]); 438 | mp_int_t y = mp_obj_get_int(args[2]); 439 | mp_int_t w = mp_obj_get_int(args[3]); 440 | mp_int_t color = mp_obj_get_int(args[4]); 441 | 442 | fast_hline(self, x, y, w, color); 443 | 444 | return mp_const_none; 445 | } 446 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_hline_obj, 5, 5, st7789_ST7789_hline); 447 | 448 | 449 | STATIC mp_obj_t st7789_ST7789_vline(size_t n_args, const mp_obj_t *args) { 450 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]); 451 | mp_int_t x = mp_obj_get_int(args[1]); 452 | mp_int_t y = mp_obj_get_int(args[2]); 453 | mp_int_t w = mp_obj_get_int(args[3]); 454 | mp_int_t color = mp_obj_get_int(args[4]); 455 | 456 | fast_vline(self, x, y, w, color); 457 | 458 | return mp_const_none; 459 | } 460 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_vline_obj, 5, 5, st7789_ST7789_vline); 461 | 462 | 463 | STATIC mp_obj_t st7789_ST7789_rect(size_t n_args, const mp_obj_t *args) { 464 | st7789_ST7789_obj_t *self = MP_OBJ_TO_PTR(args[0]); 465 | mp_int_t x = mp_obj_get_int(args[1]); 466 | mp_int_t y = mp_obj_get_int(args[2]); 467 | mp_int_t w = mp_obj_get_int(args[3]); 468 | mp_int_t h = mp_obj_get_int(args[4]); 469 | mp_int_t color = mp_obj_get_int(args[5]); 470 | 471 | fast_hline(self, x, y, w, color); 472 | fast_vline(self, x, y, h, color); 473 | fast_hline(self, x, y + h - 1, w, color); 474 | fast_vline(self, x + w - 1, y, h, color); 475 | return mp_const_none; 476 | } 477 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(st7789_ST7789_rect_obj, 6, 6, st7789_ST7789_rect); 478 | 479 | 480 | STATIC const mp_rom_map_elem_t st7789_ST7789_locals_dict_table[] = { 481 | // Do not expose internal functions to fit iram_0 section 482 | #ifdef EXPOSE_EXTRA_METHODS 483 | { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&st7789_ST7789_write_obj) }, 484 | { MP_ROM_QSTR(MP_QSTR_hard_reset), MP_ROM_PTR(&st7789_ST7789_hard_reset_obj) }, 485 | { MP_ROM_QSTR(MP_QSTR_soft_reset), MP_ROM_PTR(&st7789_ST7789_soft_reset_obj) }, 486 | { MP_ROM_QSTR(MP_QSTR_sleep_mode), MP_ROM_PTR(&st7789_ST7789_sleep_mode_obj) }, 487 | { MP_ROM_QSTR(MP_QSTR_inversion_mode), MP_ROM_PTR(&st7789_ST7789_inversion_mode_obj) }, 488 | { MP_ROM_QSTR(MP_QSTR_set_window), MP_ROM_PTR(&st7789_ST7789_set_window_obj) }, 489 | #endif 490 | { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&st7789_ST7789_init_obj) }, 491 | { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&st7789_ST7789_on_obj) }, 492 | { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&st7789_ST7789_off_obj) }, 493 | { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&st7789_ST7789_pixel_obj) }, 494 | { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&st7789_ST7789_line_obj) }, 495 | { MP_ROM_QSTR(MP_QSTR_blit_buffer), MP_ROM_PTR(&st7789_ST7789_blit_buffer_obj) }, 496 | { MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&st7789_ST7789_fill_rect_obj) }, 497 | { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&st7789_ST7789_fill_obj) }, 498 | { MP_ROM_QSTR(MP_QSTR_hline), MP_ROM_PTR(&st7789_ST7789_hline_obj) }, 499 | { MP_ROM_QSTR(MP_QSTR_vline), MP_ROM_PTR(&st7789_ST7789_vline_obj) }, 500 | { MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&st7789_ST7789_rect_obj) }, 501 | }; 502 | 503 | STATIC MP_DEFINE_CONST_DICT(st7789_ST7789_locals_dict, st7789_ST7789_locals_dict_table); 504 | /* methods end */ 505 | 506 | 507 | const mp_obj_type_t st7789_ST7789_type = { 508 | { &mp_type_type }, 509 | .name = MP_QSTR_ST7789, 510 | .print = st7789_ST7789_print, 511 | .make_new = st7789_ST7789_make_new, 512 | .locals_dict = (mp_obj_dict_t*)&st7789_ST7789_locals_dict, 513 | }; 514 | 515 | mp_obj_t st7789_ST7789_make_new(const mp_obj_type_t *type, 516 | size_t n_args, 517 | size_t n_kw, 518 | const mp_obj_t *all_args ) { 519 | enum { 520 | ARG_spi, ARG_width, ARG_height, ARG_reset, ARG_dc, ARG_cs, 521 | ARG_backlight, ARG_xstart, ARG_ystart 522 | }; 523 | static const mp_arg_t allowed_args[] = { 524 | { MP_QSTR_spi, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, 525 | { MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} }, 526 | { MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} }, 527 | { MP_QSTR_reset, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, 528 | { MP_QSTR_dc, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, 529 | { MP_QSTR_cs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, 530 | { MP_QSTR_backlight, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, 531 | { MP_QSTR_xstart, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, 532 | { MP_QSTR_ystart, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, 533 | }; 534 | mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 535 | mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); 536 | 537 | // create new object 538 | st7789_ST7789_obj_t *self = m_new_obj(st7789_ST7789_obj_t); 539 | self->base.type = &st7789_ST7789_type; 540 | 541 | // set parameters 542 | mp_obj_base_t *spi_obj = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[ARG_spi].u_obj); 543 | self->spi_obj = spi_obj; 544 | self->width = args[ARG_width].u_int; 545 | self->height = args[ARG_height].u_int; 546 | 547 | if (args[ARG_xstart].u_int >= 0 && args[ARG_ystart].u_int >= 0) { 548 | self->xstart = args[ARG_xstart].u_int; 549 | self->ystart = args[ARG_ystart].u_int; 550 | } else if (self->width == 240 && self->height == 240) { 551 | self->xstart = ST7789_240x240_XSTART; 552 | self->ystart = ST7789_240x240_YSTART; 553 | } else if (self->width == 135 && self->height == 240) { 554 | self->xstart = ST7789_135x240_XSTART; 555 | self->ystart = ST7789_135x240_YSTART; 556 | } else { 557 | mp_raise_ValueError(MP_ERROR_TEXT("Unsupported display. Only 240x240 and 135x240 are supported without xstart and ystart provided")); 558 | } 559 | 560 | if (args[ARG_reset].u_obj == MP_OBJ_NULL 561 | || args[ARG_dc].u_obj == MP_OBJ_NULL) { 562 | mp_raise_ValueError(MP_ERROR_TEXT("must specify all of reset/dc pins")); 563 | } 564 | 565 | self->reset = mp_hal_get_pin_obj(args[ARG_reset].u_obj); 566 | self->dc = mp_hal_get_pin_obj(args[ARG_dc].u_obj); 567 | 568 | if (args[ARG_cs].u_obj != MP_OBJ_NULL) { 569 | self->cs = mp_hal_get_pin_obj(args[ARG_cs].u_obj); 570 | } 571 | if (args[ARG_backlight].u_obj != MP_OBJ_NULL) { 572 | self->backlight = mp_hal_get_pin_obj(args[ARG_backlight].u_obj); 573 | } 574 | 575 | return MP_OBJ_FROM_PTR(self); 576 | } 577 | 578 | 579 | STATIC uint16_t color565(uint8_t r, uint8_t g, uint8_t b) { 580 | return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3); 581 | } 582 | 583 | 584 | STATIC mp_obj_t st7789_color565(mp_obj_t r, mp_obj_t g, mp_obj_t b) { 585 | return MP_OBJ_NEW_SMALL_INT(color565( 586 | (uint8_t)mp_obj_get_int(r), 587 | (uint8_t)mp_obj_get_int(g), 588 | (uint8_t)mp_obj_get_int(b) 589 | )); 590 | } 591 | STATIC MP_DEFINE_CONST_FUN_OBJ_3(st7789_color565_obj, st7789_color565); 592 | 593 | 594 | STATIC void map_bitarray_to_rgb565(uint8_t const *bitarray, uint8_t *buffer, int length, int width, 595 | uint16_t color, uint16_t bg_color) { 596 | int row_pos = 0; 597 | for (int i = 0; i < length; i++) { 598 | uint8_t byte = bitarray[i]; 599 | for (int bi = 7; bi >= 0; bi--) { 600 | uint8_t b = byte & (1 << bi); 601 | uint16_t cur_color = b ? color : bg_color; 602 | *buffer = (cur_color & 0xff00) >> 8; 603 | buffer ++; 604 | *buffer = cur_color & 0xff; 605 | buffer ++; 606 | 607 | row_pos ++; 608 | if (row_pos >= width) { 609 | row_pos = 0; 610 | break; 611 | } 612 | } 613 | } 614 | } 615 | 616 | 617 | STATIC mp_obj_t st7789_map_bitarray_to_rgb565(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { 618 | enum { ARG_bitarray, ARG_buffer, ARG_width, ARG_color, ARG_bg_color }; 619 | static const mp_arg_t allowed_args[] = { 620 | { MP_QSTR_bitarray, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, 621 | { MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, 622 | { MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = -1} }, 623 | { MP_QSTR_color, MP_ARG_INT, {.u_int = WHITE} }, 624 | { MP_QSTR_bg_color, MP_ARG_INT, {.u_int = BLACK } }, 625 | }; 626 | mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 627 | mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); 628 | 629 | mp_buffer_info_t bitarray_info; 630 | mp_buffer_info_t buffer_info; 631 | mp_get_buffer_raise(args[ARG_bitarray].u_obj, &bitarray_info, MP_BUFFER_READ); 632 | mp_get_buffer_raise(args[ARG_buffer].u_obj, &buffer_info, MP_BUFFER_WRITE); 633 | mp_int_t width = args[ARG_width].u_int; 634 | mp_int_t color = args[ARG_color].u_int; 635 | mp_int_t bg_color = args[ARG_bg_color].u_int; 636 | 637 | map_bitarray_to_rgb565(bitarray_info.buf, buffer_info.buf, bitarray_info.len, width, color, bg_color); 638 | 639 | return mp_const_none; 640 | } 641 | STATIC MP_DEFINE_CONST_FUN_OBJ_KW(st7789_map_bitarray_to_rgb565_obj, 3, st7789_map_bitarray_to_rgb565); 642 | 643 | 644 | STATIC const mp_map_elem_t st7789_module_globals_table[] = { 645 | { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_st7789) }, 646 | { MP_ROM_QSTR(MP_QSTR_color565), (mp_obj_t)&st7789_color565_obj }, 647 | { MP_ROM_QSTR(MP_QSTR_map_bitarray_to_rgb565), (mp_obj_t)&st7789_map_bitarray_to_rgb565_obj }, 648 | { MP_ROM_QSTR(MP_QSTR_ST7789), (mp_obj_t)&st7789_ST7789_type }, 649 | { MP_ROM_QSTR(MP_QSTR_BLACK), MP_ROM_INT(BLACK) }, 650 | { MP_ROM_QSTR(MP_QSTR_BLUE), MP_ROM_INT(BLUE) }, 651 | { MP_ROM_QSTR(MP_QSTR_RED), MP_ROM_INT(RED) }, 652 | { MP_ROM_QSTR(MP_QSTR_GREEN), MP_ROM_INT(GREEN) }, 653 | { MP_ROM_QSTR(MP_QSTR_CYAN), MP_ROM_INT(CYAN) }, 654 | { MP_ROM_QSTR(MP_QSTR_MAGENTA), MP_ROM_INT(MAGENTA) }, 655 | { MP_ROM_QSTR(MP_QSTR_YELLOW), MP_ROM_INT(YELLOW) }, 656 | { MP_ROM_QSTR(MP_QSTR_WHITE), MP_ROM_INT(WHITE) }, 657 | }; 658 | 659 | STATIC MP_DEFINE_CONST_DICT (mp_module_st7789_globals, st7789_module_globals_table ); 660 | 661 | const mp_obj_module_t mp_module_st7789 = { 662 | .base = { &mp_type_module }, 663 | .globals = (mp_obj_dict_t*)&mp_module_st7789_globals, 664 | }; 665 | 666 | MP_REGISTER_MODULE(MP_QSTR_st7789, mp_module_st7789, MODULE_ST7789_ENABLED); 667 | -------------------------------------------------------------------------------- /st7789/st7789.h: -------------------------------------------------------------------------------- 1 | #ifndef __ST7789_H__ 2 | #define __ST7789_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #define ST7789_240x240_XSTART 0 9 | #define ST7789_240x240_YSTART 0 10 | #define ST7789_135x240_XSTART 52 11 | #define ST7789_135x240_YSTART 40 12 | 13 | 14 | // color modes 15 | #define COLOR_MODE_65K 0x50 16 | #define COLOR_MODE_262K 0x60 17 | #define COLOR_MODE_12BIT 0x03 18 | #define COLOR_MODE_16BIT 0x05 19 | #define COLOR_MODE_18BIT 0x06 20 | #define COLOR_MODE_16M 0x07 21 | 22 | // commands 23 | #define ST7789_NOP 0x00 24 | #define ST7789_SWRESET 0x01 25 | #define ST7789_RDDID 0x04 26 | #define ST7789_RDDST 0x09 27 | 28 | #define ST7789_SLPIN 0x10 29 | #define ST7789_SLPOUT 0x11 30 | #define ST7789_PTLON 0x12 31 | #define ST7789_NORON 0x13 32 | 33 | #define ST7789_INVOFF 0x20 34 | #define ST7789_INVON 0x21 35 | #define ST7789_DISPOFF 0x28 36 | #define ST7789_DISPON 0x29 37 | #define ST7789_CASET 0x2A 38 | #define ST7789_RASET 0x2B 39 | #define ST7789_RAMWR 0x2C 40 | #define ST7789_RAMRD 0x2E 41 | 42 | #define ST7789_PTLAR 0x30 43 | #define ST7789_COLMOD 0x3A 44 | #define ST7789_MADCTL 0x36 45 | 46 | #define ST7789_MADCTL_MY 0x80 // Page Address Order 47 | #define ST7789_MADCTL_MX 0x40 // Column Address Order 48 | #define ST7789_MADCTL_MV 0x20 // Page/Column Order 49 | #define ST7789_MADCTL_ML 0x10 // Line Address Order 50 | #define ST7789_MADCTL_MH 0x04 // Display Data Latch Order 51 | #define ST7789_MADCTL_RGB 0x00 52 | #define ST7789_MADCTL_BGR 0x08 53 | 54 | #define ST7789_RDID1 0xDA 55 | #define ST7789_RDID2 0xDB 56 | #define ST7789_RDID3 0xDC 57 | #define ST7789_RDID4 0xDD 58 | 59 | // Color definitions 60 | #define BLACK 0x0000 61 | #define BLUE 0x001F 62 | #define RED 0xF800 63 | #define GREEN 0x07E0 64 | #define CYAN 0x07FF 65 | #define MAGENTA 0xF81F 66 | #define YELLOW 0xFFE0 67 | #define WHITE 0xFFFF 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif /* __cplusplus */ 72 | 73 | #endif /* __ST7789_H__ */ 74 | --------------------------------------------------------------------------------