aboutsummaryrefslogtreecommitdiff
path: root/docs/feature_oled_driver.md
diff options
context:
space:
mode:
authorfauxpark <fauxpark@gmail.com>2020-01-20 09:05:54 +1100
committerDrashna Jaelre <drashna@live.com>2020-01-19 14:05:54 -0800
commit31c0fe69f6f0b9e3a88e7f4cc6388727aecbeb08 (patch)
treee95887a874fedd841b1661cf08cab554463a38d7 /docs/feature_oled_driver.md
parent65f7bfcc8dbd47557846047457191d871faffff4 (diff)
downloadqmk_firmware-31c0fe69f6f0b9e3a88e7f4cc6388727aecbeb08.tar.gz
qmk_firmware-31c0fe69f6f0b9e3a88e7f4cc6388727aecbeb08.zip
[Docs] Misc cleanups for OLED documentation (#7864)
Diffstat (limited to 'docs/feature_oled_driver.md')
-rw-r--r--docs/feature_oled_driver.md227
1 files changed, 115 insertions, 112 deletions
diff --git a/docs/feature_oled_driver.md b/docs/feature_oled_driver.md
index b124ba5a8..29548cb82 100644
--- a/docs/feature_oled_driver.md
+++ b/docs/feature_oled_driver.md
@@ -1,139 +1,142 @@
1# OLED Driver 1# OLED Driver
2 2
3## OLED Supported Hardware 3## Supported Hardware
4 4
5OLED modules using SSD1306 or SH1106 driver ICs, communicating over I2C. 5OLED modules using SSD1306 or SH1106 driver ICs, communicating over I2C.
6Tested combinations: 6Tested combinations:
7 7
8| IC driver | Size | Keyboard Platform | Notes | 8|IC |Size |Platform|Notes |
9|-----------|--------|-------------------|--------------------------| 9|---------|------|--------|------------------------|
10| SSD1306 | 128x32 | AVR | Primary support | 10|SSD1306 |128x32|AVR |Primary support |
11| SSD1306 | 128x64 | AVR | Verified working | 11|SSD1306 |128x64|AVR |Verified working |
12| SSD1306 | 128x32 | ARM | | 12|SSD1306 |128x32|Arm | |
13| SH1106 | 128x64 | AVR | No rotation or scrolling | 13|SH1106 |128x64|AVR |No rotation or scrolling|
14 14
15Hardware configurations using ARM-based microcontrollers or different sizes of OLED modules may be compatible, but are untested. 15Hardware configurations using Arm-based microcontrollers or different sizes of OLED modules may be compatible, but are untested.
16 16
17!> Warning: This OLED Driver currently uses the new i2c_master driver from split common code. If your split keyboard uses I2C to communicate 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. In addition, the display timeout system to reduce OLED burn-in also uses split common to detect keypresses, so you will need to implement custom timeout logic for non-split common keyboards. 17!> Warning: This OLED driver currently uses the new i2c_master driver from Split Common code. If your split keyboard uses I2C to communicate 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. In addition, the display timeout system to reduce OLED burn-in also uses Split Common to detect keypresses, so you will need to implement custom timeout logic for non-Split Common keyboards.
18 18
19## Usage 19## Usage
20 20
21To enable the OLED feature, there are three steps. First, when compiling your keyboard, you'll need to set `OLED_DRIVER_ENABLE=yes` in `rules.mk`, e.g.: 21To enable the OLED feature, there are three steps. First, when compiling your keyboard, you'll need to add the following to your `rules.mk`:
22 22
23``` 23```make
24OLED_DRIVER_ENABLE = yes 24OLED_DRIVER_ENABLE = yes
25``` 25```
26 26
27This enables the feature and the `OLED_DRIVER_ENABLE` define. Then in your `keymap.c` file, you will need to implement the user task call, e.g: 27Then in your `keymap.c` file, implement the OLED task call. This example assumes your keymap has three layers named `_QWERTY`, `_FN` and `_ADJ`:
28 28
29```C++ 29```c
30#ifdef OLED_DRIVER_ENABLE 30#ifdef OLED_DRIVER_ENABLE
31void oled_task_user(void) { 31void oled_task_user(void) {
32 // Host Keyboard Layer Status 32 // Host Keyboard Layer Status
33 oled_write_P(PSTR("Layer: "), false); 33 oled_write_P(PSTR("Layer: "), false);
34 switch (get_highest_layer(layer_state)) { 34
35 case _QWERTY: 35 switch (get_highest_layer(layer_state)) {
36 oled_write_P(PSTR("Default\n"), false); 36 case _QWERTY:
37 break; 37 oled_write_P(PSTR("Default\n"), false);
38 case _FN: 38 break;
39 oled_write_P(PSTR("FN\n"), false); 39 case _FN:
40 break; 40 oled_write_P(PSTR("FN\n"), false);
41 case _ADJ: 41 break;
42 oled_write_P(PSTR("ADJ\n"), false); 42 case _ADJ:
43 break; 43 oled_write_P(PSTR("ADJ\n"), false);
44 default: 44 break;
45 // Or use the write_ln shortcut over adding '\n' to the end of your string 45 default:
46 oled_write_ln_P(PSTR("Undefined"), false); 46 // Or use the write_ln shortcut over adding '\n' to the end of your string
47 } 47 oled_write_ln_P(PSTR("Undefined"), false);
48 48 }
49 // Host Keyboard LED Status 49
50 uint8_t led_usb_state = host_keyboard_leds(); 50 // Host Keyboard LED Status
51 oled_write_P(led_usb_state & (1<<USB_LED_NUM_LOCK) ? PSTR("NUMLCK ") : PSTR(" "), false); 51 led_t led_state = host_keyboard_led_state();
52 oled_write_P(led_usb_state & (1<<USB_LED_CAPS_LOCK) ? PSTR("CAPLCK ") : PSTR(" "), false); 52 oled_write_P(led_state.num_lock ? PSTR("NUM ") : PSTR(" "), false);
53 oled_write_P(led_usb_state & (1<<USB_LED_SCROLL_LOCK) ? PSTR("SCRLCK ") : PSTR(" "), false); 53 oled_write_P(led_state.caps_lock ? PSTR("CAP ") : PSTR(" "), false);
54 oled_write_P(led_state.scroll_lock ? PSTR("SCR ") : PSTR(" "), false);
54} 55}
55#endif 56#endif
56``` 57```
57 58
58## Logo Example 59## Logo Example
59 60
60In the default font, ranges in the font file are reserved for a QMK Logo. To Render this logo to the oled screen, use the following code example: 61In the default font, certain ranges of characters are reserved for a QMK logo. To render this logo to the OLED screen, use the following code example:
61 62
62```C++ 63```c
63static void render_logo(void) { 64static void render_logo(void) {
64 static const char PROGMEM qmk_logo[] = { 65 static const char PROGMEM qmk_logo[] = {
65 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,0x90,0x91,0x92,0x93,0x94, 66 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94,
66 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,0xb0,0xb1,0xb2,0xb3,0xb4, 67 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4,
67 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,0xd0,0xd1,0xd2,0xd3,0xd4,0}; 68 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0x00
69 };
68 70
69 oled_write_P(qmk_logo, false); 71 oled_write_P(qmk_logo, false);
70} 72}
71``` 73```
72 74
73## Other Examples 75## Other Examples
74 76
75In split keyboards, it is very common to have two OLED displays that each render different content and oriented flipped differently. You can do this by switching which content to render by using the return from `is_keyboard_master()` or `is_keyboard_left()` found in `split_util.h`, e.g: 77In split keyboards, it is very common to have two OLED displays that each render different content and are oriented or flipped differently. You can do this by switching which content to render by using the return value from `is_keyboard_master()` or `is_keyboard_left()` found in `split_util.h`, e.g:
76 78
77```C++ 79```c
78#ifdef OLED_DRIVER_ENABLE 80#ifdef OLED_DRIVER_ENABLE
79oled_rotation_t oled_init_user(oled_rotation_t rotation) { 81oled_rotation_t oled_init_user(oled_rotation_t rotation) {
80 if (!is_keyboard_master()) 82 if (!is_keyboard_master()) {
81 return OLED_ROTATION_180; // flips the display 180 degrees if offhand 83 return OLED_ROTATION_180; // flips the display 180 degrees if offhand
82 return rotation; 84 }
85
86 return rotation;
83} 87}
84 88
85void oled_task_user(void) { 89void oled_task_user(void) {
86 if (is_keyboard_master()) { 90 if (is_keyboard_master()) {
87 render_status(); // Renders the current keyboard state (layer, lock, caps, scroll, etc) 91 render_status(); // Renders the current keyboard state (layer, lock, caps, scroll, etc)
88 } else { 92 } else {
89 render_logo(); // Renders a statuc logo 93 render_logo(); // Renders a static logo
90 oled_scroll_left(); // Turns on scrolling 94 oled_scroll_left(); // Turns on scrolling
91 } 95 }
92} 96}
93#endif 97#endif
94``` 98```
95 99
96 100## Basic Configuration
97 ## Basic Configuration 101
98 102|Define |Default |Description |
99| Define | Default | Description | 103|---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------|
100|----------------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------| 104|`OLED_DISPLAY_ADDRESS` |`0x3C` |The i2c address of the OLED Display |
101| `OLED_DISPLAY_ADDRESS` | `0x3C` | The i2c address of the OLED Display | 105|`OLED_FONT_H` |`"glcdfont.c"` |The font code file to use for custom fonts |
102| `OLED_FONT_H` | `"glcdfont.c"` | The font code file to use for custom fonts | 106|`OLED_FONT_START` |`0` |The starting characer index for custom fonts |
103| `OLED_FONT_START` | `0` | The starting characer index for custom fonts | 107|`OLED_FONT_END` |`224` |The ending characer index for custom fonts |
104| `OLED_FONT_END` | `224` | The ending characer index for custom fonts | 108|`OLED_FONT_WIDTH` |`6` |The font width |
105| `OLED_FONT_WIDTH` | `6` | The font width | 109|`OLED_FONT_HEIGHT` |`8` |The font height (untested) |
106| `OLED_FONT_HEIGHT` | `8` | The font height (untested) | 110|`OLED_TIMEOUT` |`60000` |Turns off the OLED screen after 60000ms of keyboard inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. |
107| `OLED_TIMEOUT` | `60000` | Turns off the OLED screen after 60000ms of keyboard inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. | 111|`OLED_SCROLL_TIMEOUT` |`0` |Scrolls the OLED screen after 0ms of OLED inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. |
108| `OLED_SCROLL_TIMEOUT` | `0` | Scrolls the OLED screen after 0ms of OLED inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. | 112|`OLED_SCROLL_TIMEOUT_RIGHT`|*Not defined* |Scroll timeout direction is right when defined, left when undefined. |
109| `OLED_SCROLL_TIMEOUT_RIGHT`| *Not defined* | Scroll timeout direction is right when defined, left when undefined. | 113|`OLED_IC` |`OLED_IC_SSD1306`|Set to `OLED_IC_SH1106` if you're using the SH1106 OLED controller. |
110| `OLED_IC` | `OLED_IC_SSD1306` | Set to `OLED_IC_SH1106` if you're using the SH1106 OLED controller. | 114|`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.|
111| `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. |
112 115
113 ## 128x64 & Custom sized OLED Displays 116 ## 128x64 & Custom sized OLED Displays
114 117
115 The default display size for this feature is 128x32 and all necessary defines are precalculated with that in mind. We have added a define, `OLED_DISPLAY_128X64`, to switch all the values to be used in a 128x64 display, as well as added a custom define, `OLED_DISPLAY_CUSTOM`, that allows you to provide the necessary values to the driver. 118 The default display size for this feature is 128x32 and all necessary defines are precalculated with that in mind. We have added a define, `OLED_DISPLAY_128X64`, to switch all the values to be used in a 128x64 display, as well as added a custom define, `OLED_DISPLAY_CUSTOM`, that allows you to provide the necessary values to the driver.
116 119
117|Define |Default |Description | 120|Define |Default |Description |
118|-----------------------|---------------|-----------------------------------------------------------------| 121|---------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------|
119|`OLED_DISPLAY_128X64` |*Not defined* |Changes the display defines for use with 128x64 displays. | 122|`OLED_DISPLAY_128X64`|*Not defined* |Changes the display defines for use with 128x64 displays. |
120|`OLED_DISPLAY_CUSTOM` |*Not defined* |Changes the display defines for use with custom displays.<br />Requires user to implement the below defines. | 123|`OLED_DISPLAY_CUSTOM`|*Not defined* |Changes the display defines for use with custom displays.<br>Requires user to implement the below defines. |
121|`OLED_DISPLAY_WIDTH` |`128` |The width of the OLED display. | 124|`OLED_DISPLAY_WIDTH` |`128` |The width of the OLED display. |
122|`OLED_DISPLAY_HEIGHT` |`32` |The height of the OLED display. | 125|`OLED_DISPLAY_HEIGHT`|`32` |The height of the OLED display. |
123|`OLED_MATRIX_SIZE` |`512` |The local buffer size to allocate.<br />`(OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)`. | 126|`OLED_MATRIX_SIZE` |`512` |The local buffer size to allocate.<br>`(OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)`. |
124|`OLED_BLOCK_TYPE` |`uint16_t` |The unsigned integer type to use for dirty rendering. | 127|`OLED_BLOCK_TYPE` |`uint16_t` |The unsigned integer type to use for dirty rendering. |
125|`OLED_BLOCK_COUNT` |`16` |The number of blocks the display is divided into for dirty rendering.<br />`(sizeof(OLED_BLOCK_TYPE) * 8)`. | 128|`OLED_BLOCK_COUNT` |`16` |The number of blocks the display is divided into for dirty rendering.<br>`(sizeof(OLED_BLOCK_TYPE) * 8)`. |
126|`OLED_BLOCK_SIZE` |`32` |The size of each block for dirty rendering<br />`(OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)`. | 129|`OLED_BLOCK_SIZE` |`32` |The size of each block for dirty rendering<br>`(OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)`. |
127|`OLED_COM_PINS` |`COM_PINS_SEQ` |How the SSD1306 chip maps it's memory to display.<br />Options are `COM_PINS_SEQ`, `COM_PINS_ALT`, `COM_PINS_SEQ_LR`, & `COM_PINS_ALT_LR`. | 130|`OLED_COM_PINS` |`COM_PINS_SEQ` |How the SSD1306 chip maps it's memory to display.<br>Options are `COM_PINS_SEQ`, `COM_PINS_ALT`, `COM_PINS_SEQ_LR`, & `COM_PINS_ALT_LR`.|
128|`OLED_SOURCE_MAP` |`{ 0, ... N }` |Precalculated source array to use for mapping source buffer to target OLED memory in 90 degree rendering. | 131|`OLED_SOURCE_MAP` |`{ 0, ... N }` |Precalculated source array to use for mapping source buffer to target OLED memory in 90 degree rendering. |
129|`OLED_TARGET_MAP` |`{ 24, ... N }`|Precalculated target array to use for mapping source buffer to target OLED memory in 90 degree rendering. | 132|`OLED_TARGET_MAP` |`{ 24, ... N }`|Precalculated target array to use for mapping source buffer to target OLED memory in 90 degree rendering. |
130 133
131 134
132### 90 Degree Rotation - Technical Mumbo Jumbo 135### 90 Degree Rotation - Technical Mumbo Jumbo
133 136
134!> Rotation is unsupported on the SH1106. 137!> Rotation is unsupported on the SH1106.
135 138
136```C 139```c
137// OLED Rotation enum values are flags 140// OLED Rotation enum values are flags
138typedef enum { 141typedef enum {
139 OLED_ROTATION_0 = 0, 142 OLED_ROTATION_0 = 0,
@@ -143,9 +146,9 @@ typedef enum {
143} oled_rotation_t; 146} oled_rotation_t;
144``` 147```
145 148
146 OLED displays driven by SSD1306 drivers only natively support in hard ware 0 degree and 180 degree rendering. This feature is done in software and not free. Using this feature will increase the time to calculate what data to send over i2c to the OLED. If you are strapped for cycles, this can cause keycodes to not register. In testing however, the rendering time on an `atmega32u4` board only went from 2ms to 5ms and keycodes not registering was only noticed once we hit 15ms. 149OLED displays driven by SSD1306 drivers only natively support in hardware 0 degree and 180 degree rendering. This feature is done in software and not free. Using this feature will increase the time to calculate what data to send over i2c to the OLED. If you are strapped for cycles, this can cause keycodes to not register. In testing however, the rendering time on an ATmega32U4 board only went from 2ms to 5ms and keycodes not registering was only noticed once we hit 15ms.
147 150
148 90 Degree Rotated Rendering is achieved by using bitwise operations to rotate each 8 block of memory and uses two precalculated arrays to remap buffer memory to OLED memory. The memory map defines are precalculated for remap performance and are calculated based on the OLED Height, Width, and Block Size. For example, in the 128x32 implementation with a `uint8_t` block type, we have a 64 byte block size. This gives us eight 8 byte blocks that need to be rotated and rendered. The OLED renders horizontally two 8 byte blocks before moving down a page, e.g: 15190 degree rotation is achieved by using bitwise operations to rotate each 8 block of memory and uses two precalculated arrays to remap buffer memory to OLED memory. The memory map defines are precalculated for remap performance and are calculated based on the display height, width, and block size. For example, in the 128x32 implementation with a `uint8_t` block type, we have a 64 byte block size. This gives us eight 8 byte blocks that need to be rotated and rendered. The OLED renders horizontally two 8 byte blocks before moving down a page, e.g:
149 152
150| | | | | | | 153| | | | | | |
151|---|---|---|---|---|---| 154|---|---|---|---|---|---|
@@ -167,8 +170,8 @@ So those precalculated arrays just index the memory offsets in the order in whic
167 170
168## OLED API 171## OLED API
169 172
170```C++ 173```c
171// OLED Rotation enum values are flags 174// OLED rotation enum values are flags
172typedef enum { 175typedef enum {
173 OLED_ROTATION_0 = 0, 176 OLED_ROTATION_0 = 0,
174 OLED_ROTATION_90 = 1, 177 OLED_ROTATION_90 = 1,
@@ -272,26 +275,26 @@ uint8_t oled_max_lines(void);
272 275
273!> Scrolling and rotation are unsupported on the SH1106. 276!> Scrolling and rotation are unsupported on the SH1106.
274 277
275## SSD1306.h driver conversion guide 278## SSD1306.h Driver Conversion Guide
276 279
277|Old API |Recommended New API | 280|Old API |Recommended New API |
278|---------------------------|-----------------------------------| 281|-------------------------|---------------------------------|
279|`struct CharacterMatrix` |*removed - delete all references* | 282|`struct CharacterMatrix` |*removed - delete all references*|
280|`iota_gfx_init` |`oled_init` | 283|`iota_gfx_init` |`oled_init` |
281|`iota_gfx_on` |`oled_on` | 284|`iota_gfx_on` |`oled_on` |
282|`iota_gfx_off` |`oled_off` | 285|`iota_gfx_off` |`oled_off` |
283|`iota_gfx_flush` |`oled_render` | 286|`iota_gfx_flush` |`oled_render` |
284|`iota_gfx_write_char` |`oled_write_char` | 287|`iota_gfx_write_char` |`oled_write_char` |
285|`iota_gfx_write` |`oled_write` | 288|`iota_gfx_write` |`oled_write` |
286|`iota_gfx_write_P` |`oled_write_P` | 289|`iota_gfx_write_P` |`oled_write_P` |
287|`iota_gfx_clear_screen` |`oled_clear` | 290|`iota_gfx_clear_screen` |`oled_clear` |
288|`matrix_clear` |*removed - delete all references* | 291|`matrix_clear` |*removed - delete all references*|
289|`matrix_write_char_inner` |`oled_write_char` | 292|`matrix_write_char_inner`|`oled_write_char` |
290|`matrix_write_char` |`oled_write_char` | 293|`matrix_write_char` |`oled_write_char` |
291|`matrix_write` |`oled_write` | 294|`matrix_write` |`oled_write` |
292|`matrix_write_ln` |`oled_write_ln` | 295|`matrix_write_ln` |`oled_write_ln` |
293|`matrix_write_P` |`oled_write_P` | 296|`matrix_write_P` |`oled_write_P` |
294|`matrix_write_ln_P` |`oled_write_ln_P` | 297|`matrix_write_ln_P` |`oled_write_ln_P` |
295|`matrix_render` |`oled_render` | 298|`matrix_render` |`oled_render` |
296|`iota_gfx_task` |`oled_task` | 299|`iota_gfx_task` |`oled_task` |
297|`iota_gfx_task_user` |`oled_task_user` | 300|`iota_gfx_task_user` |`oled_task_user` |