aboutsummaryrefslogtreecommitdiff
path: root/drivers/avr/ssd1306.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/avr/ssd1306.c')
-rw-r--r--drivers/avr/ssd1306.c470
1 files changed, 470 insertions, 0 deletions
diff --git a/drivers/avr/ssd1306.c b/drivers/avr/ssd1306.c
new file mode 100644
index 000000000..f9312d2dc
--- /dev/null
+++ b/drivers/avr/ssd1306.c
@@ -0,0 +1,470 @@
1#ifdef SSD1306OLED
2
3#include "ssd1306.h"
4#include "config.h"
5#include "i2c.h"
6#include <string.h>
7#include "print.h"
8#include "lets_split.h"
9#include "glcdfont.c"
10#ifdef ADAFRUIT_BLE_ENABLE
11#include "adafruit_ble.h"
12#endif
13#ifdef PROTOCOL_LUFA
14#include "lufa.h"
15#endif
16#include "sendchar.h"
17#include "pincontrol.h"
18
19//assign the right code to your layers
20#define _BASE 0
21#define _LOWER 8
22#define _RAISE 16
23#define _FNLAYER 64
24#define _NUMLAY 128
25#define _NLOWER 136
26#define _NFNLAYER 192
27#define _MOUSECURSOR 256
28#define _ADJUST 65560
29
30// Set this to 1 to help diagnose early startup problems
31// when testing power-on with ble. Turn it off otherwise,
32// as the latency of printing most of the debug info messes
33// with the matrix scan, causing keys to drop.
34#define DEBUG_TO_SCREEN 0
35
36// Controls the SSD1306 128x32 OLED display via i2c
37
38#define i2cAddress 0x3C
39
40#define DisplayHeight 32
41#define DisplayWidth 128
42
43#define FontHeight 8
44#define FontWidth 6
45
46#define MatrixRows (DisplayHeight / FontHeight)
47#define MatrixCols (DisplayWidth / FontWidth)
48
49struct CharacterMatrix {
50 uint8_t display[MatrixRows][MatrixCols];
51 uint8_t *cursor;
52 bool dirty;
53};
54
55static struct CharacterMatrix display;
56//static uint16_t last_battery_update;
57//static uint32_t vbat;
58//#define BatteryUpdateInterval 10000 /* milliseconds */
59#define ScreenOffInterval 300000 /* milliseconds */
60#if DEBUG_TO_SCREEN
61static uint8_t displaying;
62#endif
63static uint16_t last_flush;
64
65enum ssd1306_cmds {
66 DisplayOff = 0xAE,
67 DisplayOn = 0xAF,
68
69 SetContrast = 0x81,
70 DisplayAllOnResume = 0xA4,
71
72 DisplayAllOn = 0xA5,
73 NormalDisplay = 0xA6,
74 InvertDisplay = 0xA7,
75 SetDisplayOffset = 0xD3,
76 SetComPins = 0xda,
77 SetVComDetect = 0xdb,
78 SetDisplayClockDiv = 0xD5,
79 SetPreCharge = 0xd9,
80 SetMultiPlex = 0xa8,
81 SetLowColumn = 0x00,
82 SetHighColumn = 0x10,
83 SetStartLine = 0x40,
84
85 SetMemoryMode = 0x20,
86 ColumnAddr = 0x21,
87 PageAddr = 0x22,
88
89 ComScanInc = 0xc0,
90 ComScanDec = 0xc8,
91 SegRemap = 0xa0,
92 SetChargePump = 0x8d,
93 ExternalVcc = 0x01,
94 SwitchCapVcc = 0x02,
95
96 ActivateScroll = 0x2f,
97 DeActivateScroll = 0x2e,
98 SetVerticalScrollArea = 0xa3,
99 RightHorizontalScroll = 0x26,
100 LeftHorizontalScroll = 0x27,
101 VerticalAndRightHorizontalScroll = 0x29,
102 VerticalAndLeftHorizontalScroll = 0x2a,
103};
104
105
106// Write command sequence.
107// Returns true on success.
108static inline bool _send_cmd1(uint8_t cmd) {
109 bool res = false;
110
111 if (i2c_start_write(i2cAddress)) {
112 xprintf("failed to start write to %d\n", i2cAddress);
113 goto done;
114 }
115
116 if (i2c_master_write(0x0 /* command byte follows */)) {
117 print("failed to write control byte\n");
118
119 goto done;
120 }
121
122 if (i2c_master_write(cmd)) {
123 xprintf("failed to write command %d\n", cmd);
124 goto done;
125 }
126 res = true;
127done:
128 i2c_master_stop();
129 return res;
130}
131
132// Write 2-byte command sequence.
133// Returns true on success
134static inline bool _send_cmd2(uint8_t cmd, uint8_t opr) {
135 if (!_send_cmd1(cmd)) {
136 return false;
137 }
138 return _send_cmd1(opr);
139}
140
141// Write 3-byte command sequence.
142// Returns true on success
143static inline bool _send_cmd3(uint8_t cmd, uint8_t opr1, uint8_t opr2) {
144 if (!_send_cmd1(cmd)) {
145 return false;
146 }
147 if (!_send_cmd1(opr1)) {
148 return false;
149 }
150 return _send_cmd1(opr2);
151}
152
153#define send_cmd1(c) if (!_send_cmd1(c)) {goto done;}
154#define send_cmd2(c,o) if (!_send_cmd2(c,o)) {goto done;}
155#define send_cmd3(c,o1,o2) if (!_send_cmd3(c,o1,o2)) {goto done;}
156
157static void matrix_clear(struct CharacterMatrix *matrix);
158
159static void clear_display(void) {
160 matrix_clear(&display);
161
162 // Clear all of the display bits (there can be random noise
163 // in the RAM on startup)
164 send_cmd3(PageAddr, 0, (DisplayHeight / 8) - 1);
165 send_cmd3(ColumnAddr, 0, DisplayWidth - 1);
166
167 if (i2c_start_write(i2cAddress)) {
168 goto done;
169 }
170 if (i2c_master_write(0x40)) {
171 // Data mode
172 goto done;
173 }
174 for (uint8_t row = 0; row < MatrixRows; ++row) {
175 for (uint8_t col = 0; col < DisplayWidth; ++col) {
176 i2c_master_write(0);
177 }
178 }
179
180 display.dirty = false;
181
182done:
183 i2c_master_stop();
184}
185
186#if DEBUG_TO_SCREEN
187#undef sendchar
188static int8_t capture_sendchar(uint8_t c) {
189 sendchar(c);
190 iota_gfx_write_char(c);
191
192 if (!displaying) {
193 iota_gfx_flush();
194 }
195 return 0;
196}
197#endif
198
199bool iota_gfx_init(void) {
200 bool success = false;
201
202 send_cmd1(DisplayOff);
203 send_cmd2(SetDisplayClockDiv, 0x80);
204 send_cmd2(SetMultiPlex, DisplayHeight - 1);
205
206 send_cmd2(SetDisplayOffset, 0);
207
208
209 send_cmd1(SetStartLine | 0x0);
210 send_cmd2(SetChargePump, 0x14 /* Enable */);
211 send_cmd2(SetMemoryMode, 0 /* horizontal addressing */);
212
213/// Flips the display orientation 0 degrees
214 send_cmd1(SegRemap | 0x1);
215 send_cmd1(ComScanDec);
216/*
217// the following Flip the display orientation 180 degrees
218 send_cmd1(SegRemap);
219 send_cmd1(ComScanInc);
220// end flip */
221 send_cmd2(SetComPins, 0x2);
222 send_cmd2(SetContrast, 0x8f);
223 send_cmd2(SetPreCharge, 0xf1);
224 send_cmd2(SetVComDetect, 0x40);
225 send_cmd1(DisplayAllOnResume);
226 send_cmd1(NormalDisplay);
227 send_cmd1(DeActivateScroll);
228 send_cmd1(DisplayOn);
229
230 send_cmd2(SetContrast, 0); // Dim
231
232 clear_display();
233
234 success = true;
235
236 iota_gfx_flush();
237
238#if DEBUG_TO_SCREEN
239 print_set_sendchar(capture_sendchar);
240#endif
241
242done:
243 return success;
244}
245
246bool iota_gfx_off(void) {
247 bool success = false;
248
249 send_cmd1(DisplayOff);
250 success = true;
251
252done:
253 return success;
254}
255
256bool iota_gfx_on(void) {
257 bool success = false;
258
259 send_cmd1(DisplayOn);
260 success = true;
261
262done:
263 return success;
264}
265
266static void matrix_write_char_inner(struct CharacterMatrix *matrix, uint8_t c) {
267 *matrix->cursor = c;
268 ++matrix->cursor;
269
270 if (matrix->cursor - &matrix->display[0][0] == sizeof(matrix->display)) {
271 // We went off the end; scroll the display upwards by one line
272 memmove(&matrix->display[0], &matrix->display[1],
273 MatrixCols * (MatrixRows - 1));
274 matrix->cursor = &matrix->display[MatrixRows - 1][0];
275 memset(matrix->cursor, ' ', MatrixCols);
276 }
277}
278
279static void matrix_write_char(struct CharacterMatrix *matrix, uint8_t c) {
280 matrix->dirty = true;
281
282 if (c == '\n') {
283 // Clear to end of line from the cursor and then move to the
284 // start of the next line
285 uint8_t cursor_col = (matrix->cursor - &matrix->display[0][0]) % MatrixCols;
286
287 while (cursor_col++ < MatrixCols) {
288 matrix_write_char_inner(matrix, ' ');
289 }
290 return;
291 }
292
293 matrix_write_char_inner(matrix, c);
294}
295
296void iota_gfx_write_char(uint8_t c) {
297 matrix_write_char(&display, c);
298}
299
300static void matrix_write(struct CharacterMatrix *matrix, const char *data) {
301 const char *end = data + strlen(data);
302 while (data < end) {
303 matrix_write_char(matrix, *data);
304 ++data;
305 }
306}
307
308void iota_gfx_write(const char *data) {
309 matrix_write(&display, data);
310}
311
312static void matrix_write_P(struct CharacterMatrix *matrix, const char *data) {
313 while (true) {
314 uint8_t c = pgm_read_byte(data);
315 if (c == 0) {
316 return;
317 }
318 matrix_write_char(matrix, c);
319 ++data;
320 }
321}
322
323void iota_gfx_write_P(const char *data) {
324 matrix_write_P(&display, data);
325}
326
327static void matrix_clear(struct CharacterMatrix *matrix) {
328 memset(matrix->display, ' ', sizeof(matrix->display));
329 matrix->cursor = &matrix->display[0][0];
330 matrix->dirty = true;
331}
332
333void iota_gfx_clear_screen(void) {
334 matrix_clear(&display);
335}
336
337static void matrix_render(struct CharacterMatrix *matrix) {
338 last_flush = timer_read();
339 iota_gfx_on();
340#if DEBUG_TO_SCREEN
341 ++displaying;
342#endif
343
344 // Move to the home position
345 send_cmd3(PageAddr, 0, MatrixRows - 1);
346 send_cmd3(ColumnAddr, 0, (MatrixCols * FontWidth) - 1);
347
348 if (i2c_start_write(i2cAddress)) {
349 goto done;
350 }
351 if (i2c_master_write(0x40)) {
352 // Data mode
353 goto done;
354 }
355
356 for (uint8_t row = 0; row < MatrixRows; ++row) {
357 for (uint8_t col = 0; col < MatrixCols; ++col) {
358 const uint8_t *glyph = font + (matrix->display[row][col] * (FontWidth - 1));
359
360 for (uint8_t glyphCol = 0; glyphCol < FontWidth - 1; ++glyphCol) {
361 uint8_t colBits = pgm_read_byte(glyph + glyphCol);
362 i2c_master_write(colBits);
363 }
364
365 // 1 column of space between chars (it's not included in the glyph)
366 i2c_master_write(0);
367 }
368 }
369
370 matrix->dirty = false;
371
372done:
373 i2c_master_stop();
374#if DEBUG_TO_SCREEN
375 --displaying;
376#endif
377}
378
379void iota_gfx_flush(void) {
380 matrix_render(&display);
381}
382
383static void matrix_update(struct CharacterMatrix *dest,
384 const struct CharacterMatrix *source) {
385 if (memcmp(dest->display, source->display, sizeof(dest->display))) {
386 memcpy(dest->display, source->display, sizeof(dest->display));
387 dest->dirty = true;
388 }
389}
390
391static void render_status_info(void) {
392#if DEBUG_TO_SCREEN
393 if (debug_enable) {
394 return;
395 }
396#endif
397
398 struct CharacterMatrix matrix;
399
400 matrix_clear(&matrix);
401 matrix_write_P(&matrix, PSTR("USB: "));
402#ifdef PROTOCOL_LUFA
403 switch (USB_DeviceState) {
404 case DEVICE_STATE_Unattached:
405 matrix_write_P(&matrix, PSTR("Unattached"));
406 break;
407 case DEVICE_STATE_Suspended:
408 matrix_write_P(&matrix, PSTR("Suspended"));
409 break;
410 case DEVICE_STATE_Configured:
411 matrix_write_P(&matrix, PSTR("Connected"));
412 break;
413 case DEVICE_STATE_Powered:
414 matrix_write_P(&matrix, PSTR("Powered"));
415 break;
416 case DEVICE_STATE_Default:
417 matrix_write_P(&matrix, PSTR("Default"));
418 break;
419 case DEVICE_STATE_Addressed:
420 matrix_write_P(&matrix, PSTR("Addressed"));
421 break;
422 default:
423 matrix_write_P(&matrix, PSTR("Invalid"));
424 }
425#endif
426
427// Define layers here, Have not worked out how to have text displayed for each layer. Copy down the number you see and add a case for it below
428
429 char buf[40];
430 snprintf(buf,sizeof(buf), "Undef-%ld", layer_state);
431 matrix_write_P(&matrix, PSTR("\n\nLayer: "));
432 switch (layer_state) {
433 case _BASE:
434 matrix_write_P(&matrix, PSTR("Default"));
435 break;
436 case _RAISE:
437 matrix_write_P(&matrix, PSTR("Raise"));
438 break;
439 case _LOWER:
440 matrix_write_P(&matrix, PSTR("Lower"));
441 break;
442 case _ADJUST:
443 matrix_write_P(&matrix, PSTR("ADJUST"));
444 break;
445 default:
446 matrix_write(&matrix, buf);
447 }
448
449 // Host Keyboard LED Status
450 char led[40];
451 snprintf(led, sizeof(led), "\n%s %s %s",
452 (host_keyboard_leds() & (1<<USB_LED_NUM_LOCK)) ? "NUMLOCK" : " ",
453 (host_keyboard_leds() & (1<<USB_LED_CAPS_LOCK)) ? "CAPS" : " ",
454 (host_keyboard_leds() & (1<<USB_LED_SCROLL_LOCK)) ? "SCLK" : " ");
455 matrix_write(&matrix, led);
456 matrix_update(&display, &matrix);
457}
458
459void iota_gfx_task(void) {
460 render_status_info();
461
462 if (display.dirty) {
463 iota_gfx_flush();
464 }
465
466 if (timer_elapsed(last_flush) > ScreenOffInterval) {
467 iota_gfx_off();
468 }
469}
470#endif