summaryrefslogtreecommitdiffstats
path: root/drivers/sensors/adns5050.c
blob: 254ef2ee87e5fa5892c045a007b5fa979cbaf4a4 (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
/* 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 "print.h"
#include "gpio.h"

#ifndef OPTIC_ROTATED
#    define OPTIC_ROTATED false
#endif

// Definitions for the ADNS serial line.
#ifndef ADNS_SCLK_PIN
#    define ADNS_SCLK_PIN B7
#endif

#ifndef ADNS_SDIO_PIN
#    define ADNS_SDIO_PIN C6
#endif

#ifndef ADNS_CS_PIN
#    define ADNS_CS_PIN B4
#endif

#ifdef CONSOLE_ENABLE
void 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')); }
#endif

// Initialize the ADNS serial pins.
void adns_init(void) {
    setPinOutput(ADNS_SCLK_PIN);
    setPinOutput(ADNS_SDIO_PIN);
    setPinOutput(ADNS_CS_PIN);
}

// 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 adns_sync(void) {
    writePinLow(ADNS_CS_PIN);
    wait_us(1);
    writePinHigh(ADNS_CS_PIN);
}

void adns_cs_select(void) { writePinLow(ADNS_CS_PIN); }

void adns_cs_deselect(void) { writePinHigh(ADNS_CS_PIN); }

uint8_t adns_serial_read(void) {
    setPinInput(ADNS_SDIO_PIN);
    uint8_t byte = 0;

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

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

        writePinHigh(ADNS_SCLK_PIN);
        wait_us(1);
    }

    return byte;
}

void adns_serial_write(uint8_t data) {
    setPinOutput(ADNS_SDIO_PIN);

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

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

        wait_us(2);

        writePinHigh(ADNS_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 adns_read_reg(uint8_t reg_addr) {
    adns_cs_select();

    adns_serial_write(reg_addr);

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

    uint8_t byte = adns_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);

    adns_cs_deselect();

    return byte;
}

void adns_write_reg(uint8_t reg_addr, uint8_t data) {
    adns_cs_select();
    adns_serial_write(0b10000000 | reg_addr);
    adns_serial_write(data);
    adns_cs_deselect();
}

report_adns_t adns_read_burst(void) {
    adns_cs_select();

    report_adns_t data;
    data.dx = 0;
    data.dy = 0;

    adns_serial_write(REG_MOTION_BURST);

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

    uint8_t x = adns_serial_read();
    uint8_t y = adns_serial_read();

    // Burst mode returns a bunch of other shit that we don't really need.
    // Setting CS to high ends burst mode early.
    adns_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 adns_set_cpi(uint8_t cpi) { adns_write_reg(REG_MOUSE_CONTROL2, cpi); }

bool adns_check_signature(void) {
    uint8_t pid  = adns_read_reg(REG_PRODUCT_ID);
    uint8_t rid  = adns_read_reg(REG_REVISION_ID);
    uint8_t pid2 = adns_read_reg(REG_PRODUCT_ID2);

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