aboutsummaryrefslogtreecommitdiff
path: root/drivers/lcd
diff options
context:
space:
mode:
authorRyan <fauxpark@gmail.com>2021-06-10 17:16:09 +1000
committerGitHub <noreply@github.com>2021-06-10 17:16:09 +1000
commitb2fdd4874434ef6921a436fc82d9f24909c726f8 (patch)
tree0348596d5938326e95c09270d209556bbe60ba72 /drivers/lcd
parentcfc7ee61c5cb9822a1195028681b928bbeac2fd3 (diff)
downloadqmk_firmware-b2fdd4874434ef6921a436fc82d9f24909c726f8.tar.gz
qmk_firmware-b2fdd4874434ef6921a436fc82d9f24909c726f8.zip
Add ST7565 LCD driver (#13089)
Co-authored-by: Joakim Tufvegren <jocke@barbanet.com>
Diffstat (limited to 'drivers/lcd')
-rw-r--r--drivers/lcd/st7565.c479
-rw-r--r--drivers/lcd/st7565.h215
2 files changed, 694 insertions, 0 deletions
diff --git a/drivers/lcd/st7565.c b/drivers/lcd/st7565.c
new file mode 100644
index 000000000..4b4891ce7
--- /dev/null
+++ b/drivers/lcd/st7565.c
@@ -0,0 +1,479 @@
1/*
2Copyright 2021
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include "st7565.h"
19
20#include <string.h>
21
22#include "keyboard.h"
23#include "progmem.h"
24#include "timer.h"
25#include "wait.h"
26
27#include ST7565_FONT_H
28
29// Fundamental Commands
30#define CONTRAST 0x81
31#define DISPLAY_ALL_ON 0xA5
32#define DISPLAY_ALL_ON_RESUME 0xA4
33#define NORMAL_DISPLAY 0xA6
34#define DISPLAY_ON 0xAF
35#define DISPLAY_OFF 0xAE
36#define NOP 0xE3
37
38// Addressing Setting Commands
39#define PAM_SETCOLUMN_LSB 0x00
40#define PAM_SETCOLUMN_MSB 0x10
41#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7
42
43// Hardware Configuration Commands
44#define DISPLAY_START_LINE 0x40
45#define SEGMENT_REMAP 0xA0
46#define SEGMENT_REMAP_INV 0xA1
47#define COM_SCAN_INC 0xC0
48#define COM_SCAN_DEC 0xC8
49#define LCD_BIAS_7 0xA3
50#define LCD_BIAS_9 0xA2
51#define RESISTOR_RATIO 0x20
52#define POWER_CONTROL 0x28
53
54// Misc defines
55#ifndef ST7565_BLOCK_COUNT
56# define ST7565_BLOCK_COUNT (sizeof(ST7565_BLOCK_TYPE) * 8)
57#endif
58#ifndef ST7565_BLOCK_SIZE
59# define ST7565_BLOCK_SIZE (ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT)
60#endif
61
62#define ST7565_ALL_BLOCKS_MASK (((((ST7565_BLOCK_TYPE)1 << (ST7565_BLOCK_COUNT - 1)) - 1) << 1) | 1)
63
64#define HAS_FLAGS(bits, flags) ((bits & flags) == flags)
65
66// Display buffer's is the same as the display memory layout
67// this is so we don't end up with rounding errors with
68// parts of the display unusable or don't get cleared correctly
69// and also allows for drawing & inverting
70uint8_t st7565_buffer[ST7565_MATRIX_SIZE];
71uint8_t * st7565_cursor;
72ST7565_BLOCK_TYPE st7565_dirty = 0;
73bool st7565_initialized = false;
74bool st7565_active = false;
75display_rotation_t st7565_rotation = DISPLAY_ROTATION_0;
76#if ST7565_TIMEOUT > 0
77uint32_t st7565_timeout;
78#endif
79#if ST7565_UPDATE_INTERVAL > 0
80uint16_t st7565_update_timeout;
81#endif
82
83// Flips the rendering bits for a character at the current cursor position
84static void InvertCharacter(uint8_t *cursor) {
85 const uint8_t *end = cursor + ST7565_FONT_WIDTH;
86 while (cursor < end) {
87 *cursor = ~(*cursor);
88 cursor++;
89 }
90}
91
92bool st7565_init(display_rotation_t rotation) {
93 setPinOutput(ST7565_A0_PIN);
94 writePinHigh(ST7565_A0_PIN);
95 setPinOutput(ST7565_RST_PIN);
96 writePinHigh(ST7565_RST_PIN);
97
98 st7565_rotation = st7565_init_user(rotation);
99
100 spi_init();
101 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
102
103 st7565_reset();
104
105 st7565_send_cmd(LCD_BIAS_7);
106 if (!HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) {
107 st7565_send_cmd(SEGMENT_REMAP);
108 st7565_send_cmd(COM_SCAN_DEC);
109 } else {
110 st7565_send_cmd(SEGMENT_REMAP_INV);
111 st7565_send_cmd(COM_SCAN_INC);
112 }
113 st7565_send_cmd(DISPLAY_START_LINE | 0x00);
114 st7565_send_cmd(CONTRAST);
115 st7565_send_cmd(ST7565_CONTRAST);
116 st7565_send_cmd(RESISTOR_RATIO | 0x01);
117 st7565_send_cmd(POWER_CONTROL | 0x04);
118 wait_ms(50);
119 st7565_send_cmd(POWER_CONTROL | 0x06);
120 wait_ms(50);
121 st7565_send_cmd(POWER_CONTROL | 0x07);
122 wait_ms(10);
123 st7565_send_cmd(DISPLAY_ON);
124 st7565_send_cmd(DISPLAY_ALL_ON_RESUME);
125 st7565_send_cmd(NORMAL_DISPLAY);
126
127 spi_stop();
128
129#if ST7565_TIMEOUT > 0
130 st7565_timeout = timer_read32() + ST7565_TIMEOUT;
131#endif
132
133 st7565_clear();
134 st7565_initialized = true;
135 st7565_active = true;
136 return true;
137}
138
139__attribute__((weak)) display_rotation_t st7565_init_user(display_rotation_t rotation) { return rotation; }
140
141void st7565_clear(void) {
142 memset(st7565_buffer, 0, sizeof(st7565_buffer));
143 st7565_cursor = &st7565_buffer[0];
144 st7565_dirty = ST7565_ALL_BLOCKS_MASK;
145}
146
147uint8_t crot(uint8_t a, int8_t n) {
148 const uint8_t mask = 0x7;
149 n &= mask;
150 return a << n | a >> (-n & mask);
151}
152
153void st7565_render(void) {
154 if (!st7565_initialized) {
155 return;
156 }
157
158 // Do we have work to do?
159 st7565_dirty &= ST7565_ALL_BLOCKS_MASK;
160 if (!st7565_dirty) {
161 return;
162 }
163
164 // Find first dirty block
165 uint8_t update_start = 0;
166 while (!(st7565_dirty & ((ST7565_BLOCK_TYPE)1 << update_start))) {
167 ++update_start;
168 }
169
170 // Calculate commands to set memory addressing bounds.
171 uint8_t start_page = ST7565_BLOCK_SIZE * update_start / ST7565_DISPLAY_WIDTH;
172 uint8_t start_column = ST7565_BLOCK_SIZE * update_start % ST7565_DISPLAY_WIDTH;
173 // IC has 132 segment drivers, for panels with less width we need to offset the starting column
174 if (HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) {
175 start_column += (132 - ST7565_DISPLAY_WIDTH);
176 }
177
178 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
179
180 st7565_send_cmd(PAM_PAGE_ADDR | start_page);
181 st7565_send_cmd(PAM_SETCOLUMN_LSB | ((ST7565_COLUMN_OFFSET + start_column) & 0x0f));
182 st7565_send_cmd(PAM_SETCOLUMN_MSB | ((ST7565_COLUMN_OFFSET + start_column) >> 4 & 0x0f));
183
184 st7565_send_data(&st7565_buffer[ST7565_BLOCK_SIZE * update_start], ST7565_BLOCK_SIZE);
185
186 // Turn on display if it is off
187 st7565_on();
188
189 // Clear dirty flag
190 st7565_dirty &= ~((ST7565_BLOCK_TYPE)1 << update_start);
191}
192
193void st7565_set_cursor(uint8_t col, uint8_t line) {
194 uint16_t index = line * ST7565_DISPLAY_WIDTH + col * ST7565_FONT_WIDTH;
195
196 // Out of bounds?
197 if (index >= ST7565_MATRIX_SIZE) {
198 index = 0;
199 }
200
201 st7565_cursor = &st7565_buffer[index];
202}
203
204void st7565_advance_page(bool clearPageRemainder) {
205 uint16_t index = st7565_cursor - &st7565_buffer[0];
206 uint8_t remaining = ST7565_DISPLAY_WIDTH - (index % ST7565_DISPLAY_WIDTH);
207
208 if (clearPageRemainder) {
209 // Remaining Char count
210 remaining = remaining / ST7565_FONT_WIDTH;
211
212 // Write empty character until next line
213 while (remaining--) st7565_write_char(' ', false);
214 } else {
215 // Next page index out of bounds?
216 if (index + remaining >= ST7565_MATRIX_SIZE) {
217 index = 0;
218 remaining = 0;
219 }
220
221 st7565_cursor = &st7565_buffer[index + remaining];
222 }
223}
224
225void st7565_advance_char(void) {
226 uint16_t nextIndex = st7565_cursor - &st7565_buffer[0] + ST7565_FONT_WIDTH;
227 uint8_t remainingSpace = ST7565_DISPLAY_WIDTH - (nextIndex % ST7565_DISPLAY_WIDTH);
228
229 // Do we have enough space on the current line for the next character
230 if (remainingSpace < ST7565_FONT_WIDTH) {
231 nextIndex += remainingSpace;
232 }
233
234 // Did we go out of bounds
235 if (nextIndex >= ST7565_MATRIX_SIZE) {
236 nextIndex = 0;
237 }
238
239 // Update cursor position
240 st7565_cursor = &st7565_buffer[nextIndex];
241}
242
243// Main handler that writes character data to the display buffer
244void st7565_write_char(const char data, bool invert) {
245 // Advance to the next line if newline
246 if (data == '\n') {
247 // Old source wrote ' ' until end of line...
248 st7565_advance_page(true);
249 return;
250 }
251
252 if (data == '\r') {
253 st7565_advance_page(false);
254 return;
255 }
256
257 // copy the current render buffer to check for dirty after
258 static uint8_t st7565_temp_buffer[ST7565_FONT_WIDTH];
259 memcpy(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH);
260
261 _Static_assert(sizeof(font) >= ((ST7565_FONT_END + 1 - ST7565_FONT_START) * ST7565_FONT_WIDTH), "ST7565_FONT_END references outside array");
262
263 // set the reder buffer data
264 uint8_t cast_data = (uint8_t)data; // font based on unsigned type for index
265 if (cast_data < ST7565_FONT_START || cast_data > ST7565_FONT_END) {
266 memset(st7565_cursor, 0x00, ST7565_FONT_WIDTH);
267 } else {
268 const uint8_t *glyph = &font[(cast_data - ST7565_FONT_START) * ST7565_FONT_WIDTH];
269 memcpy_P(st7565_cursor, glyph, ST7565_FONT_WIDTH);
270 }
271
272 // Invert if needed
273 if (invert) {
274 InvertCharacter(st7565_cursor);
275 }
276
277 // Dirty check
278 if (memcmp(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH)) {
279 uint16_t index = st7565_cursor - &st7565_buffer[0];
280 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));
281 // Edgecase check if the written data spans the 2 chunks
282 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << ((index + ST7565_FONT_WIDTH - 1) / ST7565_BLOCK_SIZE));
283 }
284
285 // Finally move to the next char
286 st7565_advance_char();
287}
288
289void st7565_write(const char *data, bool invert) {
290 const char *end = data + strlen(data);
291 while (data < end) {
292 st7565_write_char(*data, invert);
293 data++;
294 }
295}
296
297void st7565_write_ln(const char *data, bool invert) {
298 st7565_write(data, invert);
299 st7565_advance_page(true);
300}
301
302void st7565_pan(bool left) {
303 uint16_t i = 0;
304 for (uint16_t y = 0; y < ST7565_DISPLAY_HEIGHT / 8; y++) {
305 if (left) {
306 for (uint16_t x = 0; x < ST7565_DISPLAY_WIDTH - 1; x++) {
307 i = y * ST7565_DISPLAY_WIDTH + x;
308 st7565_buffer[i] = st7565_buffer[i + 1];
309 }
310 } else {
311 for (uint16_t x = ST7565_DISPLAY_WIDTH - 1; x > 0; x--) {
312 i = y * ST7565_DISPLAY_WIDTH + x;
313 st7565_buffer[i] = st7565_buffer[i - 1];
314 }
315 }
316 }
317 st7565_dirty = ST7565_ALL_BLOCKS_MASK;
318}
319
320display_buffer_reader_t st7565_read_raw(uint16_t start_index) {
321 if (start_index > ST7565_MATRIX_SIZE) start_index = ST7565_MATRIX_SIZE;
322 display_buffer_reader_t ret_reader;
323 ret_reader.current_element = &st7565_buffer[start_index];
324 ret_reader.remaining_element_count = ST7565_MATRIX_SIZE - start_index;
325 return ret_reader;
326}
327
328void st7565_write_raw_byte(const char data, uint16_t index) {
329 if (index > ST7565_MATRIX_SIZE) index = ST7565_MATRIX_SIZE;
330 if (st7565_buffer[index] == data) return;
331 st7565_buffer[index] = data;
332 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));
333}
334
335void st7565_write_raw(const char *data, uint16_t size) {
336 uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0];
337 if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index;
338 for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {
339 if (st7565_buffer[i] == data[i]) continue;
340 st7565_buffer[i] = data[i];
341 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE));
342 }
343}
344
345void st7565_write_pixel(uint8_t x, uint8_t y, bool on) {
346 if (x >= ST7565_DISPLAY_WIDTH) {
347 return;
348 }
349 uint16_t index = x + (y / 8) * ST7565_DISPLAY_WIDTH;
350 if (index >= ST7565_MATRIX_SIZE) {
351 return;
352 }
353 uint8_t data = st7565_buffer[index];
354 if (on) {
355 data |= (1 << (y % 8));
356 } else {
357 data &= ~(1 << (y % 8));
358 }
359 if (st7565_buffer[index] != data) {
360 st7565_buffer[index] = data;
361 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));
362 }
363}
364
365#if defined(__AVR__)
366void st7565_write_P(const char *data, bool invert) {
367 uint8_t c = pgm_read_byte(data);
368 while (c != 0) {
369 st7565_write_char(c, invert);
370 c = pgm_read_byte(++data);
371 }
372}
373
374void st7565_write_ln_P(const char *data, bool invert) {
375 st7565_write_P(data, invert);
376 st7565_advance_page(true);
377}
378
379void st7565_write_raw_P(const char *data, uint16_t size) {
380 uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0];
381 if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index;
382 for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {
383 uint8_t c = pgm_read_byte(data++);
384 if (st7565_buffer[i] == c) continue;
385 st7565_buffer[i] = c;
386 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE));
387 }
388}
389#endif // defined(__AVR__)
390
391bool st7565_on(void) {
392 if (!st7565_initialized) {
393 return st7565_active;
394 }
395
396#if ST7565_TIMEOUT > 0
397 st7565_timeout = timer_read32() + ST7565_TIMEOUT;
398#endif
399
400 if (!st7565_active) {
401 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
402 st7565_send_cmd(DISPLAY_ON);
403 spi_stop();
404 st7565_active = true;
405 st7565_on_user();
406 }
407 return st7565_active;
408}
409
410__attribute__((weak)) void st7565_on_user(void) {}
411
412bool st7565_off(void) {
413 if (!st7565_initialized) {
414 return !st7565_active;
415 }
416
417 if (st7565_active) {
418 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
419 st7565_send_cmd(DISPLAY_OFF);
420 spi_stop();
421 st7565_active = false;
422 st7565_off_user();
423 }
424 return !st7565_active;
425}
426
427__attribute__((weak)) void st7565_off_user(void) {}
428
429bool st7565_is_on(void) { return st7565_active; }
430
431uint8_t st7565_max_chars(void) { return ST7565_DISPLAY_WIDTH / ST7565_FONT_WIDTH; }
432
433uint8_t st7565_max_lines(void) { return ST7565_DISPLAY_HEIGHT / ST7565_FONT_HEIGHT; }
434
435void st7565_task(void) {
436 if (!st7565_initialized) {
437 return;
438 }
439
440#if ST7565_UPDATE_INTERVAL > 0
441 if (timer_elapsed(st7565_update_timeout) >= ST7565_UPDATE_INTERVAL) {
442 st7565_update_timeout = timer_read();
443 st7565_set_cursor(0, 0);
444 st7565_task_user();
445 }
446#else
447 st7565_set_cursor(0, 0);
448 st7565_task_user();
449#endif
450
451 // Smart render system, no need to check for dirty
452 st7565_render();
453
454 // Display timeout check
455#if ST7565_TIMEOUT > 0
456 if (st7565_active && timer_expired32(timer_read32(), st7565_timeout)) {
457 st7565_off();
458 }
459#endif
460}
461
462__attribute__((weak)) void st7565_task_user(void) {}
463
464void st7565_reset(void) {
465 writePinLow(ST7565_RST_PIN);
466 wait_ms(20);
467 writePinHigh(ST7565_RST_PIN);
468 wait_ms(20);
469}
470
471spi_status_t st7565_send_cmd(uint8_t cmd) {
472 writePinLow(ST7565_A0_PIN);
473 return spi_write(cmd);
474}
475
476spi_status_t st7565_send_data(uint8_t *data, uint16_t length) {
477 writePinHigh(ST7565_A0_PIN);
478 return spi_transmit(data, length);
479}
diff --git a/drivers/lcd/st7565.h b/drivers/lcd/st7565.h
new file mode 100644
index 000000000..53cfc9a81
--- /dev/null
+++ b/drivers/lcd/st7565.h
@@ -0,0 +1,215 @@
1/*
2Copyright 2021
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#pragma once
19
20#include <stdint.h>
21#include <stdbool.h>
22
23#include "spi_master.h"
24
25#ifndef ST7565_DISPLAY_WIDTH
26# define ST7565_DISPLAY_WIDTH 128
27#endif
28#ifndef ST7565_DISPLAY_HEIGHT
29# define ST7565_DISPLAY_HEIGHT 32
30#endif
31#ifndef ST7565_MATRIX_SIZE
32# define ST7565_MATRIX_SIZE (ST7565_DISPLAY_HEIGHT / 8 * ST7565_DISPLAY_WIDTH) // 1024 (compile time mathed)
33#endif
34#ifndef ST7565_BLOCK_TYPE
35# define ST7565_BLOCK_TYPE uint16_t
36#endif
37#ifndef ST7565_BLOCK_COUNT
38# define ST7565_BLOCK_COUNT (sizeof(ST7565_BLOCK_TYPE) * 8) // 32 (compile time mathed)
39#endif
40#ifndef ST7565_BLOCK_SIZE
41# define ST7565_BLOCK_SIZE (ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT) // 32 (compile time mathed)
42#endif
43
44// the column address corresponding to the first column in the display hardware
45#if !defined(ST7565_COLUMN_OFFSET)
46# define ST7565_COLUMN_OFFSET 0
47#endif
48
49// spi clock divisor
50#if !defined(ST7565_SPI_CLK_DIVISOR)
51# define ST7565_SPI_CLK_DIVISOR 4
52#endif
53
54// Custom font file to use
55#if !defined(ST7565_FONT_H)
56# define ST7565_FONT_H "glcdfont.c"
57#endif
58// unsigned char value of the first character in the font file
59#if !defined(ST7565_FONT_START)
60# define ST7565_FONT_START 0
61#endif
62// unsigned char value of the last character in the font file
63#if !defined(ST7565_FONT_END)
64# define ST7565_FONT_END 223
65#endif
66// Font render width
67#if !defined(ST7565_FONT_WIDTH)
68# define ST7565_FONT_WIDTH 6
69#endif
70// Font render height
71#if !defined(ST7565_FONT_HEIGHT)
72# define ST7565_FONT_HEIGHT 8
73#endif
74// Default contrast level
75#if !defined(ST7565_CONTRAST)
76# define ST7565_CONTRAST 32
77#endif
78
79#if !defined(ST7565_TIMEOUT)
80# if defined(ST7565_DISABLE_TIMEOUT)
81# define ST7565_TIMEOUT 0
82# else
83# define ST7565_TIMEOUT 60000
84# endif
85#endif
86
87#if !defined(ST7565_UPDATE_INTERVAL) && defined(SPLIT_KEYBOARD)
88# define ST7565_UPDATE_INTERVAL 50
89#endif
90
91typedef struct __attribute__((__packed__)) {
92 uint8_t *current_element;
93 uint16_t remaining_element_count;
94} display_buffer_reader_t;
95
96// Rotation enum values are flags
97typedef enum { DISPLAY_ROTATION_0, DISPLAY_ROTATION_180 } display_rotation_t;
98
99// Initialize the display, rotating the rendered output based on the define passed in.
100// Returns true if the display was initialized successfully
101bool st7565_init(display_rotation_t rotation);
102
103// Called at the start of st7565_init, weak function overridable by the user
104// rotation - the value passed into st7565_init
105// Return new display_rotation_t if you want to override default rotation
106display_rotation_t st7565_init_user(display_rotation_t rotation);
107
108// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering
109void st7565_clear(void);
110
111// Renders the dirty chunks of the buffer to display
112void st7565_render(void);
113
114// Moves cursor to character position indicated by column and line, wraps if out of bounds
115// Max column denoted by 'st7565_max_chars()' and max lines by 'st7565_max_lines()' functions
116void st7565_set_cursor(uint8_t col, uint8_t line);
117
118// Advances the cursor to the next page, writing ' ' if true
119// Wraps to the begining when out of bounds
120void st7565_advance_page(bool clearPageRemainder);
121
122// Moves the cursor forward 1 character length
123// Advance page if there is not enough room for the next character
124// Wraps to the begining when out of bounds
125void st7565_advance_char(void);
126
127// Writes a single character to the buffer at current cursor position
128// Advances the cursor while writing, inverts the pixels if true
129// Main handler that writes character data to the display buffer
130void st7565_write_char(const char data, bool invert);
131
132// Writes a string to the buffer at current cursor position
133// Advances the cursor while writing, inverts the pixels if true
134void st7565_write(const char *data, bool invert);
135
136// Writes a string to the buffer at current cursor position
137// Advances the cursor while writing, inverts the pixels if true
138// Advances the cursor to the next page, wiring ' ' to the remainder of the current page
139void st7565_write_ln(const char *data, bool invert);
140
141// Pans the buffer to the right (or left by passing true) by moving contents of the buffer
142// Useful for moving the screen in preparation for new drawing
143void st7565_pan(bool left);
144
145// Returns a pointer to the requested start index in the buffer plus remaining
146// buffer length as struct
147display_buffer_reader_t st7565_read_raw(uint16_t start_index);
148
149// Writes a string to the buffer at current cursor position
150void st7565_write_raw(const char *data, uint16_t size);
151
152// Writes a single byte into the buffer at the specified index
153void st7565_write_raw_byte(const char data, uint16_t index);
154
155// Sets a specific pixel on or off
156// Coordinates start at top-left and go right and down for positive x and y
157void st7565_write_pixel(uint8_t x, uint8_t y, bool on);
158
159#if defined(__AVR__)
160// Writes a PROGMEM string to the buffer at current cursor position
161// Advances the cursor while writing, inverts the pixels if true
162// Remapped to call 'void st7565_write(const char *data, bool invert);' on ARM
163void st7565_write_P(const char *data, bool invert);
164
165// Writes a PROGMEM string to the buffer at current cursor position
166// Advances the cursor while writing, inverts the pixels if true
167// Advances the cursor to the next page, wiring ' ' to the remainder of the current page
168// Remapped to call 'void st7565_write_ln(const char *data, bool invert);' on ARM
169void st7565_write_ln_P(const char *data, bool invert);
170
171// Writes a PROGMEM string to the buffer at current cursor position
172void st7565_write_raw_P(const char *data, uint16_t size);
173#else
174# define st7565_write_P(data, invert) st7565_write(data, invert)
175# define st7565_write_ln_P(data, invert) st7565_write_ln(data, invert)
176# define st7565_write_raw_P(data, size) st7565_write_raw(data, size)
177#endif // defined(__AVR__)
178
179// Can be used to manually turn on the screen if it is off
180// Returns true if the screen was on or turns on
181bool st7565_on(void);
182
183// Called when st7565_on() turns on the screen, weak function overridable by the user
184// Not called if the screen is already on
185void st7565_on_user(void);
186
187// Can be used to manually turn off the screen if it is on
188// Returns true if the screen was off or turns off
189bool st7565_off(void);
190
191// Called when st7565_off() turns off the screen, weak function overridable by the user
192// Not called if the screen is already off
193void st7565_off_user(void);
194
195// Returns true if the screen is currently on, false if it is
196// not
197bool st7565_is_on(void);
198
199// Basically it's st7565_render, but with timeout management and st7565_task_user calling!
200void st7565_task(void);
201
202// Called at the start of st7565_task, weak function overridable by the user
203void st7565_task_user(void);
204
205// Returns the maximum number of characters that will fit on a line
206uint8_t st7565_max_chars(void);
207
208// Returns the maximum number of lines that will fit on the display
209uint8_t st7565_max_lines(void);
210
211void st7565_reset(void);
212
213spi_status_t st7565_send_cmd(uint8_t cmd);
214
215spi_status_t st7565_send_data(uint8_t *data, uint16_t length);