]> git.donarmstrong.com Git - qmk_firmware.git/commitdiff
Add SH1106 OLED support (#5787)
authorMichael F. Lamb <mike@datagrok.org>
Tue, 11 Jun 2019 22:27:17 +0000 (15:27 -0700)
committerDrashna Jaelre <drashna@live.com>
Tue, 11 Jun 2019 22:27:17 +0000 (15:27 -0700)
* modify oled_driver to support SH1106

also:
- improve mechanism to specify which OLED IC we use
- comment calc_bounds()
- give OLED_COLUMN_OFFSET a default value
- inline comment re: OLED MEMORY_MODE and SH1106
- update docs/feature_oled_driver.h for SH1106 support and related changes
- docs: OLED: note we have tested SSD1306 on ARM boards (per @XScorpion2)
- define out MEMORY_MODE when using SH1106 OLED driver

* document that SSD1306 128x64 on AVR works

Per @XScorpion2: https://github.com/qmk/qmk_firmware/pull/5787#discussion_r291837842

docs/feature_oled_driver.md
drivers/oled/oled_driver.c
drivers/oled/oled_driver.h

index 144b695b76f3b1e608d5a84473ed53407148f282..155dfa9d29298834e79ebee35bfecf7e86f23914 100644 (file)
@@ -2,7 +2,17 @@
 
 ## OLED Supported Hardware
 
-128x32 OLED modules using SSD1306 driver IC over I2C. Supported on AVR based keyboards. Possible but untested hardware includes ARM based keyboards and other sized OLED modules using SSD1306 over I2C, such as 128x64. 
+OLED modules using SSD1306 or SH1106 driver ICs, communicating over I2C.
+Tested combinations:
+
+| IC driver |   Size | Keyboard Platform | Notes                    |
+|-----------|--------|-------------------|--------------------------|
+| SSD1306   | 128x32 | AVR               | Primary support          |
+| SSD1306   | 128x64 | AVR               | Verified working         |
+| SSD1306   | 128x32 | ARM               |                          |
+| SH1106    | 128x64 | AVR               | No rotation or scrolling |
+
+Hardware configurations using ARM-based microcontrollers or different sizes of OLED modules may be compatible, but are untested.
 
 !> Warning: This OLED Driver currently uses the new i2c_master driver from split common code. If your split keyboard uses i2c to communication between sides this driver could cause an address conflict (serial is fine). Please contact your keyboard vendor and ask them to migrate to the latest split common code to fix this. 
 
@@ -86,17 +96,17 @@ void oled_task_user(void) {
 
  ## Basic Configuration
 
-|Define                 |Default        |Description                                     |
-|-----------------------|---------------|------------------------------------------------|
-|`OLED_DISPLAY_ADDRESS` |`0x3C`         |The i2c address of the OLED Display             |
-|`OLED_FONT_H`          |`"glcdfont.c"` |The font code file to use for custom fonts      |
-|`OLED_FONT_START`      |`0`            |The starting characer index for custom fonts    |
-|`OLED_FONT_END`        |`224`          |The ending characer index for custom fonts      |
-|`OLED_FONT_WIDTH`      |`6`            |The font width                                  |
-|`OLED_FONT_HEIGHT`     |`8`            |The font height (untested)                      |
-|`OLED_DISABLE_TIMEOUT` |*Not defined*  |Disables the built in OLED timeout feature. Useful when implementing custom timeout rules.|
-
-
+| Define                 | Default           | Description                                                                                                                |
+|------------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------|
+| `OLED_DISPLAY_ADDRESS` | `0x3C`            | The i2c address of the OLED Display                                                                                        |
+| `OLED_FONT_H`          | `"glcdfont.c"`    | The font code file to use for custom fonts                                                                                 |
+| `OLED_FONT_START`      | `0`               | The starting characer index for custom fonts                                                                               |
+| `OLED_FONT_END`        | `224`             | The ending characer index for custom fonts                                                                                 |
+| `OLED_FONT_WIDTH`      | `6`               | The font width                                                                                                             |
+| `OLED_FONT_HEIGHT`     | `8`               | The font height (untested)                                                                                                 |
+| `OLED_DISABLE_TIMEOUT` | *Not defined*     | Disables the built in OLED timeout feature. Useful when implementing custom timeout rules.                                 |
+| `OLED_IC`              | `OLED_IC_SSD1306` | Set to `OLED_IC_SH1106` if you're using the SH1106 OLED controller.                                                        |
+| `OLED_COLUMN_OFFSET`   | `0`               | (SH1106 only.) Shift output to the right this many pixels.<br />Useful for 128x64 displays centered on a 132x64 SH1106 IC. |
 
  ## 128x64 & Custom sized OLED Displays
 
@@ -119,6 +129,8 @@ void oled_task_user(void) {
 
 ### 90 Degree Rotation - Technical Mumbo Jumbo 
 
+!> Rotation is unsupported on the SH1106.
+
 ```C
 // OLED Rotation enum values are flags
 typedef enum {
@@ -250,6 +262,8 @@ uint8_t oled_max_chars(void);
 uint8_t oled_max_lines(void);
 ```
 
+!> Scrolling and rotation are unsupported on the SH1106.
+
 ## SSD1306.h driver conversion guide
 
 |Old API                    |Recommended New API                |
index 643e5289465cad8a097bc0eefd74e0b6fc49ae8b..a54f5fadc3b2809a0b8b5e1fc8bdd1808baef998 100644 (file)
@@ -33,6 +33,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #endif // defined(__AVR__)
 
 // Used commands from spec sheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
+// for SH1106: https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf
+
 // Fundamental Commands
 #define CONTRAST                0x81
 #define DISPLAY_ALL_ON          0xA5
@@ -40,6 +42,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #define NORMAL_DISPLAY          0xA6
 #define DISPLAY_ON              0xAF
 #define DISPLAY_OFF             0xAE
+#define NOP                     0xE3
 
 // Scrolling Commands
 #define ACTIVATE_SCROLL         0x2F
@@ -53,6 +56,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #define MEMORY_MODE             0x20
 #define COLUMN_ADDR             0x21
 #define PAGE_ADDR               0x22
+#define PAM_SETCOLUMN_LSB       0x00
+#define PAM_SETCOLUMN_MSB       0x10
+#define PAM_PAGE_ADDR           0xB0 // 0xb0 -- 0xb7
 
 // Hardware Configuration Commands
 #define DISPLAY_START_LINE      0x40
@@ -158,7 +164,11 @@ bool oled_init(uint8_t rotation) {
     DISPLAY_OFFSET, 0x00,
     DISPLAY_START_LINE | 0x00,
     CHARGE_PUMP, 0x14,
-    MEMORY_MODE, 0x00, }; // Horizontal addressing mode
+#if (OLED_IC != OLED_IC_SH1106)
+    // MEMORY_MODE is unsupported on SH1106 (Page Addressing only)
+    MEMORY_MODE, 0x00, // Horizontal addressing mode
+#endif
+  };
   if (I2C_TRANSMIT_P(display_setup1) != I2C_STATUS_SUCCESS) {
     print("oled_init cmd set 1 failed\n");
     return false;
@@ -219,10 +229,25 @@ void oled_clear(void) {
 
 static void calc_bounds(uint8_t update_start, uint8_t* cmd_array)
 {
-  cmd_array[1] = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_WIDTH;
-  cmd_array[4] = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_WIDTH;
+  // Calculate commands to set memory addressing bounds.
+  uint8_t start_page = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_WIDTH;
+  uint8_t start_column = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_WIDTH;
+#if (OLED_IC == OLED_IC_SH1106)
+  // Commands for Page Addressing Mode. Sets starting page and column; has no end bound.
+  // Column value must be split into high and low nybble and sent as two commands.
+  cmd_array[0] = PAM_PAGE_ADDR | start_page;
+  cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f);
+  cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f);
+  cmd_array[3] = NOP;
+  cmd_array[4] = NOP;
+  cmd_array[5] = NOP;
+#else
+  // Commands for use in Horizontal Addressing mode.
+  cmd_array[1] = start_column;
+  cmd_array[4] = start_page;
   cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) % OLED_DISPLAY_WIDTH + cmd_array[1];
   cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) / OLED_DISPLAY_WIDTH - 1;
+#endif
 }
 
 static void calc_bounds_90(uint8_t update_start, uint8_t* cmd_array)
index abbdde57ed69672cf1626e8d5c9550cfe87e6523..03dda2e64a563bda6bdf1c48a235a86e6a01bbc3 100644 (file)
@@ -19,6 +19,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include <stdint.h>
 #include <stdbool.h>
 
+// an enumeration of the chips this driver supports
+#define OLED_IC_SSD1306 0
+#define OLED_IC_SH1106  1
 
 #if defined(OLED_DISPLAY_CUSTOM)
   // Expected user to implement the necessary defines
@@ -100,7 +103,16 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   // #define OLED_TARGET_MAP { 48, 32, 16, 0, 56, 40, 24, 8 }
 #endif // defined(OLED_DISPLAY_CUSTOM)
 
-// Address to use for tthe i2d oled communication
+#if !defined(OLED_IC)
+  #define OLED_IC OLED_IC_SSD1306
+#endif
+
+// the column address corresponding to the first column in the display hardware
+#if !defined(OLED_COLUMN_OFFSET)
+  #define OLED_COLUMN_OFFSET 0
+#endif
+
+// Address to use for the i2c oled communication
 #if !defined(OLED_DISPLAY_ADDRESS)
   #define OLED_DISPLAY_ADDRESS 0x3C
 #endif