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