aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorDrashna Jaelre <drashna@live.com>2021-11-14 22:03:24 -0800
committerGitHub <noreply@github.com>2021-11-14 22:03:24 -0800
commit56e3f06a26851976e559aacf7a096c61403304be (patch)
tree1e9ec98ad239fdd241e77ac4c4822fc2721a9cea /drivers
parent462c3a615113e84ac3ca837a5caeb928c0ec8505 (diff)
downloadqmk_firmware-56e3f06a26851976e559aacf7a096c61403304be.tar.gz
qmk_firmware-56e3f06a26851976e559aacf7a096c61403304be.zip
Rework and expand Pointing Device support (#14343)
Co-authored-by: Dasky <32983009+daskygit@users.noreply.github.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/sensors/adns5050.c157
-rw-r--r--drivers/sensors/adns5050.h75
-rw-r--r--drivers/sensors/adns9800.c175
-rw-r--r--drivers/sensors/adns9800.h42
-rw-r--r--drivers/sensors/adns9800_srom_A6.h2
-rw-r--r--drivers/sensors/analog_joystick.c94
-rw-r--r--drivers/sensors/analog_joystick.h51
-rw-r--r--drivers/sensors/cirque_pinnacle.c232
-rw-r--r--drivers/sensors/cirque_pinnacle.h74
-rw-r--r--drivers/sensors/cirque_pinnacle_i2c.c43
-rw-r--r--drivers/sensors/cirque_pinnacle_spi.c52
-rw-r--r--drivers/sensors/pimoroni_trackball.c187
-rw-r--r--drivers/sensors/pimoroni_trackball.h48
-rw-r--r--drivers/sensors/pmw3360.c140
-rw-r--r--drivers/sensors/pmw3360.h38
-rw-r--r--drivers/sensors/pmw3360_firmware.h5
-rw-r--r--drivers/sensors/pmw3389_firmware.h303
17 files changed, 1281 insertions, 437 deletions
diff --git a/drivers/sensors/adns5050.c b/drivers/sensors/adns5050.c
index 254ef2ee8..c23d24d5a 100644
--- a/drivers/sensors/adns5050.c
+++ b/drivers/sensors/adns5050.c
@@ -20,81 +20,95 @@
20#include "adns5050.h" 20#include "adns5050.h"
21#include "wait.h" 21#include "wait.h"
22#include "debug.h" 22#include "debug.h"
23#include "print.h"
24#include "gpio.h" 23#include "gpio.h"
25 24
26#ifndef OPTIC_ROTATED 25// Registers
27# define OPTIC_ROTATED false 26// clang-format off
28#endif 27#define REG_PRODUCT_ID 0x00
29 28#define REG_REVISION_ID 0x01
30// Definitions for the ADNS serial line. 29#define REG_MOTION 0x02
31#ifndef ADNS_SCLK_PIN 30#define REG_DELTA_X 0x03
32# define ADNS_SCLK_PIN B7 31#define REG_DELTA_Y 0x04
33#endif 32#define REG_SQUAL 0x05
34 33#define REG_SHUTTER_UPPER 0x06
35#ifndef ADNS_SDIO_PIN 34#define REG_SHUTTER_LOWER 0x07
36# define ADNS_SDIO_PIN C6 35#define REG_MAXIMUM_PIXEL 0x08
37#endif 36#define REG_PIXEL_SUM 0x09
38 37#define REG_MINIMUM_PIXEL 0x0a
39#ifndef ADNS_CS_PIN 38#define REG_PIXEL_GRAB 0x0b
40# define ADNS_CS_PIN B4 39#define REG_MOUSE_CONTROL 0x0d
41#endif 40#define REG_MOUSE_CONTROL2 0x19
42 41#define REG_LED_DC_MODE 0x22
43#ifdef CONSOLE_ENABLE 42#define REG_CHIP_RESET 0x3a
44void 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')); } 43#define REG_PRODUCT_ID2 0x3e
45#endif 44#define REG_INV_REV_ID 0x3f
46 45#define REG_MOTION_BURST 0x63
47// Initialize the ADNS serial pins. 46// clang-format on
48void adns_init(void) { 47
49 setPinOutput(ADNS_SCLK_PIN); 48void adns5050_init(void) {
50 setPinOutput(ADNS_SDIO_PIN); 49 // Initialize the ADNS serial pins.
51 setPinOutput(ADNS_CS_PIN); 50 setPinOutput(ADNS5050_SCLK_PIN);
51 setPinOutput(ADNS5050_SDIO_PIN);
52 setPinOutput(ADNS5050_CS_PIN);
53
54 // reboot the adns.
55 // if the adns hasn't initialized yet, this is harmless.
56 adns5050_write_reg(REG_CHIP_RESET, 0x5a);
57
58 // wait maximum time before adns is ready.
59 // this ensures that the adns is actuall ready after reset.
60 wait_ms(55);
61
62 // read a burst from the adns and then discard it.
63 // gets the adns ready for write commands
64 // (for example, setting the dpi).
65 adns5050_read_burst();
52} 66}
53 67
54// Perform a synchronization with the ADNS. 68// Perform a synchronization with the ADNS.
55// Just as with the serial protocol, this is used by the slave to send a 69// Just as with the serial protocol, this is used by the slave to send a
56// synchronization signal to the master. 70// synchronization signal to the master.
57void adns_sync(void) { 71void adns5050_sync(void) {
58 writePinLow(ADNS_CS_PIN); 72 writePinLow(ADNS5050_CS_PIN);
59 wait_us(1); 73 wait_us(1);
60 writePinHigh(ADNS_CS_PIN); 74 writePinHigh(ADNS5050_CS_PIN);
61} 75}
62 76
63void adns_cs_select(void) { writePinLow(ADNS_CS_PIN); } 77void adns5050_cs_select(void) { writePinLow(ADNS5050_CS_PIN); }
64 78
65void adns_cs_deselect(void) { writePinHigh(ADNS_CS_PIN); } 79void adns5050_cs_deselect(void) { writePinHigh(ADNS5050_CS_PIN); }
66 80
67uint8_t adns_serial_read(void) { 81uint8_t adns5050_serial_read(void) {
68 setPinInput(ADNS_SDIO_PIN); 82 setPinInput(ADNS5050_SDIO_PIN);
69 uint8_t byte = 0; 83 uint8_t byte = 0;
70 84
71 for (uint8_t i = 0; i < 8; ++i) { 85 for (uint8_t i = 0; i < 8; ++i) {
72 writePinLow(ADNS_SCLK_PIN); 86 writePinLow(ADNS5050_SCLK_PIN);
73 wait_us(1); 87 wait_us(1);
74 88
75 byte = (byte << 1) | readPin(ADNS_SDIO_PIN); 89 byte = (byte << 1) | readPin(ADNS5050_SDIO_PIN);
76 90
77 writePinHigh(ADNS_SCLK_PIN); 91 writePinHigh(ADNS5050_SCLK_PIN);
78 wait_us(1); 92 wait_us(1);
79 } 93 }
80 94
81 return byte; 95 return byte;
82} 96}
83 97
84void adns_serial_write(uint8_t data) { 98void adns5050_serial_write(uint8_t data) {
85 setPinOutput(ADNS_SDIO_PIN); 99 setPinOutput(ADNS5050_SDIO_PIN);
86 100
87 for (int8_t b = 7; b >= 0; b--) { 101 for (int8_t b = 7; b >= 0; b--) {
88 writePinLow(ADNS_SCLK_PIN); 102 writePinLow(ADNS5050_SCLK_PIN);
89 103
90 if (data & (1 << b)) 104 if (data & (1 << b))
91 writePinHigh(ADNS_SDIO_PIN); 105 writePinHigh(ADNS5050_SDIO_PIN);
92 else 106 else
93 writePinLow(ADNS_SDIO_PIN); 107 writePinLow(ADNS5050_SDIO_PIN);
94 108
95 wait_us(2); 109 wait_us(2);
96 110
97 writePinHigh(ADNS_SCLK_PIN); 111 writePinHigh(ADNS5050_SCLK_PIN);
98 } 112 }
99 113
100 // tSWR. See page 15 of the ADNS spec sheet. 114 // tSWR. See page 15 of the ADNS spec sheet.
@@ -108,17 +122,17 @@ void adns_serial_write(uint8_t data) {
108 122
109// Read a byte of data from a register on the ADNS. 123// Read a byte of data from a register on the ADNS.
110// Don't forget to use the register map (as defined in the header file). 124// Don't forget to use the register map (as defined in the header file).
111uint8_t adns_read_reg(uint8_t reg_addr) { 125uint8_t adns5050_read_reg(uint8_t reg_addr) {
112 adns_cs_select(); 126 adns5050_cs_select();
113 127
114 adns_serial_write(reg_addr); 128 adns5050_serial_write(reg_addr);
115 129
116 // We don't need a minimum tSRAD here. That's because a 4ms wait time is 130 // We don't need a minimum tSRAD here. That's because a 4ms wait time is
117 // already included in adns_serial_write(), so we're good. 131 // already included in adns5050_serial_write(), so we're good.
118 // See page 10 and 15 of the ADNS spec sheet. 132 // See page 10 and 15 of the ADNS spec sheet.
119 // wait_us(4); 133 // wait_us(4);
120 134
121 uint8_t byte = adns_serial_read(); 135 uint8_t byte = adns5050_serial_read();
122 136
123 // tSRW & tSRR. See page 15 of the ADNS spec sheet. 137 // tSRW & tSRR. See page 15 of the ADNS spec sheet.
124 // Technically, this is only necessary if the next operation is an SDIO 138 // Technically, this is only necessary if the next operation is an SDIO
@@ -126,38 +140,38 @@ uint8_t adns_read_reg(uint8_t reg_addr) {
126 // Honestly, this wait could probably be removed. 140 // Honestly, this wait could probably be removed.
127 wait_us(1); 141 wait_us(1);
128 142
129 adns_cs_deselect(); 143 adns5050_cs_deselect();
130 144
131 return byte; 145 return byte;
132} 146}
133 147
134void adns_write_reg(uint8_t reg_addr, uint8_t data) { 148void adns5050_write_reg(uint8_t reg_addr, uint8_t data) {
135 adns_cs_select(); 149 adns5050_cs_select();
136 adns_serial_write(0b10000000 | reg_addr); 150 adns5050_serial_write(0b10000000 | reg_addr);
137 adns_serial_write(data); 151 adns5050_serial_write(data);
138 adns_cs_deselect(); 152 adns5050_cs_deselect();
139} 153}
140 154
141report_adns_t adns_read_burst(void) { 155report_adns5050_t adns5050_read_burst(void) {
142 adns_cs_select(); 156 adns5050_cs_select();
143 157
144 report_adns_t data; 158 report_adns5050_t data;
145 data.dx = 0; 159 data.dx = 0;
146 data.dy = 0; 160 data.dy = 0;
147 161
148 adns_serial_write(REG_MOTION_BURST); 162 adns5050_serial_write(REG_MOTION_BURST);
149 163
150 // We don't need a minimum tSRAD here. That's because a 4ms wait time is 164 // We don't need a minimum tSRAD here. That's because a 4ms wait time is
151 // already included in adns_serial_write(), so we're good. 165 // already included in adns5050_serial_write(), so we're good.
152 // See page 10 and 15 of the ADNS spec sheet. 166 // See page 10 and 15 of the ADNS spec sheet.
153 // wait_us(4); 167 // wait_us(4);
154 168
155 uint8_t x = adns_serial_read(); 169 uint8_t x = adns5050_serial_read();
156 uint8_t y = adns_serial_read(); 170 uint8_t y = adns5050_serial_read();
157 171
158 // Burst mode returns a bunch of other shit that we don't really need. 172 // Burst mode returns a bunch of other shit that we don't really need.
159 // Setting CS to high ends burst mode early. 173 // Setting CS to high ends burst mode early.
160 adns_cs_deselect(); 174 adns5050_cs_deselect();
161 175
162 data.dx = convert_twoscomp(x); 176 data.dx = convert_twoscomp(x);
163 data.dy = convert_twoscomp(y); 177 data.dy = convert_twoscomp(y);
@@ -175,12 +189,21 @@ int8_t convert_twoscomp(uint8_t data) {
175} 189}
176 190
177// Don't forget to use the definitions for CPI in the header file. 191// Don't forget to use the definitions for CPI in the header file.
178void adns_set_cpi(uint8_t cpi) { adns_write_reg(REG_MOUSE_CONTROL2, cpi); } 192void adns5050_set_cpi(uint16_t cpi) {
193 uint8_t cpival = constrain((cpi / 125), 0x1, 0xD); // limits to 0--119
194
195 adns5050_write_reg(REG_MOUSE_CONTROL2, 0b10000 | cpival);
196}
197
198uint16_t adns5050_get_cpi(void) {
199 uint8_t cpival = adns5050_read_reg(REG_MOUSE_CONTROL2);
200 return (uint16_t)((cpival & 0b10000) * 125);
201}
179 202
180bool adns_check_signature(void) { 203bool adns5050_check_signature(void) {
181 uint8_t pid = adns_read_reg(REG_PRODUCT_ID); 204 uint8_t pid = adns5050_read_reg(REG_PRODUCT_ID);
182 uint8_t rid = adns_read_reg(REG_REVISION_ID); 205 uint8_t rid = adns5050_read_reg(REG_REVISION_ID);
183 uint8_t pid2 = adns_read_reg(REG_PRODUCT_ID2); 206 uint8_t pid2 = adns5050_read_reg(REG_PRODUCT_ID2);
184 207
185 return (pid == 0x12 && rid == 0x01 && pid2 == 0x26); 208 return (pid == 0x12 && rid == 0x01 && pid2 == 0x26);
186} 209}
diff --git a/drivers/sensors/adns5050.h b/drivers/sensors/adns5050.h
index 5e9edc296..e45a25019 100644
--- a/drivers/sensors/adns5050.h
+++ b/drivers/sensors/adns5050.h
@@ -21,59 +21,52 @@
21 21
22#include <stdbool.h> 22#include <stdbool.h>
23 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 24// CPI values
46#define CPI125 0x11 25// clang-format off
47#define CPI250 0x12 26#define CPI125 0x11
48#define CPI375 0x13 27#define CPI250 0x12
49#define CPI500 0x14 28#define CPI375 0x13
50#define CPI625 0x15 29#define CPI500 0x14
51#define CPI750 0x16 30#define CPI625 0x15
52#define CPI875 0x17 31#define CPI750 0x16
32#define CPI875 0x17
53#define CPI1000 0x18 33#define CPI1000 0x18
54#define CPI1125 0x19 34#define CPI1125 0x19
55#define CPI1250 0x1a 35#define CPI1250 0x1a
56#define CPI1375 0x1b 36#define CPI1375 0x1b
37// clang-format on
38
39#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
40
41// Definitions for the ADNS serial line.
42#ifndef ADNS5050_SCLK_PIN
43# error "No clock pin defined -- missing ADNS5050_SCLK_PIN"
44#endif
45
46#ifndef ADNS5050_SDIO_PIN
47# error "No data pin defined -- missing ADNS5050_SDIO_PIN"
48#endif
57 49
58#ifdef CONSOLE_ENABLE 50#ifndef ADNS5050_CS_PIN
59void print_byte(uint8_t byte); 51# error "No chip select pin defined -- missing ADNS5050_CS_PIN"
60#endif 52#endif
61 53
62typedef struct { 54typedef struct {
63 int8_t dx; 55 int8_t dx;
64 int8_t dy; 56 int8_t dy;
65} report_adns_t; 57} report_adns5050_t;
66 58
67// A bunch of functions to implement the ADNS5050-specific serial protocol. 59// A bunch of functions to implement the ADNS5050-specific serial protocol.
68// Note that the "serial.h" driver is insufficient, because it does not 60// Note that the "serial.h" driver is insufficient, because it does not
69// manually manipulate a serial clock signal. 61// manually manipulate a serial clock signal.
70void adns_init(void); 62void adns5050_init(void);
71void adns_sync(void); 63void adns5050_sync(void);
72uint8_t adns_serial_read(void); 64uint8_t adns5050_serial_read(void);
73void adns_serial_write(uint8_t data); 65void adns5050_serial_write(uint8_t data);
74uint8_t adns_read_reg(uint8_t reg_addr); 66uint8_t adns5050_read_reg(uint8_t reg_addr);
75void adns_write_reg(uint8_t reg_addr, uint8_t data); 67void adns5050_write_reg(uint8_t reg_addr, uint8_t data);
76report_adns_t adns_read_burst(void); 68report_adns5050_t adns5050_read_burst(void);
77int8_t convert_twoscomp(uint8_t data); 69void adns5050_set_cpi(uint16_t cpi);
78void adns_set_cpi(uint8_t cpi); 70uint16_t adns5050_get_cpi(void);
79bool adns_check_signature(void); 71int8_t convert_twoscomp(uint8_t data);
72bool adns5050_check_signature(void);
diff --git a/drivers/sensors/adns9800.c b/drivers/sensors/adns9800.c
index b4f683452..c52f99180 100644
--- a/drivers/sensors/adns9800.c
+++ b/drivers/sensors/adns9800.c
@@ -15,83 +15,80 @@
15 */ 15 */
16 16
17#include "spi_master.h" 17#include "spi_master.h"
18#include "quantum.h"
19#include "adns9800_srom_A6.h" 18#include "adns9800_srom_A6.h"
20#include "adns9800.h" 19#include "adns9800.h"
20#include "wait.h"
21 21
22// registers 22// registers
23#define REG_Product_ID 0x00 23// clang-format off
24#define REG_Revision_ID 0x01 24#define REG_Product_ID 0x00
25#define REG_Motion 0x02 25#define REG_Revision_ID 0x01
26#define REG_Delta_X_L 0x03 26#define REG_Motion 0x02
27#define REG_Delta_X_H 0x04 27#define REG_Delta_X_L 0x03
28#define REG_Delta_Y_L 0x05 28#define REG_Delta_X_H 0x04
29#define REG_Delta_Y_H 0x06 29#define REG_Delta_Y_L 0x05
30#define REG_SQUAL 0x07 30#define REG_Delta_Y_H 0x06
31#define REG_Pixel_Sum 0x08 31#define REG_SQUAL 0x07
32#define REG_Maximum_Pixel 0x09 32#define REG_Pixel_Sum 0x08
33#define REG_Minimum_Pixel 0x0a 33#define REG_Maximum_Pixel 0x09
34#define REG_Shutter_Lower 0x0b 34#define REG_Minimum_Pixel 0x0a
35#define REG_Shutter_Upper 0x0c 35#define REG_Shutter_Lower 0x0b
36#define REG_Frame_Period_Lower 0x0d 36#define REG_Shutter_Upper 0x0c
37#define REG_Frame_Period_Upper 0x0e 37#define REG_Frame_Period_Lower 0x0d
38#define REG_Configuration_I 0x0f 38#define REG_Frame_Period_Upper 0x0e
39#define REG_Configuration_II 0x10 39#define REG_Configuration_I 0x0f
40#define REG_Frame_Capture 0x12 40#define REG_Configuration_II 0x10
41#define REG_SROM_Enable 0x13 41#define REG_Frame_Capture 0x12
42#define REG_Run_Downshift 0x14 42#define REG_SROM_Enable 0x13
43#define REG_Rest1_Rate 0x15 43#define REG_Run_Downshift 0x14
44#define REG_Rest1_Downshift 0x16 44#define REG_Rest1_Rate 0x15
45#define REG_Rest2_Rate 0x17 45#define REG_Rest1_Downshift 0x16
46#define REG_Rest2_Downshift 0x18 46#define REG_Rest2_Rate 0x17
47#define REG_Rest3_Rate 0x19 47#define REG_Rest2_Downshift 0x18
48#define REG_Rest3_Rate 0x19
48#define REG_Frame_Period_Max_Bound_Lower 0x1a 49#define REG_Frame_Period_Max_Bound_Lower 0x1a
49#define REG_Frame_Period_Max_Bound_Upper 0x1b 50#define REG_Frame_Period_Max_Bound_Upper 0x1b
50#define REG_Frame_Period_Min_Bound_Lower 0x1c 51#define REG_Frame_Period_Min_Bound_Lower 0x1c
51#define REG_Frame_Period_Min_Bound_Upper 0x1d 52#define REG_Frame_Period_Min_Bound_Upper 0x1d
52#define REG_Shutter_Max_Bound_Lower 0x1e 53#define REG_Shutter_Max_Bound_Lower 0x1e
53#define REG_Shutter_Max_Bound_Upper 0x1f 54#define REG_Shutter_Max_Bound_Upper 0x1f
54#define REG_LASER_CTRL0 0x20 55#define REG_LASER_CTRL0 0x20
55#define REG_Observation 0x24 56#define REG_Observation 0x24
56#define REG_Data_Out_Lower 0x25 57#define REG_Data_Out_Lower 0x25
57#define REG_Data_Out_Upper 0x26 58#define REG_Data_Out_Upper 0x26
58#define REG_SROM_ID 0x2a 59#define REG_SROM_ID 0x2a
59#define REG_Lift_Detection_Thr 0x2e 60#define REG_Lift_Detection_Thr 0x2e
60#define REG_Configuration_V 0x2f 61#define REG_Configuration_V 0x2f
61#define REG_Configuration_IV 0x39 62#define REG_Configuration_IV 0x39
62#define REG_Power_Up_Reset 0x3a 63#define REG_Power_Up_Reset 0x3a
63#define REG_Shutdown 0x3b 64#define REG_Shutdown 0x3b
64#define REG_Inverse_Product_ID 0x3f 65#define REG_Inverse_Product_ID 0x3f
65#define REG_Motion_Burst 0x50 66#define REG_Motion_Burst 0x50
66#define REG_SROM_Load_Burst 0x62 67#define REG_SROM_Load_Burst 0x62
67#define REG_Pixel_Burst 0x64 68#define REG_Pixel_Burst 0x64
68 69
69#define ADNS_CLOCK_SPEED 2000000 70#define MIN_CPI 200
70#define MIN_CPI 200 71#define MAX_CPI 8200
71#define MAX_CPI 8200 72#define CPI_STEP 200
72#define CPI_STEP 200 73#define CLAMP_CPI(value) value<MIN_CPI ? MIN_CPI : value> MAX_CPI ? MAX_CPI : value
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 74#define US_BETWEEN_WRITES 120
77#define US_BETWEEN_READS 20 75#define US_BETWEEN_READS 20
78#define US_BEFORE_MOTION 100 76#define US_BEFORE_MOTION 100
79#define MSB1 0x80 77#define MSB1 0x80
78// clang-format on
80 79
81extern const uint8_t firmware_data[]; 80void adns9800_spi_start(void) { spi_start(ADNS9800_CS_PIN, false, ADNS9800_SPI_MODE, ADNS9800_SPI_DIVISOR); }
82 81
83void adns_spi_start(void) { spi_start(SPI_SS_PIN, false, SPI_MODE, SPI_DIVISOR); } 82void adns9800_write(uint8_t reg_addr, uint8_t data) {
84 83 adns9800_spi_start();
85void adns_write(uint8_t reg_addr, uint8_t data) {
86 adns_spi_start();
87 spi_write(reg_addr | MSB1); 84 spi_write(reg_addr | MSB1);
88 spi_write(data); 85 spi_write(data);
89 spi_stop(); 86 spi_stop();
90 wait_us(US_BETWEEN_WRITES); 87 wait_us(US_BETWEEN_WRITES);
91} 88}
92 89
93uint8_t adns_read(uint8_t reg_addr) { 90uint8_t adns9800_read(uint8_t reg_addr) {
94 adns_spi_start(); 91 adns9800_spi_start();
95 spi_write(reg_addr & 0x7f); 92 spi_write(reg_addr & 0x7f);
96 uint8_t data = spi_read(); 93 uint8_t data = spi_read();
97 spi_stop(); 94 spi_stop();
@@ -100,39 +97,39 @@ uint8_t adns_read(uint8_t reg_addr) {
100 return data; 97 return data;
101} 98}
102 99
103void adns_init() { 100void adns9800_init() {
104 setPinOutput(SPI_SS_PIN); 101 setPinOutput(ADNS9800_CS_PIN);
105 102
106 spi_init(); 103 spi_init();
107 104
108 // reboot 105 // reboot
109 adns_write(REG_Power_Up_Reset, 0x5a); 106 adns9800_write(REG_Power_Up_Reset, 0x5a);
110 wait_ms(50); 107 wait_ms(50);
111 108
112 // read registers and discard 109 // read registers and discard
113 adns_read(REG_Motion); 110 adns9800_read(REG_Motion);
114 adns_read(REG_Delta_X_L); 111 adns9800_read(REG_Delta_X_L);
115 adns_read(REG_Delta_X_H); 112 adns9800_read(REG_Delta_X_H);
116 adns_read(REG_Delta_Y_L); 113 adns9800_read(REG_Delta_Y_L);
117 adns_read(REG_Delta_Y_H); 114 adns9800_read(REG_Delta_Y_H);
118 115
119 // upload firmware 116 // upload firmware
120 117
121 // 3k firmware mode 118 // 3k firmware mode
122 adns_write(REG_Configuration_IV, 0x02); 119 adns9800_write(REG_Configuration_IV, 0x02);
123 120
124 // enable initialisation 121 // enable initialisation
125 adns_write(REG_SROM_Enable, 0x1d); 122 adns9800_write(REG_SROM_Enable, 0x1d);
126 123
127 // wait a frame 124 // wait a frame
128 wait_ms(10); 125 wait_ms(10);
129 126
130 // start SROM download 127 // start SROM download
131 adns_write(REG_SROM_Enable, 0x18); 128 adns9800_write(REG_SROM_Enable, 0x18);
132 129
133 // write the SROM file 130 // write the SROM file
134 131
135 adns_spi_start(); 132 adns9800_spi_start();
136 133
137 spi_write(REG_SROM_Load_Burst | 0x80); 134 spi_write(REG_SROM_Load_Burst | 0x80);
138 wait_us(15); 135 wait_us(15);
@@ -140,7 +137,7 @@ void adns_init() {
140 // send all bytes of the firmware 137 // send all bytes of the firmware
141 unsigned char c; 138 unsigned char c;
142 for (int i = 0; i < FIRMWARE_LENGTH; i++) { 139 for (int i = 0; i < FIRMWARE_LENGTH; i++) {
143 c = (unsigned char)pgm_read_byte(firmware_data + i); 140 c = (unsigned char)pgm_read_byte(adns9800_firmware_data + i);
144 spi_write(c); 141 spi_write(c);
145 wait_us(15); 142 wait_us(15);
146 } 143 }
@@ -150,18 +147,30 @@ void adns_init() {
150 wait_ms(10); 147 wait_ms(10);
151 148
152 // enable laser 149 // enable laser
153 uint8_t laser_ctrl0 = adns_read(REG_LASER_CTRL0); 150 uint8_t laser_ctrl0 = adns9800_read(REG_LASER_CTRL0);
154 adns_write(REG_LASER_CTRL0, laser_ctrl0 & 0xf0); 151 adns9800_write(REG_LASER_CTRL0, laser_ctrl0 & 0xf0);
152
153 adns9800_set_cpi(ADNS9800_CPI);
155} 154}
156 155
157config_adns_t adns_get_config(void) { 156config_adns9800_t adns9800_get_config(void) {
158 uint8_t config_1 = adns_read(REG_Configuration_I); 157 uint8_t config_1 = adns9800_read(REG_Configuration_I);
159 return (config_adns_t){(config_1 & 0xFF) * CPI_STEP}; 158 return (config_adns9800_t){(config_1 & 0xFF) * CPI_STEP};
160} 159}
161 160
162void adns_set_config(config_adns_t config) { 161void adns9800_set_config(config_adns9800_t config) {
163 uint8_t config_1 = (CLAMP_CPI(config.cpi) / CPI_STEP) & 0xFF; 162 uint8_t config_1 = (CLAMP_CPI(config.cpi) / CPI_STEP) & 0xFF;
164 adns_write(REG_Configuration_I, config_1); 163 adns9800_write(REG_Configuration_I, config_1);
164}
165
166uint16_t adns9800_get_cpi(void) {
167 uint8_t config_1 = adns9800_read(REG_Configuration_I);
168 return (uint16_t){(config_1 & 0xFF) * CPI_STEP};
169}
170
171void adns9800_set_cpi(uint16_t cpi) {
172 uint8_t config_1 = (CLAMP_CPI(cpi) / CPI_STEP) & 0xFF;
173 adns9800_write(REG_Configuration_I, config_1);
165} 174}
166 175
167static int16_t convertDeltaToInt(uint8_t high, uint8_t low) { 176static int16_t convertDeltaToInt(uint8_t high, uint8_t low) {
@@ -174,10 +183,10 @@ static int16_t convertDeltaToInt(uint8_t high, uint8_t low) {
174 return twos_comp; 183 return twos_comp;
175} 184}
176 185
177report_adns_t adns_get_report(void) { 186report_adns9800_t adns9800_get_report(void) {
178 report_adns_t report = {0, 0}; 187 report_adns9800_t report = {0, 0};
179 188
180 adns_spi_start(); 189 adns9800_spi_start();
181 190
182 // start burst mode 191 // start burst mode
183 spi_write(REG_Motion_Burst & 0x7f); 192 spi_write(REG_Motion_Burst & 0x7f);
diff --git a/drivers/sensors/adns9800.h b/drivers/sensors/adns9800.h
index d19ded401..e75a869c0 100644
--- a/drivers/sensors/adns9800.h
+++ b/drivers/sensors/adns9800.h
@@ -18,18 +18,48 @@
18 18
19#include <stdint.h> 19#include <stdint.h>
20 20
21#ifndef ADNS9800_CPI
22# define ADNS9800_CPI 1600
23#endif
24
25#ifndef ADNS9800_CLOCK_SPEED
26# define ADNS9800_CLOCK_SPEED 2000000
27#endif
28
29#ifndef ADNS9800_SPI_LSBFIRST
30# define ADNS9800_SPI_LSBFIRST false
31#endif
32
33#ifndef ADNS9800_SPI_MODE
34# define ADNS9800_SPI_MODE 3
35#endif
36
37#ifndef ADNS9800_SPI_DIVISOR
38# ifdef __AVR__
39# define ADNS9800_SPI_DIVISOR (F_CPU / ADNS9800_CLOCK_SPEED)
40# else
41# define ADNS9800_SPI_DIVISOR 64
42# endif
43#endif
44
45#ifndef ADNS9800_CS_PIN
46# error "No chip select pin defined -- missing ADNS9800_CS_PIN"
47#endif
48
21typedef struct { 49typedef struct {
22 /* 200 - 8200 CPI supported */ 50 /* 200 - 8200 CPI supported */
23 uint16_t cpi; 51 uint16_t cpi;
24} config_adns_t; 52} config_adns9800_t;
25 53
26typedef struct { 54typedef struct {
27 int16_t x; 55 int16_t x;
28 int16_t y; 56 int16_t y;
29} report_adns_t; 57} report_adns9800_t;
30 58
31void adns_init(void); 59void adns9800_init(void);
32config_adns_t adns_get_config(void); 60config_adns9800_t adns9800_get_config(void);
33void adns_set_config(config_adns_t); 61void adns9800_set_config(config_adns9800_t);
62uint16_t adns9800_get_cpi(void);
63void adns9800_set_cpi(uint16_t cpi);
34/* Reads and clears the current delta values on the ADNS sensor */ 64/* Reads and clears the current delta values on the ADNS sensor */
35report_adns_t adns_get_report(void); 65report_adns9800_t adns9800_get_report(void);
diff --git a/drivers/sensors/adns9800_srom_A6.h b/drivers/sensors/adns9800_srom_A6.h
index e698a401b..d86ecbbd9 100644
--- a/drivers/sensors/adns9800_srom_A6.h
+++ b/drivers/sensors/adns9800_srom_A6.h
@@ -6,7 +6,7 @@
6 6
7// clang-format off 7// clang-format off
8 8
9const uint8_t firmware_data[FIRMWARE_LENGTH] PROGMEM = { 9const uint8_t adns9800_firmware_data[FIRMWARE_LENGTH] PROGMEM = {
10 0x03, 0xA6, 0x68, 0x1E, 0x7D, 0x10, 0x7E, 0x7E, 0x5F, 0x1C, 0xB8, 0xF2, 0x47, 0x0C, 0x7B, 0x74, 10 0x03, 0xA6, 0x68, 0x1E, 0x7D, 0x10, 0x7E, 0x7E, 0x5F, 0x1C, 0xB8, 0xF2, 0x47, 0x0C, 0x7B, 0x74,
11 0x4B, 0x14, 0x8B, 0x75, 0x66, 0x51, 0x0B, 0x8C, 0x76, 0x74, 0x4B, 0x14, 0xAA, 0xD6, 0x0F, 0x9C, 11 0x4B, 0x14, 0x8B, 0x75, 0x66, 0x51, 0x0B, 0x8C, 0x76, 0x74, 0x4B, 0x14, 0xAA, 0xD6, 0x0F, 0x9C,
12 0xBA, 0xF6, 0x6E, 0x3F, 0xDD, 0x38, 0xD5, 0x02, 0x80, 0x9B, 0x82, 0x6D, 0x58, 0x13, 0xA4, 0xAB, 12 0xBA, 0xF6, 0x6E, 0x3F, 0xDD, 0x38, 0xD5, 0x02, 0x80, 0x9B, 0x82, 0x6D, 0x58, 0x13, 0xA4, 0xAB,
diff --git a/drivers/sensors/analog_joystick.c b/drivers/sensors/analog_joystick.c
new file mode 100644
index 000000000..0f4d1d7a4
--- /dev/null
+++ b/drivers/sensors/analog_joystick.c
@@ -0,0 +1,94 @@
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 "analog_joystick.h"
18#include "analog.h"
19#include "gpio.h"
20#include "wait.h"
21
22// Set Parameters
23uint16_t minAxisValue = ANALOG_JOYSTICK_AXIS_MIN;
24uint16_t maxAxisValue = ANALOG_JOYSTICK_AXIS_MAX;
25
26uint8_t maxCursorSpeed = ANALOG_JOYSTICK_SPEED_MAX;
27uint8_t speedRegulator = ANALOG_JOYSTICK_SPEED_REGULATOR; // Lower Values Create Faster Movement
28
29int16_t xOrigin, yOrigin;
30
31uint16_t lastCursor = 0;
32
33int16_t axisCoordinate(uint8_t pin, uint16_t origin) {
34 int8_t direction;
35 int16_t distanceFromOrigin;
36 int16_t range;
37
38 int16_t position = analogReadPin(pin);
39
40 if (origin == position) {
41 return 0;
42 } else if (origin > position) {
43 distanceFromOrigin = origin - position;
44 range = origin - minAxisValue;
45 direction = -1;
46 } else {
47 distanceFromOrigin = position - origin;
48 range = maxAxisValue - origin;
49 direction = 1;
50 }
51
52 float percent = (float)distanceFromOrigin / range;
53 int16_t coordinate = (int16_t)(percent * 100);
54 if (coordinate < 0) {
55 return 0;
56 } else if (coordinate > 100) {
57 return 100 * direction;
58 } else {
59 return coordinate * direction;
60 }
61}
62
63int8_t axisToMouseComponent(uint8_t pin, int16_t origin, uint8_t maxSpeed) {
64 int16_t coordinate = axisCoordinate(pin, origin);
65 if (coordinate != 0) {
66 float percent = (float)coordinate / 100;
67 return percent * maxCursorSpeed * (abs(coordinate) / speedRegulator);
68 } else {
69 return 0;
70 }
71}
72
73report_analog_joystick_t analog_joystick_read(void) {
74 report_analog_joystick_t report = {0};
75
76 if (timer_elapsed(lastCursor) > ANALOG_JOYSTICK_READ_INTERVAL) {
77 lastCursor = timer_read();
78 report.x = axisToMouseComponent(ANALOG_JOYSTICK_X_AXIS_PIN, xOrigin, maxCursorSpeed);
79 report.y = axisToMouseComponent(ANALOG_JOYSTICK_Y_AXIS_PIN, yOrigin, maxCursorSpeed);
80 }
81#ifdef ANALOG_JOYSTICK_CLICK_PIN
82 report.button = !readPin(ANALOG_JOYSTICK_CLICK_PIN);
83#endif
84 return report;
85}
86
87void analog_joystick_init(void) {
88#ifdef ANALOG_JOYSTICK_CLICK_PIN
89 setPinInputHigh(ANALOG_JOYSTICK_CLICK_PIN);
90#endif
91 // Account for drift
92 xOrigin = analogReadPin(ANALOG_JOYSTICK_X_AXIS_PIN);
93 yOrigin = analogReadPin(ANALOG_JOYSTICK_Y_AXIS_PIN);
94}
diff --git a/drivers/sensors/analog_joystick.h b/drivers/sensors/analog_joystick.h
new file mode 100644
index 000000000..6892a0881
--- /dev/null
+++ b/drivers/sensors/analog_joystick.h
@@ -0,0 +1,51 @@
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 <stdbool.h>
20#include <stdint.h>
21
22#ifndef ANALOG_JOYSTICK_X_AXIS_PIN
23# error No pin specified for X Axis
24#endif
25#ifndef ANALOG_JOYSTICK_Y_AXIS_PIN
26# error No pin specified for Y Axis
27#endif
28
29#ifndef ANALOG_JOYSTICK_AXIS_MIN
30# define ANALOG_JOYSTICK_AXIS_MIN 0
31#endif
32#ifndef ANALOG_JOYSTICK_AXIS_MAX
33# define ANALOG_JOYSTICK_AXIS_MAX 1023
34#endif
35#ifndef ANALOG_JOYSTICK_SPEED_REGULATOR
36# define ANALOG_JOYSTICK_SPEED_REGULATOR 20
37#endif
38#ifndef ANALOG_JOYSTICK_READ_INTERVAL
39# define ANALOG_JOYSTICK_READ_INTERVAL 10
40#endif
41#ifndef ANALOG_JOYSTICK_SPEED_MAX
42# define ANALOG_JOYSTICK_SPEED_MAX 2
43#endif
44
45typedef struct {
46 int8_t x;
47 int8_t y;
48 bool button;
49} report_analog_joystick_t;
50report_analog_joystick_t analog_joystick_read(void);
51void analog_joystick_init(void);
diff --git a/drivers/sensors/cirque_pinnacle.c b/drivers/sensors/cirque_pinnacle.c
new file mode 100644
index 000000000..b807c4f07
--- /dev/null
+++ b/drivers/sensors/cirque_pinnacle.c
@@ -0,0 +1,232 @@
1// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license
2#include "cirque_pinnacle.h"
3#include "print.h"
4#include "debug.h"
5#include "wait.h"
6
7// Registers for RAP
8// clang-format off
9#define FIRMWARE_ID 0x00
10#define FIRMWARE_VERSION_C 0x01
11#define STATUS_1 0x02
12#define SYSCONFIG_1 0x03
13#define FEEDCONFIG_1 0x04
14#define FEEDCONFIG_2 0x05
15#define CALIBRATION_CONFIG_1 0x07
16#define PS2_AU_CONTROL 0x08
17#define SAMPLE_RATE 0x09
18#define Z_IDLE_COUNT 0x0A
19#define Z_SCALER 0x0B
20#define SLEEP_INTERVAL 0x0C
21#define SLEEP_TIMER 0x0D
22#define PACKET_BYTE_0 0x12
23#define PACKET_BYTE_1 0x13
24#define PACKET_BYTE_2 0x14
25#define PACKET_BYTE_3 0x15
26#define PACKET_BYTE_4 0x16
27#define PACKET_BYTE_5 0x17
28
29#define ERA_VALUE 0x1B
30#define ERA_HIGH_BYTE 0x1C
31#define ERA_LOW_BYTE 0x1D
32#define ERA_CONTROL 0x1E
33
34// ADC-attenuation settings (held in BIT_7 and BIT_6)
35// 1X = most sensitive, 4X = least sensitive
36#define ADC_ATTENUATE_1X 0x00
37#define ADC_ATTENUATE_2X 0x40
38#define ADC_ATTENUATE_3X 0x80
39#define ADC_ATTENUATE_4X 0xC0
40
41// Register config values for this demo
42#define SYSCONFIG_1_VALUE 0x00
43#define FEEDCONFIG_1_VALUE 0x03 // 0x03 for absolute mode 0x01 for relative mode
44#define FEEDCONFIG_2_VALUE 0x1C // 0x1F for normal functionality 0x1E for intellimouse disabled
45#define Z_IDLE_COUNT_VALUE 0x05
46// clang-format on
47
48bool touchpad_init;
49uint16_t scale_data = 1024;
50
51void cirque_pinnacle_clear_flags(void);
52void cirque_pinnacle_enable_feed(bool feedEnable);
53void RAP_ReadBytes(uint8_t address, uint8_t* data, uint8_t count);
54void RAP_Write(uint8_t address, uint8_t data);
55
56#ifdef CONSOLE_ENABLE
57void print_byte(uint8_t byte) { xprintf("%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')); }
58#endif
59
60/* Logical Scaling Functions */
61// Clips raw coordinates to "reachable" window of sensor
62// NOTE: values outside this window can only appear as a result of noise
63void ClipCoordinates(pinnacle_data_t* coordinates) {
64 if (coordinates->xValue < CIRQUE_PINNACLE_X_LOWER) {
65 coordinates->xValue = CIRQUE_PINNACLE_X_LOWER;
66 } else if (coordinates->xValue > CIRQUE_PINNACLE_X_UPPER) {
67 coordinates->xValue = CIRQUE_PINNACLE_X_UPPER;
68 }
69 if (coordinates->yValue < CIRQUE_PINNACLE_Y_LOWER) {
70 coordinates->yValue = CIRQUE_PINNACLE_Y_LOWER;
71 } else if (coordinates->yValue > CIRQUE_PINNACLE_Y_UPPER) {
72 coordinates->yValue = CIRQUE_PINNACLE_Y_UPPER;
73 }
74}
75
76uint16_t cirque_pinnacle_get_scale(void) { return scale_data; }
77void cirque_pinnacle_set_scale(uint16_t scale) { scale_data = scale; }
78
79// Scales data to desired X & Y resolution
80void cirque_pinnacle_scale_data(pinnacle_data_t* coordinates, uint16_t xResolution, uint16_t yResolution) {
81 uint32_t xTemp = 0;
82 uint32_t yTemp = 0;
83
84 ClipCoordinates(coordinates);
85
86 xTemp = coordinates->xValue;
87 yTemp = coordinates->yValue;
88
89 // translate coordinates to (0, 0) reference by subtracting edge-offset
90 xTemp -= CIRQUE_PINNACLE_X_LOWER;
91 yTemp -= CIRQUE_PINNACLE_Y_LOWER;
92
93 // scale coordinates to (xResolution, yResolution) range
94 coordinates->xValue = (uint16_t)(xTemp * xResolution / CIRQUE_PINNACLE_X_RANGE);
95 coordinates->yValue = (uint16_t)(yTemp * yResolution / CIRQUE_PINNACLE_Y_RANGE);
96}
97
98// Clears Status1 register flags (SW_CC and SW_DR)
99void cirque_pinnacle_clear_flags() {
100 RAP_Write(STATUS_1, 0x00);
101 wait_us(50);
102}
103
104// Enables/Disables the feed
105void cirque_pinnacle_enable_feed(bool feedEnable) {
106 uint8_t temp;
107
108 RAP_ReadBytes(FEEDCONFIG_1, &temp, 1); // Store contents of FeedConfig1 register
109
110 if (feedEnable) {
111 temp |= 0x01; // Set Feed Enable bit
112 RAP_Write(0x04, temp);
113 } else {
114 temp &= ~0x01; // Clear Feed Enable bit
115 RAP_Write(0x04, temp);
116 }
117}
118
119/* ERA (Extended Register Access) Functions */
120// Reads <count> bytes from an extended register at <address> (16-bit address),
121// stores values in <*data>
122void ERA_ReadBytes(uint16_t address, uint8_t* data, uint16_t count) {
123 uint8_t ERAControlValue = 0xFF;
124
125 cirque_pinnacle_enable_feed(false); // Disable feed
126
127 RAP_Write(ERA_HIGH_BYTE, (uint8_t)(address >> 8)); // Send upper byte of ERA address
128 RAP_Write(ERA_LOW_BYTE, (uint8_t)(address & 0x00FF)); // Send lower byte of ERA address
129
130 for (uint16_t i = 0; i < count; i++) {
131 RAP_Write(ERA_CONTROL, 0x05); // Signal ERA-read (auto-increment) to Pinnacle
132
133 // Wait for status register 0x1E to clear
134 do {
135 RAP_ReadBytes(ERA_CONTROL, &ERAControlValue, 1);
136 } while (ERAControlValue != 0x00);
137
138 RAP_ReadBytes(ERA_VALUE, data + i, 1);
139
140 cirque_pinnacle_clear_flags();
141 }
142}
143
144// Writes a byte, <data>, to an extended register at <address> (16-bit address)
145void ERA_WriteByte(uint16_t address, uint8_t data) {
146 uint8_t ERAControlValue = 0xFF;
147
148 cirque_pinnacle_enable_feed(false); // Disable feed
149
150 RAP_Write(ERA_VALUE, data); // Send data byte to be written
151
152 RAP_Write(ERA_HIGH_BYTE, (uint8_t)(address >> 8)); // Upper byte of ERA address
153 RAP_Write(ERA_LOW_BYTE, (uint8_t)(address & 0x00FF)); // Lower byte of ERA address
154
155 RAP_Write(ERA_CONTROL, 0x02); // Signal an ERA-write to Pinnacle
156
157 // Wait for status register 0x1E to clear
158 do {
159 RAP_ReadBytes(ERA_CONTROL, &ERAControlValue, 1);
160 } while (ERAControlValue != 0x00);
161
162 cirque_pinnacle_clear_flags();
163}
164
165void cirque_pinnacle_set_adc_attenuation(uint8_t adcGain) {
166 uint8_t temp = 0x00;
167
168 ERA_ReadBytes(0x0187, &temp, 1);
169 temp &= 0x3F; // clear top two bits
170 temp |= adcGain;
171 ERA_WriteByte(0x0187, temp);
172 ERA_ReadBytes(0x0187, &temp, 1);
173}
174
175// Changes thresholds to improve detection of fingers
176void cirque_pinnacle_tune_edge_sensitivity(void) {
177 uint8_t temp = 0x00;
178
179 ERA_ReadBytes(0x0149, &temp, 1);
180 ERA_WriteByte(0x0149, 0x04);
181 ERA_ReadBytes(0x0149, &temp, 1);
182
183 ERA_ReadBytes(0x0168, &temp, 1);
184 ERA_WriteByte(0x0168, 0x03);
185 ERA_ReadBytes(0x0168, &temp, 1);
186}
187
188/* Pinnacle-based TM040040 Functions */
189void cirque_pinnacle_init(void) {
190#if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)
191 spi_init();
192#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c)
193 i2c_init();
194#endif
195
196 touchpad_init = true;
197 // Host clears SW_CC flag
198 cirque_pinnacle_clear_flags();
199
200 // Host configures bits of registers 0x03 and 0x05
201 RAP_Write(SYSCONFIG_1, SYSCONFIG_1_VALUE);
202 RAP_Write(FEEDCONFIG_2, FEEDCONFIG_2_VALUE);
203
204 // Host enables preferred output mode (absolute)
205 RAP_Write(FEEDCONFIG_1, FEEDCONFIG_1_VALUE);
206
207 // Host sets z-idle packet count to 5 (default is 30)
208 RAP_Write(Z_IDLE_COUNT, Z_IDLE_COUNT_VALUE);
209
210 cirque_pinnacle_set_adc_attenuation(0xFF);
211 cirque_pinnacle_tune_edge_sensitivity();
212 cirque_pinnacle_enable_feed(true);
213}
214
215// Reads XYZ data from Pinnacle registers 0x14 through 0x17
216// Stores result in pinnacle_data_t struct with xValue, yValue, and zValue members
217pinnacle_data_t cirque_pinnacle_read_data(void) {
218 uint8_t data[6] = {0};
219 pinnacle_data_t result = {0};
220 RAP_ReadBytes(PACKET_BYTE_0, data, 6);
221
222 cirque_pinnacle_clear_flags();
223
224 result.buttonFlags = data[0] & 0x3F;
225 result.xValue = data[2] | ((data[4] & 0x0F) << 8);
226 result.yValue = data[3] | ((data[4] & 0xF0) << 4);
227 result.zValue = data[5] & 0x3F;
228
229 result.touchDown = (result.xValue != 0 || result.yValue != 0);
230
231 return result;
232}
diff --git a/drivers/sensors/cirque_pinnacle.h b/drivers/sensors/cirque_pinnacle.h
new file mode 100644
index 000000000..db891122a
--- /dev/null
+++ b/drivers/sensors/cirque_pinnacle.h
@@ -0,0 +1,74 @@
1// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license
2
3#pragma once
4
5#include <stdint.h>
6#include <stdbool.h>
7
8// Convenient way to store and access measurements
9typedef struct {
10 uint16_t xValue;
11 uint16_t yValue;
12 uint16_t zValue;
13 uint8_t buttonFlags;
14 bool touchDown;
15} pinnacle_data_t;
16
17void cirque_pinnacle_init(void);
18pinnacle_data_t cirque_pinnacle_read_data(void);
19void cirque_pinnacle_scale_data(pinnacle_data_t* coordinates, uint16_t xResolution, uint16_t yResolution);
20uint16_t cirque_pinnacle_get_scale(void);
21void cirque_pinnacle_set_scale(uint16_t scale);
22
23#ifndef CIRQUE_PINNACLE_TIMEOUT
24# define CIRQUE_PINNACLE_TIMEOUT 20
25#endif
26
27// Coordinate scaling values
28#ifndef CIRQUE_PINNACLE_X_LOWER
29# define CIRQUE_PINNACLE_X_LOWER 127 // min "reachable" X value
30#endif
31#ifndef CIRQUE_PINNACLE_X_UPPER
32# define CIRQUE_PINNACLE_X_UPPER 1919 // max "reachable" X value
33#endif
34#ifndef CIRQUE_PINNACLE_Y_LOWER
35# define CIRQUE_PINNACLE_Y_LOWER 63 // min "reachable" Y value
36#endif
37#ifndef CIRQUE_PINNACLE_Y_UPPER
38# define CIRQUE_PINNACLE_Y_UPPER 1471 // max "reachable" Y value
39#endif
40#ifndef CIRQUE_PINNACLE_X_RANGE
41# define CIRQUE_PINNACLE_X_RANGE (CIRQUE_PINNACLE_X_UPPER - CIRQUE_PINNACLE_X_LOWER)
42#endif
43#ifndef CIRQUE_PINNACLE_Y_RANGE
44# define CIRQUE_PINNACLE_Y_RANGE (CIRQUE_PINNACLE_Y_UPPER - CIRQUE_PINNACLE_Y_LOWER)
45#endif
46
47#if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c)
48# include "i2c_master.h"
49// Cirque's 7-bit I2C Slave Address
50# ifndef CIRQUE_PINNACLE_ADDR
51# define CIRQUE_PINNACLE_ADDR 0x2A
52# endif
53#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)
54# include "spi_master.h"
55# ifndef CIRQUE_PINNACLE_CLOCK_SPEED
56# define CIRQUE_PINNACLE_CLOCK_SPEED 10000000
57# endif
58# ifndef CIRQUE_PINNACLE_SPI_LSBFIRST
59# define CIRQUE_PINNACLE_SPI_LSBFIRST false
60# endif
61# ifndef CIRQUE_PINNACLE_SPI_MODE
62# define CIRQUE_PINNACLE_SPI_MODE 1
63# endif
64# ifndef CIRQUE_PINNACLE_SPI_DIVISOR
65# ifdef __AVR__
66# define CIRQUE_PINNACLE_SPI_DIVISOR (F_CPU / CIRQUE_PINNACLE_CLOCK_SPEED)
67# else
68# define CIRQUE_PINNACLE_SPI_DIVISOR 64
69# endif
70# ifndef CIRQUE_PINNACLE_SPI_CS_PIN
71# error "No Chip Select pin has been defined -- missing CIRQUE_PINNACLE_SPI_CS_PIN define"
72# endif
73# endif
74#endif
diff --git a/drivers/sensors/cirque_pinnacle_i2c.c b/drivers/sensors/cirque_pinnacle_i2c.c
new file mode 100644
index 000000000..81dd982b0
--- /dev/null
+++ b/drivers/sensors/cirque_pinnacle_i2c.c
@@ -0,0 +1,43 @@
1// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license
2#include "cirque_pinnacle.h"
3#include "i2c_master.h"
4#include "print.h"
5#include "debug.h"
6#include "stdio.h"
7
8// Masks for Cirque Register Access Protocol (RAP)
9#define WRITE_MASK 0x80
10#define READ_MASK 0xA0
11
12extern bool touchpad_init;
13
14/* RAP Functions */
15// Reads <count> Pinnacle registers starting at <address>
16void RAP_ReadBytes(uint8_t address, uint8_t* data, uint8_t count) {
17 uint8_t cmdByte = READ_MASK | address; // Form the READ command byte
18 if (touchpad_init) {
19 i2c_writeReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, NULL, 0, CIRQUE_PINNACLE_TIMEOUT);
20 if (i2c_readReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, data, count, CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) {
21#ifdef CONSOLE_ENABLE
22 dprintf("error right touchpad\n");
23#endif
24 touchpad_init = false;
25 }
26 i2c_stop();
27 }
28}
29
30// Writes single-byte <data> to <address>
31void RAP_Write(uint8_t address, uint8_t data) {
32 uint8_t cmdByte = WRITE_MASK | address; // Form the WRITE command byte
33
34 if (touchpad_init) {
35 if (i2c_writeReg(CIRQUE_PINNACLE_ADDR << 1, cmdByte, &data, sizeof(data), CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) {
36#ifdef CONSOLE_ENABLE
37 dprintf("error right touchpad\n");
38#endif
39 touchpad_init = false;
40 }
41 i2c_stop();
42 }
43}
diff --git a/drivers/sensors/cirque_pinnacle_spi.c b/drivers/sensors/cirque_pinnacle_spi.c
new file mode 100644
index 000000000..f3eee8875
--- /dev/null
+++ b/drivers/sensors/cirque_pinnacle_spi.c
@@ -0,0 +1,52 @@
1// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license
2#include "cirque_pinnacle.h"
3#include "spi_master.h"
4#include "print.h"
5#include "debug.h"
6
7// Masks for Cirque Register Access Protocol (RAP)
8#define WRITE_MASK 0x80
9#define READ_MASK 0xA0
10
11extern bool touchpad_init;
12
13/* RAP Functions */
14// Reads <count> Pinnacle registers starting at <address>
15void RAP_ReadBytes(uint8_t address, uint8_t* data, uint8_t count) {
16 uint8_t cmdByte = READ_MASK | address; // Form the READ command byte
17 if (touchpad_init) {
18 if (spi_start(CIRQUE_PINNACLE_SPI_CS_PIN, CIRQUE_TRACKPAD_SPI_LSBFIRST, CIRQUE_PINNACLE_SPI_MODE, CIRQUE_PINNACLE_SPI_DIVISOR)) {
19 spi_write(cmdByte);
20 spi_read(); // filler
21 spi_read(); // filler
22 for (uint8_t i = 0; i < count; i++) {
23 data[i] = spi_read(); // each sepsequent read gets another register's contents
24 }
25 } else {
26#ifdef CONSOLE_ENABLE
27 dprintf("error right touchpad\n");
28#endif
29 touchpad_init = false;
30 j
31 }
32 spi_stop();
33 }
34}
35
36// Writes single-byte <data> to <address>
37void RAP_Write(uint8_t address, uint8_t data) {
38 uint8_t cmdByte = WRITE_MASK | address; // Form the WRITE command byte
39
40 if (touchpad_init) {
41 if (spi_start(CIRQUE_PINNACLE_SPI_CS_PIN, CIRQUE_TRACKPAD_SPI_LSBFIRST, CIRQUE_PINNACLE_SPI_MODE, CIRQUE_PINNACLE_SPI_DIVISOR)) {
42 spi_write(cmdByte);
43 spi_write(data);
44 } else {
45#ifdef CONSOLE_ENABLE
46 dprintf("error right touchpad\n");
47#endif
48 touchpad_init = false;
49 }
50 spi_stop();
51 }
52}
diff --git a/drivers/sensors/pimoroni_trackball.c b/drivers/sensors/pimoroni_trackball.c
index 48098ff0c..7d390056e 100644
--- a/drivers/sensors/pimoroni_trackball.c
+++ b/drivers/sensors/pimoroni_trackball.c
@@ -17,73 +17,55 @@
17#include "pimoroni_trackball.h" 17#include "pimoroni_trackball.h"
18#include "i2c_master.h" 18#include "i2c_master.h"
19#include "print.h" 19#include "print.h"
20 20#include "debug.h"
21#ifndef PIMORONI_TRACKBALL_ADDRESS 21#include "timer.h"
22# define PIMORONI_TRACKBALL_ADDRESS 0x0A 22
23#endif 23// clang-format off
24#ifndef PIMORONI_TRACKBALL_INTERVAL_MS 24#define PIMORONI_TRACKBALL_REG_LED_RED 0x00
25# define PIMORONI_TRACKBALL_INTERVAL_MS 8 25#define PIMORONI_TRACKBALL_REG_LED_GRN 0x01
26#endif 26#define PIMORONI_TRACKBALL_REG_LED_BLU 0x02
27#ifndef PIMORONI_TRACKBALL_MOUSE_SCALE 27#define PIMORONI_TRACKBALL_REG_LED_WHT 0x03
28# define PIMORONI_TRACKBALL_MOUSE_SCALE 5 28#define PIMORONI_TRACKBALL_REG_LEFT 0x04
29#endif 29#define PIMORONI_TRACKBALL_REG_RIGHT 0x05
30#ifndef PIMORONI_TRACKBALL_SCROLL_SCALE 30#define PIMORONI_TRACKBALL_REG_UP 0x06
31# define PIMORONI_TRACKBALL_SCROLL_SCALE 1 31#define PIMORONI_TRACKBALL_REG_DOWN 0x07
32#endif 32// clang-format on
33#ifndef PIMORONI_TRACKBALL_DEBOUNCE_CYCLES 33
34# define PIMORONI_TRACKBALL_DEBOUNCE_CYCLES 20 34static uint16_t precision = 128;
35#endif 35
36#ifndef PIMORONI_TRACKBALL_ERROR_COUNT 36float pimoroni_trackball_get_precision(void) { return ((float)precision / 128); }
37# define PIMORONI_TRACKBALL_ERROR_COUNT 10 37void pimoroni_trackball_set_precision(float floatprecision) { precision = (floatprecision * 128); }
38#endif 38
39 39void pimoroni_trackball_set_rgbw(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
40#define TRACKBALL_TIMEOUT 100
41#define TRACKBALL_REG_LED_RED 0x00
42#define TRACKBALL_REG_LED_GRN 0x01
43#define TRACKBALL_REG_LED_BLU 0x02
44#define TRACKBALL_REG_LED_WHT 0x03
45#define TRACKBALL_REG_LEFT 0x04
46#define TRACKBALL_REG_RIGHT 0x05
47#define TRACKBALL_REG_UP 0x06
48#define TRACKBALL_REG_DOWN 0x07
49
50static pimoroni_data current_pimoroni_data;
51static report_mouse_t mouse_report;
52static bool scrolling = false;
53static int16_t x_offset = 0;
54static int16_t y_offset = 0;
55static int16_t h_offset = 0;
56static int16_t v_offset = 0;
57static uint16_t precision = 128;
58static uint8_t error_count = 0;
59
60float trackball_get_precision(void) { return ((float)precision / 128); }
61void trackball_set_precision(float floatprecision) { precision = (floatprecision * 128); }
62bool trackball_is_scrolling(void) { return scrolling; }
63void trackball_set_scrolling(bool scroll) { scrolling = scroll; }
64
65void trackball_set_rgbw(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
66 uint8_t data[4] = {r, g, b, w}; 40 uint8_t data[4] = {r, g, b, w};
67 __attribute__((unused)) i2c_status_t status = i2c_writeReg(PIMORONI_TRACKBALL_ADDRESS << 1, TRACKBALL_REG_LED_RED, data, sizeof(data), TRACKBALL_TIMEOUT); 41 __attribute__((unused)) i2c_status_t status = i2c_writeReg(PIMORONI_TRACKBALL_ADDRESS << 1, PIMORONI_TRACKBALL_REG_LED_RED, data, sizeof(data), PIMORONI_TRACKBALL_TIMEOUT);
68#ifdef TRACKBALL_DEBUG 42
69 dprintf("Trackball RGBW i2c_status_t: %d\n", status); 43#ifdef CONSOLE_ENABLE
44 if (debug_mouse) dprintf("Trackball RGBW i2c_status_t: %d\n", status);
70#endif 45#endif
71} 46}
72 47
73i2c_status_t read_pimoroni_trackball(pimoroni_data* data) { 48i2c_status_t read_pimoroni_trackball(pimoroni_data_t* data) {
74 i2c_status_t status = i2c_readReg(PIMORONI_TRACKBALL_ADDRESS << 1, TRACKBALL_REG_LEFT, (uint8_t*)data, sizeof(*data), TRACKBALL_TIMEOUT); 49 i2c_status_t status = i2c_readReg(PIMORONI_TRACKBALL_ADDRESS << 1, PIMORONI_TRACKBALL_REG_LEFT, (uint8_t*)data, sizeof(*data), PIMORONI_TRACKBALL_TIMEOUT);
75#ifdef TRACKBALL_DEBUG 50#ifdef CONSOLE_ENABLE
76 dprintf("Trackball READ i2c_status_t: %d\nLeft: %d\nRight: %d\nUp: %d\nDown: %d\nSwtich: %d\n", status, data->left, data->right, data->up, data->down, data->click); 51 if (debug_mouse) {
52 static uint16_t d_timer;
53 if (timer_elapsed(d_timer) > PIMORONI_TRACKBALL_DEBUG_INTERVAL) {
54 dprintf("Trackball READ i2c_status_t: %d L: %d R: %d Up: %d D: %d SW: %d\n", status, data->left, data->right, data->up, data->down, data->click);
55 d_timer = timer_read();
56 }
57 }
77#endif 58#endif
59
78 return status; 60 return status;
79} 61}
80 62
81__attribute__((weak)) void pointing_device_init(void) { 63__attribute__((weak)) void pimironi_trackball_device_init(void) {
82 i2c_init(); 64 i2c_init();
83 trackball_set_rgbw(0x00, 0x00, 0x00, 0x00); 65 pimoroni_trackball_set_rgbw(0x00, 0x00, 0x00, 0x00);
84} 66}
85 67
86int16_t trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_t scale) { 68int16_t pimoroni_trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_t scale) {
87 uint8_t offset = 0; 69 uint8_t offset = 0;
88 bool isnegative = false; 70 bool isnegative = false;
89 if (negative_dir > positive_dir) { 71 if (negative_dir > positive_dir) {
@@ -96,7 +78,7 @@ int16_t trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_
96 return isnegative ? -(int16_t)(magnitude) : (int16_t)(magnitude); 78 return isnegative ? -(int16_t)(magnitude) : (int16_t)(magnitude);
97} 79}
98 80
99void trackball_adapt_values(int8_t* mouse, int16_t* offset) { 81void pimoroni_trackball_adapt_values(int8_t* mouse, int16_t* offset) {
100 if (*offset > 127) { 82 if (*offset > 127) {
101 *mouse = 127; 83 *mouse = 127;
102 *offset -= 127; 84 *offset -= 127;
@@ -108,94 +90,3 @@ void trackball_adapt_values(int8_t* mouse, int16_t* offset) {
108 *offset = 0; 90 *offset = 0;
109 } 91 }
110} 92}
111
112__attribute__((weak)) void trackball_click(bool pressed, report_mouse_t* mouse) {
113#ifdef PIMORONI_TRACKBALL_CLICK
114 if (pressed) {
115 mouse->buttons |= MOUSE_BTN1;
116 } else {
117 mouse->buttons &= ~MOUSE_BTN1;
118 }
119#endif
120}
121
122__attribute__((weak)) bool pointing_device_task_user(pimoroni_data* trackball_data) { return true; };
123
124__attribute__((weak)) void pointing_device_task() {
125 static fast_timer_t throttle = 0;
126 static uint16_t debounce = 0;
127
128 if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT && timer_elapsed_fast(throttle) >= PIMORONI_TRACKBALL_INTERVAL_MS) {
129 i2c_status_t status = read_pimoroni_trackball(&current_pimoroni_data);
130
131 if (status == I2C_STATUS_SUCCESS) {
132 error_count = 0;
133
134 if (pointing_device_task_user(&current_pimoroni_data)) {
135 mouse_report = pointing_device_get_report();
136
137 if (!(current_pimoroni_data.click & 128)) {
138 trackball_click(false, &mouse_report);
139 if (!debounce) {
140 if (scrolling) {
141#ifdef PIMORONI_TRACKBALL_INVERT_X
142 h_offset += trackball_get_offsets(current_pimoroni_data.right, current_pimoroni_data.left, PIMORONI_TRACKBALL_SCROLL_SCALE);
143#else
144 h_offset -= trackball_get_offsets(current_pimoroni_data.right, current_pimoroni_data.left, PIMORONI_TRACKBALL_SCROLL_SCALE);
145#endif
146#ifdef PIMORONI_TRACKBALL_INVERT_Y
147 v_offset += trackball_get_offsets(current_pimoroni_data.down, current_pimoroni_data.up, PIMORONI_TRACKBALL_SCROLL_SCALE);
148#else
149 v_offset -= trackball_get_offsets(current_pimoroni_data.down, current_pimoroni_data.up, PIMORONI_TRACKBALL_SCROLL_SCALE);
150#endif
151 } else {
152#ifdef PIMORONI_TRACKBALL_INVERT_X
153 x_offset -= trackball_get_offsets(current_pimoroni_data.right, current_pimoroni_data.left, PIMORONI_TRACKBALL_MOUSE_SCALE);
154#else
155 x_offset += trackball_get_offsets(current_pimoroni_data.right, current_pimoroni_data.left, PIMORONI_TRACKBALL_MOUSE_SCALE);
156#endif
157#ifdef PIMORONI_TRACKBALL_INVERT_Y
158 y_offset -= trackball_get_offsets(current_pimoroni_data.down, current_pimoroni_data.up, PIMORONI_TRACKBALL_MOUSE_SCALE);
159#else
160 y_offset += trackball_get_offsets(current_pimoroni_data.down, current_pimoroni_data.up, PIMORONI_TRACKBALL_MOUSE_SCALE);
161#endif
162 }
163 if (scrolling) {
164#ifndef PIMORONI_TRACKBALL_ROTATE
165 trackball_adapt_values(&mouse_report.h, &h_offset);
166 trackball_adapt_values(&mouse_report.v, &v_offset);
167#else
168 trackball_adapt_values(&mouse_report.h, &v_offset);
169 trackball_adapt_values(&mouse_report.v, &h_offset);
170#endif
171 mouse_report.x = 0;
172 mouse_report.y = 0;
173 } else {
174#ifndef PIMORONI_TRACKBALL_ROTATE
175 trackball_adapt_values(&mouse_report.x, &x_offset);
176 trackball_adapt_values(&mouse_report.y, &y_offset);
177#else
178 trackball_adapt_values(&mouse_report.x, &y_offset);
179 trackball_adapt_values(&mouse_report.y, &x_offset);
180#endif
181 mouse_report.h = 0;
182 mouse_report.v = 0;
183 }
184 } else {
185 debounce--;
186 }
187 } else {
188 trackball_click(true, &mouse_report);
189 debounce = PIMORONI_TRACKBALL_DEBOUNCE_CYCLES;
190 }
191 }
192 } else {
193 error_count++;
194 }
195
196 pointing_device_set_report(mouse_report);
197 pointing_device_send();
198
199 throttle = timer_read_fast();
200 }
201}
diff --git a/drivers/sensors/pimoroni_trackball.h b/drivers/sensors/pimoroni_trackball.h
index 6b2a41425..59ee8724b 100644
--- a/drivers/sensors/pimoroni_trackball.h
+++ b/drivers/sensors/pimoroni_trackball.h
@@ -16,22 +16,46 @@
16 */ 16 */
17#pragma once 17#pragma once
18 18
19#include "quantum.h" 19#include <stdint.h>
20#include "pointing_device.h" 20#include "report.h"
21#include "i2c_master.h"
21 22
22typedef struct pimoroni_data { 23#ifndef PIMORONI_TRACKBALL_ADDRESS
24# define PIMORONI_TRACKBALL_ADDRESS 0x0A
25#endif
26#ifndef PIMORONI_TRACKBALL_INTERVAL_MS
27# define PIMORONI_TRACKBALL_INTERVAL_MS 8
28#endif
29#ifndef PIMORONI_TRACKBALL_SCALE
30# define PIMORONI_TRACKBALL_SCALE 5
31#endif
32#ifndef PIMORONI_TRACKBALL_DEBOUNCE_CYCLES
33# define PIMORONI_TRACKBALL_DEBOUNCE_CYCLES 20
34#endif
35#ifndef PIMORONI_TRACKBALL_ERROR_COUNT
36# define PIMORONI_TRACKBALL_ERROR_COUNT 10
37#endif
38
39#ifndef PIMORONI_TRACKBALL_TIMEOUT
40# define PIMORONI_TRACKBALL_TIMEOUT 100
41#endif
42
43#ifndef PIMORONI_TRACKBALL_DEBUG_INTERVAL
44# define PIMORONI_TRACKBALL_DEBUG_INTERVAL 100
45#endif
46
47typedef struct {
23 uint8_t left; 48 uint8_t left;
24 uint8_t right; 49 uint8_t right;
25 uint8_t up; 50 uint8_t up;
26 uint8_t down; 51 uint8_t down;
27 uint8_t click; 52 uint8_t click;
28} pimoroni_data; 53} pimoroni_data_t;
29 54
30void trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white); 55void pimironi_trackball_device_init(void);
31void trackball_click(bool pressed, report_mouse_t* mouse); 56void pimoroni_trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white);
32int16_t trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_t scale); 57int16_t pimoroni_trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_t scale);
33void trackball_adapt_values(int8_t* mouse, int16_t* offset); 58void pimoroni_trackball_adapt_values(int8_t* mouse, int16_t* offset);
34float trackball_get_precision(void); 59float pimoroni_trackball_get_precision(void);
35void trackball_set_precision(float precision); 60void pimoroni_trackball_set_precision(float precision);
36bool trackball_is_scrolling(void); 61i2c_status_t read_pimoroni_trackball(pimoroni_data_t* data);
37void trackball_set_scrolling(bool scroll);
diff --git a/drivers/sensors/pmw3360.c b/drivers/sensors/pmw3360.c
index 79b653e45..2b27dccbb 100644
--- a/drivers/sensors/pmw3360.c
+++ b/drivers/sensors/pmw3360.c
@@ -20,62 +20,71 @@
20#include "wait.h" 20#include "wait.h"
21#include "debug.h" 21#include "debug.h"
22#include "print.h" 22#include "print.h"
23#include "pmw3360_firmware.h" 23#include PMW3360_FIRMWARE_H
24 24
25// Registers 25// Registers
26#define REG_Product_ID 0x00 26// clang-format off
27#define REG_Revision_ID 0x01 27#define REG_Product_ID 0x00
28#define REG_Motion 0x02 28#define REG_Revision_ID 0x01
29#define REG_Delta_X_L 0x03 29#define REG_Motion 0x02
30#define REG_Delta_X_H 0x04 30#define REG_Delta_X_L 0x03
31#define REG_Delta_Y_L 0x05 31#define REG_Delta_X_H 0x04
32#define REG_Delta_Y_H 0x06 32#define REG_Delta_Y_L 0x05
33#define REG_SQUAL 0x07 33#define REG_Delta_Y_H 0x06
34#define REG_Raw_Data_Sum 0x08 34#define REG_SQUAL 0x07
35#define REG_Maximum_Raw_data 0x09 35#define REG_Raw_Data_Sum 0x08
36#define REG_Minimum_Raw_data 0x0A 36#define REG_Maximum_Raw_data 0x09
37#define REG_Shutter_Lower 0x0B 37#define REG_Minimum_Raw_data 0x0A
38#define REG_Shutter_Upper 0x0C 38#define REG_Shutter_Lower 0x0B
39#define REG_Control 0x0D 39#define REG_Shutter_Upper 0x0C
40#define REG_Config1 0x0F 40#define REG_Control 0x0D
41#define REG_Config2 0x10 41#define REG_Config1 0x0F
42#define REG_Angle_Tune 0x11 42#define REG_Config2 0x10
43#define REG_Frame_Capture 0x12 43#define REG_Angle_Tune 0x11
44#define REG_SROM_Enable 0x13 44#define REG_Frame_Capture 0x12
45#define REG_Run_Downshift 0x14 45#define REG_SROM_Enable 0x13
46#define REG_Rest1_Rate_Lower 0x15 46#define REG_Run_Downshift 0x14
47#define REG_Rest1_Rate_Upper 0x16 47#define REG_Rest1_Rate_Lower 0x15
48#define REG_Rest1_Downshift 0x17 48#define REG_Rest1_Rate_Upper 0x16
49#define REG_Rest2_Rate_Lower 0x18 49#define REG_Rest1_Downshift 0x17
50#define REG_Rest2_Rate_Upper 0x19 50#define REG_Rest2_Rate_Lower 0x18
51#define REG_Rest2_Downshift 0x1A 51#define REG_Rest2_Rate_Upper 0x19
52#define REG_Rest3_Rate_Lower 0x1B 52#define REG_Rest2_Downshift 0x1A
53#define REG_Rest3_Rate_Upper 0x1C 53#define REG_Rest3_Rate_Lower 0x1B
54#define REG_Observation 0x24 54#define REG_Rest3_Rate_Upper 0x1C
55#define REG_Data_Out_Lower 0x25 55#define REG_Observation 0x24
56#define REG_Data_Out_Upper 0x26 56#define REG_Data_Out_Lower 0x25
57#define REG_Raw_Data_Dump 0x29 57#define REG_Data_Out_Upper 0x26
58#define REG_SROM_ID 0x2A 58#define REG_Raw_Data_Dump 0x29
59#define REG_Min_SQ_Run 0x2B 59#define REG_SROM_ID 0x2A
60#define REG_Raw_Data_Threshold 0x2C 60#define REG_Min_SQ_Run 0x2B
61#define REG_Config5 0x2F 61#define REG_Raw_Data_Threshold 0x2C
62#define REG_Power_Up_Reset 0x3A 62#define REG_Config5 0x2F
63#define REG_Shutdown 0x3B 63#define REG_Power_Up_Reset 0x3A
64#define REG_Inverse_Product_ID 0x3F 64#define REG_Shutdown 0x3B
65#define REG_LiftCutoff_Tune3 0x41 65#define REG_Inverse_Product_ID 0x3F
66#define REG_Angle_Snap 0x42 66#define REG_LiftCutoff_Tune3 0x41
67#define REG_LiftCutoff_Tune1 0x4A 67#define REG_Angle_Snap 0x42
68#define REG_Motion_Burst 0x50 68#define REG_LiftCutoff_Tune1 0x4A
69#define REG_LiftCutoff_Tune_Timeout 0x58 69#define REG_Motion_Burst 0x50
70#define REG_LiftCutoff_Tune_Timeout 0x58
70#define REG_LiftCutoff_Tune_Min_Length 0x5A 71#define REG_LiftCutoff_Tune_Min_Length 0x5A
71#define REG_SROM_Load_Burst 0x62 72#define REG_SROM_Load_Burst 0x62
72#define REG_Lift_Config 0x63 73#define REG_Lift_Config 0x63
73#define REG_Raw_Data_Burst 0x64 74#define REG_Raw_Data_Burst 0x64
74#define REG_LiftCutoff_Tune2 0x65 75#define REG_LiftCutoff_Tune2 0x65
76// clang-format on
77
78#ifndef MAX_CPI
79# define MAX_CPI 0x77 // limits to 0--119, should be max cpi/100
80#endif
75 81
76bool _inBurst = false; 82bool _inBurst = false;
77 83
84#ifdef CONSOLE_ENABLE
78void 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')); } 85void 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')); }
86#endif
87#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
79 88
80bool spi_start_adv(void) { 89bool spi_start_adv(void) {
81 bool status = spi_start(PMW3360_CS_PIN, PMW3360_SPI_LSBFIRST, PMW3360_SPI_MODE, PMW3360_SPI_DIVISOR); 90 bool status = spi_start(PMW3360_CS_PIN, PMW3360_SPI_LSBFIRST, PMW3360_SPI_MODE, PMW3360_SPI_DIVISOR);
@@ -124,20 +133,20 @@ uint8_t spi_read_adv(uint8_t reg_addr) {
124 return data; 133 return data;
125} 134}
126 135
127void pmw_set_cpi(uint16_t cpi) { 136void pmw3360_set_cpi(uint16_t cpi) {
128 uint8_t cpival = constrain((cpi / 100) - 1, 0, 0x77); // limits to 0--119 137 uint8_t cpival = constrain((cpi / 100) - 1, 0, MAX_CPI);
129 138
130 spi_start_adv(); 139 spi_start_adv();
131 spi_write_adv(REG_Config1, cpival); 140 spi_write_adv(REG_Config1, cpival);
132 spi_stop(); 141 spi_stop();
133} 142}
134 143
135uint16_t pmw_get_cpi(void) { 144uint16_t pmw3360_get_cpi(void) {
136 uint8_t cpival = spi_read_adv(REG_Config1); 145 uint8_t cpival = spi_read_adv(REG_Config1);
137 return (uint16_t)(cpival & 0xFF) * 100; 146 return (uint16_t)(cpival & 0xFF) * 100;
138} 147}
139 148
140bool pmw_spi_init(void) { 149bool pmw3360_init(void) {
141 setPinOutput(PMW3360_CS_PIN); 150 setPinOutput(PMW3360_CS_PIN);
142 151
143 spi_init(); 152 spi_init();
@@ -164,12 +173,12 @@ bool pmw_spi_init(void) {
164 spi_read_adv(REG_Delta_Y_L); 173 spi_read_adv(REG_Delta_Y_L);
165 spi_read_adv(REG_Delta_Y_H); 174 spi_read_adv(REG_Delta_Y_H);
166 175
167 pmw_upload_firmware(); 176 pmw3360_upload_firmware();
168 177
169 spi_stop_adv(); 178 spi_stop_adv();
170 179
171 wait_ms(10); 180 wait_ms(10);
172 pmw_set_cpi(PMW3360_CPI); 181 pmw3360_set_cpi(PMW3360_CPI);
173 182
174 wait_ms(1); 183 wait_ms(1);
175 184
@@ -177,14 +186,14 @@ bool pmw_spi_init(void) {
177 186
178 spi_write_adv(REG_Angle_Tune, constrain(ROTATIONAL_TRANSFORM_ANGLE, -30, 30)); 187 spi_write_adv(REG_Angle_Tune, constrain(ROTATIONAL_TRANSFORM_ANGLE, -30, 30));
179 188
180 bool init_success = pmw_check_signature(); 189 bool init_success = pmw3360_check_signature();
181 190
182 writePinLow(PMW3360_CS_PIN); 191 writePinLow(PMW3360_CS_PIN);
183 192
184 return init_success; 193 return init_success;
185} 194}
186 195
187void pmw_upload_firmware(void) { 196void pmw3360_upload_firmware(void) {
188 spi_write_adv(REG_SROM_Enable, 0x1d); 197 spi_write_adv(REG_SROM_Enable, 0x1d);
189 198
190 wait_ms(10); 199 wait_ms(10);
@@ -211,16 +220,18 @@ void pmw_upload_firmware(void) {
211 wait_ms(10); 220 wait_ms(10);
212} 221}
213 222
214bool pmw_check_signature(void) { 223bool pmw3360_check_signature(void) {
215 uint8_t pid = spi_read_adv(REG_Product_ID); 224 uint8_t pid = spi_read_adv(REG_Product_ID);
216 uint8_t iv_pid = spi_read_adv(REG_Inverse_Product_ID); 225 uint8_t iv_pid = spi_read_adv(REG_Inverse_Product_ID);
217 uint8_t SROM_ver = spi_read_adv(REG_SROM_ID); 226 uint8_t SROM_ver = spi_read_adv(REG_SROM_ID);
218 return (pid == 0x42 && iv_pid == 0xBD && SROM_ver == 0x04); // signature for SROM 0x04 227 return (pid == firmware_signature[0] && iv_pid == firmware_signature[1] && SROM_ver == firmware_signature[2]); // signature for SROM 0x04
219} 228}
220 229
221report_pmw_t pmw_read_burst(void) { 230report_pmw3360_t pmw3360_read_burst(void) {
222 if (!_inBurst) { 231 if (!_inBurst) {
232#ifdef CONSOLE_ENABLE
223 dprintf("burst on"); 233 dprintf("burst on");
234#endif
224 spi_write_adv(REG_Motion_Burst, 0x00); 235 spi_write_adv(REG_Motion_Burst, 0x00);
225 _inBurst = true; 236 _inBurst = true;
226 } 237 }
@@ -229,12 +240,7 @@ report_pmw_t pmw_read_burst(void) {
229 spi_write(REG_Motion_Burst); 240 spi_write(REG_Motion_Burst);
230 wait_us(35); // waits for tSRAD 241 wait_us(35); // waits for tSRAD
231 242
232 report_pmw_t data; 243 report_pmw3360_t data = {0};
233 data.motion = 0;
234 data.dx = 0;
235 data.mdx = 0;
236 data.dy = 0;
237 data.mdx = 0;
238 244
239 data.motion = spi_read(); 245 data.motion = spi_read();
240 spi_write(0x00); // skip Observation 246 spi_write(0x00); // skip Observation
@@ -245,6 +251,7 @@ report_pmw_t pmw_read_burst(void) {
245 251
246 spi_stop(); 252 spi_stop();
247 253
254#ifdef CONSOLE_ENABLE
248 if (debug_mouse) { 255 if (debug_mouse) {
249 print_byte(data.motion); 256 print_byte(data.motion);
250 print_byte(data.dx); 257 print_byte(data.dx);
@@ -253,6 +260,7 @@ report_pmw_t pmw_read_burst(void) {
253 print_byte(data.mdy); 260 print_byte(data.mdy);
254 dprintf("\n"); 261 dprintf("\n");
255 } 262 }
263#endif
256 264
257 data.isMotion = (data.motion & 0x80) != 0; 265 data.isMotion = (data.motion & 0x80) != 0;
258 data.isOnSurface = (data.motion & 0x08) == 0; 266 data.isOnSurface = (data.motion & 0x08) == 0;
diff --git a/drivers/sensors/pmw3360.h b/drivers/sensors/pmw3360.h
index 7429a6ba0..7b2bef5b8 100644
--- a/drivers/sensors/pmw3360.h
+++ b/drivers/sensors/pmw3360.h
@@ -18,6 +18,8 @@
18 18
19#pragma once 19#pragma once
20 20
21#include <stdint.h>
22#include "report.h"
21#include "spi_master.h" 23#include "spi_master.h"
22 24
23#ifndef PMW3360_CPI 25#ifndef PMW3360_CPI
@@ -25,7 +27,7 @@
25#endif 27#endif
26 28
27#ifndef PMW3360_CLOCK_SPEED 29#ifndef PMW3360_CLOCK_SPEED
28# define PMW3360_CLOCK_SPEED 70000000 30# define PMW3360_CLOCK_SPEED 2000000
29#endif 31#endif
30 32
31#ifndef PMW3360_SPI_LSBFIRST 33#ifndef PMW3360_SPI_LSBFIRST
@@ -52,6 +54,17 @@
52# error "No chip select pin defined -- missing PMW3360_CS_PIN" 54# error "No chip select pin defined -- missing PMW3360_CS_PIN"
53#endif 55#endif
54 56
57/*
58The pmw33660 and pmw3389 use the same registers and timing and such.
59The only differences between the two is the firmware used, and the
60range for the DPI. So add a semi-secret hack to allow use of the
61pmw3389's firmware blob. Also, can set the max cpi range too.
62This should work for the 3390 and 3391 too, in theory.
63*/
64#ifndef PMW3360_FIRMWARE_H
65# define PMW3360_FIRMWARE_H "pmw3360_firmware.h"
66#endif
67
55#ifdef CONSOLE_ENABLE 68#ifdef CONSOLE_ENABLE
56void print_byte(uint8_t byte); 69void print_byte(uint8_t byte);
57#endif 70#endif
@@ -64,19 +77,18 @@ typedef struct {
64 int8_t mdx; 77 int8_t mdx;
65 int16_t dy; // displacement on y directions. 78 int16_t dy; // displacement on y directions.
66 int8_t mdy; 79 int8_t mdy;
67} report_pmw_t; 80} report_pmw3360_t;
68 81
69bool spi_start_adv(void); 82bool spi_start_adv(void);
70void spi_stop_adv(void); 83void spi_stop_adv(void);
71spi_status_t spi_write_adv(uint8_t reg_addr, uint8_t data); 84spi_status_t spi_write_adv(uint8_t reg_addr, uint8_t data);
72uint8_t spi_read_adv(uint8_t reg_addr); 85uint8_t spi_read_adv(uint8_t reg_addr);
73bool pmw_spi_init(void); 86bool pmw3360_init(void);
74void pmw_set_cpi(uint16_t cpi); 87void pmw3360_set_cpi(uint16_t cpi);
75uint16_t pmw_get_cpi(void); 88uint16_t pmw3360_get_cpi(void);
76void pmw_upload_firmware(void); 89void pmw3360_upload_firmware(void);
77bool pmw_check_signature(void); 90bool pmw3360_check_signature(void);
78report_pmw_t pmw_read_burst(void); 91report_pmw3360_t pmw3360_read_burst(void);
79 92
80#define degToRad(angleInDegrees) ((angleInDegrees)*M_PI / 180.0) 93#define degToRad(angleInDegrees) ((angleInDegrees)*M_PI / 180.0)
81#define radToDeg(angleInRadians) ((angleInRadians)*180.0 / M_PI) 94#define radToDeg(angleInRadians) ((angleInRadians)*180.0 / M_PI)
82#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
index 4d5fe7a0b..ed9fda5a7 100644
--- a/drivers/sensors/pmw3360_firmware.h
+++ b/drivers/sensors/pmw3360_firmware.h
@@ -20,8 +20,13 @@
20 20
21#include "progmem.h" 21#include "progmem.h"
22 22
23// PID, Inverse PID, SROM version
24const uint8_t firmware_signature[] PROGMEM = {0x42, 0xBD, 0x04};
25
23#define FIRMWARE_LENGTH 4094 26#define FIRMWARE_LENGTH 4094
24 27
28// Firmware Blob foor PMW3360
29
25// clang-format off 30// clang-format off
26const uint8_t firmware_data[FIRMWARE_LENGTH] PROGMEM = { 31const uint8_t firmware_data[FIRMWARE_LENGTH] PROGMEM = {
27 0x01, 0x04, 0x8E, 0x96, 0x6E, 0x77, 0x3E, 0xFE, 0x7E, 0x5F, 0x1D, 0xB8, 0xF2, 0x66, 0x4E, 0xFF, 32 0x01, 0x04, 0x8E, 0x96, 0x6E, 0x77, 0x3E, 0xFE, 0x7E, 0x5F, 0x1D, 0xB8, 0xF2, 0x66, 0x4E, 0xFF,
diff --git a/drivers/sensors/pmw3389_firmware.h b/drivers/sensors/pmw3389_firmware.h
new file mode 100644
index 000000000..0564dab73
--- /dev/null
+++ b/drivers/sensors/pmw3389_firmware.h
@@ -0,0 +1,303 @@
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// PID, Inverse PID, SROM version
22const uint8_t firmware_signature[] PROGMEM = {0x42, 0xBD, 0x04};
23
24// clang-format off
25// Firmware Blob foor PMW3389
26const uint16_t firmware_length = 4094;
27// clang-format off
28const uint8_t firmware_data[] PROGMEM = { // SROM 0x04
290x01, 0xe8, 0xba, 0x26, 0x0b, 0xb2, 0xbe, 0xfe, 0x7e, 0x5f, 0x3c, 0xdb, 0x15, 0xa8, 0xb3,
300xe4, 0x2b, 0xb5, 0xe8, 0x53, 0x07, 0x6d, 0x3b, 0xd1, 0x20, 0xc2, 0x06, 0x6f, 0x3d, 0xd9,
310x11, 0xa0, 0xc2, 0xe7, 0x2d, 0xb9, 0xd1, 0x20, 0xa3, 0xa5, 0xc8, 0xf3, 0x64, 0x4a, 0xf7,
320x4d, 0x18, 0x93, 0xa4, 0xca, 0xf7, 0x6c, 0x5a, 0x36, 0xee, 0x5e, 0x3e, 0xfe, 0x7e, 0x7e,
330x5f, 0x1d, 0x99, 0xb0, 0xc3, 0xe5, 0x29, 0xd3, 0x03, 0x65, 0x48, 0x12, 0x87, 0x6d, 0x58,
340x32, 0xe6, 0x2f, 0xdc, 0x3a, 0xf2, 0x4f, 0xfd, 0x59, 0x11, 0x81, 0x61, 0x21, 0xc0, 0x02,
350x86, 0x8e, 0x7f, 0x5d, 0x38, 0xf2, 0x47, 0x0c, 0x7b, 0x55, 0x28, 0xb3, 0xe4, 0x4a, 0x16,
360xab, 0xbf, 0xdd, 0x38, 0xf2, 0x66, 0x4e, 0xff, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xa5, 0xc8,
370x12, 0xa6, 0xaf, 0xdc, 0x3a, 0xd1, 0x41, 0x60, 0x75, 0x58, 0x24, 0x92, 0xd4, 0x72, 0x6c,
380xe0, 0x2f, 0xfd, 0x23, 0x8d, 0x1c, 0x5b, 0xb2, 0x97, 0x36, 0x3d, 0x0b, 0xa2, 0x49, 0xb1,
390x58, 0xf2, 0x1f, 0xc0, 0xcb, 0xf8, 0x41, 0x4f, 0xcd, 0x1e, 0x6b, 0x39, 0xa7, 0x2b, 0xe9,
400x30, 0x16, 0x83, 0xd2, 0x0e, 0x47, 0x8f, 0xe3, 0xb1, 0xdf, 0xa2, 0x15, 0xdb, 0x5d, 0x30,
410xc5, 0x1a, 0xab, 0x31, 0x99, 0xf3, 0xfa, 0xb2, 0x86, 0x69, 0xad, 0x7a, 0xe8, 0xa7, 0x18,
420x6a, 0xcc, 0xc8, 0x65, 0x23, 0x87, 0xa8, 0x5f, 0xf5, 0x21, 0x59, 0x75, 0x09, 0x71, 0x45,
430x55, 0x25, 0x4b, 0xda, 0xa1, 0xc3, 0xf7, 0x41, 0xab, 0x59, 0xd9, 0x74, 0x12, 0x55, 0x5f,
440xbc, 0xaf, 0xd9, 0xfd, 0xb0, 0x1e, 0xa3, 0x0f, 0xff, 0xde, 0x11, 0x16, 0x6a, 0xae, 0x0e,
450xe1, 0x5d, 0x3c, 0x10, 0x43, 0x9a, 0xa1, 0x0b, 0x24, 0x8f, 0x0d, 0x7f, 0x0b, 0x5e, 0x4c,
460x42, 0xa4, 0x84, 0x2c, 0x40, 0xd0, 0x55, 0x39, 0xe6, 0x4b, 0xf8, 0x9b, 0x2f, 0xdc, 0x28,
470xff, 0xfa, 0xb5, 0x85, 0x19, 0xe5, 0x28, 0xa1, 0x77, 0xaa, 0x73, 0xf3, 0x03, 0xc7, 0x62,
480xa6, 0x91, 0x18, 0xc9, 0xb0, 0xcd, 0x05, 0xdc, 0xca, 0x81, 0x26, 0x1a, 0x47, 0x40, 0xda,
490x36, 0x7d, 0x6a, 0x53, 0xc8, 0x5a, 0x77, 0x5d, 0x19, 0xa4, 0x1b, 0x23, 0x83, 0xd0, 0xb2,
500xaa, 0x0e, 0xbf, 0x77, 0x4e, 0x3a, 0x3b, 0x59, 0x00, 0x31, 0x0d, 0x02, 0x1b, 0x88, 0x7a,
510xd4, 0xbd, 0x9d, 0xcc, 0x58, 0x04, 0x69, 0xf6, 0x3b, 0xca, 0x42, 0xe2, 0xfd, 0xc3, 0x3d,
520x39, 0xc5, 0xd0, 0x71, 0xe4, 0xc8, 0xb7, 0x3e, 0x3f, 0xc8, 0xe9, 0xca, 0xc9, 0x3f, 0x04,
530x4e, 0x1b, 0x79, 0xca, 0xa5, 0x61, 0xc2, 0xed, 0x1d, 0xa6, 0xda, 0x5a, 0xe9, 0x7f, 0x65,
540x8c, 0xbe, 0x12, 0x6e, 0xa4, 0x5b, 0x33, 0x2f, 0x84, 0x28, 0x9c, 0x1c, 0x88, 0x2d, 0xff,
550x07, 0xbf, 0xa6, 0xd7, 0x5a, 0x88, 0x86, 0xb0, 0x3f, 0xf6, 0x31, 0x5b, 0x11, 0x6d, 0xf5,
560x58, 0xeb, 0x58, 0x02, 0x9e, 0xb5, 0x9a, 0xb1, 0xff, 0x25, 0x9d, 0x8b, 0x4f, 0xb6, 0x0a,
570xf9, 0xea, 0x3e, 0x3f, 0x21, 0x09, 0x65, 0x21, 0x22, 0xfe, 0x3d, 0x4e, 0x11, 0x5b, 0x9e,
580x5a, 0x59, 0x8b, 0xdd, 0xd8, 0xce, 0xd6, 0xd9, 0x59, 0xd2, 0x1e, 0xfd, 0xef, 0x0d, 0x1b,
590xd9, 0x61, 0x7f, 0xd7, 0x2d, 0xad, 0x62, 0x09, 0xe5, 0x22, 0x63, 0xea, 0xc7, 0x31, 0xd9,
600xa1, 0x38, 0x80, 0x5c, 0xa7, 0x32, 0x82, 0xec, 0x1b, 0xa2, 0x49, 0x5a, 0x06, 0xd2, 0x7c,
610xc9, 0x96, 0x57, 0xbb, 0x17, 0x75, 0xfc, 0x7a, 0x8f, 0x0d, 0x77, 0xb5, 0x7a, 0x8e, 0x3e,
620xf4, 0xba, 0x2f, 0x69, 0x13, 0x26, 0xd6, 0xd9, 0x21, 0x60, 0x2f, 0x21, 0x3e, 0x87, 0xee,
630xfd, 0x87, 0x16, 0x0d, 0xc8, 0x08, 0x00, 0x25, 0x71, 0xac, 0x2c, 0x03, 0x2a, 0x37, 0x2d,
640xb3, 0x34, 0x09, 0x91, 0xe3, 0x06, 0x2c, 0x38, 0x37, 0x95, 0x3b, 0x17, 0x7a, 0xaf, 0xac,
650x99, 0x55, 0xab, 0x41, 0x39, 0x5f, 0x8e, 0xa6, 0x43, 0x80, 0x03, 0x88, 0x6f, 0x7d, 0xbd,
660x5a, 0xb4, 0x2b, 0x32, 0x23, 0x5a, 0xa9, 0x31, 0x32, 0x39, 0x4c, 0x5b, 0xf4, 0x6b, 0xaf,
670x66, 0x6f, 0x3c, 0x8e, 0x2d, 0x82, 0x97, 0x9f, 0x4a, 0x01, 0xdc, 0x99, 0x98, 0x00, 0xec,
680x38, 0x7a, 0x79, 0x70, 0xa6, 0x85, 0xd6, 0x21, 0x63, 0x0d, 0x45, 0x9a, 0x2e, 0x5e, 0xa7,
690xb1, 0xea, 0x66, 0x6a, 0xbc, 0x62, 0x2d, 0x7b, 0x7d, 0x85, 0xea, 0x95, 0x2f, 0xc0, 0xe8,
700x6f, 0x35, 0xa0, 0x3a, 0x02, 0x25, 0xbc, 0xb2, 0x5f, 0x5c, 0x43, 0x96, 0xcc, 0x26, 0xd2,
710x16, 0xb4, 0x96, 0x73, 0xd7, 0x13, 0xc7, 0xae, 0x53, 0x15, 0x31, 0x89, 0x68, 0x66, 0x6d,
720x2c, 0x92, 0x1f, 0xcc, 0x5b, 0xa7, 0x8f, 0x5d, 0xbb, 0xc9, 0xdb, 0xe8, 0x3b, 0x9d, 0x61,
730x74, 0x8b, 0x05, 0xa1, 0x58, 0x52, 0x68, 0xee, 0x3d, 0x39, 0x79, 0xa0, 0x9b, 0xdd, 0xe1,
740x55, 0xc9, 0x60, 0xeb, 0xad, 0xb8, 0x5b, 0xc2, 0x5a, 0xb5, 0x2c, 0x18, 0x55, 0xa9, 0x50,
750xc3, 0xf6, 0x72, 0x5f, 0xcc, 0xe2, 0xf4, 0x55, 0xb5, 0xd6, 0xb5, 0x4a, 0x99, 0xa5, 0x28,
760x74, 0x97, 0x18, 0xe8, 0xc0, 0x84, 0x89, 0x50, 0x03, 0x86, 0x4d, 0x1a, 0xb7, 0x09, 0x90,
770xa2, 0x01, 0x04, 0xbb, 0x73, 0x62, 0xcb, 0x97, 0x22, 0x70, 0x5d, 0x52, 0x41, 0x8e, 0xd9,
780x90, 0x15, 0xaa, 0xab, 0x0a, 0x31, 0x65, 0xb4, 0xda, 0xd0, 0xee, 0x24, 0xc9, 0x41, 0x91,
790x1e, 0xbc, 0x46, 0x70, 0x40, 0x9d, 0xda, 0x0e, 0x2a, 0xe4, 0xb2, 0x4c, 0x9f, 0xf2, 0xfc,
800xf3, 0x84, 0x17, 0x44, 0x1e, 0xd7, 0xca, 0x23, 0x1f, 0x3f, 0x5a, 0x22, 0x3d, 0xaf, 0x9b,
810x2d, 0xfc, 0x41, 0xad, 0x26, 0xb4, 0x45, 0x67, 0x0b, 0x80, 0x0e, 0xf9, 0x61, 0x37, 0xec,
820x3b, 0xf4, 0x4b, 0x14, 0xdf, 0x5a, 0x0c, 0x3a, 0x50, 0x0b, 0x14, 0x0c, 0x72, 0xae, 0xc6,
830xc5, 0xec, 0x35, 0x53, 0x2d, 0x59, 0xed, 0x91, 0x74, 0xe2, 0xc4, 0xc8, 0xf2, 0x25, 0x6b,
840x97, 0x6f, 0xc9, 0x76, 0xce, 0xa9, 0xb1, 0x99, 0x8f, 0x5a, 0x92, 0x3b, 0xc4, 0x8d, 0x54,
850x50, 0x40, 0x72, 0xd6, 0x90, 0x83, 0xfc, 0xe5, 0x49, 0x8b, 0x17, 0xf5, 0xfd, 0x6b, 0x8d,
860x32, 0x02, 0xe9, 0x0a, 0xfe, 0xbf, 0x00, 0x6b, 0xa3, 0xad, 0x5f, 0x09, 0x4b, 0x97, 0x2b,
870x00, 0x58, 0x65, 0x2e, 0x07, 0x49, 0x0a, 0x3b, 0x6b, 0x2e, 0x50, 0x6c, 0x1d, 0xac, 0xb7,
880x6a, 0x26, 0xd8, 0x13, 0xa4, 0xca, 0x16, 0xae, 0xab, 0x93, 0xb9, 0x1c, 0x1c, 0xb4, 0x47,
890x6a, 0x38, 0x36, 0x17, 0x27, 0xc9, 0x7f, 0xc7, 0x64, 0xcb, 0x89, 0x58, 0xc5, 0x61, 0xc2,
900xc6, 0xea, 0x15, 0x0b, 0x34, 0x0c, 0x5d, 0x61, 0x76, 0x6e, 0x2b, 0x62, 0x40, 0x92, 0xa3,
910x6c, 0xef, 0xf4, 0xe4, 0xc3, 0xa1, 0xa8, 0xf5, 0x94, 0x79, 0x0d, 0xd1, 0x3d, 0xcb, 0x3d,
920x40, 0xb6, 0xd0, 0xf0, 0x10, 0x54, 0xd8, 0x47, 0x25, 0x51, 0xc5, 0x41, 0x79, 0x00, 0xe5,
930xa0, 0x72, 0xde, 0xbb, 0x3b, 0x62, 0x17, 0xf6, 0xbc, 0x5d, 0x00, 0x76, 0x2e, 0xa7, 0x3b,
940xb6, 0xf1, 0x98, 0x72, 0x59, 0x2a, 0x73, 0xb0, 0x21, 0xd6, 0x49, 0xe0, 0xc0, 0xd5, 0xeb,
950x02, 0x7d, 0x4b, 0x41, 0x28, 0x70, 0x2d, 0xec, 0x2b, 0x71, 0x1f, 0x0b, 0xb9, 0x71, 0x63,
960x06, 0xe6, 0xbc, 0x60, 0xbb, 0xf4, 0x9a, 0x62, 0x43, 0x09, 0x18, 0x4e, 0x93, 0x06, 0x4d,
970x76, 0xfa, 0x7f, 0xbd, 0x02, 0xe4, 0x50, 0x91, 0x12, 0xe5, 0x86, 0xff, 0x64, 0x1e, 0xaf,
980x7e, 0xb3, 0xb2, 0xde, 0x89, 0xc1, 0xa2, 0x6f, 0x40, 0x7b, 0x41, 0x51, 0x63, 0xea, 0x25,
990xd1, 0x97, 0x57, 0x92, 0xa8, 0x45, 0xa1, 0xa5, 0x45, 0x21, 0x43, 0x7f, 0x83, 0x15, 0x29,
1000xd0, 0x30, 0x53, 0x32, 0xb4, 0x5a, 0x17, 0x96, 0xbc, 0xc2, 0x68, 0xa9, 0xb7, 0xaf, 0xac,
1010xdf, 0xf1, 0xe3, 0x89, 0xba, 0x24, 0x79, 0x54, 0xc6, 0x14, 0x07, 0x1c, 0x1e, 0x0d, 0x3a,
1020x6b, 0xe5, 0x3d, 0x4e, 0x10, 0x60, 0x96, 0xec, 0x6c, 0xda, 0x47, 0xae, 0x03, 0x25, 0x39,
1030x1d, 0x74, 0xc8, 0xac, 0x6a, 0xf2, 0x6b, 0x05, 0x2a, 0x9a, 0xe7, 0xe8, 0x92, 0xd6, 0xc2,
1040x6d, 0xfa, 0xe8, 0xa7, 0x9d, 0x5f, 0x48, 0xc9, 0x75, 0xf1, 0x66, 0x6a, 0xdb, 0x5d, 0x9a,
1050xcd, 0x27, 0xdd, 0xb9, 0x24, 0x04, 0x9c, 0x18, 0xc2, 0x6d, 0x0c, 0x91, 0x34, 0x48, 0x42,
1060x6f, 0xe9, 0x59, 0x70, 0xc4, 0x7e, 0x81, 0x0e, 0x32, 0x0a, 0x93, 0x48, 0xb0, 0xc0, 0x15,
1070x9e, 0x05, 0xac, 0x36, 0x16, 0xcb, 0x59, 0x65, 0xa0, 0x83, 0xdf, 0x3e, 0xda, 0xfb, 0x1d,
1080x1a, 0xdb, 0x65, 0xec, 0x9a, 0xc6, 0xc3, 0x8e, 0x3c, 0x45, 0xfd, 0xc8, 0xf5, 0x1c, 0x6a,
1090x67, 0x0d, 0x8f, 0x99, 0x7d, 0x30, 0x21, 0x8c, 0xea, 0x22, 0x87, 0x65, 0xc9, 0xb2, 0x4c,
1100xe4, 0x1b, 0x46, 0xba, 0x54, 0xbd, 0x7c, 0xca, 0xd5, 0x8f, 0x5b, 0xa5, 0x01, 0x04, 0xd8,
1110x0a, 0x16, 0xbf, 0xb9, 0x50, 0x2e, 0x37, 0x2f, 0x64, 0xf3, 0x70, 0x11, 0x02, 0x05, 0x31,
1120x9b, 0xa0, 0xb2, 0x01, 0x5e, 0x4f, 0x19, 0xc9, 0xd4, 0xea, 0xa1, 0x79, 0x54, 0x53, 0xa7,
1130xde, 0x2f, 0x49, 0xd3, 0xd1, 0x63, 0xb5, 0x03, 0x15, 0x4e, 0xbf, 0x04, 0xb3, 0x26, 0x8b,
1140x20, 0xb2, 0x45, 0xcf, 0xcd, 0x5b, 0x82, 0x32, 0x88, 0x61, 0xa7, 0xa8, 0xb2, 0xa0, 0x72,
1150x96, 0xc0, 0xdb, 0x2b, 0xe2, 0x5f, 0xba, 0xe3, 0xf5, 0x8a, 0xde, 0xf1, 0x18, 0x01, 0x16,
1160x40, 0xd9, 0x86, 0x12, 0x09, 0x18, 0x1b, 0x05, 0x0c, 0xb1, 0xb5, 0x47, 0xe2, 0x43, 0xab,
1170xfe, 0x92, 0x63, 0x7e, 0x95, 0x2b, 0xf0, 0xaf, 0xe1, 0xf1, 0xc3, 0x4a, 0xff, 0x2b, 0x09,
1180xbb, 0x4a, 0x0e, 0x9a, 0xc4, 0xd8, 0x64, 0x7d, 0x83, 0xa0, 0x4f, 0x44, 0xdb, 0xc4, 0xa8,
1190x58, 0xef, 0xfc, 0x9e, 0x77, 0xf9, 0xa6, 0x8f, 0x58, 0x8b, 0x12, 0xf4, 0xe9, 0x81, 0x12,
1200x47, 0x51, 0x41, 0x83, 0xef, 0xf6, 0x73, 0xbc, 0x8e, 0x0f, 0x4c, 0x8f, 0x4e, 0x69, 0x90,
1210x77, 0x29, 0x5d, 0x92, 0xb0, 0x6d, 0x06, 0x67, 0x29, 0x60, 0xbd, 0x4b, 0x17, 0xc8, 0x89,
1220x69, 0x28, 0x29, 0xd6, 0x78, 0xcb, 0x11, 0x4c, 0xba, 0x8b, 0x68, 0xae, 0x7e, 0x9f, 0xef,
1230x95, 0xda, 0xe2, 0x9e, 0x7f, 0xe9, 0x55, 0xe5, 0xe1, 0xe2, 0xb7, 0xe6, 0x5f, 0xbb, 0x2c,
1240xa2, 0xe6, 0xee, 0xc7, 0x0a, 0x60, 0xa9, 0xd1, 0x80, 0xdf, 0x7f, 0xd6, 0x97, 0xab, 0x1d,
1250x22, 0x25, 0xfc, 0x79, 0x23, 0xe0, 0xae, 0xc5, 0xef, 0x16, 0xa4, 0xa1, 0x0f, 0x92, 0xa9,
1260xc7, 0xe3, 0x3a, 0x55, 0xdf, 0x62, 0x49, 0xd9, 0xf5, 0x84, 0x49, 0xc5, 0x90, 0x34, 0xd3,
1270xe1, 0xac, 0x99, 0x21, 0xb1, 0x02, 0x76, 0x4a, 0xfa, 0xd4, 0xbb, 0xa4, 0x9c, 0xa2, 0xe2,
1280xcb, 0x3d, 0x3b, 0x14, 0x75, 0x60, 0xd1, 0x02, 0xb4, 0xa3, 0xb4, 0x72, 0x06, 0xf9, 0x19,
1290x9c, 0xe2, 0xe4, 0xa7, 0x0f, 0x25, 0x88, 0xc6, 0x86, 0xd6, 0x8c, 0x74, 0x4e, 0x6e, 0xfc,
1300xa8, 0x48, 0x9e, 0xa7, 0x9d, 0x1a, 0x4b, 0x37, 0x09, 0xc8, 0xb0, 0x10, 0xbe, 0x6f, 0xfe,
1310xa3, 0xc4, 0x7a, 0xb5, 0x3d, 0xe8, 0x30, 0xf1, 0x0d, 0xa0, 0xb2, 0x44, 0xfc, 0x9b, 0x8c,
1320xf8, 0x61, 0xed, 0x81, 0xd1, 0x62, 0x11, 0xb4, 0xe1, 0xd5, 0x39, 0x52, 0x89, 0xd3, 0xa8,
1330x49, 0x31, 0xdf, 0xb6, 0xf9, 0x91, 0xf4, 0x1c, 0x9d, 0x09, 0x95, 0x40, 0x56, 0xe7, 0xe3,
1340xcd, 0x5c, 0x92, 0xc1, 0x1d, 0x6b, 0xe9, 0x78, 0x6f, 0x8e, 0x94, 0x42, 0x66, 0xa2, 0xaa,
1350xd3, 0xc8, 0x2e, 0xe3, 0xf6, 0x07, 0x72, 0x0b, 0x6b, 0x1e, 0x7b, 0xb9, 0x7c, 0xe0, 0xa0,
1360xbc, 0xd9, 0x25, 0xdf, 0x87, 0xa8, 0x5f, 0x9c, 0xcc, 0xf0, 0xdb, 0x42, 0x8e, 0x07, 0x31,
1370x13, 0x01, 0x66, 0x32, 0xd1, 0xb8, 0xd6, 0xe3, 0x5e, 0x12, 0x76, 0x61, 0xd3, 0x38, 0x89,
1380xe6, 0x17, 0x6f, 0xa5, 0xf2, 0x71, 0x0e, 0xa5, 0xe2, 0x88, 0x30, 0xbb, 0xbe, 0x8a, 0xea,
1390xc7, 0x62, 0xc4, 0xcf, 0xb8, 0xcd, 0x33, 0x8d, 0x3d, 0x3e, 0xb5, 0x60, 0x3a, 0x03, 0x92,
1400xe4, 0x6d, 0x1b, 0xe0, 0xb4, 0x84, 0x08, 0x55, 0x88, 0xa7, 0x3a, 0xb9, 0x3d, 0x43, 0xc3,
1410xc0, 0xfa, 0x07, 0x6a, 0xca, 0x94, 0xad, 0x99, 0x55, 0xf1, 0xf1, 0xc0, 0x23, 0x87, 0x1d,
1420x3d, 0x1c, 0xd1, 0x66, 0xa0, 0x57, 0x10, 0x52, 0xa2, 0x7f, 0xbe, 0xf9, 0x88, 0xb6, 0x02,
1430xbf, 0x08, 0x23, 0xa9, 0x0c, 0x63, 0x17, 0x2a, 0xae, 0xf5, 0xf7, 0xb7, 0x21, 0x83, 0x92,
1440x31, 0x23, 0x0d, 0x20, 0xc3, 0xc2, 0x05, 0x21, 0x62, 0x8e, 0x45, 0xe8, 0x14, 0xc1, 0xda,
1450x75, 0xb8, 0xf8, 0x92, 0x01, 0xd0, 0x5d, 0x18, 0x9f, 0x99, 0x11, 0x19, 0xf5, 0x35, 0xe8,
1460x7f, 0x20, 0x88, 0x8c, 0x05, 0x75, 0xf5, 0xd7, 0x40, 0x17, 0xbb, 0x1e, 0x36, 0x52, 0xd9,
1470xa4, 0x9c, 0xc2, 0x9d, 0x42, 0x81, 0xd8, 0xc7, 0x8a, 0xe7, 0x4c, 0x81, 0xe0, 0xb7, 0x57,
1480xed, 0x48, 0x8b, 0xf0, 0x97, 0x15, 0x61, 0xd9, 0x2c, 0x7c, 0x45, 0xaf, 0xc2, 0xcd, 0xfc,
1490xaa, 0x13, 0xad, 0x59, 0xcc, 0xb2, 0xb2, 0x6e, 0xdd, 0x63, 0x9c, 0x32, 0x0f, 0xec, 0x83,
1500xbe, 0x78, 0xac, 0x91, 0x44, 0x1a, 0x1f, 0xea, 0xfd, 0x5d, 0x8e, 0xb4, 0xc0, 0x84, 0xd4,
1510xac, 0xb4, 0x87, 0x5f, 0xac, 0xef, 0xdf, 0xcd, 0x12, 0x56, 0xc8, 0xcd, 0xfe, 0xc5, 0xda,
1520xd3, 0xc1, 0x69, 0xf3, 0x61, 0x05, 0xea, 0x25, 0xe2, 0x12, 0x05, 0x8f, 0x39, 0x08, 0x08,
1530x7c, 0x37, 0xb6, 0x7e, 0x5b, 0xd8, 0xb1, 0x0e, 0xf2, 0xdb, 0x4b, 0xf1, 0xad, 0x90, 0x01,
1540x57, 0xcd, 0xa0, 0xb4, 0x52, 0xe8, 0xf3, 0xd7, 0x8a, 0xbd, 0x4f, 0x9f, 0x21, 0x40, 0x72,
1550xa4, 0xfc, 0x0b, 0x01, 0x2b, 0x2f, 0xb6, 0x4c, 0x95, 0x2d, 0x35, 0x33, 0x41, 0x6b, 0xa0,
1560x93, 0xe7, 0x2c, 0xf2, 0xd3, 0x72, 0x8b, 0xf4, 0x4f, 0x15, 0x3c, 0xaf, 0xd6, 0x12, 0xde,
1570x3f, 0x83, 0x3f, 0xff, 0xf8, 0x7f, 0xf6, 0xcc, 0xa6, 0x7f, 0xc9, 0x9a, 0x6e, 0x1f, 0xc1,
1580x0c, 0xfb, 0xee, 0x9c, 0xe7, 0xaf, 0xc9, 0x26, 0x54, 0xef, 0xb0, 0x39, 0xef, 0xb2, 0xe9,
1590x23, 0xc4, 0xef, 0xd1, 0xa1, 0xa4, 0x25, 0x24, 0x6f, 0x8d, 0x6a, 0xe5, 0x8a, 0x32, 0x3a,
1600xaf, 0xfc, 0xda, 0xce, 0x18, 0x25, 0x42, 0x07, 0x4d, 0x45, 0x8b, 0xdf, 0x85, 0xcf, 0x55,
1610xb2, 0x24, 0xfe, 0x9c, 0x69, 0x74, 0xa7, 0x6e, 0xa0, 0xce, 0xc0, 0x39, 0xf4, 0x86, 0xc6,
1620x8d, 0xae, 0xb9, 0x48, 0x64, 0x13, 0x0b, 0x40, 0x81, 0xa2, 0xc9, 0xa8, 0x85, 0x51, 0xee,
1630x9f, 0xcf, 0xa2, 0x8c, 0x19, 0x52, 0x48, 0xe2, 0xc1, 0xa8, 0x58, 0xb4, 0x10, 0x24, 0x06,
1640x58, 0x51, 0xfc, 0xb9, 0x12, 0xec, 0xfd, 0x73, 0xb4, 0x6d, 0x84, 0xfa, 0x06, 0x8b, 0x05,
1650x0b, 0x2d, 0xd6, 0xd6, 0x1f, 0x29, 0x82, 0x9f, 0x19, 0x12, 0x1e, 0xb2, 0x04, 0x8f, 0x7f,
1660x4d, 0xbd, 0x30, 0x2e, 0xe3, 0xe0, 0x88, 0x29, 0xc5, 0x93, 0xd6, 0x6c, 0x1f, 0x29, 0x45,
1670x91, 0xa7, 0x58, 0xcd, 0x05, 0x17, 0xd6, 0x6d, 0xb3, 0xca, 0x66, 0xcc, 0x3c, 0x4a, 0x74,
1680xfd, 0x08, 0x10, 0xa6, 0x99, 0x92, 0x10, 0xd2, 0x85, 0xab, 0x6e, 0x1d, 0x0e, 0x8b, 0x26,
1690x46, 0xd1, 0x6c, 0x84, 0xc0, 0x26, 0x43, 0x59, 0x68, 0xf0, 0x13, 0x1d, 0xfb, 0xe3, 0xd1,
1700xd2, 0xb4, 0x71, 0x9e, 0xf2, 0x59, 0x6a, 0x33, 0x29, 0x79, 0xd2, 0xd7, 0x26, 0xf1, 0xae,
1710x78, 0x9e, 0x1f, 0x0f, 0x3f, 0xe3, 0xe8, 0xd0, 0x27, 0x78, 0x77, 0xf6, 0xac, 0x9c, 0x56,
1720x39, 0x73, 0x8a, 0x6b, 0x2f, 0x34, 0x78, 0xb1, 0x11, 0xdb, 0xa4, 0x5c, 0x80, 0x01, 0x71,
1730x6a, 0xc2, 0xd1, 0x2e, 0x5e, 0x76, 0x28, 0x70, 0x93, 0xae, 0x3e, 0x78, 0xb0, 0x1f, 0x0f,
1740xda, 0xbf, 0xfb, 0x8a, 0x67, 0x65, 0x4f, 0x91, 0xed, 0x49, 0x75, 0x78, 0x62, 0xa2, 0x93,
1750xb5, 0x70, 0x7f, 0x4d, 0x08, 0x4e, 0x79, 0x61, 0xa8, 0x5f, 0x7f, 0xb4, 0x65, 0x9f, 0x91,
1760x54, 0x3a, 0xe8, 0x50, 0x33, 0xd3, 0xd5, 0x8a, 0x7c, 0xf3, 0x9e, 0x8b, 0x77, 0x7b, 0xc6,
1770xc6, 0x0c, 0x45, 0x95, 0x1f, 0xb0, 0xd0, 0x0b, 0x27, 0x4a, 0xfd, 0xc7, 0xf7, 0x0d, 0x5a,
1780x43, 0xc9, 0x7d, 0x35, 0xb0, 0x7d, 0xc4, 0x9c, 0x57, 0x1e, 0x76, 0x0d, 0xf1, 0x95, 0x30,
1790x71, 0xcc, 0xb3, 0x66, 0x3b, 0x63, 0xa8, 0x6c, 0xa3, 0x43, 0xa0, 0x24, 0xcc, 0xb7, 0x53,
1800xfe, 0xfe, 0xbc, 0x6e, 0x60, 0x89, 0xaf, 0x16, 0x21, 0xc8, 0x91, 0x6a, 0x89, 0xce, 0x80,
1810x2c, 0xf1, 0x59, 0xce, 0xc3, 0x60, 0x61, 0x3b, 0x0b, 0x19, 0xfe, 0x99, 0xac, 0x65, 0x90,
1820x15, 0x12, 0x05, 0xac, 0x7e, 0xff, 0x98, 0x7b, 0x66, 0x64, 0x0e, 0x4b, 0x5b, 0xaa, 0x8d,
1830x3b, 0xd2, 0x56, 0xcf, 0x99, 0x39, 0xee, 0x22, 0x81, 0xd0, 0x60, 0x06, 0x66, 0x20, 0x81,
1840x48, 0x3c, 0x6f, 0x3a, 0x77, 0xba, 0xcb, 0x52, 0xac, 0x79, 0x56, 0xaf, 0xe9, 0x16, 0x17,
1850x0a, 0xa3, 0x82, 0x08, 0xd5, 0x3c, 0x97, 0xcb, 0x09, 0xff, 0x7f, 0xf9, 0x4f, 0x60, 0x05,
1860xb9, 0x53, 0x26, 0xaa, 0xb8, 0x50, 0xaa, 0x19, 0x25, 0xae, 0x5f, 0xea, 0x8a, 0xd0, 0x89,
1870x12, 0x80, 0x43, 0x50, 0x24, 0x12, 0x21, 0x14, 0xcd, 0x77, 0xeb, 0x21, 0xcc, 0x5c, 0x09,
1880x64, 0xf3, 0xc7, 0xcb, 0xc5, 0x4b, 0xc3, 0xe7, 0xed, 0xe7, 0x86, 0x2c, 0x1d, 0x8e, 0x19,
1890x52, 0x9b, 0x2a, 0x0c, 0x18, 0x72, 0x0b, 0x1e, 0x1b, 0xb0, 0x0f, 0x42, 0x99, 0x04, 0xae,
1900xd5, 0xb7, 0x89, 0x1a, 0xb9, 0x4f, 0xd6, 0xaf, 0xf3, 0xc9, 0x93, 0x6f, 0xb0, 0x60, 0x83,
1910x6e, 0x6b, 0xd1, 0x5f, 0x3f, 0x1a, 0x83, 0x1e, 0x24, 0x00, 0x87, 0xb5, 0x3e, 0xdb, 0xf9,
1920x4d, 0xa7, 0x16, 0x2e, 0x19, 0x5b, 0x8f, 0x1b, 0x0d, 0x47, 0x72, 0x42, 0xe9, 0x0a, 0x11,
1930x08, 0x2d, 0x88, 0x1c, 0xbc, 0xc7, 0xb4, 0xbe, 0x29, 0x4d, 0x03, 0x5e, 0xec, 0xdf, 0xf3,
1940x3d, 0x2f, 0xe8, 0x1d, 0x9a, 0xd2, 0xd1, 0xab, 0x41, 0x3d, 0x87, 0x11, 0x45, 0xb0, 0x0d,
1950x46, 0xf5, 0xe8, 0x95, 0x62, 0x1c, 0x68, 0xf7, 0xa6, 0x5b, 0x39, 0x4e, 0xbf, 0x47, 0xba,
1960x5d, 0x7f, 0xb7, 0x6a, 0xf4, 0xba, 0x1d, 0x69, 0xf6, 0xa4, 0xe7, 0xe4, 0x6b, 0x3b, 0x0d,
1970x23, 0x16, 0x4a, 0xb2, 0x68, 0xf0, 0xb2, 0x0d, 0x09, 0x17, 0x6a, 0x63, 0x8c, 0x83, 0xd3,
1980xbd, 0x05, 0xc9, 0xf6, 0xf0, 0xa1, 0x31, 0x0b, 0x2c, 0xac, 0x83, 0xac, 0x80, 0x34, 0x32,
1990xb4, 0xec, 0xd0, 0xbc, 0x54, 0x82, 0x9a, 0xc8, 0xf6, 0xa0, 0x7d, 0xc6, 0x79, 0x73, 0xf4,
2000x20, 0x99, 0xf3, 0xb4, 0x01, 0xde, 0x91, 0x27, 0xf2, 0xc0, 0xdc, 0x81, 0x00, 0x4e, 0x7e,
2010x07, 0x99, 0xc8, 0x3a, 0x51, 0xbc, 0x38, 0xd6, 0x8a, 0xa2, 0xde, 0x3b, 0x6a, 0x8c, 0x1a,
2020x7c, 0x81, 0x0f, 0x3a, 0x1f, 0xe4, 0x05, 0x7b, 0x20, 0x35, 0x6b, 0xa5, 0x6a, 0xa7, 0xe7,
2030xbc, 0x9c, 0x20, 0xec, 0x00, 0x15, 0xe2, 0x51, 0xaf, 0x77, 0xeb, 0x29, 0x3c, 0x7d, 0x2e,
2040x00, 0x5c, 0x81, 0x21, 0xfa, 0x35, 0x6f, 0x40, 0xef, 0xfb, 0xd1, 0x3f, 0xcc, 0x9d, 0x55,
2050x53, 0xfb, 0x5a, 0xa5, 0x56, 0x89, 0x0b, 0x52, 0xeb, 0x57, 0x73, 0x4f, 0x1b, 0x67, 0x24,
2060xcb, 0xb8, 0x6a, 0x10, 0x69, 0xd6, 0xfb, 0x52, 0x40, 0xff, 0x20, 0xa5, 0xf3, 0x72, 0xe1,
2070x3d, 0xa4, 0x8c, 0x81, 0x66, 0x16, 0x0d, 0x5d, 0xad, 0xa8, 0x50, 0x25, 0x78, 0x31, 0x77,
2080x0c, 0x57, 0xe4, 0xe9, 0x15, 0x2d, 0xdb, 0x07, 0x87, 0xc8, 0xb0, 0x43, 0xde, 0xfc, 0xfe,
2090xa9, 0xeb, 0xf5, 0xb0, 0xd3, 0x7b, 0xe9, 0x1f, 0x6e, 0xca, 0xe4, 0x03, 0x95, 0xc5, 0xd1,
2100x59, 0x72, 0x63, 0xf0, 0x86, 0x54, 0xe8, 0x16, 0x62, 0x0b, 0x35, 0x29, 0xc2, 0x68, 0xd0,
2110xd6, 0x3e, 0x90, 0x60, 0x57, 0x1d, 0xc9, 0xed, 0x3f, 0xed, 0xb0, 0x2f, 0x7e, 0x97, 0x02,
2120x51, 0xec, 0xee, 0x6f, 0x82, 0x74, 0x76, 0x7f, 0xfb, 0xd6, 0xc4, 0xc3, 0xdd, 0xe8, 0xb1,
2130x60, 0xfc, 0xc6, 0xb9, 0x0d, 0x6a, 0x33, 0x78, 0xc6, 0xc1, 0xbf, 0x86, 0x2c, 0x50, 0xcc,
2140x9a, 0x70, 0x8e, 0x7b, 0xec, 0xab, 0x95, 0xac, 0x53, 0xa0, 0x4b, 0x07, 0x88, 0xaf, 0x42,
2150xed, 0x19, 0x8d, 0xf6, 0x32, 0x17, 0x48, 0x47, 0x1d, 0x41, 0x6f, 0xfe, 0x2e, 0xa7, 0x8f,
2160x4b, 0xa0, 0x51, 0xf3, 0xbf, 0x02, 0x0a, 0x48, 0x58, 0xf7, 0xa1, 0x6d, 0xea, 0xa5, 0x13,
2170x5a, 0x5b, 0xea, 0x0c, 0x9e, 0x52, 0x4f, 0x9e, 0xb9, 0x71, 0x7f, 0x23, 0x83, 0xda, 0x1b,
2180x86, 0x9a, 0x41, 0x29, 0xda, 0x70, 0xe7, 0x64, 0xa1, 0x7b, 0xd5, 0x0a, 0x22, 0x0d, 0x5c,
2190x40, 0xc4, 0x81, 0x07, 0x25, 0x35, 0x4a, 0x1c, 0x10, 0xdb, 0x45, 0x0a, 0xff, 0x36, 0xd4,
2200xe0, 0xeb, 0x5f, 0x68, 0xd6, 0x67, 0xc6, 0xd0, 0x8b, 0x76, 0x1a, 0x7d, 0x59, 0x42, 0xa1,
2210xcb, 0x96, 0x4d, 0x84, 0x09, 0x9a, 0x3d, 0xe0, 0x52, 0x85, 0x6e, 0x48, 0x90, 0x85, 0x2a,
2220x63, 0xb2, 0x69, 0xd2, 0x00, 0x43, 0x31, 0x37, 0xb3, 0x52, 0xaf, 0x62, 0xfa, 0xc1, 0xe0,
2230x03, 0xfb, 0x62, 0xaa, 0x88, 0xc9, 0xb2, 0x2c, 0xd5, 0xa8, 0xf5, 0xa5, 0x4c, 0x12, 0x59,
2240x4e, 0x06, 0x5e, 0x9b, 0x15, 0x66, 0x11, 0xb2, 0x27, 0x92, 0xdc, 0x98, 0x59, 0xde, 0xdf,
2250xfa, 0x9a, 0x32, 0x2e, 0xc0, 0x5d, 0x3c, 0x33, 0x41, 0x6d, 0xaf, 0xb2, 0x25, 0x23, 0x14,
2260xa5, 0x7b, 0xc7, 0x9b, 0x68, 0xf3, 0xda, 0xeb, 0xe3, 0xa9, 0xe2, 0x6f, 0x0e, 0x1d, 0x1c,
2270xba, 0x55, 0xb6, 0x34, 0x6a, 0x93, 0x1f, 0x1f, 0xb8, 0x34, 0xc8, 0x84, 0x08, 0xb1, 0x6b,
2280x6a, 0x28, 0x74, 0x74, 0xe5, 0xeb, 0x75, 0xe9, 0x7c, 0xd8, 0xba, 0xd8, 0x42, 0xa5, 0xee,
2290x1f, 0x80, 0xd9, 0x96, 0xb2, 0x2e, 0xe7, 0xbf, 0xba, 0xeb, 0xd1, 0x69, 0xbb, 0x8f, 0xfd,
2300x5a, 0x63, 0x8f, 0x39, 0x7f, 0xdf, 0x1d, 0x37, 0xd2, 0x18, 0x35, 0x9d, 0xb6, 0xcc, 0xe4,
2310x27, 0x81, 0x89, 0x38, 0x38, 0x68, 0x33, 0xe7, 0x78, 0xd8, 0x76, 0xf5, 0xee, 0xd0, 0x4a,
2320x07, 0x69, 0x19, 0x7a, 0xad, 0x18, 0xb1, 0x94, 0x61, 0x45, 0x53, 0xa2, 0x48, 0xda, 0x96,
2330x4a, 0xf9, 0xee, 0x94, 0x2a, 0x1f, 0x6e, 0x18, 0x3c, 0x92, 0x46, 0xd1, 0x1a, 0x28, 0x18,
2340x32, 0x1f, 0x3a, 0x45, 0xbe, 0x04, 0x35, 0x92, 0xe5, 0xa3, 0xcb, 0xb5, 0x2e, 0x32, 0x43,
2350xac, 0x65, 0x17, 0x89, 0x99, 0x15, 0x03, 0x9e, 0xb1, 0x23, 0x2f, 0xed, 0x76, 0x4d, 0xd8,
2360xac, 0x21, 0x40, 0xc4, 0x99, 0x4e, 0x65, 0x71, 0x2c, 0xb3, 0x45, 0xab, 0xfb, 0xe7, 0x72,
2370x39, 0x56, 0x30, 0x6d, 0xfb, 0x74, 0xeb, 0x99, 0xf3, 0xcd, 0x57, 0x5c, 0x78, 0x75, 0xe9,
2380x8d, 0xc3, 0xa2, 0xfb, 0x5d, 0xe0, 0x90, 0xc5, 0x55, 0xad, 0x91, 0x53, 0x4e, 0x9e, 0xbd,
2390x8c, 0x49, 0xa4, 0xa4, 0x69, 0x10, 0x0c, 0xc5, 0x76, 0xe9, 0x25, 0x86, 0x8d, 0x66, 0x23,
2400xa8, 0xdb, 0x5c, 0xe8, 0xd9, 0x30, 0xe1, 0x15, 0x7b, 0xc0, 0x99, 0x0f, 0x03, 0xec, 0xaa,
2410x12, 0xef, 0xce, 0xd4, 0xea, 0x55, 0x5c, 0x08, 0x86, 0xf4, 0xf4, 0xb0, 0x83, 0x42, 0x95,
2420x37, 0xb6, 0x38, 0xe0, 0x2b, 0x54, 0x89, 0xbd, 0x4e, 0x20, 0x9d, 0x3f, 0xc3, 0x4b, 0xb7,
2430xec, 0xfa, 0x5a, 0x14, 0x03, 0xcb, 0x64, 0xc8, 0x34, 0x4a, 0x4b, 0x6e, 0xf8, 0x6e, 0x56,
2440xf6, 0xdd, 0x5f, 0xa1, 0x24, 0xe2, 0xd4, 0xd0, 0x82, 0x64, 0x1f, 0x8e, 0x9b, 0xfa, 0xb4,
2450xcb, 0xdb, 0x0a, 0xe8, 0x15, 0xfc, 0x15, 0xab, 0x4b, 0x18, 0xbf, 0xd4, 0x42, 0x14, 0x48,
2460x82, 0x85, 0xdd, 0xeb, 0x49, 0x1b, 0x0b, 0x0b, 0x05, 0xe9, 0xb4, 0xa1, 0x33, 0x0a, 0x5d,
2470x0e, 0x6c, 0x4b, 0xc0, 0xd6, 0x6c, 0x7c, 0xfb, 0x69, 0x0b, 0x53, 0x19, 0xe4, 0xf3, 0x35,
2480xfc, 0xbe, 0xa1, 0x34, 0x02, 0x09, 0x4f, 0x74, 0x86, 0x92, 0xcd, 0x5d, 0x1a, 0xc1, 0x27,
2490x0c, 0xf2, 0xc5, 0xcf, 0xdd, 0x23, 0x93, 0x02, 0xbd, 0x41, 0x5e, 0x42, 0xf0, 0xa0, 0x9d,
2500x0c, 0x72, 0xc8, 0xec, 0x32, 0x0a, 0x8a, 0xfd, 0x3d, 0x5a, 0x41, 0x27, 0x0c, 0x88, 0x59,
2510xad, 0x94, 0x2e, 0xef, 0x5d, 0x8f, 0xc7, 0xdf, 0x66, 0xe4, 0xdd, 0x56, 0x6c, 0x7b, 0xca,
2520x55, 0x81, 0xae, 0xae, 0x5c, 0x1b, 0x1a, 0xab, 0xae, 0x99, 0x8d, 0xcc, 0x42, 0x97, 0x59,
2530xf4, 0x14, 0x3f, 0x75, 0xc6, 0xd1, 0x88, 0xba, 0xaa, 0x84, 0x4a, 0xd0, 0x34, 0x08, 0x3b,
2540x7d, 0xdb, 0x15, 0x06, 0xb0, 0x5c, 0xbd, 0x40, 0xf5, 0xa8, 0xec, 0xae, 0x36, 0x40, 0xdd,
2550x90, 0x1c, 0x3e, 0x0d, 0x7e, 0x73, 0xc7, 0xc2, 0xc5, 0x6a, 0xff, 0x52, 0x05, 0x7f, 0xbe,
2560xd0, 0x92, 0xfd, 0xb3, 0x6f, 0xff, 0x5d, 0xb7, 0x97, 0x64, 0x73, 0x7b, 0xca, 0xd1, 0x98,
2570x24, 0x6b, 0x0b, 0x01, 0x68, 0xdd, 0x27, 0x85, 0x85, 0xb5, 0x83, 0xc1, 0xe0, 0x50, 0x64,
2580xc7, 0xaf, 0xf1, 0xc6, 0x4d, 0xb1, 0xef, 0xc9, 0xb4, 0x0a, 0x6d, 0x65, 0xf3, 0x47, 0xcc,
2590xa3, 0x02, 0x21, 0x0c, 0xbe, 0x22, 0x29, 0x05, 0xcf, 0x5f, 0xe8, 0x94, 0x6c, 0xe5, 0xdc,
2600xc4, 0xdf, 0xbe, 0x3e, 0xa8, 0xb4, 0x18, 0xb0, 0x99, 0xb8, 0x6f, 0xff, 0x5d, 0xb9, 0xfd,
2610x3b, 0x5d, 0x16, 0xbf, 0x3e, 0xd8, 0xb3, 0xd8, 0x08, 0x34, 0xf6, 0x47, 0x35, 0x5b, 0x72,
2620x1a, 0x33, 0xad, 0x52, 0x5d, 0xb8, 0xd0, 0x77, 0xc6, 0xab, 0xba, 0x55, 0x09, 0x5f, 0x02,
2630xf8, 0xd4, 0x5f, 0x53, 0x06, 0x91, 0xcd, 0x74, 0x42, 0xae, 0x54, 0x91, 0x81, 0x62, 0x13,
2640x6f, 0xd8, 0xa9, 0x77, 0xc3, 0x6c, 0xcb, 0xf1, 0x29, 0x5a, 0xcc, 0xda, 0x35, 0xbd, 0x52,
2650x23, 0xbe, 0x59, 0xeb, 0x12, 0x6d, 0xb7, 0x53, 0xee, 0xfc, 0xb4, 0x1b, 0x13, 0x5e, 0xba,
2660x16, 0x7c, 0xc5, 0xf3, 0xe3, 0x6d, 0x07, 0x78, 0xf5, 0x2b, 0x21, 0x05, 0x88, 0x4c, 0xc0,
2670xa1, 0xe3, 0x36, 0x10, 0xf8, 0x1b, 0xd8, 0x17, 0xfb, 0x6a, 0x4e, 0xd8, 0xb3, 0x47, 0x2d,
2680x99, 0xbd, 0xbb, 0x5d, 0x37, 0x7d, 0xba, 0xf1, 0xe1, 0x7c, 0xc0, 0xc5, 0x54, 0x62, 0x7f,
2690xcf, 0x5a, 0x4a, 0x93, 0xcc, 0xf1, 0x1b, 0x34, 0xc8, 0xa6, 0x05, 0x4c, 0x55, 0x8b, 0x54,
2700x84, 0xd5, 0x77, 0xeb, 0xc0, 0x6d, 0x3a, 0x29, 0xbd, 0x75, 0x61, 0x09, 0x9a, 0x2c, 0xbb,
2710xf7, 0x18, 0x79, 0x34, 0x90, 0x24, 0xa5, 0x81, 0x70, 0x87, 0xc5, 0x02, 0x7c, 0xba, 0xd4,
2720x5e, 0x14, 0x8e, 0xe4, 0xed, 0xa2, 0x61, 0x6a, 0xb9, 0x6e, 0xb5, 0x4a, 0xb9, 0x01, 0x46,
2730xf4, 0xcf, 0xbc, 0x09, 0x2f, 0x27, 0x4b, 0xbd, 0x86, 0x7a, 0x10, 0xe1, 0xd4, 0xc8, 0xd9,
2740x20, 0x8d, 0x8a, 0x63, 0x00, 0x63, 0x44, 0xeb, 0x54, 0x0b, 0x75, 0x49, 0x10, 0xa2, 0xa7,
2750xad, 0xb9, 0xd1, 0x01, 0x80, 0x63, 0x25, 0xc8, 0x12, 0xa6, 0xce, 0x1e, 0xbe, 0xfe, 0x7e,
2760x5f, 0x3c, 0xdb, 0x34, 0xea, 0x37, 0xec, 0x3b, 0xd5, 0x28, 0xd2, 0x07, 0x8c, 0x9a, 0xb6,
2770xee, 0x5e, 0x3e, 0xdf, 0x1d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x5c, 0x1b, 0xb4, 0xea, 0x56,
2780x2e, 0xde, 0x1f, 0x9d, 0xb8, 0xd3, 0x24, 0xab, 0xd4, 0x2a, 0xd6, 0x2e, 0xde, 0x1f, 0x9d,
2790xb8, 0xf2, 0x66, 0x2f, 0xbd, 0xf8, 0x72, 0x66, 0x4e, 0x1e, 0x9f, 0x9d, 0xb8, 0xf2, 0x47,
2800x0c, 0x9a, 0xb6, 0xee, 0x3f, 0xfc, 0x7a, 0x57, 0x0d, 0x79, 0x70, 0x62, 0x27, 0xad, 0xb9,
2810xd1, 0x01, 0x61, 0x40, 0x02, 0x67, 0x2d, 0xd8, 0x32, 0xe6, 0x2f, 0xdc, 0x3a, 0xd7, 0x2c,
2820xbb, 0xf4, 0x4b, 0xf5, 0x49, 0xf1, 0x60, 0x23, 0xc4, 0x0a, 0x77, 0x4d, 0xf9, 0x51, 0x01,
2830x80, 0x63, 0x25, 0xa9, 0xb1, 0xe0, 0x42, 0xe7, 0x4c, 0x1a, 0x97, 0xac, 0xbb, 0xf4, 0x6a,
2840x37, 0xcd, 0x18, 0xb2, 0xe6, 0x2f, 0xdc, 0x1b, 0x95, 0xa8, 0xd2, 0x07, 0x6d, 0x58, 0x32,
2850xe6, 0x4e, 0x1e, 0x9f, 0xbc, 0xfa, 0x57, 0x0d, 0x79, 0x51, 0x20, 0xc2, 0x06, 0x6f, 0x5c,
2860x1b, 0x95, 0xa8, 0xb3, 0xc5, 0xe9, 0x31, 0xe0, 0x23, 0xc4, 0x0a, 0x77, 0x4d, 0x18, 0x93,
2870x85, 0x69, 0x31, 0xc1, 0xe1, 0x21, 0xc0, 0xe3, 0x44, 0x0a, 0x77, 0x6c, 0x5a, 0x17, 0x8d,
2880x98, 0x93, 0xa4, 0xab, 0xd4, 0x2a, 0xb7, 0xec, 0x5a, 0x17, 0xac, 0xbb, 0xf4, 0x4b, 0x14,
2890xaa, 0xb7, 0xec, 0x3b, 0xd5, 0x28, 0xb3, 0xc5, 0xe9, 0x31, 0xc1, 0x00, 0x82, 0x67, 0x4c,
2900xfb, 0x55, 0x28, 0xd2, 0x26, 0xaf, 0xbd, 0xd9, 0x11, 0x81, 0x61, 0x21, 0xa1, 0xa1, 0xc0,
2910x02, 0x86, 0x6f, 0x5c, 0x1b, 0xb4, 0xcb, 0x14, 0x8b, 0x94, 0xaa, 0xd6, 0x2e, 0xbf, 0xdd,
2920x19, 0xb0, 0xe2, 0x46, 0x0e, 0x7f, 0x7c, 0x5b, 0x15, 0x89, 0x90, 0x83, 0x84, 0x6b, 0x54,
2930x0b, 0x75, 0x68, 0x52, 0x07, 0x6d, 0x58, 0x32, 0xc7, 0xed, 0x58, 0x32, 0xc7, 0xed, 0x58,
2940x32, 0xe6, 0x4e, 0xff, 0x7c, 0x7a, 0x76, 0x6e, 0x3f, 0xdd, 0x38, 0xd3, 0x05, 0x88, 0x92,
2950xa6, 0xaf, 0xdc, 0x1b, 0xb4, 0xcb, 0xf5, 0x68, 0x52, 0x07, 0x8c, 0x7b, 0x55, 0x09, 0x90,
2960x83, 0x84, 0x6b, 0x54, 0x2a, 0xb7, 0xec, 0x3b, 0xd5, 0x09, 0x90, 0xa2, 0xc6, 0x0e, 0x7f,
2970x7c, 0x7a, 0x57, 0x0d, 0x98, 0xb2, 0xc7, 0xed, 0x58, 0x32, 0xc7, 0x0c, 0x7b, 0x74, 0x4b,
2980x14, 0x8b, 0x94, 0xaa, 0xb7, 0xcd, 0x18, 0x93, 0xa4, 0xca, 0x16, 0xae, 0xbf, 0xdd, 0x19,
2990xb0, 0xe2, 0x46, 0x0e, 0x7f, 0x5d, 0x19, 0x91, 0x81, 0x80, 0x63, 0x44, 0xeb, 0x35, 0xc9,
3000x10, 0x83, 0x65, 0x48, 0x12, 0xa6, 0xce, 0x1e, 0x9f, 0xbc, 0xdb, 0x15, 0x89, 0x71, 0x60,
3010x23, 0xc4, 0xeb, 0x54, 0x2a, 0xb7, 0xec, 0x5a, 0x36, 0xcf, 0x81, 0x10, 0xac, 0x74 };
302
303// clang-format off