aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/avr/analog.c138
-rw-r--r--drivers/avr/analog.h53
-rw-r--r--drivers/avr/glcdfont.c23
-rw-r--r--drivers/avr/hd44780.c536
-rw-r--r--drivers/avr/hd44780.h348
-rw-r--r--drivers/avr/i2c_master.c224
-rw-r--r--drivers/avr/i2c_master.h43
-rw-r--r--drivers/avr/i2c_slave.c86
-rw-r--r--drivers/avr/i2c_slave.h30
-rw-r--r--drivers/avr/serial.c563
-rw-r--r--drivers/avr/serial.h62
-rw-r--r--drivers/avr/spi_master.c180
-rw-r--r--drivers/avr/spi_master.h59
-rw-r--r--drivers/avr/ssd1306.c319
-rw-r--r--drivers/avr/ssd1306.h87
-rw-r--r--drivers/avr/uart.c170
-rw-r--r--drivers/avr/uart.h35
-rw-r--r--drivers/avr/ws2812.c176
-rw-r--r--drivers/avr/ws2812_i2c.c27
-rw-r--r--drivers/chibios/analog.c321
-rw-r--r--drivers/chibios/i2c_master.c121
-rw-r--r--drivers/chibios/i2c_master.h113
-rw-r--r--drivers/chibios/serial.c290
-rw-r--r--drivers/chibios/serial.h62
-rw-r--r--drivers/chibios/serial_usart.c208
-rw-r--r--drivers/chibios/serial_usart.h90
-rw-r--r--drivers/chibios/serial_usart_duplex.c261
-rw-r--r--drivers/chibios/spi_master.c148
-rw-r--r--drivers/chibios/spi_master.h80
-rw-r--r--drivers/chibios/uart.c50
-rw-r--r--drivers/chibios/uart.h77
-rw-r--r--drivers/chibios/usbpd_stm32g4.c76
-rw-r--r--drivers/chibios/ws2812.c114
-rw-r--r--drivers/chibios/ws2812_pwm.c311
-rw-r--r--drivers/chibios/ws2812_spi.c159
-rw-r--r--drivers/eeprom/eeprom_i2c.c23
-rw-r--r--drivers/eeprom/eeprom_stm32_L0_L1.c96
-rw-r--r--drivers/eeprom/eeprom_stm32_L0_L1.h33
-rw-r--r--drivers/haptic/haptic.c355
-rw-r--r--drivers/haptic/haptic.h81
-rw-r--r--drivers/haptic/solenoid.c1
-rw-r--r--drivers/lcd/st7565.c496
-rw-r--r--drivers/lcd/st7565.h219
-rw-r--r--drivers/led/apa102.c (renamed from drivers/apa102/apa102.c)0
-rw-r--r--drivers/led/apa102.h (renamed from drivers/apa102/apa102.h)0
-rw-r--r--drivers/led/aw20216.c141
-rw-r--r--drivers/led/aw20216.h253
-rw-r--r--drivers/led/issi/is31fl3218.c (renamed from drivers/issi/is31fl3218.c)0
-rw-r--r--drivers/led/issi/is31fl3218.h (renamed from drivers/issi/is31fl3218.h)0
-rw-r--r--drivers/led/issi/is31fl3731-simple.c (renamed from drivers/issi/is31fl3731-simple.c)0
-rw-r--r--drivers/led/issi/is31fl3731-simple.h (renamed from drivers/issi/is31fl3731-simple.h)3
-rw-r--r--drivers/led/issi/is31fl3731.c (renamed from drivers/issi/is31fl3731.c)0
-rw-r--r--drivers/led/issi/is31fl3731.h (renamed from drivers/issi/is31fl3731.h)3
-rw-r--r--drivers/led/issi/is31fl3733.c (renamed from drivers/issi/is31fl3733.c)0
-rw-r--r--drivers/led/issi/is31fl3733.h (renamed from drivers/issi/is31fl3733.h)3
-rw-r--r--drivers/led/issi/is31fl3736.c (renamed from drivers/issi/is31fl3736.c)0
-rw-r--r--drivers/led/issi/is31fl3736.h (renamed from drivers/issi/is31fl3736.h)3
-rw-r--r--drivers/led/issi/is31fl3737.c (renamed from drivers/issi/is31fl3737.c)43
-rw-r--r--drivers/led/issi/is31fl3737.h (renamed from drivers/issi/is31fl3737.h)3
-rw-r--r--drivers/led/issi/is31fl3741.c (renamed from drivers/issi/is31fl3741.c)0
-rw-r--r--drivers/led/issi/is31fl3741.h (renamed from drivers/issi/is31fl3741.h)3
-rw-r--r--drivers/oled/oled_driver.c26
-rw-r--r--drivers/oled/oled_driver.h4
-rw-r--r--drivers/sensors/adns5050.c193
-rw-r--r--drivers/sensors/adns5050.h79
-rw-r--r--drivers/sensors/adns9800.c219
-rw-r--r--drivers/sensors/adns9800.h (renamed from drivers/chibios/analog.h)32
-rw-r--r--drivers/sensors/adns9800_srom_A6.h3078
-rw-r--r--drivers/sensors/pimoroni_trackball.c140
-rw-r--r--drivers/sensors/pimoroni_trackball.h35
-rw-r--r--drivers/sensors/pmw3360.c239
-rw-r--r--drivers/sensors/pmw3360.h104
-rw-r--r--drivers/sensors/pmw3360_firmware.h300
-rw-r--r--drivers/serial.h46
74 files changed, 5641 insertions, 6253 deletions
diff --git a/drivers/avr/analog.c b/drivers/avr/analog.c
deleted file mode 100644
index 8d299ffdb..000000000
--- a/drivers/avr/analog.c
+++ /dev/null
@@ -1,138 +0,0 @@
1/* Copyright 2015 Jack Humbert
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 <avr/io.h>
18#include <avr/pgmspace.h>
19#include <stdint.h>
20#include "analog.h"
21
22static uint8_t aref = ADC_REF_POWER;
23
24void analogReference(uint8_t mode) { aref = mode & (_BV(REFS1) | _BV(REFS0)); }
25
26// Arduino compatible pin input
27int16_t analogRead(uint8_t pin) {
28#if defined(__AVR_ATmega32U4__)
29 // clang-format off
30 static const uint8_t PROGMEM pin_to_mux[] = {
31 //A0 A1 A2 A3 A4 A5
32 //F7 F6 F5 F4 F1 F0
33 0x07, 0x06, 0x05, 0x04, 0x01, 0x00,
34 //A6 A7 A8 A9 A10 A11
35 //D4 D7 B4 B5 B6 D6
36 0x20, 0x22, 0x23, 0x24, 0x25, 0x21
37 };
38 // clang-format on
39 if (pin >= 12) return 0;
40 return adc_read(pgm_read_byte(pin_to_mux + pin));
41#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
42 if (pin >= 8) return 0;
43 return adc_read(pin);
44#else
45 return 0;
46#endif
47}
48
49int16_t analogReadPin(pin_t pin) { return adc_read(pinToMux(pin)); }
50
51uint8_t pinToMux(pin_t pin) {
52 switch (pin) {
53 // clang-format off
54#if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
55 case F0: return 0; // ADC0
56 case F1: return _BV(MUX0); // ADC1
57 case F2: return _BV(MUX1); // ADC2
58 case F3: return _BV(MUX1) | _BV(MUX0); // ADC3
59 case F4: return _BV(MUX2); // ADC4
60 case F5: return _BV(MUX2) | _BV(MUX0); // ADC5
61 case F6: return _BV(MUX2) | _BV(MUX1); // ADC6
62 case F7: return _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // ADC7
63 default: return _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // 0V
64#elif defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)
65 case F0: return 0; // ADC0
66 case F1: return _BV(MUX0); // ADC1
67 case F4: return _BV(MUX2); // ADC4
68 case F5: return _BV(MUX2) | _BV(MUX0); // ADC5
69 case F6: return _BV(MUX2) | _BV(MUX1); // ADC6
70 case F7: return _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // ADC7
71 case D4: return _BV(MUX5); // ADC8
72 case D6: return _BV(MUX5) | _BV(MUX0); // ADC9
73 case D7: return _BV(MUX5) | _BV(MUX1); // ADC10
74 case B4: return _BV(MUX5) | _BV(MUX1) | _BV(MUX0); // ADC11
75 case B5: return _BV(MUX5) | _BV(MUX2); // ADC12
76 case B6: return _BV(MUX5) | _BV(MUX2) | _BV(MUX0); // ADC13
77 default: return _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // 0V
78#elif defined(__AVR_ATmega32A__)
79 case A0: return 0; // ADC0
80 case A1: return _BV(MUX0); // ADC1
81 case A2: return _BV(MUX1); // ADC2
82 case A3: return _BV(MUX1) | _BV(MUX0); // ADC3
83 case A4: return _BV(MUX2); // ADC4
84 case A5: return _BV(MUX2) | _BV(MUX0); // ADC5
85 case A6: return _BV(MUX2) | _BV(MUX1); // ADC6
86 case A7: return _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // ADC7
87 default: return _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // 0V
88#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
89 case C0: return 0; // ADC0
90 case C1: return _BV(MUX0); // ADC1
91 case C2: return _BV(MUX1); // ADC2
92 case C3: return _BV(MUX1) | _BV(MUX0); // ADC3
93 case C4: return _BV(MUX2); // ADC4
94 case C5: return _BV(MUX2) | _BV(MUX0); // ADC5
95 // ADC7:6 not present in DIP package and not shared by GPIO pins
96 default: return _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // 0V
97#endif
98 // clang-format on
99 }
100 return 0;
101}
102
103int16_t adc_read(uint8_t mux) {
104 uint16_t low;
105
106 // Enable ADC and configure prescaler
107 ADCSRA = _BV(ADEN) | ADC_PRESCALER;
108
109#if defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)
110 // High speed mode and ADC8-13
111 ADCSRB = _BV(ADHSM) | (mux & _BV(MUX5));
112#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
113 // High speed mode only
114 ADCSRB = _BV(ADHSM);
115#endif
116
117 // Configure mux input
118#if defined(MUX4)
119 ADMUX = aref | (mux & (_BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0)));
120#else
121 ADMUX = aref | (mux & (_BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0)));
122#endif
123
124 // Start the conversion
125 ADCSRA |= _BV(ADSC);
126 // Wait for result
127 while (ADCSRA & _BV(ADSC))
128 ;
129 // Must read LSB first
130 low = ADCL;
131 // Must read MSB only once!
132 low |= (ADCH << 8);
133
134 // turn off the ADC
135 ADCSRA &= ~(1 << ADEN);
136
137 return low;
138}
diff --git a/drivers/avr/analog.h b/drivers/avr/analog.h
deleted file mode 100644
index 058882450..000000000
--- a/drivers/avr/analog.h
+++ /dev/null
@@ -1,53 +0,0 @@
1/* Copyright 2015 Jack Humbert
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 "quantum.h"
21
22#ifdef __cplusplus
23extern "C" {
24#endif
25void analogReference(uint8_t mode);
26int16_t analogRead(uint8_t pin);
27
28int16_t analogReadPin(pin_t pin);
29uint8_t pinToMux(pin_t pin);
30
31int16_t adc_read(uint8_t mux);
32#ifdef __cplusplus
33}
34#endif
35
36#define ADC_REF_EXTERNAL 0 // AREF, Internal Vref turned off
37#define ADC_REF_POWER _BV(REFS0) // AVCC with external capacitor on AREF pin
38#define ADC_REF_INTERNAL (_BV(REFS1) | _BV(REFS0)) // Internal 2.56V Voltage Reference with external capacitor on AREF pin (1.1V for 328P)
39
40// These prescaler values are for high speed mode, ADHSM = 1
41#if F_CPU == 16000000L || F_CPU == 12000000L
42# define ADC_PRESCALER (_BV(ADPS2) | _BV(ADPS1)) // /64
43#elif F_CPU == 8000000L
44# define ADC_PRESCALER (_BV(ADPS2) | _BV(ADPS0)) // /32
45#elif F_CPU == 4000000L
46# define ADC_PRESCALER (_BV(ADPS2)) // /16
47#elif F_CPU == 2000000L
48# define ADC_PRESCALER (_BV(ADPS1) | _BV(ADPS0)) // /8
49#elif F_CPU == 1000000L
50# define ADC_PRESCALER _BV(ADPS1) // /4
51#else
52# define ADC_PRESCALER _BV(ADPS0) // /2
53#endif
diff --git a/drivers/avr/glcdfont.c b/drivers/avr/glcdfont.c
deleted file mode 100644
index 5e763b054..000000000
--- a/drivers/avr/glcdfont.c
+++ /dev/null
@@ -1,23 +0,0 @@
1// This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0.
2// See gfxfont.h for newer custom bitmap font info.
3
4#include "progmem.h"
5
6// Standard ASCII 5x7 font
7
8static const unsigned char font[] PROGMEM = {
9 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x18, 0x3C, 0x7E, 0x3C, 0x18, 0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00, 0x18, 0x3C, 0x18, 0x00, 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00, 0x18, 0x24, 0x18, 0x00, 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x30, 0x48, 0x3A, 0x06, 0x0E, 0x26, 0x29, 0x79, 0x29, 0x26, 0x40, 0x7F, 0x05, 0x05, 0x07, 0x40, 0x7F, 0x05, 0x25, 0x3F, 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x7F, 0x3E, 0x1C, 0x1C, 0x08, 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x14, 0x22, 0x7F, 0x22, 0x14, 0x5F, 0x5F, 0x00, 0x5F, 0x5F, 0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00, 0x66, 0x89, 0x95, 0x6A, 0x60, 0x60, 0x60, 0x60, 0x60, 0x94, 0xA2, 0xFF, 0xA2, 0x94, 0x08, 0x04, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x20, 0x10, 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x1E, 0x10, 0x10, 0x10, 0x10, 0x0C, 0x1E, 0x0C, 0x1E, 0x0C,
10 0x30, 0x38, 0x3E, 0x38, 0x30, 0x06, 0x0E, 0x3E, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62, 0x36, 0x49, 0x56, 0x20, 0x50, 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, 0x1C, 0x22, 0x41, 0x00, 0x00, 0x41, 0x22, 0x1C, 0x00, 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x80, 0x70, 0x30, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x60, 0x60, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x72, 0x49, 0x49, 0x49, 0x46, 0x21, 0x41, 0x49, 0x4D, 0x33, 0x18, 0x14, 0x12, 0x7F, 0x10, 0x27, 0x45, 0x45, 0x45, 0x39, 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x41, 0x21, 0x11, 0x09, 0x07, 0x36, 0x49, 0x49, 0x49, 0x36, 0x46, 0x49, 0x49, 0x29, 0x1E, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00,
11 0x00, 0x08, 0x14, 0x22, 0x41, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41, 0x22, 0x14, 0x08, 0x02, 0x01, 0x59, 0x09, 0x06, 0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x3E, 0x41, 0x41, 0x41, 0x22, 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x3E, 0x41, 0x41, 0x51, 0x73, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x41, 0x7F, 0x41, 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x26, 0x49, 0x49, 0x49, 0x32, 0x03, 0x01, 0x7F, 0x01, 0x03, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x63, 0x14, 0x08, 0x14, 0x63, 0x03, 0x04, 0x78, 0x04, 0x03,
12 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00, 0x7F, 0x41, 0x41, 0x41, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41, 0x41, 0x7F, 0x04, 0x02, 0x01, 0x02, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x03, 0x07, 0x08, 0x00, 0x20, 0x54, 0x54, 0x78, 0x40, 0x7F, 0x28, 0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x28, 0x7F, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x08, 0x7E, 0x09, 0x02, 0x18, 0xA4, 0xA4, 0x9C, 0x78, 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7D, 0x40, 0x00, 0x20, 0x40, 0x40, 0x3D, 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, 0x7C, 0x04, 0x78, 0x04, 0x78, 0x7C, 0x08, 0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, 0xFC, 0x18, 0x24, 0x24, 0x18, 0x18, 0x24, 0x24, 0x18, 0xFC, 0x7C, 0x08, 0x04, 0x04, 0x08, 0x48, 0x54, 0x54, 0x54, 0x24, 0x04, 0x04, 0x3F, 0x44, 0x24, 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x3C, 0x40, 0x30, 0x40, 0x3C,
13 0x44, 0x28, 0x10, 0x28, 0x44, 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x41, 0x36, 0x08, 0x00, 0x02, 0x01, 0x02, 0x04, 0x02, 0x3C, 0x26, 0x23, 0x26, 0x3C, 0x1E, 0xA1, 0xA1, 0x61, 0x12, 0x3A, 0x40, 0x40, 0x20, 0x7A, 0x38, 0x54, 0x54, 0x55, 0x59, 0x21, 0x55, 0x55, 0x79, 0x41, 0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut
14 0x21, 0x55, 0x54, 0x78, 0x40, 0x20, 0x54, 0x55, 0x79, 0x40, 0x0C, 0x1E, 0x52, 0x72, 0x12, 0x39, 0x55, 0x55, 0x55, 0x59, 0x39, 0x54, 0x54, 0x54, 0x59, 0x39, 0x55, 0x54, 0x54, 0x58, 0x00, 0x00, 0x45, 0x7C, 0x41, 0x00, 0x02, 0x45, 0x7D, 0x42, 0x00, 0x01, 0x45, 0x7C, 0x40, 0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut
15 0xF0, 0x28, 0x25, 0x28, 0xF0, 0x7C, 0x54, 0x55, 0x45, 0x00, 0x20, 0x54, 0x54, 0x7C, 0x54, 0x7C, 0x0A, 0x09, 0x7F, 0x49, 0x32, 0x49, 0x49, 0x49, 0x32, 0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut
16 0x32, 0x4A, 0x48, 0x48, 0x30, 0x3A, 0x41, 0x41, 0x21, 0x7A, 0x3A, 0x42, 0x40, 0x20, 0x78, 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut
17 0x3D, 0x40, 0x40, 0x40, 0x3D, 0x3C, 0x24, 0xFF, 0x24, 0x24, 0x48, 0x7E, 0x49, 0x43, 0x66, 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 0xFF, 0x09, 0x29, 0xF6, 0x20, 0xC0, 0x88, 0x7E, 0x09, 0x03, 0x20, 0x54, 0x54, 0x79, 0x41, 0x00, 0x00, 0x44, 0x7D, 0x41, 0x30, 0x48, 0x48, 0x4A, 0x32, 0x38, 0x40, 0x40, 0x22, 0x7A, 0x00, 0x7A, 0x0A, 0x0A, 0x72, 0x7D, 0x0D, 0x19, 0x31, 0x7D, 0x26, 0x29, 0x29, 0x2F, 0x28, 0x26, 0x29, 0x29, 0x29, 0x26, 0x30, 0x48, 0x4D, 0x40, 0x20, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 0x2F, 0x10, 0x28, 0x34, 0xFA, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x08, 0x14, 0x2A, 0x14, 0x22, 0x22, 0x14, 0x2A, 0x14, 0x08, 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old code
18 0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block
19 0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block
20 0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x10, 0x10, 0xFF, 0x00, 0x14, 0x14, 0x14, 0xFF, 0x00, 0x10, 0x10, 0xFF, 0x00, 0xFF, 0x10, 0x10, 0xF0, 0x10, 0xF0, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x14, 0x14, 0x17, 0x10, 0x1F, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0x1F, 0x00, 0x10, 0x10, 0x10, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x1F, 0x10, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x00, 0x00, 0x00, 0xFF, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0x14, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x1F, 0x10, 0x17, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14, 0x17, 0x10, 0x17, 0x14, 0x14, 0xF4, 0x04, 0xF4, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x17, 0x14, 0x10, 0x10, 0x1F, 0x10, 0x1F,
21 0x14, 0x14, 0x14, 0xF4, 0x14, 0x10, 0x10, 0xF0, 0x10, 0xF0, 0x00, 0x00, 0x1F, 0x10, 0x1F, 0x00, 0x00, 0x00, 0x1F, 0x14, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x00, 0x00, 0xF0, 0x10, 0xF0, 0x10, 0x10, 0xFF, 0x10, 0xFF, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x10, 0x10, 0x10, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x38, 0x44, 0x44, 0x38, 0x44, 0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta
22 0x7E, 0x02, 0x02, 0x06, 0x06, 0x02, 0x7E, 0x02, 0x7E, 0x02, 0x63, 0x55, 0x49, 0x41, 0x63, 0x38, 0x44, 0x44, 0x3C, 0x04, 0x40, 0x7E, 0x20, 0x1E, 0x20, 0x06, 0x02, 0x7E, 0x02, 0x02, 0x99, 0xA5, 0xE7, 0xA5, 0x99, 0x1C, 0x2A, 0x49, 0x2A, 0x1C, 0x4C, 0x72, 0x01, 0x72, 0x4C, 0x30, 0x4A, 0x4D, 0x4D, 0x30, 0x30, 0x48, 0x78, 0x48, 0x30, 0xBC, 0x62, 0x5A, 0x46, 0x3D, 0x3E, 0x49, 0x49, 0x49, 0x00, 0x7E, 0x01, 0x01, 0x01, 0x7E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x44, 0x44, 0x5F, 0x44, 0x44, 0x40, 0x51, 0x4A, 0x44, 0x40, 0x40, 0x44, 0x4A, 0x51, 0x40, 0x00, 0x00, 0xFF, 0x01, 0x03, 0xE0, 0x80, 0xFF, 0x00, 0x00, 0x08, 0x08, 0x6B, 0x6B, 0x08, 0x36, 0x12, 0x36, 0x24, 0x36, 0x06, 0x0F, 0x09, 0x0F, 0x06, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x30, 0x40, 0xFF, 0x01, 0x01, 0x00, 0x1F, 0x01, 0x01, 0x1E, 0x00, 0x19, 0x1D, 0x17, 0x12, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP
23};
diff --git a/drivers/avr/hd44780.c b/drivers/avr/hd44780.c
deleted file mode 100644
index f71069dec..000000000
--- a/drivers/avr/hd44780.c
+++ /dev/null
@@ -1,536 +0,0 @@
1/****************************************************************************
2 Title: HD44780U LCD library
3 Author: Peter Fleury <pfleury@gmx.ch> http://tinyurl.com/peterfleury
4 License: GNU General Public License Version 3
5 File: $Id: lcd.c,v 1.15.2.2 2015/01/17 12:16:05 peter Exp $
6 Software: AVR-GCC 3.3
7 Target: any AVR device, memory mapped mode only for AT90S4414/8515/Mega
8
9 DESCRIPTION
10 Basic routines for interfacing a HD44780U-based text lcd display
11
12 Originally based on Volker Oth's lcd library,
13 changed lcd_init(), added additional constants for lcd_command(),
14 added 4-bit I/O mode, improved and optimized code.
15
16 Library can be operated in memory mapped mode (LCD_IO_MODE=0) or in
17 4-bit IO port mode (LCD_IO_MODE=1). 8-bit IO port mode not supported.
18
19 Memory mapped mode compatible with Kanda STK200, but supports also
20 generation of R/W signal through A8 address line.
21
22 USAGE
23 See the C include lcd.h file for a description of each function
24
25*****************************************************************************/
26#include <inttypes.h>
27#include <avr/io.h>
28#include <avr/pgmspace.h>
29#include <util/delay.h>
30#include "hd44780.h"
31
32/*
33** constants/macros
34*/
35#define DDR(x) (*(&x - 1)) /* address of data direction register of port x */
36#if defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__)
37/* on ATmega64/128 PINF is on port 0x00 and not 0x60 */
38# define PIN(x) (&PORTF == &(x) ? _SFR_IO8(0x00) : (*(&x - 2)))
39#else
40# define PIN(x) (*(&x - 2)) /* address of input register of port x */
41#endif
42
43#if LCD_IO_MODE
44# define lcd_e_delay() _delay_us(LCD_DELAY_ENABLE_PULSE)
45# define lcd_e_high() LCD_E_PORT |= _BV(LCD_E_PIN);
46# define lcd_e_low() LCD_E_PORT &= ~_BV(LCD_E_PIN);
47# define lcd_e_toggle() toggle_e()
48# define lcd_rw_high() LCD_RW_PORT |= _BV(LCD_RW_PIN)
49# define lcd_rw_low() LCD_RW_PORT &= ~_BV(LCD_RW_PIN)
50# define lcd_rs_high() LCD_RS_PORT |= _BV(LCD_RS_PIN)
51# define lcd_rs_low() LCD_RS_PORT &= ~_BV(LCD_RS_PIN)
52#endif
53
54#if LCD_IO_MODE
55# if LCD_LINES == 1
56# define LCD_FUNCTION_DEFAULT LCD_FUNCTION_4BIT_1LINE
57# else
58# define LCD_FUNCTION_DEFAULT LCD_FUNCTION_4BIT_2LINES
59# endif
60#else
61# if LCD_LINES == 1
62# define LCD_FUNCTION_DEFAULT LCD_FUNCTION_8BIT_1LINE
63# else
64# define LCD_FUNCTION_DEFAULT LCD_FUNCTION_8BIT_2LINES
65# endif
66#endif
67
68#if LCD_CONTROLLER_KS0073
69# if LCD_LINES == 4
70
71# define KS0073_EXTENDED_FUNCTION_REGISTER_ON 0x2C /* |0|010|1100 4-bit mode, extension-bit RE = 1 */
72# define KS0073_EXTENDED_FUNCTION_REGISTER_OFF 0x28 /* |0|010|1000 4-bit mode, extension-bit RE = 0 */
73# define KS0073_4LINES_MODE 0x09 /* |0|000|1001 4 lines mode */
74
75# endif
76#endif
77
78/*
79** function prototypes
80*/
81#if LCD_IO_MODE
82static void toggle_e(void);
83#endif
84
85/*
86** local functions
87*/
88
89/*************************************************************************
90delay for a minimum of <us> microseconds
91the number of loops is calculated at compile-time from MCU clock frequency
92*************************************************************************/
93#define delay(us) _delay_us(us)
94
95#if LCD_IO_MODE
96/* toggle Enable Pin to initiate write */
97static void toggle_e(void) {
98 lcd_e_high();
99 lcd_e_delay();
100 lcd_e_low();
101}
102#endif
103
104/*************************************************************************
105Low-level function to write byte to LCD controller
106Input: data byte to write to LCD
107 rs 1: write data
108 0: write instruction
109Returns: none
110*************************************************************************/
111#if LCD_IO_MODE
112static void lcd_write(uint8_t data, uint8_t rs) {
113 unsigned char dataBits;
114
115 if (rs) { /* write data (RS=1, RW=0) */
116 lcd_rs_high();
117 } else { /* write instruction (RS=0, RW=0) */
118 lcd_rs_low();
119 }
120 lcd_rw_low(); /* RW=0 write mode */
121
122 if ((&LCD_DATA0_PORT == &LCD_DATA1_PORT) && (&LCD_DATA1_PORT == &LCD_DATA2_PORT) && (&LCD_DATA2_PORT == &LCD_DATA3_PORT) && (LCD_DATA0_PIN == 0) && (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3)) {
123 /* configure data pins as output */
124 DDR(LCD_DATA0_PORT) |= 0x0F;
125
126 /* output high nibble first */
127 dataBits = LCD_DATA0_PORT & 0xF0;
128 LCD_DATA0_PORT = dataBits | ((data >> 4) & 0x0F);
129 lcd_e_toggle();
130
131 /* output low nibble */
132 LCD_DATA0_PORT = dataBits | (data & 0x0F);
133 lcd_e_toggle();
134
135 /* all data pins high (inactive) */
136 LCD_DATA0_PORT = dataBits | 0x0F;
137 } else {
138 /* configure data pins as output */
139 DDR(LCD_DATA0_PORT) |= _BV(LCD_DATA0_PIN);
140 DDR(LCD_DATA1_PORT) |= _BV(LCD_DATA1_PIN);
141 DDR(LCD_DATA2_PORT) |= _BV(LCD_DATA2_PIN);
142 DDR(LCD_DATA3_PORT) |= _BV(LCD_DATA3_PIN);
143
144 /* output high nibble first */
145 LCD_DATA3_PORT &= ~_BV(LCD_DATA3_PIN);
146 LCD_DATA2_PORT &= ~_BV(LCD_DATA2_PIN);
147 LCD_DATA1_PORT &= ~_BV(LCD_DATA1_PIN);
148 LCD_DATA0_PORT &= ~_BV(LCD_DATA0_PIN);
149 if (data & 0x80) LCD_DATA3_PORT |= _BV(LCD_DATA3_PIN);
150 if (data & 0x40) LCD_DATA2_PORT |= _BV(LCD_DATA2_PIN);
151 if (data & 0x20) LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN);
152 if (data & 0x10) LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN);
153 lcd_e_toggle();
154
155 /* output low nibble */
156 LCD_DATA3_PORT &= ~_BV(LCD_DATA3_PIN);
157 LCD_DATA2_PORT &= ~_BV(LCD_DATA2_PIN);
158 LCD_DATA1_PORT &= ~_BV(LCD_DATA1_PIN);
159 LCD_DATA0_PORT &= ~_BV(LCD_DATA0_PIN);
160 if (data & 0x08) LCD_DATA3_PORT |= _BV(LCD_DATA3_PIN);
161 if (data & 0x04) LCD_DATA2_PORT |= _BV(LCD_DATA2_PIN);
162 if (data & 0x02) LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN);
163 if (data & 0x01) LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN);
164 lcd_e_toggle();
165
166 /* all data pins high (inactive) */
167 LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN);
168 LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN);
169 LCD_DATA2_PORT |= _BV(LCD_DATA2_PIN);
170 LCD_DATA3_PORT |= _BV(LCD_DATA3_PIN);
171 }
172}
173#else
174# define lcd_write(d, rs) \
175 if (rs) \
176 *(volatile uint8_t *)(LCD_IO_DATA) = d; \
177 else \
178 *(volatile uint8_t *)(LCD_IO_FUNCTION) = d;
179/* rs==0 -> write instruction to LCD_IO_FUNCTION */
180/* rs==1 -> write data to LCD_IO_DATA */
181#endif
182
183/*************************************************************************
184Low-level function to read byte from LCD controller
185Input: rs 1: read data
186 0: read busy flag / address counter
187Returns: byte read from LCD controller
188*************************************************************************/
189#if LCD_IO_MODE
190static uint8_t lcd_read(uint8_t rs) {
191 uint8_t data;
192
193 if (rs)
194 lcd_rs_high(); /* RS=1: read data */
195 else
196 lcd_rs_low(); /* RS=0: read busy flag */
197 lcd_rw_high(); /* RW=1 read mode */
198
199 if ((&LCD_DATA0_PORT == &LCD_DATA1_PORT) && (&LCD_DATA1_PORT == &LCD_DATA2_PORT) && (&LCD_DATA2_PORT == &LCD_DATA3_PORT) && (LCD_DATA0_PIN == 0) && (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3)) {
200 DDR(LCD_DATA0_PORT) &= 0xF0; /* configure data pins as input */
201
202 lcd_e_high();
203 lcd_e_delay();
204 data = PIN(LCD_DATA0_PORT) << 4; /* read high nibble first */
205 lcd_e_low();
206
207 lcd_e_delay(); /* Enable 500ns low */
208
209 lcd_e_high();
210 lcd_e_delay();
211 data |= PIN(LCD_DATA0_PORT) & 0x0F; /* read low nibble */
212 lcd_e_low();
213 } else {
214 /* configure data pins as input */
215 DDR(LCD_DATA0_PORT) &= ~_BV(LCD_DATA0_PIN);
216 DDR(LCD_DATA1_PORT) &= ~_BV(LCD_DATA1_PIN);
217 DDR(LCD_DATA2_PORT) &= ~_BV(LCD_DATA2_PIN);
218 DDR(LCD_DATA3_PORT) &= ~_BV(LCD_DATA3_PIN);
219
220 /* read high nibble first */
221 lcd_e_high();
222 lcd_e_delay();
223 data = 0;
224 if (PIN(LCD_DATA0_PORT) & _BV(LCD_DATA0_PIN)) data |= 0x10;
225 if (PIN(LCD_DATA1_PORT) & _BV(LCD_DATA1_PIN)) data |= 0x20;
226 if (PIN(LCD_DATA2_PORT) & _BV(LCD_DATA2_PIN)) data |= 0x40;
227 if (PIN(LCD_DATA3_PORT) & _BV(LCD_DATA3_PIN)) data |= 0x80;
228 lcd_e_low();
229
230 lcd_e_delay(); /* Enable 500ns low */
231
232 /* read low nibble */
233 lcd_e_high();
234 lcd_e_delay();
235 if (PIN(LCD_DATA0_PORT) & _BV(LCD_DATA0_PIN)) data |= 0x01;
236 if (PIN(LCD_DATA1_PORT) & _BV(LCD_DATA1_PIN)) data |= 0x02;
237 if (PIN(LCD_DATA2_PORT) & _BV(LCD_DATA2_PIN)) data |= 0x04;
238 if (PIN(LCD_DATA3_PORT) & _BV(LCD_DATA3_PIN)) data |= 0x08;
239 lcd_e_low();
240 }
241 return data;
242}
243#else
244# define lcd_read(rs) (rs) ? *(volatile uint8_t *)(LCD_IO_DATA + LCD_IO_READ) : *(volatile uint8_t *)(LCD_IO_FUNCTION + LCD_IO_READ)
245/* rs==0 -> read instruction from LCD_IO_FUNCTION */
246/* rs==1 -> read data from LCD_IO_DATA */
247#endif
248
249/*************************************************************************
250loops while lcd is busy, returns address counter
251*************************************************************************/
252static uint8_t lcd_waitbusy(void)
253
254{
255 register uint8_t c;
256
257 /* wait until busy flag is cleared */
258 while ((c = lcd_read(0)) & (1 << LCD_BUSY)) {
259 }
260
261 /* the address counter is updated 4us after the busy flag is cleared */
262 delay(LCD_DELAY_BUSY_FLAG);
263
264 /* now read the address counter */
265 return (lcd_read(0)); // return address counter
266
267} /* lcd_waitbusy */
268
269/*************************************************************************
270Move cursor to the start of next line or to the first line if the cursor
271is already on the last line.
272*************************************************************************/
273static inline void lcd_newline(uint8_t pos) {
274 register uint8_t addressCounter;
275
276#if LCD_LINES == 1
277 addressCounter = 0;
278#endif
279#if LCD_LINES == 2
280 if (pos < (LCD_START_LINE2))
281 addressCounter = LCD_START_LINE2;
282 else
283 addressCounter = LCD_START_LINE1;
284#endif
285#if LCD_LINES == 4
286# if KS0073_4LINES_MODE
287 if (pos < LCD_START_LINE2)
288 addressCounter = LCD_START_LINE2;
289 else if ((pos >= LCD_START_LINE2) && (pos < LCD_START_LINE3))
290 addressCounter = LCD_START_LINE3;
291 else if ((pos >= LCD_START_LINE3) && (pos < LCD_START_LINE4))
292 addressCounter = LCD_START_LINE4;
293 else
294 addressCounter = LCD_START_LINE1;
295# else
296 if (pos < LCD_START_LINE3)
297 addressCounter = LCD_START_LINE2;
298 else if ((pos >= LCD_START_LINE2) && (pos < LCD_START_LINE4))
299 addressCounter = LCD_START_LINE3;
300 else if ((pos >= LCD_START_LINE3) && (pos < LCD_START_LINE2))
301 addressCounter = LCD_START_LINE4;
302 else
303 addressCounter = LCD_START_LINE1;
304# endif
305#endif
306 lcd_command((1 << LCD_DDRAM) + addressCounter);
307
308} /* lcd_newline */
309
310/*
311** PUBLIC FUNCTIONS
312*/
313
314/*************************************************************************
315Send LCD controller instruction command
316Input: instruction to send to LCD controller, see HD44780 data sheet
317Returns: none
318*************************************************************************/
319void lcd_command(uint8_t cmd) {
320 lcd_waitbusy();
321 lcd_write(cmd, 0);
322}
323
324/*************************************************************************
325Send data byte to LCD controller
326Input: data to send to LCD controller, see HD44780 data sheet
327Returns: none
328*************************************************************************/
329void lcd_data(uint8_t data) {
330 lcd_waitbusy();
331 lcd_write(data, 1);
332}
333
334/*************************************************************************
335Set cursor to specified position
336Input: x horizontal position (0: left most position)
337 y vertical position (0: first line)
338Returns: none
339*************************************************************************/
340void lcd_gotoxy(uint8_t x, uint8_t y) {
341#if LCD_LINES == 1
342 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE1 + x);
343#endif
344#if LCD_LINES == 2
345 if (y == 0)
346 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE1 + x);
347 else
348 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE2 + x);
349#endif
350#if LCD_LINES == 4
351 if (y == 0)
352 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE1 + x);
353 else if (y == 1)
354 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE2 + x);
355 else if (y == 2)
356 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE3 + x);
357 else /* y==3 */
358 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE4 + x);
359#endif
360
361} /* lcd_gotoxy */
362
363/*************************************************************************
364*************************************************************************/
365int lcd_getxy(void) { return lcd_waitbusy(); }
366
367/*************************************************************************
368Clear display and set cursor to home position
369*************************************************************************/
370void lcd_clrscr(void) { lcd_command(1 << LCD_CLR); }
371
372/*************************************************************************
373Set cursor to home position
374*************************************************************************/
375void lcd_home(void) { lcd_command(1 << LCD_HOME); }
376
377/*************************************************************************
378Display character at current cursor position
379Input: character to be displayed
380Returns: none
381*************************************************************************/
382void lcd_putc(char c) {
383 uint8_t pos;
384
385 pos = lcd_waitbusy(); // read busy-flag and address counter
386 if (c == '\n') {
387 lcd_newline(pos);
388 } else {
389#if LCD_WRAP_LINES == 1
390# if LCD_LINES == 1
391 if (pos == LCD_START_LINE1 + LCD_DISP_LENGTH) {
392 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE1, 0);
393 }
394# elif LCD_LINES == 2
395 if (pos == LCD_START_LINE1 + LCD_DISP_LENGTH) {
396 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE2, 0);
397 } else if (pos == LCD_START_LINE2 + LCD_DISP_LENGTH) {
398 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE1, 0);
399 }
400# elif LCD_LINES == 4
401 if (pos == LCD_START_LINE1 + LCD_DISP_LENGTH) {
402 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE2, 0);
403 } else if (pos == LCD_START_LINE2 + LCD_DISP_LENGTH) {
404 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE3, 0);
405 } else if (pos == LCD_START_LINE3 + LCD_DISP_LENGTH) {
406 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE4, 0);
407 } else if (pos == LCD_START_LINE4 + LCD_DISP_LENGTH) {
408 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE1, 0);
409 }
410# endif
411 lcd_waitbusy();
412#endif
413 lcd_write(c, 1);
414 }
415
416} /* lcd_putc */
417
418/*************************************************************************
419Display string without auto linefeed
420Input: string to be displayed
421Returns: none
422*************************************************************************/
423void lcd_puts(const char *s)
424/* print string on lcd (no auto linefeed) */
425{
426 register char c;
427
428 while ((c = *s++)) {
429 lcd_putc(c);
430 }
431
432} /* lcd_puts */
433
434/*************************************************************************
435Display string from program memory without auto linefeed
436Input: string from program memory be be displayed
437Returns: none
438*************************************************************************/
439void lcd_puts_p(const char *progmem_s)
440/* print string from program memory on lcd (no auto linefeed) */
441{
442 register char c;
443
444 while ((c = pgm_read_byte(progmem_s++))) {
445 lcd_putc(c);
446 }
447
448} /* lcd_puts_p */
449
450/*************************************************************************
451Initialize display and select type of cursor
452Input: dispAttr LCD_DISP_OFF display off
453 LCD_DISP_ON display on, cursor off
454 LCD_DISP_ON_CURSOR display on, cursor on
455 LCD_DISP_CURSOR_BLINK display on, cursor on flashing
456Returns: none
457*************************************************************************/
458void lcd_init(uint8_t dispAttr) {
459#if LCD_IO_MODE
460 /*
461 * Initialize LCD to 4 bit I/O mode
462 */
463
464 if ((&LCD_DATA0_PORT == &LCD_DATA1_PORT) && (&LCD_DATA1_PORT == &LCD_DATA2_PORT) && (&LCD_DATA2_PORT == &LCD_DATA3_PORT) && (&LCD_RS_PORT == &LCD_DATA0_PORT) && (&LCD_RW_PORT == &LCD_DATA0_PORT) && (&LCD_E_PORT == &LCD_DATA0_PORT) && (LCD_DATA0_PIN == 0) && (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3) && (LCD_RS_PIN == 4) && (LCD_RW_PIN == 5) && (LCD_E_PIN == 6)) {
465 /* configure all port bits as output (all LCD lines on same port) */
466 DDR(LCD_DATA0_PORT) |= 0x7F;
467 } else if ((&LCD_DATA0_PORT == &LCD_DATA1_PORT) && (&LCD_DATA1_PORT == &LCD_DATA2_PORT) && (&LCD_DATA2_PORT == &LCD_DATA3_PORT) && (LCD_DATA0_PIN == 0) && (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3)) {
468 /* configure all port bits as output (all LCD data lines on same port, but control lines on different ports) */
469 DDR(LCD_DATA0_PORT) |= 0x0F;
470 DDR(LCD_RS_PORT) |= _BV(LCD_RS_PIN);
471 DDR(LCD_RW_PORT) |= _BV(LCD_RW_PIN);
472 DDR(LCD_E_PORT) |= _BV(LCD_E_PIN);
473 } else {
474 /* configure all port bits as output (LCD data and control lines on different ports */
475 DDR(LCD_RS_PORT) |= _BV(LCD_RS_PIN);
476 DDR(LCD_RW_PORT) |= _BV(LCD_RW_PIN);
477 DDR(LCD_E_PORT) |= _BV(LCD_E_PIN);
478 DDR(LCD_DATA0_PORT) |= _BV(LCD_DATA0_PIN);
479 DDR(LCD_DATA1_PORT) |= _BV(LCD_DATA1_PIN);
480 DDR(LCD_DATA2_PORT) |= _BV(LCD_DATA2_PIN);
481 DDR(LCD_DATA3_PORT) |= _BV(LCD_DATA3_PIN);
482 }
483 delay(LCD_DELAY_BOOTUP); /* wait 16ms or more after power-on */
484
485 /* initial write to lcd is 8bit */
486 LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN); // LCD_FUNCTION>>4;
487 LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN); // LCD_FUNCTION_8BIT>>4;
488 lcd_e_toggle();
489 delay(LCD_DELAY_INIT); /* delay, busy flag can't be checked here */
490
491 /* repeat last command */
492 lcd_e_toggle();
493 delay(LCD_DELAY_INIT_REP); /* delay, busy flag can't be checked here */
494
495 /* repeat last command a third time */
496 lcd_e_toggle();
497 delay(LCD_DELAY_INIT_REP); /* delay, busy flag can't be checked here */
498
499 /* now configure for 4bit mode */
500 LCD_DATA0_PORT &= ~_BV(LCD_DATA0_PIN); // LCD_FUNCTION_4BIT_1LINE>>4
501 lcd_e_toggle();
502 delay(LCD_DELAY_INIT_4BIT); /* some displays need this additional delay */
503
504 /* from now the LCD only accepts 4 bit I/O, we can use lcd_command() */
505#else
506 /*
507 * Initialize LCD to 8 bit memory mapped mode
508 */
509
510 /* enable external SRAM (memory mapped lcd) and one wait state */
511 MCUCR = _BV(SRE) | _BV(SRW);
512
513 /* reset LCD */
514 delay(LCD_DELAY_BOOTUP); /* wait 16ms after power-on */
515 lcd_write(LCD_FUNCTION_8BIT_1LINE, 0); /* function set: 8bit interface */
516 delay(LCD_DELAY_INIT); /* wait 5ms */
517 lcd_write(LCD_FUNCTION_8BIT_1LINE, 0); /* function set: 8bit interface */
518 delay(LCD_DELAY_INIT_REP); /* wait 64us */
519 lcd_write(LCD_FUNCTION_8BIT_1LINE, 0); /* function set: 8bit interface */
520 delay(LCD_DELAY_INIT_REP); /* wait 64us */
521#endif
522
523#if KS0073_4LINES_MODE
524 /* Display with KS0073 controller requires special commands for enabling 4 line mode */
525 lcd_command(KS0073_EXTENDED_FUNCTION_REGISTER_ON);
526 lcd_command(KS0073_4LINES_MODE);
527 lcd_command(KS0073_EXTENDED_FUNCTION_REGISTER_OFF);
528#else
529 lcd_command(LCD_FUNCTION_DEFAULT); /* function set: display lines */
530#endif
531 lcd_command(LCD_DISP_OFF); /* display off */
532 lcd_clrscr(); /* display clear */
533 lcd_command(LCD_MODE_DEFAULT); /* set entry mode */
534 lcd_command(dispAttr); /* display/cursor control */
535
536} /* lcd_init */
diff --git a/drivers/avr/hd44780.h b/drivers/avr/hd44780.h
deleted file mode 100644
index 08e60f8a4..000000000
--- a/drivers/avr/hd44780.h
+++ /dev/null
@@ -1,348 +0,0 @@
1/*************************************************************************
2 Title : C include file for the HD44780U LCD library (lcd.c)
3 Author: Peter Fleury <pfleury@gmx.ch> http://tinyurl.com/peterfleury
4 License: GNU General Public License Version 3
5 File: $Id: lcd.h,v 1.14.2.4 2015/01/20 17:16:07 peter Exp $
6 Software: AVR-GCC 4.x
7 Hardware: any AVR device, memory mapped mode only for AVR with
8 memory mapped interface (AT90S8515/ATmega8515/ATmega128)
9***************************************************************************/
10
11/**
12 @mainpage
13 Collection of libraries for AVR-GCC
14 @author Peter Fleury pfleury@gmx.ch http://tinyurl.com/peterfleury
15 @copyright (C) 2015 Peter Fleury, GNU General Public License Version 3
16
17 @file
18 @defgroup pfleury_lcd LCD library <lcd.h>
19 @code #include <lcd.h> @endcode
20
21 @brief Basic routines for interfacing a HD44780U-based character LCD display
22
23 LCD character displays can be found in many devices, like espresso machines, laser printers.
24 The Hitachi HD44780 controller and its compatible controllers like Samsung KS0066U have become an industry standard for these types of displays.
25
26 This library allows easy interfacing with a HD44780 compatible display and can be
27 operated in memory mapped mode (LCD_IO_MODE defined as 0 in the include file lcd.h.) or in
28 4-bit IO port mode (LCD_IO_MODE defined as 1). 8-bit IO port mode is not supported.
29
30 Memory mapped mode is compatible with old Kanda STK200 starter kit, but also supports
31 generation of R/W signal through A8 address line.
32
33 @see The chapter <a href=" http://homepage.hispeed.ch/peterfleury/avr-lcd44780.html" target="_blank">Interfacing a HD44780 Based LCD to an AVR</a>
34 on my home page, which shows example circuits how to connect an LCD to an AVR controller.
35
36 @author Peter Fleury pfleury@gmx.ch http://tinyurl.com/peterfleury
37
38 @version 2.0
39
40 @copyright (C) 2015 Peter Fleury, GNU General Public License Version 3
41
42*/
43
44#pragma once
45
46#include <inttypes.h>
47#include <avr/pgmspace.h>
48
49#if (__GNUC__ * 100 + __GNUC_MINOR__) < 405
50# error "This library requires AVR-GCC 4.5 or later, update to newer AVR-GCC compiler !"
51#endif
52
53/**@{*/
54
55/*
56 * LCD and target specific definitions below can be defined in a separate include file with name lcd_definitions.h instead modifying this file
57 * by adding -D_LCD_DEFINITIONS_FILE to the CDEFS section in the Makefile
58 * All definitions added to the file lcd_definitions.h will override the default definitions from lcd.h
59 */
60#ifdef _LCD_DEFINITIONS_FILE
61# include "lcd_definitions.h"
62#endif
63
64/**
65 * @name Definition for LCD controller type
66 * Use 0 for HD44780 controller, change to 1 for displays with KS0073 controller.
67 */
68#ifndef LCD_CONTROLLER_KS0073
69# define LCD_CONTROLLER_KS0073 0 /**< Use 0 for HD44780 controller, 1 for KS0073 controller */
70#endif
71
72/**
73 * @name Definitions for Display Size
74 * Change these definitions to adapt setting to your display
75 *
76 * These definitions can be defined in a separate include file \b lcd_definitions.h instead modifying this file by
77 * adding -D_LCD_DEFINITIONS_FILE to the CDEFS section in the Makefile.
78 * All definitions added to the file lcd_definitions.h will override the default definitions from lcd.h
79 *
80 */
81#ifndef LCD_LINES
82# define LCD_LINES 2 /**< number of visible lines of the display */
83#endif
84#ifndef LCD_DISP_LENGTH
85# define LCD_DISP_LENGTH 16 /**< visibles characters per line of the display */
86#endif
87#ifndef LCD_LINE_LENGTH
88# define LCD_LINE_LENGTH 0x40 /**< internal line length of the display */
89#endif
90#ifndef LCD_START_LINE1
91# define LCD_START_LINE1 0x00 /**< DDRAM address of first char of line 1 */
92#endif
93#ifndef LCD_START_LINE2
94# define LCD_START_LINE2 0x40 /**< DDRAM address of first char of line 2 */
95#endif
96#ifndef LCD_START_LINE3
97# define LCD_START_LINE3 0x14 /**< DDRAM address of first char of line 3 */
98#endif
99#ifndef LCD_START_LINE4
100# define LCD_START_LINE4 0x54 /**< DDRAM address of first char of line 4 */
101#endif
102#ifndef LCD_WRAP_LINES
103# define LCD_WRAP_LINES 0 /**< 0: no wrap, 1: wrap at end of visibile line */
104#endif
105
106/**
107 * @name Definitions for 4-bit IO mode
108 *
109 * The four LCD data lines and the three control lines RS, RW, E can be on the
110 * same port or on different ports.
111 * Change LCD_RS_PORT, LCD_RW_PORT, LCD_E_PORT if you want the control lines on
112 * different ports.
113 *
114 * Normally the four data lines should be mapped to bit 0..3 on one port, but it
115 * is possible to connect these data lines in different order or even on different
116 * ports by adapting the LCD_DATAx_PORT and LCD_DATAx_PIN definitions.
117 *
118 * Adjust these definitions to your target.\n
119 * These definitions can be defined in a separate include file \b lcd_definitions.h instead modifying this file by
120 * adding \b -D_LCD_DEFINITIONS_FILE to the \b CDEFS section in the Makefile.
121 * All definitions added to the file lcd_definitions.h will override the default definitions from lcd.h
122 *
123 */
124#define LCD_IO_MODE 1 /**< 0: memory mapped mode, 1: IO port mode */
125
126#if LCD_IO_MODE
127
128# ifndef LCD_PORT
129# define LCD_PORT PORTA /**< port for the LCD lines */
130# endif
131# ifndef LCD_DATA0_PORT
132# define LCD_DATA0_PORT LCD_PORT /**< port for 4bit data bit 0 */
133# endif
134# ifndef LCD_DATA1_PORT
135# define LCD_DATA1_PORT LCD_PORT /**< port for 4bit data bit 1 */
136# endif
137# ifndef LCD_DATA2_PORT
138# define LCD_DATA2_PORT LCD_PORT /**< port for 4bit data bit 2 */
139# endif
140# ifndef LCD_DATA3_PORT
141# define LCD_DATA3_PORT LCD_PORT /**< port for 4bit data bit 3 */
142# endif
143# ifndef LCD_DATA0_PIN
144# define LCD_DATA0_PIN 4 /**< pin for 4bit data bit 0 */
145# endif
146# ifndef LCD_DATA1_PIN
147# define LCD_DATA1_PIN 5 /**< pin for 4bit data bit 1 */
148# endif
149# ifndef LCD_DATA2_PIN
150# define LCD_DATA2_PIN 6 /**< pin for 4bit data bit 2 */
151# endif
152# ifndef LCD_DATA3_PIN
153# define LCD_DATA3_PIN 7 /**< pin for 4bit data bit 3 */
154# endif
155# ifndef LCD_RS_PORT
156# define LCD_RS_PORT LCD_PORT /**< port for RS line */
157# endif
158# ifndef LCD_RS_PIN
159# define LCD_RS_PIN 3 /**< pin for RS line */
160# endif
161# ifndef LCD_RW_PORT
162# define LCD_RW_PORT LCD_PORT /**< port for RW line */
163# endif
164# ifndef LCD_RW_PIN
165# define LCD_RW_PIN 2 /**< pin for RW line */
166# endif
167# ifndef LCD_E_PORT
168# define LCD_E_PORT LCD_PORT /**< port for Enable line */
169# endif
170# ifndef LCD_E_PIN
171# define LCD_E_PIN 1 /**< pin for Enable line */
172# endif
173
174#elif defined(__AVR_AT90S4414__) || defined(__AVR_AT90S8515__) || defined(__AVR_ATmega64__) || defined(__AVR_ATmega8515__) || defined(__AVR_ATmega103__) || defined(__AVR_ATmega128__) || defined(__AVR_ATmega161__) || defined(__AVR_ATmega162__)
175/*
176 * memory mapped mode is only supported when the device has an external data memory interface
177 */
178# define LCD_IO_DATA 0xC000 /* A15=E=1, A14=RS=1 */
179# define LCD_IO_FUNCTION 0x8000 /* A15=E=1, A14=RS=0 */
180# define LCD_IO_READ 0x0100 /* A8 =R/W=1 (R/W: 1=Read, 0=Write */
181
182#else
183# error "external data memory interface not available for this device, use 4-bit IO port mode"
184
185#endif
186
187/**
188 * @name Definitions of delays
189 * Used to calculate delay timers.
190 * Adapt the F_CPU define in the Makefile to the clock frequency in Hz of your target
191 *
192 * These delay times can be adjusted, if some displays require different delays.\n
193 * These definitions can be defined in a separate include file \b lcd_definitions.h instead modifying this file by
194 * adding \b -D_LCD_DEFINITIONS_FILE to the \b CDEFS section in the Makefile.
195 * All definitions added to the file lcd_definitions.h will override the default definitions from lcd.h
196 */
197#ifndef LCD_DELAY_BOOTUP
198# define LCD_DELAY_BOOTUP 16000 /**< delay in micro seconds after power-on */
199#endif
200#ifndef LCD_DELAY_INIT
201# define LCD_DELAY_INIT 5000 /**< delay in micro seconds after initialization command sent */
202#endif
203#ifndef LCD_DELAY_INIT_REP
204# define LCD_DELAY_INIT_REP 64 /**< delay in micro seconds after initialization command repeated */
205#endif
206#ifndef LCD_DELAY_INIT_4BIT
207# define LCD_DELAY_INIT_4BIT 64 /**< delay in micro seconds after setting 4-bit mode */
208#endif
209#ifndef LCD_DELAY_BUSY_FLAG
210# define LCD_DELAY_BUSY_FLAG 4 /**< time in micro seconds the address counter is updated after busy flag is cleared */
211#endif
212#ifndef LCD_DELAY_ENABLE_PULSE
213# define LCD_DELAY_ENABLE_PULSE 1 /**< enable signal pulse width in micro seconds */
214#endif
215
216/**
217 * @name Definitions for LCD command instructions
218 * The constants define the various LCD controller instructions which can be passed to the
219 * function lcd_command(), see HD44780 data sheet for a complete description.
220 */
221
222/* instruction register bit positions, see HD44780U data sheet */
223#define LCD_CLR 0 /* DB0: clear display */
224#define LCD_HOME 1 /* DB1: return to home position */
225#define LCD_ENTRY_MODE 2 /* DB2: set entry mode */
226#define LCD_ENTRY_INC 1 /* DB1: 1=increment, 0=decrement */
227#define LCD_ENTRY_SHIFT 0 /* DB2: 1=display shift on */
228#define LCD_ON 3 /* DB3: turn lcd/cursor on */
229#define LCD_ON_DISPLAY 2 /* DB2: turn display on */
230#define LCD_ON_CURSOR 1 /* DB1: turn cursor on */
231#define LCD_ON_BLINK 0 /* DB0: blinking cursor ? */
232#define LCD_MOVE 4 /* DB4: move cursor/display */
233#define LCD_MOVE_DISP 3 /* DB3: move display (0-> cursor) ? */
234#define LCD_MOVE_RIGHT 2 /* DB2: move right (0-> left) ? */
235#define LCD_FUNCTION 5 /* DB5: function set */
236#define LCD_FUNCTION_8BIT 4 /* DB4: set 8BIT mode (0->4BIT mode) */
237#define LCD_FUNCTION_2LINES 3 /* DB3: two lines (0->one line) */
238#define LCD_FUNCTION_10DOTS 2 /* DB2: 5x10 font (0->5x7 font) */
239#define LCD_CGRAM 6 /* DB6: set CG RAM address */
240#define LCD_DDRAM 7 /* DB7: set DD RAM address */
241#define LCD_BUSY 7 /* DB7: LCD is busy */
242
243/* set entry mode: display shift on/off, dec/inc cursor move direction */
244#define LCD_ENTRY_DEC 0x04 /* display shift off, dec cursor move dir */
245#define LCD_ENTRY_DEC_SHIFT 0x05 /* display shift on, dec cursor move dir */
246#define LCD_ENTRY_INC_ 0x06 /* display shift off, inc cursor move dir */
247#define LCD_ENTRY_INC_SHIFT 0x07 /* display shift on, inc cursor move dir */
248
249/* display on/off, cursor on/off, blinking char at cursor position */
250#define LCD_DISP_OFF 0x08 /* display off */
251#define LCD_DISP_ON 0x0C /* display on, cursor off */
252#define LCD_DISP_ON_BLINK 0x0D /* display on, cursor off, blink char */
253#define LCD_DISP_ON_CURSOR 0x0E /* display on, cursor on */
254#define LCD_DISP_ON_CURSOR_BLINK 0x0F /* display on, cursor on, blink char */
255
256/* move cursor/shift display */
257#define LCD_MOVE_CURSOR_LEFT 0x10 /* move cursor left (decrement) */
258#define LCD_MOVE_CURSOR_RIGHT 0x14 /* move cursor right (increment) */
259#define LCD_MOVE_DISP_LEFT 0x18 /* shift display left */
260#define LCD_MOVE_DISP_RIGHT 0x1C /* shift display right */
261
262/* function set: set interface data length and number of display lines */
263#define LCD_FUNCTION_4BIT_1LINE 0x20 /* 4-bit interface, single line, 5x7 dots */
264#define LCD_FUNCTION_4BIT_2LINES 0x28 /* 4-bit interface, dual line, 5x7 dots */
265#define LCD_FUNCTION_8BIT_1LINE 0x30 /* 8-bit interface, single line, 5x7 dots */
266#define LCD_FUNCTION_8BIT_2LINES 0x38 /* 8-bit interface, dual line, 5x7 dots */
267
268#define LCD_MODE_DEFAULT ((1 << LCD_ENTRY_MODE) | (1 << LCD_ENTRY_INC))
269
270/**
271 * @name Functions
272 */
273
274/**
275 @brief Initialize display and select type of cursor
276 @param dispAttr \b LCD_DISP_OFF display off\n
277 \b LCD_DISP_ON display on, cursor off\n
278 \b LCD_DISP_ON_CURSOR display on, cursor on\n
279 \b LCD_DISP_ON_CURSOR_BLINK display on, cursor on flashing
280 @return none
281*/
282extern void lcd_init(uint8_t dispAttr);
283
284/**
285 @brief Clear display and set cursor to home position
286 @return none
287*/
288extern void lcd_clrscr(void);
289
290/**
291 @brief Set cursor to home position
292 @return none
293*/
294extern void lcd_home(void);
295
296/**
297 @brief Set cursor to specified position
298
299 @param x horizontal position\n (0: left most position)
300 @param y vertical position\n (0: first line)
301 @return none
302*/
303extern void lcd_gotoxy(uint8_t x, uint8_t y);
304
305/**
306 @brief Display character at current cursor position
307 @param c character to be displayed
308 @return none
309*/
310extern void lcd_putc(char c);
311
312/**
313 @brief Display string without auto linefeed
314 @param s string to be displayed
315 @return none
316*/
317extern void lcd_puts(const char *s);
318
319/**
320 @brief Display string from program memory without auto linefeed
321 @param progmem_s string from program memory be be displayed
322 @return none
323 @see lcd_puts_P
324*/
325extern void lcd_puts_p(const char *progmem_s);
326
327/**
328 @brief Send LCD controller instruction command
329 @param cmd instruction to send to LCD controller, see HD44780 data sheet
330 @return none
331*/
332extern void lcd_command(uint8_t cmd);
333
334/**
335 @brief Send data byte to LCD controller
336
337 Similar to lcd_putc(), but without interpreting LF
338 @param data byte to send to LCD controller, see HD44780 data sheet
339 @return none
340*/
341extern void lcd_data(uint8_t data);
342
343/**
344 @brief macros for automatically storing string constant in program memory
345*/
346#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))
347
348/**@}*/
diff --git a/drivers/avr/i2c_master.c b/drivers/avr/i2c_master.c
deleted file mode 100644
index b1e488529..000000000
--- a/drivers/avr/i2c_master.c
+++ /dev/null
@@ -1,224 +0,0 @@
1/* Copyright (C) 2019 Elia Ritterbusch
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 3 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 <https://www.gnu.org/licenses/>.
15 */
16/* Library made by: g4lvanix
17 * GitHub repository: https://github.com/g4lvanix/I2C-master-lib
18 */
19
20#include <avr/io.h>
21#include <util/twi.h>
22
23#include "i2c_master.h"
24#include "timer.h"
25#include "wait.h"
26
27#ifndef F_SCL
28# define F_SCL 400000UL // SCL frequency
29#endif
30
31#define TWBR_val (((F_CPU / F_SCL) - 16) / 2)
32
33void i2c_init(void) {
34 TWSR = 0; /* no prescaler */
35 TWBR = (uint8_t)TWBR_val;
36
37#ifdef __AVR_ATmega32A__
38 // set pull-up resistors on I2C bus pins
39 PORTC |= 0b11;
40
41 // enable TWI (two-wire interface)
42 TWCR |= (1 << TWEN);
43
44 // enable TWI interrupt and slave address ACK
45 TWCR |= (1 << TWIE);
46 TWCR |= (1 << TWEA);
47#endif
48}
49
50i2c_status_t i2c_start(uint8_t address, uint16_t timeout) {
51 // reset TWI control register
52 TWCR = 0;
53 // transmit START condition
54 TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
55
56 uint16_t timeout_timer = timer_read();
57 while (!(TWCR & (1 << TWINT))) {
58 if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
59 return I2C_STATUS_TIMEOUT;
60 }
61 }
62
63 // check if the start condition was successfully transmitted
64 if (((TW_STATUS & 0xF8) != TW_START) && ((TW_STATUS & 0xF8) != TW_REP_START)) {
65 return I2C_STATUS_ERROR;
66 }
67
68 // load slave address into data register
69 TWDR = address;
70 // start transmission of address
71 TWCR = (1 << TWINT) | (1 << TWEN);
72
73 timeout_timer = timer_read();
74 while (!(TWCR & (1 << TWINT))) {
75 if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
76 return I2C_STATUS_TIMEOUT;
77 }
78 }
79
80 // check if the device has acknowledged the READ / WRITE mode
81 uint8_t twst = TW_STATUS & 0xF8;
82 if ((twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK)) {
83 return I2C_STATUS_ERROR;
84 }
85
86 return I2C_STATUS_SUCCESS;
87}
88
89i2c_status_t i2c_write(uint8_t data, uint16_t timeout) {
90 // load data into data register
91 TWDR = data;
92 // start transmission of data
93 TWCR = (1 << TWINT) | (1 << TWEN);
94
95 uint16_t timeout_timer = timer_read();
96 while (!(TWCR & (1 << TWINT))) {
97 if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
98 return I2C_STATUS_TIMEOUT;
99 }
100 }
101
102 if ((TW_STATUS & 0xF8) != TW_MT_DATA_ACK) {
103 return I2C_STATUS_ERROR;
104 }
105
106 return I2C_STATUS_SUCCESS;
107}
108
109int16_t i2c_read_ack(uint16_t timeout) {
110 // start TWI module and acknowledge data after reception
111 TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
112
113 uint16_t timeout_timer = timer_read();
114 while (!(TWCR & (1 << TWINT))) {
115 if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
116 return I2C_STATUS_TIMEOUT;
117 }
118 }
119
120 // return received data from TWDR
121 return TWDR;
122}
123
124int16_t i2c_read_nack(uint16_t timeout) {
125 // start receiving without acknowledging reception
126 TWCR = (1 << TWINT) | (1 << TWEN);
127
128 uint16_t timeout_timer = timer_read();
129 while (!(TWCR & (1 << TWINT))) {
130 if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
131 return I2C_STATUS_TIMEOUT;
132 }
133 }
134
135 // return received data from TWDR
136 return TWDR;
137}
138
139i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout) {
140 i2c_status_t status = i2c_start(address | I2C_WRITE, timeout);
141
142 for (uint16_t i = 0; i < length && status >= 0; i++) {
143 status = i2c_write(data[i], timeout);
144 }
145
146 i2c_stop();
147
148 return status;
149}
150
151i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) {
152 i2c_status_t status = i2c_start(address | I2C_READ, timeout);
153
154 for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) {
155 status = i2c_read_ack(timeout);
156 if (status >= 0) {
157 data[i] = status;
158 }
159 }
160
161 if (status >= 0) {
162 status = i2c_read_nack(timeout);
163 if (status >= 0) {
164 data[(length - 1)] = status;
165 }
166 }
167
168 i2c_stop();
169
170 return (status < 0) ? status : I2C_STATUS_SUCCESS;
171}
172
173i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) {
174 i2c_status_t status = i2c_start(devaddr | 0x00, timeout);
175 if (status >= 0) {
176 status = i2c_write(regaddr, timeout);
177
178 for (uint16_t i = 0; i < length && status >= 0; i++) {
179 status = i2c_write(data[i], timeout);
180 }
181 }
182
183 i2c_stop();
184
185 return status;
186}
187
188i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {
189 i2c_status_t status = i2c_start(devaddr, timeout);
190 if (status < 0) {
191 goto error;
192 }
193
194 status = i2c_write(regaddr, timeout);
195 if (status < 0) {
196 goto error;
197 }
198
199 status = i2c_start(devaddr | 0x01, timeout);
200
201 for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) {
202 status = i2c_read_ack(timeout);
203 if (status >= 0) {
204 data[i] = status;
205 }
206 }
207
208 if (status >= 0) {
209 status = i2c_read_nack(timeout);
210 if (status >= 0) {
211 data[(length - 1)] = status;
212 }
213 }
214
215error:
216 i2c_stop();
217
218 return (status < 0) ? status : I2C_STATUS_SUCCESS;
219}
220
221void i2c_stop(void) {
222 // transmit STOP condition
223 TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
224}
diff --git a/drivers/avr/i2c_master.h b/drivers/avr/i2c_master.h
deleted file mode 100644
index e5af73364..000000000
--- a/drivers/avr/i2c_master.h
+++ /dev/null
@@ -1,43 +0,0 @@
1/* Copyright (C) 2019 Elia Ritterbusch
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 3 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 <https://www.gnu.org/licenses/>.
15 */
16/* Library made by: g4lvanix
17 * GitHub repository: https://github.com/g4lvanix/I2C-master-lib
18 */
19
20#pragma once
21
22#define I2C_READ 0x01
23#define I2C_WRITE 0x00
24
25typedef int16_t i2c_status_t;
26
27#define I2C_STATUS_SUCCESS (0)
28#define I2C_STATUS_ERROR (-1)
29#define I2C_STATUS_TIMEOUT (-2)
30
31#define I2C_TIMEOUT_IMMEDIATE (0)
32#define I2C_TIMEOUT_INFINITE (0xFFFF)
33
34void i2c_init(void);
35i2c_status_t i2c_start(uint8_t address, uint16_t timeout);
36i2c_status_t i2c_write(uint8_t data, uint16_t timeout);
37int16_t i2c_read_ack(uint16_t timeout);
38int16_t i2c_read_nack(uint16_t timeout);
39i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout);
40i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);
41i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);
42i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
43void i2c_stop(void);
diff --git a/drivers/avr/i2c_slave.c b/drivers/avr/i2c_slave.c
deleted file mode 100644
index 62a378165..000000000
--- a/drivers/avr/i2c_slave.c
+++ /dev/null
@@ -1,86 +0,0 @@
1/* Copyright (C) 2019 Elia Ritterbusch
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 3 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 <https://www.gnu.org/licenses/>.
15 */
16/* Library made by: g4lvanix
17 * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib
18 */
19
20#include <avr/io.h>
21#include <util/twi.h>
22#include <avr/interrupt.h>
23#include <stdbool.h>
24
25#include "i2c_slave.h"
26
27volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];
28
29static volatile uint8_t buffer_address;
30static volatile bool slave_has_register_set = false;
31
32void i2c_slave_init(uint8_t address) {
33 // load address into TWI address register
34 TWAR = address;
35 // set the TWCR to enable address matching and enable TWI, clear TWINT, enable TWI interrupt
36 TWCR = (1 << TWIE) | (1 << TWEA) | (1 << TWINT) | (1 << TWEN);
37}
38
39void i2c_slave_stop(void) {
40 // clear acknowledge and enable bits
41 TWCR &= ~((1 << TWEA) | (1 << TWEN));
42}
43
44ISR(TWI_vect) {
45 uint8_t ack = 1;
46
47 switch (TW_STATUS) {
48 case TW_SR_SLA_ACK:
49 // The device is now a slave receiver
50 slave_has_register_set = false;
51 break;
52
53 case TW_SR_DATA_ACK:
54 // This device is a slave receiver and has received data
55 // First byte is the location then the bytes will be writen in buffer with auto-incriment
56 if (!slave_has_register_set) {
57 buffer_address = TWDR;
58
59 if (buffer_address >= I2C_SLAVE_REG_COUNT) { // address out of bounds dont ack
60 ack = 0;
61 buffer_address = 0;
62 }
63 slave_has_register_set = true; // address has been receaved now fill in buffer
64 } else {
65 i2c_slave_reg[buffer_address] = TWDR;
66 buffer_address++;
67 }
68 break;
69
70 case TW_ST_SLA_ACK:
71 case TW_ST_DATA_ACK:
72 // This device is a slave transmitter and master has requested data
73 TWDR = i2c_slave_reg[buffer_address];
74 buffer_address++;
75 break;
76
77 case TW_BUS_ERROR:
78 // We got an error, reset i2c
79 TWCR = 0;
80 default:
81 break;
82 }
83
84 // Reset i2c state machine to be ready for next interrupt
85 TWCR |= (1 << TWIE) | (1 << TWINT) | (ack << TWEA) | (1 << TWEN);
86}
diff --git a/drivers/avr/i2c_slave.h b/drivers/avr/i2c_slave.h
deleted file mode 100644
index 1cd0625ef..000000000
--- a/drivers/avr/i2c_slave.h
+++ /dev/null
@@ -1,30 +0,0 @@
1/* Copyright (C) 2019 Elia Ritterbusch
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 3 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 <https://www.gnu.org/licenses/>.
15 */
16/* Library made by: g4lvanix
17 * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib
18
19 Info: Inititate the library by giving the required address.
20 Read or write to the necessary buffer according to the opperation.
21 */
22
23#pragma once
24
25#define I2C_SLAVE_REG_COUNT 30
26
27extern volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];
28
29void i2c_slave_init(uint8_t address);
30void i2c_slave_stop(void);
diff --git a/drivers/avr/serial.c b/drivers/avr/serial.c
deleted file mode 100644
index 3647bee0d..000000000
--- a/drivers/avr/serial.c
+++ /dev/null
@@ -1,563 +0,0 @@
1/*
2 * WARNING: be careful changing this code, it is very timing dependent
3 *
4 * 2018-10-28 checked
5 * avr-gcc 4.9.2
6 * avr-gcc 5.4.0
7 * avr-gcc 7.3.0
8 */
9
10#ifndef F_CPU
11# define F_CPU 16000000
12#endif
13
14#include <avr/io.h>
15#include <avr/interrupt.h>
16#include <util/delay.h>
17#include <stddef.h>
18#include <stdbool.h>
19#include "serial.h"
20
21#ifdef SOFT_SERIAL_PIN
22
23# if !(defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))
24# error serial.c is not supported for the currently selected MCU
25# endif
26// if using ATmega32U4/2, AT90USBxxx I2C, can not use PD0 and PD1 in soft serial.
27# if defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
28# if defined(USE_AVR_I2C) && (SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1)
29# error Using I2C, so can not use PD0, PD1
30# endif
31# endif
32// PD0..PD3, common config
33# if SOFT_SERIAL_PIN == D0
34# define EIMSK_BIT _BV(INT0)
35# define EICRx_BIT (~(_BV(ISC00) | _BV(ISC01)))
36# define SERIAL_PIN_INTERRUPT INT0_vect
37# define EICRx EICRA
38# elif SOFT_SERIAL_PIN == D1
39# define EIMSK_BIT _BV(INT1)
40# define EICRx_BIT (~(_BV(ISC10) | _BV(ISC11)))
41# define SERIAL_PIN_INTERRUPT INT1_vect
42# define EICRx EICRA
43# elif SOFT_SERIAL_PIN == D2
44# define EIMSK_BIT _BV(INT2)
45# define EICRx_BIT (~(_BV(ISC20) | _BV(ISC21)))
46# define SERIAL_PIN_INTERRUPT INT2_vect
47# define EICRx EICRA
48# elif SOFT_SERIAL_PIN == D3
49# define EIMSK_BIT _BV(INT3)
50# define EICRx_BIT (~(_BV(ISC30) | _BV(ISC31)))
51# define SERIAL_PIN_INTERRUPT INT3_vect
52# define EICRx EICRA
53# endif
54
55// ATmegaxxU2/AT90USB162 specific config
56# if defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_AT90USB162__)
57// PD4(INT5), PD6(INT6), PD7(INT7), PC7(INT4)
58# if SOFT_SERIAL_PIN == D4
59# define EIMSK_BIT _BV(INT5)
60# define EICRx_BIT (~(_BV(ISC50) | _BV(ISC51)))
61# define SERIAL_PIN_INTERRUPT INT5_vect
62# define EICRx EICRB
63# elif SOFT_SERIAL_PIN == D6
64# define EIMSK_BIT _BV(INT6)
65# define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61)))
66# define SERIAL_PIN_INTERRUPT INT6_vect
67# define EICRx EICRB
68# elif SOFT_SERIAL_PIN == D7
69# define EIMSK_BIT _BV(INT7)
70# define EICRx_BIT (~(_BV(ISC70) | _BV(ISC71)))
71# define SERIAL_PIN_INTERRUPT INT7_vect
72# define EICRx EICRB
73# elif SOFT_SERIAL_PIN == C7
74# define EIMSK_BIT _BV(INT4)
75# define EICRx_BIT (~(_BV(ISC40) | _BV(ISC41)))
76# define SERIAL_PIN_INTERRUPT INT4_vect
77# define EICRx EICRB
78# endif
79# endif
80
81// ATmegaxxU4 specific config
82# if defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)
83// PE6(INT6)
84# if SOFT_SERIAL_PIN == E6
85# define EIMSK_BIT _BV(INT6)
86# define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61)))
87# define SERIAL_PIN_INTERRUPT INT6_vect
88# define EICRx EICRB
89# endif
90# endif
91
92// AT90USBxxx specific config
93# if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
94// PE4..PE7(INT4..INT7)
95# if SOFT_SERIAL_PIN == E4
96# define EIMSK_BIT _BV(INT4)
97# define EICRx_BIT (~(_BV(ISC40) | _BV(ISC41)))
98# define SERIAL_PIN_INTERRUPT INT4_vect
99# define EICRx EICRB
100# elif SOFT_SERIAL_PIN == E5
101# define EIMSK_BIT _BV(INT5)
102# define EICRx_BIT (~(_BV(ISC50) | _BV(ISC51)))
103# define SERIAL_PIN_INTERRUPT INT5_vect
104# define EICRx EICRB
105# elif SOFT_SERIAL_PIN == E6
106# define EIMSK_BIT _BV(INT6)
107# define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61)))
108# define SERIAL_PIN_INTERRUPT INT6_vect
109# define EICRx EICRB
110# elif SOFT_SERIAL_PIN == E7
111# define EIMSK_BIT _BV(INT7)
112# define EICRx_BIT (~(_BV(ISC70) | _BV(ISC71)))
113# define SERIAL_PIN_INTERRUPT INT7_vect
114# define EICRx EICRB
115# endif
116# endif
117
118# ifndef SERIAL_PIN_INTERRUPT
119# error invalid SOFT_SERIAL_PIN value
120# endif
121
122# define setPinInputHigh(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) |= _BV((pin)&0xF))
123# define setPinOutput(pin) (DDRx_ADDRESS(pin) |= _BV((pin)&0xF))
124# define writePinHigh(pin) (PORTx_ADDRESS(pin) |= _BV((pin)&0xF))
125# define writePinLow(pin) (PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF))
126# define readPin(pin) ((bool)(PINx_ADDRESS(pin) & _BV((pin)&0xF)))
127
128# define ALWAYS_INLINE __attribute__((always_inline))
129# define NO_INLINE __attribute__((noinline))
130# define _delay_sub_us(x) __builtin_avr_delay_cycles(x)
131
132// parity check
133# define ODD_PARITY 1
134# define EVEN_PARITY 0
135# define PARITY EVEN_PARITY
136
137# ifdef SERIAL_DELAY
138// custom setup in config.h
139// #define TID_SEND_ADJUST 2
140// #define SERIAL_DELAY 6 // micro sec
141// #define READ_WRITE_START_ADJUST 30 // cycles
142// #define READ_WRITE_WIDTH_ADJUST 8 // cycles
143# else
144// ============ Standard setups ============
145
146# ifndef SELECT_SOFT_SERIAL_SPEED
147# define SELECT_SOFT_SERIAL_SPEED 1
148// 0: about 189kbps (Experimental only)
149// 1: about 137kbps (default)
150// 2: about 75kbps
151// 3: about 39kbps
152// 4: about 26kbps
153// 5: about 20kbps
154# endif
155
156# if __GNUC__ < 6
157# define TID_SEND_ADJUST 14
158# else
159# define TID_SEND_ADJUST 2
160# endif
161
162# if SELECT_SOFT_SERIAL_SPEED == 0
163// Very High speed
164# define SERIAL_DELAY 4 // micro sec
165# if __GNUC__ < 6
166# define READ_WRITE_START_ADJUST 33 // cycles
167# define READ_WRITE_WIDTH_ADJUST 3 // cycles
168# else
169# define READ_WRITE_START_ADJUST 34 // cycles
170# define READ_WRITE_WIDTH_ADJUST 7 // cycles
171# endif
172# elif SELECT_SOFT_SERIAL_SPEED == 1
173// High speed
174# define SERIAL_DELAY 6 // micro sec
175# if __GNUC__ < 6
176# define READ_WRITE_START_ADJUST 30 // cycles
177# define READ_WRITE_WIDTH_ADJUST 3 // cycles
178# else
179# define READ_WRITE_START_ADJUST 33 // cycles
180# define READ_WRITE_WIDTH_ADJUST 7 // cycles
181# endif
182# elif SELECT_SOFT_SERIAL_SPEED == 2
183// Middle speed
184# define SERIAL_DELAY 12 // micro sec
185# define READ_WRITE_START_ADJUST 30 // cycles
186# if __GNUC__ < 6
187# define READ_WRITE_WIDTH_ADJUST 3 // cycles
188# else
189# define READ_WRITE_WIDTH_ADJUST 7 // cycles
190# endif
191# elif SELECT_SOFT_SERIAL_SPEED == 3
192// Low speed
193# define SERIAL_DELAY 24 // micro sec
194# define READ_WRITE_START_ADJUST 30 // cycles
195# if __GNUC__ < 6
196# define READ_WRITE_WIDTH_ADJUST 3 // cycles
197# else
198# define READ_WRITE_WIDTH_ADJUST 7 // cycles
199# endif
200# elif SELECT_SOFT_SERIAL_SPEED == 4
201// Very Low speed
202# define SERIAL_DELAY 36 // micro sec
203# define READ_WRITE_START_ADJUST 30 // cycles
204# if __GNUC__ < 6
205# define READ_WRITE_WIDTH_ADJUST 3 // cycles
206# else
207# define READ_WRITE_WIDTH_ADJUST 7 // cycles
208# endif
209# elif SELECT_SOFT_SERIAL_SPEED == 5
210// Ultra Low speed
211# define SERIAL_DELAY 48 // micro sec
212# define READ_WRITE_START_ADJUST 30 // cycles
213# if __GNUC__ < 6
214# define READ_WRITE_WIDTH_ADJUST 3 // cycles
215# else
216# define READ_WRITE_WIDTH_ADJUST 7 // cycles
217# endif
218# else
219# error invalid SELECT_SOFT_SERIAL_SPEED value
220# endif /* SELECT_SOFT_SERIAL_SPEED */
221# endif /* SERIAL_DELAY */
222
223# define SERIAL_DELAY_HALF1 (SERIAL_DELAY / 2)
224# define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2)
225
226# define SLAVE_INT_WIDTH_US 1
227# ifndef SERIAL_USE_MULTI_TRANSACTION
228# define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY
229# else
230# define SLAVE_INT_ACK_WIDTH_UNIT 2
231# define SLAVE_INT_ACK_WIDTH 4
232# endif
233
234static SSTD_t *Transaction_table = NULL;
235static uint8_t Transaction_table_size = 0;
236
237inline static void serial_delay(void) ALWAYS_INLINE;
238inline static void serial_delay(void) { _delay_us(SERIAL_DELAY); }
239
240inline static void serial_delay_half1(void) ALWAYS_INLINE;
241inline static void serial_delay_half1(void) { _delay_us(SERIAL_DELAY_HALF1); }
242
243inline static void serial_delay_half2(void) ALWAYS_INLINE;
244inline static void serial_delay_half2(void) { _delay_us(SERIAL_DELAY_HALF2); }
245
246inline static void serial_output(void) ALWAYS_INLINE;
247inline static void serial_output(void) { setPinOutput(SOFT_SERIAL_PIN); }
248
249// make the serial pin an input with pull-up resistor
250inline static void serial_input_with_pullup(void) ALWAYS_INLINE;
251inline static void serial_input_with_pullup(void) { setPinInputHigh(SOFT_SERIAL_PIN); }
252
253inline static uint8_t serial_read_pin(void) ALWAYS_INLINE;
254inline static uint8_t serial_read_pin(void) { return !!readPin(SOFT_SERIAL_PIN); }
255
256inline static void serial_low(void) ALWAYS_INLINE;
257inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN); }
258
259inline static void serial_high(void) ALWAYS_INLINE;
260inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); }
261
262void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) {
263 Transaction_table = sstd_table;
264 Transaction_table_size = (uint8_t)sstd_table_size;
265 serial_output();
266 serial_high();
267}
268
269void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) {
270 Transaction_table = sstd_table;
271 Transaction_table_size = (uint8_t)sstd_table_size;
272 serial_input_with_pullup();
273
274 // Enable INT0-INT7
275 EIMSK |= EIMSK_BIT;
276 EICRx &= EICRx_BIT;
277}
278
279// Used by the sender to synchronize timing with the reciver.
280static void sync_recv(void) NO_INLINE;
281static void sync_recv(void) {
282 for (uint8_t i = 0; i < SERIAL_DELAY * 5 && serial_read_pin(); i++) {
283 }
284 // This shouldn't hang if the target disconnects because the
285 // serial line will float to high if the target does disconnect.
286 while (!serial_read_pin())
287 ;
288}
289
290// Used by the reciver to send a synchronization signal to the sender.
291static void sync_send(void) NO_INLINE;
292static void sync_send(void) {
293 serial_low();
294 serial_delay();
295 serial_high();
296}
297
298// Reads a byte from the serial line
299static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) NO_INLINE;
300static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) {
301 uint8_t byte, i, p, pb;
302
303 _delay_sub_us(READ_WRITE_START_ADJUST);
304 for (i = 0, byte = 0, p = PARITY; i < bit; i++) {
305 serial_delay_half1(); // read the middle of pulses
306 if (serial_read_pin()) {
307 byte = (byte << 1) | 1;
308 p ^= 1;
309 } else {
310 byte = (byte << 1) | 0;
311 p ^= 0;
312 }
313 _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
314 serial_delay_half2();
315 }
316 /* recive parity bit */
317 serial_delay_half1(); // read the middle of pulses
318 pb = serial_read_pin();
319 _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
320 serial_delay_half2();
321
322 *pterrcount += (p != pb) ? 1 : 0;
323
324 return byte;
325}
326
327// Sends a byte with MSB ordering
328void serial_write_chunk(uint8_t data, uint8_t bit) NO_INLINE;
329void serial_write_chunk(uint8_t data, uint8_t bit) {
330 uint8_t b, p;
331 for (p = PARITY, b = 1 << (bit - 1); b; b >>= 1) {
332 if (data & b) {
333 serial_high();
334 p ^= 1;
335 } else {
336 serial_low();
337 p ^= 0;
338 }
339 serial_delay();
340 }
341 /* send parity bit */
342 if (p & 1) {
343 serial_high();
344 } else {
345 serial_low();
346 }
347 serial_delay();
348
349 serial_low(); // sync_send() / senc_recv() need raise edge
350}
351
352static void serial_send_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
353static void serial_send_packet(uint8_t *buffer, uint8_t size) {
354 for (uint8_t i = 0; i < size; ++i) {
355 uint8_t data;
356 data = buffer[i];
357 sync_send();
358 serial_write_chunk(data, 8);
359 }
360}
361
362static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
363static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) {
364 uint8_t pecount = 0;
365 for (uint8_t i = 0; i < size; ++i) {
366 uint8_t data;
367 sync_recv();
368 data = serial_read_chunk(&pecount, 8);
369 buffer[i] = data;
370 }
371 return pecount == 0;
372}
373
374inline static void change_sender2reciver(void) {
375 sync_send(); // 0
376 serial_delay_half1(); // 1
377 serial_low(); // 2
378 serial_input_with_pullup(); // 2
379 serial_delay_half1(); // 3
380}
381
382inline static void change_reciver2sender(void) {
383 sync_recv(); // 0
384 serial_delay(); // 1
385 serial_low(); // 3
386 serial_output(); // 3
387 serial_delay_half1(); // 4
388}
389
390static inline uint8_t nibble_bits_count(uint8_t bits) {
391 bits = (bits & 0x5) + (bits >> 1 & 0x5);
392 bits = (bits & 0x3) + (bits >> 2 & 0x3);
393 return bits;
394}
395
396// interrupt handle to be used by the target device
397ISR(SERIAL_PIN_INTERRUPT) {
398# ifndef SERIAL_USE_MULTI_TRANSACTION
399 serial_low();
400 serial_output();
401 SSTD_t *trans = Transaction_table;
402# else
403 // recive transaction table index
404 uint8_t tid, bits;
405 uint8_t pecount = 0;
406 sync_recv();
407 bits = serial_read_chunk(&pecount, 7);
408 tid = bits >> 3;
409 bits = (bits & 7) != nibble_bits_count(tid);
410 if (bits || pecount > 0 || tid > Transaction_table_size) {
411 return;
412 }
413 serial_delay_half1();
414
415 serial_high(); // response step1 low->high
416 serial_output();
417 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT * SLAVE_INT_ACK_WIDTH);
418 SSTD_t *trans = &Transaction_table[tid];
419 serial_low(); // response step2 ack high->low
420# endif
421
422 // target send phase
423 if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size);
424 // target switch to input
425 change_sender2reciver();
426
427 // target recive phase
428 if (trans->initiator2target_buffer_size > 0) {
429 if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size)) {
430 *trans->status = TRANSACTION_ACCEPTED;
431 } else {
432 *trans->status = TRANSACTION_DATA_ERROR;
433 }
434 } else {
435 *trans->status = TRANSACTION_ACCEPTED;
436 }
437
438 sync_recv(); // weit initiator output to high
439}
440
441/////////
442// start transaction by initiator
443//
444// int soft_serial_transaction(int sstd_index)
445//
446// Returns:
447// TRANSACTION_END
448// TRANSACTION_NO_RESPONSE
449// TRANSACTION_DATA_ERROR
450// this code is very time dependent, so we need to disable interrupts
451# ifndef SERIAL_USE_MULTI_TRANSACTION
452int soft_serial_transaction(void) {
453 SSTD_t *trans = Transaction_table;
454# else
455int soft_serial_transaction(int sstd_index) {
456 if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR;
457 SSTD_t *trans = &Transaction_table[sstd_index];
458# endif
459 cli();
460
461 // signal to the target that we want to start a transaction
462 serial_output();
463 serial_low();
464 _delay_us(SLAVE_INT_WIDTH_US);
465
466# ifndef SERIAL_USE_MULTI_TRANSACTION
467 // wait for the target response
468 serial_input_with_pullup();
469 _delay_us(SLAVE_INT_RESPONSE_TIME);
470
471 // check if the target is present
472 if (serial_read_pin()) {
473 // target failed to pull the line low, assume not present
474 serial_output();
475 serial_high();
476 *trans->status = TRANSACTION_NO_RESPONSE;
477 sei();
478 return TRANSACTION_NO_RESPONSE;
479 }
480
481# else
482 // send transaction table index
483 int tid = (sstd_index << 3) | (7 & nibble_bits_count(sstd_index));
484 sync_send();
485 _delay_sub_us(TID_SEND_ADJUST);
486 serial_write_chunk(tid, 7);
487 serial_delay_half1();
488
489 // wait for the target response (step1 low->high)
490 serial_input_with_pullup();
491 while (!serial_read_pin()) {
492 _delay_sub_us(2);
493 }
494
495 // check if the target is present (step2 high->low)
496 for (int i = 0; serial_read_pin(); i++) {
497 if (i > SLAVE_INT_ACK_WIDTH + 1) {
498 // slave failed to pull the line low, assume not present
499 serial_output();
500 serial_high();
501 *trans->status = TRANSACTION_NO_RESPONSE;
502 sei();
503 return TRANSACTION_NO_RESPONSE;
504 }
505 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);
506 }
507# endif
508
509 // initiator recive phase
510 // if the target is present syncronize with it
511 if (trans->target2initiator_buffer_size > 0) {
512 if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size)) {
513 serial_output();
514 serial_high();
515 *trans->status = TRANSACTION_DATA_ERROR;
516 sei();
517 return TRANSACTION_DATA_ERROR;
518 }
519 }
520
521 // initiator switch to output
522 change_reciver2sender();
523
524 // initiator send phase
525 if (trans->initiator2target_buffer_size > 0) {
526 serial_send_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size);
527 }
528
529 // always, release the line when not in use
530 sync_send();
531
532 *trans->status = TRANSACTION_END;
533 sei();
534 return TRANSACTION_END;
535}
536
537# ifdef SERIAL_USE_MULTI_TRANSACTION
538int soft_serial_get_and_clean_status(int sstd_index) {
539 SSTD_t *trans = &Transaction_table[sstd_index];
540 cli();
541 int retval = *trans->status;
542 *trans->status = 0;
543 ;
544 sei();
545 return retval;
546}
547# endif
548
549#endif
550
551// Helix serial.c history
552// 2018-1-29 fork from let's split and add PD2, modify sync_recv() (#2308, bceffdefc)
553// 2018-6-28 bug fix master to slave comm and speed up (#3255, 1038bbef4)
554// (adjusted with avr-gcc 4.9.2)
555// 2018-7-13 remove USE_SERIAL_PD2 macro (#3374, f30d6dd78)
556// (adjusted with avr-gcc 4.9.2)
557// 2018-8-11 add support multi-type transaction (#3608, feb5e4aae)
558// (adjusted with avr-gcc 4.9.2)
559// 2018-10-21 fix serial and RGB animation conflict (#4191, 4665e4fff)
560// (adjusted with avr-gcc 7.3.0)
561// 2018-10-28 re-adjust compiler depend value of delay (#4269, 8517f8a66)
562// (adjusted with avr-gcc 5.4.0, 7.3.0)
563// 2018-12-17 copy to TOP/quantum/split_common/ and remove backward compatibility code (#4669)
diff --git a/drivers/avr/serial.h b/drivers/avr/serial.h
deleted file mode 100644
index 53e66cf90..000000000
--- a/drivers/avr/serial.h
+++ /dev/null
@@ -1,62 +0,0 @@
1#pragma once
2
3#include <stdbool.h>
4
5// /////////////////////////////////////////////////////////////////
6// Need Soft Serial defines in config.h
7// /////////////////////////////////////////////////////////////////
8// ex.
9// #define SOFT_SERIAL_PIN ?? // ?? = D0,D1,D2,D3,E6
10// OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5
11// // 1: about 137kbps (default)
12// // 2: about 75kbps
13// // 3: about 39kbps
14// // 4: about 26kbps
15// // 5: about 20kbps
16//
17// //// USE simple API (using signle-type transaction function)
18// /* nothing */
19// //// USE flexible API (using multi-type transaction function)
20// #define SERIAL_USE_MULTI_TRANSACTION
21//
22// /////////////////////////////////////////////////////////////////
23
24// Soft Serial Transaction Descriptor
25typedef struct _SSTD_t {
26 uint8_t *status;
27 uint8_t initiator2target_buffer_size;
28 uint8_t *initiator2target_buffer;
29 uint8_t target2initiator_buffer_size;
30 uint8_t *target2initiator_buffer;
31} SSTD_t;
32#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t))
33
34// initiator is transaction start side
35void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size);
36// target is interrupt accept side
37void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size);
38
39// initiator resullt
40#define TRANSACTION_END 0
41#define TRANSACTION_NO_RESPONSE 0x1
42#define TRANSACTION_DATA_ERROR 0x2
43#define TRANSACTION_TYPE_ERROR 0x4
44#ifndef SERIAL_USE_MULTI_TRANSACTION
45int soft_serial_transaction(void);
46#else
47int soft_serial_transaction(int sstd_index);
48#endif
49
50// target status
51// *SSTD_t.status has
52// initiator:
53// TRANSACTION_END
54// or TRANSACTION_NO_RESPONSE
55// or TRANSACTION_DATA_ERROR
56// target:
57// TRANSACTION_DATA_ERROR
58// or TRANSACTION_ACCEPTED
59#define TRANSACTION_ACCEPTED 0x8
60#ifdef SERIAL_USE_MULTI_TRANSACTION
61int soft_serial_get_and_clean_status(int sstd_index);
62#endif
diff --git a/drivers/avr/spi_master.c b/drivers/avr/spi_master.c
deleted file mode 100644
index 4e8fd3bcd..000000000
--- a/drivers/avr/spi_master.c
+++ /dev/null
@@ -1,180 +0,0 @@
1/* Copyright 2020
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 3 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 <https://www.gnu.org/licenses/>.
15 */
16
17#include "spi_master.h"
18
19#include "timer.h"
20
21#if defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
22# define SPI_SCK_PIN B1
23# define SPI_MOSI_PIN B2
24# define SPI_MISO_PIN B3
25#elif defined(__AVR_ATmega32A__)
26# define SPI_SCK_PIN B7
27# define SPI_MOSI_PIN B5
28# define SPI_MISO_PIN B6
29#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
30# define SPI_SCK_PIN B5
31# define SPI_MOSI_PIN B3
32# define SPI_MISO_PIN B4
33#endif
34
35#ifndef SPI_TIMEOUT
36# define SPI_TIMEOUT 100
37#endif
38
39static pin_t currentSlavePin = NO_PIN;
40static uint8_t currentSlaveConfig = 0;
41static bool currentSlave2X = false;
42
43void spi_init(void) {
44 writePinHigh(SPI_SS_PIN);
45 setPinOutput(SPI_SCK_PIN);
46 setPinOutput(SPI_MOSI_PIN);
47 setPinInput(SPI_MISO_PIN);
48
49 SPCR = (_BV(SPE) | _BV(MSTR));
50}
51
52bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {
53 if (currentSlavePin != NO_PIN || slavePin == NO_PIN) {
54 return false;
55 }
56
57 currentSlaveConfig = 0;
58
59 if (lsbFirst) {
60 currentSlaveConfig |= _BV(DORD);
61 }
62
63 switch (mode) {
64 case 1:
65 currentSlaveConfig |= _BV(CPHA);
66 break;
67 case 2:
68 currentSlaveConfig |= _BV(CPOL);
69 break;
70 case 3:
71 currentSlaveConfig |= (_BV(CPOL) | _BV(CPHA));
72 break;
73 }
74
75 uint16_t roundedDivisor = 1;
76 while (roundedDivisor < divisor) {
77 roundedDivisor <<= 1;
78 }
79
80 switch (roundedDivisor) {
81 case 16:
82 currentSlaveConfig |= _BV(SPR0);
83 break;
84 case 64:
85 currentSlaveConfig |= _BV(SPR1);
86 break;
87 case 128:
88 currentSlaveConfig |= (_BV(SPR1) | _BV(SPR0));
89 break;
90 case 2:
91 currentSlave2X = true;
92 break;
93 case 8:
94 currentSlave2X = true;
95 currentSlaveConfig |= _BV(SPR0);
96 break;
97 case 32:
98 currentSlave2X = true;
99 currentSlaveConfig |= _BV(SPR1);
100 break;
101 }
102
103 SPCR |= currentSlaveConfig;
104 if (currentSlave2X) {
105 SPSR |= _BV(SPI2X);
106 }
107 currentSlavePin = slavePin;
108 setPinOutput(currentSlavePin);
109 writePinLow(currentSlavePin);
110
111 return true;
112}
113
114spi_status_t spi_write(uint8_t data) {
115 SPDR = data;
116
117 uint16_t timeout_timer = timer_read();
118 while (!(SPSR & _BV(SPIF))) {
119 if ((timer_read() - timeout_timer) >= SPI_TIMEOUT) {
120 return SPI_STATUS_TIMEOUT;
121 }
122 }
123
124 return SPDR;
125}
126
127spi_status_t spi_read() {
128 SPDR = 0x00; // Dummy
129
130 uint16_t timeout_timer = timer_read();
131 while (!(SPSR & _BV(SPIF))) {
132 if ((timer_read() - timeout_timer) >= SPI_TIMEOUT) {
133 return SPI_STATUS_TIMEOUT;
134 }
135 }
136
137 return SPDR;
138}
139
140spi_status_t spi_transmit(const uint8_t *data, uint16_t length) {
141 spi_status_t status;
142
143 for (uint16_t i = 0; i < length; i++) {
144 status = spi_write(data[i]);
145
146 if (status < 0) {
147 return status;
148 }
149 }
150
151 return SPI_STATUS_SUCCESS;
152}
153
154spi_status_t spi_receive(uint8_t *data, uint16_t length) {
155 spi_status_t status;
156
157 for (uint16_t i = 0; i < length; i++) {
158 status = spi_read();
159
160 if (status >= 0) {
161 data[i] = status;
162 } else {
163 return status;
164 }
165 }
166
167 return SPI_STATUS_SUCCESS;
168}
169
170void spi_stop(void) {
171 if (currentSlavePin != NO_PIN) {
172 setPinOutput(currentSlavePin);
173 writePinHigh(currentSlavePin);
174 currentSlavePin = NO_PIN;
175 SPSR &= ~(_BV(SPI2X));
176 SPCR &= ~(currentSlaveConfig);
177 currentSlaveConfig = 0;
178 currentSlave2X = false;
179 }
180}
diff --git a/drivers/avr/spi_master.h b/drivers/avr/spi_master.h
deleted file mode 100644
index 8a30f47ae..000000000
--- a/drivers/avr/spi_master.h
+++ /dev/null
@@ -1,59 +0,0 @@
1/* Copyright 2020
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 3 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 <https://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdbool.h>
20
21#include "gpio.h"
22
23typedef int16_t spi_status_t;
24
25// Hardware SS pin is defined in the header so that user code can refer to it
26#if defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
27# define SPI_SS_PIN B0
28#elif defined(__AVR_ATmega32A__)
29# define SPI_SS_PIN B4
30#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
31# define SPI_SS_PIN B2
32#endif
33
34#define SPI_STATUS_SUCCESS (0)
35#define SPI_STATUS_ERROR (-1)
36#define SPI_STATUS_TIMEOUT (-2)
37
38#define SPI_TIMEOUT_IMMEDIATE (0)
39#define SPI_TIMEOUT_INFINITE (0xFFFF)
40
41#ifdef __cplusplus
42extern "C" {
43#endif
44void spi_init(void);
45
46bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor);
47
48spi_status_t spi_write(uint8_t data);
49
50spi_status_t spi_read(void);
51
52spi_status_t spi_transmit(const uint8_t *data, uint16_t length);
53
54spi_status_t spi_receive(uint8_t *data, uint16_t length);
55
56void spi_stop(void);
57#ifdef __cplusplus
58}
59#endif
diff --git a/drivers/avr/ssd1306.c b/drivers/avr/ssd1306.c
deleted file mode 100644
index 1a09a2bcb..000000000
--- a/drivers/avr/ssd1306.c
+++ /dev/null
@@ -1,319 +0,0 @@
1#ifdef SSD1306OLED
2
3# include "ssd1306.h"
4# include "i2c.h"
5# include <string.h>
6# include "print.h"
7# include "glcdfont.c"
8# ifdef PROTOCOL_LUFA
9# include "lufa.h"
10# endif
11# include "sendchar.h"
12# include "timer.h"
13
14struct CharacterMatrix display;
15
16// Set this to 1 to help diagnose early startup problems
17// when testing power-on with ble. Turn it off otherwise,
18// as the latency of printing most of the debug info messes
19// with the matrix scan, causing keys to drop.
20# define DEBUG_TO_SCREEN 0
21
22// static uint16_t last_battery_update;
23// static uint32_t vbat;
24//#define BatteryUpdateInterval 10000 /* milliseconds */
25# define ScreenOffInterval 300000 /* milliseconds */
26# if DEBUG_TO_SCREEN
27static uint8_t displaying;
28# endif
29static uint16_t last_flush;
30
31// Write command sequence.
32// Returns true on success.
33static inline bool _send_cmd1(uint8_t cmd) {
34 bool res = false;
35
36 if (i2c_start_write(SSD1306_ADDRESS)) {
37 xprintf("failed to start write to %d\n", SSD1306_ADDRESS);
38 goto done;
39 }
40
41 if (i2c_master_write(0x0 /* command byte follows */)) {
42 print("failed to write control byte\n");
43
44 goto done;
45 }
46
47 if (i2c_master_write(cmd)) {
48 xprintf("failed to write command %d\n", cmd);
49 goto done;
50 }
51 res = true;
52done:
53 i2c_master_stop();
54 return res;
55}
56
57// Write 2-byte command sequence.
58// Returns true on success
59static inline bool _send_cmd2(uint8_t cmd, uint8_t opr) {
60 if (!_send_cmd1(cmd)) {
61 return false;
62 }
63 return _send_cmd1(opr);
64}
65
66// Write 3-byte command sequence.
67// Returns true on success
68static inline bool _send_cmd3(uint8_t cmd, uint8_t opr1, uint8_t opr2) {
69 if (!_send_cmd1(cmd)) {
70 return false;
71 }
72 if (!_send_cmd1(opr1)) {
73 return false;
74 }
75 return _send_cmd1(opr2);
76}
77
78# define send_cmd1(c) \
79 if (!_send_cmd1(c)) { \
80 goto done; \
81 }
82# define send_cmd2(c, o) \
83 if (!_send_cmd2(c, o)) { \
84 goto done; \
85 }
86# define send_cmd3(c, o1, o2) \
87 if (!_send_cmd3(c, o1, o2)) { \
88 goto done; \
89 }
90
91static void clear_display(void) {
92 matrix_clear(&display);
93
94 // Clear all of the display bits (there can be random noise
95 // in the RAM on startup)
96 send_cmd3(PageAddr, 0, (DisplayHeight / 8) - 1);
97 send_cmd3(ColumnAddr, 0, DisplayWidth - 1);
98
99 if (i2c_start_write(SSD1306_ADDRESS)) {
100 goto done;
101 }
102 if (i2c_master_write(0x40)) {
103 // Data mode
104 goto done;
105 }
106 for (uint8_t row = 0; row < MatrixRows; ++row) {
107 for (uint8_t col = 0; col < DisplayWidth; ++col) {
108 i2c_master_write(0);
109 }
110 }
111
112 display.dirty = false;
113
114done:
115 i2c_master_stop();
116}
117
118# if DEBUG_TO_SCREEN
119# undef sendchar
120static int8_t capture_sendchar(uint8_t c) {
121 sendchar(c);
122 iota_gfx_write_char(c);
123
124 if (!displaying) {
125 iota_gfx_flush();
126 }
127 return 0;
128}
129# endif
130
131bool iota_gfx_init(void) {
132 bool success = false;
133
134 send_cmd1(DisplayOff);
135 send_cmd2(SetDisplayClockDiv, 0x80);
136 send_cmd2(SetMultiPlex, DisplayHeight - 1);
137
138 send_cmd2(SetDisplayOffset, 0);
139
140 send_cmd1(SetStartLine | 0x0);
141 send_cmd2(SetChargePump, 0x14 /* Enable */);
142 send_cmd2(SetMemoryMode, 0 /* horizontal addressing */);
143
144# ifdef OLED_ROTATE180
145 // the following Flip the display orientation 180 degrees
146 send_cmd1(SegRemap);
147 send_cmd1(ComScanInc);
148# endif
149# ifndef OLED_ROTATE180
150 // Flips the display orientation 0 degrees
151 send_cmd1(SegRemap | 0x1);
152 send_cmd1(ComScanDec);
153# endif
154
155 send_cmd2(SetComPins, 0x2);
156 send_cmd2(SetContrast, 0x8f);
157 send_cmd2(SetPreCharge, 0xf1);
158 send_cmd2(SetVComDetect, 0x40);
159 send_cmd1(DisplayAllOnResume);
160 send_cmd1(NormalDisplay);
161 send_cmd1(DeActivateScroll);
162 send_cmd1(DisplayOn);
163
164 send_cmd2(SetContrast, 0); // Dim
165
166 clear_display();
167
168 success = true;
169
170 iota_gfx_flush();
171
172# if DEBUG_TO_SCREEN
173 print_set_sendchar(capture_sendchar);
174# endif
175
176done:
177 return success;
178}
179
180bool iota_gfx_off(void) {
181 bool success = false;
182
183 send_cmd1(DisplayOff);
184 success = true;
185
186done:
187 return success;
188}
189
190bool iota_gfx_on(void) {
191 bool success = false;
192
193 send_cmd1(DisplayOn);
194 success = true;
195
196done:
197 return success;
198}
199
200void matrix_write_char_inner(struct CharacterMatrix *matrix, uint8_t c) {
201 *matrix->cursor = c;
202 ++matrix->cursor;
203
204 if (matrix->cursor - &matrix->display[0][0] == sizeof(matrix->display)) {
205 // We went off the end; scroll the display upwards by one line
206 memmove(&matrix->display[0], &matrix->display[1], MatrixCols * (MatrixRows - 1));
207 matrix->cursor = &matrix->display[MatrixRows - 1][0];
208 memset(matrix->cursor, ' ', MatrixCols);
209 }
210}
211
212void matrix_write_char(struct CharacterMatrix *matrix, uint8_t c) {
213 matrix->dirty = true;
214
215 if (c == '\n') {
216 // Clear to end of line from the cursor and then move to the
217 // start of the next line
218 uint8_t cursor_col = (matrix->cursor - &matrix->display[0][0]) % MatrixCols;
219
220 while (cursor_col++ < MatrixCols) {
221 matrix_write_char_inner(matrix, ' ');
222 }
223 return;
224 }
225
226 matrix_write_char_inner(matrix, c);
227}
228
229void iota_gfx_write_char(uint8_t c) { matrix_write_char(&display, c); }
230
231void matrix_write(struct CharacterMatrix *matrix, const char *data) {
232 const char *end = data + strlen(data);
233 while (data < end) {
234 matrix_write_char(matrix, *data);
235 ++data;
236 }
237}
238
239void iota_gfx_write(const char *data) { matrix_write(&display, data); }
240
241void matrix_write_P(struct CharacterMatrix *matrix, const char *data) {
242 while (true) {
243 uint8_t c = pgm_read_byte(data);
244 if (c == 0) {
245 return;
246 }
247 matrix_write_char(matrix, c);
248 ++data;
249 }
250}
251
252void iota_gfx_write_P(const char *data) { matrix_write_P(&display, data); }
253
254void matrix_clear(struct CharacterMatrix *matrix) {
255 memset(matrix->display, ' ', sizeof(matrix->display));
256 matrix->cursor = &matrix->display[0][0];
257 matrix->dirty = true;
258}
259
260void iota_gfx_clear_screen(void) { matrix_clear(&display); }
261
262void matrix_render(struct CharacterMatrix *matrix) {
263 last_flush = timer_read();
264 iota_gfx_on();
265# if DEBUG_TO_SCREEN
266 ++displaying;
267# endif
268
269 // Move to the home position
270 send_cmd3(PageAddr, 0, MatrixRows - 1);
271 send_cmd3(ColumnAddr, 0, (MatrixCols * FontWidth) - 1);
272
273 if (i2c_start_write(SSD1306_ADDRESS)) {
274 goto done;
275 }
276 if (i2c_master_write(0x40)) {
277 // Data mode
278 goto done;
279 }
280
281 for (uint8_t row = 0; row < MatrixRows; ++row) {
282 for (uint8_t col = 0; col < MatrixCols; ++col) {
283 const uint8_t *glyph = font + (matrix->display[row][col] * (FontWidth - 1));
284
285 for (uint8_t glyphCol = 0; glyphCol < FontWidth - 1; ++glyphCol) {
286 uint8_t colBits = pgm_read_byte(glyph + glyphCol);
287 i2c_master_write(colBits);
288 }
289
290 // 1 column of space between chars (it's not included in the glyph)
291 i2c_master_write(0);
292 }
293 }
294
295 matrix->dirty = false;
296
297done:
298 i2c_master_stop();
299# if DEBUG_TO_SCREEN
300 --displaying;
301# endif
302}
303
304void iota_gfx_flush(void) { matrix_render(&display); }
305
306__attribute__((weak)) void iota_gfx_task_user(void) {}
307
308void iota_gfx_task(void) {
309 iota_gfx_task_user();
310
311 if (display.dirty) {
312 iota_gfx_flush();
313 }
314
315 if (timer_elapsed(last_flush) > ScreenOffInterval) {
316 iota_gfx_off();
317 }
318}
319#endif
diff --git a/drivers/avr/ssd1306.h b/drivers/avr/ssd1306.h
deleted file mode 100644
index 6eecdcfaa..000000000
--- a/drivers/avr/ssd1306.h
+++ /dev/null
@@ -1,87 +0,0 @@
1#pragma once
2
3#include <stdbool.h>
4#include <stdio.h>
5#include "config.h"
6
7enum ssd1306_cmds {
8 DisplayOff = 0xAE,
9 DisplayOn = 0xAF,
10
11 SetContrast = 0x81,
12 DisplayAllOnResume = 0xA4,
13
14 DisplayAllOn = 0xA5,
15 NormalDisplay = 0xA6,
16 InvertDisplay = 0xA7,
17 SetDisplayOffset = 0xD3,
18 SetComPins = 0xda,
19 SetVComDetect = 0xdb,
20 SetDisplayClockDiv = 0xD5,
21 SetPreCharge = 0xd9,
22 SetMultiPlex = 0xa8,
23 SetLowColumn = 0x00,
24 SetHighColumn = 0x10,
25 SetStartLine = 0x40,
26
27 SetMemoryMode = 0x20,
28 ColumnAddr = 0x21,
29 PageAddr = 0x22,
30
31 ComScanInc = 0xc0,
32 ComScanDec = 0xc8,
33 SegRemap = 0xa0,
34 SetChargePump = 0x8d,
35 ExternalVcc = 0x01,
36 SwitchCapVcc = 0x02,
37
38 ActivateScroll = 0x2f,
39 DeActivateScroll = 0x2e,
40 SetVerticalScrollArea = 0xa3,
41 RightHorizontalScroll = 0x26,
42 LeftHorizontalScroll = 0x27,
43 VerticalAndRightHorizontalScroll = 0x29,
44 VerticalAndLeftHorizontalScroll = 0x2a,
45};
46
47// Controls the SSD1306 128x32 OLED display via i2c
48
49#ifndef SSD1306_ADDRESS
50# define SSD1306_ADDRESS 0x3C
51#endif
52
53#define DisplayHeight 32
54#define DisplayWidth 128
55
56#define FontHeight 8
57#define FontWidth 6
58
59#define MatrixRows (DisplayHeight / FontHeight)
60#define MatrixCols (DisplayWidth / FontWidth)
61
62struct CharacterMatrix {
63 uint8_t display[MatrixRows][MatrixCols];
64 uint8_t *cursor;
65 bool dirty;
66};
67
68extern struct CharacterMatrix display;
69
70bool iota_gfx_init(void);
71void iota_gfx_task(void);
72bool iota_gfx_off(void);
73bool iota_gfx_on(void);
74void iota_gfx_flush(void);
75void iota_gfx_write_char(uint8_t c);
76void iota_gfx_write(const char *data);
77void iota_gfx_write_P(const char *data);
78void iota_gfx_clear_screen(void);
79
80void iota_gfx_task_user(void);
81
82void matrix_clear(struct CharacterMatrix *matrix);
83void matrix_write_char_inner(struct CharacterMatrix *matrix, uint8_t c);
84void matrix_write_char(struct CharacterMatrix *matrix, uint8_t c);
85void matrix_write(struct CharacterMatrix *matrix, const char *data);
86void matrix_write_P(struct CharacterMatrix *matrix, const char *data);
87void matrix_render(struct CharacterMatrix *matrix);
diff --git a/drivers/avr/uart.c b/drivers/avr/uart.c
deleted file mode 100644
index c6abcb6fe..000000000
--- a/drivers/avr/uart.c
+++ /dev/null
@@ -1,170 +0,0 @@
1/* UART Example for Teensy USB Development Board
2 * http://www.pjrc.com/teensy/
3 * Copyright (c) 2009 PJRC.COM, LLC
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24// Version 1.0: Initial Release
25// Version 1.1: Add support for Teensy 2.0, minor optimizations
26
27#include <avr/io.h>
28#include <avr/interrupt.h>
29
30#include "uart.h"
31
32#if defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
33# define UDRn UDR1
34# define UBRRnL UBRR1L
35# define UCSRnA UCSR1A
36# define UCSRnB UCSR1B
37# define UCSRnC UCSR1C
38# define U2Xn U2X1
39# define RXENn RXEN1
40# define TXENn TXEN1
41# define RXCIEn RXCIE1
42# define UCSZn1 UCSZ11
43# define UCSZn0 UCSZ10
44# define UDRIEn UDRIE1
45# define USARTn_UDRE_vect USART1_UDRE_vect
46# define USARTn_RX_vect USART1_RX_vect
47#elif defined(__AVR_ATmega32A__)
48# define UDRn UDR
49# define UBRRnL UBRRL
50# define UCSRnA UCSRA
51# define UCSRnB UCSRB
52# define UCSRnC UCSRC
53# define U2Xn U2X
54# define RXENn RXEN
55# define TXENn TXEN
56# define RXCIEn RXCIE
57# define UCSZn1 UCSZ1
58# define UCSZn0 UCSZ0
59# define UDRIEn UDRIE
60# define USARTn_UDRE_vect USART_UDRE_vect
61# define USARTn_RX_vect USART_RX_vect
62#elif defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
63# define UDRn UDR0
64# define UBRRnL UBRR0L
65# define UCSRnA UCSR0A
66# define UCSRnB UCSR0B
67# define UCSRnC UCSR0C
68# define U2Xn U2X0
69# define RXENn RXEN0
70# define TXENn TXEN0
71# define RXCIEn RXCIE0
72# define UCSZn1 UCSZ01
73# define UCSZn0 UCSZ00
74# define UDRIEn UDRIE0
75# define USARTn_UDRE_vect USART_UDRE_vect
76# define USARTn_RX_vect USART_RX_vect
77#endif
78
79// These buffers may be any size from 2 to 256 bytes.
80#define RX_BUFFER_SIZE 64
81#define TX_BUFFER_SIZE 256
82
83static volatile uint8_t tx_buffer[TX_BUFFER_SIZE];
84static volatile uint8_t tx_buffer_head;
85static volatile uint8_t tx_buffer_tail;
86static volatile uint8_t rx_buffer[RX_BUFFER_SIZE];
87static volatile uint8_t rx_buffer_head;
88static volatile uint8_t rx_buffer_tail;
89
90// Initialize the UART
91void uart_init(uint32_t baud) {
92 cli();
93 UBRRnL = (F_CPU / 4 / baud - 1) / 2;
94 UCSRnA = (1 << U2Xn);
95 UCSRnB = (1 << RXENn) | (1 << TXENn) | (1 << RXCIEn);
96 UCSRnC = (1 << UCSZn1) | (1 << UCSZn0);
97 tx_buffer_head = tx_buffer_tail = 0;
98 rx_buffer_head = rx_buffer_tail = 0;
99 sei();
100}
101
102// Transmit a byte
103void uart_putchar(uint8_t c) {
104 uint8_t i;
105
106 i = tx_buffer_head + 1;
107 if (i >= TX_BUFFER_SIZE) i = 0;
108 // return immediately to avoid deadlock when interrupt is disabled(called from ISR)
109 if (tx_buffer_tail == i && (SREG & (1 << SREG_I)) == 0) return;
110 while (tx_buffer_tail == i)
111 ; // wait until space in buffer
112 // cli();
113 tx_buffer[i] = c;
114 tx_buffer_head = i;
115 UCSRnB = (1 << RXENn) | (1 << TXENn) | (1 << RXCIEn) | (1 << UDRIEn);
116 // sei();
117}
118
119// Receive a byte
120uint8_t uart_getchar(void) {
121 uint8_t c, i;
122
123 while (rx_buffer_head == rx_buffer_tail)
124 ; // wait for character
125 i = rx_buffer_tail + 1;
126 if (i >= RX_BUFFER_SIZE) i = 0;
127 c = rx_buffer[i];
128 rx_buffer_tail = i;
129 return c;
130}
131
132// Return whether the number of bytes waiting in the receive buffer is nonzero.
133// Call this before uart_getchar() to check if it will need
134// to wait for a byte to arrive.
135bool uart_available(void) {
136 uint8_t head, tail;
137
138 head = rx_buffer_head;
139 tail = rx_buffer_tail;
140 if (head >= tail) return (head - tail) > 0;
141 return (RX_BUFFER_SIZE + head - tail) > 0;
142}
143
144// Transmit Interrupt
145ISR(USARTn_UDRE_vect) {
146 uint8_t i;
147
148 if (tx_buffer_head == tx_buffer_tail) {
149 // buffer is empty, disable transmit interrupt
150 UCSRnB = (1 << RXENn) | (1 << TXENn) | (1 << RXCIEn);
151 } else {
152 i = tx_buffer_tail + 1;
153 if (i >= TX_BUFFER_SIZE) i = 0;
154 UDRn = tx_buffer[i];
155 tx_buffer_tail = i;
156 }
157}
158
159// Receive Interrupt
160ISR(USARTn_RX_vect) {
161 uint8_t c, i;
162
163 c = UDRn;
164 i = rx_buffer_head + 1;
165 if (i >= RX_BUFFER_SIZE) i = 0;
166 if (i != rx_buffer_tail) {
167 rx_buffer[i] = c;
168 rx_buffer_head = i;
169 }
170}
diff --git a/drivers/avr/uart.h b/drivers/avr/uart.h
deleted file mode 100644
index 602eb3d8b..000000000
--- a/drivers/avr/uart.h
+++ /dev/null
@@ -1,35 +0,0 @@
1/* UART Example for Teensy USB Development Board
2 * http://www.pjrc.com/teensy/
3 * Copyright (c) 2009 PJRC.COM, LLC
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24#pragma once
25
26#include <stdint.h>
27#include <stdbool.h>
28
29void uart_init(uint32_t baud);
30
31void uart_putchar(uint8_t c);
32
33uint8_t uart_getchar(void);
34
35bool uart_available(void);
diff --git a/drivers/avr/ws2812.c b/drivers/avr/ws2812.c
deleted file mode 100644
index 77c492cd4..000000000
--- a/drivers/avr/ws2812.c
+++ /dev/null
@@ -1,176 +0,0 @@
1/*
2 * light weight WS2812 lib V2.0b
3 *
4 * Controls WS2811/WS2812/WS2812B RGB-LEDs
5 * Author: Tim (cpldcpu@gmail.com)
6 *
7 * Jan 18th, 2014 v2.0b Initial Version
8 * Nov 29th, 2015 v2.3 Added SK6812RGBW support
9 *
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23#include "ws2812.h"
24#include <avr/interrupt.h>
25#include <avr/io.h>
26#include <util/delay.h>
27
28#define pinmask(pin) (_BV((pin)&0xF))
29
30/*
31 * Forward declare internal functions
32 *
33 * The functions take a byte-array and send to the data output as WS2812 bitstream.
34 * The length is the number of bytes to send - three per LED.
35 */
36
37static inline void ws2812_sendarray_mask(uint8_t *data, uint16_t datlen, uint8_t masklo, uint8_t maskhi);
38
39void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds) {
40 DDRx_ADDRESS(RGB_DI_PIN) |= pinmask(RGB_DI_PIN);
41
42 uint8_t masklo = ~(pinmask(RGB_DI_PIN)) & PORTx_ADDRESS(RGB_DI_PIN);
43 uint8_t maskhi = pinmask(RGB_DI_PIN) | PORTx_ADDRESS(RGB_DI_PIN);
44
45 ws2812_sendarray_mask((uint8_t *)ledarray, number_of_leds * sizeof(LED_TYPE), masklo, maskhi);
46
47 _delay_us(WS2812_TRST_US);
48}
49
50/*
51 This routine writes an array of bytes with RGB values to the Dataout pin
52 using the fast 800kHz clockless WS2811/2812 protocol.
53*/
54
55// Timing in ns
56#define w_zeropulse 350
57#define w_onepulse 900
58#define w_totalperiod 1250
59
60// Fixed cycles used by the inner loop
61#define w_fixedlow 2
62#define w_fixedhigh 4
63#define w_fixedtotal 8
64
65// Insert NOPs to match the timing, if possible
66#define w_zerocycles (((F_CPU / 1000) * w_zeropulse) / 1000000)
67#define w_onecycles (((F_CPU / 1000) * w_onepulse + 500000) / 1000000)
68#define w_totalcycles (((F_CPU / 1000) * w_totalperiod + 500000) / 1000000)
69
70// w1_nops - nops between rising edge and falling edge - low
71#if w_zerocycles >= w_fixedlow
72# define w1_nops (w_zerocycles - w_fixedlow)
73#else
74# define w1_nops 0
75#endif
76
77// w2_nops - nops between fe low and fe high
78#if w_onecycles >= (w_fixedhigh + w1_nops)
79# define w2_nops (w_onecycles - w_fixedhigh - w1_nops)
80#else
81# define w2_nops 0
82#endif
83
84// w3_nops - nops to complete loop
85#if w_totalcycles >= (w_fixedtotal + w1_nops + w2_nops)
86# define w3_nops (w_totalcycles - w_fixedtotal - w1_nops - w2_nops)
87#else
88# define w3_nops 0
89#endif
90
91// The only critical timing parameter is the minimum pulse length of the "0"
92// Warn or throw error if this timing can not be met with current F_CPU settings.
93#define w_lowtime ((w1_nops + w_fixedlow) * 1000000) / (F_CPU / 1000)
94#if w_lowtime > 550
95# error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?"
96#elif w_lowtime > 450
97# warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)."
98# warning "Please consider a higher clockspeed, if possible"
99#endif
100
101#define w_nop1 "nop \n\t"
102#define w_nop2 "rjmp .+0 \n\t"
103#define w_nop4 w_nop2 w_nop2
104#define w_nop8 w_nop4 w_nop4
105#define w_nop16 w_nop8 w_nop8
106
107static inline void ws2812_sendarray_mask(uint8_t *data, uint16_t datlen, uint8_t masklo, uint8_t maskhi) {
108 uint8_t curbyte, ctr, sreg_prev;
109
110 sreg_prev = SREG;
111 cli();
112
113 while (datlen--) {
114 curbyte = (*data++);
115
116 asm volatile(" ldi %0,8 \n\t"
117 "loop%=: \n\t"
118 " out %2,%3 \n\t" // '1' [01] '0' [01] - re
119#if (w1_nops & 1)
120 w_nop1
121#endif
122#if (w1_nops & 2)
123 w_nop2
124#endif
125#if (w1_nops & 4)
126 w_nop4
127#endif
128#if (w1_nops & 8)
129 w_nop8
130#endif
131#if (w1_nops & 16)
132 w_nop16
133#endif
134 " sbrs %1,7 \n\t" // '1' [03] '0' [02]
135 " out %2,%4 \n\t" // '1' [--] '0' [03] - fe-low
136 " lsl %1 \n\t" // '1' [04] '0' [04]
137#if (w2_nops & 1)
138 w_nop1
139#endif
140#if (w2_nops & 2)
141 w_nop2
142#endif
143#if (w2_nops & 4)
144 w_nop4
145#endif
146#if (w2_nops & 8)
147 w_nop8
148#endif
149#if (w2_nops & 16)
150 w_nop16
151#endif
152 " out %2,%4 \n\t" // '1' [+1] '0' [+1] - fe-high
153#if (w3_nops & 1)
154 w_nop1
155#endif
156#if (w3_nops & 2)
157 w_nop2
158#endif
159#if (w3_nops & 4)
160 w_nop4
161#endif
162#if (w3_nops & 8)
163 w_nop8
164#endif
165#if (w3_nops & 16)
166 w_nop16
167#endif
168
169 " dec %0 \n\t" // '1' [+2] '0' [+2]
170 " brne loop%=\n\t" // '1' [+3] '0' [+4]
171 : "=&d"(ctr)
172 : "r"(curbyte), "I"(_SFR_IO_ADDR(PORTx_ADDRESS(RGB_DI_PIN))), "r"(maskhi), "r"(masklo));
173 }
174
175 SREG = sreg_prev;
176}
diff --git a/drivers/avr/ws2812_i2c.c b/drivers/avr/ws2812_i2c.c
deleted file mode 100644
index 1c332e24b..000000000
--- a/drivers/avr/ws2812_i2c.c
+++ /dev/null
@@ -1,27 +0,0 @@
1#include "ws2812.h"
2#include "i2c_master.h"
3
4#ifdef RGBW
5# error "RGBW not supported"
6#endif
7
8#ifndef WS2812_ADDRESS
9# define WS2812_ADDRESS 0xb0
10#endif
11
12#ifndef WS2812_TIMEOUT
13# define WS2812_TIMEOUT 100
14#endif
15
16void ws2812_init(void) { i2c_init(); }
17
18// Setleds for standard RGB
19void ws2812_setleds(LED_TYPE *ledarray, uint16_t leds) {
20 static bool s_init = false;
21 if (!s_init) {
22 ws2812_init();
23 s_init = true;
24 }
25
26 i2c_transmit(WS2812_ADDRESS, (uint8_t *)ledarray, sizeof(LED_TYPE) * leds, WS2812_TIMEOUT);
27}
diff --git a/drivers/chibios/analog.c b/drivers/chibios/analog.c
deleted file mode 100644
index 8c476fcac..000000000
--- a/drivers/chibios/analog.c
+++ /dev/null
@@ -1,321 +0,0 @@
1/* Copyright 2019 Drew Mills
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 "quantum.h"
18#include "analog.h"
19#include <ch.h>
20#include <hal.h>
21
22#if !HAL_USE_ADC
23# error "You need to set HAL_USE_ADC to TRUE in your halconf.h to use the ADC."
24#endif
25
26#if !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && !STM32_ADC_USE_ADC3 && !STM32_ADC_USE_ADC4
27# error "You need to set one of the 'STM32_ADC_USE_ADCx' settings to TRUE in your mcuconf.h to use the ADC."
28#endif
29
30#if STM32_ADC_DUAL_MODE
31# error "STM32 ADC Dual Mode is not supported at this time."
32#endif
33
34#if STM32_ADCV3_OVERSAMPLING
35# error "STM32 ADCV3 Oversampling is not supported at this time."
36#endif
37
38// Otherwise assume V3
39#if defined(STM32F0XX) || defined(STM32L0XX)
40# define USE_ADCV1
41#elif defined(STM32F1XX) || defined(STM32F2XX) || defined(STM32F4XX)
42# define USE_ADCV2
43#endif
44
45// BODGE to make v2 look like v1,3 and 4
46#ifdef USE_ADCV2
47# if !defined(ADC_SMPR_SMP_1P5) && defined(ADC_SAMPLE_3)
48# define ADC_SMPR_SMP_1P5 ADC_SAMPLE_3
49# define ADC_SMPR_SMP_7P5 ADC_SAMPLE_15
50# define ADC_SMPR_SMP_13P5 ADC_SAMPLE_28
51# define ADC_SMPR_SMP_28P5 ADC_SAMPLE_56
52# define ADC_SMPR_SMP_41P5 ADC_SAMPLE_84
53# define ADC_SMPR_SMP_55P5 ADC_SAMPLE_112
54# define ADC_SMPR_SMP_71P5 ADC_SAMPLE_144
55# define ADC_SMPR_SMP_239P5 ADC_SAMPLE_480
56# endif
57
58# if !defined(ADC_SMPR_SMP_1P5) && defined(ADC_SAMPLE_1P5)
59# define ADC_SMPR_SMP_1P5 ADC_SAMPLE_1P5
60# define ADC_SMPR_SMP_7P5 ADC_SAMPLE_7P5
61# define ADC_SMPR_SMP_13P5 ADC_SAMPLE_13P5
62# define ADC_SMPR_SMP_28P5 ADC_SAMPLE_28P5
63# define ADC_SMPR_SMP_41P5 ADC_SAMPLE_41P5
64# define ADC_SMPR_SMP_55P5 ADC_SAMPLE_55P5
65# define ADC_SMPR_SMP_71P5 ADC_SAMPLE_71P5
66# define ADC_SMPR_SMP_239P5 ADC_SAMPLE_239P5
67# endif
68
69// we still sample at 12bit, but scale down to the requested bit range
70# define ADC_CFGR1_RES_12BIT 12
71# define ADC_CFGR1_RES_10BIT 10
72# define ADC_CFGR1_RES_8BIT 8
73# define ADC_CFGR1_RES_6BIT 6
74#endif
75
76/* User configurable ADC options */
77#ifndef ADC_COUNT
78# if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F4XX)
79# define ADC_COUNT 1
80# elif defined(STM32F3XX)
81# define ADC_COUNT 4
82# else
83# error "ADC_COUNT has not been set for this ARM microcontroller."
84# endif
85#endif
86
87#ifndef ADC_NUM_CHANNELS
88# define ADC_NUM_CHANNELS 1
89#elif ADC_NUM_CHANNELS != 1
90# error "The ARM ADC implementation currently only supports reading one channel at a time."
91#endif
92
93#ifndef ADC_BUFFER_DEPTH
94# define ADC_BUFFER_DEPTH 1
95#endif
96
97// For more sampling rate options, look at hal_adc_lld.h in ChibiOS
98#ifndef ADC_SAMPLING_RATE
99# define ADC_SAMPLING_RATE ADC_SMPR_SMP_1P5
100#endif
101
102// Options are 12, 10, 8, and 6 bit.
103#ifndef ADC_RESOLUTION
104# ifdef ADC_CFGR_RES_10BITS // ADCv3, ADCv4
105# define ADC_RESOLUTION ADC_CFGR_RES_10BITS
106# else // ADCv1, ADCv5, or the bodge for ADCv2 above
107# define ADC_RESOLUTION ADC_CFGR1_RES_10BIT
108# endif
109#endif
110
111static ADCConfig adcCfg = {};
112static adcsample_t sampleBuffer[ADC_NUM_CHANNELS * ADC_BUFFER_DEPTH];
113
114// Initialize to max number of ADCs, set to empty object to initialize all to false.
115static bool adcInitialized[ADC_COUNT] = {};
116
117// TODO: add back TR handling???
118static ADCConversionGroup adcConversionGroup = {
119 .circular = FALSE,
120 .num_channels = (uint16_t)(ADC_NUM_CHANNELS),
121#if defined(USE_ADCV1)
122 .cfgr1 = ADC_CFGR1_CONT | ADC_RESOLUTION,
123 .smpr = ADC_SAMPLING_RATE,
124#elif defined(USE_ADCV2)
125# if !defined(STM32F1XX)
126 .cr2 = ADC_CR2_SWSTART, // F103 seem very unhappy with, F401 seems very unhappy without...
127# endif
128 .smpr2 = ADC_SMPR2_SMP_AN0(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN1(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN2(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN3(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN4(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN5(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN6(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN7(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN8(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN9(ADC_SAMPLING_RATE),
129 .smpr1 = ADC_SMPR1_SMP_AN10(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN11(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN12(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN13(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN14(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN15(ADC_SAMPLING_RATE),
130#else
131 .cfgr = ADC_CFGR_CONT | ADC_RESOLUTION,
132 .smpr = {ADC_SMPR1_SMP_AN0(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN1(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN2(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN3(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN4(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN5(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN6(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN7(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN8(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN9(ADC_SAMPLING_RATE), ADC_SMPR2_SMP_AN10(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN11(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN12(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN13(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN14(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN15(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN16(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN17(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN18(ADC_SAMPLING_RATE)},
133#endif
134};
135
136// clang-format off
137__attribute__((weak)) adc_mux pinToMux(pin_t pin) {
138 switch (pin) {
139#if defined(STM32F0XX)
140 case A0: return TO_MUX( ADC_CHSELR_CHSEL0, 0 );
141 case A1: return TO_MUX( ADC_CHSELR_CHSEL1, 0 );
142 case A2: return TO_MUX( ADC_CHSELR_CHSEL2, 0 );
143 case A3: return TO_MUX( ADC_CHSELR_CHSEL3, 0 );
144 case A4: return TO_MUX( ADC_CHSELR_CHSEL4, 0 );
145 case A5: return TO_MUX( ADC_CHSELR_CHSEL5, 0 );
146 case A6: return TO_MUX( ADC_CHSELR_CHSEL6, 0 );
147 case A7: return TO_MUX( ADC_CHSELR_CHSEL7, 0 );
148 case B0: return TO_MUX( ADC_CHSELR_CHSEL8, 0 );
149 case B1: return TO_MUX( ADC_CHSELR_CHSEL9, 0 );
150 case C0: return TO_MUX( ADC_CHSELR_CHSEL10, 0 );
151 case C1: return TO_MUX( ADC_CHSELR_CHSEL11, 0 );
152 case C2: return TO_MUX( ADC_CHSELR_CHSEL12, 0 );
153 case C3: return TO_MUX( ADC_CHSELR_CHSEL13, 0 );
154 case C4: return TO_MUX( ADC_CHSELR_CHSEL14, 0 );
155 case C5: return TO_MUX( ADC_CHSELR_CHSEL15, 0 );
156#elif defined(STM32F3XX)
157 case A0: return TO_MUX( ADC_CHANNEL_IN1, 0 );
158 case A1: return TO_MUX( ADC_CHANNEL_IN2, 0 );
159 case A2: return TO_MUX( ADC_CHANNEL_IN3, 0 );
160 case A3: return TO_MUX( ADC_CHANNEL_IN4, 0 );
161 case A4: return TO_MUX( ADC_CHANNEL_IN1, 1 );
162 case A5: return TO_MUX( ADC_CHANNEL_IN2, 1 );
163 case A6: return TO_MUX( ADC_CHANNEL_IN3, 1 );
164 case A7: return TO_MUX( ADC_CHANNEL_IN4, 1 );
165 case B0: return TO_MUX( ADC_CHANNEL_IN12, 2 );
166 case B1: return TO_MUX( ADC_CHANNEL_IN1, 2 );
167 case B2: return TO_MUX( ADC_CHANNEL_IN12, 1 );
168 case B12: return TO_MUX( ADC_CHANNEL_IN3, 3 );
169 case B13: return TO_MUX( ADC_CHANNEL_IN5, 2 );
170 case B14: return TO_MUX( ADC_CHANNEL_IN4, 3 );
171 case B15: return TO_MUX( ADC_CHANNEL_IN5, 3 );
172 case C0: return TO_MUX( ADC_CHANNEL_IN6, 0 ); // Can also be ADC2
173 case C1: return TO_MUX( ADC_CHANNEL_IN7, 0 ); // Can also be ADC2
174 case C2: return TO_MUX( ADC_CHANNEL_IN8, 0 ); // Can also be ADC2
175 case C3: return TO_MUX( ADC_CHANNEL_IN9, 0 ); // Can also be ADC2
176 case C4: return TO_MUX( ADC_CHANNEL_IN5, 1 );
177 case C5: return TO_MUX( ADC_CHANNEL_IN11, 1 );
178 case D8: return TO_MUX( ADC_CHANNEL_IN12, 3 );
179 case D9: return TO_MUX( ADC_CHANNEL_IN13, 3 );
180 case D10: return TO_MUX( ADC_CHANNEL_IN7, 2 ); // Can also be ADC4
181 case D11: return TO_MUX( ADC_CHANNEL_IN8, 2 ); // Can also be ADC4
182 case D12: return TO_MUX( ADC_CHANNEL_IN9, 2 ); // Can also be ADC4
183 case D13: return TO_MUX( ADC_CHANNEL_IN10, 2 ); // Can also be ADC4
184 case D14: return TO_MUX( ADC_CHANNEL_IN11, 2 ); // Can also be ADC4
185 case E7: return TO_MUX( ADC_CHANNEL_IN13, 2 );
186 case E8: return TO_MUX( ADC_CHANNEL_IN6, 2 ); // Can also be ADC4
187 case E9: return TO_MUX( ADC_CHANNEL_IN2, 2 );
188 case E10: return TO_MUX( ADC_CHANNEL_IN14, 2 );
189 case E11: return TO_MUX( ADC_CHANNEL_IN15, 2 );
190 case E12: return TO_MUX( ADC_CHANNEL_IN16, 2 );
191 case E13: return TO_MUX( ADC_CHANNEL_IN3, 2 );
192 case E14: return TO_MUX( ADC_CHANNEL_IN1, 3 );
193 case E15: return TO_MUX( ADC_CHANNEL_IN2, 3 );
194 case F2: return TO_MUX( ADC_CHANNEL_IN10, 0 ); // Can also be ADC2
195 case F4: return TO_MUX( ADC_CHANNEL_IN5, 0 );
196#elif defined(STM32F4XX)
197 case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 );
198 case A1: return TO_MUX( ADC_CHANNEL_IN1, 0 );
199 case A2: return TO_MUX( ADC_CHANNEL_IN2, 0 );
200 case A3: return TO_MUX( ADC_CHANNEL_IN3, 0 );
201 case A4: return TO_MUX( ADC_CHANNEL_IN4, 0 );
202 case A5: return TO_MUX( ADC_CHANNEL_IN5, 0 );
203 case A6: return TO_MUX( ADC_CHANNEL_IN6, 0 );
204 case A7: return TO_MUX( ADC_CHANNEL_IN7, 0 );
205 case B0: return TO_MUX( ADC_CHANNEL_IN8, 0 );
206 case B1: return TO_MUX( ADC_CHANNEL_IN9, 0 );
207 case C0: return TO_MUX( ADC_CHANNEL_IN10, 0 );
208 case C1: return TO_MUX( ADC_CHANNEL_IN11, 0 );
209 case C2: return TO_MUX( ADC_CHANNEL_IN12, 0 );
210 case C3: return TO_MUX( ADC_CHANNEL_IN13, 0 );
211 case C4: return TO_MUX( ADC_CHANNEL_IN14, 0 );
212 case C5: return TO_MUX( ADC_CHANNEL_IN15, 0 );
213# if STM32_ADC_USE_ADC3
214 case F3: return TO_MUX( ADC_CHANNEL_IN9, 2 );
215 case F4: return TO_MUX( ADC_CHANNEL_IN14, 2 );
216 case F5: return TO_MUX( ADC_CHANNEL_IN15, 2 );
217 case F6: return TO_MUX( ADC_CHANNEL_IN4, 2 );
218 case F7: return TO_MUX( ADC_CHANNEL_IN5, 2 );
219 case F8: return TO_MUX( ADC_CHANNEL_IN6, 2 );
220 case F9: return TO_MUX( ADC_CHANNEL_IN7, 2 );
221 case F10: return TO_MUX( ADC_CHANNEL_IN8, 2 );
222# endif
223#elif defined(STM32F1XX)
224 case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 );
225 case A1: return TO_MUX( ADC_CHANNEL_IN1, 0 );
226 case A2: return TO_MUX( ADC_CHANNEL_IN2, 0 );
227 case A3: return TO_MUX( ADC_CHANNEL_IN3, 0 );
228 case A4: return TO_MUX( ADC_CHANNEL_IN4, 0 );
229 case A5: return TO_MUX( ADC_CHANNEL_IN5, 0 );
230 case A6: return TO_MUX( ADC_CHANNEL_IN6, 0 );
231 case A7: return TO_MUX( ADC_CHANNEL_IN7, 0 );
232 case B0: return TO_MUX( ADC_CHANNEL_IN8, 0 );
233 case B1: return TO_MUX( ADC_CHANNEL_IN9, 0 );
234 case C0: return TO_MUX( ADC_CHANNEL_IN10, 0 );
235 case C1: return TO_MUX( ADC_CHANNEL_IN11, 0 );
236 case C2: return TO_MUX( ADC_CHANNEL_IN12, 0 );
237 case C3: return TO_MUX( ADC_CHANNEL_IN13, 0 );
238 case C4: return TO_MUX( ADC_CHANNEL_IN14, 0 );
239 case C5: return TO_MUX( ADC_CHANNEL_IN15, 0 );
240 // STM32F103x[C-G] in 144-pin packages also have analog inputs on F6...F10, but they are on ADC3, and the
241 // ChibiOS ADC driver for STM32F1xx currently supports only ADC1, therefore these pins are not usable.
242#endif
243 }
244
245 // return an adc that would never be used so intToADCDriver will bail out
246 return TO_MUX(0, 0xFF);
247}
248// clang-format on
249
250static inline ADCDriver* intToADCDriver(uint8_t adcInt) {
251 switch (adcInt) {
252#if STM32_ADC_USE_ADC1
253 case 0:
254 return &ADCD1;
255#endif
256#if STM32_ADC_USE_ADC2
257 case 1:
258 return &ADCD2;
259#endif
260#if STM32_ADC_USE_ADC3
261 case 2:
262 return &ADCD3;
263#endif
264#if STM32_ADC_USE_ADC4
265 case 3:
266 return &ADCD4;
267#endif
268 }
269
270 return NULL;
271}
272
273static inline void manageAdcInitializationDriver(uint8_t adc, ADCDriver* adcDriver) {
274 if (!adcInitialized[adc]) {
275 adcStart(adcDriver, &adcCfg);
276 adcInitialized[adc] = true;
277 }
278}
279
280int16_t analogReadPin(pin_t pin) {
281 palSetLineMode(pin, PAL_MODE_INPUT_ANALOG);
282
283 return adc_read(pinToMux(pin));
284}
285
286int16_t analogReadPinAdc(pin_t pin, uint8_t adc) {
287 palSetLineMode(pin, PAL_MODE_INPUT_ANALOG);
288
289 adc_mux target = pinToMux(pin);
290 target.adc = adc;
291 return adc_read(target);
292}
293
294int16_t adc_read(adc_mux mux) {
295#if defined(USE_ADCV1)
296 // TODO: fix previous assumption of only 1 input...
297 adcConversionGroup.chselr = 1 << mux.input; /*no macro to convert N to ADC_CHSELR_CHSEL1*/
298#elif defined(USE_ADCV2)
299 adcConversionGroup.sqr3 = ADC_SQR3_SQ1_N(mux.input);
300#else
301 adcConversionGroup.sqr[0] = ADC_SQR1_SQ1_N(mux.input);
302#endif
303
304 ADCDriver* targetDriver = intToADCDriver(mux.adc);
305 if (!targetDriver) {
306 return 0;
307 }
308
309 manageAdcInitializationDriver(mux.adc, targetDriver);
310 if (adcConvert(targetDriver, &adcConversionGroup, &sampleBuffer[0], ADC_BUFFER_DEPTH) != MSG_OK) {
311 return 0;
312 }
313
314#ifdef USE_ADCV2
315 // fake 12-bit -> N-bit scale
316 return (*sampleBuffer) >> (12 - ADC_RESOLUTION);
317#else
318 // already handled as part of adcConvert
319 return *sampleBuffer;
320#endif
321}
diff --git a/drivers/chibios/i2c_master.c b/drivers/chibios/i2c_master.c
deleted file mode 100644
index fc4bb2ab3..000000000
--- a/drivers/chibios/i2c_master.c
+++ /dev/null
@@ -1,121 +0,0 @@
1/* Copyright 2018 Jack Humbert
2 * Copyright 2018 Yiancar
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/* This library is only valid for STM32 processors.
19 * This library follows the convention of the AVR i2c_master library.
20 * As a result addresses are expected to be already shifted (addr << 1).
21 * I2CD1 is the default driver which corresponds to pins B6 and B7. This
22 * can be changed.
23 * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that
24 * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. Pins B6 and B7 are used
25 * but using any other I2C pins should be trivial.
26 */
27#include "quantum.h"
28#include "i2c_master.h"
29#include <string.h>
30#include <hal.h>
31
32static uint8_t i2c_address;
33
34static const I2CConfig i2cconfig = {
35#if defined(USE_I2CV1_CONTRIB)
36 I2C1_CLOCK_SPEED,
37#elif defined(USE_I2CV1)
38 I2C1_OPMODE,
39 I2C1_CLOCK_SPEED,
40 I2C1_DUTY_CYCLE,
41#else
42 // This configures the I2C clock to 400khz assuming a 72Mhz clock
43 // For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html
44 STM32_TIMINGR_PRESC(I2C1_TIMINGR_PRESC) | STM32_TIMINGR_SCLDEL(I2C1_TIMINGR_SCLDEL) | STM32_TIMINGR_SDADEL(I2C1_TIMINGR_SDADEL) | STM32_TIMINGR_SCLH(I2C1_TIMINGR_SCLH) | STM32_TIMINGR_SCLL(I2C1_TIMINGR_SCLL), 0, 0
45#endif
46};
47
48static i2c_status_t chibios_to_qmk(const msg_t* status) {
49 switch (*status) {
50 case I2C_NO_ERROR:
51 return I2C_STATUS_SUCCESS;
52 case I2C_TIMEOUT:
53 return I2C_STATUS_TIMEOUT;
54 // I2C_BUS_ERROR, I2C_ARBITRATION_LOST, I2C_ACK_FAILURE, I2C_OVERRUN, I2C_PEC_ERROR, I2C_SMB_ALERT
55 default:
56 return I2C_STATUS_ERROR;
57 }
58}
59
60__attribute__((weak)) void i2c_init(void) {
61 static bool is_initialised = false;
62 if (!is_initialised) {
63 is_initialised = true;
64
65 // Try releasing special pins for a short time
66 palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_INPUT);
67 palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_INPUT);
68
69 chThdSleepMilliseconds(10);
70#if defined(USE_GPIOV1)
71 palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, I2C1_SCL_PAL_MODE);
72 palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, I2C1_SDA_PAL_MODE);
73#else
74 palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_ALTERNATE(I2C1_SCL_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN);
75 palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_ALTERNATE(I2C1_SDA_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN);
76#endif
77 }
78}
79
80i2c_status_t i2c_start(uint8_t address) {
81 i2c_address = address;
82 i2cStart(&I2C_DRIVER, &i2cconfig);
83 return I2C_STATUS_SUCCESS;
84}
85
86i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout) {
87 i2c_address = address;
88 i2cStart(&I2C_DRIVER, &i2cconfig);
89 msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, 0, 0, TIME_MS2I(timeout));
90 return chibios_to_qmk(&status);
91}
92
93i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) {
94 i2c_address = address;
95 i2cStart(&I2C_DRIVER, &i2cconfig);
96 msg_t status = i2cMasterReceiveTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, TIME_MS2I(timeout));
97 return chibios_to_qmk(&status);
98}
99
100i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) {
101 i2c_address = devaddr;
102 i2cStart(&I2C_DRIVER, &i2cconfig);
103
104 uint8_t complete_packet[length + 1];
105 for (uint8_t i = 0; i < length; i++) {
106 complete_packet[i + 1] = data[i];
107 }
108 complete_packet[0] = regaddr;
109
110 msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), complete_packet, length + 1, 0, 0, TIME_MS2I(timeout));
111 return chibios_to_qmk(&status);
112}
113
114i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {
115 i2c_address = devaddr;
116 i2cStart(&I2C_DRIVER, &i2cconfig);
117 msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), &regaddr, 1, data, length, TIME_MS2I(timeout));
118 return chibios_to_qmk(&status);
119}
120
121void i2c_stop(void) { i2cStop(&I2C_DRIVER); }
diff --git a/drivers/chibios/i2c_master.h b/drivers/chibios/i2c_master.h
deleted file mode 100644
index c68109acb..000000000
--- a/drivers/chibios/i2c_master.h
+++ /dev/null
@@ -1,113 +0,0 @@
1/* Copyright 2018 Jack Humbert
2 * Copyright 2018 Yiancar
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/* This library follows the convention of the AVR i2c_master library.
19 * As a result addresses are expected to be already shifted (addr << 1).
20 * I2CD1 is the default driver which corresponds to pins B6 and B7. This
21 * can be changed.
22 * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that
23 * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file.
24 */
25#pragma once
26
27#include <ch.h>
28#include <hal.h>
29
30#ifdef I2C1_BANK
31# define I2C1_SCL_BANK I2C1_BANK
32# define I2C1_SDA_BANK I2C1_BANK
33#endif
34
35#ifndef I2C1_SCL_BANK
36# define I2C1_SCL_BANK GPIOB
37#endif
38
39#ifndef I2C1_SDA_BANK
40# define I2C1_SDA_BANK GPIOB
41#endif
42
43#ifndef I2C1_SCL
44# define I2C1_SCL 6
45#endif
46#ifndef I2C1_SDA
47# define I2C1_SDA 7
48#endif
49
50#ifdef USE_I2CV1
51# ifndef I2C1_OPMODE
52# define I2C1_OPMODE OPMODE_I2C
53# endif
54# ifndef I2C1_CLOCK_SPEED
55# define I2C1_CLOCK_SPEED 100000 /* 400000 */
56# endif
57# ifndef I2C1_DUTY_CYCLE
58# define I2C1_DUTY_CYCLE STD_DUTY_CYCLE /* FAST_DUTY_CYCLE_2 */
59# endif
60#else
61// The default timing values below configures the I2C clock to 400khz assuming a 72Mhz clock
62// For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html
63# ifndef I2C1_TIMINGR_PRESC
64# define I2C1_TIMINGR_PRESC 0U
65# endif
66# ifndef I2C1_TIMINGR_SCLDEL
67# define I2C1_TIMINGR_SCLDEL 7U
68# endif
69# ifndef I2C1_TIMINGR_SDADEL
70# define I2C1_TIMINGR_SDADEL 0U
71# endif
72# ifndef I2C1_TIMINGR_SCLH
73# define I2C1_TIMINGR_SCLH 38U
74# endif
75# ifndef I2C1_TIMINGR_SCLL
76# define I2C1_TIMINGR_SCLL 129U
77# endif
78#endif
79
80#ifndef I2C_DRIVER
81# define I2C_DRIVER I2CD1
82#endif
83
84#ifdef USE_GPIOV1
85# ifndef I2C1_SCL_PAL_MODE
86# define I2C1_SCL_PAL_MODE PAL_MODE_STM32_ALTERNATE_OPENDRAIN
87# endif
88# ifndef I2C1_SDA_PAL_MODE
89# define I2C1_SDA_PAL_MODE PAL_MODE_STM32_ALTERNATE_OPENDRAIN
90# endif
91#else
92// The default PAL alternate modes are used to signal that the pins are used for I2C
93# ifndef I2C1_SCL_PAL_MODE
94# define I2C1_SCL_PAL_MODE 4
95# endif
96# ifndef I2C1_SDA_PAL_MODE
97# define I2C1_SDA_PAL_MODE 4
98# endif
99#endif
100
101typedef int16_t i2c_status_t;
102
103#define I2C_STATUS_SUCCESS (0)
104#define I2C_STATUS_ERROR (-1)
105#define I2C_STATUS_TIMEOUT (-2)
106
107void i2c_init(void);
108i2c_status_t i2c_start(uint8_t address);
109i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout);
110i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);
111i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);
112i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
113void i2c_stop(void);
diff --git a/drivers/chibios/serial.c b/drivers/chibios/serial.c
deleted file mode 100644
index 54f7e1321..000000000
--- a/drivers/chibios/serial.c
+++ /dev/null
@@ -1,290 +0,0 @@
1/*
2 * WARNING: be careful changing this code, it is very timing dependent
3 */
4
5#include "quantum.h"
6#include "serial.h"
7#include "wait.h"
8
9#include <hal.h>
10
11// TODO: resolve/remove build warnings
12#if defined(RGBLIGHT_ENABLE) && defined(RGBLED_SPLIT) && defined(PROTOCOL_CHIBIOS) && defined(WS2812_DRIVER_BITBANG)
13# warning "RGBLED_SPLIT not supported with bitbang WS2812 driver"
14#endif
15
16// default wait implementation cannot be called within interrupt
17// this method seems to be more accurate than GPT timers
18#if PORT_SUPPORTS_RT == FALSE
19# error "chSysPolledDelayX method not supported on this platform"
20#else
21# undef wait_us
22# define wait_us(x) chSysPolledDelayX(US2RTC(STM32_SYSCLK, x))
23#endif
24
25#ifndef SELECT_SOFT_SERIAL_SPEED
26# define SELECT_SOFT_SERIAL_SPEED 1
27// TODO: correct speeds...
28// 0: about 189kbps (Experimental only)
29// 1: about 137kbps (default)
30// 2: about 75kbps
31// 3: about 39kbps
32// 4: about 26kbps
33// 5: about 20kbps
34#endif
35
36// Serial pulse period in microseconds. At the moment, going lower than 12 causes communication failure
37#if SELECT_SOFT_SERIAL_SPEED == 0
38# define SERIAL_DELAY 12
39#elif SELECT_SOFT_SERIAL_SPEED == 1
40# define SERIAL_DELAY 16
41#elif SELECT_SOFT_SERIAL_SPEED == 2
42# define SERIAL_DELAY 24
43#elif SELECT_SOFT_SERIAL_SPEED == 3
44# define SERIAL_DELAY 32
45#elif SELECT_SOFT_SERIAL_SPEED == 4
46# define SERIAL_DELAY 48
47#elif SELECT_SOFT_SERIAL_SPEED == 5
48# define SERIAL_DELAY 64
49#else
50# error invalid SELECT_SOFT_SERIAL_SPEED value
51#endif
52
53inline static void serial_delay(void) { wait_us(SERIAL_DELAY); }
54inline static void serial_delay_half(void) { wait_us(SERIAL_DELAY / 2); }
55inline static void serial_delay_blip(void) { wait_us(1); }
56inline static void serial_output(void) { setPinOutput(SOFT_SERIAL_PIN); }
57inline static void serial_input(void) { setPinInputHigh(SOFT_SERIAL_PIN); }
58inline static bool serial_read_pin(void) { return !!readPin(SOFT_SERIAL_PIN); }
59inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN); }
60inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); }
61
62void interrupt_handler(void *arg);
63
64// Use thread + palWaitLineTimeout instead of palSetLineCallback
65// - Methods like setPinOutput and palEnableLineEvent/palDisableLineEvent
66// cause the interrupt to lock up, which would limit to only receiving data...
67static THD_WORKING_AREA(waThread1, 128);
68static THD_FUNCTION(Thread1, arg) {
69 (void)arg;
70 chRegSetThreadName("blinker");
71 while (true) {
72 palWaitLineTimeout(SOFT_SERIAL_PIN, TIME_INFINITE);
73 interrupt_handler(NULL);
74 }
75}
76
77static SSTD_t *Transaction_table = NULL;
78static uint8_t Transaction_table_size = 0;
79
80void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) {
81 Transaction_table = sstd_table;
82 Transaction_table_size = (uint8_t)sstd_table_size;
83
84 serial_output();
85 serial_high();
86}
87
88void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) {
89 Transaction_table = sstd_table;
90 Transaction_table_size = (uint8_t)sstd_table_size;
91
92 serial_input();
93
94 palEnablePadEvent(PAL_PORT(SOFT_SERIAL_PIN), PAL_PAD(SOFT_SERIAL_PIN), PAL_EVENT_MODE_FALLING_EDGE);
95 chThdCreateStatic(waThread1, sizeof(waThread1), HIGHPRIO, Thread1, NULL);
96}
97
98// Used by the master to synchronize timing with the slave.
99static void __attribute__((noinline)) sync_recv(void) {
100 serial_input();
101 // This shouldn't hang if the slave disconnects because the
102 // serial line will float to high if the slave does disconnect.
103 while (!serial_read_pin()) {
104 }
105
106 serial_delay();
107}
108
109// Used by the slave to send a synchronization signal to the master.
110static void __attribute__((noinline)) sync_send(void) {
111 serial_output();
112
113 serial_low();
114 serial_delay();
115
116 serial_high();
117}
118
119// Reads a byte from the serial line
120static uint8_t __attribute__((noinline)) serial_read_byte(void) {
121 uint8_t byte = 0;
122 serial_input();
123 for (uint8_t i = 0; i < 8; ++i) {
124 byte = (byte << 1) | serial_read_pin();
125 serial_delay();
126 }
127
128 return byte;
129}
130
131// Sends a byte with MSB ordering
132static void __attribute__((noinline)) serial_write_byte(uint8_t data) {
133 uint8_t b = 8;
134 serial_output();
135 while (b--) {
136 if (data & (1 << b)) {
137 serial_high();
138 } else {
139 serial_low();
140 }
141 serial_delay();
142 }
143}
144
145// interrupt handle to be used by the slave device
146void interrupt_handler(void *arg) {
147 chSysLockFromISR();
148
149 sync_send();
150
151 // read mid pulses
152 serial_delay_blip();
153
154 uint8_t checksum_computed = 0;
155 int sstd_index = 0;
156
157#ifdef SERIAL_USE_MULTI_TRANSACTION
158 sstd_index = serial_read_byte();
159 sync_send();
160#endif
161
162 SSTD_t *trans = &Transaction_table[sstd_index];
163 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) {
164 trans->initiator2target_buffer[i] = serial_read_byte();
165 sync_send();
166 checksum_computed += trans->initiator2target_buffer[i];
167 }
168 checksum_computed ^= 7;
169 uint8_t checksum_received = serial_read_byte();
170 sync_send();
171
172 // wait for the sync to finish sending
173 serial_delay();
174
175 uint8_t checksum = 0;
176 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) {
177 serial_write_byte(trans->target2initiator_buffer[i]);
178 sync_send();
179 serial_delay_half();
180 checksum += trans->target2initiator_buffer[i];
181 }
182 serial_write_byte(checksum ^ 7);
183 sync_send();
184
185 // wait for the sync to finish sending
186 serial_delay();
187
188 *trans->status = (checksum_computed == checksum_received) ? TRANSACTION_ACCEPTED : TRANSACTION_DATA_ERROR;
189
190 // end transaction
191 serial_input();
192
193 // TODO: remove extra delay between transactions
194 serial_delay();
195
196 chSysUnlockFromISR();
197}
198
199/////////
200// start transaction by initiator
201//
202// int soft_serial_transaction(int sstd_index)
203//
204// Returns:
205// TRANSACTION_END
206// TRANSACTION_NO_RESPONSE
207// TRANSACTION_DATA_ERROR
208// this code is very time dependent, so we need to disable interrupts
209#ifndef SERIAL_USE_MULTI_TRANSACTION
210int soft_serial_transaction(void) {
211 int sstd_index = 0;
212#else
213int soft_serial_transaction(int sstd_index) {
214#endif
215
216 if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR;
217 SSTD_t *trans = &Transaction_table[sstd_index];
218
219 // TODO: remove extra delay between transactions
220 serial_delay();
221
222 // this code is very time dependent, so we need to disable interrupts
223 chSysLock();
224
225 // signal to the slave that we want to start a transaction
226 serial_output();
227 serial_low();
228 serial_delay_blip();
229
230 // wait for the slaves response
231 serial_input();
232 serial_high();
233 serial_delay();
234
235 // check if the slave is present
236 if (serial_read_pin()) {
237 // slave failed to pull the line low, assume not present
238 dprintf("serial::NO_RESPONSE\n");
239 chSysUnlock();
240 return TRANSACTION_NO_RESPONSE;
241 }
242
243 // if the slave is present syncronize with it
244
245 uint8_t checksum = 0;
246 // send data to the slave
247#ifdef SERIAL_USE_MULTI_TRANSACTION
248 serial_write_byte(sstd_index); // first chunk is transaction id
249 sync_recv();
250#endif
251 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) {
252 serial_write_byte(trans->initiator2target_buffer[i]);
253 sync_recv();
254 checksum += trans->initiator2target_buffer[i];
255 }
256 serial_write_byte(checksum ^ 7);
257 sync_recv();
258
259 serial_delay();
260 serial_delay(); // read mid pulses
261
262 // receive data from the slave
263 uint8_t checksum_computed = 0;
264 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) {
265 trans->target2initiator_buffer[i] = serial_read_byte();
266 sync_recv();
267 checksum_computed += trans->target2initiator_buffer[i];
268 }
269 checksum_computed ^= 7;
270 uint8_t checksum_received = serial_read_byte();
271
272 sync_recv();
273 serial_delay();
274
275 if ((checksum_computed) != (checksum_received)) {
276 dprintf("serial::FAIL[%u,%u,%u]\n", checksum_computed, checksum_received, sstd_index);
277 serial_output();
278 serial_high();
279
280 chSysUnlock();
281 return TRANSACTION_DATA_ERROR;
282 }
283
284 // always, release the line when not in use
285 serial_high();
286 serial_output();
287
288 chSysUnlock();
289 return TRANSACTION_END;
290}
diff --git a/drivers/chibios/serial.h b/drivers/chibios/serial.h
deleted file mode 100644
index 0c1857d52..000000000
--- a/drivers/chibios/serial.h
+++ /dev/null
@@ -1,62 +0,0 @@
1#pragma once
2
3#include <stdbool.h>
4
5// /////////////////////////////////////////////////////////////////
6// Need Soft Serial defines in config.h
7// /////////////////////////////////////////////////////////////////
8// ex.
9// #define SOFT_SERIAL_PIN ?? // ?? = D0,D1,D2,D3,E6
10// OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5
11// // 1: about 137kbps (default)
12// // 2: about 75kbps
13// // 3: about 39kbps
14// // 4: about 26kbps
15// // 5: about 20kbps
16//
17// //// USE simple API (using signle-type transaction function)
18// /* nothing */
19// //// USE flexible API (using multi-type transaction function)
20// #define SERIAL_USE_MULTI_TRANSACTION
21//
22// /////////////////////////////////////////////////////////////////
23
24// Soft Serial Transaction Descriptor
25typedef struct _SSTD_t {
26 uint8_t *status;
27 uint8_t initiator2target_buffer_size;
28 uint8_t *initiator2target_buffer;
29 uint8_t target2initiator_buffer_size;
30 uint8_t *target2initiator_buffer;
31} SSTD_t;
32#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t))
33
34// initiator is transaction start side
35void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size);
36// target is interrupt accept side
37void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size);
38
39// initiator result
40#define TRANSACTION_END 0
41#define TRANSACTION_NO_RESPONSE 0x1
42#define TRANSACTION_DATA_ERROR 0x2
43#define TRANSACTION_TYPE_ERROR 0x4
44#ifndef SERIAL_USE_MULTI_TRANSACTION
45int soft_serial_transaction(void);
46#else
47int soft_serial_transaction(int sstd_index);
48#endif
49
50// target status
51// *SSTD_t.status has
52// initiator:
53// TRANSACTION_END
54// or TRANSACTION_NO_RESPONSE
55// or TRANSACTION_DATA_ERROR
56// target:
57// TRANSACTION_DATA_ERROR
58// or TRANSACTION_ACCEPTED
59#define TRANSACTION_ACCEPTED 0x8
60#ifdef SERIAL_USE_MULTI_TRANSACTION
61int soft_serial_get_and_clean_status(int sstd_index);
62#endif
diff --git a/drivers/chibios/serial_usart.c b/drivers/chibios/serial_usart.c
deleted file mode 100644
index cae29388c..000000000
--- a/drivers/chibios/serial_usart.c
+++ /dev/null
@@ -1,208 +0,0 @@
1/* Copyright 2021 QMK
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 3 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 "serial_usart.h"
18
19#ifndef USE_GPIOV1
20// The default PAL alternate modes are used to signal that the pins are used for USART
21# ifndef SERIAL_USART_TX_PAL_MODE
22# define SERIAL_USART_TX_PAL_MODE 7
23# endif
24#endif
25
26#ifndef SERIAL_USART_DRIVER
27# define SERIAL_USART_DRIVER SD1
28#endif
29
30#ifdef SOFT_SERIAL_PIN
31# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN
32#endif
33
34static inline msg_t sdWriteHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size) {
35 msg_t ret = sdWrite(driver, data, size);
36
37 // Half duplex requires us to read back the data we just wrote - just throw it away
38 uint8_t dump[size];
39 sdRead(driver, dump, size);
40
41 return ret;
42}
43#undef sdWrite
44#define sdWrite sdWriteHalfDuplex
45
46static inline msg_t sdWriteTimeoutHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size, uint32_t timeout) {
47 msg_t ret = sdWriteTimeout(driver, data, size, timeout);
48
49 // Half duplex requires us to read back the data we just wrote - just throw it away
50 uint8_t dump[size];
51 sdReadTimeout(driver, dump, size, timeout);
52
53 return ret;
54}
55#undef sdWriteTimeout
56#define sdWriteTimeout sdWriteTimeoutHalfDuplex
57
58static inline void sdClear(SerialDriver* driver) {
59 while (sdGetTimeout(driver, TIME_IMMEDIATE) != MSG_TIMEOUT) {
60 // Do nothing with the data
61 }
62}
63
64static SerialConfig sdcfg = {
65 (SERIAL_USART_SPEED), // speed - mandatory
66 (SERIAL_USART_CR1), // CR1
67 (SERIAL_USART_CR2), // CR2
68 (SERIAL_USART_CR3) // CR3
69};
70
71void handle_soft_serial_slave(void);
72
73/*
74 * This thread runs on the slave and responds to transactions initiated
75 * by the master
76 */
77static THD_WORKING_AREA(waSlaveThread, 2048);
78static THD_FUNCTION(SlaveThread, arg) {
79 (void)arg;
80 chRegSetThreadName("slave_transport");
81
82 while (true) {
83 handle_soft_serial_slave();
84 }
85}
86
87__attribute__((weak)) void usart_init(void) {
88#if defined(USE_GPIOV1)
89 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN);
90#else
91 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN);
92#endif
93
94#if defined(USART_REMAP)
95 USART_REMAP;
96#endif
97}
98
99void usart_master_init(void) {
100 usart_init();
101
102 sdcfg.cr3 |= USART_CR3_HDSEL;
103 sdStart(&SERIAL_USART_DRIVER, &sdcfg);
104}
105
106void usart_slave_init(void) {
107 usart_init();
108
109 sdcfg.cr3 |= USART_CR3_HDSEL;
110 sdStart(&SERIAL_USART_DRIVER, &sdcfg);
111
112 // Start transport thread
113 chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);
114}
115
116static SSTD_t* Transaction_table = NULL;
117static uint8_t Transaction_table_size = 0;
118
119void soft_serial_initiator_init(SSTD_t* sstd_table, int sstd_table_size) {
120 Transaction_table = sstd_table;
121 Transaction_table_size = (uint8_t)sstd_table_size;
122
123 usart_master_init();
124}
125
126void soft_serial_target_init(SSTD_t* sstd_table, int sstd_table_size) {
127 Transaction_table = sstd_table;
128 Transaction_table_size = (uint8_t)sstd_table_size;
129
130 usart_slave_init();
131}
132
133void handle_soft_serial_slave(void) {
134 uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id
135 SSTD_t* trans = &Transaction_table[sstd_index];
136
137 // Always write back the sstd_index as part of a basic handshake
138 sstd_index ^= HANDSHAKE_MAGIC;
139 sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index));
140
141 if (trans->initiator2target_buffer_size) {
142 sdRead(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size);
143 }
144
145 if (trans->target2initiator_buffer_size) {
146 sdWrite(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size);
147 }
148
149 if (trans->status) {
150 *trans->status = TRANSACTION_ACCEPTED;
151 }
152}
153
154/////////
155// start transaction by initiator
156//
157// int soft_serial_transaction(int sstd_index)
158//
159// Returns:
160// TRANSACTION_END
161// TRANSACTION_NO_RESPONSE
162// TRANSACTION_DATA_ERROR
163#ifndef SERIAL_USE_MULTI_TRANSACTION
164int soft_serial_transaction(void) {
165 uint8_t sstd_index = 0;
166#else
167int soft_serial_transaction(int index) {
168 uint8_t sstd_index = index;
169#endif
170
171 if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR;
172 SSTD_t* trans = &Transaction_table[sstd_index];
173 msg_t res = 0;
174
175 sdClear(&SERIAL_USART_DRIVER);
176
177 // First chunk is always transaction id
178 sdWriteTimeout(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index), TIME_MS2I(SERIAL_USART_TIMEOUT));
179
180 uint8_t sstd_index_shake = 0xFF;
181
182 // Which we always read back first so that we can error out correctly
183 // - due to the half duplex limitations on return codes, we always have to read *something*
184 // - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready
185 res = sdReadTimeout(&SERIAL_USART_DRIVER, &sstd_index_shake, sizeof(sstd_index_shake), TIME_MS2I(SERIAL_USART_TIMEOUT));
186 if (res < 0 || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) {
187 dprintf("serial::usart_shake NO_RESPONSE\n");
188 return TRANSACTION_NO_RESPONSE;
189 }
190
191 if (trans->initiator2target_buffer_size) {
192 res = sdWriteTimeout(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT));
193 if (res < 0) {
194 dprintf("serial::usart_transmit NO_RESPONSE\n");
195 return TRANSACTION_NO_RESPONSE;
196 }
197 }
198
199 if (trans->target2initiator_buffer_size) {
200 res = sdReadTimeout(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT));
201 if (res < 0) {
202 dprintf("serial::usart_receive NO_RESPONSE\n");
203 return TRANSACTION_NO_RESPONSE;
204 }
205 }
206
207 return TRANSACTION_END;
208}
diff --git a/drivers/chibios/serial_usart.h b/drivers/chibios/serial_usart.h
deleted file mode 100644
index fee7b4d15..000000000
--- a/drivers/chibios/serial_usart.h
+++ /dev/null
@@ -1,90 +0,0 @@
1/* Copyright 2021 QMK
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 3 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 "quantum.h"
20#include "serial.h"
21#include "printf.h"
22
23#include <ch.h>
24#include <hal.h>
25
26#ifndef USART_CR1_M0
27# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so
28#endif
29
30#ifndef SERIAL_USART_CR1
31# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length
32#endif
33
34#ifndef SERIAL_USART_CR2
35# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits
36#endif
37
38#ifndef SERIAL_USART_CR3
39# define SERIAL_USART_CR3 0
40#endif
41
42#if defined(USART1_REMAP)
43# define USART_REMAP \
44 do { \
45 (AFIO->MAPR |= AFIO_MAPR_USART1_REMAP); \
46 } while (0)
47#elif defined(USART2_REMAP)
48# define USART_REMAP \
49 do { \
50 (AFIO->MAPR |= AFIO_MAPR_USART2_REMAP); \
51 } while (0)
52#elif defined(USART3_PARTIALREMAP)
53# define USART_REMAP \
54 do { \
55 (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_PARTIALREMAP); \
56 } while (0)
57#elif defined(USART3_FULLREMAP)
58# define USART_REMAP \
59 do { \
60 (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_FULLREMAP); \
61 } while (0)
62#endif
63
64#ifndef SELECT_SOFT_SERIAL_SPEED
65# define SELECT_SOFT_SERIAL_SPEED 1
66#endif
67
68#ifdef SERIAL_USART_SPEED
69// Allow advanced users to directly set SERIAL_USART_SPEED
70#elif SELECT_SOFT_SERIAL_SPEED == 0
71# define SERIAL_USART_SPEED 460800
72#elif SELECT_SOFT_SERIAL_SPEED == 1
73# define SERIAL_USART_SPEED 230400
74#elif SELECT_SOFT_SERIAL_SPEED == 2
75# define SERIAL_USART_SPEED 115200
76#elif SELECT_SOFT_SERIAL_SPEED == 3
77# define SERIAL_USART_SPEED 57600
78#elif SELECT_SOFT_SERIAL_SPEED == 4
79# define SERIAL_USART_SPEED 38400
80#elif SELECT_SOFT_SERIAL_SPEED == 5
81# define SERIAL_USART_SPEED 19200
82#else
83# error invalid SELECT_SOFT_SERIAL_SPEED value
84#endif
85
86#ifndef SERIAL_USART_TIMEOUT
87# define SERIAL_USART_TIMEOUT 100
88#endif
89
90#define HANDSHAKE_MAGIC 7
diff --git a/drivers/chibios/serial_usart_duplex.c b/drivers/chibios/serial_usart_duplex.c
deleted file mode 100644
index cc9b889ac..000000000
--- a/drivers/chibios/serial_usart_duplex.c
+++ /dev/null
@@ -1,261 +0,0 @@
1/* Copyright 2021 QMK
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 3 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 "serial_usart.h"
18
19#include <stdatomic.h>
20
21#if !defined(USE_GPIOV1)
22// The default PAL alternate modes are used to signal that the pins are used for USART
23# if !defined(SERIAL_USART_TX_PAL_MODE)
24# define SERIAL_USART_TX_PAL_MODE 7
25# endif
26# if !defined(SERIAL_USART_RX_PAL_MODE)
27# define SERIAL_USART_RX_PAL_MODE 7
28# endif
29#endif
30
31#if !defined(SERIAL_USART_DRIVER)
32# define SERIAL_USART_DRIVER UARTD1
33#endif
34
35#if !defined(SERIAL_USART_TX_PIN)
36# define SERIAL_USART_TX_PIN A9
37#endif
38
39#if !defined(SERIAL_USART_RX_PIN)
40# define SERIAL_USART_RX_PIN A10
41#endif
42
43#define SIGNAL_HANDSHAKE_RECEIVED 0x1
44
45void handle_transactions_slave(uint8_t sstd_index);
46static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake);
47
48/*
49 * UART driver configuration structure. We use the blocking DMA enabled API and
50 * the rxchar callback to receive handshake tokens but only on the slave halve.
51 */
52// clang-format off
53static UARTConfig uart_config = {
54 .txend1_cb = NULL,
55 .txend2_cb = NULL,
56 .rxend_cb = NULL,
57 .rxchar_cb = NULL,
58 .rxerr_cb = NULL,
59 .timeout_cb = NULL,
60 .speed = (SERIAL_USART_SPEED),
61 .cr1 = (SERIAL_USART_CR1),
62 .cr2 = (SERIAL_USART_CR2),
63 .cr3 = (SERIAL_USART_CR3)
64};
65// clang-format on
66
67static SSTD_t* Transaction_table = NULL;
68static uint8_t Transaction_table_size = 0;
69static atomic_uint_least8_t handshake = 0xFF;
70static thread_reference_t tp_target = NULL;
71
72/*
73 * This callback is invoked when a character is received but the application
74 * was not ready to receive it, the character is passed as parameter.
75 * Receive transaction table index from initiator, which doubles as basic handshake token. */
76static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake) {
77 /* Check if received handshake is not a valid transaction id.
78 * Please note that we can still catch a seemingly valid handshake
79 * i.e. a byte from a ongoing transfer which is in the allowed range.
80 * So this check mainly prevents any obviously wrong handshakes and
81 * subsequent wakeups of the receiving thread, which is a costly operation. */
82 if (received_handshake > Transaction_table_size) {
83 return;
84 }
85
86 handshake = (uint8_t)received_handshake;
87 chSysLockFromISR();
88 /* Wakeup receiving thread to start a transaction. */
89 chEvtSignalI(tp_target, (eventmask_t)SIGNAL_HANDSHAKE_RECEIVED);
90 chSysUnlockFromISR();
91}
92
93__attribute__((weak)) void usart_init(void) {
94#if defined(USE_GPIOV1)
95 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
96 palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT);
97#else
98 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
99 palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
100#endif
101}
102
103/*
104 * This thread runs on the slave half and reacts to transactions initiated from the master.
105 */
106static THD_WORKING_AREA(waSlaveThread, 1024);
107static THD_FUNCTION(SlaveThread, arg) {
108 (void)arg;
109 chRegSetThreadName("slave_usart_tx_rx");
110
111 while (true) {
112 /* We sleep as long as there is no handshake waiting for us. */
113 chEvtWaitAny((eventmask_t)SIGNAL_HANDSHAKE_RECEIVED);
114 handle_transactions_slave(handshake);
115 }
116}
117
118void soft_serial_target_init(SSTD_t* const sstd_table, int sstd_table_size) {
119 Transaction_table = sstd_table;
120 Transaction_table_size = (uint8_t)sstd_table_size;
121 usart_init();
122
123#if defined(USART_REMAP)
124 USART_REMAP;
125#endif
126
127 tp_target = chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);
128
129 // Start receiving handshake tokens on slave halve
130 uart_config.rxchar_cb = receive_transaction_handshake;
131 uartStart(&SERIAL_USART_DRIVER, &uart_config);
132}
133
134/**
135 * @brief React to transactions started by the master.
136 * This version uses duplex send and receive usart pheriphals and DMA backed transfers.
137 */
138void inline handle_transactions_slave(uint8_t sstd_index) {
139 size_t buffer_size = 0;
140 msg_t msg = 0;
141 SSTD_t* trans = &Transaction_table[sstd_index];
142
143 /* Send back the handshake which is XORed as a simple checksum,
144 to signal that the slave is ready to receive possible transaction buffers */
145 sstd_index ^= HANDSHAKE_MAGIC;
146 buffer_size = (size_t)sizeof(sstd_index);
147 msg = uartSendTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT));
148
149 if (msg != MSG_OK) {
150 if (trans->status) {
151 *trans->status = TRANSACTION_NO_RESPONSE;
152 }
153 return;
154 }
155
156 /* Receive transaction buffer from the master. If this transaction requires it.*/
157 buffer_size = (size_t)trans->initiator2target_buffer_size;
158 if (buffer_size) {
159 msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
160 if (msg != MSG_OK) {
161 if (trans->status) {
162 *trans->status = TRANSACTION_NO_RESPONSE;
163 }
164 return;
165 }
166 }
167
168 /* Send transaction buffer to the master. If this transaction requires it. */
169 buffer_size = (size_t)trans->target2initiator_buffer_size;
170 if (buffer_size) {
171 msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
172 if (msg != MSG_OK) {
173 if (trans->status) {
174 *trans->status = TRANSACTION_NO_RESPONSE;
175 }
176 return;
177 }
178 }
179
180 if (trans->status) {
181 *trans->status = TRANSACTION_ACCEPTED;
182 }
183}
184
185void soft_serial_initiator_init(SSTD_t* const sstd_table, int sstd_table_size) {
186 Transaction_table = sstd_table;
187 Transaction_table_size = (uint8_t)sstd_table_size;
188 usart_init();
189
190#if defined(SERIAL_USART_PIN_SWAP)
191 uart_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins
192#endif
193
194#if defined(USART_REMAP)
195 USART_REMAP;
196#endif
197
198 uartStart(&SERIAL_USART_DRIVER, &uart_config);
199}
200
201/**
202 * @brief Start transaction from the master to the slave.
203 * This version uses duplex send and receive usart pheriphals and DMA backed transfers.
204 *
205 * @param index Transaction Table index of the transaction to start.
206 * @return int TRANSACTION_NO_RESPONSE in case of Timeout.
207 * TRANSACTION_TYPE_ERROR in case of invalid transaction index.
208 * TRANSACTION_END in case of success.
209 */
210#if !defined(SERIAL_USE_MULTI_TRANSACTION)
211int soft_serial_transaction(void) {
212 uint8_t sstd_index = 0;
213#else
214int soft_serial_transaction(int index) {
215 uint8_t sstd_index = index;
216#endif
217
218 if (sstd_index > Transaction_table_size) {
219 return TRANSACTION_TYPE_ERROR;
220 }
221
222 SSTD_t* const trans = &Transaction_table[sstd_index];
223 msg_t msg = 0;
224 size_t buffer_size = (size_t)sizeof(sstd_index);
225
226 /* Send transaction table index to the slave, which doubles as basic handshake token. */
227 uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT));
228
229 uint8_t sstd_index_shake = 0xFF;
230 buffer_size = (size_t)sizeof(sstd_index_shake);
231
232 /* Receive the handshake token from the slave. The token was XORed by the slave as a simple checksum.
233 If the tokens match, the master will start to send and receive possible transaction buffers. */
234 msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index_shake, TIME_MS2I(SERIAL_USART_TIMEOUT));
235 if (msg != MSG_OK || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) {
236 dprintln("USART: Handshake Failed");
237 return TRANSACTION_NO_RESPONSE;
238 }
239
240 /* Send transaction buffer to the slave. If this transaction requires it. */
241 buffer_size = (size_t)trans->initiator2target_buffer_size;
242 if (buffer_size) {
243 msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
244 if (msg != MSG_OK) {
245 dprintln("USART: Send Failed");
246 return TRANSACTION_NO_RESPONSE;
247 }
248 }
249
250 /* Receive transaction buffer from the slave. If this transaction requires it. */
251 buffer_size = (size_t)trans->target2initiator_buffer_size;
252 if (buffer_size) {
253 msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
254 if (msg != MSG_OK) {
255 dprintln("USART: Receive Failed");
256 return TRANSACTION_NO_RESPONSE;
257 }
258 }
259
260 return TRANSACTION_END;
261}
diff --git a/drivers/chibios/spi_master.c b/drivers/chibios/spi_master.c
deleted file mode 100644
index 4852a6eba..000000000
--- a/drivers/chibios/spi_master.c
+++ /dev/null
@@ -1,148 +0,0 @@
1/* Copyright 2020 Nick Brassel (tzarc)
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 3 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 <https://www.gnu.org/licenses/>.
15 */
16
17#include "spi_master.h"
18
19#include "timer.h"
20
21static pin_t currentSlavePin = NO_PIN;
22static SPIConfig spiConfig = {false, NULL, 0, 0, 0, 0};
23
24__attribute__((weak)) void spi_init(void) {
25 static bool is_initialised = false;
26 if (!is_initialised) {
27 is_initialised = true;
28
29 // Try releasing special pins for a short time
30 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_INPUT);
31 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_INPUT);
32 palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_INPUT);
33
34 chThdSleepMilliseconds(10);
35#if defined(USE_GPIOV1)
36 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL);
37 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL);
38 palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL);
39#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);
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);
42 palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_ALTERNATE(SPI_MISO_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
43#endif
44 }
45}
46
47bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {
48 if (currentSlavePin != NO_PIN || slavePin == NO_PIN) {
49 return false;
50 }
51
52 uint16_t roundedDivisor = 2;
53 while (roundedDivisor < divisor) {
54 roundedDivisor <<= 1;
55 }
56
57 if (roundedDivisor < 2 || roundedDivisor > 256) {
58 return false;
59 }
60
61 spiConfig.cr1 = 0;
62
63 if (lsbFirst) {
64 spiConfig.cr1 |= SPI_CR1_LSBFIRST;
65 }
66
67 switch (mode) {
68 case 0:
69 break;
70 case 1:
71 spiConfig.cr1 |= SPI_CR1_CPHA;
72 break;
73 case 2:
74 spiConfig.cr1 |= SPI_CR1_CPOL;
75 break;
76 case 3:
77 spiConfig.cr1 |= SPI_CR1_CPHA | SPI_CR1_CPOL;
78 break;
79 }
80
81 switch (roundedDivisor) {
82 case 2:
83 break;
84 case 4:
85 spiConfig.cr1 |= SPI_CR1_BR_0;
86 break;
87 case 8:
88 spiConfig.cr1 |= SPI_CR1_BR_1;
89 break;
90 case 16:
91 spiConfig.cr1 |= SPI_CR1_BR_1 | SPI_CR1_BR_0;
92 break;
93 case 32:
94 spiConfig.cr1 |= SPI_CR1_BR_2;
95 break;
96 case 64:
97 spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_0;
98 break;
99 case 128:
100 spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1;
101 break;
102 case 256:
103 spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0;
104 break;
105 }
106
107 currentSlavePin = slavePin;
108 spiConfig.ssport = PAL_PORT(slavePin);
109 spiConfig.sspad = PAL_PAD(slavePin);
110
111 setPinOutput(slavePin);
112 spiStart(&SPI_DRIVER, &spiConfig);
113 spiSelect(&SPI_DRIVER);
114
115 return true;
116}
117
118spi_status_t spi_write(uint8_t data) {
119 uint8_t rxData;
120 spiExchange(&SPI_DRIVER, 1, &data, &rxData);
121
122 return rxData;
123}
124
125spi_status_t spi_read(void) {
126 uint8_t data = 0;
127 spiReceive(&SPI_DRIVER, 1, &data);
128
129 return data;
130}
131
132spi_status_t spi_transmit(const uint8_t *data, uint16_t length) {
133 spiSend(&SPI_DRIVER, length, data);
134 return SPI_STATUS_SUCCESS;
135}
136
137spi_status_t spi_receive(uint8_t *data, uint16_t length) {
138 spiReceive(&SPI_DRIVER, length, data);
139 return SPI_STATUS_SUCCESS;
140}
141
142void spi_stop(void) {
143 if (currentSlavePin != NO_PIN) {
144 spiUnselect(&SPI_DRIVER);
145 spiStop(&SPI_DRIVER);
146 currentSlavePin = NO_PIN;
147 }
148}
diff --git a/drivers/chibios/spi_master.h b/drivers/chibios/spi_master.h
deleted file mode 100644
index e93580e31..000000000
--- a/drivers/chibios/spi_master.h
+++ /dev/null
@@ -1,80 +0,0 @@
1/* Copyright 2020 Nick Brassel (tzarc)
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 3 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 <https://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <ch.h>
20#include <hal.h>
21#include <stdbool.h>
22
23#include "gpio.h"
24
25#ifndef SPI_DRIVER
26# define SPI_DRIVER SPID2
27#endif
28
29#ifndef SPI_SCK_PIN
30# define SPI_SCK_PIN B13
31#endif
32
33#ifndef SPI_SCK_PAL_MODE
34# define SPI_SCK_PAL_MODE 5
35#endif
36
37#ifndef SPI_MOSI_PIN
38# define SPI_MOSI_PIN B15
39#endif
40
41#ifndef SPI_MOSI_PAL_MODE
42# define SPI_MOSI_PAL_MODE 5
43#endif
44
45#ifndef SPI_MISO_PIN
46# define SPI_MISO_PIN B14
47#endif
48
49#ifndef SPI_MISO_PAL_MODE
50# define SPI_MISO_PAL_MODE 5
51#endif
52
53typedef int16_t spi_status_t;
54
55#define SPI_STATUS_SUCCESS (0)
56#define SPI_STATUS_ERROR (-1)
57#define SPI_STATUS_TIMEOUT (-2)
58
59#define SPI_TIMEOUT_IMMEDIATE (0)
60#define SPI_TIMEOUT_INFINITE (0xFFFF)
61
62#ifdef __cplusplus
63extern "C" {
64#endif
65void spi_init(void);
66
67bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor);
68
69spi_status_t spi_write(uint8_t data);
70
71spi_status_t spi_read(void);
72
73spi_status_t spi_transmit(const uint8_t *data, uint16_t length);
74
75spi_status_t spi_receive(uint8_t *data, uint16_t length);
76
77void spi_stop(void);
78#ifdef __cplusplus
79}
80#endif
diff --git a/drivers/chibios/uart.c b/drivers/chibios/uart.c
deleted file mode 100644
index 030335b34..000000000
--- a/drivers/chibios/uart.c
+++ /dev/null
@@ -1,50 +0,0 @@
1/* Copyright 2021
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 3 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 <https://www.gnu.org/licenses/>.
15 */
16
17#include "uart.h"
18
19#include "quantum.h"
20
21static SerialConfig serialConfig = {SERIAL_DEFAULT_BITRATE, SD1_CR1, SD1_CR2, SD1_CR3};
22
23void uart_init(uint32_t baud) {
24 static bool is_initialised = false;
25
26 if (!is_initialised) {
27 is_initialised = true;
28
29 serialConfig.speed = baud;
30
31#if defined(USE_GPIOV1)
32 palSetLineMode(SD1_TX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN);
33 palSetLineMode(SD1_RX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN);
34#else
35 palSetLineMode(SD1_TX_PIN, PAL_MODE_ALTERNATE(SD1_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN);
36 palSetLineMode(SD1_RX_PIN, PAL_MODE_ALTERNATE(SD1_RX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN);
37#endif
38 sdStart(&SERIAL_DRIVER, &serialConfig);
39 }
40}
41
42void uart_putchar(uint8_t c) { sdPut(&SERIAL_DRIVER, c); }
43
44uint8_t uart_getchar(void) {
45 msg_t res = sdGet(&SERIAL_DRIVER);
46
47 return (uint8_t)res;
48}
49
50bool uart_available(void) { return !sdGetWouldBlock(&SERIAL_DRIVER); }
diff --git a/drivers/chibios/uart.h b/drivers/chibios/uart.h
deleted file mode 100644
index b4e20e9fd..000000000
--- a/drivers/chibios/uart.h
+++ /dev/null
@@ -1,77 +0,0 @@
1/* Copyright 2021
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 3 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 <https://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20
21#include <hal.h>
22
23#ifndef SERIAL_DRIVER
24# define SERIAL_DRIVER SD1
25#endif
26
27#ifndef SD1_TX_PIN
28# define SD1_TX_PIN A9
29#endif
30
31#ifndef SD1_TX_PAL_MODE
32# define SD1_TX_PAL_MODE 7
33#endif
34
35#ifndef SD1_RX_PIN
36# define SD1_RX_PIN A10
37#endif
38
39#ifndef SD1_RX_PAL_MODE
40# define SD1_RX_PAL_MODE 7
41#endif
42
43#ifndef SD1_CTS_PIN
44# define SD1_CTS_PIN A11
45#endif
46
47#ifndef SD1_CTS_PAL_MODE
48# define SD1_CTS_PAL_MODE 7
49#endif
50
51#ifndef SD1_RTS_PIN
52# define SD1_RTS_PIN A12
53#endif
54
55#ifndef SD1_RTS_PAL_MODE
56# define SD1_RTS_PAL_MODE 7
57#endif
58
59#ifndef SD1_CR1
60# define SD1_CR1 0
61#endif
62
63#ifndef SD1_CR2
64# define SD1_CR2 0
65#endif
66
67#ifndef SD1_CR3
68# define SD1_CR3 0
69#endif
70
71void uart_init(uint32_t baud);
72
73void uart_putchar(uint8_t c);
74
75uint8_t uart_getchar(void);
76
77bool uart_available(void);
diff --git a/drivers/chibios/usbpd_stm32g4.c b/drivers/chibios/usbpd_stm32g4.c
deleted file mode 100644
index f16ca8aea..000000000
--- a/drivers/chibios/usbpd_stm32g4.c
+++ /dev/null
@@ -1,76 +0,0 @@
1/* Copyright 2021 Nick Brassel (@tzarc)
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 <quantum.h>
18
19#ifndef USBPD_UCPD1_CFG1
20# define USBPD_UCPD1_CFG1 (UCPD_CFG1_PSC_UCPDCLK_0 | UCPD_CFG1_TRANSWIN_3 | UCPD_CFG1_IFRGAP_4 | UCPD_CFG1_HBITCLKDIV_4)
21#endif // USBPD_UCPD1_CFG1
22
23// Initialises the USBPD subsystem
24__attribute__((weak)) void usbpd_init(void) {
25 // Disable dead-battery signals
26 PWR->CR3 |= PWR_CR3_UCPD_DBDIS;
27 // Enable the clock for the UCPD1 peripheral
28 RCC->APB1ENR2 |= RCC_APB1ENR2_UCPD1EN;
29
30 // Copy the existing value
31 uint32_t CFG1 = UCPD1->CFG1;
32 // Force-disable UCPD1 before configuring
33 CFG1 &= ~UCPD_CFG1_UCPDEN;
34 // Configure UCPD1
35 CFG1 = USBPD_UCPD1_CFG1;
36 // Apply the changes
37 UCPD1->CFG1 = CFG1;
38 // Enable UCPD1
39 UCPD1->CFG1 |= UCPD_CFG1_UCPDEN;
40
41 // Copy the existing value
42 uint32_t CR = UCPD1->CR;
43 // Clear out ANASUBMODE (irrelevant as a sink device)
44 CR &= ~UCPD_CR_ANASUBMODE_Msk;
45 // Advertise our capabilities as a sink, with both CC lines enabled
46 CR |= UCPD_CR_ANAMODE | UCPD_CR_CCENABLE_Msk;
47 // Apply the changes
48 UCPD1->CR = CR;
49}
50
51// Gets the current state of the USBPD allowance
52__attribute__((weak)) usbpd_allowance_t usbpd_get_allowance(void) {
53 uint32_t CR = UCPD1->CR;
54
55 int ucpd_enabled = (UCPD1->CFG1 & UCPD_CFG1_UCPDEN_Msk) >> UCPD_CFG1_UCPDEN_Pos;
56 int anamode = (CR & UCPD_CR_ANAMODE_Msk) >> UCPD_CR_ANAMODE_Pos;
57 int cc_enabled = (CR & UCPD_CR_CCENABLE_Msk) >> UCPD_CR_CCENABLE_Pos;
58
59 if (ucpd_enabled && anamode && cc_enabled) {
60 uint32_t SR = UCPD1->SR;
61 int vstate_cc1 = (SR & UCPD_SR_TYPEC_VSTATE_CC1_Msk) >> UCPD_SR_TYPEC_VSTATE_CC1_Pos;
62 int vstate_cc2 = (SR & UCPD_SR_TYPEC_VSTATE_CC2_Msk) >> UCPD_SR_TYPEC_VSTATE_CC2_Pos;
63 int vstate_max = vstate_cc1 > vstate_cc2 ? vstate_cc1 : vstate_cc2;
64 switch (vstate_max) {
65 case 0:
66 case 1:
67 return USBPD_500MA; // Note that this is 500mA (i.e. max USB 2.0), not 900mA, as we're not using USB 3.1 as a sink device.
68 case 2:
69 return USBPD_1500MA;
70 case 3:
71 return USBPD_3000MA;
72 }
73 }
74
75 return USBPD_500MA;
76} \ No newline at end of file
diff --git a/drivers/chibios/ws2812.c b/drivers/chibios/ws2812.c
deleted file mode 100644
index 0d12e2fb7..000000000
--- a/drivers/chibios/ws2812.c
+++ /dev/null
@@ -1,114 +0,0 @@
1#include "quantum.h"
2#include "ws2812.h"
3#include <ch.h>
4#include <hal.h>
5
6/* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */
7
8#ifndef NOP_FUDGE
9# if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX)
10# define NOP_FUDGE 0.4
11# else
12# error("NOP_FUDGE configuration required")
13# define NOP_FUDGE 1 // this just pleases the compile so the above error is easier to spot
14# endif
15#endif
16
17// Push Pull or Open Drain Configuration
18// Default Push Pull
19#ifndef WS2812_EXTERNAL_PULLUP
20# define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_PUSHPULL
21#else
22# define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_OPENDRAIN
23#endif
24
25#define NUMBER_NOPS 6
26#define CYCLES_PER_SEC (STM32_SYSCLK / NUMBER_NOPS * NOP_FUDGE)
27#define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives
28#define NS_PER_CYCLE (NS_PER_SEC / CYCLES_PER_SEC)
29#define NS_TO_CYCLES(n) ((n) / NS_PER_CYCLE)
30
31#define wait_ns(x) \
32 do { \
33 for (int i = 0; i < NS_TO_CYCLES(x); i++) { \
34 __asm__ volatile("nop\n\t" \
35 "nop\n\t" \
36 "nop\n\t" \
37 "nop\n\t" \
38 "nop\n\t" \
39 "nop\n\t"); \
40 } \
41 } while (0)
42
43// These are the timing constraints taken mostly from the WS2812 datasheets
44// These are chosen to be conservative and avoid problems rather than for maximum throughput
45
46#define T1H 900 // Width of a 1 bit in ns
47#define T1L (1250 - T1H) // Width of a 1 bit in ns
48
49#define T0H 350 // Width of a 0 bit in ns
50#define T0L (1250 - T0H) // Width of a 0 bit in ns
51
52// The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased
53// to values like 600000 ns. If it is too small, the pixels will show nothing most of the time.
54#define RES (1000 * WS2812_TRST_US) // Width of the low gap between bits to cause a frame to latch
55
56void sendByte(uint8_t byte) {
57 // WS2812 protocol wants most significant bits first
58 for (unsigned char bit = 0; bit < 8; bit++) {
59 bool is_one = byte & (1 << (7 - bit));
60 // using something like wait_ns(is_one ? T1L : T0L) here throws off timings
61 if (is_one) {
62 // 1
63 writePinHigh(RGB_DI_PIN);
64 wait_ns(T1H);
65 writePinLow(RGB_DI_PIN);
66 wait_ns(T1L);
67 } else {
68 // 0
69 writePinHigh(RGB_DI_PIN);
70 wait_ns(T0H);
71 writePinLow(RGB_DI_PIN);
72 wait_ns(T0L);
73 }
74 }
75}
76
77void ws2812_init(void) { palSetLineMode(RGB_DI_PIN, WS2812_OUTPUT_MODE); }
78
79// Setleds for standard RGB
80void ws2812_setleds(LED_TYPE *ledarray, uint16_t leds) {
81 static bool s_init = false;
82 if (!s_init) {
83 ws2812_init();
84 s_init = true;
85 }
86
87 // this code is very time dependent, so we need to disable interrupts
88 chSysLock();
89
90 for (uint8_t i = 0; i < leds; i++) {
91 // WS2812 protocol dictates grb order
92#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)
93 sendByte(ledarray[i].g);
94 sendByte(ledarray[i].r);
95 sendByte(ledarray[i].b);
96#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)
97 sendByte(ledarray[i].r);
98 sendByte(ledarray[i].g);
99 sendByte(ledarray[i].b);
100#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)
101 sendByte(ledarray[i].b);
102 sendByte(ledarray[i].g);
103 sendByte(ledarray[i].r);
104#endif
105
106#ifdef RGBW
107 sendByte(ledarray[i].w);
108#endif
109 }
110
111 wait_ns(RES);
112
113 chSysUnlock();
114}
diff --git a/drivers/chibios/ws2812_pwm.c b/drivers/chibios/ws2812_pwm.c
deleted file mode 100644
index e6af55b6b..000000000
--- a/drivers/chibios/ws2812_pwm.c
+++ /dev/null
@@ -1,311 +0,0 @@
1#include "ws2812.h"
2#include "quantum.h"
3#include <hal.h>
4
5/* Adapted from https://github.com/joewa/WS2812-LED-Driver_ChibiOS/ */
6
7#ifdef RGBW
8# error "RGBW not supported"
9#endif
10
11#ifndef WS2812_PWM_DRIVER
12# define WS2812_PWM_DRIVER PWMD2 // TIMx
13#endif
14#ifndef WS2812_PWM_CHANNEL
15# define WS2812_PWM_CHANNEL 2 // Channel
16#endif
17#ifndef WS2812_PWM_PAL_MODE
18# define WS2812_PWM_PAL_MODE 2 // DI Pin's alternate function value
19#endif
20#ifndef WS2812_DMA_STREAM
21# define WS2812_DMA_STREAM STM32_DMA1_STREAM2 // DMA Stream for TIMx_UP
22#endif
23#ifndef WS2812_DMA_CHANNEL
24# define WS2812_DMA_CHANNEL 2 // DMA Channel for TIMx_UP
25#endif
26#if (STM32_DMA_SUPPORTS_DMAMUX == TRUE) && !defined(WS2812_DMAMUX_ID)
27# error "please consult your MCU's datasheet and specify in your config.h: #define WS2812_DMAMUX_ID STM32_DMAMUX1_TIM?_UP"
28#endif
29
30#ifndef WS2812_PWM_COMPLEMENTARY_OUTPUT
31# define WS2812_PWM_OUTPUT_MODE PWM_OUTPUT_ACTIVE_HIGH
32#else
33# if !STM32_PWM_USE_ADVANCED
34# error "WS2812_PWM_COMPLEMENTARY_OUTPUT requires STM32_PWM_USE_ADVANCED == TRUE"
35# endif
36# define WS2812_PWM_OUTPUT_MODE PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH
37#endif
38
39// Push Pull or Open Drain Configuration
40// Default Push Pull
41#ifndef WS2812_EXTERNAL_PULLUP
42# if defined(USE_GPIOV1)
43# define WS2812_OUTPUT_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL
44# else
45# define WS2812_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_PWM_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUPDR_FLOATING
46# endif
47#else
48# if defined(USE_GPIOV1)
49# define WS2812_OUTPUT_MODE PAL_MODE_STM32_ALTERNATE_OPENDRAIN
50# else
51# define WS2812_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_PWM_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUPDR_FLOATING
52# endif
53#endif
54
55#ifndef WS2812_PWM_TARGET_PERIOD
56//# define WS2812_PWM_TARGET_PERIOD 800000 // Original code is 800k...?
57# define WS2812_PWM_TARGET_PERIOD 80000 // TODO: work out why 10x less on f303/f4x1
58#endif
59
60/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
61
62#define WS2812_PWM_FREQUENCY (STM32_SYSCLK / 2) /**< Clock frequency of PWM, must be valid with respect to system clock! */
63#define WS2812_PWM_PERIOD (WS2812_PWM_FREQUENCY / WS2812_PWM_TARGET_PERIOD) /**< Clock period in ticks. 1 / 800kHz = 1.25 uS (as per datasheet) */
64
65/**
66 * @brief Number of bit-periods to hold the data line low at the end of a frame
67 *
68 * The reset period for each frame is defined in WS2812_TRST_US.
69 * Calculate the number of zeroes to add at the end assuming 1.25 uS/bit:
70 */
71#define WS2812_RESET_BIT_N (1000 * WS2812_TRST_US / 1250)
72#define WS2812_COLOR_BIT_N (RGBLED_NUM * 24) /**< Number of data bits */
73#define WS2812_BIT_N (WS2812_COLOR_BIT_N + WS2812_RESET_BIT_N) /**< Total number of bits in a frame */
74
75/**
76 * @brief High period for a zero, in ticks
77 *
78 * Per the datasheet:
79 * WS2812:
80 * - T0H: 200 nS to 500 nS, inclusive
81 * - T0L: 650 nS to 950 nS, inclusive
82 * WS2812B:
83 * - T0H: 200 nS to 500 nS, inclusive
84 * - T0L: 750 nS to 1050 nS, inclusive
85 *
86 * The duty cycle is calculated for a high period of 350 nS.
87 */
88#define WS2812_DUTYCYCLE_0 (WS2812_PWM_FREQUENCY / (1000000000 / 350))
89
90/**
91 * @brief High period for a one, in ticks
92 *
93 * Per the datasheet:
94 * WS2812:
95 * - T1H: 550 nS to 850 nS, inclusive
96 * - T1L: 450 nS to 750 nS, inclusive
97 * WS2812B:
98 * - T1H: 750 nS to 1050 nS, inclusive
99 * - T1L: 200 nS to 500 nS, inclusive
100 *
101 * The duty cycle is calculated for a high period of 800 nS.
102 * This is in the middle of the specifications of the WS2812 and WS2812B.
103 */
104#define WS2812_DUTYCYCLE_1 (WS2812_PWM_FREQUENCY / (1000000000 / 800))
105
106/* --- PRIVATE MACROS ------------------------------------------------------- */
107
108/**
109 * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given bit
110 *
111 * @param[in] led: The led index [0, @ref RGBLED_NUM)
112 * @param[in] byte: The byte number [0, 2]
113 * @param[in] bit: The bit number [0, 7]
114 *
115 * @return The bit index
116 */
117#define WS2812_BIT(led, byte, bit) (24 * (led) + 8 * (byte) + (7 - (bit)))
118
119#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)
120/**
121 * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given red bit
122 *
123 * @note The red byte is the middle byte in the color packet
124 *
125 * @param[in] led: The led index [0, @ref RGBLED_NUM)
126 * @param[in] bit: The bit number [0, 7]
127 *
128 * @return The bit index
129 */
130# define WS2812_RED_BIT(led, bit) WS2812_BIT((led), 1, (bit))
131
132/**
133 * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given green bit
134 *
135 * @note The red byte is the first byte in the color packet
136 *
137 * @param[in] led: The led index [0, @ref RGBLED_NUM)
138 * @param[in] bit: The bit number [0, 7]
139 *
140 * @return The bit index
141 */
142# define WS2812_GREEN_BIT(led, bit) WS2812_BIT((led), 0, (bit))
143
144/**
145 * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given blue bit
146 *
147 * @note The red byte is the last byte in the color packet
148 *
149 * @param[in] led: The led index [0, @ref RGBLED_NUM)
150 * @param[in] bit: The bit index [0, 7]
151 *
152 * @return The bit index
153 */
154# define WS2812_BLUE_BIT(led, bit) WS2812_BIT((led), 2, (bit))
155
156#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)
157/**
158 * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given red bit
159 *
160 * @note The red byte is the middle byte in the color packet
161 *
162 * @param[in] led: The led index [0, @ref RGBLED_NUM)
163 * @param[in] bit: The bit number [0, 7]
164 *
165 * @return The bit index
166 */
167# define WS2812_RED_BIT(led, bit) WS2812_BIT((led), 0, (bit))
168
169/**
170 * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given green bit
171 *
172 * @note The red byte is the first byte in the color packet
173 *
174 * @param[in] led: The led index [0, @ref RGBLED_NUM)
175 * @param[in] bit: The bit number [0, 7]
176 *
177 * @return The bit index
178 */
179# define WS2812_GREEN_BIT(led, bit) WS2812_BIT((led), 1, (bit))
180
181/**
182 * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given blue bit
183 *
184 * @note The red byte is the last byte in the color packet
185 *
186 * @param[in] led: The led index [0, @ref RGBLED_NUM)
187 * @param[in] bit: The bit index [0, 7]
188 *
189 * @return The bit index
190 */
191# define WS2812_BLUE_BIT(led, bit) WS2812_BIT((led), 2, (bit))
192
193#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)
194/**
195 * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given red bit
196 *
197 * @note The red byte is the middle byte in the color packet
198 *
199 * @param[in] led: The led index [0, @ref RGBLED_NUM)
200 * @param[in] bit: The bit number [0, 7]
201 *
202 * @return The bit index
203 */
204# define WS2812_RED_BIT(led, bit) WS2812_BIT((led), 2, (bit))
205
206/**
207 * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given green bit
208 *
209 * @note The red byte is the first byte in the color packet
210 *
211 * @param[in] led: The led index [0, @ref RGBLED_NUM)
212 * @param[in] bit: The bit number [0, 7]
213 *
214 * @return The bit index
215 */
216# define WS2812_GREEN_BIT(led, bit) WS2812_BIT((led), 1, (bit))
217
218/**
219 * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given blue bit
220 *
221 * @note The red byte is the last byte in the color packet
222 *
223 * @param[in] led: The led index [0, @ref RGBLED_NUM)
224 * @param[in] bit: The bit index [0, 7]
225 *
226 * @return The bit index
227 */
228# define WS2812_BLUE_BIT(led, bit) WS2812_BIT((led), 0, (bit))
229#endif
230
231/* --- PRIVATE VARIABLES ---------------------------------------------------- */
232
233static uint32_t ws2812_frame_buffer[WS2812_BIT_N + 1]; /**< Buffer for a frame */
234
235/* --- PUBLIC FUNCTIONS ----------------------------------------------------- */
236/*
237 * Gedanke: Double-buffer type transactions: double buffer transfers using two memory pointers for
238the memory (while the DMA is reading/writing from/to a buffer, the application can
239write/read to/from the other buffer).
240 */
241
242void ws2812_init(void) {
243 // Initialize led frame buffer
244 uint32_t i;
245 for (i = 0; i < WS2812_COLOR_BIT_N; i++) ws2812_frame_buffer[i] = WS2812_DUTYCYCLE_0; // All color bits are zero duty cycle
246 for (i = 0; i < WS2812_RESET_BIT_N; i++) ws2812_frame_buffer[i + WS2812_COLOR_BIT_N] = 0; // All reset bits are zero
247
248 palSetLineMode(RGB_DI_PIN, WS2812_OUTPUT_MODE);
249
250 // PWM Configuration
251 //#pragma GCC diagnostic ignored "-Woverride-init" // Turn off override-init warning for this struct. We use the overriding ability to set a "default" channel config
252 static const PWMConfig ws2812_pwm_config = {
253 .frequency = WS2812_PWM_FREQUENCY,
254 .period = WS2812_PWM_PERIOD, // Mit dieser Periode wird UDE-Event erzeugt und ein neuer Wert (Länge WS2812_BIT_N) vom DMA ins CCR geschrieben
255 .callback = NULL,
256 .channels =
257 {
258 [0 ... 3] = {.mode = PWM_OUTPUT_DISABLED, .callback = NULL}, // Channels default to disabled
259 [WS2812_PWM_CHANNEL - 1] = {.mode = WS2812_PWM_OUTPUT_MODE, .callback = NULL}, // Turn on the channel we care about
260 },
261 .cr2 = 0,
262 .dier = TIM_DIER_UDE, // DMA on update event for next period
263 };
264 //#pragma GCC diagnostic pop // Restore command-line warning options
265
266 // Configure DMA
267 // dmaInit(); // Joe added this
268 dmaStreamAlloc(WS2812_DMA_STREAM - STM32_DMA_STREAM(0), 10, NULL, NULL);
269 dmaStreamSetPeripheral(WS2812_DMA_STREAM, &(WS2812_PWM_DRIVER.tim->CCR[WS2812_PWM_CHANNEL - 1])); // Ziel ist der An-Zeit im Cap-Comp-Register
270 dmaStreamSetMemory0(WS2812_DMA_STREAM, ws2812_frame_buffer);
271 dmaStreamSetTransactionSize(WS2812_DMA_STREAM, WS2812_BIT_N);
272 dmaStreamSetMode(WS2812_DMA_STREAM, STM32_DMA_CR_CHSEL(WS2812_DMA_CHANNEL) | STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD | STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(3));
273 // M2P: Memory 2 Periph; PL: Priority Level
274
275#if (STM32_DMA_SUPPORTS_DMAMUX == TRUE)
276 // If the MCU has a DMAMUX we need to assign the correct resource
277 dmaSetRequestSource(WS2812_DMA_STREAM, WS2812_DMAMUX_ID);
278#endif
279
280 // Start DMA
281 dmaStreamEnable(WS2812_DMA_STREAM);
282
283 // Configure PWM
284 // NOTE: It's required that preload be enabled on the timer channel CCR register. This is currently enabled in the
285 // ChibiOS driver code, so we don't have to do anything special to the timer. If we did, we'd have to start the timer,
286 // disable counting, enable the channel, and then make whatever configuration changes we need.
287 pwmStart(&WS2812_PWM_DRIVER, &ws2812_pwm_config);
288 pwmEnableChannel(&WS2812_PWM_DRIVER, WS2812_PWM_CHANNEL - 1, 0); // Initial period is 0; output will be low until first duty cycle is DMA'd in
289}
290
291void ws2812_write_led(uint16_t led_number, uint8_t r, uint8_t g, uint8_t b) {
292 // Write color to frame buffer
293 for (uint8_t bit = 0; bit < 8; bit++) {
294 ws2812_frame_buffer[WS2812_RED_BIT(led_number, bit)] = ((r >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0;
295 ws2812_frame_buffer[WS2812_GREEN_BIT(led_number, bit)] = ((g >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0;
296 ws2812_frame_buffer[WS2812_BLUE_BIT(led_number, bit)] = ((b >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0;
297 }
298}
299
300// Setleds for standard RGB
301void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) {
302 static bool s_init = false;
303 if (!s_init) {
304 ws2812_init();
305 s_init = true;
306 }
307
308 for (uint16_t i = 0; i < leds; i++) {
309 ws2812_write_led(i, ledarray[i].r, ledarray[i].g, ledarray[i].b);
310 }
311}
diff --git a/drivers/chibios/ws2812_spi.c b/drivers/chibios/ws2812_spi.c
deleted file mode 100644
index 377a929b9..000000000
--- a/drivers/chibios/ws2812_spi.c
+++ /dev/null
@@ -1,159 +0,0 @@
1#include "quantum.h"
2#include "ws2812.h"
3
4/* Adapted from https://github.com/gamazeps/ws2812b-chibios-SPIDMA/ */
5
6#ifdef RGBW
7# error "RGBW not supported"
8#endif
9
10// Define the spi your LEDs are plugged to here
11#ifndef WS2812_SPI
12# define WS2812_SPI SPID1
13#endif
14
15#ifndef WS2812_SPI_MOSI_PAL_MODE
16# define WS2812_SPI_MOSI_PAL_MODE 5
17#endif
18
19#ifndef WS2812_SPI_SCK_PAL_MODE
20# define WS2812_SPI_SCK_PAL_MODE 5
21#endif
22
23// Push Pull or Open Drain Configuration
24// Default Push Pull
25#ifndef WS2812_EXTERNAL_PULLUP
26# if defined(USE_GPIOV1)
27# define WS2812_MOSI_OUTPUT_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL
28# else
29# define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL
30# endif
31#else
32# if defined(USE_GPIOV1)
33# define WS2812_MOSI_OUTPUT_MODE PAL_MODE_STM32_ALTERNATE_OPENDRAIN
34# else
35# define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN
36# endif
37#endif
38
39// Define SPI config speed
40// baudrate should target 3.2MHz
41// F072 fpclk = 48MHz
42// 48/16 = 3Mhz
43#if WS2812_SPI_DIVISOR == 2
44# define WS2812_SPI_DIVISOR (0)
45#elif WS2812_SPI_DIVISOR == 4
46# define WS2812_SPI_DIVISOR (SPI_CR1_BR_0)
47#elif WS2812_SPI_DIVISOR == 8
48# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1)
49#elif WS2812_SPI_DIVISOR == 16 // same as default
50# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1 | SPI_CR1_BR_0)
51#elif WS2812_SPI_DIVISOR == 32
52# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2)
53#elif WS2812_SPI_DIVISOR == 64
54# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_0)
55#elif WS2812_SPI_DIVISOR == 128
56# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_1)
57#elif WS2812_SPI_DIVISOR == 256
58# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0)
59#else
60# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1 | SPI_CR1_BR_0) // default
61#endif
62
63// Use SPI circular buffer
64#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER
65# define WS2812_SPI_BUFFER_MODE 1 // circular buffer
66#else
67# define WS2812_SPI_BUFFER_MODE 0 // normal buffer
68#endif
69
70#if defined(USE_GPIOV1)
71# define WS2812_SCK_OUTPUT_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL
72#else
73# define WS2812_SCK_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_SCK_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL
74#endif
75
76#define BYTES_FOR_LED_BYTE 4
77#define NB_COLORS 3
78#define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * NB_COLORS)
79#define DATA_SIZE (BYTES_FOR_LED * RGBLED_NUM)
80#define RESET_SIZE (1000 * WS2812_TRST_US / (2 * 1250))
81#define PREAMBLE_SIZE 4
82
83static uint8_t txbuf[PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE] = {0};
84
85/*
86 * As the trick here is to use the SPI to send a huge pattern of 0 and 1 to
87 * the ws2812b protocol, we use this helper function to translate bytes into
88 * 0s and 1s for the LED (with the appropriate timing).
89 */
90static uint8_t get_protocol_eq(uint8_t data, int pos) {
91 uint8_t eq = 0;
92 if (data & (1 << (2 * (3 - pos))))
93 eq = 0b1110;
94 else
95 eq = 0b1000;
96 if (data & (2 << (2 * (3 - pos))))
97 eq += 0b11100000;
98 else
99 eq += 0b10000000;
100 return eq;
101}
102
103static void set_led_color_rgb(LED_TYPE color, int pos) {
104 uint8_t* tx_start = &txbuf[PREAMBLE_SIZE];
105
106#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)
107 for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.g, j);
108 for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.r, j);
109 for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.b, j);
110#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)
111 for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.r, j);
112 for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.g, j);
113 for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.b, j);
114#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)
115 for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.b, j);
116 for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.g, j);
117 for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.r, j);
118#endif
119}
120
121void ws2812_init(void) {
122 palSetLineMode(RGB_DI_PIN, WS2812_MOSI_OUTPUT_MODE);
123
124#ifdef WS2812_SPI_SCK_PIN
125 palSetLineMode(WS2812_SPI_SCK_PIN, WS2812_SCK_OUTPUT_MODE);
126#endif // WS2812_SPI_SCK_PIN
127
128 // TODO: more dynamic baudrate
129 static const SPIConfig spicfg = {WS2812_SPI_BUFFER_MODE, NULL, PAL_PORT(RGB_DI_PIN), PAL_PAD(RGB_DI_PIN), WS2812_SPI_DIVISOR};
130
131 spiAcquireBus(&WS2812_SPI); /* Acquire ownership of the bus. */
132 spiStart(&WS2812_SPI, &spicfg); /* Setup transfer parameters. */
133 spiSelect(&WS2812_SPI); /* Slave Select assertion. */
134#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER
135 spiStartSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf);
136#endif
137}
138
139void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) {
140 static bool s_init = false;
141 if (!s_init) {
142 ws2812_init();
143 s_init = true;
144 }
145
146 for (uint8_t i = 0; i < leds; i++) {
147 set_led_color_rgb(ledarray[i], i);
148 }
149
150 // Send async - each led takes ~0.03ms, 50 leds ~1.5ms, animations flushing faster than send will cause issues.
151 // Instead spiSend can be used to send synchronously (or the thread logic can be added back).
152#ifndef WS2812_SPI_USE_CIRCULAR_BUFFER
153# ifdef WS2812_SPI_SYNC
154 spiSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf);
155# else
156 spiStartSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf);
157# endif
158#endif
159}
diff --git a/drivers/eeprom/eeprom_i2c.c b/drivers/eeprom/eeprom_i2c.c
index 4210f06f9..8e80ff544 100644
--- a/drivers/eeprom/eeprom_i2c.c
+++ b/drivers/eeprom/eeprom_i2c.c
@@ -16,6 +16,9 @@
16 16
17#include <stdint.h> 17#include <stdint.h>
18#include <string.h> 18#include <string.h>
19#if defined(EXTERNAL_EEPROM_WP_PIN)
20# include "gpio.h"
21#endif
19 22
20/* 23/*
21 Note that the implementations of eeprom_XXXX_YYYY on AVR are normally 24 Note that the implementations of eeprom_XXXX_YYYY on AVR are normally
@@ -50,7 +53,14 @@ static inline void fill_target_address(uint8_t *buffer, const void *addr) {
50 } 53 }
51} 54}
52 55
53void eeprom_driver_init(void) { i2c_init(); } 56void eeprom_driver_init(void) {
57 i2c_init();
58#if defined(EXTERNAL_EEPROM_WP_PIN)
59 /* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */
60 writePin(EXTERNAL_EEPROM_WP_PIN, 1);
61 setPinInputHigh(EXTERNAL_EEPROM_WP_PIN);
62#endif
63}
54 64
55void eeprom_driver_erase(void) { 65void eeprom_driver_erase(void) {
56#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT) 66#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)
@@ -89,6 +99,11 @@ void eeprom_write_block(const void *buf, void *addr, size_t len) {
89 uint8_t * read_buf = (uint8_t *)buf; 99 uint8_t * read_buf = (uint8_t *)buf;
90 uintptr_t target_addr = (uintptr_t)addr; 100 uintptr_t target_addr = (uintptr_t)addr;
91 101
102#if defined(EXTERNAL_EEPROM_WP_PIN)
103 setPinOutput(EXTERNAL_EEPROM_WP_PIN);
104 writePin(EXTERNAL_EEPROM_WP_PIN, 0);
105#endif
106
92 while (len > 0) { 107 while (len > 0) {
93 uintptr_t page_offset = target_addr % EXTERNAL_EEPROM_PAGE_SIZE; 108 uintptr_t page_offset = target_addr % EXTERNAL_EEPROM_PAGE_SIZE;
94 int write_length = EXTERNAL_EEPROM_PAGE_SIZE - page_offset; 109 int write_length = EXTERNAL_EEPROM_PAGE_SIZE - page_offset;
@@ -116,4 +131,10 @@ void eeprom_write_block(const void *buf, void *addr, size_t len) {
116 target_addr += write_length; 131 target_addr += write_length;
117 len -= write_length; 132 len -= write_length;
118 } 133 }
134
135#if defined(EXTERNAL_EEPROM_WP_PIN)
136 /* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */
137 writePin(EXTERNAL_EEPROM_WP_PIN, 1);
138 setPinInputHigh(EXTERNAL_EEPROM_WP_PIN);
139#endif
119} 140}
diff --git a/drivers/eeprom/eeprom_stm32_L0_L1.c b/drivers/eeprom/eeprom_stm32_L0_L1.c
deleted file mode 100644
index ed26cc714..000000000
--- a/drivers/eeprom/eeprom_stm32_L0_L1.c
+++ /dev/null
@@ -1,96 +0,0 @@
1/* Copyright 2020 Nick Brassel (tzarc)
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 <stdint.h>
18#include <string.h>
19
20#include <hal.h>
21#include "eeprom_driver.h"
22#include "eeprom_stm32_L0_L1.h"
23
24#define EEPROM_BASE_ADDR 0x08080000
25#define EEPROM_ADDR(offset) (EEPROM_BASE_ADDR + (offset))
26#define EEPROM_PTR(offset) ((__IO uint8_t *)EEPROM_ADDR(offset))
27#define EEPROM_BYTE(location, offset) (*(EEPROM_PTR(((uint32_t)location) + ((uint32_t)offset))))
28
29#define BUFFER_BYTE(buffer, offset) (*(((uint8_t *)buffer) + offset))
30
31#define FLASH_PEKEY1 0x89ABCDEF
32#define FLASH_PEKEY2 0x02030405
33
34static inline void STM32_L0_L1_EEPROM_WaitNotBusy(void) {
35 while (FLASH->SR & FLASH_SR_BSY) {
36 __WFI();
37 }
38}
39
40static inline void STM32_L0_L1_EEPROM_Unlock(void) {
41 STM32_L0_L1_EEPROM_WaitNotBusy();
42 if (FLASH->PECR & FLASH_PECR_PELOCK) {
43 FLASH->PEKEYR = FLASH_PEKEY1;
44 FLASH->PEKEYR = FLASH_PEKEY2;
45 }
46}
47
48static inline void STM32_L0_L1_EEPROM_Lock(void) {
49 STM32_L0_L1_EEPROM_WaitNotBusy();
50 FLASH->PECR |= FLASH_PECR_PELOCK;
51}
52
53void eeprom_driver_init(void) {}
54
55void eeprom_driver_erase(void) {
56 STM32_L0_L1_EEPROM_Unlock();
57
58 for (size_t offset = 0; offset < STM32_ONBOARD_EEPROM_SIZE; offset += sizeof(uint32_t)) {
59 FLASH->PECR |= FLASH_PECR_ERASE | FLASH_PECR_DATA;
60
61 *(__IO uint32_t *)EEPROM_ADDR(offset) = (uint32_t)0;
62
63 STM32_L0_L1_EEPROM_WaitNotBusy();
64 FLASH->PECR &= ~(FLASH_PECR_ERASE | FLASH_PECR_DATA);
65 }
66
67 STM32_L0_L1_EEPROM_Lock();
68}
69
70void eeprom_read_block(void *buf, const void *addr, size_t len) {
71 for (size_t offset = 0; offset < len; ++offset) {
72 // Drop out if we've hit the limit of the EEPROM
73 if ((((uint32_t)addr) + offset) >= STM32_ONBOARD_EEPROM_SIZE) {
74 break;
75 }
76
77 STM32_L0_L1_EEPROM_WaitNotBusy();
78 BUFFER_BYTE(buf, offset) = EEPROM_BYTE(addr, offset);
79 }
80}
81
82void eeprom_write_block(const void *buf, void *addr, size_t len) {
83 STM32_L0_L1_EEPROM_Unlock();
84
85 for (size_t offset = 0; offset < len; ++offset) {
86 // Drop out if we've hit the limit of the EEPROM
87 if ((((uint32_t)addr) + offset) >= STM32_ONBOARD_EEPROM_SIZE) {
88 break;
89 }
90
91 STM32_L0_L1_EEPROM_WaitNotBusy();
92 EEPROM_BYTE(addr, offset) = BUFFER_BYTE(buf, offset);
93 }
94
95 STM32_L0_L1_EEPROM_Lock();
96}
diff --git a/drivers/eeprom/eeprom_stm32_L0_L1.h b/drivers/eeprom/eeprom_stm32_L0_L1.h
deleted file mode 100644
index a35defca8..000000000
--- a/drivers/eeprom/eeprom_stm32_L0_L1.h
+++ /dev/null
@@ -1,33 +0,0 @@
1/* Copyright 2020 Nick Brassel (tzarc)
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/*
20 The size used by the STM32 L0/L1 EEPROM driver.
21*/
22#ifndef STM32_ONBOARD_EEPROM_SIZE
23# ifdef VIA_ENABLE
24# define STM32_ONBOARD_EEPROM_SIZE 1024
25# else
26# include "eeconfig.h"
27# define STM32_ONBOARD_EEPROM_SIZE (((EECONFIG_SIZE + 3) / 4) * 4) // based off eeconfig's current usage, aligned to 4-byte sizes, to deal with LTO and EEPROM page sizing
28# endif
29#endif
30
31#if STM32_ONBOARD_EEPROM_SIZE > 128
32# pragma message("Please note: resetting EEPROM using an STM32L0/L1 device takes up to 1 second for every 1kB of internal EEPROM used.")
33#endif
diff --git a/drivers/haptic/haptic.c b/drivers/haptic/haptic.c
deleted file mode 100644
index de3f40052..000000000
--- a/drivers/haptic/haptic.c
+++ /dev/null
@@ -1,355 +0,0 @@
1/* Copyright 2019 ishtob
2 * Driver for haptic feedback written for QMK
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17#include "haptic.h"
18#include "eeconfig.h"
19#include "progmem.h"
20#include "debug.h"
21#ifdef DRV2605L
22# include "DRV2605L.h"
23#endif
24#ifdef SOLENOID_ENABLE
25# include "solenoid.h"
26#endif
27
28haptic_config_t haptic_config;
29
30void haptic_init(void) {
31 debug_enable = 1; // Debug is ON!
32 if (!eeconfig_is_enabled()) {
33 eeconfig_init();
34 }
35 haptic_config.raw = eeconfig_read_haptic();
36#ifdef SOLENOID_ENABLE
37 solenoid_set_dwell(haptic_config.dwell);
38#endif
39 if ((haptic_config.raw == 0)
40#ifdef SOLENOID_ENABLE
41 || (haptic_config.dwell == 0)
42#endif
43 ) {
44 // this will be called, if the eeprom is not corrupt,
45 // but the previous firmware didn't have haptic enabled,
46 // or the previous firmware didn't have solenoid enabled,
47 // and the current one has solenoid enabled.
48 haptic_reset();
49 }
50#ifdef SOLENOID_ENABLE
51 solenoid_setup();
52 dprintf("Solenoid driver initialized\n");
53#endif
54#ifdef DRV2605L
55 DRV_init();
56 dprintf("DRV2605 driver initialized\n");
57#endif
58 eeconfig_debug_haptic();
59}
60
61void haptic_task(void) {
62#ifdef SOLENOID_ENABLE
63 solenoid_check();
64#endif
65}
66
67void eeconfig_debug_haptic(void) {
68 dprintf("haptic_config eprom\n");
69 dprintf("haptic_config.enable = %d\n", haptic_config.enable);
70 dprintf("haptic_config.mode = %d\n", haptic_config.mode);
71}
72
73void haptic_enable(void) {
74 haptic_config.enable = 1;
75 xprintf("haptic_config.enable = %u\n", haptic_config.enable);
76 eeconfig_update_haptic(haptic_config.raw);
77}
78
79void haptic_disable(void) {
80 haptic_config.enable = 0;
81 xprintf("haptic_config.enable = %u\n", haptic_config.enable);
82 eeconfig_update_haptic(haptic_config.raw);
83}
84
85void haptic_toggle(void) {
86 if (haptic_config.enable) {
87 haptic_disable();
88 } else {
89 haptic_enable();
90 }
91 eeconfig_update_haptic(haptic_config.raw);
92}
93
94void haptic_feedback_toggle(void) {
95 haptic_config.feedback++;
96 if (haptic_config.feedback >= HAPTIC_FEEDBACK_MAX) haptic_config.feedback = KEY_PRESS;
97 xprintf("haptic_config.feedback = %u\n", !haptic_config.feedback);
98 eeconfig_update_haptic(haptic_config.raw);
99}
100
101void haptic_buzz_toggle(void) {
102 bool buzz_stat = !haptic_config.buzz;
103 haptic_config.buzz = buzz_stat;
104 haptic_set_buzz(buzz_stat);
105}
106
107void haptic_mode_increase(void) {
108 uint8_t mode = haptic_config.mode + 1;
109#ifdef DRV2605L
110 if (haptic_config.mode >= drv_effect_max) {
111 mode = 1;
112 }
113#endif
114 haptic_set_mode(mode);
115}
116
117void haptic_mode_decrease(void) {
118 uint8_t mode = haptic_config.mode - 1;
119#ifdef DRV2605L
120 if (haptic_config.mode < 1) {
121 mode = (drv_effect_max - 1);
122 }
123#endif
124 haptic_set_mode(mode);
125}
126
127void haptic_dwell_increase(void) {
128#ifdef SOLENOID_ENABLE
129 int16_t next_dwell = ((int16_t)haptic_config.dwell) + SOLENOID_DWELL_STEP_SIZE;
130 if (haptic_config.dwell >= SOLENOID_MAX_DWELL) {
131 // if it's already at max, we wrap back to min
132 next_dwell = SOLENOID_MIN_DWELL;
133 } else if (next_dwell > SOLENOID_MAX_DWELL) {
134 // if we overshoot the max, then cap at max
135 next_dwell = SOLENOID_MAX_DWELL;
136 }
137 solenoid_set_dwell(next_dwell);
138#else
139 int16_t next_dwell = ((int16_t)haptic_config.dwell) + 1;
140#endif
141 haptic_set_dwell(next_dwell);
142}
143
144void haptic_dwell_decrease(void) {
145#ifdef SOLENOID_ENABLE
146 int16_t next_dwell = ((int16_t)haptic_config.dwell) - SOLENOID_DWELL_STEP_SIZE;
147 if (haptic_config.dwell <= SOLENOID_MIN_DWELL) {
148 // if it's already at min, we wrap to max
149 next_dwell = SOLENOID_MAX_DWELL;
150 } else if (next_dwell < SOLENOID_MIN_DWELL) {
151 // if we go below min, then we cap to min
152 next_dwell = SOLENOID_MIN_DWELL;
153 }
154 solenoid_set_dwell(next_dwell);
155#else
156 int16_t next_dwell = ((int16_t)haptic_config.dwell) - 1;
157#endif
158 haptic_set_dwell(next_dwell);
159}
160
161void haptic_reset(void) {
162 haptic_config.enable = true;
163 uint8_t feedback = HAPTIC_FEEDBACK_DEFAULT;
164 haptic_config.feedback = feedback;
165#ifdef DRV2605L
166 uint8_t mode = HAPTIC_MODE_DEFAULT;
167 haptic_config.mode = mode;
168#endif
169#ifdef SOLENOID_ENABLE
170 uint8_t dwell = SOLENOID_DEFAULT_DWELL;
171 haptic_config.dwell = dwell;
172 haptic_config.buzz = SOLENOID_DEFAULT_BUZZ;
173 solenoid_set_dwell(dwell);
174#else
175 // This is to trigger haptic_reset again, if solenoid is enabled in the future.
176 haptic_config.dwell = 0;
177 haptic_config.buzz = 0;
178#endif
179 eeconfig_update_haptic(haptic_config.raw);
180 xprintf("haptic_config.feedback = %u\n", haptic_config.feedback);
181 xprintf("haptic_config.mode = %u\n", haptic_config.mode);
182}
183
184void haptic_set_feedback(uint8_t feedback) {
185 haptic_config.feedback = feedback;
186 eeconfig_update_haptic(haptic_config.raw);
187 xprintf("haptic_config.feedback = %u\n", haptic_config.feedback);
188}
189
190void haptic_set_mode(uint8_t mode) {
191 haptic_config.mode = mode;
192 eeconfig_update_haptic(haptic_config.raw);
193 xprintf("haptic_config.mode = %u\n", haptic_config.mode);
194}
195
196void haptic_set_amplitude(uint8_t amp) {
197 haptic_config.amplitude = amp;
198 eeconfig_update_haptic(haptic_config.raw);
199 xprintf("haptic_config.amplitude = %u\n", haptic_config.amplitude);
200#ifdef DRV2605L
201 DRV_amplitude(amp);
202#endif
203}
204
205void haptic_set_buzz(uint8_t buzz) {
206 haptic_config.buzz = buzz;
207 eeconfig_update_haptic(haptic_config.raw);
208 xprintf("haptic_config.buzz = %u\n", haptic_config.buzz);
209}
210
211void haptic_set_dwell(uint8_t dwell) {
212 haptic_config.dwell = dwell;
213 eeconfig_update_haptic(haptic_config.raw);
214 xprintf("haptic_config.dwell = %u\n", haptic_config.dwell);
215}
216
217uint8_t haptic_get_mode(void) {
218 if (!haptic_config.enable) {
219 return false;
220 }
221 return haptic_config.mode;
222}
223
224uint8_t haptic_get_feedback(void) {
225 if (!haptic_config.enable) {
226 return false;
227 }
228 return haptic_config.feedback;
229}
230
231uint8_t haptic_get_dwell(void) {
232 if (!haptic_config.enable) {
233 return false;
234 }
235 return haptic_config.dwell;
236}
237
238void haptic_enable_continuous(void) {
239 haptic_config.cont = 1;
240 xprintf("haptic_config.cont = %u\n", haptic_config.cont);
241 eeconfig_update_haptic(haptic_config.raw);
242#ifdef DRV2605L
243 DRV_rtp_init();
244#endif
245}
246
247void haptic_disable_continuous(void) {
248 haptic_config.cont = 0;
249 xprintf("haptic_config.cont = %u\n", haptic_config.cont);
250 eeconfig_update_haptic(haptic_config.raw);
251#ifdef DRV2605L
252 DRV_write(DRV_MODE, 0x00);
253#endif
254}
255
256void haptic_toggle_continuous(void) {
257#ifdef DRV2605L
258 if (haptic_config.cont) {
259 haptic_disable_continuous();
260 } else {
261 haptic_enable_continuous();
262 }
263 eeconfig_update_haptic(haptic_config.raw);
264#endif
265}
266
267void haptic_cont_increase(void) {
268 uint8_t amp = haptic_config.amplitude + 10;
269 if (haptic_config.amplitude >= 120) {
270 amp = 120;
271 }
272 haptic_set_amplitude(amp);
273}
274
275void haptic_cont_decrease(void) {
276 uint8_t amp = haptic_config.amplitude - 10;
277 if (haptic_config.amplitude < 20) {
278 amp = 20;
279 }
280 haptic_set_amplitude(amp);
281}
282
283void haptic_play(void) {
284#ifdef DRV2605L
285 uint8_t play_eff = 0;
286 play_eff = haptic_config.mode;
287 DRV_pulse(play_eff);
288#endif
289#ifdef SOLENOID_ENABLE
290 solenoid_fire();
291#endif
292}
293
294bool process_haptic(uint16_t keycode, keyrecord_t *record) {
295 if (keycode == HPT_ON && record->event.pressed) {
296 haptic_enable();
297 }
298 if (keycode == HPT_OFF && record->event.pressed) {
299 haptic_disable();
300 }
301 if (keycode == HPT_TOG && record->event.pressed) {
302 haptic_toggle();
303 }
304 if (keycode == HPT_RST && record->event.pressed) {
305 haptic_reset();
306 }
307 if (keycode == HPT_FBK && record->event.pressed) {
308 haptic_feedback_toggle();
309 }
310 if (keycode == HPT_BUZ && record->event.pressed) {
311 haptic_buzz_toggle();
312 }
313 if (keycode == HPT_MODI && record->event.pressed) {
314 haptic_mode_increase();
315 }
316 if (keycode == HPT_MODD && record->event.pressed) {
317 haptic_mode_decrease();
318 }
319 if (keycode == HPT_DWLI && record->event.pressed) {
320 haptic_dwell_increase();
321 }
322 if (keycode == HPT_DWLD && record->event.pressed) {
323 haptic_dwell_decrease();
324 }
325 if (keycode == HPT_CONT && record->event.pressed) {
326 haptic_toggle_continuous();
327 }
328 if (keycode == HPT_CONI && record->event.pressed) {
329 haptic_cont_increase();
330 }
331 if (keycode == HPT_COND && record->event.pressed) {
332 haptic_cont_decrease();
333 }
334
335 if (haptic_config.enable) {
336 if (record->event.pressed) {
337 // keypress
338 if (haptic_config.feedback < 2) {
339 haptic_play();
340 }
341 } else {
342 // keyrelease
343 if (haptic_config.feedback > 0) {
344 haptic_play();
345 }
346 }
347 }
348 return true;
349}
350
351void haptic_shutdown(void) {
352#ifdef SOLENOID_ENABLE
353 solenoid_shutdown();
354#endif
355}
diff --git a/drivers/haptic/haptic.h b/drivers/haptic/haptic.h
deleted file mode 100644
index ba8e0d20b..000000000
--- a/drivers/haptic/haptic.h
+++ /dev/null
@@ -1,81 +0,0 @@
1/* Copyright 2019 ishtob
2 * Driver for haptic feedback written for QMK
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#pragma once
19#include <stdint.h>
20#include <stdbool.h>
21#include "quantum.h"
22#ifdef DRV2605L
23# include "DRV2605L.h"
24#endif
25
26#ifndef HAPTIC_FEEDBACK_DEFAULT
27# define HAPTIC_FEEDBACK_DEFAULT 0
28#endif
29#ifndef HAPTIC_MODE_DEFAULT
30# define HAPTIC_MODE_DEFAULT DRV_MODE_DEFAULT
31#endif
32
33/* EEPROM config settings */
34typedef union {
35 uint32_t raw;
36 struct {
37 bool enable : 1;
38 uint8_t feedback : 2;
39 uint8_t mode : 7;
40 bool buzz : 1;
41 uint8_t dwell : 7;
42 bool cont : 1;
43 uint8_t amplitude : 8;
44 uint8_t reserved : 5;
45 };
46} haptic_config_t;
47
48typedef enum HAPTIC_FEEDBACK {
49 KEY_PRESS,
50 KEY_PRESS_RELEASE,
51 KEY_RELEASE,
52 HAPTIC_FEEDBACK_MAX,
53} HAPTIC_FEEDBACK;
54
55bool process_haptic(uint16_t keycode, keyrecord_t *record);
56void haptic_init(void);
57void haptic_task(void);
58void eeconfig_debug_haptic(void);
59void haptic_enable(void);
60void haptic_disable(void);
61void haptic_toggle(void);
62void haptic_feedback_toggle(void);
63void haptic_mode_increase(void);
64void haptic_mode_decrease(void);
65void haptic_mode(uint8_t mode);
66void haptic_reset(void);
67void haptic_set_feedback(uint8_t feedback);
68void haptic_set_mode(uint8_t mode);
69void haptic_set_dwell(uint8_t dwell);
70void haptic_set_buzz(uint8_t buzz);
71void haptic_buzz_toggle(void);
72uint8_t haptic_get_mode(void);
73uint8_t haptic_get_feedback(void);
74void haptic_dwell_increase(void);
75void haptic_dwell_decrease(void);
76void haptic_toggle_continuous(void);
77void haptic_cont_increase(void);
78void haptic_cont_decrease(void);
79
80void haptic_play(void);
81void haptic_shutdown(void);
diff --git a/drivers/haptic/solenoid.c b/drivers/haptic/solenoid.c
index 3e61d5a17..25cf34465 100644
--- a/drivers/haptic/solenoid.c
+++ b/drivers/haptic/solenoid.c
@@ -18,6 +18,7 @@
18#include "timer.h" 18#include "timer.h"
19#include "solenoid.h" 19#include "solenoid.h"
20#include "haptic.h" 20#include "haptic.h"
21#include "gpio.h"
21 22
22bool solenoid_on = false; 23bool solenoid_on = false;
23bool solenoid_buzzing = false; 24bool solenoid_buzzing = false;
diff --git a/drivers/lcd/st7565.c b/drivers/lcd/st7565.c
new file mode 100644
index 000000000..49b13c00f
--- /dev/null
+++ b/drivers/lcd/st7565.c
@@ -0,0 +1,496 @@
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 INVERT_DISPLAY 0xA7
35#define DISPLAY_ON 0xAF
36#define DISPLAY_OFF 0xAE
37#define NOP 0xE3
38
39// Addressing Setting Commands
40#define PAM_SETCOLUMN_LSB 0x00
41#define PAM_SETCOLUMN_MSB 0x10
42#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7
43
44// Hardware Configuration Commands
45#define DISPLAY_START_LINE 0x40
46#define SEGMENT_REMAP 0xA0
47#define SEGMENT_REMAP_INV 0xA1
48#define COM_SCAN_INC 0xC0
49#define COM_SCAN_DEC 0xC8
50#define LCD_BIAS_7 0xA3
51#define LCD_BIAS_9 0xA2
52#define RESISTOR_RATIO 0x20
53#define POWER_CONTROL 0x28
54
55// Misc defines
56#ifndef ST7565_BLOCK_COUNT
57# define ST7565_BLOCK_COUNT (sizeof(ST7565_BLOCK_TYPE) * 8)
58#endif
59#ifndef ST7565_BLOCK_SIZE
60# define ST7565_BLOCK_SIZE (ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT)
61#endif
62
63#define ST7565_ALL_BLOCKS_MASK (((((ST7565_BLOCK_TYPE)1 << (ST7565_BLOCK_COUNT - 1)) - 1) << 1) | 1)
64
65#define HAS_FLAGS(bits, flags) ((bits & flags) == flags)
66
67// Display buffer's is the same as the display memory layout
68// this is so we don't end up with rounding errors with
69// parts of the display unusable or don't get cleared correctly
70// and also allows for drawing & inverting
71uint8_t st7565_buffer[ST7565_MATRIX_SIZE];
72uint8_t * st7565_cursor;
73ST7565_BLOCK_TYPE st7565_dirty = 0;
74bool st7565_initialized = false;
75bool st7565_active = false;
76bool st7565_inverted = false;
77display_rotation_t st7565_rotation = DISPLAY_ROTATION_0;
78#if ST7565_TIMEOUT > 0
79uint32_t st7565_timeout;
80#endif
81#if ST7565_UPDATE_INTERVAL > 0
82uint16_t st7565_update_timeout;
83#endif
84
85// Flips the rendering bits for a character at the current cursor position
86static void InvertCharacter(uint8_t *cursor) {
87 const uint8_t *end = cursor + ST7565_FONT_WIDTH;
88 while (cursor < end) {
89 *cursor = ~(*cursor);
90 cursor++;
91 }
92}
93
94bool st7565_init(display_rotation_t rotation) {
95 setPinOutput(ST7565_A0_PIN);
96 writePinHigh(ST7565_A0_PIN);
97 setPinOutput(ST7565_RST_PIN);
98 writePinHigh(ST7565_RST_PIN);
99
100 st7565_rotation = st7565_init_user(rotation);
101
102 spi_init();
103 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
104
105 st7565_reset();
106
107 st7565_send_cmd(LCD_BIAS_7);
108 if (!HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) {
109 st7565_send_cmd(SEGMENT_REMAP);
110 st7565_send_cmd(COM_SCAN_DEC);
111 } else {
112 st7565_send_cmd(SEGMENT_REMAP_INV);
113 st7565_send_cmd(COM_SCAN_INC);
114 }
115 st7565_send_cmd(DISPLAY_START_LINE | 0x00);
116 st7565_send_cmd(CONTRAST);
117 st7565_send_cmd(ST7565_CONTRAST);
118 st7565_send_cmd(RESISTOR_RATIO | 0x01);
119 st7565_send_cmd(POWER_CONTROL | 0x04);
120 wait_ms(50);
121 st7565_send_cmd(POWER_CONTROL | 0x06);
122 wait_ms(50);
123 st7565_send_cmd(POWER_CONTROL | 0x07);
124 wait_ms(10);
125 st7565_send_cmd(DISPLAY_ON);
126 st7565_send_cmd(DISPLAY_ALL_ON_RESUME);
127 st7565_send_cmd(NORMAL_DISPLAY);
128
129 spi_stop();
130
131#if ST7565_TIMEOUT > 0
132 st7565_timeout = timer_read32() + ST7565_TIMEOUT;
133#endif
134
135 st7565_clear();
136 st7565_initialized = true;
137 st7565_active = true;
138 return true;
139}
140
141__attribute__((weak)) display_rotation_t st7565_init_user(display_rotation_t rotation) { return rotation; }
142
143void st7565_clear(void) {
144 memset(st7565_buffer, 0, sizeof(st7565_buffer));
145 st7565_cursor = &st7565_buffer[0];
146 st7565_dirty = ST7565_ALL_BLOCKS_MASK;
147}
148
149uint8_t crot(uint8_t a, int8_t n) {
150 const uint8_t mask = 0x7;
151 n &= mask;
152 return a << n | a >> (-n & mask);
153}
154
155void st7565_render(void) {
156 if (!st7565_initialized) {
157 return;
158 }
159
160 // Do we have work to do?
161 st7565_dirty &= ST7565_ALL_BLOCKS_MASK;
162 if (!st7565_dirty) {
163 return;
164 }
165
166 // Find first dirty block
167 uint8_t update_start = 0;
168 while (!(st7565_dirty & ((ST7565_BLOCK_TYPE)1 << update_start))) {
169 ++update_start;
170 }
171
172 // Calculate commands to set memory addressing bounds.
173 uint8_t start_page = ST7565_BLOCK_SIZE * update_start / ST7565_DISPLAY_WIDTH;
174 uint8_t start_column = ST7565_BLOCK_SIZE * update_start % ST7565_DISPLAY_WIDTH;
175 // IC has 132 segment drivers, for panels with less width we need to offset the starting column
176 if (HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) {
177 start_column += (132 - ST7565_DISPLAY_WIDTH);
178 }
179
180 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
181
182 st7565_send_cmd(PAM_PAGE_ADDR | start_page);
183 st7565_send_cmd(PAM_SETCOLUMN_LSB | ((ST7565_COLUMN_OFFSET + start_column) & 0x0f));
184 st7565_send_cmd(PAM_SETCOLUMN_MSB | ((ST7565_COLUMN_OFFSET + start_column) >> 4 & 0x0f));
185
186 st7565_send_data(&st7565_buffer[ST7565_BLOCK_SIZE * update_start], ST7565_BLOCK_SIZE);
187
188 // Turn on display if it is off
189 st7565_on();
190
191 // Clear dirty flag
192 st7565_dirty &= ~((ST7565_BLOCK_TYPE)1 << update_start);
193}
194
195void st7565_set_cursor(uint8_t col, uint8_t line) {
196 uint16_t index = line * ST7565_DISPLAY_WIDTH + col * ST7565_FONT_WIDTH;
197
198 // Out of bounds?
199 if (index >= ST7565_MATRIX_SIZE) {
200 index = 0;
201 }
202
203 st7565_cursor = &st7565_buffer[index];
204}
205
206void st7565_advance_page(bool clearPageRemainder) {
207 uint16_t index = st7565_cursor - &st7565_buffer[0];
208 uint8_t remaining = ST7565_DISPLAY_WIDTH - (index % ST7565_DISPLAY_WIDTH);
209
210 if (clearPageRemainder) {
211 // Remaining Char count
212 remaining = remaining / ST7565_FONT_WIDTH;
213
214 // Write empty character until next line
215 while (remaining--) st7565_write_char(' ', false);
216 } else {
217 // Next page index out of bounds?
218 if (index + remaining >= ST7565_MATRIX_SIZE) {
219 index = 0;
220 remaining = 0;
221 }
222
223 st7565_cursor = &st7565_buffer[index + remaining];
224 }
225}
226
227void st7565_advance_char(void) {
228 uint16_t nextIndex = st7565_cursor - &st7565_buffer[0] + ST7565_FONT_WIDTH;
229 uint8_t remainingSpace = ST7565_DISPLAY_WIDTH - (nextIndex % ST7565_DISPLAY_WIDTH);
230
231 // Do we have enough space on the current line for the next character
232 if (remainingSpace < ST7565_FONT_WIDTH) {
233 nextIndex += remainingSpace;
234 }
235
236 // Did we go out of bounds
237 if (nextIndex >= ST7565_MATRIX_SIZE) {
238 nextIndex = 0;
239 }
240
241 // Update cursor position
242 st7565_cursor = &st7565_buffer[nextIndex];
243}
244
245// Main handler that writes character data to the display buffer
246void st7565_write_char(const char data, bool invert) {
247 // Advance to the next line if newline
248 if (data == '\n') {
249 // Old source wrote ' ' until end of line...
250 st7565_advance_page(true);
251 return;
252 }
253
254 if (data == '\r') {
255 st7565_advance_page(false);
256 return;
257 }
258
259 // copy the current render buffer to check for dirty after
260 static uint8_t st7565_temp_buffer[ST7565_FONT_WIDTH];
261 memcpy(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH);
262
263 _Static_assert(sizeof(font) >= ((ST7565_FONT_END + 1 - ST7565_FONT_START) * ST7565_FONT_WIDTH), "ST7565_FONT_END references outside array");
264
265 // set the reder buffer data
266 uint8_t cast_data = (uint8_t)data; // font based on unsigned type for index
267 if (cast_data < ST7565_FONT_START || cast_data > ST7565_FONT_END) {
268 memset(st7565_cursor, 0x00, ST7565_FONT_WIDTH);
269 } else {
270 const uint8_t *glyph = &font[(cast_data - ST7565_FONT_START) * ST7565_FONT_WIDTH];
271 memcpy_P(st7565_cursor, glyph, ST7565_FONT_WIDTH);
272 }
273
274 // Invert if needed
275 if (invert) {
276 InvertCharacter(st7565_cursor);
277 }
278
279 // Dirty check
280 if (memcmp(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH)) {
281 uint16_t index = st7565_cursor - &st7565_buffer[0];
282 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));
283 // Edgecase check if the written data spans the 2 chunks
284 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << ((index + ST7565_FONT_WIDTH - 1) / ST7565_BLOCK_SIZE));
285 }
286
287 // Finally move to the next char
288 st7565_advance_char();
289}
290
291void st7565_write(const char *data, bool invert) {
292 const char *end = data + strlen(data);
293 while (data < end) {
294 st7565_write_char(*data, invert);
295 data++;
296 }
297}
298
299void st7565_write_ln(const char *data, bool invert) {
300 st7565_write(data, invert);
301 st7565_advance_page(true);
302}
303
304void st7565_pan(bool left) {
305 uint16_t i = 0;
306 for (uint16_t y = 0; y < ST7565_DISPLAY_HEIGHT / 8; y++) {
307 if (left) {
308 for (uint16_t x = 0; x < ST7565_DISPLAY_WIDTH - 1; x++) {
309 i = y * ST7565_DISPLAY_WIDTH + x;
310 st7565_buffer[i] = st7565_buffer[i + 1];
311 }
312 } else {
313 for (uint16_t x = ST7565_DISPLAY_WIDTH - 1; x > 0; x--) {
314 i = y * ST7565_DISPLAY_WIDTH + x;
315 st7565_buffer[i] = st7565_buffer[i - 1];
316 }
317 }
318 }
319 st7565_dirty = ST7565_ALL_BLOCKS_MASK;
320}
321
322display_buffer_reader_t st7565_read_raw(uint16_t start_index) {
323 if (start_index > ST7565_MATRIX_SIZE) start_index = ST7565_MATRIX_SIZE;
324 display_buffer_reader_t ret_reader;
325 ret_reader.current_element = &st7565_buffer[start_index];
326 ret_reader.remaining_element_count = ST7565_MATRIX_SIZE - start_index;
327 return ret_reader;
328}
329
330void st7565_write_raw_byte(const char data, uint16_t index) {
331 if (index > ST7565_MATRIX_SIZE) index = ST7565_MATRIX_SIZE;
332 if (st7565_buffer[index] == data) return;
333 st7565_buffer[index] = data;
334 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));
335}
336
337void st7565_write_raw(const char *data, uint16_t size) {
338 uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0];
339 if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index;
340 for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {
341 uint8_t c = *data++;
342 if (st7565_buffer[i] == c) continue;
343 st7565_buffer[i] = c;
344 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE));
345 }
346}
347
348void st7565_write_pixel(uint8_t x, uint8_t y, bool on) {
349 if (x >= ST7565_DISPLAY_WIDTH) {
350 return;
351 }
352 uint16_t index = x + (y / 8) * ST7565_DISPLAY_WIDTH;
353 if (index >= ST7565_MATRIX_SIZE) {
354 return;
355 }
356 uint8_t data = st7565_buffer[index];
357 if (on) {
358 data |= (1 << (y % 8));
359 } else {
360 data &= ~(1 << (y % 8));
361 }
362 if (st7565_buffer[index] != data) {
363 st7565_buffer[index] = data;
364 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));
365 }
366}
367
368#if defined(__AVR__)
369void st7565_write_P(const char *data, bool invert) {
370 uint8_t c = pgm_read_byte(data);
371 while (c != 0) {
372 st7565_write_char(c, invert);
373 c = pgm_read_byte(++data);
374 }
375}
376
377void st7565_write_ln_P(const char *data, bool invert) {
378 st7565_write_P(data, invert);
379 st7565_advance_page(true);
380}
381
382void st7565_write_raw_P(const char *data, uint16_t size) {
383 uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0];
384 if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index;
385 for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {
386 uint8_t c = pgm_read_byte(data++);
387 if (st7565_buffer[i] == c) continue;
388 st7565_buffer[i] = c;
389 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE));
390 }
391}
392#endif // defined(__AVR__)
393
394bool st7565_on(void) {
395 if (!st7565_initialized) {
396 return st7565_active;
397 }
398
399#if ST7565_TIMEOUT > 0
400 st7565_timeout = timer_read32() + ST7565_TIMEOUT;
401#endif
402
403 if (!st7565_active) {
404 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
405 st7565_send_cmd(DISPLAY_ON);
406 spi_stop();
407 st7565_active = true;
408 st7565_on_user();
409 }
410 return st7565_active;
411}
412
413__attribute__((weak)) void st7565_on_user(void) {}
414
415bool st7565_off(void) {
416 if (!st7565_initialized) {
417 return !st7565_active;
418 }
419
420 if (st7565_active) {
421 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
422 st7565_send_cmd(DISPLAY_OFF);
423 spi_stop();
424 st7565_active = false;
425 st7565_off_user();
426 }
427 return !st7565_active;
428}
429
430__attribute__((weak)) void st7565_off_user(void) {}
431
432bool st7565_is_on(void) { return st7565_active; }
433
434bool st7565_invert(bool invert) {
435 if (!st7565_initialized) {
436 return st7565_inverted;
437 }
438
439 if (invert != st7565_inverted) {
440 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
441 st7565_send_cmd(invert ? INVERT_DISPLAY : NORMAL_DISPLAY);
442 spi_stop();
443 st7565_inverted = invert;
444 }
445 return st7565_inverted;
446}
447
448uint8_t st7565_max_chars(void) { return ST7565_DISPLAY_WIDTH / ST7565_FONT_WIDTH; }
449
450uint8_t st7565_max_lines(void) { return ST7565_DISPLAY_HEIGHT / ST7565_FONT_HEIGHT; }
451
452void st7565_task(void) {
453 if (!st7565_initialized) {
454 return;
455 }
456
457#if ST7565_UPDATE_INTERVAL > 0
458 if (timer_elapsed(st7565_update_timeout) >= ST7565_UPDATE_INTERVAL) {
459 st7565_update_timeout = timer_read();
460 st7565_set_cursor(0, 0);
461 st7565_task_user();
462 }
463#else
464 st7565_set_cursor(0, 0);
465 st7565_task_user();
466#endif
467
468 // Smart render system, no need to check for dirty
469 st7565_render();
470
471 // Display timeout check
472#if ST7565_TIMEOUT > 0
473 if (st7565_active && timer_expired32(timer_read32(), st7565_timeout)) {
474 st7565_off();
475 }
476#endif
477}
478
479__attribute__((weak)) void st7565_task_user(void) {}
480
481void st7565_reset(void) {
482 writePinLow(ST7565_RST_PIN);
483 wait_ms(20);
484 writePinHigh(ST7565_RST_PIN);
485 wait_ms(20);
486}
487
488spi_status_t st7565_send_cmd(uint8_t cmd) {
489 writePinLow(ST7565_A0_PIN);
490 return spi_write(cmd);
491}
492
493spi_status_t st7565_send_data(uint8_t *data, uint16_t length) {
494 writePinHigh(ST7565_A0_PIN);
495 return spi_transmit(data, length);
496}
diff --git a/drivers/lcd/st7565.h b/drivers/lcd/st7565.h
new file mode 100644
index 000000000..d453dbe6d
--- /dev/null
+++ b/drivers/lcd/st7565.h
@@ -0,0 +1,219 @@
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// Inverts the display
206// Returns true if the screen was or is inverted
207bool st7565_invert(bool invert);
208
209// Returns the maximum number of characters that will fit on a line
210uint8_t st7565_max_chars(void);
211
212// Returns the maximum number of lines that will fit on the display
213uint8_t st7565_max_lines(void);
214
215void st7565_reset(void);
216
217spi_status_t st7565_send_cmd(uint8_t cmd);
218
219spi_status_t st7565_send_data(uint8_t *data, uint16_t length);
diff --git a/drivers/apa102/apa102.c b/drivers/led/apa102.c
index 7396dc3c5..7396dc3c5 100644
--- a/drivers/apa102/apa102.c
+++ b/drivers/led/apa102.c
diff --git a/drivers/apa102/apa102.h b/drivers/led/apa102.h
index 58cf020c1..58cf020c1 100644
--- a/drivers/apa102/apa102.h
+++ b/drivers/led/apa102.h
diff --git a/drivers/led/aw20216.c b/drivers/led/aw20216.c
new file mode 100644
index 000000000..c608c0ab4
--- /dev/null
+++ b/drivers/led/aw20216.c
@@ -0,0 +1,141 @@
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#define AW_PWM_REGISTER_COUNT 216
47
48#ifndef AW_SCALING_MAX
49# define AW_SCALING_MAX 150
50#endif
51
52#ifndef AW_GLOBAL_CURRENT_MAX
53# define AW_GLOBAL_CURRENT_MAX 150
54#endif
55
56#ifndef AW_SPI_DIVISOR
57# define AW_SPI_DIVISOR 4
58#endif
59
60uint8_t g_pwm_buffer[DRIVER_COUNT][AW_PWM_REGISTER_COUNT];
61bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
62
63bool AW20216_write(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t* data, uint8_t len) {
64 static uint8_t s_spi_transfer_buffer[2] = {0};
65
66 if (!spi_start(cs_pin, false, 0, AW_SPI_DIVISOR)) {
67 spi_stop();
68 return false;
69 }
70
71 s_spi_transfer_buffer[0] = (AWINIC_ID | page | AW_WRITE);
72 s_spi_transfer_buffer[1] = reg;
73
74 if (spi_transmit(s_spi_transfer_buffer, 2) != SPI_STATUS_SUCCESS) {
75 spi_stop();
76 return false;
77 }
78
79 if (spi_transmit(data, len) != SPI_STATUS_SUCCESS) {
80 spi_stop();
81 return false;
82 }
83
84 spi_stop();
85 return true;
86}
87
88static inline bool AW20216_write_register(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t value) {
89 // Little wrapper so callers need not care about sending a buffer
90 return AW20216_write(cs_pin, page, reg, &value, 1);
91}
92
93static void AW20216_init_scaling(pin_t cs_pin) {
94 // Set constant current to the max, control brightness with PWM
95 for (uint8_t i = 0; i < AW_PWM_REGISTER_COUNT; i++) {
96 AW20216_write_register(cs_pin, AW_PAGE_SCALING, i, AW_SCALING_MAX);
97 }
98}
99
100static inline void AW20216_init_current_limit(pin_t cs_pin) {
101 // Push config
102 AW20216_write_register(cs_pin, AW_PAGE_FUNCTION, AW_REG_GLOBALCURRENT, AW_GLOBAL_CURRENT_MAX);
103}
104
105static inline void AW20216_soft_enable(pin_t cs_pin) {
106 // Push config
107 AW20216_write_register(cs_pin, AW_PAGE_FUNCTION, AW_REG_CONFIGURATION, AW_CONFIG_DEFAULT | AW_CHIPEN);
108}
109
110void AW20216_init(pin_t cs_pin, pin_t en_pin) {
111 setPinOutput(en_pin);
112 writePinHigh(en_pin);
113
114 // Drivers should start with all scaling and PWM registers as off
115 AW20216_init_current_limit(cs_pin);
116 AW20216_init_scaling(cs_pin);
117
118 AW20216_soft_enable(cs_pin);
119}
120
121void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
122 aw_led led = g_aw_leds[index];
123
124 g_pwm_buffer[led.driver][led.r] = red;
125 g_pwm_buffer[led.driver][led.g] = green;
126 g_pwm_buffer[led.driver][led.b] = blue;
127 g_pwm_buffer_update_required[led.driver] = true;
128}
129
130void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
131 for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
132 AW20216_set_color(i, red, green, blue);
133 }
134}
135
136void AW20216_update_pwm_buffers(pin_t cs_pin, uint8_t index) {
137 if (g_pwm_buffer_update_required[index]) {
138 AW20216_write(cs_pin, AW_PAGE_PWM, 0, g_pwm_buffer[index], AW_PWM_REGISTER_COUNT);
139 }
140 g_pwm_buffer_update_required[index] = false;
141}
diff --git a/drivers/led/aw20216.h b/drivers/led/aw20216.h
new file mode 100644
index 000000000..97ac6dc5b
--- /dev/null
+++ b/drivers/led/aw20216.h
@@ -0,0 +1,253 @@
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#include "progmem.h"
22#include "gpio.h"
23
24typedef struct aw_led {
25 uint8_t driver : 2;
26 uint8_t r;
27 uint8_t g;
28 uint8_t b;
29} aw_led;
30
31extern const aw_led __flash g_aw_leds[DRIVER_LED_TOTAL];
32
33void AW20216_init(pin_t cs_pin, pin_t en_pin);
34void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
35void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
36void AW20216_update_pwm_buffers(pin_t cs_pin, uint8_t index);
37
38#define CS1_SW1 0x00
39#define CS2_SW1 0x01
40#define CS3_SW1 0x02
41#define CS4_SW1 0x03
42#define CS5_SW1 0x04
43#define CS6_SW1 0x05
44#define CS7_SW1 0x06
45#define CS8_SW1 0x07
46#define CS9_SW1 0x08
47#define CS10_SW1 0x09
48#define CS11_SW1 0x0A
49#define CS12_SW1 0x0B
50#define CS13_SW1 0x0C
51#define CS14_SW1 0x0D
52#define CS15_SW1 0x0E
53#define CS16_SW1 0x0F
54#define CS17_SW1 0x10
55#define CS18_SW1 0x11
56#define CS1_SW2 0x12
57#define CS2_SW2 0x13
58#define CS3_SW2 0x14
59#define CS4_SW2 0x15
60#define CS5_SW2 0x16
61#define CS6_SW2 0x17
62#define CS7_SW2 0x18
63#define CS8_SW2 0x19
64#define CS9_SW2 0x1A
65#define CS10_SW2 0x1B
66#define CS11_SW2 0x1C
67#define CS12_SW2 0x1D
68#define CS13_SW2 0x1E
69#define CS14_SW2 0x1F
70#define CS15_SW2 0x20
71#define CS16_SW2 0x21
72#define CS17_SW2 0x22
73#define CS18_SW2 0x23
74#define CS1_SW3 0x24
75#define CS2_SW3 0x25
76#define CS3_SW3 0x26
77#define CS4_SW3 0x27
78#define CS5_SW3 0x28
79#define CS6_SW3 0x29
80#define CS7_SW3 0x2A
81#define CS8_SW3 0x2B
82#define CS9_SW3 0x2C
83#define CS10_SW3 0x2D
84#define CS11_SW3 0x2E
85#define CS12_SW3 0x2F
86#define CS13_SW3 0x30
87#define CS14_SW3 0x31
88#define CS15_SW3 0x32
89#define CS16_SW3 0x33
90#define CS17_SW3 0x34
91#define CS18_SW3 0x35
92#define CS1_SW4 0x36
93#define CS2_SW4 0x37
94#define CS3_SW4 0x38
95#define CS4_SW4 0x39
96#define CS5_SW4 0x3A
97#define CS6_SW4 0x3B
98#define CS7_SW4 0x3C
99#define CS8_SW4 0x3D
100#define CS9_SW4 0x3E
101#define CS10_SW4 0x3F
102#define CS11_SW4 0x40
103#define CS12_SW4 0x41
104#define CS13_SW4 0x42
105#define CS14_SW4 0x43
106#define CS15_SW4 0x44
107#define CS16_SW4 0x45
108#define CS17_SW4 0x46
109#define CS18_SW4 0x47
110#define CS1_SW5 0x48
111#define CS2_SW5 0x49
112#define CS3_SW5 0x4A
113#define CS4_SW5 0x4B
114#define CS5_SW5 0x4C
115#define CS6_SW5 0x4D
116#define CS7_SW5 0x4E
117#define CS8_SW5 0x4F
118#define CS9_SW5 0x50
119#define CS10_SW5 0x51
120#define CS11_SW5 0x52
121#define CS12_SW5 0x53
122#define CS13_SW5 0x54
123#define CS14_SW5 0x55
124#define CS15_SW5 0x56
125#define CS16_SW5 0x57
126#define CS17_SW5 0x58
127#define CS18_SW5 0x59
128#define CS1_SW6 0x5A
129#define CS2_SW6 0x5B
130#define CS3_SW6 0x5C
131#define CS4_SW6 0x5D
132#define CS5_SW6 0x5E
133#define CS6_SW6 0x5F
134#define CS7_SW6 0x60
135#define CS8_SW6 0x61
136#define CS9_SW6 0x62
137#define CS10_SW6 0x63
138#define CS11_SW6 0x64
139#define CS12_SW6 0x65
140#define CS13_SW6 0x66
141#define CS14_SW6 0x67
142#define CS15_SW6 0x68
143#define CS16_SW6 0x69
144#define CS17_SW6 0x6A
145#define CS18_SW6 0x6B
146#define CS1_SW7 0x6C
147#define CS2_SW7 0x6D
148#define CS3_SW7 0x6E
149#define CS4_SW7 0x6F
150#define CS5_SW7 0x70
151#define CS6_SW7 0x71
152#define CS7_SW7 0x72
153#define CS8_SW7 0x73
154#define CS9_SW7 0x74
155#define CS10_SW7 0x75
156#define CS11_SW7 0x76
157#define CS12_SW7 0x77
158#define CS13_SW7 0x78
159#define CS14_SW7 0x79
160#define CS15_SW7 0x7A
161#define CS16_SW7 0x7B
162#define CS17_SW7 0x7C
163#define CS18_SW7 0x7D
164#define CS1_SW8 0x7E
165#define CS2_SW8 0x7F
166#define CS3_SW8 0x80
167#define CS4_SW8 0x81
168#define CS5_SW8 0x82
169#define CS6_SW8 0x83
170#define CS7_SW8 0x84
171#define CS8_SW8 0x85
172#define CS9_SW8 0x86
173#define CS10_SW8 0x87
174#define CS11_SW8 0x88
175#define CS12_SW8 0x89
176#define CS13_SW8 0x8A
177#define CS14_SW8 0x8B
178#define CS15_SW8 0x8C
179#define CS16_SW8 0x8D
180#define CS17_SW8 0x8E
181#define CS18_SW8 0x8F
182#define CS1_SW9 0x90
183#define CS2_SW9 0x91
184#define CS3_SW9 0x92
185#define CS4_SW9 0x93
186#define CS5_SW9 0x94
187#define CS6_SW9 0x95
188#define CS7_SW9 0x96
189#define CS8_SW9 0x97
190#define CS9_SW9 0x98
191#define CS10_SW9 0x99
192#define CS11_SW9 0x9A
193#define CS12_SW9 0x9B
194#define CS13_SW9 0x9C
195#define CS14_SW9 0x9D
196#define CS15_SW9 0x9E
197#define CS16_SW9 0x9F
198#define CS17_SW9 0xA0
199#define CS18_SW9 0xA1
200#define CS1_SW10 0xA2
201#define CS2_SW10 0xA3
202#define CS3_SW10 0xA4
203#define CS4_SW10 0xA5
204#define CS5_SW10 0xA6
205#define CS6_SW10 0xA7
206#define CS7_SW10 0xA8
207#define CS8_SW10 0xA9
208#define CS9_SW10 0xAA
209#define CS10_SW10 0xAB
210#define CS11_SW10 0xAC
211#define CS12_SW10 0xAD
212#define CS13_SW10 0xAE
213#define CS14_SW10 0xAF
214#define CS15_SW10 0xB0
215#define CS16_SW10 0xB1
216#define CS17_SW10 0xB2
217#define CS18_SW10 0xB3
218#define CS1_SW11 0xB4
219#define CS2_SW11 0xB5
220#define CS3_SW11 0xB6
221#define CS4_SW11 0xB7
222#define CS5_SW11 0xB8
223#define CS6_SW11 0xB9
224#define CS7_SW11 0xBA
225#define CS8_SW11 0xBB
226#define CS9_SW11 0xBC
227#define CS10_SW11 0xBD
228#define CS11_SW11 0xBE
229#define CS12_SW11 0xBF
230#define CS13_SW11 0xC0
231#define CS14_SW11 0xC1
232#define CS15_SW11 0xC2
233#define CS16_SW11 0xC3
234#define CS17_SW11 0xC4
235#define CS18_SW11 0xC5
236#define CS1_SW12 0xC6
237#define CS2_SW12 0xC7
238#define CS3_SW12 0xC8
239#define CS4_SW12 0xC9
240#define CS5_SW12 0xCA
241#define CS6_SW12 0xCB
242#define CS7_SW12 0xCC
243#define CS8_SW12 0xCD
244#define CS9_SW12 0xCE
245#define CS10_SW12 0xCF
246#define CS11_SW12 0xD0
247#define CS12_SW12 0xD1
248#define CS13_SW12 0xD2
249#define CS14_SW12 0xD3
250#define CS15_SW12 0xD4
251#define CS16_SW12 0xD5
252#define CS17_SW12 0xD6
253#define CS18_SW12 0xD7
diff --git a/drivers/issi/is31fl3218.c b/drivers/led/issi/is31fl3218.c
index d43863ac4..d43863ac4 100644
--- a/drivers/issi/is31fl3218.c
+++ b/drivers/led/issi/is31fl3218.c
diff --git a/drivers/issi/is31fl3218.h b/drivers/led/issi/is31fl3218.h
index fa760da19..fa760da19 100644
--- a/drivers/issi/is31fl3218.h
+++ b/drivers/led/issi/is31fl3218.h
diff --git a/drivers/issi/is31fl3731-simple.c b/drivers/led/issi/is31fl3731-simple.c
index d295772f5..d295772f5 100644
--- a/drivers/issi/is31fl3731-simple.c
+++ b/drivers/led/issi/is31fl3731-simple.c
diff --git a/drivers/issi/is31fl3731-simple.h b/drivers/led/issi/is31fl3731-simple.h
index 9665d6ed3..ecde31eed 100644
--- a/drivers/issi/is31fl3731-simple.h
+++ b/drivers/led/issi/is31fl3731-simple.h
@@ -20,13 +20,14 @@
20 20
21#include <stdint.h> 21#include <stdint.h>
22#include <stdbool.h> 22#include <stdbool.h>
23#include "progmem.h"
23 24
24typedef struct is31_led { 25typedef struct is31_led {
25 uint8_t driver : 2; 26 uint8_t driver : 2;
26 uint8_t v; 27 uint8_t v;
27} __attribute__((packed)) is31_led; 28} __attribute__((packed)) is31_led;
28 29
29extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; 30extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
30 31
31void IS31FL3731_init(uint8_t addr); 32void IS31FL3731_init(uint8_t addr);
32void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data); 33void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/drivers/issi/is31fl3731.c b/drivers/led/issi/is31fl3731.c
index 110bdc1be..110bdc1be 100644
--- a/drivers/issi/is31fl3731.c
+++ b/drivers/led/issi/is31fl3731.c
diff --git a/drivers/issi/is31fl3731.h b/drivers/led/issi/is31fl3731.h
index 19e8e6251..803ea3ea1 100644
--- a/drivers/issi/is31fl3731.h
+++ b/drivers/led/issi/is31fl3731.h
@@ -19,6 +19,7 @@
19 19
20#include <stdint.h> 20#include <stdint.h>
21#include <stdbool.h> 21#include <stdbool.h>
22#include "progmem.h"
22 23
23typedef struct is31_led { 24typedef struct is31_led {
24 uint8_t driver : 2; 25 uint8_t driver : 2;
@@ -27,7 +28,7 @@ typedef struct is31_led {
27 uint8_t b; 28 uint8_t b;
28} __attribute__((packed)) is31_led; 29} __attribute__((packed)) is31_led;
29 30
30extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; 31extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
31 32
32void IS31FL3731_init(uint8_t addr); 33void IS31FL3731_init(uint8_t addr);
33void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data); 34void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/drivers/issi/is31fl3733.c b/drivers/led/issi/is31fl3733.c
index d99e5339c..d99e5339c 100644
--- a/drivers/issi/is31fl3733.c
+++ b/drivers/led/issi/is31fl3733.c
diff --git a/drivers/issi/is31fl3733.h b/drivers/led/issi/is31fl3733.h
index 603d505a1..64fd38eb1 100644
--- a/drivers/issi/is31fl3733.h
+++ b/drivers/led/issi/is31fl3733.h
@@ -20,6 +20,7 @@
20 20
21#include <stdint.h> 21#include <stdint.h>
22#include <stdbool.h> 22#include <stdbool.h>
23#include "progmem.h"
23 24
24typedef struct is31_led { 25typedef struct is31_led {
25 uint8_t driver : 2; 26 uint8_t driver : 2;
@@ -28,7 +29,7 @@ typedef struct is31_led {
28 uint8_t b; 29 uint8_t b;
29} __attribute__((packed)) is31_led; 30} __attribute__((packed)) is31_led;
30 31
31extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; 32extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
32 33
33void IS31FL3733_init(uint8_t addr, uint8_t sync); 34void IS31FL3733_init(uint8_t addr, uint8_t sync);
34bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data); 35bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/drivers/issi/is31fl3736.c b/drivers/led/issi/is31fl3736.c
index 7dece1b1e..7dece1b1e 100644
--- a/drivers/issi/is31fl3736.c
+++ b/drivers/led/issi/is31fl3736.c
diff --git a/drivers/issi/is31fl3736.h b/drivers/led/issi/is31fl3736.h
index e48e31c27..c956c87f7 100644
--- a/drivers/issi/is31fl3736.h
+++ b/drivers/led/issi/is31fl3736.h
@@ -18,6 +18,7 @@
18 18
19#include <stdint.h> 19#include <stdint.h>
20#include <stdbool.h> 20#include <stdbool.h>
21#include "progmem.h"
21 22
22// Simple interface option. 23// Simple interface option.
23// If these aren't defined, just define them to make it compile 24// If these aren't defined, just define them to make it compile
@@ -37,7 +38,7 @@ typedef struct is31_led {
37 uint8_t b; 38 uint8_t b;
38} __attribute__((packed)) is31_led; 39} __attribute__((packed)) is31_led;
39 40
40extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; 41extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
41 42
42void IS31FL3736_init(uint8_t addr); 43void IS31FL3736_init(uint8_t addr);
43void IS31FL3736_write_register(uint8_t addr, uint8_t reg, uint8_t data); 44void IS31FL3736_write_register(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/drivers/issi/is31fl3737.c b/drivers/led/issi/is31fl3737.c
index 8647c93cc..0bb4ddd42 100644
--- a/drivers/issi/is31fl3737.c
+++ b/drivers/led/issi/is31fl3737.c
@@ -65,11 +65,12 @@ uint8_t g_twi_transfer_buffer[20];
65// We could optimize this and take out the unused registers from these 65// We could optimize this and take out the unused registers from these
66// buffers and the transfers in IS31FL3737_write_pwm_buffer() but it's 66// buffers and the transfers in IS31FL3737_write_pwm_buffer() but it's
67// probably not worth the extra complexity. 67// probably not worth the extra complexity.
68
68uint8_t g_pwm_buffer[DRIVER_COUNT][192]; 69uint8_t g_pwm_buffer[DRIVER_COUNT][192];
69bool g_pwm_buffer_update_required = false; 70bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
70 71
71uint8_t g_led_control_registers[DRIVER_COUNT][24] = {{0}}; 72uint8_t g_led_control_registers[DRIVER_COUNT][24] = {0};
72bool g_led_control_registers_update_required = false; 73bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
73 74
74void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) { 75void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
75 g_twi_transfer_buffer[0] = reg; 76 g_twi_transfer_buffer[0] = reg;
@@ -155,10 +156,10 @@ void IS31FL3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
155 if (index >= 0 && index < DRIVER_LED_TOTAL) { 156 if (index >= 0 && index < DRIVER_LED_TOTAL) {
156 is31_led led = g_is31_leds[index]; 157 is31_led led = g_is31_leds[index];
157 158
158 g_pwm_buffer[led.driver][led.r] = red; 159 g_pwm_buffer[led.driver][led.r] = red;
159 g_pwm_buffer[led.driver][led.g] = green; 160 g_pwm_buffer[led.driver][led.g] = green;
160 g_pwm_buffer[led.driver][led.b] = blue; 161 g_pwm_buffer[led.driver][led.b] = blue;
161 g_pwm_buffer_update_required = true; 162 g_pwm_buffer_update_required[led.driver] = true;
162 } 163 }
163} 164}
164 165
@@ -194,30 +195,28 @@ void IS31FL3737_set_led_control_register(uint8_t index, bool red, bool green, bo
194 g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b); 195 g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
195 } 196 }
196 197
197 g_led_control_registers_update_required = true; 198 g_led_control_registers_update_required[led.driver] = true;
198} 199}
199 200
200void IS31FL3737_update_pwm_buffers(uint8_t addr1, uint8_t addr2) { 201void IS31FL3737_update_pwm_buffers(uint8_t addr, uint8_t index) {
201 if (g_pwm_buffer_update_required) { 202 if (g_pwm_buffer_update_required[index]) {
202 // Firstly we need to unlock the command register and select PG1 203 // Firstly we need to unlock the command register and select PG1
203 IS31FL3737_write_register(addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); 204 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
204 IS31FL3737_write_register(addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM); 205 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
205 206
206 IS31FL3737_write_pwm_buffer(addr1, g_pwm_buffer[0]); 207 IS31FL3737_write_pwm_buffer(addr, g_pwm_buffer[index]);
207 // IS31FL3737_write_pwm_buffer(addr2, g_pwm_buffer[1]);
208 } 208 }
209 g_pwm_buffer_update_required = false; 209 g_pwm_buffer_update_required[index] = false;
210} 210}
211 211
212void IS31FL3737_update_led_control_registers(uint8_t addr1, uint8_t addr2) { 212void IS31FL3737_update_led_control_registers(uint8_t addr, uint8_t index) {
213 if (g_led_control_registers_update_required) { 213 if (g_led_control_registers_update_required[index]) {
214 // Firstly we need to unlock the command register and select PG0 214 // Firstly we need to unlock the command register and select PG0
215 IS31FL3737_write_register(addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); 215 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
216 IS31FL3737_write_register(addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL); 216 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
217 for (int i = 0; i < 24; i++) { 217 for (int i = 0; i < 24; i++) {
218 IS31FL3737_write_register(addr1, i, g_led_control_registers[0][i]); 218 IS31FL3737_write_register(addr, i, g_led_control_registers[index][i]);
219 // IS31FL3737_write_register(addr2, i, g_led_control_registers[1][i]);
220 } 219 }
221 g_led_control_registers_update_required = false;
222 } 220 }
221 g_led_control_registers_update_required[index] = false;
223} 222}
diff --git a/drivers/issi/is31fl3737.h b/drivers/led/issi/is31fl3737.h
index a1d228177..06886e9c9 100644
--- a/drivers/issi/is31fl3737.h
+++ b/drivers/led/issi/is31fl3737.h
@@ -20,6 +20,7 @@
20 20
21#include <stdint.h> 21#include <stdint.h>
22#include <stdbool.h> 22#include <stdbool.h>
23#include "progmem.h"
23 24
24typedef struct is31_led { 25typedef struct is31_led {
25 uint8_t driver : 2; 26 uint8_t driver : 2;
@@ -28,7 +29,7 @@ typedef struct is31_led {
28 uint8_t b; 29 uint8_t b;
29} __attribute__((packed)) is31_led; 30} __attribute__((packed)) is31_led;
30 31
31extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; 32extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
32 33
33void IS31FL3737_init(uint8_t addr); 34void IS31FL3737_init(uint8_t addr);
34void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data); 35void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/drivers/issi/is31fl3741.c b/drivers/led/issi/is31fl3741.c
index 1b533c9b6..1b533c9b6 100644
--- a/drivers/issi/is31fl3741.c
+++ b/drivers/led/issi/is31fl3741.c
diff --git a/drivers/issi/is31fl3741.h b/drivers/led/issi/is31fl3741.h
index 2df0c5b1a..cea6761ca 100644
--- a/drivers/issi/is31fl3741.h
+++ b/drivers/led/issi/is31fl3741.h
@@ -21,6 +21,7 @@
21 21
22#include <stdint.h> 22#include <stdint.h>
23#include <stdbool.h> 23#include <stdbool.h>
24#include "progmem.h"
24 25
25typedef struct is31_led { 26typedef struct is31_led {
26 uint32_t driver : 2; 27 uint32_t driver : 2;
@@ -29,7 +30,7 @@ typedef struct is31_led {
29 uint32_t b : 10; 30 uint32_t b : 10;
30} __attribute__((packed)) is31_led; 31} __attribute__((packed)) is31_led;
31 32
32extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; 33extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
33 34
34void IS31FL3741_init(uint8_t addr); 35void IS31FL3741_init(uint8_t addr);
35void IS31FL3741_write_register(uint8_t addr, uint8_t reg, uint8_t data); 36void IS31FL3741_write_register(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c
index 8e5ed5f07..7d4197890 100644
--- a/drivers/oled/oled_driver.c
+++ b/drivers/oled/oled_driver.c
@@ -34,6 +34,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
34#define DISPLAY_ALL_ON 0xA5 34#define DISPLAY_ALL_ON 0xA5
35#define DISPLAY_ALL_ON_RESUME 0xA4 35#define DISPLAY_ALL_ON_RESUME 0xA4
36#define NORMAL_DISPLAY 0xA6 36#define NORMAL_DISPLAY 0xA6
37#define INVERT_DISPLAY 0xA7
37#define DISPLAY_ON 0xAF 38#define DISPLAY_ON 0xAF
38#define DISPLAY_OFF 0xAE 39#define DISPLAY_OFF 0xAE
39#define NOP 0xE3 40#define NOP 0xE3
@@ -114,6 +115,7 @@ OLED_BLOCK_TYPE oled_dirty = 0;
114bool oled_initialized = false; 115bool oled_initialized = false;
115bool oled_active = false; 116bool oled_active = false;
116bool oled_scrolling = false; 117bool oled_scrolling = false;
118bool oled_inverted = false;
117uint8_t oled_brightness = OLED_BRIGHTNESS; 119uint8_t oled_brightness = OLED_BRIGHTNESS;
118oled_rotation_t oled_rotation = 0; 120oled_rotation_t oled_rotation = 0;
119uint8_t oled_rotation_width = 0; 121uint8_t oled_rotation_width = 0;
@@ -690,6 +692,30 @@ bool oled_scroll_off(void) {
690 return !oled_scrolling; 692 return !oled_scrolling;
691} 693}
692 694
695bool oled_invert(bool invert) {
696 if (!oled_initialized) {
697 return oled_inverted;
698 }
699
700 if (invert && !oled_inverted) {
701 static const uint8_t PROGMEM display_inverted[] = {I2C_CMD, INVERT_DISPLAY};
702 if (I2C_TRANSMIT_P(display_inverted) != I2C_STATUS_SUCCESS) {
703 print("oled_invert cmd failed\n");
704 return oled_inverted;
705 }
706 oled_inverted = true;
707 } else if (!invert && oled_inverted) {
708 static const uint8_t PROGMEM display_normal[] = {I2C_CMD, NORMAL_DISPLAY};
709 if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) {
710 print("oled_invert cmd failed\n");
711 return oled_inverted;
712 }
713 oled_inverted = false;
714 }
715
716 return oled_inverted;
717}
718
693uint8_t oled_max_chars(void) { 719uint8_t oled_max_chars(void) {
694 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { 720 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
695 return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH; 721 return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH;
diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h
index a6b85f37e..fc68f0ec9 100644
--- a/drivers/oled/oled_driver.h
+++ b/drivers/oled/oled_driver.h
@@ -313,6 +313,10 @@ bool oled_scroll_left(void);
313// Returns true if the screen was not scrolling or stops scrolling 313// Returns true if the screen was not scrolling or stops scrolling
314bool oled_scroll_off(void); 314bool oled_scroll_off(void);
315 315
316// Inverts the display
317// Returns true if the screen was or is inverted
318bool oled_invert(bool invert);
319
316// Returns the maximum number of characters that will fit on a line 320// Returns the maximum number of characters that will fit on a line
317uint8_t oled_max_chars(void); 321uint8_t oled_max_chars(void);
318 322
diff --git a/drivers/sensors/adns5050.c b/drivers/sensors/adns5050.c
new file mode 100644
index 000000000..e7273977d
--- /dev/null
+++ b/drivers/sensors/adns5050.c
@@ -0,0 +1,193 @@
1/* Copyright 2021 Colin Lam (Ploopy Corporation)
2 * Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
3 * Copyright 2019 Sunjun Kim
4 * Copyright 2019 Hiroyuki Okada
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20
21#include "adns5050.h"
22#include "wait.h"
23#include "debug.h"
24#include "print.h"
25#include "gpio.h"
26
27#ifndef OPTIC_ROTATED
28# define OPTIC_ROTATED false
29#endif
30
31// Definitions for the ADNS serial line.
32#ifndef ADNS_SCLK_PIN
33# define ADNS_SCLK_PIN B7
34#endif
35
36#ifndef ADNS_SDIO_PIN
37# define ADNS_SDIO_PIN C6
38#endif
39
40#ifndef ADNS_CS_PIN
41# define ADNS_CS_PIN B4
42#endif
43
44#ifdef CONSOLE_ENABLE
45void print_byte(uint8_t byte) { dprintf("%c%c%c%c%c%c%c%c|", (byte & 0x80 ? '1' : '0'), (byte & 0x40 ? '1' : '0'), (byte & 0x20 ? '1' : '0'), (byte & 0x10 ? '1' : '0'), (byte & 0x08 ? '1' : '0'), (byte & 0x04 ? '1' : '0'), (byte & 0x02 ? '1' : '0'), (byte & 0x01 ? '1' : '0')); }
46#endif
47
48// Initialize the ADNS serial pins.
49void adns_init(void) {
50 setPinOutput(ADNS_SCLK_PIN);
51 setPinOutput(ADNS_SDIO_PIN);
52 setPinOutput(ADNS_CS_PIN);
53}
54
55// Perform a synchronization with the ADNS.
56// Just as with the serial protocol, this is used by the slave to send a
57// synchronization signal to the master.
58void adns_sync(void) {
59 writePinLow(ADNS_CS_PIN);
60 wait_us(1);
61 writePinHigh(ADNS_CS_PIN);
62}
63
64void adns_cs_select(void) {
65 writePinLow(ADNS_CS_PIN);
66}
67
68void adns_cs_deselect(void) {
69 writePinHigh(ADNS_CS_PIN);
70}
71
72uint8_t adns_serial_read(void) {
73 setPinInput(ADNS_SDIO_PIN);
74 uint8_t byte = 0;
75
76 for (uint8_t i = 0; i < 8; ++i) {
77 writePinLow(ADNS_SCLK_PIN);
78 wait_us(1);
79
80 byte = (byte << 1) | readPin(ADNS_SDIO_PIN);
81
82 writePinHigh(ADNS_SCLK_PIN);
83 wait_us(1);
84 }
85
86 return byte;
87}
88
89void adns_serial_write(uint8_t data) {
90 setPinOutput(ADNS_SDIO_PIN);
91
92 for (int8_t b = 7; b >= 0; b--) {
93 writePinLow(ADNS_SCLK_PIN);
94
95 if (data & (1 << b))
96 writePinHigh(ADNS_SDIO_PIN);
97 else
98 writePinLow(ADNS_SDIO_PIN);
99
100 wait_us(2);
101
102 writePinHigh(ADNS_SCLK_PIN);
103 }
104
105 // tSWR. See page 15 of the ADNS spec sheet.
106 // Technically, this is only necessary if the next operation is an SDIO
107 // read. This is not guaranteed to be the case, but we're being lazy.
108 wait_us(4);
109
110 // Note that tSWW is never necessary. All write operations require at
111 // least 32us, which exceeds tSWW, so there's never a need to wait for it.
112}
113
114// Read a byte of data from a register on the ADNS.
115// Don't forget to use the register map (as defined in the header file).
116uint8_t adns_read_reg(uint8_t reg_addr) {
117 adns_cs_select();
118
119 adns_serial_write(reg_addr);
120
121 // We don't need a minimum tSRAD here. That's because a 4ms wait time is
122 // already included in adns_serial_write(), so we're good.
123 // See page 10 and 15 of the ADNS spec sheet.
124 //wait_us(4);
125
126 uint8_t byte = adns_serial_read();
127
128 // tSRW & tSRR. See page 15 of the ADNS spec sheet.
129 // Technically, this is only necessary if the next operation is an SDIO
130 // read or write. This is not guaranteed to be the case.
131 // Honestly, this wait could probably be removed.
132 wait_us(1);
133
134 adns_cs_deselect();
135
136 return byte;
137}
138
139void adns_write_reg(uint8_t reg_addr, uint8_t data) {
140 adns_cs_select();
141 adns_serial_write( 0b10000000 | reg_addr );
142 adns_serial_write(data);
143 adns_cs_deselect();
144}
145
146report_adns_t adns_read_burst(void) {
147 adns_cs_select();
148
149 report_adns_t data;
150 data.dx = 0;
151 data.dy = 0;
152
153 adns_serial_write(REG_MOTION_BURST);
154
155 // We don't need a minimum tSRAD here. That's because a 4ms wait time is
156 // already included in adns_serial_write(), so we're good.
157 // See page 10 and 15 of the ADNS spec sheet.
158 //wait_us(4);
159
160 uint8_t x = adns_serial_read();
161 uint8_t y = adns_serial_read();
162
163 // Burst mode returns a bunch of other shit that we don't really need.
164 // Setting CS to high ends burst mode early.
165 adns_cs_deselect();
166
167 data.dx = convert_twoscomp(x);
168 data.dy = convert_twoscomp(y);
169
170 return data;
171}
172
173// Convert a two's complement byte from an unsigned data type into a signed
174// data type.
175int8_t convert_twoscomp(uint8_t data) {
176 if ((data & 0x80) == 0x80)
177 return -128 + (data & 0x7F);
178 else
179 return data;
180}
181
182// Don't forget to use the definitions for CPI in the header file.
183void adns_set_cpi(uint8_t cpi) {
184 adns_write_reg(REG_MOUSE_CONTROL2, cpi);
185}
186
187bool adns_check_signature(void) {
188 uint8_t pid = adns_read_reg(REG_PRODUCT_ID);
189 uint8_t rid = adns_read_reg(REG_REVISION_ID);
190 uint8_t pid2 = adns_read_reg(REG_PRODUCT_ID2);
191
192 return (pid == 0x12 && rid == 0x01 && pid2 == 0x26);
193}
diff --git a/drivers/sensors/adns5050.h b/drivers/sensors/adns5050.h
new file mode 100644
index 000000000..ff8e8f78e
--- /dev/null
+++ b/drivers/sensors/adns5050.h
@@ -0,0 +1,79 @@
1/* Copyright 2021 Colin Lam (Ploopy Corporation)
2 * Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
3 * Copyright 2019 Sunjun Kim
4 * Copyright 2019 Hiroyuki Okada
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <stdbool.h>
23
24// Registers
25#define REG_PRODUCT_ID 0x00
26#define REG_REVISION_ID 0x01
27#define REG_MOTION 0x02
28#define REG_DELTA_X 0x03
29#define REG_DELTA_Y 0x04
30#define REG_SQUAL 0x05
31#define REG_SHUTTER_UPPER 0x06
32#define REG_SHUTTER_LOWER 0x07
33#define REG_MAXIMUM_PIXEL 0x08
34#define REG_PIXEL_SUM 0x09
35#define REG_MINIMUM_PIXEL 0x0a
36#define REG_PIXEL_GRAB 0x0b
37#define REG_MOUSE_CONTROL 0x0d
38#define REG_MOUSE_CONTROL2 0x19
39#define REG_LED_DC_MODE 0x22
40#define REG_CHIP_RESET 0x3a
41#define REG_PRODUCT_ID2 0x3e
42#define REG_INV_REV_ID 0x3f
43#define REG_MOTION_BURST 0x63
44
45// CPI values
46#define CPI125 0x11
47#define CPI250 0x12
48#define CPI375 0x13
49#define CPI500 0x14
50#define CPI625 0x15
51#define CPI750 0x16
52#define CPI875 0x17
53#define CPI1000 0x18
54#define CPI1125 0x19
55#define CPI1250 0x1a
56#define CPI1375 0x1b
57
58#ifdef CONSOLE_ENABLE
59void print_byte(uint8_t byte);
60#endif
61
62typedef struct {
63 int8_t dx;
64 int8_t dy;
65} report_adns_t;
66
67// A bunch of functions to implement the ADNS5050-specific serial protocol.
68// Note that the "serial.h" driver is insufficient, because it does not
69// manually manipulate a serial clock signal.
70void adns_init(void);
71void adns_sync(void);
72uint8_t adns_serial_read(void);
73void adns_serial_write(uint8_t data);
74uint8_t adns_read_reg(uint8_t reg_addr);
75void adns_write_reg(uint8_t reg_addr, uint8_t data);
76report_adns_t adns_read_burst(void);
77int8_t convert_twoscomp(uint8_t data);
78void adns_set_cpi(uint8_t cpi);
79bool adns_check_signature(void);
diff --git a/drivers/sensors/adns9800.c b/drivers/sensors/adns9800.c
new file mode 100644
index 000000000..36213179f
--- /dev/null
+++ b/drivers/sensors/adns9800.c
@@ -0,0 +1,219 @@
1/* Copyright 2020 Alexander Tulloh
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 "spi_master.h"
18#include "quantum.h"
19#include "adns9800_srom_A6.h"
20#include "adns9800.h"
21
22// registers
23#define REG_Product_ID 0x00
24#define REG_Revision_ID 0x01
25#define REG_Motion 0x02
26#define REG_Delta_X_L 0x03
27#define REG_Delta_X_H 0x04
28#define REG_Delta_Y_L 0x05
29#define REG_Delta_Y_H 0x06
30#define REG_SQUAL 0x07
31#define REG_Pixel_Sum 0x08
32#define REG_Maximum_Pixel 0x09
33#define REG_Minimum_Pixel 0x0a
34#define REG_Shutter_Lower 0x0b
35#define REG_Shutter_Upper 0x0c
36#define REG_Frame_Period_Lower 0x0d
37#define REG_Frame_Period_Upper 0x0e
38#define REG_Configuration_I 0x0f
39#define REG_Configuration_II 0x10
40#define REG_Frame_Capture 0x12
41#define REG_SROM_Enable 0x13
42#define REG_Run_Downshift 0x14
43#define REG_Rest1_Rate 0x15
44#define REG_Rest1_Downshift 0x16
45#define REG_Rest2_Rate 0x17
46#define REG_Rest2_Downshift 0x18
47#define REG_Rest3_Rate 0x19
48#define REG_Frame_Period_Max_Bound_Lower 0x1a
49#define REG_Frame_Period_Max_Bound_Upper 0x1b
50#define REG_Frame_Period_Min_Bound_Lower 0x1c
51#define REG_Frame_Period_Min_Bound_Upper 0x1d
52#define REG_Shutter_Max_Bound_Lower 0x1e
53#define REG_Shutter_Max_Bound_Upper 0x1f
54#define REG_LASER_CTRL0 0x20
55#define REG_Observation 0x24
56#define REG_Data_Out_Lower 0x25
57#define REG_Data_Out_Upper 0x26
58#define REG_SROM_ID 0x2a
59#define REG_Lift_Detection_Thr 0x2e
60#define REG_Configuration_V 0x2f
61#define REG_Configuration_IV 0x39
62#define REG_Power_Up_Reset 0x3a
63#define REG_Shutdown 0x3b
64#define REG_Inverse_Product_ID 0x3f
65#define REG_Motion_Burst 0x50
66#define REG_SROM_Load_Burst 0x62
67#define REG_Pixel_Burst 0x64
68
69#define ADNS_CLOCK_SPEED 2000000
70#define MIN_CPI 200
71#define MAX_CPI 8200
72#define CPI_STEP 200
73#define CLAMP_CPI(value) value < MIN_CPI ? MIN_CPI : value > MAX_CPI ? MAX_CPI : value
74#define SPI_MODE 3
75#define SPI_DIVISOR (F_CPU / ADNS_CLOCK_SPEED)
76#define US_BETWEEN_WRITES 120
77#define US_BETWEEN_READS 20
78#define US_BEFORE_MOTION 100
79#define MSB1 0x80
80
81extern const uint16_t adns_firmware_length;
82extern const uint8_t adns_firmware_data[];
83
84void adns_spi_start(void){
85 spi_start(SPI_SS_PIN, false, SPI_MODE, SPI_DIVISOR);
86}
87
88void adns_write(uint8_t reg_addr, uint8_t data){
89
90 adns_spi_start();
91 spi_write(reg_addr | MSB1);
92 spi_write(data);
93 spi_stop();
94 wait_us(US_BETWEEN_WRITES);
95}
96
97uint8_t adns_read(uint8_t reg_addr){
98
99 adns_spi_start();
100 spi_write(reg_addr & 0x7f );
101 uint8_t data = spi_read();
102 spi_stop();
103 wait_us(US_BETWEEN_READS);
104
105 return data;
106}
107
108void adns_init() {
109
110 setPinOutput(SPI_SS_PIN);
111
112 spi_init();
113
114 // reboot
115 adns_write(REG_Power_Up_Reset, 0x5a);
116 wait_ms(50);
117
118 // read registers and discard
119 adns_read(REG_Motion);
120 adns_read(REG_Delta_X_L);
121 adns_read(REG_Delta_X_H);
122 adns_read(REG_Delta_Y_L);
123 adns_read(REG_Delta_Y_H);
124
125 // upload firmware
126
127 // 3k firmware mode
128 adns_write(REG_Configuration_IV, 0x02);
129
130 // enable initialisation
131 adns_write(REG_SROM_Enable, 0x1d);
132
133 // wait a frame
134 wait_ms(10);
135
136 // start SROM download
137 adns_write(REG_SROM_Enable, 0x18);
138
139 // write the SROM file
140
141 adns_spi_start();
142
143 spi_write(REG_SROM_Load_Burst | 0x80);
144 wait_us(15);
145
146 // send all bytes of the firmware
147 unsigned char c;
148 for(int i = 0; i < adns_firmware_length; i++){
149 c = (unsigned char)pgm_read_byte(adns_firmware_data + i);
150 spi_write(c);
151 wait_us(15);
152 }
153
154 spi_stop();
155
156 wait_ms(10);
157
158 // enable laser
159 uint8_t laser_ctrl0 = adns_read(REG_LASER_CTRL0);
160 adns_write(REG_LASER_CTRL0, laser_ctrl0 & 0xf0);
161}
162
163config_adns_t adns_get_config(void) {
164 uint8_t config_1 = adns_read(REG_Configuration_I);
165 return (config_adns_t){ (config_1 & 0xFF) * CPI_STEP };
166}
167
168void adns_set_config(config_adns_t config) {
169 uint8_t config_1 = (CLAMP_CPI(config.cpi) / CPI_STEP) & 0xFF;
170 adns_write(REG_Configuration_I, config_1);
171}
172
173static int16_t convertDeltaToInt(uint8_t high, uint8_t low){
174
175 // join bytes into twos compliment
176 uint16_t twos_comp = (high << 8) | low;
177
178 // convert twos comp to int
179 if (twos_comp & 0x8000)
180 return -1 * (~twos_comp + 1);
181
182 return twos_comp;
183}
184
185report_adns_t adns_get_report(void) {
186
187 report_adns_t report = {0, 0};
188
189 adns_spi_start();
190
191 // start burst mode
192 spi_write(REG_Motion_Burst & 0x7f);
193
194 wait_us(US_BEFORE_MOTION);
195
196 uint8_t motion = spi_read();
197
198 if(motion & 0x80) {
199
200 // clear observation register
201 spi_read();
202
203 // delta registers
204 uint8_t delta_x_l = spi_read();
205 uint8_t delta_x_h = spi_read();
206 uint8_t delta_y_l = spi_read();
207 uint8_t delta_y_h = spi_read();
208
209 report.x = convertDeltaToInt(delta_x_h, delta_x_l);
210 report.y = convertDeltaToInt(delta_y_h, delta_y_l);
211 }
212
213 // clear residual motion
214 spi_write(REG_Motion & 0x7f);
215
216 spi_stop();
217
218 return report;
219}
diff --git a/drivers/chibios/analog.h b/drivers/sensors/adns9800.h
index e61c39426..2f50b8f1b 100644
--- a/drivers/chibios/analog.h
+++ b/drivers/sensors/adns9800.h
@@ -1,4 +1,4 @@
1/* Copyright 2019 Drew Mills 1/* Copyright 2020 Alexander Tulloh
2 * 2 *
3 * This program is free software: you can redistribute it and/or modify 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 4 * it under the terms of the GNU General Public License as published by
@@ -17,25 +17,19 @@
17#pragma once 17#pragma once
18 18
19#include <stdint.h> 19#include <stdint.h>
20#include "quantum.h"
21
22#ifdef __cplusplus
23extern "C" {
24#endif
25 20
26typedef struct { 21typedef struct {
27 uint16_t input; 22 /* 200 - 8200 CPI supported */
28 uint8_t adc; 23 uint16_t cpi;
29} adc_mux; 24} config_adns_t;
30#define TO_MUX(i, a) \
31 (adc_mux) { i, a }
32
33int16_t analogReadPin(pin_t pin);
34int16_t analogReadPinAdc(pin_t pin, uint8_t adc);
35adc_mux pinToMux(pin_t pin);
36 25
37int16_t adc_read(adc_mux mux); 26typedef struct {
27 int16_t x;
28 int16_t y;
29} report_adns_t;
38 30
39#ifdef __cplusplus 31void adns_init(void);
40} 32config_adns_t adns_get_config(void);
41#endif 33void adns_set_config(config_adns_t);
34/* Reads and clears the current delta values on the ADNS sensor */
35report_adns_t adns_get_report(void);
diff --git a/drivers/sensors/adns9800_srom_A6.h b/drivers/sensors/adns9800_srom_A6.h
new file mode 100644
index 000000000..f5b3abeb6
--- /dev/null
+++ b/drivers/sensors/adns9800_srom_A6.h
@@ -0,0 +1,3078 @@
1#pragma once
2
3#include "progmem.h"
4
5const uint16_t adns_firmware_length = 3070;
6
7const uint8_t adns_firmware_data[] PROGMEM = {
80x03,
90xa6,
100x68,
110x1e,
120x7d,
130x10,
140x7e,
150x7e,
160x5f,
170x1c,
180xb8,
190xf2,
200x47,
210x0c,
220x7b,
230x74,
240x4b,
250x14,
260x8b,
270x75,
280x66,
290x51,
300x0b,
310x8c,
320x76,
330x74,
340x4b,
350x14,
360xaa,
370xd6,
380x0f,
390x9c,
400xba,
410xf6,
420x6e,
430x3f,
440xdd,
450x38,
460xd5,
470x02,
480x80,
490x9b,
500x82,
510x6d,
520x58,
530x13,
540xa4,
550xab,
560xb5,
570xc9,
580x10,
590xa2,
600xc6,
610x0a,
620x7f,
630x5d,
640x19,
650x91,
660xa0,
670xa3,
680xce,
690xeb,
700x3e,
710xc9,
720xf1,
730x60,
740x42,
750xe7,
760x4c,
770xfb,
780x74,
790x6a,
800x56,
810x2e,
820xbf,
830xdd,
840x38,
850xd3,
860x05,
870x88,
880x92,
890xa6,
900xce,
910xff,
920x5d,
930x38,
940xd1,
950xcf,
960xef,
970x58,
980xcb,
990x65,
1000x48,
1010xf0,
1020x35,
1030x85,
1040xa9,
1050xb2,
1060x8f,
1070x5e,
1080xf3,
1090x80,
1100x94,
1110x97,
1120x7e,
1130x75,
1140x97,
1150x87,
1160x73,
1170x13,
1180xb0,
1190x8a,
1200x69,
1210xd4,
1220x0a,
1230xde,
1240xc1,
1250x79,
1260x59,
1270x36,
1280xdb,
1290x9d,
1300xd6,
1310xb8,
1320x15,
1330x6f,
1340xce,
1350x3c,
1360x72,
1370x32,
1380x45,
1390x88,
1400xdf,
1410x6c,
1420xa5,
1430x6d,
1440xe8,
1450x76,
1460x96,
1470x14,
1480x74,
1490x20,
1500xdc,
1510xf4,
1520xfa,
1530x37,
1540x6a,
1550x27,
1560x32,
1570xe3,
1580x29,
1590xbf,
1600xc4,
1610xc7,
1620x06,
1630x9d,
1640x58,
1650xe7,
1660x87,
1670x7c,
1680x2e,
1690x9f,
1700x6e,
1710x49,
1720x07,
1730x5d,
1740x23,
1750x64,
1760x54,
1770x83,
1780x6e,
1790xcb,
1800xb7,
1810x77,
1820xf7,
1830x2b,
1840x6e,
1850x0f,
1860x2e,
1870x66,
1880x12,
1890x60,
1900x55,
1910x65,
1920xfc,
1930x43,
1940xb3,
1950x58,
1960x73,
1970x5b,
1980xe8,
1990x67,
2000x04,
2010x43,
2020x02,
2030xde,
2040xb3,
2050x89,
2060xa0,
2070x6d,
2080x3a,
2090x27,
2100x79,
2110x64,
2120x5b,
2130x0c,
2140x16,
2150x9e,
2160x66,
2170xb1,
2180x8b,
2190x87,
2200x0c,
2210x5d,
2220xf2,
2230xb6,
2240x3d,
2250x71,
2260xdf,
2270x42,
2280x03,
2290x8a,
2300x06,
2310x8d,
2320xef,
2330x1d,
2340xa8,
2350x96,
2360x5c,
2370xed,
2380x31,
2390x61,
2400x5c,
2410xa1,
2420x34,
2430xf6,
2440x8c,
2450x08,
2460x60,
2470x33,
2480x07,
2490x00,
2500x3e,
2510x79,
2520x95,
2530x1b,
2540x43,
2550x7f,
2560xfe,
2570xb6,
2580xa6,
2590xd4,
2600x9d,
2610x76,
2620x72,
2630xbf,
2640xad,
2650xc0,
2660x15,
2670xe8,
2680x37,
2690x31,
2700xa3,
2710x72,
2720x63,
2730x52,
2740x1d,
2750x1c,
2760x5d,
2770x51,
2780x1b,
2790xe1,
2800xa9,
2810xed,
2820x60,
2830x32,
2840x3e,
2850xa9,
2860x50,
2870x28,
2880x53,
2890x06,
2900x59,
2910xe2,
2920xfc,
2930xe7,
2940x02,
2950x64,
2960x39,
2970x21,
2980x56,
2990x4a,
3000xa5,
3010x40,
3020x80,
3030x81,
3040xd5,
3050x5a,
3060x60,
3070x7b,
3080x68,
3090x84,
3100xf1,
3110xe0,
3120xb1,
3130xb6,
3140x5b,
3150xdf,
3160xa8,
3170x1d,
3180x6d,
3190x65,
3200x20,
3210xc0,
3220xa2,
3230xb9,
3240xd9,
3250xbb,
3260x00,
3270xa6,
3280xdb,
3290x8b,
3300x01,
3310x53,
3320x91,
3330xfe,
3340xc4,
3350x51,
3360x85,
3370xb0,
3380x96,
3390x7f,
3400xfd,
3410x51,
3420xdd,
3430x14,
3440x03,
3450x67,
3460x2e,
3470x75,
3480x1c,
3490x76,
3500xd3,
3510x6e,
3520xdd,
3530x99,
3540x55,
3550x76,
3560xe5,
3570xab,
3580x23,
3590xfc,
3600x4a,
3610xd5,
3620xc6,
3630xe8,
3640x2e,
3650xca,
3660x8a,
3670xb3,
3680xf6,
3690x8c,
3700x6c,
3710xb0,
3720xe9,
3730xf2,
3740xe7,
3750x9e,
3760x69,
3770x41,
3780xed,
3790xf1,
3800x6d,
3810xd2,
3820x86,
3830xd8,
3840x7e,
3850xcb,
3860x5d,
3870x47,
3880x6c,
3890x85,
3900x6a,
3910x23,
3920xed,
3930x20,
3940x40,
3950x93,
3960xb4,
3970x20,
3980xc7,
3990xa5,
4000xc9,
4010xaf,
4020x03,
4030x15,
4040xac,
4050x19,
4060xe5,
4070x2a,
4080x36,
4090xdf,
4100x6d,
4110xc5,
4120x8c,
4130x80,
4140x07,
4150xce,
4160x92,
4170x0c,
4180xd8,
4190x06,
4200x62,
4210x0f,
4220xdd,
4230x48,
4240x46,
4250x1a,
4260x53,
4270xc7,
4280x8a,
4290x8c,
4300x5d,
4310x5d,
4320xb4,
4330xa1,
4340x02,
4350xd3,
4360xa9,
4370xb8,
4380xf3,
4390x94,
4400x8f,
4410x3f,
4420xe5,
4430x54,
4440xd4,
4450x11,
4460x65,
4470xb2,
4480x5e,
4490x09,
4500x0b,
4510x81,
4520xe3,
4530x75,
4540xa7,
4550x89,
4560x81,
4570x39,
4580x6c,
4590x46,
4600xf6,
4610x06,
4620x9f,
4630x27,
4640x3b,
4650xb6,
4660x2d,
4670x5f,
4680x1d,
4690x4b,
4700xd4,
4710x7b,
4720x1d,
4730x61,
4740x74,
4750x89,
4760xe4,
4770xe3,
4780xbd,
4790x98,
4800x1b,
4810xc4,
4820x51,
4830x3b,
4840xa4,
4850xfa,
4860xe0,
4870x92,
4880xf7,
4890xbe,
4900xf2,
4910x4d,
4920xbb,
4930xff,
4940xad,
4950x4f,
4960x6d,
4970x68,
4980xc2,
4990x79,
5000x40,
5010xaa,
5020x9b,
5030x8f,
5040x0c,
5050x32,
5060x4b,
5070x5f,
5080x3e,
5090xab,
5100x59,
5110x98,
5120xb3,
5130xf5,
5140x1d,
5150xac,
5160x5e,
5170xbc,
5180x78,
5190xd3,
5200x01,
5210x6c,
5220x64,
5230x15,
5240x2f,
5250xd8,
5260x71,
5270xa6,
5280x2d,
5290x45,
5300xe1,
5310x22,
5320x42,
5330xe4,
5340x4e,
5350x04,
5360x3c,
5370x7d,
5380xf4,
5390x40,
5400x21,
5410xb4,
5420x67,
5430x05,
5440xa8,
5450xe2,
5460xf3,
5470x72,
5480x87,
5490x4c,
5500x7d,
5510xd9,
5520x1b,
5530x65,
5540x97,
5550xf3,
5560xc2,
5570xe3,
5580xe4,
5590xc8,
5600xd2,
5610xde,
5620xf6,
5630xef,
5640xdc,
5650xbb,
5660x44,
5670x08,
5680x5e,
5690xe2,
5700x45,
5710x27,
5720x01,
5730xb0,
5740xf6,
5750x43,
5760xe7,
5770x3a,
5780xf6,
5790xdc,
5800x9d,
5810xed,
5820xf3,
5830xc5,
5840x0c,
5850xb8,
5860x9c,
5870x98,
5880x3a,
5890xd8,
5900x36,
5910xee,
5920x96,
5930x72,
5940x67,
5950xe7,
5960x81,
5970x91,
5980xd5,
5990x05,
6000x0a,
6010xe0,
6020x82,
6030xd5,
6040x8f,
6050xe8,
6060xf9,
6070xb0,
6080xc9,
6090xcf,
6100x93,
6110xe7,
6120x04,
6130xc5,
6140xbc,
6150x2b,
6160x43,
6170x56,
6180x7e,
6190xe8,
6200x67,
6210x7c,
6220xe5,
6230xfb,
6240x49,
6250xad,
6260x5e,
6270x9f,
6280x25,
6290x13,
6300xde,
6310x6e,
6320x6e,
6330xe9,
6340xf1,
6350xec,
6360x87,
6370x0b,
6380x59,
6390x81,
6400x76,
6410x84,
6420x76,
6430xb3,
6440x24,
6450xaf,
6460x30,
6470xfd,
6480x27,
6490x8b,
6500xab,
6510xd8,
6520x00,
6530x8b,
6540x9b,
6550x0c,
6560xd2,
6570xb2,
6580x4e,
6590x5e,
6600x9d,
6610x1d,
6620x96,
6630x01,
6640x00,
6650x67,
6660xc1,
6670x5f,
6680x02,
6690x20,
6700xfd,
6710x45,
6720x6a,
6730x01,
6740x60,
6750x58,
6760x45,
6770xca,
6780x47,
6790x21,
6800x90,
6810x5a,
6820xc4,
6830x43,
6840x26,
6850x1a,
6860xd7,
6870xa5,
6880x4a,
6890xb2,
6900x5d,
6910x2b,
6920x35,
6930x49,
6940xfb,
6950xa5,
6960x17,
6970x92,
6980x21,
6990x1e,
7000x93,
7010x96,
7020x67,
7030xa2,
7040x7e,
7050x36,
7060x7a,
7070xde,
7080x5f,
7090xbe,
7100x7a,
7110x58,
7120x9d,
7130xf8,
7140x78,
7150xa3,
7160xfa,
7170xc8,
7180xd5,
7190x17,
7200xf0,
7210x21,
7220x97,
7230x8c,
7240x80,
7250xb5,
7260x4b,
7270x3b,
7280xbd,
7290xbb,
7300x41,
7310x21,
7320xa8,
7330x50,
7340x67,
7350xf7,
7360xe7,
7370x19,
7380x80,
7390x10,
7400x8e,
7410xce,
7420x04,
7430x18,
7440x3f,
7450x51,
7460x6b,
7470x77,
7480xd8,
7490x9e,
7500x16,
7510xaf,
7520xec,
7530xef,
7540x48,
7550x16,
7560x4d,
7570x9e,
7580x85,
7590x38,
7600x18,
7610x3e,
7620xd4,
7630x28,
7640x87,
7650x60,
7660x2a,
7670xf6,
7680x7f,
7690x09,
7700x86,
7710x6f,
7720x9c,
7730x3c,
7740x3a,
7750xff,
7760xab,
7770xd0,
7780x61,
7790xa2,
7800x97,
7810x0d,
7820x71,
7830x94,
7840x7e,
7850xfd,
7860xb9,
7870x80,
7880x02,
7890x89,
7900x6a,
7910xb3,
7920x84,
7930x6c,
7940x2a,
7950x77,
7960x62,
7970xbe,
7980x0b,
7990xf4,
8000xaf,
8010xac,
8020x7b,
8030x7c,
8040x8e,
8050xca,
8060x01,
8070xba,
8080x71,
8090x78,
8100x94,
8110xfd,
8120xb5,
8130x39,
8140xa4,
8150x4d,
8160x2f,
8170x78,
8180xcf,
8190xca,
8200x92,
8210x0c,
8220x1a,
8230x99,
8240x48,
8250x4c,
8260x11,
8270x96,
8280xb5,
8290x4e,
8300x41,
8310x28,
8320xe4,
8330xa6,
8340xfe,
8350x4b,
8360x72,
8370x91,
8380xe7,
8390xd4,
8400xdd,
8410x9f,
8420x12,
8430xe6,
8440x29,
8450x38,
8460xce,
8470x45,
8480xae,
8490x02,
8500xb8,
8510x24,
8520xae,
8530xbd,
8540xe9,
8550x66,
8560x08,
8570x62,
8580xa2,
8590x2c,
8600x2b,
8610x00,
8620xe2,
8630x23,
8640xd9,
8650xc4,
8660x48,
8670xe4,
8680xd3,
8690xac,
8700xbb,
8710x34,
8720xc7,
8730xf0,
8740xe3,
8750x4f,
8760xb9,
8770x30,
8780xea,
8790xa2,
8800x12,
8810xf1,
8820x30,
8830x2c,
8840x36,
8850xde,
8860x48,
8870xf2,
8880xb0,
8890x4c,
8900x43,
8910x3f,
8920x2e,
8930x58,
8940xe4,
8950x20,
8960xe3,
8970x58,
8980xcd,
8990x31,
9000x22,
9010xf0,
9020xa2,
9030x2a,
9040xe6,
9050x19,
9060x90,
9070x55,
9080x86,
9090xf6,
9100x55,
9110x79,
9120xd1,
9130xd7,
9140x46,
9150x2f,
9160xc0,
9170xdc,
9180x99,
9190xe8,
9200xf3,
9210x6a,
9220xdf,
9230x7f,
9240xeb,
9250x24,
9260x4a,
9270x1e,
9280x5a,
9290x75,
9300xde,
9310x2f,
9320x5c,
9330x19,
9340x61,
9350x03,
9360x53,
9370x54,
9380x6a,
9390x3b,
9400x18,
9410x70,
9420xb6,
9430x4f,
9440xf1,
9450x9c,
9460x0a,
9470x59,
9480x9d,
9490x19,
9500x92,
9510x65,
9520x8c,
9530x83,
9540x14,
9550x2d,
9560x44,
9570x8a,
9580x75,
9590xa9,
9600xf5,
9610x90,
9620xd2,
9630x66,
9640x4e,
9650xfa,
9660x69,
9670x0f,
9680x5b,
9690x0b,
9700x98,
9710x65,
9720xc8,
9730x11,
9740x42,
9750x59,
9760x7f,
9770xdd,
9780x1b,
9790x75,
9800x17,
9810x31,
9820x4c,
9830x75,
9840x58,
9850xeb,
9860x58,
9870x63,
9880x7d,
9890xf2,
9900xa6,
9910xc2,
9920x6e,
9930xb7,
9940x3f,
9950x3e,
9960x5e,
9970x47,
9980xad,
9990xb7,
10000x04,
10010xe8,
10020x05,
10030xf8,
10040xb2,
10050xcf,
10060x19,
10070xf3,
10080xd2,
10090x85,
10100xfe,
10110x3e,
10120x3e,
10130xb1,
10140x62,
10150x08,
10160x2c,
10170x10,
10180x07,
10190x0d,
10200x73,
10210x90,
10220x17,
10230xfa,
10240x9b,
10250x56,
10260x02,
10270x75,
10280xf9,
10290x51,
10300xe0,
10310xe9,
10320x1a,
10330x7b,
10340x9f,
10350xb3,
10360xf3,
10370x98,
10380xb8,
10390x1c,
10400x9c,
10410xe1,
10420xd5,
10430x35,
10440xae,
10450xc8,
10460x60,
10470x48,
10480x11,
10490x09,
10500x94,
10510x6b,
10520xd0,
10530x8b,
10540x15,
10550xbc,
10560x05,
10570x68,
10580xd3,
10590x54,
10600x8a,
10610x51,
10620x39,
10630x5c,
10640x42,
10650x76,
10660xce,
10670xd8,
10680xad,
10690x89,
10700x30,
10710xc9,
10720x05,
10730x1c,
10740xcc,
10750x94,
10760x3f,
10770x0f,
10780x90,
10790x6f,
10800x72,
10810x2d,
10820x85,
10830x64,
10840x9a,
10850xb9,
10860x23,
10870xf9,
10880x0b,
10890xc3,
10900x7c,
10910x39,
10920x0f,
10930x97,
10940x07,
10950x97,
10960xda,
10970x58,
10980x48,
10990x33,
11000x05,
11010x23,
11020xb8,
11030x82,
11040xe8,
11050xd3,
11060x53,
11070x89,
11080xaf,
11090x33,
11100x80,
11110x22,
11120x84,
11130x0c,
11140x95,
11150x5c,
11160x67,
11170xb8,
11180x77,
11190x0c,
11200x5c,
11210xa2,
11220x5f,
11230x3d,
11240x58,
11250x0f,
11260x27,
11270xf3,
11280x2f,
11290xae,
11300x48,
11310xbd,
11320x0b,
11330x6f,
11340x54,
11350xfb,
11360x67,
11370x4c,
11380xea,
11390x32,
11400x27,
11410xf1,
11420xfa,
11430xe2,
11440xb0,
11450xec,
11460x0b,
11470x15,
11480xb4,
11490x70,
11500xf6,
11510x5c,
11520xdd,
11530x71,
11540x60,
11550xc3,
11560xc1,
11570xa8,
11580x32,
11590x65,
11600xac,
11610x7a,
11620x77,
11630x41,
11640xe5,
11650xa9,
11660x6b,
11670x11,
11680x81,
11690xfa,
11700x34,
11710x8d,
11720xfb,
11730xc1,
11740x80,
11750x6e,
11760xc4,
11770x60,
11780x30,
11790x07,
11800xd4,
11810x8b,
11820x67,
11830xbd,
11840xaa,
11850x8c,
11860x9c,
11870x64,
11880xac,
11890xdb,
11900x0b,
11910x24,
11920x8b,
11930x63,
11940x6f,
11950xe6,
11960xbc,
11970xe7,
11980x33,
11990xa4,
12000x4a,
12010x4c,
12020xa7,
12030x9f,
12040x43,
12050x53,
12060xd2,
12070xbb,
12080x8f,
12090x43,
12100xc7,
12110x3d,
12120x78,
12130x68,
12140x3f,
12150xa5,
12160x3d,
12170xca,
12180x69,
12190x84,
12200xa6,
12210x97,
12220x2d,
12230xc0,
12240x7d,
12250x31,
12260x34,
12270x55,
12280x1d,
12290x07,
12300xb1,
12310x5f,
12320x40,
12330x5c,
12340x93,
12350xb0,
12360xbc,
12370x7c,
12380xb0,
12390xbc,
12400xe7,
12410x12,
12420xee,
12430x6b,
12440x2b,
12450xd3,
12460x4d,
12470x67,
12480x70,
12490x3a,
12500x9a,
12510xf2,
12520x3c,
12530x7c,
12540x81,
12550xfa,
12560xd7,
12570xd9,
12580x90,
12590x91,
12600x81,
12610xb8,
12620xb1,
12630xf3,
12640x48,
12650x6a,
12660x26,
12670x4f,
12680x0c,
12690xce,
12700xb0,
12710x9e,
12720xfd,
12730x4a,
12740x3a,
12750xaf,
12760xac,
12770x5b,
12780x3f,
12790xbf,
12800x44,
12810x5a,
12820xa3,
12830x19,
12840x1e,
12850x4b,
12860xe7,
12870x36,
12880x6a,
12890xd7,
12900x20,
12910xae,
12920xd7,
12930x7d,
12940x3b,
12950xe7,
12960xff,
12970x3a,
12980x86,
12990x2e,
13000xd0,
13010x4a,
13020x3e,
13030xaf,
13040x9f,
13050x8e,
13060x01,
13070xbf,
13080xf8,
13090x4f,
13100xc1,
13110xe8,
13120x6f,
13130x74,
13140xe1,
13150x45,
13160xd3,
13170xf7,
13180x04,
13190x6a,
13200x4b,
13210x9d,
13220xec,
13230x33,
13240x27,
13250x76,
13260xd7,
13270xc5,
13280xe1,
13290xb0,
13300x3b,
13310x0e,
13320x23,
13330xec,
13340xf0,
13350x86,
13360xd2,
13370x1a,
13380xbf,
13390x3d,
13400x04,
13410x62,
13420xb3,
13430x6c,
13440xb2,
13450xeb,
13460x17,
13470x05,
13480xa6,
13490x0a,
13500x8a,
13510x7e,
13520x83,
13530x1c,
13540xb6,
13550x37,
13560x09,
13570xc6,
13580x0b,
13590x70,
13600x3c,
13610xb5,
13620x93,
13630x81,
13640xd8,
13650x93,
13660xa0,
13670x5f,
13680x1e,
13690x08,
13700xe2,
13710xc6,
13720xe5,
13730xc9,
13740x72,
13750xf1,
13760xf1,
13770xc1,
13780xed,
13790xd5,
13800x58,
13810x93,
13820x83,
13830xf8,
13840x65,
13850x67,
13860x2e,
13870x0d,
13880xa9,
13890xf1,
13900x64,
13910x12,
13920xe6,
13930x4c,
13940xea,
13950x15,
13960x3f,
13970x8c,
13980x1a,
13990xb6,
14000xbf,
14010xf6,
14020xb9,
14030x52,
14040x35,
14050x09,
14060xb0,
14070xe6,
14080xf7,
14090xcd,
14100xf1,
14110xa5,
14120xaa,
14130x81,
14140xd1,
14150x81,
14160x6f,
14170xb4,
14180xa9,
14190x66,
14200x1f,
14210xfc,
14220x48,
14230xc0,
14240xb6,
14250xd1,
14260x8b,
14270x06,
14280x2f,
14290xf6,
14300xef,
14310x1f,
14320x0a,
14330xe6,
14340xce,
14350x3a,
14360x4a,
14370x55,
14380xbf,
14390x6d,
14400xf9,
14410x4d,
14420xd4,
14430x08,
14440x45,
14450x4b,
14460xc3,
14470x66,
14480x19,
14490x92,
14500x10,
14510xe1,
14520x17,
14530x8e,
14540x28,
14550x91,
14560x16,
14570xbf,
14580x3c,
14590xee,
14600xa3,
14610xa6,
14620x99,
14630x92,
14640x10,
14650xe1,
14660xf6,
14670xcc,
14680xac,
14690xb8,
14700x65,
14710x0b,
14720x43,
14730x66,
14740xf8,
14750xe3,
14760xe5,
14770x3f,
14780x24,
14790x89,
14800x47,
14810x5d,
14820x78,
14830x43,
14840xd0,
14850x61,
14860x17,
14870xbd,
14880x5b,
14890x64,
14900x54,
14910x08,
14920x45,
14930x59,
14940x93,
14950xf6,
14960x95,
14970x8a,
14980x41,
14990x51,
15000x62,
15010x4b,
15020x51,
15030x02,
15040x30,
15050x73,
15060xc7,
15070x87,
15080xc5,
15090x4b,
15100xa2,
15110x97,
15120x0f,
15130xe8,
15140x46,
15150x5f,
15160x7e,
15170x2a,
15180xe1,
15190x30,
15200x20,
15210xb0,
15220xfa,
15230xe7,
15240xce,
15250x61,
15260x42,
15270x57,
15280x6e,
15290x21,
15300xf3,
15310x7a,
15320xec,
15330xe3,
15340x25,
15350xc7,
15360x25,
15370xf3,
15380x67,
15390xa7,
15400x57,
15410x40,
15420x00,
15430x02,
15440xcf,
15450x1c,
15460x80,
15470x77,
15480x67,
15490xbd,
15500x70,
15510xa1,
15520x19,
15530x92,
15540x31,
15550x75,
15560x93,
15570x27,
15580x27,
15590xb6,
15600x82,
15610xe4,
15620xeb,
15630x1d,
15640x78,
15650x48,
15660xe7,
15670xa5,
15680x5e,
15690x57,
15700xef,
15710x64,
15720x28,
15730x64,
15740x1b,
15750xf6,
15760x11,
15770xb2,
15780x03,
15790x9d,
15800xb9,
15810x18,
15820x02,
15830x27,
15840xf7,
15850xbe,
15860x9d,
15870x55,
15880xfc,
15890x00,
15900xd2,
15910xc7,
15920xae,
15930xad,
15940x0b,
15950xc5,
15960xe9,
15970x42,
15980x41,
15990x48,
16000xd8,
16010x32,
16020xcf,
16030xf6,
16040x0f,
16050xf5,
16060xbc,
16070x97,
16080xc6,
16090x99,
16100x47,
16110x76,
16120xbd,
16130x89,
16140x06,
16150x0f,
16160x63,
16170x0c,
16180x51,
16190xd4,
16200x5e,
16210xea,
16220x48,
16230xa8,
16240xa2,
16250x56,
16260x1c,
16270x79,
16280x84,
16290x86,
16300x40,
16310x88,
16320x41,
16330x76,
16340x55,
16350xfc,
16360xc2,
16370xd7,
16380xfd,
16390xc9,
16400xc7,
16410x80,
16420x61,
16430x35,
16440xa7,
16450x43,
16460x20,
16470xf7,
16480xeb,
16490x6c,
16500x66,
16510x13,
16520xb0,
16530xec,
16540x02,
16550x75,
16560x3e,
16570x4b,
16580xaf,
16590xb9,
16600x5d,
16610x40,
16620xda,
16630xd6,
16640x6e,
16650x2d,
16660x39,
16670x54,
16680xc2,
16690x95,
16700x35,
16710x54,
16720x25,
16730x72,
16740xe1,
16750x78,
16760xb8,
16770xeb,
16780xc1,
16790x16,
16800x58,
16810x0f,
16820x9c,
16830x9b,
16840xb4,
16850xea,
16860x37,
16870xec,
16880x3b,
16890x11,
16900xba,
16910xd5,
16920x8a,
16930xa9,
16940xe3,
16950x98,
16960x00,
16970x51,
16980x1c,
16990x14,
17000xe0,
17010x40,
17020x96,
17030xe5,
17040xe9,
17050xf2,
17060x21,
17070x22,
17080xb1,
17090x23,
17100x60,
17110x78,
17120xd3,
17130x17,
17140xf8,
17150x7a,
17160xa5,
17170xa8,
17180xba,
17190x20,
17200xd3,
17210x15,
17220x1e,
17230x32,
17240xe4,
17250x5e,
17260x15,
17270x48,
17280xae,
17290xa9,
17300xe5,
17310xb8,
17320x33,
17330xec,
17340xe8,
17350xa2,
17360x42,
17370xac,
17380xbf,
17390x10,
17400x84,
17410x53,
17420x87,
17430x19,
17440xb4,
17450x5f,
17460x76,
17470x4d,
17480x01,
17490x9d,
17500x56,
17510x74,
17520xd9,
17530x5c,
17540x97,
17550xe7,
17560x88,
17570xea,
17580x3a,
17590xbf,
17600xdc,
17610x4c,
17620x33,
17630x8a,
17640x16,
17650xb9,
17660x5b,
17670xfa,
17680xd8,
17690x42,
17700xa7,
17710xbb,
17720x3c,
17730x04,
17740x27,
17750x78,
17760x49,
17770x81,
17780x2a,
17790x5a,
17800x7d,
17810x7c,
17820x23,
17830xa8,
17840xba,
17850xf7,
17860x9a,
17870x9f,
17880xd2,
17890x66,
17900x3e,
17910x38,
17920x3c,
17930x75,
17940xf9,
17950xd1,
17960x30,
17970x26,
17980x30,
17990x6e,
18000x5a,
18010x6e,
18020xdc,
18030x6a,
18040x69,
18050x32,
18060x50,
18070x33,
18080x47,
18090x9e,
18100xa4,
18110xa8,
18120x64,
18130x66,
18140xf0,
18150x8a,
18160xe4,
18170xfd,
18180x27,
18190x6f,
18200x51,
18210x25,
18220x8b,
18230x43,
18240x74,
18250xc9,
18260x8e,
18270xbd,
18280x88,
18290x31,
18300xbe,
18310xec,
18320x65,
18330xd2,
18340xcb,
18350x8d,
18360x5a,
18370x13,
18380x48,
18390x16,
18400x8c,
18410x61,
18420x0b,
18430x11,
18440xf6,
18450xc6,
18460x66,
18470xae,
18480xc3,
18490xcc,
18500x0c,
18510xd2,
18520xe1,
18530x9f,
18540x82,
18550x41,
18560x3f,
18570x56,
18580xf9,
18590x73,
18600xef,
18610xdc,
18620x30,
18630x50,
18640xcf,
18650xb6,
18660x7f,
18670xbc,
18680xd0,
18690xb3,
18700x10,
18710xab,
18720x24,
18730xe4,
18740xec,
18750xad,
18760x18,
18770x8c,
18780x39,
18790x2d,
18800x30,
18810x4c,
18820xc5,
18830x40,
18840x0d,
18850xf6,
18860xac,
18870xd6,
18880x18,
18890x5d,
18900x96,
18910xbf,
18920x5f,
18930x71,
18940x75,
18950x96,
18960x22,
18970x97,
18980x0f,
18990x02,
19000x94,
19010x6e,
19020xa6,
19030xae,
19040x6d,
19050x8f,
19060x1e,
19070xca,
19080x12,
19090x9b,
19100x2a,
19110x1c,
19120xce,
19130xa9,
19140xee,
19150xfd,
19160x12,
19170x8e,
19180xfc,
19190xed,
19200x09,
19210x33,
19220xba,
19230xf4,
19240x1a,
19250x15,
19260xf6,
19270x9d,
19280x87,
19290x16,
19300x43,
19310x7c,
19320x78,
19330x57,
19340xe1,
19350x44,
19360xc9,
19370xeb,
19380x1f,
19390x58,
19400x4d,
19410xc1,
19420x49,
19430x11,
19440x5c,
19450xb2,
19460x11,
19470xa8,
19480x55,
19490x16,
19500xf1,
19510xc6,
19520x50,
19530xe9,
19540x87,
19550x89,
19560xf6,
19570xcf,
19580xd8,
19590x9c,
19600x51,
19610xa7,
19620xbc,
19630x5b,
19640x31,
19650x6d,
19660x4d,
19670x51,
19680xd0,
19690x4c,
19700xbc,
19710x0d,
19720x58,
19730x2d,
19740x7b,
19750x88,
19760x7a,
19770xf9,
19780x8e,
19790xd6,
19800x40,
19810x4d,
19820xbb,
19830xbe,
19840xc4,
19850xe5,
19860x07,
19870xfc,
19880xd9,
19890x7b,
19900x6d,
19910xa6,
19920x42,
19930x57,
19940x8f,
19950x02,
19960x94,
19970x4f,
19980xe4,
19990x2a,
20000x65,
20010xe2,
20020x19,
20030x5a,
20040x50,
20050xe1,
20060x25,
20070x65,
20080x4a,
20090x60,
20100xc2,
20110xcd,
20120xa8,
20130xec,
20140x05,
20150x2e,
20160x87,
20170x7b,
20180x95,
20190xb7,
20200x4f,
20210xa0,
20220x0b,
20230x1b,
20240x4a,
20250x7f,
20260x92,
20270xc8,
20280x90,
20290xee,
20300x89,
20310x1e,
20320x10,
20330xd2,
20340x85,
20350xe4,
20360x9f,
20370x63,
20380xc8,
20390x12,
20400xbb,
20410x4e,
20420xb8,
20430xcf,
20440x0a,
20450xec,
20460x18,
20470x4e,
20480xe6,
20490x7c,
20500xb3,
20510x33,
20520x26,
20530xc7,
20540x1f,
20550xd2,
20560x04,
20570x23,
20580xea,
20590x07,
20600x0c,
20610x5f,
20620x90,
20630xbd,
20640xa7,
20650x6a,
20660x0f,
20670x4a,
20680xd6,
20690x10,
20700x01,
20710x3c,
20720x12,
20730x29,
20740x2e,
20750x96,
20760xc0,
20770x4d,
20780xbb,
20790xbe,
20800xe5,
20810xa7,
20820x83,
20830xd5,
20840x6a,
20850x3c,
20860xe3,
20870x5b,
20880xb8,
20890xf2,
20900x5c,
20910x6d,
20920x1f,
20930xa6,
20940xf3,
20950x12,
20960x24,
20970xf6,
20980xd6,
20990x3b,
21000x10,
21010x14,
21020x09,
21030x07,
21040x82,
21050xe8,
21060x30,
21070x6a,
21080x99,
21090xdc,
21100x95,
21110x01,
21120x9c,
21130xd4,
21140x68,
21150x3b,
21160xca,
21170x98,
21180x12,
21190xab,
21200x77,
21210x25,
21220x15,
21230x7d,
21240x10,
21250x32,
21260x45,
21270x98,
21280xcd,
21290x7a,
21300xdf,
21310x71,
21320x8a,
21330x75,
21340xc1,
21350x1c,
21360xd4,
21370x68,
21380x25,
21390xeb,
21400xbb,
21410x54,
21420x27,
21430x6f,
21440x2a,
21450xf7,
21460xb9,
21470x98,
21480x03,
21490x27,
21500xde,
21510x24,
21520xa8,
21530xbb,
21540x98,
21550xc2,
21560x84,
21570xff,
21580x9b,
21590x51,
21600xd8,
21610x53,
21620x50,
21630xda,
21640xf5,
21650x88,
21660xaa,
21670x87,
21680x2f,
21690xae,
21700xd6,
21710xea,
21720x6b,
21730xde,
21740xc8,
21750xd7,
21760xa7,
21770x28,
21780x65,
21790x81,
21800xe8,
21810xb2,
21820x3b,
21830x1d,
21840x4f,
21850x75,
21860x8f,
21870x9f,
21880x7a,
21890x74,
21900x8e,
21910xc1,
21920x5f,
21930x9a,
21940xa8,
21950x9d,
21960xfa,
21970x03,
21980xa3,
21990x71,
22000x9b,
22010x37,
22020x6d,
22030xd5,
22040x0b,
22050xf5,
22060xe1,
22070xa1,
22080x1b,
22090x01,
22100x6a,
22110xc6,
22120x67,
22130xaa,
22140xea,
22150x2c,
22160x9d,
22170xa4,
22180xd2,
22190x6e,
22200xfc,
22210xde,
22220x2e,
22230x7f,
22240x94,
22250x69,
22260xe5,
22270x4a,
22280xe0,
22290x01,
22300x48,
22310x3c,
22320x6b,
22330xf7,
22340x1e,
22350xb6,
22360x0b,
22370x5f,
22380xf9,
22390x2e,
22400x07,
22410xc5,
22420xe8,
22430xae,
22440x37,
22450x1b,
22460xbc,
22470x3c,
22480xd8,
22490xd5,
22500x0b,
22510x91,
22520x9e,
22530x80,
22540x24,
22550xf5,
22560x06,
22570x0c,
22580x0e,
22590x98,
22600x07,
22610x96,
22620x2d,
22630x19,
22640xdc,
22650x58,
22660x93,
22670xcc,
22680xfb,
22690x4e,
22700xeb,
22710xbd,
22720x0f,
22730xf5,
22740xaf,
22750x01,
22760xfa,
22770xf1,
22780x7c,
22790x43,
22800x8c,
22810xb8,
22820x56,
22830x3e,
22840xbe,
22850x77,
22860x4e,
22870x2b,
22880xf7,
22890xbb,
22900xb7,
22910x45,
22920x47,
22930xcd,
22940xcc,
22950xa6,
22960x4c,
22970x72,
22980x7b,
22990x6a,
23000x2a,
23010x70,
23020x13,
23030x07,
23040xfd,
23050xb8,
23060x9c,
23070x98,
23080x3a,
23090xd8,
23100x23,
23110x67,
23120x5b,
23130x34,
23140xd5,
23150x14,
23160x0c,
23170xab,
23180x77,
23190x1f,
23200xf8,
23210x3d,
23220x5a,
23230x9f,
23240x92,
23250xb7,
23260x2c,
23270xad,
23280x31,
23290xde,
23300x61,
23310x07,
23320xb3,
23330x6b,
23340xf7,
23350x38,
23360x15,
23370x95,
23380x46,
23390x14,
23400x48,
23410x53,
23420x69,
23430x52,
23440x66,
23450x07,
23460x6d,
23470x83,
23480x71,
23490x8a,
23500x67,
23510x25,
23520x20,
23530x0f,
23540xfe,
23550xd7,
23560x02,
23570xd7,
23580x6e,
23590x2c,
23600xd2,
23610x1a,
23620x0a,
23630x5d,
23640xfd,
23650x0f,
23660x74,
23670xe3,
23680xa4,
23690x36,
23700x07,
23710x9a,
23720xdf,
23730xd4,
23740x79,
23750xbf,
23760xef,
23770x59,
23780xc0,
23790x44,
23800x52,
23810x87,
23820x9a,
23830x6e,
23840x1d,
23850x0e,
23860xee,
23870xde,
23880x2e,
23890x1a,
23900xa9,
23910x8f,
23920x3a,
23930xc9,
23940xba,
23950xec,
23960x99,
23970x78,
23980x2d,
23990x55,
24000x6b,
24010x14,
24020xc2,
24030x06,
24040xd5,
24050xfc,
24060x93,
24070x53,
24080x4d,
24090x11,
24100x8c,
24110xf8,
24120xfa,
24130x79,
24140x7c,
24150xa6,
24160x64,
24170xae,
24180x61,
24190xb8,
24200x7b,
24210x94,
24220x56,
24230xa6,
24240x39,
24250x78,
24260x9a,
24270xe5,
24280xc7,
24290xdf,
24300x18,
24310x63,
24320x23,
24330x9c,
24340xfa,
24350x66,
24360xbb,
24370xb7,
24380x5a,
24390x27,
24400x4c,
24410xd1,
24420xa1,
24430x83,
24440x22,
24450xb3,
24460x52,
24470x49,
24480x35,
24490xb0,
24500x22,
24510x83,
24520x59,
24530x12,
24540x00,
24550x16,
24560x98,
24570xdd,
24580xad,
24590xc2,
24600x94,
24610xf9,
24620xd3,
24630x7b,
24640x64,
24650x7f,
24660x44,
24670x3e,
24680x3c,
24690x8b,
24700x9a,
24710x83,
24720x9c,
24730x69,
24740x6b,
24750xe4,
24760xdf,
24770x9f,
24780xed,
24790x54,
24800x1f,
24810xe5,
24820x5d,
24830x7a,
24840x05,
24850x82,
24860xb3,
24870xdd,
24880xef,
24890xfc,
24900x53,
24910x96,
24920xb0,
24930x2c,
24940x5a,
24950xf8,
24960xdf,
24970x9c,
24980x8b,
24990x16,
25000x4e,
25010xdf,
25020xda,
25030x4d,
25040x09,
25050x09,
25060x69,
25070x50,
25080x03,
25090x65,
25100xd8,
25110x73,
25120x70,
25130xe8,
25140x86,
25150xbf,
25160xbb,
25170x35,
25180xce,
25190xb2,
25200x46,
25210xcb,
25220x02,
25230x00,
25240x5b,
25250xb4,
25260xe2,
25270xc6,
25280x8f,
25290x2f,
25300x98,
25310xaf,
25320x87,
25330x4b,
25340x48,
25350x45,
25360xed,
25370xcc,
25380x1d,
25390xe6,
25400x58,
25410xd6,
25420xf2,
25430x50,
25440x25,
25450x9f,
25460x52,
25470xc7,
25480xcb,
25490x8a,
25500x17,
25510x9d,
25520x5b,
25530xe5,
25540xc8,
25550xd7,
25560x72,
25570xb7,
25580x52,
25590xb2,
25600xc4,
25610x98,
25620xe3,
25630x7a,
25640x17,
25650x3e,
25660xc6,
25670x60,
25680xa7,
25690x97,
25700xb0,
25710xcf,
25720x18,
25730x81,
25740x53,
25750x84,
25760x4c,
25770xd5,
25780x17,
25790x32,
25800x03,
25810x13,
25820x39,
25830x51,
25840x09,
25850x10,
25860xe3,
25870x77,
25880x49,
25890x4f,
25900x62,
25910x01,
25920xbf,
25930x8c,
25940x9a,
25950xe0,
25960x41,
25970x9e,
25980x89,
25990x74,
26000x36,
26010xf9,
26020x96,
26030x86,
26040x2e,
26050x96,
26060x1c,
26070x4a,
26080xb7,
26090x2b,
26100x4a,
26110x97,
26120xbc,
26130x99,
26140x40,
26150xa3,
26160xe0,
26170x3d,
26180xc8,
26190xad,
26200x2f,
26210xdf,
26220x4f,
26230x2c,
26240xc4,
26250x69,
26260x82,
26270x9f,
26280x9b,
26290x81,
26300x0c,
26310x61,
26320x5c,
26330xa5,
26340x9d,
26350x8c,
26360x89,
26370xc0,
26380x2c,
26390xb4,
26400x4a,
26410x33,
26420x4e,
26430xeb,
26440xa2,
26450x56,
26460x40,
26470xc0,
26480xc2,
26490x46,
26500xaf,
26510x6a,
26520xfc,
26530x67,
26540xd1,
26550x80,
26560x5e,
26570xc5,
26580x6d,
26590x84,
26600x43,
26610x27,
26620x3f,
26630x55,
26640x15,
26650x96,
26660x6a,
26670xa0,
26680xa5,
26690xda,
26700xb7,
26710xff,
26720xb7,
26730x75,
26740x6e,
26750x4c,
26760x49,
26770x91,
26780x9d,
26790x22,
26800xa3,
26810x46,
26820xea,
26830xed,
26840x9a,
26850x00,
26860xe2,
26870x32,
26880xc3,
26890xd6,
26900xa9,
26910x71,
26920x20,
26930x55,
26940xa3,
26950x19,
26960xed,
26970xf8,
26980x4f,
26990xa7,
27000x12,
27010x9c,
27020x66,
27030x87,
27040xaf,
27050x4e,
27060xb7,
27070xf0,
27080xdb,
27090xbf,
27100xef,
27110xf0,
27120xf6,
27130xaf,
27140xea,
27150xda,
27160x09,
27170xfe,
27180xde,
27190x38,
27200x5c,
27210xa5,
27220xa2,
27230xdf,
27240x99,
27250x45,
27260xa8,
27270xe4,
27280xe7,
27290x92,
27300xac,
27310x67,
27320xaa,
27330x4f,
27340xbf,
27350x77,
27360x3e,
27370xa2,
27380x40,
27390x49,
27400x22,
27410x4a,
27420x1e,
27430x3b,
27440xaa,
27450x70,
27460x7f,
27470x95,
27480xaf,
27490x37,
27500x4b,
27510xfc,
27520x99,
27530xe2,
27540xe0,
27550xba,
27560xd7,
27570x34,
27580xce,
27590x55,
27600x88,
27610x5b,
27620x84,
27630x1b,
27640x57,
27650xc4,
27660x80,
27670x03,
27680x53,
27690xc9,
27700x2f,
27710x93,
27720x04,
27730x4d,
27740xd5,
27750x96,
27760xe5,
27770x70,
27780xa6,
27790x6e,
27800x63,
27810x5d,
27820x9d,
27830x6c,
27840xdb,
27850x02,
27860x0a,
27870xa9,
27880xda,
27890x8b,
27900x53,
27910xdc,
27920xd9,
27930x9a,
27940xc5,
27950x94,
27960x2c,
27970x91,
27980x92,
27990x2a,
28000xde,
28010xbb,
28020x8b,
28030x13,
28040xb9,
28050x19,
28060x96,
28070x64,
28080xcc,
28090xf2,
28100x64,
28110x39,
28120xb7,
28130x75,
28140x49,
28150xe9,
28160x86,
28170xc2,
28180x86,
28190x62,
28200xd9,
28210x24,
28220xd3,
28230x81,
28240x35,
28250x49,
28260xfc,
28270xa0,
28280xa5,
28290xa0,
28300x93,
28310x05,
28320x64,
28330xb4,
28340x1a,
28350x57,
28360xce,
28370x0c,
28380x90,
28390x02,
28400x27,
28410xc5,
28420x7a,
28430x2b,
28440x5d,
28450xae,
28460x3e,
28470xd5,
28480xdd,
28490x10,
28500x7c,
28510x14,
28520xea,
28530x3a,
28540x08,
28550xac,
28560x72,
28570x4e,
28580x90,
28590x3d,
28600x3b,
28610x7c,
28620x86,
28630x2e,
28640xeb,
28650xd4,
28660x06,
28670x70,
28680xe6,
28690xc7,
28700xfb,
28710x5f,
28720xbd,
28730x18,
28740xf4,
28750x11,
28760xa4,
28770x1a,
28780x93,
28790xc3,
28800xbe,
28810xd9,
28820xfb,
28830x26,
28840x48,
28850x2f,
28860x37,
28870x3c,
28880xd0,
28890x03,
28900x47,
28910x1a,
28920xf7,
28930x62,
28940x19,
28950x24,
28960x5c,
28970xf4,
28980xa8,
28990x92,
29000x20,
29010x7a,
29020xf2,
29030x9e,
29040x2a,
29050xc5,
29060x95,
29070xa2,
29080xfb,
29090xa4,
29100xea,
29110x85,
29120xd8,
29130x56,
29140xb7,
29150x70,
29160xd1,
29170x60,
29180x30,
29190xa5,
29200x30,
29210x82,
29220x70,
29230xdc,
29240x7a,
29250x65,
29260x8a,
29270x36,
29280x3f,
29290x5b,
29300x0c,
29310xae,
29320x54,
29330x7c,
29340xd3,
29350x57,
29360x84,
29370x7b,
29380x3a,
29390x65,
29400x18,
29410x81,
29420xee,
29430x05,
29440x9b,
29450x44,
29460x4d,
29470xb8,
29480xda,
29490xa2,
29500xa1,
29510xc9,
29520x15,
29530xd3,
29540x73,
29550x03,
29560x0e,
29570x43,
29580xe9,
29590x8e,
29600x15,
29610xf9,
29620xbe,
29630xc6,
29640xc5,
29650x8a,
29660xe5,
29670xc0,
29680x1e,
29690xc2,
29700x37,
29710x9e,
29720x2a,
29730x26,
29740xa5,
29750xa0,
29760xbd,
29770x24,
29780x5f,
29790xb9,
29800xc1,
29810xab,
29820x34,
29830x48,
29840xb9,
29850x5d,
29860x98,
29870xb4,
29880x65,
29890x18,
29900xf3,
29910x63,
29920x19,
29930x44,
29940x1b,
29950x11,
29960x16,
29970xff,
29980xdc,
29990xf1,
30000x79,
30010x08,
30020x86,
30030x0f,
30040x52,
30050x98,
30060x73,
30070xc4,
30080x92,
30090x90,
30100x2b,
30110x47,
30120x09,
30130xd0,
30140x43,
30150x6c,
30160x2f,
30170x20,
30180xeb,
30190xdc,
30200xda,
30210xc5,
30220x08,
30230x7b,
30240x94,
30250x42,
30260x30,
30270x6a,
30280xc7,
30290xda,
30300x8c,
30310xc3,
30320x76,
30330xa7,
30340xa5,
30350xcc,
30360x62,
30370x13,
30380x00,
30390x60,
30400x31,
30410x58,
30420x44,
30430x9b,
30440xf5,
30450x64,
30460x14,
30470xf5,
30480x11,
30490xc5,
30500x54,
30510x52,
30520x83,
30530xd4,
30540x73,
30550x01,
30560x16,
30570x0e,
30580xb3,
30590x7a,
30600x29,
30610x69,
30620x35,
30630x56,
30640xd4,
30650xee,
30660x8a,
30670x17,
30680xa2,
30690x99,
30700x24,
30710x9c,
30720xd7,
30730x8f,
30740xdb,
30750x55,
30760xb5,
30770x3e
3078};
diff --git a/drivers/sensors/pimoroni_trackball.c b/drivers/sensors/pimoroni_trackball.c
new file mode 100644
index 000000000..c0ac644f7
--- /dev/null
+++ b/drivers/sensors/pimoroni_trackball.c
@@ -0,0 +1,140 @@
1/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
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 "pimoroni_trackball.h"
18#include "i2c_master.h"
19
20static uint8_t scrolling = 0;
21static int16_t x_offset = 0;
22static int16_t y_offset = 0;
23static int16_t h_offset = 0;
24static int16_t v_offset = 0;
25static float precisionSpeed = 1;
26
27static uint16_t i2c_timeout_timer;
28
29#ifndef PIMORONI_I2C_TIMEOUT
30# define PIMORONI_I2C_TIMEOUT 100
31#endif
32#ifndef I2C_WAITCHECK
33# define I2C_WAITCHECK 1000
34#endif
35#ifndef MOUSE_DEBOUNCE
36# define MOUSE_DEBOUNCE 5
37#endif
38
39void trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) {
40 uint8_t data[] = {0x00, red, green, blue, white};
41 i2c_transmit(TRACKBALL_WRITE, data, sizeof(data), PIMORONI_I2C_TIMEOUT);
42}
43
44int16_t mouse_offset(uint8_t positive, uint8_t negative, int16_t scale) {
45 int16_t offset = (int16_t)positive - (int16_t)negative;
46 int16_t magnitude = (int16_t)(scale * offset * offset * precisionSpeed);
47 return offset < 0 ? -magnitude : magnitude;
48}
49
50void update_member(int8_t* member, int16_t* offset) {
51 if (*offset > 127) {
52 *member = 127;
53 *offset -= 127;
54 } else if (*offset < -127) {
55 *member = -127;
56 *offset += 127;
57 } else {
58 *member = *offset;
59 *offset = 0;
60 }
61}
62
63__attribute__((weak)) void trackball_check_click(bool pressed, report_mouse_t* mouse) {
64 if (pressed) {
65 mouse->buttons |= MOUSE_BTN1;
66 } else {
67 mouse->buttons &= ~MOUSE_BTN1;
68 }
69}
70
71float trackball_get_precision(void) { return precisionSpeed; }
72void trackball_set_precision(float precision) { precisionSpeed = precision; }
73bool trackball_is_scrolling(void) { return scrolling; }
74void trackball_set_scrolling(bool scroll) { scrolling = scroll; }
75
76__attribute__((weak)) void pointing_device_init(void) { i2c_init(); trackball_set_rgbw(0x00, 0x00, 0x00, 0x00); }
77
78void pointing_device_task(void) {
79 static bool debounce;
80 static uint16_t debounce_timer;
81 uint8_t state[5] = {};
82 if (timer_elapsed(i2c_timeout_timer) > I2C_WAITCHECK) {
83 if (i2c_readReg(TRACKBALL_READ, 0x04, state, 5, PIMORONI_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) {
84 if (!state[4] && !debounce) {
85 if (scrolling) {
86#ifdef PIMORONI_TRACKBALL_INVERT_X
87 h_offset += mouse_offset(state[2], state[3], 1);
88#else
89 h_offset -= mouse_offset(state[2], state[3], 1);
90#endif
91#ifdef PIMORONI_TRACKBALL_INVERT_Y
92 v_offset += mouse_offset(state[1], state[0], 1);
93#else
94 v_offset -= mouse_offset(state[1], state[0], 1);
95#endif
96 } else {
97#ifdef PIMORONI_TRACKBALL_INVERT_X
98 x_offset -= mouse_offset(state[2], state[3], 5);
99#else
100 x_offset += mouse_offset(state[2], state[3], 5);
101#endif
102#ifdef PIMORONI_TRACKBALL_INVERT_Y
103 y_offset -= mouse_offset(state[1], state[0], 5);
104#else
105 y_offset += mouse_offset(state[1], state[0], 5);
106#endif
107 }
108 } else {
109 if (state[4]) {
110 debounce = true;
111 debounce_timer = timer_read();
112 }
113 }
114 } else {
115 i2c_timeout_timer = timer_read();
116 }
117 }
118
119 if (timer_elapsed(debounce_timer) > MOUSE_DEBOUNCE) debounce = false;
120
121 report_mouse_t mouse = pointing_device_get_report();
122
123#ifdef PIMORONI_TRACKBALL_CLICK
124 trackball_check_click(state[4] & (1 << 7), &mouse);
125#endif
126
127#ifndef PIMORONI_TRACKBALL_ROTATE
128 update_member(&mouse.x, &x_offset);
129 update_member(&mouse.y, &y_offset);
130 update_member(&mouse.h, &h_offset);
131 update_member(&mouse.v, &v_offset);
132#else
133 update_member(&mouse.x, &y_offset);
134 update_member(&mouse.y, &x_offset);
135 update_member(&mouse.h, &v_offset);
136 update_member(&mouse.v, &h_offset);
137#endif
138 pointing_device_set_report(mouse);
139 pointing_device_send();
140}
diff --git a/drivers/sensors/pimoroni_trackball.h b/drivers/sensors/pimoroni_trackball.h
new file mode 100644
index 000000000..9e3c64c19
--- /dev/null
+++ b/drivers/sensors/pimoroni_trackball.h
@@ -0,0 +1,35 @@
1/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
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 "quantum.h"
20#include "pointing_device.h"
21
22#ifndef TRACKBALL_ADDRESS
23# define TRACKBALL_ADDRESS 0x0A
24#endif
25#define TRACKBALL_WRITE ((TRACKBALL_ADDRESS << 1) | I2C_WRITE)
26#define TRACKBALL_READ (TRACKBALL_ADDRESS << 1)
27
28void trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white);
29void trackball_check_click(bool pressed, report_mouse_t *mouse);
30void trackball_register_button(bool pressed, enum mouse_buttons button);
31
32float trackball_get_precision(void);
33void trackball_set_precision(float precision);
34bool trackball_is_scrolling(void);
35void trackball_set_scrolling(bool scroll);
diff --git a/drivers/sensors/pmw3360.c b/drivers/sensors/pmw3360.c
new file mode 100644
index 000000000..13c5bdea2
--- /dev/null
+++ b/drivers/sensors/pmw3360.c
@@ -0,0 +1,239 @@
1/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
2 * Copyright 2019 Sunjun Kim
3 * Copyright 2020 Ploopy Corporation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "wait.h"
20#include "debug.h"
21#include "print.h"
22#include "pmw3360.h"
23#include "pmw3360_firmware.h"
24
25bool _inBurst = false;
26
27#ifndef PMW_CPI
28# define PMW_CPI 1600
29#endif
30#ifndef PMW_CLOCK_SPEED
31# define PMW_CLOCK_SPEED 70000000
32#endif
33#ifndef SPI_MODE
34# define SPI_MODE 3
35#endif
36#ifndef SPI_DIVISOR
37# define SPI_DIVISOR (F_CPU / PMW_CLOCK_SPEED)
38#endif
39#ifndef ROTATIONAL_TRANSFORM_ANGLE
40# define ROTATIONAL_TRANSFORM_ANGLE 0x00
41#endif
42#ifndef PMW_CS_PIN
43# define PMW_CS_PIN SPI_SS_PIN
44#endif
45
46void print_byte(uint8_t byte) { dprintf("%c%c%c%c%c%c%c%c|", (byte & 0x80 ? '1' : '0'), (byte & 0x40 ? '1' : '0'), (byte & 0x20 ? '1' : '0'), (byte & 0x10 ? '1' : '0'), (byte & 0x08 ? '1' : '0'), (byte & 0x04 ? '1' : '0'), (byte & 0x02 ? '1' : '0'), (byte & 0x01 ? '1' : '0')); }
47
48bool spi_start_adv(void) {
49 bool status = spi_start(PMW_CS_PIN, false, SPI_MODE, SPI_DIVISOR);
50 wait_us(1);
51 return status;
52}
53
54void spi_stop_adv(void) {
55 wait_us(1);
56 spi_stop();
57}
58
59spi_status_t spi_write_adv(uint8_t reg_addr, uint8_t data) {
60 if (reg_addr != REG_Motion_Burst) {
61 _inBurst = false;
62 }
63
64 spi_start_adv();
65 // send address of the register, with MSBit = 1 to indicate it's a write
66 spi_status_t status = spi_write(reg_addr | 0x80);
67 status = spi_write(data);
68
69 // tSCLK-NCS for write operation
70 wait_us(20);
71
72 // tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound
73 wait_us(100);
74 spi_stop();
75 return status;
76}
77
78uint8_t spi_read_adv(uint8_t reg_addr) {
79 spi_start_adv();
80 // send adress of the register, with MSBit = 0 to indicate it's a read
81 spi_write(reg_addr & 0x7f);
82
83 uint8_t data = spi_read();
84
85 // tSCLK-NCS for read operation is 120ns
86 wait_us(1);
87
88 // tSRW/tSRR (=20us) minus tSCLK-NCS
89 wait_us(19);
90
91 spi_stop();
92 return data;
93}
94
95void pmw_set_cpi(uint16_t cpi) {
96 uint8_t cpival = constrain((cpi / 100) - 1, 0, 0x77); // limits to 0--119
97
98 spi_start_adv();
99 spi_write_adv(REG_Config1, cpival);
100 spi_stop();
101}
102
103uint16_t pmw_get_cpi(void) {
104 uint8_t cpival = spi_read_adv(REG_Config1);
105 return (uint16_t)(cpival & 0xFF) * 100;
106}
107
108bool pmw_spi_init(void) {
109 setPinOutput(PMW_CS_PIN);
110
111 spi_init();
112 _inBurst = false;
113
114 spi_stop();
115 spi_start_adv();
116 spi_stop();
117
118 spi_write_adv(REG_Shutdown, 0xb6); // Shutdown first
119 wait_ms(300);
120
121 spi_start_adv();
122 wait_us(40);
123 spi_stop_adv();
124 wait_us(40);
125
126 spi_write_adv(REG_Power_Up_Reset, 0x5a);
127 wait_ms(50);
128
129 spi_read_adv(REG_Motion);
130 spi_read_adv(REG_Delta_X_L);
131 spi_read_adv(REG_Delta_X_H);
132 spi_read_adv(REG_Delta_Y_L);
133 spi_read_adv(REG_Delta_Y_H);
134
135 pmw_upload_firmware();
136
137 spi_stop_adv();
138
139 wait_ms(10);
140 pmw_set_cpi(PMW_CPI);
141
142 wait_ms(1);
143
144 spi_write_adv(REG_Config2, 0x00);
145
146 spi_write_adv(REG_Angle_Tune, constrain(ROTATIONAL_TRANSFORM_ANGLE, -30, 30));
147
148 bool init_success = pmw_check_signature();
149
150 writePinLow(PMW_CS_PIN);
151
152 return init_success;
153}
154
155void pmw_upload_firmware(void) {
156 spi_write_adv(REG_SROM_Enable, 0x1d);
157
158 wait_ms(10);
159
160 spi_write_adv(REG_SROM_Enable, 0x18);
161
162 spi_start_adv();
163 spi_write(REG_SROM_Load_Burst | 0x80);
164 wait_us(15);
165
166 unsigned char c;
167 for (int i = 0; i < firmware_length; i++) {
168 c = (unsigned char)pgm_read_byte(firmware_data + i);
169 spi_write(c);
170 wait_us(15);
171 }
172 wait_us(200);
173
174 spi_read_adv(REG_SROM_ID);
175
176 spi_write_adv(REG_Config2, 0x00);
177
178 spi_stop();
179 wait_ms(10);
180}
181
182bool pmw_check_signature(void) {
183 uint8_t pid = spi_read_adv(REG_Product_ID);
184 uint8_t iv_pid = spi_read_adv(REG_Inverse_Product_ID);
185 uint8_t SROM_ver = spi_read_adv(REG_SROM_ID);
186 return (pid == 0x42 && iv_pid == 0xBD && SROM_ver == 0x04); // signature for SROM 0x04
187}
188
189report_pmw_t pmw_read_burst(void) {
190 if (!_inBurst) {
191 dprintf("burst on");
192 spi_write_adv(REG_Motion_Burst, 0x00);
193 _inBurst = true;
194 }
195
196 spi_start_adv();
197 spi_write(REG_Motion_Burst);
198 wait_us(35); // waits for tSRAD
199
200 report_pmw_t data;
201 data.motion = 0;
202 data.dx = 0;
203 data.mdx = 0;
204 data.dy = 0;
205 data.mdx = 0;
206
207 data.motion = spi_read();
208 spi_write(0x00); // skip Observation
209 data.dx = spi_read();
210 data.mdx = spi_read();
211 data.dy = spi_read();
212 data.mdy = spi_read();
213
214 spi_stop();
215
216 if (debug_mouse) {
217 print_byte(data.motion);
218 print_byte(data.dx);
219 print_byte(data.mdx);
220 print_byte(data.dy);
221 print_byte(data.mdy);
222 dprintf("\n");
223 }
224
225 data.isMotion = (data.motion & 0x80) != 0;
226 data.isOnSurface = (data.motion & 0x08) == 0;
227 data.dx |= (data.mdx << 8);
228 data.dx = data.dx * -1;
229 data.dy |= (data.mdy << 8);
230 data.dy = data.dy * -1;
231
232 spi_stop();
233
234 if (data.motion & 0b111) { // panic recovery, sometimes burst mode works weird.
235 _inBurst = false;
236 }
237
238 return data;
239}
diff --git a/drivers/sensors/pmw3360.h b/drivers/sensors/pmw3360.h
new file mode 100644
index 000000000..d5b174179
--- /dev/null
+++ b/drivers/sensors/pmw3360.h
@@ -0,0 +1,104 @@
1/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
2 * Copyright 2019 Sunjun Kim
3 * Copyright 2020 Ploopy Corporation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include "spi_master.h"
22
23// Registers
24#define REG_Product_ID 0x00
25#define REG_Revision_ID 0x01
26#define REG_Motion 0x02
27#define REG_Delta_X_L 0x03
28#define REG_Delta_X_H 0x04
29#define REG_Delta_Y_L 0x05
30#define REG_Delta_Y_H 0x06
31#define REG_SQUAL 0x07
32#define REG_Raw_Data_Sum 0x08
33#define REG_Maximum_Raw_data 0x09
34#define REG_Minimum_Raw_data 0x0A
35#define REG_Shutter_Lower 0x0B
36#define REG_Shutter_Upper 0x0C
37#define REG_Control 0x0D
38#define REG_Config1 0x0F
39#define REG_Config2 0x10
40#define REG_Angle_Tune 0x11
41#define REG_Frame_Capture 0x12
42#define REG_SROM_Enable 0x13
43#define REG_Run_Downshift 0x14
44#define REG_Rest1_Rate_Lower 0x15
45#define REG_Rest1_Rate_Upper 0x16
46#define REG_Rest1_Downshift 0x17
47#define REG_Rest2_Rate_Lower 0x18
48#define REG_Rest2_Rate_Upper 0x19
49#define REG_Rest2_Downshift 0x1A
50#define REG_Rest3_Rate_Lower 0x1B
51#define REG_Rest3_Rate_Upper 0x1C
52#define REG_Observation 0x24
53#define REG_Data_Out_Lower 0x25
54#define REG_Data_Out_Upper 0x26
55#define REG_Raw_Data_Dump 0x29
56#define REG_SROM_ID 0x2A
57#define REG_Min_SQ_Run 0x2B
58#define REG_Raw_Data_Threshold 0x2C
59#define REG_Config5 0x2F
60#define REG_Power_Up_Reset 0x3A
61#define REG_Shutdown 0x3B
62#define REG_Inverse_Product_ID 0x3F
63#define REG_LiftCutoff_Tune3 0x41
64#define REG_Angle_Snap 0x42
65#define REG_LiftCutoff_Tune1 0x4A
66#define REG_Motion_Burst 0x50
67#define REG_LiftCutoff_Tune_Timeout 0x58
68#define REG_LiftCutoff_Tune_Min_Length 0x5A
69#define REG_SROM_Load_Burst 0x62
70#define REG_Lift_Config 0x63
71#define REG_Raw_Data_Burst 0x64
72#define REG_LiftCutoff_Tune2 0x65
73
74#ifdef CONSOLE_ENABLE
75void print_byte(uint8_t byte);
76#endif
77
78typedef struct {
79 int8_t motion;
80 bool isMotion; // True if a motion is detected.
81 bool isOnSurface; // True when a chip is on a surface
82 int16_t dx; // displacement on x directions. Unit: Count. (CPI * Count = Inch value)
83 int8_t mdx;
84 int16_t dy; // displacement on y directions.
85 int8_t mdy;
86} report_pmw_t;
87
88
89
90bool spi_start_adv(void);
91void spi_stop_adv(void);
92spi_status_t spi_write_adv(uint8_t reg_addr, uint8_t data);
93uint8_t spi_read_adv(uint8_t reg_addr);
94bool pmw_spi_init(void);
95void pmw_set_cpi(uint16_t cpi);
96uint16_t pmw_get_cpi(void);
97void pmw_upload_firmware(void);
98bool pmw_check_signature(void);
99report_pmw_t pmw_read_burst(void);
100
101
102#define degToRad(angleInDegrees) ((angleInDegrees)*M_PI / 180.0)
103#define radToDeg(angleInRadians) ((angleInRadians)*180.0 / M_PI)
104#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
diff --git a/drivers/sensors/pmw3360_firmware.h b/drivers/sensors/pmw3360_firmware.h
new file mode 100644
index 000000000..cca5a6a4d
--- /dev/null
+++ b/drivers/sensors/pmw3360_firmware.h
@@ -0,0 +1,300 @@
1/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
2 * Copyright 2019 Sunjun Kim
3 * Copyright 2020 Ploopy Corporation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21// clang-format off
22// Firmware Blob foor PMW3360
23const uint16_t firmware_length = 4094;
24// clang-format off
25const uint8_t firmware_data[] PROGMEM = { // SROM 0x04
260x01, 0x04, 0x8e, 0x96, 0x6e, 0x77, 0x3e, 0xfe, 0x7e, 0x5f, 0x1d, 0xb8, 0xf2, 0x66, 0x4e,
270xff, 0x5d, 0x19, 0xb0, 0xc2, 0x04, 0x69, 0x54, 0x2a, 0xd6, 0x2e, 0xbf, 0xdd, 0x19, 0xb0,
280xc3, 0xe5, 0x29, 0xb1, 0xe0, 0x23, 0xa5, 0xa9, 0xb1, 0xc1, 0x00, 0x82, 0x67, 0x4c, 0x1a,
290x97, 0x8d, 0x79, 0x51, 0x20, 0xc7, 0x06, 0x8e, 0x7c, 0x7c, 0x7a, 0x76, 0x4f, 0xfd, 0x59,
300x30, 0xe2, 0x46, 0x0e, 0x9e, 0xbe, 0xdf, 0x1d, 0x99, 0x91, 0xa0, 0xa5, 0xa1, 0xa9, 0xd0,
310x22, 0xc6, 0xef, 0x5c, 0x1b, 0x95, 0x89, 0x90, 0xa2, 0xa7, 0xcc, 0xfb, 0x55, 0x28, 0xb3,
320xe4, 0x4a, 0xf7, 0x6c, 0x3b, 0xf4, 0x6a, 0x56, 0x2e, 0xde, 0x1f, 0x9d, 0xb8, 0xd3, 0x05,
330x88, 0x92, 0xa6, 0xce, 0x1e, 0xbe, 0xdf, 0x1d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x5c, 0x07,
340x11, 0x5d, 0x98, 0x0b, 0x9d, 0x94, 0x97, 0xee, 0x4e, 0x45, 0x33, 0x6b, 0x44, 0xc7, 0x29,
350x56, 0x27, 0x30, 0xc6, 0xa7, 0xd5, 0xf2, 0x56, 0xdf, 0xb4, 0x38, 0x62, 0xcb, 0xa0, 0xb6,
360xe3, 0x0f, 0x84, 0x06, 0x24, 0x05, 0x65, 0x6f, 0x76, 0x89, 0xb5, 0x77, 0x41, 0x27, 0x82,
370x66, 0x65, 0x82, 0xcc, 0xd5, 0xe6, 0x20, 0xd5, 0x27, 0x17, 0xc5, 0xf8, 0x03, 0x23, 0x7c,
380x5f, 0x64, 0xa5, 0x1d, 0xc1, 0xd6, 0x36, 0xcb, 0x4c, 0xd4, 0xdb, 0x66, 0xd7, 0x8b, 0xb1,
390x99, 0x7e, 0x6f, 0x4c, 0x36, 0x40, 0x06, 0xd6, 0xeb, 0xd7, 0xa2, 0xe4, 0xf4, 0x95, 0x51,
400x5a, 0x54, 0x96, 0xd5, 0x53, 0x44, 0xd7, 0x8c, 0xe0, 0xb9, 0x40, 0x68, 0xd2, 0x18, 0xe9,
410xdd, 0x9a, 0x23, 0x92, 0x48, 0xee, 0x7f, 0x43, 0xaf, 0xea, 0x77, 0x38, 0x84, 0x8c, 0x0a,
420x72, 0xaf, 0x69, 0xf8, 0xdd, 0xf1, 0x24, 0x83, 0xa3, 0xf8, 0x4a, 0xbf, 0xf5, 0x94, 0x13,
430xdb, 0xbb, 0xd8, 0xb4, 0xb3, 0xa0, 0xfb, 0x45, 0x50, 0x60, 0x30, 0x59, 0x12, 0x31, 0x71,
440xa2, 0xd3, 0x13, 0xe7, 0xfa, 0xe7, 0xce, 0x0f, 0x63, 0x15, 0x0b, 0x6b, 0x94, 0xbb, 0x37,
450x83, 0x26, 0x05, 0x9d, 0xfb, 0x46, 0x92, 0xfc, 0x0a, 0x15, 0xd1, 0x0d, 0x73, 0x92, 0xd6,
460x8c, 0x1b, 0x8c, 0xb8, 0x55, 0x8a, 0xce, 0xbd, 0xfe, 0x8e, 0xfc, 0xed, 0x09, 0x12, 0x83,
470x91, 0x82, 0x51, 0x31, 0x23, 0xfb, 0xb4, 0x0c, 0x76, 0xad, 0x7c, 0xd9, 0xb4, 0x4b, 0xb2,
480x67, 0x14, 0x09, 0x9c, 0x7f, 0x0c, 0x18, 0xba, 0x3b, 0xd6, 0x8e, 0x14, 0x2a, 0xe4, 0x1b,
490x52, 0x9f, 0x2b, 0x7d, 0xe1, 0xfb, 0x6a, 0x33, 0x02, 0xfa, 0xac, 0x5a, 0xf2, 0x3e, 0x88,
500x7e, 0xae, 0xd1, 0xf3, 0x78, 0xe8, 0x05, 0xd1, 0xe3, 0xdc, 0x21, 0xf6, 0xe1, 0x9a, 0xbd,
510x17, 0x0e, 0xd9, 0x46, 0x9b, 0x88, 0x03, 0xea, 0xf6, 0x66, 0xbe, 0x0e, 0x1b, 0x50, 0x49,
520x96, 0x40, 0x97, 0xf1, 0xf1, 0xe4, 0x80, 0xa6, 0x6e, 0xe8, 0x77, 0x34, 0xbf, 0x29, 0x40,
530x44, 0xc2, 0xff, 0x4e, 0x98, 0xd3, 0x9c, 0xa3, 0x32, 0x2b, 0x76, 0x51, 0x04, 0x09, 0xe7,
540xa9, 0xd1, 0xa6, 0x32, 0xb1, 0x23, 0x53, 0xe2, 0x47, 0xab, 0xd6, 0xf5, 0x69, 0x5c, 0x3e,
550x5f, 0xfa, 0xae, 0x45, 0x20, 0xe5, 0xd2, 0x44, 0xff, 0x39, 0x32, 0x6d, 0xfd, 0x27, 0x57,
560x5c, 0xfd, 0xf0, 0xde, 0xc1, 0xb5, 0x99, 0xe5, 0xf5, 0x1c, 0x77, 0x01, 0x75, 0xc5, 0x6d,
570x58, 0x92, 0xf2, 0xb2, 0x47, 0x00, 0x01, 0x26, 0x96, 0x7a, 0x30, 0xff, 0xb7, 0xf0, 0xef,
580x77, 0xc1, 0x8a, 0x5d, 0xdc, 0xc0, 0xd1, 0x29, 0x30, 0x1e, 0x77, 0x38, 0x7a, 0x94, 0xf1,
590xb8, 0x7a, 0x7e, 0xef, 0xa4, 0xd1, 0xac, 0x31, 0x4a, 0xf2, 0x5d, 0x64, 0x3d, 0xb2, 0xe2,
600xf0, 0x08, 0x99, 0xfc, 0x70, 0xee, 0x24, 0xa7, 0x7e, 0xee, 0x1e, 0x20, 0x69, 0x7d, 0x44,
610xbf, 0x87, 0x42, 0xdf, 0x88, 0x3b, 0x0c, 0xda, 0x42, 0xc9, 0x04, 0xf9, 0x45, 0x50, 0xfc,
620x83, 0x8f, 0x11, 0x6a, 0x72, 0xbc, 0x99, 0x95, 0xf0, 0xac, 0x3d, 0xa7, 0x3b, 0xcd, 0x1c,
630xe2, 0x88, 0x79, 0x37, 0x11, 0x5f, 0x39, 0x89, 0x95, 0x0a, 0x16, 0x84, 0x7a, 0xf6, 0x8a,
640xa4, 0x28, 0xe4, 0xed, 0x83, 0x80, 0x3b, 0xb1, 0x23, 0xa5, 0x03, 0x10, 0xf4, 0x66, 0xea,
650xbb, 0x0c, 0x0f, 0xc5, 0xec, 0x6c, 0x69, 0xc5, 0xd3, 0x24, 0xab, 0xd4, 0x2a, 0xb7, 0x99,
660x88, 0x76, 0x08, 0xa0, 0xa8, 0x95, 0x7c, 0xd8, 0x38, 0x6d, 0xcd, 0x59, 0x02, 0x51, 0x4b,
670xf1, 0xb5, 0x2b, 0x50, 0xe3, 0xb6, 0xbd, 0xd0, 0x72, 0xcf, 0x9e, 0xfd, 0x6e, 0xbb, 0x44,
680xc8, 0x24, 0x8a, 0x77, 0x18, 0x8a, 0x13, 0x06, 0xef, 0x97, 0x7d, 0xfa, 0x81, 0xf0, 0x31,
690xe6, 0xfa, 0x77, 0xed, 0x31, 0x06, 0x31, 0x5b, 0x54, 0x8a, 0x9f, 0x30, 0x68, 0xdb, 0xe2,
700x40, 0xf8, 0x4e, 0x73, 0xfa, 0xab, 0x74, 0x8b, 0x10, 0x58, 0x13, 0xdc, 0xd2, 0xe6, 0x78,
710xd1, 0x32, 0x2e, 0x8a, 0x9f, 0x2c, 0x58, 0x06, 0x48, 0x27, 0xc5, 0xa9, 0x5e, 0x81, 0x47,
720x89, 0x46, 0x21, 0x91, 0x03, 0x70, 0xa4, 0x3e, 0x88, 0x9c, 0xda, 0x33, 0x0a, 0xce, 0xbc,
730x8b, 0x8e, 0xcf, 0x9f, 0xd3, 0x71, 0x80, 0x43, 0xcf, 0x6b, 0xa9, 0x51, 0x83, 0x76, 0x30,
740x82, 0xc5, 0x6a, 0x85, 0x39, 0x11, 0x50, 0x1a, 0x82, 0xdc, 0x1e, 0x1c, 0xd5, 0x7d, 0xa9,
750x71, 0x99, 0x33, 0x47, 0x19, 0x97, 0xb3, 0x5a, 0xb1, 0xdf, 0xed, 0xa4, 0xf2, 0xe6, 0x26,
760x84, 0xa2, 0x28, 0x9a, 0x9e, 0xdf, 0xa6, 0x6a, 0xf4, 0xd6, 0xfc, 0x2e, 0x5b, 0x9d, 0x1a,
770x2a, 0x27, 0x68, 0xfb, 0xc1, 0x83, 0x21, 0x4b, 0x90, 0xe0, 0x36, 0xdd, 0x5b, 0x31, 0x42,
780x55, 0xa0, 0x13, 0xf7, 0xd0, 0x89, 0x53, 0x71, 0x99, 0x57, 0x09, 0x29, 0xc5, 0xf3, 0x21,
790xf8, 0x37, 0x2f, 0x40, 0xf3, 0xd4, 0xaf, 0x16, 0x08, 0x36, 0x02, 0xfc, 0x77, 0xc5, 0x8b,
800x04, 0x90, 0x56, 0xb9, 0xc9, 0x67, 0x9a, 0x99, 0xe8, 0x00, 0xd3, 0x86, 0xff, 0x97, 0x2d,
810x08, 0xe9, 0xb7, 0xb3, 0x91, 0xbc, 0xdf, 0x45, 0xc6, 0xed, 0x0f, 0x8c, 0x4c, 0x1e, 0xe6,
820x5b, 0x6e, 0x38, 0x30, 0xe4, 0xaa, 0xe3, 0x95, 0xde, 0xb9, 0xe4, 0x9a, 0xf5, 0xb2, 0x55,
830x9a, 0x87, 0x9b, 0xf6, 0x6a, 0xb2, 0xf2, 0x77, 0x9a, 0x31, 0xf4, 0x7a, 0x31, 0xd1, 0x1d,
840x04, 0xc0, 0x7c, 0x32, 0xa2, 0x9e, 0x9a, 0xf5, 0x62, 0xf8, 0x27, 0x8d, 0xbf, 0x51, 0xff,
850xd3, 0xdf, 0x64, 0x37, 0x3f, 0x2a, 0x6f, 0x76, 0x3a, 0x7d, 0x77, 0x06, 0x9e, 0x77, 0x7f,
860x5e, 0xeb, 0x32, 0x51, 0xf9, 0x16, 0x66, 0x9a, 0x09, 0xf3, 0xb0, 0x08, 0xa4, 0x70, 0x96,
870x46, 0x30, 0xff, 0xda, 0x4f, 0xe9, 0x1b, 0xed, 0x8d, 0xf8, 0x74, 0x1f, 0x31, 0x92, 0xb3,
880x73, 0x17, 0x36, 0xdb, 0x91, 0x30, 0xd6, 0x88, 0x55, 0x6b, 0x34, 0x77, 0x87, 0x7a, 0xe7,
890xee, 0x06, 0xc6, 0x1c, 0x8c, 0x19, 0x0c, 0x48, 0x46, 0x23, 0x5e, 0x9c, 0x07, 0x5c, 0xbf,
900xb4, 0x7e, 0xd6, 0x4f, 0x74, 0x9c, 0xe2, 0xc5, 0x50, 0x8b, 0xc5, 0x8b, 0x15, 0x90, 0x60,
910x62, 0x57, 0x29, 0xd0, 0x13, 0x43, 0xa1, 0x80, 0x88, 0x91, 0x00, 0x44, 0xc7, 0x4d, 0x19,
920x86, 0xcc, 0x2f, 0x2a, 0x75, 0x5a, 0xfc, 0xeb, 0x97, 0x2a, 0x70, 0xe3, 0x78, 0xd8, 0x91,
930xb0, 0x4f, 0x99, 0x07, 0xa3, 0x95, 0xea, 0x24, 0x21, 0xd5, 0xde, 0x51, 0x20, 0x93, 0x27,
940x0a, 0x30, 0x73, 0xa8, 0xff, 0x8a, 0x97, 0xe9, 0xa7, 0x6a, 0x8e, 0x0d, 0xe8, 0xf0, 0xdf,
950xec, 0xea, 0xb4, 0x6c, 0x1d, 0x39, 0x2a, 0x62, 0x2d, 0x3d, 0x5a, 0x8b, 0x65, 0xf8, 0x90,
960x05, 0x2e, 0x7e, 0x91, 0x2c, 0x78, 0xef, 0x8e, 0x7a, 0xc1, 0x2f, 0xac, 0x78, 0xee, 0xaf,
970x28, 0x45, 0x06, 0x4c, 0x26, 0xaf, 0x3b, 0xa2, 0xdb, 0xa3, 0x93, 0x06, 0xb5, 0x3c, 0xa5,
980xd8, 0xee, 0x8f, 0xaf, 0x25, 0xcc, 0x3f, 0x85, 0x68, 0x48, 0xa9, 0x62, 0xcc, 0x97, 0x8f,
990x7f, 0x2a, 0xea, 0xe0, 0x15, 0x0a, 0xad, 0x62, 0x07, 0xbd, 0x45, 0xf8, 0x41, 0xd8, 0x36,
1000xcb, 0x4c, 0xdb, 0x6e, 0xe6, 0x3a, 0xe7, 0xda, 0x15, 0xe9, 0x29, 0x1e, 0x12, 0x10, 0xa0,
1010x14, 0x2c, 0x0e, 0x3d, 0xf4, 0xbf, 0x39, 0x41, 0x92, 0x75, 0x0b, 0x25, 0x7b, 0xa3, 0xce,
1020x39, 0x9c, 0x15, 0x64, 0xc8, 0xfa, 0x3d, 0xef, 0x73, 0x27, 0xfe, 0x26, 0x2e, 0xce, 0xda,
1030x6e, 0xfd, 0x71, 0x8e, 0xdd, 0xfe, 0x76, 0xee, 0xdc, 0x12, 0x5c, 0x02, 0xc5, 0x3a, 0x4e,
1040x4e, 0x4f, 0xbf, 0xca, 0x40, 0x15, 0xc7, 0x6e, 0x8d, 0x41, 0xf1, 0x10, 0xe0, 0x4f, 0x7e,
1050x97, 0x7f, 0x1c, 0xae, 0x47, 0x8e, 0x6b, 0xb1, 0x25, 0x31, 0xb0, 0x73, 0xc7, 0x1b, 0x97,
1060x79, 0xf9, 0x80, 0xd3, 0x66, 0x22, 0x30, 0x07, 0x74, 0x1e, 0xe4, 0xd0, 0x80, 0x21, 0xd6,
1070xee, 0x6b, 0x6c, 0x4f, 0xbf, 0xf5, 0xb7, 0xd9, 0x09, 0x87, 0x2f, 0xa9, 0x14, 0xbe, 0x27,
1080xd9, 0x72, 0x50, 0x01, 0xd4, 0x13, 0x73, 0xa6, 0xa7, 0x51, 0x02, 0x75, 0x25, 0xe1, 0xb3,
1090x45, 0x34, 0x7d, 0xa8, 0x8e, 0xeb, 0xf3, 0x16, 0x49, 0xcb, 0x4f, 0x8c, 0xa1, 0xb9, 0x36,
1100x85, 0x39, 0x75, 0x5d, 0x08, 0x00, 0xae, 0xeb, 0xf6, 0xea, 0xd7, 0x13, 0x3a, 0x21, 0x5a,
1110x5f, 0x30, 0x84, 0x52, 0x26, 0x95, 0xc9, 0x14, 0xf2, 0x57, 0x55, 0x6b, 0xb1, 0x10, 0xc2,
1120xe1, 0xbd, 0x3b, 0x51, 0xc0, 0xb7, 0x55, 0x4c, 0x71, 0x12, 0x26, 0xc7, 0x0d, 0xf9, 0x51,
1130xa4, 0x38, 0x02, 0x05, 0x7f, 0xb8, 0xf1, 0x72, 0x4b, 0xbf, 0x71, 0x89, 0x14, 0xf3, 0x77,
1140x38, 0xd9, 0x71, 0x24, 0xf3, 0x00, 0x11, 0xa1, 0xd8, 0xd4, 0x69, 0x27, 0x08, 0x37, 0x35,
1150xc9, 0x11, 0x9d, 0x90, 0x1c, 0x0e, 0xe7, 0x1c, 0xff, 0x2d, 0x1e, 0xe8, 0x92, 0xe1, 0x18,
1160x10, 0x95, 0x7c, 0xe0, 0x80, 0xf4, 0x96, 0x43, 0x21, 0xf9, 0x75, 0x21, 0x64, 0x38, 0xdd,
1170x9f, 0x1e, 0x95, 0x16, 0xda, 0x56, 0x1d, 0x4f, 0x9a, 0x53, 0xb2, 0xe2, 0xe4, 0x18, 0xcb,
1180x6b, 0x1a, 0x65, 0xeb, 0x56, 0xc6, 0x3b, 0xe5, 0xfe, 0xd8, 0x26, 0x3f, 0x3a, 0x84, 0x59,
1190x72, 0x66, 0xa2, 0xf3, 0x75, 0xff, 0xfb, 0x60, 0xb3, 0x22, 0xad, 0x3f, 0x2d, 0x6b, 0xf9,
1200xeb, 0xea, 0x05, 0x7c, 0xd8, 0x8f, 0x6d, 0x2c, 0x98, 0x9e, 0x2b, 0x93, 0xf1, 0x5e, 0x46,
1210xf0, 0x87, 0x49, 0x29, 0x73, 0x68, 0xd7, 0x7f, 0xf9, 0xf0, 0xe5, 0x7d, 0xdb, 0x1d, 0x75,
1220x19, 0xf3, 0xc4, 0x58, 0x9b, 0x17, 0x88, 0xa8, 0x92, 0xe0, 0xbe, 0xbd, 0x8b, 0x1d, 0x8d,
1230x9f, 0x56, 0x76, 0xad, 0xaf, 0x29, 0xe2, 0xd9, 0xd5, 0x52, 0xf6, 0xb5, 0x56, 0x35, 0x57,
1240x3a, 0xc8, 0xe1, 0x56, 0x43, 0x19, 0x94, 0xd3, 0x04, 0x9b, 0x6d, 0x35, 0xd8, 0x0b, 0x5f,
1250x4d, 0x19, 0x8e, 0xec, 0xfa, 0x64, 0x91, 0x0a, 0x72, 0x20, 0x2b, 0xbc, 0x1a, 0x4a, 0xfe,
1260x8b, 0xfd, 0xbb, 0xed, 0x1b, 0x23, 0xea, 0xad, 0x72, 0x82, 0xa1, 0x29, 0x99, 0x71, 0xbd,
1270xf0, 0x95, 0xc1, 0x03, 0xdd, 0x7b, 0xc2, 0xb2, 0x3c, 0x28, 0x54, 0xd3, 0x68, 0xa4, 0x72,
1280xc8, 0x66, 0x96, 0xe0, 0xd1, 0xd8, 0x7f, 0xf8, 0xd1, 0x26, 0x2b, 0xf7, 0xad, 0xba, 0x55,
1290xca, 0x15, 0xb9, 0x32, 0xc3, 0xe5, 0x88, 0x97, 0x8e, 0x5c, 0xfb, 0x92, 0x25, 0x8b, 0xbf,
1300xa2, 0x45, 0x55, 0x7a, 0xa7, 0x6f, 0x8b, 0x57, 0x5b, 0xcf, 0x0e, 0xcb, 0x1d, 0xfb, 0x20,
1310x82, 0x77, 0xa8, 0x8c, 0xcc, 0x16, 0xce, 0x1d, 0xfa, 0xde, 0xcc, 0x0b, 0x62, 0xfe, 0xcc,
1320xe1, 0xb7, 0xf0, 0xc3, 0x81, 0x64, 0x73, 0x40, 0xa0, 0xc2, 0x4d, 0x89, 0x11, 0x75, 0x33,
1330x55, 0x33, 0x8d, 0xe8, 0x4a, 0xfd, 0xea, 0x6e, 0x30, 0x0b, 0xd7, 0x31, 0x2c, 0xde, 0x47,
1340xe3, 0xbf, 0xf8, 0x55, 0x42, 0xe2, 0x7f, 0x59, 0xe5, 0x17, 0xef, 0x99, 0x34, 0x69, 0x91,
1350xb1, 0x23, 0x8e, 0x20, 0x87, 0x2d, 0xa8, 0xfe, 0xd5, 0x8a, 0xf3, 0x84, 0x3a, 0xf0, 0x37,
1360xe4, 0x09, 0x00, 0x54, 0xee, 0x67, 0x49, 0x93, 0xe4, 0x81, 0x70, 0xe3, 0x90, 0x4d, 0xef,
1370xfe, 0x41, 0xb7, 0x99, 0x7b, 0xc1, 0x83, 0xba, 0x62, 0x12, 0x6f, 0x7d, 0xde, 0x6b, 0xaf,
1380xda, 0x16, 0xf9, 0x55, 0x51, 0xee, 0xa6, 0x0c, 0x2b, 0x02, 0xa3, 0xfd, 0x8d, 0xfb, 0x30,
1390x17, 0xe4, 0x6f, 0xdf, 0x36, 0x71, 0xc4, 0xca, 0x87, 0x25, 0x48, 0xb0, 0x47, 0xec, 0xea,
1400xb4, 0xbf, 0xa5, 0x4d, 0x9b, 0x9f, 0x02, 0x93, 0xc4, 0xe3, 0xe4, 0xe8, 0x42, 0x2d, 0x68,
1410x81, 0x15, 0x0a, 0xeb, 0x84, 0x5b, 0xd6, 0xa8, 0x74, 0xfb, 0x7d, 0x1d, 0xcb, 0x2c, 0xda,
1420x46, 0x2a, 0x76, 0x62, 0xce, 0xbc, 0x5c, 0x9e, 0x8b, 0xe7, 0xcf, 0xbe, 0x78, 0xf5, 0x7c,
1430xeb, 0xb3, 0x3a, 0x9c, 0xaa, 0x6f, 0xcc, 0x72, 0xd1, 0x59, 0xf2, 0x11, 0x23, 0xd6, 0x3f,
1440x48, 0xd1, 0xb7, 0xce, 0xb0, 0xbf, 0xcb, 0xea, 0x80, 0xde, 0x57, 0xd4, 0x5e, 0x97, 0x2f,
1450x75, 0xd1, 0x50, 0x8e, 0x80, 0x2c, 0x66, 0x79, 0xbf, 0x72, 0x4b, 0xbd, 0x8a, 0x81, 0x6c,
1460xd3, 0xe1, 0x01, 0xdc, 0xd2, 0x15, 0x26, 0xc5, 0x36, 0xda, 0x2c, 0x1a, 0xc0, 0x27, 0x94,
1470xed, 0xb7, 0x9b, 0x85, 0x0b, 0x5e, 0x80, 0x97, 0xc5, 0xec, 0x4f, 0xec, 0x88, 0x5d, 0x50,
1480x07, 0x35, 0x47, 0xdc, 0x0b, 0x3b, 0x3d, 0xdd, 0x60, 0xaf, 0xa8, 0x5d, 0x81, 0x38, 0x24,
1490x25, 0x5d, 0x5c, 0x15, 0xd1, 0xde, 0xb3, 0xab, 0xec, 0x05, 0x69, 0xef, 0x83, 0xed, 0x57,
1500x54, 0xb8, 0x64, 0x64, 0x11, 0x16, 0x32, 0x69, 0xda, 0x9f, 0x2d, 0x7f, 0x36, 0xbb, 0x44,
1510x5a, 0x34, 0xe8, 0x7f, 0xbf, 0x03, 0xeb, 0x00, 0x7f, 0x59, 0x68, 0x22, 0x79, 0xcf, 0x73,
1520x6c, 0x2c, 0x29, 0xa7, 0xa1, 0x5f, 0x38, 0xa1, 0x1d, 0xf0, 0x20, 0x53, 0xe0, 0x1a, 0x63,
1530x14, 0x58, 0x71, 0x10, 0xaa, 0x08, 0x0c, 0x3e, 0x16, 0x1a, 0x60, 0x22, 0x82, 0x7f, 0xba,
1540xa4, 0x43, 0xa0, 0xd0, 0xac, 0x1b, 0xd5, 0x6b, 0x64, 0xb5, 0x14, 0x93, 0x31, 0x9e, 0x53,
1550x50, 0xd0, 0x57, 0x66, 0xee, 0x5a, 0x4f, 0xfb, 0x03, 0x2a, 0x69, 0x58, 0x76, 0xf1, 0x83,
1560xf7, 0x4e, 0xba, 0x8c, 0x42, 0x06, 0x60, 0x5d, 0x6d, 0xce, 0x60, 0x88, 0xae, 0xa4, 0xc3,
1570xf1, 0x03, 0xa5, 0x4b, 0x98, 0xa1, 0xff, 0x67, 0xe1, 0xac, 0xa2, 0xb8, 0x62, 0xd7, 0x6f,
1580xa0, 0x31, 0xb4, 0xd2, 0x77, 0xaf, 0x21, 0x10, 0x06, 0xc6, 0x9a, 0xff, 0x1d, 0x09, 0x17,
1590x0e, 0x5f, 0xf1, 0xaa, 0x54, 0x34, 0x4b, 0x45, 0x8a, 0x87, 0x63, 0xa6, 0xdc, 0xf9, 0x24,
1600x30, 0x67, 0xc6, 0xb2, 0xd6, 0x61, 0x33, 0x69, 0xee, 0x50, 0x61, 0x57, 0x28, 0xe7, 0x7e,
1610xee, 0xec, 0x3a, 0x5a, 0x73, 0x4e, 0xa8, 0x8d, 0xe4, 0x18, 0xea, 0xec, 0x41, 0x64, 0xc8,
1620xe2, 0xe8, 0x66, 0xb6, 0x2d, 0xb6, 0xfb, 0x6a, 0x6c, 0x16, 0xb3, 0xdd, 0x46, 0x43, 0xb9,
1630x73, 0x00, 0x6a, 0x71, 0xed, 0x4e, 0x9d, 0x25, 0x1a, 0xc3, 0x3c, 0x4a, 0x95, 0x15, 0x99,
1640x35, 0x81, 0x14, 0x02, 0xd6, 0x98, 0x9b, 0xec, 0xd8, 0x23, 0x3b, 0x84, 0x29, 0xaf, 0x0c,
1650x99, 0x83, 0xa6, 0x9a, 0x34, 0x4f, 0xfa, 0xe8, 0xd0, 0x3c, 0x4b, 0xd0, 0xfb, 0xb6, 0x68,
1660xb8, 0x9e, 0x8f, 0xcd, 0xf7, 0x60, 0x2d, 0x7a, 0x22, 0xe5, 0x7d, 0xab, 0x65, 0x1b, 0x95,
1670xa7, 0xa8, 0x7f, 0xb6, 0x77, 0x47, 0x7b, 0x5f, 0x8b, 0x12, 0x72, 0xd0, 0xd4, 0x91, 0xef,
1680xde, 0x19, 0x50, 0x3c, 0xa7, 0x8b, 0xc4, 0xa9, 0xb3, 0x23, 0xcb, 0x76, 0xe6, 0x81, 0xf0,
1690xc1, 0x04, 0x8f, 0xa3, 0xb8, 0x54, 0x5b, 0x97, 0xac, 0x19, 0xff, 0x3f, 0x55, 0x27, 0x2f,
1700xe0, 0x1d, 0x42, 0x9b, 0x57, 0xfc, 0x4b, 0x4e, 0x0f, 0xce, 0x98, 0xa9, 0x43, 0x57, 0x03,
1710xbd, 0xe7, 0xc8, 0x94, 0xdf, 0x6e, 0x36, 0x73, 0x32, 0xb4, 0xef, 0x2e, 0x85, 0x7a, 0x6e,
1720xfc, 0x6c, 0x18, 0x82, 0x75, 0x35, 0x90, 0x07, 0xf3, 0xe4, 0x9f, 0x3e, 0xdc, 0x68, 0xf3,
1730xb5, 0xf3, 0x19, 0x80, 0x92, 0x06, 0x99, 0xa2, 0xe8, 0x6f, 0xff, 0x2e, 0x7f, 0xae, 0x42,
1740xa4, 0x5f, 0xfb, 0xd4, 0x0e, 0x81, 0x2b, 0xc3, 0x04, 0xff, 0x2b, 0xb3, 0x74, 0x4e, 0x36,
1750x5b, 0x9c, 0x15, 0x00, 0xc6, 0x47, 0x2b, 0xe8, 0x8b, 0x3d, 0xf1, 0x9c, 0x03, 0x9a, 0x58,
1760x7f, 0x9b, 0x9c, 0xbf, 0x85, 0x49, 0x79, 0x35, 0x2e, 0x56, 0x7b, 0x41, 0x14, 0x39, 0x47,
1770x83, 0x26, 0xaa, 0x07, 0x89, 0x98, 0x11, 0x1b, 0x86, 0xe7, 0x73, 0x7a, 0xd8, 0x7d, 0x78,
1780x61, 0x53, 0xe9, 0x79, 0xf5, 0x36, 0x8d, 0x44, 0x92, 0x84, 0xf9, 0x13, 0x50, 0x58, 0x3b,
1790xa4, 0x6a, 0x36, 0x65, 0x49, 0x8e, 0x3c, 0x0e, 0xf1, 0x6f, 0xd2, 0x84, 0xc4, 0x7e, 0x8e,
1800x3f, 0x39, 0xae, 0x7c, 0x84, 0xf1, 0x63, 0x37, 0x8e, 0x3c, 0xcc, 0x3e, 0x44, 0x81, 0x45,
1810xf1, 0x4b, 0xb9, 0xed, 0x6b, 0x36, 0x5d, 0xbb, 0x20, 0x60, 0x1a, 0x0f, 0xa3, 0xaa, 0x55,
1820x77, 0x3a, 0xa9, 0xae, 0x37, 0x4d, 0xba, 0xb8, 0x86, 0x6b, 0xbc, 0x08, 0x50, 0xf6, 0xcc,
1830xa4, 0xbd, 0x1d, 0x40, 0x72, 0xa5, 0x86, 0xfa, 0xe2, 0x10, 0xae, 0x3d, 0x58, 0x4b, 0x97,
1840xf3, 0x43, 0x74, 0xa9, 0x9e, 0xeb, 0x21, 0xb7, 0x01, 0xa4, 0x86, 0x93, 0x97, 0xee, 0x2f,
1850x4f, 0x3b, 0x86, 0xa1, 0x41, 0x6f, 0x41, 0x26, 0x90, 0x78, 0x5c, 0x7f, 0x30, 0x38, 0x4b,
1860x3f, 0xaa, 0xec, 0xed, 0x5c, 0x6f, 0x0e, 0xad, 0x43, 0x87, 0xfd, 0x93, 0x35, 0xe6, 0x01,
1870xef, 0x41, 0x26, 0x90, 0x99, 0x9e, 0xfb, 0x19, 0x5b, 0xad, 0xd2, 0x91, 0x8a, 0xe0, 0x46,
1880xaf, 0x65, 0xfa, 0x4f, 0x84, 0xc1, 0xa1, 0x2d, 0xcf, 0x45, 0x8b, 0xd3, 0x85, 0x50, 0x55,
1890x7c, 0xf9, 0x67, 0x88, 0xd4, 0x4e, 0xe9, 0xd7, 0x6b, 0x61, 0x54, 0xa1, 0xa4, 0xa6, 0xa2,
1900xc2, 0xbf, 0x30, 0x9c, 0x40, 0x9f, 0x5f, 0xd7, 0x69, 0x2b, 0x24, 0x82, 0x5e, 0xd9, 0xd6,
1910xa7, 0x12, 0x54, 0x1a, 0xf7, 0x55, 0x9f, 0x76, 0x50, 0xa9, 0x95, 0x84, 0xe6, 0x6b, 0x6d,
1920xb5, 0x96, 0x54, 0xd6, 0xcd, 0xb3, 0xa1, 0x9b, 0x46, 0xa7, 0x94, 0x4d, 0xc4, 0x94, 0xb4,
1930x98, 0xe3, 0xe1, 0xe2, 0x34, 0xd5, 0x33, 0x16, 0x07, 0x54, 0xcd, 0xb7, 0x77, 0x53, 0xdb,
1940x4f, 0x4d, 0x46, 0x9d, 0xe9, 0xd4, 0x9c, 0x8a, 0x36, 0xb6, 0xb8, 0x38, 0x26, 0x6c, 0x0e,
1950xff, 0x9c, 0x1b, 0x43, 0x8b, 0x80, 0xcc, 0xb9, 0x3d, 0xda, 0xc7, 0xf1, 0x8a, 0xf2, 0x6d,
1960xb8, 0xd7, 0x74, 0x2f, 0x7e, 0x1e, 0xb7, 0xd3, 0x4a, 0xb4, 0xac, 0xfc, 0x79, 0x48, 0x6c,
1970xbc, 0x96, 0xb6, 0x94, 0x46, 0x57, 0x2d, 0xb0, 0xa3, 0xfc, 0x1e, 0xb9, 0x52, 0x60, 0x85,
1980x2d, 0x41, 0xd0, 0x43, 0x01, 0x1e, 0x1c, 0xd5, 0x7d, 0xfc, 0xf3, 0x96, 0x0d, 0xc7, 0xcb,
1990x2a, 0x29, 0x9a, 0x93, 0xdd, 0x88, 0x2d, 0x37, 0x5d, 0xaa, 0xfb, 0x49, 0x68, 0xa0, 0x9c,
2000x50, 0x86, 0x7f, 0x68, 0x56, 0x57, 0xf9, 0x79, 0x18, 0x39, 0xd4, 0xe0, 0x01, 0x84, 0x33,
2010x61, 0xca, 0xa5, 0xd2, 0xd6, 0xe4, 0xc9, 0x8a, 0x4a, 0x23, 0x44, 0x4e, 0xbc, 0xf0, 0xdc,
2020x24, 0xa1, 0xa0, 0xc4, 0xe2, 0x07, 0x3c, 0x10, 0xc4, 0xb5, 0x25, 0x4b, 0x65, 0x63, 0xf4,
2030x80, 0xe7, 0xcf, 0x61, 0xb1, 0x71, 0x82, 0x21, 0x87, 0x2c, 0xf5, 0x91, 0x00, 0x32, 0x0c,
2040xec, 0xa9, 0xb5, 0x9a, 0x74, 0x85, 0xe3, 0x36, 0x8f, 0x76, 0x4f, 0x9c, 0x6d, 0xce, 0xbc,
2050xad, 0x0a, 0x4b, 0xed, 0x76, 0x04, 0xcb, 0xc3, 0xb9, 0x33, 0x9e, 0x01, 0x93, 0x96, 0x69,
2060x7d, 0xc5, 0xa2, 0x45, 0x79, 0x9b, 0x04, 0x5c, 0x84, 0x09, 0xed, 0x88, 0x43, 0xc7, 0xab,
2070x93, 0x14, 0x26, 0xa1, 0x40, 0xb5, 0xce, 0x4e, 0xbf, 0x2a, 0x42, 0x85, 0x3e, 0x2c, 0x3b,
2080x54, 0xe8, 0x12, 0x1f, 0x0e, 0x97, 0x59, 0xb2, 0x27, 0x89, 0xfa, 0xf2, 0xdf, 0x8e, 0x68,
2090x59, 0xdc, 0x06, 0xbc, 0xb6, 0x85, 0x0d, 0x06, 0x22, 0xec, 0xb1, 0xcb, 0xe5, 0x04, 0xe6,
2100x3d, 0xb3, 0xb0, 0x41, 0x73, 0x08, 0x3f, 0x3c, 0x58, 0x86, 0x63, 0xeb, 0x50, 0xee, 0x1d,
2110x2c, 0x37, 0x74, 0xa9, 0xd3, 0x18, 0xa3, 0x47, 0x6e, 0x93, 0x54, 0xad, 0x0a, 0x5d, 0xb8,
2120x2a, 0x55, 0x5d, 0x78, 0xf6, 0xee, 0xbe, 0x8e, 0x3c, 0x76, 0x69, 0xb9, 0x40, 0xc2, 0x34,
2130xec, 0x2a, 0xb9, 0xed, 0x7e, 0x20, 0xe4, 0x8d, 0x00, 0x38, 0xc7, 0xe6, 0x8f, 0x44, 0xa8,
2140x86, 0xce, 0xeb, 0x2a, 0xe9, 0x90, 0xf1, 0x4c, 0xdf, 0x32, 0xfb, 0x73, 0x1b, 0x6d, 0x92,
2150x1e, 0x95, 0xfe, 0xb4, 0xdb, 0x65, 0xdf, 0x4d, 0x23, 0x54, 0x89, 0x48, 0xbf, 0x4a, 0x2e,
2160x70, 0xd6, 0xd7, 0x62, 0xb4, 0x33, 0x29, 0xb1, 0x3a, 0x33, 0x4c, 0x23, 0x6d, 0xa6, 0x76,
2170xa5, 0x21, 0x63, 0x48, 0xe6, 0x90, 0x5d, 0xed, 0x90, 0x95, 0x0b, 0x7a, 0x84, 0xbe, 0xb8,
2180x0d, 0x5e, 0x63, 0x0c, 0x62, 0x26, 0x4c, 0x14, 0x5a, 0xb3, 0xac, 0x23, 0xa4, 0x74, 0xa7,
2190x6f, 0x33, 0x30, 0x05, 0x60, 0x01, 0x42, 0xa0, 0x28, 0xb7, 0xee, 0x19, 0x38, 0xf1, 0x64,
2200x80, 0x82, 0x43, 0xe1, 0x41, 0x27, 0x1f, 0x1f, 0x90, 0x54, 0x7a, 0xd5, 0x23, 0x2e, 0xd1,
2210x3d, 0xcb, 0x28, 0xba, 0x58, 0x7f, 0xdc, 0x7c, 0x91, 0x24, 0xe9, 0x28, 0x51, 0x83, 0x6e,
2220xc5, 0x56, 0x21, 0x42, 0xed, 0xa0, 0x56, 0x22, 0xa1, 0x40, 0x80, 0x6b, 0xa8, 0xf7, 0x94,
2230xca, 0x13, 0x6b, 0x0c, 0x39, 0xd9, 0xfd, 0xe9, 0xf3, 0x6f, 0xa6, 0x9e, 0xfc, 0x70, 0x8a,
2240xb3, 0xbc, 0x59, 0x3c, 0x1e, 0x1d, 0x6c, 0xf9, 0x7c, 0xaf, 0xf9, 0x88, 0x71, 0x95, 0xeb,
2250x57, 0x00, 0xbd, 0x9f, 0x8c, 0x4f, 0xe1, 0x24, 0x83, 0xc5, 0x22, 0xea, 0xfd, 0xd3, 0x0c,
2260xe2, 0x17, 0x18, 0x7c, 0x6a, 0x4c, 0xde, 0x77, 0xb4, 0x53, 0x9b, 0x4c, 0x81, 0xcd, 0x23,
2270x60, 0xaa, 0x0e, 0x25, 0x73, 0x9c, 0x02, 0x79, 0x32, 0x30, 0xdf, 0x74, 0xdf, 0x75, 0x19,
2280xf4, 0xa5, 0x14, 0x5c, 0xf7, 0x7a, 0xa8, 0xa5, 0x91, 0x84, 0x7c, 0x60, 0x03, 0x06, 0x3b,
2290xcd, 0x50, 0xb6, 0x27, 0x9c, 0xfe, 0xb1, 0xdd, 0xcc, 0xd3, 0xb0, 0x59, 0x24, 0xb2, 0xca,
2300xe2, 0x1c, 0x81, 0x22, 0x9d, 0x07, 0x8f, 0x8e, 0xb9, 0xbe, 0x4e, 0xfa, 0xfc, 0x39, 0x65,
2310xba, 0xbf, 0x9d, 0x12, 0x37, 0x5e, 0x97, 0x7e, 0xf3, 0x89, 0xf5, 0x5d, 0xf5, 0xe3, 0x09,
2320x8c, 0x62, 0xb5, 0x20, 0x9d, 0x0c, 0x53, 0x8a, 0x68, 0x1b, 0xd2, 0x8f, 0x75, 0x17, 0x5d,
2330xd4, 0xe5, 0xda, 0x75, 0x62, 0x19, 0x14, 0x6a, 0x26, 0x2d, 0xeb, 0xf8, 0xaf, 0x37, 0xf0,
2340x6c, 0xa4, 0x55, 0xb1, 0xbc, 0xe2, 0x33, 0xc0, 0x9a, 0xca, 0xb0, 0x11, 0x49, 0x4f, 0x68,
2350x9b, 0x3b, 0x6b, 0x3c, 0xcc, 0x13, 0xf6, 0xc7, 0x85, 0x61, 0x68, 0x42, 0xae, 0xbb, 0xdd,
2360xcd, 0x45, 0x16, 0x29, 0x1d, 0xea, 0xdb, 0xc8, 0x03, 0x94, 0x3c, 0xee, 0x4f, 0x82, 0x11,
2370xc3, 0xec, 0x28, 0xbd, 0x97, 0x05, 0x99, 0xde, 0xd7, 0xbb, 0x5e, 0x22, 0x1f, 0xd4, 0xeb,
2380x64, 0xd9, 0x92, 0xd9, 0x85, 0xb7, 0x6a, 0x05, 0x6a, 0xe4, 0x24, 0x41, 0xf1, 0xcd, 0xf0,
2390xd8, 0x3f, 0xf8, 0x9e, 0x0e, 0xcd, 0x0b, 0x7a, 0x70, 0x6b, 0x5a, 0x75, 0x0a, 0x6a, 0x33,
2400x88, 0xec, 0x17, 0x75, 0x08, 0x70, 0x10, 0x2f, 0x24, 0xcf, 0xc4, 0xe9, 0x42, 0x00, 0x61,
2410x94, 0xca, 0x1f, 0x3a, 0x76, 0x06, 0xfa, 0xd2, 0x48, 0x81, 0xf0, 0x77, 0x60, 0x03, 0x45,
2420xd9, 0x61, 0xf4, 0xa4, 0x6f, 0x3d, 0xd9, 0x30, 0xc3, 0x04, 0x6b, 0x54, 0x2a, 0xb7, 0xec,
2430x3b, 0xf4, 0x4b, 0xf5, 0x68, 0x52, 0x26, 0xce, 0xff, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xa5,
2440xa9, 0xb1, 0xe0, 0x23, 0xc4, 0x0a, 0x77, 0x4d, 0xf9, 0x51, 0x20, 0xa3, 0xa5, 0xa9, 0xb1,
2450xc1, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xc4, 0xeb, 0x54, 0x0b,
2460x75, 0x68, 0x52, 0x07, 0x8c, 0x9a, 0x97, 0x8d, 0x79, 0x70, 0x62, 0x46, 0xef, 0x5c, 0x1b,
2470x95, 0x89, 0x71, 0x41, 0xe1, 0x21, 0xa1, 0xa1, 0xa1, 0xc0, 0x02, 0x67, 0x4c, 0x1a, 0xb6,
2480xcf, 0xfd, 0x78, 0x53, 0x24, 0xab, 0xb5, 0xc9, 0xf1, 0x60, 0x23, 0xa5, 0xc8, 0x12, 0x87,
2490x6d, 0x58, 0x13, 0x85, 0x88, 0x92, 0x87, 0x6d, 0x58, 0x32, 0xc7, 0x0c, 0x9a, 0x97, 0xac,
2500xda, 0x36, 0xee, 0x5e, 0x3e, 0xdf, 0x1d, 0xb8, 0xf2, 0x66, 0x2f, 0xbd, 0xf8, 0x72, 0x47,
2510xed, 0x58, 0x13, 0x85, 0x88, 0x92, 0x87, 0x8c, 0x7b, 0x55, 0x09, 0x90, 0xa2, 0xc6, 0xef,
2520x3d, 0xf8, 0x53, 0x24, 0xab, 0xd4, 0x2a, 0xb7, 0xec, 0x5a, 0x36, 0xee, 0x5e, 0x3e, 0xdf,
2530x3c, 0xfa, 0x76, 0x4f, 0xfd, 0x59, 0x30, 0xe2, 0x46, 0xef, 0x3d, 0xf8, 0x53, 0x05, 0x69,
2540x31, 0xc1, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, 0x19, 0xb0, 0xe2, 0x27, 0xcc, 0xfb, 0x74,
2550x4b, 0x14, 0x8b, 0x94, 0x8b, 0x75, 0x68, 0x33, 0xc5, 0x08, 0x92, 0x87, 0x8c, 0x9a, 0xb6,
2560xcf, 0x1c, 0xba, 0xd7, 0x0d, 0x98, 0xb2, 0xe6, 0x2f, 0xdc, 0x1b, 0x95, 0x89, 0x71, 0x60,
2570x23, 0xc4, 0x0a, 0x96, 0x8f, 0x9c, 0xba, 0xf6, 0x6e, 0x3f, 0xfc, 0x5b, 0x15, 0xa8, 0xd2,
2580x26, 0xaf, 0xbd, 0xf8, 0x72, 0x66, 0x2f, 0xdc, 0x1b, 0xb4, 0xcb, 0x14, 0x8b, 0x94, 0xaa,
2590xb7, 0xcd, 0xf9, 0x51, 0x01, 0x80, 0x82, 0x86, 0x6f, 0x3d, 0xd9, 0x30, 0xe2, 0x27, 0xcc,
2600xfb, 0x74, 0x4b, 0x14, 0xaa, 0xb7, 0xcd, 0xf9, 0x70, 0x43, 0x04, 0x6b, 0x35, 0xc9, 0xf1,
2610x60, 0x23, 0xa5, 0xc8, 0xf3, 0x45, 0x08, 0x92, 0x87, 0x6d, 0x58, 0x32, 0xe6, 0x2f, 0xbd,
2620xf8, 0x72, 0x66, 0x4e, 0x1e, 0xbe, 0xfe, 0x7e, 0x7e, 0x7e, 0x5f, 0x1d, 0x99, 0x91, 0xa0,
2630xa3, 0xc4, 0x0a, 0x77, 0x4d, 0x18, 0x93, 0xa4, 0xab, 0xd4, 0x0b, 0x75, 0x49, 0x10, 0xa2,
2640xc6, 0xef, 0x3d, 0xf8, 0x53, 0x24, 0xab, 0xb5, 0xe8, 0x33, 0xe4, 0x4a, 0x16, 0xae, 0xde,
2650x1f, 0xbc, 0xdb, 0x15, 0xa8, 0xb3, 0xc5, 0x08, 0x73, 0x45, 0xe9, 0x31, 0xc1, 0xe1, 0x21,
2660xa1, 0xa1, 0xa1, 0xc0, 0x02, 0x86, 0x6f, 0x5c, 0x3a, 0xd7, 0x0d, 0x98, 0x93, 0xa4, 0xca,
2670x16, 0xae, 0xde, 0x1f, 0x9d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x3d, 0xf8, 0x72, 0x47, 0x0c,
2680x9a, 0xb6, 0xcf, 0xfd, 0x59, 0x11, 0xa0, 0xa3, 0xa5, 0xc8, 0xf3, 0x45, 0x08, 0x92, 0x87,
2690x6d, 0x39, 0xf0, 0x43, 0x04, 0x8a, 0x96, 0xae, 0xde, 0x3e, 0xdf, 0x1d, 0x99, 0x91, 0xa0,
2700xc2, 0x06, 0x6f, 0x3d, 0xf8, 0x72, 0x47, 0x0c, 0x9a, 0x97, 0x8d, 0x98, 0x93, 0x85, 0x88,
2710x73, 0x45, 0xe9, 0x31, 0xe0, 0x23, 0xa5, 0xa9, 0xd0, 0x03, 0x84, 0x8a, 0x96, 0xae, 0xde,
2720x1f, 0xbc, 0xdb, 0x15, 0xa8, 0xd2, 0x26, 0xce, 0xff, 0x5d, 0x19, 0x91, 0x81, 0x80, 0x82,
2730x67, 0x2d, 0xd8, 0x13, 0xa4, 0xab, 0xd4, 0x0b, 0x94, 0xaa, 0xb7, 0xcd, 0xf9, 0x51, 0x20,
2740xa3, 0xa5, 0xc8, 0xf3, 0x45, 0xe9, 0x50, 0x22, 0xc6, 0xef, 0x5c, 0x3a, 0xd7, 0x0d, 0x98,
2750x93, 0x85, 0x88, 0x73, 0x64, 0x4a, 0xf7, 0x4d, 0xf9, 0x51, 0x20, 0xa3, 0xc4, 0x0a, 0x96,
2760xae, 0xde, 0x3e, 0xfe, 0x7e, 0x7e, 0x7e, 0x5f, 0x3c, 0xfa, 0x76, 0x4f, 0xfd, 0x78, 0x72,
2770x66, 0x2f, 0xbd, 0xd9, 0x30, 0xc3, 0xe5, 0x48, 0x12, 0x87, 0x8c, 0x7b, 0x55, 0x28, 0xd2,
2780x07, 0x8c, 0x9a, 0x97, 0xac, 0xda, 0x17, 0x8d, 0x79, 0x51, 0x20, 0xa3, 0xc4, 0xeb, 0x54,
2790x0b, 0x94, 0x8b, 0x94, 0xaa, 0xd6, 0x2e, 0xbf, 0xfc, 0x5b, 0x15, 0xa8, 0xd2, 0x26, 0xaf,
2800xdc, 0x1b, 0xb4, 0xea, 0x37, 0xec, 0x3b, 0xf4, 0x6a, 0x37, 0xcd, 0x18, 0x93, 0x85, 0x69,
2810x31, 0xc1, 0xe1, 0x40, 0xe3, 0x25, 0xc8, 0x12, 0x87, 0x8c, 0x9a, 0xb6, 0xcf, 0xfd, 0x59,
2820x11, 0xa0, 0xc2, 0x06, 0x8e, 0x7f, 0x5d, 0x38, 0xf2, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x37,
2830xec, 0x5a, 0x36, 0xee, 0x3f, 0xfc, 0x7a, 0x76, 0x4f, 0x1c, 0x9b, 0x95, 0x89, 0x71, 0x41,
2840x00, 0x63, 0x44, 0xeb, 0x54, 0x2a, 0xd6, 0x0f, 0x9c, 0xba, 0xd7, 0x0d, 0x98, 0x93, 0x85,
2850x69, 0x31, 0xc1, 0x00, 0x82, 0x86, 0x8e, 0x9e, 0xbe, 0xdf, 0x3c, 0xfa, 0x57, 0x2c, 0xda,
2860x36, 0xee, 0x3f, 0xfc, 0x5b, 0x15, 0x89, 0x71, 0x41, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d,
2870x38, 0xf2, 0x47, 0xed, 0x58, 0x13, 0xa4, 0xca, 0xf7, 0x4d, 0xf9, 0x51, 0x01, 0x80, 0x63,
2880x44, 0xeb, 0x54, 0x2a, 0xd6, 0x2e, 0xbf, 0xdd, 0x19, 0x91, 0xa0, 0xa3, 0xa5, 0xa9, 0xb1,
2890xe0, 0x42, 0x06, 0x8e, 0x7f, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xc4, 0x0a, 0x96, 0x8f, 0x7d,
2900x78, 0x72, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x56, 0x2e, 0xde, 0x1f, 0xbc, 0xfa, 0x57, 0x0d,
2910x79, 0x51, 0x01, 0x61, 0x21, 0xa1, 0xc0, 0xe3, 0x25, 0xa9, 0xb1, 0xc1, 0xe1, 0x40, 0x02,
2920x67, 0x4c, 0x1a, 0x97, 0x8d, 0x98, 0x93, 0xa4, 0xab, 0xd4, 0x2a, 0xd6, 0x0f, 0x9c, 0x9b,
2930xb4, 0xcb, 0x14, 0xaa, 0xb7, 0xcd, 0xf9, 0x51, 0x20, 0xa3, 0xc4, 0xeb, 0x35, 0xc9, 0xf1,
2940x60, 0x42, 0x06, 0x8e, 0x7f, 0x7c, 0x7a, 0x76, 0x6e, 0x3f, 0xfc, 0x7a, 0x76, 0x6e, 0x5e,
2950x3e, 0xfe, 0x7e, 0x5f, 0x3c, 0xdb, 0x15, 0x89, 0x71, 0x41, 0xe1, 0x21, 0xc0, 0xe3, 0x44,
2960xeb, 0x54, 0x2a, 0xb7, 0xcd, 0xf9, 0x70, 0x62, 0x27, 0xad, 0xd8, 0x32, 0xc7, 0x0c, 0x7b,
2970x74, 0x4b, 0x14, 0xaa, 0xb7, 0xec, 0x3b, 0xd5, 0x28, 0xd2, 0x07, 0x6d, 0x39, 0xd1, 0x20,
2980xc2, 0xe7, 0x4c, 0x1a, 0x97, 0x8d, 0x98, 0xb2, 0xc7, 0x0c, 0x59, 0x28, 0xf3, 0x9b };
299
300// clang-format off
diff --git a/drivers/serial.h b/drivers/serial.h
new file mode 100644
index 000000000..d9c2a69e9
--- /dev/null
+++ b/drivers/serial.h
@@ -0,0 +1,46 @@
1/* Copyright 2021 QMK
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
22#include <transactions.h>
23
24// initiator is transaction start side
25void soft_serial_initiator_init(void);
26// target is interrupt accept side
27void soft_serial_target_init(void);
28
29// initiator result
30#define TRANSACTION_END 0
31#define TRANSACTION_NO_RESPONSE 0x1
32#define TRANSACTION_DATA_ERROR 0x2
33#define TRANSACTION_TYPE_ERROR 0x4
34int soft_serial_transaction(int sstd_index);
35
36// target status
37// *SSTD_t.status has
38// initiator:
39// TRANSACTION_END
40// or TRANSACTION_NO_RESPONSE
41// or TRANSACTION_DATA_ERROR
42// target:
43// TRANSACTION_DATA_ERROR
44// or TRANSACTION_ACCEPTED
45#define TRANSACTION_ACCEPTED 0x8
46int soft_serial_get_and_clean_status(int sstd_index);