summaryrefslogtreecommitdiffstats
path: root/drivers/sensors/adns5050.c
blob: c23d24d5af5edcd7356687698ade135b633f6c2e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/* Copyright 2021 Colin Lam (Ploopy Corporation)
 * Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 * Copyright 2019 Sunjun Kim
 * Copyright 2019 Hiroyuki Okada
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "adns5050.h"
#include "wait.h"
#include "debug.h"
#include "gpio.h"

// Registers
// clang-format off
#define REG_PRODUCT_ID     0x00
#define REG_REVISION_ID    0x01
#define REG_MOTION         0x02
#define REG_DELTA_X        0x03
#define REG_DELTA_Y        0x04
#define REG_SQUAL          0x05
#define REG_SHUTTER_UPPER  0x06
#define REG_SHUTTER_LOWER  0x07
#define REG_MAXIMUM_PIXEL  0x08
#define REG_PIXEL_SUM      0x09
#define REG_MINIMUM_PIXEL  0x0a
#define REG_PIXEL_GRAB     0x0b
#define REG_MOUSE_CONTROL  0x0d
#define REG_MOUSE_CONTROL2 0x19
#define REG_LED_DC_MODE    0x22
#define REG_CHIP_RESET     0x3a
#define REG_PRODUCT_ID2    0x3e
#define REG_INV_REV_ID     0x3f
#define REG_MOTION_BURST   0x63
// clang-format on

void adns5050_init(void) {
    // Initialize the ADNS serial pins.
    setPinOutput(ADNS5050_SCLK_PIN);
    setPinOutput(ADNS5050_SDIO_PIN);
    setPinOutput(ADNS5050_CS_PIN);

    // reboot the adns.
    // if the adns hasn't initialized yet, this is harmless.
    adns5050_write_reg(REG_CHIP_RESET, 0x5a);

    // wait maximum time before adns is ready.
    // this ensures that the adns is actuall ready after reset.
    wait_ms(55);

    // read a burst from the adns and then discard it.
    // gets the adns ready for write commands
    // (for example, setting the dpi).
    adns5050_read_burst();
}

// Perform a synchronization with the ADNS.
// Just as with the serial protocol, this is used by the slave to send a
// synchronization signal to the master.
void adns5050_sync(void) {
    writePinLow(ADNS5050_CS_PIN);
    wait_us(1);
    writePinHigh(ADNS5050_CS_PIN);
}

void adns5050_cs_select(void) { writePinLow(ADNS5050_CS_PIN); }

void adns5050_cs_deselect(void) { writePinHigh(ADNS5050_CS_PIN); }

uint8_t adns5050_serial_read(void) {
    setPinInput(ADNS5050_SDIO_PIN);
    uint8_t byte = 0;

    for (uint8_t i = 0; i < 8; ++i) {
        writePinLow(ADNS5050_SCLK_PIN);
        wait_us(1);

        byte = (byte << 1) | readPin(ADNS5050_SDIO_PIN);

        writePinHigh(ADNS5050_SCLK_PIN);
        wait_us(1);
    }

    return byte;
}

void adns5050_serial_write(uint8_t data) {
    setPinOutput(ADNS5050_SDIO_PIN);

    for (int8_t b = 7; b >= 0; b--) {
        writePinLow(ADNS5050_SCLK_PIN);

        if (data & (1 << b))
            writePinHigh(ADNS5050_SDIO_PIN);
        else
            writePinLow(ADNS5050_SDIO_PIN);

        wait_us(2);

        writePinHigh(ADNS5050_SCLK_PIN);
    }

    // tSWR. See page 15 of the ADNS spec sheet.
    // Technically, this is only necessary if the next operation is an SDIO
    // read. This is not guaranteed to be the case, but we're being lazy.
    wait_us(4);

    // Note that tSWW is never necessary. All write operations require at
    // least 32us, which exceeds tSWW, so there's never a need to wait for it.
}

// Read a byte of data from a register on the ADNS.
// Don't forget to use the register map (as defined in the header file).
uint8_t adns5050_read_reg(uint8_t reg_addr) {
    adns5050_cs_select();

    adns5050_serial_write(reg_addr);

    // We don't need a minimum tSRAD here. That's because a 4ms wait time is
    // already included in adns5050_serial_write(), so we're good.
    // See page 10 and 15 of the ADNS spec sheet.
    // wait_us(4);

    uint8_t byte = adns5050_serial_read();

    // tSRW & tSRR. See page 15 of the ADNS spec sheet.
    // Technically, this is only necessary if the next operation is an SDIO
    // read or write. This is not guaranteed to be the case.
    // Honestly, this wait could probably be removed.
    wait_us(1);

    adns5050_cs_deselect();

    return byte;
}

void adns5050_write_reg(uint8_t reg_addr, uint8_t data) {
    adns5050_cs_select();
    adns5050_serial_write(0b10000000 | reg_addr);
    adns5050_serial_write(data);
    adns5050_cs_deselect();
}

report_adns5050_t adns5050_read_burst(void) {
    adns5050_cs_select();

    report_adns5050_t data;
    data.dx = 0;
    data.dy = 0;

    adns5050_serial_write(REG_MOTION_BURST);

    // We don't need a minimum tSRAD here. That's because a 4ms wait time is
    // already included in adns5050_serial_write(), so we're good.
    // See page 10 and 15 of the ADNS spec sheet.
    // wait_us(4);

    uint8_t x = adns5050_serial_read();
    uint8_t y = adns5050_serial_read();

    // Burst mode returns a bunch of other shit that we don't really need.
    // Setting CS to high ends burst mode early.
    adns5050_cs_deselect();

    data.dx = convert_twoscomp(x);
    data.dy = convert_twoscomp(y);

    return data;
}

// Convert a two's complement byte from an unsigned data type into a signed
// data type.
int8_t convert_twoscomp(uint8_t data) {
    if ((data & 0x80) == 0x80)
        return -128 + (data & 0x7F);
    else
        return data;
}

// Don't forget to use the definitions for CPI in the header file.
void adns5050_set_cpi(uint16_t cpi) {
    uint8_t cpival = constrain((cpi / 125), 0x1, 0xD);  // limits to 0--119

    adns5050_write_reg(REG_MOUSE_CONTROL2, 0b10000 | cpival);
}

uint16_t adns5050_get_cpi(void) {
    uint8_t cpival = adns5050_read_reg(REG_MOUSE_CONTROL2);
    return (uint16_t)((cpival & 0b10000) * 125);
}

bool adns5050_check_signature(void) {
    uint8_t pid  = adns5050_read_reg(REG_PRODUCT_ID);
    uint8_t rid  = adns5050_read_reg(REG_REVISION_ID);
    uint8_t pid2 = adns5050_read_reg(REG_PRODUCT_ID2);

    return (pid == 0x12 && rid == 0x01 && pid2 == 0x26);
}