]> git.donarmstrong.com Git - qmk_firmware.git/blob - drivers/avr/ssd1306.c
03a909e9df28e36cec28678482196d5e7e66c302
[qmk_firmware.git] / drivers / avr / 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(void) {
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 /// Flips the display orientation 0 degrees
138   send_cmd1(SegRemap | 0x1);
139   send_cmd1(ComScanDec);
140 /*
141 // the following Flip the display orientation 180 degrees
142   send_cmd1(SegRemap);
143   send_cmd1(ComScanInc);
144 // end flip */
145   send_cmd2(SetComPins, 0x2);
146   send_cmd2(SetContrast, 0x8f);
147   send_cmd2(SetPreCharge, 0xf1);
148   send_cmd2(SetVComDetect, 0x40);
149   send_cmd1(DisplayAllOnResume);
150   send_cmd1(NormalDisplay);
151   send_cmd1(DeActivateScroll);
152   send_cmd1(DisplayOn);
153
154   send_cmd2(SetContrast, 0); // Dim
155
156   clear_display();
157
158   success = true;
159
160   iota_gfx_flush();
161
162 #if DEBUG_TO_SCREEN
163   print_set_sendchar(capture_sendchar);
164 #endif
165
166 done:
167   return success;
168 }
169
170 bool iota_gfx_off(void) {
171   bool success = false;
172
173   send_cmd1(DisplayOff);
174   success = true;
175
176 done:
177   return success;
178
179
180 bool iota_gfx_on(void) {
181   bool success = false;
182
183   send_cmd1(DisplayOn);
184   success = true;
185
186 done:
187   return success;
188 }
189
190 void matrix_write_char_inner(struct CharacterMatrix *matrix, uint8_t c) {
191   *matrix->cursor = c;
192   ++matrix->cursor;
193
194   if (matrix->cursor - &matrix->display[0][0] == sizeof(matrix->display)) {
195     // We went off the end; scroll the display upwards by one line
196     memmove(&matrix->display[0], &matrix->display[1],
197             MatrixCols * (MatrixRows - 1));
198     matrix->cursor = &matrix->display[MatrixRows - 1][0];
199     memset(matrix->cursor, ' ', MatrixCols);
200   }
201 }
202
203 void matrix_write_char(struct CharacterMatrix *matrix, uint8_t c) {
204   matrix->dirty = true;
205
206   if (c == '\n') {
207     // Clear to end of line from the cursor and then move to the
208     // start of the next line
209     uint8_t cursor_col = (matrix->cursor - &matrix->display[0][0]) % MatrixCols;
210
211     while (cursor_col++ < MatrixCols) {
212       matrix_write_char_inner(matrix, ' ');
213     }
214     return;
215   }
216
217   matrix_write_char_inner(matrix, c);
218 }
219
220 void iota_gfx_write_char(uint8_t c) {
221   matrix_write_char(&display, c);
222 }
223
224 void matrix_write(struct CharacterMatrix *matrix, const char *data) {
225   const char *end = data + strlen(data);
226   while (data < end) {
227     matrix_write_char(matrix, *data);
228     ++data;
229   }
230 }
231
232 void iota_gfx_write(const char *data) {
233   matrix_write(&display, data);
234 }
235
236 void matrix_write_P(struct CharacterMatrix *matrix, const char *data) {
237   while (true) {
238     uint8_t c = pgm_read_byte(data);
239     if (c == 0) {
240       return;
241     }
242     matrix_write_char(matrix, c);
243     ++data;
244   }
245 }
246
247 void iota_gfx_write_P(const char *data) {
248   matrix_write_P(&display, data);
249 }
250
251 void matrix_clear(struct CharacterMatrix *matrix) {
252   memset(matrix->display, ' ', sizeof(matrix->display));
253   matrix->cursor = &matrix->display[0][0];
254   matrix->dirty = true;
255 }
256
257 void iota_gfx_clear_screen(void) {
258   matrix_clear(&display);
259 }
260
261 void matrix_render(struct CharacterMatrix *matrix) {
262   last_flush = timer_read();
263   iota_gfx_on();
264 #if DEBUG_TO_SCREEN
265   ++displaying;
266 #endif
267
268   // Move to the home position
269   send_cmd3(PageAddr, 0, MatrixRows - 1);
270   send_cmd3(ColumnAddr, 0, (MatrixCols * FontWidth) - 1);
271
272   if (i2c_start_write(SSD1306_ADDRESS)) {
273     goto done;
274   }
275   if (i2c_master_write(0x40)) {
276     // Data mode
277     goto done;
278   }
279
280   for (uint8_t row = 0; row < MatrixRows; ++row) {
281     for (uint8_t col = 0; col < MatrixCols; ++col) {
282       const uint8_t *glyph = font + (matrix->display[row][col] * (FontWidth - 1));
283
284       for (uint8_t glyphCol = 0; glyphCol < FontWidth - 1; ++glyphCol) {
285         uint8_t colBits = pgm_read_byte(glyph + glyphCol);
286         i2c_master_write(colBits);
287       }
288
289       // 1 column of space between chars (it's not included in the glyph)
290       i2c_master_write(0);
291     }
292   }
293
294   matrix->dirty = false;
295
296 done:
297   i2c_master_stop();
298 #if DEBUG_TO_SCREEN
299   --displaying;
300 #endif
301 }
302
303 void iota_gfx_flush(void) {
304   matrix_render(&display);
305 }
306
307 __attribute__((weak))
308 void iota_gfx_task_user(void) {
309
310 }
311
312 void iota_gfx_task(void) {
313   iota_gfx_task_user();
314
315   if (display.dirty) {
316     iota_gfx_flush();
317   }
318
319   if (timer_elapsed(last_flush) > ScreenOffInterval) {
320     iota_gfx_off();
321   }
322 }
323 #endif