summaryrefslogtreecommitdiff
path: root/drivers/oled
diff options
context:
space:
mode:
authorDrashna Jaelre <drashna@live.com>2023-05-10 14:04:53 -0700
committerGitHub <noreply@github.com>2023-05-10 14:04:53 -0700
commit2ddad246ce85066d5aee798e6ea516ea8e49eea9 (patch)
tree9cf408192e62be0fb251d8668879458e91a58a4f /drivers/oled
parent8a332e6f0105d2db9239e3c3f997bae754522804 (diff)
OLED Driver improvements (#20331)
Co-authored-by: Sergey Vlasov <sigprof@gmail.com>
Diffstat (limited to 'drivers/oled')
-rw-r--r--drivers/oled/oled_driver.c (renamed from drivers/oled/ssd1306_sh1106.c)294
-rw-r--r--drivers/oled/oled_driver.h153
2 files changed, 392 insertions, 55 deletions
diff --git a/drivers/oled/ssd1306_sh1106.c b/drivers/oled/oled_driver.c
index 342920572e..e50a881120 100644
--- a/drivers/oled/ssd1306_sh1106.c
+++ b/drivers/oled/oled_driver.c
@@ -14,20 +14,23 @@ GNU General Public License for more details.
14You should have received a copy of the GNU General Public License 14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>. 15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/ 16*/
17#include "i2c_master.h" 17
18#if defined(OLED_TRANSPORT_SPI)
19# include "spi_master.h"
20#elif defined(OLED_TRANSPORT_I2C)
21# include "i2c_master.h"
22#endif
18#include "oled_driver.h" 23#include "oled_driver.h"
19#include OLED_FONT_H 24#include OLED_FONT_H
20#include "timer.h" 25#include "timer.h"
21#include "print.h" 26#include "print.h"
22
23#include <string.h> 27#include <string.h>
24
25#include "progmem.h" 28#include "progmem.h"
26 29#include "wait.h"
27#include "keyboard.h"
28 30
29// Used commands from spec sheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf 31// Used commands from spec sheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
30// for SH1106: https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf 32// for SH1106: https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf
33// for SH1107: https://www.displayfuture.com/Display/datasheet/controller/SH1107.pdf
31 34
32// Fundamental Commands 35// Fundamental Commands
33#define CONTRAST 0x81 36#define CONTRAST 0x81
@@ -82,6 +85,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
82// Charge Pump Commands 85// Charge Pump Commands
83#define CHARGE_PUMP 0x8D 86#define CHARGE_PUMP 0x8D
84 87
88// Commands specific to the SH1107 chip
89#define SH1107_DISPLAY_START_LINE 0xDC
90#define SH1107_MEMORY_MODE_PAGE 0x20
91#define SH1107_MEMORY_MODE_VERTICAL 0x21
92
85// Misc defines 93// Misc defines
86#ifndef OLED_BLOCK_COUNT 94#ifndef OLED_BLOCK_COUNT
87# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) 95# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8)
@@ -89,19 +97,43 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
89#ifndef OLED_BLOCK_SIZE 97#ifndef OLED_BLOCK_SIZE
90# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) 98# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)
91#endif 99#endif
100// Default display clock
101#if !defined(OLED_DISPLAY_CLOCK)
102# define OLED_DISPLAY_CLOCK 0x80
103#endif
104// Default VCOMH deselect value
105#if !defined(OLED_VCOM_DETECT)
106# define OLED_VCOM_DETECT 0x20
107#endif
108#if !defined(OLED_PRE_CHARGE_PERIOD)
109# define OLED_PRE_CHARGE_PERIOD 0xF1
110#endif
111
92 112
93#define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1) 113#define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1)
94 114
115#define OLED_IC_HAS_HORIZONTAL_MODE (OLED_IC == OLED_IC_SSD1306)
116#define OLED_IC_COM_PINS_ARE_COLUMNS (OLED_IC == OLED_IC_SH1107)
117
118#ifndef OLED_COM_PIN_COUNT
119# if OLED_IC == OLED_IC_SSD1306
120# define OLED_COM_PIN_COUNT 64
121# elif OLED_IC == OLED_IC_SH1106
122# define OLED_COM_PIN_COUNT 64
123# elif OLED_IC == OLED_IC_SH1107
124# define OLED_COM_PIN_COUNT 128
125# else
126# error Invalid OLED_IC value
127# endif
128#endif
129
130#ifndef OLED_COM_PIN_OFFSET
131# define OLED_COM_PIN_OFFSET 0
132#endif
133
95// i2c defines 134// i2c defines
96#define I2C_CMD 0x00 135#define I2C_CMD 0x00
97#define I2C_DATA 0x40 136#define I2C_DATA 0x40
98#if defined(__AVR__)
99# define I2C_TRANSMIT_P(data) i2c_transmit_P((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT)
100#else // defined(__AVR__)
101# define I2C_TRANSMIT_P(data) i2c_transmit((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT)
102#endif // defined(__AVR__)
103#define I2C_TRANSMIT(data) i2c_transmit((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT)
104#define I2C_WRITE_REG(mode, data, size) i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), mode, data, size, OLED_I2C_TIMEOUT)
105 137
106#define HAS_FLAGS(bits, flags) ((bits & flags) == flags) 138#define HAS_FLAGS(bits, flags) ((bits & flags) == flags)
107 139
@@ -110,7 +142,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
110// parts of the display unusable or don't get cleared correctly 142// parts of the display unusable or don't get cleared correctly
111// and also allows for drawing & inverting 143// and also allows for drawing & inverting
112uint8_t oled_buffer[OLED_MATRIX_SIZE]; 144uint8_t oled_buffer[OLED_MATRIX_SIZE];
113uint8_t * oled_cursor; 145uint8_t *oled_cursor;
114OLED_BLOCK_TYPE oled_dirty = 0; 146OLED_BLOCK_TYPE oled_dirty = 0;
115bool oled_initialized = false; 147bool oled_initialized = false;
116bool oled_active = false; 148bool oled_active = false;
@@ -132,24 +164,118 @@ uint32_t oled_scroll_timeout;
132uint16_t oled_update_timeout; 164uint16_t oled_update_timeout;
133#endif 165#endif
134 166
135// Internal variables to reduce math instructions 167#if defined(OLED_TRANSPORT_SPI)
168# ifndef OLED_DC_PIN
169# error "The OLED driver in SPI needs a D/C pin defined"
170# endif
171# ifndef OLED_CS_PIN
172# error "The OLED driver in SPI needs a CS pin defined"
173# endif
174# ifndef OLED_SPI_MODE
175# define OLED_SPI_MODE 3
176# endif
177# ifndef OLED_SPI_DIVISOR
178# define OLED_SPI_DIVISOR 2
179# endif
180#elif defined(OLED_TRANSPORT_I2C)
181# if !defined(OLED_DISPLAY_ADDRESS)
182# define OLED_DISPLAY_ADDRESS 0x3C
183# endif
184#endif
185
186// Transmit/Write Funcs.
187__attribute__((weak)) bool oled_send_cmd(const uint8_t *data, uint16_t size) {
188#if defined(OLED_TRANSPORT_SPI)
189 if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) {
190 return false;
191 }
192 // Command Mode
193 writePinLow(OLED_DC_PIN);
194 // Send the commands
195 if (spi_transmit(&data[1], size - 1) != SPI_STATUS_SUCCESS) {
196 spi_stop();
197 return false;
198 }
199 spi_stop();
200 return true;
201#elif defined(OLED_TRANSPORT_I2C)
202 i2c_status_t status = i2c_transmit((OLED_DISPLAY_ADDRESS << 1), data, size, OLED_I2C_TIMEOUT);
203
204 return (status == I2C_STATUS_SUCCESS);
205#endif
206}
136 207
208__attribute__((weak)) bool oled_send_cmd_P(const uint8_t *data, uint16_t size) {
137#if defined(__AVR__) 209#if defined(__AVR__)
138// identical to i2c_transmit, but for PROGMEM since all initialization is in PROGMEM arrays currently 210# if defined(OLED_TRANSPORT_SPI)
139// probably should move this into i2c_master... 211 if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) {
140static i2c_status_t i2c_transmit_P(uint8_t address, const uint8_t *data, uint16_t length, uint16_t timeout) { 212 return false;
141 i2c_status_t status = i2c_start(address | I2C_WRITE, timeout); 213 }
214 spi_status_t status = SPI_STATUS_SUCCESS;
215 // Command Mode
216 writePinLow(OLED_DC_PIN);
217 // Send the commands
218 for (uint16_t i = 1; i < size && status >= 0; i++) {
219 status = spi_write(pgm_read_byte((const char *)&data[i]));
220 }
221 spi_stop();
222 return (status >= 0);
223# elif defined(OLED_TRANSPORT_I2C)
224 i2c_status_t status = i2c_start((OLED_DISPLAY_ADDRESS << 1) | I2C_WRITE, OLED_I2C_TIMEOUT);
142 225
143 for (uint16_t i = 0; i < length && status >= 0; i++) { 226 for (uint16_t i = 0; i < size && status >= 0; i++) {
144 status = i2c_write(pgm_read_byte((const char *)data++), timeout); 227 status = i2c_write(pgm_read_byte((const char *)data++), OLED_I2C_TIMEOUT);
145 if (status) break;
146 } 228 }
147 229
148 i2c_stop(); 230 i2c_stop();
149 231
150 return status; 232 return (status == I2C_STATUS_SUCCESS);
233# endif
234#else
235 return oled_send_cmd(data, size);
236#endif
237}
238
239__attribute__((weak)) bool oled_send_data(const uint8_t *data, uint16_t size) {
240#if defined(OLED_TRANSPORT_SPI)
241 if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) {
242 return false;
243 }
244 // Data Mode
245 writePinHigh(OLED_DC_PIN);
246 // Send the commands
247 if (spi_transmit(data, size) != SPI_STATUS_SUCCESS) {
248 spi_stop();
249 return false;
250 }
251 spi_stop();
252 return true;
253#elif defined(OLED_TRANSPORT_I2C)
254 i2c_status_t status = i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), I2C_DATA, data, size, OLED_I2C_TIMEOUT);
255 return (status == I2C_STATUS_SUCCESS);
256#endif
151} 257}
258
259__attribute__((weak)) void oled_driver_init(void) {
260#if defined(OLED_TRANSPORT_SPI)
261 spi_init();
262 setPinOutput(OLED_CS_PIN);
263 writePinHigh(OLED_CS_PIN);
264
265 setPinOutput(OLED_DC_PIN);
266 writePinLow(OLED_DC_PIN);
267# ifdef OLED_RST_PIN
268 /* Reset device */
269 setPinOutput(OLED_RST_PIN);
270 writePinLow(OLED_RST_PIN);
271 wait_ms(20);
272 writePinHigh(OLED_RST_PIN);
273 wait_ms(20);
274# endif
275#elif defined(OLED_TRANSPORT_I2C)
276 i2c_init();
152#endif 277#endif
278}
153 279
154// Flips the rendering bits for a character at the current cursor position 280// Flips the rendering bits for a character at the current cursor position
155static void InvertCharacter(uint8_t *cursor) { 281static void InvertCharacter(uint8_t *cursor) {
@@ -161,7 +287,7 @@ static void InvertCharacter(uint8_t *cursor) {
161} 287}
162 288
163bool oled_init(oled_rotation_t rotation) { 289bool oled_init(oled_rotation_t rotation) {
164#if defined(USE_I2C) && defined(SPLIT_KEYBOARD) 290#if defined(USE_I2C) && defined(SPLIT_KEYBOARD) && defined(OLED_TRANSPORT_I2C)
165 if (!is_keyboard_master()) { 291 if (!is_keyboard_master()) {
166 return true; 292 return true;
167 } 293 }
@@ -173,47 +299,61 @@ bool oled_init(oled_rotation_t rotation) {
173 } else { 299 } else {
174 oled_rotation_width = OLED_DISPLAY_HEIGHT; 300 oled_rotation_width = OLED_DISPLAY_HEIGHT;
175 } 301 }
176 i2c_init(); 302 oled_driver_init();
177 303
178 static const uint8_t PROGMEM display_setup1[] = { 304 static const uint8_t PROGMEM display_setup1[] = {
179 I2C_CMD, 305 I2C_CMD,
180 DISPLAY_OFF, 306 DISPLAY_OFF,
181 DISPLAY_CLOCK, 307 DISPLAY_CLOCK,
182 0x80, 308 OLED_DISPLAY_CLOCK,
183 MULTIPLEX_RATIO, 309 MULTIPLEX_RATIO,
310#if OLED_IC_COM_PINS_ARE_COLUMNS
311 OLED_DISPLAY_WIDTH - 1,
312#else
184 OLED_DISPLAY_HEIGHT - 1, 313 OLED_DISPLAY_HEIGHT - 1,
185 DISPLAY_OFFSET, 314#endif
315#if OLED_IC == OLED_IC_SH1107
316 SH1107_DISPLAY_START_LINE,
186 0x00, 317 0x00,
318#else
187 DISPLAY_START_LINE | 0x00, 319 DISPLAY_START_LINE | 0x00,
320#endif
188 CHARGE_PUMP, 321 CHARGE_PUMP,
189 0x14, 322 0x14,
190#if (OLED_IC != OLED_IC_SH1106) 323#if OLED_IC_HAS_HORIZONTAL_MODE
191 // MEMORY_MODE is unsupported on SH1106 (Page Addressing only) 324 // MEMORY_MODE is unsupported on SH1106 (Page Addressing only)
192 MEMORY_MODE, 325 MEMORY_MODE,
193 0x00, // Horizontal addressing mode 326 0x00, // Horizontal addressing mode
327#elif OLED_IC == OLED_IC_SH1107
328 // Page addressing mode
329 SH1107_MEMORY_MODE_PAGE,
194#endif 330#endif
195 }; 331 };
196 if (I2C_TRANSMIT_P(display_setup1) != I2C_STATUS_SUCCESS) { 332 if (!oled_send_cmd_P(display_setup1, ARRAY_SIZE(display_setup1))) {
197 print("oled_init cmd set 1 failed\n"); 333 print("oled_init cmd set 1 failed\n");
198 return false; 334 return false;
199 } 335 }
200 336
201 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_180)) { 337 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_180)) {
202 static const uint8_t PROGMEM display_normal[] = {I2C_CMD, SEGMENT_REMAP_INV, COM_SCAN_DEC}; 338 static const uint8_t PROGMEM display_normal[] = {
203 if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) { 339 I2C_CMD, SEGMENT_REMAP_INV, COM_SCAN_DEC, DISPLAY_OFFSET, OLED_COM_PIN_OFFSET,
340 };
341 if (!oled_send_cmd_P(display_normal, ARRAY_SIZE(display_normal))) {
204 print("oled_init cmd normal rotation failed\n"); 342 print("oled_init cmd normal rotation failed\n");
205 return false; 343 return false;
206 } 344 }
207 } else { 345 } else {
208 static const uint8_t PROGMEM display_flipped[] = {I2C_CMD, SEGMENT_REMAP, COM_SCAN_INC}; 346 static const uint8_t PROGMEM display_flipped[] = {
209 if (I2C_TRANSMIT_P(display_flipped) != I2C_STATUS_SUCCESS) { 347 I2C_CMD, SEGMENT_REMAP, COM_SCAN_INC, DISPLAY_OFFSET, (OLED_COM_PIN_COUNT - OLED_COM_PIN_OFFSET) % OLED_COM_PIN_COUNT,
348 };
349 if (!oled_send_cmd_P(display_flipped, ARRAY_SIZE(display_flipped))) {
210 print("display_flipped failed\n"); 350 print("display_flipped failed\n");
211 return false; 351 return false;
212 } 352 }
213 } 353 }
214 354
215 static const uint8_t PROGMEM display_setup2[] = {I2C_CMD, COM_PINS, OLED_COM_PINS, CONTRAST, OLED_BRIGHTNESS, PRE_CHARGE_PERIOD, 0xF1, VCOM_DETECT, 0x20, DISPLAY_ALL_ON_RESUME, NORMAL_DISPLAY, DEACTIVATE_SCROLL, DISPLAY_ON}; 355 static const uint8_t PROGMEM display_setup2[] = {I2C_CMD, COM_PINS, OLED_COM_PINS, CONTRAST, OLED_BRIGHTNESS, PRE_CHARGE_PERIOD, OLED_PRE_CHARGE_PERIOD, VCOM_DETECT, OLED_VCOM_DETECT, DISPLAY_ALL_ON_RESUME, NORMAL_DISPLAY, DEACTIVATE_SCROLL, DISPLAY_ON};
216 if (I2C_TRANSMIT_P(display_setup2) != I2C_STATUS_SUCCESS) { 356 if (!oled_send_cmd_P(display_setup2, ARRAY_SIZE(display_setup2))) {
217 print("display_setup2 failed\n"); 357 print("display_setup2 failed\n");
218 return false; 358 return false;
219 } 359 }
@@ -249,30 +389,49 @@ static void calc_bounds(uint8_t update_start, uint8_t *cmd_array) {
249 // Calculate commands to set memory addressing bounds. 389 // Calculate commands to set memory addressing bounds.
250 uint8_t start_page = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_WIDTH; 390 uint8_t start_page = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_WIDTH;
251 uint8_t start_column = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_WIDTH; 391 uint8_t start_column = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_WIDTH;
252#if (OLED_IC == OLED_IC_SH1106) 392#if !OLED_IC_HAS_HORIZONTAL_MODE
253 // Commands for Page Addressing Mode. Sets starting page and column; has no end bound. 393 // Commands for Page Addressing Mode. Sets starting page and column; has no end bound.
254 // Column value must be split into high and low nybble and sent as two commands. 394 // Column value must be split into high and low nybble and sent as two commands.
255 cmd_array[0] = PAM_PAGE_ADDR | start_page; 395 cmd_array[0] = PAM_PAGE_ADDR | start_page;
256 cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f); 396 cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f);
257 cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f); 397 cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f);
258 cmd_array[3] = NOP;
259 cmd_array[4] = NOP;
260 cmd_array[5] = NOP;
261#else 398#else
262 // Commands for use in Horizontal Addressing mode. 399 // Commands for use in Horizontal Addressing mode.
263 cmd_array[1] = start_column; 400 cmd_array[1] = start_column + OLED_COLUMN_OFFSET;
264 cmd_array[4] = start_page; 401 cmd_array[4] = start_page;
265 cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) % OLED_DISPLAY_WIDTH + cmd_array[1]; 402 cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) % OLED_DISPLAY_WIDTH + cmd_array[1];
266 cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) / OLED_DISPLAY_WIDTH - 1; 403 cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) / OLED_DISPLAY_WIDTH - 1 + cmd_array[4];
267#endif 404#endif
268} 405}
269 406
270static void calc_bounds_90(uint8_t update_start, uint8_t *cmd_array) { 407static void calc_bounds_90(uint8_t update_start, uint8_t *cmd_array) {
271 cmd_array[1] = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8; 408 // Block numbering starts from the bottom left corner, going up and then to
272 cmd_array[4] = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT; 409 // the right. The controller needs the page and column numbers for the top
410 // left and bottom right corners of that block.
411
412 // Total number of pages across the screen height.
413 const uint8_t height_in_pages = OLED_DISPLAY_HEIGHT / 8;
414
415 // Difference of starting page numbers for adjacent blocks; may be 0 if
416 // blocks are large enough to occupy one or more whole 8px columns.
417 const uint8_t page_inc_per_block = OLED_BLOCK_SIZE % OLED_DISPLAY_HEIGHT / 8;
418
419 // Top page number for a block which is at the bottom edge of the screen.
420 const uint8_t bottom_block_top_page = (height_in_pages - page_inc_per_block) % height_in_pages;
421
422#if !OLED_IC_HAS_HORIZONTAL_MODE
423 // Only the Page Addressing Mode is supported
424 uint8_t start_page = bottom_block_top_page - (OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT / 8);
425 uint8_t start_column = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8;
426 cmd_array[0] = PAM_PAGE_ADDR | start_page;
427 cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f);
428 cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f);
429#else
430 cmd_array[1] = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8 + OLED_COLUMN_OFFSET;
431 cmd_array[4] = bottom_block_top_page - (OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT / 8);
273 cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8 - 1 + cmd_array[1]; 432 cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8 - 1 + cmd_array[1];
274 ; 433 cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) % OLED_DISPLAY_HEIGHT / 8 + cmd_array[4];
275 cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) % OLED_DISPLAY_HEIGHT / 8; 434#endif
276} 435}
277 436
278uint8_t crot(uint8_t a, int8_t n) { 437uint8_t crot(uint8_t a, int8_t n) {
@@ -309,7 +468,11 @@ void oled_render(void) {
309 } 468 }
310 469
311 // Set column & page position 470 // Set column & page position
471#if OLED_IC_HAS_HORIZONTAL_MODE
312 static uint8_t display_start[] = {I2C_CMD, COLUMN_ADDR, 0, OLED_DISPLAY_WIDTH - 1, PAGE_ADDR, 0, OLED_DISPLAY_HEIGHT / 8 - 1}; 472 static uint8_t display_start[] = {I2C_CMD, COLUMN_ADDR, 0, OLED_DISPLAY_WIDTH - 1, PAGE_ADDR, 0, OLED_DISPLAY_HEIGHT / 8 - 1};
473#else
474 static uint8_t display_start[] = {I2C_CMD, PAM_PAGE_ADDR, PAM_SETCOLUMN_LSB, PAM_SETCOLUMN_MSB};
475#endif
313 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { 476 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
314 calc_bounds(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start 477 calc_bounds(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start
315 } else { 478 } else {
@@ -317,14 +480,14 @@ void oled_render(void) {
317 } 480 }
318 481
319 // Send column & page position 482 // Send column & page position
320 if (I2C_TRANSMIT(display_start) != I2C_STATUS_SUCCESS) { 483 if (!oled_send_cmd(display_start, ARRAY_SIZE(display_start))) {
321 print("oled_render offset command failed\n"); 484 print("oled_render offset command failed\n");
322 return; 485 return;
323 } 486 }
324 487
325 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { 488 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
326 // Send render data chunk as is 489 // Send render data chunk as is
327 if (I2C_WRITE_REG(I2C_DATA, &oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE) != I2C_STATUS_SUCCESS) { 490 if (!oled_send_data(&oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE)) {
328 print("oled_render data failed\n"); 491 print("oled_render data failed\n");
329 return; 492 return;
330 } 493 }
@@ -339,11 +502,32 @@ void oled_render(void) {
339 rotate_90(&oled_buffer[OLED_BLOCK_SIZE * update_start + source_map[i]], &temp_buffer[target_map[i]]); 502 rotate_90(&oled_buffer[OLED_BLOCK_SIZE * update_start + source_map[i]], &temp_buffer[target_map[i]]);
340 } 503 }
341 504
505#if OLED_IC_HAS_HORIZONTAL_MODE
342 // Send render data chunk after rotating 506 // Send render data chunk after rotating
343 if (I2C_WRITE_REG(I2C_DATA, &temp_buffer[0], OLED_BLOCK_SIZE) != I2C_STATUS_SUCCESS) { 507 if (!oled_send_data(&temp_buffer[0], OLED_BLOCK_SIZE)) {
344 print("oled_render90 data failed\n"); 508 print("oled_render90 data failed\n");
345 return; 509 return;
346 } 510 }
511#else
512 // For SH1106 or SH1107 the data chunk must be split into separate pieces for each page
513 const uint8_t columns_in_block = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8;
514 const uint8_t num_pages = OLED_BLOCK_SIZE / columns_in_block;
515 for (uint8_t i = 0; i < num_pages; ++i) {
516 // Send column & page position for all pages except the first one
517 if (i > 0) {
518 display_start[1]++;
519 if (!oled_send_cmd(display_start, ARRAY_SIZE(display_start))) {
520 print("oled_render offset command failed\n");
521 return;
522 }
523 }
524 // Send data for the page
525 if (!oled_send_data(&temp_buffer[columns_in_block * i], columns_in_block)) {
526 print("oled_render90 data failed\n");
527 return;
528 }
529 }
530#endif
347 } 531 }
348 532
349 // Clear dirty flag of just rendered block 533 // Clear dirty flag of just rendered block
@@ -568,7 +752,7 @@ bool oled_on(void) {
568#endif 752#endif
569 753
570 if (!oled_active) { 754 if (!oled_active) {
571 if (I2C_TRANSMIT_P(display_on) != I2C_STATUS_SUCCESS) { 755 if (!oled_send_cmd_P(display_on, ARRAY_SIZE(display_on))) {
572 print("oled_on cmd failed\n"); 756 print("oled_on cmd failed\n");
573 return oled_active; 757 return oled_active;
574 } 758 }
@@ -590,7 +774,7 @@ bool oled_off(void) {
590#endif 774#endif
591 775
592 if (oled_active) { 776 if (oled_active) {
593 if (I2C_TRANSMIT_P(display_off) != I2C_STATUS_SUCCESS) { 777 if (!oled_send_cmd_P(display_off, ARRAY_SIZE(display_off))) {
594 print("oled_off cmd failed\n"); 778 print("oled_off cmd failed\n");
595 return oled_active; 779 return oled_active;
596 } 780 }
@@ -610,7 +794,7 @@ uint8_t oled_set_brightness(uint8_t level) {
610 794
611 uint8_t set_contrast[] = {I2C_CMD, CONTRAST, level}; 795 uint8_t set_contrast[] = {I2C_CMD, CONTRAST, level};
612 if (oled_brightness != level) { 796 if (oled_brightness != level) {
613 if (I2C_TRANSMIT(set_contrast) != I2C_STATUS_SUCCESS) { 797 if (!oled_send_cmd(set_contrast, ARRAY_SIZE(set_contrast))) {
614 print("set_brightness cmd failed\n"); 798 print("set_brightness cmd failed\n");
615 return oled_brightness; 799 return oled_brightness;
616 } 800 }
@@ -657,7 +841,7 @@ bool oled_scroll_right(void) {
657 // This prevents scrolling of bad data from starting the scroll too early after init 841 // This prevents scrolling of bad data from starting the scroll too early after init
658 if (!oled_dirty && !oled_scrolling) { 842 if (!oled_dirty && !oled_scrolling) {
659 uint8_t display_scroll_right[] = {I2C_CMD, SCROLL_RIGHT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL}; 843 uint8_t display_scroll_right[] = {I2C_CMD, SCROLL_RIGHT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL};
660 if (I2C_TRANSMIT(display_scroll_right) != I2C_STATUS_SUCCESS) { 844 if (!oled_send_cmd(display_scroll_right, ARRAY_SIZE(display_scroll_right))) {
661 print("oled_scroll_right cmd failed\n"); 845 print("oled_scroll_right cmd failed\n");
662 return oled_scrolling; 846 return oled_scrolling;
663 } 847 }
@@ -675,7 +859,7 @@ bool oled_scroll_left(void) {
675 // This prevents scrolling of bad data from starting the scroll too early after init 859 // This prevents scrolling of bad data from starting the scroll too early after init
676 if (!oled_dirty && !oled_scrolling) { 860 if (!oled_dirty && !oled_scrolling) {
677 uint8_t display_scroll_left[] = {I2C_CMD, SCROLL_LEFT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL}; 861 uint8_t display_scroll_left[] = {I2C_CMD, SCROLL_LEFT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL};
678 if (I2C_TRANSMIT(display_scroll_left) != I2C_STATUS_SUCCESS) { 862 if (!oled_send_cmd(display_scroll_left, ARRAY_SIZE(display_scroll_left))) {
679 print("oled_scroll_left cmd failed\n"); 863 print("oled_scroll_left cmd failed\n");
680 return oled_scrolling; 864 return oled_scrolling;
681 } 865 }
@@ -691,7 +875,7 @@ bool oled_scroll_off(void) {
691 875
692 if (oled_scrolling) { 876 if (oled_scrolling) {
693 static const uint8_t PROGMEM display_scroll_off[] = {I2C_CMD, DEACTIVATE_SCROLL}; 877 static const uint8_t PROGMEM display_scroll_off[] = {I2C_CMD, DEACTIVATE_SCROLL};
694 if (I2C_TRANSMIT_P(display_scroll_off) != I2C_STATUS_SUCCESS) { 878 if (!oled_send_cmd_P(display_scroll_off, ARRAY_SIZE(display_scroll_off))) {
695 print("oled_scroll_off cmd failed\n"); 879 print("oled_scroll_off cmd failed\n");
696 return oled_scrolling; 880 return oled_scrolling;
697 } 881 }
@@ -712,14 +896,14 @@ bool oled_invert(bool invert) {
712 896
713 if (invert && !oled_inverted) { 897 if (invert && !oled_inverted) {
714 static const uint8_t PROGMEM display_inverted[] = {I2C_CMD, INVERT_DISPLAY}; 898 static const uint8_t PROGMEM display_inverted[] = {I2C_CMD, INVERT_DISPLAY};
715 if (I2C_TRANSMIT_P(display_inverted) != I2C_STATUS_SUCCESS) { 899 if (!oled_send_cmd_P(display_inverted, ARRAY_SIZE(display_inverted))) {
716 print("oled_invert cmd failed\n"); 900 print("oled_invert cmd failed\n");
717 return oled_inverted; 901 return oled_inverted;
718 } 902 }
719 oled_inverted = true; 903 oled_inverted = true;
720 } else if (!invert && oled_inverted) { 904 } else if (!invert && oled_inverted) {
721 static const uint8_t PROGMEM display_normal[] = {I2C_CMD, NORMAL_DISPLAY}; 905 static const uint8_t PROGMEM display_normal[] = {I2C_CMD, NORMAL_DISPLAY};
722 if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) { 906 if (!oled_send_cmd_P(display_normal, ARRAY_SIZE(display_normal))) {
723 print("oled_invert cmd failed\n"); 907 print("oled_invert cmd failed\n");
724 return oled_inverted; 908 return oled_inverted;
725 } 909 }
diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h
index 291049e36b..627a3da0ba 100644
--- a/drivers/oled/oled_driver.h
+++ b/drivers/oled/oled_driver.h
@@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
22// an enumeration of the chips this driver supports 22// an enumeration of the chips this driver supports
23#define OLED_IC_SSD1306 0 23#define OLED_IC_SSD1306 0
24#define OLED_IC_SH1106 1 24#define OLED_IC_SH1106 1
25#define OLED_IC_SH1107 2
25 26
26#if defined(OLED_DISPLAY_CUSTOM) 27#if defined(OLED_DISPLAY_CUSTOM)
27// Expected user to implement the necessary defines 28// Expected user to implement the necessary defines
@@ -68,6 +69,152 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
68// If OLED_BLOCK_TYPE is uint8_t, these tables would look like: 69// If OLED_BLOCK_TYPE is uint8_t, these tables would look like:
69// #define OLED_SOURCE_MAP { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120 } 70// #define OLED_SOURCE_MAP { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120 }
70// #define OLED_TARGET_MAP { 56, 120, 48, 112, 40, 104, 32, 96, 24, 88, 16, 80, 8, 72, 0, 64 } 71// #define OLED_TARGET_MAP { 56, 120, 48, 112, 40, 104, 32, 96, 24, 88, 16, 80, 8, 72, 0, 64 }
72
73#elif defined(OLED_DISPLAY_64X32)
74# ifndef OLED_DISPLAY_WIDTH
75# define OLED_DISPLAY_WIDTH 64
76# endif
77# ifndef OLED_DISPLAY_HEIGHT
78# define OLED_DISPLAY_HEIGHT 32
79# endif
80# ifndef OLED_COLUMN_OFFSET
81# define OLED_COLUMN_OFFSET 32
82# endif
83# ifndef OLED_MATRIX_SIZE
84# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)
85# endif
86# ifndef OLED_BLOCK_TYPE
87# define OLED_BLOCK_TYPE uint8_t
88# endif
89# ifndef OLED_BLOCK_COUNT
90# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 8 (compile time mathed)
91# endif
92# ifndef OLED_BLOCK_SIZE
93# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 32 (compile time mathed)
94# endif
95# ifndef OLED_COM_PINS
96# define OLED_COM_PINS COM_PINS_ALT
97# endif
98
99# ifndef OLED_SOURCE_MAP
100# define OLED_SOURCE_MAP \
101 { 0, 8, 16, 24 }
102# endif
103# ifndef OLED_TARGET_MAP
104# define OLED_TARGET_MAP \
105 { 24, 16, 8, 0 }
106# endif
107
108#elif defined(OLED_DISPLAY_64X48)
109# ifndef OLED_DISPLAY_WIDTH
110# define OLED_DISPLAY_WIDTH 64
111# endif
112# ifndef OLED_DISPLAY_HEIGHT
113# define OLED_DISPLAY_HEIGHT 48
114# endif
115# ifndef OLED_COLUMN_OFFSET
116# define OLED_COLUMN_OFFSET 32
117# endif
118# ifndef OLED_MATRIX_SIZE
119# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)
120# endif
121# ifndef OLED_BLOCK_TYPE
122# define OLED_BLOCK_TYPE uint32_t
123# endif
124# ifndef OLED_BLOCK_COUNT
125# define OLED_BLOCK_COUNT 24
126# endif
127# ifndef OLED_BLOCK_SIZE
128# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)
129# endif
130# ifndef OLED_COM_PINS
131# define OLED_COM_PINS COM_PINS_ALT
132# endif
133
134# ifndef OLED_SOURCE_MAP
135# define OLED_SOURCE_MAP \
136 { 0, 8 }
137# endif
138# ifndef OLED_TARGET_MAP
139# define OLED_TARGET_MAP \
140 { 8, 0 }
141# endif
142
143#elif defined(OLED_DISPLAY_64X128)
144# ifndef OLED_DISPLAY_WIDTH
145# define OLED_DISPLAY_WIDTH 64
146# endif
147# ifndef OLED_DISPLAY_HEIGHT
148# define OLED_DISPLAY_HEIGHT 128
149# endif
150# ifndef OLED_IC
151# define OLED_IC OLED_IC_SH1107
152# endif
153# ifndef OLED_COM_PIN_OFFSET
154# define OLED_COM_PIN_OFFSET 32
155# endif
156# ifndef OLED_MATRIX_SIZE
157# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)
158# endif
159# ifndef OLED_BLOCK_TYPE
160# define OLED_BLOCK_TYPE uint16_t
161# endif
162# ifndef OLED_BLOCK_COUNT
163# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8)
164# endif
165# ifndef OLED_BLOCK_SIZE
166# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)
167# endif
168# ifndef OLED_COM_PINS
169# define OLED_COM_PINS COM_PINS_ALT
170# endif
171
172# ifndef OLED_SOURCE_MAP
173# define OLED_SOURCE_MAP \
174 { 0, 8, 16, 24, 32, 40, 48, 56 }
175# endif
176# ifndef OLED_TARGET_MAP
177# define OLED_TARGET_MAP \
178 { 56, 48, 40, 32, 24, 16, 8, 0 }
179# endif
180
181#elif defined(OLED_DISPLAY_128X128)
182// Quad height 128x128
183# ifndef OLED_DISPLAY_WIDTH
184# define OLED_DISPLAY_WIDTH 128
185# endif
186# ifndef OLED_DISPLAY_HEIGHT
187# define OLED_DISPLAY_HEIGHT 128
188# endif
189# ifndef OLED_IC
190# define OLED_IC OLED_IC_SH1107
191# endif
192# ifndef OLED_MATRIX_SIZE
193# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) // 2048 (compile time mathed)
194# endif
195# ifndef OLED_BLOCK_TYPE
196# define OLED_BLOCK_TYPE uint32_t
197# endif
198# ifndef OLED_BLOCK_COUNT
199# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 32 (compile time mathed)
200# endif
201# ifndef OLED_BLOCK_SIZE
202# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 64 (compile time mathed)
203# endif
204# ifndef OLED_COM_PINS
205# define OLED_COM_PINS COM_PINS_ALT
206# endif
207
208// For 90 degree rotation, we map our internal matrix to oled matrix using fixed arrays
209// The OLED writes to it's memory horizontally, starting top left, but our memory starts bottom left in this mode
210# ifndef OLED_SOURCE_MAP
211# define OLED_SOURCE_MAP \
212 { 0, 8, 16, 24, 32, 40, 48, 56 }
213# endif
214# ifndef OLED_TARGET_MAP
215# define OLED_TARGET_MAP \
216 { 56, 48, 40, 32, 24, 16, 8, 0 }
217# endif
71#else // defined(OLED_DISPLAY_128X64) 218#else // defined(OLED_DISPLAY_128X64)
72// Default 128x32 219// Default 128x32
73# ifndef OLED_DISPLAY_WIDTH 220# ifndef OLED_DISPLAY_WIDTH
@@ -191,6 +338,12 @@ typedef enum {
191// Returns true if the OLED was initialized successfully 338// Returns true if the OLED was initialized successfully
192bool oled_init(oled_rotation_t rotation); 339bool oled_init(oled_rotation_t rotation);
193 340
341// Send commands and data to screen
342bool oled_send_cmd(const uint8_t *data, uint16_t size);
343bool oled_send_cmd_P(const uint8_t *data, uint16_t size);
344bool oled_send_data(const uint8_t *data, uint16_t size);
345void oled_driver_init(void);
346
194// Called at the start of oled_init, weak function overridable by the user 347// Called at the start of oled_init, weak function overridable by the user
195// rotation - the value passed into oled_init 348// rotation - the value passed into oled_init
196// Return new oled_rotation_t if you want to override default rotation 349// Return new oled_rotation_t if you want to override default rotation