aboutsummaryrefslogtreecommitdiff
path: root/drivers/oled
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/oled')
-rw-r--r--drivers/oled/glcdfont.c240
-rw-r--r--drivers/oled/oled_driver.c528
-rw-r--r--drivers/oled/oled_driver.h183
3 files changed, 951 insertions, 0 deletions
diff --git a/drivers/oled/glcdfont.c b/drivers/oled/glcdfont.c
new file mode 100644
index 000000000..150be9e94
--- /dev/null
+++ b/drivers/oled/glcdfont.c
@@ -0,0 +1,240 @@
1#pragma once
2
3#ifdef __AVR__
4 #include <avr/io.h>
5 #include <avr/pgmspace.h>
6#elif defined(ESP8266)
7 #include <pgmspace.h>
8#else
9 #define PROGMEM
10#endif
11
12// Helidox 8x6 font with QMK Firmware Logo
13// Online editor: http://teripom.x0.com/
14
15static const unsigned char font[] PROGMEM = {
16 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
17 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x00,
18 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 0x00,
19 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x00,
20 0x18, 0x3C, 0x7E, 0x3C, 0x18, 0x00,
21 0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x00,
22 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00,
23 0x00, 0x18, 0x3C, 0x18, 0x00, 0x00,
24 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00,
25 0x00, 0x18, 0x24, 0x18, 0x00, 0x00,
26 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x00,
27 0x30, 0x48, 0x3A, 0x06, 0x0E, 0x00,
28 0x26, 0x29, 0x79, 0x29, 0x26, 0x00,
29 0x40, 0x7F, 0x05, 0x05, 0x07, 0x00,
30 0x40, 0x7F, 0x05, 0x25, 0x3F, 0x00,
31 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x00,
32 0x7F, 0x3E, 0x1C, 0x1C, 0x08, 0x00,
33 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x00,
34 0x14, 0x22, 0x7F, 0x22, 0x14, 0x00,
35 0x5F, 0x5F, 0x00, 0x5F, 0x5F, 0x00,
36 0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00,
37 0x00, 0x66, 0x89, 0x95, 0x6A, 0x00,
38 0x60, 0x60, 0x60, 0x60, 0x60, 0x00,
39 0x94, 0xA2, 0xFF, 0xA2, 0x94, 0x00,
40 0x08, 0x04, 0x7E, 0x04, 0x08, 0x00,
41 0x10, 0x20, 0x7E, 0x20, 0x10, 0x00,
42 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x00,
43 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x00,
44 0x1E, 0x10, 0x10, 0x10, 0x10, 0x00,
45 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 0x00,
46 0x30, 0x38, 0x3E, 0x38, 0x30, 0x00,
47 0x06, 0x0E, 0x3E, 0x0E, 0x06, 0x00,
48 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
49 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00,
50 0x00, 0x07, 0x00, 0x07, 0x00, 0x00,
51 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00,
52 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00,
53 0x23, 0x13, 0x08, 0x64, 0x62, 0x00,
54 0x36, 0x49, 0x56, 0x20, 0x50, 0x00,
55 0x00, 0x08, 0x07, 0x03, 0x00, 0x00,
56 0x00, 0x1C, 0x22, 0x41, 0x00, 0x00,
57 0x00, 0x41, 0x22, 0x1C, 0x00, 0x00,
58 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x00,
59 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00,
60 0x00, 0x80, 0x70, 0x30, 0x00, 0x00,
61 0x08, 0x08, 0x08, 0x08, 0x08, 0x00,
62 0x00, 0x00, 0x60, 0x60, 0x00, 0x00,
63 0x20, 0x10, 0x08, 0x04, 0x02, 0x00,
64 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00,
65 0x00, 0x42, 0x7F, 0x40, 0x00, 0x00,
66 0x72, 0x49, 0x49, 0x49, 0x46, 0x00,
67 0x21, 0x41, 0x49, 0x4D, 0x33, 0x00,
68 0x18, 0x14, 0x12, 0x7F, 0x10, 0x00,
69 0x27, 0x45, 0x45, 0x45, 0x39, 0x00,
70 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x00,
71 0x41, 0x21, 0x11, 0x09, 0x07, 0x00,
72 0x36, 0x49, 0x49, 0x49, 0x36, 0x00,
73 0x46, 0x49, 0x49, 0x29, 0x1E, 0x00,
74 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
75 0x00, 0x40, 0x34, 0x00, 0x00, 0x00,
76 0x00, 0x08, 0x14, 0x22, 0x41, 0x00,
77 0x14, 0x14, 0x14, 0x14, 0x14, 0x00,
78 0x00, 0x41, 0x22, 0x14, 0x08, 0x00,
79 0x02, 0x01, 0x59, 0x09, 0x06, 0x00,
80 0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x00,
81 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x00,
82 0x7F, 0x49, 0x49, 0x49, 0x36, 0x00,
83 0x3E, 0x41, 0x41, 0x41, 0x22, 0x00,
84 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x00,
85 0x7F, 0x49, 0x49, 0x49, 0x41, 0x00,
86 0x7F, 0x09, 0x09, 0x09, 0x01, 0x00,
87 0x3E, 0x41, 0x41, 0x51, 0x73, 0x00,
88 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00,
89 0x00, 0x41, 0x7F, 0x41, 0x00, 0x00,
90 0x20, 0x40, 0x41, 0x3F, 0x01, 0x00,
91 0x7F, 0x08, 0x14, 0x22, 0x41, 0x00,
92 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00,
93 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x00,
94 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00,
95 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00,
96 0x7F, 0x09, 0x09, 0x09, 0x06, 0x00,
97 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00,
98 0x7F, 0x09, 0x19, 0x29, 0x46, 0x00,
99 0x26, 0x49, 0x49, 0x49, 0x32, 0x00,
100 0x03, 0x01, 0x7F, 0x01, 0x03, 0x00,
101 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00,
102 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00,
103 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00,
104 0x63, 0x14, 0x08, 0x14, 0x63, 0x00,
105 0x03, 0x04, 0x78, 0x04, 0x03, 0x00,
106 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00,
107 0x00, 0x7F, 0x41, 0x41, 0x41, 0x00,
108 0x02, 0x04, 0x08, 0x10, 0x20, 0x00,
109 0x00, 0x41, 0x41, 0x41, 0x7F, 0x00,
110 0x04, 0x02, 0x01, 0x02, 0x04, 0x00,
111 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
112 0x00, 0x03, 0x07, 0x08, 0x00, 0x00,
113 0x20, 0x54, 0x54, 0x78, 0x40, 0x00,
114 0x7F, 0x28, 0x44, 0x44, 0x38, 0x00,
115 0x38, 0x44, 0x44, 0x44, 0x28, 0x00,
116 0x38, 0x44, 0x44, 0x28, 0x7F, 0x00,
117 0x38, 0x54, 0x54, 0x54, 0x18, 0x00,
118 0x00, 0x08, 0x7E, 0x09, 0x02, 0x00,
119 0x18, 0xA4, 0xA4, 0x9C, 0x78, 0x00,
120 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00,
121 0x00, 0x44, 0x7D, 0x40, 0x00, 0x00,
122 0x20, 0x40, 0x40, 0x3D, 0x00, 0x00,
123 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00,
124 0x00, 0x41, 0x7F, 0x40, 0x00, 0x00,
125 0x7C, 0x04, 0x78, 0x04, 0x78, 0x00,
126 0x7C, 0x08, 0x04, 0x04, 0x78, 0x00,
127 0x38, 0x44, 0x44, 0x44, 0x38, 0x00,
128 0xFC, 0x18, 0x24, 0x24, 0x18, 0x00,
129 0x18, 0x24, 0x24, 0x18, 0xFC, 0x00,
130 0x7C, 0x08, 0x04, 0x04, 0x08, 0x00,
131 0x48, 0x54, 0x54, 0x54, 0x24, 0x00,
132 0x04, 0x04, 0x3F, 0x44, 0x24, 0x00,
133 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00,
134 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00,
135 0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00,
136 0x44, 0x28, 0x10, 0x28, 0x44, 0x00,
137 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x00,
138 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00,
139 0x00, 0x08, 0x36, 0x41, 0x00, 0x00,
140 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,
141 0x00, 0x41, 0x36, 0x08, 0x00, 0x00,
142 0x02, 0x01, 0x02, 0x04, 0x02, 0x00,
143 0x3C, 0x26, 0x23, 0x26, 0x3C, 0x00,
144 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145 0x40, 0x40, 0x40, 0xF0, 0xF8, 0xF8,
146 0xFF, 0x38, 0xFF, 0xF8, 0xF8, 0x3F,
147 0xF8, 0xF8, 0xFF, 0x38, 0xFF, 0xF8,
148 0xF8, 0xF0, 0x40, 0x40, 0x40, 0x00,
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
150 0xC0, 0xC0, 0xC0, 0x80, 0x00, 0x00,
151 0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00,
152 0x80, 0xC0, 0xC0, 0x00, 0xC0, 0xC0,
153 0x00, 0x00, 0x80, 0xC0, 0xC0, 0x00,
154 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0,
155 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0xC0,
156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
157 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165 0xE0, 0xF0, 0xF0, 0xF0, 0xE0, 0xEC,
166 0xEE, 0xF7, 0xF3, 0x70, 0x20, 0x00,
167 0x7C, 0x7C, 0x7C, 0x7E, 0x00, 0x7E,
168 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x00,
169 0x00, 0x80, 0xC0, 0xE0, 0x7E, 0x5B,
170 0x4F, 0x5B, 0xFE, 0xC0, 0x00, 0x00,
171 0xC0, 0x00, 0xDC, 0xD7, 0xDE, 0xDE,
172 0xDE, 0xD7, 0xDC, 0x00, 0xC0, 0x00,
173 0x00, 0x00, 0x00, 0xE0, 0xEC, 0xDF,
174 0xFC, 0xE0, 0x00, 0x00, 0x00, 0x00,
175 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177 0x49, 0x49, 0x49, 0xFF, 0xFF, 0xFF,
178 0xFF, 0xE0, 0xDF, 0xBF, 0xBF, 0x00,
179 0xBF, 0xBF, 0xDF, 0xE0, 0xFF, 0xFF,
180 0xFF, 0xFF, 0x49, 0x49, 0x49, 0x00,
181 0x00, 0x00, 0x00, 0x00, 0x1F, 0x3F,
182 0x60, 0x60, 0xE0, 0xBF, 0x1F, 0x00,
183 0x7F, 0x7F, 0x07, 0x1E, 0x38, 0x1E,
184 0x07, 0x7F, 0x7F, 0x00, 0x7F, 0x7F,
185 0x0E, 0x1F, 0x3B, 0x71, 0x60, 0x00,
186 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F,
187 0x0C, 0x0C, 0x0C, 0x00, 0x7E, 0x7E,
188 0x00, 0x7F, 0x7E, 0x03, 0x03, 0x00,
189 0x7F, 0x7E, 0x03, 0x03, 0x7E, 0x7E,
190 0x03, 0x03, 0x7F, 0x7E, 0x00, 0x0F,
191 0x3E, 0x70, 0x3C, 0x06, 0x3C, 0x70,
192 0x3E, 0x0F, 0x00, 0x32, 0x7B, 0x49,
193 0x49, 0x3F, 0x7E, 0x00, 0x7F, 0x7E,
194 0x03, 0x03, 0x00, 0x1E, 0x3F, 0x69,
195 0x69, 0x6F, 0x26, 0x00, 0x00, 0x00,
196 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
197 0x0F, 0x1F, 0x3F, 0x7F, 0x7F, 0x7F,
198 0x7F, 0x7F, 0x3F, 0x1E, 0x0C, 0x00,
199 0x1F, 0x1F, 0x1F, 0x3F, 0x00, 0x3F,
200 0x3F, 0x3F, 0x7F, 0x7F, 0x7F, 0x00,
201 0x30, 0x7B, 0x7F, 0x78, 0x30, 0x20,
202 0x20, 0x30, 0x78, 0x7F, 0x3B, 0x00,
203 0x03, 0x00, 0x0F, 0x7F, 0x0F, 0x0F,
204 0x0F, 0x7F, 0x0F, 0x00, 0x03, 0x00,
205 0x40, 0x7C, 0x3F, 0x3F, 0x23, 0x01,
206 0x23, 0x3F, 0x37, 0x6C, 0x40, 0x00,
207 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
208 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
209 0x01, 0x01, 0x01, 0x07, 0x0F, 0x0F,
210 0x7F, 0x0F, 0x7F, 0x0F, 0x0F, 0x7E,
211 0x0F, 0x0F, 0x7F, 0x0F, 0x7F, 0x0F,
212 0x0F, 0x07, 0x01, 0x01, 0x01, 0x00,
213 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
214 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
215 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
216 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
218 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
219 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
220 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
221 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
222 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
223 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
228 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
232 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
239 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240};
diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c
new file mode 100644
index 000000000..aa025d7a4
--- /dev/null
+++ b/drivers/oled/oled_driver.c
@@ -0,0 +1,528 @@
1/*
2Copyright 2019 Ryan Caltabiano <https://github.com/XScorpion2>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17#include "i2c_master.h"
18#include "oled_driver.h"
19#include OLED_FONT_H
20#include "timer.h"
21#include "print.h"
22
23#include <string.h>
24
25#if defined(__AVR__)
26 #include <avr/io.h>
27 #include <avr/pgmspace.h>
28#elif defined(ESP8266)
29 #include <pgmspace.h>
30#else // defined(ESP8266)
31 #define PROGMEM
32 #define memcpy_P(des, src, len) memcpy(des, src, len)
33#endif // defined(__AVR__)
34
35// Used commands from spec sheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
36// Fundamental Commands
37#define CONTRAST 0x81
38#define DISPLAY_ALL_ON 0xA5
39#define DISPLAY_ALL_ON_RESUME 0xA4
40#define NORMAL_DISPLAY 0xA6
41#define DISPLAY_ON 0xAF
42#define DISPLAY_OFF 0xAE
43
44// Scrolling Commands
45#define ACTIVATE_SCROLL 0x2F
46#define DEACTIVATE_SCROLL 0x2E
47#define SCROLL_RIGHT 0x26
48#define SCROLL_LEFT 0x27
49#define SCROLL_RIGHT_UP 0x29
50#define SCROLL_LEFT_UP 0x2A
51
52// Addressing Setting Commands
53#define MEMORY_MODE 0x20
54#define COLUMN_ADDR 0x21
55#define PAGE_ADDR 0x22
56
57// Hardware Configuration Commands
58#define DISPLAY_START_LINE 0x40
59#define SEGMENT_REMAP 0xA0
60#define SEGMENT_REMAP_INV 0xA1
61#define MULTIPLEX_RATIO 0xA8
62#define COM_SCAN_INC 0xC0
63#define COM_SCAN_DEC 0xC8
64#define DISPLAY_OFFSET 0xD3
65#define COM_PINS 0xDA
66
67// Timing & Driving Commands
68#define DISPLAY_CLOCK 0xD5
69#define PRE_CHARGE_PERIOD 0xD9
70#define VCOM_DETECT 0xDB
71
72// Charge Pump Commands
73#define CHARGE_PUMP 0x8D
74
75// Misc defines
76#define OLED_TIMEOUT 60000
77#define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8)
78#define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)
79
80// i2c defines
81#define I2C_CMD 0x00
82#define I2C_DATA 0x40
83#if defined(__AVR__)
84 // already defined on ARM
85 #define I2C_TIMEOUT 100
86 #define I2C_TRANSMIT_P(data) i2c_transmit_P((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), I2C_TIMEOUT)
87#else // defined(__AVR__)
88 #define I2C_TRANSMIT_P(data) i2c_transmit((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), I2C_TIMEOUT)
89#endif // defined(__AVR__)
90#define I2C_TRANSMIT(data) i2c_transmit((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), I2C_TIMEOUT)
91#define I2C_WRITE_REG(mode, data, size) i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), mode, data, size, I2C_TIMEOUT)
92
93#define HAS_FLAGS(bits, flags) ((bits & flags) == flags)
94
95// Display buffer's is the same as the OLED memory layout
96// this is so we don't end up with rounding errors with
97// parts of the display unusable or don't get cleared correctly
98// and also allows for drawing & inverting
99uint8_t oled_buffer[OLED_MATRIX_SIZE];
100uint8_t* oled_cursor;
101OLED_BLOCK_TYPE oled_dirty = 0;
102bool oled_initialized = false;
103bool oled_active = false;
104bool oled_scrolling = false;
105uint8_t oled_rotation = 0;
106uint8_t oled_rotation_width = 0;
107#if !defined(OLED_DISABLE_TIMEOUT)
108 uint16_t oled_last_activity;
109#endif
110
111// Internal variables to reduce math instructions
112
113#if defined(__AVR__)
114// identical to i2c_transmit, but for PROGMEM since all initialization is in PROGMEM arrays currently
115// probably should move this into i2c_master...
116static i2c_status_t i2c_transmit_P(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout) {
117 i2c_status_t status = i2c_start(address | I2C_WRITE, timeout);
118
119 for (uint16_t i = 0; i < length && status >= 0; i++) {
120 status = i2c_write(pgm_read_byte((const char*)data++), timeout);
121 if (status) break;
122 }
123
124 i2c_stop();
125
126 return status;
127}
128#endif
129
130// Flips the rendering bits for a character at the current cursor position
131static void InvertCharacter(uint8_t *cursor)
132{
133 const uint8_t *end = cursor + OLED_FONT_WIDTH;
134 while (cursor < end) {
135 *cursor = ~(*cursor);
136 cursor++;
137 }
138}
139
140bool oled_init(uint8_t rotation) {
141 oled_rotation = oled_init_user(rotation);
142 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
143 oled_rotation_width = OLED_DISPLAY_WIDTH;
144 } else {
145 oled_rotation_width = OLED_DISPLAY_HEIGHT;
146 }
147 i2c_init();
148
149 static const uint8_t PROGMEM display_setup1[] = {
150 I2C_CMD,
151 DISPLAY_OFF,
152 DISPLAY_CLOCK, 0x80,
153 MULTIPLEX_RATIO, OLED_DISPLAY_HEIGHT - 1,
154 DISPLAY_OFFSET, 0x00,
155 DISPLAY_START_LINE | 0x00,
156 CHARGE_PUMP, 0x14,
157 MEMORY_MODE, 0x00, }; // Horizontal addressing mode
158 if (I2C_TRANSMIT_P(display_setup1) != I2C_STATUS_SUCCESS) {
159 print("oled_init cmd set 1 failed\n");
160 return false;
161 }
162
163 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_180)) {
164 static const uint8_t PROGMEM display_normal[] = {
165 I2C_CMD,
166 SEGMENT_REMAP_INV,
167 COM_SCAN_DEC };
168 if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) {
169 print("oled_init cmd normal rotation failed\n");
170 return false;
171 }
172 } else {
173 static const uint8_t PROGMEM display_flipped[] = {
174 I2C_CMD,
175 SEGMENT_REMAP,
176 COM_SCAN_INC };
177 if (I2C_TRANSMIT_P(display_flipped) != I2C_STATUS_SUCCESS) {
178 print("display_flipped failed\n");
179 return false;
180 }
181 }
182
183 static const uint8_t PROGMEM display_setup2[] = {
184 I2C_CMD,
185 COM_PINS, 0x02,
186 CONTRAST, 0x8F,
187 PRE_CHARGE_PERIOD, 0xF1,
188 VCOM_DETECT, 0x40,
189 DISPLAY_ALL_ON_RESUME,
190 NORMAL_DISPLAY,
191 DEACTIVATE_SCROLL,
192 DISPLAY_ON };
193 if (I2C_TRANSMIT_P(display_setup2) != I2C_STATUS_SUCCESS) {
194 print("display_setup2 failed\n");
195 return false;
196 }
197
198 oled_clear();
199 oled_initialized = true;
200 oled_active = true;
201 oled_scrolling = false;
202 return true;
203}
204
205__attribute__((weak))
206uint8_t oled_init_user(uint8_t rotation) {
207 return rotation;
208}
209
210void oled_clear(void) {
211 memset(oled_buffer, 0, sizeof(oled_buffer));
212 oled_cursor = &oled_buffer[0];
213 oled_dirty = -1; // -1 will be max value as long as display_dirty is unsigned type
214}
215
216static void calc_bounds(uint8_t update_start, uint8_t* cmd_array)
217{
218 cmd_array[1] = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_WIDTH;
219 cmd_array[4] = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_WIDTH;
220 cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) % OLED_DISPLAY_WIDTH + cmd_array[1];
221 cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) / OLED_DISPLAY_WIDTH - 1;
222}
223
224static void calc_bounds_90(uint8_t update_start, uint8_t* cmd_array)
225{
226 cmd_array[1] = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8;
227 cmd_array[4] = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT;
228 cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8 - 1 + cmd_array[1];;
229 cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) % OLED_DISPLAY_HEIGHT / 8;
230}
231
232uint8_t crot(uint8_t a, int8_t n)
233{
234 const uint8_t mask = 0x7;
235 n &= mask;
236 return a << n | a >> (-n & mask);
237}
238
239static void rotate_90(const uint8_t* src, uint8_t* dest)
240{
241 for (uint8_t i = 0, shift = 7; i < 8; ++i, --shift) {
242 uint8_t selector = (1 << i);
243 for (uint8_t j = 0; j < 8; ++j) {
244 dest[i] |= crot(src[j] & selector, shift - (int8_t)j);
245 }
246 }
247}
248
249void oled_render(void) {
250 // Do we have work to do?
251 if (!oled_dirty || oled_scrolling) {
252 return;
253 }
254
255 // Find first dirty block
256 uint8_t update_start = 0;
257 while (!(oled_dirty & (1 << update_start))) { ++update_start; }
258
259 // Set column & page position
260 static uint8_t display_start[] = {
261 I2C_CMD,
262 COLUMN_ADDR, 0, OLED_DISPLAY_WIDTH - 1,
263 PAGE_ADDR, 0, OLED_DISPLAY_HEIGHT / 8 - 1 };
264 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
265 calc_bounds(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start
266 } else {
267 calc_bounds_90(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start
268 }
269
270 // Send column & page position
271 if (I2C_TRANSMIT(display_start) != I2C_STATUS_SUCCESS) {
272 print("oled_render offset command failed\n");
273 return;
274 }
275
276 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
277 // Send render data chunk as is
278 if (I2C_WRITE_REG(I2C_DATA, &oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE) != I2C_STATUS_SUCCESS) {
279 print("oled_render data failed\n");
280 return;
281 }
282 } else {
283 // Rotate the render chunks
284 const static uint8_t source_map[] = OLED_SOURCE_MAP;
285 const static uint8_t target_map[] = OLED_TARGET_MAP;
286
287 static uint8_t temp_buffer[OLED_BLOCK_SIZE];
288 memset(temp_buffer, 0, sizeof(temp_buffer));
289 for(uint8_t i = 0; i < sizeof(source_map); ++i) {
290 rotate_90(&oled_buffer[OLED_BLOCK_SIZE * update_start + source_map[i]], &temp_buffer[target_map[i]]);
291 }
292
293 // Send render data chunk after rotating
294 if (I2C_WRITE_REG(I2C_DATA, &temp_buffer[0], OLED_BLOCK_SIZE) != I2C_STATUS_SUCCESS) {
295 print("oled_render data failed\n");
296 return;
297 }
298 }
299
300 // Turn on display if it is off
301 oled_on();
302
303 // Clear dirty flag
304 oled_dirty &= ~(1 << update_start);
305}
306
307void oled_set_cursor(uint8_t col, uint8_t line) {
308 uint16_t index = line * oled_rotation_width + col * OLED_FONT_WIDTH;
309
310 // Out of bounds?
311 if (index >= OLED_MATRIX_SIZE) {
312 index = 0;
313 }
314
315 oled_cursor = &oled_buffer[index];
316}
317
318void oled_advance_page(bool clearPageRemainder) {
319 uint16_t index = oled_cursor - &oled_buffer[0];
320 uint8_t remaining = oled_rotation_width - (index % oled_rotation_width);
321
322 if (clearPageRemainder) {
323 // Remaining Char count
324 remaining = remaining / OLED_FONT_WIDTH;
325
326 // Write empty character until next line
327 while (remaining--)
328 oled_write_char(' ', false);
329 } else {
330 // Next page index out of bounds?
331 if (index + remaining >= OLED_MATRIX_SIZE) {
332 index = 0;
333 remaining = 0;
334 }
335
336 oled_cursor = &oled_buffer[index + remaining];
337 }
338}
339
340void oled_advance_char(void) {
341 uint16_t nextIndex = oled_cursor - &oled_buffer[0] + OLED_FONT_WIDTH;
342 uint8_t remainingSpace = oled_rotation_width - (nextIndex % oled_rotation_width);
343
344 // Do we have enough space on the current line for the next character
345 if (remainingSpace < OLED_FONT_WIDTH) {
346 nextIndex += remainingSpace;
347 }
348
349 // Did we go out of bounds
350 if (nextIndex >= OLED_MATRIX_SIZE) {
351 nextIndex = 0;
352 }
353
354 // Update cursor position
355 oled_cursor = &oled_buffer[nextIndex];
356}
357
358// Main handler that writes character data to the display buffer
359void oled_write_char(const char data, bool invert) {
360 // Advance to the next line if newline
361 if (data == '\n') {
362 // Old source wrote ' ' until end of line...
363 oled_advance_page(true);
364 return;
365 }
366
367 // copy the current render buffer to check for dirty after
368 static uint8_t oled_temp_buffer[OLED_FONT_WIDTH];
369 memcpy(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH);
370
371 // set the reder buffer data
372 uint8_t cast_data = (uint8_t)data; // font based on unsigned type for index
373 if (cast_data < OLED_FONT_START || cast_data > OLED_FONT_END) {
374 memset(oled_cursor, 0x00, OLED_FONT_WIDTH);
375 } else {
376 const uint8_t *glyph = &font[(cast_data - OLED_FONT_START) * OLED_FONT_WIDTH];
377 memcpy_P(oled_cursor, glyph, OLED_FONT_WIDTH);
378 }
379
380 // Invert if needed
381 if (invert) {
382 InvertCharacter(oled_cursor);
383 }
384
385 // Dirty check
386 if (memcmp(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH)) {
387 oled_dirty |= (1 << ((oled_cursor - &oled_buffer[0]) / OLED_BLOCK_SIZE));
388 }
389
390 // Finally move to the next char
391 oled_advance_char();
392}
393
394void oled_write(const char *data, bool invert) {
395 const char *end = data + strlen(data);
396 while (data < end) {
397 oled_write_char(*data, invert);
398 data++;
399 }
400}
401
402void oled_write_ln(const char *data, bool invert) {
403 oled_write(data, invert);
404 oled_advance_page(true);
405}
406
407#if defined(__AVR__)
408void oled_write_P(const char *data, bool invert) {
409 uint8_t c = pgm_read_byte(data);
410 while (c != 0) {
411 oled_write_char(c, invert);
412 c = pgm_read_byte(++data);
413 }
414}
415
416void oled_write_ln_P(const char *data, bool invert) {
417 oled_write_P(data, invert);
418 oled_advance_page(true);
419}
420#endif // defined(__AVR__)
421
422bool oled_on(void) {
423#if !defined(OLED_DISABLE_TIMEOUT)
424 oled_last_activity = timer_read();
425#endif
426
427 static const uint8_t PROGMEM display_on[] = { I2C_CMD, DISPLAY_ON };
428 if (!oled_active) {
429 if (I2C_TRANSMIT_P(display_on) != I2C_STATUS_SUCCESS) {
430 print("oled_on cmd failed\n");
431 return oled_active;
432 }
433 oled_active = true;
434 }
435 return oled_active;
436}
437
438bool oled_off(void) {
439 static const uint8_t PROGMEM display_off[] = { I2C_CMD, DISPLAY_OFF };
440 if (oled_active) {
441 if (I2C_TRANSMIT_P(display_off) != I2C_STATUS_SUCCESS) {
442 print("oled_off cmd failed\n");
443 return oled_active;
444 }
445 oled_active = false;
446 }
447 return !oled_active;
448}
449
450bool oled_scroll_right(void) {
451 // Dont enable scrolling if we need to update the display
452 // This prevents scrolling of bad data from starting the scroll too early after init
453 if (!oled_dirty && !oled_scrolling) {
454 static const uint8_t PROGMEM display_scroll_right[] = {
455 I2C_CMD, SCROLL_RIGHT, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xFF, ACTIVATE_SCROLL };
456 if (I2C_TRANSMIT_P(display_scroll_right) != I2C_STATUS_SUCCESS) {
457 print("oled_scroll_right cmd failed\n");
458 return oled_scrolling;
459 }
460 oled_scrolling = true;
461 }
462 return oled_scrolling;
463}
464
465bool oled_scroll_left(void) {
466 // Dont enable scrolling if we need to update the display
467 // This prevents scrolling of bad data from starting the scroll too early after init
468 if (!oled_dirty && !oled_scrolling) {
469 static const uint8_t PROGMEM display_scroll_left[] = {
470 I2C_CMD, SCROLL_LEFT, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xFF, ACTIVATE_SCROLL };
471 if (I2C_TRANSMIT_P(display_scroll_left) != I2C_STATUS_SUCCESS) {
472 print("oled_scroll_left cmd failed\n");
473 return oled_scrolling;
474 }
475 oled_scrolling = true;
476 }
477 return oled_scrolling;
478}
479
480bool oled_scroll_off(void) {
481 if (oled_scrolling) {
482 static const uint8_t PROGMEM display_scroll_off[] = { I2C_CMD, DEACTIVATE_SCROLL };
483 if (I2C_TRANSMIT_P(display_scroll_off) != I2C_STATUS_SUCCESS) {
484 print("oled_scroll_off cmd failed\n");
485 return oled_scrolling;
486 }
487 oled_scrolling = false;
488 }
489 return !oled_scrolling;
490}
491
492uint8_t oled_max_chars(void) {
493 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
494 return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH;
495 }
496 return OLED_DISPLAY_HEIGHT / OLED_FONT_WIDTH;
497}
498
499uint8_t oled_max_lines(void) {
500 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
501 return OLED_DISPLAY_HEIGHT / OLED_FONT_HEIGHT;
502 }
503 return OLED_DISPLAY_WIDTH / OLED_FONT_HEIGHT;
504}
505
506void oled_task(void) {
507 if (!oled_initialized) {
508 return;
509 }
510
511 oled_set_cursor(0, 0);
512
513 oled_task_user();
514
515 // Smart render system, no need to check for dirty
516 oled_render();
517
518 // Display timeout check
519#if !defined(OLED_DISABLE_TIMEOUT)
520 if (oled_active && timer_elapsed(oled_last_activity) > OLED_TIMEOUT) {
521 oled_off();
522 }
523#endif
524}
525
526__attribute__((weak))
527void oled_task_user(void) {
528}
diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h
new file mode 100644
index 000000000..1ca31df11
--- /dev/null
+++ b/drivers/oled/oled_driver.h
@@ -0,0 +1,183 @@
1/*
2Copyright 2019 Ryan Caltabiano <https://github.com/XScorpion2>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17#pragma once
18
19#include <stdint.h>
20#include <stdbool.h>
21
22
23#if defined(OLED_DISPLAY_CUSTOM)
24 // Expected user to implement the necessary defines
25#elif defined(OLED_DISPLAY_128X64)
26 // Double height 128x64
27 #define OLED_DISPLAY_WIDTH 128
28 #define OLED_DISPLAY_HEIGHT 64
29 #define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) // 1024 (compile time mathed)
30 #define OLED_BLOCK_TYPE uint16_t
31 #define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 16 (compile time mathed)
32 #define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 64 (compile time mathed)
33
34 // For 90 degree rotation, we map our internal matrix to oled matrix using fixed arrays
35 // The OLED writes to it's memory horizontally, starting top left, but our memory starts bottom left in this mode
36 #define OLED_SOURCE_MAP { 0, 8, 16, 24, 32, 40, 48, 56 }
37 #define OLED_TARGET_MAP { 56, 48, 40, 32, 24, 16, 8, 0 }
38 // If OLED_BLOCK_TYPE is uint8_t, these tables would look like:
39 // #define OLED_SOURCE_MAP { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120 }
40 // #define OLED_TARGET_MAP { 56, 120, 48, 112, 40, 104, 32, 96, 24, 88, 16, 80, 8, 72, 0, 64 }
41#else // defined(OLED_DISPLAY_128X64)
42 // Default 128x32
43 #define OLED_DISPLAY_WIDTH 128
44 #define OLED_DISPLAY_HEIGHT 32
45 #define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) // 512 (compile time mathed)
46 #define OLED_BLOCK_TYPE uint8_t // Type to use for segmenting the oled display for smart rendering, use unsigned types only
47 #define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 8 (compile time mathed)
48 #define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 128 (compile time mathed)
49
50 // For 90 degree rotation, we map our internal matrix to oled matrix using fixed arrays
51 // The OLED writes to it's memory horizontally, starting top left, but our memory starts bottom left in this mode
52 #define OLED_SOURCE_MAP { 0, 8, 16, 24, 32, 40, 48, 56 }
53 #define OLED_TARGET_MAP { 48, 32, 16, 0, 56, 40, 24, 8 }
54#endif // defined(OLED_DISPLAY_CUSTOM)
55
56// Address to use for tthe i2d oled communication
57#if !defined(OLED_DISPLAY_ADDRESS)
58 #define OLED_DISPLAY_ADDRESS 0x3C
59#endif
60
61// Custom font file to use
62#if !defined(OLED_FONT_H)
63 #define OLED_FONT_H "glcdfont.c"
64#endif
65// unsigned char value of the first character in the font file
66#if !defined(OLED_FONT_START)
67 #define OLED_FONT_START 0
68#endif
69// unsigned char value of the last character in the font file
70#if !defined(OLED_FONT_END)
71 #define OLED_FONT_END 224
72#endif
73// Font render width
74#if !defined(OLED_FONT_WIDTH)
75 #define OLED_FONT_WIDTH 6
76#endif
77// Font render height
78#if !defined(OLED_FONT_HEIGHT)
79 #define OLED_FONT_HEIGHT 8
80#endif
81
82#define OLED_ROTATION_0 0x00
83#define OLED_ROTATION_90 0x01
84#define OLED_ROTATION_180 0x02
85#define OLED_ROTATION_270 0x03
86
87// Initialize the oled display, rotating the rendered output based on the define passed in.
88// Returns true if the OLED was initialized successfully
89bool oled_init(uint8_t rotation);
90
91// Called at the start of oled_init, weak function overridable by the user
92// rotation - the value passed into oled_init
93// Return new uint8_t if you want to override default rotation
94uint8_t oled_init_user(uint8_t rotation);
95
96// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering
97void oled_clear(void);
98
99// Renders the dirty chunks of the buffer to oled display
100void oled_render(void);
101
102// Moves cursor to character position indicated by column and line, wraps if out of bounds
103// Max column denoted by 'oled_max_chars()' and max lines by 'oled_max_lines()' functions
104void oled_set_cursor(uint8_t col, uint8_t line);
105
106// Advances the cursor to the next page, writing ' ' if true
107// Wraps to the begining when out of bounds
108void oled_advance_page(bool clearPageRemainder);
109
110// Moves the cursor forward 1 character length
111// Advance page if there is not enough room for the next character
112// Wraps to the begining when out of bounds
113void oled_advance_char(void);
114
115// Writes a single character to the buffer at current cursor position
116// Advances the cursor while writing, inverts the pixels if true
117// Main handler that writes character data to the display buffer
118void oled_write_char(const char data, bool invert);
119
120// Writes a string to the buffer at current cursor position
121// Advances the cursor while writing, inverts the pixels if true
122void oled_write(const char *data, bool invert);
123
124// Writes a string to the buffer at current cursor position
125// Advances the cursor while writing, inverts the pixels if true
126// Advances the cursor to the next page, wiring ' ' to the remainder of the current page
127void oled_write_ln(const char *data, bool invert);
128
129#if defined(__AVR__)
130// Writes a PROGMEM string to the buffer at current cursor position
131// Advances the cursor while writing, inverts the pixels if true
132// Remapped to call 'void oled_write(const char *data, bool invert);' on ARM
133void oled_write_P(const char *data, bool invert);
134
135// Writes a PROGMEM string to the buffer at current cursor position
136// Advances the cursor while writing, inverts the pixels if true
137// Advances the cursor to the next page, wiring ' ' to the remainder of the current page
138// Remapped to call 'void oled_write_ln(const char *data, bool invert);' on ARM
139void oled_write_ln_P(const char *data, bool invert);
140#else
141 // Writes a string to the buffer at current cursor position
142 // Advances the cursor while writing, inverts the pixels if true
143 #define oled_write_P(data, invert) oled_write(data, invert)
144
145 // Writes a string to the buffer at current cursor position
146 // Advances the cursor while writing, inverts the pixels if true
147 // Advances the cursor to the next page, wiring ' ' to the remainder of the current page
148 #define oled_write_ln_P(data, invert) oled_write(data, invert)
149#endif // defined(__AVR__)
150
151// Can be used to manually turn on the screen if it is off
152// Returns true if the screen was on or turns on
153bool oled_on(void);
154
155// Can be used to manually turn off the screen if it is on
156// Returns true if the screen was off or turns off
157bool oled_off(void);
158
159// Basically it's oled_render, but with timeout management and oled_task_user calling!
160void oled_task(void);
161
162// Called at the start of oled_task, weak function overridable by the user
163void oled_task_user(void);
164
165// Scrolls the entire display right
166// Returns true if the screen was scrolling or starts scrolling
167// NOTE: display contents cannot be changed while scrolling
168bool oled_scroll_right(void);
169
170// Scrolls the entire display left
171// Returns true if the screen was scrolling or starts scrolling
172// NOTE: display contents cannot be changed while scrolling
173bool oled_scroll_left(void);
174
175// Turns off display scrolling
176// Returns true if the screen was not scrolling or stops scrolling
177bool oled_scroll_off(void);
178
179// Returns the maximum number of characters that will fit on a line
180uint8_t oled_max_chars(void);
181
182// Returns the maximum number of lines that will fit on the oled
183uint8_t oled_max_lines(void);