aboutsummaryrefslogtreecommitdiff
path: root/drivers/qwiic/micro_oled.c
diff options
context:
space:
mode:
authorishtob <ishtob@gmail.com>2018-12-04 11:04:57 -0500
committerDrashna Jaelre <drashna@live.com>2018-12-04 08:04:57 -0800
commit4099536c0e7a099b181a80e483b4b95f389b5a7e (patch)
tree311c8a15013cce5ee9275fe8654c9b52dc9ca1e2 /drivers/qwiic/micro_oled.c
parent4bb28d2df092408a7a0e32a8d8ab47b7f4008fcd (diff)
downloadqmk_firmware-4099536c0e7a099b181a80e483b4b95f389b5a7e.tar.gz
qmk_firmware-4099536c0e7a099b181a80e483b4b95f389b5a7e.zip
adding Hadron v3 keyboard, QWIIC devices support, haptic feedback support (#4462)
* add initial support for hadron ver3 * add initial support for hadron ver3 * pull qwiic support for micro_led to be modified for use in hadron's 64x24 ssd1306 oled display * initial work on OLED using qwiic driver * early work to get 128x32 oled working by redefining qwiic micro oled parameters. Currently working, but would affect qwiic's micro oled functionality * moved oled defines to config.h and added ifndef to micro_oled driver * WORKING :D - note, still work in progress to get the start location correct on the 128x32 display. * added equation to automatically calculate display offset based on screen width * adding time-out timer to oled display * changed read lock staus via read_led_state * lock indications fixes * Added scroll lock indication to oled * add support for DRV2605 haptic driver * Improve readabiity of DRV2605 driver. -added typedef for waveform library -added unions for registers * Update keyboards/hadron/ver2/keymaps/default/config.h Co-Authored-By: ishtob <ishtob@gmail.com> * Update keyboards/hadron/ver2/keymaps/default/config.h Co-Authored-By: ishtob <ishtob@gmail.com> * Update keyboards/hadron/ver2/keymaps/default/config.h Co-Authored-By: ishtob <ishtob@gmail.com> * Update keyboards/hadron/ver2/keymaps/default/config.h Co-Authored-By: ishtob <ishtob@gmail.com> * Fixes for PR * PR fixes * fix old persistent layer function to use new set_single_persistent_default_layer * fix issues with changing makefile defines that broken per-key haptic pulse * Comment fixes * Add definable parameter and auto-calibration based on motor choice
Diffstat (limited to 'drivers/qwiic/micro_oled.c')
-rw-r--r--drivers/qwiic/micro_oled.c691
1 files changed, 691 insertions, 0 deletions
diff --git a/drivers/qwiic/micro_oled.c b/drivers/qwiic/micro_oled.c
new file mode 100644
index 000000000..35c5d6ee1
--- /dev/null
+++ b/drivers/qwiic/micro_oled.c
@@ -0,0 +1,691 @@
1/* Jim Lindblom @ SparkFun Electronics
2 * October 26, 2014
3 * https://github.com/sparkfun/Micro_OLED_Breakout/tree/master/Firmware/Arduino/libraries/SFE_MicroOLED
4 *
5 * Modified by:
6 * Emil Varughese @ Edwin Robotics Pvt. Ltd.
7 * July 27, 2015
8 * https://github.com/emil01/SparkFun_Micro_OLED_Arduino_Library/
9 *
10 * This code was heavily based around the MicroView library, written by GeekAmmo
11 * (https://github.com/geekammo/MicroView-Arduino-Library).
12 *
13 * Adapted for QMK by:
14 * Jack Humbert <jack.humb@gmail.com>
15 * October 11, 2018
16 *
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29 */
30#include "micro_oled.h"
31#include <stdlib.h>
32#include "util/font5x7.h"
33#include "util/font8x16.h"
34#include "string.h"
35
36#define TOTALFONTS 2
37const unsigned char * fonts_pointer[]= { font5x7, font8x16 };
38
39uint8_t foreColor,drawMode,fontWidth, fontHeight, fontType, fontStartChar, fontTotalChar, cursorX, cursorY;
40uint16_t fontMapWidth;
41
42#define _BV(x) (1 << (x))
43#define swap(a, b) { uint8_t t = a; a = b; b = t; }
44
45uint8_t micro_oled_transfer_buffer[20];
46static uint8_t micro_oled_screen_current[LCDWIDTH*LCDWIDTH/8] = { 0 };
47
48/* LCD Memory organised in 64 horizontal pixel and 6 rows of byte
49 B B .............B -----
50 y y .............y \
51 t t .............t \
52 e e .............e \
53 0 1 .............63 \
54 \
55 D0 D0.............D0 \
56 D1 D1.............D1 / ROW 0
57 D2 D2.............D2 /
58 D3 D3.............D3 /
59 D4 D4.............D4 /
60 D5 D5.............D5 /
61 D6 D6.............D6 /
62 D7 D7.............D7 ----
63 */
64
65#if LCDWIDTH == 64
66 #if LCDWIDTH == 48
67static uint8_t micro_oled_screen_buffer[] = {
68// QMK Logo - generated at http://www.majer.ch/lcd/adf_bitmap.php
69//64x48 image
700x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
710x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
720x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00,
730x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00,
740x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
750x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
760x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
770x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60,
780xF8, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0x1F, 0x1F, 0xFF, 0xFF,
790xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF,
800xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, 0xFE,
810xFE, 0xF8, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00,
820x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
830x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
840x8C, 0x8C, 0x8C, 0x8C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
850x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
860x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
870xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8C, 0x8C, 0x8C, 0x8C,
880x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
890x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
900x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x31, 0x31, 0xFF, 0xFF,
910xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xF8, 0xF1, 0xE3, 0xE7, 0xCF,
920xCF, 0xCF, 0xCF, 0x00, 0x00, 0xCF, 0xCF, 0xCF, 0xC7, 0xE7,
930xE3, 0xF1, 0xF8, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
940x31, 0x31, 0x31, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
950x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
960x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06,
970x06, 0x06, 0x1F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
980xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xF8, 0xFF,
990xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1000xFF, 0x7F, 0x7F, 0x1F, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00,
1010x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1020x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1030x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1040x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00,
1050x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00,
1060x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1070x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1080x00, 0x00, 0x00, 0x00
109};
110 #endif
111#elif LCDWIDTH == 128
112 #if LCDHEIGHT == 32
113 static uint8_t micro_oled_screen_buffer[LCDWIDTH*LCDWIDTH/8] = {
114//128x32 qmk image
1150x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1160x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1170x00, 0x80, 0xC0, 0xE0, 0xE0, 0xFC, 0xFC, 0xE0, 0xFC, 0xFC,
1180xE0, 0xF0, 0xFC, 0xE0, 0xE0, 0xFC, 0xE0, 0xE0, 0xFC, 0xFC,
1190xE0, 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1200x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1210x00, 0x00, 0x00, 0xF0, 0x10, 0x10, 0x30, 0xE0, 0x00, 0x00,
1220x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00,
1230x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x80,
1240x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
1250x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80,
1260x80, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1270x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1280x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1290x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xB2, 0xB2, 0xFF,
1300xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x03, 0xFF, 0xFF, 0xFF, 0x03,
1310x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x03, 0xFF, 0xFF, 0xFF,
1320xFF, 0xB7, 0xB2, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1330x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1340x00, 0x1F, 0x02, 0x02, 0x03, 0x01, 0x00, 0x06, 0x1F, 0x10,
1350x10, 0x10, 0x1F, 0x06, 0x00, 0x03, 0x1E, 0x18, 0x0F, 0x01,
1360x0F, 0x18, 0x1E, 0x01, 0x00, 0x0F, 0x1F, 0x12, 0x02, 0x12,
1370x13, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x0E, 0x1F, 0x12,
1380x02, 0x12, 0x13, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10, 0x1F,
1390x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1400x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1410x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1420x00, 0x00, 0x00, 0x00, 0x48, 0x4D, 0x4D, 0xFF, 0xFF, 0xFF,
1430xFF, 0xFF, 0xFE, 0xF8, 0xF9, 0xF3, 0xF3, 0xC0, 0x80, 0xF3,
1440xF3, 0xF3, 0xF9, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xED,
1450x4D, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1460x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1470x00, 0xFE, 0x20, 0x10, 0x10, 0xE0, 0xC0, 0x00, 0x70, 0xC0,
1480x00, 0x80, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x0C,
1490x04, 0x04, 0x04, 0x04, 0x1C, 0xF0, 0x00, 0x00, 0xFC, 0x0C,
1500x38, 0xE0, 0x00, 0x00, 0xC0, 0x38, 0x0C, 0xFC, 0x00, 0x00,
1510xFC, 0xFC, 0x60, 0x90, 0x0C, 0x04, 0x00, 0x00, 0x00, 0x00,
1520x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1530x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1540x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1550x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x07, 0x3F,
1560x3F, 0x07, 0x3F, 0x3F, 0x07, 0x0F, 0x3F, 0x07, 0x07, 0x3F,
1570x07, 0x07, 0x3F, 0x3F, 0x07, 0x07, 0x03, 0x00, 0x00, 0x00,
1580x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1590x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
1600x06, 0x04, 0x04, 0x07, 0x01, 0x00, 0x00, 0x13, 0x1E, 0x03,
1610x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x04, 0x04,
1620x04, 0x04, 0x07, 0x0D, 0x08, 0x00, 0x07, 0x00, 0x00, 0x01,
1630x07, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x07,
1640x00, 0x01, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1650x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1660x00, 0x00
167 };
168 #elif LCDHEIGHT == 64
169 static uint8_t micro_oled_screen_buffer[LCDWIDTH*LCDWIDTH/8] = {
1700x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1710x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1720x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1730x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1740x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1750x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1760x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1770x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0,
1780x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00,
1790xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00,
1800x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1810x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1820x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1830x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1840x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1850x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1860x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1870x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1880x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1890x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1900x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1910x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1920xC0, 0xC0, 0xC0, 0xC0, 0xF8, 0xFC, 0xFC, 0xFE, 0xFE, 0xFF,
1930x7F, 0x7E, 0xFE, 0xFF, 0xFF, 0xFE, 0xFE, 0x7F, 0x7F, 0xFE,
1940xFE, 0xFF, 0xFF, 0xFE, 0x7E, 0x7F, 0xFF, 0xFE, 0xFE, 0xFC,
1950xFC, 0xF8, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00,
1960x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1970x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1980x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1990x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2000x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2010x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2020x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2030x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2040x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2050x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2060x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2070x00, 0x88, 0x88, 0x88, 0xDD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
2080xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
2090xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
2100xFF, 0xFF, 0xFF, 0xDD, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00,
2110x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2120x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2130x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2140x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2150x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2160x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2170x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2180x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2190x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2200x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2210x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2220x00, 0x00, 0x99, 0x99, 0x99, 0x99, 0xFF, 0xFF, 0xFF, 0xFF,
2230xFF, 0xFF, 0xFE, 0xF8, 0xF0, 0xF3, 0xF3, 0xE7, 0xE7, 0x00,
2240x00, 0xE7, 0xE7, 0xF3, 0xF3, 0xF0, 0xF8, 0xFE, 0xFF, 0xFF,
2250xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x99, 0x99, 0x99, 0x00, 0x00,
2260x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2270x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2280x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2290x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2300x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2310x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2320x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2330x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2340x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2350x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2360x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2370x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x0F, 0x1F, 0x3F,
2380x3F, 0x3F, 0xFF, 0xFF, 0x3F, 0x3F, 0xFF, 0xFF, 0x3F, 0x3F,
2390xFF, 0xFF, 0x3F, 0x3F, 0xFF, 0xFF, 0x3F, 0x3F, 0xFF, 0xFF,
2400x3F, 0x3F, 0x3F, 0x1F, 0x0F, 0x01, 0x01, 0x01, 0x01, 0x00,
2410x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2420x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2430x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2440x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2450x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2460x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2470x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2480x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2490x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2500x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2510x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2520x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2530x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00,
2540x80, 0x03, 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x80, 0x01,
2550x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2560x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2570x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2580x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2590x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2600x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2610x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2620x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2630x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2640x00, 0x00, 0x00, 0xFF, 0x11, 0x11, 0x11, 0x0E, 0x00, 0x70,
2650x88, 0x04, 0x04, 0x04, 0xF8, 0x00, 0x00, 0x3C, 0xE0, 0xC0,
2660x38, 0x1C, 0xE0, 0x80, 0x70, 0x0C, 0x00, 0xF8, 0xAC, 0x24,
2670x24, 0x3C, 0x30, 0x00, 0x00, 0xFC, 0x0C, 0x04, 0x00, 0xF8,
2680xAC, 0x24, 0x24, 0x2C, 0x30, 0x00, 0x70, 0xDC, 0x04, 0x04,
2690x88, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
2700x8C, 0x04, 0x04, 0xF8, 0x00, 0x04, 0x3C, 0xE0, 0x80, 0xF0,
2710x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x83, 0x01, 0x01,
2720x01, 0x81, 0xFE, 0x3C, 0x00, 0x00, 0xFF, 0x03, 0x0E, 0x70,
2730xC0, 0xE0, 0x38, 0x06, 0x03, 0xFF, 0x00, 0x00, 0xFF, 0x18,
2740x38, 0x66, 0xC3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2750x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2760x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2770x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2780x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2790x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
2800x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
2810x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
2820x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
2830x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01,
2840x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2850x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03,
2860x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
2870x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00,
2880x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
2890x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
2900x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2910x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2920x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2930x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2940x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2950x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2960x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2970x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2980x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2990x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3000x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3010x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3020x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3030x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3040x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3050x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
306 };
307//TODO: generate bitmap of QMK logo here
308 #endif
309#else
310//catchall for custom screen szies
311 static uint8_t micro_oled_screen_buffer[LCDWIDTH*LCDWIDTH/8] = {0};
312#endif
313
314
315
316void micro_oled_init(void) {
317
318 i2c_init();
319 i2c_start(I2C_ADDRESS_SA0_1);
320
321 // Display Init sequence for 64x48 OLED module
322 send_command(DISPLAYOFF); // 0xAE
323
324 send_command(SETDISPLAYCLOCKDIV); // 0xD5
325 send_command(0x80); // the suggested ratio 0x80
326
327 send_command(SETMULTIPLEX); // 0xA8
328 send_command(LCDHEIGHT - 1);
329
330 send_command(SETDISPLAYOFFSET); // 0xD3
331 send_command(0x00); // no offset
332
333 send_command(SETSTARTLINE | 0x00); // line #0
334
335 send_command(CHARGEPUMP); // enable charge pump
336 send_command(0x14);
337
338 send_command(NORMALDISPLAY); // 0xA6
339 send_command(DISPLAYALLONRESUME); // 0xA4
340
341//display at regular orientation
342 send_command(SEGREMAP | 0x1);
343 send_command(COMSCANDEC);
344
345//rotate display 180
346#ifdef micro_oled_rotate_180
347 send_command(SEGREMAP);
348 send_command(COMSCANINC);
349#endif
350
351 send_command(MEMORYMODE);
352 send_command(0x10);
353
354 send_command(SETCOMPINS); // 0xDA
355if (LCDHEIGHT > 32) {
356 send_command(0x12);
357} else {
358 send_command(0x02);
359}
360 send_command(SETCONTRAST); // 0x81
361 send_command(0x8F);
362
363 send_command(SETPRECHARGE); // 0xd9
364 send_command(0xF1);
365
366 send_command(SETVCOMDESELECT); // 0xDB
367 send_command(0x40);
368
369 send_command(DISPLAYON); //--turn on oled panel
370 clear_screen(); // Erase hardware memory inside the OLED controller to avoid random data in memory.
371 send_buffer();
372}
373
374void send_command(uint8_t command) {
375 micro_oled_transfer_buffer[0] = I2C_COMMAND;
376 micro_oled_transfer_buffer[1] = command;
377 i2c_transmit(I2C_ADDRESS_SA0_1 << 1, micro_oled_transfer_buffer, 2, 100);
378}
379
380void send_data(uint8_t data) {
381 micro_oled_transfer_buffer[0] = I2C_DATA;
382 micro_oled_transfer_buffer[1] = data;
383 i2c_transmit(I2C_ADDRESS_SA0_1 << 1, micro_oled_transfer_buffer, 2, 100);
384}
385
386/** \brief Set SSD1306 page address.
387 Send page address command and address to the SSD1306 OLED controller.
388*/
389void set_page_address(uint8_t address) {
390 address = (0xB0 | address);
391 send_command(address);
392}
393
394/** \brief Set SSD1306 column address.
395 Send column address command and address to the SSD1306 OLED controller.
396*/
397void set_column_address(uint8_t address) {
398 send_command( ( 0x10 | (address >> 4) ) + ((128 - LCDWIDTH) >> 8) );
399 send_command( 0x0F & address );
400}
401
402/** \brief Clear SSD1306's memory.
403 To clear GDRAM inside the LCD controller.
404*/
405void clear_screen(void) {
406 for (int i=0;i<8; i++) {
407 set_page_address(i);
408 set_column_address(0);
409 for (int j=0; j<0x80; j++) {
410 send_data(0);
411 }
412 }
413}
414
415/** \brief Clear SSD1306's memory.
416 To clear GDRAM inside the LCD controller.
417*/
418void clear_buffer(void) {
419//384
420 memset(micro_oled_screen_buffer, 0, LCDWIDTH*LCDWIDTH/8);
421}
422
423/** \brief Invert display.
424 The PIXEL_ON color of the display will turn to PIXEL_OFF and the PIXEL_OFF will turn to PIXEL_ON.
425*/
426void invert_screen(bool invert) {
427 if (invert) {
428 send_command(INVERTDISPLAY);
429 } else {
430 send_command(NORMALDISPLAY);
431 }
432}
433
434/** \brief Set contrast.
435 OLED contract value from 0 to 255. Note: Contrast level is not very obvious.
436*/
437void set_contrast(uint8_t contrast) {
438 send_command(SETCONTRAST); // 0x81
439 send_command(contrast);
440}
441
442/** \brief Transfer display buffer.
443 Sends the updated buffer to the controller - the current status is checked before to save i2c exectution time
444*/
445void send_buffer(void) {
446 uint8_t i, j;
447
448 uint8_t page_addr = 0xFF;
449 for (i = 0; i < LCDHEIGHT/8; i++) {
450 uint8_t col_addr = 0xFF;
451 for (j = 0; j < LCDWIDTH; j++) {
452 if (micro_oled_screen_buffer[i*LCDWIDTH+j] != micro_oled_screen_current[i*LCDWIDTH+j]) {
453 if (page_addr != i) {
454 set_page_address(i);
455 }
456 if (col_addr != j) {
457 set_column_address(j);
458 }
459 send_data(micro_oled_screen_buffer[i*LCDWIDTH+j]);
460 micro_oled_screen_current[i*LCDWIDTH+j] = micro_oled_screen_buffer[i*LCDWIDTH+j];
461 col_addr = j + 1;
462 }
463 }
464 }
465}
466
467/** \brief Draw pixel with color and mode.
468 Draw color pixel in the screen buffer's x,y position with NORM or XOR draw mode.
469*/
470void draw_pixel(uint8_t x, uint8_t y, uint8_t color, uint8_t mode) {
471 if ((x<0) || (x>=LCDWIDTH) || (y<0) || (y>=LCDHEIGHT))
472 return;
473
474 if (mode == XOR) {
475 if (color == PIXEL_ON)
476 micro_oled_screen_buffer[x + (y/8)*LCDWIDTH] ^= _BV((y%8));
477 } else {
478 if (color == PIXEL_ON)
479 micro_oled_screen_buffer[x + (y/8)*LCDWIDTH] |= _BV((y%8));
480 else
481 micro_oled_screen_buffer[x + (y/8)*LCDWIDTH] &= ~_BV((y%8));
482 }
483}
484
485/** \brief Draw line with color and mode.
486Draw line using color and mode from x0,y0 to x1,y1 of the screen buffer.
487*/
488void draw_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t color, uint8_t mode) {
489 uint8_t steep = abs(y1 - y0) > abs(x1 - x0);
490 if (steep) {
491 swap(x0, y0);
492 swap(x1, y1);
493 }
494
495 if (x0 > x1) {
496 swap(x0, x1);
497 swap(y0, y1);
498 }
499
500 uint8_t dx, dy;
501 dx = x1 - x0;
502 dy = abs(y1 - y0);
503
504 int8_t err = dx / 2;
505 int8_t ystep;
506
507 if (y0 < y1) {
508 ystep = 1;
509 } else {
510 ystep = -1;}
511
512 for (; x0<x1; x0++) {
513 if (steep) {
514 draw_pixel(y0, x0, color, mode);
515 } else {
516 draw_pixel(x0, y0, color, mode);
517 }
518 err -= dy;
519 if (err < 0) {
520 y0 += ystep;
521 err += dx;
522 }
523 }
524}
525
526/** \brief Draw horizontal line with color and mode.
527Draw horizontal line using color and mode from x,y to x+width,y of the screen buffer.
528*/
529void draw_line_hori(uint8_t x, uint8_t y, uint8_t width, uint8_t color, uint8_t mode) {
530 draw_line(x,y,x+width,y,color,mode);
531}
532
533/** \brief Draw vertical line.
534Draw vertical line using current fore color and current draw mode from x,y to x,y+height of the screen buffer.
535*/
536void draw_line_vert(uint8_t x, uint8_t y, uint8_t height, bool color, uint8_t mode) {
537 draw_line(x,y,x,y+height,color,mode);
538}
539
540/** \brief Draw rectangle with color and mode.
541Draw rectangle using color and mode from x,y to x+width,y+height of the screen buffer.
542*/
543void draw_rect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode) {
544 uint8_t tempHeight;
545
546 draw_line_hori(x,y, width, color, mode);
547 draw_line_hori(x,y+height-1, width, color, mode);
548
549 tempHeight=height-2;
550
551 // skip drawing vertical lines to avoid overlapping of pixel that will
552 // affect XOR plot if no pixel in between horizontal lines
553 if (tempHeight<1) return;
554
555 draw_line_vert(x,y+1, tempHeight, color, mode);
556 draw_line_vert(x+width-1, y+1, tempHeight, color, mode);
557}
558
559/** \brief Draw rectangle with color and mode.
560Draw rectangle using color and mode from x,y to x+width,y+height of the screen buffer.
561*/
562void draw_rect_soft(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode) {
563 uint8_t tempHeight;
564
565 draw_line_hori(x+1,y, width-2, color, mode);
566 draw_line_hori(x+1,y+height-1, width-2, color, mode);
567
568 tempHeight=height-2;
569
570 // skip drawing vertical lines to avoid overlapping of pixel that will
571 // affect XOR plot if no pixel in between horizontal lines
572 if (tempHeight<1) return;
573
574 draw_line_vert(x,y+1, tempHeight, color, mode);
575 draw_line_vert(x+width-1, y+1, tempHeight, color, mode);
576}
577
578/** \brief Draw filled rectangle with color and mode.
579Draw filled rectangle using color and mode from x,y to x+width,y+height of the screen buffer.
580*/
581void draw_rect_filled(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode) {
582 // TODO - need to optimise the memory map draw so that this function will not call pixel one by one
583 for (int i=x; i<x+width;i++) {
584 draw_line_vert(i,y, height, color, mode);
585 }
586}
587
588/** \brief Draw filled rectangle with color and mode.
589Draw filled rectangle using color and mode from x,y to x+width,y+height of the screen buffer.
590*/
591void draw_rect_filled_soft(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode) {
592 // TODO - need to optimise the memory map draw so that this function will not call pixel one by one
593 for (int i=x; i<x+width;i++) {
594 if (i == x || i == (x + width - 1))
595 draw_line_vert(i, y+1, height-2, color, mode);
596 else
597 draw_line_vert(i, y, height, color, mode);
598 }
599}
600
601/** \brief Draw character with color and mode.
602 Draw character c using color and draw mode at x,y.
603*/
604void draw_char(uint8_t x, uint8_t y, uint8_t c, uint8_t color, uint8_t mode, uint8_t font) {
605 // TODO - New routine to take font of any height, at the moment limited to font height in multiple of 8 pixels
606
607 uint8_t rowsToDraw,row, tempC;
608 uint8_t i,j,temp;
609 uint16_t charPerBitmapRow,charColPositionOnBitmap,charRowPositionOnBitmap,charBitmapStartPosition;
610
611 if ((font>=TOTALFONTS) || (font<0))
612 return;
613
614 uint8_t fontType = font;
615 uint8_t fontWidth = pgm_read_byte(fonts_pointer[fontType]+0);
616 uint8_t fontHeight = pgm_read_byte(fonts_pointer[fontType]+1);
617 uint8_t fontStartChar = pgm_read_byte(fonts_pointer[fontType]+2);
618 uint8_t fontTotalChar = pgm_read_byte(fonts_pointer[fontType]+3);
619 uint16_t fontMapWidth = (pgm_read_byte(fonts_pointer[fontType]+4)*100)+pgm_read_byte(fonts_pointer[fontType]+5); // two bytes values into integer 16
620
621 if ((c<fontStartChar) || (c>(fontStartChar+fontTotalChar-1))) // no bitmap for the required c
622 return;
623
624 tempC=c-fontStartChar;
625
626 // each row (in datasheet is call page) is 8 bits high, 16 bit high character will have 2 rows to be drawn
627 rowsToDraw=fontHeight/8; // 8 is LCD's page size, see SSD1306 datasheet
628 if (rowsToDraw<=1) rowsToDraw=1;
629
630 // the following draw function can draw anywhere on the screen, but SLOW pixel by pixel draw
631 if (rowsToDraw==1) {
632 for (i=0;i<fontWidth+1;i++) {
633 if (i==fontWidth) // this is done in a weird way because for 5x7 font, there is no margin, this code add a margin after col 5
634 temp=0;
635 else
636 temp=pgm_read_byte(fonts_pointer[fontType]+FONTHEADERSIZE+(tempC*fontWidth)+i);
637
638 for (j=0;j<8;j++) { // 8 is the LCD's page height (see datasheet for explanation)
639 if (temp & 0x1) {
640 draw_pixel(x+i, y+j, color,mode);
641 }
642 else {
643 draw_pixel(x+i, y+j, !color,mode);
644 }
645
646 temp >>=1;
647 }
648 }
649 return;
650 }
651
652 // font height over 8 bit
653 // take character "0" ASCII 48 as example
654 charPerBitmapRow = fontMapWidth/fontWidth; // 256/8 =32 char per row
655 charColPositionOnBitmap = tempC % charPerBitmapRow; // =16
656 charRowPositionOnBitmap = (int)(tempC/charPerBitmapRow); // =1
657 charBitmapStartPosition = (charRowPositionOnBitmap * fontMapWidth * (fontHeight/8)) + (charColPositionOnBitmap * fontWidth) ;
658
659 // each row on LCD is 8 bit height (see datasheet for explanation)
660 for(row=0;row<rowsToDraw;row++) {
661 for (i=0; i<fontWidth;i++) {
662 temp=pgm_read_byte(fonts_pointer[fontType]+FONTHEADERSIZE+(charBitmapStartPosition+i+(row*fontMapWidth)));
663 for (j=0;j<8;j++) { // 8 is the LCD's page height (see datasheet for explanation)
664 if (temp & 0x1) {
665 draw_pixel(x+i,y+j+(row*8), color, mode);
666 }
667 else {
668 draw_pixel(x+i,y+j+(row*8), !color, mode);
669 }
670 temp >>=1;
671 }
672 }
673 }
674
675}
676
677void draw_string(uint8_t x, uint8_t y, char * string, uint8_t color, uint8_t mode, uint8_t font) {
678
679 if ((font>=TOTALFONTS) || (font<0))
680 return;
681
682 uint8_t fontType = font;
683 uint8_t fontWidth = pgm_read_byte(fonts_pointer[fontType]+0);
684
685 uint8_t cur_x = x;
686 for (int i = 0; i < strlen(string); i++) {
687 draw_char(cur_x, y, string[i], color, mode, font);
688 cur_x += fontWidth + 1;
689 }
690
691}