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