|
@@ -0,0 +1,452 @@
|
|
|
+
|
|
|
+ *
|
|
|
+ * This program is free software: you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
|
+ * the Free Software Foundation, either version 2 of the License, or
|
|
|
+ * (at your option) any later version.
|
|
|
+ *
|
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ * GNU General Public License for more details.
|
|
|
+ *
|
|
|
+ * You should have received a copy of the GNU General Public License
|
|
|
+ * along with this program. If not, see <http:
|
|
|
+ */
|
|
|
+
|
|
|
+#include QMK_KEYBOARD_H
|
|
|
+
|
|
|
+enum tap_dances {
|
|
|
+ TD_OLED,
|
|
|
+};
|
|
|
+
|
|
|
+enum oled_test_modes {
|
|
|
+
|
|
|
+ TEST_FIRST,
|
|
|
+ TEST_LOGO = TEST_FIRST,
|
|
|
+ TEST_CHARACTERS,
|
|
|
+ TEST_SLOW_UPDATE,
|
|
|
+ TEST_ALL_ON,
|
|
|
+ TEST_FRAME,
|
|
|
+ TEST_ALL_OFF,
|
|
|
+ TEST_FILL_HORZ_0,
|
|
|
+ TEST_FILL_HORZ_1,
|
|
|
+ TEST_FILL_VERT_0,
|
|
|
+ TEST_FILL_VERT_1,
|
|
|
+ TEST_FILL_CHECKERBOARD_1,
|
|
|
+ TEST_FILL_CHECKERBOARD_2,
|
|
|
+ TEST_FILL_CHECKERBOARD_4,
|
|
|
+ TEST_LAST = TEST_FILL_CHECKERBOARD_4,
|
|
|
+
|
|
|
+
|
|
|
+ TEST_DRAW_ALWAYS_ON,
|
|
|
+ TEST_DRAW_ALWAYS_OFF,
|
|
|
+};
|
|
|
+
|
|
|
+static enum oled_test_modes test_mode = TEST_FIRST;
|
|
|
+
|
|
|
+static oled_rotation_t rotation = OLED_ROTATION_0;
|
|
|
+
|
|
|
+static bool scrolling;
|
|
|
+static uint8_t scrolling_speed;
|
|
|
+static bool need_update = true;
|
|
|
+static bool draw_always;
|
|
|
+static bool update_speed_test;
|
|
|
+static uint32_t update_speed_start_timer;
|
|
|
+static uint16_t update_speed_count;
|
|
|
+static bool restart_test;
|
|
|
+
|
|
|
+static void stop_scrolling(void) {
|
|
|
+ if (scrolling) {
|
|
|
+ oled_scroll_off();
|
|
|
+ scrolling = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void dance_oled_finished(qk_tap_dance_state_t *state, void *user_data) {
|
|
|
+ switch (state->count) {
|
|
|
+ case 1:
|
|
|
+ if (state->pressed) {
|
|
|
+
|
|
|
+ switch (rotation) {
|
|
|
+ case OLED_ROTATION_0:
|
|
|
+ rotation = OLED_ROTATION_90;
|
|
|
+ break;
|
|
|
+ case OLED_ROTATION_90:
|
|
|
+ rotation = OLED_ROTATION_180;
|
|
|
+ break;
|
|
|
+ case OLED_ROTATION_180:
|
|
|
+ rotation = OLED_ROTATION_270;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ rotation = OLED_ROTATION_0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ stop_scrolling();
|
|
|
+ oled_init(rotation);
|
|
|
+ } else {
|
|
|
+
|
|
|
+ if (test_mode < TEST_LAST) {
|
|
|
+ ++test_mode;
|
|
|
+ } else {
|
|
|
+ test_mode = TEST_FIRST;
|
|
|
+ }
|
|
|
+ stop_scrolling();
|
|
|
+ oled_clear();
|
|
|
+ }
|
|
|
+ restart_test = true;
|
|
|
+ need_update = true;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 2:
|
|
|
+ if (state->pressed) {
|
|
|
+
|
|
|
+ scrolling_speed = (scrolling_speed + 1) % 8;
|
|
|
+ stop_scrolling();
|
|
|
+ oled_scroll_set_speed(scrolling_speed);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ if (!scrolling) {
|
|
|
+ scrolling = true;
|
|
|
+ oled_scroll_left();
|
|
|
+ } else {
|
|
|
+ scrolling = false;
|
|
|
+ oled_scroll_off();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ need_update = true;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 3:
|
|
|
+ if (state->pressed) {
|
|
|
+
|
|
|
+ draw_always = !draw_always;
|
|
|
+ if (draw_always) {
|
|
|
+ test_mode = TEST_DRAW_ALWAYS_ON;
|
|
|
+ } else {
|
|
|
+ test_mode = TEST_DRAW_ALWAYS_OFF;
|
|
|
+ }
|
|
|
+ stop_scrolling();
|
|
|
+ oled_clear();
|
|
|
+ restart_test = true;
|
|
|
+ need_update = true;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ update_speed_test = !update_speed_test;
|
|
|
+ if (update_speed_test) {
|
|
|
+ stop_scrolling();
|
|
|
+ update_speed_start_timer = timer_read32();
|
|
|
+ update_speed_count = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+qk_tap_dance_action_t tap_dance_actions[] = {[TD_OLED] = ACTION_TAP_DANCE_FN(dance_oled_finished)};
|
|
|
+
|
|
|
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {LAYOUT_ortho_1x1(TD(TD_OLED))};
|
|
|
+
|
|
|
+
|
|
|
+extern OLED_BLOCK_TYPE oled_dirty;
|
|
|
+
|
|
|
+static inline uint8_t pixel_width(void) {
|
|
|
+ if (!(rotation & OLED_ROTATION_90)) {
|
|
|
+ return OLED_DISPLAY_WIDTH;
|
|
|
+ }
|
|
|
+ return OLED_DISPLAY_HEIGHT;
|
|
|
+}
|
|
|
+
|
|
|
+static inline uint8_t pixel_height(void) {
|
|
|
+ if (!(rotation & OLED_ROTATION_90)) {
|
|
|
+ return OLED_DISPLAY_HEIGHT;
|
|
|
+ }
|
|
|
+ return OLED_DISPLAY_WIDTH;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void test_logo(void) {
|
|
|
+ uint8_t lines = oled_max_lines();
|
|
|
+ if (lines > 3) {
|
|
|
+ lines = 3;
|
|
|
+ }
|
|
|
+ uint8_t chars = oled_max_chars();
|
|
|
+ if (chars > 21) {
|
|
|
+ chars = 21;
|
|
|
+ }
|
|
|
+ for (uint8_t row = 0; row < lines; ++row) {
|
|
|
+ oled_set_cursor(0, row);
|
|
|
+ for (uint8_t col = 0; col < chars; ++col) {
|
|
|
+ oled_write_char(0x80 + 0x20 * row + col, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static const PROGMEM char fill_ff[OLED_MATRIX_SIZE] = {[0 ... OLED_MATRIX_SIZE - 1] = 0xff};
|
|
|
+
|
|
|
+
|
|
|
+static void test_fill(uint8_t byte0, uint8_t byte1, uint8_t repeats) {
|
|
|
+ uint8_t width = pixel_width();
|
|
|
+ uint8_t lines = oled_max_lines();
|
|
|
+ uint16_t index = 0;
|
|
|
+ for (uint8_t row = 0; row < lines; ++row) {
|
|
|
+ for (uint8_t col = 0; col < width; ++col) {
|
|
|
+ uint8_t byte = ((col / repeats) % 2) ? byte1 : byte0;
|
|
|
+ oled_write_raw_byte(byte, index++);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void test_frame(void) {
|
|
|
+ uint8_t width = pixel_width();
|
|
|
+ uint8_t height = pixel_height();
|
|
|
+ for (uint8_t x = 0; x < width; ++x) {
|
|
|
+ oled_write_pixel(x, 0, true);
|
|
|
+ oled_write_pixel(x, height - 1, true);
|
|
|
+ }
|
|
|
+ for (uint8_t y = 1; y < height - 1; ++y) {
|
|
|
+ oled_write_pixel(0, y, true);
|
|
|
+ oled_write_pixel(width - 1, y, true);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#define TEST_CHAR_COUNT ('~' - '!' + 1)
|
|
|
+
|
|
|
+static char get_test_char(uint8_t char_index) { return char_index + '!'; }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+static void test_characters(void) {
|
|
|
+ uint8_t cols = oled_max_chars();
|
|
|
+ uint8_t rows = oled_max_lines();
|
|
|
+ bool invert = false;
|
|
|
+ uint8_t char_index = 0;
|
|
|
+ for (uint8_t row = 0; row < rows; ++row) {
|
|
|
+ for (uint8_t col = 0; col < cols; ++col) {
|
|
|
+ oled_write_char(get_test_char(char_index), invert);
|
|
|
+ if (++char_index >= TEST_CHAR_COUNT) {
|
|
|
+ char_index = 0;
|
|
|
+ invert = !invert;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void test_slow_update(void) {
|
|
|
+ static uint8_t phase, x, y, char_index, first_char;
|
|
|
+ static uint16_t timer;
|
|
|
+ static uint16_t delay = 500;
|
|
|
+
|
|
|
+ if (restart_test) {
|
|
|
+
|
|
|
+ restart_test = false;
|
|
|
+ phase = 0;
|
|
|
+ x = 0;
|
|
|
+ y = 0;
|
|
|
+ char_index = 0;
|
|
|
+ first_char = 0;
|
|
|
+ delay = 500;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ if (timer_elapsed(timer) < delay) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ timer = timer_read();
|
|
|
+ switch (phase) {
|
|
|
+ case 0:
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ oled_set_cursor(x, y);
|
|
|
+ oled_write_char(get_test_char(char_index), false);
|
|
|
+ if (++char_index >= TEST_CHAR_COUNT) {
|
|
|
+ char_index = 0;
|
|
|
+ }
|
|
|
+ if (++x >= oled_max_chars()) {
|
|
|
+ x = 0;
|
|
|
+ if (++y >= oled_max_lines()) {
|
|
|
+
|
|
|
+ ++phase;
|
|
|
+ x = y = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ delay = 250;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 1:
|
|
|
+
|
|
|
+ oled_write_pixel(x, y, true);
|
|
|
+ if (y < pixel_height() - 1) {
|
|
|
+ ++y;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ ++phase;
|
|
|
+ ++x;
|
|
|
+ }
|
|
|
+ delay = 50;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 2:
|
|
|
+
|
|
|
+ oled_write_pixel(x, y, true);
|
|
|
+ if (x < pixel_width() - 1) {
|
|
|
+ ++x;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ ++phase;
|
|
|
+ --y;
|
|
|
+ }
|
|
|
+ delay = 50;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 3:
|
|
|
+
|
|
|
+ oled_write_pixel(x, y, true);
|
|
|
+ if (y > 0) {
|
|
|
+ --y;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ ++phase;
|
|
|
+ --x;
|
|
|
+ }
|
|
|
+ delay = 50;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 4:
|
|
|
+
|
|
|
+ oled_write_pixel(x, y, true);
|
|
|
+ if (x > 0) {
|
|
|
+ --x;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ ++phase;
|
|
|
+ }
|
|
|
+ delay = 50;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+
|
|
|
+ if (++first_char >= TEST_CHAR_COUNT) {
|
|
|
+ first_char = 0;
|
|
|
+ }
|
|
|
+ phase = 0;
|
|
|
+ x = 0;
|
|
|
+ y = 0;
|
|
|
+ char_index = first_char;
|
|
|
+ delay = 500;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+oled_rotation_t oled_init_user(oled_rotation_t rotation) {
|
|
|
+ oled_scroll_set_area(0, 0);
|
|
|
+ oled_scroll_set_speed(scrolling_speed);
|
|
|
+ return rotation;
|
|
|
+}
|
|
|
+
|
|
|
+void oled_task_user(void) {
|
|
|
+ if (update_speed_test) {
|
|
|
+
|
|
|
+ if (!oled_dirty) {
|
|
|
+
|
|
|
+ update_speed_count++;
|
|
|
+ if (update_speed_count % 256 == 0) {
|
|
|
+ uprintf("OLED: %u updates, %lu ms\n", update_speed_count, timer_elapsed32(update_speed_start_timer));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (test_mode == TEST_ALL_ON) {
|
|
|
+ test_mode = TEST_ALL_OFF;
|
|
|
+ } else {
|
|
|
+ test_mode = TEST_ALL_ON;
|
|
|
+ }
|
|
|
+ need_update = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if (!draw_always || update_speed_test) {
|
|
|
+
|
|
|
+
|
|
|
+ if (!need_update) {
|
|
|
+ if (test_mode != TEST_SLOW_UPDATE) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ need_update = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (test_mode) {
|
|
|
+ case TEST_LOGO:
|
|
|
+ test_logo();
|
|
|
+ break;
|
|
|
+ case TEST_CHARACTERS:
|
|
|
+ test_characters();
|
|
|
+ break;
|
|
|
+ case TEST_SLOW_UPDATE:
|
|
|
+ test_slow_update();
|
|
|
+ break;
|
|
|
+ case TEST_ALL_ON:
|
|
|
+ oled_write_raw_P(fill_ff, sizeof(fill_ff));
|
|
|
+ break;
|
|
|
+ case TEST_FRAME:
|
|
|
+ test_frame();
|
|
|
+ break;
|
|
|
+ case TEST_ALL_OFF:
|
|
|
+
|
|
|
+
|
|
|
+ if (update_speed_test) {
|
|
|
+ oled_clear();
|
|
|
+ } else {
|
|
|
+ test_fill(0x00, 0x00, 1);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case TEST_FILL_HORZ_0:
|
|
|
+ test_fill(0x55, 0x55, 1);
|
|
|
+ break;
|
|
|
+ case TEST_FILL_HORZ_1:
|
|
|
+ test_fill(0xaa, 0xaa, 1);
|
|
|
+ break;
|
|
|
+ case TEST_FILL_VERT_0:
|
|
|
+ test_fill(0xff, 0x00, 1);
|
|
|
+ break;
|
|
|
+ case TEST_FILL_VERT_1:
|
|
|
+ test_fill(0x00, 0xff, 1);
|
|
|
+ break;
|
|
|
+ case TEST_FILL_CHECKERBOARD_1:
|
|
|
+ test_fill(0x55, 0xaa, 1);
|
|
|
+ break;
|
|
|
+ case TEST_FILL_CHECKERBOARD_2:
|
|
|
+ test_fill(0x33, 0xcc, 2);
|
|
|
+ break;
|
|
|
+ case TEST_FILL_CHECKERBOARD_4:
|
|
|
+ test_fill(0x0f, 0xf0, 4);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case TEST_DRAW_ALWAYS_ON:
|
|
|
+ oled_write_P(PSTR("Draw Always"), false);
|
|
|
+ break;
|
|
|
+ case TEST_DRAW_ALWAYS_OFF:
|
|
|
+ oled_write_P(PSTR("Draw Once"), false);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void keyboard_post_init_user(void) {
|
|
|
+
|
|
|
+ debug_enable = true;
|
|
|
+}
|