qmk

QMK Firmware
git clone git://git.z3bra.org/qmk.git
Log | Files | Refs | Submodules | LICENSE

ssd1306.c (7533B)


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