aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/awinic/aw20216.c166
-rw-r--r--drivers/awinic/aw20216.h251
-rw-r--r--drivers/chibios/spi_master.c70
-rw-r--r--drivers/chibios/spi_master.h19
-rw-r--r--drivers/lcd/st7565.c479
-rw-r--r--drivers/lcd/st7565.h215
6 files changed, 1189 insertions, 11 deletions
diff --git a/drivers/awinic/aw20216.c b/drivers/awinic/aw20216.c
new file mode 100644
index 000000000..236c42a3c
--- /dev/null
+++ b/drivers/awinic/aw20216.c
@@ -0,0 +1,166 @@
1/* Copyright 2021 Jasper Chan
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "aw20216.h"
18#include "spi_master.h"
19
20/* The AW20216 appears to be somewhat similar to the IS31FL743, although quite
21 * a few things are different, such as the command byte format and page ordering.
22 * The LED addresses start from 0x00 instead of 0x01.
23 */
24#define AWINIC_ID 0b1010 << 4
25
26#define AW_PAGE_FUNCTION 0x00 << 1 // PG0, Function registers
27#define AW_PAGE_PWM 0x01 << 1 // PG1, LED PWM control
28#define AW_PAGE_SCALING 0x02 << 1 // PG2, LED current scaling control
29#define AW_PAGE_PATCHOICE 0x03 << 1 // PG3, Pattern choice?
30#define AW_PAGE_PWMSCALING 0x04 << 1 // PG4, LED PWM + Scaling control?
31
32#define AW_WRITE 0
33#define AW_READ 1
34
35#define AW_REG_CONFIGURATION 0x00 // PG0
36#define AW_REG_GLOBALCURRENT 0x01 // PG0
37
38// Default value of AW_REG_CONFIGURATION
39// D7:D4 = 1011, SWSEL (SW1~SW12 active)
40// D3 = 0?, reserved (apparently this should be 1 but it doesn't seem to matter)
41// D2:D1 = 00, OSDE (open/short detection enable)
42// D0 = 0, CHIPEN (write 1 to enable LEDs when hardware enable pulled high)
43#define AW_CONFIG_DEFAULT 0b10110000
44#define AW_CHIPEN 1
45
46#ifndef AW_SCALING_MAX
47# define AW_SCALING_MAX 150
48#endif
49
50#ifndef AW_GLOBAL_CURRENT_MAX
51# define AW_GLOBAL_CURRENT_MAX 150
52#endif
53
54#ifndef DRIVER_1_CS
55# define DRIVER_1_CS B13
56#endif
57
58#ifndef DRIVER_1_EN
59# define DRIVER_1_EN C13
60#endif
61
62uint8_t g_spi_transfer_buffer[20] = {0};
63aw_led g_pwm_buffer[DRIVER_LED_TOTAL];
64bool g_pwm_buffer_update_required[DRIVER_LED_TOTAL];
65
66bool AW20216_write_register(pin_t slave_pin, uint8_t page, uint8_t reg, uint8_t data) {
67 // Do we need to call spi_stop() if this fails?
68 if (!spi_start(slave_pin, false, 0, 16)) {
69 return false;
70 }
71
72 g_spi_transfer_buffer[0] = (AWINIC_ID | page | AW_WRITE);
73 g_spi_transfer_buffer[1] = reg;
74 g_spi_transfer_buffer[2] = data;
75
76 if (spi_transmit(g_spi_transfer_buffer, 3) != SPI_STATUS_SUCCESS) {
77 spi_stop();
78 return false;
79 }
80 spi_stop();
81 return true;
82}
83
84bool AW20216_init_scaling(void) {
85 // Set constant current to the max, control brightness with PWM
86 aw_led led;
87 for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
88 led = g_aw_leds[i];
89 if (led.driver == 0) {
90 AW20216_write_register(DRIVER_1_CS, AW_PAGE_SCALING, led.r, AW_SCALING_MAX);
91 AW20216_write_register(DRIVER_1_CS, AW_PAGE_SCALING, led.g, AW_SCALING_MAX);
92 AW20216_write_register(DRIVER_1_CS, AW_PAGE_SCALING, led.b, AW_SCALING_MAX);
93 }
94#ifdef DRIVER_2_CS
95 else if (led.driver == 1) {
96 AW20216_write_register(DRIVER_2_CS, AW_PAGE_SCALING, led.r, AW_SCALING_MAX);
97 AW20216_write_register(DRIVER_2_CS, AW_PAGE_SCALING, led.g, AW_SCALING_MAX);
98 AW20216_write_register(DRIVER_2_CS, AW_PAGE_SCALING, led.b, AW_SCALING_MAX);
99 }
100#endif
101 }
102 return true;
103}
104
105bool AW20216_soft_enable(void) {
106 AW20216_write_register(DRIVER_1_CS, AW_PAGE_FUNCTION, AW_REG_CONFIGURATION, AW_CONFIG_DEFAULT | AW_CHIPEN);
107#ifdef DRIVER_2_CS
108 AW20216_write_register(DRIVER_2_CS, AW_PAGE_FUNCTION, AW_REG_CONFIGURATION, AW_CONFIG_DEFAULT | AW_CHIPEN);
109#endif
110 return true;
111}
112
113void AW20216_update_pwm(int index, uint8_t red, uint8_t green, uint8_t blue) {
114 aw_led led = g_aw_leds[index];
115 if (led.driver == 0) {
116 AW20216_write_register(DRIVER_1_CS, AW_PAGE_PWM, led.r, red);
117 AW20216_write_register(DRIVER_1_CS, AW_PAGE_PWM, led.g, green);
118 AW20216_write_register(DRIVER_1_CS, AW_PAGE_PWM, led.b, blue);
119 }
120#ifdef DRIVER_2_CS
121 else if (led.driver == 1) {
122 AW20216_write_register(DRIVER_2_CS, AW_PAGE_PWM, led.r, red);
123 AW20216_write_register(DRIVER_2_CS, AW_PAGE_PWM, led.g, green);
124 AW20216_write_register(DRIVER_2_CS, AW_PAGE_PWM, led.b, blue);
125 }
126#endif
127 return;
128}
129
130void AW20216_init(void) {
131 // All LEDs should start with all scaling and PWM registers as off
132 setPinOutput(DRIVER_1_EN);
133 writePinHigh(DRIVER_1_EN);
134 AW20216_write_register(DRIVER_1_CS, AW_PAGE_FUNCTION, AW_REG_GLOBALCURRENT, AW_GLOBAL_CURRENT_MAX);
135#ifdef DRIVER_2_EN
136 setPinOutput(DRIVER_2_EN);
137 writePinHigh(DRIVER_2_EN);
138 AW20216_write_register(DRIVER_2_CS, AW_PAGE_FUNCTION, AW_REG_GLOBALCURRENT, AW_GLOBAL_CURRENT_MAX);
139#endif
140 AW20216_init_scaling();
141 AW20216_soft_enable();
142 return;
143}
144
145void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
146 g_pwm_buffer[index].r = red;
147 g_pwm_buffer[index].g = green;
148 g_pwm_buffer[index].b = blue;
149 g_pwm_buffer_update_required[index] = true;
150 return;
151}
152void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
153 for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
154 AW20216_set_color(i, red, green, blue);
155 }
156 return;
157}
158void AW20216_update_pwm_buffers(void) {
159 for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
160 if (g_pwm_buffer_update_required[i]) {
161 AW20216_update_pwm(i, g_pwm_buffer[i].r, g_pwm_buffer[i].g, g_pwm_buffer[i].b);
162 g_pwm_buffer_update_required[i] = false;
163 }
164 }
165 return;
166}
diff --git a/drivers/awinic/aw20216.h b/drivers/awinic/aw20216.h
new file mode 100644
index 000000000..9c6865cc8
--- /dev/null
+++ b/drivers/awinic/aw20216.h
@@ -0,0 +1,251 @@
1/* Copyright 2021 Jasper Chan (Gigahawk)
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20#include <stdbool.h>
21
22typedef struct aw_led {
23 uint8_t driver : 2;
24 uint8_t r;
25 uint8_t g;
26 uint8_t b;
27} aw_led;
28
29extern const aw_led g_aw_leds[DRIVER_LED_TOTAL];
30
31void AW20216_init(void);
32void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
33void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
34void AW20216_update_pwm_buffers(void);
35
36#define CS1_SW1 0x00
37#define CS2_SW1 0x01
38#define CS3_SW1 0x02
39#define CS4_SW1 0x03
40#define CS5_SW1 0x04
41#define CS6_SW1 0x05
42#define CS7_SW1 0x06
43#define CS8_SW1 0x07
44#define CS9_SW1 0x08
45#define CS10_SW1 0x09
46#define CS11_SW1 0x0A
47#define CS12_SW1 0x0B
48#define CS13_SW1 0x0C
49#define CS14_SW1 0x0D
50#define CS15_SW1 0x0E
51#define CS16_SW1 0x0F
52#define CS17_SW1 0x10
53#define CS18_SW1 0x11
54#define CS1_SW2 0x12
55#define CS2_SW2 0x13
56#define CS3_SW2 0x14
57#define CS4_SW2 0x15
58#define CS5_SW2 0x16
59#define CS6_SW2 0x17
60#define CS7_SW2 0x18
61#define CS8_SW2 0x19
62#define CS9_SW2 0x1A
63#define CS10_SW2 0x1B
64#define CS11_SW2 0x1C
65#define CS12_SW2 0x1D
66#define CS13_SW2 0x1E
67#define CS14_SW2 0x1F
68#define CS15_SW2 0x20
69#define CS16_SW2 0x21
70#define CS17_SW2 0x22
71#define CS18_SW2 0x23
72#define CS1_SW3 0x24
73#define CS2_SW3 0x25
74#define CS3_SW3 0x26
75#define CS4_SW3 0x27
76#define CS5_SW3 0x28
77#define CS6_SW3 0x29
78#define CS7_SW3 0x2A
79#define CS8_SW3 0x2B
80#define CS9_SW3 0x2C
81#define CS10_SW3 0x2D
82#define CS11_SW3 0x2E
83#define CS12_SW3 0x2F
84#define CS13_SW3 0x30
85#define CS14_SW3 0x31
86#define CS15_SW3 0x32
87#define CS16_SW3 0x33
88#define CS17_SW3 0x34
89#define CS18_SW3 0x35
90#define CS1_SW4 0x36
91#define CS2_SW4 0x37
92#define CS3_SW4 0x38
93#define CS4_SW4 0x39
94#define CS5_SW4 0x3A
95#define CS6_SW4 0x3B
96#define CS7_SW4 0x3C
97#define CS8_SW4 0x3D
98#define CS9_SW4 0x3E
99#define CS10_SW4 0x3F
100#define CS11_SW4 0x40
101#define CS12_SW4 0x41
102#define CS13_SW4 0x42
103#define CS14_SW4 0x43
104#define CS15_SW4 0x44
105#define CS16_SW4 0x45
106#define CS17_SW4 0x46
107#define CS18_SW4 0x47
108#define CS1_SW5 0x48
109#define CS2_SW5 0x49
110#define CS3_SW5 0x4A
111#define CS4_SW5 0x4B
112#define CS5_SW5 0x4C
113#define CS6_SW5 0x4D
114#define CS7_SW5 0x4E
115#define CS8_SW5 0x4F
116#define CS9_SW5 0x50
117#define CS10_SW5 0x51
118#define CS11_SW5 0x52
119#define CS12_SW5 0x53
120#define CS13_SW5 0x54
121#define CS14_SW5 0x55
122#define CS15_SW5 0x56
123#define CS16_SW5 0x57
124#define CS17_SW5 0x58
125#define CS18_SW5 0x59
126#define CS1_SW6 0x5A
127#define CS2_SW6 0x5B
128#define CS3_SW6 0x5C
129#define CS4_SW6 0x5D
130#define CS5_SW6 0x5E
131#define CS6_SW6 0x5F
132#define CS7_SW6 0x60
133#define CS8_SW6 0x61
134#define CS9_SW6 0x62
135#define CS10_SW6 0x63
136#define CS11_SW6 0x64
137#define CS12_SW6 0x65
138#define CS13_SW6 0x66
139#define CS14_SW6 0x67
140#define CS15_SW6 0x68
141#define CS16_SW6 0x69
142#define CS17_SW6 0x6A
143#define CS18_SW6 0x6B
144#define CS1_SW7 0x6C
145#define CS2_SW7 0x6D
146#define CS3_SW7 0x6E
147#define CS4_SW7 0x6F
148#define CS5_SW7 0x70
149#define CS6_SW7 0x71
150#define CS7_SW7 0x72
151#define CS8_SW7 0x73
152#define CS9_SW7 0x74
153#define CS10_SW7 0x75
154#define CS11_SW7 0x76
155#define CS12_SW7 0x77
156#define CS13_SW7 0x78
157#define CS14_SW7 0x79
158#define CS15_SW7 0x7A
159#define CS16_SW7 0x7B
160#define CS17_SW7 0x7C
161#define CS18_SW7 0x7D
162#define CS1_SW8 0x7E
163#define CS2_SW8 0x7F
164#define CS3_SW8 0x80
165#define CS4_SW8 0x81
166#define CS5_SW8 0x82
167#define CS6_SW8 0x83
168#define CS7_SW8 0x84
169#define CS8_SW8 0x85
170#define CS9_SW8 0x86
171#define CS10_SW8 0x87
172#define CS11_SW8 0x88
173#define CS12_SW8 0x89
174#define CS13_SW8 0x8A
175#define CS14_SW8 0x8B
176#define CS15_SW8 0x8C
177#define CS16_SW8 0x8D
178#define CS17_SW8 0x8E
179#define CS18_SW8 0x8F
180#define CS1_SW9 0x90
181#define CS2_SW9 0x91
182#define CS3_SW9 0x92
183#define CS4_SW9 0x93
184#define CS5_SW9 0x94
185#define CS6_SW9 0x95
186#define CS7_SW9 0x96
187#define CS8_SW9 0x97
188#define CS9_SW9 0x98
189#define CS10_SW9 0x99
190#define CS11_SW9 0x9A
191#define CS12_SW9 0x9B
192#define CS13_SW9 0x9C
193#define CS14_SW9 0x9D
194#define CS15_SW9 0x9E
195#define CS16_SW9 0x9F
196#define CS17_SW9 0xA0
197#define CS18_SW9 0xA1
198#define CS1_SW10 0xA2
199#define CS2_SW10 0xA3
200#define CS3_SW10 0xA4
201#define CS4_SW10 0xA5
202#define CS5_SW10 0xA6
203#define CS6_SW10 0xA7
204#define CS7_SW10 0xA8
205#define CS8_SW10 0xA9
206#define CS9_SW10 0xAA
207#define CS10_SW10 0xAB
208#define CS11_SW10 0xAC
209#define CS12_SW10 0xAD
210#define CS13_SW10 0xAE
211#define CS14_SW10 0xAF
212#define CS15_SW10 0xB0
213#define CS16_SW10 0xB1
214#define CS17_SW10 0xB2
215#define CS18_SW10 0xB3
216#define CS1_SW11 0xB4
217#define CS2_SW11 0xB5
218#define CS3_SW11 0xB6
219#define CS4_SW11 0xB7
220#define CS5_SW11 0xB8
221#define CS6_SW11 0xB9
222#define CS7_SW11 0xBA
223#define CS8_SW11 0xBB
224#define CS9_SW11 0xBC
225#define CS10_SW11 0xBD
226#define CS11_SW11 0xBE
227#define CS12_SW11 0xBF
228#define CS13_SW11 0xC0
229#define CS14_SW11 0xC1
230#define CS15_SW11 0xC2
231#define CS16_SW11 0xC3
232#define CS17_SW11 0xC4
233#define CS18_SW11 0xC5
234#define CS1_SW12 0xC6
235#define CS2_SW12 0xC7
236#define CS3_SW12 0xC8
237#define CS4_SW12 0xC9
238#define CS5_SW12 0xCA
239#define CS6_SW12 0xCB
240#define CS7_SW12 0xCC
241#define CS8_SW12 0xCD
242#define CS9_SW12 0xCE
243#define CS10_SW12 0xCF
244#define CS11_SW12 0xD0
245#define CS12_SW12 0xD1
246#define CS13_SW12 0xD2
247#define CS14_SW12 0xD3
248#define CS15_SW12 0xD4
249#define CS16_SW12 0xD5
250#define CS17_SW12 0xD6
251#define CS18_SW12 0xD7
diff --git a/drivers/chibios/spi_master.c b/drivers/chibios/spi_master.c
index 4852a6eba..28ddcbb2b 100644
--- a/drivers/chibios/spi_master.c
+++ b/drivers/chibios/spi_master.c
@@ -18,8 +18,13 @@
18 18
19#include "timer.h" 19#include "timer.h"
20 20
21static pin_t currentSlavePin = NO_PIN; 21static pin_t currentSlavePin = NO_PIN;
22static SPIConfig spiConfig = {false, NULL, 0, 0, 0, 0}; 22
23#if defined(K20x) || defined(KL2x)
24static SPIConfig spiConfig = {NULL, 0, 0, 0};
25#else
26static SPIConfig spiConfig = {false, NULL, 0, 0, 0, 0};
27#endif
23 28
24__attribute__((weak)) void spi_init(void) { 29__attribute__((weak)) void spi_init(void) {
25 static bool is_initialised = false; 30 static bool is_initialised = false;
@@ -27,15 +32,15 @@ __attribute__((weak)) void spi_init(void) {
27 is_initialised = true; 32 is_initialised = true;
28 33
29 // Try releasing special pins for a short time 34 // Try releasing special pins for a short time
30 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_INPUT); 35 setPinInput(SPI_SCK_PIN);
31 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_INPUT); 36 setPinInput(SPI_MOSI_PIN);
32 palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_INPUT); 37 setPinInput(SPI_MISO_PIN);
33 38
34 chThdSleepMilliseconds(10); 39 chThdSleepMilliseconds(10);
35#if defined(USE_GPIOV1) 40#if defined(USE_GPIOV1)
36 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); 41 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), SPI_SCK_PAL_MODE);
37 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); 42 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), SPI_MOSI_PAL_MODE);
38 palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); 43 palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), SPI_MISO_PAL_MODE);
39#else 44#else
40 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_ALTERNATE(SPI_SCK_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); 45 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_ALTERNATE(SPI_SCK_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
41 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_ALTERNATE(SPI_MOSI_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); 46 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_ALTERNATE(SPI_MOSI_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
@@ -58,6 +63,54 @@ bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {
58 return false; 63 return false;
59 } 64 }
60 65
66#if defined(K20x) || defined(KL2x)
67 spiConfig.tar0 = SPIx_CTARn_FMSZ(7) | SPIx_CTARn_ASC(1);
68
69 if (lsbFirst) {
70 spiConfig.tar0 |= SPIx_CTARn_LSBFE;
71 }
72
73 switch (mode) {
74 case 0:
75 break;
76 case 1:
77 spiConfig.tar0 |= SPIx_CTARn_CPHA;
78 break;
79 case 2:
80 spiConfig.tar0 |= SPIx_CTARn_CPOL;
81 break;
82 case 3:
83 spiConfig.tar0 |= SPIx_CTARn_CPHA | SPIx_CTARn_CPOL;
84 break;
85 }
86
87 switch (roundedDivisor) {
88 case 2:
89 spiConfig.tar0 |= SPIx_CTARn_BR(0);
90 break;
91 case 4:
92 spiConfig.tar0 |= SPIx_CTARn_BR(1);
93 break;
94 case 8:
95 spiConfig.tar0 |= SPIx_CTARn_BR(3);
96 break;
97 case 16:
98 spiConfig.tar0 |= SPIx_CTARn_BR(4);
99 break;
100 case 32:
101 spiConfig.tar0 |= SPIx_CTARn_BR(5);
102 break;
103 case 64:
104 spiConfig.tar0 |= SPIx_CTARn_BR(6);
105 break;
106 case 128:
107 spiConfig.tar0 |= SPIx_CTARn_BR(7);
108 break;
109 case 256:
110 spiConfig.tar0 |= SPIx_CTARn_BR(8);
111 break;
112 }
113#else
61 spiConfig.cr1 = 0; 114 spiConfig.cr1 = 0;
62 115
63 if (lsbFirst) { 116 if (lsbFirst) {
@@ -103,6 +156,7 @@ bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {
103 spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0; 156 spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0;
104 break; 157 break;
105 } 158 }
159#endif
106 160
107 currentSlavePin = slavePin; 161 currentSlavePin = slavePin;
108 spiConfig.ssport = PAL_PORT(slavePin); 162 spiConfig.ssport = PAL_PORT(slavePin);
diff --git a/drivers/chibios/spi_master.h b/drivers/chibios/spi_master.h
index e93580e31..b5a6ef143 100644
--- a/drivers/chibios/spi_master.h
+++ b/drivers/chibios/spi_master.h
@@ -21,6 +21,7 @@
21#include <stdbool.h> 21#include <stdbool.h>
22 22
23#include "gpio.h" 23#include "gpio.h"
24#include "chibios_config.h"
24 25
25#ifndef SPI_DRIVER 26#ifndef SPI_DRIVER
26# define SPI_DRIVER SPID2 27# define SPI_DRIVER SPID2
@@ -31,7 +32,11 @@
31#endif 32#endif
32 33
33#ifndef SPI_SCK_PAL_MODE 34#ifndef SPI_SCK_PAL_MODE
34# define SPI_SCK_PAL_MODE 5 35# if defined(USE_GPIOV1)
36# define SPI_SCK_PAL_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL
37# else
38# define SPI_SCK_PAL_MODE 5
39# endif
35#endif 40#endif
36 41
37#ifndef SPI_MOSI_PIN 42#ifndef SPI_MOSI_PIN
@@ -39,7 +44,11 @@
39#endif 44#endif
40 45
41#ifndef SPI_MOSI_PAL_MODE 46#ifndef SPI_MOSI_PAL_MODE
42# define SPI_MOSI_PAL_MODE 5 47# if defined(USE_GPIOV1)
48# define SPI_MOSI_PAL_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL
49# else
50# define SPI_MOSI_PAL_MODE 5
51# endif
43#endif 52#endif
44 53
45#ifndef SPI_MISO_PIN 54#ifndef SPI_MISO_PIN
@@ -47,7 +56,11 @@
47#endif 56#endif
48 57
49#ifndef SPI_MISO_PAL_MODE 58#ifndef SPI_MISO_PAL_MODE
50# define SPI_MISO_PAL_MODE 5 59# if defined(USE_GPIOV1)
60# define SPI_MISO_PAL_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL
61# else
62# define SPI_MISO_PAL_MODE 5
63# endif
51#endif 64#endif
52 65
53typedef int16_t spi_status_t; 66typedef int16_t spi_status_t;
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);