diff options
Diffstat (limited to 'keyboards/comet46/ssd1306.c')
-rw-r--r-- | keyboards/comet46/ssd1306.c | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/keyboards/comet46/ssd1306.c b/keyboards/comet46/ssd1306.c new file mode 100644 index 000000000..4bd2d80bc --- /dev/null +++ b/keyboards/comet46/ssd1306.c | |||
@@ -0,0 +1,343 @@ | |||
1 | #ifdef SSD1306OLED | ||
2 | |||
3 | #include "ssd1306.h" | ||
4 | #include "i2c.h" | ||
5 | #include <string.h> | ||
6 | #include "print.h" | ||
7 | #ifdef PROTOCOL_LUFA | ||
8 | #include "lufa.h" | ||
9 | #endif | ||
10 | #include "sendchar.h" | ||
11 | #include "timer.h" | ||
12 | |||
13 | struct CharacterMatrix display; | ||
14 | |||
15 | extern const unsigned char font[] PROGMEM; | ||
16 | |||
17 | // Set this to 1 to help diagnose early startup problems | ||
18 | // when testing power-on with ble. Turn it off otherwise, | ||
19 | // as the latency of printing most of the debug info messes | ||
20 | // with the matrix scan, causing keys to drop. | ||
21 | #define DEBUG_TO_SCREEN 0 | ||
22 | |||
23 | //static uint16_t last_battery_update; | ||
24 | //static uint32_t vbat; | ||
25 | //#define BatteryUpdateInterval 10000 /* milliseconds */ | ||
26 | |||
27 | // 'last_flush' is declared as uint16_t, | ||
28 | // so this must be less than 65535 | ||
29 | #define ScreenOffInterval 60000 /* milliseconds */ | ||
30 | #if DEBUG_TO_SCREEN | ||
31 | static uint8_t displaying; | ||
32 | #endif | ||
33 | static uint16_t last_flush; | ||
34 | |||
35 | static bool force_dirty = true; | ||
36 | |||
37 | // Write command sequence. | ||
38 | // Returns true on success. | ||
39 | static inline bool _send_cmd1(uint8_t cmd) { | ||
40 | bool res = false; | ||
41 | |||
42 | if (i2c_start_write(SSD1306_ADDRESS)) { | ||
43 | xprintf("failed to start write to %d\n", SSD1306_ADDRESS); | ||
44 | goto done; | ||
45 | } | ||
46 | |||
47 | if (i2c_master_write(0x0 /* command byte follows */)) { | ||
48 | print("failed to write control byte\n"); | ||
49 | |||
50 | goto done; | ||
51 | } | ||
52 | |||
53 | if (i2c_master_write(cmd)) { | ||
54 | xprintf("failed to write command %d\n", cmd); | ||
55 | goto done; | ||
56 | } | ||
57 | res = true; | ||
58 | done: | ||
59 | i2c_master_stop(); | ||
60 | return res; | ||
61 | } | ||
62 | |||
63 | // Write 2-byte command sequence. | ||
64 | // Returns true on success | ||
65 | static inline bool _send_cmd2(uint8_t cmd, uint8_t opr) { | ||
66 | if (!_send_cmd1(cmd)) { | ||
67 | return false; | ||
68 | } | ||
69 | return _send_cmd1(opr); | ||
70 | } | ||
71 | |||
72 | // Write 3-byte command sequence. | ||
73 | // Returns true on success | ||
74 | static inline bool _send_cmd3(uint8_t cmd, uint8_t opr1, uint8_t opr2) { | ||
75 | if (!_send_cmd1(cmd)) { | ||
76 | return false; | ||
77 | } | ||
78 | if (!_send_cmd1(opr1)) { | ||
79 | return false; | ||
80 | } | ||
81 | return _send_cmd1(opr2); | ||
82 | } | ||
83 | |||
84 | #define send_cmd1(c) if (!_send_cmd1(c)) {goto done;} | ||
85 | #define send_cmd2(c,o) if (!_send_cmd2(c,o)) {goto done;} | ||
86 | #define send_cmd3(c,o1,o2) if (!_send_cmd3(c,o1,o2)) {goto done;} | ||
87 | |||
88 | static void clear_display(void) { | ||
89 | matrix_clear(&display); | ||
90 | |||
91 | // Clear all of the display bits (there can be random noise | ||
92 | // in the RAM on startup) | ||
93 | send_cmd3(PageAddr, 0, (DisplayHeight / 8) - 1); | ||
94 | send_cmd3(ColumnAddr, 0, DisplayWidth - 1); | ||
95 | |||
96 | if (i2c_start_write(SSD1306_ADDRESS)) { | ||
97 | goto done; | ||
98 | } | ||
99 | if (i2c_master_write(0x40)) { | ||
100 | // Data mode | ||
101 | goto done; | ||
102 | } | ||
103 | for (uint8_t row = 0; row < MatrixRows; ++row) { | ||
104 | for (uint8_t col = 0; col < DisplayWidth; ++col) { | ||
105 | i2c_master_write(0); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | display.dirty = false; | ||
110 | |||
111 | done: | ||
112 | i2c_master_stop(); | ||
113 | } | ||
114 | |||
115 | #if DEBUG_TO_SCREEN | ||
116 | #undef sendchar | ||
117 | static int8_t capture_sendchar(uint8_t c) { | ||
118 | sendchar(c); | ||
119 | iota_gfx_write_char(c); | ||
120 | |||
121 | if (!displaying) { | ||
122 | iota_gfx_flush(); | ||
123 | } | ||
124 | return 0; | ||
125 | } | ||
126 | #endif | ||
127 | |||
128 | bool iota_gfx_init(bool rotate) { | ||
129 | bool success = false; | ||
130 | |||
131 | i2c_master_init(); | ||
132 | send_cmd1(DisplayOff); | ||
133 | send_cmd2(SetDisplayClockDiv, 0x80); | ||
134 | send_cmd2(SetMultiPlex, DisplayHeight - 1); | ||
135 | |||
136 | send_cmd2(SetDisplayOffset, 0); | ||
137 | |||
138 | |||
139 | send_cmd1(SetStartLine | 0x0); | ||
140 | send_cmd2(SetChargePump, 0x14 /* Enable */); | ||
141 | send_cmd2(SetMemoryMode, 0 /* horizontal addressing */); | ||
142 | |||
143 | if(rotate){ | ||
144 | // the following Flip the display orientation 180 degrees | ||
145 | send_cmd1(SegRemap); | ||
146 | send_cmd1(ComScanInc); | ||
147 | }else{ | ||
148 | // Flips the display orientation 0 degrees | ||
149 | send_cmd1(SegRemap | 0x1); | ||
150 | send_cmd1(ComScanDec); | ||
151 | } | ||
152 | |||
153 | send_cmd2(SetComPins, 0x2); | ||
154 | send_cmd2(SetContrast, 0x8f); | ||
155 | send_cmd2(SetPreCharge, 0xf1); | ||
156 | send_cmd2(SetVComDetect, 0x40); | ||
157 | send_cmd1(DisplayAllOnResume); | ||
158 | send_cmd1(NormalDisplay); | ||
159 | send_cmd1(DeActivateScroll); | ||
160 | send_cmd1(DisplayOn); | ||
161 | |||
162 | send_cmd2(SetContrast, 0); // Dim | ||
163 | |||
164 | clear_display(); | ||
165 | |||
166 | success = true; | ||
167 | |||
168 | iota_gfx_flush(); | ||
169 | |||
170 | #if DEBUG_TO_SCREEN | ||
171 | print_set_sendchar(capture_sendchar); | ||
172 | #endif | ||
173 | |||
174 | done: | ||
175 | return success; | ||
176 | } | ||
177 | |||
178 | bool iota_gfx_off(void) { | ||
179 | bool success = false; | ||
180 | |||
181 | send_cmd1(DisplayOff); | ||
182 | success = true; | ||
183 | |||
184 | done: | ||
185 | return success; | ||
186 | } | ||
187 | |||
188 | bool iota_gfx_on(void) { | ||
189 | bool success = false; | ||
190 | |||
191 | send_cmd1(DisplayOn); | ||
192 | success = true; | ||
193 | |||
194 | done: | ||
195 | return success; | ||
196 | } | ||
197 | |||
198 | void matrix_write_char_inner(struct CharacterMatrix *matrix, uint8_t c) { | ||
199 | *matrix->cursor = c; | ||
200 | ++matrix->cursor; | ||
201 | |||
202 | if (matrix->cursor - &matrix->display[0][0] == sizeof(matrix->display)) { | ||
203 | // We went off the end; scroll the display upwards by one line | ||
204 | memmove(&matrix->display[0], &matrix->display[1], | ||
205 | MatrixCols * (MatrixRows - 1)); | ||
206 | matrix->cursor = &matrix->display[MatrixRows - 1][0]; | ||
207 | memset(matrix->cursor, ' ', MatrixCols); | ||
208 | } | ||
209 | } | ||
210 | |||
211 | void matrix_write_char(struct CharacterMatrix *matrix, uint8_t c) { | ||
212 | matrix->dirty = true; | ||
213 | |||
214 | if (c == '\n') { | ||
215 | // Clear to end of line from the cursor and then move to the | ||
216 | // start of the next line | ||
217 | uint8_t cursor_col = (matrix->cursor - &matrix->display[0][0]) % MatrixCols; | ||
218 | |||
219 | while (cursor_col++ < MatrixCols) { | ||
220 | matrix_write_char_inner(matrix, ' '); | ||
221 | } | ||
222 | return; | ||
223 | } | ||
224 | |||
225 | matrix_write_char_inner(matrix, c); | ||
226 | } | ||
227 | |||
228 | void iota_gfx_write_char(uint8_t c) { | ||
229 | matrix_write_char(&display, c); | ||
230 | } | ||
231 | |||
232 | void matrix_write(struct CharacterMatrix *matrix, const char *data) { | ||
233 | const char *end = data + strlen(data); | ||
234 | while (data < end) { | ||
235 | matrix_write_char(matrix, *data); | ||
236 | ++data; | ||
237 | } | ||
238 | } | ||
239 | |||
240 | void matrix_write_ln(struct CharacterMatrix *matrix, const char *data) { | ||
241 | char data_ln[strlen(data)+2]; | ||
242 | snprintf(data_ln, sizeof(data_ln), "%s\n", data); | ||
243 | matrix_write(matrix, data_ln); | ||
244 | } | ||
245 | |||
246 | void iota_gfx_write(const char *data) { | ||
247 | matrix_write(&display, data); | ||
248 | } | ||
249 | |||
250 | void matrix_write_P(struct CharacterMatrix *matrix, const char *data) { | ||
251 | while (true) { | ||
252 | uint8_t c = pgm_read_byte(data); | ||
253 | if (c == 0) { | ||
254 | return; | ||
255 | } | ||
256 | matrix_write_char(matrix, c); | ||
257 | ++data; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | void iota_gfx_write_P(const char *data) { | ||
262 | matrix_write_P(&display, data); | ||
263 | } | ||
264 | |||
265 | void matrix_clear(struct CharacterMatrix *matrix) { | ||
266 | memset(matrix->display, ' ', sizeof(matrix->display)); | ||
267 | matrix->cursor = &matrix->display[0][0]; | ||
268 | matrix->dirty = true; | ||
269 | } | ||
270 | |||
271 | void iota_gfx_clear_screen(void) { | ||
272 | matrix_clear(&display); | ||
273 | } | ||
274 | |||
275 | void matrix_render(struct CharacterMatrix *matrix) { | ||
276 | last_flush = timer_read(); | ||
277 | iota_gfx_on(); | ||
278 | #if DEBUG_TO_SCREEN | ||
279 | ++displaying; | ||
280 | #endif | ||
281 | |||
282 | // Move to the home position | ||
283 | send_cmd3(PageAddr, 0, MatrixRows - 1); | ||
284 | send_cmd3(ColumnAddr, 0, (MatrixCols * FontWidth) - 1); | ||
285 | |||
286 | if (i2c_start_write(SSD1306_ADDRESS)) { | ||
287 | goto done; | ||
288 | } | ||
289 | if (i2c_master_write(0x40)) { | ||
290 | // Data mode | ||
291 | goto done; | ||
292 | } | ||
293 | |||
294 | for (uint8_t row = 0; row < MatrixRows; ++row) { | ||
295 | for (uint8_t col = 0; col < MatrixCols; ++col) { | ||
296 | const uint8_t *glyph = font + (matrix->display[row][col] * FontWidth); | ||
297 | |||
298 | for (uint8_t glyphCol = 0; glyphCol < FontWidth; ++glyphCol) { | ||
299 | uint8_t colBits = pgm_read_byte(glyph + glyphCol); | ||
300 | i2c_master_write(colBits); | ||
301 | } | ||
302 | |||
303 | // 1 column of space between chars (it's not included in the glyph) | ||
304 | //i2c_master_write(0); | ||
305 | } | ||
306 | } | ||
307 | |||
308 | matrix->dirty = false; | ||
309 | |||
310 | done: | ||
311 | i2c_master_stop(); | ||
312 | #if DEBUG_TO_SCREEN | ||
313 | --displaying; | ||
314 | #endif | ||
315 | } | ||
316 | |||
317 | void iota_gfx_flush(void) { | ||
318 | matrix_render(&display); | ||
319 | } | ||
320 | |||
321 | __attribute__ ((weak)) | ||
322 | void iota_gfx_task_user(void) { | ||
323 | } | ||
324 | |||
325 | void iota_gfx_task(void) { | ||
326 | iota_gfx_task_user(); | ||
327 | |||
328 | if (display.dirty|| force_dirty) { | ||
329 | iota_gfx_flush(); | ||
330 | force_dirty = false; | ||
331 | } | ||
332 | |||
333 | if (timer_elapsed(last_flush) > ScreenOffInterval) { | ||
334 | iota_gfx_off(); | ||
335 | } | ||
336 | } | ||
337 | |||
338 | bool process_record_gfx(uint16_t keycode, keyrecord_t *record) { | ||
339 | force_dirty = true; | ||
340 | return true; | ||
341 | } | ||
342 | |||
343 | #endif | ||