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