Add SH1106 OLED support (#5787)

* 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
This commit is contained in:
Michael F. Lamb 2019-06-11 15:27:17 -07:00 committed by Drashna Jaelre
parent b92387b749
commit e6a81133dd
3 changed files with 67 additions and 16 deletions

View 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 |

View 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)

View 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