aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Vlasov <sigprof@gmail.com>2020-10-03 21:44:19 +0300
committerGitHub <noreply@github.com>2020-10-04 05:44:19 +1100
commit459ccb681f991d5b39060d25197233ac9c51d8b9 (patch)
tree9f3bccfc5c8880ffc67a3471c05892be69830d70
parent1fe7743af88cf0d65800d3ccfc1c59d1c2d65db2 (diff)
downloadqmk_firmware-459ccb681f991d5b39060d25197233ac9c51d8b9.tar.gz
qmk_firmware-459ccb681f991d5b39060d25197233ac9c51d8b9.zip
OLED driver fixes (#10377)
* Fix dirtying in oled_write_pixel() Set the dirty bit for the block only if oled_write_pixel() actually changed the buffer state. Without this check oled_write_pixel() could not be used inside the oled_task_user() code using the “redraw always” style, because the blocks touched by oled_write_pixel() would always appear dirty, and oled_render() would not proceed beyond the first such dirty block. * Fix oled_write_pixel() with 90/270 degree rotation Use oled_rotation_width instead of OLED_DISPLAY_WIDTH, so that a rotated display would be handled correctly. * Fix compilation with custom OLED_BLOCK_COUNT and OLED_BLOCK_SIZE Some OLED sizes (e.g., 64×48) may require a nonstandard value of OLED_BLOCK_COUNT. The documentation says that this value may be redefined in config.h, but actually trying to redefine it caused a compile error, because the macro was redefined in oled_driver.c. Make the OLED_BLOCK_COUNT definition in oled_driver.c respect any user override, and do the same for OLED_BLOCK_SIZE just in case. * Fix handling of out-of-range bits in oled_dirty If a custom OLED_BLOCK_COUNT value is specified, some bits in oled_dirty may not correspond to existing blocks; however, if those bits are set somewhere (e.g., by code with sets oled_dirty to ~0 or even -1), oled_render() would try to handle them and could access memory beyond oled_buffer and perform hardware operations with out of range values. Prevent this by masking off unused bits in oled_render(), and also avoid setting those bits in other functions. * Fix potentially wrong dirtying in oled_write_char() oled_write_char() tried to mark the position just beyond the written character as dirty; use (OLED_FONT_WIDTH - 1) to dirty the last position still belonging to the character instead. * Fix `#define OLED_BLOCK_TYPE uint32_t` on AVR Using uint32_t as OLED_BLOCK_TYPE did not work properly on AVR, because some bit shifts were performed using 16-bit int. Add explicit casts to OLED_BLOCK_TYPE to those shifts.
-rw-r--r--drivers/oled/oled_driver.c48
1 files changed, 31 insertions, 17 deletions
diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c
index 0e15c3c4b..0b24a987d 100644
--- a/drivers/oled/oled_driver.c
+++ b/drivers/oled/oled_driver.c
@@ -75,8 +75,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
75#define CHARGE_PUMP 0x8D 75#define CHARGE_PUMP 0x8D
76 76
77// Misc defines 77// Misc defines
78#define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) 78#ifndef OLED_BLOCK_COUNT
79#define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) 79# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8)
80#endif
81#ifndef OLED_BLOCK_SIZE
82# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)
83#endif
84
85#define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1)
80 86
81// i2c defines 87// i2c defines
82#define I2C_CMD 0x00 88#define I2C_CMD 0x00
@@ -212,7 +218,7 @@ __attribute__((weak)) oled_rotation_t oled_init_user(oled_rotation_t rotation) {
212void oled_clear(void) { 218void oled_clear(void) {
213 memset(oled_buffer, 0, sizeof(oled_buffer)); 219 memset(oled_buffer, 0, sizeof(oled_buffer));
214 oled_cursor = &oled_buffer[0]; 220 oled_cursor = &oled_buffer[0];
215 oled_dirty = -1; // -1 will be max value as long as display_dirty is unsigned type 221 oled_dirty = OLED_ALL_BLOCKS_MASK;
216} 222}
217 223
218static void calc_bounds(uint8_t update_start, uint8_t *cmd_array) { 224static void calc_bounds(uint8_t update_start, uint8_t *cmd_array) {
@@ -262,13 +268,14 @@ static void rotate_90(const uint8_t *src, uint8_t *dest) {
262 268
263void oled_render(void) { 269void oled_render(void) {
264 // Do we have work to do? 270 // Do we have work to do?
271 oled_dirty &= OLED_ALL_BLOCKS_MASK;
265 if (!oled_dirty || oled_scrolling) { 272 if (!oled_dirty || oled_scrolling) {
266 return; 273 return;
267 } 274 }
268 275
269 // Find first dirty block 276 // Find first dirty block
270 uint8_t update_start = 0; 277 uint8_t update_start = 0;
271 while (!(oled_dirty & (1 << update_start))) { 278 while (!(oled_dirty & ((OLED_BLOCK_TYPE)1 << update_start))) {
272 ++update_start; 279 ++update_start;
273 } 280 }
274 281
@@ -314,7 +321,7 @@ void oled_render(void) {
314 oled_on(); 321 oled_on();
315 322
316 // Clear dirty flag 323 // Clear dirty flag
317 oled_dirty &= ~(1 << update_start); 324 oled_dirty &= ~((OLED_BLOCK_TYPE)1 << update_start);
318} 325}
319 326
320void oled_set_cursor(uint8_t col, uint8_t line) { 327void oled_set_cursor(uint8_t col, uint8_t line) {
@@ -404,9 +411,9 @@ void oled_write_char(const char data, bool invert) {
404 // Dirty check 411 // Dirty check
405 if (memcmp(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH)) { 412 if (memcmp(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH)) {
406 uint16_t index = oled_cursor - &oled_buffer[0]; 413 uint16_t index = oled_cursor - &oled_buffer[0];
407 oled_dirty |= (1 << (index / OLED_BLOCK_SIZE)); 414 oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE));
408 // Edgecase check if the written data spans the 2 chunks 415 // Edgecase check if the written data spans the 2 chunks
409 oled_dirty |= (1 << ((index + OLED_FONT_WIDTH) / OLED_BLOCK_SIZE)); 416 oled_dirty |= ((OLED_BLOCK_TYPE)1 << ((index + OLED_FONT_WIDTH - 1) / OLED_BLOCK_SIZE));
410 } 417 }
411 418
412 // Finally move to the next char 419 // Finally move to the next char
@@ -441,7 +448,7 @@ void oled_pan(bool left) {
441 } 448 }
442 } 449 }
443 } 450 }
444 oled_dirty = ~((OLED_BLOCK_TYPE)0); 451 oled_dirty = OLED_ALL_BLOCKS_MASK;
445} 452}
446 453
447oled_buffer_reader_t oled_read_raw(uint16_t start_index) { 454oled_buffer_reader_t oled_read_raw(uint16_t start_index) {
@@ -456,7 +463,7 @@ void oled_write_raw_byte(const char data, uint16_t index) {
456 if (index > OLED_MATRIX_SIZE) index = OLED_MATRIX_SIZE; 463 if (index > OLED_MATRIX_SIZE) index = OLED_MATRIX_SIZE;
457 if (oled_buffer[index] == data) return; 464 if (oled_buffer[index] == data) return;
458 oled_buffer[index] = data; 465 oled_buffer[index] = data;
459 oled_dirty |= (1 << (index / OLED_BLOCK_SIZE)); 466 oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE));
460} 467}
461 468
462void oled_write_raw(const char *data, uint16_t size) { 469void oled_write_raw(const char *data, uint16_t size) {
@@ -464,21 +471,28 @@ void oled_write_raw(const char *data, uint16_t size) {
464 for (uint16_t i = 0; i < size; i++) { 471 for (uint16_t i = 0; i < size; i++) {
465 if (oled_buffer[i] == data[i]) continue; 472 if (oled_buffer[i] == data[i]) continue;
466 oled_buffer[i] = data[i]; 473 oled_buffer[i] = data[i];
467 oled_dirty |= (1 << (i / OLED_BLOCK_SIZE)); 474 oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE));
468 } 475 }
469} 476}
470 477
471void oled_write_pixel(uint8_t x, uint8_t y, bool on) { 478void oled_write_pixel(uint8_t x, uint8_t y, bool on) {
472 if (x >= OLED_DISPLAY_WIDTH || y >= OLED_DISPLAY_HEIGHT) { 479 if (x >= oled_rotation_width) {
473 return; 480 return;
474 } 481 }
475 uint16_t index = x + (y / 8) * OLED_DISPLAY_WIDTH; 482 uint16_t index = x + (y / 8) * oled_rotation_width;
483 if (index >= OLED_MATRIX_SIZE) {
484 return;
485 }
486 uint8_t data = oled_buffer[index];
476 if (on) { 487 if (on) {
477 oled_buffer[index] |= (1 << (y % 8)); 488 data |= (1 << (y % 8));
478 } else { 489 } else {
479 oled_buffer[index] &= ~(1 << (y % 8)); 490 data &= ~(1 << (y % 8));
491 }
492 if (oled_buffer[index] != data) {
493 oled_buffer[index] = data;
494 oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE));
480 } 495 }
481 oled_dirty |= (1 << (index / OLED_BLOCK_SIZE));
482} 496}
483 497
484#if defined(__AVR__) 498#if defined(__AVR__)
@@ -501,7 +515,7 @@ void oled_write_raw_P(const char *data, uint16_t size) {
501 uint8_t c = pgm_read_byte(data++); 515 uint8_t c = pgm_read_byte(data++);
502 if (oled_buffer[i] == c) continue; 516 if (oled_buffer[i] == c) continue;
503 oled_buffer[i] = c; 517 oled_buffer[i] = c;
504 oled_dirty |= (1 << (i / OLED_BLOCK_SIZE)); 518 oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE));
505 } 519 }
506} 520}
507#endif // defined(__AVR__) 521#endif // defined(__AVR__)
@@ -597,7 +611,7 @@ bool oled_scroll_off(void) {
597 return oled_scrolling; 611 return oled_scrolling;
598 } 612 }
599 oled_scrolling = false; 613 oled_scrolling = false;
600 oled_dirty = -1; 614 oled_dirty = OLED_ALL_BLOCKS_MASK;
601 } 615 }
602 return !oled_scrolling; 616 return !oled_scrolling;
603} 617}