ssd1306.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. #ifdef SSD1306OLED
  2. #include "ssd1306.h"
  3. #include "config.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 "pincontrol.h"
  16. //assign the right code to your layers
  17. #define _BASE 0
  18. #define _LOWER 8
  19. #define _RAISE 16
  20. #define _FNLAYER 64
  21. #define _NUMLAY 128
  22. #define _NLOWER 136
  23. #define _NFNLAYER 192
  24. #define _MOUSECURSOR 256
  25. #define _ADJUST 65560
  26. // Set this to 1 to help diagnose early startup problems
  27. // when testing power-on with ble. Turn it off otherwise,
  28. // as the latency of printing most of the debug info messes
  29. // with the matrix scan, causing keys to drop.
  30. #define DEBUG_TO_SCREEN 0
  31. // Controls the SSD1306 128x32 OLED display via i2c
  32. #define i2cAddress 0x3C
  33. #define DisplayHeight 32
  34. #define DisplayWidth 128
  35. #define FontHeight 8
  36. #define FontWidth 6
  37. #define MatrixRows (DisplayHeight / FontHeight)
  38. #define MatrixCols (DisplayWidth / FontWidth)
  39. struct CharacterMatrix {
  40. uint8_t display[MatrixRows][MatrixCols];
  41. uint8_t *cursor;
  42. bool dirty;
  43. };
  44. static struct CharacterMatrix display;
  45. //static uint16_t last_battery_update;
  46. //static uint32_t vbat;
  47. //#define BatteryUpdateInterval 10000 /* milliseconds */
  48. #define ScreenOffInterval 300000 /* milliseconds */
  49. #if DEBUG_TO_SCREEN
  50. static uint8_t displaying;
  51. #endif
  52. static uint16_t last_flush;
  53. enum ssd1306_cmds {
  54. DisplayOff = 0xAE,
  55. DisplayOn = 0xAF,
  56. SetContrast = 0x81,
  57. DisplayAllOnResume = 0xA4,
  58. DisplayAllOn = 0xA5,
  59. NormalDisplay = 0xA6,
  60. InvertDisplay = 0xA7,
  61. SetDisplayOffset = 0xD3,
  62. SetComPins = 0xda,
  63. SetVComDetect = 0xdb,
  64. SetDisplayClockDiv = 0xD5,
  65. SetPreCharge = 0xd9,
  66. SetMultiPlex = 0xa8,
  67. SetLowColumn = 0x00,
  68. SetHighColumn = 0x10,
  69. SetStartLine = 0x40,
  70. SetMemoryMode = 0x20,
  71. ColumnAddr = 0x21,
  72. PageAddr = 0x22,
  73. ComScanInc = 0xc0,
  74. ComScanDec = 0xc8,
  75. SegRemap = 0xa0,
  76. SetChargePump = 0x8d,
  77. ExternalVcc = 0x01,
  78. SwitchCapVcc = 0x02,
  79. ActivateScroll = 0x2f,
  80. DeActivateScroll = 0x2e,
  81. SetVerticalScrollArea = 0xa3,
  82. RightHorizontalScroll = 0x26,
  83. LeftHorizontalScroll = 0x27,
  84. VerticalAndRightHorizontalScroll = 0x29,
  85. VerticalAndLeftHorizontalScroll = 0x2a,
  86. };
  87. // Write command sequence.
  88. // Returns true on success.
  89. static inline bool _send_cmd1(uint8_t cmd) {
  90. bool res = false;
  91. if (i2c_start_write(i2cAddress)) {
  92. xprintf("failed to start write to %d\n", i2cAddress);
  93. goto done;
  94. }
  95. if (i2c_master_write(0x0 /* command byte follows */)) {
  96. print("failed to write control byte\n");
  97. goto done;
  98. }
  99. if (i2c_master_write(cmd)) {
  100. xprintf("failed to write command %d\n", cmd);
  101. goto done;
  102. }
  103. res = true;
  104. done:
  105. i2c_master_stop();
  106. return res;
  107. }
  108. // Write 2-byte command sequence.
  109. // Returns true on success
  110. static inline bool _send_cmd2(uint8_t cmd, uint8_t opr) {
  111. if (!_send_cmd1(cmd)) {
  112. return false;
  113. }
  114. return _send_cmd1(opr);
  115. }
  116. // Write 3-byte command sequence.
  117. // Returns true on success
  118. static inline bool _send_cmd3(uint8_t cmd, uint8_t opr1, uint8_t opr2) {
  119. if (!_send_cmd1(cmd)) {
  120. return false;
  121. }
  122. if (!_send_cmd1(opr1)) {
  123. return false;
  124. }
  125. return _send_cmd1(opr2);
  126. }
  127. #define send_cmd1(c) if (!_send_cmd1(c)) {goto done;}
  128. #define send_cmd2(c,o) if (!_send_cmd2(c,o)) {goto done;}
  129. #define send_cmd3(c,o1,o2) if (!_send_cmd3(c,o1,o2)) {goto done;}
  130. static void matrix_clear(struct CharacterMatrix *matrix);
  131. static void clear_display(void) {
  132. matrix_clear(&display);
  133. // Clear all of the display bits (there can be random noise
  134. // in the RAM on startup)
  135. send_cmd3(PageAddr, 0, (DisplayHeight / 8) - 1);
  136. send_cmd3(ColumnAddr, 0, DisplayWidth - 1);
  137. if (i2c_start_write(i2cAddress)) {
  138. goto done;
  139. }
  140. if (i2c_master_write(0x40)) {
  141. // Data mode
  142. goto done;
  143. }
  144. for (uint8_t row = 0; row < MatrixRows; ++row) {
  145. for (uint8_t col = 0; col < DisplayWidth; ++col) {
  146. i2c_master_write(0);
  147. }
  148. }
  149. display.dirty = false;
  150. done:
  151. i2c_master_stop();
  152. }
  153. #if DEBUG_TO_SCREEN
  154. #undef sendchar
  155. static int8_t capture_sendchar(uint8_t c) {
  156. sendchar(c);
  157. iota_gfx_write_char(c);
  158. if (!displaying) {
  159. iota_gfx_flush();
  160. }
  161. return 0;
  162. }
  163. #endif
  164. bool iota_gfx_init(void) {
  165. bool success = false;
  166. send_cmd1(DisplayOff);
  167. send_cmd2(SetDisplayClockDiv, 0x80);
  168. send_cmd2(SetMultiPlex, DisplayHeight - 1);
  169. send_cmd2(SetDisplayOffset, 0);
  170. send_cmd1(SetStartLine | 0x0);
  171. send_cmd2(SetChargePump, 0x14 /* Enable */);
  172. send_cmd2(SetMemoryMode, 0 /* horizontal addressing */);
  173. /// Flips the display orientation 0 degrees
  174. send_cmd1(SegRemap | 0x1);
  175. send_cmd1(ComScanDec);
  176. /*
  177. // the following Flip the display orientation 180 degrees
  178. send_cmd1(SegRemap);
  179. send_cmd1(ComScanInc);
  180. // end flip */
  181. send_cmd2(SetComPins, 0x2);
  182. send_cmd2(SetContrast, 0x8f);
  183. send_cmd2(SetPreCharge, 0xf1);
  184. send_cmd2(SetVComDetect, 0x40);
  185. send_cmd1(DisplayAllOnResume);
  186. send_cmd1(NormalDisplay);
  187. send_cmd1(DeActivateScroll);
  188. send_cmd1(DisplayOn);
  189. send_cmd2(SetContrast, 0); // Dim
  190. clear_display();
  191. success = true;
  192. iota_gfx_flush();
  193. #if DEBUG_TO_SCREEN
  194. print_set_sendchar(capture_sendchar);
  195. #endif
  196. done:
  197. return success;
  198. }
  199. bool iota_gfx_off(void) {
  200. bool success = false;
  201. send_cmd1(DisplayOff);
  202. success = true;
  203. done:
  204. return success;
  205. }
  206. bool iota_gfx_on(void) {
  207. bool success = false;
  208. send_cmd1(DisplayOn);
  209. success = true;
  210. done:
  211. return success;
  212. }
  213. static void matrix_write_char_inner(struct CharacterMatrix *matrix, uint8_t c) {
  214. *matrix->cursor = c;
  215. ++matrix->cursor;
  216. if (matrix->cursor - &matrix->display[0][0] == sizeof(matrix->display)) {
  217. // We went off the end; scroll the display upwards by one line
  218. memmove(&matrix->display[0], &matrix->display[1],
  219. MatrixCols * (MatrixRows - 1));
  220. matrix->cursor = &matrix->display[MatrixRows - 1][0];
  221. memset(matrix->cursor, ' ', MatrixCols);
  222. }
  223. }
  224. static void matrix_write_char(struct CharacterMatrix *matrix, uint8_t c) {
  225. matrix->dirty = true;
  226. if (c == '\n') {
  227. // Clear to end of line from the cursor and then move to the
  228. // start of the next line
  229. uint8_t cursor_col = (matrix->cursor - &matrix->display[0][0]) % MatrixCols;
  230. while (cursor_col++ < MatrixCols) {
  231. matrix_write_char_inner(matrix, ' ');
  232. }
  233. return;
  234. }
  235. matrix_write_char_inner(matrix, c);
  236. }
  237. void iota_gfx_write_char(uint8_t c) {
  238. matrix_write_char(&display, c);
  239. }
  240. static void matrix_write(struct CharacterMatrix *matrix, const char *data) {
  241. const char *end = data + strlen(data);
  242. while (data < end) {
  243. matrix_write_char(matrix, *data);
  244. ++data;
  245. }
  246. }
  247. void iota_gfx_write(const char *data) {
  248. matrix_write(&display, data);
  249. }
  250. static void matrix_write_P(struct CharacterMatrix *matrix, const char *data) {
  251. while (true) {
  252. uint8_t c = pgm_read_byte(data);
  253. if (c == 0) {
  254. return;
  255. }
  256. matrix_write_char(matrix, c);
  257. ++data;
  258. }
  259. }
  260. void iota_gfx_write_P(const char *data) {
  261. matrix_write_P(&display, data);
  262. }
  263. static 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. void iota_gfx_clear_screen(void) {
  269. matrix_clear(&display);
  270. }
  271. static void matrix_render(struct CharacterMatrix *matrix) {
  272. last_flush = timer_read();
  273. iota_gfx_on();
  274. #if DEBUG_TO_SCREEN
  275. ++displaying;
  276. #endif
  277. // Move to the home position
  278. send_cmd3(PageAddr, 0, MatrixRows - 1);
  279. send_cmd3(ColumnAddr, 0, (MatrixCols * FontWidth) - 1);
  280. if (i2c_start_write(i2cAddress)) {
  281. goto done;
  282. }
  283. if (i2c_master_write(0x40)) {
  284. // Data mode
  285. goto done;
  286. }
  287. for (uint8_t row = 0; row < MatrixRows; ++row) {
  288. for (uint8_t col = 0; col < MatrixCols; ++col) {
  289. const uint8_t *glyph = font + (matrix->display[row][col] * (FontWidth - 1));
  290. for (uint8_t glyphCol = 0; glyphCol < FontWidth - 1; ++glyphCol) {
  291. uint8_t colBits = pgm_read_byte(glyph + glyphCol);
  292. i2c_master_write(colBits);
  293. }
  294. // 1 column of space between chars (it's not included in the glyph)
  295. i2c_master_write(0);
  296. }
  297. }
  298. matrix->dirty = false;
  299. done:
  300. i2c_master_stop();
  301. #if DEBUG_TO_SCREEN
  302. --displaying;
  303. #endif
  304. }
  305. void iota_gfx_flush(void) {
  306. matrix_render(&display);
  307. }
  308. static void matrix_update(struct CharacterMatrix *dest,
  309. const struct CharacterMatrix *source) {
  310. if (memcmp(dest->display, source->display, sizeof(dest->display))) {
  311. memcpy(dest->display, source->display, sizeof(dest->display));
  312. dest->dirty = true;
  313. }
  314. }
  315. static void render_status_info(void) {
  316. #if DEBUG_TO_SCREEN
  317. if (debug_enable) {
  318. return;
  319. }
  320. #endif
  321. struct CharacterMatrix matrix;
  322. matrix_clear(&matrix);
  323. matrix_write_P(&matrix, PSTR("USB: "));
  324. #ifdef PROTOCOL_LUFA
  325. switch (USB_DeviceState) {
  326. case DEVICE_STATE_Unattached:
  327. matrix_write_P(&matrix, PSTR("Unattached"));
  328. break;
  329. case DEVICE_STATE_Suspended:
  330. matrix_write_P(&matrix, PSTR("Suspended"));
  331. break;
  332. case DEVICE_STATE_Configured:
  333. matrix_write_P(&matrix, PSTR("Connected"));
  334. break;
  335. case DEVICE_STATE_Powered:
  336. matrix_write_P(&matrix, PSTR("Powered"));
  337. break;
  338. case DEVICE_STATE_Default:
  339. matrix_write_P(&matrix, PSTR("Default"));
  340. break;
  341. case DEVICE_STATE_Addressed:
  342. matrix_write_P(&matrix, PSTR("Addressed"));
  343. break;
  344. default:
  345. matrix_write_P(&matrix, PSTR("Invalid"));
  346. }
  347. #endif
  348. // Define layers here, Have not worked out how to have text displayed for each layer. Copy down the number you see and add a case for it below
  349. char buf[40];
  350. snprintf(buf,sizeof(buf), "Undef-%ld", layer_state);
  351. matrix_write_P(&matrix, PSTR("\n\nLayer: "));
  352. switch (layer_state) {
  353. case _BASE:
  354. matrix_write_P(&matrix, PSTR("Default"));
  355. break;
  356. case _RAISE:
  357. matrix_write_P(&matrix, PSTR("Raise"));
  358. break;
  359. case _LOWER:
  360. matrix_write_P(&matrix, PSTR("Lower"));
  361. break;
  362. case _ADJUST:
  363. matrix_write_P(&matrix, PSTR("ADJUST"));
  364. break;
  365. default:
  366. matrix_write(&matrix, buf);
  367. }
  368. // Host Keyboard LED Status
  369. char led[40];
  370. snprintf(led, sizeof(led), "\n%s %s %s",
  371. (host_keyboard_leds() & (1<<USB_LED_NUM_LOCK)) ? "NUMLOCK" : " ",
  372. (host_keyboard_leds() & (1<<USB_LED_CAPS_LOCK)) ? "CAPS" : " ",
  373. (host_keyboard_leds() & (1<<USB_LED_SCROLL_LOCK)) ? "SCLK" : " ");
  374. matrix_write(&matrix, led);
  375. matrix_update(&display, &matrix);
  376. }
  377. void iota_gfx_task(void) {
  378. render_status_info();
  379. if (display.dirty) {
  380. iota_gfx_flush();
  381. }
  382. if (timer_elapsed(last_flush) > ScreenOffInterval) {
  383. iota_gfx_off();
  384. }
  385. }
  386. #endif