oled.c (7621B)
1 /* 2 MNT Reform 2.0 Keyboard Firmware 3 See keyboard.c for Copyright 4 SPDX-License-Identifier: MIT 5 */ 6 7 // OLED (SSD1306) rendering code. The OLED is interfaced via I2C. 8 9 #include "oled.h" 10 #include "i2c.h" 11 #include <stdio.h> 12 #include <string.h> 13 #include <avr/pgmspace.h> 14 #include "gfx/font.c" 15 16 struct CharacterMatrix display; 17 int oledbrt = 0; 18 19 // Write command sequence. 20 // Returns true on success. 21 static inline bool _send_cmd1(uint8_t cmd) { 22 bool res = false; 23 24 if (i2c_start_write(SSD1306_ADDRESS)) { 25 // failed to start write 26 goto done; 27 } 28 29 if (i2c_master_write(0x0 /* command byte follows */)) { 30 // failed to write control byte 31 goto done; 32 } 33 34 if (i2c_master_write(cmd)) { 35 // failed to write command 36 goto done; 37 } 38 res = true; 39 done: 40 i2c_master_stop(); 41 return res; 42 } 43 44 // Write 2-byte command sequence. 45 // Returns true on success 46 static inline bool _send_cmd2(uint8_t cmd, uint8_t opr) { 47 if (!_send_cmd1(cmd)) { 48 //return false; 49 } 50 return _send_cmd1(opr); 51 } 52 53 // Write 3-byte command sequence. 54 // Returns true on success 55 static inline bool _send_cmd3(uint8_t cmd, uint8_t opr1, uint8_t opr2) { 56 if (!_send_cmd1(cmd)) { 57 //return false; 58 } 59 if (!_send_cmd1(opr1)) { 60 //return false; 61 } 62 return _send_cmd1(opr2); 63 } 64 65 #define send_cmd1(c) if (!_send_cmd1(c)) {goto done;} 66 #define send_cmd2(c,o) if (!_send_cmd2(c,o)) {goto done;} 67 #define send_cmd3(c,o1,o2) if (!_send_cmd3(c,o1,o2)) {goto done;} 68 69 void oled_clear(void) { 70 matrix_clear(&display); 71 72 // Clear all of the display bits (there can be random noise 73 // in the RAM on startup) 74 send_cmd3(PageAddr, 0, (DisplayHeight / 8) - 1); 75 send_cmd3(ColumnAddr, 0, DisplayWidth - 1); 76 77 if (i2c_start_write(SSD1306_ADDRESS)) { 78 goto done; 79 } 80 if (i2c_master_write(0x40)) { 81 // Data mode 82 goto done; 83 } 84 for (uint8_t row = 0; row < MatrixRows; ++row) { 85 for (uint8_t col = 0; col < DisplayWidth; ++col) { 86 i2c_master_write(0); 87 } 88 } 89 90 display.dirty = false; 91 92 done: 93 i2c_master_stop(); 94 } 95 96 bool gfx_init(bool rotate) { 97 bool success = false; 98 rotate = false; // FIXME 99 100 i2c_master_init(); 101 send_cmd1(DisplayOff); 102 send_cmd2(SetDisplayClockDiv, 0x80); 103 send_cmd2(SetMultiPlex, DisplayHeight - 1); 104 105 send_cmd2(SetDisplayOffset, 0); 106 107 send_cmd1(SetStartLine | 0x0); 108 send_cmd2(SetChargePump, 0x14 /* Enable */); 109 send_cmd2(SetMemoryMode, 0 /* horizontal addressing */); 110 111 if (rotate) { 112 // the following Flip the display orientation 180 degrees 113 send_cmd1(SegRemap); 114 send_cmd1(ComScanInc); 115 } else { 116 // Flips the display orientation 0 degrees 117 send_cmd1(SegRemap | 0x1); 118 send_cmd1(ComScanDec); 119 } 120 121 send_cmd2(SetComPins, 0x2); 122 send_cmd2(SetContrast, 0x8f); 123 send_cmd2(SetPreCharge, 0xf1); 124 send_cmd2(SetVComDetect, 0x40); 125 send_cmd1(DisplayAllOnResume); 126 send_cmd1(NormalDisplay); 127 send_cmd1(DeActivateScroll); 128 send_cmd1(DisplayOn); 129 130 send_cmd2(SetContrast, 0); // Dim 131 132 oled_clear(); 133 134 success = true; 135 136 gfx_flush(); 137 138 done: 139 return success; 140 } 141 142 bool gfx_off(void) { 143 bool success = false; 144 145 //send_cmd1(InvertDisplay); 146 send_cmd1(DisplayOff); 147 success = true; 148 149 done: 150 return success; 151 } 152 153 bool gfx_on(void) { 154 bool success = false; 155 156 send_cmd1(NormalDisplay); 157 send_cmd1(DisplayOn); 158 success = true; 159 160 done: 161 return success; 162 } 163 164 void gfx_clear(void) { 165 for (int y=0; y<4; y++) { 166 for (int x=0; x<21; x++) { 167 gfx_poke(x,y,' '); 168 } 169 } 170 gfx_clear_invert(); 171 } 172 173 void gfx_contrast(int c) { 174 send_cmd2(SetContrast, c); 175 done: 176 return; 177 } 178 179 void matrix_write_char_inner(struct CharacterMatrix *matrix, uint8_t c) { 180 *matrix->cursor = c; 181 ++matrix->cursor; 182 183 if (matrix->cursor - &matrix->display[0][0] == sizeof(matrix->display)) { 184 // We went off the end; scroll the display upwards by one line 185 memmove(&matrix->display[0], &matrix->display[1], 186 MatrixCols * (MatrixRows - 1)); 187 matrix->cursor = &matrix->display[MatrixRows - 1][0]; 188 memset(matrix->cursor, ' ', MatrixCols); 189 } 190 } 191 192 void matrix_write_char(struct CharacterMatrix *matrix, uint8_t c) { 193 matrix->dirty = true; 194 195 if (c == '\n') { 196 // Clear to end of line from the cursor and then move to the 197 // start of the next line 198 uint8_t cursor_col = (matrix->cursor - &matrix->display[0][0]) % MatrixCols; 199 200 while (cursor_col++ < MatrixCols) { 201 matrix_write_char_inner(matrix, ' '); 202 } 203 return; 204 } 205 206 matrix_write_char_inner(matrix, c); 207 } 208 209 void gfx_poke(uint8_t x, uint8_t y, uint8_t c) { 210 display.display[y][x] = c; 211 } 212 213 void gfx_poke_str(uint8_t x, uint8_t y, char* str) { 214 int len = strlen(str); 215 if (len>21) len = 21; 216 // clip 217 if (y<0 || y>3) return; 218 219 for (int xx=x; xx<x+len && xx<21; xx++) { 220 if (xx>=0 && xx<21) { 221 display.display[y][xx] = (uint8_t)str[xx-x]; 222 } 223 } 224 } 225 226 void gfx_write_char(uint8_t c) { 227 matrix_write_char(&display, c); 228 } 229 230 void matrix_write(struct CharacterMatrix *matrix, const char *data) { 231 const char *end = data + strlen(data); 232 while (data < end) { 233 matrix_write_char(matrix, *data); 234 ++data; 235 } 236 } 237 238 void matrix_write_ln(struct CharacterMatrix *matrix, const char *data) { 239 char data_ln[strlen(data)+2]; 240 snprintf(data_ln, sizeof(data_ln), "%s\n", data); 241 matrix_write(matrix, data_ln); 242 } 243 244 void gfx_write(const char *data) { 245 matrix_write(&display, data); 246 } 247 248 void matrix_write_P(struct CharacterMatrix *matrix, const char *data) { 249 while (true) { 250 uint8_t c = pgm_read_byte(data); 251 if (c == 0) { 252 return; 253 } 254 matrix_write_char(matrix, c); 255 ++data; 256 } 257 } 258 259 void gfx_write_P(const char *data) { 260 matrix_write_P(&display, data); 261 } 262 263 void matrix_clear(struct CharacterMatrix *matrix) { 264 memset(matrix->display, ' ', sizeof(matrix->display)); 265 matrix->cursor = &matrix->display[0][0]; 266 matrix->dirty = true; 267 } 268 269 void gfx_clear_screen(void) { 270 matrix_clear(&display); 271 } 272 273 void gfx_clear_invert(void) { 274 for (int y=0;y<4;y++) { 275 for (int x=0;x<21;x++) { 276 display.invert[y][x] = 0; 277 } 278 } 279 } 280 281 void gfx_invert_row(uint8_t y) { 282 if (y<0 || y>3) return; 283 for (int x=0;x<21;x++) { 284 display.invert[y][x] = 1; 285 } 286 } 287 288 void matrix_render(struct CharacterMatrix *matrix) { 289 gfx_on(); 290 291 // Move to the home position 292 send_cmd3(PageAddr, 0, MatrixRows - 1); 293 send_cmd3(ColumnAddr, 0, (MatrixCols * FontWidth) - 1); 294 295 if (i2c_start_write(SSD1306_ADDRESS)) { 296 //goto done; 297 } 298 if (i2c_master_write(0x40)) { 299 // Data mode 300 //goto done; 301 } 302 303 for (uint8_t row = 0; row < MatrixRows; ++row) { 304 for (uint8_t col = 0; col < MatrixCols; ++col) { 305 const uint8_t *glyph = font + (matrix->display[row][col] * FontWidth); 306 const uint8_t invert = matrix->invert[row][col]; 307 308 for (uint8_t glyphCol = 0; glyphCol < FontWidth; ++glyphCol) { 309 uint8_t colBits = pgm_read_byte(glyph + glyphCol); 310 if (invert) colBits = ~colBits; 311 i2c_master_write(colBits); 312 } 313 } 314 } 315 316 matrix->dirty = false; 317 318 done: 319 i2c_master_stop(); 320 } 321 322 void matrix_render_direct(uint8_t* bitmap) { 323 gfx_on(); 324 325 // Move to the home position 326 send_cmd3(PageAddr, 0, (DisplayHeight / 8) - 1); 327 send_cmd3(ColumnAddr, 0, DisplayWidth - 1); 328 329 i2c_start_write(SSD1306_ADDRESS); 330 i2c_master_write(0x40); 331 332 int c = 0; 333 for (uint16_t y=0; y<4; y++) { 334 for (uint16_t x=0; x<128; x++) { 335 i2c_master_write(bitmap[c++]); 336 } 337 } 338 339 done: 340 i2c_master_stop(); 341 } 342 343 void gfx_flush(void) { 344 matrix_render(&display); 345 } 346 347 void oled_brightness_inc(void) { 348 oledbrt+=10; 349 if (oledbrt>=0xff) oledbrt = 0xff; 350 gfx_contrast(oledbrt); 351 } 352 353 void oled_brightness_dec(void) { 354 oledbrt-=10; 355 if (oledbrt<0) oledbrt = 0; 356 gfx_contrast(oledbrt); 357 }